From 131257cef1b2677dff8b3acc7ec8490568097af9 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 14 Feb 2020 08:31:31 +0100 Subject: [PATCH 001/503] GCodeProcessor basic framework --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/GCode.cpp | 8 ++ src/libslic3r/GCode.hpp | 12 ++ src/libslic3r/GCode/GCodeProcessor.cpp | 177 +++++++++++++++++++++++++ src/libslic3r/GCode/GCodeProcessor.hpp | 87 ++++++++++++ src/libslic3r/Technologies.hpp | 11 ++ 6 files changed, 297 insertions(+) create mode 100644 src/libslic3r/GCode/GCodeProcessor.cpp create mode 100644 src/libslic3r/GCode/GCodeProcessor.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index c8e259caa9..52b5e60f17 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -91,6 +91,8 @@ add_library(libslic3r STATIC GCode/ToolOrdering.hpp GCode/WipeTower.cpp GCode/WipeTower.hpp + GCode/GCodeProcessor.cpp + GCode/GCodeProcessor.hpp GCode.cpp GCode.hpp GCodeReader.cpp diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 4cbab67a63..e09dc54fe8 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -760,6 +760,14 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ throw std::runtime_error(msg); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_GCODE_VIEWER + m_processor.apply_config(print->config()); + m_processor.reset(); + m_processor.process_file(path_tmp); +#endif // ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + GCodeTimeEstimator::PostProcessData normal_data = m_normal_time_estimator.get_post_process_data(); GCodeTimeEstimator::PostProcessData silent_data = m_silent_time_estimator.get_post_process_data(); diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 0344924a13..8ddf2db61d 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -14,6 +14,11 @@ #include "GCode/SpiralVase.hpp" #include "GCode/ToolOrdering.hpp" #include "GCode/WipeTower.hpp" +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_GCODE_VIEWER +#include "GCode/GCodeProcessor.hpp" +#endif // ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "GCodeTimeEstimator.hpp" #include "EdgeGrid.hpp" #include "GCode/Analyzer.hpp" @@ -383,6 +388,13 @@ private: // Analyzer GCodeAnalyzer m_analyzer; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_GCODE_VIEWER + // Processor + GCodeProcessor m_processor; +#endif // ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + // Write a string into a file. void _write(FILE* file, const std::string& what) { this->_write(file, what.c_str()); } void _write(FILE* file, const char *what); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp new file mode 100644 index 0000000000..08066ee57e --- /dev/null +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -0,0 +1,177 @@ +#include "../libslic3r.h" +#include "GCodeProcessor.hpp" + +#if ENABLE_GCODE_VIEWER + +static const float INCHES_TO_MM = 25.4f; +static const float MMMIN_TO_MMSEC = 1.0f / 60.0f; + +namespace Slic3r { + +void GCodeProcessor::apply_config(const PrintConfig& config) +{ + m_parser.apply_config(config); +} + +void GCodeProcessor::reset() +{ + m_units = EUnits::Millimeters; + m_global_positioning_type = EPositioningType::Absolute; + m_e_local_positioning_type = EPositioningType::Absolute; + + ::memset(m_start_position.data(), 0, sizeof(AxisCoords)); + ::memset(m_end_position.data(), 0, sizeof(AxisCoords)); + ::memset(m_origin.data(), 0, sizeof(AxisCoords)); + + m_feedrate = 0.0f; + + m_moves.clear(); +} + +void GCodeProcessor::process_file(const std::string& filename) +{ + MoveStep start_step {}; + m_moves.emplace_back(start_step); + m_parser.parse_file(filename, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { process_gcode_line(line); }); + int a = 0; +} + +void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) +{ +/* + std::cout << line.raw() << std::endl; +*/ + + // update start position + m_start_position = m_end_position; + + std::string cmd = line.cmd(); + std::string comment = line.comment(); + + if (cmd.length() > 1) + { + // process command lines + switch (::toupper(cmd[0])) + { + case 'G': + { + switch (::atoi(&cmd[1])) + { + // Move + case 1: { process_G1(line); } + default: { break; } + } + break; + } + case 'M': + { + switch (::atoi(&cmd[1])) + { + default: { break; } + } + break; + } + case 'T': + { + break; + } + default: { break; } + } + } + else if (comment.length() > 1) + { + // process tags embedded into comments + process_comment(line); + } +} + +void GCodeProcessor::process_comment(const GCodeReader::GCodeLine& line) +{ +} + +void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) +{ + auto axis_absolute_position = [this](Axis axis, const GCodeReader::GCodeLine& lineG1) + { + bool is_relative = (m_global_positioning_type == EPositioningType::Relative); + if (axis == E) + is_relative |= (m_e_local_positioning_type == EPositioningType::Relative); + + if (lineG1.has(Slic3r::Axis(axis))) + { + float lengthsScaleFactor = (m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f; + float ret = lineG1.value(Slic3r::Axis(axis)) * lengthsScaleFactor; + return is_relative ? m_start_position[axis] + ret : m_origin[axis] + ret; + } + else + return m_start_position[axis]; + }; + + // updates axes positions from line + for (unsigned char a = X; a <= E; ++a) + { + m_end_position[a] = axis_absolute_position((Axis)a, line); + } + + // updates feedrate from line, if present + if (line.has_f()) + m_feedrate = line.f() * MMMIN_TO_MMSEC; + + // calculates movement deltas + float max_abs_delta = 0.0f; + AxisCoords delta_pos; + for (unsigned char a = X; a <= E; ++a) + { + delta_pos[a] = m_end_position[a] - m_start_position[a]; + max_abs_delta = std::max(max_abs_delta, std::abs(delta_pos[a])); + } + + // no displacement, return + if (max_abs_delta == 0.0f) + return; // <<<<<<<<<<<<<<<<< is this correct for time estimate ? + + // detect move type + EMoveType move_type = EMoveType::Noop; + if (delta_pos[E] < 0.0f) + { + if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f) || (delta_pos[Z] != 0.0f)) + move_type = EMoveType::Travel; + else + move_type = EMoveType::Retract; + } + else if (delta_pos[E] > 0.0f) + { + if ((delta_pos[X] == 0.0f) && (delta_pos[Y] == 0.0f) && (delta_pos[Z] == 0.0f)) + move_type = EMoveType::Unretract; + else if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f)) + move_type = EMoveType::Extrude; + } + else if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f) || (delta_pos[Z] != 0.0f)) + move_type = EMoveType::Travel; + + + MoveStep move_step { m_end_position, m_feedrate, move_type }; + m_moves.emplace_back(move_step); + +/* + std::cout << "start: "; + for (unsigned char a = X; a <= E; ++a) + { + std::cout << m_start_position[a]; + if (a != E) + std::cout << ", "; + } + std::cout << " - end: "; + for (unsigned char a = X; a <= E; ++a) + { + std::cout << m_end_position[a]; + if (a != E) + std::cout << ", "; + } + std::cout << "\n"; +*/ +} + +} /* namespace Slic3r */ + +#endif // ENABLE_GCODE_VIEWER diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp new file mode 100644 index 0000000000..0a3dae0327 --- /dev/null +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -0,0 +1,87 @@ +#ifndef slic3r_GCodeProcessor_hpp_ +#define slic3r_GCodeProcessor_hpp_ + +#if ENABLE_GCODE_VIEWER +#include "../GCodeReader.hpp" + +namespace Slic3r { + + class GCodeProcessor + { + using AxisCoords = std::array; + + enum class EUnits : unsigned char + { + Millimeters, + Inches + }; + + enum class EPositioningType : unsigned char + { + Absolute, + Relative + }; + + enum class EMoveType : unsigned char + { + Noop, + Retract, + Unretract, + Tool_change, + Travel, + Extrude, + Num_Types + }; + + struct MoveStep + { + AxisCoords position; // mm + float feedrate; // mm/s + EMoveType type; + }; + + using MoveStepsList = std::vector; + + GCodeReader m_parser; + + EUnits m_units; + EPositioningType m_global_positioning_type; + EPositioningType m_e_local_positioning_type; + + AxisCoords m_start_position; // mm + AxisCoords m_end_position; // mm + AxisCoords m_origin; // mm + + float m_feedrate; // mm/s + + MoveStepsList m_moves; + + + public: + GCodeProcessor() { reset(); } + + void apply_config(const PrintConfig& config); + + void reset(); + + // Process the gcode contained in the file with the given filename + // Return false if any error occourred + void process_file(const std::string& filename); + + private: + void process_gcode_line(const GCodeReader::GCodeLine& line); + + // Process tags embedded into comments + void process_comment(const GCodeReader::GCodeLine& line); + + // Move + void process_G1(const GCodeReader::GCodeLine& line); + }; + +} /* namespace Slic3r */ + +#endif // ENABLE_GCODE_VIEWER + +#endif /* slic3r_GCodeProcessor_hpp_ */ + + diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 0728dedc60..4983787861 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -63,4 +63,15 @@ #define ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK (1 && ENABLE_2_2_0_BETA1) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//================== +// 2.3.0.alpha1 techs +//================== +#define ENABLE_2_3_0_ALPHA1 1 + +// Enable G-Code viewer +#define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + + #endif // _technologies_h_ From 3b6d334d7bb0331e372cde14c69e153810fc0cbe Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 2 Mar 2020 15:13:23 +0100 Subject: [PATCH 002/503] ENABLE_GCODE_VIEWER - Basic framework for new gcode viewer --- src/PrusaSlicer.cpp | 4 +++ src/libslic3r/GCode.cpp | 34 +++++++++++++++++---- src/libslic3r/GCode.hpp | 10 +++--- src/libslic3r/GCode/GCodeProcessor.cpp | 30 ++++++++++-------- src/libslic3r/GCode/GCodeProcessor.hpp | 32 +++++++++++++------ src/libslic3r/Print.cpp | 12 +++++--- src/libslic3r/Print.hpp | 9 ++++-- src/libslic3r/Technologies.hpp | 2 -- src/slic3r/GUI/BackgroundSlicingProcess.cpp | 22 +++++++++++-- src/slic3r/GUI/BackgroundSlicingProcess.hpp | 6 ++++ src/slic3r/GUI/GLCanvas3D.cpp | 6 ++++ src/slic3r/GUI/GLCanvas3D.hpp | 7 +++++ src/slic3r/GUI/GUI_Preview.cpp | 12 ++++++++ src/slic3r/GUI/GUI_Preview.hpp | 15 +++++++-- src/slic3r/GUI/Plater.cpp | 10 ++++++ tests/fff_print/test_data.cpp | 6 +++- tests/fff_print/test_model.cpp | 4 +++ 17 files changed, 173 insertions(+), 48 deletions(-) diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 048aea8869..4557caa7c9 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -443,7 +443,11 @@ int CLI::run(int argc, char **argv) print->process(); if (printer_technology == ptFFF) { // The outfile is processed by a PlaceholderParser. +#if ENABLE_GCODE_VIEWER + outfile = fff_print.export_gcode(outfile, nullptr, nullptr); +#else outfile = fff_print.export_gcode(outfile, nullptr); +#endif // ENABLE_GCODE_VIEWER outfile_final = fff_print.print_statistics().finalize_output_path(outfile); } else { outfile = sla_print.output_filepath(outfile); diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 6717e961d1..701ca43778 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -698,11 +698,13 @@ std::vector>> GCode::collec return layers_to_print; } -#if ENABLE_THUMBNAIL_GENERATOR +#if ENABLE_GCODE_VIEWER +void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_data, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb) +#elif ENABLE_THUMBNAIL_GENERATOR void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb) #else void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_data) -#endif // ENABLE_THUMBNAIL_GENERATOR +#endif // ENABLE_GCODE_VIEWER { PROFILE_CLEAR(); @@ -761,13 +763,11 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ throw std::runtime_error(msg); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER - m_processor.apply_config(print->config()); - m_processor.reset(); m_processor.process_file(path_tmp); + if (result != nullptr) + *result = std::move(m_processor.extract_result()); #endif // ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GCodeTimeEstimator::PostProcessData normal_data = m_normal_time_estimator.get_post_process_data(); GCodeTimeEstimator::PostProcessData silent_data = m_silent_time_estimator.get_post_process_data(); @@ -905,6 +905,25 @@ namespace DoExport { analyzer.set_gcode_flavor(config.gcode_flavor); } +#if ENABLE_GCODE_VIEWER + static void init_gcode_processor(const PrintConfig& config, GCodeProcessor& processor) + { + processor.reset(); + processor.apply_config(config); + + // send extruder offset data to processor + unsigned int num_extruders = static_cast(config.nozzle_diameter.values.size()); + GCodeProcessor::ExtruderOffsetsMap extruder_offsets; + for (unsigned int id = 0; id < num_extruders; ++id) + { + Vec2d offset = config.extruder_offset.get_at(id); + if (!offset.isApprox(Vec2d::Zero())) + extruder_offsets[id] = offset; + } + processor.set_extruder_offsets(extruder_offsets); + } +#endif // ENABLE_GCODE_VIEWER + static double autospeed_volumetric_limit(const Print &print) { // get the minimum cross-section used in the print @@ -1142,6 +1161,9 @@ void GCode::_do_export(Print& print, FILE* file) // modifies the following: m_normal_time_estimator, m_silent_time_estimator, m_silent_time_estimator_enabled); DoExport::init_gcode_analyzer(print.config(), m_analyzer); +#if ENABLE_GCODE_VIEWER + DoExport::init_gcode_processor(print.config(), m_processor); +#endif // ENABLE_GCODE_VIEWER // resets analyzer's tracking data m_last_mm3_per_mm = GCodeAnalyzer::Default_mm3_per_mm; diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index e3da956c20..4b66d15218 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -14,11 +14,9 @@ #include "GCode/SpiralVase.hpp" #include "GCode/ToolOrdering.hpp" #include "GCode/WipeTower.hpp" -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER #include "GCode/GCodeProcessor.hpp" #endif // ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "GCodeTimeEstimator.hpp" #include "EdgeGrid.hpp" #include "GCode/Analyzer.hpp" @@ -171,11 +169,13 @@ public: // throws std::runtime_exception on error, // throws CanceledException through print->throw_if_canceled(). -#if ENABLE_THUMBNAIL_GENERATOR +#if ENABLE_GCODE_VIEWER + void do_export(Print* print, const char* path, GCodePreviewData* preview_data = nullptr, GCodeProcessor::Result* result = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); +#elif ENABLE_THUMBNAIL_GENERATOR void do_export(Print* print, const char* path, GCodePreviewData* preview_data = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); #else void do_export(Print *print, const char *path, GCodePreviewData *preview_data = nullptr); -#endif // ENABLE_THUMBNAIL_GENERATOR +#endif // ENABLE_GCODE_VIEWER // Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests. const Vec2d& origin() const { return m_origin; } @@ -388,12 +388,10 @@ private: // Analyzer GCodeAnalyzer m_analyzer; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER // Processor GCodeProcessor m_processor; #endif // ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // Write a string into a file. void _write(FILE* file, const std::string& what) { this->_write(file, what.c_str()); } diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 08066ee57e..493321a6e7 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -8,30 +8,26 @@ static const float MMMIN_TO_MMSEC = 1.0f / 60.0f; namespace Slic3r { -void GCodeProcessor::apply_config(const PrintConfig& config) -{ - m_parser.apply_config(config); -} - void GCodeProcessor::reset() { m_units = EUnits::Millimeters; m_global_positioning_type = EPositioningType::Absolute; m_e_local_positioning_type = EPositioningType::Absolute; - ::memset(m_start_position.data(), 0, sizeof(AxisCoords)); - ::memset(m_end_position.data(), 0, sizeof(AxisCoords)); - ::memset(m_origin.data(), 0, sizeof(AxisCoords)); + std::fill(m_start_position.begin(), m_start_position.end(), 0.0f); + std::fill(m_end_position.begin(), m_end_position.end(), 0.0f); + std::fill(m_origin.begin(), m_origin.end(), 0.0f); m_feedrate = 0.0f; + m_extruder_id = 0; - m_moves.clear(); + m_result.reset(); } void GCodeProcessor::process_file(const std::string& filename) { - MoveStep start_step {}; - m_moves.emplace_back(start_step); + MoveVertex start_vertex {}; + m_result.moves.emplace_back(start_vertex); m_parser.parse_file(filename, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { process_gcode_line(line); }); int a = 0; } @@ -149,9 +145,17 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) else if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f) || (delta_pos[Z] != 0.0f)) move_type = EMoveType::Travel; + // correct position by extruder offset + Vec3d extruder_offset = Vec3d::Zero(); + auto it = m_extruder_offsets.find(m_extruder_id); + if (it != m_extruder_offsets.end()) + extruder_offset = Vec3d(it->second(0), it->second(1), 0.0); - MoveStep move_step { m_end_position, m_feedrate, move_type }; - m_moves.emplace_back(move_step); + MoveVertex vertex; + vertex.position = Vec3d(m_end_position[0], m_end_position[1], m_end_position[2]) + extruder_offset; + vertex.feedrate = m_feedrate; + vertex.type = move_type; + m_result.moves.emplace_back(vertex); /* std::cout << "start: "; diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 0a3dae0327..7fa1733b78 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -33,15 +33,24 @@ namespace Slic3r { Num_Types }; - struct MoveStep + struct MoveVertex { - AxisCoords position; // mm - float feedrate; // mm/s - EMoveType type; + Vec3d position{ Vec3d::Zero() }; // mm + float feedrate{ 0.0f }; // mm/s + // type of the move terminating at this vertex + EMoveType type{ EMoveType::Noop }; }; - using MoveStepsList = std::vector; + public: + typedef std::map ExtruderOffsetsMap; + struct Result + { + std::vector moves; + void reset() { moves = std::vector(); } + }; + + private: GCodeReader m_parser; EUnits m_units; @@ -53,17 +62,22 @@ namespace Slic3r { AxisCoords m_origin; // mm float m_feedrate; // mm/s + unsigned int m_extruder_id; + ExtruderOffsetsMap m_extruder_offsets; - MoveStepsList m_moves; - + Result m_result; public: GCodeProcessor() { reset(); } - void apply_config(const PrintConfig& config); - + void apply_config(const PrintConfig& config) { m_parser.apply_config(config); } void reset(); + void set_extruder_offsets(const ExtruderOffsetsMap& extruder_offsets) { m_extruder_offsets = extruder_offsets; } + + const Result& get_result() const { return m_result; } + Result&& extract_result() { return std::move(m_result); } + // Process the gcode contained in the file with the given filename // Return false if any error occourred void process_file(const std::string& filename); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index d5a1fa178d..d342c9056d 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1619,11 +1619,13 @@ void Print::process() // The export_gcode may die for various reasons (fails to process output_filename_format, // write error into the G-code, cannot execute post-processing scripts). // It is up to the caller to show an error message. -#if ENABLE_THUMBNAIL_GENERATOR +#if ENABLE_GCODE_VIEWER +std::string Print::export_gcode(const std::string& path_template, GCodePreviewData* preview_data, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb) +#elif ENABLE_THUMBNAIL_GENERATOR std::string Print::export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb) #else std::string Print::export_gcode(const std::string &path_template, GCodePreviewData *preview_data) -#endif // ENABLE_THUMBNAIL_GENERATOR +#endif // ENABLE_GCODE_VIEWER { // output everything to a G-code file // The following call may die if the output_filename_format template substitution fails. @@ -1640,11 +1642,13 @@ std::string Print::export_gcode(const std::string &path_template, GCodePreviewDa // The following line may die for multiple reasons. GCode gcode; -#if ENABLE_THUMBNAIL_GENERATOR +#if ENABLE_GCODE_VIEWER + gcode.do_export(this, path.c_str(), preview_data, result, thumbnail_cb); +#elif ENABLE_THUMBNAIL_GENERATOR gcode.do_export(this, path.c_str(), preview_data, thumbnail_cb); #else gcode.do_export(this, path.c_str(), preview_data); -#endif // ENABLE_THUMBNAIL_GENERATOR +#endif // ENABLE_GCODE_VIEWER return path.c_str(); } diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 7b326472e4..ef55482224 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -14,6 +14,9 @@ #if ENABLE_THUMBNAIL_GENERATOR #include "GCode/ThumbnailData.hpp" #endif // ENABLE_THUMBNAIL_GENERATOR +#if ENABLE_GCODE_VIEWER +#include "GCode/GCodeProcessor.hpp" +#endif // ENABLE_GCODE_VIEWER #include "libslic3r.h" @@ -364,11 +367,13 @@ public: void process() override; // Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file. // If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r). -#if ENABLE_THUMBNAIL_GENERATOR +#if ENABLE_GCODE_VIEWER + std::string export_gcode(const std::string& path_template, GCodePreviewData* preview_data, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); +#elif ENABLE_THUMBNAIL_GENERATOR std::string export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); #else std::string export_gcode(const std::string &path_template, GCodePreviewData *preview_data); -#endif // ENABLE_THUMBNAIL_GENERATOR +#endif // ENABLE_GCODE_VIEWER // methods for handling state bool is_step_done(PrintStep step) const { return Inherited::is_step_done(step); } diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index e5a2f3faee..4cebe98b1c 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -48,7 +48,6 @@ #define ENABLE_2_2_0_BETA1 1 -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ //================== // 2.3.0.alpha1 techs //================== @@ -56,7 +55,6 @@ // Enable G-Code viewer #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // _technologies_h_ diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 548a19f776..126a77ae74 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -89,11 +89,13 @@ void BackgroundSlicingProcess::process_fff() assert(m_print == m_fff_print); m_print->process(); wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_slicing_completed_id)); -#if ENABLE_THUMBNAIL_GENERATOR - m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_thumbnail_cb); +#if ENABLE_GCODE_VIEWER + m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_gcode_result, m_thumbnail_cb); +#elif ENABLE_THUMBNAIL_GENERATOR + m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_thumbnail_cb); #else m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data); -#endif // ENABLE_THUMBNAIL_GENERATOR +#endif // ENABLE_GCODE_VIEWER if (this->set_step_started(bspsGCodeFinalize)) { if (! m_export_path.empty()) { @@ -377,6 +379,19 @@ Print::ApplyStatus BackgroundSlicingProcess::apply(const Model &model, const Dyn assert(m_print != nullptr); assert(config.opt_enum("printer_technology") == m_print->technology()); Print::ApplyStatus invalidated = m_print->apply(model, config); +#if ENABLE_GCODE_VIEWER + if ((invalidated & PrintBase::APPLY_STATUS_INVALIDATED) != 0 && m_print->technology() == ptFFF && + !this->m_fff_print->is_step_done(psGCodeExport)) + { + // Some FFF status was invalidated, and the G-code was not exported yet. + // Let the G-code preview UI know that the final G-code preview is not valid. + // In addition, this early memory deallocation reduces memory footprint. + if (m_gcode_preview_data != nullptr) + m_gcode_preview_data->reset(); + else if (m_gcode_result != nullptr) + m_gcode_result->reset(); + } +#else if ((invalidated & PrintBase::APPLY_STATUS_INVALIDATED) != 0 && m_print->technology() == ptFFF && m_gcode_preview_data != nullptr && ! this->m_fff_print->is_step_done(psGCodeExport)) { // Some FFF status was invalidated, and the G-code was not exported yet. @@ -384,6 +399,7 @@ Print::ApplyStatus BackgroundSlicingProcess::apply(const Model &model, const Dyn // In addition, this early memory deallocation reduces memory footprint. m_gcode_preview_data->reset(); } +#endif // ENABLE_GCODE_VIEWER return invalidated; } diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index c8ece38f09..5b6f09d270 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -52,6 +52,9 @@ public: #if ENABLE_THUMBNAIL_GENERATOR void set_thumbnail_cb(ThumbnailsGeneratorCallback cb) { m_thumbnail_cb = cb; } #endif // ENABLE_THUMBNAIL_GENERATOR +#if ENABLE_GCODE_VIEWER + void set_gcode_result(GCodeProcessor::Result* result) { m_gcode_result = result; } +#endif // ENABLE_GCODE_VIEWER // The following wxCommandEvent will be sent to the UI thread / Plater window, when the slicing is finished // and the background processing will transition into G-code export. @@ -159,6 +162,9 @@ private: // Callback function, used to write thumbnails into gcode. ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr; #endif // ENABLE_THUMBNAIL_GENERATOR +#if ENABLE_GCODE_VIEWER + GCodeProcessor::Result* m_gcode_result = nullptr; +#endif // ENABLE_GCODE_VIEWER // Temporary G-code, there is one defined for the BackgroundSlicingProcess, differentiated from the other processes by a process ID. std::string m_temp_output_path; // Output path provided by the user. The output path may be set even if the slicing is running, diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index b5319a2f18..b96b8b47f1 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2480,6 +2480,12 @@ static void load_gcode_retractions(const GCodePreviewData::Retraction& retractio volume->indexed_vertex_array.finalize_geometry(gl_initialized); } +#if ENABLE_GCODE_VIEWER +void GLCanvas3D::load_gcode_preview_2(const GCodeProcessor::Result& gcode_result) +{ +} +#endif // ENABLE_GCODE_VIEWER + void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const std::vector& str_tool_colors) { const Print *print = this->fff_print(); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 9ae1278800..0dc2b26dd1 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -13,6 +13,9 @@ #include "Gizmos/GLGizmosManager.hpp" #include "GUI_ObjectLayers.hpp" #include "MeshUtils.hpp" +#if ENABLE_GCODE_VIEWER +#include "libslic3r/GCode/GCodeProcessor.hpp" +#endif // ENABLE_GCODE_VIEWER #include @@ -577,6 +580,10 @@ public: void reload_scene(bool refresh_immediately, bool force_full_scene_refresh = false); +#if ENABLE_GCODE_VIEWER + void load_gcode_preview_2(const GCodeProcessor::Result& gcode_result); +#endif // ENABLE_GCODE_VIEWER + void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector& str_tool_colors); void load_sla_preview(); void load_preview(const std::vector& str_tool_colors, const std::vector& color_print_values); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 5afdb3bb43..d0493adfc6 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -163,9 +163,15 @@ void View3D::render() m_canvas->set_as_dirty(); } +#if ENABLE_GCODE_VIEWER Preview::Preview( wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, + BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, GCodeProcessor::Result* gcode_result, std::function schedule_background_process_func) +#else +Preview::Preview( + wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function schedule_background_process_func) +#endif // ENABLE_GCODE_VIEWER : m_canvas_widget(nullptr) , m_canvas(nullptr) , m_double_slider_sizer(nullptr) @@ -181,6 +187,9 @@ Preview::Preview( , m_config(config) , m_process(process) , m_gcode_preview_data(gcode_preview_data) +#if ENABLE_GCODE_VIEWER + , m_gcode_result(gcode_result) +#endif // ENABLE_GCODE_VIEWER , m_number_extruders(1) , m_preferred_color_mode("feature") , m_loaded(false) @@ -860,6 +869,9 @@ void Preview::load_print_as_fff(bool keep_z_range) if (gcode_preview_data_valid) { // Load the real G-code preview. m_canvas->load_gcode_preview(*m_gcode_preview_data, colors); +#if ENABLE_GCODE_VIEWER + m_canvas->load_gcode_preview_2(*m_gcode_result); +#endif // ENABLE_GCODE_VIEWER m_loaded = true; } else { // Load the initial preview based on slices, not the final G-code. diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index cc8f153251..f0e7abfeca 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -6,6 +6,9 @@ #include #include "libslic3r/Model.hpp" +#if ENABLE_GCODE_VIEWER +#include "libslic3r/GCode/GCodeProcessor.hpp" +#endif // ENABLE_GCODE_VIEWER class wxNotebook; class wxGLCanvas; @@ -90,6 +93,9 @@ class Preview : public wxPanel DynamicPrintConfig* m_config; BackgroundSlicingProcess* m_process; GCodePreviewData* m_gcode_preview_data; +#if ENABLE_GCODE_VIEWER + GCodeProcessor::Result* m_gcode_result; +#endif // ENABLE_GCODE_VIEWER #ifdef __linux__ // We are getting mysterious crashes on Linux in gtk due to OpenGL context activation GH #1874 #1955. @@ -109,8 +115,13 @@ class Preview : public wxPanel DoubleSlider::Control* m_slider {nullptr}; public: - Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, - BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function schedule_background_process = [](){}); +#if ENABLE_GCODE_VIEWER + Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, + BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, GCodeProcessor::Result* gcode_result, std::function schedule_background_process = []() {}); +#else + Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, + BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function schedule_background_process = []() {}); +#endif // ENABLE_GCODE_VIEWER virtual ~Preview(); wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 3171818875..3886d5f359 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1440,6 +1440,9 @@ struct Plater::priv Slic3r::Model model; PrinterTechnology printer_technology = ptFFF; Slic3r::GCodePreviewData gcode_preview_data; +#if ENABLE_GCODE_VIEWER + Slic3r::GCodeProcessor::Result gcode_result; +#endif // ENABLE_GCODE_VIEWER // GUI elements wxSizer* panel_sizer{ nullptr }; @@ -1990,6 +1993,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) background_process.set_fff_print(&fff_print); background_process.set_sla_print(&sla_print); background_process.set_gcode_preview_data(&gcode_preview_data); +#if ENABLE_GCODE_VIEWER + background_process.set_gcode_result(&gcode_result); +#endif // ENABLE_GCODE_VIEWER #if ENABLE_THUMBNAIL_GENERATOR background_process.set_thumbnail_cb([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) { @@ -2015,7 +2021,11 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) this->q->Bind(EVT_SLICING_UPDATE, &priv::on_slicing_update, this); view3D = new View3D(q, bed, camera, view_toolbar, &model, config, &background_process); +#if ENABLE_GCODE_VIEWER + preview = new Preview(q, bed, camera, view_toolbar, &model, config, &background_process, &gcode_preview_data, &gcode_result, [this]() { schedule_background_process(); }); +#else preview = new Preview(q, bed, camera, view_toolbar, &model, config, &background_process, &gcode_preview_data, [this](){ schedule_background_process(); }); +#endif // ENABLE_GCODE_VIEWER panels.push_back(view3D); panels.push_back(preview); diff --git a/tests/fff_print/test_data.cpp b/tests/fff_print/test_data.cpp index b3c16f4b01..50b3383254 100644 --- a/tests/fff_print/test_data.cpp +++ b/tests/fff_print/test_data.cpp @@ -244,8 +244,12 @@ std::string gcode(Print & print) boost::filesystem::path temp = boost::filesystem::unique_path(); print.set_status_silent(); print.process(); +#if ENABLE_GCODE_VIEWER + print.export_gcode(temp.string(), nullptr, nullptr); +#else print.export_gcode(temp.string(), nullptr); - std::ifstream t(temp.string()); +#endif // ENABLE_GCODE_VIEWER + std::ifstream t(temp.string()); std::string str((std::istreambuf_iterator(t)), std::istreambuf_iterator()); boost::nowide::remove(temp.string().c_str()); return str; diff --git a/tests/fff_print/test_model.cpp b/tests/fff_print/test_model.cpp index 3378a83638..3d3b54ef02 100644 --- a/tests/fff_print/test_model.cpp +++ b/tests/fff_print/test_model.cpp @@ -50,7 +50,11 @@ SCENARIO("Model construction", "[Model]") { print.apply(model, config); print.process(); boost::filesystem::path temp = boost::filesystem::unique_path(); +#if ENABLE_GCODE_VIEWER + print.export_gcode(temp.string(), nullptr, nullptr); +#else print.export_gcode(temp.string(), nullptr); +#endif // ENABLE_GCODE_VIEWER REQUIRE(boost::filesystem::exists(temp)); REQUIRE(boost::filesystem::is_regular_file(temp)); REQUIRE(boost::filesystem::file_size(temp) > 0); From 35e963a566e9199d0010891312bbb3667d45ebe9 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 30 Mar 2020 09:01:50 +0200 Subject: [PATCH 003/503] Small refactoring --- src/libslic3r/GCode.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 6a3a0bee24..28fa2ca28f 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -906,13 +906,13 @@ namespace DoExport { processor.apply_config(config); // send extruder offset data to processor - unsigned int num_extruders = static_cast(config.nozzle_diameter.values.size()); GCodeProcessor::ExtruderOffsetsMap extruder_offsets; - for (unsigned int id = 0; id < num_extruders; ++id) + const size_t num_extruders = config.nozzle_diameter.values.size(); + for (size_t id = 0; id < num_extruders; ++id) { - Vec2d offset = config.extruder_offset.get_at(id); + const Vec2d& offset = config.extruder_offset.get_at(id); if (!offset.isApprox(Vec2d::Zero())) - extruder_offsets[id] = offset; + extruder_offsets[static_cast(id)] = offset; } processor.set_extruder_offsets(extruder_offsets); } From 956f7a4593887237f68150eb9da855d50a030e5f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 2 Apr 2020 12:03:18 +0200 Subject: [PATCH 004/503] GCodeProcessor additions: process G90 lines process G91 lines process G92 lines process M82 lines process M83 lines process T lines process extrusion role/width/height comment tags debug output --- src/libslic3r/GCode.cpp | 43 +++-- src/libslic3r/GCode/Analyzer.cpp | 29 ++- src/libslic3r/GCode/Analyzer.hpp | 12 ++ src/libslic3r/GCode/GCodeProcessor.cpp | 251 +++++++++++++++++++------ src/libslic3r/GCode/GCodeProcessor.hpp | 66 +++++-- src/libslic3r/GCode/WipeTower.cpp | 15 ++ src/libslic3r/Technologies.hpp | 7 +- src/slic3r/GUI/GLCanvas3D.cpp | 10 + 8 files changed, 349 insertions(+), 84 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 28fa2ca28f..3ea365b3cc 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -780,6 +780,10 @@ void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_ // starts analyzer calculations if (m_enable_analyzer) { +#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT + m_analyzer.close_debug_output_file(); +#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT + BOOST_LOG_TRIVIAL(debug) << "Preparing G-code preview data" << log_memory_info(); m_analyzer.calc_gcode_preview_data(*preview_data, [print]() { print->throw_if_canceled(); }); m_analyzer.reset(); @@ -897,24 +901,17 @@ namespace DoExport { // tell analyzer about the gcode flavor analyzer.set_gcode_flavor(config.gcode_flavor); - } + +#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT + analyzer.open_debug_output_file(); +#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT + } #if ENABLE_GCODE_VIEWER static void init_gcode_processor(const PrintConfig& config, GCodeProcessor& processor) { processor.reset(); processor.apply_config(config); - - // send extruder offset data to processor - GCodeProcessor::ExtruderOffsetsMap extruder_offsets; - const size_t num_extruders = config.nozzle_diameter.values.size(); - for (size_t id = 0; id < num_extruders; ++id) - { - const Vec2d& offset = config.extruder_offset.get_at(id); - if (!offset.isApprox(Vec2d::Zero())) - extruder_offsets[static_cast(id)] = offset; - } - processor.set_extruder_offsets(extruder_offsets); } #endif // ENABLE_GCODE_VIEWER @@ -1340,6 +1337,11 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu // adds tag for analyzer _write_format(file, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erCustom); +#if ENABLE_GCODE_VIEWER + // adds tag for processor + _write_format(file, ";%s%d\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), erCustom); +#endif // ENABLE_GCODE_VIEWER + // Write the custom start G-code _writeln(file, start_gcode); @@ -1493,6 +1495,11 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu // adds tag for analyzer _write_format(file, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erCustom); +#if ENABLE_GCODE_VIEWER + // adds tag for processor + _write_format(file, ";%s%d\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), erCustom); +#endif // ENABLE_GCODE_VIEWER + // Process filament-specific gcode in extruder order. { DynamicConfig config; @@ -3112,6 +3119,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, { m_last_analyzer_extrusion_role = path.role(); sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), int(m_last_analyzer_extrusion_role)); +#if ENABLE_GCODE_VIEWER + gcode += buf; + sprintf(buf, ";%s%d\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), int(m_last_analyzer_extrusion_role)); +#endif // ENABLE_GCODE_VIEWER gcode += buf; } @@ -3127,6 +3138,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, m_last_width = path.width; sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), m_last_width); gcode += buf; +#if ENABLE_GCODE_VIEWER + sprintf(buf, ";%s%f\n", GCodeProcessor::Width_Tag.c_str(), m_last_width); + gcode += buf; +#endif // ENABLE_GCODE_VIEWER } if (last_was_wipe_tower || (m_last_height != path.height)) @@ -3134,6 +3149,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, m_last_height = path.height; sprintf(buf, ";%s%f\n", GCodeAnalyzer::Height_Tag.c_str(), m_last_height); gcode += buf; +#if ENABLE_GCODE_VIEWER + sprintf(buf, ";%s%f\n", GCodeProcessor::Height_Tag.c_str(), m_last_height); + gcode += buf; +#endif // ENABLE_GCODE_VIEWER } } diff --git a/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp index 442d5ec839..8a2ba7804b 100644 --- a/src/libslic3r/GCode/Analyzer.cpp +++ b/src/libslic3r/GCode/Analyzer.cpp @@ -174,6 +174,19 @@ bool GCodeAnalyzer::is_valid_extrusion_role(ExtrusionRole role) return ((erPerimeter <= role) && (role < erMixed)); } +#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT +void GCodeAnalyzer::open_debug_output_file() +{ + boost::filesystem::path path("d:/analyzer.output"); + m_debug_output.open(path.string()); +} + +void GCodeAnalyzer::close_debug_output_file() +{ + m_debug_output.close(); +} +#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT + void GCodeAnalyzer::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLine& line) { // processes 'special' comments contained in line @@ -350,7 +363,7 @@ void GCodeAnalyzer::_processG1(const GCodeReader::GCodeLine& line) if (delta_pos[E] < 0.0f) { if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f) || (delta_pos[Z] != 0.0f)) - type = GCodeMove::Move; + type = GCodeMove::Move; else type = GCodeMove::Retract; } @@ -922,6 +935,20 @@ void GCodeAnalyzer::_store_move(GCodeAnalyzer::GCodeMove::EType type) Vec3d start_position = _get_start_position() + extruder_offset; Vec3d end_position = _get_end_position() + extruder_offset; it->second.emplace_back(type, _get_extrusion_role(), extruder_id, _get_mm3_per_mm(), _get_width(), _get_height(), _get_feedrate(), start_position, end_position, _get_delta_extrusion(), _get_fan_speed(), _get_cp_color_id()); + +#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT + if (m_debug_output.good()) + { + m_debug_output << std::to_string((int)type); + m_debug_output << ", " << std::to_string((int)_get_extrusion_role()); + m_debug_output << ", " << Slic3r::to_string(_get_end_position()); + m_debug_output << ", " << std::to_string(extruder_id); + m_debug_output << ", " << std::to_string(_get_feedrate()); + m_debug_output << ", " << std::to_string(_get_width()); + m_debug_output << ", " << std::to_string(_get_height()); + m_debug_output << "\n"; + } +#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT } bool GCodeAnalyzer::_is_valid_extrusion_role(int value) const diff --git a/src/libslic3r/GCode/Analyzer.hpp b/src/libslic3r/GCode/Analyzer.hpp index cd5654a745..fb5ef3b4c1 100644 --- a/src/libslic3r/GCode/Analyzer.hpp +++ b/src/libslic3r/GCode/Analyzer.hpp @@ -8,6 +8,10 @@ #include "../Point.hpp" #include "../GCodeReader.hpp" +#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT +#include +#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT + namespace Slic3r { class GCodePreviewData; @@ -147,6 +151,14 @@ public: static bool is_valid_extrusion_role(ExtrusionRole role); +#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT +private: + boost::nowide::ofstream m_debug_output; +public: + void open_debug_output_file(); + void close_debug_output_file(); +#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT + private: // Processes the given gcode line void _process_gcode_line(GCodeReader& reader, const GCodeReader::GCodeLine& line); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 493321a6e7..ffb4e17146 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -6,19 +6,47 @@ static const float INCHES_TO_MM = 25.4f; static const float MMMIN_TO_MMSEC = 1.0f / 60.0f; +static bool is_valid_extrusion_role(int value) +{ + return ((int)Slic3r::erNone <= value) && (value <= (int)Slic3r::erMixed); +} + namespace Slic3r { +const std::string GCodeProcessor::Extrusion_Role_Tag = "_PROCESSOR_EXTRUSION_ROLE:"; +const std::string GCodeProcessor::Width_Tag = "_PROCESSOR_WIDTH:"; +const std::string GCodeProcessor::Height_Tag = "_PROCESSOR_HEIGHT:"; + +void GCodeProcessor::apply_config(const PrintConfig& config) +{ + m_parser.apply_config(config); + + size_t extruders_count = config.nozzle_diameter.values.size(); + if (m_extruder_offsets.size() != extruders_count) + m_extruder_offsets.resize(extruders_count); + + for (size_t id = 0; id < extruders_count; ++id) + { + Vec2f offset = config.extruder_offset.get_at(id).cast(); + m_extruder_offsets[id] = Vec3f(offset(0), offset(1), 0.0f); + } +} + void GCodeProcessor::reset() { m_units = EUnits::Millimeters; m_global_positioning_type = EPositioningType::Absolute; m_e_local_positioning_type = EPositioningType::Absolute; - + m_extruder_offsets = std::vector(1, Vec3f::Zero()); + std::fill(m_start_position.begin(), m_start_position.end(), 0.0f); std::fill(m_end_position.begin(), m_end_position.end(), 0.0f); std::fill(m_origin.begin(), m_origin.end(), 0.0f); m_feedrate = 0.0f; + m_width = 0.0f; + m_height = 0.0f; + m_extrusion_role = erNone; m_extruder_id = 0; m_result.reset(); @@ -26,10 +54,8 @@ void GCodeProcessor::reset() void GCodeProcessor::process_file(const std::string& filename) { - MoveVertex start_vertex {}; - m_result.moves.emplace_back(start_vertex); + m_result.moves.emplace_back(MoveVertex()); m_parser.parse_file(filename, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { process_gcode_line(line); }); - int a = 0; } void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) @@ -42,8 +68,6 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) m_start_position = m_end_position; std::string cmd = line.cmd(); - std::string comment = line.comment(); - if (cmd.length() > 1) { // process command lines @@ -54,7 +78,13 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) switch (::atoi(&cmd[1])) { // Move - case 1: { process_G1(line); } + case 1: { process_G1(line); break; } + // Set to Absolute Positioning + case 90: { processG90(line); break; } + // Set to Relative Positioning + case 91: { processG91(line); break; } + // Set Position + case 92: { processG92(line); break; } default: { break; } } break; @@ -63,6 +93,10 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) { switch (::atoi(&cmd[1])) { + // Set extruder to absolute mode + case 82: { processM82(line); break; } + // Set extruder to relative mode + case 83: { processM83(line); break; } default: { break; } } break; @@ -74,20 +108,52 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) default: { break; } } } - else if (comment.length() > 1) + else { - // process tags embedded into comments - process_comment(line); + std::string comment = line.comment(); + if (comment.length() > 1) + // process tags embedded into comments + process_tags(comment); } } -void GCodeProcessor::process_comment(const GCodeReader::GCodeLine& line) +void GCodeProcessor::process_tags(const std::string& comment) { + // extrusion role tag + size_t pos = comment.find(Extrusion_Role_Tag); + if (pos != comment.npos) + { + int role = std::stoi(comment.substr(pos + Extrusion_Role_Tag.length())); + if (is_valid_extrusion_role(role)) + m_extrusion_role = static_cast(role); + else + { + // todo: show some error ? + } + + return; + } + + // width tag + pos = comment.find(Width_Tag); + if (pos != comment.npos) + { + m_width = std::stof(comment.substr(pos + Width_Tag.length())); + return; + } + + // height tag + pos = comment.find(Height_Tag); + if (pos != comment.npos) + { + m_height = std::stof(comment.substr(pos + Height_Tag.length())); + return; + } } void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) { - auto axis_absolute_position = [this](Axis axis, const GCodeReader::GCodeLine& lineG1) + auto absolute_position = [this](Axis axis, const GCodeReader::GCodeLine& lineG1) { bool is_relative = (m_global_positioning_type == EPositioningType::Relative); if (axis == E) @@ -103,10 +169,36 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) return m_start_position[axis]; }; + auto move_type = [this](const AxisCoords& delta_pos) { + EMoveType type = EMoveType::Noop; + + if (delta_pos[E] < 0.0f) + { + if (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f || delta_pos[Z] != 0.0f) + type = EMoveType::Travel; + else + type = EMoveType::Retract; + } + else if (delta_pos[E] > 0.0f) + { + if (delta_pos[X] == 0.0f && delta_pos[Y] == 0.0f && delta_pos[Z] == 0.0f) + type = EMoveType::Unretract; + else if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f)) + type = EMoveType::Extrude; + } + else if (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f || delta_pos[Z] != 0.0f) + type = EMoveType::Travel; + + if (type == EMoveType::Extrude && (m_width == 0.0f || m_height == 0.0f || !is_valid_extrusion_role(m_extrusion_role))) + type = EMoveType::Travel; + + return type; + }; + // updates axes positions from line for (unsigned char a = X; a <= E; ++a) { - m_end_position[a] = axis_absolute_position((Axis)a, line); + m_end_position[a] = absolute_position((Axis)a, line); } // updates feedrate from line, if present @@ -124,56 +216,109 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) // no displacement, return if (max_abs_delta == 0.0f) - return; // <<<<<<<<<<<<<<<<< is this correct for time estimate ? + return; - // detect move type - EMoveType move_type = EMoveType::Noop; - if (delta_pos[E] < 0.0f) + // store g1 move + store_move_vertex(move_type(delta_pos)); +} + +void GCodeProcessor::processG90(const GCodeReader::GCodeLine& line) +{ + m_global_positioning_type = EPositioningType::Absolute; +} + +void GCodeProcessor::processG91(const GCodeReader::GCodeLine& line) +{ + m_global_positioning_type = EPositioningType::Relative; +} + +void GCodeProcessor::processG92(const GCodeReader::GCodeLine& line) +{ + float lengthsScaleFactor = (m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f; + bool anyFound = false; + + if (line.has_x()) { - if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f) || (delta_pos[Z] != 0.0f)) - move_type = EMoveType::Travel; - else - move_type = EMoveType::Retract; + m_origin[X] = m_end_position[X] - line.x() * lengthsScaleFactor; + anyFound = true; } - else if (delta_pos[E] > 0.0f) + + if (line.has_y()) { - if ((delta_pos[X] == 0.0f) && (delta_pos[Y] == 0.0f) && (delta_pos[Z] == 0.0f)) - move_type = EMoveType::Unretract; - else if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f)) - move_type = EMoveType::Extrude; + m_origin[Y] = m_end_position[Y] - line.y() * lengthsScaleFactor; + anyFound = true; } - else if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f) || (delta_pos[Z] != 0.0f)) - move_type = EMoveType::Travel; - // correct position by extruder offset - Vec3d extruder_offset = Vec3d::Zero(); - auto it = m_extruder_offsets.find(m_extruder_id); - if (it != m_extruder_offsets.end()) - extruder_offset = Vec3d(it->second(0), it->second(1), 0.0); + if (line.has_z()) + { + m_origin[Z] = m_end_position[Z] - line.z() * lengthsScaleFactor; + anyFound = true; + } + if (line.has_e()) + { + // extruder coordinate can grow to the point where its float representation does not allow for proper addition with small increments, + // we set the value taken from the G92 line as the new current position for it + m_end_position[E] = line.e() * lengthsScaleFactor; + anyFound = true; + } + + if (!anyFound && !line.has_unknown_axis()) + { + // The G92 may be called for axes that PrusaSlicer does not recognize, for example see GH issue #3510, + // where G92 A0 B0 is called although the extruder axis is till E. + for (unsigned char a = X; a <= E; ++a) + { + m_origin[a] = m_end_position[a]; + } + } +} + +void GCodeProcessor::processM82(const GCodeReader::GCodeLine& line) +{ + m_e_local_positioning_type = EPositioningType::Absolute; +} + +void GCodeProcessor::processM83(const GCodeReader::GCodeLine& line) +{ + m_e_local_positioning_type = EPositioningType::Relative; +} + +void GCodeProcessor::processT(const GCodeReader::GCodeLine& line) +{ + const std::string& cmd = line.cmd(); + if (cmd.length() > 1) + { + unsigned int id = (unsigned int)std::stoi(cmd.substr(1)); + if (m_extruder_id != id) + { + unsigned int extruders_count = (unsigned int)m_extruder_offsets.size(); + if (id >= extruders_count) + BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid toolchange, maybe from a custom gcode."; + else + { + m_extruder_id = id; +// if (_get_cp_color_id() != INT_MAX) <<<<<<<<<<<<<<<<<<< TODO +// _set_cp_color_id(m_extruder_color[id]); + } + + // store tool change move + store_move_vertex(EMoveType::Tool_change); + } + } +} + +void GCodeProcessor::store_move_vertex(EMoveType type) +{ MoveVertex vertex; - vertex.position = Vec3d(m_end_position[0], m_end_position[1], m_end_position[2]) + extruder_offset; + vertex.type = type; + vertex.extrusion_role = m_extrusion_role; + vertex.position = Vec3f(m_end_position[0], m_end_position[1], m_end_position[2]) + m_extruder_offsets[m_extruder_id]; vertex.feedrate = m_feedrate; - vertex.type = move_type; + vertex.width = m_width; + vertex.height = m_height; + vertex.extruder_id = m_extruder_id; m_result.moves.emplace_back(vertex); - -/* - std::cout << "start: "; - for (unsigned char a = X; a <= E; ++a) - { - std::cout << m_start_position[a]; - if (a != E) - std::cout << ", "; - } - std::cout << " - end: "; - for (unsigned char a = X; a <= E; ++a) - { - std::cout << m_end_position[a]; - if (a != E) - std::cout << ", "; - } - std::cout << "\n"; -*/ } } /* namespace Slic3r */ diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 7fa1733b78..537617485f 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -3,11 +3,19 @@ #if ENABLE_GCODE_VIEWER #include "../GCodeReader.hpp" +#include "../Point.hpp" +#include "../ExtrusionEntity.hpp" namespace Slic3r { class GCodeProcessor { + public: + static const std::string Extrusion_Role_Tag; + static const std::string Width_Tag; + static const std::string Height_Tag; + + private: using AxisCoords = std::array; enum class EUnits : unsigned char @@ -33,16 +41,29 @@ namespace Slic3r { Num_Types }; + public: struct MoveVertex { - Vec3d position{ Vec3d::Zero() }; // mm - float feedrate{ 0.0f }; // mm/s - // type of the move terminating at this vertex EMoveType type{ EMoveType::Noop }; - }; + ExtrusionRole extrusion_role{ erNone }; + Vec3f position{ Vec3f::Zero() }; // mm + float feedrate{ 0.0f }; // mm/s + float width{ 0.0f }; // mm + float height{ 0.0f }; // mm + unsigned int extruder_id{ 0 }; - public: - typedef std::map ExtruderOffsetsMap; + std::string to_string() const + { + std::string str = std::to_string((int)type); + str += ", " + std::to_string((int)extrusion_role); + str += ", " + Slic3r::to_string((Vec3d)position.cast()); + str += ", " + std::to_string(extruder_id); + str += ", " + std::to_string(feedrate); + str += ", " + std::to_string(width); + str += ", " + std::to_string(height); + return str; + } + }; struct Result { @@ -56,25 +77,26 @@ namespace Slic3r { EUnits m_units; EPositioningType m_global_positioning_type; EPositioningType m_e_local_positioning_type; + std::vector m_extruder_offsets; AxisCoords m_start_position; // mm AxisCoords m_end_position; // mm AxisCoords m_origin; // mm float m_feedrate; // mm/s + float m_width; // mm + float m_height; // mm + ExtrusionRole m_extrusion_role; unsigned int m_extruder_id; - ExtruderOffsetsMap m_extruder_offsets; Result m_result; public: GCodeProcessor() { reset(); } - void apply_config(const PrintConfig& config) { m_parser.apply_config(config); } + void apply_config(const PrintConfig& config); void reset(); - void set_extruder_offsets(const ExtruderOffsetsMap& extruder_offsets) { m_extruder_offsets = extruder_offsets; } - const Result& get_result() const { return m_result; } Result&& extract_result() { return std::move(m_result); } @@ -86,11 +108,31 @@ namespace Slic3r { void process_gcode_line(const GCodeReader::GCodeLine& line); // Process tags embedded into comments - void process_comment(const GCodeReader::GCodeLine& line); + void process_tags(const std::string& comment); // Move void process_G1(const GCodeReader::GCodeLine& line); - }; + + // Set to Absolute Positioning + void processG90(const GCodeReader::GCodeLine& line); + + // Set to Relative Positioning + void processG91(const GCodeReader::GCodeLine& line); + + // Set Position + void processG92(const GCodeReader::GCodeLine& line); + + // Set extruder to absolute mode + void processM82(const GCodeReader::GCodeLine& line); + + // Set extruder to relative mode + void processM83(const GCodeReader::GCodeLine& line); + + // Processes T line (Select Tool) + void processT(const GCodeReader::GCodeLine& line); + + void store_move_vertex(EMoveType type); + }; } /* namespace Slic3r */ diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index d31adbd8fc..339012d0b4 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -22,6 +22,9 @@ TODO LIST #include #include "Analyzer.hpp" +#if ENABLE_GCODE_VIEWER +#include "GCodeProcessor.hpp" +#endif // ENABLE_GCODE_VIEWER #include "BoundingBox.hpp" #if defined(__linux) || defined(__GNUC__ ) @@ -55,7 +58,15 @@ public: char buf[64]; sprintf(buf, ";%s%f\n", GCodeAnalyzer::Height_Tag.c_str(), m_layer_height); // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming m_gcode += buf; +#if ENABLE_GCODE_VIEWER + sprintf(buf, ";%s%f\n", GCodeProcessor::Height_Tag.c_str(), m_layer_height); // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming + m_gcode += buf; +#endif // ENABLE_GCODE_VIEWER sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erWipeTower); +#if ENABLE_GCODE_VIEWER + m_gcode += buf; + sprintf(buf, ";%s%d\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), erWipeTower); +#endif // ENABLE_GCODE_VIEWER m_gcode += buf; change_analyzer_line_width(line_width); } @@ -65,6 +76,10 @@ public: char buf[64]; sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), line_width); m_gcode += buf; +#if ENABLE_GCODE_VIEWER + sprintf(buf, ";%s%f\n", GCodeProcessor::Width_Tag.c_str(), line_width); + m_gcode += buf; +#endif // ENABLE_GCODE_VIEWER return *this; } diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 44c10fa548..85ce8a2cb2 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -60,14 +60,9 @@ // Moves GLCanvas3DManager from being a static member of _3DScene to be a normal member of GUI_App #define ENABLE_NON_STATIC_CANVAS_MANAGER (1 && ENABLE_2_3_0_ALPHA1) - -//================== -// 2.3.0.alpha1 techs -//================== -#define ENABLE_2_3_0_ALPHA1 1 - // Enable G-Code viewer #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) +#define ENABLE_GCODE_VIEWER_DEBUG_OUTPUT (1 && ENABLE_GCODE_VIEWER) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index d8b102cee5..de85929ea1 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2772,6 +2772,16 @@ static void load_gcode_retractions(const GCodePreviewData::Retraction& retractio #if ENABLE_GCODE_VIEWER void GLCanvas3D::load_gcode_preview_2(const GCodeProcessor::Result& gcode_result) { +#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT + boost::filesystem::path path("d:/processor.output"); + boost::nowide::ofstream out; + out.open(path.string()); + for (const GCodeProcessor::MoveVertex& v : gcode_result.moves) + { + out << v.to_string() << "\n"; + } + out.close(); +#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT } #endif // ENABLE_GCODE_VIEWER From ece4f10bf75475057b71a94087cc2db531ca87ce Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 2 Apr 2020 12:30:54 +0200 Subject: [PATCH 005/503] Updated Print.xsp --- xs/xsp/Print.xsp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 0952513ca3..160fd3e60c 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -164,7 +164,7 @@ _constant() void export_gcode(char *path_template) %code%{ try { - THIS->export_gcode(path_template, nullptr); + THIS->export_gcode(path_template, nullptr, nullptr); } catch (std::exception& e) { croak("%s\n", e.what()); } From d0ce17656f34219a764dc5c926394096f7946231 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 2 Apr 2020 13:19:42 +0200 Subject: [PATCH 006/503] Added missing includes --- src/libslic3r/GCode/Analyzer.cpp | 3 +++ src/libslic3r/GCode/GCodeProcessor.hpp | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp index a2a809ca3f..885c5e9e87 100644 --- a/src/libslic3r/GCode/Analyzer.cpp +++ b/src/libslic3r/GCode/Analyzer.cpp @@ -8,6 +8,9 @@ #include "Print.hpp" #include +#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT +#include +#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT #include "Analyzer.hpp" #include "PreviewData.hpp" diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 537617485f..0fa8187fd3 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -6,6 +6,8 @@ #include "../Point.hpp" #include "../ExtrusionEntity.hpp" +#include + namespace Slic3r { class GCodeProcessor From f05de150c58608ff6f59c06cab0e022b08b66795 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 2 Apr 2020 15:52:42 +0200 Subject: [PATCH 007/503] Added another missing include --- src/libslic3r/GCode/Analyzer.cpp | 29 ++++++++++++++++---------- src/libslic3r/GCode/Analyzer.hpp | 7 ------- src/libslic3r/GCode/GCodeProcessor.cpp | 2 ++ 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp index 885c5e9e87..b283d70a9b 100644 --- a/src/libslic3r/GCode/Analyzer.cpp +++ b/src/libslic3r/GCode/Analyzer.cpp @@ -15,6 +15,13 @@ #include "Analyzer.hpp" #include "PreviewData.hpp" +#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT +#include + +// don't worry, this is just temporary +static boost::nowide::ofstream g_debug_output; +#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT + static const std::string AXIS_STR = "XYZE"; static const float MMMIN_TO_MMSEC = 1.0f / 60.0f; static const float INCHES_TO_MM = 25.4f; @@ -181,12 +188,12 @@ bool GCodeAnalyzer::is_valid_extrusion_role(ExtrusionRole role) void GCodeAnalyzer::open_debug_output_file() { boost::filesystem::path path("d:/analyzer.output"); - m_debug_output.open(path.string()); + g_debug_output.open(path.string()); } void GCodeAnalyzer::close_debug_output_file() { - m_debug_output.close(); + g_debug_output.close(); } #endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT @@ -940,16 +947,16 @@ void GCodeAnalyzer::_store_move(GCodeAnalyzer::GCodeMove::EType type) it->second.emplace_back(type, _get_extrusion_role(), extruder_id, _get_mm3_per_mm(), _get_width(), _get_height(), _get_feedrate(), start_position, end_position, _get_delta_extrusion(), _get_fan_speed(), _get_cp_color_id()); #if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - if (m_debug_output.good()) + if (g_debug_output.good()) { - m_debug_output << std::to_string((int)type); - m_debug_output << ", " << std::to_string((int)_get_extrusion_role()); - m_debug_output << ", " << Slic3r::to_string((Vec3d)_get_end_position().cast()); - m_debug_output << ", " << std::to_string(extruder_id); - m_debug_output << ", " << std::to_string(_get_feedrate()); - m_debug_output << ", " << std::to_string(_get_width()); - m_debug_output << ", " << std::to_string(_get_height()); - m_debug_output << "\n"; + g_debug_output << std::to_string((int)type); + g_debug_output << ", " << std::to_string((int)_get_extrusion_role()); + g_debug_output << ", " << Slic3r::to_string((Vec3d)_get_end_position().cast()); + g_debug_output << ", " << std::to_string(extruder_id); + g_debug_output << ", " << std::to_string(_get_feedrate()); + g_debug_output << ", " << std::to_string(_get_width()); + g_debug_output << ", " << std::to_string(_get_height()); + g_debug_output << "\n"; } #endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT } diff --git a/src/libslic3r/GCode/Analyzer.hpp b/src/libslic3r/GCode/Analyzer.hpp index 5e5a017357..9d16ab4944 100644 --- a/src/libslic3r/GCode/Analyzer.hpp +++ b/src/libslic3r/GCode/Analyzer.hpp @@ -8,10 +8,6 @@ #include "../Point.hpp" #include "../GCodeReader.hpp" -#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT -#include -#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - namespace Slic3r { class GCodePreviewData; @@ -152,9 +148,6 @@ public: static bool is_valid_extrusion_role(ExtrusionRole role); #if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT -private: - boost::nowide::ofstream m_debug_output; -public: void open_debug_output_file(); void close_debug_output_file(); #endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index ffb4e17146..01ac11c172 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1,6 +1,8 @@ #include "../libslic3r.h" #include "GCodeProcessor.hpp" +#include + #if ENABLE_GCODE_VIEWER static const float INCHES_TO_MM = 25.4f; From 824e4360584550f74b9a953879afa042f67b34fe Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 2 Apr 2020 16:07:54 +0200 Subject: [PATCH 008/503] Hopefully last missing include --- src/slic3r/GUI/GLCanvas3D.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 2236e8fab8..85ea44ef14 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -59,6 +59,10 @@ #include #include +#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT +#include +#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT + #include #include #include From dce1f24ad81c481ba0c4e8c2bf3a39820e08aaf3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 3 Apr 2020 10:15:46 +0200 Subject: [PATCH 009/503] GCodeProcessor additions: process G10 lines process G11 lines process G22 lines process G23 lines process M106 lines process M107 lines process mm3_per_mm comment tag --- src/libslic3r/GCode.cpp | 4 + src/libslic3r/GCode/Analyzer.cpp | 2 + src/libslic3r/GCode/GCodeProcessor.cpp | 162 +++++++++++++++++++------ src/libslic3r/GCode/GCodeProcessor.hpp | 44 +++++-- src/libslic3r/GCode/WipeTower.cpp | 4 + 5 files changed, 171 insertions(+), 45 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 3ea365b3cc..720b9a1fa7 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3131,6 +3131,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, m_last_mm3_per_mm = path.mm3_per_mm; sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), m_last_mm3_per_mm); gcode += buf; +#if ENABLE_GCODE_VIEWER + sprintf(buf, ";%s%f\n", GCodeProcessor::Mm3_Per_Mm_Tag.c_str(), m_last_mm3_per_mm); + gcode += buf; +#endif // ENABLE_GCODE_VIEWER } if (last_was_wipe_tower || (m_last_width != path.width)) diff --git a/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp index b283d70a9b..c7b67647f1 100644 --- a/src/libslic3r/GCode/Analyzer.cpp +++ b/src/libslic3r/GCode/Analyzer.cpp @@ -956,6 +956,8 @@ void GCodeAnalyzer::_store_move(GCodeAnalyzer::GCodeMove::EType type) g_debug_output << ", " << std::to_string(_get_feedrate()); g_debug_output << ", " << std::to_string(_get_width()); g_debug_output << ", " << std::to_string(_get_height()); + g_debug_output << ", " << std::to_string(_get_mm3_per_mm()); + g_debug_output << ", " << std::to_string(_get_fan_speed()); g_debug_output << "\n"; } #endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 01ac11c172..0f42c6796e 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -18,6 +18,7 @@ namespace Slic3r { const std::string GCodeProcessor::Extrusion_Role_Tag = "_PROCESSOR_EXTRUSION_ROLE:"; const std::string GCodeProcessor::Width_Tag = "_PROCESSOR_WIDTH:"; const std::string GCodeProcessor::Height_Tag = "_PROCESSOR_HEIGHT:"; +const std::string GCodeProcessor::Mm3_Per_Mm_Tag = "_PROCESSOR_MM3_PER_MM:"; void GCodeProcessor::apply_config(const PrintConfig& config) { @@ -48,6 +49,9 @@ void GCodeProcessor::reset() m_feedrate = 0.0f; m_width = 0.0f; m_height = 0.0f; + m_mm3_per_mm = 0.0f; + m_fan_speed = 0.0f; + m_extrusion_role = erNone; m_extruder_id = 0; @@ -79,14 +83,14 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) { switch (::atoi(&cmd[1])) { - // Move - case 1: { process_G1(line); break; } - // Set to Absolute Positioning - case 90: { processG90(line); break; } - // Set to Relative Positioning - case 91: { processG91(line); break; } - // Set Position - case 92: { processG92(line); break; } + case 1: { process_G1(line); break; } // Move + case 10: { process_G10(line); break; } // Retract + case 11: { process_G11(line); break; } // Unretract + case 22: { process_G22(line); break; } // Firmware controlled retract + case 23: { process_G23(line); break; } // Firmware controlled unretract + case 90: { process_G90(line); break; } // Set to Absolute Positioning + case 91: { process_G91(line); break; } // Set to Relative Positioning + case 92: { process_G92(line); break; } // Set Position default: { break; } } break; @@ -95,16 +99,17 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) { switch (::atoi(&cmd[1])) { - // Set extruder to absolute mode - case 82: { processM82(line); break; } - // Set extruder to relative mode - case 83: { processM83(line); break; } + case 82: { process_M82(line); break; } // Set extruder to absolute mode + case 83: { process_M83(line); break; } // Set extruder to relative mode + case 106: { process_M106(line); break; } // Set fan speed + case 107: { process_M107(line); break; } // Disable fan default: { break; } } break; } case 'T': { + process_T(line); // Select Tool break; } default: { break; } @@ -125,12 +130,19 @@ void GCodeProcessor::process_tags(const std::string& comment) size_t pos = comment.find(Extrusion_Role_Tag); if (pos != comment.npos) { - int role = std::stoi(comment.substr(pos + Extrusion_Role_Tag.length())); - if (is_valid_extrusion_role(role)) - m_extrusion_role = static_cast(role); - else + try { - // todo: show some error ? + int role = std::stoi(comment.substr(pos + Extrusion_Role_Tag.length())); + if (is_valid_extrusion_role(role)) + m_extrusion_role = static_cast(role); + else + { + // todo: show some error ? + } + } + catch (...) + { + BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Extrusion Role (" << comment << ")."; } return; @@ -140,7 +152,14 @@ void GCodeProcessor::process_tags(const std::string& comment) pos = comment.find(Width_Tag); if (pos != comment.npos) { - m_width = std::stof(comment.substr(pos + Width_Tag.length())); + try + { + m_width = std::stof(comment.substr(pos + Width_Tag.length())); + } + catch (...) + { + BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Width (" << comment << ")."; + } return; } @@ -148,7 +167,29 @@ void GCodeProcessor::process_tags(const std::string& comment) pos = comment.find(Height_Tag); if (pos != comment.npos) { - m_height = std::stof(comment.substr(pos + Height_Tag.length())); + try + { + m_height = std::stof(comment.substr(pos + Height_Tag.length())); + } + catch (...) + { + BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Height (" << comment << ")."; + } + return; + } + + // mm3 per mm tag + pos = comment.find(Mm3_Per_Mm_Tag); + if (pos != comment.npos) + { + try + { + m_mm3_per_mm = std::stof(comment.substr(pos + Mm3_Per_Mm_Tag.length())); + } + catch (...) + { + BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Mm3_Per_Mm (" << comment << ")."; + } return; } } @@ -224,17 +265,41 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) store_move_vertex(move_type(delta_pos)); } -void GCodeProcessor::processG90(const GCodeReader::GCodeLine& line) +void GCodeProcessor::process_G10(const GCodeReader::GCodeLine& line) +{ + // stores retract move + store_move_vertex(EMoveType::Retract); +} + +void GCodeProcessor::process_G11(const GCodeReader::GCodeLine& line) +{ + // stores unretract move + store_move_vertex(EMoveType::Unretract); +} + +void GCodeProcessor::process_G22(const GCodeReader::GCodeLine& line) +{ + // stores retract move + store_move_vertex(EMoveType::Retract); +} + +void GCodeProcessor::process_G23(const GCodeReader::GCodeLine& line) +{ + // stores unretract move + store_move_vertex(EMoveType::Unretract); +} + +void GCodeProcessor::process_G90(const GCodeReader::GCodeLine& line) { m_global_positioning_type = EPositioningType::Absolute; } -void GCodeProcessor::processG91(const GCodeReader::GCodeLine& line) +void GCodeProcessor::process_G91(const GCodeReader::GCodeLine& line) { m_global_positioning_type = EPositioningType::Relative; } -void GCodeProcessor::processG92(const GCodeReader::GCodeLine& line) +void GCodeProcessor::process_G92(const GCodeReader::GCodeLine& line) { float lengthsScaleFactor = (m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f; bool anyFound = false; @@ -276,36 +341,61 @@ void GCodeProcessor::processG92(const GCodeReader::GCodeLine& line) } } -void GCodeProcessor::processM82(const GCodeReader::GCodeLine& line) +void GCodeProcessor::process_M82(const GCodeReader::GCodeLine& line) { m_e_local_positioning_type = EPositioningType::Absolute; } -void GCodeProcessor::processM83(const GCodeReader::GCodeLine& line) +void GCodeProcessor::process_M83(const GCodeReader::GCodeLine& line) { m_e_local_positioning_type = EPositioningType::Relative; } -void GCodeProcessor::processT(const GCodeReader::GCodeLine& line) +void GCodeProcessor::process_M106(const GCodeReader::GCodeLine& line) +{ + if (!line.has('P')) + { + // The absence of P means the print cooling fan, so ignore anything else. + float new_fan_speed; + if (line.has_value('S', new_fan_speed)) + m_fan_speed = (100.0f / 255.0f) * new_fan_speed; + else + m_fan_speed = 100.0f; + } +} + +void GCodeProcessor::process_M107(const GCodeReader::GCodeLine& line) +{ + m_fan_speed = 0.0f; +} + +void GCodeProcessor::process_T(const GCodeReader::GCodeLine& line) { const std::string& cmd = line.cmd(); if (cmd.length() > 1) { - unsigned int id = (unsigned int)std::stoi(cmd.substr(1)); - if (m_extruder_id != id) + try { - unsigned int extruders_count = (unsigned int)m_extruder_offsets.size(); - if (id >= extruders_count) - BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid toolchange, maybe from a custom gcode."; - else + unsigned int id = (unsigned int)std::stoi(cmd.substr(1)); + if (m_extruder_id != id) { - m_extruder_id = id; + unsigned int extruders_count = (unsigned int)m_extruder_offsets.size(); + if (id >= extruders_count) + BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid toolchange, maybe from a custom gcode."; + else + { + m_extruder_id = id; // if (_get_cp_color_id() != INT_MAX) <<<<<<<<<<<<<<<<<<< TODO // _set_cp_color_id(m_extruder_color[id]); - } + } - // store tool change move - store_move_vertex(EMoveType::Tool_change); + // store tool change move + store_move_vertex(EMoveType::Tool_change); + } + } + catch (...) + { + BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid toolchange (" << cmd << ")."; } } } @@ -319,6 +409,8 @@ void GCodeProcessor::store_move_vertex(EMoveType type) vertex.feedrate = m_feedrate; vertex.width = m_width; vertex.height = m_height; + vertex.mm3_per_mm = m_mm3_per_mm; + vertex.fan_speed = m_fan_speed; vertex.extruder_id = m_extruder_id; m_result.moves.emplace_back(vertex); } diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 0fa8187fd3..66036728dd 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -16,6 +16,7 @@ namespace Slic3r { static const std::string Extrusion_Role_Tag; static const std::string Width_Tag; static const std::string Height_Tag; + static const std::string Mm3_Per_Mm_Tag; private: using AxisCoords = std::array; @@ -52,6 +53,8 @@ namespace Slic3r { float feedrate{ 0.0f }; // mm/s float width{ 0.0f }; // mm float height{ 0.0f }; // mm + float mm3_per_mm{ 0.0f }; + float fan_speed{ 0.0f }; // percentage unsigned int extruder_id{ 0 }; std::string to_string() const @@ -63,6 +66,8 @@ namespace Slic3r { str += ", " + std::to_string(feedrate); str += ", " + std::to_string(width); str += ", " + std::to_string(height); + str += ", " + std::to_string(mm3_per_mm); + str += ", " + std::to_string(fan_speed); return str; } }; @@ -85,9 +90,11 @@ namespace Slic3r { AxisCoords m_end_position; // mm AxisCoords m_origin; // mm - float m_feedrate; // mm/s - float m_width; // mm - float m_height; // mm + float m_feedrate; // mm/s + float m_width; // mm + float m_height; // mm + float m_mm3_per_mm; + float m_fan_speed; // percentage ExtrusionRole m_extrusion_role; unsigned int m_extruder_id; @@ -103,7 +110,6 @@ namespace Slic3r { Result&& extract_result() { return std::move(m_result); } // Process the gcode contained in the file with the given filename - // Return false if any error occourred void process_file(const std::string& filename); private: @@ -115,23 +121,41 @@ namespace Slic3r { // Move void process_G1(const GCodeReader::GCodeLine& line); + // Retract + void process_G10(const GCodeReader::GCodeLine& line); + + // Unretract + void process_G11(const GCodeReader::GCodeLine& line); + + // Firmware controlled Retract + void process_G22(const GCodeReader::GCodeLine& line); + + // Firmware controlled Unretract + void process_G23(const GCodeReader::GCodeLine& line); + // Set to Absolute Positioning - void processG90(const GCodeReader::GCodeLine& line); + void process_G90(const GCodeReader::GCodeLine& line); // Set to Relative Positioning - void processG91(const GCodeReader::GCodeLine& line); + void process_G91(const GCodeReader::GCodeLine& line); // Set Position - void processG92(const GCodeReader::GCodeLine& line); + void process_G92(const GCodeReader::GCodeLine& line); // Set extruder to absolute mode - void processM82(const GCodeReader::GCodeLine& line); + void process_M82(const GCodeReader::GCodeLine& line); // Set extruder to relative mode - void processM83(const GCodeReader::GCodeLine& line); + void process_M83(const GCodeReader::GCodeLine& line); + + // Set fan speed + void process_M106(const GCodeReader::GCodeLine& line); + + // Disable fan + void process_M107(const GCodeReader::GCodeLine& line); // Processes T line (Select Tool) - void processT(const GCodeReader::GCodeLine& line); + void process_T(const GCodeReader::GCodeLine& line); void store_move_vertex(EMoveType type); }; diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 339012d0b4..d5d060f773 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -90,6 +90,10 @@ public: char buf[64]; sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm); m_gcode += buf; +#if ENABLE_GCODE_VIEWER + sprintf(buf, ";%s%f\n", GCodeProcessor::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm); + m_gcode += buf; +#endif // ENABLE_GCODE_VIEWER return *this; } From 1caac17b0237c36d36b9e5043a36a75422001b6c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 6 Apr 2020 08:55:48 +0200 Subject: [PATCH 010/503] GCodeProcessor additions: process M108 lines process M132 lines process M135 lines process M401 lines process M402 lines --- src/libslic3r/GCode/Analyzer.cpp | 2 +- src/libslic3r/GCode/GCodeProcessor.cpp | 117 ++++++++++++++++++++++++- src/libslic3r/GCode/GCodeProcessor.hpp | 24 +++++ 3 files changed, 138 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp index c7b67647f1..5db2ff3dea 100644 --- a/src/libslic3r/GCode/Analyzer.cpp +++ b/src/libslic3r/GCode/Analyzer.cpp @@ -951,7 +951,7 @@ void GCodeAnalyzer::_store_move(GCodeAnalyzer::GCodeMove::EType type) { g_debug_output << std::to_string((int)type); g_debug_output << ", " << std::to_string((int)_get_extrusion_role()); - g_debug_output << ", " << Slic3r::to_string((Vec3d)_get_end_position().cast()); + g_debug_output << ", " << Slic3r::to_string((Vec3d)end_position.cast()); g_debug_output << ", " << std::to_string(extruder_id); g_debug_output << ", " << std::to_string(_get_feedrate()); g_debug_output << ", " << std::to_string(_get_width()); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 0f42c6796e..bbb5f6046e 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -24,6 +24,8 @@ void GCodeProcessor::apply_config(const PrintConfig& config) { m_parser.apply_config(config); + m_flavor = config.gcode_flavor; + size_t extruders_count = config.nozzle_diameter.values.size(); if (m_extruder_offsets.size() != extruders_count) m_extruder_offsets.resize(extruders_count); @@ -41,10 +43,13 @@ void GCodeProcessor::reset() m_global_positioning_type = EPositioningType::Absolute; m_e_local_positioning_type = EPositioningType::Absolute; m_extruder_offsets = std::vector(1, Vec3f::Zero()); + m_flavor = gcfRepRap; std::fill(m_start_position.begin(), m_start_position.end(), 0.0f); std::fill(m_end_position.begin(), m_end_position.end(), 0.0f); std::fill(m_origin.begin(), m_origin.end(), 0.0f); + std::fill(m_cached_position.position.begin(), m_cached_position.position.end(), FLT_MAX); + m_cached_position.feedrate = FLT_MAX; m_feedrate = 0.0f; m_width = 0.0f; @@ -103,6 +108,11 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) case 83: { process_M83(line); break; } // Set extruder to relative mode case 106: { process_M106(line); break; } // Set fan speed case 107: { process_M107(line); break; } // Disable fan + case 108: { process_M108(line); break; } // Set tool (Sailfish) + case 132: { process_M132(line); break; } // Recall stored home offsets + case 135: { process_M135(line); break; } // Set tool (MakerWare) + case 401: { process_M401(line); break; } // Repetier: Store x, y and z position + case 402: { process_M402(line); break; } // Repetier: Go to stored position default: { break; } } break; @@ -369,14 +379,113 @@ void GCodeProcessor::process_M107(const GCodeReader::GCodeLine& line) m_fan_speed = 0.0f; } +void GCodeProcessor::process_M108(const GCodeReader::GCodeLine& line) +{ + // These M-codes are used by 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 + + if (m_flavor != gcfSailfish) + return; + + std::string cmd = line.raw(); + size_t pos = cmd.find("T"); + if (pos != std::string::npos) + process_T(cmd.substr(pos)); +} + +void GCodeProcessor::process_M132(const GCodeReader::GCodeLine& line) +{ + // This command is used by Makerbot to load the current home position from EEPROM + // see: https://github.com/makerbot/s3g/blob/master/doc/GCodeProtocol.md + // Using this command to reset the axis origin to zero helps in fixing: https://github.com/prusa3d/PrusaSlicer/issues/3082 + + if (line.has_x()) + m_origin[X] = 0.0f; + + if (line.has_y()) + m_origin[Y] = 0.0f; + + if (line.has_z()) + m_origin[Z] = 0.0f; + + if (line.has_e()) + m_origin[E] = 0.0f; +} + +void GCodeProcessor::process_M135(const GCodeReader::GCodeLine& line) +{ + // These M-codes are used by MakerWare 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 + + if (m_flavor != gcfMakerWare) + return; + + std::string cmd = line.raw(); + size_t pos = cmd.find("T"); + if (pos != std::string::npos) + process_T(cmd.substr(pos)); +} + +void GCodeProcessor::process_M401(const GCodeReader::GCodeLine& line) +{ + if (m_flavor != gcfRepetier) + return; + + for (unsigned char a = 0; a <= 3; ++a) + { + m_cached_position.position[a] = m_start_position[a]; + } + m_cached_position.feedrate = m_feedrate; +} + +void GCodeProcessor::process_M402(const GCodeReader::GCodeLine& line) +{ + if (m_flavor != gcfRepetier) + return; + + // see for reference: + // https://github.com/repetier/Repetier-Firmware/blob/master/src/ArduinoAVR/Repetier/Printer.cpp + // void Printer::GoToMemoryPosition(bool x, bool y, bool z, bool e, float feed) + + bool has_xyz = !(line.has_x() || line.has_y() || line.has_z()); + + float p = FLT_MAX; + for (unsigned char a = X; a <= Z; ++a) + { + if (has_xyz || line.has(a)) + { + p = m_cached_position.position[a]; + if (p != FLT_MAX) + m_start_position[a] = p; + } + } + + p = m_cached_position.position[E]; + if (p != FLT_MAX) + m_start_position[E] = p; + + p = FLT_MAX; + if (!line.has_value(4, p)) + p = m_cached_position.feedrate; + + if (p != FLT_MAX) + m_feedrate = p; +} + void GCodeProcessor::process_T(const GCodeReader::GCodeLine& line) { - const std::string& cmd = line.cmd(); - if (cmd.length() > 1) + process_T(line.cmd()); +} + +void GCodeProcessor::process_T(const std::string& command) +{ + if (command.length() > 1) { try { - unsigned int id = (unsigned int)std::stoi(cmd.substr(1)); + unsigned int id = (unsigned int)std::stoi(command.substr(1)); if (m_extruder_id != id) { unsigned int extruders_count = (unsigned int)m_extruder_offsets.size(); @@ -395,7 +504,7 @@ void GCodeProcessor::process_T(const GCodeReader::GCodeLine& line) } catch (...) { - BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid toolchange (" << cmd << ")."; + BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid toolchange (" << command << ")."; } } } diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 66036728dd..a3cfbdc787 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -44,6 +44,12 @@ namespace Slic3r { Num_Types }; + struct CachedPosition + { + AxisCoords position; // mm + float feedrate; // mm/s + }; + public: struct MoveVertex { @@ -85,10 +91,12 @@ namespace Slic3r { EPositioningType m_global_positioning_type; EPositioningType m_e_local_positioning_type; std::vector m_extruder_offsets; + GCodeFlavor m_flavor; AxisCoords m_start_position; // mm AxisCoords m_end_position; // mm AxisCoords m_origin; // mm + CachedPosition m_cached_position; float m_feedrate; // mm/s float m_width; // mm @@ -154,8 +162,24 @@ namespace Slic3r { // Disable fan void process_M107(const GCodeReader::GCodeLine& line); + // Set tool (Sailfish) + void process_M108(const GCodeReader::GCodeLine& line); + + // Recall stored home offsets + void process_M132(const GCodeReader::GCodeLine& line); + + // Set tool (MakerWare) + void process_M135(const GCodeReader::GCodeLine& line); + + // Repetier: Store x, y and z position + void process_M401(const GCodeReader::GCodeLine& line); + + // Repetier: Go to stored position + void process_M402(const GCodeReader::GCodeLine& line); + // Processes T line (Select Tool) void process_T(const GCodeReader::GCodeLine& line); + void process_T(const std::string& command); void store_move_vertex(EMoveType type); }; From 57dad5dfd2b3d01320ed36d14bdf6a2d93bc5c75 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 6 Apr 2020 11:53:15 +0200 Subject: [PATCH 011/503] GCodeProcessor additions: process color change comment tag process pause print comment tag process custom code comment tag process end pause print or custom code comment tag --- src/libslic3r/GCode.cpp | 26 +++++++- src/libslic3r/GCode/Analyzer.cpp | 13 ++-- src/libslic3r/GCode/GCodeProcessor.cpp | 88 +++++++++++++++++++++++--- src/libslic3r/GCode/GCodeProcessor.hpp | 20 ++++++ 4 files changed, 130 insertions(+), 17 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 720b9a1fa7..ee7be7fea7 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1823,7 +1823,11 @@ namespace ProcessLayer // Color Change or Tool Change as Color Change. // add tag for analyzer gcode += "; " + GCodeAnalyzer::Color_Change_Tag + ",T" + std::to_string(m600_extruder_before_layer) + "\n"; - // add tag for time estimator +#if ENABLE_GCODE_VIEWER + // add tag for processor + gcode += "; " + GCodeProcessor::Color_Change_Tag + ",T" + std::to_string(m600_extruder_before_layer) + "\n"; +#endif // ENABLE_GCODE_VIEWER + // add tag for time estimator gcode += "; " + GCodeTimeEstimator::Color_Change_Tag + "\n"; if (!single_extruder_printer && m600_extruder_before_layer >= 0 && first_extruder_id != (unsigned)m600_extruder_before_layer @@ -1844,7 +1848,11 @@ namespace ProcessLayer { // add tag for analyzer gcode += "; " + GCodeAnalyzer::Pause_Print_Tag + "\n"; - //! FIXME_in_fw show message during print pause +#if ENABLE_GCODE_VIEWER + // add tag for processor + gcode += "; " + GCodeProcessor::Pause_Print_Tag + "\n"; +#endif // ENABLE_GCODE_VIEWER + //! FIXME_in_fw show message during print pause if (!pause_print_msg.empty()) gcode += "M117 " + pause_print_msg + "\n"; // add tag for time estimator @@ -1854,7 +1862,11 @@ namespace ProcessLayer { // add tag for analyzer gcode += "; " + GCodeAnalyzer::Custom_Code_Tag + "\n"; - // add tag for time estimator +#if ENABLE_GCODE_VIEWER + // add tag for processor + gcode += "; " + GCodeProcessor::Custom_Code_Tag + "\n"; +#endif // ENABLE_GCODE_VIEWER + // add tag for time estimator //gcode += "; " + GCodeTimeEstimator::Custom_Code_Tag + "\n"; } gcode += custom_code + "\n"; @@ -2316,6 +2328,14 @@ void GCode::process_layer( else if (gcode.find(GCodeAnalyzer::Custom_Code_Tag) != gcode.npos) gcode += "\n; " + GCodeAnalyzer::End_Pause_Print_Or_Custom_Code_Tag + "\n"; +#if ENABLE_GCODE_VIEWER + // add tag for processor + if (gcode.find(GCodeProcessor::Pause_Print_Tag) != gcode.npos) + gcode += "\n; " + GCodeProcessor::End_Pause_Print_Or_Custom_Code_Tag + "\n"; + else if (gcode.find(GCodeProcessor::Custom_Code_Tag) != gcode.npos) + gcode += "\n; " + GCodeProcessor::End_Pause_Print_Or_Custom_Code_Tag + "\n"; +#endif // ENABLE_GCODE_VIEWER + #ifdef HAS_PRESSURE_EQUALIZER // Apply pressure equalization if enabled; // printf("G-code before filter:\n%s\n", gcode.c_str()); diff --git a/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp index 5db2ff3dea..974176dbd7 100644 --- a/src/libslic3r/GCode/Analyzer.cpp +++ b/src/libslic3r/GCode/Analyzer.cpp @@ -674,7 +674,7 @@ bool GCodeAnalyzer::_process_tags(const GCodeReader::GCodeLine& line) return true; } - // color change tag + // pause print tag pos = comment.find(Pause_Print_Tag); if (pos != comment.npos) { @@ -682,7 +682,7 @@ bool GCodeAnalyzer::_process_tags(const GCodeReader::GCodeLine& line) return true; } - // color change tag + // custom code tag pos = comment.find(Custom_Code_Tag); if (pos != comment.npos) { @@ -690,7 +690,7 @@ bool GCodeAnalyzer::_process_tags(const GCodeReader::GCodeLine& line) return true; } - // color change tag + // end pause print or custom code tag pos = comment.find(End_Pause_Print_Or_Custom_Code_Tag); if (pos != comment.npos) { @@ -949,10 +949,11 @@ void GCodeAnalyzer::_store_move(GCodeAnalyzer::GCodeMove::EType type) #if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT if (g_debug_output.good()) { - g_debug_output << std::to_string((int)type); - g_debug_output << ", " << std::to_string((int)_get_extrusion_role()); - g_debug_output << ", " << Slic3r::to_string((Vec3d)end_position.cast()); + g_debug_output << std::to_string(static_cast(type)); + g_debug_output << ", " << std::to_string(static_cast(_get_extrusion_role())); + g_debug_output << ", " << Slic3r::to_string(static_cast(end_position.cast())); g_debug_output << ", " << std::to_string(extruder_id); + g_debug_output << ", " << std::to_string(_get_cp_color_id()); g_debug_output << ", " << std::to_string(_get_feedrate()); g_debug_output << ", " << std::to_string(_get_width()); g_debug_output << ", " << std::to_string(_get_height()); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index bbb5f6046e..0a61879d80 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -19,6 +19,22 @@ const std::string GCodeProcessor::Extrusion_Role_Tag = "_PROCESSOR_EXTRUSION_ROL const std::string GCodeProcessor::Width_Tag = "_PROCESSOR_WIDTH:"; const std::string GCodeProcessor::Height_Tag = "_PROCESSOR_HEIGHT:"; const std::string GCodeProcessor::Mm3_Per_Mm_Tag = "_PROCESSOR_MM3_PER_MM:"; +const std::string GCodeProcessor::Color_Change_Tag = "_PROCESSOR_COLOR_CHANGE"; +const std::string GCodeProcessor::Pause_Print_Tag = "_PROCESSOR_PAUSE_PRINT"; +const std::string GCodeProcessor::Custom_Code_Tag = "_PROCESSOR_CUSTOM_CODE"; +const std::string GCodeProcessor::End_Pause_Print_Or_Custom_Code_Tag = "_PROCESSOR_END_PAUSE_PRINT_OR_CUSTOM_CODE"; + +void GCodeProcessor::CachedPosition::reset() +{ + std::fill(position.begin(), position.end(), FLT_MAX); + feedrate = FLT_MAX; +} + +void GCodeProcessor::CpColor::reset() +{ + counter = 0; + current = 0; +} void GCodeProcessor::apply_config(const PrintConfig& config) { @@ -27,14 +43,19 @@ void GCodeProcessor::apply_config(const PrintConfig& config) m_flavor = config.gcode_flavor; size_t extruders_count = config.nozzle_diameter.values.size(); - if (m_extruder_offsets.size() != extruders_count) - m_extruder_offsets.resize(extruders_count); + m_extruder_offsets.resize(extruders_count); for (size_t id = 0; id < extruders_count; ++id) { Vec2f offset = config.extruder_offset.get_at(id).cast(); m_extruder_offsets[id] = Vec3f(offset(0), offset(1), 0.0f); } + + m_extruders_color.resize(extruders_count); + for (size_t id = 0; id < extruders_count; ++id) + { + m_extruders_color[id] = static_cast(id); + } } void GCodeProcessor::reset() @@ -48,8 +69,7 @@ void GCodeProcessor::reset() std::fill(m_start_position.begin(), m_start_position.end(), 0.0f); std::fill(m_end_position.begin(), m_end_position.end(), 0.0f); std::fill(m_origin.begin(), m_origin.end(), 0.0f); - std::fill(m_cached_position.position.begin(), m_cached_position.position.end(), FLT_MAX); - m_cached_position.feedrate = FLT_MAX; + m_cached_position.reset(); m_feedrate = 0.0f; m_width = 0.0f; @@ -59,6 +79,8 @@ void GCodeProcessor::reset() m_extrusion_role = erNone; m_extruder_id = 0; + m_extruders_color = ExtrudersColor(); + m_cp_color.reset(); m_result.reset(); } @@ -202,6 +224,55 @@ void GCodeProcessor::process_tags(const std::string& comment) } return; } + + // color change tag + pos = comment.find(Color_Change_Tag); + if (pos != comment.npos) + { + pos = comment.find_last_of(",T"); + try + { + unsigned int extruder_id = (pos == comment.npos) ? 0 : static_cast(std::stoi(comment.substr(pos + 1, comment.npos))); + + m_extruders_color[extruder_id] = static_cast(m_extruder_offsets.size()) + m_cp_color.counter; // color_change position in list of color for preview + ++m_cp_color.counter; + + if (m_extruder_id == extruder_id) + m_cp_color.current = m_extruders_color[extruder_id]; + } + catch (...) + { + BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Color_Change (" << comment << ")."; + } + + return; + } + + // pause print tag + pos = comment.find(Pause_Print_Tag); + if (pos != comment.npos) + { + m_cp_color.current = INT_MAX; + return; + } + + // custom code tag + pos = comment.find(Custom_Code_Tag); + if (pos != comment.npos) + { + m_cp_color.current = INT_MAX; + return; + } + + // end pause print or custom code tag + pos = comment.find(End_Pause_Print_Or_Custom_Code_Tag); + if (pos != comment.npos) + { + if (m_cp_color.current == INT_MAX) + m_cp_color.current = m_extruders_color[m_extruder_id]; + + return; + } } void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) @@ -485,17 +556,17 @@ void GCodeProcessor::process_T(const std::string& command) { try { - unsigned int id = (unsigned int)std::stoi(command.substr(1)); + unsigned int id = static_cast(std::stoi(command.substr(1))); if (m_extruder_id != id) { - unsigned int extruders_count = (unsigned int)m_extruder_offsets.size(); + unsigned int extruders_count = static_cast(m_extruder_offsets.size()); if (id >= extruders_count) BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid toolchange, maybe from a custom gcode."; else { m_extruder_id = id; -// if (_get_cp_color_id() != INT_MAX) <<<<<<<<<<<<<<<<<<< TODO -// _set_cp_color_id(m_extruder_color[id]); + if (m_cp_color.current != INT_MAX) + m_cp_color.current = m_extruders_color[id]; } // store tool change move @@ -521,6 +592,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type) vertex.mm3_per_mm = m_mm3_per_mm; vertex.fan_speed = m_fan_speed; vertex.extruder_id = m_extruder_id; + vertex.cp_color_id = m_cp_color.current; m_result.moves.emplace_back(vertex); } diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index a3cfbdc787..ce1f695dca 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -7,6 +7,7 @@ #include "../ExtrusionEntity.hpp" #include +#include namespace Slic3r { @@ -17,9 +18,14 @@ namespace Slic3r { static const std::string Width_Tag; static const std::string Height_Tag; static const std::string Mm3_Per_Mm_Tag; + static const std::string Color_Change_Tag; + static const std::string Pause_Print_Tag; + static const std::string Custom_Code_Tag; + static const std::string End_Pause_Print_Or_Custom_Code_Tag; private: using AxisCoords = std::array; + using ExtrudersColor = std::vector; enum class EUnits : unsigned char { @@ -48,6 +54,16 @@ namespace Slic3r { { AxisCoords position; // mm float feedrate; // mm/s + + void reset(); + }; + + struct CpColor + { + unsigned int counter; + unsigned int current; + + void reset(); }; public: @@ -62,6 +78,7 @@ namespace Slic3r { float mm3_per_mm{ 0.0f }; float fan_speed{ 0.0f }; // percentage unsigned int extruder_id{ 0 }; + unsigned int cp_color_id{ 0 }; std::string to_string() const { @@ -69,6 +86,7 @@ namespace Slic3r { str += ", " + std::to_string((int)extrusion_role); str += ", " + Slic3r::to_string((Vec3d)position.cast()); str += ", " + std::to_string(extruder_id); + str += ", " + std::to_string(cp_color_id); str += ", " + std::to_string(feedrate); str += ", " + std::to_string(width); str += ", " + std::to_string(height); @@ -105,6 +123,8 @@ namespace Slic3r { float m_fan_speed; // percentage ExtrusionRole m_extrusion_role; unsigned int m_extruder_id; + ExtrudersColor m_extruders_color; + CpColor m_cp_color; Result m_result; From 2c69d962398c3ceb17ef620270f8c09d504c4202 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 6 Apr 2020 17:24:11 +0200 Subject: [PATCH 012/503] Reduced size of GCodeProcessor::MoveVertex --- src/libslic3r/GCode/GCodeProcessor.cpp | 16 ++++++++-------- src/libslic3r/GCode/GCodeProcessor.hpp | 12 ++++++------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 0a61879d80..7fb06bbda0 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -232,9 +232,9 @@ void GCodeProcessor::process_tags(const std::string& comment) pos = comment.find_last_of(",T"); try { - unsigned int extruder_id = (pos == comment.npos) ? 0 : static_cast(std::stoi(comment.substr(pos + 1, comment.npos))); + unsigned char extruder_id = (pos == comment.npos) ? 0 : static_cast(std::stoi(comment.substr(pos + 1, comment.npos))); - m_extruders_color[extruder_id] = static_cast(m_extruder_offsets.size()) + m_cp_color.counter; // color_change position in list of color for preview + m_extruders_color[extruder_id] = static_cast(m_extruder_offsets.size()) + m_cp_color.counter; // color_change position in list of color for preview ++m_cp_color.counter; if (m_extruder_id == extruder_id) @@ -252,7 +252,7 @@ void GCodeProcessor::process_tags(const std::string& comment) pos = comment.find(Pause_Print_Tag); if (pos != comment.npos) { - m_cp_color.current = INT_MAX; + m_cp_color.current = 255; return; } @@ -260,7 +260,7 @@ void GCodeProcessor::process_tags(const std::string& comment) pos = comment.find(Custom_Code_Tag); if (pos != comment.npos) { - m_cp_color.current = INT_MAX; + m_cp_color.current = 255; return; } @@ -268,7 +268,7 @@ void GCodeProcessor::process_tags(const std::string& comment) pos = comment.find(End_Pause_Print_Or_Custom_Code_Tag); if (pos != comment.npos) { - if (m_cp_color.current == INT_MAX) + if (m_cp_color.current == 255) m_cp_color.current = m_extruders_color[m_extruder_id]; return; @@ -556,16 +556,16 @@ void GCodeProcessor::process_T(const std::string& command) { try { - unsigned int id = static_cast(std::stoi(command.substr(1))); + unsigned char id = static_cast(std::stoi(command.substr(1))); if (m_extruder_id != id) { - unsigned int extruders_count = static_cast(m_extruder_offsets.size()); + unsigned char extruders_count = static_cast(m_extruder_offsets.size()); if (id >= extruders_count) BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid toolchange, maybe from a custom gcode."; else { m_extruder_id = id; - if (m_cp_color.current != INT_MAX) + if (m_cp_color.current != 255) m_cp_color.current = m_extruders_color[id]; } diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index ce1f695dca..54ac546b38 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -25,7 +25,7 @@ namespace Slic3r { private: using AxisCoords = std::array; - using ExtrudersColor = std::vector; + using ExtrudersColor = std::vector; enum class EUnits : unsigned char { @@ -60,8 +60,8 @@ namespace Slic3r { struct CpColor { - unsigned int counter; - unsigned int current; + unsigned char counter; + unsigned char current; void reset(); }; @@ -71,14 +71,14 @@ namespace Slic3r { { EMoveType type{ EMoveType::Noop }; ExtrusionRole extrusion_role{ erNone }; + unsigned char extruder_id{ 0 }; + unsigned char cp_color_id{ 0 }; Vec3f position{ Vec3f::Zero() }; // mm float feedrate{ 0.0f }; // mm/s float width{ 0.0f }; // mm float height{ 0.0f }; // mm float mm3_per_mm{ 0.0f }; float fan_speed{ 0.0f }; // percentage - unsigned int extruder_id{ 0 }; - unsigned int cp_color_id{ 0 }; std::string to_string() const { @@ -122,7 +122,7 @@ namespace Slic3r { float m_mm3_per_mm; float m_fan_speed; // percentage ExtrusionRole m_extrusion_role; - unsigned int m_extruder_id; + unsigned char m_extruder_id; ExtrudersColor m_extruders_color; CpColor m_cp_color; From 22cf0396fcd8b51ef8f0cc0155305538379efb9f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 6 Apr 2020 17:32:35 +0200 Subject: [PATCH 013/503] Added missing include --- src/libslic3r/GCode/GCodeProcessor.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 7fb06bbda0..f5094553a5 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -3,6 +3,8 @@ #include +#include + #if ENABLE_GCODE_VIEWER static const float INCHES_TO_MM = 25.4f; From c3eb65c4612c6011cda2bdb9a683d3e12af3295a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 14 Apr 2020 10:02:08 +0200 Subject: [PATCH 014/503] Added class GCodeViewer -> basic render of gcode toolpaths using dedicated shaders --- resources/shaders/extrusions.fs | 45 +++++ resources/shaders/extrusions.vs | 15 ++ resources/shaders/retractions.fs | 45 +++++ resources/shaders/retractions.vs | 15 ++ resources/shaders/toolchanges.fs | 45 +++++ resources/shaders/toolchanges.vs | 15 ++ resources/shaders/travels.fs | 45 +++++ resources/shaders/travels.vs | 15 ++ resources/shaders/unretractions.fs | 45 +++++ resources/shaders/unretractions.vs | 15 ++ src/libslic3r/GCode/GCodeProcessor.cpp | 2 + src/libslic3r/GCode/GCodeProcessor.hpp | 25 +-- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/GCodeViewer.cpp | 242 +++++++++++++++++++++++++ src/slic3r/GUI/GCodeViewer.hpp | 63 +++++++ src/slic3r/GUI/GLCanvas3D.cpp | 20 ++ src/slic3r/GUI/GLCanvas3D.hpp | 8 + src/slic3r/GUI/GUI_Preview.cpp | 3 +- 18 files changed, 652 insertions(+), 13 deletions(-) create mode 100644 resources/shaders/extrusions.fs create mode 100644 resources/shaders/extrusions.vs create mode 100644 resources/shaders/retractions.fs create mode 100644 resources/shaders/retractions.vs create mode 100644 resources/shaders/toolchanges.fs create mode 100644 resources/shaders/toolchanges.vs create mode 100644 resources/shaders/travels.fs create mode 100644 resources/shaders/travels.vs create mode 100644 resources/shaders/unretractions.fs create mode 100644 resources/shaders/unretractions.vs create mode 100644 src/slic3r/GUI/GCodeViewer.cpp create mode 100644 src/slic3r/GUI/GCodeViewer.hpp diff --git a/resources/shaders/extrusions.fs b/resources/shaders/extrusions.fs new file mode 100644 index 0000000000..046dade8a9 --- /dev/null +++ b/resources/shaders/extrusions.fs @@ -0,0 +1,45 @@ +#version 110 + +#define INTENSITY_AMBIENT 0.3 +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +uniform vec4 uniform_color; + +varying vec3 eye_position; +varying vec3 eye_normal; +//varying float world_normal_z; + +// x = tainted, y = specular; +vec2 intensity; + +void main() +{ + vec3 normal = normalize(eye_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + +// // darkens fragments whose normal points downward +// if (world_normal_z < 0.0) +// intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); + + gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); +} diff --git a/resources/shaders/extrusions.vs b/resources/shaders/extrusions.vs new file mode 100644 index 0000000000..d97adbabe0 --- /dev/null +++ b/resources/shaders/extrusions.vs @@ -0,0 +1,15 @@ +#version 110 + +varying vec3 eye_position; +varying vec3 eye_normal; +//// world z component of the normal used to darken the lower side of the toolpaths +//varying float world_normal_z; + +void main() +{ + eye_position = (gl_ModelViewMatrix * gl_Vertex).xyz; + eye_normal = gl_NormalMatrix * vec3(0.0, 0.0, 1.0); +// eye_normal = gl_NormalMatrix * gl_Normal; +// world_normal_z = gl_Normal.z; + gl_Position = ftransform(); +} diff --git a/resources/shaders/retractions.fs b/resources/shaders/retractions.fs new file mode 100644 index 0000000000..046dade8a9 --- /dev/null +++ b/resources/shaders/retractions.fs @@ -0,0 +1,45 @@ +#version 110 + +#define INTENSITY_AMBIENT 0.3 +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +uniform vec4 uniform_color; + +varying vec3 eye_position; +varying vec3 eye_normal; +//varying float world_normal_z; + +// x = tainted, y = specular; +vec2 intensity; + +void main() +{ + vec3 normal = normalize(eye_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + +// // darkens fragments whose normal points downward +// if (world_normal_z < 0.0) +// intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); + + gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); +} diff --git a/resources/shaders/retractions.vs b/resources/shaders/retractions.vs new file mode 100644 index 0000000000..d97adbabe0 --- /dev/null +++ b/resources/shaders/retractions.vs @@ -0,0 +1,15 @@ +#version 110 + +varying vec3 eye_position; +varying vec3 eye_normal; +//// world z component of the normal used to darken the lower side of the toolpaths +//varying float world_normal_z; + +void main() +{ + eye_position = (gl_ModelViewMatrix * gl_Vertex).xyz; + eye_normal = gl_NormalMatrix * vec3(0.0, 0.0, 1.0); +// eye_normal = gl_NormalMatrix * gl_Normal; +// world_normal_z = gl_Normal.z; + gl_Position = ftransform(); +} diff --git a/resources/shaders/toolchanges.fs b/resources/shaders/toolchanges.fs new file mode 100644 index 0000000000..046dade8a9 --- /dev/null +++ b/resources/shaders/toolchanges.fs @@ -0,0 +1,45 @@ +#version 110 + +#define INTENSITY_AMBIENT 0.3 +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +uniform vec4 uniform_color; + +varying vec3 eye_position; +varying vec3 eye_normal; +//varying float world_normal_z; + +// x = tainted, y = specular; +vec2 intensity; + +void main() +{ + vec3 normal = normalize(eye_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + +// // darkens fragments whose normal points downward +// if (world_normal_z < 0.0) +// intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); + + gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); +} diff --git a/resources/shaders/toolchanges.vs b/resources/shaders/toolchanges.vs new file mode 100644 index 0000000000..d97adbabe0 --- /dev/null +++ b/resources/shaders/toolchanges.vs @@ -0,0 +1,15 @@ +#version 110 + +varying vec3 eye_position; +varying vec3 eye_normal; +//// world z component of the normal used to darken the lower side of the toolpaths +//varying float world_normal_z; + +void main() +{ + eye_position = (gl_ModelViewMatrix * gl_Vertex).xyz; + eye_normal = gl_NormalMatrix * vec3(0.0, 0.0, 1.0); +// eye_normal = gl_NormalMatrix * gl_Normal; +// world_normal_z = gl_Normal.z; + gl_Position = ftransform(); +} diff --git a/resources/shaders/travels.fs b/resources/shaders/travels.fs new file mode 100644 index 0000000000..046dade8a9 --- /dev/null +++ b/resources/shaders/travels.fs @@ -0,0 +1,45 @@ +#version 110 + +#define INTENSITY_AMBIENT 0.3 +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +uniform vec4 uniform_color; + +varying vec3 eye_position; +varying vec3 eye_normal; +//varying float world_normal_z; + +// x = tainted, y = specular; +vec2 intensity; + +void main() +{ + vec3 normal = normalize(eye_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + +// // darkens fragments whose normal points downward +// if (world_normal_z < 0.0) +// intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); + + gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); +} diff --git a/resources/shaders/travels.vs b/resources/shaders/travels.vs new file mode 100644 index 0000000000..d97adbabe0 --- /dev/null +++ b/resources/shaders/travels.vs @@ -0,0 +1,15 @@ +#version 110 + +varying vec3 eye_position; +varying vec3 eye_normal; +//// world z component of the normal used to darken the lower side of the toolpaths +//varying float world_normal_z; + +void main() +{ + eye_position = (gl_ModelViewMatrix * gl_Vertex).xyz; + eye_normal = gl_NormalMatrix * vec3(0.0, 0.0, 1.0); +// eye_normal = gl_NormalMatrix * gl_Normal; +// world_normal_z = gl_Normal.z; + gl_Position = ftransform(); +} diff --git a/resources/shaders/unretractions.fs b/resources/shaders/unretractions.fs new file mode 100644 index 0000000000..046dade8a9 --- /dev/null +++ b/resources/shaders/unretractions.fs @@ -0,0 +1,45 @@ +#version 110 + +#define INTENSITY_AMBIENT 0.3 +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +uniform vec4 uniform_color; + +varying vec3 eye_position; +varying vec3 eye_normal; +//varying float world_normal_z; + +// x = tainted, y = specular; +vec2 intensity; + +void main() +{ + vec3 normal = normalize(eye_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + +// // darkens fragments whose normal points downward +// if (world_normal_z < 0.0) +// intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); + + gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); +} diff --git a/resources/shaders/unretractions.vs b/resources/shaders/unretractions.vs new file mode 100644 index 0000000000..d97adbabe0 --- /dev/null +++ b/resources/shaders/unretractions.vs @@ -0,0 +1,15 @@ +#version 110 + +varying vec3 eye_position; +varying vec3 eye_normal; +//// world z component of the normal used to darken the lower side of the toolpaths +//varying float world_normal_z; + +void main() +{ + eye_position = (gl_ModelViewMatrix * gl_Vertex).xyz; + eye_normal = gl_NormalMatrix * vec3(0.0, 0.0, 1.0); +// eye_normal = gl_NormalMatrix * gl_Normal; +// world_normal_z = gl_Normal.z; + gl_Position = ftransform(); +} diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index f5094553a5..c75c240020 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -38,6 +38,8 @@ void GCodeProcessor::CpColor::reset() current = 0; } +unsigned int GCodeProcessor::Result::id = 0; + void GCodeProcessor::apply_config(const PrintConfig& config) { m_parser.apply_config(config); diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 54ac546b38..1f7af9c29d 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -39,17 +39,6 @@ namespace Slic3r { Relative }; - enum class EMoveType : unsigned char - { - Noop, - Retract, - Unretract, - Tool_change, - Travel, - Extrude, - Num_Types - }; - struct CachedPosition { AxisCoords position; // mm @@ -67,6 +56,17 @@ namespace Slic3r { }; public: + enum class EMoveType : unsigned char + { + Noop, + Retract, + Unretract, + Tool_change, + Travel, + Extrude, + Count + }; + struct MoveVertex { EMoveType type{ EMoveType::Noop }; @@ -98,8 +98,9 @@ namespace Slic3r { struct Result { + static unsigned int id; std::vector moves; - void reset() { moves = std::vector(); } + void reset() { ++id; moves = std::vector(); } }; private: diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index c8f7e9f1c9..f5f5f6eb62 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -56,6 +56,8 @@ set(SLIC3R_GUI_SOURCES GUI/GLTexture.cpp GUI/GLToolbar.hpp GUI/GLToolbar.cpp + GUI/GCodeViewer.hpp + GUI/GCodeViewer.cpp GUI/Preferences.cpp GUI/Preferences.hpp GUI/Preset.cpp diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp new file mode 100644 index 0000000000..584ef06c32 --- /dev/null +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -0,0 +1,242 @@ +#include "libslic3r/libslic3r.h" +#include "GCodeViewer.hpp" +#include "3DScene.hpp" + +#if ENABLE_GCODE_VIEWER + +#include +#include + +#include + +namespace Slic3r { +namespace GUI { + +static unsigned char buffer_id(GCodeProcessor::EMoveType type) { + return static_cast(type) - static_cast(GCodeProcessor::EMoveType::Retract); +} + +static GCodeProcessor::EMoveType buffer_type(unsigned char id) { + return static_cast(static_cast(GCodeProcessor::EMoveType::Retract) + id); +} + +void GCodeViewer::generate(const GCodeProcessor::Result& gcode_result) +{ + if (m_last_result_id == gcode_result.id) + return; + + m_last_result_id = gcode_result.id; + + // release gpu memory, if used + reset_buffers(); + + // convert data + size_t vertices_count = gcode_result.moves.size(); + for (size_t i = 0; i < vertices_count; ++i) + { + // skip first vertex + if (i == 0) + continue; + + const GCodeProcessor::MoveVertex& prev = gcode_result.moves[i - 1]; + const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i]; + + Buffer& buffer = m_buffers[buffer_id(curr.type)]; + + switch (curr.type) + { + case GCodeProcessor::EMoveType::Tool_change: + case GCodeProcessor::EMoveType::Retract: + case GCodeProcessor::EMoveType::Unretract: + { + for (int j = 0; j < 3; ++j) + { + buffer.data.insert(buffer.data.end(), curr.position[j]); + } + break; + } + case GCodeProcessor::EMoveType::Extrude: + case GCodeProcessor::EMoveType::Travel: + { + for (int j = 0; j < 3; ++j) + { + buffer.data.insert(buffer.data.end(), prev.position[j]); + } + for (int j = 0; j < 3; ++j) + { + buffer.data.insert(buffer.data.end(), curr.position[j]); + } + break; + } + default: + { + continue; + } + } + } + + // send data to gpu + for (Buffer& buffer : m_buffers) + { + glsafe(::glGenBuffers(1, &buffer.vbo_id)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vbo_id)); + glsafe(::glBufferData(GL_ARRAY_BUFFER, buffer.data.size() * sizeof(float), buffer.data.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + } +} + +void GCodeViewer::render() const +{ + auto set_color = [](GLint current_program_id, const std::array& color) { + if (current_program_id > 0) + { + GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1; + if (color_id >= 0) + { + glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)color.data())); + return; + } + } + BOOST_LOG_TRIVIAL(error) << "Unable to find uniform_color uniform"; + }; + + unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); + unsigned char end_id = buffer_id(GCodeProcessor::EMoveType::Count); + + glsafe(::glEnable(GL_DEPTH_TEST)); + + for (unsigned char i = begin_id; i < end_id; ++i) + { + const Buffer& buffer = m_buffers[i]; + if (buffer.vbo_id == 0) + continue; + + const Shader& shader = m_shaders[i]; + if (shader.is_initialized()) + { + shader.start_using(); + + GLint current_program_id; + glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id)); + + GCodeProcessor::EMoveType type = buffer_type(i); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vbo_id)); + glsafe(::glVertexPointer(3, GL_FLOAT, Buffer::stride(type), (const void*)0)); + glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + + switch (type) + { + case GCodeProcessor::EMoveType::Tool_change: + case GCodeProcessor::EMoveType::Retract: + case GCodeProcessor::EMoveType::Unretract: + { + std::array color = { 0.0f, 1.0f, 0.0f, 1.0f }; + set_color(current_program_id, color); + glsafe(::glDrawArrays(GL_POINTS, 0, (GLsizei)buffer.data.size() / Buffer::record_size(type))); + break; + } + case GCodeProcessor::EMoveType::Extrude: + { + std::array color = { 1.0f, 0.0f, 0.0f, 1.0f }; + set_color(current_program_id, color); + glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)buffer.data.size() / Buffer::record_size(type))); + break; + } + case GCodeProcessor::EMoveType::Travel: + { + std::array color = { 1.0f, 1.0f, 0.0f, 1.0f }; + set_color(current_program_id, color); + glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)buffer.data.size() / Buffer::record_size(type))); + break; + } + default: + { + break; + } + } + + glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + shader.stop_using(); + } + } +} + +bool GCodeViewer::init_shaders() +{ + unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); + unsigned char end_id = buffer_id(GCodeProcessor::EMoveType::Count); + + for (unsigned char i = begin_id; i < end_id; ++i) + { + Shader& shader = m_shaders[i]; + std::string vertex_shader_src; + std::string fragment_shader_src; + GCodeProcessor::EMoveType type = buffer_type(i); + switch (type) + { + case GCodeProcessor::EMoveType::Tool_change: + { + vertex_shader_src = "toolchanges.vs"; + fragment_shader_src = "toolchanges.fs"; + break; + } + case GCodeProcessor::EMoveType::Retract: + { + vertex_shader_src = "retractions.vs"; + fragment_shader_src = "retractions.fs"; + break; + } + case GCodeProcessor::EMoveType::Unretract: + { + vertex_shader_src = "unretractions.vs"; + fragment_shader_src = "unretractions.fs"; + break; + } + case GCodeProcessor::EMoveType::Extrude: + { + vertex_shader_src = "extrusions.vs"; + fragment_shader_src = "extrusions.fs"; + break; + } + case GCodeProcessor::EMoveType::Travel: + { + vertex_shader_src = "travels.vs"; + fragment_shader_src = "travels.fs"; + break; + } + default: + { + break; + } + } + + if (!shader.init(vertex_shader_src, fragment_shader_src)) + { + BOOST_LOG_TRIVIAL(error) << "Unable to initialize toolpaths shader: please, check that the files " << vertex_shader_src << " and " << fragment_shader_src << " are available"; + return false; + } + } + + return true; +} + +void GCodeViewer::reset_buffers() +{ + for (Buffer& buffer : m_buffers) + { + // release gpu memory + if (buffer.vbo_id > 0) + glsafe(::glDeleteBuffers(1, &buffer.vbo_id)); + + // release cpu memory + buffer.data = std::vector(); + } +} + +} // namespace GUI +} // namespace Slic3r + +#endif // ENABLE_GCODE_VIEWER diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp new file mode 100644 index 0000000000..95250b2034 --- /dev/null +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -0,0 +1,63 @@ +#ifndef slic3r_GCodeViewer_hpp_ +#define slic3r_GCodeViewer_hpp_ + +#if ENABLE_GCODE_VIEWER + +#include "GLShader.hpp" +#include "libslic3r/GCode/GCodeProcessor.hpp" + +#include + +namespace Slic3r { +namespace GUI { + +class GCodeViewer +{ + struct Buffer + { + unsigned int vbo_id{ 0 }; + std::vector data; + + static size_t stride(GCodeProcessor::EMoveType type) + { + return 3 * sizeof(float); + } + + static size_t record_size(GCodeProcessor::EMoveType type) + { + switch (type) + { + case GCodeProcessor::EMoveType::Tool_change: + case GCodeProcessor::EMoveType::Retract: + case GCodeProcessor::EMoveType::Unretract: { return 3; } + case GCodeProcessor::EMoveType::Extrude: + case GCodeProcessor::EMoveType::Travel: { return 6; } + default: { return 0; } + } + } + }; + + std::vector m_buffers{ static_cast(GCodeProcessor::EMoveType::Extrude) }; + std::vector m_shaders{ static_cast(GCodeProcessor::EMoveType::Extrude) }; + unsigned int m_last_result_id{ 0 }; + +public: + GCodeViewer() = default; + ~GCodeViewer() { reset_buffers(); } + + bool init() { return init_shaders(); } + void generate(const GCodeProcessor::Result& gcode_result); + void render() const; + +private: + bool init_shaders(); + void reset_buffers(); +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // ENABLE_GCODE_VIEWER + +#endif // slic3r_GCodeViewer_hpp_ + diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 85ea44ef14..c9c4ab3364 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1678,6 +1678,14 @@ bool GLCanvas3D::init() return false; } +#if ENABLE_GCODE_VIEWER + if (!m_main_toolbar.is_enabled()) + { + if (!m_gcode_viewer.init()) + return false; + } +#endif // ENABLE_GCODE_VIEWER + // on linux the gl context is not valid until the canvas is not shown on screen // we defer the geometry finalization of volumes until the first call to render() m_volumes.finalize_geometry(true); @@ -2109,6 +2117,9 @@ void GLCanvas3D::render() _render_background(); _render_objects(); +#if ENABLE_GCODE_VIEWER + _render_gcode(); +#endif // ENABLE_GCODE_VIEWER _render_sla_slices(); _render_selection(); #if ENABLE_NON_STATIC_CANVAS_MANAGER @@ -2783,6 +2794,8 @@ void GLCanvas3D::load_gcode_preview_2(const GCodeProcessor::Result& gcode_result out << v.to_string() << "\n"; } out.close(); + + m_gcode_viewer.generate(gcode_result); #endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT } #endif // ENABLE_GCODE_VIEWER @@ -5440,6 +5453,13 @@ void GLCanvas3D::_render_objects() const m_camera_clipping_plane = ClippingPlane::ClipsNothing(); } +#if ENABLE_GCODE_VIEWER +void GLCanvas3D::_render_gcode() const +{ + m_gcode_viewer.render(); +} +#endif // ENABLE_GCODE_VIEWER + void GLCanvas3D::_render_selection() const { float scale_factor = 1.0; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 0c82f058fe..8c6b3c3f0c 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -21,6 +21,7 @@ #endif // !ENABLE_NON_STATIC_CANVAS_MANAGER #if ENABLE_GCODE_VIEWER #include "libslic3r/GCode/GCodeProcessor.hpp" +#include "GCodeViewer.hpp" #endif // ENABLE_GCODE_VIEWER #include @@ -468,6 +469,10 @@ private: bool m_extra_frame_requested; mutable GLVolumeCollection m_volumes; +#if ENABLE_GCODE_VIEWER + GCodeViewer m_gcode_viewer; +#endif // ENABLE_GCODE_VIEWER + Selection m_selection; const DynamicPrintConfig* m_config; Model* m_model; @@ -764,6 +769,9 @@ private: void _render_background() const; void _render_bed(float theta, bool show_axes) const; void _render_objects() const; +#if ENABLE_GCODE_VIEWER + void _render_gcode() const; +#endif // ENABLE_GCODE_VIEWER void _render_selection() const; #if ENABLE_RENDER_SELECTION_CENTER void _render_selection_center() const; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 9795095626..0171dd597a 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -946,9 +946,10 @@ void Preview::load_print_as_fff(bool keep_z_range) m_canvas->set_selected_extruder(0); if (gcode_preview_data_valid) { // Load the real G-code preview. - m_canvas->load_gcode_preview(*m_gcode_preview_data, colors); #if ENABLE_GCODE_VIEWER m_canvas->load_gcode_preview_2(*m_gcode_result); +#else + m_canvas->load_gcode_preview(*m_gcode_preview_data, colors); #endif // ENABLE_GCODE_VIEWER m_loaded = true; } else { From bc05ab985c278af18590730f7c253caa332c618b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 14 Apr 2020 16:40:08 +0200 Subject: [PATCH 015/503] GCodeViewer -> Toggle visibility of travel paths, retractions and uretractions --- resources/shaders/retractions.vs | 2 + resources/shaders/toolchanges.vs | 2 + resources/shaders/unretractions.vs | 2 + src/slic3r/GUI/GCodeViewer.cpp | 121 ++++++++++++++++++++++------- src/slic3r/GUI/GCodeViewer.hpp | 36 ++++----- src/slic3r/GUI/GLCanvas3D.cpp | 34 ++++++-- src/slic3r/GUI/GLCanvas3D.hpp | 9 +++ src/slic3r/GUI/GUI_Preview.cpp | 20 +++++ 8 files changed, 173 insertions(+), 53 deletions(-) diff --git a/resources/shaders/retractions.vs b/resources/shaders/retractions.vs index d97adbabe0..ba18073356 100644 --- a/resources/shaders/retractions.vs +++ b/resources/shaders/retractions.vs @@ -12,4 +12,6 @@ void main() // eye_normal = gl_NormalMatrix * gl_Normal; // world_normal_z = gl_Normal.z; gl_Position = ftransform(); + + gl_PointSize = 3.0; } diff --git a/resources/shaders/toolchanges.vs b/resources/shaders/toolchanges.vs index d97adbabe0..ba18073356 100644 --- a/resources/shaders/toolchanges.vs +++ b/resources/shaders/toolchanges.vs @@ -12,4 +12,6 @@ void main() // eye_normal = gl_NormalMatrix * gl_Normal; // world_normal_z = gl_Normal.z; gl_Position = ftransform(); + + gl_PointSize = 3.0; } diff --git a/resources/shaders/unretractions.vs b/resources/shaders/unretractions.vs index d97adbabe0..ba18073356 100644 --- a/resources/shaders/unretractions.vs +++ b/resources/shaders/unretractions.vs @@ -12,4 +12,6 @@ void main() // eye_normal = gl_NormalMatrix * gl_Normal; // world_normal_z = gl_Normal.z; gl_Position = ftransform(); + + gl_PointSize = 3.0; } diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 584ef06c32..00567eab6e 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -8,6 +8,8 @@ #include #include +#include +#include namespace Slic3r { namespace GUI { @@ -20,15 +22,27 @@ static GCodeProcessor::EMoveType buffer_type(unsigned char id) { return static_cast(static_cast(GCodeProcessor::EMoveType::Retract) + id); } +void GCodeViewer::Buffer::reset() +{ + // release gpu memory + if (vbo_id > 0) + glsafe(::glDeleteBuffers(1, &vbo_id)); + + // release cpu memory + data = std::vector(); +} + void GCodeViewer::generate(const GCodeProcessor::Result& gcode_result) { if (m_last_result_id == gcode_result.id) return; + auto start_time = std::chrono::high_resolution_clock::now(); + m_last_result_id = gcode_result.id; // release gpu memory, if used - reset_buffers(); + reset(); // convert data size_t vertices_count = gcode_result.moves.size(); @@ -73,16 +87,49 @@ void GCodeViewer::generate(const GCodeProcessor::Result& gcode_result) continue; } } + if (curr.type == GCodeProcessor::EMoveType::Extrude) + m_layers_zs.emplace_back(curr.position[2]); } + std::sort(m_layers_zs.begin(), m_layers_zs.end()); + + // Replace intervals of layers with similar top positions with their average value. + int n = int(m_layers_zs.size()); + int k = 0; + for (int i = 0; i < n;) { + int j = i + 1; + double zmax = m_layers_zs[i] + EPSILON; + for (; j < n && m_layers_zs[j] <= zmax; ++j); + m_layers_zs[k++] = (j > i + 1) ? (0.5 * (m_layers_zs[i] + m_layers_zs[j - 1])) : m_layers_zs[i]; + i = j; + } + if (k < n) + m_layers_zs.erase(m_layers_zs.begin() + k, m_layers_zs.end()); + // send data to gpu for (Buffer& buffer : m_buffers) { - glsafe(::glGenBuffers(1, &buffer.vbo_id)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vbo_id)); - glsafe(::glBufferData(GL_ARRAY_BUFFER, buffer.data.size() * sizeof(float), buffer.data.data(), GL_STATIC_DRAW)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + if (buffer.data.size() > 0) + { + glsafe(::glGenBuffers(1, &buffer.vbo_id)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vbo_id)); + glsafe(::glBufferData(GL_ARRAY_BUFFER, buffer.data.size() * sizeof(float), buffer.data.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + } } + + auto end_time = std::chrono::high_resolution_clock::now(); + std::cout << "toolpaths generation time: " << std::chrono::duration_cast(end_time - start_time).count() << "ms \n"; +} + +void GCodeViewer::reset() +{ + for (Buffer& buffer : m_buffers) + { + buffer.reset(); + } + + m_layers_zs = std::vector(); } void GCodeViewer::render() const @@ -111,10 +158,12 @@ void GCodeViewer::render() const if (buffer.vbo_id == 0) continue; - const Shader& shader = m_shaders[i]; - if (shader.is_initialized()) + if (!buffer.visible) + continue; + + if (buffer.shader.is_initialized()) { - shader.start_using(); + buffer.shader.start_using(); GLint current_program_id; glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id)); @@ -122,32 +171,50 @@ void GCodeViewer::render() const GCodeProcessor::EMoveType type = buffer_type(i); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vbo_id)); - glsafe(::glVertexPointer(3, GL_FLOAT, Buffer::stride(type), (const void*)0)); + glsafe(::glVertexPointer(3, GL_FLOAT, Buffer::vertex_size_bytes(), (const void*)0)); glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); switch (type) { case GCodeProcessor::EMoveType::Tool_change: + { + std::array color = { 1.0f, 1.0f, 1.0f, 1.0f }; + set_color(current_program_id, color); + glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); + glsafe(::glDrawArrays(GL_POINTS, 0, (GLsizei)buffer.data.size() / Buffer::vertex_size())); + glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + break; + } case GCodeProcessor::EMoveType::Retract: + { + std::array color = { 1.0f, 0.0f, 1.0f, 1.0f }; + set_color(current_program_id, color); + glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); + glsafe(::glDrawArrays(GL_POINTS, 0, (GLsizei)buffer.data.size() / Buffer::vertex_size())); + glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + break; + } case GCodeProcessor::EMoveType::Unretract: { std::array color = { 0.0f, 1.0f, 0.0f, 1.0f }; set_color(current_program_id, color); - glsafe(::glDrawArrays(GL_POINTS, 0, (GLsizei)buffer.data.size() / Buffer::record_size(type))); + glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); + glsafe(::glDrawArrays(GL_POINTS, 0, (GLsizei)buffer.data.size() / Buffer::vertex_size())); + glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); break; } case GCodeProcessor::EMoveType::Extrude: { std::array color = { 1.0f, 0.0f, 0.0f, 1.0f }; set_color(current_program_id, color); - glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)buffer.data.size() / Buffer::record_size(type))); + glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)buffer.data.size() / Buffer::vertex_size())); break; } case GCodeProcessor::EMoveType::Travel: { std::array color = { 1.0f, 1.0f, 0.0f, 1.0f }; set_color(current_program_id, color); - glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)buffer.data.size() / Buffer::record_size(type))); + glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)buffer.data.size() / Buffer::vertex_size())); break; } default: @@ -159,11 +226,24 @@ void GCodeViewer::render() const glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - shader.stop_using(); + buffer.shader.stop_using(); } } } +bool GCodeViewer::is_toolpath_visible(GCodeProcessor::EMoveType type) const +{ + size_t id = static_cast(buffer_id(type)); + return (id < m_buffers.size()) ? m_buffers[id].visible : false; +} + +void GCodeViewer::set_toolpath_visible(GCodeProcessor::EMoveType type, bool visible) +{ + size_t id = static_cast(buffer_id(type)); + if (id < m_buffers.size()) + m_buffers[id].visible = visible; +} + bool GCodeViewer::init_shaders() { unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); @@ -171,7 +251,7 @@ bool GCodeViewer::init_shaders() for (unsigned char i = begin_id; i < end_id; ++i) { - Shader& shader = m_shaders[i]; + Shader& shader = m_buffers[i].shader; std::string vertex_shader_src; std::string fragment_shader_src; GCodeProcessor::EMoveType type = buffer_type(i); @@ -223,19 +303,6 @@ bool GCodeViewer::init_shaders() return true; } -void GCodeViewer::reset_buffers() -{ - for (Buffer& buffer : m_buffers) - { - // release gpu memory - if (buffer.vbo_id > 0) - glsafe(::glDeleteBuffers(1, &buffer.vbo_id)); - - // release cpu memory - buffer.data = std::vector(); - } -} - } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 95250b2034..0ed5c337c4 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -16,42 +16,38 @@ class GCodeViewer struct Buffer { unsigned int vbo_id{ 0 }; + Shader shader; std::vector data; + bool visible{ false }; - static size_t stride(GCodeProcessor::EMoveType type) - { - return 3 * sizeof(float); - } + void reset(); - static size_t record_size(GCodeProcessor::EMoveType type) - { - switch (type) - { - case GCodeProcessor::EMoveType::Tool_change: - case GCodeProcessor::EMoveType::Retract: - case GCodeProcessor::EMoveType::Unretract: { return 3; } - case GCodeProcessor::EMoveType::Extrude: - case GCodeProcessor::EMoveType::Travel: { return 6; } - default: { return 0; } - } - } + static size_t vertex_size() { return 3; } + + static size_t vertex_size_bytes() { return vertex_size() * sizeof(float); } }; std::vector m_buffers{ static_cast(GCodeProcessor::EMoveType::Extrude) }; - std::vector m_shaders{ static_cast(GCodeProcessor::EMoveType::Extrude) }; + unsigned int m_last_result_id{ 0 }; + std::vector m_layers_zs; public: GCodeViewer() = default; - ~GCodeViewer() { reset_buffers(); } + ~GCodeViewer() { reset(); } - bool init() { return init_shaders(); } + bool init() { set_toolpath_visible(GCodeProcessor::EMoveType::Extrude, true); return init_shaders(); } void generate(const GCodeProcessor::Result& gcode_result); + void reset(); void render() const; + const std::vector& get_layers_zs() const { return m_layers_zs; }; + + bool is_toolpath_visible(GCodeProcessor::EMoveType type) const; + void set_toolpath_visible(GCodeProcessor::EMoveType type, bool visible); + private: bool init_shaders(); - void reset_buffers(); }; } // namespace GUI diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index c9c4ab3364..66120668c6 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -932,7 +932,11 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items( const GLCanvas3D std::vector> cp_values; cp_values.reserve(custom_gcode_per_print_z.size()); +#if ENABLE_GCODE_VIEWER + const std::vector& print_zs = canvas.get_layers_zs(); +#else std::vector print_zs = canvas.get_current_print_zs(true); +#endif // ENABLE_GCODE_VIEWER for (auto custom_code : custom_gcode_per_print_z) { if (custom_code.gcode != ColorChangeCode) @@ -2303,10 +2307,23 @@ void GLCanvas3D::ensure_on_bed(unsigned int object_idx) } } + +#if ENABLE_GCODE_VIEWER +const std::vector& GLCanvas3D::get_layers_zs() const +{ + return m_gcode_viewer.get_layers_zs(); +} + +void GLCanvas3D::set_toolpath_visible(GCodeProcessor::EMoveType type, bool visible) +{ + m_gcode_viewer.set_toolpath_visible(type, visible); +} +#else std::vector GLCanvas3D::get_current_print_zs(bool active_only) const { return m_volumes.get_current_print_zs(active_only); } +#endif // ENABLE_GCODE_VIEWER void GLCanvas3D::set_toolpaths_range(double low, double high) { @@ -2786,14 +2803,19 @@ static void load_gcode_retractions(const GCodePreviewData::Retraction& retractio void GLCanvas3D::load_gcode_preview_2(const GCodeProcessor::Result& gcode_result) { #if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - boost::filesystem::path path("d:/processor.output"); - boost::nowide::ofstream out; - out.open(path.string()); - for (const GCodeProcessor::MoveVertex& v : gcode_result.moves) + static unsigned int last_result_id = 0; + if (last_result_id != gcode_result.id) { - out << v.to_string() << "\n"; + last_result_id = gcode_result.id; + boost::filesystem::path path("d:/processor.output"); + boost::nowide::ofstream out; + out.open(path.string()); + for (const GCodeProcessor::MoveVertex& v : gcode_result.moves) + { + out << v.to_string() << "\n"; + } + out.close(); } - out.close(); m_gcode_viewer.generate(gcode_result); #endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 8c6b3c3f0c..592c85c03f 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -549,6 +549,10 @@ public: void reset_volumes(); int check_volumes_outside_state() const; +#if ENABLE_GCODE_VIEWER + void reset_gcode_toolpaths() { m_gcode_viewer.reset(); } +#endif // ENABLE_GCODE_VIEWER + void toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1); void toggle_model_objects_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1); void update_instance_printable_state_for_object(size_t obj_idx); @@ -635,7 +639,12 @@ public: void delete_selected(); void ensure_on_bed(unsigned int object_idx); +#if ENABLE_GCODE_VIEWER + const std::vector& get_layers_zs() const; + void set_toolpath_visible(GCodeProcessor::EMoveType type, bool visible); +#else std::vector get_current_print_zs(bool active_only) const; +#endif // ENABLE_GCODE_VIEWER void set_toolpaths_range(double low, double high); std::vector load_object(const ModelObject& model_object, int obj_idx, std::vector instance_idxs); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 0171dd597a..2e0c71268c 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -474,6 +474,9 @@ void Preview::reload_print(bool keep_volumes) !keep_volumes) { m_canvas->reset_volumes(); +#if ENABLE_GCODE_VIEWER + m_canvas->reset_gcode_toolpaths(); +#endif // ENABLE_GCODE_VIEWER m_canvas->reset_legend_texture(); m_loaded = false; #ifdef __linux__ @@ -614,21 +617,34 @@ void Preview::on_combochecklist_features(wxCommandEvent& evt) void Preview::on_checkbox_travel(wxCommandEvent& evt) { +#if ENABLE_GCODE_VIEWER + m_canvas->set_toolpath_visible(GCodeProcessor::EMoveType::Travel, m_checkbox_travel->IsChecked()); + refresh_print(); +#else m_gcode_preview_data->travel.is_visible = m_checkbox_travel->IsChecked(); m_gcode_preview_data->ranges.feedrate.set_mode(GCodePreviewData::FeedrateKind::TRAVEL, m_gcode_preview_data->travel.is_visible); // Rather than refresh, reload print so that speed color ranges get recomputed (affected by travel visibility) reload_print(); +#endif // ENABLE_GCODE_VIEWER } void Preview::on_checkbox_retractions(wxCommandEvent& evt) { +#if ENABLE_GCODE_VIEWER + m_canvas->set_toolpath_visible(GCodeProcessor::EMoveType::Retract, m_checkbox_retractions->IsChecked()); +#else m_gcode_preview_data->retraction.is_visible = m_checkbox_retractions->IsChecked(); +#endif // ENABLE_GCODE_VIEWER refresh_print(); } void Preview::on_checkbox_unretractions(wxCommandEvent& evt) { +#if ENABLE_GCODE_VIEWER + m_canvas->set_toolpath_visible(GCodeProcessor::EMoveType::Unretract, m_checkbox_unretractions->IsChecked()); +#else m_gcode_preview_data->unretraction.is_visible = m_checkbox_unretractions->IsChecked(); +#endif // ENABLE_GCODE_VIEWER refresh_print(); } @@ -958,7 +974,11 @@ void Preview::load_print_as_fff(bool keep_z_range) } show_hide_ui_elements(gcode_preview_data_valid ? "full" : "simple"); // recalculates zs and update sliders accordingly +#if ENABLE_GCODE_VIEWER + const std::vector& zs = m_canvas->get_layers_zs(); +#else std::vector zs = m_canvas->get_current_print_zs(true); +#endif // ENABLE_GCODE_VIEWER if (zs.empty()) { // all layers filtered out reset_sliders(true); From cc774dece7fa902d674353cf051cdb1b4abdc2c5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 15 Apr 2020 14:31:39 +0200 Subject: [PATCH 016/503] GCodeViewer -> Toggle visibility of shells --- resources/shaders/shells.fs | 13 + resources/shaders/shells.vs | 42 ++++ src/slic3r/GUI/GCodeViewer.cpp | 440 ++++++++++++++++++++------------- src/slic3r/GUI/GCodeViewer.hpp | 21 +- src/slic3r/GUI/GLCanvas3D.cpp | 16 +- src/slic3r/GUI/GLCanvas3D.hpp | 7 +- src/slic3r/GUI/GUI_Preview.cpp | 4 + 7 files changed, 363 insertions(+), 180 deletions(-) create mode 100644 resources/shaders/shells.fs create mode 100644 resources/shaders/shells.vs diff --git a/resources/shaders/shells.fs b/resources/shaders/shells.fs new file mode 100644 index 0000000000..0c3388df70 --- /dev/null +++ b/resources/shaders/shells.fs @@ -0,0 +1,13 @@ +#version 110 + +const vec3 ZERO = vec3(0.0, 0.0, 0.0); + +uniform vec4 uniform_color; + +// x = tainted, y = specular; +varying vec2 intensity; + +void main() +{ + gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); +} diff --git a/resources/shaders/shells.vs b/resources/shaders/shells.vs new file mode 100644 index 0000000000..bb9c144e65 --- /dev/null +++ b/resources/shaders/shells.vs @@ -0,0 +1,42 @@ +#version 110 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +// x = tainted, y = specular; +varying vec2 intensity; + +void main() +{ + // First transform the normal into camera space and normalize the result. + vec3 normal = normalize(gl_NormalMatrix * gl_Normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + intensity.y = 0.0; + + if (NdotL > 0.0) + { + vec3 position = (gl_ModelViewMatrix * gl_Vertex).xyz; + intensity.y += LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + } + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + intensity.x += max(dot(normal, LIGHT_FRONT_DIR), 0.0) * LIGHT_FRONT_DIFFUSE; + + gl_Position = ftransform(); +} diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 00567eab6e..155c7d765d 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1,8 +1,11 @@ #include "libslic3r/libslic3r.h" #include "GCodeViewer.hpp" -#include "3DScene.hpp" #if ENABLE_GCODE_VIEWER +#include "libslic3r/Print.hpp" +#include "GUI_App.hpp" +#include "PresetBundle.hpp" +#include "Camera.hpp" #include #include @@ -30,96 +33,21 @@ void GCodeViewer::Buffer::reset() // release cpu memory data = std::vector(); + data_size = 0; } -void GCodeViewer::generate(const GCodeProcessor::Result& gcode_result) +void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized) { if (m_last_result_id == gcode_result.id) return; - auto start_time = std::chrono::high_resolution_clock::now(); - m_last_result_id = gcode_result.id; // release gpu memory, if used reset(); - // convert data - size_t vertices_count = gcode_result.moves.size(); - for (size_t i = 0; i < vertices_count; ++i) - { - // skip first vertex - if (i == 0) - continue; - - const GCodeProcessor::MoveVertex& prev = gcode_result.moves[i - 1]; - const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i]; - - Buffer& buffer = m_buffers[buffer_id(curr.type)]; - - switch (curr.type) - { - case GCodeProcessor::EMoveType::Tool_change: - case GCodeProcessor::EMoveType::Retract: - case GCodeProcessor::EMoveType::Unretract: - { - for (int j = 0; j < 3; ++j) - { - buffer.data.insert(buffer.data.end(), curr.position[j]); - } - break; - } - case GCodeProcessor::EMoveType::Extrude: - case GCodeProcessor::EMoveType::Travel: - { - for (int j = 0; j < 3; ++j) - { - buffer.data.insert(buffer.data.end(), prev.position[j]); - } - for (int j = 0; j < 3; ++j) - { - buffer.data.insert(buffer.data.end(), curr.position[j]); - } - break; - } - default: - { - continue; - } - } - if (curr.type == GCodeProcessor::EMoveType::Extrude) - m_layers_zs.emplace_back(curr.position[2]); - } - - std::sort(m_layers_zs.begin(), m_layers_zs.end()); - - // Replace intervals of layers with similar top positions with their average value. - int n = int(m_layers_zs.size()); - int k = 0; - for (int i = 0; i < n;) { - int j = i + 1; - double zmax = m_layers_zs[i] + EPSILON; - for (; j < n && m_layers_zs[j] <= zmax; ++j); - m_layers_zs[k++] = (j > i + 1) ? (0.5 * (m_layers_zs[i] + m_layers_zs[j - 1])) : m_layers_zs[i]; - i = j; - } - if (k < n) - m_layers_zs.erase(m_layers_zs.begin() + k, m_layers_zs.end()); - - // send data to gpu - for (Buffer& buffer : m_buffers) - { - if (buffer.data.size() > 0) - { - glsafe(::glGenBuffers(1, &buffer.vbo_id)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vbo_id)); - glsafe(::glBufferData(GL_ARRAY_BUFFER, buffer.data.size() * sizeof(float), buffer.data.data(), GL_STATIC_DRAW)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - } - } - - auto end_time = std::chrono::high_resolution_clock::now(); - std::cout << "toolpaths generation time: " << std::chrono::duration_cast(end_time - start_time).count() << "ms \n"; + load_toolpaths(gcode_result); + load_shells(print, initialized); } void GCodeViewer::reset() @@ -129,106 +57,15 @@ void GCodeViewer::reset() buffer.reset(); } + m_shells.volumes.clear(); m_layers_zs = std::vector(); } void GCodeViewer::render() const { - auto set_color = [](GLint current_program_id, const std::array& color) { - if (current_program_id > 0) - { - GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1; - if (color_id >= 0) - { - glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)color.data())); - return; - } - } - BOOST_LOG_TRIVIAL(error) << "Unable to find uniform_color uniform"; - }; - - unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); - unsigned char end_id = buffer_id(GCodeProcessor::EMoveType::Count); - glsafe(::glEnable(GL_DEPTH_TEST)); - - for (unsigned char i = begin_id; i < end_id; ++i) - { - const Buffer& buffer = m_buffers[i]; - if (buffer.vbo_id == 0) - continue; - - if (!buffer.visible) - continue; - - if (buffer.shader.is_initialized()) - { - buffer.shader.start_using(); - - GLint current_program_id; - glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id)); - - GCodeProcessor::EMoveType type = buffer_type(i); - - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vbo_id)); - glsafe(::glVertexPointer(3, GL_FLOAT, Buffer::vertex_size_bytes(), (const void*)0)); - glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); - - switch (type) - { - case GCodeProcessor::EMoveType::Tool_change: - { - std::array color = { 1.0f, 1.0f, 1.0f, 1.0f }; - set_color(current_program_id, color); - glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawArrays(GL_POINTS, 0, (GLsizei)buffer.data.size() / Buffer::vertex_size())); - glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); - break; - } - case GCodeProcessor::EMoveType::Retract: - { - std::array color = { 1.0f, 0.0f, 1.0f, 1.0f }; - set_color(current_program_id, color); - glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawArrays(GL_POINTS, 0, (GLsizei)buffer.data.size() / Buffer::vertex_size())); - glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); - break; - } - case GCodeProcessor::EMoveType::Unretract: - { - std::array color = { 0.0f, 1.0f, 0.0f, 1.0f }; - set_color(current_program_id, color); - glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawArrays(GL_POINTS, 0, (GLsizei)buffer.data.size() / Buffer::vertex_size())); - glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); - break; - } - case GCodeProcessor::EMoveType::Extrude: - { - std::array color = { 1.0f, 0.0f, 0.0f, 1.0f }; - set_color(current_program_id, color); - glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)buffer.data.size() / Buffer::vertex_size())); - break; - } - case GCodeProcessor::EMoveType::Travel: - { - std::array color = { 1.0f, 1.0f, 0.0f, 1.0f }; - set_color(current_program_id, color); - glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)buffer.data.size() / Buffer::vertex_size())); - break; - } - default: - { - break; - } - } - - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); - - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - buffer.shader.stop_using(); - } - } + render_toolpaths(); + render_shells(); } bool GCodeViewer::is_toolpath_visible(GCodeProcessor::EMoveType type) const @@ -300,9 +137,264 @@ bool GCodeViewer::init_shaders() } } + if (!m_shells.shader.init("shells.vs", "shells.fs")) + { + BOOST_LOG_TRIVIAL(error) << "Unable to initialize shells shader: please, check that the files shells.vs and shells.fs are available"; + return false; + } + return true; } +void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) +{ + auto start_time = std::chrono::high_resolution_clock::now(); + + // convert data + size_t vertices_count = gcode_result.moves.size(); + for (size_t i = 0; i < vertices_count; ++i) + { + // skip first vertex + if (i == 0) + continue; + + const GCodeProcessor::MoveVertex& prev = gcode_result.moves[i - 1]; + const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i]; + + Buffer& buffer = m_buffers[buffer_id(curr.type)]; + + switch (curr.type) + { + case GCodeProcessor::EMoveType::Tool_change: + case GCodeProcessor::EMoveType::Retract: + case GCodeProcessor::EMoveType::Unretract: + { + for (int j = 0; j < 3; ++j) + { + buffer.data.insert(buffer.data.end(), curr.position[j]); + } + break; + } + case GCodeProcessor::EMoveType::Extrude: + case GCodeProcessor::EMoveType::Travel: + { + for (int j = 0; j < 3; ++j) + { + buffer.data.insert(buffer.data.end(), prev.position[j]); + } + for (int j = 0; j < 3; ++j) + { + buffer.data.insert(buffer.data.end(), curr.position[j]); + } + break; + } + default: + { + continue; + } + } + + if (curr.type == GCodeProcessor::EMoveType::Extrude) + m_layers_zs.emplace_back(curr.position[2]); + } + + std::sort(m_layers_zs.begin(), m_layers_zs.end()); + + // Replace intervals of layers with similar top positions with their average value. + int n = int(m_layers_zs.size()); + int k = 0; + for (int i = 0; i < n;) { + int j = i + 1; + double zmax = m_layers_zs[i] + EPSILON; + for (; j < n && m_layers_zs[j] <= zmax; ++j); + m_layers_zs[k++] = (j > i + 1) ? (0.5 * (m_layers_zs[i] + m_layers_zs[j - 1])) : m_layers_zs[i]; + i = j; + } + if (k < n) + m_layers_zs.erase(m_layers_zs.begin() + k, m_layers_zs.end()); + + // send data to gpu + for (Buffer& buffer : m_buffers) + { + buffer.data_size = buffer.data.size(); + if (buffer.data_size > 0) + { + glsafe(::glGenBuffers(1, &buffer.vbo_id)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vbo_id)); + glsafe(::glBufferData(GL_ARRAY_BUFFER, buffer.data_size * sizeof(float), buffer.data.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + buffer.data = std::vector(); + } + } + + auto end_time = std::chrono::high_resolution_clock::now(); + std::cout << "toolpaths generation time: " << std::chrono::duration_cast(end_time - start_time).count() << "ms \n"; +} + +void GCodeViewer::load_shells(const Print& print, bool initialized) +{ + if (print.objects().empty()) + // no shells, return + return; + + // adds objects' volumes + int object_id = 0; + for (const PrintObject* obj : print.objects()) + { + const ModelObject* model_obj = obj->model_object(); + + std::vector instance_ids(model_obj->instances.size()); + for (int i = 0; i < (int)model_obj->instances.size(); ++i) + { + instance_ids[i] = i; + } + + m_shells.volumes.load_object(model_obj, object_id, instance_ids, "object", initialized); + + ++object_id; + } + + if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF) { + // adds wipe tower's volume + double max_z = print.objects()[0]->model_object()->get_model()->bounding_box().max(2); + const PrintConfig& config = print.config(); + size_t extruders_count = config.nozzle_diameter.size(); + if ((extruders_count > 1) && config.wipe_tower && !config.complete_objects) { + + const DynamicPrintConfig& print_config = wxGetApp().preset_bundle->prints.get_edited_preset().config; + double layer_height = print_config.opt_float("layer_height"); + double first_layer_height = print_config.get_abs_value("first_layer_height", layer_height); + double nozzle_diameter = print.config().nozzle_diameter.values[0]; + float depth = print.wipe_tower_data(extruders_count, first_layer_height, nozzle_diameter).depth; + float brim_width = print.wipe_tower_data(extruders_count, first_layer_height, nozzle_diameter).brim_width; + + m_shells.volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle, + !print.is_step_done(psWipeTower), brim_width, initialized); + } + } + + for (GLVolume* volume : m_shells.volumes.volumes) + { + volume->zoom_to_volumes = false; + volume->color[3] = 0.25f; + volume->force_native_color = true; + volume->set_render_color(); + } +} + +void GCodeViewer::render_toolpaths() const +{ + auto set_color = [](GLint current_program_id, const std::array& color) { + if (current_program_id > 0) + { + GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1; + if (color_id >= 0) + { + glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)color.data())); + return; + } + } + BOOST_LOG_TRIVIAL(error) << "Unable to find uniform_color uniform"; + }; + + glsafe(::glCullFace(GL_BACK)); + + unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); + unsigned char end_id = buffer_id(GCodeProcessor::EMoveType::Count); + + for (unsigned char i = begin_id; i < end_id; ++i) + { + const Buffer& buffer = m_buffers[i]; + if (buffer.vbo_id == 0) + continue; + + if (!buffer.visible) + continue; + + if (buffer.shader.is_initialized()) + { + buffer.shader.start_using(); + + GLint current_program_id; + glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id)); + + GCodeProcessor::EMoveType type = buffer_type(i); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vbo_id)); + glsafe(::glVertexPointer(3, GL_FLOAT, Buffer::vertex_size_bytes(), (const void*)0)); + glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + + switch (type) + { + case GCodeProcessor::EMoveType::Tool_change: + { + std::array color = { 1.0f, 1.0f, 1.0f, 1.0f }; + set_color(current_program_id, color); + glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); + glsafe(::glDrawArrays(GL_POINTS, 0, (GLsizei)(buffer.data_size / Buffer::vertex_size()))); + glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + break; + } + case GCodeProcessor::EMoveType::Retract: + { + std::array color = { 1.0f, 0.0f, 1.0f, 1.0f }; + set_color(current_program_id, color); + glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); + glsafe(::glDrawArrays(GL_POINTS, 0, (GLsizei)(buffer.data_size / Buffer::vertex_size()))); + glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + break; + } + case GCodeProcessor::EMoveType::Unretract: + { + std::array color = { 0.0f, 1.0f, 0.0f, 1.0f }; + set_color(current_program_id, color); + glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); + glsafe(::glDrawArrays(GL_POINTS, 0, (GLsizei)(buffer.data_size / Buffer::vertex_size()))); + glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + break; + } + case GCodeProcessor::EMoveType::Extrude: + { + std::array color = { 1.0f, 0.0f, 0.0f, 1.0f }; + set_color(current_program_id, color); + glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)(buffer.data_size / Buffer::vertex_size()))); + break; + } + case GCodeProcessor::EMoveType::Travel: + { + std::array color = { 1.0f, 1.0f, 0.0f, 1.0f }; + set_color(current_program_id, color); + glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)(buffer.data_size / Buffer::vertex_size()))); + break; + } + default: + { + break; + } + } + + glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + buffer.shader.stop_using(); + } + } +} + +void GCodeViewer::render_shells() const +{ + if (!m_shells.visible || m_shells.volumes.empty() || !m_shells.shader.is_initialized()) + return; + +// glsafe(::glDepthMask(GL_FALSE)); + + m_shells.shader.start_using(); + m_shells.volumes.render(GLVolumeCollection::Transparent, true, wxGetApp().plater()->get_camera().get_view_matrix()); + m_shells.shader.stop_using(); + +// glsafe(::glDepthMask(GL_TRUE)); +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 0ed5c337c4..92929cffe9 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -4,11 +4,13 @@ #if ENABLE_GCODE_VIEWER #include "GLShader.hpp" +#include "3DScene.hpp" #include "libslic3r/GCode/GCodeProcessor.hpp" #include namespace Slic3r { +class Print; namespace GUI { class GCodeViewer @@ -18,26 +20,34 @@ class GCodeViewer unsigned int vbo_id{ 0 }; Shader shader; std::vector data; + size_t data_size{ 0 }; bool visible{ false }; void reset(); static size_t vertex_size() { return 3; } - static size_t vertex_size_bytes() { return vertex_size() * sizeof(float); } }; + struct Shells + { + GLVolumeCollection volumes; + bool visible{ false }; + Shader shader; + }; + std::vector m_buffers{ static_cast(GCodeProcessor::EMoveType::Extrude) }; unsigned int m_last_result_id{ 0 }; std::vector m_layers_zs; + Shells m_shells; public: GCodeViewer() = default; ~GCodeViewer() { reset(); } bool init() { set_toolpath_visible(GCodeProcessor::EMoveType::Extrude, true); return init_shaders(); } - void generate(const GCodeProcessor::Result& gcode_result); + void load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized); void reset(); void render() const; @@ -46,8 +56,15 @@ public: bool is_toolpath_visible(GCodeProcessor::EMoveType type) const; void set_toolpath_visible(GCodeProcessor::EMoveType type, bool visible); + bool are_shells_visible() const { return m_shells.visible; } + void set_shells_visible(bool visible) { m_shells.visible = visible; } + private: bool init_shaders(); + void load_toolpaths(const GCodeProcessor::Result& gcode_result); + void load_shells(const Print& print, bool initialized); + void render_toolpaths() const; + void render_shells() const; }; } // namespace GUI diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 66120668c6..4b8ca69d21 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2122,7 +2122,8 @@ void GLCanvas3D::render() _render_objects(); #if ENABLE_GCODE_VIEWER - _render_gcode(); + if (!m_main_toolbar.is_enabled()) + _render_gcode(); #endif // ENABLE_GCODE_VIEWER _render_sla_slices(); _render_selection(); @@ -2318,6 +2319,11 @@ void GLCanvas3D::set_toolpath_visible(GCodeProcessor::EMoveType type, bool visib { m_gcode_viewer.set_toolpath_visible(type, visible); } + +void GLCanvas3D::set_shells_visible(bool visible) +{ + m_gcode_viewer.set_shells_visible(visible); +} #else std::vector GLCanvas3D::get_current_print_zs(bool active_only) const { @@ -2768,6 +2774,7 @@ static void reserve_new_volume_finalize_old_volume(GLVolume& vol_new, GLVolume& vol_old.finalize_geometry(gl_initialized); } +#if !ENABLE_GCODE_VIEWER static void load_gcode_retractions(const GCodePreviewData::Retraction& retractions, GLCanvas3D::GCodePreviewVolumeIndex::EType extrusion_type, GLVolumeCollection &volumes, GLCanvas3D::GCodePreviewVolumeIndex &volume_index, bool gl_initialized) { // nothing to render, return @@ -2798,6 +2805,7 @@ static void load_gcode_retractions(const GCodePreviewData::Retraction& retractio } volume->indexed_vertex_array.finalize_geometry(gl_initialized); } +#endif // !ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER void GLCanvas3D::load_gcode_preview_2(const GCodeProcessor::Result& gcode_result) @@ -2817,11 +2825,12 @@ void GLCanvas3D::load_gcode_preview_2(const GCodeProcessor::Result& gcode_result out.close(); } - m_gcode_viewer.generate(gcode_result); + m_gcode_viewer.load(gcode_result , *this->fff_print(), m_initialized); #endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT } #endif // ENABLE_GCODE_VIEWER +#if !ENABLE_GCODE_VIEWER void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const std::vector& str_tool_colors) { const Print *print = this->fff_print(); @@ -2890,6 +2899,7 @@ void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const _generate_legend_texture(preview_data, tool_colors); } } +#endif // !ENABLE_GCODE_VIEWER void GLCanvas3D::load_sla_preview() { @@ -6547,6 +6557,7 @@ static inline int hex_digit_to_int(const char c) (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; } +#if !ENABLE_GCODE_VIEWER void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector& tool_colors) { BOOST_LOG_TRIVIAL(debug) << "Loading G-code extrusion paths - start" << m_volumes.log_memory_info() << log_memory_info(); @@ -6866,6 +6877,7 @@ void GLCanvas3D::_load_fff_shells() } } } +#endif // !ENABLE_GCODE_VIEWER // While it looks like we can call // this->reload_scene(true, true) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 592c85c03f..7033f05a0b 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -642,6 +642,7 @@ public: #if ENABLE_GCODE_VIEWER const std::vector& get_layers_zs() const; void set_toolpath_visible(GCodeProcessor::EMoveType type, bool visible); + void set_shells_visible(bool visible); #else std::vector get_current_print_zs(bool active_only) const; #endif // ENABLE_GCODE_VIEWER @@ -656,9 +657,9 @@ public: #if ENABLE_GCODE_VIEWER void load_gcode_preview_2(const GCodeProcessor::Result& gcode_result); -#endif // ENABLE_GCODE_VIEWER - +#else void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector& str_tool_colors); +#endif // ENABLE_GCODE_VIEWER void load_sla_preview(); void load_preview(const std::vector& str_tool_colors, const std::vector& color_print_values); void bind_event_handlers(); @@ -833,12 +834,14 @@ private: // Create 3D thick extrusion lines for wipe tower extrusions void _load_wipe_tower_toolpaths(const std::vector& str_tool_colors); +#if !ENABLE_GCODE_VIEWER // generates gcode extrusion paths geometry void _load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector& tool_colors); // generates gcode travel paths geometry void _load_gcode_travel_paths(const GCodePreviewData& preview_data, const std::vector& tool_colors); // generates objects and wipe tower geometry void _load_fff_shells(); +#endif // !ENABLE_GCODE_VIEWER // Load SLA objects and support structures for objects, for which the slaposSliceSupports step has been finished. void _load_sla_shells(); // sets gcode geometry visibility according to user selection diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 2e0c71268c..0c4f655c58 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -650,7 +650,11 @@ void Preview::on_checkbox_unretractions(wxCommandEvent& evt) void Preview::on_checkbox_shells(wxCommandEvent& evt) { +#if ENABLE_GCODE_VIEWER + m_canvas->set_shells_visible(m_checkbox_shells->IsChecked()); +#else m_gcode_preview_data->shell.is_visible = m_checkbox_shells->IsChecked(); +#endif // ENABLE_GCODE_VIEWER refresh_print(); } From 61ab7bbebfaabc21a5a6a08750c8230c9a9bbb39 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 15 Apr 2020 16:29:11 +0200 Subject: [PATCH 017/503] GCodeViewer -> Basic indexed rendering --- resources/shaders/retractions.vs | 2 +- resources/shaders/toolchanges.vs | 2 +- resources/shaders/unretractions.vs | 2 +- src/slic3r/GUI/GCodeViewer.cpp | 153 +++++++++++++++++------------ src/slic3r/GUI/GCodeViewer.hpp | 25 +++-- 5 files changed, 113 insertions(+), 71 deletions(-) diff --git a/resources/shaders/retractions.vs b/resources/shaders/retractions.vs index ba18073356..2cf5ca2dd0 100644 --- a/resources/shaders/retractions.vs +++ b/resources/shaders/retractions.vs @@ -13,5 +13,5 @@ void main() // world_normal_z = gl_Normal.z; gl_Position = ftransform(); - gl_PointSize = 3.0; + gl_PointSize = 5.0; } diff --git a/resources/shaders/toolchanges.vs b/resources/shaders/toolchanges.vs index ba18073356..2cf5ca2dd0 100644 --- a/resources/shaders/toolchanges.vs +++ b/resources/shaders/toolchanges.vs @@ -13,5 +13,5 @@ void main() // world_normal_z = gl_Normal.z; gl_Position = ftransform(); - gl_PointSize = 3.0; + gl_PointSize = 5.0; } diff --git a/resources/shaders/unretractions.vs b/resources/shaders/unretractions.vs index ba18073356..2cf5ca2dd0 100644 --- a/resources/shaders/unretractions.vs +++ b/resources/shaders/unretractions.vs @@ -13,5 +13,5 @@ void main() // world_normal_z = gl_Normal.z; gl_Position = ftransform(); - gl_PointSize = 3.0; + gl_PointSize = 5.0; } diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 155c7d765d..963f8d2269 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -25,14 +25,23 @@ static GCodeProcessor::EMoveType buffer_type(unsigned char id) { return static_cast(static_cast(GCodeProcessor::EMoveType::Retract) + id); } -void GCodeViewer::Buffer::reset() +void GCodeViewer::VBuffer::reset() { // release gpu memory if (vbo_id > 0) glsafe(::glDeleteBuffers(1, &vbo_id)); + vertices_count = 0; +} + +void GCodeViewer::IBuffer::reset() +{ + // release gpu memory + if (ibo_id > 0) + glsafe(::glDeleteBuffers(1, &ibo_id)); + // release cpu memory - data = std::vector(); + data = std::vector(); data_size = 0; } @@ -52,7 +61,9 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& void GCodeViewer::reset() { - for (Buffer& buffer : m_buffers) + m_vertices.reset(); + + for (IBuffer& buffer : m_buffers) { buffer.reset(); } @@ -150,9 +161,32 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) { auto start_time = std::chrono::high_resolution_clock::now(); - // convert data - size_t vertices_count = gcode_result.moves.size(); - for (size_t i = 0; i < vertices_count; ++i) + // vertex data + m_vertices.vertices_count = gcode_result.moves.size(); + if (m_vertices.vertices_count == 0) + return; + + // vertex data -> extract from result + std::vector vertices_data; + for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) + { + for (int j = 0; j < 3; ++j) + { + vertices_data.insert(vertices_data.end(), move.position[j]); + } + } + + // vertex data -> send to gpu + glsafe(::glGenBuffers(1, &m_vertices.vbo_id)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vertices.vbo_id)); + glsafe(::glBufferData(GL_ARRAY_BUFFER, vertices_data.size() * sizeof(float), vertices_data.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + + // vertex data -> free ram + vertices_data = std::vector(); + + // indices data -> extract from result + for (size_t i = 0; i < m_vertices.vertices_count; ++i) { // skip first vertex if (i == 0) @@ -161,7 +195,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) const GCodeProcessor::MoveVertex& prev = gcode_result.moves[i - 1]; const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i]; - Buffer& buffer = m_buffers[buffer_id(curr.type)]; + IBuffer& buffer = m_buffers[buffer_id(curr.type)]; switch (curr.type) { @@ -169,23 +203,14 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) case GCodeProcessor::EMoveType::Retract: case GCodeProcessor::EMoveType::Unretract: { - for (int j = 0; j < 3; ++j) - { - buffer.data.insert(buffer.data.end(), curr.position[j]); - } + buffer.data.push_back(static_cast(i)); break; } case GCodeProcessor::EMoveType::Extrude: case GCodeProcessor::EMoveType::Travel: { - for (int j = 0; j < 3; ++j) - { - buffer.data.insert(buffer.data.end(), prev.position[j]); - } - for (int j = 0; j < 3; ++j) - { - buffer.data.insert(buffer.data.end(), curr.position[j]); - } + buffer.data.push_back(static_cast(i - 1)); + buffer.data.push_back(static_cast(i)); break; } default: @@ -193,14 +218,35 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) continue; } } - - if (curr.type == GCodeProcessor::EMoveType::Extrude) - m_layers_zs.emplace_back(curr.position[2]); } + // indices data -> send data to gpu + for (IBuffer& buffer : m_buffers) + { + buffer.data_size = buffer.data.size(); + if (buffer.data_size > 0) + { + glsafe(::glGenBuffers(1, &buffer.ibo_id)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.ibo_id)); + glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, buffer.data_size * sizeof(unsigned int), buffer.data.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + // indices data -> free ram + buffer.data = std::vector(); + } + } + + // layers zs -> extract from result + for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) + { + if (move.type == GCodeProcessor::EMoveType::Extrude) + m_layers_zs.emplace_back(move.position[2]); + } + + // layers zs -> sort std::sort(m_layers_zs.begin(), m_layers_zs.end()); - // Replace intervals of layers with similar top positions with their average value. + // layers zs -> replace intervals of layers with similar top positions with their average value. int n = int(m_layers_zs.size()); int k = 0; for (int i = 0; i < n;) { @@ -213,20 +259,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) if (k < n) m_layers_zs.erase(m_layers_zs.begin() + k, m_layers_zs.end()); - // send data to gpu - for (Buffer& buffer : m_buffers) - { - buffer.data_size = buffer.data.size(); - if (buffer.data_size > 0) - { - glsafe(::glGenBuffers(1, &buffer.vbo_id)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vbo_id)); - glsafe(::glBufferData(GL_ARRAY_BUFFER, buffer.data_size * sizeof(float), buffer.data.data(), GL_STATIC_DRAW)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - buffer.data = std::vector(); - } - } - auto end_time = std::chrono::high_resolution_clock::now(); std::cout << "toolpaths generation time: " << std::chrono::duration_cast(end_time - start_time).count() << "ms \n"; } @@ -285,11 +317,9 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) void GCodeViewer::render_toolpaths() const { auto set_color = [](GLint current_program_id, const std::array& color) { - if (current_program_id > 0) - { + if (current_program_id > 0) { GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1; - if (color_id >= 0) - { + if (color_id >= 0) { glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)color.data())); return; } @@ -302,27 +332,29 @@ void GCodeViewer::render_toolpaths() const unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); unsigned char end_id = buffer_id(GCodeProcessor::EMoveType::Count); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vertices.vbo_id)); + glsafe(::glVertexPointer(3, GL_FLOAT, VBuffer::vertex_size_bytes(), (const void*)0)); + glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + for (unsigned char i = begin_id; i < end_id; ++i) { - const Buffer& buffer = m_buffers[i]; - if (buffer.vbo_id == 0) + const IBuffer& buffer = m_buffers[i]; + if (buffer.ibo_id == 0) continue; - + if (!buffer.visible) continue; if (buffer.shader.is_initialized()) { - buffer.shader.start_using(); + GCodeProcessor::EMoveType type = buffer_type(i); + buffer.shader.start_using(); + GLint current_program_id; glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id)); - GCodeProcessor::EMoveType type = buffer_type(i); - - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vbo_id)); - glsafe(::glVertexPointer(3, GL_FLOAT, Buffer::vertex_size_bytes(), (const void*)0)); - glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.ibo_id)); switch (type) { @@ -331,7 +363,7 @@ void GCodeViewer::render_toolpaths() const std::array color = { 1.0f, 1.0f, 1.0f, 1.0f }; set_color(current_program_id, color); glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawArrays(GL_POINTS, 0, (GLsizei)(buffer.data_size / Buffer::vertex_size()))); + glsafe(::glDrawElements(GL_POINTS, (GLsizei)buffer.data_size, GL_UNSIGNED_INT, nullptr)); glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); break; } @@ -340,7 +372,7 @@ void GCodeViewer::render_toolpaths() const std::array color = { 1.0f, 0.0f, 1.0f, 1.0f }; set_color(current_program_id, color); glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawArrays(GL_POINTS, 0, (GLsizei)(buffer.data_size / Buffer::vertex_size()))); + glsafe(::glDrawElements(GL_POINTS, (GLsizei)buffer.data_size, GL_UNSIGNED_INT, nullptr)); glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); break; } @@ -349,7 +381,7 @@ void GCodeViewer::render_toolpaths() const std::array color = { 0.0f, 1.0f, 0.0f, 1.0f }; set_color(current_program_id, color); glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawArrays(GL_POINTS, 0, (GLsizei)(buffer.data_size / Buffer::vertex_size()))); + glsafe(::glDrawElements(GL_POINTS, (GLsizei)buffer.data_size, GL_UNSIGNED_INT, nullptr)); glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); break; } @@ -357,28 +389,25 @@ void GCodeViewer::render_toolpaths() const { std::array color = { 1.0f, 0.0f, 0.0f, 1.0f }; set_color(current_program_id, color); - glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)(buffer.data_size / Buffer::vertex_size()))); + glsafe(::glDrawElements(GL_LINES, (GLsizei)buffer.data_size, GL_UNSIGNED_INT, nullptr)); break; } case GCodeProcessor::EMoveType::Travel: { std::array color = { 1.0f, 1.0f, 0.0f, 1.0f }; set_color(current_program_id, color); - glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)(buffer.data_size / Buffer::vertex_size()))); - break; - } - default: - { + glsafe(::glDrawElements(GL_LINES, (GLsizei)buffer.data_size, GL_UNSIGNED_INT, nullptr)); break; } } - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); - - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); buffer.shader.stop_using(); } } + + glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); } void GCodeViewer::render_shells() const diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 92929cffe9..6d6a6f8e01 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -15,13 +15,13 @@ namespace GUI { class GCodeViewer { - struct Buffer + // buffer containing vertices data + struct VBuffer { unsigned int vbo_id{ 0 }; - Shader shader; - std::vector data; - size_t data_size{ 0 }; - bool visible{ false }; + size_t vertices_count{ 0 }; + + size_t data_size_bytes() { return vertices_count * vertex_size_bytes(); } void reset(); @@ -29,6 +29,18 @@ class GCodeViewer static size_t vertex_size_bytes() { return vertex_size() * sizeof(float); } }; + // buffer containing indices data + struct IBuffer + { + unsigned int ibo_id{ 0 }; + Shader shader; + std::vector data; + size_t data_size{ 0 }; + bool visible{ false }; + + void reset(); + }; + struct Shells { GLVolumeCollection volumes; @@ -36,7 +48,8 @@ class GCodeViewer Shader shader; }; - std::vector m_buffers{ static_cast(GCodeProcessor::EMoveType::Extrude) }; + VBuffer m_vertices; + std::vector m_buffers{ static_cast(GCodeProcessor::EMoveType::Extrude) }; unsigned int m_last_result_id{ 0 }; std::vector m_layers_zs; From 75d1e8373d9ed949468b3bd733dee3c2a83fb951 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 16 Apr 2020 15:09:04 +0200 Subject: [PATCH 018/503] GCodeViewer -> extrusion paths colored by extrusion role --- src/libslic3r/GCode/GCodeProcessor.cpp | 2 +- src/libslic3r/GCode/GCodeProcessor.hpp | 6 +- src/slic3r/GUI/GCodeViewer.cpp | 100 ++++++++++++++++++------- src/slic3r/GUI/GCodeViewer.hpp | 28 ++++++- src/slic3r/GUI/GLCanvas3D.cpp | 3 + 5 files changed, 105 insertions(+), 34 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index c75c240020..78943dca89 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1,4 +1,4 @@ -#include "../libslic3r.h" +#include "libslic3r/libslic3r.h" #include "GCodeProcessor.hpp" #include diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 1f7af9c29d..623a2ae3bd 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -2,9 +2,9 @@ #define slic3r_GCodeProcessor_hpp_ #if ENABLE_GCODE_VIEWER -#include "../GCodeReader.hpp" -#include "../Point.hpp" -#include "../ExtrusionEntity.hpp" +#include "libslic3r/GCodeReader.hpp" +#include "libslic3r/Point.hpp" +#include "libslic3r/ExtrusionEntity.hpp" #include #include diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 963f8d2269..a554ecb8ba 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -43,8 +43,44 @@ void GCodeViewer::IBuffer::reset() // release cpu memory data = std::vector(); data_size = 0; + paths = std::vector(); } +bool GCodeViewer::IBuffer::init_shader(const std::string& vertex_shader_src, const std::string& fragment_shader_src) +{ + if (!shader.init(vertex_shader_src, fragment_shader_src)) + { + BOOST_LOG_TRIVIAL(error) << "Unable to initialize toolpaths shader: please, check that the files " << vertex_shader_src << " and " << fragment_shader_src << " are available"; + return false; + } + + return true; +} + +void GCodeViewer::IBuffer::add_path(GCodeProcessor::EMoveType type, ExtrusionRole role) +{ + unsigned int id = static_cast(data.size()); + paths.push_back({ type, role, id, id }); +} + +const std::array, erCount> GCodeViewer::Default_Extrusion_Role_Colors {{ + { 0.00f, 0.00f, 0.00f, 1.0f }, // erNone + { 1.00f, 1.00f, 0.40f, 1.0f }, // erPerimeter + { 1.00f, 0.65f, 0.00f, 1.0f }, // erExternalPerimeter + { 0.00f, 0.00f, 1.00f, 1.0f }, // erOverhangPerimeter + { 0.69f, 0.19f, 0.16f, 1.0f }, // erInternalInfill + { 0.84f, 0.20f, 0.84f, 1.0f }, // erSolidInfill + { 1.00f, 0.10f, 0.10f, 1.0f }, // erTopSolidInfill + { 0.60f, 0.60f, 1.00f, 1.0f }, // erBridgeInfill + { 1.00f, 1.00f, 1.00f, 1.0f }, // erGapFill + { 0.52f, 0.48f, 0.13f, 1.0f }, // erSkirt + { 0.00f, 1.00f, 0.00f, 1.0f }, // erSupportMaterial + { 0.00f, 0.50f, 0.00f, 1.0f }, // erSupportMaterialInterface + { 0.70f, 0.89f, 0.67f, 1.0f }, // erWipeTower + { 0.16f, 0.80f, 0.58f, 1.0f }, // erCustom + { 0.00f, 0.00f, 0.00f, 1.0f } // erMixed +}}; + void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized) { if (m_last_result_id == gcode_result.id) @@ -99,40 +135,41 @@ bool GCodeViewer::init_shaders() for (unsigned char i = begin_id; i < end_id; ++i) { - Shader& shader = m_buffers[i].shader; - std::string vertex_shader_src; - std::string fragment_shader_src; - GCodeProcessor::EMoveType type = buffer_type(i); - switch (type) + switch (buffer_type(i)) { case GCodeProcessor::EMoveType::Tool_change: { - vertex_shader_src = "toolchanges.vs"; - fragment_shader_src = "toolchanges.fs"; + if (!m_buffers[i].init_shader("toolchanges.vs", "toolchanges.fs")) + return false; + break; } case GCodeProcessor::EMoveType::Retract: { - vertex_shader_src = "retractions.vs"; - fragment_shader_src = "retractions.fs"; + if (!m_buffers[i].init_shader("retractions.vs", "retractions.fs")) + return false; + break; } case GCodeProcessor::EMoveType::Unretract: { - vertex_shader_src = "unretractions.vs"; - fragment_shader_src = "unretractions.fs"; + if (!m_buffers[i].init_shader("unretractions.vs", "unretractions.fs")) + return false; + break; } case GCodeProcessor::EMoveType::Extrude: { - vertex_shader_src = "extrusions.vs"; - fragment_shader_src = "extrusions.fs"; + if (!m_buffers[i].init_shader("extrusions.vs", "extrusions.fs")) + return false; + break; } case GCodeProcessor::EMoveType::Travel: { - vertex_shader_src = "travels.vs"; - fragment_shader_src = "travels.fs"; + if (!m_buffers[i].init_shader("travels.vs", "travels.fs")) + return false; + break; } default: @@ -140,12 +177,6 @@ bool GCodeViewer::init_shaders() break; } } - - if (!shader.init(vertex_shader_src, fragment_shader_src)) - { - BOOST_LOG_TRIVIAL(error) << "Unable to initialize toolpaths shader: please, check that the files " << vertex_shader_src << " and " << fragment_shader_src << " are available"; - return false; - } } if (!m_shells.shader.init("shells.vs", "shells.fs")) @@ -203,13 +234,20 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) case GCodeProcessor::EMoveType::Retract: case GCodeProcessor::EMoveType::Unretract: { + buffer.add_path(curr.type, curr.extrusion_role); buffer.data.push_back(static_cast(i)); break; } case GCodeProcessor::EMoveType::Extrude: case GCodeProcessor::EMoveType::Travel: { - buffer.data.push_back(static_cast(i - 1)); + if (prev.type != curr.type) + { + buffer.add_path(curr.type, curr.extrusion_role); + buffer.data.push_back(static_cast(i - 1)); + } + + buffer.paths.back().last = static_cast(buffer.data.size()); buffer.data.push_back(static_cast(i)); break; } @@ -387,16 +425,26 @@ void GCodeViewer::render_toolpaths() const } case GCodeProcessor::EMoveType::Extrude: { - std::array color = { 1.0f, 0.0f, 0.0f, 1.0f }; - set_color(current_program_id, color); - glsafe(::glDrawElements(GL_LINES, (GLsizei)buffer.data_size, GL_UNSIGNED_INT, nullptr)); + for (const Path& path : buffer.paths) + { + unsigned int color_id = static_cast(path.role); + if (color_id >= erCount) + color_id = 0; + + set_color(current_program_id, m_extrusion_role_colors[color_id]); + + glsafe(::glDrawElements(GL_LINE_STRIP, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + } break; } case GCodeProcessor::EMoveType::Travel: { std::array color = { 1.0f, 1.0f, 0.0f, 1.0f }; set_color(current_program_id, color); - glsafe(::glDrawElements(GL_LINES, (GLsizei)buffer.data_size, GL_UNSIGNED_INT, nullptr)); + for (const Path& path : buffer.paths) + { + glsafe(::glDrawElements(GL_LINE_STRIP, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + } break; } } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 6d6a6f8e01..e44fb01800 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -7,14 +7,14 @@ #include "3DScene.hpp" #include "libslic3r/GCode/GCodeProcessor.hpp" -#include - namespace Slic3r { class Print; namespace GUI { class GCodeViewer { + static const std::array, erCount> Default_Extrusion_Role_Colors; + // buffer containing vertices data struct VBuffer { @@ -29,16 +29,30 @@ class GCodeViewer static size_t vertex_size_bytes() { return vertex_size() * sizeof(float); } }; - // buffer containing indices data + struct Path + { + GCodeProcessor::EMoveType type{ GCodeProcessor::EMoveType::Noop }; + ExtrusionRole role{ erNone }; + unsigned int first{ 0 }; + unsigned int last{ 0 }; + + bool matches(GCodeProcessor::EMoveType type, ExtrusionRole role) const { return this->type == type && this->role == role; } + }; + + // buffer containing indices data and shader for a specific toolpath type struct IBuffer { unsigned int ibo_id{ 0 }; Shader shader; std::vector data; size_t data_size{ 0 }; + std::vector paths; bool visible{ false }; void reset(); + bool init_shader(const std::string& vertex_shader_src, const std::string& fragment_shader_src); + + void add_path(GCodeProcessor::EMoveType type, ExtrusionRole role); }; struct Shells @@ -55,11 +69,17 @@ class GCodeViewer std::vector m_layers_zs; Shells m_shells; + std::array, erCount> m_extrusion_role_colors; + public: GCodeViewer() = default; ~GCodeViewer() { reset(); } - bool init() { set_toolpath_visible(GCodeProcessor::EMoveType::Extrude, true); return init_shaders(); } + bool init() { + m_extrusion_role_colors = Default_Extrusion_Role_Colors; + set_toolpath_visible(GCodeProcessor::EMoveType::Extrude, true); + return init_shaders(); + } void load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized); void reset(); void render() const; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 4b8ca69d21..c7c3eaaf59 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2140,6 +2140,9 @@ void GLCanvas3D::render() // we need to set the mouse's scene position here because the depth buffer // could be invalidated by the following gizmo render methods // this position is used later into on_mouse() to drag the objects +#if ENABLE_GCODE_VIEWER + if (m_picking_enabled) +#endif // ENABLE_GCODE_VIEWER m_mouse.scene_position = _mouse_to_3d(m_mouse.position.cast()); _render_current_gizmo(); From 7b0e35e70d8cc63343d4ca9b57769e870728a977 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 16 Apr 2020 15:59:36 +0200 Subject: [PATCH 019/503] GCodeViewer -> Selection of extrusions view type --- src/slic3r/GUI/GCodeViewer.cpp | 36 ++++++++++++++++++++++++++++------ src/slic3r/GUI/GCodeViewer.hpp | 25 +++++++++++++++++++++++ src/slic3r/GUI/GLCanvas3D.cpp | 5 +++++ src/slic3r/GUI/GLCanvas3D.hpp | 1 + src/slic3r/GUI/GUI_Preview.cpp | 7 +++++++ 5 files changed, 68 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index a554ecb8ba..d6fc7fd8ef 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -354,6 +354,35 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) void GCodeViewer::render_toolpaths() const { + auto extrusion_color = [this](const Path& path) { + std::array color; + switch (m_view_type) + { + case EViewType::FeatureType: + { + unsigned int color_id = static_cast(path.role); + if (color_id >= erCount) + color_id = 0; + + color = m_extrusion_role_colors[color_id]; + break; + } + case EViewType::Height: + case EViewType::Width: + case EViewType::Feedrate: + case EViewType::FanSpeed: + case EViewType::VolumetricRate: + case EViewType::Tool: + case EViewType::ColorPrint: + default: + { + color = { 1.0f, 1.0f, 1.0f, 1.0f }; + break; + } + } + return color; + }; + auto set_color = [](GLint current_program_id, const std::array& color) { if (current_program_id > 0) { GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1; @@ -427,12 +456,7 @@ void GCodeViewer::render_toolpaths() const { for (const Path& path : buffer.paths) { - unsigned int color_id = static_cast(path.role); - if (color_id >= erCount) - color_id = 0; - - set_color(current_program_id, m_extrusion_role_colors[color_id]); - + set_color(current_program_id, extrusion_color(path)); glsafe(::glDrawElements(GL_LINE_STRIP, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); } break; diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index e44fb01800..9bc644c4eb 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -62,6 +62,21 @@ class GCodeViewer Shader shader; }; +public: + enum class EViewType : unsigned char + { + FeatureType, + Height, + Width, + Feedrate, + FanSpeed, + VolumetricRate, + Tool, + ColorPrint, + Count + }; + +private: VBuffer m_vertices; std::vector m_buffers{ static_cast(GCodeProcessor::EMoveType::Extrude) }; @@ -71,6 +86,8 @@ class GCodeViewer std::array, erCount> m_extrusion_role_colors; + EViewType m_view_type{ EViewType::FeatureType }; + public: GCodeViewer() = default; ~GCodeViewer() { reset(); } @@ -86,6 +103,14 @@ public: const std::vector& get_layers_zs() const { return m_layers_zs; }; + EViewType get_view_type() const { return m_view_type; } + void set_view_type(EViewType type) { + if (type == EViewType::Count) + type = EViewType::FeatureType; + + m_view_type = type; + } + bool is_toolpath_visible(GCodeProcessor::EMoveType type) const; void set_toolpath_visible(GCodeProcessor::EMoveType type, bool visible); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index c7c3eaaf59..9f3c9653c5 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2323,6 +2323,11 @@ void GLCanvas3D::set_toolpath_visible(GCodeProcessor::EMoveType type, bool visib m_gcode_viewer.set_toolpath_visible(type, visible); } +void GLCanvas3D::set_toolpath_view_type(GCodeViewer::EViewType type) +{ + m_gcode_viewer.set_view_type(type); +} + void GLCanvas3D::set_shells_visible(bool visible) { m_gcode_viewer.set_shells_visible(visible); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 7033f05a0b..246a298fe4 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -642,6 +642,7 @@ public: #if ENABLE_GCODE_VIEWER const std::vector& get_layers_zs() const; void set_toolpath_visible(GCodeProcessor::EMoveType type, bool visible); + void set_toolpath_view_type(GCodeViewer::EViewType type); void set_shells_visible(bool visible); #else std::vector get_current_print_zs(bool active_only) const; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 0c4f655c58..f2a56e8a20 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -602,10 +602,17 @@ void Preview::on_choice_view_type(wxCommandEvent& evt) { m_preferred_color_mode = (m_choice_view_type->GetStringSelection() == L("Tool")) ? "tool" : "feature"; int selection = m_choice_view_type->GetCurrentSelection(); +#if ENABLE_GCODE_VIEWER + if (0 <= selection && selection < static_cast(GCodeViewer::EViewType::Count)) + m_canvas->set_toolpath_view_type(static_cast(selection)); + + refresh_print(); +#else if ((0 <= selection) && (selection < (int)GCodePreviewData::Extrusion::Num_View_Types)) m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)selection; reload_print(); +#endif // ENABLE_GCODE_VIEWER } void Preview::on_combochecklist_features(wxCommandEvent& evt) From 9776d7c5a1333787ae88915b6f8e1b01380953cf Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 17 Apr 2020 10:43:29 +0200 Subject: [PATCH 020/503] GCodeViewer -> Toggle visibility of extrusions roles --- src/slic3r/GUI/GCodeViewer.cpp | 12 ++++++++++-- src/slic3r/GUI/GCodeViewer.hpp | 29 +++++++++++++++++++++++------ src/slic3r/GUI/GLCanvas3D.cpp | 9 +++++++-- src/slic3r/GUI/GLCanvas3D.hpp | 3 ++- src/slic3r/GUI/GUI_Preview.cpp | 13 ++++++++++--- 5 files changed, 52 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index d6fc7fd8ef..fb899164d3 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -104,6 +104,7 @@ void GCodeViewer::reset() buffer.reset(); } + m_extrusions.reset_role_visibility_flags(); m_shells.volumes.clear(); m_layers_zs = std::vector(); } @@ -121,7 +122,7 @@ bool GCodeViewer::is_toolpath_visible(GCodeProcessor::EMoveType type) const return (id < m_buffers.size()) ? m_buffers[id].visible : false; } -void GCodeViewer::set_toolpath_visible(GCodeProcessor::EMoveType type, bool visible) +void GCodeViewer::set_toolpath_move_type_visible(GCodeProcessor::EMoveType type, bool visible) { size_t id = static_cast(buffer_id(type)); if (id < m_buffers.size()) @@ -364,7 +365,7 @@ void GCodeViewer::render_toolpaths() const if (color_id >= erCount) color_id = 0; - color = m_extrusion_role_colors[color_id]; + color = m_extrusions.role_colors[color_id]; break; } case EViewType::Height: @@ -394,6 +395,10 @@ void GCodeViewer::render_toolpaths() const BOOST_LOG_TRIVIAL(error) << "Unable to find uniform_color uniform"; }; + auto is_path_visible = [](unsigned int flags, const Path& path) { + return Extrusions::is_role_visible(flags, path.role); + }; + glsafe(::glCullFace(GL_BACK)); unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); @@ -456,6 +461,9 @@ void GCodeViewer::render_toolpaths() const { for (const Path& path : buffer.paths) { + if (!is_path_visible(m_extrusions.role_visibility_flags, path)) + continue; + set_color(current_program_id, extrusion_color(path)); glsafe(::glDrawElements(GL_LINE_STRIP, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 9bc644c4eb..1020606b86 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -62,6 +62,24 @@ class GCodeViewer Shader shader; }; + struct Extrusions + { + std::array, erCount> role_colors; + unsigned int role_visibility_flags{ 0 }; + + void reset_role_visibility_flags() { + role_visibility_flags = 0; + for (unsigned int i = 0; i < erCount; ++i) + { + role_visibility_flags |= 1 << i; + } + } + + static bool is_role_visible(unsigned int flags, ExtrusionRole role) { + return role < erCount && (flags & (1 << role)) != 0; + } + }; + public: enum class EViewType : unsigned char { @@ -82,10 +100,8 @@ private: unsigned int m_last_result_id{ 0 }; std::vector m_layers_zs; + Extrusions m_extrusions; Shells m_shells; - - std::array, erCount> m_extrusion_role_colors; - EViewType m_view_type{ EViewType::FeatureType }; public: @@ -93,8 +109,8 @@ public: ~GCodeViewer() { reset(); } bool init() { - m_extrusion_role_colors = Default_Extrusion_Role_Colors; - set_toolpath_visible(GCodeProcessor::EMoveType::Extrude, true); + m_extrusions.role_colors = Default_Extrusion_Role_Colors; + set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Extrude, true); return init_shaders(); } void load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized); @@ -112,7 +128,8 @@ public: } bool is_toolpath_visible(GCodeProcessor::EMoveType type) const; - void set_toolpath_visible(GCodeProcessor::EMoveType type, bool visible); + void set_toolpath_move_type_visible(GCodeProcessor::EMoveType type, bool visible); + void set_toolpath_role_visibility_flags(unsigned int flags) { m_extrusions.role_visibility_flags = flags; } bool are_shells_visible() const { return m_shells.visible; } void set_shells_visible(bool visible) { m_shells.visible = visible; } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 8d01d2c566..fcefc8f8a8 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2322,9 +2322,14 @@ const std::vector& GLCanvas3D::get_layers_zs() const return m_gcode_viewer.get_layers_zs(); } -void GLCanvas3D::set_toolpath_visible(GCodeProcessor::EMoveType type, bool visible) +void GLCanvas3D::set_toolpath_move_type_visible(GCodeProcessor::EMoveType type, bool visible) { - m_gcode_viewer.set_toolpath_visible(type, visible); + m_gcode_viewer.set_toolpath_move_type_visible(type, visible); +} + +void GLCanvas3D::set_toolpath_role_visibility_flags(unsigned int flags) +{ + m_gcode_viewer.set_toolpath_role_visibility_flags(flags); } void GLCanvas3D::set_toolpath_view_type(GCodeViewer::EViewType type) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 246a298fe4..fce805b51d 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -641,7 +641,8 @@ public: #if ENABLE_GCODE_VIEWER const std::vector& get_layers_zs() const; - void set_toolpath_visible(GCodeProcessor::EMoveType type, bool visible); + void set_toolpath_move_type_visible(GCodeProcessor::EMoveType type, bool visible); + void set_toolpath_role_visibility_flags(unsigned int flags); void set_toolpath_view_type(GCodeViewer::EViewType type); void set_shells_visible(bool visible); #else diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index f2a56e8a20..6b13304c0e 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -308,6 +308,9 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view m_combochecklist_features->Create(this, wxID_ANY, _(L("Feature types")), wxDefaultPosition, wxSize(15 * wxGetApp().em_unit(), -1), wxCB_READONLY); std::string feature_text = GUI::into_u8(_(L("Feature types"))); std::string feature_items = GUI::into_u8( +#if ENABLE_GCODE_VIEWER + _L("Unknown") + "|" + +#endif // ENABLE_GCODE_VIEWER _(L("Perimeter")) + "|" + _(L("External perimeter")) + "|" + _(L("Overhang perimeter")) + "|" + @@ -618,14 +621,18 @@ void Preview::on_choice_view_type(wxCommandEvent& evt) void Preview::on_combochecklist_features(wxCommandEvent& evt) { int flags = Slic3r::GUI::combochecklist_get_flags(m_combochecklist_features); +#if ENABLE_GCODE_VIEWER + m_canvas->set_toolpath_role_visibility_flags(static_cast(flags)); +#else m_gcode_preview_data->extrusion.role_flags = (unsigned int)flags; +#endif // ENABLE_GCODE_VIEWER refresh_print(); } void Preview::on_checkbox_travel(wxCommandEvent& evt) { #if ENABLE_GCODE_VIEWER - m_canvas->set_toolpath_visible(GCodeProcessor::EMoveType::Travel, m_checkbox_travel->IsChecked()); + m_canvas->set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Travel, m_checkbox_travel->IsChecked()); refresh_print(); #else m_gcode_preview_data->travel.is_visible = m_checkbox_travel->IsChecked(); @@ -638,7 +645,7 @@ void Preview::on_checkbox_travel(wxCommandEvent& evt) void Preview::on_checkbox_retractions(wxCommandEvent& evt) { #if ENABLE_GCODE_VIEWER - m_canvas->set_toolpath_visible(GCodeProcessor::EMoveType::Retract, m_checkbox_retractions->IsChecked()); + m_canvas->set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Retract, m_checkbox_retractions->IsChecked()); #else m_gcode_preview_data->retraction.is_visible = m_checkbox_retractions->IsChecked(); #endif // ENABLE_GCODE_VIEWER @@ -648,7 +655,7 @@ void Preview::on_checkbox_retractions(wxCommandEvent& evt) void Preview::on_checkbox_unretractions(wxCommandEvent& evt) { #if ENABLE_GCODE_VIEWER - m_canvas->set_toolpath_visible(GCodeProcessor::EMoveType::Unretract, m_checkbox_unretractions->IsChecked()); + m_canvas->set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Unretract, m_checkbox_unretractions->IsChecked()); #else m_gcode_preview_data->unretraction.is_visible = m_checkbox_unretractions->IsChecked(); #endif // ENABLE_GCODE_VIEWER From 83816afb3f9a09050769e5896d50de2f9205fa72 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 17 Apr 2020 13:28:25 +0200 Subject: [PATCH 021/503] GCodeViewer -> Added bounding box to fix camera frustum tighting --- src/slic3r/GUI/GCodeViewer.cpp | 4 +++- src/slic3r/GUI/GCodeViewer.hpp | 2 ++ src/slic3r/GUI/GLCanvas3D.cpp | 9 +++++++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index fb899164d3..e458d933bb 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -104,6 +104,7 @@ void GCodeViewer::reset() buffer.reset(); } + m_bounding_box = BoundingBoxf3(); m_extrusions.reset_role_visibility_flags(); m_shells.volumes.clear(); m_layers_zs = std::vector(); @@ -198,13 +199,14 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) if (m_vertices.vertices_count == 0) return; - // vertex data -> extract from result + // vertex data / bounding box -> extract from result std::vector vertices_data; for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) { for (int j = 0; j < 3; ++j) { vertices_data.insert(vertices_data.end(), move.position[j]); + m_bounding_box.merge(move.position.cast()); } } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 1020606b86..aff2a807dd 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -97,6 +97,7 @@ public: private: VBuffer m_vertices; std::vector m_buffers{ static_cast(GCodeProcessor::EMoveType::Extrude) }; + BoundingBoxf3 m_bounding_box; unsigned int m_last_result_id{ 0 }; std::vector m_layers_zs; @@ -117,6 +118,7 @@ public: void reset(); void render() const; + const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; } const std::vector& get_layers_zs() const { return m_layers_zs; }; EViewType get_view_type() const { return m_view_type; } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index fcefc8f8a8..604f98a79c 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2841,9 +2841,8 @@ void GLCanvas3D::load_gcode_preview_2(const GCodeProcessor::Result& gcode_result } out.close(); } - - m_gcode_viewer.load(gcode_result , *this->fff_print(), m_initialized); #endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT + m_gcode_viewer.load(gcode_result, *this->fff_print(), m_initialized); } #endif // ENABLE_GCODE_VIEWER @@ -5213,6 +5212,12 @@ BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_be #else bb.merge(m_bed.get_bounding_box(include_bed_model)); #endif // ENABLE_NON_STATIC_CANVAS_MANAGER + +#if ENABLE_GCODE_VIEWER + if (!m_main_toolbar.is_enabled()) + bb.merge(m_gcode_viewer.get_bounding_box()); +#endif // ENABLE_GCODE_VIEWER + return bb; } From 3a07e8730f0888fd3a401aa375d19b073e08c865 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Sat, 18 Apr 2020 10:41:37 +0200 Subject: [PATCH 022/503] GCodeViewer -> Basic legend using imgui --- src/libslic3r/ExtrusionEntity.cpp | 4 ++ src/slic3r/GUI/GCodeViewer.cpp | 85 +++++++++++++++++++++++++++---- src/slic3r/GUI/GCodeViewer.hpp | 6 +++ src/slic3r/GUI/GLCanvas3D.cpp | 4 ++ 4 files changed, 90 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index c0d08c84b7..0b09d16020 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -306,7 +306,11 @@ double ExtrusionLoop::min_mm3_per_mm() const std::string ExtrusionEntity::role_to_string(ExtrusionRole role) { switch (role) { +#if ENABLE_GCODE_VIEWER + case erNone : return L("Unknown"); +#else case erNone : return L("None"); +#endif // ENABLE_GCODE_VIEWER case erPerimeter : return L("Perimeter"); case erExternalPerimeter : return L("External perimeter"); case erOverhangPerimeter : return L("Overhang perimeter"); diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index e458d933bb..be05030da2 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -6,6 +6,8 @@ #include "GUI_App.hpp" #include "PresetBundle.hpp" #include "Camera.hpp" +#include "I18N.hpp" +#include "libslic3r/I18N.hpp" #include #include @@ -108,6 +110,7 @@ void GCodeViewer::reset() m_extrusions.reset_role_visibility_flags(); m_shells.volumes.clear(); m_layers_zs = std::vector(); + m_roles = std::vector(); } void GCodeViewer::render() const @@ -115,6 +118,7 @@ void GCodeViewer::render() const glsafe(::glEnable(GL_DEPTH_TEST)); render_toolpaths(); render_shells(); + render_overlay(); } bool GCodeViewer::is_toolpath_visible(GCodeProcessor::EMoveType type) const @@ -277,17 +281,17 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) } } - // layers zs -> extract from result + // layers zs / roles -> extract from result for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) { if (move.type == GCodeProcessor::EMoveType::Extrude) m_layers_zs.emplace_back(move.position[2]); + + m_roles.emplace_back(move.extrusion_role); } - // layers zs -> sort - std::sort(m_layers_zs.begin(), m_layers_zs.end()); - // layers zs -> replace intervals of layers with similar top positions with their average value. + std::sort(m_layers_zs.begin(), m_layers_zs.end()); int n = int(m_layers_zs.size()); int k = 0; for (int i = 0; i < n;) { @@ -300,6 +304,10 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) if (k < n) m_layers_zs.erase(m_layers_zs.begin() + k, m_layers_zs.end()); + // roles -> remove duplicates + std::sort(m_roles.begin(), m_roles.end()); + m_roles.erase(std::unique(m_roles.begin(), m_roles.end()), m_roles.end()); + auto end_time = std::chrono::high_resolution_clock::now(); std::cout << "toolpaths generation time: " << std::chrono::duration_cast(end_time - start_time).count() << "ms \n"; } @@ -363,11 +371,7 @@ void GCodeViewer::render_toolpaths() const { case EViewType::FeatureType: { - unsigned int color_id = static_cast(path.role); - if (color_id >= erCount) - color_id = 0; - - color = m_extrusions.role_colors[color_id]; + color = m_extrusions.role_colors[static_cast(path.role)]; break; } case EViewType::Height: @@ -506,6 +510,69 @@ void GCodeViewer::render_shells() const // glsafe(::glDepthMask(GL_TRUE)); } +void GCodeViewer::render_overlay() const +{ + static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); + static const float ICON_SIZE = 20.0f; + static const ImU32 ICON_BORDER_COLOR = ImGui::GetColorU32(ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); + + if (!m_legend_enabled || m_roles.empty()) + return; + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + + imgui.set_next_window_pos(0, 0, ImGuiCond_Always); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + imgui.begin(_L("Legend"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); + + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + switch (m_view_type) + { + case EViewType::FeatureType: { imgui.text(Slic3r::I18N::translate(L("Feature type"))); break; } + case EViewType::Height: { imgui.text(Slic3r::I18N::translate(L("Height (mm)"))); break; } + case EViewType::Width: { imgui.text(Slic3r::I18N::translate(L("Width (mm)"))); break; } + case EViewType::Feedrate: { imgui.text(Slic3r::I18N::translate(L("Speed (mm/s)"))); break; } + case EViewType::FanSpeed: { imgui.text(Slic3r::I18N::translate(L("Fan Speed (%)"))); break; } + case EViewType::VolumetricRate: { imgui.text(Slic3r::I18N::translate(L("Volumetric flow rate (mm³/s)"))); break; } + case EViewType::Tool: { imgui.text(Slic3r::I18N::translate(L("Tool"))); break; } + case EViewType::ColorPrint: { imgui.text(Slic3r::I18N::translate(L("Color Print"))); break; } + } + ImGui::PopStyleColor(); + + ImGui::Separator(); + + switch (m_view_type) + { + case EViewType::FeatureType: + { + for (ExtrusionRole role : m_roles) + { + ImVec2 pos(ImGui::GetCursorPosX() + 2.0f, ImGui::GetCursorPosY() + 2.0f); + draw_list->AddRect(ImVec2(pos.x, pos.y), ImVec2(pos.x + ICON_SIZE, pos.y + ICON_SIZE), ICON_BORDER_COLOR, 0.0f, 0); + const std::array& role_color = m_extrusions.role_colors[static_cast(role)]; + ImU32 fill_color = ImGui::GetColorU32(ImVec4(role_color[0], role_color[1], role_color[2], role_color[3])); + draw_list->AddRectFilled(ImVec2(pos.x + 1.0f, pos.y + 1.0f), ImVec2(pos.x + ICON_SIZE - 1.0f, pos.y + ICON_SIZE - 1.0f), fill_color); + ImGui::SetCursorPosX(pos.x + ICON_SIZE + 4.0f); + ImGui::AlignTextToFramePadding(); + imgui.text(Slic3r::I18N::translate(ExtrusionEntity::role_to_string(role))); + } + break; + } + case EViewType::Height: { break; } + case EViewType::Width: { break; } + case EViewType::Feedrate: { break; } + case EViewType::FanSpeed: { break; } + case EViewType::VolumetricRate: { break; } + case EViewType::Tool: { break; } + case EViewType::ColorPrint: { break; } + } + + imgui.end(); + ImGui::PopStyleVar(); +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index aff2a807dd..4ee187144c 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -101,9 +101,11 @@ private: unsigned int m_last_result_id{ 0 }; std::vector m_layers_zs; + std::vector m_roles; Extrusions m_extrusions; Shells m_shells; EViewType m_view_type{ EViewType::FeatureType }; + bool m_legend_enabled{ true }; public: GCodeViewer() = default; @@ -136,12 +138,16 @@ public: bool are_shells_visible() const { return m_shells.visible; } void set_shells_visible(bool visible) { m_shells.visible = visible; } + bool is_legend_enabled() const { return m_legend_enabled; } + void enable_legend(bool enable) { m_legend_enabled = enable; } + private: bool init_shaders(); void load_toolpaths(const GCodeProcessor::Result& gcode_result); void load_shells(const Print& print, bool initialized); void render_toolpaths() const; void render_shells() const; + void render_overlay() const; }; } // namespace GUI diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 604f98a79c..ac773102ce 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1941,7 +1941,11 @@ void GLCanvas3D::enable_layers_editing(bool enable) void GLCanvas3D::enable_legend_texture(bool enable) { +#if ENABLE_GCODE_VIEWER + m_gcode_viewer.enable_legend(enable); +#else m_legend_texture_enabled = enable; +#endif // ENABLE_GCODE_VIEWER } void GLCanvas3D::enable_picking(bool enable) From 179dbc7d0e10c6a7d76f1a0438de78d19e3d4e03 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Sat, 18 Apr 2020 11:49:20 +0200 Subject: [PATCH 023/503] Tech ENABLE_GCODE_VIEWER -> removed legend texture from GLCanvas3D --- src/slic3r/GUI/GCodeViewer.cpp | 8 ++++---- src/slic3r/GUI/GLCanvas3D.cpp | 14 ++++++++++++++ src/slic3r/GUI/GLCanvas3D.hpp | 12 ++++++++++++ src/slic3r/GUI/GUI_Preview.cpp | 5 ++++- 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index be05030da2..1c828198da 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -513,7 +513,7 @@ void GCodeViewer::render_shells() const void GCodeViewer::render_overlay() const { static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); - static const float ICON_SIZE = 20.0f; + static const float ICON_BORDER_SIZE = 20.0f; static const ImU32 ICON_BORDER_COLOR = ImGui::GetColorU32(ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); if (!m_legend_enabled || m_roles.empty()) @@ -550,11 +550,11 @@ void GCodeViewer::render_overlay() const for (ExtrusionRole role : m_roles) { ImVec2 pos(ImGui::GetCursorPosX() + 2.0f, ImGui::GetCursorPosY() + 2.0f); - draw_list->AddRect(ImVec2(pos.x, pos.y), ImVec2(pos.x + ICON_SIZE, pos.y + ICON_SIZE), ICON_BORDER_COLOR, 0.0f, 0); + draw_list->AddRect(ImVec2(pos.x, pos.y), ImVec2(pos.x + ICON_BORDER_SIZE, pos.y + ICON_BORDER_SIZE), ICON_BORDER_COLOR, 0.0f, 0); const std::array& role_color = m_extrusions.role_colors[static_cast(role)]; ImU32 fill_color = ImGui::GetColorU32(ImVec4(role_color[0], role_color[1], role_color[2], role_color[3])); - draw_list->AddRectFilled(ImVec2(pos.x + 1.0f, pos.y + 1.0f), ImVec2(pos.x + ICON_SIZE - 1.0f, pos.y + ICON_SIZE - 1.0f), fill_color); - ImGui::SetCursorPosX(pos.x + ICON_SIZE + 4.0f); + draw_list->AddRectFilled(ImVec2(pos.x + 1.0f, pos.y + 1.0f), ImVec2(pos.x + ICON_BORDER_SIZE - 1.0f, pos.y + ICON_BORDER_SIZE - 1.0f), fill_color); + ImGui::SetCursorPosX(pos.x + ICON_BORDER_SIZE + 4.0f); ImGui::AlignTextToFramePadding(); imgui.text(Slic3r::I18N::translate(ExtrusionEntity::role_to_string(role))); } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index ac773102ce..8c3af4a587 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -901,6 +901,7 @@ void GLCanvas3D::WarningTexture::msw_rescale(const GLCanvas3D& canvas) generate(m_msg_text, canvas, true, m_is_colored_red); } +#if !ENABLE_GCODE_VIEWER const unsigned char GLCanvas3D::LegendTexture::Squares_Border_Color[3] = { 64, 64, 64 }; const unsigned char GLCanvas3D::LegendTexture::Default_Background_Color[3] = { (unsigned char)(DEFAULT_BG_LIGHT_COLOR[0] * 255.0f), (unsigned char)(DEFAULT_BG_LIGHT_COLOR[1] * 255.0f), (unsigned char)(DEFAULT_BG_LIGHT_COLOR[2] * 255.0f) }; const unsigned char GLCanvas3D::LegendTexture::Error_Background_Color[3] = { (unsigned char)(ERROR_BG_LIGHT_COLOR[0] * 255.0f), (unsigned char)(ERROR_BG_LIGHT_COLOR[1] * 255.0f), (unsigned char)(ERROR_BG_LIGHT_COLOR[2] * 255.0f) }; @@ -1267,6 +1268,7 @@ void GLCanvas3D::LegendTexture::render(const GLCanvas3D& canvas) const GLTexture::render_sub_texture(m_id, left, right, bottom, top, uvs); } } +#endif // !ENABLE_GCODE_VIEWER void GLCanvas3D::Labels::render(const std::vector& sorted_instances) const { @@ -1574,7 +1576,9 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar , m_dirty(true) , m_initialized(false) , m_apply_zoom_to_volumes_filter(false) +#if !ENABLE_GCODE_VIEWER , m_legend_texture_enabled(false) +#endif // !ENABLE_GCODE_VIEWER , m_picking_enabled(false) , m_moving_enabled(false) , m_dynamic_background_enabled(false) @@ -2953,6 +2957,7 @@ void GLCanvas3D::load_preview(const std::vector& str_tool_colors, c _update_toolpath_volumes_outside_state(); _show_warning_texture_if_needed(WarningTexture::ToolpathOutside); +#if !ENABLE_GCODE_VIEWER if (color_print_values.empty()) reset_legend_texture(); else { @@ -2961,6 +2966,7 @@ void GLCanvas3D::load_preview(const std::vector& str_tool_colors, c const std::vector tool_colors = _parse_colors(str_tool_colors); _generate_legend_texture(preview_data, tool_colors); } +#endif // !ENABLE_GCODE_VIEWER } void GLCanvas3D::bind_event_handlers() @@ -4080,6 +4086,7 @@ Vec2d GLCanvas3D::get_local_mouse_position() const return Vec2d(factor * mouse_pos.x, factor * mouse_pos.y); } +#if !ENABLE_GCODE_VIEWER void GLCanvas3D::reset_legend_texture() { if (m_legend_texture.get_id() != 0) @@ -4088,6 +4095,7 @@ void GLCanvas3D::reset_legend_texture() m_legend_texture.reset(); } } +#endif // !ENABLE_GCODE_VIEWER void GLCanvas3D::set_tooltip(const std::string& tooltip) const { @@ -5556,7 +5564,9 @@ void GLCanvas3D::_render_overlays() const _render_gizmos_overlay(); _render_warning_texture(); +#if !ENABLE_GCODE_VIEWER _render_legend_texture(); +#endif // !ENABLE_GCODE_VIEWER // main toolbar and undoredo toolbar need to be both updated before rendering because both their sizes are needed // to correctly place them @@ -5600,6 +5610,7 @@ void GLCanvas3D::_render_warning_texture() const m_warning_texture.render(*this); } +#if !ENABLE_GCODE_VIEWER void GLCanvas3D::_render_legend_texture() const { if (!m_legend_texture_enabled) @@ -5607,6 +5618,7 @@ void GLCanvas3D::_render_legend_texture() const m_legend_texture.render(*this); } +#endif // !ENABLE_GCODE_VIEWER void GLCanvas3D::_render_volumes_for_picking() const { @@ -7096,10 +7108,12 @@ std::vector GLCanvas3D::_parse_colors(const std::vector& col return output; } +#if !ENABLE_GCODE_VIEWER void GLCanvas3D::_generate_legend_texture(const GCodePreviewData& preview_data, const std::vector& tool_colors) { m_legend_texture.generate(preview_data, tool_colors, *this, true); } +#endif // !ENABLE_GCODE_VIEWER void GLCanvas3D::_set_warning_texture(WarningTexture::Warning warning, bool state) { diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index fce805b51d..af1f6149c8 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -345,6 +345,7 @@ private: bool generate(const std::string& msg, const GLCanvas3D& canvas, bool compress, bool red_colored = false); }; +#if !ENABLE_GCODE_VIEWER class LegendTexture : public GUI::GLTexture { static const int Px_Title_Offset = 5; @@ -371,6 +372,7 @@ private: void render(const GLCanvas3D& canvas) const; }; +#endif // !ENABLE_GCODE_VIEWER #if ENABLE_RENDER_STATISTICS struct RenderStats @@ -445,7 +447,9 @@ private: std::unique_ptr m_retina_helper; #endif bool m_in_render; +#if !ENABLE_GCODE_VIEWER LegendTexture m_legend_texture; +#endif // !ENABLE_GCODE_VIEWER WarningTexture m_warning_texture; wxTimer m_timer; #if !ENABLE_NON_STATIC_CANVAS_MANAGER @@ -483,7 +487,9 @@ private: bool m_initialized; bool m_apply_zoom_to_volumes_filter; mutable std::vector m_hover_volume_idxs; +#if !ENABLE_GCODE_VIEWER bool m_legend_texture_enabled; +#endif // !ENABLE_GCODE_VIEWER bool m_picking_enabled; bool m_moving_enabled; bool m_dynamic_background_enabled; @@ -679,7 +685,9 @@ public: Size get_canvas_size() const; Vec2d get_local_mouse_position() const; +#if !ENABLE_GCODE_VIEWER void reset_legend_texture(); +#endif // !ENABLE_GCODE_VIEWER void set_tooltip(const std::string& tooltip) const; @@ -790,7 +798,9 @@ private: #endif // ENABLE_RENDER_SELECTION_CENTER void _render_overlays() const; void _render_warning_texture() const; +#if !ENABLE_GCODE_VIEWER void _render_legend_texture() const; +#endif // !ENABLE_GCODE_VIEWER void _render_volumes_for_picking() const; void _render_current_gizmo() const; void _render_gizmos_overlay() const; @@ -852,8 +862,10 @@ private: void _update_sla_shells_outside_state(); void _show_warning_texture_if_needed(WarningTexture::Warning warning); +#if !ENABLE_GCODE_VIEWER // generates the legend texture in dependence of the current shown view type void _generate_legend_texture(const GCodePreviewData& preview_data, const std::vector& tool_colors); +#endif // !ENABLE_GCODE_VIEWER // generates a warning texture containing the given message void _set_warning_texture(WarningTexture::Warning warning, bool state); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 6b13304c0e..562058dd95 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -479,8 +479,9 @@ void Preview::reload_print(bool keep_volumes) m_canvas->reset_volumes(); #if ENABLE_GCODE_VIEWER m_canvas->reset_gcode_toolpaths(); -#endif // ENABLE_GCODE_VIEWER +#else m_canvas->reset_legend_texture(); +#endif // ENABLE_GCODE_VIEWER m_loaded = false; #ifdef __linux__ m_volumes_cleanup_required = false; @@ -937,7 +938,9 @@ void Preview::load_print_as_fff(bool keep_z_range) if (! has_layers) { reset_sliders(true); +#if !ENABLE_GCODE_VIEWER m_canvas->reset_legend_texture(); +#endif // !ENABLE_GCODE_VIEWER m_canvas_widget->Refresh(); return; } From 6e5a6f3b43f7a29c6bd0d79207068298772c5be4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 20 Apr 2020 10:52:16 +0200 Subject: [PATCH 024/503] GCodeViewer -> Extrusion toolpaths colored by height --- src/libslic3r/GCode/PreviewData.hpp | 11 ++-- src/slic3r/GUI/GCodeViewer.cpp | 88 +++++++++++++++++++++++++---- src/slic3r/GUI/GCodeViewer.hpp | 58 ++++++++++++++++++- 3 files changed, 137 insertions(+), 20 deletions(-) diff --git a/src/libslic3r/GCode/PreviewData.hpp b/src/libslic3r/GCode/PreviewData.hpp index 35bbfa50ac..c0f768088e 100644 --- a/src/libslic3r/GCode/PreviewData.hpp +++ b/src/libslic3r/GCode/PreviewData.hpp @@ -56,8 +56,7 @@ public: // Color mapping to convert a float into a smooth rainbow of 10 colors. class RangeBase { - public: - + public: virtual void reset() = 0; virtual bool empty() const = 0; virtual float min() const = 0; @@ -73,7 +72,7 @@ public: // Color mapping converting a float in a range between a min and a max into a smooth rainbow of 10 colors. class Range : public RangeBase { - public: + public: Range(); // RangeBase Overrides @@ -97,8 +96,7 @@ public: template class MultiRange : public RangeBase { - public: - + public: void reset() override { bounds = decltype(bounds){}; @@ -160,8 +158,7 @@ public: mode.set(static_cast(range_type_value), enable); } - private: - + private: // Interval bounds struct Bounds { diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 1c828198da..10282642c3 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -59,14 +59,38 @@ bool GCodeViewer::IBuffer::init_shader(const std::string& vertex_shader_src, con return true; } -void GCodeViewer::IBuffer::add_path(GCodeProcessor::EMoveType type, ExtrusionRole role) +void GCodeViewer::IBuffer::add_path(GCodeProcessor::EMoveType type, ExtrusionRole role, float height) { unsigned int id = static_cast(data.size()); - paths.push_back({ type, role, id, id }); + paths.push_back({ type, role, id, id, height }); +} + +std::array GCodeViewer::Extrusions::Range::get_color_at(float value, const std::array, Default_Range_Colors_Count>& colors) const +{ + // Input value scaled to the color range + const float step = step_size(); + const float global_t = (step != 0.0f) ? std::max(0.0f, value - min) / step : 0.0f; // lower limit of 0.0f + + const size_t color_max_idx = colors.size() - 1; + + // Compute the two colors just below (low) and above (high) the input value + const size_t color_low_idx = std::clamp(static_cast(global_t), std::size_t{ 0 }, color_max_idx); + const size_t color_high_idx = std::clamp(color_low_idx + 1, std::size_t{ 0 }, color_max_idx); + + // Compute how far the value is between the low and high colors so that they can be interpolated + const float local_t = std::min(global_t - static_cast(color_low_idx), 1.0f); // upper limit of 1.0f + + // Interpolate between the low and high colors in RGB space to find exactly which color the input value should get + std::array ret; + for (unsigned int i = 0; i < 4; ++i) + { + ret[i] = lerp(colors[color_low_idx][i], colors[color_high_idx][i], local_t); + } + return ret; } const std::array, erCount> GCodeViewer::Default_Extrusion_Role_Colors {{ - { 0.00f, 0.00f, 0.00f, 1.0f }, // erNone + { 1.00f, 1.00f, 1.00f, 1.0f }, // erNone { 1.00f, 1.00f, 0.40f, 1.0f }, // erPerimeter { 1.00f, 0.65f, 0.00f, 1.0f }, // erExternalPerimeter { 0.00f, 0.00f, 1.00f, 1.0f }, // erOverhangPerimeter @@ -83,6 +107,19 @@ const std::array, erCount> GCodeViewer::Default_Extrusion_R { 0.00f, 0.00f, 0.00f, 1.0f } // erMixed }}; +const std::array, GCodeViewer::Default_Range_Colors_Count> GCodeViewer::Default_Range_Colors {{ + { 0.043f, 0.173f, 0.478f, 1.0f }, + { 0.075f, 0.349f, 0.522f, 1.0f }, + { 0.110f, 0.533f, 0.569f, 1.0f }, + { 0.016f, 0.839f, 0.059f, 1.0f }, + { 0.667f, 0.949f, 0.000f, 1.0f }, + { 0.988f, 0.975f, 0.012f, 1.0f }, + { 0.961f, 0.808f, 0.039f, 1.0f }, + { 0.890f, 0.533f, 0.125f, 1.0f }, + { 0.820f, 0.408f, 0.188f, 1.0f }, + { 0.761f, 0.322f, 0.235f, 1.0f } +}}; + void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized) { if (m_last_result_id == gcode_result.id) @@ -108,6 +145,7 @@ void GCodeViewer::reset() m_bounding_box = BoundingBoxf3(); m_extrusions.reset_role_visibility_flags(); + m_extrusions.reset_ranges(); m_shells.volumes.clear(); m_layers_zs = std::vector(); m_roles = std::vector(); @@ -241,17 +279,22 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) case GCodeProcessor::EMoveType::Retract: case GCodeProcessor::EMoveType::Unretract: { - buffer.add_path(curr.type, curr.extrusion_role); + buffer.add_path(curr.type, curr.extrusion_role, curr.height); buffer.data.push_back(static_cast(i)); break; } case GCodeProcessor::EMoveType::Extrude: case GCodeProcessor::EMoveType::Travel: { - if (prev.type != curr.type) + if (prev.type != curr.type || !buffer.paths.back().matches(curr)) { - buffer.add_path(curr.type, curr.extrusion_role); + buffer.add_path(curr.type, curr.extrusion_role, curr.height); buffer.data.push_back(static_cast(i - 1)); + + if (curr.type == GCodeProcessor::EMoveType::Extrude) + { + m_extrusions.ranges.height.update_from(curr.height); + } } buffer.paths.back().last = static_cast(buffer.data.size()); @@ -375,6 +418,10 @@ void GCodeViewer::render_toolpaths() const break; } case EViewType::Height: + { + color = m_extrusions.ranges.height.get_color_at(path.height, m_extrusions.ranges.colors); + break; + } case EViewType::Width: case EViewType::Feedrate: case EViewType::FanSpeed: @@ -513,8 +560,9 @@ void GCodeViewer::render_shells() const void GCodeViewer::render_overlay() const { static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); - static const float ICON_BORDER_SIZE = 20.0f; + static const float ICON_BORDER_SIZE = 25.0f; static const ImU32 ICON_BORDER_COLOR = ImGui::GetColorU32(ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); + static const float GAP_ICON_TEXT = 5.0f; if (!m_legend_enabled || m_roles.empty()) return; @@ -551,16 +599,34 @@ void GCodeViewer::render_overlay() const { ImVec2 pos(ImGui::GetCursorPosX() + 2.0f, ImGui::GetCursorPosY() + 2.0f); draw_list->AddRect(ImVec2(pos.x, pos.y), ImVec2(pos.x + ICON_BORDER_SIZE, pos.y + ICON_BORDER_SIZE), ICON_BORDER_COLOR, 0.0f, 0); - const std::array& role_color = m_extrusions.role_colors[static_cast(role)]; - ImU32 fill_color = ImGui::GetColorU32(ImVec4(role_color[0], role_color[1], role_color[2], role_color[3])); + const std::array& color = m_extrusions.role_colors[static_cast(role)]; + ImU32 fill_color = ImGui::GetColorU32(ImVec4(color[0], color[1], color[2], color[3])); draw_list->AddRectFilled(ImVec2(pos.x + 1.0f, pos.y + 1.0f), ImVec2(pos.x + ICON_BORDER_SIZE - 1.0f, pos.y + ICON_BORDER_SIZE - 1.0f), fill_color); - ImGui::SetCursorPosX(pos.x + ICON_BORDER_SIZE + 4.0f); + ImGui::SetCursorPosX(pos.x + ICON_BORDER_SIZE + GAP_ICON_TEXT); ImGui::AlignTextToFramePadding(); imgui.text(Slic3r::I18N::translate(ExtrusionEntity::role_to_string(role))); } break; } - case EViewType::Height: { break; } + case EViewType::Height: + { + float step_size = m_extrusions.ranges.height.step_size(); + for (int i = Default_Range_Colors_Count - 1; i >= 0; --i) + { + ImVec2 pos(ImGui::GetCursorPosX() + 2.0f, ImGui::GetCursorPosY() + 2.0f); + draw_list->AddRect(ImVec2(pos.x, pos.y), ImVec2(pos.x + ICON_BORDER_SIZE, pos.y + ICON_BORDER_SIZE), ICON_BORDER_COLOR, 0.0f, 0); + const std::array& color = m_extrusions.ranges.colors[i]; + ImU32 fill_color = ImGui::GetColorU32(ImVec4(color[0], color[1], color[2], color[3])); + draw_list->AddRectFilled(ImVec2(pos.x + 1.0f, pos.y + 1.0f), ImVec2(pos.x + ICON_BORDER_SIZE - 1.0f, pos.y + ICON_BORDER_SIZE - 1.0f), fill_color); + ImGui::SetCursorPosX(pos.x + ICON_BORDER_SIZE + GAP_ICON_TEXT); + ImGui::AlignTextToFramePadding(); + char buf[1024]; + ::sprintf(buf, "%.*f", 3, m_extrusions.ranges.height.min + static_cast(i) * step_size); + imgui.text(buf); + } + + break; + } case EViewType::Width: { break; } case EViewType::Feedrate: { break; } case EViewType::FanSpeed: { break; } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 4ee187144c..5185f54090 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -7,6 +7,8 @@ #include "3DScene.hpp" #include "libslic3r/GCode/GCodeProcessor.hpp" +#include + namespace Slic3r { class Print; namespace GUI { @@ -14,6 +16,8 @@ namespace GUI { class GCodeViewer { static const std::array, erCount> Default_Extrusion_Role_Colors; + static const size_t Default_Range_Colors_Count = 10; + static const std::array, Default_Range_Colors_Count> Default_Range_Colors; // buffer containing vertices data struct VBuffer @@ -29,14 +33,16 @@ class GCodeViewer static size_t vertex_size_bytes() { return vertex_size() * sizeof(float); } }; + // Used to identify different toolpath sub-types inside a IBuffer struct Path { GCodeProcessor::EMoveType type{ GCodeProcessor::EMoveType::Noop }; ExtrusionRole role{ erNone }; unsigned int first{ 0 }; unsigned int last{ 0 }; + float height{ 0.0f }; - bool matches(GCodeProcessor::EMoveType type, ExtrusionRole role) const { return this->type == type && this->role == role; } + bool matches(const GCodeProcessor::MoveVertex& move) const { return type == move.type && role == move.extrusion_role && height == move.height; } }; // buffer containing indices data and shader for a specific toolpath type @@ -52,7 +58,7 @@ class GCodeViewer void reset(); bool init_shader(const std::string& vertex_shader_src, const std::string& fragment_shader_src); - void add_path(GCodeProcessor::EMoveType type, ExtrusionRole role); + void add_path(GCodeProcessor::EMoveType type, ExtrusionRole role, float height); }; struct Shells @@ -62,10 +68,55 @@ class GCodeViewer Shader shader; }; + // helper to render extrusion paths struct Extrusions { + struct Range + { + float min; + float max; + + Range() { reset(); } + + void update_from(const float value) + { + min = std::min(min, value); + max = std::max(max, value); + } + + void reset() + { + min = FLT_MAX; + max = -FLT_MAX; + } + + float step_size() const { return (max - min) / static_cast(Default_Range_Colors_Count); } + std::array get_color_at(float value, const std::array, Default_Range_Colors_Count>& colors) const; + }; + + struct Ranges + { + std::array, Default_Range_Colors_Count> colors; + + // Color mapping by layer height. + Range height; +// // Color mapping by extrusion width. +// Range width; +// // Color mapping by feedrate. +// MultiRange feedrate; +// // Color mapping by fan speed. +// Range fan_speed; +// // Color mapping by volumetric extrusion rate. +// Range volumetric_rate; + + void reset() { + height.reset(); + } + }; + std::array, erCount> role_colors; unsigned int role_visibility_flags{ 0 }; + Ranges ranges; void reset_role_visibility_flags() { role_visibility_flags = 0; @@ -75,6 +126,8 @@ class GCodeViewer } } + void reset_ranges() { ranges.reset(); } + static bool is_role_visible(unsigned int flags, ExtrusionRole role) { return role < erCount && (flags & (1 << role)) != 0; } @@ -113,6 +166,7 @@ public: bool init() { m_extrusions.role_colors = Default_Extrusion_Role_Colors; + m_extrusions.ranges.colors = Default_Range_Colors; set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Extrude, true); return init_shaders(); } From aee80dbd01ff0ba8d2ae7a3881e3fd21d22835cd Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 20 Apr 2020 13:24:25 +0200 Subject: [PATCH 025/503] GCodeViewer -> Extrusion toolpaths colored by width --- src/libslic3r/Technologies.hpp | 2 +- src/slic3r/GUI/GCodeViewer.cpp | 75 +++++++++++++++++++--------------- src/slic3r/GUI/GCodeViewer.hpp | 15 ++++--- 3 files changed, 52 insertions(+), 40 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 51d5b0fa70..1ed939ba3e 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -58,7 +58,7 @@ // Enable G-Code viewer #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) -#define ENABLE_GCODE_VIEWER_DEBUG_OUTPUT (1 && ENABLE_GCODE_VIEWER) +#define ENABLE_GCODE_VIEWER_DEBUG_OUTPUT (0 && ENABLE_GCODE_VIEWER) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 10282642c3..b240d76f55 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -59,10 +59,10 @@ bool GCodeViewer::IBuffer::init_shader(const std::string& vertex_shader_src, con return true; } -void GCodeViewer::IBuffer::add_path(GCodeProcessor::EMoveType type, ExtrusionRole role, float height) +void GCodeViewer::IBuffer::add_path(const GCodeProcessor::MoveVertex& move) { unsigned int id = static_cast(data.size()); - paths.push_back({ type, role, id, id, height }); + paths.push_back({ move.type, move.extrusion_role, id, id, move.height, move.width }); } std::array GCodeViewer::Extrusions::Range::get_color_at(float value, const std::array, Default_Range_Colors_Count>& colors) const @@ -74,8 +74,8 @@ std::array GCodeViewer::Extrusions::Range::get_color_at(float value, c const size_t color_max_idx = colors.size() - 1; // Compute the two colors just below (low) and above (high) the input value - const size_t color_low_idx = std::clamp(static_cast(global_t), std::size_t{ 0 }, color_max_idx); - const size_t color_high_idx = std::clamp(color_low_idx + 1, std::size_t{ 0 }, color_max_idx); + const size_t color_low_idx = std::clamp(static_cast(global_t), 0, color_max_idx); + const size_t color_high_idx = std::clamp(color_low_idx + 1, 0, color_max_idx); // Compute how far the value is between the low and high colors so that they can be interpolated const float local_t = std::min(global_t - static_cast(color_low_idx), 1.0f); // upper limit of 1.0f @@ -279,7 +279,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) case GCodeProcessor::EMoveType::Retract: case GCodeProcessor::EMoveType::Unretract: { - buffer.add_path(curr.type, curr.extrusion_role, curr.height); + buffer.add_path(curr); buffer.data.push_back(static_cast(i)); break; } @@ -288,12 +288,13 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) { if (prev.type != curr.type || !buffer.paths.back().matches(curr)) { - buffer.add_path(curr.type, curr.extrusion_role, curr.height); + buffer.add_path(curr); buffer.data.push_back(static_cast(i - 1)); if (curr.type == GCodeProcessor::EMoveType::Extrude) { m_extrusions.ranges.height.update_from(curr.height); + m_extrusions.ranges.width.update_from(curr.width); } } @@ -412,17 +413,9 @@ void GCodeViewer::render_toolpaths() const std::array color; switch (m_view_type) { - case EViewType::FeatureType: - { - color = m_extrusions.role_colors[static_cast(path.role)]; - break; - } - case EViewType::Height: - { - color = m_extrusions.ranges.height.get_color_at(path.height, m_extrusions.ranges.colors); - break; - } - case EViewType::Width: + case EViewType::FeatureType: { color = m_extrusions.role_colors[static_cast(path.role)]; break; } + case EViewType::Height: { color = m_extrusions.ranges.height.get_color_at(path.height, m_extrusions.ranges.colors); break; } + case EViewType::Width: { color = m_extrusions.ranges.width.get_color_at(path.width, m_extrusions.ranges.colors); break; } case EViewType::Feedrate: case EViewType::FanSpeed: case EViewType::VolumetricRate: @@ -575,6 +568,32 @@ void GCodeViewer::render_overlay() const ImDrawList* draw_list = ImGui::GetWindowDrawList(); + auto add_range = [this, draw_list, &imgui](const Extrusions::Range& range) { + auto add_item = [this, draw_list, &imgui](int i, float value) { + ImVec2 pos(ImGui::GetCursorPosX() + 2.0f, ImGui::GetCursorPosY() + 2.0f); + draw_list->AddRect(ImVec2(pos.x, pos.y), ImVec2(pos.x + ICON_BORDER_SIZE, pos.y + ICON_BORDER_SIZE), ICON_BORDER_COLOR, 0.0f, 0); + const std::array& color = m_extrusions.ranges.colors[i]; + ImU32 fill_color = ImGui::GetColorU32(ImVec4(color[0], color[1], color[2], color[3])); + draw_list->AddRectFilled(ImVec2(pos.x + 1.0f, pos.y + 1.0f), ImVec2(pos.x + ICON_BORDER_SIZE - 1.0f, pos.y + ICON_BORDER_SIZE - 1.0f), fill_color); + ImGui::SetCursorPosX(pos.x + ICON_BORDER_SIZE + GAP_ICON_TEXT); + ImGui::AlignTextToFramePadding(); + char buf[1024]; + ::sprintf(buf, "%.*f", 3, value); + imgui.text(buf); + }; + + float step_size = range.step_size(); + if (step_size == 0.0f) + add_item(0, range.min); + else + { + for (int i = Default_Range_Colors_Count - 1; i >= 0; --i) + { + add_item(i, range.min + static_cast(i) * step_size); + } + } + }; + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); switch (m_view_type) { @@ -610,24 +629,14 @@ void GCodeViewer::render_overlay() const } case EViewType::Height: { - float step_size = m_extrusions.ranges.height.step_size(); - for (int i = Default_Range_Colors_Count - 1; i >= 0; --i) - { - ImVec2 pos(ImGui::GetCursorPosX() + 2.0f, ImGui::GetCursorPosY() + 2.0f); - draw_list->AddRect(ImVec2(pos.x, pos.y), ImVec2(pos.x + ICON_BORDER_SIZE, pos.y + ICON_BORDER_SIZE), ICON_BORDER_COLOR, 0.0f, 0); - const std::array& color = m_extrusions.ranges.colors[i]; - ImU32 fill_color = ImGui::GetColorU32(ImVec4(color[0], color[1], color[2], color[3])); - draw_list->AddRectFilled(ImVec2(pos.x + 1.0f, pos.y + 1.0f), ImVec2(pos.x + ICON_BORDER_SIZE - 1.0f, pos.y + ICON_BORDER_SIZE - 1.0f), fill_color); - ImGui::SetCursorPosX(pos.x + ICON_BORDER_SIZE + GAP_ICON_TEXT); - ImGui::AlignTextToFramePadding(); - char buf[1024]; - ::sprintf(buf, "%.*f", 3, m_extrusions.ranges.height.min + static_cast(i) * step_size); - imgui.text(buf); - } - + add_range(m_extrusions.ranges.height); + break; + } + case EViewType::Width: + { + add_range(m_extrusions.ranges.width); break; } - case EViewType::Width: { break; } case EViewType::Feedrate: { break; } case EViewType::FanSpeed: { break; } case EViewType::VolumetricRate: { break; } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 5185f54090..342f3ba901 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -41,8 +41,11 @@ class GCodeViewer unsigned int first{ 0 }; unsigned int last{ 0 }; float height{ 0.0f }; + float width{ 0.0f }; - bool matches(const GCodeProcessor::MoveVertex& move) const { return type == move.type && role == move.extrusion_role && height == move.height; } + bool matches(const GCodeProcessor::MoveVertex& move) const { + return type == move.type && role == move.extrusion_role && height == move.height && width == move.width; + } }; // buffer containing indices data and shader for a specific toolpath type @@ -57,8 +60,7 @@ class GCodeViewer void reset(); bool init_shader(const std::string& vertex_shader_src, const std::string& fragment_shader_src); - - void add_path(GCodeProcessor::EMoveType type, ExtrusionRole role, float height); + void add_path(const GCodeProcessor::MoveVertex& move); }; struct Shells @@ -90,7 +92,7 @@ class GCodeViewer max = -FLT_MAX; } - float step_size() const { return (max - min) / static_cast(Default_Range_Colors_Count); } + float step_size() const { return (max - min) / (static_cast(Default_Range_Colors_Count) - 1.0f); } std::array get_color_at(float value, const std::array, Default_Range_Colors_Count>& colors) const; }; @@ -100,8 +102,8 @@ class GCodeViewer // Color mapping by layer height. Range height; -// // Color mapping by extrusion width. -// Range width; + // Color mapping by extrusion width. + Range width; // // Color mapping by feedrate. // MultiRange feedrate; // // Color mapping by fan speed. @@ -111,6 +113,7 @@ class GCodeViewer void reset() { height.reset(); + width.reset(); } }; From dc3c5db9fe6251306159889ffed1b8ba3035aee5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 20 Apr 2020 13:44:14 +0200 Subject: [PATCH 026/503] GCodeViewer -> Use rgb instead of rgba colors --- resources/shaders/extrusions.fs | 4 +- resources/shaders/retractions.fs | 4 +- resources/shaders/toolchanges.fs | 4 +- resources/shaders/travels.fs | 4 +- resources/shaders/unretractions.fs | 4 +- src/slic3r/GUI/GCodeViewer.cpp | 82 +++++++++++++++--------------- src/slic3r/GUI/GCodeViewer.hpp | 10 ++-- 7 files changed, 56 insertions(+), 56 deletions(-) diff --git a/resources/shaders/extrusions.fs b/resources/shaders/extrusions.fs index 046dade8a9..fc81e487fb 100644 --- a/resources/shaders/extrusions.fs +++ b/resources/shaders/extrusions.fs @@ -13,7 +13,7 @@ const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); #define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) -uniform vec4 uniform_color; +uniform vec3 uniform_color; varying vec3 eye_position; varying vec3 eye_normal; @@ -41,5 +41,5 @@ void main() // if (world_normal_z < 0.0) // intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); - gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); + gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color * intensity.x, 1.0); } diff --git a/resources/shaders/retractions.fs b/resources/shaders/retractions.fs index 046dade8a9..fc81e487fb 100644 --- a/resources/shaders/retractions.fs +++ b/resources/shaders/retractions.fs @@ -13,7 +13,7 @@ const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); #define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) -uniform vec4 uniform_color; +uniform vec3 uniform_color; varying vec3 eye_position; varying vec3 eye_normal; @@ -41,5 +41,5 @@ void main() // if (world_normal_z < 0.0) // intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); - gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); + gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color * intensity.x, 1.0); } diff --git a/resources/shaders/toolchanges.fs b/resources/shaders/toolchanges.fs index 046dade8a9..fc81e487fb 100644 --- a/resources/shaders/toolchanges.fs +++ b/resources/shaders/toolchanges.fs @@ -13,7 +13,7 @@ const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); #define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) -uniform vec4 uniform_color; +uniform vec3 uniform_color; varying vec3 eye_position; varying vec3 eye_normal; @@ -41,5 +41,5 @@ void main() // if (world_normal_z < 0.0) // intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); - gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); + gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color * intensity.x, 1.0); } diff --git a/resources/shaders/travels.fs b/resources/shaders/travels.fs index 046dade8a9..fc81e487fb 100644 --- a/resources/shaders/travels.fs +++ b/resources/shaders/travels.fs @@ -13,7 +13,7 @@ const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); #define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) -uniform vec4 uniform_color; +uniform vec3 uniform_color; varying vec3 eye_position; varying vec3 eye_normal; @@ -41,5 +41,5 @@ void main() // if (world_normal_z < 0.0) // intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); - gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); + gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color * intensity.x, 1.0); } diff --git a/resources/shaders/unretractions.fs b/resources/shaders/unretractions.fs index 046dade8a9..fc81e487fb 100644 --- a/resources/shaders/unretractions.fs +++ b/resources/shaders/unretractions.fs @@ -13,7 +13,7 @@ const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); #define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) -uniform vec4 uniform_color; +uniform vec3 uniform_color; varying vec3 eye_position; varying vec3 eye_normal; @@ -41,5 +41,5 @@ void main() // if (world_normal_z < 0.0) // intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); - gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); + gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color * intensity.x, 1.0); } diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index b240d76f55..52d3927982 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -65,7 +65,7 @@ void GCodeViewer::IBuffer::add_path(const GCodeProcessor::MoveVertex& move) paths.push_back({ move.type, move.extrusion_role, id, id, move.height, move.width }); } -std::array GCodeViewer::Extrusions::Range::get_color_at(float value, const std::array, Default_Range_Colors_Count>& colors) const +std::array GCodeViewer::Extrusions::Range::get_color_at(float value, const std::array, Default_Range_Colors_Count>& colors) const { // Input value scaled to the color range const float step = step_size(); @@ -81,43 +81,43 @@ std::array GCodeViewer::Extrusions::Range::get_color_at(float value, c const float local_t = std::min(global_t - static_cast(color_low_idx), 1.0f); // upper limit of 1.0f // Interpolate between the low and high colors in RGB space to find exactly which color the input value should get - std::array ret; - for (unsigned int i = 0; i < 4; ++i) + std::array ret; + for (unsigned int i = 0; i < 3; ++i) { ret[i] = lerp(colors[color_low_idx][i], colors[color_high_idx][i], local_t); } return ret; } -const std::array, erCount> GCodeViewer::Default_Extrusion_Role_Colors {{ - { 1.00f, 1.00f, 1.00f, 1.0f }, // erNone - { 1.00f, 1.00f, 0.40f, 1.0f }, // erPerimeter - { 1.00f, 0.65f, 0.00f, 1.0f }, // erExternalPerimeter - { 0.00f, 0.00f, 1.00f, 1.0f }, // erOverhangPerimeter - { 0.69f, 0.19f, 0.16f, 1.0f }, // erInternalInfill - { 0.84f, 0.20f, 0.84f, 1.0f }, // erSolidInfill - { 1.00f, 0.10f, 0.10f, 1.0f }, // erTopSolidInfill - { 0.60f, 0.60f, 1.00f, 1.0f }, // erBridgeInfill - { 1.00f, 1.00f, 1.00f, 1.0f }, // erGapFill - { 0.52f, 0.48f, 0.13f, 1.0f }, // erSkirt - { 0.00f, 1.00f, 0.00f, 1.0f }, // erSupportMaterial - { 0.00f, 0.50f, 0.00f, 1.0f }, // erSupportMaterialInterface - { 0.70f, 0.89f, 0.67f, 1.0f }, // erWipeTower - { 0.16f, 0.80f, 0.58f, 1.0f }, // erCustom - { 0.00f, 0.00f, 0.00f, 1.0f } // erMixed +const std::array, erCount> GCodeViewer::Default_Extrusion_Role_Colors {{ + { 1.00f, 1.00f, 1.00f }, // erNone + { 1.00f, 1.00f, 0.40f }, // erPerimeter + { 1.00f, 0.65f, 0.00f }, // erExternalPerimeter + { 0.00f, 0.00f, 1.00f }, // erOverhangPerimeter + { 0.69f, 0.19f, 0.16f }, // erInternalInfill + { 0.84f, 0.20f, 0.84f }, // erSolidInfill + { 1.00f, 0.10f, 0.10f }, // erTopSolidInfill + { 0.60f, 0.60f, 1.00f }, // erBridgeInfill + { 1.00f, 1.00f, 1.00f }, // erGapFill + { 0.52f, 0.48f, 0.13f }, // erSkirt + { 0.00f, 1.00f, 0.00f }, // erSupportMaterial + { 0.00f, 0.50f, 0.00f }, // erSupportMaterialInterface + { 0.70f, 0.89f, 0.67f }, // erWipeTower + { 0.16f, 0.80f, 0.58f }, // erCustom + { 0.00f, 0.00f, 0.00f } // erMixed }}; -const std::array, GCodeViewer::Default_Range_Colors_Count> GCodeViewer::Default_Range_Colors {{ - { 0.043f, 0.173f, 0.478f, 1.0f }, - { 0.075f, 0.349f, 0.522f, 1.0f }, - { 0.110f, 0.533f, 0.569f, 1.0f }, - { 0.016f, 0.839f, 0.059f, 1.0f }, - { 0.667f, 0.949f, 0.000f, 1.0f }, - { 0.988f, 0.975f, 0.012f, 1.0f }, - { 0.961f, 0.808f, 0.039f, 1.0f }, - { 0.890f, 0.533f, 0.125f, 1.0f }, - { 0.820f, 0.408f, 0.188f, 1.0f }, - { 0.761f, 0.322f, 0.235f, 1.0f } +const std::array, GCodeViewer::Default_Range_Colors_Count> GCodeViewer::Default_Range_Colors {{ + { 0.043f, 0.173f, 0.478f }, + { 0.075f, 0.349f, 0.522f }, + { 0.110f, 0.533f, 0.569f }, + { 0.016f, 0.839f, 0.059f }, + { 0.667f, 0.949f, 0.000f }, + { 0.988f, 0.975f, 0.012f }, + { 0.961f, 0.808f, 0.039f }, + { 0.890f, 0.533f, 0.125f }, + { 0.820f, 0.408f, 0.188f }, + { 0.761f, 0.322f, 0.235f } }}; void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized) @@ -410,7 +410,7 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) void GCodeViewer::render_toolpaths() const { auto extrusion_color = [this](const Path& path) { - std::array color; + std::array color; switch (m_view_type) { case EViewType::FeatureType: { color = m_extrusions.role_colors[static_cast(path.role)]; break; } @@ -423,18 +423,18 @@ void GCodeViewer::render_toolpaths() const case EViewType::ColorPrint: default: { - color = { 1.0f, 1.0f, 1.0f, 1.0f }; + color = { 1.0f, 1.0f, 1.0f }; break; } } return color; }; - auto set_color = [](GLint current_program_id, const std::array& color) { + auto set_color = [](GLint current_program_id, const std::array& color) { if (current_program_id > 0) { GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1; if (color_id >= 0) { - glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)color.data())); + glsafe(::glUniform3fv(color_id, 1, (const GLfloat*)color.data())); return; } } @@ -478,7 +478,7 @@ void GCodeViewer::render_toolpaths() const { case GCodeProcessor::EMoveType::Tool_change: { - std::array color = { 1.0f, 1.0f, 1.0f, 1.0f }; + std::array color = { 1.0f, 1.0f, 1.0f }; set_color(current_program_id, color); glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); glsafe(::glDrawElements(GL_POINTS, (GLsizei)buffer.data_size, GL_UNSIGNED_INT, nullptr)); @@ -487,7 +487,7 @@ void GCodeViewer::render_toolpaths() const } case GCodeProcessor::EMoveType::Retract: { - std::array color = { 1.0f, 0.0f, 1.0f, 1.0f }; + std::array color = { 1.0f, 0.0f, 1.0f }; set_color(current_program_id, color); glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); glsafe(::glDrawElements(GL_POINTS, (GLsizei)buffer.data_size, GL_UNSIGNED_INT, nullptr)); @@ -496,7 +496,7 @@ void GCodeViewer::render_toolpaths() const } case GCodeProcessor::EMoveType::Unretract: { - std::array color = { 0.0f, 1.0f, 0.0f, 1.0f }; + std::array color = { 0.0f, 1.0f, 0.0f }; set_color(current_program_id, color); glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); glsafe(::glDrawElements(GL_POINTS, (GLsizei)buffer.data_size, GL_UNSIGNED_INT, nullptr)); @@ -517,7 +517,7 @@ void GCodeViewer::render_toolpaths() const } case GCodeProcessor::EMoveType::Travel: { - std::array color = { 1.0f, 1.0f, 0.0f, 1.0f }; + std::array color = { 1.0f, 1.0f, 0.0f }; set_color(current_program_id, color); for (const Path& path : buffer.paths) { @@ -572,8 +572,8 @@ void GCodeViewer::render_overlay() const auto add_item = [this, draw_list, &imgui](int i, float value) { ImVec2 pos(ImGui::GetCursorPosX() + 2.0f, ImGui::GetCursorPosY() + 2.0f); draw_list->AddRect(ImVec2(pos.x, pos.y), ImVec2(pos.x + ICON_BORDER_SIZE, pos.y + ICON_BORDER_SIZE), ICON_BORDER_COLOR, 0.0f, 0); - const std::array& color = m_extrusions.ranges.colors[i]; - ImU32 fill_color = ImGui::GetColorU32(ImVec4(color[0], color[1], color[2], color[3])); + const std::array& color = m_extrusions.ranges.colors[i]; + ImU32 fill_color = ImGui::GetColorU32(ImVec4(color[0], color[1], color[2], 1.0f)); draw_list->AddRectFilled(ImVec2(pos.x + 1.0f, pos.y + 1.0f), ImVec2(pos.x + ICON_BORDER_SIZE - 1.0f, pos.y + ICON_BORDER_SIZE - 1.0f), fill_color); ImGui::SetCursorPosX(pos.x + ICON_BORDER_SIZE + GAP_ICON_TEXT); ImGui::AlignTextToFramePadding(); @@ -618,7 +618,7 @@ void GCodeViewer::render_overlay() const { ImVec2 pos(ImGui::GetCursorPosX() + 2.0f, ImGui::GetCursorPosY() + 2.0f); draw_list->AddRect(ImVec2(pos.x, pos.y), ImVec2(pos.x + ICON_BORDER_SIZE, pos.y + ICON_BORDER_SIZE), ICON_BORDER_COLOR, 0.0f, 0); - const std::array& color = m_extrusions.role_colors[static_cast(role)]; + const std::array& color = m_extrusions.role_colors[static_cast(role)]; ImU32 fill_color = ImGui::GetColorU32(ImVec4(color[0], color[1], color[2], color[3])); draw_list->AddRectFilled(ImVec2(pos.x + 1.0f, pos.y + 1.0f), ImVec2(pos.x + ICON_BORDER_SIZE - 1.0f, pos.y + ICON_BORDER_SIZE - 1.0f), fill_color); ImGui::SetCursorPosX(pos.x + ICON_BORDER_SIZE + GAP_ICON_TEXT); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 342f3ba901..39361050b8 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -15,9 +15,9 @@ namespace GUI { class GCodeViewer { - static const std::array, erCount> Default_Extrusion_Role_Colors; + static const std::array, erCount> Default_Extrusion_Role_Colors; static const size_t Default_Range_Colors_Count = 10; - static const std::array, Default_Range_Colors_Count> Default_Range_Colors; + static const std::array, Default_Range_Colors_Count> Default_Range_Colors; // buffer containing vertices data struct VBuffer @@ -93,12 +93,12 @@ class GCodeViewer } float step_size() const { return (max - min) / (static_cast(Default_Range_Colors_Count) - 1.0f); } - std::array get_color_at(float value, const std::array, Default_Range_Colors_Count>& colors) const; + std::array get_color_at(float value, const std::array, Default_Range_Colors_Count>& colors) const; }; struct Ranges { - std::array, Default_Range_Colors_Count> colors; + std::array, Default_Range_Colors_Count> colors; // Color mapping by layer height. Range height; @@ -117,7 +117,7 @@ class GCodeViewer } }; - std::array, erCount> role_colors; + std::array, erCount> role_colors; unsigned int role_visibility_flags{ 0 }; Ranges ranges; From 3ba6ac7836606049394704621768c6a26196bd51 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 20 Apr 2020 16:04:59 +0200 Subject: [PATCH 027/503] GCodeViewer -> Extrusion toolpaths colored by feedrate and ranges calculations dependent on travel paths visibility --- src/slic3r/GUI/GCodeViewer.cpp | 82 +++++++++++++++++++++++----------- src/slic3r/GUI/GCodeViewer.hpp | 11 +++-- src/slic3r/GUI/GLCanvas3D.cpp | 7 ++- src/slic3r/GUI/GLCanvas3D.hpp | 3 +- src/slic3r/GUI/GUI_Preview.cpp | 3 +- 5 files changed, 74 insertions(+), 32 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 52d3927982..e14969eddc 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -62,7 +62,7 @@ bool GCodeViewer::IBuffer::init_shader(const std::string& vertex_shader_src, con void GCodeViewer::IBuffer::add_path(const GCodeProcessor::MoveVertex& move) { unsigned int id = static_cast(data.size()); - paths.push_back({ move.type, move.extrusion_role, id, id, move.height, move.width }); + paths.push_back({ move.type, move.extrusion_role, id, id, move.height, move.width, move.feedrate }); } std::array GCodeViewer::Extrusions::Range::get_color_at(float value, const std::array, Default_Range_Colors_Count>& colors) const @@ -78,7 +78,7 @@ std::array GCodeViewer::Extrusions::Range::get_color_at(float value, c const size_t color_high_idx = std::clamp(color_low_idx + 1, 0, color_max_idx); // Compute how far the value is between the low and high colors so that they can be interpolated - const float local_t = std::min(global_t - static_cast(color_low_idx), 1.0f); // upper limit of 1.0f + const float local_t = std::clamp(global_t - static_cast(color_low_idx), 0.0f, 1.0f); // Interpolate between the low and high colors in RGB space to find exactly which color the input value should get std::array ret; @@ -134,6 +134,42 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& load_shells(print, initialized); } +void GCodeViewer::refresh_toolpaths_ranges(const GCodeProcessor::Result& gcode_result) +{ + if (m_vertices.vertices_count == 0) + return; + + m_extrusions.reset_ranges(); + + for (size_t i = 0; i < m_vertices.vertices_count; ++i) + { + // skip first vertex + if (i == 0) + continue; + + const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i]; + + switch (curr.type) + { + case GCodeProcessor::EMoveType::Extrude: + case GCodeProcessor::EMoveType::Travel: + { + if (m_buffers[buffer_id(curr.type)].visible) + { + m_extrusions.ranges.height.update_from(curr.height); + m_extrusions.ranges.width.update_from(curr.width); + m_extrusions.ranges.feedrate.update_from(curr.feedrate); + } + break; + } + default: + { + break; + } + } + } +} + void GCodeViewer::reset() { m_vertices.reset(); @@ -290,12 +326,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) { buffer.add_path(curr); buffer.data.push_back(static_cast(i - 1)); - - if (curr.type == GCodeProcessor::EMoveType::Extrude) - { - m_extrusions.ranges.height.update_from(curr.height); - m_extrusions.ranges.width.update_from(curr.width); - } } buffer.paths.back().last = static_cast(buffer.data.size()); @@ -413,19 +443,15 @@ void GCodeViewer::render_toolpaths() const std::array color; switch (m_view_type) { - case EViewType::FeatureType: { color = m_extrusions.role_colors[static_cast(path.role)]; break; } - case EViewType::Height: { color = m_extrusions.ranges.height.get_color_at(path.height, m_extrusions.ranges.colors); break; } - case EViewType::Width: { color = m_extrusions.ranges.width.get_color_at(path.width, m_extrusions.ranges.colors); break; } - case EViewType::Feedrate: + case EViewType::FeatureType: { color = m_extrusions.role_colors[static_cast(path.role)]; break; } + case EViewType::Height: { color = m_extrusions.ranges.height.get_color_at(path.height, m_extrusions.ranges.colors); break; } + case EViewType::Width: { color = m_extrusions.ranges.width.get_color_at(path.width, m_extrusions.ranges.colors); break; } + case EViewType::Feedrate: { color = m_extrusions.ranges.feedrate.get_color_at(path.feedrate, m_extrusions.ranges.colors); break; } case EViewType::FanSpeed: case EViewType::VolumetricRate: case EViewType::Tool: case EViewType::ColorPrint: - default: - { - color = { 1.0f, 1.0f, 1.0f }; - break; - } + default: { color = { 1.0f, 1.0f, 1.0f }; break; } } return color; }; @@ -568,8 +594,8 @@ void GCodeViewer::render_overlay() const ImDrawList* draw_list = ImGui::GetWindowDrawList(); - auto add_range = [this, draw_list, &imgui](const Extrusions::Range& range) { - auto add_item = [this, draw_list, &imgui](int i, float value) { + auto add_range = [this, draw_list, &imgui](const Extrusions::Range& range, unsigned int decimals) { + auto add_item = [this, draw_list, &imgui](int i, float value, unsigned int decimals) { ImVec2 pos(ImGui::GetCursorPosX() + 2.0f, ImGui::GetCursorPosY() + 2.0f); draw_list->AddRect(ImVec2(pos.x, pos.y), ImVec2(pos.x + ICON_BORDER_SIZE, pos.y + ICON_BORDER_SIZE), ICON_BORDER_COLOR, 0.0f, 0); const std::array& color = m_extrusions.ranges.colors[i]; @@ -578,18 +604,18 @@ void GCodeViewer::render_overlay() const ImGui::SetCursorPosX(pos.x + ICON_BORDER_SIZE + GAP_ICON_TEXT); ImGui::AlignTextToFramePadding(); char buf[1024]; - ::sprintf(buf, "%.*f", 3, value); + ::sprintf(buf, "%.*f", decimals, value); imgui.text(buf); }; float step_size = range.step_size(); if (step_size == 0.0f) - add_item(0, range.min); + add_item(0, range.min, decimals); else { for (int i = Default_Range_Colors_Count - 1; i >= 0; --i) { - add_item(i, range.min + static_cast(i) * step_size); + add_item(i, range.min + static_cast(i) * step_size, decimals); } } }; @@ -619,7 +645,7 @@ void GCodeViewer::render_overlay() const ImVec2 pos(ImGui::GetCursorPosX() + 2.0f, ImGui::GetCursorPosY() + 2.0f); draw_list->AddRect(ImVec2(pos.x, pos.y), ImVec2(pos.x + ICON_BORDER_SIZE, pos.y + ICON_BORDER_SIZE), ICON_BORDER_COLOR, 0.0f, 0); const std::array& color = m_extrusions.role_colors[static_cast(role)]; - ImU32 fill_color = ImGui::GetColorU32(ImVec4(color[0], color[1], color[2], color[3])); + ImU32 fill_color = ImGui::GetColorU32(ImVec4(color[0], color[1], color[2], 1.0)); draw_list->AddRectFilled(ImVec2(pos.x + 1.0f, pos.y + 1.0f), ImVec2(pos.x + ICON_BORDER_SIZE - 1.0f, pos.y + ICON_BORDER_SIZE - 1.0f), fill_color); ImGui::SetCursorPosX(pos.x + ICON_BORDER_SIZE + GAP_ICON_TEXT); ImGui::AlignTextToFramePadding(); @@ -629,15 +655,19 @@ void GCodeViewer::render_overlay() const } case EViewType::Height: { - add_range(m_extrusions.ranges.height); + add_range(m_extrusions.ranges.height, 3); break; } case EViewType::Width: { - add_range(m_extrusions.ranges.width); + add_range(m_extrusions.ranges.width, 3); + break; + } + case EViewType::Feedrate: + { + add_range(m_extrusions.ranges.feedrate, 1); break; } - case EViewType::Feedrate: { break; } case EViewType::FanSpeed: { break; } case EViewType::VolumetricRate: { break; } case EViewType::Tool: { break; } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 39361050b8..34b81ad7a6 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -42,9 +42,10 @@ class GCodeViewer unsigned int last{ 0 }; float height{ 0.0f }; float width{ 0.0f }; + float feedrate{ 0.0f }; bool matches(const GCodeProcessor::MoveVertex& move) const { - return type == move.type && role == move.extrusion_role && height == move.height && width == move.width; + return type == move.type && role == move.extrusion_role && height == move.height && width == move.width && feedrate == move.feedrate; } }; @@ -104,8 +105,8 @@ class GCodeViewer Range height; // Color mapping by extrusion width. Range width; -// // Color mapping by feedrate. -// MultiRange feedrate; + // Color mapping by feedrate. + Range feedrate; // // Color mapping by fan speed. // Range fan_speed; // // Color mapping by volumetric extrusion rate. @@ -114,6 +115,7 @@ class GCodeViewer void reset() { height.reset(); width.reset(); + feedrate.reset(); } }; @@ -173,7 +175,10 @@ public: set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Extrude, true); return init_shaders(); } + void load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized); + void refresh_toolpaths_ranges(const GCodeProcessor::Result& gcode_result); + void reset(); void render() const; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 8c3af4a587..23cccb76f9 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2833,7 +2833,7 @@ static void load_gcode_retractions(const GCodePreviewData::Retraction& retractio #endif // !ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER -void GLCanvas3D::load_gcode_preview_2(const GCodeProcessor::Result& gcode_result) +void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result) { #if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT static unsigned int last_result_id = 0; @@ -2852,6 +2852,11 @@ void GLCanvas3D::load_gcode_preview_2(const GCodeProcessor::Result& gcode_result #endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT m_gcode_viewer.load(gcode_result, *this->fff_print(), m_initialized); } + +void GLCanvas3D::refresh_toolpaths_ranges(const GCodeProcessor::Result& gcode_result) +{ + m_gcode_viewer.refresh_toolpaths_ranges(gcode_result); +} #endif // ENABLE_GCODE_VIEWER #if !ENABLE_GCODE_VIEWER diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index af1f6149c8..312eeaa3b6 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -664,7 +664,8 @@ public: void reload_scene(bool refresh_immediately, bool force_full_scene_refresh = false); #if ENABLE_GCODE_VIEWER - void load_gcode_preview_2(const GCodeProcessor::Result& gcode_result); + void load_gcode_preview(const GCodeProcessor::Result& gcode_result); + void refresh_toolpaths_ranges(const GCodeProcessor::Result& gcode_result); #else void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector& str_tool_colors); #endif // ENABLE_GCODE_VIEWER diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 562058dd95..2060511491 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -984,7 +984,8 @@ void Preview::load_print_as_fff(bool keep_z_range) if (gcode_preview_data_valid) { // Load the real G-code preview. #if ENABLE_GCODE_VIEWER - m_canvas->load_gcode_preview_2(*m_gcode_result); + m_canvas->load_gcode_preview(*m_gcode_result); + m_canvas->refresh_toolpaths_ranges(*m_gcode_result); #else m_canvas->load_gcode_preview(*m_gcode_preview_data, colors); #endif // ENABLE_GCODE_VIEWER From 53d758639f5966b524ed6d9a9cb4008123e612f3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 21 Apr 2020 09:06:43 +0200 Subject: [PATCH 028/503] GCodeViewer -> Extrusion toolpaths colored by fan speed --- src/slic3r/GUI/GCodeViewer.cpp | 33 +++++++++++---------------------- src/slic3r/GUI/GCodeViewer.hpp | 8 +++++--- 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index e14969eddc..4cffb39bd5 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -62,12 +62,12 @@ bool GCodeViewer::IBuffer::init_shader(const std::string& vertex_shader_src, con void GCodeViewer::IBuffer::add_path(const GCodeProcessor::MoveVertex& move) { unsigned int id = static_cast(data.size()); - paths.push_back({ move.type, move.extrusion_role, id, id, move.height, move.width, move.feedrate }); + paths.push_back({ move.type, move.extrusion_role, id, id, move.height, move.width, move.feedrate, move.fan_speed }); } std::array GCodeViewer::Extrusions::Range::get_color_at(float value, const std::array, Default_Range_Colors_Count>& colors) const { - // Input value scaled to the color range + // Input value scaled to the colors range const float step = step_size(); const float global_t = (step != 0.0f) ? std::max(0.0f, value - min) / step : 0.0f; // lower limit of 0.0f @@ -80,7 +80,7 @@ std::array GCodeViewer::Extrusions::Range::get_color_at(float value, c // Compute how far the value is between the low and high colors so that they can be interpolated const float local_t = std::clamp(global_t - static_cast(color_low_idx), 0.0f, 1.0f); - // Interpolate between the low and high colors in RGB space to find exactly which color the input value should get + // Interpolate between the low and high colors to find exactly which color the input value should get std::array ret; for (unsigned int i = 0; i < 3; ++i) { @@ -108,7 +108,7 @@ const std::array, erCount> GCodeViewer::Default_Extrusion_R }}; const std::array, GCodeViewer::Default_Range_Colors_Count> GCodeViewer::Default_Range_Colors {{ - { 0.043f, 0.173f, 0.478f }, + { 0.043f, 0.173f, 0.478f }, // bluish { 0.075f, 0.349f, 0.522f }, { 0.110f, 0.533f, 0.569f }, { 0.016f, 0.839f, 0.059f }, @@ -117,7 +117,7 @@ const std::array, GCodeViewer::Default_Range_Colors_Count> { 0.961f, 0.808f, 0.039f }, { 0.890f, 0.533f, 0.125f }, { 0.820f, 0.408f, 0.188f }, - { 0.761f, 0.322f, 0.235f } + { 0.761f, 0.322f, 0.235f } // reddish }}; void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized) @@ -159,6 +159,7 @@ void GCodeViewer::refresh_toolpaths_ranges(const GCodeProcessor::Result& gcode_r m_extrusions.ranges.height.update_from(curr.height); m_extrusions.ranges.width.update_from(curr.width); m_extrusions.ranges.feedrate.update_from(curr.feedrate); + m_extrusions.ranges.fan_speed.update_from(curr.fan_speed); } break; } @@ -447,7 +448,7 @@ void GCodeViewer::render_toolpaths() const case EViewType::Height: { color = m_extrusions.ranges.height.get_color_at(path.height, m_extrusions.ranges.colors); break; } case EViewType::Width: { color = m_extrusions.ranges.width.get_color_at(path.width, m_extrusions.ranges.colors); break; } case EViewType::Feedrate: { color = m_extrusions.ranges.feedrate.get_color_at(path.feedrate, m_extrusions.ranges.colors); break; } - case EViewType::FanSpeed: + case EViewType::FanSpeed: { color = m_extrusions.ranges.fan_speed.get_color_at(path.fan_speed, m_extrusions.ranges.colors); break; } case EViewType::VolumetricRate: case EViewType::Tool: case EViewType::ColorPrint: @@ -653,22 +654,10 @@ void GCodeViewer::render_overlay() const } break; } - case EViewType::Height: - { - add_range(m_extrusions.ranges.height, 3); - break; - } - case EViewType::Width: - { - add_range(m_extrusions.ranges.width, 3); - break; - } - case EViewType::Feedrate: - { - add_range(m_extrusions.ranges.feedrate, 1); - break; - } - case EViewType::FanSpeed: { break; } + case EViewType::Height: { add_range(m_extrusions.ranges.height, 3); break; } + case EViewType::Width: { add_range(m_extrusions.ranges.width, 3); break; } + case EViewType::Feedrate: { add_range(m_extrusions.ranges.feedrate, 1); break; } + case EViewType::FanSpeed: { add_range(m_extrusions.ranges.fan_speed, 0); break; } case EViewType::VolumetricRate: { break; } case EViewType::Tool: { break; } case EViewType::ColorPrint: { break; } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 34b81ad7a6..644f088424 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -43,9 +43,10 @@ class GCodeViewer float height{ 0.0f }; float width{ 0.0f }; float feedrate{ 0.0f }; + float fan_speed{ 0.0f }; bool matches(const GCodeProcessor::MoveVertex& move) const { - return type == move.type && role == move.extrusion_role && height == move.height && width == move.width && feedrate == move.feedrate; + return type == move.type && role == move.extrusion_role && height == move.height && width == move.width && feedrate == move.feedrate && fan_speed == move.fan_speed; } }; @@ -107,8 +108,8 @@ class GCodeViewer Range width; // Color mapping by feedrate. Range feedrate; -// // Color mapping by fan speed. -// Range fan_speed; + // Color mapping by fan speed. + Range fan_speed; // // Color mapping by volumetric extrusion rate. // Range volumetric_rate; @@ -116,6 +117,7 @@ class GCodeViewer height.reset(); width.reset(); feedrate.reset(); + fan_speed.reset(); } }; From 443a511420523a68cf4517fa158405358c1f6f97 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 21 Apr 2020 11:38:42 +0200 Subject: [PATCH 029/503] GCodeViewer -> Extrusion toolpaths colored by volumetric rate --- src/libslic3r/GCode/GCodeProcessor.hpp | 2 + src/slic3r/GUI/GCodeViewer.cpp | 72 ++++++++++---------------- src/slic3r/GUI/GCodeViewer.hpp | 22 +++----- 3 files changed, 36 insertions(+), 60 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 623a2ae3bd..38adce3329 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -80,6 +80,8 @@ namespace Slic3r { float mm3_per_mm{ 0.0f }; float fan_speed{ 0.0f }; // percentage + float volumetric_rate() const { return feedrate * mm3_per_mm; } + std::string to_string() const { std::string str = std::to_string((int)type); diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 4cffb39bd5..e02a809934 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -31,7 +31,10 @@ void GCodeViewer::VBuffer::reset() { // release gpu memory if (vbo_id > 0) + { glsafe(::glDeleteBuffers(1, &vbo_id)); + vbo_id = 0; + } vertices_count = 0; } @@ -40,7 +43,10 @@ void GCodeViewer::IBuffer::reset() { // release gpu memory if (ibo_id > 0) + { glsafe(::glDeleteBuffers(1, &ibo_id)); + ibo_id = 0; + } // release cpu memory data = std::vector(); @@ -62,7 +68,7 @@ bool GCodeViewer::IBuffer::init_shader(const std::string& vertex_shader_src, con void GCodeViewer::IBuffer::add_path(const GCodeProcessor::MoveVertex& move) { unsigned int id = static_cast(data.size()); - paths.push_back({ move.type, move.extrusion_role, id, id, move.height, move.width, move.feedrate, move.fan_speed }); + paths.push_back({ move.type, move.extrusion_role, id, id, move.height, move.width, move.feedrate, move.fan_speed, move.volumetric_rate() }); } std::array GCodeViewer::Extrusions::Range::get_color_at(float value, const std::array, Default_Range_Colors_Count>& colors) const @@ -160,6 +166,7 @@ void GCodeViewer::refresh_toolpaths_ranges(const GCodeProcessor::Result& gcode_r m_extrusions.ranges.width.update_from(curr.width); m_extrusions.ranges.feedrate.update_from(curr.feedrate); m_extrusions.ranges.fan_speed.update_from(curr.fan_speed); + m_extrusions.ranges.volumetric_rate.update_from(curr.volumetric_rate()); } break; } @@ -216,48 +223,21 @@ bool GCodeViewer::init_shaders() for (unsigned char i = begin_id; i < end_id; ++i) { + std::string vertex_shader; + std::string fragment_shader; + switch (buffer_type(i)) { - case GCodeProcessor::EMoveType::Tool_change: - { - if (!m_buffers[i].init_shader("toolchanges.vs", "toolchanges.fs")) - return false; + case GCodeProcessor::EMoveType::Tool_change: { vertex_shader = "toolchanges.vs"; fragment_shader = "toolchanges.fs"; break; } + case GCodeProcessor::EMoveType::Retract: { vertex_shader = "retractions.vs"; fragment_shader = "retractions.fs"; break; } + case GCodeProcessor::EMoveType::Unretract: { vertex_shader = "unretractions.vs"; fragment_shader = "unretractions.fs"; break; } + case GCodeProcessor::EMoveType::Extrude: { vertex_shader = "extrusions.vs"; fragment_shader = "extrusions.fs"; break; } + case GCodeProcessor::EMoveType::Travel: { vertex_shader = "travels.vs"; fragment_shader = "travels.fs"; break; } + default: { break; } + } - break; - } - case GCodeProcessor::EMoveType::Retract: - { - if (!m_buffers[i].init_shader("retractions.vs", "retractions.fs")) - return false; - - break; - } - case GCodeProcessor::EMoveType::Unretract: - { - if (!m_buffers[i].init_shader("unretractions.vs", "unretractions.fs")) - return false; - - break; - } - case GCodeProcessor::EMoveType::Extrude: - { - if (!m_buffers[i].init_shader("extrusions.vs", "extrusions.fs")) - return false; - - break; - } - case GCodeProcessor::EMoveType::Travel: - { - if (!m_buffers[i].init_shader("travels.vs", "travels.fs")) - return false; - - break; - } - default: - { - break; - } - } + if (vertex_shader.empty() || fragment_shader.empty() || !m_buffers[i].init_shader(vertex_shader, fragment_shader)) + return false; } if (!m_shells.shader.init("shells.vs", "shells.fs")) @@ -449,7 +429,7 @@ void GCodeViewer::render_toolpaths() const case EViewType::Width: { color = m_extrusions.ranges.width.get_color_at(path.width, m_extrusions.ranges.colors); break; } case EViewType::Feedrate: { color = m_extrusions.ranges.feedrate.get_color_at(path.feedrate, m_extrusions.ranges.colors); break; } case EViewType::FanSpeed: { color = m_extrusions.ranges.fan_speed.get_color_at(path.fan_speed, m_extrusions.ranges.colors); break; } - case EViewType::VolumetricRate: + case EViewType::VolumetricRate: { color = m_extrusions.ranges.volumetric_rate.get_color_at(path.volumetric_rate, m_extrusions.ranges.colors); break; } case EViewType::Tool: case EViewType::ColorPrint: default: { color = { 1.0f, 1.0f, 1.0f }; break; } @@ -654,11 +634,11 @@ void GCodeViewer::render_overlay() const } break; } - case EViewType::Height: { add_range(m_extrusions.ranges.height, 3); break; } - case EViewType::Width: { add_range(m_extrusions.ranges.width, 3); break; } - case EViewType::Feedrate: { add_range(m_extrusions.ranges.feedrate, 1); break; } - case EViewType::FanSpeed: { add_range(m_extrusions.ranges.fan_speed, 0); break; } - case EViewType::VolumetricRate: { break; } + case EViewType::Height: { add_range(m_extrusions.ranges.height, 3); break; } + case EViewType::Width: { add_range(m_extrusions.ranges.width, 3); break; } + case EViewType::Feedrate: { add_range(m_extrusions.ranges.feedrate, 1); break; } + case EViewType::FanSpeed: { add_range(m_extrusions.ranges.fan_speed, 0); break; } + case EViewType::VolumetricRate: { add_range(m_extrusions.ranges.volumetric_rate, 3); break; } case EViewType::Tool: { break; } case EViewType::ColorPrint: { break; } } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 644f088424..6f27c68522 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -44,9 +44,11 @@ class GCodeViewer float width{ 0.0f }; float feedrate{ 0.0f }; float fan_speed{ 0.0f }; + float volumetric_rate{ 0.0f }; bool matches(const GCodeProcessor::MoveVertex& move) const { - return type == move.type && role == move.extrusion_role && height == move.height && width == move.width && feedrate == move.feedrate && fan_speed == move.fan_speed; + return type == move.type && role == move.extrusion_role && height == move.height && width == move.width && + feedrate == move.feedrate && fan_speed == move.fan_speed && volumetric_rate == move.volumetric_rate(); } }; @@ -82,17 +84,8 @@ class GCodeViewer Range() { reset(); } - void update_from(const float value) - { - min = std::min(min, value); - max = std::max(max, value); - } - - void reset() - { - min = FLT_MAX; - max = -FLT_MAX; - } + void update_from(const float value) { min = std::min(min, value); max = std::max(max, value); } + void reset() { min = FLT_MAX; max = -FLT_MAX; } float step_size() const { return (max - min) / (static_cast(Default_Range_Colors_Count) - 1.0f); } std::array get_color_at(float value, const std::array, Default_Range_Colors_Count>& colors) const; @@ -110,14 +103,15 @@ class GCodeViewer Range feedrate; // Color mapping by fan speed. Range fan_speed; -// // Color mapping by volumetric extrusion rate. -// Range volumetric_rate; + // Color mapping by volumetric extrusion rate. + Range volumetric_rate; void reset() { height.reset(); width.reset(); feedrate.reset(); fan_speed.reset(); + volumetric_rate.reset(); } }; From 61db595f5328b2b04153217ee63994c5719acd61 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 21 Apr 2020 12:51:58 +0200 Subject: [PATCH 030/503] GCodeViewer -> Refactoring --- src/slic3r/GUI/GCodeViewer.cpp | 29 +++++++++++++++-------------- src/slic3r/GUI/GCodeViewer.hpp | 15 +++++---------- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index e02a809934..ad15a0bb9d 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -71,13 +71,13 @@ void GCodeViewer::IBuffer::add_path(const GCodeProcessor::MoveVertex& move) paths.push_back({ move.type, move.extrusion_role, id, id, move.height, move.width, move.feedrate, move.fan_speed, move.volumetric_rate() }); } -std::array GCodeViewer::Extrusions::Range::get_color_at(float value, const std::array, Default_Range_Colors_Count>& colors) const +std::array GCodeViewer::Extrusions::Range::get_color_at(float value) const { // Input value scaled to the colors range const float step = step_size(); const float global_t = (step != 0.0f) ? std::max(0.0f, value - min) / step : 0.0f; // lower limit of 0.0f - const size_t color_max_idx = colors.size() - 1; + const size_t color_max_idx = Range_Colors.size() - 1; // Compute the two colors just below (low) and above (high) the input value const size_t color_low_idx = std::clamp(static_cast(global_t), 0, color_max_idx); @@ -90,12 +90,12 @@ std::array GCodeViewer::Extrusions::Range::get_color_at(float value, c std::array ret; for (unsigned int i = 0; i < 3; ++i) { - ret[i] = lerp(colors[color_low_idx][i], colors[color_high_idx][i], local_t); + ret[i] = lerp(Range_Colors[color_low_idx][i], Range_Colors[color_high_idx][i], local_t); } return ret; } -const std::array, erCount> GCodeViewer::Default_Extrusion_Role_Colors {{ +const std::array, erCount> GCodeViewer::Extrusion_Role_Colors{ { { 1.00f, 1.00f, 1.00f }, // erNone { 1.00f, 1.00f, 0.40f }, // erPerimeter { 1.00f, 0.65f, 0.00f }, // erExternalPerimeter @@ -113,7 +113,7 @@ const std::array, erCount> GCodeViewer::Default_Extrusion_R { 0.00f, 0.00f, 0.00f } // erMixed }}; -const std::array, GCodeViewer::Default_Range_Colors_Count> GCodeViewer::Default_Range_Colors {{ +const std::array, GCodeViewer::Range_Colors_Count> GCodeViewer::Range_Colors{ { { 0.043f, 0.173f, 0.478f }, // bluish { 0.075f, 0.349f, 0.522f }, { 0.110f, 0.533f, 0.569f }, @@ -424,12 +424,12 @@ void GCodeViewer::render_toolpaths() const std::array color; switch (m_view_type) { - case EViewType::FeatureType: { color = m_extrusions.role_colors[static_cast(path.role)]; break; } - case EViewType::Height: { color = m_extrusions.ranges.height.get_color_at(path.height, m_extrusions.ranges.colors); break; } - case EViewType::Width: { color = m_extrusions.ranges.width.get_color_at(path.width, m_extrusions.ranges.colors); break; } - case EViewType::Feedrate: { color = m_extrusions.ranges.feedrate.get_color_at(path.feedrate, m_extrusions.ranges.colors); break; } - case EViewType::FanSpeed: { color = m_extrusions.ranges.fan_speed.get_color_at(path.fan_speed, m_extrusions.ranges.colors); break; } - case EViewType::VolumetricRate: { color = m_extrusions.ranges.volumetric_rate.get_color_at(path.volumetric_rate, m_extrusions.ranges.colors); break; } + case EViewType::FeatureType: { color = Extrusion_Role_Colors[static_cast(path.role)]; break; } + case EViewType::Height: { color = m_extrusions.ranges.height.get_color_at(path.height); break; } + case EViewType::Width: { color = m_extrusions.ranges.width.get_color_at(path.width); break; } + case EViewType::Feedrate: { color = m_extrusions.ranges.feedrate.get_color_at(path.feedrate); break; } + case EViewType::FanSpeed: { color = m_extrusions.ranges.fan_speed.get_color_at(path.fan_speed); break; } + case EViewType::VolumetricRate: { color = m_extrusions.ranges.volumetric_rate.get_color_at(path.volumetric_rate); break; } case EViewType::Tool: case EViewType::ColorPrint: default: { color = { 1.0f, 1.0f, 1.0f }; break; } @@ -453,6 +453,7 @@ void GCodeViewer::render_toolpaths() const }; glsafe(::glCullFace(GL_BACK)); + glsafe(::glLineWidth(1.0f)); unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); unsigned char end_id = buffer_id(GCodeProcessor::EMoveType::Count); @@ -579,7 +580,7 @@ void GCodeViewer::render_overlay() const auto add_item = [this, draw_list, &imgui](int i, float value, unsigned int decimals) { ImVec2 pos(ImGui::GetCursorPosX() + 2.0f, ImGui::GetCursorPosY() + 2.0f); draw_list->AddRect(ImVec2(pos.x, pos.y), ImVec2(pos.x + ICON_BORDER_SIZE, pos.y + ICON_BORDER_SIZE), ICON_BORDER_COLOR, 0.0f, 0); - const std::array& color = m_extrusions.ranges.colors[i]; + const std::array& color = Range_Colors[i]; ImU32 fill_color = ImGui::GetColorU32(ImVec4(color[0], color[1], color[2], 1.0f)); draw_list->AddRectFilled(ImVec2(pos.x + 1.0f, pos.y + 1.0f), ImVec2(pos.x + ICON_BORDER_SIZE - 1.0f, pos.y + ICON_BORDER_SIZE - 1.0f), fill_color); ImGui::SetCursorPosX(pos.x + ICON_BORDER_SIZE + GAP_ICON_TEXT); @@ -594,7 +595,7 @@ void GCodeViewer::render_overlay() const add_item(0, range.min, decimals); else { - for (int i = Default_Range_Colors_Count - 1; i >= 0; --i) + for (int i = Range_Colors_Count - 1; i >= 0; --i) { add_item(i, range.min + static_cast(i) * step_size, decimals); } @@ -625,7 +626,7 @@ void GCodeViewer::render_overlay() const { ImVec2 pos(ImGui::GetCursorPosX() + 2.0f, ImGui::GetCursorPosY() + 2.0f); draw_list->AddRect(ImVec2(pos.x, pos.y), ImVec2(pos.x + ICON_BORDER_SIZE, pos.y + ICON_BORDER_SIZE), ICON_BORDER_COLOR, 0.0f, 0); - const std::array& color = m_extrusions.role_colors[static_cast(role)]; + const std::array& color = Extrusion_Role_Colors[static_cast(role)]; ImU32 fill_color = ImGui::GetColorU32(ImVec4(color[0], color[1], color[2], 1.0)); draw_list->AddRectFilled(ImVec2(pos.x + 1.0f, pos.y + 1.0f), ImVec2(pos.x + ICON_BORDER_SIZE - 1.0f, pos.y + ICON_BORDER_SIZE - 1.0f), fill_color); ImGui::SetCursorPosX(pos.x + ICON_BORDER_SIZE + GAP_ICON_TEXT); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 6f27c68522..a4d42556cb 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -15,9 +15,9 @@ namespace GUI { class GCodeViewer { - static const std::array, erCount> Default_Extrusion_Role_Colors; - static const size_t Default_Range_Colors_Count = 10; - static const std::array, Default_Range_Colors_Count> Default_Range_Colors; + static const std::array, erCount> Extrusion_Role_Colors; + static const size_t Range_Colors_Count = 10; + static const std::array, Range_Colors_Count> Range_Colors; // buffer containing vertices data struct VBuffer @@ -87,14 +87,12 @@ class GCodeViewer void update_from(const float value) { min = std::min(min, value); max = std::max(max, value); } void reset() { min = FLT_MAX; max = -FLT_MAX; } - float step_size() const { return (max - min) / (static_cast(Default_Range_Colors_Count) - 1.0f); } - std::array get_color_at(float value, const std::array, Default_Range_Colors_Count>& colors) const; + float step_size() const { return (max - min) / (static_cast(Range_Colors_Count) - 1.0f); } + std::array get_color_at(float value) const; }; struct Ranges { - std::array, Default_Range_Colors_Count> colors; - // Color mapping by layer height. Range height; // Color mapping by extrusion width. @@ -115,7 +113,6 @@ class GCodeViewer } }; - std::array, erCount> role_colors; unsigned int role_visibility_flags{ 0 }; Ranges ranges; @@ -166,8 +163,6 @@ public: ~GCodeViewer() { reset(); } bool init() { - m_extrusions.role_colors = Default_Extrusion_Role_Colors; - m_extrusions.ranges.colors = Default_Range_Colors; set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Extrude, true); return init_shaders(); } From 4c4485a9b5338a17b4838e94d65761659fa6cd9a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 21 Apr 2020 15:55:26 +0200 Subject: [PATCH 031/503] GCodeViewer -> Extrusion toolpaths colored by tool --- src/slic3r/GUI/BitmapCache.cpp | 5 +++ src/slic3r/GUI/GCodeViewer.cpp | 64 ++++++++++++++++++++++++++++------ src/slic3r/GUI/GCodeViewer.hpp | 12 ++++--- src/slic3r/GUI/GLCanvas3D.cpp | 6 ++-- src/slic3r/GUI/GLCanvas3D.hpp | 2 +- src/slic3r/GUI/GUI_Preview.cpp | 2 +- src/slic3r/GUI/GUI_Utils.hpp | 9 +++++ 7 files changed, 81 insertions(+), 19 deletions(-) diff --git a/src/slic3r/GUI/BitmapCache.cpp b/src/slic3r/GUI/BitmapCache.cpp index 8627ef4cb6..c345f6bf7a 100644 --- a/src/slic3r/GUI/BitmapCache.cpp +++ b/src/slic3r/GUI/BitmapCache.cpp @@ -3,6 +3,9 @@ #include "libslic3r/Utils.hpp" #include "../Utils/MacDarkMode.hpp" #include "GUI.hpp" +#if ENABLE_GCODE_VIEWER +#include "GUI_Utils.hpp" +#endif // ENABLE_GCODE_VIEWER #include @@ -352,6 +355,7 @@ wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsi } +#if !ENABLE_GCODE_VIEWER static inline int hex_digit_to_int(const char c) { return @@ -359,6 +363,7 @@ static inline int hex_digit_to_int(const char c) (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; } +#endif // !ENABLE_GCODE_VIEWER bool BitmapCache::parse_color(const std::string& scolor, unsigned char* rgb_out) { diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index ad15a0bb9d..0b5be19743 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -8,6 +8,9 @@ #include "Camera.hpp" #include "I18N.hpp" #include "libslic3r/I18N.hpp" +#if ENABLE_GCODE_VIEWER +#include "GUI_Utils.hpp" +#endif // ENABLE_GCODE_VIEWER #include #include @@ -27,6 +30,31 @@ static GCodeProcessor::EMoveType buffer_type(unsigned char id) { return static_cast(static_cast(GCodeProcessor::EMoveType::Retract) + id); } +std::vector> decode_colors(const std::vector& colors) +{ + static const float INV_255 = 1.0f / 255.0f; + + std::vector> output(colors.size(), {0.0f, 0.0f, 0.0f} ); + for (size_t i = 0; i < colors.size(); ++i) + { + const std::string& color = colors[i]; + const char* c = color.data() + 1; + if ((color.size() == 7) && (color.front() == '#')) + { + for (size_t j = 0; j < 3; ++j) + { + int digit1 = hex_digit_to_int(*c++); + int digit2 = hex_digit_to_int(*c++); + if ((digit1 == -1) || (digit2 == -1)) + break; + + output[i][j] = float(digit1 * 16 + digit2) * INV_255; + } + } + } + return output; +} + void GCodeViewer::VBuffer::reset() { // release gpu memory @@ -68,7 +96,7 @@ bool GCodeViewer::IBuffer::init_shader(const std::string& vertex_shader_src, con void GCodeViewer::IBuffer::add_path(const GCodeProcessor::MoveVertex& move) { unsigned int id = static_cast(data.size()); - paths.push_back({ move.type, move.extrusion_role, id, id, move.height, move.width, move.feedrate, move.fan_speed, move.volumetric_rate() }); + paths.push_back({ move.type, move.extrusion_role, id, id, move.height, move.width, move.feedrate, move.fan_speed, move.volumetric_rate(), move.extruder_id }); } std::array GCodeViewer::Extrusions::Range::get_color_at(float value) const @@ -95,7 +123,7 @@ std::array GCodeViewer::Extrusions::Range::get_color_at(float value) c return ret; } -const std::array, erCount> GCodeViewer::Extrusion_Role_Colors{ { +const std::array, erCount> GCodeViewer::Extrusion_Role_Colors {{ { 1.00f, 1.00f, 1.00f }, // erNone { 1.00f, 1.00f, 0.40f }, // erPerimeter { 1.00f, 0.65f, 0.00f }, // erExternalPerimeter @@ -113,7 +141,7 @@ const std::array, erCount> GCodeViewer::Extrusion_Role_Colo { 0.00f, 0.00f, 0.00f } // erMixed }}; -const std::array, GCodeViewer::Range_Colors_Count> GCodeViewer::Range_Colors{ { +const std::array, GCodeViewer::Range_Colors_Count> GCodeViewer::Range_Colors {{ { 0.043f, 0.173f, 0.478f }, // bluish { 0.075f, 0.349f, 0.522f }, { 0.110f, 0.533f, 0.569f }, @@ -126,8 +154,9 @@ const std::array, GCodeViewer::Range_Colors_Count> GCodeVie { 0.761f, 0.322f, 0.235f } // reddish }}; -void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized) +void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const std::vector& str_tool_colors, const Print& print, bool initialized) { + // avoid processing if called with the same gcode_result if (m_last_result_id == gcode_result.id) return; @@ -136,6 +165,8 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& // release gpu memory, if used reset(); + m_tool_colors = decode_colors(str_tool_colors); + load_toolpaths(gcode_result); load_shells(print, initialized); } @@ -170,10 +201,7 @@ void GCodeViewer::refresh_toolpaths_ranges(const GCodeProcessor::Result& gcode_r } break; } - default: - { - break; - } + default: { break; } } } } @@ -188,6 +216,7 @@ void GCodeViewer::reset() } m_bounding_box = BoundingBoxf3(); + m_tool_colors = std::vector>(); m_extrusions.reset_role_visibility_flags(); m_extrusions.reset_ranges(); m_shells.volumes.clear(); @@ -430,7 +459,7 @@ void GCodeViewer::render_toolpaths() const case EViewType::Feedrate: { color = m_extrusions.ranges.feedrate.get_color_at(path.feedrate); break; } case EViewType::FanSpeed: { color = m_extrusions.ranges.fan_speed.get_color_at(path.fan_speed); break; } case EViewType::VolumetricRate: { color = m_extrusions.ranges.volumetric_rate.get_color_at(path.volumetric_rate); break; } - case EViewType::Tool: + case EViewType::Tool: { color = m_tool_colors[path.extruder_id]; break; } case EViewType::ColorPrint: default: { color = { 1.0f, 1.0f, 1.0f }; break; } } @@ -640,7 +669,22 @@ void GCodeViewer::render_overlay() const case EViewType::Feedrate: { add_range(m_extrusions.ranges.feedrate, 1); break; } case EViewType::FanSpeed: { add_range(m_extrusions.ranges.fan_speed, 0); break; } case EViewType::VolumetricRate: { add_range(m_extrusions.ranges.volumetric_rate, 3); break; } - case EViewType::Tool: { break; } + case EViewType::Tool: + { + size_t tools_count = m_tool_colors.size(); + for (size_t i = 0; i < tools_count; ++i) + { + ImVec2 pos(ImGui::GetCursorPosX() + 2.0f, ImGui::GetCursorPosY() + 2.0f); + draw_list->AddRect(ImVec2(pos.x, pos.y), ImVec2(pos.x + ICON_BORDER_SIZE, pos.y + ICON_BORDER_SIZE), ICON_BORDER_COLOR, 0.0f, 0); + const std::array& color = m_tool_colors[i]; + ImU32 fill_color = ImGui::GetColorU32(ImVec4(color[0], color[1], color[2], 1.0)); + draw_list->AddRectFilled(ImVec2(pos.x + 1.0f, pos.y + 1.0f), ImVec2(pos.x + ICON_BORDER_SIZE - 1.0f, pos.y + ICON_BORDER_SIZE - 1.0f), fill_color); + ImGui::SetCursorPosX(pos.x + ICON_BORDER_SIZE + GAP_ICON_TEXT); + ImGui::AlignTextToFramePadding(); + imgui.text((boost::format(Slic3r::I18N::translate(L("Extruder %d"))) % (i + 1)).str()); + } + break; + } case EViewType::ColorPrint: { break; } } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index a4d42556cb..956dd0b5d0 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -45,10 +45,12 @@ class GCodeViewer float feedrate{ 0.0f }; float fan_speed{ 0.0f }; float volumetric_rate{ 0.0f }; + unsigned char extruder_id{ 0 }; bool matches(const GCodeProcessor::MoveVertex& move) const { return type == move.type && role == move.extrusion_role && height == move.height && width == move.width && - feedrate == move.feedrate && fan_speed == move.fan_speed && volumetric_rate == move.volumetric_rate(); + feedrate == move.feedrate && fan_speed == move.fan_speed && volumetric_rate == move.volumetric_rate() && + extruder_id == move.extruder_id; } }; @@ -146,11 +148,11 @@ public: }; private: + unsigned int m_last_result_id{ 0 }; VBuffer m_vertices; std::vector m_buffers{ static_cast(GCodeProcessor::EMoveType::Extrude) }; BoundingBoxf3 m_bounding_box; - - unsigned int m_last_result_id{ 0 }; + std::vector> m_tool_colors; std::vector m_layers_zs; std::vector m_roles; Extrusions m_extrusions; @@ -167,7 +169,9 @@ public: return init_shaders(); } - void load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized); + // extract rendering data from the given parameters + void load(const GCodeProcessor::Result& gcode_result, const std::vector& str_tool_colors, const Print& print, bool initialized); + // recalculate ranges in dependence of what is visible void refresh_toolpaths_ranges(const GCodeProcessor::Result& gcode_result); void reset(); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e174a57833..540896a59f 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2835,7 +2835,7 @@ static void load_gcode_retractions(const GCodePreviewData::Retraction& retractio #endif // !ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER -void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result) +void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector& str_tool_colors) { #if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT static unsigned int last_result_id = 0; @@ -2852,7 +2852,7 @@ void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result) out.close(); } #endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - m_gcode_viewer.load(gcode_result, *this->fff_print(), m_initialized); + m_gcode_viewer.load(gcode_result, str_tool_colors , *this->fff_print(), m_initialized); } void GLCanvas3D::refresh_toolpaths_ranges(const GCodeProcessor::Result& gcode_result) @@ -6594,6 +6594,7 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector& str_ BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info(); } +#if !ENABLE_GCODE_VIEWER static inline int hex_digit_to_int(const char c) { return @@ -6602,7 +6603,6 @@ static inline int hex_digit_to_int(const char c) (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; } -#if !ENABLE_GCODE_VIEWER void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector& tool_colors) { BOOST_LOG_TRIVIAL(debug) << "Loading G-code extrusion paths - start" << m_volumes.log_memory_info() << log_memory_info(); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 312eeaa3b6..54f6e181e5 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -664,7 +664,7 @@ public: void reload_scene(bool refresh_immediately, bool force_full_scene_refresh = false); #if ENABLE_GCODE_VIEWER - void load_gcode_preview(const GCodeProcessor::Result& gcode_result); + void load_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector& str_tool_colors); void refresh_toolpaths_ranges(const GCodeProcessor::Result& gcode_result); #else void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector& str_tool_colors); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 2060511491..9476ad1f0b 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -984,7 +984,7 @@ void Preview::load_print_as_fff(bool keep_z_range) if (gcode_preview_data_valid) { // Load the real G-code preview. #if ENABLE_GCODE_VIEWER - m_canvas->load_gcode_preview(*m_gcode_result); + m_canvas->load_gcode_preview(*m_gcode_result, colors); m_canvas->refresh_toolpaths_ranges(*m_gcode_result); #else m_canvas->load_gcode_preview(*m_gcode_preview_data, colors); diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp index 0d5249e254..057c72b1d3 100644 --- a/src/slic3r/GUI/GUI_Utils.hpp +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -343,6 +343,15 @@ public: std::ostream& operator<<(std::ostream &os, const WindowMetrics& metrics); +#if ENABLE_GCODE_VIEWER +inline int hex_digit_to_int(const char c) +{ + return + (c >= '0' && c <= '9') ? int(c - '0') : + (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : + (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; +} +#endif // ENABLE_GCODE_VIEWER }} From 7a0df4bcb487c6f6e6fd3d3505ee12a5416df368 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 22 Apr 2020 16:29:07 +0200 Subject: [PATCH 032/503] GCodeViewer -> Extrusion toolpaths colored by color print (wip) + visualization of tool changes, color changes, pause prints, custom gcodes + refactoring --- resources/shaders/colorchanges.fs | 45 +++++ resources/shaders/colorchanges.vs | 17 ++ resources/shaders/customs.fs | 45 +++++ resources/shaders/customs.vs | 17 ++ resources/shaders/pauses.fs | 45 +++++ resources/shaders/pauses.vs | 17 ++ src/libslic3r/GCode.cpp | 2 + src/libslic3r/GCode/GCodeProcessor.cpp | 31 +++- src/libslic3r/GCode/GCodeProcessor.hpp | 7 + src/libslic3r/Technologies.hpp | 1 + src/slic3r/GUI/GCodeViewer.cpp | 226 +++++++++++++++++++------ src/slic3r/GUI/GCodeViewer.hpp | 18 +- src/slic3r/GUI/GLCanvas3D.cpp | 8 +- src/slic3r/GUI/GLCanvas3D.hpp | 6 +- src/slic3r/GUI/GUI_Preview.cpp | 96 ++++++++++- src/slic3r/GUI/GUI_Preview.hpp | 12 ++ 16 files changed, 516 insertions(+), 77 deletions(-) create mode 100644 resources/shaders/colorchanges.fs create mode 100644 resources/shaders/colorchanges.vs create mode 100644 resources/shaders/customs.fs create mode 100644 resources/shaders/customs.vs create mode 100644 resources/shaders/pauses.fs create mode 100644 resources/shaders/pauses.vs diff --git a/resources/shaders/colorchanges.fs b/resources/shaders/colorchanges.fs new file mode 100644 index 0000000000..fc81e487fb --- /dev/null +++ b/resources/shaders/colorchanges.fs @@ -0,0 +1,45 @@ +#version 110 + +#define INTENSITY_AMBIENT 0.3 +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +uniform vec3 uniform_color; + +varying vec3 eye_position; +varying vec3 eye_normal; +//varying float world_normal_z; + +// x = tainted, y = specular; +vec2 intensity; + +void main() +{ + vec3 normal = normalize(eye_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + +// // darkens fragments whose normal points downward +// if (world_normal_z < 0.0) +// intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); + + gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color * intensity.x, 1.0); +} diff --git a/resources/shaders/colorchanges.vs b/resources/shaders/colorchanges.vs new file mode 100644 index 0000000000..3b78a59700 --- /dev/null +++ b/resources/shaders/colorchanges.vs @@ -0,0 +1,17 @@ +#version 110 + +varying vec3 eye_position; +varying vec3 eye_normal; +//// world z component of the normal used to darken the lower side of the toolpaths +//varying float world_normal_z; + +void main() +{ + eye_position = (gl_ModelViewMatrix * gl_Vertex).xyz; + eye_normal = gl_NormalMatrix * vec3(0.0, 0.0, 1.0); +// eye_normal = gl_NormalMatrix * gl_Normal; +// world_normal_z = gl_Normal.z; + gl_Position = ftransform(); + + gl_PointSize = 15.0; +} diff --git a/resources/shaders/customs.fs b/resources/shaders/customs.fs new file mode 100644 index 0000000000..fc81e487fb --- /dev/null +++ b/resources/shaders/customs.fs @@ -0,0 +1,45 @@ +#version 110 + +#define INTENSITY_AMBIENT 0.3 +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +uniform vec3 uniform_color; + +varying vec3 eye_position; +varying vec3 eye_normal; +//varying float world_normal_z; + +// x = tainted, y = specular; +vec2 intensity; + +void main() +{ + vec3 normal = normalize(eye_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + +// // darkens fragments whose normal points downward +// if (world_normal_z < 0.0) +// intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); + + gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color * intensity.x, 1.0); +} diff --git a/resources/shaders/customs.vs b/resources/shaders/customs.vs new file mode 100644 index 0000000000..45fa543f45 --- /dev/null +++ b/resources/shaders/customs.vs @@ -0,0 +1,17 @@ +#version 110 + +varying vec3 eye_position; +varying vec3 eye_normal; +//// world z component of the normal used to darken the lower side of the toolpaths +//varying float world_normal_z; + +void main() +{ + eye_position = (gl_ModelViewMatrix * gl_Vertex).xyz; + eye_normal = gl_NormalMatrix * vec3(0.0, 0.0, 1.0); +// eye_normal = gl_NormalMatrix * gl_Normal; +// world_normal_z = gl_Normal.z; + gl_Position = ftransform(); + + gl_PointSize = 10.0; +} diff --git a/resources/shaders/pauses.fs b/resources/shaders/pauses.fs new file mode 100644 index 0000000000..fc81e487fb --- /dev/null +++ b/resources/shaders/pauses.fs @@ -0,0 +1,45 @@ +#version 110 + +#define INTENSITY_AMBIENT 0.3 +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +uniform vec3 uniform_color; + +varying vec3 eye_position; +varying vec3 eye_normal; +//varying float world_normal_z; + +// x = tainted, y = specular; +vec2 intensity; + +void main() +{ + vec3 normal = normalize(eye_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + +// // darkens fragments whose normal points downward +// if (world_normal_z < 0.0) +// intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); + + gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color * intensity.x, 1.0); +} diff --git a/resources/shaders/pauses.vs b/resources/shaders/pauses.vs new file mode 100644 index 0000000000..45fa543f45 --- /dev/null +++ b/resources/shaders/pauses.vs @@ -0,0 +1,17 @@ +#version 110 + +varying vec3 eye_position; +varying vec3 eye_normal; +//// world z component of the normal used to darken the lower side of the toolpaths +//varying float world_normal_z; + +void main() +{ + eye_position = (gl_ModelViewMatrix * gl_Vertex).xyz; + eye_normal = gl_NormalMatrix * vec3(0.0, 0.0, 1.0); +// eye_normal = gl_NormalMatrix * gl_Normal; +// world_normal_z = gl_Normal.z; + gl_Position = ftransform(); + + gl_PointSize = 10.0; +} diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index ee7be7fea7..a1114e938e 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2329,11 +2329,13 @@ void GCode::process_layer( gcode += "\n; " + GCodeAnalyzer::End_Pause_Print_Or_Custom_Code_Tag + "\n"; #if ENABLE_GCODE_VIEWER +#if !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT // add tag for processor if (gcode.find(GCodeProcessor::Pause_Print_Tag) != gcode.npos) gcode += "\n; " + GCodeProcessor::End_Pause_Print_Or_Custom_Code_Tag + "\n"; else if (gcode.find(GCodeProcessor::Custom_Code_Tag) != gcode.npos) gcode += "\n; " + GCodeProcessor::End_Pause_Print_Or_Custom_Code_Tag + "\n"; +#endif // !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT #endif // ENABLE_GCODE_VIEWER #ifdef HAS_PRESSURE_EQUALIZER diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 78943dca89..2219facb51 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -24,7 +24,9 @@ const std::string GCodeProcessor::Mm3_Per_Mm_Tag = "_PROCESSOR_MM3_PER_MM:"; const std::string GCodeProcessor::Color_Change_Tag = "_PROCESSOR_COLOR_CHANGE"; const std::string GCodeProcessor::Pause_Print_Tag = "_PROCESSOR_PAUSE_PRINT"; const std::string GCodeProcessor::Custom_Code_Tag = "_PROCESSOR_CUSTOM_CODE"; +#if !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT const std::string GCodeProcessor::End_Pause_Print_Or_Custom_Code_Tag = "_PROCESSOR_END_PAUSE_PRINT_OR_CUSTOM_CODE"; +#endif // !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT void GCodeProcessor::CachedPosition::reset() { @@ -236,13 +238,20 @@ void GCodeProcessor::process_tags(const std::string& comment) pos = comment.find_last_of(",T"); try { - unsigned char extruder_id = (pos == comment.npos) ? 0 : static_cast(std::stoi(comment.substr(pos + 1, comment.npos))); + unsigned char extruder_id = (pos == comment.npos) ? 0 : static_cast(std::stoi(comment.substr(pos + 1))); m_extruders_color[extruder_id] = static_cast(m_extruder_offsets.size()) + m_cp_color.counter; // color_change position in list of color for preview ++m_cp_color.counter; + if (m_cp_color.counter == UCHAR_MAX) + m_cp_color.counter = 0; if (m_extruder_id == extruder_id) + { m_cp_color.current = m_extruders_color[extruder_id]; +#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT + store_move_vertex(EMoveType::Color_change); +#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT + } } catch (...) { @@ -256,7 +265,11 @@ void GCodeProcessor::process_tags(const std::string& comment) pos = comment.find(Pause_Print_Tag); if (pos != comment.npos) { - m_cp_color.current = 255; +#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT + store_move_vertex(EMoveType::Pause_Print); +#else + m_cp_color.current = UCHAR_MAX; +#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT return; } @@ -264,19 +277,25 @@ void GCodeProcessor::process_tags(const std::string& comment) pos = comment.find(Custom_Code_Tag); if (pos != comment.npos) { - m_cp_color.current = 255; +#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT + store_move_vertex(EMoveType::Custom_GCode); +#else + m_cp_color.current = UCHAR_MAX; +#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT return; } +#if !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT // end pause print or custom code tag pos = comment.find(End_Pause_Print_Or_Custom_Code_Tag); if (pos != comment.npos) { - if (m_cp_color.current == 255) + if (m_cp_color.current == UCHAR_MAX) m_cp_color.current = m_extruders_color[m_extruder_id]; return; } +#endif // !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT } void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) @@ -569,7 +588,9 @@ void GCodeProcessor::process_T(const std::string& command) else { m_extruder_id = id; - if (m_cp_color.current != 255) +#if !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT + if (m_cp_color.current != UCHAR_MAX) +#endif // !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT m_cp_color.current = m_extruders_color[id]; } diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 38adce3329..cd4e021cae 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -21,7 +21,9 @@ namespace Slic3r { static const std::string Color_Change_Tag; static const std::string Pause_Print_Tag; static const std::string Custom_Code_Tag; +#if !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT static const std::string End_Pause_Print_Or_Custom_Code_Tag; +#endif // !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT private: using AxisCoords = std::array; @@ -62,6 +64,11 @@ namespace Slic3r { Retract, Unretract, Tool_change, +#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT + Color_change, + Pause_Print, + Custom_GCode, +#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT Travel, Extrude, Count diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 1ed939ba3e..8d1e19eceb 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -59,6 +59,7 @@ // Enable G-Code viewer #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) #define ENABLE_GCODE_VIEWER_DEBUG_OUTPUT (0 && ENABLE_GCODE_VIEWER) +#define ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT (1 && ENABLE_GCODE_VIEWER) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 0b5be19743..2f039da0b3 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -7,9 +7,10 @@ #include "PresetBundle.hpp" #include "Camera.hpp" #include "I18N.hpp" -#include "libslic3r/I18N.hpp" #if ENABLE_GCODE_VIEWER #include "GUI_Utils.hpp" +#include "DoubleSlider.hpp" +#include "libslic3r/Model.hpp" #endif // ENABLE_GCODE_VIEWER #include @@ -96,7 +97,7 @@ bool GCodeViewer::IBuffer::init_shader(const std::string& vertex_shader_src, con void GCodeViewer::IBuffer::add_path(const GCodeProcessor::MoveVertex& move) { unsigned int id = static_cast(data.size()); - paths.push_back({ move.type, move.extrusion_role, id, id, move.height, move.width, move.feedrate, move.fan_speed, move.volumetric_rate(), move.extruder_id }); + paths.push_back({ move.type, move.extrusion_role, id, id, move.height, move.width, move.feedrate, move.fan_speed, move.volumetric_rate(), move.extruder_id, move.cp_color_id }); } std::array GCodeViewer::Extrusions::Range::get_color_at(float value) const @@ -123,8 +124,8 @@ std::array GCodeViewer::Extrusions::Range::get_color_at(float value) c return ret; } -const std::array, erCount> GCodeViewer::Extrusion_Role_Colors {{ - { 1.00f, 1.00f, 1.00f }, // erNone +const std::vector> GCodeViewer::Extrusion_Role_Colors {{ + { 0.50f, 0.50f, 0.50f }, // erNone { 1.00f, 1.00f, 0.40f }, // erPerimeter { 1.00f, 0.65f, 0.00f }, // erExternalPerimeter { 0.00f, 0.00f, 1.00f }, // erOverhangPerimeter @@ -141,7 +142,7 @@ const std::array, erCount> GCodeViewer::Extrusion_Role_Colo { 0.00f, 0.00f, 0.00f } // erMixed }}; -const std::array, GCodeViewer::Range_Colors_Count> GCodeViewer::Range_Colors {{ +const std::vector> GCodeViewer::Range_Colors {{ { 0.043f, 0.173f, 0.478f }, // bluish { 0.075f, 0.349f, 0.522f }, { 0.110f, 0.533f, 0.569f }, @@ -154,7 +155,7 @@ const std::array, GCodeViewer::Range_Colors_Count> GCodeVie { 0.761f, 0.322f, 0.235f } // reddish }}; -void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const std::vector& str_tool_colors, const Print& print, bool initialized) +void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized) { // avoid processing if called with the same gcode_result if (m_last_result_id == gcode_result.id) @@ -165,17 +166,17 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const std::ve // release gpu memory, if used reset(); - m_tool_colors = decode_colors(str_tool_colors); - load_toolpaths(gcode_result); load_shells(print, initialized); } -void GCodeViewer::refresh_toolpaths_ranges(const GCodeProcessor::Result& gcode_result) +void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std::vector& str_tool_colors) { if (m_vertices.vertices_count == 0) return; + m_tool_colors = decode_colors(str_tool_colors); + m_extrusions.reset_ranges(); for (size_t i = 0; i < m_vertices.vertices_count; ++i) @@ -217,6 +218,8 @@ void GCodeViewer::reset() m_bounding_box = BoundingBoxf3(); m_tool_colors = std::vector>(); + m_extruder_ids = std::vector(); +// m_cp_color_ids = std::vector(); m_extrusions.reset_role_visibility_flags(); m_extrusions.reset_ranges(); m_shells.volumes.clear(); @@ -257,11 +260,16 @@ bool GCodeViewer::init_shaders() switch (buffer_type(i)) { - case GCodeProcessor::EMoveType::Tool_change: { vertex_shader = "toolchanges.vs"; fragment_shader = "toolchanges.fs"; break; } - case GCodeProcessor::EMoveType::Retract: { vertex_shader = "retractions.vs"; fragment_shader = "retractions.fs"; break; } - case GCodeProcessor::EMoveType::Unretract: { vertex_shader = "unretractions.vs"; fragment_shader = "unretractions.fs"; break; } - case GCodeProcessor::EMoveType::Extrude: { vertex_shader = "extrusions.vs"; fragment_shader = "extrusions.fs"; break; } - case GCodeProcessor::EMoveType::Travel: { vertex_shader = "travels.vs"; fragment_shader = "travels.fs"; break; } + case GCodeProcessor::EMoveType::Tool_change: { vertex_shader = "toolchanges.vs"; fragment_shader = "toolchanges.fs"; break; } +#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT + case GCodeProcessor::EMoveType::Color_change: { vertex_shader = "colorchanges.vs"; fragment_shader = "colorchanges.fs"; break; } + case GCodeProcessor::EMoveType::Pause_Print: { vertex_shader = "pauses.vs"; fragment_shader = "pauses.fs"; break; } + case GCodeProcessor::EMoveType::Custom_GCode: { vertex_shader = "customs.vs"; fragment_shader = "customs.fs"; break; } +#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT + case GCodeProcessor::EMoveType::Retract: { vertex_shader = "retractions.vs"; fragment_shader = "retractions.fs"; break; } + case GCodeProcessor::EMoveType::Unretract: { vertex_shader = "unretractions.vs"; fragment_shader = "unretractions.fs"; break; } + case GCodeProcessor::EMoveType::Extrude: { vertex_shader = "extrusions.vs"; fragment_shader = "extrusions.fs"; break; } + case GCodeProcessor::EMoveType::Travel: { vertex_shader = "travels.vs"; fragment_shader = "travels.fs"; break; } default: { break; } } @@ -322,6 +330,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) switch (curr.type) { case GCodeProcessor::EMoveType::Tool_change: +#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT + case GCodeProcessor::EMoveType::Color_change: + case GCodeProcessor::EMoveType::Pause_Print: + case GCodeProcessor::EMoveType::Custom_GCode: +#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT case GCodeProcessor::EMoveType::Retract: case GCodeProcessor::EMoveType::Unretract: { @@ -365,13 +378,15 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) } } - // layers zs / roles -> extract from result + // layers zs / roles / extruder ids / cp color ids -> extract from result for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) { if (move.type == GCodeProcessor::EMoveType::Extrude) m_layers_zs.emplace_back(move.position[2]); m_roles.emplace_back(move.extrusion_role); + m_extruder_ids.emplace_back(move.extruder_id); +// m_cp_color_ids.emplace_back(move.cp_color_id); } // layers zs -> replace intervals of layers with similar top positions with their average value. @@ -392,6 +407,14 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) std::sort(m_roles.begin(), m_roles.end()); m_roles.erase(std::unique(m_roles.begin(), m_roles.end()), m_roles.end()); + // extruder ids -> remove duplicates + std::sort(m_extruder_ids.begin(), m_extruder_ids.end()); + m_extruder_ids.erase(std::unique(m_extruder_ids.begin(), m_extruder_ids.end()), m_extruder_ids.end()); + +// // cp color ids -> remove duplicates +// std::sort(m_cp_color_ids.begin(), m_cp_color_ids.end()); +// m_cp_color_ids.erase(std::unique(m_cp_color_ids.begin(), m_cp_color_ids.end()), m_cp_color_ids.end()); + auto end_time = std::chrono::high_resolution_clock::now(); std::cout << "toolpaths generation time: " << std::chrono::duration_cast(end_time - start_time).count() << "ms \n"; } @@ -460,7 +483,7 @@ void GCodeViewer::render_toolpaths() const case EViewType::FanSpeed: { color = m_extrusions.ranges.fan_speed.get_color_at(path.fan_speed); break; } case EViewType::VolumetricRate: { color = m_extrusions.ranges.volumetric_rate.get_color_at(path.volumetric_rate); break; } case EViewType::Tool: { color = m_tool_colors[path.extruder_id]; break; } - case EViewType::ColorPrint: + case EViewType::ColorPrint: { color = m_tool_colors[path.cp_color_id]; break; } default: { color = { 1.0f, 1.0f, 1.0f }; break; } } return color; @@ -482,7 +505,7 @@ void GCodeViewer::render_toolpaths() const }; glsafe(::glCullFace(GL_BACK)); - glsafe(::glLineWidth(1.0f)); + glsafe(::glLineWidth(3.0f)); unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); unsigned char end_id = buffer_id(GCodeProcessor::EMoveType::Count); @@ -522,6 +545,35 @@ void GCodeViewer::render_toolpaths() const glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); break; } +#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT + case GCodeProcessor::EMoveType::Color_change: + { + std::array color = { 1.0f, 0.0f, 0.0f }; + set_color(current_program_id, color); + glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); + glsafe(::glDrawElements(GL_POINTS, (GLsizei)buffer.data_size, GL_UNSIGNED_INT, nullptr)); + glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + break; + } + case GCodeProcessor::EMoveType::Pause_Print: + { + std::array color = { 0.0f, 1.0f, 0.0f }; + set_color(current_program_id, color); + glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); + glsafe(::glDrawElements(GL_POINTS, (GLsizei)buffer.data_size, GL_UNSIGNED_INT, nullptr)); + glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + break; + } + case GCodeProcessor::EMoveType::Custom_GCode: + { + std::array color = { 0.0f, 0.0f, 1.0f }; + set_color(current_program_id, color); + glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); + glsafe(::glDrawElements(GL_POINTS, (GLsizei)buffer.data_size, GL_UNSIGNED_INT, nullptr)); + glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + break; + } +#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT case GCodeProcessor::EMoveType::Retract: { std::array color = { 1.0f, 0.0f, 1.0f }; @@ -533,7 +585,7 @@ void GCodeViewer::render_toolpaths() const } case GCodeProcessor::EMoveType::Unretract: { - std::array color = { 0.0f, 1.0f, 0.0f }; + std::array color = { 0.0f, 1.0f, 1.0f }; set_color(current_program_id, color); glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); glsafe(::glDrawElements(GL_POINTS, (GLsizei)buffer.data_size, GL_UNSIGNED_INT, nullptr)); @@ -605,28 +657,32 @@ void GCodeViewer::render_overlay() const ImDrawList* draw_list = ImGui::GetWindowDrawList(); - auto add_range = [this, draw_list, &imgui](const Extrusions::Range& range, unsigned int decimals) { - auto add_item = [this, draw_list, &imgui](int i, float value, unsigned int decimals) { - ImVec2 pos(ImGui::GetCursorPosX() + 2.0f, ImGui::GetCursorPosY() + 2.0f); - draw_list->AddRect(ImVec2(pos.x, pos.y), ImVec2(pos.x + ICON_BORDER_SIZE, pos.y + ICON_BORDER_SIZE), ICON_BORDER_COLOR, 0.0f, 0); - const std::array& color = Range_Colors[i]; - ImU32 fill_color = ImGui::GetColorU32(ImVec4(color[0], color[1], color[2], 1.0f)); - draw_list->AddRectFilled(ImVec2(pos.x + 1.0f, pos.y + 1.0f), ImVec2(pos.x + ICON_BORDER_SIZE - 1.0f, pos.y + ICON_BORDER_SIZE - 1.0f), fill_color); - ImGui::SetCursorPosX(pos.x + ICON_BORDER_SIZE + GAP_ICON_TEXT); - ImGui::AlignTextToFramePadding(); + auto add_item = [draw_list, &imgui](const std::array& color, const std::string& label) { + ImVec2 pos(ImGui::GetCursorPosX() + 2.0f, ImGui::GetCursorPosY() + 2.0f); + draw_list->AddRect(ImVec2(pos.x, pos.y), ImVec2(pos.x + ICON_BORDER_SIZE, pos.y + ICON_BORDER_SIZE), ICON_BORDER_COLOR, 0.0f, 0); + ImU32 fill_color = ImGui::GetColorU32(ImVec4(color[0], color[1], color[2], 1.0f)); + draw_list->AddRectFilled(ImVec2(pos.x + 1.0f, pos.y + 1.0f), ImVec2(pos.x + ICON_BORDER_SIZE - 1.0f, pos.y + ICON_BORDER_SIZE - 1.0f), fill_color); + ImGui::SetCursorPosX(pos.x + ICON_BORDER_SIZE + GAP_ICON_TEXT); + ImGui::AlignTextToFramePadding(); + imgui.text(label); + }; + + auto add_range = [this, draw_list, &imgui, add_item](const Extrusions::Range& range, unsigned int decimals) { + auto add_range_item = [this, draw_list, &imgui, add_item](int i, float value, unsigned int decimals) { char buf[1024]; ::sprintf(buf, "%.*f", decimals, value); - imgui.text(buf); + add_item(Range_Colors[i], buf); }; float step_size = range.step_size(); if (step_size == 0.0f) - add_item(0, range.min, decimals); + // single item use case + add_range_item(0, range.min, decimals); else { - for (int i = Range_Colors_Count - 1; i >= 0; --i) + for (int i = static_cast(Range_Colors.size()) - 1; i >= 0; --i) { - add_item(i, range.min + static_cast(i) * step_size, decimals); + add_range_item(i, range.min + static_cast(i) * step_size, decimals); } } }; @@ -634,14 +690,15 @@ void GCodeViewer::render_overlay() const ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); switch (m_view_type) { - case EViewType::FeatureType: { imgui.text(Slic3r::I18N::translate(L("Feature type"))); break; } - case EViewType::Height: { imgui.text(Slic3r::I18N::translate(L("Height (mm)"))); break; } - case EViewType::Width: { imgui.text(Slic3r::I18N::translate(L("Width (mm)"))); break; } - case EViewType::Feedrate: { imgui.text(Slic3r::I18N::translate(L("Speed (mm/s)"))); break; } - case EViewType::FanSpeed: { imgui.text(Slic3r::I18N::translate(L("Fan Speed (%)"))); break; } - case EViewType::VolumetricRate: { imgui.text(Slic3r::I18N::translate(L("Volumetric flow rate (mm³/s)"))); break; } - case EViewType::Tool: { imgui.text(Slic3r::I18N::translate(L("Tool"))); break; } - case EViewType::ColorPrint: { imgui.text(Slic3r::I18N::translate(L("Color Print"))); break; } + case EViewType::FeatureType: { imgui.text(I18N::translate_utf8(L("Feature type"))); break; } + case EViewType::Height: { imgui.text(I18N::translate_utf8(L("Height (mm)"))); break; } + case EViewType::Width: { imgui.text(I18N::translate_utf8(L("Width (mm)"))); break; } + case EViewType::Feedrate: { imgui.text(I18N::translate_utf8(L("Speed (mm/s)"))); break; } + case EViewType::FanSpeed: { imgui.text(I18N::translate_utf8(L("Fan Speed (%)"))); break; } + case EViewType::VolumetricRate: { imgui.text(I18N::translate_utf8(L("Volumetric flow rate (mm³/s)"))); break; } + case EViewType::Tool: { imgui.text(I18N::translate_utf8(L("Tool"))); break; } + case EViewType::ColorPrint: { imgui.text(I18N::translate_utf8(L("Color Print"))); break; } + default: { break; } } ImGui::PopStyleColor(); @@ -653,14 +710,7 @@ void GCodeViewer::render_overlay() const { for (ExtrusionRole role : m_roles) { - ImVec2 pos(ImGui::GetCursorPosX() + 2.0f, ImGui::GetCursorPosY() + 2.0f); - draw_list->AddRect(ImVec2(pos.x, pos.y), ImVec2(pos.x + ICON_BORDER_SIZE, pos.y + ICON_BORDER_SIZE), ICON_BORDER_COLOR, 0.0f, 0); - const std::array& color = Extrusion_Role_Colors[static_cast(role)]; - ImU32 fill_color = ImGui::GetColorU32(ImVec4(color[0], color[1], color[2], 1.0)); - draw_list->AddRectFilled(ImVec2(pos.x + 1.0f, pos.y + 1.0f), ImVec2(pos.x + ICON_BORDER_SIZE - 1.0f, pos.y + ICON_BORDER_SIZE - 1.0f), fill_color); - ImGui::SetCursorPosX(pos.x + ICON_BORDER_SIZE + GAP_ICON_TEXT); - ImGui::AlignTextToFramePadding(); - imgui.text(Slic3r::I18N::translate(ExtrusionEntity::role_to_string(role))); + add_item(Extrusion_Role_Colors[static_cast(role)], I18N::translate_utf8(ExtrusionEntity::role_to_string(role))); } break; } @@ -674,18 +724,82 @@ void GCodeViewer::render_overlay() const size_t tools_count = m_tool_colors.size(); for (size_t i = 0; i < tools_count; ++i) { - ImVec2 pos(ImGui::GetCursorPosX() + 2.0f, ImGui::GetCursorPosY() + 2.0f); - draw_list->AddRect(ImVec2(pos.x, pos.y), ImVec2(pos.x + ICON_BORDER_SIZE, pos.y + ICON_BORDER_SIZE), ICON_BORDER_COLOR, 0.0f, 0); - const std::array& color = m_tool_colors[i]; - ImU32 fill_color = ImGui::GetColorU32(ImVec4(color[0], color[1], color[2], 1.0)); - draw_list->AddRectFilled(ImVec2(pos.x + 1.0f, pos.y + 1.0f), ImVec2(pos.x + ICON_BORDER_SIZE - 1.0f, pos.y + ICON_BORDER_SIZE - 1.0f), fill_color); - ImGui::SetCursorPosX(pos.x + ICON_BORDER_SIZE + GAP_ICON_TEXT); - ImGui::AlignTextToFramePadding(); - imgui.text((boost::format(Slic3r::I18N::translate(L("Extruder %d"))) % (i + 1)).str()); + auto it = std::find(m_extruder_ids.begin(), m_extruder_ids.end(), static_cast(i)); + if (it == m_extruder_ids.end()) + continue; + + add_item(m_tool_colors[i], (boost::format(I18N::translate_utf8(L("Extruder %d"))) % (i + 1)).str()); } break; } - case EViewType::ColorPrint: { break; } + case EViewType::ColorPrint: + { + const std::vector& custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes; + const int extruders_count = wxGetApp().extruders_edited_cnt(); + if (extruders_count == 1) // single extruder use case + { + if (custom_gcode_per_print_z.empty()) + // no data to show + add_item(m_tool_colors.front(), I18N::translate_utf8(L("Default print color"))); + else + { + std::vector> cp_values; + cp_values.reserve(custom_gcode_per_print_z.size()); + + for (auto custom_code : custom_gcode_per_print_z) + { + if (custom_code.gcode != ColorChangeCode) + continue; + + auto lower_b = std::lower_bound(m_layers_zs.begin(), m_layers_zs.end(), custom_code.print_z - Slic3r::DoubleSlider::epsilon()); + + if (lower_b == m_layers_zs.end()) + continue; + + double current_z = *lower_b; + double previous_z = lower_b == m_layers_zs.begin() ? 0.0 : *(--lower_b); + + // to avoid duplicate values, check adding values + if (cp_values.empty() || + !(cp_values.back().first == previous_z && cp_values.back().second == current_z)) + cp_values.emplace_back(std::make_pair(previous_z, current_z)); + } + + const int items_cnt = static_cast(cp_values.size()); + if (items_cnt == 0) // There is no one color change, but there are some pause print or custom Gcode + { + add_item(m_tool_colors.front(), I18N::translate_utf8(L("Default print color"))); +#if !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT + add_item(m_tool_colors.back(), I18N::translate_utf8(L("Pause print or custom G-code"))); +#endif // !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT + } + else + { + for (int i = items_cnt; i >= 0; --i) + { + // create label for color print item + std::string id_str = std::to_string(i + 1) + ": "; + + if (i == 0) { + add_item(m_tool_colors[i], id_str + (boost::format(I18N::translate_utf8(L("up to %.2f mm"))) % cp_values.front().first).str()); + break; + } + else if (i == items_cnt) { + add_item(m_tool_colors[i], id_str + (boost::format(I18N::translate_utf8(L("above %.2f mm"))) % cp_values[i - 1].second).str()); + continue; + } + add_item(m_tool_colors[i], id_str + (boost::format(I18N::translate_utf8(L("%.2f - %.2f mm"))) % cp_values[i - 1].second % cp_values[i].first).str()); + } + } + } + } + else + { + } + + break; + } + default: { break; } } imgui.end(); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 956dd0b5d0..5ce497c976 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -15,9 +15,8 @@ namespace GUI { class GCodeViewer { - static const std::array, erCount> Extrusion_Role_Colors; - static const size_t Range_Colors_Count = 10; - static const std::array, Range_Colors_Count> Range_Colors; + static const std::vector> Extrusion_Role_Colors; + static const std::vector> Range_Colors; // buffer containing vertices data struct VBuffer @@ -46,11 +45,12 @@ class GCodeViewer float fan_speed{ 0.0f }; float volumetric_rate{ 0.0f }; unsigned char extruder_id{ 0 }; + unsigned char cp_color_id{ 0 }; bool matches(const GCodeProcessor::MoveVertex& move) const { return type == move.type && role == move.extrusion_role && height == move.height && width == move.width && feedrate == move.feedrate && fan_speed == move.fan_speed && volumetric_rate == move.volumetric_rate() && - extruder_id == move.extruder_id; + extruder_id == move.extruder_id && cp_color_id == move.cp_color_id; } }; @@ -89,7 +89,7 @@ class GCodeViewer void update_from(const float value) { min = std::min(min, value); max = std::max(max, value); } void reset() { min = FLT_MAX; max = -FLT_MAX; } - float step_size() const { return (max - min) / (static_cast(Range_Colors_Count) - 1.0f); } + float step_size() const { return (max - min) / (static_cast(Range_Colors.size()) - 1.0f); } std::array get_color_at(float value) const; }; @@ -155,6 +155,8 @@ private: std::vector> m_tool_colors; std::vector m_layers_zs; std::vector m_roles; + std::vector m_extruder_ids; +// std::vector m_cp_color_ids; Extrusions m_extrusions; Shells m_shells; EViewType m_view_type{ EViewType::FeatureType }; @@ -170,9 +172,9 @@ public: } // extract rendering data from the given parameters - void load(const GCodeProcessor::Result& gcode_result, const std::vector& str_tool_colors, const Print& print, bool initialized); - // recalculate ranges in dependence of what is visible - void refresh_toolpaths_ranges(const GCodeProcessor::Result& gcode_result); + void load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized); + // recalculate ranges in dependence of what is visible and sets tool/print colors + void refresh(const GCodeProcessor::Result& gcode_result, const std::vector& str_tool_colors); void reset(); void render() const; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 540896a59f..225fb427cf 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2835,7 +2835,7 @@ static void load_gcode_retractions(const GCodePreviewData::Retraction& retractio #endif // !ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER -void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector& str_tool_colors) +void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result) { #if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT static unsigned int last_result_id = 0; @@ -2852,12 +2852,12 @@ void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result, out.close(); } #endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - m_gcode_viewer.load(gcode_result, str_tool_colors , *this->fff_print(), m_initialized); + m_gcode_viewer.load(gcode_result, *this->fff_print(), m_initialized); } -void GLCanvas3D::refresh_toolpaths_ranges(const GCodeProcessor::Result& gcode_result) +void GLCanvas3D::refresh_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector& str_tool_colors) { - m_gcode_viewer.refresh_toolpaths_ranges(gcode_result); + m_gcode_viewer.refresh(gcode_result, str_tool_colors); } #endif // ENABLE_GCODE_VIEWER diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 54f6e181e5..25647b5e55 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -664,8 +664,10 @@ public: void reload_scene(bool refresh_immediately, bool force_full_scene_refresh = false); #if ENABLE_GCODE_VIEWER - void load_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector& str_tool_colors); - void refresh_toolpaths_ranges(const GCodeProcessor::Result& gcode_result); + void load_gcode_preview(const GCodeProcessor::Result& gcode_result); + void refresh_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector& str_tool_colors); + void set_gcode_view_preview_type(GCodeViewer::EViewType type) { return m_gcode_viewer.set_view_type(type); } + GCodeViewer::EViewType get_gcode_view_preview_type() const { return m_gcode_viewer.get_view_type(); } #else void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector& str_tool_colors); #endif // ENABLE_GCODE_VIEWER diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 9476ad1f0b..520534ca7d 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -229,6 +229,12 @@ Preview::Preview( , m_checkbox_travel(nullptr) , m_checkbox_retractions(nullptr) , m_checkbox_unretractions(nullptr) +#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT + , m_checkbox_tool_changes(nullptr) + , m_checkbox_color_changes(nullptr) + , m_checkbox_pause_prints(nullptr) + , m_checkbox_custom_gcodes(nullptr) +#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT , m_checkbox_shells(nullptr) , m_checkbox_legend(nullptr) , m_config(config) @@ -330,6 +336,12 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view m_checkbox_travel = new wxCheckBox(this, wxID_ANY, _(L("Travel"))); m_checkbox_retractions = new wxCheckBox(this, wxID_ANY, _(L("Retractions"))); m_checkbox_unretractions = new wxCheckBox(this, wxID_ANY, _(L("Unretractions"))); +#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT + m_checkbox_tool_changes = new wxCheckBox(this, wxID_ANY, _(L("Tool changes"))); + m_checkbox_color_changes = new wxCheckBox(this, wxID_ANY, _(L("Color changes"))); + m_checkbox_pause_prints = new wxCheckBox(this, wxID_ANY, _(L("Pause prints"))); + m_checkbox_custom_gcodes = new wxCheckBox(this, wxID_ANY, _(L("Custom GCodes"))); +#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT m_checkbox_shells = new wxCheckBox(this, wxID_ANY, _(L("Shells"))); m_checkbox_legend = new wxCheckBox(this, wxID_ANY, _(L("Legend"))); m_checkbox_legend->SetValue(true); @@ -351,6 +363,16 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view bottom_sizer->AddSpacer(10); bottom_sizer->Add(m_checkbox_unretractions, 0, wxEXPAND | wxALL, 5); bottom_sizer->AddSpacer(10); +#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT + bottom_sizer->Add(m_checkbox_tool_changes, 0, wxEXPAND | wxALL, 5); + bottom_sizer->AddSpacer(10); + bottom_sizer->Add(m_checkbox_color_changes, 0, wxEXPAND | wxALL, 5); + bottom_sizer->AddSpacer(10); + bottom_sizer->Add(m_checkbox_pause_prints, 0, wxEXPAND | wxALL, 5); + bottom_sizer->AddSpacer(10); + bottom_sizer->Add(m_checkbox_custom_gcodes, 0, wxEXPAND | wxALL, 5); + bottom_sizer->AddSpacer(10); +#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT bottom_sizer->Add(m_checkbox_shells, 0, wxEXPAND | wxALL, 5); bottom_sizer->AddSpacer(20); bottom_sizer->Add(m_checkbox_legend, 0, wxEXPAND | wxALL, 5); @@ -533,6 +555,12 @@ void Preview::bind_event_handlers() m_checkbox_travel->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_travel, this); m_checkbox_retractions->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_retractions, this); m_checkbox_unretractions->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_unretractions, this); +#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT + m_checkbox_tool_changes->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_tool_changes, this); + m_checkbox_color_changes->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_color_changes, this); + m_checkbox_pause_prints->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_pause_prints, this); + m_checkbox_custom_gcodes->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_custom_gcodes, this); +#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT m_checkbox_shells->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_shells, this); m_checkbox_legend->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_legend, this); } @@ -545,6 +573,12 @@ void Preview::unbind_event_handlers() m_checkbox_travel->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_travel, this); m_checkbox_retractions->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_retractions, this); m_checkbox_unretractions->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_unretractions, this); +#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT + m_checkbox_tool_changes->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_tool_changes, this); + m_checkbox_color_changes->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_color_changes, this); + m_checkbox_pause_prints->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_pause_prints, this); + m_checkbox_custom_gcodes->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_custom_gcodes, this); +#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT m_checkbox_shells->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_shells, this); m_checkbox_legend->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_legend, this); } @@ -557,6 +591,12 @@ void Preview::show_hide_ui_elements(const std::string& what) m_checkbox_travel->Enable(enable); m_checkbox_retractions->Enable(enable); m_checkbox_unretractions->Enable(enable); +#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT + m_checkbox_tool_changes->Enable(enable); + m_checkbox_color_changes->Enable(enable); + m_checkbox_pause_prints->Enable(enable); + m_checkbox_custom_gcodes->Enable(enable); +#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT m_checkbox_shells->Enable(enable); m_checkbox_legend->Enable(enable); @@ -570,6 +610,12 @@ void Preview::show_hide_ui_elements(const std::string& what) m_checkbox_travel->Show(visible); m_checkbox_retractions->Show(visible); m_checkbox_unretractions->Show(visible); +#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT + m_checkbox_tool_changes->Show(visible); + m_checkbox_color_changes->Show(visible); + m_checkbox_pause_prints->Show(visible); + m_checkbox_custom_gcodes->Show(visible); +#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT m_checkbox_shells->Show(visible); m_checkbox_legend->Show(visible); m_label_view_type->Show(visible); @@ -663,6 +709,32 @@ void Preview::on_checkbox_unretractions(wxCommandEvent& evt) refresh_print(); } +#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT +void Preview::on_checkbox_tool_changes(wxCommandEvent& evt) +{ + m_canvas->set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Tool_change, m_checkbox_tool_changes->IsChecked()); + refresh_print(); +} + +void Preview::on_checkbox_color_changes(wxCommandEvent& evt) +{ + m_canvas->set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Color_change, m_checkbox_color_changes->IsChecked()); + refresh_print(); +} + +void Preview::on_checkbox_pause_prints(wxCommandEvent& evt) +{ + m_canvas->set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Pause_Print, m_checkbox_pause_prints->IsChecked()); + refresh_print(); +} + +void Preview::on_checkbox_custom_gcodes(wxCommandEvent& evt) +{ + m_canvas->set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Custom_GCode, m_checkbox_custom_gcodes->IsChecked()); + refresh_print(); +} +#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT + void Preview::on_checkbox_shells(wxCommandEvent& evt) { #if ENABLE_GCODE_VIEWER @@ -953,26 +1025,46 @@ void Preview::load_print_as_fff(bool keep_z_range) int tool_idx = m_choice_view_type->FindString(_(L("Tool"))); int type = (number_extruders > 1) ? tool_idx /* color by a tool number */ : 0; // color by a feature type m_choice_view_type->SetSelection(type); +#if ENABLE_GCODE_VIEWER + if (0 <= type && type < static_cast(GCodeViewer::EViewType::Count)) + m_canvas->set_gcode_view_preview_type(static_cast(type)); +#else if ((0 <= type) && (type < (int)GCodePreviewData::Extrusion::Num_View_Types)) m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type; +#endif // ENABLE_GCODE_VIEWER // If the->SetSelection changed the following line, revert it to "decide yourself". m_preferred_color_mode = "tool_or_feature"; } +#if ENABLE_GCODE_VIEWER + GCodeViewer::EViewType gcode_view_type = m_canvas->get_gcode_view_preview_type(); + bool gcode_preview_data_valid = print->is_step_done(psGCodeExport); +#else bool gcode_preview_data_valid = print->is_step_done(psGCodeExport) && ! m_gcode_preview_data->empty(); +#endif // ENABLE_GCODE_VIEWER // Collect colors per extruder. std::vector colors; std::vector color_print_values = {}; // set color print values, if it si selected "ColorPrint" view type +#if ENABLE_GCODE_VIEWER + if (gcode_view_type == GCodeViewer::EViewType::ColorPrint) +#else if (m_gcode_preview_data->extrusion.view_type == GCodePreviewData::Extrusion::ColorPrint) +#endif // ENABLE_GCODE_VIEWER { colors = wxGetApp().plater()->get_colors_for_color_print(); +#if !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT colors.push_back("#808080"); // gray color for pause print or custom G-code +#endif // !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT if (!gcode_preview_data_valid) color_print_values = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes; } +#if ENABLE_GCODE_VIEWER + else if (gcode_preview_data_valid || gcode_view_type == GCodeViewer::EViewType::Tool) +#else else if (gcode_preview_data_valid || (m_gcode_preview_data->extrusion.view_type == GCodePreviewData::Extrusion::Tool) ) +#endif // ENABLE_GCODE_VIEWER { colors = wxGetApp().plater()->get_extruder_colors_from_plater_config(); color_print_values.clear(); @@ -984,8 +1076,8 @@ void Preview::load_print_as_fff(bool keep_z_range) if (gcode_preview_data_valid) { // Load the real G-code preview. #if ENABLE_GCODE_VIEWER - m_canvas->load_gcode_preview(*m_gcode_result, colors); - m_canvas->refresh_toolpaths_ranges(*m_gcode_result); + m_canvas->load_gcode_preview(*m_gcode_result); + m_canvas->refresh_gcode_preview(*m_gcode_result, colors); #else m_canvas->load_gcode_preview(*m_gcode_preview_data, colors); #endif // ENABLE_GCODE_VIEWER diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 654ce4dbf2..a5d93a1925 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -98,6 +98,12 @@ class Preview : public wxPanel wxCheckBox* m_checkbox_travel; wxCheckBox* m_checkbox_retractions; wxCheckBox* m_checkbox_unretractions; +#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT + wxCheckBox* m_checkbox_tool_changes; + wxCheckBox* m_checkbox_color_changes; + wxCheckBox* m_checkbox_pause_prints; + wxCheckBox* m_checkbox_custom_gcodes; +#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT wxCheckBox* m_checkbox_shells; wxCheckBox* m_checkbox_legend; @@ -189,6 +195,12 @@ private: void on_checkbox_travel(wxCommandEvent& evt); void on_checkbox_retractions(wxCommandEvent& evt); void on_checkbox_unretractions(wxCommandEvent& evt); +#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT + void on_checkbox_tool_changes(wxCommandEvent& evt); + void on_checkbox_color_changes(wxCommandEvent& evt); + void on_checkbox_pause_prints(wxCommandEvent& evt); + void on_checkbox_custom_gcodes(wxCommandEvent& evt); +#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT void on_checkbox_shells(wxCommandEvent& evt); void on_checkbox_legend(wxCommandEvent& evt); From 7be12e8f1eb2f2e37c39b1a778402a6a2e06fec1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 23 Apr 2020 10:24:03 +0200 Subject: [PATCH 033/503] GCodeViewer -> Completed extrusion toolpaths colored by color print --- src/slic3r/GUI/GCodeViewer.cpp | 120 +++++++++++++++------------------ src/slic3r/GUI/GCodeViewer.hpp | 4 +- 2 files changed, 56 insertions(+), 68 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 2f039da0b3..75b2fa98de 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -40,10 +40,8 @@ std::vector> decode_colors(const std::vector& { const std::string& color = colors[i]; const char* c = color.data() + 1; - if ((color.size() == 7) && (color.front() == '#')) - { - for (size_t j = 0; j < 3; ++j) - { + if ((color.size() == 7) && (color.front() == '#')) { + for (size_t j = 0; j < 3; ++j) { int digit1 = hex_digit_to_int(*c++); int digit2 = hex_digit_to_int(*c++); if ((digit1 == -1) || (digit2 == -1)) @@ -59,8 +57,7 @@ std::vector> decode_colors(const std::vector& void GCodeViewer::VBuffer::reset() { // release gpu memory - if (vbo_id > 0) - { + if (vbo_id > 0) { glsafe(::glDeleteBuffers(1, &vbo_id)); vbo_id = 0; } @@ -71,8 +68,7 @@ void GCodeViewer::VBuffer::reset() void GCodeViewer::IBuffer::reset() { // release gpu memory - if (ibo_id > 0) - { + if (ibo_id > 0) { glsafe(::glDeleteBuffers(1, &ibo_id)); ibo_id = 0; } @@ -85,8 +81,7 @@ void GCodeViewer::IBuffer::reset() bool GCodeViewer::IBuffer::init_shader(const std::string& vertex_shader_src, const std::string& fragment_shader_src) { - if (!shader.init(vertex_shader_src, fragment_shader_src)) - { + if (!shader.init(vertex_shader_src, fragment_shader_src)) { BOOST_LOG_TRIVIAL(error) << "Unable to initialize toolpaths shader: please, check that the files " << vertex_shader_src << " and " << fragment_shader_src << " are available"; return false; } @@ -117,8 +112,7 @@ std::array GCodeViewer::Extrusions::Range::get_color_at(float value) c // Interpolate between the low and high colors to find exactly which color the input value should get std::array ret; - for (unsigned int i = 0; i < 3; ++i) - { + for (unsigned int i = 0; i < 3; ++i) { ret[i] = lerp(Range_Colors[color_low_idx][i], Range_Colors[color_high_idx][i], local_t); } return ret; @@ -192,8 +186,7 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: case GCodeProcessor::EMoveType::Extrude: case GCodeProcessor::EMoveType::Travel: { - if (m_buffers[buffer_id(curr.type)].visible) - { + if (m_buffers[buffer_id(curr.type)].visible) { m_extrusions.ranges.height.update_from(curr.height); m_extrusions.ranges.width.update_from(curr.width); m_extrusions.ranges.feedrate.update_from(curr.feedrate); @@ -211,8 +204,7 @@ void GCodeViewer::reset() { m_vertices.reset(); - for (IBuffer& buffer : m_buffers) - { + for (IBuffer& buffer : m_buffers) { buffer.reset(); } @@ -235,7 +227,7 @@ void GCodeViewer::render() const render_overlay(); } -bool GCodeViewer::is_toolpath_visible(GCodeProcessor::EMoveType type) const +bool GCodeViewer::is_toolpath_move_type_visible(GCodeProcessor::EMoveType type) const { size_t id = static_cast(buffer_id(type)); return (id < m_buffers.size()) ? m_buffers[id].visible : false; @@ -277,8 +269,7 @@ bool GCodeViewer::init_shaders() return false; } - if (!m_shells.shader.init("shells.vs", "shells.fs")) - { + if (!m_shells.shader.init("shells.vs", "shells.fs")) { BOOST_LOG_TRIVIAL(error) << "Unable to initialize shells shader: please, check that the files shells.vs and shells.fs are available"; return false; } @@ -297,10 +288,8 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) // vertex data / bounding box -> extract from result std::vector vertices_data; - for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) - { - for (int j = 0; j < 3; ++j) - { + for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) { + for (int j = 0; j < 3; ++j) { vertices_data.insert(vertices_data.end(), move.position[j]); m_bounding_box.merge(move.position.cast()); } @@ -345,8 +334,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) case GCodeProcessor::EMoveType::Extrude: case GCodeProcessor::EMoveType::Travel: { - if (prev.type != curr.type || !buffer.paths.back().matches(curr)) - { + if (prev.type != curr.type || !buffer.paths.back().matches(curr)) { buffer.add_path(curr); buffer.data.push_back(static_cast(i - 1)); } @@ -366,8 +354,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) for (IBuffer& buffer : m_buffers) { buffer.data_size = buffer.data.size(); - if (buffer.data_size > 0) - { + if (buffer.data_size > 0) { glsafe(::glGenBuffers(1, &buffer.ibo_id)); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.ibo_id)); glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, buffer.data_size * sizeof(unsigned int), buffer.data.data(), GL_STATIC_DRAW)); @@ -379,8 +366,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) } // layers zs / roles / extruder ids / cp color ids -> extract from result - for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) - { + for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) { if (move.type == GCodeProcessor::EMoveType::Extrude) m_layers_zs.emplace_back(move.position[2]); @@ -432,8 +418,7 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) const ModelObject* model_obj = obj->model_object(); std::vector instance_ids(model_obj->instances.size()); - for (int i = 0; i < (int)model_obj->instances.size(); ++i) - { + for (int i = 0; i < (int)model_obj->instances.size(); ++i) { instance_ids[i] = i; } @@ -448,7 +433,6 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) const PrintConfig& config = print.config(); size_t extruders_count = config.nozzle_diameter.size(); if ((extruders_count > 1) && config.wipe_tower && !config.complete_objects) { - const DynamicPrintConfig& print_config = wxGetApp().preset_bundle->prints.get_edited_preset().config; double layer_height = print_config.opt_float("layer_height"); double first_layer_height = print_config.get_abs_value("first_layer_height", layer_height); @@ -514,8 +498,7 @@ void GCodeViewer::render_toolpaths() const glsafe(::glVertexPointer(3, GL_FLOAT, VBuffer::vertex_size_bytes(), (const void*)0)); glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); - for (unsigned char i = begin_id; i < end_id; ++i) - { + for (unsigned char i = begin_id; i < end_id; ++i) { const IBuffer& buffer = m_buffers[i]; if (buffer.ibo_id == 0) continue; @@ -523,8 +506,7 @@ void GCodeViewer::render_toolpaths() const if (!buffer.visible) continue; - if (buffer.shader.is_initialized()) - { + if (buffer.shader.is_initialized()) { GCodeProcessor::EMoveType type = buffer_type(i); buffer.shader.start_using(); @@ -594,8 +576,7 @@ void GCodeViewer::render_toolpaths() const } case GCodeProcessor::EMoveType::Extrude: { - for (const Path& path : buffer.paths) - { + for (const Path& path : buffer.paths) { if (!is_path_visible(m_extrusions.role_visibility_flags, path)) continue; @@ -608,8 +589,7 @@ void GCodeViewer::render_toolpaths() const { std::array color = { 1.0f, 1.0f, 0.0f }; set_color(current_program_id, color); - for (const Path& path : buffer.paths) - { + for (const Path& path : buffer.paths) { glsafe(::glDrawElements(GL_LINE_STRIP, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); } break; @@ -680,8 +660,7 @@ void GCodeViewer::render_overlay() const add_range_item(0, range.min, decimals); else { - for (int i = static_cast(Range_Colors.size()) - 1; i >= 0; --i) - { + for (int i = static_cast(Range_Colors.size()) - 1; i >= 0; --i) { add_range_item(i, range.min + static_cast(i) * step_size, decimals); } } @@ -708,8 +687,7 @@ void GCodeViewer::render_overlay() const { case EViewType::FeatureType: { - for (ExtrusionRole role : m_roles) - { + for (ExtrusionRole role : m_roles) { add_item(Extrusion_Role_Colors[static_cast(role)], I18N::translate_utf8(ExtrusionEntity::role_to_string(role))); } break; @@ -722,8 +700,8 @@ void GCodeViewer::render_overlay() const case EViewType::Tool: { size_t tools_count = m_tool_colors.size(); - for (size_t i = 0; i < tools_count; ++i) - { + for (size_t i = 0; i < tools_count; ++i) { + // shows only extruders actually used auto it = std::find(m_extruder_ids.begin(), m_extruder_ids.end(), static_cast(i)); if (it == m_extruder_ids.end()) continue; @@ -736,18 +714,15 @@ void GCodeViewer::render_overlay() const { const std::vector& custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes; const int extruders_count = wxGetApp().extruders_edited_cnt(); - if (extruders_count == 1) // single extruder use case - { + if (extruders_count == 1) { // single extruder use case if (custom_gcode_per_print_z.empty()) // no data to show add_item(m_tool_colors.front(), I18N::translate_utf8(L("Default print color"))); - else - { + else { std::vector> cp_values; cp_values.reserve(custom_gcode_per_print_z.size()); - for (auto custom_code : custom_gcode_per_print_z) - { + for (auto custom_code : custom_gcode_per_print_z) { if (custom_code.gcode != ColorChangeCode) continue; @@ -760,41 +735,54 @@ void GCodeViewer::render_overlay() const double previous_z = lower_b == m_layers_zs.begin() ? 0.0 : *(--lower_b); // to avoid duplicate values, check adding values - if (cp_values.empty() || - !(cp_values.back().first == previous_z && cp_values.back().second == current_z)) + if (cp_values.empty() || !(cp_values.back().first == previous_z && cp_values.back().second == current_z)) cp_values.emplace_back(std::make_pair(previous_z, current_z)); } const int items_cnt = static_cast(cp_values.size()); - if (items_cnt == 0) // There is no one color change, but there are some pause print or custom Gcode - { + if (items_cnt == 0) { // There is no one color change, but there are some pause print or custom Gcode add_item(m_tool_colors.front(), I18N::translate_utf8(L("Default print color"))); #if !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT add_item(m_tool_colors.back(), I18N::translate_utf8(L("Pause print or custom G-code"))); #endif // !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT } - else - { - for (int i = items_cnt; i >= 0; --i) - { - // create label for color print item - std::string id_str = std::to_string(i + 1) + ": "; + else { + for (int i = items_cnt; i >= 0; --i) { + // create label for color change item + std::string id_str = " (" + std::to_string(i + 1) + ")"; if (i == 0) { - add_item(m_tool_colors[i], id_str + (boost::format(I18N::translate_utf8(L("up to %.2f mm"))) % cp_values.front().first).str()); + add_item(m_tool_colors[i], (boost::format(I18N::translate_utf8(L("up to %.2f mm"))) % cp_values.front().first).str() + id_str); break; } else if (i == items_cnt) { - add_item(m_tool_colors[i], id_str + (boost::format(I18N::translate_utf8(L("above %.2f mm"))) % cp_values[i - 1].second).str()); + add_item(m_tool_colors[i], (boost::format(I18N::translate_utf8(L("above %.2f mm"))) % cp_values[i - 1].second).str() + id_str); continue; } - add_item(m_tool_colors[i], id_str + (boost::format(I18N::translate_utf8(L("%.2f - %.2f mm"))) % cp_values[i - 1].second % cp_values[i].first).str()); + add_item(m_tool_colors[i], (boost::format(I18N::translate_utf8(L("%.2f - %.2f mm"))) % cp_values[i - 1].second % cp_values[i].first).str() + id_str); } } } } - else + else // multi extruder use case { + // extruders + for (unsigned int i = 0; i < (unsigned int)extruders_count; ++i) { + add_item(m_tool_colors[i], (boost::format(I18N::translate_utf8(L("Extruder %d"))) % (i + 1)).str()); + } + + // color changes + int color_change_idx = 1 + static_cast(m_tool_colors.size()) - extruders_count; + size_t last_color_id = m_tool_colors.size() - 1; + for (int i = static_cast(custom_gcode_per_print_z.size()) - 1; i >= 0; --i) { + if (custom_gcode_per_print_z[i].gcode == ColorChangeCode) { + // create label for color change item + std::string id_str = " (" + std::to_string(color_change_idx--) + ")"; + + add_item(m_tool_colors[last_color_id--], + (boost::format(I18N::translate_utf8(L("Color change for Extruder %d at %.2f mm"))) % custom_gcode_per_print_z[i].extruder % custom_gcode_per_print_z[i].print_z).str() + id_str); + } + } } break; diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 5ce497c976..669d56cf49 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -150,7 +150,7 @@ public: private: unsigned int m_last_result_id{ 0 }; VBuffer m_vertices; - std::vector m_buffers{ static_cast(GCodeProcessor::EMoveType::Extrude) }; + mutable std::vector m_buffers{ static_cast(GCodeProcessor::EMoveType::Extrude) }; BoundingBoxf3 m_bounding_box; std::vector> m_tool_colors; std::vector m_layers_zs; @@ -190,7 +190,7 @@ public: m_view_type = type; } - bool is_toolpath_visible(GCodeProcessor::EMoveType type) const; + bool is_toolpath_move_type_visible(GCodeProcessor::EMoveType type) const; void set_toolpath_move_type_visible(GCodeProcessor::EMoveType type, bool visible); void set_toolpath_role_visibility_flags(unsigned int flags) { m_extrusions.role_visibility_flags = flags; } From 6e2307f56d85dae6cce9eb3ab228dc356ed35698 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 23 Apr 2020 14:02:47 +0200 Subject: [PATCH 034/503] GCodeViewer -> Refactoring --- src/slic3r/GUI/GCodeViewer.cpp | 36 +++++++++++++++++++++------------- src/slic3r/GUI/GCodeViewer.hpp | 5 +++-- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 75b2fa98de..8079008ed3 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -10,6 +10,8 @@ #if ENABLE_GCODE_VIEWER #include "GUI_Utils.hpp" #include "DoubleSlider.hpp" +#include "GLToolbar.hpp" +#include "GLCanvas3D.hpp" #include "libslic3r/Model.hpp" #endif // ENABLE_GCODE_VIEWER @@ -211,7 +213,6 @@ void GCodeViewer::reset() m_bounding_box = BoundingBoxf3(); m_tool_colors = std::vector>(); m_extruder_ids = std::vector(); -// m_cp_color_ids = std::vector(); m_extrusions.reset_role_visibility_flags(); m_extrusions.reset_ranges(); m_shells.volumes.clear(); @@ -372,7 +373,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) m_roles.emplace_back(move.extrusion_role); m_extruder_ids.emplace_back(move.extruder_id); -// m_cp_color_ids.emplace_back(move.cp_color_id); } // layers zs -> replace intervals of layers with similar top positions with their average value. @@ -397,10 +397,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) std::sort(m_extruder_ids.begin(), m_extruder_ids.end()); m_extruder_ids.erase(std::unique(m_extruder_ids.begin(), m_extruder_ids.end()), m_extruder_ids.end()); -// // cp color ids -> remove duplicates -// std::sort(m_cp_color_ids.begin(), m_cp_color_ids.end()); -// m_cp_color_ids.erase(std::unique(m_cp_color_ids.begin(), m_cp_color_ids.end()), m_cp_color_ids.end()); - auto end_time = std::chrono::high_resolution_clock::now(); std::cout << "toolpaths generation time: " << std::chrono::duration_cast(end_time - start_time).count() << "ms \n"; } @@ -620,11 +616,17 @@ void GCodeViewer::render_shells() const } void GCodeViewer::render_overlay() const +{ + render_legend(); + render_toolbar(); +} + +void GCodeViewer::render_legend() const { static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); static const float ICON_BORDER_SIZE = 25.0f; static const ImU32 ICON_BORDER_COLOR = ImGui::GetColorU32(ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); - static const float GAP_ICON_TEXT = 5.0f; + static const float GAP_ICON_TEXT = 7.5f; if (!m_legend_enabled || m_roles.empty()) return; @@ -633,17 +635,19 @@ void GCodeViewer::render_overlay() const imgui.set_next_window_pos(0, 0, ImGuiCond_Always); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - imgui.begin(_L("Legend"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); + imgui.begin(std::string("Legend"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); ImDrawList* draw_list = ImGui::GetWindowDrawList(); auto add_item = [draw_list, &imgui](const std::array& color, const std::string& label) { + // draw icon ImVec2 pos(ImGui::GetCursorPosX() + 2.0f, ImGui::GetCursorPosY() + 2.0f); - draw_list->AddRect(ImVec2(pos.x, pos.y), ImVec2(pos.x + ICON_BORDER_SIZE, pos.y + ICON_BORDER_SIZE), ICON_BORDER_COLOR, 0.0f, 0); - ImU32 fill_color = ImGui::GetColorU32(ImVec4(color[0], color[1], color[2], 1.0f)); - draw_list->AddRectFilled(ImVec2(pos.x + 1.0f, pos.y + 1.0f), ImVec2(pos.x + ICON_BORDER_SIZE - 1.0f, pos.y + ICON_BORDER_SIZE - 1.0f), fill_color); - ImGui::SetCursorPosX(pos.x + ICON_BORDER_SIZE + GAP_ICON_TEXT); - ImGui::AlignTextToFramePadding(); + draw_list->AddRect({ pos.x, pos.y }, { pos.x + ICON_BORDER_SIZE, pos.y + ICON_BORDER_SIZE }, ICON_BORDER_COLOR, 0.0f, 0); + draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, + { pos.x + ICON_BORDER_SIZE - 1.0f, pos.y + ICON_BORDER_SIZE - 1.0f }, + ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f })); + // draw text + ImGui::SetCursorPos({ pos.x + ICON_BORDER_SIZE + GAP_ICON_TEXT, pos.y + 0.5f * (ICON_BORDER_SIZE - ImGui::GetTextLineHeight()) }); imgui.text(label); }; @@ -673,7 +677,7 @@ void GCodeViewer::render_overlay() const case EViewType::Height: { imgui.text(I18N::translate_utf8(L("Height (mm)"))); break; } case EViewType::Width: { imgui.text(I18N::translate_utf8(L("Width (mm)"))); break; } case EViewType::Feedrate: { imgui.text(I18N::translate_utf8(L("Speed (mm/s)"))); break; } - case EViewType::FanSpeed: { imgui.text(I18N::translate_utf8(L("Fan Speed (%)"))); break; } + case EViewType::FanSpeed: { imgui.text(I18N::translate_utf8(L("Fan Speed (%%)"))); break; } case EViewType::VolumetricRate: { imgui.text(I18N::translate_utf8(L("Volumetric flow rate (mm³/s)"))); break; } case EViewType::Tool: { imgui.text(I18N::translate_utf8(L("Tool"))); break; } case EViewType::ColorPrint: { imgui.text(I18N::translate_utf8(L("Color Print"))); break; } @@ -794,6 +798,10 @@ void GCodeViewer::render_overlay() const ImGui::PopStyleVar(); } +void GCodeViewer::render_toolbar() const +{ +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 669d56cf49..53d1fbd75e 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -156,10 +156,9 @@ private: std::vector m_layers_zs; std::vector m_roles; std::vector m_extruder_ids; -// std::vector m_cp_color_ids; Extrusions m_extrusions; Shells m_shells; - EViewType m_view_type{ EViewType::FeatureType }; + mutable EViewType m_view_type{ EViewType::FeatureType }; bool m_legend_enabled{ true }; public: @@ -207,6 +206,8 @@ private: void render_toolpaths() const; void render_shells() const; void render_overlay() const; + void render_legend() const; + void render_toolbar() const; }; } // namespace GUI From 66964c44c10ff7f36c01f80ceedf9bfb483e247b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 23 Apr 2020 15:12:40 +0200 Subject: [PATCH 035/503] GCodeViewer -> Refactoring and code cleanup --- src/libslic3r/GCode.cpp | 10 ------- src/libslic3r/GCode/GCodeProcessor.cpp | 30 +-------------------- src/libslic3r/GCode/GCodeProcessor.hpp | 5 ---- src/libslic3r/GCode/ToolOrdering.cpp | 4 ++- src/libslic3r/Technologies.hpp | 1 - src/slic3r/GUI/GCodeViewer.cpp | 9 ------- src/slic3r/GUI/GUI_Preview.cpp | 36 +++++++++++++------------- src/slic3r/GUI/GUI_Preview.hpp | 8 +++--- 8 files changed, 26 insertions(+), 77 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index a1114e938e..58eb2e5ae7 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2328,16 +2328,6 @@ void GCode::process_layer( else if (gcode.find(GCodeAnalyzer::Custom_Code_Tag) != gcode.npos) gcode += "\n; " + GCodeAnalyzer::End_Pause_Print_Or_Custom_Code_Tag + "\n"; -#if ENABLE_GCODE_VIEWER -#if !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT - // add tag for processor - if (gcode.find(GCodeProcessor::Pause_Print_Tag) != gcode.npos) - gcode += "\n; " + GCodeProcessor::End_Pause_Print_Or_Custom_Code_Tag + "\n"; - else if (gcode.find(GCodeProcessor::Custom_Code_Tag) != gcode.npos) - gcode += "\n; " + GCodeProcessor::End_Pause_Print_Or_Custom_Code_Tag + "\n"; -#endif // !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT -#endif // ENABLE_GCODE_VIEWER - #ifdef HAS_PRESSURE_EQUALIZER // Apply pressure equalization if enabled; // printf("G-code before filter:\n%s\n", gcode.c_str()); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 2219facb51..6d491a88c9 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -24,9 +24,6 @@ const std::string GCodeProcessor::Mm3_Per_Mm_Tag = "_PROCESSOR_MM3_PER_MM:"; const std::string GCodeProcessor::Color_Change_Tag = "_PROCESSOR_COLOR_CHANGE"; const std::string GCodeProcessor::Pause_Print_Tag = "_PROCESSOR_PAUSE_PRINT"; const std::string GCodeProcessor::Custom_Code_Tag = "_PROCESSOR_CUSTOM_CODE"; -#if !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT -const std::string GCodeProcessor::End_Pause_Print_Or_Custom_Code_Tag = "_PROCESSOR_END_PAUSE_PRINT_OR_CUSTOM_CODE"; -#endif // !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT void GCodeProcessor::CachedPosition::reset() { @@ -248,9 +245,7 @@ void GCodeProcessor::process_tags(const std::string& comment) if (m_extruder_id == extruder_id) { m_cp_color.current = m_extruders_color[extruder_id]; -#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT store_move_vertex(EMoveType::Color_change); -#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT } } catch (...) @@ -265,11 +260,7 @@ void GCodeProcessor::process_tags(const std::string& comment) pos = comment.find(Pause_Print_Tag); if (pos != comment.npos) { -#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT store_move_vertex(EMoveType::Pause_Print); -#else - m_cp_color.current = UCHAR_MAX; -#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT return; } @@ -277,25 +268,9 @@ void GCodeProcessor::process_tags(const std::string& comment) pos = comment.find(Custom_Code_Tag); if (pos != comment.npos) { -#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT store_move_vertex(EMoveType::Custom_GCode); -#else - m_cp_color.current = UCHAR_MAX; -#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT return; } - -#if !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT - // end pause print or custom code tag - pos = comment.find(End_Pause_Print_Or_Custom_Code_Tag); - if (pos != comment.npos) - { - if (m_cp_color.current == UCHAR_MAX) - m_cp_color.current = m_extruders_color[m_extruder_id]; - - return; - } -#endif // !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT } void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) @@ -588,10 +563,7 @@ void GCodeProcessor::process_T(const std::string& command) else { m_extruder_id = id; -#if !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT - if (m_cp_color.current != UCHAR_MAX) -#endif // !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT - m_cp_color.current = m_extruders_color[id]; + m_cp_color.current = m_extruders_color[id]; } // store tool change move diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index cd4e021cae..05aca4e089 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -21,9 +21,6 @@ namespace Slic3r { static const std::string Color_Change_Tag; static const std::string Pause_Print_Tag; static const std::string Custom_Code_Tag; -#if !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT - static const std::string End_Pause_Print_Or_Custom_Code_Tag; -#endif // !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT private: using AxisCoords = std::array; @@ -64,11 +61,9 @@ namespace Slic3r { Retract, Unretract, Tool_change, -#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT Color_change, Pause_Print, Custom_GCode, -#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT Travel, Extrude, Count diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index db398f06c8..7be15bd351 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -400,7 +400,9 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_ // and maybe other problems. We will therefore go through layer_tools and detect and fix this. // So, if there is a non-object layer starting with different extruder than the last one ended with (or containing more than one extruder), // we'll mark it with has_wipe tower. - assert(! m_layer_tools.empty() && m_layer_tools.front().has_wipe_tower); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// assert(! m_layer_tools.empty() && m_layer_tools.front().has_wipe_tower); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (! m_layer_tools.empty() && m_layer_tools.front().has_wipe_tower) { for (size_t i = 0; i + 1 < m_layer_tools.size();) { const LayerTools < = m_layer_tools[i]; diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 8d1e19eceb..1ed939ba3e 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -59,7 +59,6 @@ // Enable G-Code viewer #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) #define ENABLE_GCODE_VIEWER_DEBUG_OUTPUT (0 && ENABLE_GCODE_VIEWER) -#define ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT (1 && ENABLE_GCODE_VIEWER) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 8079008ed3..86a9d38a7c 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -254,11 +254,9 @@ bool GCodeViewer::init_shaders() switch (buffer_type(i)) { case GCodeProcessor::EMoveType::Tool_change: { vertex_shader = "toolchanges.vs"; fragment_shader = "toolchanges.fs"; break; } -#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT case GCodeProcessor::EMoveType::Color_change: { vertex_shader = "colorchanges.vs"; fragment_shader = "colorchanges.fs"; break; } case GCodeProcessor::EMoveType::Pause_Print: { vertex_shader = "pauses.vs"; fragment_shader = "pauses.fs"; break; } case GCodeProcessor::EMoveType::Custom_GCode: { vertex_shader = "customs.vs"; fragment_shader = "customs.fs"; break; } -#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT case GCodeProcessor::EMoveType::Retract: { vertex_shader = "retractions.vs"; fragment_shader = "retractions.fs"; break; } case GCodeProcessor::EMoveType::Unretract: { vertex_shader = "unretractions.vs"; fragment_shader = "unretractions.fs"; break; } case GCodeProcessor::EMoveType::Extrude: { vertex_shader = "extrusions.vs"; fragment_shader = "extrusions.fs"; break; } @@ -320,11 +318,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) switch (curr.type) { case GCodeProcessor::EMoveType::Tool_change: -#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT case GCodeProcessor::EMoveType::Color_change: case GCodeProcessor::EMoveType::Pause_Print: case GCodeProcessor::EMoveType::Custom_GCode: -#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT case GCodeProcessor::EMoveType::Retract: case GCodeProcessor::EMoveType::Unretract: { @@ -523,7 +519,6 @@ void GCodeViewer::render_toolpaths() const glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); break; } -#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT case GCodeProcessor::EMoveType::Color_change: { std::array color = { 1.0f, 0.0f, 0.0f }; @@ -551,7 +546,6 @@ void GCodeViewer::render_toolpaths() const glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); break; } -#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT case GCodeProcessor::EMoveType::Retract: { std::array color = { 1.0f, 0.0f, 1.0f }; @@ -746,9 +740,6 @@ void GCodeViewer::render_legend() const const int items_cnt = static_cast(cp_values.size()); if (items_cnt == 0) { // There is no one color change, but there are some pause print or custom Gcode add_item(m_tool_colors.front(), I18N::translate_utf8(L("Default print color"))); -#if !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT - add_item(m_tool_colors.back(), I18N::translate_utf8(L("Pause print or custom G-code"))); -#endif // !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT } else { for (int i = items_cnt; i >= 0; --i) { diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 520534ca7d..6c961a4902 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -229,12 +229,12 @@ Preview::Preview( , m_checkbox_travel(nullptr) , m_checkbox_retractions(nullptr) , m_checkbox_unretractions(nullptr) -#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT +#if ENABLE_GCODE_VIEWER , m_checkbox_tool_changes(nullptr) , m_checkbox_color_changes(nullptr) , m_checkbox_pause_prints(nullptr) , m_checkbox_custom_gcodes(nullptr) -#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT +#endif // ENABLE_GCODE_VIEWER , m_checkbox_shells(nullptr) , m_checkbox_legend(nullptr) , m_config(config) @@ -336,12 +336,12 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view m_checkbox_travel = new wxCheckBox(this, wxID_ANY, _(L("Travel"))); m_checkbox_retractions = new wxCheckBox(this, wxID_ANY, _(L("Retractions"))); m_checkbox_unretractions = new wxCheckBox(this, wxID_ANY, _(L("Unretractions"))); -#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT +#if ENABLE_GCODE_VIEWER m_checkbox_tool_changes = new wxCheckBox(this, wxID_ANY, _(L("Tool changes"))); m_checkbox_color_changes = new wxCheckBox(this, wxID_ANY, _(L("Color changes"))); m_checkbox_pause_prints = new wxCheckBox(this, wxID_ANY, _(L("Pause prints"))); m_checkbox_custom_gcodes = new wxCheckBox(this, wxID_ANY, _(L("Custom GCodes"))); -#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT +#endif // ENABLE_GCODE_VIEWER m_checkbox_shells = new wxCheckBox(this, wxID_ANY, _(L("Shells"))); m_checkbox_legend = new wxCheckBox(this, wxID_ANY, _(L("Legend"))); m_checkbox_legend->SetValue(true); @@ -363,7 +363,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view bottom_sizer->AddSpacer(10); bottom_sizer->Add(m_checkbox_unretractions, 0, wxEXPAND | wxALL, 5); bottom_sizer->AddSpacer(10); -#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT +#if ENABLE_GCODE_VIEWER bottom_sizer->Add(m_checkbox_tool_changes, 0, wxEXPAND | wxALL, 5); bottom_sizer->AddSpacer(10); bottom_sizer->Add(m_checkbox_color_changes, 0, wxEXPAND | wxALL, 5); @@ -372,7 +372,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view bottom_sizer->AddSpacer(10); bottom_sizer->Add(m_checkbox_custom_gcodes, 0, wxEXPAND | wxALL, 5); bottom_sizer->AddSpacer(10); -#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT +#endif // ENABLE_GCODE_VIEWER bottom_sizer->Add(m_checkbox_shells, 0, wxEXPAND | wxALL, 5); bottom_sizer->AddSpacer(20); bottom_sizer->Add(m_checkbox_legend, 0, wxEXPAND | wxALL, 5); @@ -555,12 +555,12 @@ void Preview::bind_event_handlers() m_checkbox_travel->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_travel, this); m_checkbox_retractions->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_retractions, this); m_checkbox_unretractions->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_unretractions, this); -#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT +#if ENABLE_GCODE_VIEWER m_checkbox_tool_changes->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_tool_changes, this); m_checkbox_color_changes->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_color_changes, this); m_checkbox_pause_prints->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_pause_prints, this); m_checkbox_custom_gcodes->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_custom_gcodes, this); -#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT +#endif // ENABLE_GCODE_VIEWER m_checkbox_shells->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_shells, this); m_checkbox_legend->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_legend, this); } @@ -573,12 +573,12 @@ void Preview::unbind_event_handlers() m_checkbox_travel->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_travel, this); m_checkbox_retractions->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_retractions, this); m_checkbox_unretractions->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_unretractions, this); -#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT +#if ENABLE_GCODE_VIEWER m_checkbox_tool_changes->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_tool_changes, this); m_checkbox_color_changes->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_color_changes, this); m_checkbox_pause_prints->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_pause_prints, this); m_checkbox_custom_gcodes->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_custom_gcodes, this); -#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT +#endif // ENABLE_GCODE_VIEWER m_checkbox_shells->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_shells, this); m_checkbox_legend->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_legend, this); } @@ -591,12 +591,12 @@ void Preview::show_hide_ui_elements(const std::string& what) m_checkbox_travel->Enable(enable); m_checkbox_retractions->Enable(enable); m_checkbox_unretractions->Enable(enable); -#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT +#if ENABLE_GCODE_VIEWER m_checkbox_tool_changes->Enable(enable); m_checkbox_color_changes->Enable(enable); m_checkbox_pause_prints->Enable(enable); m_checkbox_custom_gcodes->Enable(enable); -#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT +#endif // ENABLE_GCODE_VIEWER m_checkbox_shells->Enable(enable); m_checkbox_legend->Enable(enable); @@ -610,12 +610,12 @@ void Preview::show_hide_ui_elements(const std::string& what) m_checkbox_travel->Show(visible); m_checkbox_retractions->Show(visible); m_checkbox_unretractions->Show(visible); -#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT +#if ENABLE_GCODE_VIEWER m_checkbox_tool_changes->Show(visible); m_checkbox_color_changes->Show(visible); m_checkbox_pause_prints->Show(visible); m_checkbox_custom_gcodes->Show(visible); -#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT +#endif // ENABLE_GCODE_VIEWER m_checkbox_shells->Show(visible); m_checkbox_legend->Show(visible); m_label_view_type->Show(visible); @@ -709,7 +709,7 @@ void Preview::on_checkbox_unretractions(wxCommandEvent& evt) refresh_print(); } -#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT +#if ENABLE_GCODE_VIEWER void Preview::on_checkbox_tool_changes(wxCommandEvent& evt) { m_canvas->set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Tool_change, m_checkbox_tool_changes->IsChecked()); @@ -733,7 +733,7 @@ void Preview::on_checkbox_custom_gcodes(wxCommandEvent& evt) m_canvas->set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Custom_GCode, m_checkbox_custom_gcodes->IsChecked()); refresh_print(); } -#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT +#endif // ENABLE_GCODE_VIEWER void Preview::on_checkbox_shells(wxCommandEvent& evt) { @@ -1053,9 +1053,9 @@ void Preview::load_print_as_fff(bool keep_z_range) #endif // ENABLE_GCODE_VIEWER { colors = wxGetApp().plater()->get_colors_for_color_print(); -#if !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT +#if !ENABLE_GCODE_VIEWER colors.push_back("#808080"); // gray color for pause print or custom G-code -#endif // !ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT +#endif // !ENABLE_GCODE_VIEWER if (!gcode_preview_data_valid) color_print_values = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes; diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index a5d93a1925..bdbbe79daa 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -98,12 +98,12 @@ class Preview : public wxPanel wxCheckBox* m_checkbox_travel; wxCheckBox* m_checkbox_retractions; wxCheckBox* m_checkbox_unretractions; -#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT +#if ENABLE_GCODE_VIEWER wxCheckBox* m_checkbox_tool_changes; wxCheckBox* m_checkbox_color_changes; wxCheckBox* m_checkbox_pause_prints; wxCheckBox* m_checkbox_custom_gcodes; -#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT +#endif // ENABLE_GCODE_VIEWER wxCheckBox* m_checkbox_shells; wxCheckBox* m_checkbox_legend; @@ -195,12 +195,12 @@ private: void on_checkbox_travel(wxCommandEvent& evt); void on_checkbox_retractions(wxCommandEvent& evt); void on_checkbox_unretractions(wxCommandEvent& evt); -#if ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT +#if ENABLE_GCODE_VIEWER void on_checkbox_tool_changes(wxCommandEvent& evt); void on_checkbox_color_changes(wxCommandEvent& evt); void on_checkbox_pause_prints(wxCommandEvent& evt); void on_checkbox_custom_gcodes(wxCommandEvent& evt); -#endif // ENABLE_GCODE_VIEWER_SEPARATE_PAUSE_PRINT +#endif // ENABLE_GCODE_VIEWER void on_checkbox_shells(wxCommandEvent& evt); void on_checkbox_legend(wxCommandEvent& evt); From 90d5cf173596063e2ccf323c01ec81d88e2a5f68 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 23 Apr 2020 15:46:21 +0200 Subject: [PATCH 036/503] Fix to previous commit --- src/libslic3r/GCode/ToolOrdering.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 7be15bd351..db398f06c8 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -400,9 +400,7 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_ // and maybe other problems. We will therefore go through layer_tools and detect and fix this. // So, if there is a non-object layer starting with different extruder than the last one ended with (or containing more than one extruder), // we'll mark it with has_wipe tower. -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -// assert(! m_layer_tools.empty() && m_layer_tools.front().has_wipe_tower); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + assert(! m_layer_tools.empty() && m_layer_tools.front().has_wipe_tower); if (! m_layer_tools.empty() && m_layer_tools.front().has_wipe_tower) { for (size_t i = 0; i + 1 < m_layer_tools.size();) { const LayerTools < = m_layer_tools[i]; From 81a29169aee1e9af381c360a8005897189b06b6e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 24 Apr 2020 08:46:31 +0200 Subject: [PATCH 037/503] GCodeViewer -> Coloring of travel paths --- src/libslic3r/GCode/GCodeProcessor.cpp | 3 ++- src/libslic3r/GCode/GCodeProcessor.hpp | 1 + src/slic3r/GUI/GCodeViewer.cpp | 28 +++++++++++++++++++------- src/slic3r/GUI/GCodeViewer.hpp | 2 ++ 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 6d491a88c9..9dc3964436 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -582,7 +582,8 @@ void GCodeProcessor::store_move_vertex(EMoveType type) MoveVertex vertex; vertex.type = type; vertex.extrusion_role = m_extrusion_role; - vertex.position = Vec3f(m_end_position[0], m_end_position[1], m_end_position[2]) + m_extruder_offsets[m_extruder_id]; + vertex.position = Vec3f(m_end_position[X], m_end_position[Y], m_end_position[Z]) + m_extruder_offsets[m_extruder_id]; + vertex.delta_extruder = m_end_position[E] - m_start_position[E]; vertex.feedrate = m_feedrate; vertex.width = m_width; vertex.height = m_height; diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 05aca4e089..2212148367 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -76,6 +76,7 @@ namespace Slic3r { unsigned char extruder_id{ 0 }; unsigned char cp_color_id{ 0 }; Vec3f position{ Vec3f::Zero() }; // mm + float delta_extruder{ 0.0f }; // mm float feedrate{ 0.0f }; // mm/s float width{ 0.0f }; // mm float height{ 0.0f }; // mm diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 86a9d38a7c..aac22be6f6 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -94,7 +94,7 @@ bool GCodeViewer::IBuffer::init_shader(const std::string& vertex_shader_src, con void GCodeViewer::IBuffer::add_path(const GCodeProcessor::MoveVertex& move) { unsigned int id = static_cast(data.size()); - paths.push_back({ move.type, move.extrusion_role, id, id, move.height, move.width, move.feedrate, move.fan_speed, move.volumetric_rate(), move.extruder_id, move.cp_color_id }); + paths.push_back({ move.type, move.extrusion_role, id, id, move.delta_extruder, move.height, move.width, move.feedrate, move.fan_speed, move.volumetric_rate(), move.extruder_id, move.cp_color_id }); } std::array GCodeViewer::Extrusions::Range::get_color_at(float value) const @@ -138,6 +138,12 @@ const std::vector> GCodeViewer::Extrusion_Role_Colors {{ { 0.00f, 0.00f, 0.00f } // erMixed }}; +const std::vector> GCodeViewer::Travel_Colors {{ + { 0.0f, 0.0f, 0.5f }, // Move + { 0.0f, 0.5f, 0.0f }, // Extrude + { 0.5f, 0.0f, 0.0f } // Retract +}}; + const std::vector> GCodeViewer::Range_Colors {{ { 0.043f, 0.173f, 0.478f }, // bluish { 0.075f, 0.349f, 0.522f }, @@ -186,14 +192,17 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: switch (curr.type) { case GCodeProcessor::EMoveType::Extrude: + { + m_extrusions.ranges.height.update_from(curr.height); + m_extrusions.ranges.width.update_from(curr.width); + m_extrusions.ranges.fan_speed.update_from(curr.fan_speed); + m_extrusions.ranges.volumetric_rate.update_from(curr.volumetric_rate()); + [[fallthrough]]; + } case GCodeProcessor::EMoveType::Travel: { if (m_buffers[buffer_id(curr.type)].visible) { - m_extrusions.ranges.height.update_from(curr.height); - m_extrusions.ranges.width.update_from(curr.width); m_extrusions.ranges.feedrate.update_from(curr.feedrate); - m_extrusions.ranges.fan_speed.update_from(curr.fan_speed); - m_extrusions.ranges.volumetric_rate.update_from(curr.volumetric_rate()); } break; } @@ -465,6 +474,12 @@ void GCodeViewer::render_toolpaths() const return color; }; + auto travel_color = [this](const Path& path) { + return (path.delta_extruder < 0.0f) ? Travel_Colors[2] /* Retract */ : + ((path.delta_extruder > 0.0f) ? Travel_Colors[1] /* Extrude */ : + Travel_Colors[0] /* Move */); + }; + auto set_color = [](GLint current_program_id, const std::array& color) { if (current_program_id > 0) { GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1; @@ -577,9 +592,8 @@ void GCodeViewer::render_toolpaths() const } case GCodeProcessor::EMoveType::Travel: { - std::array color = { 1.0f, 1.0f, 0.0f }; - set_color(current_program_id, color); for (const Path& path : buffer.paths) { + set_color(current_program_id, (m_view_type == EViewType::Feedrate || m_view_type == EViewType::Tool || m_view_type == EViewType::ColorPrint) ? extrusion_color(path) : travel_color(path)); glsafe(::glDrawElements(GL_LINE_STRIP, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); } break; diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 53d1fbd75e..70e20ce0b1 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -16,6 +16,7 @@ namespace GUI { class GCodeViewer { static const std::vector> Extrusion_Role_Colors; + static const std::vector> Travel_Colors; static const std::vector> Range_Colors; // buffer containing vertices data @@ -39,6 +40,7 @@ class GCodeViewer ExtrusionRole role{ erNone }; unsigned int first{ 0 }; unsigned int last{ 0 }; + float delta_extruder{ 0.0f }; float height{ 0.0f }; float width{ 0.0f }; float feedrate{ 0.0f }; From d8091b7ad76fb580b8f22b151bc4c76134d6f3e2 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 24 Apr 2020 16:12:38 +0200 Subject: [PATCH 038/503] ENABLE_GCODE_VIEWER -> Preview toolbar checkboxes moved into a new combo --- src/slic3r/GUI/GCodeViewer.cpp | 17 +++ src/slic3r/GUI/GCodeViewer.hpp | 4 +- src/slic3r/GUI/GLCanvas3D.cpp | 9 +- src/slic3r/GUI/GLCanvas3D.hpp | 3 +- src/slic3r/GUI/GUI.cpp | 25 ++--- src/slic3r/GUI/GUI.hpp | 6 +- src/slic3r/GUI/GUI_Preview.cpp | 190 +++++++++++++-------------------- src/slic3r/GUI/GUI_Preview.hpp | 22 ++-- 8 files changed, 123 insertions(+), 153 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 3d8d288d17..e6ddba8d11 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -250,6 +250,23 @@ void GCodeViewer::set_toolpath_move_type_visible(GCodeProcessor::EMoveType type, m_buffers[id].visible = visible; } +void GCodeViewer::set_options_visibility_from_flags(unsigned int flags) +{ + auto is_flag_set = [flags](unsigned int flag) { + return (flags& (1 << flag)) != 0; + }; + + set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Travel, is_flag_set(0)); + set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Retract, is_flag_set(1)); + set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Unretract, is_flag_set(2)); + set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Tool_change, is_flag_set(3)); + set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Color_change, is_flag_set(4)); + set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Pause_Print, is_flag_set(5)); + set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Custom_GCode, is_flag_set(6)); + m_shells.visible = is_flag_set(7); + enable_legend(is_flag_set(8)); +} + bool GCodeViewer::init_shaders() { unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 6e7cc2f172..bfd61e8d4c 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -194,9 +194,7 @@ public: bool is_toolpath_move_type_visible(GCodeProcessor::EMoveType type) const; void set_toolpath_move_type_visible(GCodeProcessor::EMoveType type, bool visible); void set_toolpath_role_visibility_flags(unsigned int flags) { m_extrusions.role_visibility_flags = flags; } - - bool are_shells_visible() const { return m_shells.visible; } - void set_shells_visible(bool visible) { m_shells.visible = visible; } + void set_options_visibility_from_flags(unsigned int flags); bool is_legend_enabled() const { return m_legend_enabled; } void enable_legend(bool enable) { m_legend_enabled = enable; } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 225fb427cf..ab2dee6938 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2332,9 +2332,9 @@ const std::vector& GLCanvas3D::get_layers_zs() const return m_gcode_viewer.get_layers_zs(); } -void GLCanvas3D::set_toolpath_move_type_visible(GCodeProcessor::EMoveType type, bool visible) +void GLCanvas3D::set_gcode_options_visibility_from_flags(unsigned int flags) { - m_gcode_viewer.set_toolpath_move_type_visible(type, visible); + m_gcode_viewer.set_options_visibility_from_flags(flags); } void GLCanvas3D::set_toolpath_role_visibility_flags(unsigned int flags) @@ -2346,11 +2346,6 @@ void GLCanvas3D::set_toolpath_view_type(GCodeViewer::EViewType type) { m_gcode_viewer.set_view_type(type); } - -void GLCanvas3D::set_shells_visible(bool visible) -{ - m_gcode_viewer.set_shells_visible(visible); -} #else std::vector GLCanvas3D::get_current_print_zs(bool active_only) const { diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 25647b5e55..7a295b900d 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -647,10 +647,9 @@ public: #if ENABLE_GCODE_VIEWER const std::vector& get_layers_zs() const; - void set_toolpath_move_type_visible(GCodeProcessor::EMoveType type, bool visible); + void set_gcode_options_visibility_from_flags(unsigned int flags); void set_toolpath_role_visibility_flags(unsigned int flags); void set_toolpath_view_type(GCodeViewer::EViewType type); - void set_shells_visible(bool visible); #else std::vector get_current_print_zs(bool active_only) const; #endif // ENABLE_GCODE_VIEWER diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index caeb8da034..dd9ddcab8f 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -259,7 +259,7 @@ void warning_catcher(wxWindow* parent, const wxString& message) msg.ShowModal(); } -void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string items, bool initial_value) +void create_combochecklist(wxComboCtrl* comboCtrl, const std::string& text, const std::string& items) { if (comboCtrl == nullptr) return; @@ -273,8 +273,9 @@ void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string comboCtrl->EnablePopupAnimation(false); comboCtrl->SetPopupControl(popup); - popup->SetStringValue(from_u8(text)); - popup->Bind(wxEVT_CHECKLISTBOX, [popup](wxCommandEvent& evt) { popup->OnCheckListBox(evt); }); + wxString title = from_u8(text); + popup->SetStringValue(title); + popup->Bind(wxEVT_CHECKLISTBOX, [popup](wxCommandEvent& evt) { popup->OnCheckListBox(evt); }); popup->Bind(wxEVT_LISTBOX, [popup](wxCommandEvent& evt) { popup->OnListBoxSelection(evt); }); popup->Bind(wxEVT_KEY_DOWN, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); }); popup->Bind(wxEVT_KEY_UP, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); }); @@ -282,16 +283,16 @@ void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string std::vector items_str; boost::split(items_str, items, boost::is_any_of("|"), boost::token_compress_off); - for (const std::string& item : items_str) - { - popup->Append(from_u8(item)); - } + // each item must be composed by 2 parts + assert(items_str.size() %2 == 0); - for (unsigned int i = 0; i < popup->GetCount(); ++i) - { - popup->Check(i, initial_value); - } - } + for (size_t i = 0; i < items_str.size(); i += 2) + { + wxString label = from_u8(items_str[i]); + popup->Append(label); + popup->Check(i / 2, items_str[i + 1] == "1"); + } + } } int combochecklist_get_flags(wxComboCtrl* comboCtrl) diff --git a/src/slic3r/GUI/GUI.hpp b/src/slic3r/GUI/GUI.hpp index a54288df45..6690b21207 100644 --- a/src/slic3r/GUI/GUI.hpp +++ b/src/slic3r/GUI/GUI.hpp @@ -49,9 +49,9 @@ inline void show_info(wxWindow* parent, const std::string& message,const std::st void warning_catcher(wxWindow* parent, const wxString& message); // Creates a wxCheckListBoxComboPopup inside the given wxComboCtrl, filled with the given text and items. -// Items are all initialized to the given value. -// Items must be separated by '|', for example "Item1|Item2|Item3", and so on. -void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string items, bool initial_value); +// Items data must be separated by '|', and contain the item name to be shown followed by its initial value (0 for false, 1 for true). +// For example "Item1|0|Item2|1|Item3|0", and so on. +void create_combochecklist(wxComboCtrl* comboCtrl, const std::string& text, const std::string& items); // Returns the current state of the items listed in the wxCheckListBoxComboPopup contained in the given wxComboCtrl, // encoded inside an int. diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 6c961a4902..876d212fb4 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -224,19 +224,17 @@ Preview::Preview( , m_double_slider_sizer(nullptr) , m_label_view_type(nullptr) , m_choice_view_type(nullptr) - , m_label_show_features(nullptr) + , m_label_show(nullptr) , m_combochecklist_features(nullptr) +#if ENABLE_GCODE_VIEWER + , m_combochecklist_options(nullptr) +#else , m_checkbox_travel(nullptr) , m_checkbox_retractions(nullptr) , m_checkbox_unretractions(nullptr) -#if ENABLE_GCODE_VIEWER - , m_checkbox_tool_changes(nullptr) - , m_checkbox_color_changes(nullptr) - , m_checkbox_pause_prints(nullptr) - , m_checkbox_custom_gcodes(nullptr) -#endif // ENABLE_GCODE_VIEWER , m_checkbox_shells(nullptr) , m_checkbox_legend(nullptr) +#endif // ENABLE_GCODE_VIEWER , m_config(config) , m_process(process) , m_gcode_preview_data(gcode_preview_data) @@ -308,43 +306,53 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view m_choice_view_type->Append(_(L("Color Print"))); m_choice_view_type->SetSelection(0); - m_label_show_features = new wxStaticText(this, wxID_ANY, _(L("Show"))); + m_label_show = new wxStaticText(this, wxID_ANY, _(L("Show"))); m_combochecklist_features = new wxComboCtrl(); m_combochecklist_features->Create(this, wxID_ANY, _(L("Feature types")), wxDefaultPosition, wxSize(15 * wxGetApp().em_unit(), -1), wxCB_READONLY); - std::string feature_text = GUI::into_u8(_(L("Feature types"))); std::string feature_items = GUI::into_u8( #if ENABLE_GCODE_VIEWER - _L("Unknown") + "|" + + _L("Unknown") + "|1|" + #endif // ENABLE_GCODE_VIEWER - _(L("Perimeter")) + "|" + - _(L("External perimeter")) + "|" + - _(L("Overhang perimeter")) + "|" + - _(L("Internal infill")) + "|" + - _(L("Solid infill")) + "|" + - _(L("Top solid infill")) + "|" + - _(L("Bridge infill")) + "|" + - _(L("Gap fill")) + "|" + - _(L("Skirt")) + "|" + - _(L("Support material")) + "|" + - _(L("Support material interface")) + "|" + - _(L("Wipe tower")) + "|" + - _(L("Custom")) + _(L("Perimeter")) + "|1|" + + _(L("External perimeter")) + "|1|" + + _(L("Overhang perimeter")) + "|1|" + + _(L("Internal infill")) + "|1|" + + _(L("Solid infill")) + "|1|" + + _(L("Top solid infill")) + "|1|" + + _(L("Bridge infill")) + "|1|" + + _(L("Gap fill")) + "|1|" + + _(L("Skirt")) + "|1|" + + _(L("Support material")) + "|1|" + + _(L("Support material interface")) + "|1|" + + _(L("Wipe tower")) + "|1|" + + _(L("Custom")) + "|1" ); - Slic3r::GUI::create_combochecklist(m_combochecklist_features, feature_text, feature_items, true); + Slic3r::GUI::create_combochecklist(m_combochecklist_features, GUI::into_u8(_(L("Feature types"))), feature_items); +#if ENABLE_GCODE_VIEWER + m_combochecklist_options = new wxComboCtrl(); + m_combochecklist_options->Create(this, wxID_ANY, _(L("Options")), wxDefaultPosition, wxSize(15 * wxGetApp().em_unit(), -1), wxCB_READONLY); + std::string options_items = GUI::into_u8( + _(L("Travel")) + "|0|" + + _(L("Retractions")) + "|0|" + + _(L("Unretractions")) + "|0|" + + _(L("Tool changes")) + "|0|" + + _(L("Color changes")) + "|0|" + + _(L("Pause prints")) + "|0|" + + _(L("Custom GCodes")) + "|0|" + + _(L("Shells")) + "|0|" + + _(L("Legend")) + "|1" + ); + Slic3r::GUI::create_combochecklist(m_combochecklist_options, GUI::into_u8(_(L("Options"))), options_items); +#else m_checkbox_travel = new wxCheckBox(this, wxID_ANY, _(L("Travel"))); m_checkbox_retractions = new wxCheckBox(this, wxID_ANY, _(L("Retractions"))); m_checkbox_unretractions = new wxCheckBox(this, wxID_ANY, _(L("Unretractions"))); -#if ENABLE_GCODE_VIEWER - m_checkbox_tool_changes = new wxCheckBox(this, wxID_ANY, _(L("Tool changes"))); - m_checkbox_color_changes = new wxCheckBox(this, wxID_ANY, _(L("Color changes"))); - m_checkbox_pause_prints = new wxCheckBox(this, wxID_ANY, _(L("Pause prints"))); - m_checkbox_custom_gcodes = new wxCheckBox(this, wxID_ANY, _(L("Custom GCodes"))); -#endif // ENABLE_GCODE_VIEWER m_checkbox_shells = new wxCheckBox(this, wxID_ANY, _(L("Shells"))); m_checkbox_legend = new wxCheckBox(this, wxID_ANY, _(L("Legend"))); m_checkbox_legend->SetValue(true); +#endif // ENABLE_GCODE_VIEWER wxBoxSizer* top_sizer = new wxBoxSizer(wxHORIZONTAL); top_sizer->Add(m_canvas_widget, 1, wxALL | wxEXPAND, 0); @@ -354,8 +362,11 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view bottom_sizer->Add(m_label_view_type, 0, wxALIGN_CENTER_VERTICAL, 5); bottom_sizer->Add(m_choice_view_type, 0, wxEXPAND | wxALL, 5); bottom_sizer->AddSpacer(10); - bottom_sizer->Add(m_label_show_features, 0, wxALIGN_CENTER_VERTICAL, 5); + bottom_sizer->Add(m_label_show, 0, wxALIGN_CENTER_VERTICAL, 5); bottom_sizer->Add(m_combochecklist_features, 0, wxEXPAND | wxALL, 5); +#if ENABLE_GCODE_VIEWER + bottom_sizer->Add(m_combochecklist_options, 0, wxEXPAND | wxALL, 5); +#else bottom_sizer->AddSpacer(20); bottom_sizer->Add(m_checkbox_travel, 0, wxEXPAND | wxALL, 5); bottom_sizer->AddSpacer(10); @@ -363,19 +374,10 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view bottom_sizer->AddSpacer(10); bottom_sizer->Add(m_checkbox_unretractions, 0, wxEXPAND | wxALL, 5); bottom_sizer->AddSpacer(10); -#if ENABLE_GCODE_VIEWER - bottom_sizer->Add(m_checkbox_tool_changes, 0, wxEXPAND | wxALL, 5); - bottom_sizer->AddSpacer(10); - bottom_sizer->Add(m_checkbox_color_changes, 0, wxEXPAND | wxALL, 5); - bottom_sizer->AddSpacer(10); - bottom_sizer->Add(m_checkbox_pause_prints, 0, wxEXPAND | wxALL, 5); - bottom_sizer->AddSpacer(10); - bottom_sizer->Add(m_checkbox_custom_gcodes, 0, wxEXPAND | wxALL, 5); - bottom_sizer->AddSpacer(10); -#endif // ENABLE_GCODE_VIEWER bottom_sizer->Add(m_checkbox_shells, 0, wxEXPAND | wxALL, 5); bottom_sizer->AddSpacer(20); bottom_sizer->Add(m_checkbox_legend, 0, wxEXPAND | wxALL, 5); +#endif // ENABLE_GCODE_VIEWER wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); main_sizer->Add(top_sizer, 1, wxALL | wxEXPAND, 0); @@ -552,17 +554,15 @@ void Preview::bind_event_handlers() this->Bind(wxEVT_SIZE, &Preview::on_size, this); m_choice_view_type->Bind(wxEVT_CHOICE, &Preview::on_choice_view_type, this); m_combochecklist_features->Bind(wxEVT_CHECKLISTBOX, &Preview::on_combochecklist_features, this); +#if ENABLE_GCODE_VIEWER + m_combochecklist_options->Bind(wxEVT_CHECKLISTBOX, &Preview::on_combochecklist_options, this); +#else m_checkbox_travel->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_travel, this); m_checkbox_retractions->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_retractions, this); m_checkbox_unretractions->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_unretractions, this); -#if ENABLE_GCODE_VIEWER - m_checkbox_tool_changes->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_tool_changes, this); - m_checkbox_color_changes->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_color_changes, this); - m_checkbox_pause_prints->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_pause_prints, this); - m_checkbox_custom_gcodes->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_custom_gcodes, this); -#endif // ENABLE_GCODE_VIEWER m_checkbox_shells->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_shells, this); m_checkbox_legend->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_legend, this); +#endif // ENABLE_GCODE_VIEWER } void Preview::unbind_event_handlers() @@ -570,54 +570,48 @@ void Preview::unbind_event_handlers() this->Unbind(wxEVT_SIZE, &Preview::on_size, this); m_choice_view_type->Unbind(wxEVT_CHOICE, &Preview::on_choice_view_type, this); m_combochecklist_features->Unbind(wxEVT_CHECKLISTBOX, &Preview::on_combochecklist_features, this); +#if ENABLE_GCODE_VIEWER + m_combochecklist_options->Unbind(wxEVT_CHECKLISTBOX, &Preview::on_combochecklist_options, this); +#else m_checkbox_travel->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_travel, this); m_checkbox_retractions->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_retractions, this); m_checkbox_unretractions->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_unretractions, this); -#if ENABLE_GCODE_VIEWER - m_checkbox_tool_changes->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_tool_changes, this); - m_checkbox_color_changes->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_color_changes, this); - m_checkbox_pause_prints->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_pause_prints, this); - m_checkbox_custom_gcodes->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_custom_gcodes, this); -#endif // ENABLE_GCODE_VIEWER m_checkbox_shells->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_shells, this); m_checkbox_legend->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_legend, this); +#endif // ENABLE_GCODE_VIEWER } void Preview::show_hide_ui_elements(const std::string& what) { bool enable = (what == "full"); - m_label_show_features->Enable(enable); + m_label_show->Enable(enable); m_combochecklist_features->Enable(enable); - m_checkbox_travel->Enable(enable); +#if ENABLE_GCODE_VIEWER + m_combochecklist_options->Enable(enable); +#else + m_checkbox_travel->Enable(enable); m_checkbox_retractions->Enable(enable); m_checkbox_unretractions->Enable(enable); -#if ENABLE_GCODE_VIEWER - m_checkbox_tool_changes->Enable(enable); - m_checkbox_color_changes->Enable(enable); - m_checkbox_pause_prints->Enable(enable); - m_checkbox_custom_gcodes->Enable(enable); -#endif // ENABLE_GCODE_VIEWER m_checkbox_shells->Enable(enable); m_checkbox_legend->Enable(enable); +#endif // ENABLE_GCODE_VIEWER enable = (what != "none"); m_label_view_type->Enable(enable); m_choice_view_type->Enable(enable); bool visible = (what != "none"); - m_label_show_features->Show(visible); + m_label_show->Show(visible); m_combochecklist_features->Show(visible); +#if ENABLE_GCODE_VIEWER + m_combochecklist_options->Show(visible); +#else m_checkbox_travel->Show(visible); m_checkbox_retractions->Show(visible); m_checkbox_unretractions->Show(visible); -#if ENABLE_GCODE_VIEWER - m_checkbox_tool_changes->Show(visible); - m_checkbox_color_changes->Show(visible); - m_checkbox_pause_prints->Show(visible); - m_checkbox_custom_gcodes->Show(visible); -#endif // ENABLE_GCODE_VIEWER m_checkbox_shells->Show(visible); m_checkbox_legend->Show(visible); +#endif // ENABLE_GCODE_VIEWER m_label_view_type->Show(visible); m_choice_view_type->Show(visible); } @@ -676,72 +670,36 @@ void Preview::on_combochecklist_features(wxCommandEvent& evt) refresh_print(); } +#if ENABLE_GCODE_VIEWER +void Preview::on_combochecklist_options(wxCommandEvent& evt) +{ + m_canvas->set_gcode_options_visibility_from_flags(static_cast(Slic3r::GUI::combochecklist_get_flags(m_combochecklist_options))); + refresh_print(); +} +#else void Preview::on_checkbox_travel(wxCommandEvent& evt) { -#if ENABLE_GCODE_VIEWER - m_canvas->set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Travel, m_checkbox_travel->IsChecked()); - refresh_print(); -#else m_gcode_preview_data->travel.is_visible = m_checkbox_travel->IsChecked(); m_gcode_preview_data->ranges.feedrate.set_mode(GCodePreviewData::FeedrateKind::TRAVEL, m_gcode_preview_data->travel.is_visible); // Rather than refresh, reload print so that speed color ranges get recomputed (affected by travel visibility) reload_print(); -#endif // ENABLE_GCODE_VIEWER } void Preview::on_checkbox_retractions(wxCommandEvent& evt) { -#if ENABLE_GCODE_VIEWER - m_canvas->set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Retract, m_checkbox_retractions->IsChecked()); -#else m_gcode_preview_data->retraction.is_visible = m_checkbox_retractions->IsChecked(); -#endif // ENABLE_GCODE_VIEWER refresh_print(); } void Preview::on_checkbox_unretractions(wxCommandEvent& evt) { -#if ENABLE_GCODE_VIEWER - m_canvas->set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Unretract, m_checkbox_unretractions->IsChecked()); -#else m_gcode_preview_data->unretraction.is_visible = m_checkbox_unretractions->IsChecked(); -#endif // ENABLE_GCODE_VIEWER refresh_print(); } -#if ENABLE_GCODE_VIEWER -void Preview::on_checkbox_tool_changes(wxCommandEvent& evt) -{ - m_canvas->set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Tool_change, m_checkbox_tool_changes->IsChecked()); - refresh_print(); -} - -void Preview::on_checkbox_color_changes(wxCommandEvent& evt) -{ - m_canvas->set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Color_change, m_checkbox_color_changes->IsChecked()); - refresh_print(); -} - -void Preview::on_checkbox_pause_prints(wxCommandEvent& evt) -{ - m_canvas->set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Pause_Print, m_checkbox_pause_prints->IsChecked()); - refresh_print(); -} - -void Preview::on_checkbox_custom_gcodes(wxCommandEvent& evt) -{ - m_canvas->set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Custom_GCode, m_checkbox_custom_gcodes->IsChecked()); - refresh_print(); -} -#endif // ENABLE_GCODE_VIEWER - void Preview::on_checkbox_shells(wxCommandEvent& evt) { -#if ENABLE_GCODE_VIEWER - m_canvas->set_shells_visible(m_checkbox_shells->IsChecked()); -#else m_gcode_preview_data->shell.is_visible = m_checkbox_shells->IsChecked(); -#endif // ENABLE_GCODE_VIEWER refresh_print(); } @@ -750,6 +708,7 @@ void Preview::on_checkbox_legend(wxCommandEvent& evt) m_canvas->enable_legend_texture(m_checkbox_legend->IsChecked()); m_canvas_widget->Refresh(); } +#endif // ENABLE_GCODE_VIEWER void Preview::update_view_type(bool slice_completed) { @@ -969,11 +928,13 @@ void Preview::update_double_slider_from_canvas(wxKeyEvent& event) m_slider->SetHigherValue(new_pos); if (event.ShiftDown() || m_slider->is_one_layer()) m_slider->SetLowerValue(m_slider->GetHigherValue()); } +#if !ENABLE_GCODE_VIEWER else if (key == 'L') { m_checkbox_legend->SetValue(!m_checkbox_legend->GetValue()); auto evt = wxCommandEvent(); on_checkbox_legend(evt); } +#endif // !ENABLE_GCODE_VIEWER else if (key == 'S') m_slider->ChangeOneLayerLock(); else if (key == WXK_SHIFT) @@ -1078,6 +1039,7 @@ void Preview::load_print_as_fff(bool keep_z_range) #if ENABLE_GCODE_VIEWER m_canvas->load_gcode_preview(*m_gcode_result); m_canvas->refresh_gcode_preview(*m_gcode_result, colors); + show_hide_ui_elements(gcode_preview_data_valid ? "full" : "simple"); #else m_canvas->load_gcode_preview(*m_gcode_preview_data, colors); #endif // ENABLE_GCODE_VIEWER @@ -1085,12 +1047,14 @@ void Preview::load_print_as_fff(bool keep_z_range) } else { // Load the initial preview based on slices, not the final G-code. m_canvas->load_preview(colors, color_print_values); +#if ENABLE_GCODE_VIEWER + show_hide_ui_elements("none"); +#endif // ENABLE_GCODE_VIEWER } - show_hide_ui_elements(gcode_preview_data_valid ? "full" : "simple"); - // recalculates zs and update sliders accordingly #if ENABLE_GCODE_VIEWER const std::vector& zs = m_canvas->get_layers_zs(); #else + show_hide_ui_elements(gcode_preview_data_valid ? "full" : "simple"); std::vector zs = m_canvas->get_current_print_zs(true); #endif // ENABLE_GCODE_VIEWER if (zs.empty()) { diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index bdbbe79daa..c4ad4eb79c 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -93,19 +93,17 @@ class Preview : public wxPanel wxBoxSizer* m_double_slider_sizer; wxStaticText* m_label_view_type; wxChoice* m_choice_view_type; - wxStaticText* m_label_show_features; + wxStaticText* m_label_show; wxComboCtrl* m_combochecklist_features; +#if ENABLE_GCODE_VIEWER + wxComboCtrl* m_combochecklist_options; +#else wxCheckBox* m_checkbox_travel; wxCheckBox* m_checkbox_retractions; wxCheckBox* m_checkbox_unretractions; -#if ENABLE_GCODE_VIEWER - wxCheckBox* m_checkbox_tool_changes; - wxCheckBox* m_checkbox_color_changes; - wxCheckBox* m_checkbox_pause_prints; - wxCheckBox* m_checkbox_custom_gcodes; -#endif // ENABLE_GCODE_VIEWER wxCheckBox* m_checkbox_shells; wxCheckBox* m_checkbox_legend; +#endif // ENABLE_GCODE_VIEWER DynamicPrintConfig* m_config; BackgroundSlicingProcess* m_process; @@ -192,17 +190,15 @@ private: void on_size(wxSizeEvent& evt); void on_choice_view_type(wxCommandEvent& evt); void on_combochecklist_features(wxCommandEvent& evt); +#if ENABLE_GCODE_VIEWER + void on_combochecklist_options(wxCommandEvent& evt); +#else void on_checkbox_travel(wxCommandEvent& evt); void on_checkbox_retractions(wxCommandEvent& evt); void on_checkbox_unretractions(wxCommandEvent& evt); -#if ENABLE_GCODE_VIEWER - void on_checkbox_tool_changes(wxCommandEvent& evt); - void on_checkbox_color_changes(wxCommandEvent& evt); - void on_checkbox_pause_prints(wxCommandEvent& evt); - void on_checkbox_custom_gcodes(wxCommandEvent& evt); -#endif // ENABLE_GCODE_VIEWER void on_checkbox_shells(wxCommandEvent& evt); void on_checkbox_legend(wxCommandEvent& evt); +#endif // ENABLE_GCODE_VIEWER // Create/Update/Reset double slider on 3dPreview void create_double_slider(); From 85676af17131eebebd61ed747d7827d2411bb9e0 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Sat, 25 Apr 2020 10:36:51 +0200 Subject: [PATCH 039/503] Modified wxCheckListBoxComboPopup::GetAdjustedSize() and create_combochecklist() to size the combo control taking in account the items width --- src/slic3r/GUI/GUI.cpp | 6 ++++++ src/slic3r/GUI/wxExtensions.cpp | 14 +++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index dd9ddcab8f..a66396b277 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -271,9 +271,12 @@ void create_combochecklist(wxComboCtrl* comboCtrl, const std::string& text, cons // On the other side, with this line the combo box popup cannot be closed by clicking on the combo button on Windows 10. comboCtrl->UseAltPopupWindow(); + int max_width = 0; + comboCtrl->EnablePopupAnimation(false); comboCtrl->SetPopupControl(popup); wxString title = from_u8(text); + max_width = std::max(max_width, 60 + comboCtrl->GetTextExtent(title).x); popup->SetStringValue(title); popup->Bind(wxEVT_CHECKLISTBOX, [popup](wxCommandEvent& evt) { popup->OnCheckListBox(evt); }); popup->Bind(wxEVT_LISTBOX, [popup](wxCommandEvent& evt) { popup->OnListBoxSelection(evt); }); @@ -289,9 +292,12 @@ void create_combochecklist(wxComboCtrl* comboCtrl, const std::string& text, cons for (size_t i = 0; i < items_str.size(); i += 2) { wxString label = from_u8(items_str[i]); + max_width = std::max(max_width, 60 + popup->GetTextExtent(label).x); popup->Append(label); popup->Check(i / 2, items_str[i + 1] == "1"); } + + comboCtrl->SetMinClientSize(wxSize(max_width, -1)); } } diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index b3f2791961..3e55f6ae30 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -197,8 +197,8 @@ wxString wxCheckListBoxComboPopup::GetStringValue() const wxSize wxCheckListBoxComboPopup::GetAdjustedSize(int minWidth, int prefHeight, int maxHeight) { - // matches owner wxComboCtrl's width - // and sets height dinamically in dependence of contained items count + // set width dinamically in dependence of items text + // and set height dinamically in dependence of items count wxComboCtrl* cmb = GetComboCtrl(); if (cmb != nullptr) @@ -207,7 +207,15 @@ wxSize wxCheckListBoxComboPopup::GetAdjustedSize(int minWidth, int prefHeight, i unsigned int count = GetCount(); if (count > 0) - size.SetHeight(count * DefaultItemHeight); + { + int max_width = size.x; + for (unsigned int i = 0; i < count; ++i) + { + max_width = std::max(max_width, 60 + GetTextExtent(GetString(i)).x); + } + size.SetWidth(max_width); + size.SetHeight(count * GetTextExtent(GetString(0)).y); + } else size.SetHeight(DefaultHeight); From eadad6c1d1c37a5afd8a7beb059fb2005621d3c1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Sat, 25 Apr 2020 11:16:28 +0200 Subject: [PATCH 040/503] GCodeViewer -> Add travel paths to legend --- src/slic3r/GUI/GCodeViewer.cpp | 41 ++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index e6ddba8d11..73f41cb85e 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -177,10 +177,11 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: if (m_vertices.vertices_count == 0) return; + // update tool colors m_tool_colors = decode_colors(str_tool_colors); + // update ranges m_extrusions.reset_ranges(); - for (size_t i = 0; i < m_vertices.vertices_count; ++i) { // skip first vertex @@ -201,9 +202,9 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: } case GCodeProcessor::EMoveType::Travel: { - if (m_buffers[buffer_id(curr.type)].visible) { + if (m_buffers[buffer_id(curr.type)].visible) m_extrusions.ranges.feedrate.update_from(curr.feedrate); - } + break; } default: { break; } @@ -689,6 +690,7 @@ void GCodeViewer::render_overlay() const } }; + // extrusion paths -> title ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); switch (m_view_type) { @@ -703,9 +705,9 @@ void GCodeViewer::render_overlay() const default: { break; } } ImGui::PopStyleColor(); - ImGui::Separator(); + // extrusion paths -> items switch (m_view_type) { case EViewType::FeatureType: @@ -810,6 +812,37 @@ void GCodeViewer::render_overlay() const default: { break; } } + // travel paths + if (m_buffers[buffer_id(GCodeProcessor::EMoveType::Travel)].visible) + { + switch (m_view_type) + { + case EViewType::Feedrate: + case EViewType::Tool: + case EViewType::ColorPrint: + { + break; + } + default: + { + // title + ImGui::Spacing(); + ImGui::Spacing(); + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(I18N::translate_utf8(L("Travel"))); + ImGui::PopStyleColor(); + ImGui::Separator(); + + // items + add_item(Travel_Colors[0], I18N::translate_utf8(L("Movement"))); + add_item(Travel_Colors[1], I18N::translate_utf8(L("Extrusion"))); + add_item(Travel_Colors[2], I18N::translate_utf8(L("Retraction"))); + + break; + } + } + } + imgui.end(); ImGui::PopStyleVar(); } From 4ea96340d8817df9df627d9a54adefb65d07739d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Sat, 25 Apr 2020 12:24:56 +0200 Subject: [PATCH 041/503] GCodeViewer -> Draw alphed extrusion paths into legend when set as not visible --- src/slic3r/GUI/GCodeViewer.cpp | 19 ++++++++++++------- src/slic3r/GUI/GCodeViewer.hpp | 6 ++++++ src/slic3r/GUI/GUI_Preview.cpp | 4 ++-- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 73f41cb85e..e6878c6191 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -33,8 +33,7 @@ static GCodeProcessor::EMoveType buffer_type(unsigned char id) { return static_cast(static_cast(GCodeProcessor::EMoveType::Retract) + id); } -std::vector> decode_colors(const std::vector& colors) -{ +std::vector> decode_colors(const std::vector& colors) { static const float INV_255 = 1.0f / 255.0f; std::vector> output(colors.size(), {0.0f, 0.0f, 0.0f} ); @@ -67,6 +66,10 @@ void GCodeViewer::VBuffer::reset() vertices_count = 0; } +bool GCodeViewer::Path::is_path_visible(unsigned int flags, const Path& path) { + return Extrusions::is_role_visible(flags, path.role); +}; + void GCodeViewer::IBuffer::reset() { // release gpu memory @@ -509,10 +512,6 @@ void GCodeViewer::render_toolpaths() const BOOST_LOG_TRIVIAL(error) << "Unable to find uniform_color uniform"; }; - auto is_path_visible = [](unsigned int flags, const Path& path) { - return Extrusions::is_role_visible(flags, path.role); - }; - glsafe(::glCullFace(GL_BACK)); glsafe(::glLineWidth(3.0f)); @@ -600,7 +599,7 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Extrude: { for (const Path& path : buffer.paths) { - if (!is_path_visible(m_extrusions.role_visibility_flags, path)) + if (!Path::is_path_visible(m_extrusions.role_visibility_flags, path)) continue; set_color(current_program_id, extrusion_color(path)); @@ -713,7 +712,13 @@ void GCodeViewer::render_overlay() const case EViewType::FeatureType: { for (ExtrusionRole role : m_roles) { + bool visible = m_extrusions.is_role_visible(role); + if (!visible) + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f); + add_item(Extrusion_Role_Colors[static_cast(role)], I18N::translate_utf8(ExtrusionEntity::role_to_string(role))); + if (!visible) + ImGui::PopStyleVar(); } break; } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index bfd61e8d4c..f644d3481e 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -54,6 +54,8 @@ class GCodeViewer feedrate == move.feedrate && fan_speed == move.fan_speed && volumetric_rate == move.volumetric_rate() && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id; } + + static bool is_path_visible(unsigned int flags, const Path& path); }; // buffer containing indices data and shader for a specific toolpath type @@ -130,6 +132,10 @@ class GCodeViewer void reset_ranges() { ranges.reset(); } + bool is_role_visible(ExtrusionRole role) const { + return role < erCount && (role_visibility_flags & (1 << role)) != 0; + } + static bool is_role_visible(unsigned int flags, ExtrusionRole role) { return role < erCount && (flags & (1 << role)) != 0; } diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 876d212fb4..65adfe8def 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -332,7 +332,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view #if ENABLE_GCODE_VIEWER m_combochecklist_options = new wxComboCtrl(); - m_combochecklist_options->Create(this, wxID_ANY, _(L("Options")), wxDefaultPosition, wxSize(15 * wxGetApp().em_unit(), -1), wxCB_READONLY); + m_combochecklist_options->Create(this, wxID_ANY, _(L("Others")), wxDefaultPosition, wxSize(15 * wxGetApp().em_unit(), -1), wxCB_READONLY); std::string options_items = GUI::into_u8( _(L("Travel")) + "|0|" + _(L("Retractions")) + "|0|" + @@ -344,7 +344,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view _(L("Shells")) + "|0|" + _(L("Legend")) + "|1" ); - Slic3r::GUI::create_combochecklist(m_combochecklist_options, GUI::into_u8(_(L("Options"))), options_items); + Slic3r::GUI::create_combochecklist(m_combochecklist_options, GUI::into_u8(_(L("Others"))), options_items); #else m_checkbox_travel = new wxCheckBox(this, wxID_ANY, _(L("Travel"))); m_checkbox_retractions = new wxCheckBox(this, wxID_ANY, _(L("Retractions"))); From c76bf934f73bf5c1097bf558ff1606a8b0769c44 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 27 Apr 2020 08:19:48 +0200 Subject: [PATCH 042/503] GCodeViewer -> Shortcut to show/hide legend --- src/slic3r/GUI/GCodeViewer.cpp | 1 - src/slic3r/GUI/GLCanvas3D.cpp | 11 +++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index e6878c6191..2baaedbc2f 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -10,7 +10,6 @@ #if ENABLE_GCODE_VIEWER #include "GUI_Utils.hpp" #include "DoubleSlider.hpp" -#include "GLToolbar.hpp" #include "GLCanvas3D.hpp" #include "libslic3r/Model.hpp" #endif // ENABLE_GCODE_VIEWER diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index ab2dee6938..3e4487c70c 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3202,6 +3202,17 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) #else case 'k': { m_camera.select_next_type(); m_dirty = true; break; } #endif // ENABLE_NON_STATIC_CANVAS_MANAGER +#if ENABLE_GCODE_VIEWER + case 'L': + case 'l': { + if (!m_main_toolbar.is_enabled()) + { + m_gcode_viewer.enable_legend(!m_gcode_viewer.is_legend_enabled()); + m_dirty = true; + } + break; + } +#endif // ENABLE_GCODE_VIEWER case 'O': case 'o': { _update_camera_zoom(-1.0); break; } #if ENABLE_RENDER_PICKING_PASS From a6ed1d817a0327745aed6a907af652ee2ad9a441 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 27 Apr 2020 11:44:29 +0200 Subject: [PATCH 043/503] GCodeViewer -> Layers z slider wip --- resources/shaders/customs.vs | 2 +- resources/shaders/pauses.vs | 2 +- resources/shaders/retractions.vs | 2 +- resources/shaders/toolchanges.vs | 2 +- resources/shaders/unretractions.vs | 2 +- src/slic3r/GUI/3DScene.cpp | 2 + src/slic3r/GUI/3DScene.hpp | 4 ++ src/slic3r/GUI/GCodeViewer.cpp | 97 +++++++++++++++++++++++------- src/slic3r/GUI/GCodeViewer.hpp | 7 ++- src/slic3r/GUI/GLCanvas3D.cpp | 7 ++- src/slic3r/GUI/GLCanvas3D.hpp | 2 + src/slic3r/GUI/GUI_Preview.cpp | 5 ++ 12 files changed, 104 insertions(+), 30 deletions(-) diff --git a/resources/shaders/customs.vs b/resources/shaders/customs.vs index 45fa543f45..3b78a59700 100644 --- a/resources/shaders/customs.vs +++ b/resources/shaders/customs.vs @@ -13,5 +13,5 @@ void main() // world_normal_z = gl_Normal.z; gl_Position = ftransform(); - gl_PointSize = 10.0; + gl_PointSize = 15.0; } diff --git a/resources/shaders/pauses.vs b/resources/shaders/pauses.vs index 45fa543f45..3b78a59700 100644 --- a/resources/shaders/pauses.vs +++ b/resources/shaders/pauses.vs @@ -13,5 +13,5 @@ void main() // world_normal_z = gl_Normal.z; gl_Position = ftransform(); - gl_PointSize = 10.0; + gl_PointSize = 15.0; } diff --git a/resources/shaders/retractions.vs b/resources/shaders/retractions.vs index 2cf5ca2dd0..3b78a59700 100644 --- a/resources/shaders/retractions.vs +++ b/resources/shaders/retractions.vs @@ -13,5 +13,5 @@ void main() // world_normal_z = gl_Normal.z; gl_Position = ftransform(); - gl_PointSize = 5.0; + gl_PointSize = 15.0; } diff --git a/resources/shaders/toolchanges.vs b/resources/shaders/toolchanges.vs index 2cf5ca2dd0..3b78a59700 100644 --- a/resources/shaders/toolchanges.vs +++ b/resources/shaders/toolchanges.vs @@ -13,5 +13,5 @@ void main() // world_normal_z = gl_Normal.z; gl_Position = ftransform(); - gl_PointSize = 5.0; + gl_PointSize = 15.0; } diff --git a/resources/shaders/unretractions.vs b/resources/shaders/unretractions.vs index 2cf5ca2dd0..3b78a59700 100644 --- a/resources/shaders/unretractions.vs +++ b/resources/shaders/unretractions.vs @@ -13,5 +13,5 @@ void main() // world_normal_z = gl_Normal.z; gl_Position = ftransform(); - gl_PointSize = 5.0; + gl_PointSize = 15.0; } diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index e60676ca31..63cacdd457 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -341,6 +341,7 @@ BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d & } +#if !ENABLE_GCODE_VIEWER void GLVolume::set_range(double min_z, double max_z) { this->qverts_range.first = 0; @@ -375,6 +376,7 @@ void GLVolume::set_range(double min_z, double max_z) } } } +#endif // !ENABLE_GCODE_VIEWER void GLVolume::render() const { diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 70d6fb016a..28295a35f7 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -442,7 +442,9 @@ public: bool empty() const { return this->indexed_vertex_array.empty(); } +#if !ENABLE_GCODE_VIEWER void set_range(double low, double high); +#endif // !ENABLE_GCODE_VIEWER void render() const; #if !ENABLE_SLOPE_RENDERING @@ -560,7 +562,9 @@ public: void clear() { for (auto *v : volumes) delete v; volumes.clear(); } bool empty() const { return volumes.empty(); } +#if !ENABLE_GCODE_VIEWER void set_range(double low, double high) { for (GLVolume *vol : this->volumes) vol->set_range(low, high); } +#endif // !ENABLE_GCODE_VIEWER void set_print_box(float min_x, float min_y, float min_z, float max_x, float max_y, float max_z) { m_print_box_min[0] = min_x; m_print_box_min[1] = min_y; m_print_box_min[2] = min_z; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 2baaedbc2f..045f6ed570 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -65,10 +65,19 @@ void GCodeViewer::VBuffer::reset() vertices_count = 0; } -bool GCodeViewer::Path::is_path_visible(unsigned int flags, const Path& path) { +bool GCodeViewer::Path::is_path_visible(const Path& path, unsigned int flags) { return Extrusions::is_role_visible(flags, path.role); }; +bool GCodeViewer::Path::is_path_in_z_range(const Path& path, const std::array& z_range) +{ + auto in_z_range = [z_range](double z) { + return z > z_range[0] - EPSILON && z < z_range[1] + EPSILON; + }; + + return in_z_range(path.first_z) || in_z_range(path.last_z); +} + void GCodeViewer::IBuffer::reset() { // release gpu memory @@ -96,7 +105,8 @@ bool GCodeViewer::IBuffer::init_shader(const std::string& vertex_shader_src, con void GCodeViewer::IBuffer::add_path(const GCodeProcessor::MoveVertex& move) { unsigned int id = static_cast(data.size()); - paths.push_back({ move.type, move.extrusion_role, id, id, move.delta_extruder, move.height, move.width, move.feedrate, move.fan_speed, move.volumetric_rate(), move.extruder_id, move.cp_color_id }); + double z = static_cast(move.position[2]); + paths.push_back({ move.type, move.extrusion_role, id, id, z, z, move.delta_extruder, move.height, move.width, move.feedrate, move.fan_speed, move.volumetric_rate(), move.extruder_id, move.cp_color_id }); } std::array GCodeViewer::Extrusions::Range::get_color_at(float value) const @@ -182,7 +192,7 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: // update tool colors m_tool_colors = decode_colors(str_tool_colors); - // update ranges + // update ranges for coloring / legend m_extrusions.reset_ranges(); for (size_t i = 0; i < m_vertices.vertices_count; ++i) { @@ -229,6 +239,7 @@ void GCodeViewer::reset() m_extrusions.reset_ranges(); m_shells.volumes.clear(); m_layers_zs = std::vector(); + m_layers_z_range = { 0.0, 0.0 }; m_roles = std::vector(); } @@ -394,7 +405,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) // layers zs / roles / extruder ids / cp color ids -> extract from result for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) { if (move.type == GCodeProcessor::EMoveType::Extrude) - m_layers_zs.emplace_back(move.position[2]); + m_layers_zs.emplace_back(static_cast(move.position[2])); m_roles.emplace_back(move.extrusion_role); m_extruder_ids.emplace_back(move.extruder_id); @@ -414,6 +425,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) if (k < n) m_layers_zs.erase(m_layers_zs.begin() + k, m_layers_zs.end()); + // set layers z range + m_layers_z_range = { m_layers_zs.front(), m_layers_zs.back() }; + // roles -> remove duplicates std::sort(m_roles.begin(), m_roles.end()); m_roles.erase(std::unique(m_roles.begin(), m_roles.end()), m_roles.end()); @@ -545,60 +559,90 @@ void GCodeViewer::render_toolpaths() const { std::array color = { 1.0f, 1.0f, 1.0f }; set_color(current_program_id, color); - glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawElements(GL_POINTS, (GLsizei)buffer.data_size, GL_UNSIGNED_INT, nullptr)); - glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + for (const Path& path : buffer.paths) { + if (!Path::is_path_in_z_range(path, m_layers_z_range)) + continue; + + glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); + glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + } break; } case GCodeProcessor::EMoveType::Color_change: { std::array color = { 1.0f, 0.0f, 0.0f }; set_color(current_program_id, color); - glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawElements(GL_POINTS, (GLsizei)buffer.data_size, GL_UNSIGNED_INT, nullptr)); - glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + for (const Path& path : buffer.paths) { + if (!Path::is_path_in_z_range(path, m_layers_z_range)) + continue; + + glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); + glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + } break; } case GCodeProcessor::EMoveType::Pause_Print: { std::array color = { 0.0f, 1.0f, 0.0f }; set_color(current_program_id, color); - glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawElements(GL_POINTS, (GLsizei)buffer.data_size, GL_UNSIGNED_INT, nullptr)); - glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + for (const Path& path : buffer.paths) { + if (!Path::is_path_in_z_range(path, m_layers_z_range)) + continue; + + glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); + glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + } break; } case GCodeProcessor::EMoveType::Custom_GCode: { std::array color = { 0.0f, 0.0f, 1.0f }; set_color(current_program_id, color); - glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawElements(GL_POINTS, (GLsizei)buffer.data_size, GL_UNSIGNED_INT, nullptr)); - glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + for (const Path& path : buffer.paths) { + if (!Path::is_path_in_z_range(path, m_layers_z_range)) + continue; + + glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); + glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + } break; } case GCodeProcessor::EMoveType::Retract: { std::array color = { 1.0f, 0.0f, 1.0f }; set_color(current_program_id, color); - glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawElements(GL_POINTS, (GLsizei)buffer.data_size, GL_UNSIGNED_INT, nullptr)); - glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + for (const Path& path : buffer.paths) { + if (!Path::is_path_in_z_range(path, m_layers_z_range)) + continue; + + glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); + glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + } break; } case GCodeProcessor::EMoveType::Unretract: { std::array color = { 0.0f, 1.0f, 1.0f }; set_color(current_program_id, color); - glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawElements(GL_POINTS, (GLsizei)buffer.data_size, GL_UNSIGNED_INT, nullptr)); - glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + for (const Path& path : buffer.paths) { + if (!Path::is_path_in_z_range(path, m_layers_z_range)) + continue; + + glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); + glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + } break; } case GCodeProcessor::EMoveType::Extrude: { for (const Path& path : buffer.paths) { - if (!Path::is_path_visible(m_extrusions.role_visibility_flags, path)) + if (!Path::is_path_visible(path, m_extrusions.role_visibility_flags) || !Path::is_path_in_z_range(path, m_layers_z_range)) continue; set_color(current_program_id, extrusion_color(path)); @@ -609,6 +653,9 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Travel: { for (const Path& path : buffer.paths) { + if (!Path::is_path_in_z_range(path, m_layers_z_range)) + continue; + set_color(current_program_id, (m_view_type == EViewType::Feedrate || m_view_type == EViewType::Tool || m_view_type == EViewType::ColorPrint) ? extrusion_color(path) : travel_color(path)); glsafe(::glDrawElements(GL_LINE_STRIP, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); } @@ -655,6 +702,10 @@ void GCodeViewer::render_overlay() const ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); imgui.begin(std::string("Legend"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); + if (ImGui::IsWindowAppearing()) + // force an extra farme + wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); auto add_item = [draw_list, &imgui](const std::array& color, const std::string& label) { diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index f644d3481e..3c73cee5a2 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -40,6 +40,8 @@ class GCodeViewer ExtrusionRole role{ erNone }; unsigned int first{ 0 }; unsigned int last{ 0 }; + double first_z{ 0.0f }; + double last_z{ 0.0f }; float delta_extruder{ 0.0f }; float height{ 0.0f }; float width{ 0.0f }; @@ -55,7 +57,8 @@ class GCodeViewer extruder_id == move.extruder_id && cp_color_id == move.cp_color_id; } - static bool is_path_visible(unsigned int flags, const Path& path); + static bool is_path_visible(const Path& path, unsigned int flags); + static bool is_path_in_z_range(const Path& path, const std::array& z_range); }; // buffer containing indices data and shader for a specific toolpath type @@ -162,6 +165,7 @@ private: BoundingBoxf3 m_bounding_box; std::vector> m_tool_colors; std::vector m_layers_zs; + std::array m_layers_z_range; std::vector m_roles; std::vector m_extruder_ids; Extrusions m_extrusions; @@ -201,6 +205,7 @@ public: void set_toolpath_move_type_visible(GCodeProcessor::EMoveType type, bool visible); void set_toolpath_role_visibility_flags(unsigned int flags) { m_extrusions.role_visibility_flags = flags; } void set_options_visibility_from_flags(unsigned int flags); + void set_layers_z_range(const std::array& layers_z_range) { m_layers_z_range = layers_z_range; } bool is_legend_enabled() const { return m_legend_enabled; } void enable_legend(bool enable) { m_legend_enabled = enable; } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 3e4487c70c..6e7b53f689 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2346,17 +2346,22 @@ void GLCanvas3D::set_toolpath_view_type(GCodeViewer::EViewType type) { m_gcode_viewer.set_view_type(type); } + +void GLCanvas3D::set_toolpaths_z_range(const std::array& range) +{ + m_gcode_viewer.set_layers_z_range(range); +} #else std::vector GLCanvas3D::get_current_print_zs(bool active_only) const { return m_volumes.get_current_print_zs(active_only); } -#endif // ENABLE_GCODE_VIEWER void GLCanvas3D::set_toolpaths_range(double low, double high) { m_volumes.set_range(low, high); } +#endif // ENABLE_GCODE_VIEWER std::vector GLCanvas3D::load_object(const ModelObject& model_object, int obj_idx, std::vector instance_idxs) { diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 7a295b900d..152658b13a 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -650,8 +650,10 @@ public: void set_gcode_options_visibility_from_flags(unsigned int flags); void set_toolpath_role_visibility_flags(unsigned int flags); void set_toolpath_view_type(GCodeViewer::EViewType type); + void set_toolpaths_z_range(const std::array& range); #else std::vector get_current_print_zs(bool active_only) const; + void set_toolpaths_range(double low, double high); #endif // ENABLE_GCODE_VIEWER void set_toolpaths_range(double low, double high); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 65adfe8def..3e0d5cd5a4 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -1113,9 +1113,14 @@ void Preview::on_sliders_scroll_changed(wxCommandEvent& event) PrinterTechnology tech = m_process->current_printer_technology(); if (tech == ptFFF) { +#if ENABLE_GCODE_VIEWER + m_canvas->set_toolpaths_z_range({ m_slider->GetLowerValueD(), m_slider->GetHigherValueD() }); + m_canvas->set_as_dirty(); +#else m_canvas->set_toolpaths_range(m_slider->GetLowerValueD() - 1e-6, m_slider->GetHigherValueD() + 1e-6); m_canvas->render(); m_canvas->set_use_clipping_planes(false); +#endif // ENABLE_GCODE_VIEWER } else if (tech == ptSLA) { From c1246f86eb7cd1c92f8a3c57ba924bc2f9b1ce9a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 27 Apr 2020 12:43:51 +0200 Subject: [PATCH 044/503] GCodeViewer -> Refactoring --- src/slic3r/GUI/GCodeViewer.cpp | 53 ++++++++++++++-------------------- src/slic3r/GUI/GCodeViewer.hpp | 22 +++++++------- src/slic3r/GUI/GLCanvas3D.cpp | 2 ++ 3 files changed, 35 insertions(+), 42 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 045f6ed570..268b998fa4 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -65,19 +65,6 @@ void GCodeViewer::VBuffer::reset() vertices_count = 0; } -bool GCodeViewer::Path::is_path_visible(const Path& path, unsigned int flags) { - return Extrusions::is_role_visible(flags, path.role); -}; - -bool GCodeViewer::Path::is_path_in_z_range(const Path& path, const std::array& z_range) -{ - auto in_z_range = [z_range](double z) { - return z > z_range[0] - EPSILON && z < z_range[1] + EPSILON; - }; - - return in_z_range(path.first_z) || in_z_range(path.last_z); -} - void GCodeViewer::IBuffer::reset() { // release gpu memory @@ -186,6 +173,8 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std::vector& str_tool_colors) { + auto start_time = std::chrono::high_resolution_clock::now(); + if (m_vertices.vertices_count == 0) return; @@ -222,6 +211,9 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: default: { break; } } } + + auto end_time = std::chrono::high_resolution_clock::now(); + std::cout << "refresh: " << std::chrono::duration_cast(end_time - start_time).count() << "ms \n"; } void GCodeViewer::reset() @@ -560,7 +552,7 @@ void GCodeViewer::render_toolpaths() const std::array color = { 1.0f, 1.0f, 1.0f }; set_color(current_program_id, color); for (const Path& path : buffer.paths) { - if (!Path::is_path_in_z_range(path, m_layers_z_range)) + if (!is_in_z_range(path)) continue; glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -574,7 +566,7 @@ void GCodeViewer::render_toolpaths() const std::array color = { 1.0f, 0.0f, 0.0f }; set_color(current_program_id, color); for (const Path& path : buffer.paths) { - if (!Path::is_path_in_z_range(path, m_layers_z_range)) + if (!is_in_z_range(path)) continue; glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -588,7 +580,7 @@ void GCodeViewer::render_toolpaths() const std::array color = { 0.0f, 1.0f, 0.0f }; set_color(current_program_id, color); for (const Path& path : buffer.paths) { - if (!Path::is_path_in_z_range(path, m_layers_z_range)) + if (!is_in_z_range(path)) continue; glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -602,7 +594,7 @@ void GCodeViewer::render_toolpaths() const std::array color = { 0.0f, 0.0f, 1.0f }; set_color(current_program_id, color); for (const Path& path : buffer.paths) { - if (!Path::is_path_in_z_range(path, m_layers_z_range)) + if (!is_in_z_range(path)) continue; glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -616,7 +608,7 @@ void GCodeViewer::render_toolpaths() const std::array color = { 1.0f, 0.0f, 1.0f }; set_color(current_program_id, color); for (const Path& path : buffer.paths) { - if (!Path::is_path_in_z_range(path, m_layers_z_range)) + if (!is_in_z_range(path)) continue; glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -630,7 +622,7 @@ void GCodeViewer::render_toolpaths() const std::array color = { 0.0f, 1.0f, 1.0f }; set_color(current_program_id, color); for (const Path& path : buffer.paths) { - if (!Path::is_path_in_z_range(path, m_layers_z_range)) + if (!is_in_z_range(path)) continue; glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -642,7 +634,7 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Extrude: { for (const Path& path : buffer.paths) { - if (!Path::is_path_visible(path, m_extrusions.role_visibility_flags) || !Path::is_path_in_z_range(path, m_layers_z_range)) + if (!is_visible(path) || !is_in_z_range(path)) continue; set_color(current_program_id, extrusion_color(path)); @@ -653,7 +645,7 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Travel: { for (const Path& path : buffer.paths) { - if (!Path::is_path_in_z_range(path, m_layers_z_range)) + if (!is_in_z_range(path)) continue; set_color(current_program_id, (m_view_type == EViewType::Feedrate || m_view_type == EViewType::Tool || m_view_type == EViewType::ColorPrint) ? extrusion_color(path) : travel_color(path)); @@ -689,9 +681,7 @@ void GCodeViewer::render_shells() const void GCodeViewer::render_overlay() const { static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); - static const float ICON_BORDER_SIZE = 25.0f; static const ImU32 ICON_BORDER_COLOR = ImGui::GetColorU32(ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); - static const float GAP_ICON_TEXT = 7.5f; if (!m_legend_enabled || m_roles.empty()) return; @@ -709,14 +699,15 @@ void GCodeViewer::render_overlay() const ImDrawList* draw_list = ImGui::GetWindowDrawList(); auto add_item = [draw_list, &imgui](const std::array& color, const std::string& label) { - // draw icon - ImVec2 pos(ImGui::GetCursorPosX() + 2.0f, ImGui::GetCursorPosY() + 2.0f); - draw_list->AddRect({ pos.x, pos.y }, { pos.x + ICON_BORDER_SIZE, pos.y + ICON_BORDER_SIZE }, ICON_BORDER_COLOR, 0.0f, 0); - draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, - { pos.x + ICON_BORDER_SIZE - 1.0f, pos.y + ICON_BORDER_SIZE - 1.0f }, - ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f })); + float icon_size = ImGui::GetTextLineHeight(); + ImVec2 pos = ImGui::GetCursorPos(); + draw_list->AddRect({ pos.x, pos.y }, { pos.x + icon_size, pos.y + icon_size }, ICON_BORDER_COLOR, 0.0f, 0); + draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, + ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f })); + // draw text - ImGui::SetCursorPos({ pos.x + ICON_BORDER_SIZE + GAP_ICON_TEXT, pos.y + 0.5f * (ICON_BORDER_SIZE - ImGui::GetTextLineHeight()) }); + ImGui::Dummy({ icon_size, icon_size }); + ImGui::SameLine(); imgui.text(label); }; @@ -762,7 +753,7 @@ void GCodeViewer::render_overlay() const case EViewType::FeatureType: { for (ExtrusionRole role : m_roles) { - bool visible = m_extrusions.is_role_visible(role); + bool visible = is_visible(role); if (!visible) ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 3c73cee5a2..d2eb103f65 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -56,9 +56,6 @@ class GCodeViewer feedrate == move.feedrate && fan_speed == move.fan_speed && volumetric_rate == move.volumetric_rate() && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id; } - - static bool is_path_visible(const Path& path, unsigned int flags); - static bool is_path_in_z_range(const Path& path, const std::array& z_range); }; // buffer containing indices data and shader for a specific toolpath type @@ -134,14 +131,6 @@ class GCodeViewer } void reset_ranges() { ranges.reset(); } - - bool is_role_visible(ExtrusionRole role) const { - return role < erCount && (role_visibility_flags & (1 << role)) != 0; - } - - static bool is_role_visible(unsigned int flags, ExtrusionRole role) { - return role < erCount && (flags & (1 << role)) != 0; - } }; public: @@ -217,6 +206,17 @@ private: void render_toolpaths() const; void render_shells() const; void render_overlay() const; + bool is_visible(ExtrusionRole role) const { + return role < erCount && (m_extrusions.role_visibility_flags & (1 << role)) != 0; + } + bool is_visible(const Path& path) const { return is_visible(path.role); } + bool is_in_z_range(const Path& path) const { + auto in_z_range = [this](double z) { + return z > m_layers_z_range[0] - EPSILON && z < m_layers_z_range[1] + EPSILON; + }; + + return in_z_range(path.first_z) || in_z_range(path.last_z); + } }; } // namespace GUI diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 6e7b53f689..fa38aca470 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2858,6 +2858,8 @@ void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result) void GLCanvas3D::refresh_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector& str_tool_colors) { m_gcode_viewer.refresh(gcode_result, str_tool_colors); + set_as_dirty(); + request_extra_frame(); } #endif // ENABLE_GCODE_VIEWER From eac4b3c15ac2866b59062f9e45988b3bccf2e849 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 27 Apr 2020 14:10:18 +0200 Subject: [PATCH 045/503] GCodeViewer -> Added debug statistics imgui dialog --- src/libslic3r/Technologies.hpp | 1 + src/slic3r/GUI/GCodeViewer.cpp | 136 ++++++++++++++++++++++++++++++--- src/slic3r/GUI/GCodeViewer.hpp | 42 +++++++++- src/slic3r/GUI/GLCanvas3D.cpp | 4 + 4 files changed, 169 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 1ed939ba3e..d04bc97fa4 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -59,6 +59,7 @@ // Enable G-Code viewer #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) #define ENABLE_GCODE_VIEWER_DEBUG_OUTPUT (0 && ENABLE_GCODE_VIEWER) +#define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_2_3_0_ALPHA1) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 268b998fa4..7426eafc9e 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -7,12 +7,13 @@ #include "PresetBundle.hpp" #include "Camera.hpp" #include "I18N.hpp" -#if ENABLE_GCODE_VIEWER #include "GUI_Utils.hpp" #include "DoubleSlider.hpp" #include "GLCanvas3D.hpp" #include "libslic3r/Model.hpp" -#endif // ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER_STATISTICS +#include +#endif // ENABLE_GCODE_VIEWER_STATISTICS #include #include @@ -173,7 +174,9 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std::vector& str_tool_colors) { +#if ENABLE_GCODE_VIEWER_STATISTICS auto start_time = std::chrono::high_resolution_clock::now(); +#endif // ENABLE_GCODE_VIEWER_STATISTICS if (m_vertices.vertices_count == 0) return; @@ -212,8 +215,9 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: } } - auto end_time = std::chrono::high_resolution_clock::now(); - std::cout << "refresh: " << std::chrono::duration_cast(end_time - start_time).count() << "ms \n"; +#if ENABLE_GCODE_VIEWER_STATISTICS + m_statistics.refresh_time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start_time).count(); +#endif // ENABLE_GCODE_VIEWER_STATISTICS } void GCodeViewer::reset() @@ -233,14 +237,25 @@ void GCodeViewer::reset() m_layers_zs = std::vector(); m_layers_z_range = { 0.0, 0.0 }; m_roles = std::vector(); + +#if ENABLE_GCODE_VIEWER_STATISTICS + m_statistics.reset_all(); +#endif // ENABLE_GCODE_VIEWER_STATISTICS } void GCodeViewer::render() const { +#if ENABLE_GCODE_VIEWER_STATISTICS + m_statistics.reset_opengl(); +#endif // ENABLE_GCODE_VIEWER_STATISTICS + glsafe(::glEnable(GL_DEPTH_TEST)); render_toolpaths(); render_shells(); - render_overlay(); + render_legend(); +#if ENABLE_GCODE_VIEWER_STATISTICS + render_statistics(); +#endif // ENABLE_GCODE_VIEWER_STATISTICS } bool GCodeViewer::is_toolpath_move_type_visible(GCodeProcessor::EMoveType type) const @@ -310,7 +325,9 @@ bool GCodeViewer::init_shaders() void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) { +#if ENABLE_GCODE_VIEWER_STATISTICS auto start_time = std::chrono::high_resolution_clock::now(); +#endif // ENABLE_GCODE_VIEWER_STATISTICS // vertex data m_vertices.vertices_count = gcode_result.moves.size(); @@ -326,6 +343,10 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) } } +#if ENABLE_GCODE_VIEWER_STATISTICS + m_statistics.vertices_size = vertices_data.size() * sizeof(float); +#endif // ENABLE_GCODE_VIEWER_STATISTICS + // vertex data -> send to gpu glsafe(::glGenBuffers(1, &m_vertices.vbo_id)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vertices.vbo_id)); @@ -383,6 +404,10 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) for (IBuffer& buffer : m_buffers) { buffer.data_size = buffer.data.size(); +#if ENABLE_GCODE_VIEWER_STATISTICS + m_statistics.indices_size += buffer.data_size * sizeof(unsigned int); +#endif // ENABLE_GCODE_VIEWER_STATISTICS + if (buffer.data_size > 0) { glsafe(::glGenBuffers(1, &buffer.ibo_id)); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.ibo_id)); @@ -428,8 +453,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) std::sort(m_extruder_ids.begin(), m_extruder_ids.end()); m_extruder_ids.erase(std::unique(m_extruder_ids.begin(), m_extruder_ids.end()), m_extruder_ids.end()); - auto end_time = std::chrono::high_resolution_clock::now(); - std::cout << "toolpaths generation time: " << std::chrono::duration_cast(end_time - start_time).count() << "ms \n"; +#if ENABLE_GCODE_VIEWER_STATISTICS + m_statistics.load_time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start_time).count(); +#endif // ENABLE_GCODE_VIEWER_STATISTICS } void GCodeViewer::load_shells(const Print& print, bool initialized) @@ -558,6 +584,10 @@ void GCodeViewer::render_toolpaths() const glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_points_calls_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS } break; } @@ -572,6 +602,10 @@ void GCodeViewer::render_toolpaths() const glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_points_calls_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS } break; } @@ -586,6 +620,10 @@ void GCodeViewer::render_toolpaths() const glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_points_calls_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS } break; } @@ -600,6 +638,10 @@ void GCodeViewer::render_toolpaths() const glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_points_calls_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS } break; } @@ -614,6 +656,10 @@ void GCodeViewer::render_toolpaths() const glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_points_calls_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS } break; } @@ -628,6 +674,10 @@ void GCodeViewer::render_toolpaths() const glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_points_calls_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS } break; } @@ -639,6 +689,10 @@ void GCodeViewer::render_toolpaths() const set_color(current_program_id, extrusion_color(path)); glsafe(::glDrawElements(GL_LINE_STRIP, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_line_strip_calls_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS } break; } @@ -650,6 +704,10 @@ void GCodeViewer::render_toolpaths() const set_color(current_program_id, (m_view_type == EViewType::Feedrate || m_view_type == EViewType::Tool || m_view_type == EViewType::ColorPrint) ? extrusion_color(path) : travel_color(path)); glsafe(::glDrawElements(GL_LINE_STRIP, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_line_strip_calls_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS } break; } @@ -678,7 +736,7 @@ void GCodeViewer::render_shells() const // glsafe(::glDepthMask(GL_TRUE)); } -void GCodeViewer::render_overlay() const +void GCodeViewer::render_legend() const { static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); static const ImU32 ICON_BORDER_COLOR = ImGui::GetColorU32(ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); @@ -692,10 +750,6 @@ void GCodeViewer::render_overlay() const ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); imgui.begin(std::string("Legend"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); - if (ImGui::IsWindowAppearing()) - // force an extra farme - wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); - ImDrawList* draw_list = ImGui::GetWindowDrawList(); auto add_item = [draw_list, &imgui](const std::array& color, const std::string& label) { @@ -893,6 +947,64 @@ void GCodeViewer::render_overlay() const ImGui::PopStyleVar(); } +#if ENABLE_GCODE_VIEWER_STATISTICS +void GCodeViewer::render_statistics() const +{ + static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); + static const float offset = 250.0f; + + if (!m_legend_enabled || m_roles.empty()) + return; + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + + imgui.begin(std::string("Statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); + + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(std::string("Load time:")); + ImGui::PopStyleColor(); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.load_time) + "ms"); + + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(std::string("Resfresh time:")); + ImGui::PopStyleColor(); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.refresh_time) + "ms"); + + ImGui::Separator(); + + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(std::string("GL_POINTS calls:")); + ImGui::PopStyleColor(); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.gl_points_calls_count)); + + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(std::string("GL_LINE_STRIP calls:")); + ImGui::PopStyleColor(); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.gl_line_strip_calls_count)); + + ImGui::Separator(); + + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(std::string("Vertices:")); + ImGui::PopStyleColor(); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.vertices_size) + " bytes"); + + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(std::string("Indices:")); + ImGui::PopStyleColor(); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.indices_size) + " bytes"); + + imgui.end(); +} +#endif // ENABLE_GCODE_VIEWER_STATISTICS + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index d2eb103f65..be239e20ce 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -2,7 +2,6 @@ #define slic3r_GCodeViewer_hpp_ #if ENABLE_GCODE_VIEWER - #include "GLShader.hpp" #include "3DScene.hpp" #include "libslic3r/GCode/GCodeProcessor.hpp" @@ -133,6 +132,39 @@ class GCodeViewer void reset_ranges() { ranges.reset(); } }; +#if ENABLE_GCODE_VIEWER_STATISTICS + struct Statistics + { + long long load_time{ 0 }; + long long refresh_time{ 0 }; + long long gl_points_calls_count{ 0 }; + long long gl_line_strip_calls_count{ 0 }; + long long vertices_size{ 0 }; + long long indices_size{ 0 }; + + void reset_all() { + reset_times(); + reset_opengl(); + reset_sizes(); + } + + void reset_times() { + load_time = 0; + refresh_time = 0; + } + + void reset_opengl() { + gl_points_calls_count = 0; + gl_line_strip_calls_count = 0; + } + + void reset_sizes() { + vertices_size = 0; + indices_size = 0; + } + }; +#endif // ENABLE_GCODE_VIEWER_STATISTICS + public: enum class EViewType : unsigned char { @@ -161,6 +193,9 @@ private: Shells m_shells; EViewType m_view_type{ EViewType::FeatureType }; bool m_legend_enabled{ true }; +#if ENABLE_GCODE_VIEWER_STATISTICS + mutable Statistics m_statistics; +#endif // ENABLE_GCODE_VIEWER_STATISTICS public: GCodeViewer() = default; @@ -205,7 +240,10 @@ private: void load_shells(const Print& print, bool initialized); void render_toolpaths() const; void render_shells() const; - void render_overlay() const; + void render_legend() const; +#if ENABLE_GCODE_VIEWER_STATISTICS + void render_statistics() const; +#endif // ENABLE_GCODE_VIEWER_STATISTICS bool is_visible(ExtrusionRole role) const { return role < erCount && (m_extrusions.role_visibility_flags & (1 << role)) != 0; } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index fa38aca470..91b8e37929 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4050,9 +4050,13 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (m_selection.is_empty()) m_gizmos.reset_all_states(); +#if ENABLE_GCODE_VIEWER + m_dirty = true; +#else // Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor hovers over. if (m_picking_enabled) m_dirty = true; +#endif // ENABLE_GCODE_VIEWER } else evt.Skip(); From 2a4d011817912c4420a9c8458df9eb8451e12e2b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 28 Apr 2020 08:50:52 +0200 Subject: [PATCH 046/503] GCodeViewer -> Toggle extrusion role visibility by clicking on legend --- src/libslic3r/Technologies.hpp | 2 +- src/slic3r/GUI/GCodeViewer.cpp | 41 ++++++++++++++++++++--- src/slic3r/GUI/GCodeViewer.hpp | 4 ++- src/slic3r/GUI/GLCanvas3D.hpp | 3 ++ src/slic3r/GUI/GUI.cpp | 36 ++++++++++++++------- src/slic3r/GUI/GUI.hpp | 8 +++-- src/slic3r/GUI/GUI_Preview.cpp | 59 +++++++++++++++++++++++++++------- src/slic3r/GUI/GUI_Preview.hpp | 9 ++++++ src/slic3r/GUI/Plater.cpp | 17 ++++++++++ src/slic3r/GUI/Plater.hpp | 4 +++ 10 files changed, 152 insertions(+), 31 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index d04bc97fa4..255afc631e 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -59,7 +59,7 @@ // Enable G-Code viewer #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) #define ENABLE_GCODE_VIEWER_DEBUG_OUTPUT (0 && ENABLE_GCODE_VIEWER) -#define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_2_3_0_ALPHA1) +#define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 7426eafc9e..71053819dc 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -271,10 +271,29 @@ void GCodeViewer::set_toolpath_move_type_visible(GCodeProcessor::EMoveType type, m_buffers[id].visible = visible; } +unsigned int GCodeViewer::get_options_visibility_flags() const +{ + auto set_flag = [](unsigned int flags, unsigned int flag, bool active) { + return active ? (flags | (1 << flag)) : flags; + }; + + unsigned int flags = 0; + flags = set_flag(flags, 0, is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Travel)); + flags = set_flag(flags, 1, is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Retract)); + flags = set_flag(flags, 2, is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Unretract)); + flags = set_flag(flags, 3, is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Tool_change)); + flags = set_flag(flags, 4, is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Color_change)); + flags = set_flag(flags, 5, is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Pause_Print)); + flags = set_flag(flags, 6, is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Custom_GCode)); + flags = set_flag(flags, 7, m_shells.visible); + flags = set_flag(flags, 8, is_legend_enabled()); + return flags; +} + void GCodeViewer::set_options_visibility_from_flags(unsigned int flags) { auto is_flag_set = [flags](unsigned int flag) { - return (flags& (1 << flag)) != 0; + return (flags & (1 << flag)) != 0; }; set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Travel, is_flag_set(0)); @@ -752,7 +771,7 @@ void GCodeViewer::render_legend() const ImDrawList* draw_list = ImGui::GetWindowDrawList(); - auto add_item = [draw_list, &imgui](const std::array& color, const std::string& label) { + auto add_item = [draw_list, &imgui](const std::array& color, const std::string& label, std::function callback = nullptr) { float icon_size = ImGui::GetTextLineHeight(); ImVec2 pos = ImGui::GetCursorPos(); draw_list->AddRect({ pos.x, pos.y }, { pos.x + icon_size, pos.y + icon_size }, ICON_BORDER_COLOR, 0.0f, 0); @@ -762,7 +781,13 @@ void GCodeViewer::render_legend() const // draw text ImGui::Dummy({ icon_size, icon_size }); ImGui::SameLine(); - imgui.text(label); + if (callback != nullptr) + { + if (ImGui::MenuItem(label.c_str())) + callback(); + } + else + imgui.text(label); }; auto add_range = [this, draw_list, &imgui, add_item](const Extrusions::Range& range, unsigned int decimals) { @@ -811,7 +836,15 @@ void GCodeViewer::render_legend() const if (!visible) ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f); - add_item(Extrusion_Role_Colors[static_cast(role)], I18N::translate_utf8(ExtrusionEntity::role_to_string(role))); + add_item(Extrusion_Role_Colors[static_cast(role)], I18N::translate_utf8(ExtrusionEntity::role_to_string(role)), [this, role]() { + if (role < erCount) + { + m_extrusions.role_visibility_flags = is_visible(role) ? m_extrusions.role_visibility_flags & ~(1 << role) : m_extrusions.role_visibility_flags | (1 << role); + wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); + wxGetApp().plater()->update_preview_bottom_toolbar(); + } + }); + if (!visible) ImGui::PopStyleVar(); } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index be239e20ce..a8816f7a16 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -189,7 +189,7 @@ private: std::array m_layers_z_range; std::vector m_roles; std::vector m_extruder_ids; - Extrusions m_extrusions; + mutable Extrusions m_extrusions; Shells m_shells; EViewType m_view_type{ EViewType::FeatureType }; bool m_legend_enabled{ true }; @@ -227,7 +227,9 @@ public: bool is_toolpath_move_type_visible(GCodeProcessor::EMoveType type) const; void set_toolpath_move_type_visible(GCodeProcessor::EMoveType type, bool visible); + unsigned int get_toolpath_role_visibility_flags() const { return m_extrusions.role_visibility_flags; } void set_toolpath_role_visibility_flags(unsigned int flags) { m_extrusions.role_visibility_flags = flags; } + unsigned int get_options_visibility_flags() const; void set_options_visibility_from_flags(unsigned int flags); void set_layers_z_range(const std::array& layers_z_range) { m_layers_z_range = layers_z_range; } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 152658b13a..bbe53c5cd2 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -646,8 +646,11 @@ public: void ensure_on_bed(unsigned int object_idx); #if ENABLE_GCODE_VIEWER + GCodeViewer::EViewType get_gcode_view_type() const { return m_gcode_viewer.get_view_type(); } const std::vector& get_layers_zs() const; + unsigned int get_gcode_options_visibility_flags() const { return m_gcode_viewer.get_options_visibility_flags(); } void set_gcode_options_visibility_from_flags(unsigned int flags); + unsigned int get_toolpath_role_visibility_flags() const { return m_gcode_viewer.get_toolpath_role_visibility_flags(); } void set_toolpath_role_visibility_flags(unsigned int flags); void set_toolpath_view_type(GCodeViewer::EViewType type); void set_toolpaths_z_range(const std::array& range); diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index a66396b277..ebdc51c6b4 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -301,21 +301,33 @@ void create_combochecklist(wxComboCtrl* comboCtrl, const std::string& text, cons } } -int combochecklist_get_flags(wxComboCtrl* comboCtrl) +unsigned int combochecklist_get_flags(wxComboCtrl* comboCtrl) { - int flags = 0; + unsigned int flags = 0; - wxCheckListBoxComboPopup* popup = wxDynamicCast(comboCtrl->GetPopupControl(), wxCheckListBoxComboPopup); - if (popup != nullptr) - { - for (unsigned int i = 0; i < popup->GetCount(); ++i) - { - if (popup->IsChecked(i)) - flags |= 1 << i; - } - } + wxCheckListBoxComboPopup* popup = wxDynamicCast(comboCtrl->GetPopupControl(), wxCheckListBoxComboPopup); + if (popup != nullptr) + { + for (unsigned int i = 0; i < popup->GetCount(); ++i) + { + if (popup->IsChecked(i)) + flags |= 1 << i; + } + } - return flags; + return flags; +} + +void combochecklist_set_flags(wxComboCtrl* comboCtrl, unsigned int flags) +{ + wxCheckListBoxComboPopup* popup = wxDynamicCast(comboCtrl->GetPopupControl(), wxCheckListBoxComboPopup); + if (popup != nullptr) + { + for (unsigned int i = 0; i < popup->GetCount(); ++i) + { + popup->Check(i, (flags & (1 << i)) != 0); + } + } } AppConfig* get_app_config() diff --git a/src/slic3r/GUI/GUI.hpp b/src/slic3r/GUI/GUI.hpp index 6690b21207..cf133971e3 100644 --- a/src/slic3r/GUI/GUI.hpp +++ b/src/slic3r/GUI/GUI.hpp @@ -54,8 +54,12 @@ void warning_catcher(wxWindow* parent, const wxString& message); void create_combochecklist(wxComboCtrl* comboCtrl, const std::string& text, const std::string& items); // Returns the current state of the items listed in the wxCheckListBoxComboPopup contained in the given wxComboCtrl, -// encoded inside an int. -int combochecklist_get_flags(wxComboCtrl* comboCtrl); +// encoded inside an unsigned int. +unsigned int combochecklist_get_flags(wxComboCtrl* comboCtrl); + +// Sets the current state of the items listed in the wxCheckListBoxComboPopup contained in the given wxComboCtrl, +// with the flags encoded in the given unsigned int. +void combochecklist_set_flags(wxComboCtrl* comboCtrl, unsigned int flags); // wxString conversions: diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 3e0d5cd5a4..e84d1a9d56 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -222,6 +222,9 @@ Preview::Preview( : m_canvas_widget(nullptr) , m_canvas(nullptr) , m_double_slider_sizer(nullptr) +#if ENABLE_GCODE_VIEWER + , m_bottom_toolbar_sizer(nullptr) +#endif // ENABLE_GCODE_VIEWER , m_label_view_type(nullptr) , m_choice_view_type(nullptr) , m_label_show(nullptr) @@ -256,7 +259,9 @@ Preview::Preview( if (init(parent, bed, camera, view_toolbar, model)) #endif // ENABLE_NON_STATIC_CANVAS_MANAGER { +#if !ENABLE_GCODE_VIEWER show_hide_ui_elements("none"); +#endif // !ENABLE_GCODE_VIEWER load_print(); } } @@ -358,15 +363,21 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view top_sizer->Add(m_canvas_widget, 1, wxALL | wxEXPAND, 0); top_sizer->Add(m_double_slider_sizer, 0, wxEXPAND, 0); +#if ENABLE_GCODE_VIEWER + m_bottom_toolbar_sizer = new wxBoxSizer(wxHORIZONTAL); + m_bottom_toolbar_sizer->Add(m_label_view_type, 0, wxALIGN_CENTER_VERTICAL, 5); + m_bottom_toolbar_sizer->Add(m_choice_view_type, 0, wxEXPAND | wxALL, 5); + m_bottom_toolbar_sizer->AddSpacer(10); + m_bottom_toolbar_sizer->Add(m_label_show, 0, wxALIGN_CENTER_VERTICAL, 5); + m_bottom_toolbar_sizer->Add(m_combochecklist_features, 0, wxEXPAND | wxALL, 5); + m_bottom_toolbar_sizer->Add(m_combochecklist_options, 0, wxEXPAND | wxALL, 5); +#else wxBoxSizer* bottom_sizer = new wxBoxSizer(wxHORIZONTAL); bottom_sizer->Add(m_label_view_type, 0, wxALIGN_CENTER_VERTICAL, 5); bottom_sizer->Add(m_choice_view_type, 0, wxEXPAND | wxALL, 5); bottom_sizer->AddSpacer(10); bottom_sizer->Add(m_label_show, 0, wxALIGN_CENTER_VERTICAL, 5); bottom_sizer->Add(m_combochecklist_features, 0, wxEXPAND | wxALL, 5); -#if ENABLE_GCODE_VIEWER - bottom_sizer->Add(m_combochecklist_options, 0, wxEXPAND | wxALL, 5); -#else bottom_sizer->AddSpacer(20); bottom_sizer->Add(m_checkbox_travel, 0, wxEXPAND | wxALL, 5); bottom_sizer->AddSpacer(10); @@ -381,8 +392,12 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); main_sizer->Add(top_sizer, 1, wxALL | wxEXPAND, 0); +#if ENABLE_GCODE_VIEWER + main_sizer->Add(m_bottom_toolbar_sizer, 0, wxALL | wxEXPAND, 0); + main_sizer->Hide(m_bottom_toolbar_sizer); +#else main_sizer->Add(bottom_sizer, 0, wxALL | wxEXPAND, 0); - +#endif // ENABLE_GCODE_VIEWER SetSizer(main_sizer); SetMinSize(GetSize()); GetSizer()->SetSizeHints(this); @@ -480,6 +495,9 @@ void Preview::load_print(bool keep_z_range) else if (tech == ptSLA) load_print_as_sla(); +#if ENABLE_GCODE_VIEWER + update_bottom_toolbar(); +#endif // ENABLE_GCODE_VIEWER Layout(); } @@ -581,6 +599,7 @@ void Preview::unbind_event_handlers() #endif // ENABLE_GCODE_VIEWER } +#if !ENABLE_GCODE_VIEWER void Preview::show_hide_ui_elements(const std::string& what) { bool enable = (what == "full"); @@ -615,6 +634,7 @@ void Preview::show_hide_ui_elements(const std::string& what) m_label_view_type->Show(visible); m_choice_view_type->Show(visible); } +#endif // !ENABLE_GCODE_VIEWER void Preview::reset_sliders(bool reset_all) { @@ -661,11 +681,11 @@ void Preview::on_choice_view_type(wxCommandEvent& evt) void Preview::on_combochecklist_features(wxCommandEvent& evt) { - int flags = Slic3r::GUI::combochecklist_get_flags(m_combochecklist_features); + unsigned int flags = Slic3r::GUI::combochecklist_get_flags(m_combochecklist_features); #if ENABLE_GCODE_VIEWER - m_canvas->set_toolpath_role_visibility_flags(static_cast(flags)); + m_canvas->set_toolpath_role_visibility_flags(flags); #else - m_gcode_preview_data->extrusion.role_flags = (unsigned int)flags; + m_gcode_preview_data->extrusion.role_flags = flags; #endif // ENABLE_GCODE_VIEWER refresh_print(); } @@ -673,7 +693,7 @@ void Preview::on_combochecklist_features(wxCommandEvent& evt) #if ENABLE_GCODE_VIEWER void Preview::on_combochecklist_options(wxCommandEvent& evt) { - m_canvas->set_gcode_options_visibility_from_flags(static_cast(Slic3r::GUI::combochecklist_get_flags(m_combochecklist_options))); + m_canvas->set_gcode_options_visibility_from_flags(Slic3r::GUI::combochecklist_get_flags(m_combochecklist_options)); refresh_print(); } #else @@ -730,6 +750,17 @@ void Preview::update_view_type(bool slice_completed) } } +#if ENABLE_GCODE_VIEWER +void Preview::update_bottom_toolbar() +{ + combochecklist_set_flags(m_combochecklist_features, m_canvas->get_toolpath_role_visibility_flags()); + combochecklist_set_flags(m_combochecklist_options, m_canvas->get_gcode_options_visibility_flags()); + + m_bottom_toolbar_sizer->Show(m_combochecklist_features, m_canvas->get_gcode_view_type() != GCodeViewer::EViewType::FeatureType); + m_bottom_toolbar_sizer->Layout(); +} +#endif // ENABLE_GCODE_VIEWER + void Preview::create_double_slider() { m_slider = new DoubleSlider::Control(this, wxID_ANY, 0, 0, 0, 100); @@ -1039,7 +1070,8 @@ void Preview::load_print_as_fff(bool keep_z_range) #if ENABLE_GCODE_VIEWER m_canvas->load_gcode_preview(*m_gcode_result); m_canvas->refresh_gcode_preview(*m_gcode_result, colors); - show_hide_ui_elements(gcode_preview_data_valid ? "full" : "simple"); + GetSizer()->Show(m_bottom_toolbar_sizer); + GetSizer()->Layout(); #else m_canvas->load_gcode_preview(*m_gcode_preview_data, colors); #endif // ENABLE_GCODE_VIEWER @@ -1048,7 +1080,8 @@ void Preview::load_print_as_fff(bool keep_z_range) // Load the initial preview based on slices, not the final G-code. m_canvas->load_preview(colors, color_print_values); #if ENABLE_GCODE_VIEWER - show_hide_ui_elements("none"); + GetSizer()->Hide(m_bottom_toolbar_sizer); + GetSizer()->Layout(); #endif // ENABLE_GCODE_VIEWER } #if ENABLE_GCODE_VIEWER @@ -1097,7 +1130,12 @@ void Preview::load_print_as_sla() if (IsShown()) { m_canvas->load_sla_preview(); +#if ENABLE_GCODE_VIEWER + GetSizer()->Hide(m_bottom_toolbar_sizer); + GetSizer()->Layout(); +#else show_hide_ui_elements("none"); +#endif // ENABLE_GCODE_VIEWER if (n_layers > 0) update_sliders(zs); @@ -1132,6 +1170,5 @@ void Preview::on_sliders_scroll_changed(wxCommandEvent& event) } } - } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index c4ad4eb79c..a7db054bc3 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -91,6 +91,9 @@ class Preview : public wxPanel wxGLCanvas* m_canvas_widget; GLCanvas3D* m_canvas; wxBoxSizer* m_double_slider_sizer; +#if ENABLE_GCODE_VIEWER + wxBoxSizer* m_bottom_toolbar_sizer; +#endif // ENABLE_GCODE_VIEWER wxStaticText* m_label_view_type; wxChoice* m_choice_view_type; wxStaticText* m_label_show; @@ -172,6 +175,10 @@ public: bool is_loaded() const { return m_loaded; } +#if ENABLE_GCODE_VIEWER + void update_bottom_toolbar(); +#endif // ENABLE_GCODE_VIEWER + private: #if ENABLE_NON_STATIC_CANVAS_MANAGER bool init(wxWindow* parent, Model* model); @@ -182,7 +189,9 @@ private: void bind_event_handlers(); void unbind_event_handlers(); +#if !ENABLE_GCODE_VIEWER void show_hide_ui_elements(const std::string& what); +#endif // !ENABLE_GCODE_VIEWER void reset_sliders(bool reset_all); void update_sliders(const std::vector& layers_z, bool keep_z_range = false); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b64f705dd0..861e0e55fe 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1570,6 +1570,9 @@ struct Plater::priv #endif // ENABLE_NON_STATIC_CANVAS_MANAGER bool init_view_toolbar(); +#if ENABLE_GCODE_VIEWER + void update_preview_bottom_toolbar(); +#endif // ENABLE_GCODE_VIEWER void reset_all_gizmos(); void update_ui_from_settings(); @@ -3765,6 +3768,13 @@ bool Plater::priv::init_view_toolbar() return true; } +#if ENABLE_GCODE_VIEWER +void Plater::priv::update_preview_bottom_toolbar() +{ + preview->update_bottom_toolbar(); +} +#endif // ENABLE_GCODE_VIEWER + bool Plater::priv::can_set_instance_to_object() const { const int obj_idx = get_selected_object_idx(); @@ -5313,6 +5323,13 @@ GLToolbar& Plater::get_view_toolbar() } #endif // ENABLE_NON_STATIC_CANVAS_MANAGER +#if ENABLE_GCODE_VIEWER +void Plater::update_preview_bottom_toolbar() +{ + p->update_preview_bottom_toolbar(); +} +#endif // ENABLE_GCODE_VIEWER + const Mouse3DController& Plater::get_mouse3d_controller() const { return p->mouse3d_controller; diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 2ac4f23c18..f4ca22578f 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -310,6 +310,10 @@ public: GLToolbar& get_view_toolbar(); #endif // ENABLE_NON_STATIC_CANVAS_MANAGER +#if ENABLE_GCODE_VIEWER + void update_preview_bottom_toolbar(); +#endif // ENABLE_GCODE_VIEWER + const Mouse3DController& get_mouse3d_controller() const; Mouse3DController& get_mouse3d_controller(); From a77461b467ba68d6650452d82979476cc4581f70 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 28 Apr 2020 09:09:24 +0200 Subject: [PATCH 047/503] GCodeViewer -> Fixed synchronization between legend and bottom toolbar --- src/slic3r/GUI/GLCanvas3D.cpp | 1 + src/slic3r/GUI/GLCanvas3D.hpp | 1 + src/slic3r/GUI/GUI_Preview.cpp | 5 +++-- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 91b8e37929..080ba2045b 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3216,6 +3216,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) { m_gcode_viewer.enable_legend(!m_gcode_viewer.is_legend_enabled()); m_dirty = true; + wxGetApp().plater()->update_preview_bottom_toolbar(); } break; } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index bbe53c5cd2..c640108fdf 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -646,6 +646,7 @@ public: void ensure_on_bed(unsigned int object_idx); #if ENABLE_GCODE_VIEWER + bool is_gcode_legend_enabled() const { return m_gcode_viewer.is_legend_enabled(); } GCodeViewer::EViewType get_gcode_view_type() const { return m_gcode_viewer.get_view_type(); } const std::vector& get_layers_zs() const; unsigned int get_gcode_options_visibility_flags() const { return m_gcode_viewer.get_options_visibility_flags(); } diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index e84d1a9d56..28fd45b6c2 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -369,8 +369,8 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view m_bottom_toolbar_sizer->Add(m_choice_view_type, 0, wxEXPAND | wxALL, 5); m_bottom_toolbar_sizer->AddSpacer(10); m_bottom_toolbar_sizer->Add(m_label_show, 0, wxALIGN_CENTER_VERTICAL, 5); - m_bottom_toolbar_sizer->Add(m_combochecklist_features, 0, wxEXPAND | wxALL, 5); m_bottom_toolbar_sizer->Add(m_combochecklist_options, 0, wxEXPAND | wxALL, 5); + m_bottom_toolbar_sizer->Add(m_combochecklist_features, 0, wxEXPAND | wxALL, 5); #else wxBoxSizer* bottom_sizer = new wxBoxSizer(wxHORIZONTAL); bottom_sizer->Add(m_label_view_type, 0, wxALIGN_CENTER_VERTICAL, 5); @@ -756,7 +756,8 @@ void Preview::update_bottom_toolbar() combochecklist_set_flags(m_combochecklist_features, m_canvas->get_toolpath_role_visibility_flags()); combochecklist_set_flags(m_combochecklist_options, m_canvas->get_gcode_options_visibility_flags()); - m_bottom_toolbar_sizer->Show(m_combochecklist_features, m_canvas->get_gcode_view_type() != GCodeViewer::EViewType::FeatureType); + m_bottom_toolbar_sizer->Show(m_combochecklist_features, + !m_canvas->is_gcode_legend_enabled() || m_canvas->get_gcode_view_type() != GCodeViewer::EViewType::FeatureType); m_bottom_toolbar_sizer->Layout(); } #endif // ENABLE_GCODE_VIEWER From 1cb0f044db9ec1430074e4de59a32ed299c28912 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 28 Apr 2020 10:29:25 +0200 Subject: [PATCH 048/503] GCodeProcessor::MoveVertex -> added placeholder for time --- src/libslic3r/GCode/GCodeProcessor.cpp | 1 + src/libslic3r/GCode/GCodeProcessor.hpp | 1 + src/slic3r/GUI/GCodeViewer.cpp | 7 +++++++ src/slic3r/GUI/GCodeViewer.hpp | 2 ++ 4 files changed, 11 insertions(+) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 9dc3964436..9fda79a136 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -591,6 +591,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type) vertex.fan_speed = m_fan_speed; vertex.extruder_id = m_extruder_id; vertex.cp_color_id = m_cp_color.current; + vertex.time = static_cast(m_result.moves.size()); m_result.moves.emplace_back(vertex); } diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 2212148367..4f6cf7430f 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -82,6 +82,7 @@ namespace Slic3r { float height{ 0.0f }; // mm float mm3_per_mm{ 0.0f }; float fan_speed{ 0.0f }; // percentage + float time{ 0.0f }; // s float volumetric_rate() const { return feedrate * mm3_per_mm; } diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 71053819dc..d60b5e9b89 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -346,6 +346,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) { #if ENABLE_GCODE_VIEWER_STATISTICS auto start_time = std::chrono::high_resolution_clock::now(); + m_statistics.results_size = gcode_result.moves.size() * sizeof(GCodeProcessor::MoveVertex); #endif // ENABLE_GCODE_VIEWER_STATISTICS // vertex data @@ -1022,6 +1023,12 @@ void GCodeViewer::render_statistics() const ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(std::string("Results:")); + ImGui::PopStyleColor(); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.results_size) + " bytes"); + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); imgui.text(std::string("Vertices:")); ImGui::PopStyleColor(); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index a8816f7a16..be1c7e9986 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -139,6 +139,7 @@ class GCodeViewer long long refresh_time{ 0 }; long long gl_points_calls_count{ 0 }; long long gl_line_strip_calls_count{ 0 }; + long long results_size{ 0 }; long long vertices_size{ 0 }; long long indices_size{ 0 }; @@ -159,6 +160,7 @@ class GCodeViewer } void reset_sizes() { + results_size = 0; vertices_size = 0; indices_size = 0; } From d265c84b76241dae01ade0658cc645bddbc29893 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 28 Apr 2020 12:24:03 +0200 Subject: [PATCH 049/503] GCodeViewer -> Refactoring --- src/slic3r/GUI/GCodeViewer.cpp | 25 ++++++++++++------------- src/slic3r/GUI/GCodeViewer.hpp | 14 +++++++++----- src/slic3r/GUI/GUI_Preview.cpp | 14 +++----------- 3 files changed, 24 insertions(+), 29 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index d60b5e9b89..db16354ebe 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -92,9 +92,8 @@ bool GCodeViewer::IBuffer::init_shader(const std::string& vertex_shader_src, con void GCodeViewer::IBuffer::add_path(const GCodeProcessor::MoveVertex& move) { - unsigned int id = static_cast(data.size()); - double z = static_cast(move.position[2]); - paths.push_back({ move.type, move.extrusion_role, id, id, z, z, move.delta_extruder, move.height, move.width, move.feedrate, move.fan_speed, move.volumetric_rate(), move.extruder_id, move.cp_color_id }); + Path::Endpoint endpoint = { static_cast(data.size()), static_cast(move.position[2]) }; + paths.push_back({ move.type, move.extrusion_role, endpoint, endpoint, move.delta_extruder, move.height, move.width, move.feedrate, move.fan_speed, move.volumetric_rate(), move.extruder_id, move.cp_color_id }); } std::array GCodeViewer::Extrusions::Range::get_color_at(float value) const @@ -409,13 +408,13 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) buffer.data.push_back(static_cast(i - 1)); } - buffer.paths.back().last = static_cast(buffer.data.size()); + buffer.paths.back().last.id = static_cast(buffer.data.size()); buffer.data.push_back(static_cast(i)); break; } default: { - continue; + break; } } } @@ -602,7 +601,7 @@ void GCodeViewer::render_toolpaths() const continue; glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last.id - path.first.id + 1), GL_UNSIGNED_INT, (const void*)(path.first.id * sizeof(GLuint)))); glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); #if ENABLE_GCODE_VIEWER_STATISTICS @@ -620,7 +619,7 @@ void GCodeViewer::render_toolpaths() const continue; glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last.id - path.first.id + 1), GL_UNSIGNED_INT, (const void*)(path.first.id * sizeof(GLuint)))); glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); #if ENABLE_GCODE_VIEWER_STATISTICS @@ -638,7 +637,7 @@ void GCodeViewer::render_toolpaths() const continue; glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last.id - path.first.id + 1), GL_UNSIGNED_INT, (const void*)(path.first.id * sizeof(GLuint)))); glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); #if ENABLE_GCODE_VIEWER_STATISTICS @@ -656,7 +655,7 @@ void GCodeViewer::render_toolpaths() const continue; glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last.id - path.first.id + 1), GL_UNSIGNED_INT, (const void*)(path.first.id * sizeof(GLuint)))); glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); #if ENABLE_GCODE_VIEWER_STATISTICS @@ -674,7 +673,7 @@ void GCodeViewer::render_toolpaths() const continue; glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last.id - path.first.id + 1), GL_UNSIGNED_INT, (const void*)(path.first.id * sizeof(GLuint)))); glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); #if ENABLE_GCODE_VIEWER_STATISTICS @@ -692,7 +691,7 @@ void GCodeViewer::render_toolpaths() const continue; glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last.id - path.first.id + 1), GL_UNSIGNED_INT, (const void*)(path.first.id * sizeof(GLuint)))); glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); #if ENABLE_GCODE_VIEWER_STATISTICS @@ -708,7 +707,7 @@ void GCodeViewer::render_toolpaths() const continue; set_color(current_program_id, extrusion_color(path)); - glsafe(::glDrawElements(GL_LINE_STRIP, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + glsafe(::glDrawElements(GL_LINE_STRIP, GLsizei(path.last.id - path.first.id + 1), GL_UNSIGNED_INT, (const void*)(path.first.id * sizeof(GLuint)))); #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_line_strip_calls_count; @@ -723,7 +722,7 @@ void GCodeViewer::render_toolpaths() const continue; set_color(current_program_id, (m_view_type == EViewType::Feedrate || m_view_type == EViewType::Tool || m_view_type == EViewType::ColorPrint) ? extrusion_color(path) : travel_color(path)); - glsafe(::glDrawElements(GL_LINE_STRIP, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + glsafe(::glDrawElements(GL_LINE_STRIP, GLsizei(path.last.id - path.first.id + 1), GL_UNSIGNED_INT, (const void*)(path.first.id * sizeof(GLuint)))); #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_line_strip_calls_count; diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index be1c7e9986..9688c5c4b8 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -35,12 +35,16 @@ class GCodeViewer // Used to identify different toolpath sub-types inside a IBuffer struct Path { + struct Endpoint + { + unsigned int id{ 0u }; + double z{ 0.0 }; + }; + GCodeProcessor::EMoveType type{ GCodeProcessor::EMoveType::Noop }; ExtrusionRole role{ erNone }; - unsigned int first{ 0 }; - unsigned int last{ 0 }; - double first_z{ 0.0f }; - double last_z{ 0.0f }; + Endpoint first; + Endpoint last; float delta_extruder{ 0.0f }; float height{ 0.0f }; float width{ 0.0f }; @@ -257,7 +261,7 @@ private: return z > m_layers_z_range[0] - EPSILON && z < m_layers_z_range[1] + EPSILON; }; - return in_z_range(path.first_z) || in_z_range(path.last_z); + return in_z_range(path.first.z) || in_z_range(path.last.z); } }; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 28fd45b6c2..72f40bb6b7 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -314,7 +314,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view m_label_show = new wxStaticText(this, wxID_ANY, _(L("Show"))); m_combochecklist_features = new wxComboCtrl(); - m_combochecklist_features->Create(this, wxID_ANY, _(L("Feature types")), wxDefaultPosition, wxSize(15 * wxGetApp().em_unit(), -1), wxCB_READONLY); + m_combochecklist_features->Create(this, wxID_ANY, _(L("Feature types")), wxDefaultPosition, wxDefaultSize, wxCB_READONLY); std::string feature_items = GUI::into_u8( #if ENABLE_GCODE_VIEWER _L("Unknown") + "|1|" + @@ -337,7 +337,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view #if ENABLE_GCODE_VIEWER m_combochecklist_options = new wxComboCtrl(); - m_combochecklist_options->Create(this, wxID_ANY, _(L("Others")), wxDefaultPosition, wxSize(15 * wxGetApp().em_unit(), -1), wxCB_READONLY); + m_combochecklist_options->Create(this, wxID_ANY, _(L("Options")), wxDefaultPosition, wxDefaultSize, wxCB_READONLY); std::string options_items = GUI::into_u8( _(L("Travel")) + "|0|" + _(L("Retractions")) + "|0|" + @@ -349,7 +349,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view _(L("Shells")) + "|0|" + _(L("Legend")) + "|1" ); - Slic3r::GUI::create_combochecklist(m_combochecklist_options, GUI::into_u8(_(L("Others"))), options_items); + Slic3r::GUI::create_combochecklist(m_combochecklist_options, GUI::into_u8(_(L("Options"))), options_items); #else m_checkbox_travel = new wxCheckBox(this, wxID_ANY, _(L("Travel"))); m_checkbox_retractions = new wxCheckBox(this, wxID_ANY, _(L("Retractions"))); @@ -605,15 +605,11 @@ void Preview::show_hide_ui_elements(const std::string& what) bool enable = (what == "full"); m_label_show->Enable(enable); m_combochecklist_features->Enable(enable); -#if ENABLE_GCODE_VIEWER - m_combochecklist_options->Enable(enable); -#else m_checkbox_travel->Enable(enable); m_checkbox_retractions->Enable(enable); m_checkbox_unretractions->Enable(enable); m_checkbox_shells->Enable(enable); m_checkbox_legend->Enable(enable); -#endif // ENABLE_GCODE_VIEWER enable = (what != "none"); m_label_view_type->Enable(enable); @@ -622,15 +618,11 @@ void Preview::show_hide_ui_elements(const std::string& what) bool visible = (what != "none"); m_label_show->Show(visible); m_combochecklist_features->Show(visible); -#if ENABLE_GCODE_VIEWER - m_combochecklist_options->Show(visible); -#else m_checkbox_travel->Show(visible); m_checkbox_retractions->Show(visible); m_checkbox_unretractions->Show(visible); m_checkbox_shells->Show(visible); m_checkbox_legend->Show(visible); -#endif // ENABLE_GCODE_VIEWER m_label_view_type->Show(visible); m_choice_view_type->Show(visible); } From 3267d3368f0d80921df5d045ed096afb248f141d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 28 Apr 2020 15:08:36 +0200 Subject: [PATCH 050/503] GCodeViewer -> Use glMultiDrawElements() in place of glDrawElements() to draw extrude and travel paths --- src/libslic3r/Technologies.hpp | 1 + src/slic3r/GUI/GCodeViewer.cpp | 135 +++++++++++++++++++++++++++++++-- src/slic3r/GUI/GCodeViewer.hpp | 28 +++++++ 3 files changed, 157 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 255afc631e..3f821ddce0 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -59,6 +59,7 @@ // Enable G-Code viewer #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) #define ENABLE_GCODE_VIEWER_DEBUG_OUTPUT (0 && ENABLE_GCODE_VIEWER) +#define ENABLE_GCODE_VIEWER_GL_OPTIMIZATION (1 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index db16354ebe..64c027a7b2 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -214,6 +214,11 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: } } +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + // update buffers' render paths + refresh_render_paths(); +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.refresh_time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start_time).count(); #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -345,7 +350,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) { #if ENABLE_GCODE_VIEWER_STATISTICS auto start_time = std::chrono::high_resolution_clock::now(); - m_statistics.results_size = gcode_result.moves.size() * sizeof(GCodeProcessor::MoveVertex); + m_statistics.results_size = SLIC3R_STDVEC_MEMSIZE(gcode_result.moves, GCodeProcessor::MoveVertex); #endif // ENABLE_GCODE_VIEWER_STATISTICS // vertex data @@ -363,7 +368,8 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) } #if ENABLE_GCODE_VIEWER_STATISTICS - m_statistics.vertices_size = vertices_data.size() * sizeof(float); + m_statistics.vertices_size = SLIC3R_STDVEC_MEMSIZE(vertices_data, float); + m_statistics.vertices_gpu_size = vertices_data.size() * sizeof(float); #endif // ENABLE_GCODE_VIEWER_STATISTICS // vertex data -> send to gpu @@ -424,7 +430,8 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) { buffer.data_size = buffer.data.size(); #if ENABLE_GCODE_VIEWER_STATISTICS - m_statistics.indices_size += buffer.data_size * sizeof(unsigned int); + m_statistics.indices_size += SLIC3R_STDVEC_MEMSIZE(buffer.data, unsigned int); + m_statistics.indices_gpu_size += buffer.data_size * sizeof(unsigned int); #endif // ENABLE_GCODE_VIEWER_STATISTICS if (buffer.data_size > 0) { @@ -526,7 +533,8 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) } } -void GCodeViewer::render_toolpaths() const +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION +void GCodeViewer::refresh_render_paths() const { auto extrusion_color = [this](const Path& path) { std::array color; @@ -551,6 +559,65 @@ void GCodeViewer::render_toolpaths() const Travel_Colors[0] /* Move */); }; + + for (IBuffer& buffer : m_buffers) { + buffer.render_paths = std::vector(); + for (const Path& path : buffer.paths) + { + if (!is_in_z_range(path)) + continue; + + if (path.type == GCodeProcessor::EMoveType::Extrude && !is_visible(path)) + continue; + + std::array color = { 0.0f, 0.0f, 0.0f }; + switch (path.type) + { + case GCodeProcessor::EMoveType::Extrude: { color = extrusion_color(path); break; } + case GCodeProcessor::EMoveType::Travel: { color = (m_view_type == EViewType::Feedrate || m_view_type == EViewType::Tool || m_view_type == EViewType::ColorPrint) ? extrusion_color(path) : travel_color(path); break; } + } + + auto it = std::find_if(buffer.render_paths.begin(), buffer.render_paths.end(), [color](const RenderPath& path) { return path.color == color; }); + if (it == buffer.render_paths.end()) + { + it = buffer.render_paths.insert(buffer.render_paths.end(), RenderPath()); + it->color = color; + } + + it->sizes.push_back(path.last.id - path.first.id + 1); + it->offsets.push_back(static_cast(path.first.id * sizeof(unsigned int))); + } + } +} +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + +void GCodeViewer::render_toolpaths() const +{ +#if !ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + auto extrusion_color = [this](const Path& path) { + std::array color; + switch (m_view_type) + { + case EViewType::FeatureType: { color = Extrusion_Role_Colors[static_cast(path.role)]; break; } + case EViewType::Height: { color = m_extrusions.ranges.height.get_color_at(path.height); break; } + case EViewType::Width: { color = m_extrusions.ranges.width.get_color_at(path.width); break; } + case EViewType::Feedrate: { color = m_extrusions.ranges.feedrate.get_color_at(path.feedrate); break; } + case EViewType::FanSpeed: { color = m_extrusions.ranges.fan_speed.get_color_at(path.fan_speed); break; } + case EViewType::VolumetricRate: { color = m_extrusions.ranges.volumetric_rate.get_color_at(path.volumetric_rate); break; } + case EViewType::Tool: { color = m_tool_colors[path.extruder_id]; break; } + case EViewType::ColorPrint: { color = m_tool_colors[path.cp_color_id]; break; } + default: { color = { 1.0f, 1.0f, 1.0f }; break; } + } + return color; + }; + + auto travel_color = [this](const Path& path) { + return (path.delta_extruder < 0.0f) ? Travel_Colors[2] /* Retract */ : + ((path.delta_extruder > 0.0f) ? Travel_Colors[1] /* Extrude */ : + Travel_Colors[0] /* Move */); + }; +#endif // !ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + auto set_color = [](GLint current_program_id, const std::array& color) { if (current_program_id > 0) { GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1; @@ -702,6 +769,17 @@ void GCodeViewer::render_toolpaths() const } case GCodeProcessor::EMoveType::Extrude: { +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + for (const RenderPath& path : buffer.render_paths) + { + set_color(current_program_id, path.color); + glsafe(::glMultiDrawElements(GL_LINE_STRIP, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_multi_line_strip_calls_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS + + } +#else for (const Path& path : buffer.paths) { if (!is_visible(path) || !is_in_z_range(path)) continue; @@ -713,10 +791,22 @@ void GCodeViewer::render_toolpaths() const ++m_statistics.gl_line_strip_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION break; } case GCodeProcessor::EMoveType::Travel: { +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + for (const RenderPath& path : buffer.render_paths) + { + set_color(current_program_id, path.color); + glsafe(::glMultiDrawElements(GL_LINE_STRIP, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_multi_line_strip_calls_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS + + } +#else for (const Path& path : buffer.paths) { if (!is_in_z_range(path)) continue; @@ -728,6 +818,7 @@ void GCodeViewer::render_toolpaths() const ++m_statistics.gl_line_strip_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION break; } } @@ -840,6 +931,10 @@ void GCodeViewer::render_legend() const if (role < erCount) { m_extrusions.role_visibility_flags = is_visible(role) ? m_extrusions.role_visibility_flags & ~(1 << role) : m_extrusions.role_visibility_flags | (1 << role); +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + // update buffers' render paths + refresh_render_paths(); +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); wxGetApp().plater()->update_preview_bottom_toolbar(); } @@ -986,7 +1081,7 @@ void GCodeViewer::render_statistics() const static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); static const float offset = 250.0f; - if (!m_legend_enabled || m_roles.empty()) + if (m_roles.empty()) return; ImGuiWrapper& imgui = *wxGetApp().imgui(); @@ -1020,6 +1115,20 @@ void GCodeViewer::render_statistics() const ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.gl_line_strip_calls_count)); +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(std::string("Multi GL_POINTS calls:")); + ImGui::PopStyleColor(); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.gl_multi_points_calls_count)); + + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(std::string("Multi GL_LINE_STRIP calls:")); + ImGui::PopStyleColor(); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.gl_multi_line_strip_calls_count)); +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + ImGui::Separator(); ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); @@ -1029,17 +1138,29 @@ void GCodeViewer::render_statistics() const imgui.text(std::to_string(m_statistics.results_size) + " bytes"); ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(std::string("Vertices:")); + imgui.text(std::string("Vertices CPU:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.vertices_size) + " bytes"); ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(std::string("Indices:")); + imgui.text(std::string("Vertices GPU:")); + ImGui::PopStyleColor(); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.vertices_gpu_size) + " bytes"); + + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(std::string("Indices CPU:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.indices_size) + " bytes"); + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(std::string("Indices GPU:")); + ImGui::PopStyleColor(); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.indices_gpu_size) + " bytes"); + imgui.end(); } #endif // ENABLE_GCODE_VIEWER_STATISTICS diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 9688c5c4b8..ec3bfc9a63 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -61,6 +61,16 @@ class GCodeViewer } }; +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + // Used to batch the indices needed to render paths + struct RenderPath + { + std::array color; + std::vector sizes; + std::vector offsets; // use size_t because we need the pointer's size (used in the call glMultiDrawElements()) + }; +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + // buffer containing indices data and shader for a specific toolpath type struct IBuffer { @@ -69,6 +79,9 @@ class GCodeViewer std::vector data; size_t data_size{ 0 }; std::vector paths; +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + std::vector render_paths; +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION bool visible{ false }; void reset(); @@ -143,9 +156,15 @@ class GCodeViewer long long refresh_time{ 0 }; long long gl_points_calls_count{ 0 }; long long gl_line_strip_calls_count{ 0 }; +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + long long gl_multi_points_calls_count{ 0 }; + long long gl_multi_line_strip_calls_count{ 0 }; +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION long long results_size{ 0 }; long long vertices_size{ 0 }; + long long vertices_gpu_size{ 0 }; long long indices_size{ 0 }; + long long indices_gpu_size{ 0 }; void reset_all() { reset_times(); @@ -161,12 +180,18 @@ class GCodeViewer void reset_opengl() { gl_points_calls_count = 0; gl_line_strip_calls_count = 0; +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + gl_multi_points_calls_count = 0; + gl_multi_line_strip_calls_count = 0; +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION } void reset_sizes() { results_size = 0; vertices_size = 0; + vertices_gpu_size = 0; indices_size = 0; + indices_gpu_size = 0; } }; #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -246,6 +271,9 @@ private: bool init_shaders(); void load_toolpaths(const GCodeProcessor::Result& gcode_result); void load_shells(const Print& print, bool initialized); +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + void refresh_render_paths() const; +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION void render_toolpaths() const; void render_shells() const; void render_legend() const; From d8f6a9179f096f4c495da269c015be34cd5bcfbb Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 28 Apr 2020 15:49:01 +0200 Subject: [PATCH 051/503] GCodeViewer -> Use glMultiDrawElements() in place of glDrawElements() to draw all entities --- src/slic3r/GUI/GCodeViewer.cpp | 92 +++++++++++++++++++++++++++++++++- src/slic3r/GUI/GCodeViewer.hpp | 8 +++ 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 64c027a7b2..4b2784df98 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -661,6 +661,20 @@ void GCodeViewer::render_toolpaths() const { case GCodeProcessor::EMoveType::Tool_change: { +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + std::array color = { 1.0f, 1.0f, 1.0f }; + set_color(current_program_id, color); + for (const RenderPath& path : buffer.render_paths) + { + glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); + glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); + glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_multi_points_calls_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS + } +#else std::array color = { 1.0f, 1.0f, 1.0f }; set_color(current_program_id, color); for (const Path& path : buffer.paths) { @@ -675,10 +689,25 @@ void GCodeViewer::render_toolpaths() const ++m_statistics.gl_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION break; } case GCodeProcessor::EMoveType::Color_change: { +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + std::array color = { 1.0f, 0.0f, 0.0f }; + set_color(current_program_id, color); + for (const RenderPath& path : buffer.render_paths) + { + glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); + glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); + glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_multi_points_calls_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS + } +#else std::array color = { 1.0f, 0.0f, 0.0f }; set_color(current_program_id, color); for (const Path& path : buffer.paths) { @@ -693,10 +722,25 @@ void GCodeViewer::render_toolpaths() const ++m_statistics.gl_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION break; } case GCodeProcessor::EMoveType::Pause_Print: { +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + std::array color = { 0.0f, 1.0f, 0.0f }; + set_color(current_program_id, color); + for (const RenderPath& path : buffer.render_paths) + { + glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); + glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); + glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_multi_points_calls_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS + } +#else std::array color = { 0.0f, 1.0f, 0.0f }; set_color(current_program_id, color); for (const Path& path : buffer.paths) { @@ -711,10 +755,25 @@ void GCodeViewer::render_toolpaths() const ++m_statistics.gl_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION break; } case GCodeProcessor::EMoveType::Custom_GCode: { +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + std::array color = { 0.0f, 0.0f, 1.0f }; + set_color(current_program_id, color); + for (const RenderPath& path : buffer.render_paths) + { + glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); + glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); + glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_multi_points_calls_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS + } +#else std::array color = { 0.0f, 0.0f, 1.0f }; set_color(current_program_id, color); for (const Path& path : buffer.paths) { @@ -729,10 +788,25 @@ void GCodeViewer::render_toolpaths() const ++m_statistics.gl_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION break; } case GCodeProcessor::EMoveType::Retract: { +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + std::array color = { 1.0f, 0.0f, 1.0f }; + set_color(current_program_id, color); + for (const RenderPath& path : buffer.render_paths) + { + glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); + glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); + glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_multi_points_calls_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS + } +#else std::array color = { 1.0f, 0.0f, 1.0f }; set_color(current_program_id, color); for (const Path& path : buffer.paths) { @@ -747,10 +821,25 @@ void GCodeViewer::render_toolpaths() const ++m_statistics.gl_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION break; } case GCodeProcessor::EMoveType::Unretract: { +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + std::array color = { 0.0f, 1.0f, 1.0f }; + set_color(current_program_id, color); + for (const RenderPath& path : buffer.render_paths) + { + glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); + glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); + glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); + +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_multi_points_calls_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS + } +#else std::array color = { 0.0f, 1.0f, 1.0f }; set_color(current_program_id, color); for (const Path& path : buffer.paths) { @@ -765,6 +854,7 @@ void GCodeViewer::render_toolpaths() const ++m_statistics.gl_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION break; } case GCodeProcessor::EMoveType::Extrude: @@ -1132,7 +1222,7 @@ void GCodeViewer::render_statistics() const ImGui::Separator(); ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(std::string("Results:")); + imgui.text(std::string("GCodeProcessor results:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.results_size) + " bytes"); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index ec3bfc9a63..0f90be5d73 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -262,7 +262,15 @@ public: void set_toolpath_role_visibility_flags(unsigned int flags) { m_extrusions.role_visibility_flags = flags; } unsigned int get_options_visibility_flags() const; void set_options_visibility_from_flags(unsigned int flags); +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + void set_layers_z_range(const std::array& layers_z_range) + { + m_layers_z_range = layers_z_range; + refresh_render_paths(); + } +#else void set_layers_z_range(const std::array& layers_z_range) { m_layers_z_range = layers_z_range; } +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION bool is_legend_enabled() const { return m_legend_enabled; } void enable_legend(bool enable) { m_legend_enabled = enable; } From c9bd0840b3d3ad20e531bd10815b4d8fe4f92814 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 29 Apr 2020 08:24:39 +0200 Subject: [PATCH 052/503] GCodeViewer -> Code cleanup --- src/libslic3r/Technologies.hpp | 1 - src/slic3r/GUI/GCodeViewer.cpp | 175 --------------------------------- src/slic3r/GUI/GCodeViewer.hpp | 18 ---- 3 files changed, 194 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 3f821ddce0..255afc631e 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -59,7 +59,6 @@ // Enable G-Code viewer #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) #define ENABLE_GCODE_VIEWER_DEBUG_OUTPUT (0 && ENABLE_GCODE_VIEWER) -#define ENABLE_GCODE_VIEWER_GL_OPTIMIZATION (1 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 4b2784df98..579ef50a54 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -214,10 +214,8 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: } } -#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION // update buffers' render paths refresh_render_paths(); -#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.refresh_time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start_time).count(); @@ -533,7 +531,6 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) } } -#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION void GCodeViewer::refresh_render_paths() const { auto extrusion_color = [this](const Path& path) { @@ -589,35 +586,9 @@ void GCodeViewer::refresh_render_paths() const } } } -#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION void GCodeViewer::render_toolpaths() const { -#if !ENABLE_GCODE_VIEWER_GL_OPTIMIZATION - auto extrusion_color = [this](const Path& path) { - std::array color; - switch (m_view_type) - { - case EViewType::FeatureType: { color = Extrusion_Role_Colors[static_cast(path.role)]; break; } - case EViewType::Height: { color = m_extrusions.ranges.height.get_color_at(path.height); break; } - case EViewType::Width: { color = m_extrusions.ranges.width.get_color_at(path.width); break; } - case EViewType::Feedrate: { color = m_extrusions.ranges.feedrate.get_color_at(path.feedrate); break; } - case EViewType::FanSpeed: { color = m_extrusions.ranges.fan_speed.get_color_at(path.fan_speed); break; } - case EViewType::VolumetricRate: { color = m_extrusions.ranges.volumetric_rate.get_color_at(path.volumetric_rate); break; } - case EViewType::Tool: { color = m_tool_colors[path.extruder_id]; break; } - case EViewType::ColorPrint: { color = m_tool_colors[path.cp_color_id]; break; } - default: { color = { 1.0f, 1.0f, 1.0f }; break; } - } - return color; - }; - - auto travel_color = [this](const Path& path) { - return (path.delta_extruder < 0.0f) ? Travel_Colors[2] /* Retract */ : - ((path.delta_extruder > 0.0f) ? Travel_Colors[1] /* Extrude */ : - Travel_Colors[0] /* Move */); - }; -#endif // !ENABLE_GCODE_VIEWER_GL_OPTIMIZATION - auto set_color = [](GLint current_program_id, const std::array& color) { if (current_program_id > 0) { GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1; @@ -661,7 +632,6 @@ void GCodeViewer::render_toolpaths() const { case GCodeProcessor::EMoveType::Tool_change: { -#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION std::array color = { 1.0f, 1.0f, 1.0f }; set_color(current_program_id, color); for (const RenderPath& path : buffer.render_paths) @@ -674,27 +644,10 @@ void GCodeViewer::render_toolpaths() const ++m_statistics.gl_multi_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } -#else - std::array color = { 1.0f, 1.0f, 1.0f }; - set_color(current_program_id, color); - for (const Path& path : buffer.paths) { - if (!is_in_z_range(path)) - continue; - - glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last.id - path.first.id + 1), GL_UNSIGNED_INT, (const void*)(path.first.id * sizeof(GLuint)))); - glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); - -#if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.gl_points_calls_count; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - } -#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION break; } case GCodeProcessor::EMoveType::Color_change: { -#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION std::array color = { 1.0f, 0.0f, 0.0f }; set_color(current_program_id, color); for (const RenderPath& path : buffer.render_paths) @@ -707,27 +660,10 @@ void GCodeViewer::render_toolpaths() const ++m_statistics.gl_multi_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } -#else - std::array color = { 1.0f, 0.0f, 0.0f }; - set_color(current_program_id, color); - for (const Path& path : buffer.paths) { - if (!is_in_z_range(path)) - continue; - - glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last.id - path.first.id + 1), GL_UNSIGNED_INT, (const void*)(path.first.id * sizeof(GLuint)))); - glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); - -#if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.gl_points_calls_count; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - } -#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION break; } case GCodeProcessor::EMoveType::Pause_Print: { -#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION std::array color = { 0.0f, 1.0f, 0.0f }; set_color(current_program_id, color); for (const RenderPath& path : buffer.render_paths) @@ -740,27 +676,10 @@ void GCodeViewer::render_toolpaths() const ++m_statistics.gl_multi_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } -#else - std::array color = { 0.0f, 1.0f, 0.0f }; - set_color(current_program_id, color); - for (const Path& path : buffer.paths) { - if (!is_in_z_range(path)) - continue; - - glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last.id - path.first.id + 1), GL_UNSIGNED_INT, (const void*)(path.first.id * sizeof(GLuint)))); - glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); - -#if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.gl_points_calls_count; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - } -#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION break; } case GCodeProcessor::EMoveType::Custom_GCode: { -#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION std::array color = { 0.0f, 0.0f, 1.0f }; set_color(current_program_id, color); for (const RenderPath& path : buffer.render_paths) @@ -773,27 +692,10 @@ void GCodeViewer::render_toolpaths() const ++m_statistics.gl_multi_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } -#else - std::array color = { 0.0f, 0.0f, 1.0f }; - set_color(current_program_id, color); - for (const Path& path : buffer.paths) { - if (!is_in_z_range(path)) - continue; - - glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last.id - path.first.id + 1), GL_UNSIGNED_INT, (const void*)(path.first.id * sizeof(GLuint)))); - glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); - -#if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.gl_points_calls_count; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - } -#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION break; } case GCodeProcessor::EMoveType::Retract: { -#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION std::array color = { 1.0f, 0.0f, 1.0f }; set_color(current_program_id, color); for (const RenderPath& path : buffer.render_paths) @@ -806,27 +708,10 @@ void GCodeViewer::render_toolpaths() const ++m_statistics.gl_multi_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } -#else - std::array color = { 1.0f, 0.0f, 1.0f }; - set_color(current_program_id, color); - for (const Path& path : buffer.paths) { - if (!is_in_z_range(path)) - continue; - - glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last.id - path.first.id + 1), GL_UNSIGNED_INT, (const void*)(path.first.id * sizeof(GLuint)))); - glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); - -#if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.gl_points_calls_count; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - } -#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION break; } case GCodeProcessor::EMoveType::Unretract: { -#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION std::array color = { 0.0f, 1.0f, 1.0f }; set_color(current_program_id, color); for (const RenderPath& path : buffer.render_paths) @@ -839,27 +724,10 @@ void GCodeViewer::render_toolpaths() const ++m_statistics.gl_multi_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } -#else - std::array color = { 0.0f, 1.0f, 1.0f }; - set_color(current_program_id, color); - for (const Path& path : buffer.paths) { - if (!is_in_z_range(path)) - continue; - - glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last.id - path.first.id + 1), GL_UNSIGNED_INT, (const void*)(path.first.id * sizeof(GLuint)))); - glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); - -#if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.gl_points_calls_count; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - } -#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION break; } case GCodeProcessor::EMoveType::Extrude: { -#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION for (const RenderPath& path : buffer.render_paths) { set_color(current_program_id, path.color); @@ -869,24 +737,10 @@ void GCodeViewer::render_toolpaths() const #endif // ENABLE_GCODE_VIEWER_STATISTICS } -#else - for (const Path& path : buffer.paths) { - if (!is_visible(path) || !is_in_z_range(path)) - continue; - - set_color(current_program_id, extrusion_color(path)); - glsafe(::glDrawElements(GL_LINE_STRIP, GLsizei(path.last.id - path.first.id + 1), GL_UNSIGNED_INT, (const void*)(path.first.id * sizeof(GLuint)))); - -#if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.gl_line_strip_calls_count; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - } -#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION break; } case GCodeProcessor::EMoveType::Travel: { -#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION for (const RenderPath& path : buffer.render_paths) { set_color(current_program_id, path.color); @@ -896,19 +750,6 @@ void GCodeViewer::render_toolpaths() const #endif // ENABLE_GCODE_VIEWER_STATISTICS } -#else - for (const Path& path : buffer.paths) { - if (!is_in_z_range(path)) - continue; - - set_color(current_program_id, (m_view_type == EViewType::Feedrate || m_view_type == EViewType::Tool || m_view_type == EViewType::ColorPrint) ? extrusion_color(path) : travel_color(path)); - glsafe(::glDrawElements(GL_LINE_STRIP, GLsizei(path.last.id - path.first.id + 1), GL_UNSIGNED_INT, (const void*)(path.first.id * sizeof(GLuint)))); - -#if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.gl_line_strip_calls_count; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - } -#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION break; } } @@ -1021,10 +862,8 @@ void GCodeViewer::render_legend() const if (role < erCount) { m_extrusions.role_visibility_flags = is_visible(role) ? m_extrusions.role_visibility_flags & ~(1 << role) : m_extrusions.role_visibility_flags | (1 << role); -#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION // update buffers' render paths refresh_render_paths(); -#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); wxGetApp().plater()->update_preview_bottom_toolbar(); } @@ -1193,19 +1032,6 @@ void GCodeViewer::render_statistics() const ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(std::string("GL_POINTS calls:")); - ImGui::PopStyleColor(); - ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.gl_points_calls_count)); - - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(std::string("GL_LINE_STRIP calls:")); - ImGui::PopStyleColor(); - ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.gl_line_strip_calls_count)); - -#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); imgui.text(std::string("Multi GL_POINTS calls:")); ImGui::PopStyleColor(); @@ -1217,7 +1043,6 @@ void GCodeViewer::render_statistics() const ImGui::PopStyleColor(); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.gl_multi_line_strip_calls_count)); -#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION ImGui::Separator(); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 0f90be5d73..9af7e7cc48 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -61,7 +61,6 @@ class GCodeViewer } }; -#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION // Used to batch the indices needed to render paths struct RenderPath { @@ -69,7 +68,6 @@ class GCodeViewer std::vector sizes; std::vector offsets; // use size_t because we need the pointer's size (used in the call glMultiDrawElements()) }; -#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION // buffer containing indices data and shader for a specific toolpath type struct IBuffer @@ -79,9 +77,7 @@ class GCodeViewer std::vector data; size_t data_size{ 0 }; std::vector paths; -#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION std::vector render_paths; -#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION bool visible{ false }; void reset(); @@ -154,12 +150,8 @@ class GCodeViewer { long long load_time{ 0 }; long long refresh_time{ 0 }; - long long gl_points_calls_count{ 0 }; - long long gl_line_strip_calls_count{ 0 }; -#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION long long gl_multi_points_calls_count{ 0 }; long long gl_multi_line_strip_calls_count{ 0 }; -#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION long long results_size{ 0 }; long long vertices_size{ 0 }; long long vertices_gpu_size{ 0 }; @@ -178,12 +170,8 @@ class GCodeViewer } void reset_opengl() { - gl_points_calls_count = 0; - gl_line_strip_calls_count = 0; -#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION gl_multi_points_calls_count = 0; gl_multi_line_strip_calls_count = 0; -#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION } void reset_sizes() { @@ -262,15 +250,11 @@ public: void set_toolpath_role_visibility_flags(unsigned int flags) { m_extrusions.role_visibility_flags = flags; } unsigned int get_options_visibility_flags() const; void set_options_visibility_from_flags(unsigned int flags); -#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION void set_layers_z_range(const std::array& layers_z_range) { m_layers_z_range = layers_z_range; refresh_render_paths(); } -#else - void set_layers_z_range(const std::array& layers_z_range) { m_layers_z_range = layers_z_range; } -#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION bool is_legend_enabled() const { return m_legend_enabled; } void enable_legend(bool enable) { m_legend_enabled = enable; } @@ -279,9 +263,7 @@ private: bool init_shaders(); void load_toolpaths(const GCodeProcessor::Result& gcode_result); void load_shells(const Print& print, bool initialized); -#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION void refresh_render_paths() const; -#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION void render_toolpaths() const; void render_shells() const; void render_legend() const; From cd5d70d5e14561375f29fccc821fcaa55381d37b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 29 Apr 2020 10:18:29 +0200 Subject: [PATCH 053/503] GCodeViewer -> Fixed z slider in initial preview --- src/slic3r/GUI/3DScene.cpp | 3 --- src/slic3r/GUI/3DScene.hpp | 4 ---- src/slic3r/GUI/GLCanvas3D.cpp | 12 +++++++----- src/slic3r/GUI/GLCanvas3D.hpp | 3 ++- src/slic3r/GUI/GUI_Preview.cpp | 12 ++++++++---- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 63cacdd457..cac0910e23 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -340,8 +340,6 @@ BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d & bounding_box().transformed(trafo); } - -#if !ENABLE_GCODE_VIEWER void GLVolume::set_range(double min_z, double max_z) { this->qverts_range.first = 0; @@ -376,7 +374,6 @@ void GLVolume::set_range(double min_z, double max_z) } } } -#endif // !ENABLE_GCODE_VIEWER void GLVolume::render() const { diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 28295a35f7..70d6fb016a 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -442,9 +442,7 @@ public: bool empty() const { return this->indexed_vertex_array.empty(); } -#if !ENABLE_GCODE_VIEWER void set_range(double low, double high); -#endif // !ENABLE_GCODE_VIEWER void render() const; #if !ENABLE_SLOPE_RENDERING @@ -562,9 +560,7 @@ public: void clear() { for (auto *v : volumes) delete v; volumes.clear(); } bool empty() const { return volumes.empty(); } -#if !ENABLE_GCODE_VIEWER void set_range(double low, double high) { for (GLVolume *vol : this->volumes) vol->set_range(low, high); } -#endif // !ENABLE_GCODE_VIEWER void set_print_box(float min_x, float min_y, float min_z, float max_x, float max_y, float max_z) { m_print_box_min[0] = min_x; m_print_box_min[1] = min_y; m_print_box_min[2] = min_z; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index f8c0889fd6..6479947175 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -932,11 +932,7 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items( const GLCanvas3D std::vector> cp_values; cp_values.reserve(custom_gcode_per_print_z.size()); -#if ENABLE_GCODE_VIEWER - const std::vector& print_zs = canvas.get_layers_zs(); -#else std::vector print_zs = canvas.get_current_print_zs(true); -#endif // ENABLE_GCODE_VIEWER for (auto custom_code : custom_gcode_per_print_z) { if (custom_code.gcode != ColorChangeCode) @@ -2327,11 +2323,16 @@ void GLCanvas3D::ensure_on_bed(unsigned int object_idx) #if ENABLE_GCODE_VIEWER -const std::vector& GLCanvas3D::get_layers_zs() const +const std::vector& GLCanvas3D::get_gcode_layers_zs() const { return m_gcode_viewer.get_layers_zs(); } +std::vector GLCanvas3D::get_volumes_print_zs(bool active_only) const +{ + return m_volumes.get_current_print_zs(active_only); +} + void GLCanvas3D::set_gcode_options_visibility_from_flags(unsigned int flags) { m_gcode_viewer.set_options_visibility_from_flags(flags); @@ -2350,6 +2351,7 @@ void GLCanvas3D::set_toolpath_view_type(GCodeViewer::EViewType type) void GLCanvas3D::set_toolpaths_z_range(const std::array& range) { m_gcode_viewer.set_layers_z_range(range); + m_volumes.set_range(range[0] - 1e-6, range[1] + 1e-6); } #else std::vector GLCanvas3D::get_current_print_zs(bool active_only) const diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index a5066d923d..f057ea7d6f 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -648,7 +648,8 @@ public: #if ENABLE_GCODE_VIEWER bool is_gcode_legend_enabled() const { return m_gcode_viewer.is_legend_enabled(); } GCodeViewer::EViewType get_gcode_view_type() const { return m_gcode_viewer.get_view_type(); } - const std::vector& get_layers_zs() const; + const std::vector& get_gcode_layers_zs() const; + std::vector get_volumes_print_zs(bool active_only) const; unsigned int get_gcode_options_visibility_flags() const { return m_gcode_viewer.get_options_visibility_flags(); } void set_gcode_options_visibility_from_flags(unsigned int flags); unsigned int get_toolpath_role_visibility_flags() const { return m_gcode_viewer.get_toolpath_role_visibility_flags(); } diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 72f40bb6b7..7771484f5f 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -1057,6 +1057,10 @@ void Preview::load_print_as_fff(bool keep_z_range) if (IsShown()) { +#if ENABLE_GCODE_VIEWER + std::vector zs; +#endif // ENABLE_GCODE_VIEWER + m_canvas->set_selected_extruder(0); if (gcode_preview_data_valid) { // Load the real G-code preview. @@ -1065,6 +1069,7 @@ void Preview::load_print_as_fff(bool keep_z_range) m_canvas->refresh_gcode_preview(*m_gcode_result, colors); GetSizer()->Show(m_bottom_toolbar_sizer); GetSizer()->Layout(); + zs = m_canvas->get_gcode_layers_zs(); #else m_canvas->load_gcode_preview(*m_gcode_preview_data, colors); #endif // ENABLE_GCODE_VIEWER @@ -1075,14 +1080,13 @@ void Preview::load_print_as_fff(bool keep_z_range) #if ENABLE_GCODE_VIEWER GetSizer()->Hide(m_bottom_toolbar_sizer); GetSizer()->Layout(); + zs = m_canvas->get_volumes_print_zs(true); #endif // ENABLE_GCODE_VIEWER } -#if ENABLE_GCODE_VIEWER - const std::vector& zs = m_canvas->get_layers_zs(); -#else +#if !ENABLE_GCODE_VIEWER show_hide_ui_elements(gcode_preview_data_valid ? "full" : "simple"); std::vector zs = m_canvas->get_current_print_zs(true); -#endif // ENABLE_GCODE_VIEWER +#endif // !ENABLE_GCODE_VIEWER if (zs.empty()) { // all layers filtered out reset_sliders(true); From 9f2f798ea29b8efe00065c4621cd3eb41fe93652 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 29 Apr 2020 13:51:20 +0200 Subject: [PATCH 054/503] GCodeViewer -> Added ironing extrusion role --- src/slic3r/GUI/GCodeViewer.cpp | 1 + src/slic3r/GUI/GUI_Preview.cpp | 73 +++++++++++++++++----------------- 2 files changed, 38 insertions(+), 36 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 579ef50a54..df8e816460 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -127,6 +127,7 @@ const std::vector> GCodeViewer::Extrusion_Role_Colors {{ { 0.69f, 0.19f, 0.16f }, // erInternalInfill { 0.84f, 0.20f, 0.84f }, // erSolidInfill { 1.00f, 0.10f, 0.10f }, // erTopSolidInfill + { 0.00f, 1.00f, 1.00f }, // erIroning { 0.60f, 0.60f, 1.00f }, // erBridgeInfill { 1.00f, 1.00f, 1.00f }, // erGapFill { 0.52f, 0.48f, 0.13f }, // erSkirt diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 7771484f5f..ea7d2dc1bc 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -298,58 +298,59 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view m_double_slider_sizer = new wxBoxSizer(wxHORIZONTAL); create_double_slider(); - m_label_view_type = new wxStaticText(this, wxID_ANY, _(L("View"))); + m_label_view_type = new wxStaticText(this, wxID_ANY, _L("View")); m_choice_view_type = new wxChoice(this, wxID_ANY); - m_choice_view_type->Append(_(L("Feature type"))); - m_choice_view_type->Append(_(L("Height"))); - m_choice_view_type->Append(_(L("Width"))); - m_choice_view_type->Append(_(L("Speed"))); - m_choice_view_type->Append(_(L("Fan speed"))); - m_choice_view_type->Append(_(L("Volumetric flow rate"))); - m_choice_view_type->Append(_(L("Tool"))); - m_choice_view_type->Append(_(L("Color Print"))); + m_choice_view_type->Append(_L("Feature type")); + m_choice_view_type->Append(_L("Height")); + m_choice_view_type->Append(_L("Width")); + m_choice_view_type->Append(_L("Speed")); + m_choice_view_type->Append(_L("Fan speed")); + m_choice_view_type->Append(_L("Volumetric flow rate")); + m_choice_view_type->Append(_L("Tool")); + m_choice_view_type->Append(_L("Color Print")); m_choice_view_type->SetSelection(0); - m_label_show = new wxStaticText(this, wxID_ANY, _(L("Show"))); + m_label_show = new wxStaticText(this, wxID_ANY, _L("Show")); m_combochecklist_features = new wxComboCtrl(); - m_combochecklist_features->Create(this, wxID_ANY, _(L("Feature types")), wxDefaultPosition, wxDefaultSize, wxCB_READONLY); + m_combochecklist_features->Create(this, wxID_ANY, _L("Feature types"), wxDefaultPosition, wxDefaultSize, wxCB_READONLY); std::string feature_items = GUI::into_u8( #if ENABLE_GCODE_VIEWER _L("Unknown") + "|1|" + #endif // ENABLE_GCODE_VIEWER - _(L("Perimeter")) + "|1|" + - _(L("External perimeter")) + "|1|" + - _(L("Overhang perimeter")) + "|1|" + - _(L("Internal infill")) + "|1|" + - _(L("Solid infill")) + "|1|" + - _(L("Top solid infill")) + "|1|" + - _(L("Bridge infill")) + "|1|" + - _(L("Gap fill")) + "|1|" + - _(L("Skirt")) + "|1|" + - _(L("Support material")) + "|1|" + - _(L("Support material interface")) + "|1|" + - _(L("Wipe tower")) + "|1|" + - _(L("Custom")) + "|1" + _L("Perimeter") + "|1|" + + _L("External perimeter") + "|1|" + + _L("Overhang perimeter") + "|1|" + + _L("Internal infill") + "|1|" + + _L("Solid infill") + "|1|" + + _L("Top solid infill") + "|1|" + + _L("Ironing") + "|1|" + + _L("Bridge infill") + "|1|" + + _L("Gap fill") + "|1|" + + _L("Skirt") + "|1|" + + _L("Support material") + "|1|" + + _L("Support material interface") + "|1|" + + _L("Wipe tower") + "|1|" + + _L("Custom") + "|1" ); - Slic3r::GUI::create_combochecklist(m_combochecklist_features, GUI::into_u8(_(L("Feature types"))), feature_items); + Slic3r::GUI::create_combochecklist(m_combochecklist_features, GUI::into_u8(_L("Feature types")), feature_items); #if ENABLE_GCODE_VIEWER m_combochecklist_options = new wxComboCtrl(); - m_combochecklist_options->Create(this, wxID_ANY, _(L("Options")), wxDefaultPosition, wxDefaultSize, wxCB_READONLY); + m_combochecklist_options->Create(this, wxID_ANY, _L("Options"), wxDefaultPosition, wxDefaultSize, wxCB_READONLY); std::string options_items = GUI::into_u8( - _(L("Travel")) + "|0|" + - _(L("Retractions")) + "|0|" + - _(L("Unretractions")) + "|0|" + - _(L("Tool changes")) + "|0|" + - _(L("Color changes")) + "|0|" + - _(L("Pause prints")) + "|0|" + - _(L("Custom GCodes")) + "|0|" + - _(L("Shells")) + "|0|" + - _(L("Legend")) + "|1" + _L("Travel") + "|0|" + + _L("Retractions") + "|0|" + + _L("Unretractions") + "|0|" + + _L("Tool changes") + "|0|" + + _L("Color changes") + "|0|" + + _L("Pause prints") + "|0|" + + _L("Custom GCodes") + "|0|" + + _L("Shells") + "|0|" + + _L("Legend") + "|1" ); - Slic3r::GUI::create_combochecklist(m_combochecklist_options, GUI::into_u8(_(L("Options"))), options_items); + Slic3r::GUI::create_combochecklist(m_combochecklist_options, GUI::into_u8(_L("Options")), options_items); #else m_checkbox_travel = new wxCheckBox(this, wxID_ANY, _(L("Travel"))); m_checkbox_retractions = new wxCheckBox(this, wxID_ANY, _(L("Retractions"))); From c7806dd021ca523a5d0e8354450a2c0d8aee1d92 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 4 May 2020 09:37:06 +0200 Subject: [PATCH 055/503] GCodeViewer -> Fixed visualization of travel paths --- src/slic3r/GUI/GCodeViewer.cpp | 114 ++++++++++++++++++++++++++++----- src/slic3r/GUI/GCodeViewer.hpp | 13 ++-- src/slic3r/GUI/GUI_Preview.cpp | 2 + 3 files changed, 107 insertions(+), 22 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index df8e816460..0c631904aa 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -66,6 +66,30 @@ void GCodeViewer::VBuffer::reset() vertices_count = 0; } +bool GCodeViewer::Path::matches(const GCodeProcessor::MoveVertex& move) const +{ + switch (move.type) + { + case GCodeProcessor::EMoveType::Tool_change: + case GCodeProcessor::EMoveType::Color_change: + case GCodeProcessor::EMoveType::Pause_Print: + case GCodeProcessor::EMoveType::Custom_GCode: + case GCodeProcessor::EMoveType::Retract: + case GCodeProcessor::EMoveType::Unretract: + case GCodeProcessor::EMoveType::Extrude: + { + return type == move.type && role == move.extrusion_role && height == move.height && width == move.width && + feedrate == move.feedrate && fan_speed == move.fan_speed && volumetric_rate == move.volumetric_rate() && + extruder_id == move.extruder_id && cp_color_id == move.cp_color_id; + } + case GCodeProcessor::EMoveType::Travel: + { + return type == move.type && feedrate == move.feedrate && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id; + } + default: { return false; } + } +} + void GCodeViewer::IBuffer::reset() { // release gpu memory @@ -92,7 +116,7 @@ bool GCodeViewer::IBuffer::init_shader(const std::string& vertex_shader_src, con void GCodeViewer::IBuffer::add_path(const GCodeProcessor::MoveVertex& move) { - Path::Endpoint endpoint = { static_cast(data.size()), static_cast(move.position[2]) }; + Path::Endpoint endpoint = { static_cast(data.size()), move.position }; paths.push_back({ move.type, move.extrusion_role, endpoint, endpoint, move.delta_extruder, move.height, move.width, move.feedrate, move.fan_speed, move.volumetric_rate(), move.extruder_id, move.cp_color_id }); } @@ -410,10 +434,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) { if (prev.type != curr.type || !buffer.paths.back().matches(curr)) { buffer.add_path(curr); + buffer.paths.back().first.position = prev.position; buffer.data.push_back(static_cast(i - 1)); } - buffer.paths.back().last.id = static_cast(buffer.data.size()); + buffer.paths.back().last = { static_cast(buffer.data.size()), curr.position }; buffer.data.push_back(static_cast(i)); break; } @@ -424,6 +449,13 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) } } +#if ENABLE_GCODE_VIEWER_STATISTICS + for (IBuffer& buffer : m_buffers) + { + m_statistics.paths_size += SLIC3R_STDVEC_MEMSIZE(buffer.paths, Path); + } +#endif // ENABLE_GCODE_VIEWER_STATISTICS + // indices data -> send data to gpu for (IBuffer& buffer : m_buffers) { @@ -445,12 +477,16 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) } // layers zs / roles / extruder ids / cp color ids -> extract from result - for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) { + for (size_t i = 0; i < m_vertices.vertices_count; ++i) + { + const GCodeProcessor::MoveVertex& move = gcode_result.moves[i]; if (move.type == GCodeProcessor::EMoveType::Extrude) m_layers_zs.emplace_back(static_cast(move.position[2])); - m_roles.emplace_back(move.extrusion_role); m_extruder_ids.emplace_back(move.extruder_id); + + if (i > 0) + m_roles.emplace_back(move.extrusion_role); } // layers zs -> replace intervals of layers with similar top positions with their average value. @@ -560,9 +596,13 @@ void GCodeViewer::refresh_render_paths() const for (IBuffer& buffer : m_buffers) { buffer.render_paths = std::vector(); - for (const Path& path : buffer.paths) - { - if (!is_in_z_range(path)) + for (size_t i = 0; i < buffer.paths.size(); ++i) { + const Path& path = buffer.paths[i]; + if (path.type == GCodeProcessor::EMoveType::Travel) { + if (!is_travel_in_z_range(i)) + continue; + } + else if (!is_in_z_range(path)) continue; if (path.type == GCodeProcessor::EMoveType::Extrude && !is_visible(path)) @@ -576,8 +616,7 @@ void GCodeViewer::refresh_render_paths() const } auto it = std::find_if(buffer.render_paths.begin(), buffer.render_paths.end(), [color](const RenderPath& path) { return path.color == color; }); - if (it == buffer.render_paths.end()) - { + if (it == buffer.render_paths.end()) { it = buffer.render_paths.insert(buffer.render_paths.end(), RenderPath()); it->color = color; } @@ -1053,34 +1092,79 @@ void GCodeViewer::render_statistics() const ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.results_size) + " bytes"); + ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); imgui.text(std::string("Vertices CPU:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.vertices_size) + " bytes"); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(std::string("Vertices GPU:")); - ImGui::PopStyleColor(); - ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.vertices_gpu_size) + " bytes"); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); imgui.text(std::string("Indices CPU:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.indices_size) + " bytes"); + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(std::string("Paths CPU:")); + ImGui::PopStyleColor(); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.paths_size) + " bytes"); + + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(std::string("TOTAL CPU:")); + ImGui::PopStyleColor(); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.vertices_size + m_statistics.indices_size + m_statistics.paths_size) + " bytes"); + + ImGui::Separator(); + + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(std::string("Vertices GPU:")); + ImGui::PopStyleColor(); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.vertices_gpu_size) + " bytes"); + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); imgui.text(std::string("Indices GPU:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.indices_gpu_size) + " bytes"); + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(std::string("TOTAL GPU:")); + ImGui::PopStyleColor(); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.vertices_gpu_size + m_statistics.indices_gpu_size) + " bytes"); + imgui.end(); } #endif // ENABLE_GCODE_VIEWER_STATISTICS +bool GCodeViewer::is_travel_in_z_range(size_t id) const +{ + const IBuffer& buffer = m_buffers[buffer_id(GCodeProcessor::EMoveType::Travel)]; + if (id >= buffer.paths.size()) + return false; + + Path path = buffer.paths[id]; + int first = static_cast(id); + unsigned int last = static_cast(id); + + // check adjacent paths + while (first > 0 && path.first.position.isApprox(buffer.paths[first - 1].last.position)) { + --first; + path.first = buffer.paths[first].first; + } + while (last < static_cast(buffer.paths.size() - 1) && path.last.position.isApprox(buffer.paths[last + 1].first.position)) { + ++last; + path.last = buffer.paths[last].last; + } + + return is_in_z_range(path); +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 9af7e7cc48..aa024a742c 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -38,7 +38,7 @@ class GCodeViewer struct Endpoint { unsigned int id{ 0u }; - double z{ 0.0 }; + Vec3f position{ Vec3f::Zero() }; }; GCodeProcessor::EMoveType type{ GCodeProcessor::EMoveType::Noop }; @@ -54,11 +54,7 @@ class GCodeViewer unsigned char extruder_id{ 0 }; unsigned char cp_color_id{ 0 }; - bool matches(const GCodeProcessor::MoveVertex& move) const { - return type == move.type && role == move.extrusion_role && height == move.height && width == move.width && - feedrate == move.feedrate && fan_speed == move.fan_speed && volumetric_rate == move.volumetric_rate() && - extruder_id == move.extruder_id && cp_color_id == move.cp_color_id; - } + bool matches(const GCodeProcessor::MoveVertex& move) const; }; // Used to batch the indices needed to render paths @@ -157,6 +153,7 @@ class GCodeViewer long long vertices_gpu_size{ 0 }; long long indices_size{ 0 }; long long indices_gpu_size{ 0 }; + long long paths_size{ 0 }; void reset_all() { reset_times(); @@ -180,6 +177,7 @@ class GCodeViewer vertices_gpu_size = 0; indices_size = 0; indices_gpu_size = 0; + paths_size = 0; } }; #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -279,8 +277,9 @@ private: return z > m_layers_z_range[0] - EPSILON && z < m_layers_z_range[1] + EPSILON; }; - return in_z_range(path.first.z) || in_z_range(path.last.z); + return in_z_range(path.first.position[2]) || in_z_range(path.last.position[2]); } + bool is_travel_in_z_range(size_t id) const; }; } // namespace GUI diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index ea7d2dc1bc..22b3d2c253 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -405,6 +405,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view bind_event_handlers(); +#if !ENABLE_GCODE_VIEWER // sets colors for gcode preview extrusion roles std::vector extrusion_roles_colors = { "Perimeter", "FFFF66", @@ -422,6 +423,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view "Custom", "28CC94" }; m_gcode_preview_data->set_extrusion_paths_colors(extrusion_roles_colors); +#endif // !ENABLE_GCODE_VIEWER return true; } From 170f91b3353d495c9d602fa5f4dce7f94ecde6f5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 5 May 2020 12:09:11 +0200 Subject: [PATCH 056/503] GCodeViewer -> Prototype for sequential view --- src/slic3r/GUI/GCodeViewer.cpp | 208 +++++++++++++++++++++++++-------- src/slic3r/GUI/GCodeViewer.hpp | 43 +++++-- src/slic3r/GUI/GLCanvas3D.cpp | 3 +- 3 files changed, 197 insertions(+), 57 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 0c631904aa..dc7c0d9731 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -10,6 +10,7 @@ #include "GUI_Utils.hpp" #include "DoubleSlider.hpp" #include "GLCanvas3D.hpp" +#include "GLToolbar.hpp" #include "libslic3r/Model.hpp" #if ENABLE_GCODE_VIEWER_STATISTICS #include @@ -33,10 +34,10 @@ static GCodeProcessor::EMoveType buffer_type(unsigned char id) { return static_cast(static_cast(GCodeProcessor::EMoveType::Retract) + id); } -std::vector> decode_colors(const std::vector& colors) { +std::vector> decode_colors(const std::vector & colors) { static const float INV_255 = 1.0f / 255.0f; - std::vector> output(colors.size(), {0.0f, 0.0f, 0.0f} ); + std::vector> output(colors.size(), { 0.0f, 0.0f, 0.0f }); for (size_t i = 0; i < colors.size(); ++i) { const std::string& color = colors[i]; @@ -102,6 +103,7 @@ void GCodeViewer::IBuffer::reset() data = std::vector(); data_size = 0; paths = std::vector(); + render_paths = std::vector(); } bool GCodeViewer::IBuffer::init_shader(const std::string& vertex_shader_src, const std::string& fragment_shader_src) @@ -114,13 +116,13 @@ bool GCodeViewer::IBuffer::init_shader(const std::string& vertex_shader_src, con return true; } -void GCodeViewer::IBuffer::add_path(const GCodeProcessor::MoveVertex& move) +void GCodeViewer::IBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsigned int v_id) { - Path::Endpoint endpoint = { static_cast(data.size()), move.position }; + Path::Endpoint endpoint = { static_cast(data.size()), v_id, move.position }; paths.push_back({ move.type, move.extrusion_role, endpoint, endpoint, move.delta_extruder, move.height, move.width, move.feedrate, move.fan_speed, move.volumetric_rate(), move.extruder_id, move.cp_color_id }); } -std::array GCodeViewer::Extrusions::Range::get_color_at(float value) const +GCodeViewer::Color GCodeViewer::Extrusions::Range::get_color_at(float value) const { // Input value scaled to the colors range const float step = step_size(); @@ -136,14 +138,14 @@ std::array GCodeViewer::Extrusions::Range::get_color_at(float value) c const float local_t = std::clamp(global_t - static_cast(color_low_idx), 0.0f, 1.0f); // Interpolate between the low and high colors to find exactly which color the input value should get - std::array ret; + Color ret; for (unsigned int i = 0; i < 3; ++i) { ret[i] = lerp(Range_Colors[color_low_idx][i], Range_Colors[color_high_idx][i], local_t); } return ret; } -const std::vector> GCodeViewer::Extrusion_Role_Colors {{ +const std::vector GCodeViewer::Extrusion_Role_Colors{ { { 0.50f, 0.50f, 0.50f }, // erNone { 1.00f, 1.00f, 0.40f }, // erPerimeter { 1.00f, 0.65f, 0.00f }, // erExternalPerimeter @@ -162,13 +164,13 @@ const std::vector> GCodeViewer::Extrusion_Role_Colors {{ { 0.00f, 0.00f, 0.00f } // erMixed }}; -const std::vector> GCodeViewer::Travel_Colors {{ +const std::vector GCodeViewer::Travel_Colors{ { { 0.0f, 0.0f, 0.5f }, // Move { 0.0f, 0.5f, 0.0f }, // Extrude { 0.5f, 0.0f, 0.0f } // Retract }}; -const std::vector> GCodeViewer::Range_Colors {{ +const std::vector GCodeViewer::Range_Colors{ { { 0.043f, 0.173f, 0.478f }, // bluish { 0.075f, 0.349f, 0.522f }, { 0.110f, 0.533f, 0.569f }, @@ -240,7 +242,7 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: } // update buffers' render paths - refresh_render_paths(); + refresh_render_paths(false); #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.refresh_time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start_time).count(); @@ -256,7 +258,7 @@ void GCodeViewer::reset() } m_bounding_box = BoundingBoxf3(); - m_tool_colors = std::vector>(); + m_tool_colors = std::vector(); m_extruder_ids = std::vector(); m_extrusions.reset_role_visibility_flags(); m_extrusions.reset_ranges(); @@ -280,6 +282,7 @@ void GCodeViewer::render() const render_toolpaths(); render_shells(); render_legend(); + render_sequential_dlg(); #if ENABLE_GCODE_VIEWER_STATISTICS render_statistics(); #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -425,7 +428,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) case GCodeProcessor::EMoveType::Retract: case GCodeProcessor::EMoveType::Unretract: { - buffer.add_path(curr); + buffer.add_path(curr, static_cast(i)); buffer.data.push_back(static_cast(i)); break; } @@ -433,12 +436,14 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) case GCodeProcessor::EMoveType::Travel: { if (prev.type != curr.type || !buffer.paths.back().matches(curr)) { - buffer.add_path(curr); - buffer.paths.back().first.position = prev.position; + buffer.add_path(curr, static_cast(i)); + Path& last_path = buffer.paths.back(); + last_path.first.position = prev.position; + last_path.first.s_id = static_cast(i - 1); buffer.data.push_back(static_cast(i - 1)); } - buffer.paths.back().last = { static_cast(buffer.data.size()), curr.position }; + buffer.paths.back().last = { static_cast(buffer.data.size()), static_cast(i), curr.position }; buffer.data.push_back(static_cast(i)); break; } @@ -568,10 +573,10 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) } } -void GCodeViewer::refresh_render_paths() const +void GCodeViewer::refresh_render_paths(bool keep_sequential_current) const { auto extrusion_color = [this](const Path& path) { - std::array color; + Color color; switch (m_view_type) { case EViewType::FeatureType: { color = Extrusion_Role_Colors[static_cast(path.role)]; break; } @@ -593,26 +598,86 @@ void GCodeViewer::refresh_render_paths() const Travel_Colors[0] /* Move */); }; + auto is_valid_path = [this](const Path& path, size_t id) { + if (path.type == GCodeProcessor::EMoveType::Travel) { + if (!is_travel_in_z_range(id)) + return false; + } + else if (!is_in_z_range(path)) + return false; + + if (path.type == GCodeProcessor::EMoveType::Extrude && !is_visible(path)) + return false; + + return true; + }; + +#if ENABLE_GCODE_VIEWER_STATISTICS + m_statistics.render_paths_size = 0; +#endif // ENABLE_GCODE_VIEWER_STATISTICS + + m_sequential_view.first = m_vertices.vertices_count; + m_sequential_view.last = 0; + if (!keep_sequential_current) + m_sequential_view.current = m_vertices.vertices_count; + + // first, detect current values for the sequential view + // to be used later to filter the paths + for (IBuffer& buffer : m_buffers) { + if (!buffer.visible) + continue; + + for (size_t i = 0; i < buffer.paths.size(); ++i) { + const Path& path = buffer.paths[i]; + if (!is_valid_path(path, i)) + continue; +// if (path.type == GCodeProcessor::EMoveType::Travel) { +// if (!is_travel_in_z_range(i)) +// continue; +// } +// else if (!is_in_z_range(path)) +// continue; +// +// if (path.type == GCodeProcessor::EMoveType::Extrude && !is_visible(path)) +// continue; + + m_sequential_view.first = std::min(m_sequential_view.first, path.first.s_id); + m_sequential_view.last = std::max(m_sequential_view.last, path.last.s_id); + } + } + + if (keep_sequential_current) + m_sequential_view.clamp_current(); + else + m_sequential_view.current = m_sequential_view.last; for (IBuffer& buffer : m_buffers) { buffer.render_paths = std::vector(); + if (!buffer.visible) + continue; for (size_t i = 0; i < buffer.paths.size(); ++i) { const Path& path = buffer.paths[i]; - if (path.type == GCodeProcessor::EMoveType::Travel) { - if (!is_travel_in_z_range(i)) - continue; - } - else if (!is_in_z_range(path)) + if (!is_valid_path(path, i)) + continue; +// if (path.type == GCodeProcessor::EMoveType::Travel) { +// if (!is_travel_in_z_range(i)) +// continue; +// } +// else if (!is_in_z_range(path)) +// continue; +// +// if (path.type == GCodeProcessor::EMoveType::Extrude && !is_visible(path)) +// continue; + + if ((m_sequential_view.current < path.first.s_id) || (path.last.s_id < m_sequential_view.first)) continue; - if (path.type == GCodeProcessor::EMoveType::Extrude && !is_visible(path)) - continue; - - std::array color = { 0.0f, 0.0f, 0.0f }; + Color color; switch (path.type) { case GCodeProcessor::EMoveType::Extrude: { color = extrusion_color(path); break; } case GCodeProcessor::EMoveType::Travel: { color = (m_view_type == EViewType::Feedrate || m_view_type == EViewType::Tool || m_view_type == EViewType::ColorPrint) ? extrusion_color(path) : travel_color(path); break; } + default: { color = { 0.0f, 0.0f, 0.0f }; break; } } auto it = std::find_if(buffer.render_paths.begin(), buffer.render_paths.end(), [color](const RenderPath& path) { return path.color == color; }); @@ -621,15 +686,25 @@ void GCodeViewer::refresh_render_paths() const it->color = color; } - it->sizes.push_back(path.last.id - path.first.id + 1); - it->offsets.push_back(static_cast(path.first.id * sizeof(unsigned int))); + it->sizes.push_back(std::min(m_sequential_view.current, path.last.s_id) - path.first.s_id + 1); + it->offsets.push_back(static_cast(path.first.i_id * sizeof(unsigned int))); } } + +#if ENABLE_GCODE_VIEWER_STATISTICS + for (const IBuffer& buffer : m_buffers) { + m_statistics.render_paths_size += SLIC3R_STDVEC_MEMSIZE(buffer.render_paths, RenderPath); + for (const RenderPath& path : buffer.render_paths) { + m_statistics.render_paths_size += SLIC3R_STDVEC_MEMSIZE(path.sizes, unsigned int); + m_statistics.render_paths_size += SLIC3R_STDVEC_MEMSIZE(path.offsets, size_t); + } + } +#endif // ENABLE_GCODE_VIEWER_STATISTICS } void GCodeViewer::render_toolpaths() const { - auto set_color = [](GLint current_program_id, const std::array& color) { + auto set_color = [](GLint current_program_id, const Color& color) { if (current_program_id > 0) { GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1; if (color_id >= 0) { @@ -672,7 +747,7 @@ void GCodeViewer::render_toolpaths() const { case GCodeProcessor::EMoveType::Tool_change: { - std::array color = { 1.0f, 1.0f, 1.0f }; + Color color = { 1.0f, 1.0f, 1.0f }; set_color(current_program_id, color); for (const RenderPath& path : buffer.render_paths) { @@ -688,7 +763,7 @@ void GCodeViewer::render_toolpaths() const } case GCodeProcessor::EMoveType::Color_change: { - std::array color = { 1.0f, 0.0f, 0.0f }; + Color color = { 1.0f, 0.0f, 0.0f }; set_color(current_program_id, color); for (const RenderPath& path : buffer.render_paths) { @@ -704,7 +779,7 @@ void GCodeViewer::render_toolpaths() const } case GCodeProcessor::EMoveType::Pause_Print: { - std::array color = { 0.0f, 1.0f, 0.0f }; + Color color = { 0.0f, 1.0f, 0.0f }; set_color(current_program_id, color); for (const RenderPath& path : buffer.render_paths) { @@ -720,7 +795,7 @@ void GCodeViewer::render_toolpaths() const } case GCodeProcessor::EMoveType::Custom_GCode: { - std::array color = { 0.0f, 0.0f, 1.0f }; + Color color = { 0.0f, 0.0f, 1.0f }; set_color(current_program_id, color); for (const RenderPath& path : buffer.render_paths) { @@ -736,7 +811,7 @@ void GCodeViewer::render_toolpaths() const } case GCodeProcessor::EMoveType::Retract: { - std::array color = { 1.0f, 0.0f, 1.0f }; + Color color = { 1.0f, 0.0f, 1.0f }; set_color(current_program_id, color); for (const RenderPath& path : buffer.render_paths) { @@ -752,7 +827,7 @@ void GCodeViewer::render_toolpaths() const } case GCodeProcessor::EMoveType::Unretract: { - std::array color = { 0.0f, 1.0f, 1.0f }; + Color color = { 0.0f, 1.0f, 1.0f }; set_color(current_program_id, color); for (const RenderPath& path : buffer.render_paths) { @@ -827,13 +902,14 @@ void GCodeViewer::render_legend() const ImGuiWrapper& imgui = *wxGetApp().imgui(); - imgui.set_next_window_pos(0, 0, ImGuiCond_Always); + imgui.set_next_window_pos(0.0f, 0.0f, ImGuiCond_Always); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::SetNextWindowBgAlpha(0.6f); imgui.begin(std::string("Legend"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); ImDrawList* draw_list = ImGui::GetWindowDrawList(); - auto add_item = [draw_list, &imgui](const std::array& color, const std::string& label, std::function callback = nullptr) { + auto add_item = [draw_list, &imgui](const Color& color, const std::string& label, std::function callback = nullptr) { float icon_size = ImGui::GetTextLineHeight(); ImVec2 pos = ImGui::GetCursorPos(); draw_list->AddRect({ pos.x, pos.y }, { pos.x + icon_size, pos.y + icon_size }, ICON_BORDER_COLOR, 0.0f, 0); @@ -903,7 +979,7 @@ void GCodeViewer::render_legend() const { m_extrusions.role_visibility_flags = is_visible(role) ? m_extrusions.role_visibility_flags & ~(1 << role) : m_extrusions.role_visibility_flags | (1 << role); // update buffers' render paths - refresh_render_paths(); + refresh_render_paths(false); wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); wxGetApp().plater()->update_preview_bottom_toolbar(); } @@ -1044,6 +1120,51 @@ void GCodeViewer::render_legend() const ImGui::PopStyleVar(); } +void GCodeViewer::render_sequential_dlg() const +{ + static const float margin = 125.0f; + + if (m_roles.empty()) + return; + + if (m_sequential_view.last <= m_sequential_view.first) + return; + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + const ImGuiStyle& style = ImGui::GetStyle(); + + const GLToolbar& view_toolbar = wxGetApp().plater()->get_view_toolbar(); + Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size(); + + float left = view_toolbar.get_width(); + float width = static_cast(cnv_size.get_width()) - left; + + ImGui::SetNextWindowBgAlpha(0.5f); + imgui.set_next_window_pos(left, static_cast(cnv_size.get_height()), ImGuiCond_Always, 0.0f, 1.0f); + ImGui::SetNextWindowSize({ width, -1.0f }, ImGuiCond_Always); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + imgui.begin(std::string("Sequential"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); + + std::string low_str = std::to_string(m_sequential_view.first); + ImGui::SetCursorPosX(margin - style.ItemSpacing.x - ImGui::CalcTextSize(low_str.c_str()).x); + ImGui::AlignTextToFramePadding(); + imgui.text(low_str); + ImGui::SameLine(margin); + ImGui::PushItemWidth(-margin); + int index = static_cast(m_sequential_view.current); + if (ImGui::SliderInt("##slider int", &index, static_cast(m_sequential_view.first), static_cast(m_sequential_view.last))) + { + m_sequential_view.current = static_cast(index); + refresh_render_paths(true); + } + ImGui::PopItemWidth(); + ImGui::SameLine(); + imgui.text(std::to_string(m_sequential_view.last)); + + imgui.end(); + ImGui::PopStyleVar(); +} + #if ENABLE_GCODE_VIEWER_STATISTICS void GCodeViewer::render_statistics() const { @@ -1055,6 +1176,7 @@ void GCodeViewer::render_statistics() const ImGuiWrapper& imgui = *wxGetApp().imgui(); + imgui.set_next_window_pos(0.5f * wxGetApp().plater()->get_current_canvas3D()->get_canvas_size().get_width(), 0.0f, ImGuiCond_Once, 0.5f, 0.0f); imgui.begin(std::string("Statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); @@ -1113,10 +1235,10 @@ void GCodeViewer::render_statistics() const imgui.text(std::to_string(m_statistics.paths_size) + " bytes"); ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(std::string("TOTAL CPU:")); + imgui.text(std::string("Render paths CPU:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.vertices_size + m_statistics.indices_size + m_statistics.paths_size) + " bytes"); + imgui.text(std::to_string(m_statistics.render_paths_size) + " bytes"); ImGui::Separator(); @@ -1132,12 +1254,6 @@ void GCodeViewer::render_statistics() const ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.indices_gpu_size) + " bytes"); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(std::string("TOTAL GPU:")); - ImGui::PopStyleColor(); - ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.vertices_gpu_size + m_statistics.indices_gpu_size) + " bytes"); - imgui.end(); } #endif // ENABLE_GCODE_VIEWER_STATISTICS diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index aa024a742c..5e4ea53964 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -14,9 +14,10 @@ namespace GUI { class GCodeViewer { - static const std::vector> Extrusion_Role_Colors; - static const std::vector> Travel_Colors; - static const std::vector> Range_Colors; + using Color = std::array; + static const std::vector Extrusion_Role_Colors; + static const std::vector Travel_Colors; + static const std::vector Range_Colors; // buffer containing vertices data struct VBuffer @@ -37,7 +38,10 @@ class GCodeViewer { struct Endpoint { - unsigned int id{ 0u }; + // index into the ibo + unsigned int i_id{ 0u }; + // sequential id + unsigned int s_id{ 0u }; Vec3f position{ Vec3f::Zero() }; }; @@ -55,12 +59,14 @@ class GCodeViewer unsigned char cp_color_id{ 0 }; bool matches(const GCodeProcessor::MoveVertex& move) const; + + unsigned int size() const { return last.i_id - first.i_id + 1; } }; // Used to batch the indices needed to render paths struct RenderPath { - std::array color; + Color color; std::vector sizes; std::vector offsets; // use size_t because we need the pointer's size (used in the call glMultiDrawElements()) }; @@ -78,9 +84,10 @@ class GCodeViewer void reset(); bool init_shader(const std::string& vertex_shader_src, const std::string& fragment_shader_src); - void add_path(const GCodeProcessor::MoveVertex& move); + void add_path(const GCodeProcessor::MoveVertex& move, unsigned int s_id); }; + struct Shells { GLVolumeCollection volumes; @@ -102,7 +109,7 @@ class GCodeViewer void reset() { min = FLT_MAX; max = -FLT_MAX; } float step_size() const { return (max - min) / (static_cast(Range_Colors.size()) - 1.0f); } - std::array get_color_at(float value) const; + Color get_color_at(float value) const; }; struct Ranges @@ -141,6 +148,15 @@ class GCodeViewer void reset_ranges() { ranges.reset(); } }; + struct SequentialView + { + unsigned int first{ 0 }; + unsigned int last{ 0 }; + unsigned int current{ 0 }; + + void clamp_current() { current = std::clamp(current, first, last); } + }; + #if ENABLE_GCODE_VIEWER_STATISTICS struct Statistics { @@ -154,6 +170,7 @@ class GCodeViewer long long indices_size{ 0 }; long long indices_gpu_size{ 0 }; long long paths_size{ 0 }; + long long render_paths_size{ 0 }; void reset_all() { reset_times(); @@ -178,6 +195,7 @@ class GCodeViewer indices_size = 0; indices_gpu_size = 0; paths_size = 0; + render_paths_size = 0; } }; #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -201,12 +219,13 @@ private: VBuffer m_vertices; mutable std::vector m_buffers{ static_cast(GCodeProcessor::EMoveType::Extrude) }; BoundingBoxf3 m_bounding_box; - std::vector> m_tool_colors; + std::vector m_tool_colors; std::vector m_layers_zs; std::array m_layers_z_range; std::vector m_roles; std::vector m_extruder_ids; mutable Extrusions m_extrusions; + mutable SequentialView m_sequential_view; Shells m_shells; EViewType m_view_type{ EViewType::FeatureType }; bool m_legend_enabled{ true }; @@ -231,6 +250,8 @@ public: void reset(); void render() const; + bool has_data() const { return !m_roles.empty(); } + const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; } const std::vector& get_layers_zs() const { return m_layers_zs; }; @@ -250,8 +271,9 @@ public: void set_options_visibility_from_flags(unsigned int flags); void set_layers_z_range(const std::array& layers_z_range) { + bool keep_sequential_current = layers_z_range[1] <= m_layers_z_range[1]; m_layers_z_range = layers_z_range; - refresh_render_paths(); + refresh_render_paths(keep_sequential_current); } bool is_legend_enabled() const { return m_legend_enabled; } @@ -261,10 +283,11 @@ private: bool init_shaders(); void load_toolpaths(const GCodeProcessor::Result& gcode_result); void load_shells(const Print& print, bool initialized); - void refresh_render_paths() const; + void refresh_render_paths(bool keep_sequential_current) const; void render_toolpaths() const; void render_shells() const; void render_legend() const; + void render_sequential_dlg() const; #if ENABLE_GCODE_VIEWER_STATISTICS void render_statistics() const; #endif // ENABLE_GCODE_VIEWER_STATISTICS diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 6881e86fd5..875e4fd525 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2363,8 +2363,9 @@ void GLCanvas3D::set_toolpath_view_type(GCodeViewer::EViewType type) void GLCanvas3D::set_toolpaths_z_range(const std::array& range) { - m_gcode_viewer.set_layers_z_range(range); m_volumes.set_range(range[0] - 1e-6, range[1] + 1e-6); + if (m_gcode_viewer.has_data()) + m_gcode_viewer.set_layers_z_range(range); } #else std::vector GLCanvas3D::get_current_print_zs(bool active_only) const From a84c4347872eb009d88296e053e930975973b91e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 5 May 2020 13:57:51 +0200 Subject: [PATCH 057/503] GCodeViewer -> Refactoring/Optimization --- src/slic3r/GUI/GCodeViewer.cpp | 125 ++++++++++++++------------------- src/slic3r/GUI/GCodeViewer.hpp | 10 ++- src/slic3r/GUI/GUI_Preview.cpp | 1 + 3 files changed, 59 insertions(+), 77 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index dc7c0d9731..3323e784cd 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -385,12 +385,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) return; // vertex data / bounding box -> extract from result - std::vector vertices_data; - for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) { - for (int j = 0; j < 3; ++j) { - vertices_data.insert(vertices_data.end(), move.position[j]); - m_bounding_box.merge(move.position.cast()); - } + std::vector vertices_data(m_vertices.vertices_count * 3); + for (size_t i = 0; i < m_vertices.vertices_count; ++i) { + const GCodeProcessor::MoveVertex& move = gcode_result.moves[i]; + m_bounding_box.merge(move.position.cast()); + ::memcpy(static_cast(&vertices_data[i * 3]), static_cast(move.position.data()), 3 * sizeof(float)); } #if ENABLE_GCODE_VIEWER_STATISTICS @@ -575,6 +574,10 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) void GCodeViewer::refresh_render_paths(bool keep_sequential_current) const { +#if ENABLE_GCODE_VIEWER_STATISTICS + auto start_time = std::chrono::high_resolution_clock::now(); +#endif // ENABLE_GCODE_VIEWER_STATISTICS + auto extrusion_color = [this](const Path& path) { Color color; switch (m_view_type) @@ -598,20 +601,6 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current) const Travel_Colors[0] /* Move */); }; - auto is_valid_path = [this](const Path& path, size_t id) { - if (path.type == GCodeProcessor::EMoveType::Travel) { - if (!is_travel_in_z_range(id)) - return false; - } - else if (!is_in_z_range(path)) - return false; - - if (path.type == GCodeProcessor::EMoveType::Extrude && !is_visible(path)) - return false; - - return true; - }; - #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.render_paths_size = 0; #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -621,74 +610,60 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current) const if (!keep_sequential_current) m_sequential_view.current = m_vertices.vertices_count; - // first, detect current values for the sequential view - // to be used later to filter the paths + // first pass: collect visible paths and update sequential view data + std::vector> paths; for (IBuffer& buffer : m_buffers) { + // reset render paths + buffer.render_paths = std::vector(); + if (!buffer.visible) continue; for (size_t i = 0; i < buffer.paths.size(); ++i) { const Path& path = buffer.paths[i]; - if (!is_valid_path(path, i)) + if (path.type == GCodeProcessor::EMoveType::Travel) { + if (!is_travel_in_z_range(i)) + continue; + } + else if (!is_in_z_range(path)) continue; -// if (path.type == GCodeProcessor::EMoveType::Travel) { -// if (!is_travel_in_z_range(i)) -// continue; -// } -// else if (!is_in_z_range(path)) -// continue; -// -// if (path.type == GCodeProcessor::EMoveType::Extrude && !is_visible(path)) -// continue; + + if (path.type == GCodeProcessor::EMoveType::Extrude && !is_visible(path)) + continue; + + // store valid path + paths.push_back({ &buffer, i }); m_sequential_view.first = std::min(m_sequential_view.first, path.first.s_id); m_sequential_view.last = std::max(m_sequential_view.last, path.last.s_id); } } - if (keep_sequential_current) - m_sequential_view.clamp_current(); - else - m_sequential_view.current = m_sequential_view.last; + // update current sequential position + m_sequential_view.current = keep_sequential_current ? std::clamp(m_sequential_view.current, m_sequential_view.first, m_sequential_view.last) : m_sequential_view.last; - for (IBuffer& buffer : m_buffers) { - buffer.render_paths = std::vector(); - if (!buffer.visible) + // second pass: filter paths by sequential data + for (auto&& [buffer, id] : paths) { + const Path& path = buffer->paths[id]; + if ((m_sequential_view.current < path.first.s_id) || (path.last.s_id < m_sequential_view.first)) continue; - for (size_t i = 0; i < buffer.paths.size(); ++i) { - const Path& path = buffer.paths[i]; - if (!is_valid_path(path, i)) - continue; -// if (path.type == GCodeProcessor::EMoveType::Travel) { -// if (!is_travel_in_z_range(i)) -// continue; -// } -// else if (!is_in_z_range(path)) -// continue; -// -// if (path.type == GCodeProcessor::EMoveType::Extrude && !is_visible(path)) -// continue; - if ((m_sequential_view.current < path.first.s_id) || (path.last.s_id < m_sequential_view.first)) - continue; - - Color color; - switch (path.type) - { - case GCodeProcessor::EMoveType::Extrude: { color = extrusion_color(path); break; } - case GCodeProcessor::EMoveType::Travel: { color = (m_view_type == EViewType::Feedrate || m_view_type == EViewType::Tool || m_view_type == EViewType::ColorPrint) ? extrusion_color(path) : travel_color(path); break; } - default: { color = { 0.0f, 0.0f, 0.0f }; break; } - } - - auto it = std::find_if(buffer.render_paths.begin(), buffer.render_paths.end(), [color](const RenderPath& path) { return path.color == color; }); - if (it == buffer.render_paths.end()) { - it = buffer.render_paths.insert(buffer.render_paths.end(), RenderPath()); - it->color = color; - } - - it->sizes.push_back(std::min(m_sequential_view.current, path.last.s_id) - path.first.s_id + 1); - it->offsets.push_back(static_cast(path.first.i_id * sizeof(unsigned int))); + Color color; + switch (path.type) + { + case GCodeProcessor::EMoveType::Extrude: { color = extrusion_color(path); break; } + case GCodeProcessor::EMoveType::Travel: { color = (m_view_type == EViewType::Feedrate || m_view_type == EViewType::Tool || m_view_type == EViewType::ColorPrint) ? extrusion_color(path) : travel_color(path); break; } + default: { color = { 0.0f, 0.0f, 0.0f }; break; } } + + auto it = std::find_if(buffer->render_paths.begin(), buffer->render_paths.end(), [color](const RenderPath& path) { return path.color == color; }); + if (it == buffer->render_paths.end()) { + it = buffer->render_paths.insert(buffer->render_paths.end(), RenderPath()); + it->color = color; + } + + it->sizes.push_back(std::min(m_sequential_view.current, path.last.s_id) - path.first.s_id + 1); + it->offsets.push_back(static_cast(path.first.i_id * sizeof(unsigned int))); } #if ENABLE_GCODE_VIEWER_STATISTICS @@ -699,6 +674,8 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current) const m_statistics.render_paths_size += SLIC3R_STDVEC_MEMSIZE(path.offsets, size_t); } } + + m_statistics.refresh_paths_time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start_time).count(); #endif // ENABLE_GCODE_VIEWER_STATISTICS } @@ -1192,6 +1169,12 @@ void GCodeViewer::render_statistics() const ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.refresh_time) + "ms"); + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(std::string("Resfresh paths time:")); + ImGui::PopStyleColor(); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.refresh_paths_time) + "ms"); + ImGui::Separator(); ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 5e4ea53964..3e73cb5d68 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -38,9 +38,9 @@ class GCodeViewer { struct Endpoint { - // index into the ibo + // index into the buffer indices ibo unsigned int i_id{ 0u }; - // sequential id + // sequential id (same as index into the vertices vbo) unsigned int s_id{ 0u }; Vec3f position{ Vec3f::Zero() }; }; @@ -59,8 +59,6 @@ class GCodeViewer unsigned char cp_color_id{ 0 }; bool matches(const GCodeProcessor::MoveVertex& move) const; - - unsigned int size() const { return last.i_id - first.i_id + 1; } }; // Used to batch the indices needed to render paths @@ -153,8 +151,6 @@ class GCodeViewer unsigned int first{ 0 }; unsigned int last{ 0 }; unsigned int current{ 0 }; - - void clamp_current() { current = std::clamp(current, first, last); } }; #if ENABLE_GCODE_VIEWER_STATISTICS @@ -162,6 +158,7 @@ class GCodeViewer { long long load_time{ 0 }; long long refresh_time{ 0 }; + long long refresh_paths_time{ 0 }; long long gl_multi_points_calls_count{ 0 }; long long gl_multi_line_strip_calls_count{ 0 }; long long results_size{ 0 }; @@ -181,6 +178,7 @@ class GCodeViewer void reset_times() { load_time = 0; refresh_time = 0; + refresh_paths_time = 0; } void reset_opengl() { diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index ce79a4bcd4..b4cbe44616 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -368,6 +368,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view #if ENABLE_GCODE_VIEWER m_bottom_toolbar_sizer = new wxBoxSizer(wxHORIZONTAL); + m_bottom_toolbar_sizer->AddSpacer(10); m_bottom_toolbar_sizer->Add(m_label_view_type, 0, wxALIGN_CENTER_VERTICAL, 5); m_bottom_toolbar_sizer->Add(m_choice_view_type, 0, wxEXPAND | wxALL, 5); m_bottom_toolbar_sizer->AddSpacer(10); From 1c4ffa9b16eb85bfed2e6fadf68c21937bf68ac8 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 5 May 2020 16:29:07 +0200 Subject: [PATCH 058/503] GCodeViewer -> Added buttons for forward/backward movements of 1, 10 and 100 moves to sequential view bar --- src/slic3r/GUI/GCodeViewer.cpp | 67 ++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 3323e784cd..44fe361c21 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1099,7 +1099,13 @@ void GCodeViewer::render_legend() const void GCodeViewer::render_sequential_dlg() const { - static const float margin = 125.0f; + static const float MARGIN = 125.0f; + static const float BUTTON_W = 50.0f; + + auto apply_button_action = [this](unsigned int value) { + m_sequential_view.current = std::clamp(value, m_sequential_view.first, m_sequential_view.last); + refresh_render_paths(true); + }; if (m_roles.empty()) return; @@ -1110,10 +1116,9 @@ void GCodeViewer::render_sequential_dlg() const ImGuiWrapper& imgui = *wxGetApp().imgui(); const ImGuiStyle& style = ImGui::GetStyle(); - const GLToolbar& view_toolbar = wxGetApp().plater()->get_view_toolbar(); Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size(); - float left = view_toolbar.get_width(); + float left = wxGetApp().plater()->get_view_toolbar().get_width(); float width = static_cast(cnv_size.get_width()) - left; ImGui::SetNextWindowBgAlpha(0.5f); @@ -1122,21 +1127,59 @@ void GCodeViewer::render_sequential_dlg() const ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); imgui.begin(std::string("Sequential"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); - std::string low_str = std::to_string(m_sequential_view.first); - ImGui::SetCursorPosX(margin - style.ItemSpacing.x - ImGui::CalcTextSize(low_str.c_str()).x); + ImGui::SetCursorPosX(MARGIN); + imgui.disabled_begin(m_sequential_view.first == m_sequential_view.current); + if (ImGui::Button("< 1", { BUTTON_W, 0.0f })) + apply_button_action(m_sequential_view.current - 1); + imgui.disabled_end(); + + ImGui::SameLine(); + imgui.disabled_begin(m_sequential_view.current - m_sequential_view.first < 10); + if (ImGui::Button("< 10", { BUTTON_W, 0.0f })) + apply_button_action(m_sequential_view.current - 10); + imgui.disabled_end(); + + ImGui::SameLine(); + imgui.disabled_begin(m_sequential_view.current - m_sequential_view.first < 100); + if (ImGui::Button("< 100", { BUTTON_W, 0.0f })) + apply_button_action(m_sequential_view.current - 100); + imgui.disabled_end(); + + ImGui::SameLine(width - MARGIN - 3 * BUTTON_W - 2 * style.ItemSpacing.x - style.WindowPadding.x); + imgui.disabled_begin(m_sequential_view.last - m_sequential_view.current < 100); + if (ImGui::Button("> 100", { BUTTON_W, 0.0f })) + apply_button_action(m_sequential_view.current + 100); + imgui.disabled_end(); + + ImGui::SameLine(); + imgui.disabled_begin(m_sequential_view.last - m_sequential_view.current < 10); + if (ImGui::Button("> 10", { BUTTON_W, 0.0f })) + apply_button_action(m_sequential_view.current + 10); + imgui.disabled_end(); + + ImGui::SameLine(); + imgui.disabled_begin(m_sequential_view.last == m_sequential_view.current); + if (ImGui::Button("> 1", { BUTTON_W, 0.0f })) + apply_button_action(m_sequential_view.current + 1); + imgui.disabled_end(); + + int index = 1 + static_cast(m_sequential_view.current); + int i_min = 1 + static_cast(m_sequential_view.first); + int i_max = 1 + static_cast(m_sequential_view.last); + + std::string low_str = std::to_string(i_min); + ImGui::SetCursorPosX(MARGIN - style.ItemSpacing.x - ImGui::CalcTextSize(low_str.c_str()).x); ImGui::AlignTextToFramePadding(); imgui.text(low_str); - ImGui::SameLine(margin); - ImGui::PushItemWidth(-margin); - int index = static_cast(m_sequential_view.current); - if (ImGui::SliderInt("##slider int", &index, static_cast(m_sequential_view.first), static_cast(m_sequential_view.last))) - { - m_sequential_view.current = static_cast(index); + ImGui::SameLine(MARGIN); + ImGui::PushItemWidth(-MARGIN); + if (ImGui::SliderInt("##slider int", &index, i_min, i_max)) { + m_sequential_view.current = static_cast(index - 1); refresh_render_paths(true); } ImGui::PopItemWidth(); ImGui::SameLine(); - imgui.text(std::to_string(m_sequential_view.last)); + imgui.text(std::to_string(i_max)); imgui.end(); ImGui::PopStyleVar(); From 82b75112bd0be3b9e5eccea447605369a6f12033 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 6 May 2020 11:18:37 +0200 Subject: [PATCH 059/503] GCodeViewer -> Sequential view marker wip + refactoring --- src/slic3r/GUI/GCodeViewer.cpp | 71 +++++++++++++++++++++++----------- src/slic3r/GUI/GCodeViewer.hpp | 27 ++++++++++--- 2 files changed, 70 insertions(+), 28 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 44fe361c21..d4cd840884 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -100,8 +100,7 @@ void GCodeViewer::IBuffer::reset() } // release cpu memory - data = std::vector(); - data_size = 0; + indices_count = 0; paths = std::vector(); render_paths = std::vector(); } @@ -116,9 +115,9 @@ bool GCodeViewer::IBuffer::init_shader(const std::string& vertex_shader_src, con return true; } -void GCodeViewer::IBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsigned int v_id) +void GCodeViewer::IBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsigned int i_id, unsigned int s_id) { - Path::Endpoint endpoint = { static_cast(data.size()), v_id, move.position }; + Path::Endpoint endpoint = { i_id, s_id, move.position }; paths.push_back({ move.type, move.extrusion_role, endpoint, endpoint, move.delta_extruder, move.height, move.width, move.feedrate, move.fan_speed, move.volumetric_rate(), move.extruder_id, move.cp_color_id }); } @@ -145,6 +144,21 @@ GCodeViewer::Color GCodeViewer::Extrusions::Range::get_color_at(float value) con return ret; } +void GCodeViewer::SequentialView::Marker::init() +{ + if (m_initialized) + return; + +} + +void GCodeViewer::SequentialView::Marker::render() const +{ + if (!m_initialized) + return; + + +} + const std::vector GCodeViewer::Extrusion_Role_Colors{ { { 0.50f, 0.50f, 0.50f }, // erNone { 1.00f, 1.00f, 0.40f }, // erPerimeter @@ -280,9 +294,11 @@ void GCodeViewer::render() const glsafe(::glEnable(GL_DEPTH_TEST)); render_toolpaths(); + if (m_sequential_view.marker.visible) + m_sequential_view.marker.render(); render_shells(); render_legend(); - render_sequential_dlg(); + render_sequential_bar(); #if ENABLE_GCODE_VIEWER_STATISTICS render_statistics(); #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -403,10 +419,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) glsafe(::glBufferData(GL_ARRAY_BUFFER, vertices_data.size() * sizeof(float), vertices_data.data(), GL_STATIC_DRAW)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - // vertex data -> free ram + // vertex data -> no more needed, free ram vertices_data = std::vector(); // indices data -> extract from result + std::vector> indices(m_buffers.size()); for (size_t i = 0; i < m_vertices.vertices_count; ++i) { // skip first vertex @@ -416,7 +433,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) const GCodeProcessor::MoveVertex& prev = gcode_result.moves[i - 1]; const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i]; - IBuffer& buffer = m_buffers[buffer_id(curr.type)]; + unsigned char id = buffer_id(curr.type); + IBuffer& buffer = m_buffers[id]; + std::vector& buffer_indices = indices[id]; switch (curr.type) { @@ -427,23 +446,23 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) case GCodeProcessor::EMoveType::Retract: case GCodeProcessor::EMoveType::Unretract: { - buffer.add_path(curr, static_cast(i)); - buffer.data.push_back(static_cast(i)); + buffer.add_path(curr, static_cast(buffer_indices.size()), static_cast(i)); + buffer_indices.push_back(static_cast(i)); break; } case GCodeProcessor::EMoveType::Extrude: case GCodeProcessor::EMoveType::Travel: { if (prev.type != curr.type || !buffer.paths.back().matches(curr)) { - buffer.add_path(curr, static_cast(i)); + buffer.add_path(curr, static_cast(buffer_indices.size()), static_cast(i)); Path& last_path = buffer.paths.back(); last_path.first.position = prev.position; last_path.first.s_id = static_cast(i - 1); - buffer.data.push_back(static_cast(i - 1)); + buffer_indices.push_back(static_cast(i - 1)); } - buffer.paths.back().last = { static_cast(buffer.data.size()), static_cast(i), curr.position }; - buffer.data.push_back(static_cast(i)); + buffer.paths.back().last = { static_cast(buffer_indices.size()), static_cast(i), curr.position }; + buffer_indices.push_back(static_cast(i)); break; } default: @@ -461,22 +480,21 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) #endif // ENABLE_GCODE_VIEWER_STATISTICS // indices data -> send data to gpu - for (IBuffer& buffer : m_buffers) + for (size_t i = 0; i < m_buffers.size(); ++i) { - buffer.data_size = buffer.data.size(); + IBuffer& buffer = m_buffers[i]; + std::vector& buffer_indices = indices[i]; + buffer.indices_count = buffer_indices.size(); #if ENABLE_GCODE_VIEWER_STATISTICS - m_statistics.indices_size += SLIC3R_STDVEC_MEMSIZE(buffer.data, unsigned int); - m_statistics.indices_gpu_size += buffer.data_size * sizeof(unsigned int); + m_statistics.indices_size += SLIC3R_STDVEC_MEMSIZE(buffer_indices, unsigned int); + m_statistics.indices_gpu_size += buffer.indices_count * sizeof(unsigned int); #endif // ENABLE_GCODE_VIEWER_STATISTICS - if (buffer.data_size > 0) { + if (buffer.indices_count > 0) { glsafe(::glGenBuffers(1, &buffer.ibo_id)); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.ibo_id)); - glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, buffer.data_size * sizeof(unsigned int), buffer.data.data(), GL_STATIC_DRAW)); + glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, buffer.indices_count * sizeof(unsigned int), buffer_indices.data(), GL_STATIC_DRAW)); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - - // indices data -> free ram - buffer.data = std::vector(); } } @@ -641,6 +659,10 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current) const // update current sequential position m_sequential_view.current = keep_sequential_current ? std::clamp(m_sequential_view.current, m_sequential_view.first, m_sequential_view.last) : m_sequential_view.last; + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vertices.vbo_id)); + size_t v_size = VBuffer::vertex_size_bytes(); + glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, static_cast(m_sequential_view.current * v_size), static_cast(v_size), static_cast(m_sequential_view.current_position.data()))); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); // second pass: filter paths by sequential data for (auto&& [buffer, id] : paths) { @@ -1097,7 +1119,7 @@ void GCodeViewer::render_legend() const ImGui::PopStyleVar(); } -void GCodeViewer::render_sequential_dlg() const +void GCodeViewer::render_sequential_bar() const { static const float MARGIN = 125.0f; static const float BUTTON_W = 50.0f; @@ -1181,6 +1203,9 @@ void GCodeViewer::render_sequential_dlg() const ImGui::SameLine(); imgui.text(std::to_string(i_max)); + ImGui::Separator(); + ImGui::Checkbox(I18N::translate_utf8(L("Show marker")).c_str(), &m_sequential_view.marker.visible); + imgui.end(); ImGui::PopStyleVar(); } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 3e73cb5d68..5354ae067f 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -74,18 +74,17 @@ class GCodeViewer { unsigned int ibo_id{ 0 }; Shader shader; - std::vector data; - size_t data_size{ 0 }; + size_t indices_count{ 0 }; std::vector paths; std::vector render_paths; bool visible{ false }; void reset(); bool init_shader(const std::string& vertex_shader_src, const std::string& fragment_shader_src); - void add_path(const GCodeProcessor::MoveVertex& move, unsigned int s_id); + void add_path(const GCodeProcessor::MoveVertex& move, unsigned int i_id, unsigned int s_id); }; - + // helper to render shells struct Shells { GLVolumeCollection volumes; @@ -148,9 +147,26 @@ class GCodeViewer struct SequentialView { + struct Marker + { + private: + bool m_initialized{ false }; + + public: + unsigned int vbo_id{ 0 }; + unsigned int ibo_id{ 0 }; + bool visible{ false }; + Shader shader; + + void init(); + void render() const; + }; + unsigned int first{ 0 }; unsigned int last{ 0 }; unsigned int current{ 0 }; + Vec3f current_position{ Vec3f::Zero() }; + Marker marker; }; #if ENABLE_GCODE_VIEWER_STATISTICS @@ -237,6 +253,7 @@ public: bool init() { set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Extrude, true); + m_sequential_view.marker.init(); return init_shaders(); } @@ -285,7 +302,7 @@ private: void render_toolpaths() const; void render_shells() const; void render_legend() const; - void render_sequential_dlg() const; + void render_sequential_bar() const; #if ENABLE_GCODE_VIEWER_STATISTICS void render_statistics() const; #endif // ENABLE_GCODE_VIEWER_STATISTICS From c29f0a4849a8ec10bbbb35bec72256565417e554 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 6 May 2020 15:17:53 +0200 Subject: [PATCH 060/503] GCodeViewer -> Increased size of wxCheckListBoxComboPopup --- src/slic3r/GUI/wxExtensions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 55468cde7b..7dadffb5b4 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -214,7 +214,7 @@ wxSize wxCheckListBoxComboPopup::GetAdjustedSize(int minWidth, int prefHeight, i max_width = std::max(max_width, 60 + GetTextExtent(GetString(i)).x); } size.SetWidth(max_width); - size.SetHeight(count * GetTextExtent(GetString(0)).y); + size.SetHeight(4 + count * (2 + GetTextExtent(GetString(0)).y)); } else size.SetHeight(DefaultHeight); From 5c6a56ca29d0faf38cf1f01e12d5da175cd1857e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 7 May 2020 10:49:12 +0200 Subject: [PATCH 061/503] GCodeAnalyzer and GCodePreviewData removed from tech ENABLE_GCODE_VIEWER --- src/libslic3r/CustomGCode.cpp | 12 +- src/libslic3r/GCode.cpp | 128 +++++++++++++------- src/libslic3r/GCode.hpp | 37 +++++- src/libslic3r/GCode/Analyzer.cpp | 42 +------ src/libslic3r/GCode/Analyzer.hpp | 9 +- src/libslic3r/GCode/PreviewData.cpp | 4 + src/libslic3r/GCode/PreviewData.hpp | 4 + src/libslic3r/GCode/WipeTower.cpp | 25 ++-- src/libslic3r/Model.cpp | 2 + src/libslic3r/Print.cpp | 8 +- src/libslic3r/Print.hpp | 2 +- src/libslic3r/Technologies.hpp | 1 - src/slic3r/GUI/3DScene.cpp | 4 + src/slic3r/GUI/BackgroundSlicingProcess.cpp | 8 +- src/slic3r/GUI/BackgroundSlicingProcess.hpp | 17 +-- src/slic3r/GUI/DoubleSlider.cpp | 8 ++ src/slic3r/GUI/GCodeViewer.cpp | 5 +- src/slic3r/GUI/GLCanvas3D.cpp | 116 ++++++++++++++---- src/slic3r/GUI/GLCanvas3D.hpp | 2 + src/slic3r/GUI/GUI_Preview.cpp | 19 ++- src/slic3r/GUI/GUI_Preview.hpp | 4 +- src/slic3r/GUI/Plater.cpp | 10 +- 22 files changed, 311 insertions(+), 156 deletions(-) diff --git a/src/libslic3r/CustomGCode.cpp b/src/libslic3r/CustomGCode.cpp index 824bcdd93c..72c5c20de9 100644 --- a/src/libslic3r/CustomGCode.cpp +++ b/src/libslic3r/CustomGCode.cpp @@ -1,6 +1,10 @@ #include "CustomGCode.hpp" #include "Config.hpp" +#if ENABLE_GCODE_VIEWER +#include "GCode.hpp" +#else #include "GCode/PreviewData.hpp" +#endif // ENABLE_GCODE_VIEWER #include "GCodeWriter.hpp" namespace Slic3r { @@ -17,8 +21,12 @@ extern void update_custom_gcode_per_print_z_from_config(Info& info, DynamicPrint return; if (info.gcodes.empty() && ! colorprint_heights->values.empty()) { // Convert the old colorprint_heighs only if there is no equivalent data in a new format. - const std::vector& colors = GCodePreviewData::ColorPrintColors(); - const auto& colorprint_values = colorprint_heights->values; +#if ENABLE_GCODE_VIEWER + const std::vector& colors = ColorPrintColors::get(); +#else + const std::vector& colors = GCodePreviewData::ColorPrintColors(); +#endif // ENABLE_GCODE_VIEWER + const auto& colorprint_values = colorprint_heights->values; info.gcodes.clear(); info.gcodes.reserve(colorprint_values.size()); int i = 0; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 10636082bb..584d1a1e3e 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -572,6 +572,10 @@ std::string WipeTowerIntegration::finalize(GCode &gcodegen) return gcode; } +#if ENABLE_GCODE_VIEWER +const std::vector ColorPrintColors::Colors = { "#C0392B", "#E67E22", "#F1C40F", "#27AE60", "#1ABC9C", "#2980B9", "#9B59B6" }; +#endif // ENABLE_GCODE_VIEWER + #define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id()) // Collect pairs of object_layer + support_layer sorted by print_z. @@ -699,7 +703,7 @@ std::vector>> GCode::collec } #if ENABLE_GCODE_VIEWER -void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_data, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb) +void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb) #else void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb) #endif // ENABLE_GCODE_VIEWER @@ -724,7 +728,9 @@ void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_ if (file == nullptr) throw std::runtime_error(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n"); +#if !ENABLE_GCODE_VIEWER m_enable_analyzer = preview_data != nullptr; +#endif // !ENABLE_GCODE_VIEWER try { m_placeholder_parser_failed_templates.clear(); @@ -778,16 +784,14 @@ void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_ m_silent_time_estimator.reset(); } +#if !ENABLE_GCODE_VIEWER // starts analyzer calculations if (m_enable_analyzer) { -#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - m_analyzer.close_debug_output_file(); -#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - BOOST_LOG_TRIVIAL(debug) << "Preparing G-code preview data" << log_memory_info(); m_analyzer.calc_gcode_preview_data(*preview_data, [print]() { print->throw_if_canceled(); }); m_analyzer.reset(); } +#endif // !ENABLE_GCODE_VIEWER if (rename_file(path_tmp, path)) throw std::runtime_error( @@ -877,7 +881,14 @@ namespace DoExport { } } - static void init_gcode_analyzer(const PrintConfig &config, GCodeAnalyzer &analyzer) +#if ENABLE_GCODE_VIEWER + static void init_gcode_processor(const PrintConfig& config, GCodeProcessor& processor) + { + processor.reset(); + processor.apply_config(config); + } +#else + static void init_gcode_analyzer(const PrintConfig &config, GCodeAnalyzer &analyzer) { // resets analyzer analyzer.reset(); @@ -901,17 +912,6 @@ namespace DoExport { // tell analyzer about the gcode flavor analyzer.set_gcode_flavor(config.gcode_flavor); - -#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - analyzer.open_debug_output_file(); -#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - } - -#if ENABLE_GCODE_VIEWER - static void init_gcode_processor(const PrintConfig& config, GCodeProcessor& processor) - { - processor.reset(); - processor.apply_config(config); } #endif // ENABLE_GCODE_VIEWER @@ -1145,15 +1145,22 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu DoExport::init_time_estimators(print.config(), // modifies the following: m_normal_time_estimator, m_silent_time_estimator, m_silent_time_estimator_enabled); - DoExport::init_gcode_analyzer(print.config(), m_analyzer); #if ENABLE_GCODE_VIEWER DoExport::init_gcode_processor(print.config(), m_processor); +#else + DoExport::init_gcode_analyzer(print.config(), m_analyzer); #endif // ENABLE_GCODE_VIEWER // resets analyzer's tracking data +#if ENABLE_GCODE_VIEWER + m_last_mm3_per_mm = 0.0f; + m_last_width = 0.0f; + m_last_height = 0.0f; +#else m_last_mm3_per_mm = GCodeAnalyzer::Default_mm3_per_mm; m_last_width = GCodeAnalyzer::Default_Width; m_last_height = GCodeAnalyzer::Default_Height; +#endif // ENABLE_GCODE_VIEWER // How many times will be change_layer() called? // change_layer() in turn increments the progress bar status. @@ -1333,13 +1340,13 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu // Set extruder(s) temperature before and after start G-code. this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, false); - if (m_enable_analyzer) - // adds tag for analyzer - _write_format(file, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erCustom); - #if ENABLE_GCODE_VIEWER // adds tag for processor _write_format(file, ";%s%d\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), erCustom); +#else + if (m_enable_analyzer) + // adds tag for analyzer + _write_format(file, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erCustom); #endif // ENABLE_GCODE_VIEWER // Write the custom start G-code @@ -1491,13 +1498,13 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu _write(file, this->retract()); _write(file, m_writer.set_fan(false)); - if (m_enable_analyzer) - // adds tag for analyzer - _write_format(file, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erCustom); - #if ENABLE_GCODE_VIEWER // adds tag for processor _write_format(file, ";%s%d\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), erCustom); +#else + if (m_enable_analyzer) + // adds tag for analyzer + _write_format(file, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erCustom); #endif // ENABLE_GCODE_VIEWER // Process filament-specific gcode in extruder order. @@ -1821,11 +1828,12 @@ namespace ProcessLayer { assert(m600_extruder_before_layer >= 0); // Color Change or Tool Change as Color Change. - // add tag for analyzer - gcode += "; " + GCodeAnalyzer::Color_Change_Tag + ",T" + std::to_string(m600_extruder_before_layer) + "\n"; #if ENABLE_GCODE_VIEWER // add tag for processor gcode += "; " + GCodeProcessor::Color_Change_Tag + ",T" + std::to_string(m600_extruder_before_layer) + "\n"; +#else + // add tag for analyzer + gcode += "; " + GCodeAnalyzer::Color_Change_Tag + ",T" + std::to_string(m600_extruder_before_layer) + "\n"; #endif // ENABLE_GCODE_VIEWER // add tag for time estimator gcode += "; " + GCodeTimeEstimator::Color_Change_Tag + "\n"; @@ -1846,11 +1854,12 @@ namespace ProcessLayer { if (custom_code == PausePrintCode) // Pause print { - // add tag for analyzer - gcode += "; " + GCodeAnalyzer::Pause_Print_Tag + "\n"; #if ENABLE_GCODE_VIEWER // add tag for processor gcode += "; " + GCodeProcessor::Pause_Print_Tag + "\n"; +#else + // add tag for analyzer + gcode += "; " + GCodeAnalyzer::Pause_Print_Tag + "\n"; #endif // ENABLE_GCODE_VIEWER //! FIXME_in_fw show message during print pause if (!pause_print_msg.empty()) @@ -1860,11 +1869,12 @@ namespace ProcessLayer } else // custom Gcode { - // add tag for analyzer - gcode += "; " + GCodeAnalyzer::Custom_Code_Tag + "\n"; #if ENABLE_GCODE_VIEWER // add tag for processor gcode += "; " + GCodeProcessor::Custom_Code_Tag + "\n"; +#else + // add tag for analyzer + gcode += "; " + GCodeAnalyzer::Custom_Code_Tag + "\n"; #endif // ENABLE_GCODE_VIEWER // add tag for time estimator //gcode += "; " + GCodeTimeEstimator::Custom_Code_Tag + "\n"; @@ -2218,9 +2228,15 @@ void GCode::process_layer( m_wipe_tower->tool_change(*this, extruder_id, extruder_id == layer_tools.extruders.back()) : this->set_extruder(extruder_id, print_z); +#if ENABLE_GCODE_VIEWER + // let analyzer tag generator aware of a role type change + if (layer_tools.has_wipe_tower && m_wipe_tower) + m_last_processor_extrusion_role = erWipeTower; +#else // let analyzer tag generator aware of a role type change if (m_enable_analyzer && layer_tools.has_wipe_tower && m_wipe_tower) m_last_analyzer_extrusion_role = erWipeTower; +#endif // ENABLE_GCODE_VIEWER if (auto loops_it = skirt_loops_per_extruder.find(extruder_id); loops_it != skirt_loops_per_extruder.end()) { const std::pair loops = loops_it->second; @@ -2324,11 +2340,13 @@ void GCode::process_layer( if (m_cooling_buffer) gcode = m_cooling_buffer->process_layer(gcode, layer.id()); +#if !ENABLE_GCODE_VIEWER // add tag for analyzer if (gcode.find(GCodeAnalyzer::Pause_Print_Tag) != gcode.npos) gcode += "\n; " + GCodeAnalyzer::End_Pause_Print_Or_Custom_Code_Tag + "\n"; else if (gcode.find(GCodeAnalyzer::Custom_Code_Tag) != gcode.npos) gcode += "\n; " + GCodeAnalyzer::End_Pause_Print_Or_Custom_Code_Tag + "\n"; +#endif // !ENABLE_GCODE_VIEWER #ifdef HAS_PRESSURE_EQUALIZER // Apply pressure equalization if enabled; @@ -2342,9 +2360,11 @@ void GCode::process_layer( BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z << ", time estimator memory: " << format_memsize_MB(m_normal_time_estimator.memory_used() + (m_silent_time_estimator_enabled ? m_silent_time_estimator.memory_used() : 0)) << - ", analyzer memory: " << +#if !ENABLE_GCODE_VIEWER + ", analyzer memory: " << format_memsize_MB(m_analyzer.memory_used()) << - log_memory_info(); +#endif // !ENABLE_GCODE_VIEWER + log_memory_info(); } void GCode::apply_print_config(const PrintConfig &print_config) @@ -2984,8 +3004,12 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill void GCode::_write(FILE* file, const char *what) { if (what != nullptr) { +#if ENABLE_GCODE_VIEWER + const char* gcode = what; +#else // apply analyzer, if enabled const char* gcode = m_enable_analyzer ? m_analyzer.process_gcode(what).c_str() : what; +#endif // !ENABLE_GCODE_VIEWER // writes string to file fwrite(gcode, 1, ::strlen(gcode), file); @@ -3132,57 +3156,73 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } // adds analyzer tags and updates analyzer's tracking data +#if !ENABLE_GCODE_VIEWER if (m_enable_analyzer) { +#endif // !ENABLE_GCODE_VIEWER // PrusaMultiMaterial::Writer may generate GCodeAnalyzer::Height_Tag and GCodeAnalyzer::Width_Tag lines without updating m_last_height and m_last_width // so, if the last role was erWipeTower we force export of GCodeAnalyzer::Height_Tag and GCodeAnalyzer::Width_Tag lines +#if ENABLE_GCODE_VIEWER + bool last_was_wipe_tower = (m_last_processor_extrusion_role == erWipeTower); +#else bool last_was_wipe_tower = (m_last_analyzer_extrusion_role == erWipeTower); +#endif // ENABLE_GCODE_VIEWER char buf[64]; +#if ENABLE_GCODE_VIEWER + if (path.role() != m_last_processor_extrusion_role) + { + m_last_processor_extrusion_role = path.role(); + sprintf(buf, ";%s%d\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), int(m_last_processor_extrusion_role)); + gcode += buf; + } +#else if (path.role() != m_last_analyzer_extrusion_role) { m_last_analyzer_extrusion_role = path.role(); sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), int(m_last_analyzer_extrusion_role)); -#if ENABLE_GCODE_VIEWER - gcode += buf; - sprintf(buf, ";%s%d\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), int(m_last_analyzer_extrusion_role)); -#endif // ENABLE_GCODE_VIEWER gcode += buf; } +#endif // ENABLE_GCODE_VIEWER if (last_was_wipe_tower || (m_last_mm3_per_mm != path.mm3_per_mm)) { m_last_mm3_per_mm = path.mm3_per_mm; - sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), m_last_mm3_per_mm); - gcode += buf; #if ENABLE_GCODE_VIEWER sprintf(buf, ";%s%f\n", GCodeProcessor::Mm3_Per_Mm_Tag.c_str(), m_last_mm3_per_mm); gcode += buf; +#else + sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), m_last_mm3_per_mm); + gcode += buf; #endif // ENABLE_GCODE_VIEWER } if (last_was_wipe_tower || (m_last_width != path.width)) { m_last_width = path.width; - sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), m_last_width); - gcode += buf; #if ENABLE_GCODE_VIEWER sprintf(buf, ";%s%f\n", GCodeProcessor::Width_Tag.c_str(), m_last_width); gcode += buf; +#else + sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), m_last_width); + gcode += buf; #endif // ENABLE_GCODE_VIEWER } if (last_was_wipe_tower || (m_last_height != path.height)) { m_last_height = path.height; - sprintf(buf, ";%s%f\n", GCodeAnalyzer::Height_Tag.c_str(), m_last_height); - gcode += buf; #if ENABLE_GCODE_VIEWER sprintf(buf, ";%s%f\n", GCodeProcessor::Height_Tag.c_str(), m_last_height); gcode += buf; +#else + sprintf(buf, ";%s%f\n", GCodeAnalyzer::Height_Tag.c_str(), m_last_height); + gcode += buf; #endif // ENABLE_GCODE_VIEWER } +#if !ENABLE_GCODE_VIEWER } +#endif // !ENABLE_GCODE_VIEWER std::string comment; if (m_enable_cooling_markers) { diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index f6668ad3d6..546c425755 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -16,10 +16,11 @@ #include "GCode/WipeTower.hpp" #if ENABLE_GCODE_VIEWER #include "GCode/GCodeProcessor.hpp" +#else +#include "GCode/Analyzer.hpp" #endif // ENABLE_GCODE_VIEWER #include "GCodeTimeEstimator.hpp" #include "EdgeGrid.hpp" -#include "GCode/Analyzer.hpp" #include "GCode/ThumbnailData.hpp" #include @@ -33,7 +34,9 @@ namespace Slic3r { // Forward declarations. class GCode; +#if !ENABLE_GCODE_VIEWER class GCodePreviewData; +#endif // !ENABLE_GCODE_VIEWER class AvoidCrossingPerimeters { public: @@ -138,6 +141,15 @@ private: double m_last_wipe_tower_print_z = 0.f; }; +#if ENABLE_GCODE_VIEWER +class ColorPrintColors +{ + static const std::vector Colors; +public: + static const std::vector& get() { return Colors; } +}; +#endif // ENABLE_GCODE_VIEWER + class GCode { public: GCode() : @@ -145,17 +157,27 @@ public: m_enable_loop_clipping(true), m_enable_cooling_markers(false), m_enable_extrusion_role_markers(false), +#if ENABLE_GCODE_VIEWER + m_last_processor_extrusion_role(erNone), +#else m_enable_analyzer(false), m_last_analyzer_extrusion_role(erNone), +#endif // ENABLE_GCODE_VIEWER m_layer_count(0), m_layer_index(-1), m_layer(nullptr), m_volumetric_speed(0), m_last_pos_defined(false), m_last_extrusion_role(erNone), +#if ENABLE_GCODE_VIEWER + m_last_mm3_per_mm(0.0f), + m_last_width(0.0f), + m_last_height(0.0f), +#else m_last_mm3_per_mm(GCodeAnalyzer::Default_mm3_per_mm), m_last_width(GCodeAnalyzer::Default_Width), m_last_height(GCodeAnalyzer::Default_Height), +#endif // ENABLE_GCODE_VIEWER m_brim_done(false), m_second_layer_things_done(false), m_normal_time_estimator(GCodeTimeEstimator::Normal), @@ -168,7 +190,7 @@ public: // throws std::runtime_exception on error, // throws CanceledException through print->throw_if_canceled(). #if ENABLE_GCODE_VIEWER - void do_export(Print* print, const char* path, GCodePreviewData* preview_data = nullptr, GCodeProcessor::Result* result = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); + void do_export(Print* print, const char* path, GCodeProcessor::Result* result = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); #else void do_export(Print* print, const char* path, GCodePreviewData* preview_data = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); #endif // ENABLE_GCODE_VIEWER @@ -331,11 +353,16 @@ private: // Markers for the Pressure Equalizer to recognize the extrusion type. // The Pressure Equalizer removes the markers from the final G-code. bool m_enable_extrusion_role_markers; +#if ENABLE_GCODE_VIEWER + // Keeps track of the last extrusion role passed to the processor + ExtrusionRole m_last_processor_extrusion_role; +#else // Enableds the G-code Analyzer. // Extended markers will be added during G-code generation. // The G-code Analyzer will remove these comments from the final G-code. bool m_enable_analyzer; ExtrusionRole m_last_analyzer_extrusion_role; +#endif // ENABLE_GCODE_VIEWER // How many times will change_layer() be called? // change_layer() will update the progress bar. unsigned int m_layer_count; @@ -377,12 +404,12 @@ private: GCodeTimeEstimator m_silent_time_estimator; bool m_silent_time_estimator_enabled; - // Analyzer - GCodeAnalyzer m_analyzer; - #if ENABLE_GCODE_VIEWER // Processor GCodeProcessor m_processor; +#else + // Analyzer + GCodeAnalyzer m_analyzer; #endif // ENABLE_GCODE_VIEWER // Write a string into a file. diff --git a/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp index 974176dbd7..d022b37983 100644 --- a/src/libslic3r/GCode/Analyzer.cpp +++ b/src/libslic3r/GCode/Analyzer.cpp @@ -8,19 +8,11 @@ #include "Print.hpp" #include -#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT -#include -#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT #include "Analyzer.hpp" #include "PreviewData.hpp" -#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT -#include - -// don't worry, this is just temporary -static boost::nowide::ofstream g_debug_output; -#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT +#if !ENABLE_GCODE_VIEWER static const std::string AXIS_STR = "XYZE"; static const float MMMIN_TO_MMSEC = 1.0f / 60.0f; @@ -184,19 +176,6 @@ bool GCodeAnalyzer::is_valid_extrusion_role(ExtrusionRole role) return ((erPerimeter <= role) && (role < erMixed)); } -#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT -void GCodeAnalyzer::open_debug_output_file() -{ - boost::filesystem::path path("d:/analyzer.output"); - g_debug_output.open(path.string()); -} - -void GCodeAnalyzer::close_debug_output_file() -{ - g_debug_output.close(); -} -#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - void GCodeAnalyzer::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLine& line) { // processes 'special' comments contained in line @@ -945,23 +924,6 @@ void GCodeAnalyzer::_store_move(GCodeAnalyzer::GCodeMove::EType type) Vec3f start_position = _get_start_position() + extruder_offset; Vec3f end_position = _get_end_position() + extruder_offset; it->second.emplace_back(type, _get_extrusion_role(), extruder_id, _get_mm3_per_mm(), _get_width(), _get_height(), _get_feedrate(), start_position, end_position, _get_delta_extrusion(), _get_fan_speed(), _get_cp_color_id()); - -#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - if (g_debug_output.good()) - { - g_debug_output << std::to_string(static_cast(type)); - g_debug_output << ", " << std::to_string(static_cast(_get_extrusion_role())); - g_debug_output << ", " << Slic3r::to_string(static_cast(end_position.cast())); - g_debug_output << ", " << std::to_string(extruder_id); - g_debug_output << ", " << std::to_string(_get_cp_color_id()); - g_debug_output << ", " << std::to_string(_get_feedrate()); - g_debug_output << ", " << std::to_string(_get_width()); - g_debug_output << ", " << std::to_string(_get_height()); - g_debug_output << ", " << std::to_string(_get_mm3_per_mm()); - g_debug_output << ", " << std::to_string(_get_fan_speed()); - g_debug_output << "\n"; - } -#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT } bool GCodeAnalyzer::_is_valid_extrusion_role(int value) const @@ -1231,3 +1193,5 @@ size_t GCodeAnalyzer::memory_used() const } } // namespace Slic3r + +#endif // !ENABLE_GCODE_VIEWER diff --git a/src/libslic3r/GCode/Analyzer.hpp b/src/libslic3r/GCode/Analyzer.hpp index 9d16ab4944..37d9072592 100644 --- a/src/libslic3r/GCode/Analyzer.hpp +++ b/src/libslic3r/GCode/Analyzer.hpp @@ -1,6 +1,8 @@ #ifndef slic3r_GCode_Analyzer_hpp_ #define slic3r_GCode_Analyzer_hpp_ +#if !ENABLE_GCODE_VIEWER + #include "../libslic3r.h" #include "../PrintConfig.hpp" #include "../ExtrusionEntity.hpp" @@ -147,11 +149,6 @@ public: static bool is_valid_extrusion_role(ExtrusionRole role); -#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - void open_debug_output_file(); - void close_debug_output_file(); -#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - private: // Processes the given gcode line void _process_gcode_line(GCodeReader& reader, const GCodeReader::GCodeLine& line); @@ -307,4 +304,6 @@ private: } // namespace Slic3r +#endif // !ENABLE_GCODE_VIEWER + #endif /* slic3r_GCode_Analyzer_hpp_ */ diff --git a/src/libslic3r/GCode/PreviewData.cpp b/src/libslic3r/GCode/PreviewData.cpp index 3aae15748d..58b15e9a46 100644 --- a/src/libslic3r/GCode/PreviewData.cpp +++ b/src/libslic3r/GCode/PreviewData.cpp @@ -5,6 +5,8 @@ #include +#if !ENABLE_GCODE_VIEWER + //! macro used to mark string used at localization, #define L(s) (s) @@ -515,3 +517,5 @@ Color operator * (float f, const Color& color) } } // namespace Slic3r + +#endif // !ENABLE_GCODE_VIEWER diff --git a/src/libslic3r/GCode/PreviewData.hpp b/src/libslic3r/GCode/PreviewData.hpp index c0f768088e..930c1659e3 100644 --- a/src/libslic3r/GCode/PreviewData.hpp +++ b/src/libslic3r/GCode/PreviewData.hpp @@ -1,6 +1,8 @@ #ifndef slic3r_GCode_PreviewData_hpp_ #define slic3r_GCode_PreviewData_hpp_ +#if !ENABLE_GCODE_VIEWER + #include "../libslic3r.h" #include "../ExtrusionEntity.hpp" #include "../Point.hpp" @@ -391,4 +393,6 @@ public: } // namespace Slic3r +#endif // !ENABLE_GCODE_VIEWER + #endif /* slic3r_GCode_PreviewData_hpp_ */ diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index d5d060f773..3b5c2a1599 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -21,9 +21,10 @@ TODO LIST #include #include -#include "Analyzer.hpp" #if ENABLE_GCODE_VIEWER #include "GCodeProcessor.hpp" +#else +#include "Analyzer.hpp" #endif // ENABLE_GCODE_VIEWER #include "BoundingBox.hpp" @@ -56,16 +57,16 @@ public: { // adds tag for analyzer: char buf[64]; - sprintf(buf, ";%s%f\n", GCodeAnalyzer::Height_Tag.c_str(), m_layer_height); // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming - m_gcode += buf; #if ENABLE_GCODE_VIEWER sprintf(buf, ";%s%f\n", GCodeProcessor::Height_Tag.c_str(), m_layer_height); // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming - m_gcode += buf; +#else + sprintf(buf, ";%s%f\n", GCodeAnalyzer::Height_Tag.c_str(), m_layer_height); // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming #endif // ENABLE_GCODE_VIEWER - sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erWipeTower); -#if ENABLE_GCODE_VIEWER m_gcode += buf; +#if ENABLE_GCODE_VIEWER sprintf(buf, ";%s%d\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), erWipeTower); +#else + sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erWipeTower); #endif // ENABLE_GCODE_VIEWER m_gcode += buf; change_analyzer_line_width(line_width); @@ -74,12 +75,12 @@ public: WipeTowerWriter& change_analyzer_line_width(float line_width) { // adds tag for analyzer: char buf[64]; - sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), line_width); - m_gcode += buf; #if ENABLE_GCODE_VIEWER sprintf(buf, ";%s%f\n", GCodeProcessor::Width_Tag.c_str(), line_width); - m_gcode += buf; +#else + sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), line_width); #endif // ENABLE_GCODE_VIEWER + m_gcode += buf; return *this; } @@ -88,12 +89,12 @@ public: float mm3_per_mm = (len == 0.f ? 0.f : area * e / len); // adds tag for analyzer: char buf[64]; - sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm); - m_gcode += buf; #if ENABLE_GCODE_VIEWER sprintf(buf, ";%s%f\n", GCodeProcessor::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm); - m_gcode += buf; +#else + sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm); #endif // ENABLE_GCODE_VIEWER + m_gcode += buf; return *this; } diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 98f595d91e..90c9d03571 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -20,7 +20,9 @@ #include "SVG.hpp" #include #include "GCodeWriter.hpp" +#if !ENABLE_GCODE_VIEWER #include "GCode/PreviewData.hpp" +#endif // !ENABLE_GCODE_VIEWER namespace Slic3r { diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 6bc0494127..0ffb5472c6 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1628,7 +1628,7 @@ void Print::process() // write error into the G-code, cannot execute post-processing scripts). // It is up to the caller to show an error message. #if ENABLE_GCODE_VIEWER -std::string Print::export_gcode(const std::string& path_template, GCodePreviewData* preview_data, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb) +std::string Print::export_gcode(const std::string& path_template, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb) #else std::string Print::export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb) #endif // ENABLE_GCODE_VIEWER @@ -1637,7 +1637,11 @@ std::string Print::export_gcode(const std::string& path_template, GCodePreviewDa // The following call may die if the output_filename_format template substitution fails. std::string path = this->output_filepath(path_template); std::string message; +#if ENABLE_GCODE_VIEWER + if (!path.empty() && result == nullptr) { +#else if (! path.empty() && preview_data == nullptr) { +#endif // ENABLE_GCODE_VIEWER // Only show the path if preview_data is not set -> running from command line. message = L("Exporting G-code"); message += " to "; @@ -1649,7 +1653,7 @@ std::string Print::export_gcode(const std::string& path_template, GCodePreviewDa // The following line may die for multiple reasons. GCode gcode; #if ENABLE_GCODE_VIEWER - gcode.do_export(this, path.c_str(), preview_data, result, thumbnail_cb); + gcode.do_export(this, path.c_str(), result, thumbnail_cb); #else gcode.do_export(this, path.c_str(), preview_data, thumbnail_cb); #endif // ENABLE_GCODE_VIEWER diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 9ae0f13945..c358cd9184 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -372,7 +372,7 @@ public: // Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file. // If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r). #if ENABLE_GCODE_VIEWER - std::string export_gcode(const std::string& path_template, GCodePreviewData* preview_data, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); + std::string export_gcode(const std::string& path_template, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); #else std::string export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); #endif // ENABLE_GCODE_VIEWER diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index a648bb244b..c22e504dff 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -55,7 +55,6 @@ // Enable G-Code viewer #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) -#define ENABLE_GCODE_VIEWER_DEBUG_OUTPUT (0 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index ca2f0389da..6aaf0b500e 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -5,11 +5,15 @@ #include "libslic3r/ExtrusionEntity.hpp" #include "libslic3r/ExtrusionEntityCollection.hpp" #include "libslic3r/Geometry.hpp" +#if !ENABLE_GCODE_VIEWER #include "libslic3r/GCode/PreviewData.hpp" +#endif // !ENABLE_GCODE_VIEWER #include "libslic3r/Print.hpp" #include "libslic3r/SLAPrint.hpp" #include "libslic3r/Slicing.hpp" +#if !ENABLE_GCODE_VIEWER #include "libslic3r/GCode/Analyzer.hpp" +#endif // !ENABLE_GCODE_VIEWER #include "slic3r/GUI/BitmapCache.hpp" #include "libslic3r/Format/STL.hpp" #include "libslic3r/Utils.hpp" diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index c8c344caaf..32b7b83653 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -18,7 +18,9 @@ #include "libslic3r/SLAPrint.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/GCode/PostProcessor.hpp" +#if !ENABLE_GCODE_VIEWER #include "libslic3r/GCode/PreviewData.hpp" +#endif // !ENABLE_GCODE_VIEWER #include "libslic3r/Format/SL1.hpp" #include "libslic3r/libslic3r.h" @@ -89,7 +91,7 @@ void BackgroundSlicingProcess::process_fff() m_print->process(); wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_slicing_completed_id)); #if ENABLE_GCODE_VIEWER - m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_gcode_result, m_thumbnail_cb); + m_fff_print->export_gcode(m_temp_output_path, m_gcode_result, m_thumbnail_cb); #else m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_thumbnail_cb); #endif // ENABLE_GCODE_VIEWER @@ -385,9 +387,7 @@ Print::ApplyStatus BackgroundSlicingProcess::apply(const Model &model, const Dyn // Some FFF status was invalidated, and the G-code was not exported yet. // Let the G-code preview UI know that the final G-code preview is not valid. // In addition, this early memory deallocation reduces memory footprint. - if (m_gcode_preview_data != nullptr) - m_gcode_preview_data->reset(); - else if (m_gcode_result != nullptr) + if (m_gcode_result != nullptr) m_gcode_result->reset(); } #else diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index d3fca88fc4..91ebc1372c 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -50,10 +50,11 @@ public: void set_fff_print(Print *print) { m_fff_print = print; } void set_sla_print(SLAPrint *print) { m_sla_print = print; m_sla_print->set_printer(&m_sla_archive); } - void set_gcode_preview_data(GCodePreviewData *gpd) { m_gcode_preview_data = gpd; } - void set_thumbnail_cb(ThumbnailsGeneratorCallback cb) { m_thumbnail_cb = cb; } + void set_thumbnail_cb(ThumbnailsGeneratorCallback cb) { m_thumbnail_cb = cb; } #if ENABLE_GCODE_VIEWER void set_gcode_result(GCodeProcessor::Result* result) { m_gcode_result = result; } +#else + void set_gcode_preview_data(GCodePreviewData* gpd) { m_gcode_preview_data = gpd; } #endif // ENABLE_GCODE_VIEWER // The following wxCommandEvent will be sent to the UI thread / Plater window, when the slicing is finished @@ -156,15 +157,17 @@ private: // Non-owned pointers to Print instances. Print *m_fff_print = nullptr; SLAPrint *m_sla_print = nullptr; +#if ENABLE_GCODE_VIEWER + // Data structure, to which the G-code export writes its annotations. + GCodeProcessor::Result *m_gcode_result = nullptr; +#else // Data structure, to which the G-code export writes its annotations. GCodePreviewData *m_gcode_preview_data = nullptr; - // Callback function, used to write thumbnails into gcode. +#endif // ENABLE_GCODE_VIEWER + // Callback function, used to write thumbnails into gcode. ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr; SL1Archive m_sla_archive; -#if ENABLE_GCODE_VIEWER - GCodeProcessor::Result* m_gcode_result = nullptr; -#endif // ENABLE_GCODE_VIEWER - // Temporary G-code, there is one defined for the BackgroundSlicingProcess, differentiated from the other processes by a process ID. + // Temporary G-code, there is one defined for the BackgroundSlicingProcess, differentiated from the other processes by a process ID. std::string m_temp_output_path; // Output path provided by the user. The output path may be set even if the slicing is running, // but once set, it cannot be re-set. diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 4c4c1aa8dd..0a89333714 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -1,5 +1,9 @@ #include "wxExtensions.hpp" +#if ENABLE_GCODE_VIEWER +#include "libslic3r/GCode.hpp" +#else #include "libslic3r/GCode/PreviewData.hpp" +#endif // ENABLE_GCODE_VIEWER #include "GUI.hpp" #include "GUI_App.hpp" #include "I18N.hpp" @@ -1945,7 +1949,11 @@ std::string TickCodeInfo::get_color_for_tick(TickCode tick, const std::string& c { if (mode == t_mode::SingleExtruder && code == ColorChangeCode && m_use_default_colors) { +#if ENABLE_GCODE_VIEWER + const std::vector& colors = ColorPrintColors::get(); +#else const std::vector& colors = GCodePreviewData::ColorPrintColors(); +#endif // ENABLE_GCODE_VIEWER if (ticks.empty()) return colors[0]; m_default_color_idx++; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index d4cd840884..0893a7d465 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -167,7 +167,7 @@ const std::vector GCodeViewer::Extrusion_Role_Colors{ { { 0.69f, 0.19f, 0.16f }, // erInternalInfill { 0.84f, 0.20f, 0.84f }, // erSolidInfill { 1.00f, 0.10f, 0.10f }, // erTopSolidInfill - { 0.00f, 1.00f, 1.00f }, // erIroning + { 1.00f, 0.55f, 0.41f }, // erIroning { 0.60f, 0.60f, 1.00f }, // erBridgeInfill { 1.00f, 1.00f, 1.00f }, // erGapFill { 0.52f, 0.48f, 0.13f }, // erSkirt @@ -404,7 +404,8 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) std::vector vertices_data(m_vertices.vertices_count * 3); for (size_t i = 0; i < m_vertices.vertices_count; ++i) { const GCodeProcessor::MoveVertex& move = gcode_result.moves[i]; - m_bounding_box.merge(move.position.cast()); + if (move.type == GCodeProcessor::EMoveType::Extrude) + m_bounding_box.merge(move.position.cast()); ::memcpy(static_cast(&vertices_data[i * 3]), static_cast(move.position.data()), 3 * sizeof(float)); } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 8859a07463..8be8f8b378 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5,7 +5,9 @@ #include "polypartition.h" #include "libslic3r/ClipperUtils.hpp" #include "libslic3r/PrintConfig.hpp" +#if !ENABLE_GCODE_VIEWER #include "libslic3r/GCode/PreviewData.hpp" +#endif // !ENABLE_GCODE_VIEWER #include "libslic3r/GCode/ThumbnailData.hpp" #include "libslic3r/Geometry.hpp" #include "libslic3r/ExtrusionEntity.hpp" @@ -56,10 +58,6 @@ #include #include -#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT -#include -#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - #include #include #include @@ -2747,22 +2745,8 @@ static void load_gcode_retractions(const GCodePreviewData::Retraction& retractio #if ENABLE_GCODE_VIEWER void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result) { -#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - static unsigned int last_result_id = 0; - if (last_result_id != gcode_result.id) - { - last_result_id = gcode_result.id; - boost::filesystem::path path("d:/processor.output"); - boost::nowide::ofstream out; - out.open(path.string()); - for (const GCodeProcessor::MoveVertex& v : gcode_result.moves) - { - out << v.to_string() << "\n"; - } - out.close(); - } -#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT m_gcode_viewer.load(gcode_result, *this->fff_print(), m_initialized); + _show_warning_texture_if_needed(WarningTexture::ToolpathOutside); } void GLCanvas3D::refresh_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector& str_tool_colors) @@ -2771,9 +2755,7 @@ void GLCanvas3D::refresh_gcode_preview(const GCodeProcessor::Result& gcode_resul set_as_dirty(); request_extra_frame(); } -#endif // ENABLE_GCODE_VIEWER - -#if !ENABLE_GCODE_VIEWER +#else void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const std::vector& str_tool_colors) { const Print *print = this->fff_print(); @@ -2842,7 +2824,7 @@ void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const _generate_legend_texture(preview_data, tool_colors); } } -#endif // !ENABLE_GCODE_VIEWER +#endif // ENABLE_GCODE_VIEWER void GLCanvas3D::load_sla_preview() { @@ -3144,7 +3126,24 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) } #endif // ENABLE_RENDER_PICKING_PASS case 'Z': +#if ENABLE_GCODE_VIEWER + case 'z': + { + if (!m_selection.is_empty()) + zoom_to_selection(); + else + { + if (!m_volumes.empty()) + zoom_to_volumes(); + else + _zoom_to_box(m_gcode_viewer.get_bounding_box()); + } + + break; + } +#else case 'z': { m_selection.is_empty() ? zoom_to_volumes() : zoom_to_selection(); break; } +#endif // ENABLE_GCODE_VIEWER default: { evt.Skip(); break; } } } @@ -5467,8 +5466,39 @@ void GLCanvas3D::_rectangular_selection_picking_pass() const _update_volumes_hover_state(); } +#if ENABLE_GCODE_VIEWER +static BoundingBoxf3 print_volume(const DynamicPrintConfig& config) +{ + // tolerance to avoid false detection at bed edges + const double tolerance_x = 0.05; + const double tolerance_y = 0.05; + + BoundingBoxf3 ret; + const ConfigOptionPoints* opt = dynamic_cast(config.option("bed_shape")); + if (opt != nullptr) + { + BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values)); + ret = BoundingBoxf3(Vec3d(unscale(bed_box_2D.min(0)) - tolerance_x, unscale(bed_box_2D.min(1)) - tolerance_y, 0.0), Vec3d(unscale(bed_box_2D.max(0)) + tolerance_x, unscale(bed_box_2D.max(1)) + tolerance_y, config.opt_float("max_print_height"))); + // Allow the objects to protrude below the print bed + ret.min(2) = -1e10; + } + return ret; +} +#endif // ENABLE_GCODE_VIEWER + void GLCanvas3D::_render_background() const { +#if ENABLE_GCODE_VIEWER + bool use_error_color = m_dynamic_background_enabled; + if (!m_volumes.empty()) + use_error_color &= _is_any_volume_outside(); + else + { + BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); + use_error_color &= (test_volume.radius() > 0.0) ? !test_volume.contains(m_gcode_viewer.get_bounding_box()) : false; + } +#endif // ENABLE_GCODE_VIEWER + glsafe(::glPushMatrix()); glsafe(::glLoadIdentity()); glsafe(::glMatrixMode(GL_PROJECTION)); @@ -5479,7 +5509,11 @@ void GLCanvas3D::_render_background() const glsafe(::glDisable(GL_DEPTH_TEST)); ::glBegin(GL_QUADS); +#if ENABLE_GCODE_VIEWER + if (use_error_color) +#else if (m_dynamic_background_enabled && _is_any_volume_outside()) +#endif // ENABLE_GCODE_VIEWER ::glColor3fv(ERROR_BG_DARK_COLOR); else ::glColor3fv(DEFAULT_BG_DARK_COLOR); @@ -5487,8 +5521,12 @@ void GLCanvas3D::_render_background() const ::glVertex2f(-1.0f, -1.0f); ::glVertex2f(1.0f, -1.0f); +#if ENABLE_GCODE_VIEWER + if (use_error_color) +#else if (m_dynamic_background_enabled && _is_any_volume_outside()) - ::glColor3fv(ERROR_BG_LIGHT_COLOR); +#endif // ENABLE_GCODE_VIEWER +::glColor3fv(ERROR_BG_LIGHT_COLOR); else ::glColor3fv(DEFAULT_BG_LIGHT_COLOR); @@ -6991,6 +7029,7 @@ void GLCanvas3D::_load_sla_shells() update_volumes_colors_by_extruder(); } +#if !ENABLE_GCODE_VIEWER void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& preview_data) { unsigned int size = (unsigned int)m_gcode_preview_volume_index.first_volumes.size(); @@ -7048,9 +7087,13 @@ void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& previe } } } +#endif // !ENABLE_GCODE_VIEWER void GLCanvas3D::_update_toolpath_volumes_outside_state() { +#if ENABLE_GCODE_VIEWER + BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); +#else // tolerance to avoid false detection at bed edges static const double tolerance_x = 0.05; static const double tolerance_y = 0.05; @@ -7067,15 +7110,23 @@ void GLCanvas3D::_update_toolpath_volumes_outside_state() print_volume.min(2) = -1e10; } } +#endif // ENABLE_GCODE_VIEWER for (GLVolume* volume : m_volumes.volumes) { +#if ENABLE_GCODE_VIEWER + volume->is_outside = ((test_volume.radius() > 0.0) && volume->is_extrusion_path) ? !test_volume.contains(volume->bounding_box()) : false; +#else volume->is_outside = ((print_volume.radius() > 0.0) && volume->is_extrusion_path) ? !print_volume.contains(volume->bounding_box()) : false; +#endif // ENABLE_GCODE_VIEWER } } void GLCanvas3D::_update_sla_shells_outside_state() { +#if ENABLE_GCODE_VIEWER + BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); +#else // tolerance to avoid false detection at bed edges static const double tolerance_x = 0.05; static const double tolerance_y = 0.05; @@ -7092,17 +7143,34 @@ void GLCanvas3D::_update_sla_shells_outside_state() print_volume.min(2) = -1e10; } } +#endif // ENABLE_GCODE_VIEWER for (GLVolume* volume : m_volumes.volumes) { +#if ENABLE_GCODE_VIEWER + volume->is_outside = ((test_volume.radius() > 0.0) && volume->shader_outside_printer_detection_enabled) ? !test_volume.contains(volume->transformed_convex_hull_bounding_box()) : false; +#else volume->is_outside = ((print_volume.radius() > 0.0) && volume->shader_outside_printer_detection_enabled) ? !print_volume.contains(volume->transformed_convex_hull_bounding_box()) : false; +#endif // ENABLE_GCODE_VIEWER } } void GLCanvas3D::_show_warning_texture_if_needed(WarningTexture::Warning warning) { _set_current(); +#if ENABLE_GCODE_VIEWER + bool show = false; + if (!m_volumes.empty()) + show = _is_any_volume_outside(); + else + { + BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); + show = (test_volume.radius() > 0.0) ? !test_volume.contains(m_gcode_viewer.get_bounding_box()) : false; + } + _set_warning_texture(warning, show); +#else _set_warning_texture(warning, _is_any_volume_outside()); +#endif // ENABLE_GCODE_VIEWER } std::vector GLCanvas3D::_parse_colors(const std::vector& colors) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index ed034bd28c..5684901f3d 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -846,8 +846,10 @@ private: #endif // !ENABLE_GCODE_VIEWER // Load SLA objects and support structures for objects, for which the slaposSliceSupports step has been finished. void _load_sla_shells(); +#if !ENABLE_GCODE_VIEWER // sets gcode geometry visibility according to user selection void _update_gcode_volumes_visibility(const GCodePreviewData& preview_data); +#endif // !ENABLE_GCODE_VIEWER void _update_toolpath_volumes_outside_state(); void _update_sla_shells_outside_state(); void _show_warning_texture_if_needed(WarningTexture::Warning warning); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 05d6071099..4f47c2d18f 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -1,5 +1,7 @@ #include "libslic3r/libslic3r.h" +#if !ENABLE_GCODE_VIEWER #include "libslic3r/GCode/PreviewData.hpp" +#endif // !ENABLE_GCODE_VIEWER #include "GUI_Preview.hpp" #include "GUI_App.hpp" #include "GUI.hpp" @@ -172,8 +174,8 @@ void View3D::render() #if ENABLE_GCODE_VIEWER Preview::Preview( - wxWindow * parent, Model * model, DynamicPrintConfig * config, - BackgroundSlicingProcess * process, GCodePreviewData * gcode_preview_data, GCodeProcessor::Result * gcode_result, std::function schedule_background_process_func) + wxWindow* parent, Model* model, DynamicPrintConfig* config, + BackgroundSlicingProcess* process, GCodeProcessor::Result* gcode_result, std::function schedule_background_process_func) #else Preview::Preview( wxWindow* parent, Model* model, DynamicPrintConfig* config, @@ -200,9 +202,10 @@ Preview::Preview( #endif // ENABLE_GCODE_VIEWER , m_config(config) , m_process(process) - , m_gcode_preview_data(gcode_preview_data) #if ENABLE_GCODE_VIEWER , m_gcode_result(gcode_result) +#else + , m_gcode_preview_data(gcode_preview_data) #endif // ENABLE_GCODE_VIEWER , m_number_extruders(1) , m_preferred_color_mode("feature") @@ -401,8 +404,13 @@ void Preview::set_number_extruders(unsigned int number_extruders) int tool_idx = m_choice_view_type->FindString(_(L("Tool"))); int type = (number_extruders > 1) ? tool_idx /* color by a tool number */ : 0; // color by a feature type m_choice_view_type->SetSelection(type); +#if ENABLE_GCODE_VIEWER + if ((0 <= type) && (type < static_cast(GCodeViewer::EViewType::Count))) + m_canvas->set_gcode_view_preview_type(static_cast(type)); +#else if ((0 <= type) && (type < (int)GCodePreviewData::Extrusion::Num_View_Types)) m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type; +#endif // ENABLE_GCODE_VIEWER m_preferred_color_mode = (type == tool_idx) ? "tool_or_feature" : "feature"; } @@ -679,8 +687,13 @@ void Preview::update_view_type(bool slice_completed) int type = m_choice_view_type->FindString(choice); if (m_choice_view_type->GetSelection() != type) { m_choice_view_type->SetSelection(type); +#if ENABLE_GCODE_VIEWER + if ((0 <= type) && (type < static_cast(GCodeViewer::EViewType::Count))) + m_canvas->set_gcode_view_preview_type(static_cast(type)); +#else if (0 <= type && type < (int)GCodePreviewData::Extrusion::Num_View_Types) m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type; +#endif // ENABLE_GCODE_VIEWER m_preferred_color_mode = "feature"; } } diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 90943de006..a11a474ccf 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -124,8 +124,8 @@ class Preview : public wxPanel public: #if ENABLE_GCODE_VIEWER -Preview(wxWindow * parent, Model * model, DynamicPrintConfig * config, - BackgroundSlicingProcess * process, GCodePreviewData * gcode_preview_data, GCodeProcessor::Result * gcode_result, std::function schedule_background_process = []() {}); +Preview(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process, + GCodeProcessor::Result* gcode_result, std::function schedule_background_process = []() {}); #else Preview(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function schedule_background_process = []() {}); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 7d6497731c..c4a43fbb00 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -33,7 +33,9 @@ #include "libslic3r/Format/STL.hpp" #include "libslic3r/Format/AMF.hpp" #include "libslic3r/Format/3mf.hpp" +#if !ENABLE_GCODE_VIEWER #include "libslic3r/GCode/PreviewData.hpp" +#endif // !ENABLE_GCODE_VIEWER #include "libslic3r/GCode/ThumbnailData.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/SLA/Hollowing.hpp" @@ -1528,9 +1530,10 @@ struct Plater::priv Slic3r::SLAPrint sla_print; Slic3r::Model model; PrinterTechnology printer_technology = ptFFF; - Slic3r::GCodePreviewData gcode_preview_data; #if ENABLE_GCODE_VIEWER Slic3r::GCodeProcessor::Result gcode_result; +#else + Slic3r::GCodePreviewData gcode_preview_data; #endif // ENABLE_GCODE_VIEWER // GUI elements @@ -1840,9 +1843,10 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) background_process.set_fff_print(&fff_print); background_process.set_sla_print(&sla_print); - background_process.set_gcode_preview_data(&gcode_preview_data); #if ENABLE_GCODE_VIEWER background_process.set_gcode_result(&gcode_result); +#else + background_process.set_gcode_preview_data(&gcode_preview_data); #endif // ENABLE_GCODE_VIEWER background_process.set_thumbnail_cb([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) { @@ -1868,7 +1872,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D = new View3D(q, &model, config, &background_process); #if ENABLE_GCODE_VIEWER - preview = new Preview(q, &model, config, &background_process, &gcode_preview_data, &gcode_result, [this]() { schedule_background_process(); }); + preview = new Preview(q, &model, config, &background_process, &gcode_result, [this]() { schedule_background_process(); }); #else preview = new Preview(q, &model, config, &background_process, &gcode_preview_data, [this]() { schedule_background_process(); }); #endif // ENABLE_GCODE_VIEWER From 27b9f856077009be98fad60920ea31d335dabd25 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 7 May 2020 11:24:36 +0200 Subject: [PATCH 062/503] Fixed build when tech ENABLE_GCODE_VIEWER is disabled + fixed perl code --- src/slic3r/GUI/DoubleSlider.cpp | 2 +- src/slic3r/GUI/GLCanvas3D.hpp | 1 - xs/src/perlglue.cpp | 4 +- xs/xsp/GCode.xsp | 66 +++++++++++++++++---------------- xs/xsp/Print.xsp | 2 +- xs/xsp/my.map | 8 ++-- xs/xsp/typemap.xspt | 8 ++-- 7 files changed, 50 insertions(+), 41 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 0a89333714..3475f50246 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -1950,7 +1950,7 @@ std::string TickCodeInfo::get_color_for_tick(TickCode tick, const std::string& c if (mode == t_mode::SingleExtruder && code == ColorChangeCode && m_use_default_colors) { #if ENABLE_GCODE_VIEWER - const std::vector& colors = ColorPrintColors::get(); + const std::vector& colors = Slic3r::ColorPrintColors::get(); #else const std::vector& colors = GCodePreviewData::ColorPrintColors(); #endif // ENABLE_GCODE_VIEWER diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index cc43e7d145..294dbabe17 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -640,7 +640,6 @@ public: void set_toolpaths_z_range(const std::array& range); #else std::vector get_current_print_zs(bool active_only) const; - void set_toolpaths_range(double low, double high); #endif // ENABLE_GCODE_VIEWER void set_toolpaths_range(double low, double high); diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index c3cd7e6165..64cf9378fc 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -15,7 +15,9 @@ REGISTER_CLASS(Filler, "Filler"); REGISTER_CLASS(Flow, "Flow"); REGISTER_CLASS(CoolingBuffer, "GCode::CoolingBuffer"); REGISTER_CLASS(GCode, "GCode"); -REGISTER_CLASS(GCodePreviewData, "GCode::PreviewData"); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//REGISTER_CLASS(GCodePreviewData, "GCode::PreviewData"); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // REGISTER_CLASS(GCodeSender, "GCode::Sender"); REGISTER_CLASS(Layer, "Layer"); REGISTER_CLASS(SupportLayer, "Layer::Support"); diff --git a/xs/xsp/GCode.xsp b/xs/xsp/GCode.xsp index 9e04edd4c6..5799cafdbd 100644 --- a/xs/xsp/GCode.xsp +++ b/xs/xsp/GCode.xsp @@ -26,14 +26,16 @@ croak("%s\n", e.what()); } %}; - void do_export_w_preview(Print *print, const char *path, GCodePreviewData *preview_data) - %code%{ - try { - THIS->do_export(print, path, preview_data); - } catch (std::exception& e) { - croak("%s\n", e.what()); - } - %}; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// void do_export_w_preview(Print *print, const char *path, GCodePreviewData *preview_data) +// %code%{ +// try { +// THIS->do_export(print, path, preview_data); +// } catch (std::exception& e) { +// croak("%s\n", e.what()); +// } +// %}; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Ref origin() %code{% RETVAL = &(THIS->origin()); %}; @@ -60,26 +62,28 @@ %code{% RETVAL = const_cast(static_cast(static_cast(&THIS->config()))); %}; }; -%name{Slic3r::GCode::PreviewData} class GCodePreviewData { - GCodePreviewData(); - ~GCodePreviewData(); - void reset(); - bool empty() const; - void set_type(int type) - %code%{ - if ((0 <= type) && (type < GCodePreviewData::Extrusion::Num_View_Types)) - THIS->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type; - %}; - int type() %code%{ RETVAL = (int)THIS->extrusion.view_type; %}; - void set_extrusion_flags(int flags) - %code%{ THIS->extrusion.role_flags = (unsigned int)flags; %}; - void set_travel_visible(bool visible) - %code%{ THIS->travel.is_visible = visible; %}; - void set_retractions_visible(bool visible) - %code%{ THIS->retraction.is_visible = visible; %}; - void set_unretractions_visible(bool visible) - %code%{ THIS->unretraction.is_visible = visible; %}; - void set_shells_visible(bool visible) - %code%{ THIS->shell.is_visible = visible; %}; - void set_extrusion_paths_colors(std::vector colors); -}; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//%name{Slic3r::GCode::PreviewData} class GCodePreviewData { +// GCodePreviewData(); +// ~GCodePreviewData(); +// void reset(); +// bool empty() const; +// void set_type(int type) +// %code%{ +// if ((0 <= type) && (type < GCodePreviewData::Extrusion::Num_View_Types)) +// THIS->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type; +// %}; +// int type() %code%{ RETVAL = (int)THIS->extrusion.view_type; %}; +// void set_extrusion_flags(int flags) +// %code%{ THIS->extrusion.role_flags = (unsigned int)flags; %}; +// void set_travel_visible(bool visible) +// %code%{ THIS->travel.is_visible = visible; %}; +// void set_retractions_visible(bool visible) +// %code%{ THIS->retraction.is_visible = visible; %}; +// void set_unretractions_visible(bool visible) +// %code%{ THIS->unretraction.is_visible = visible; %}; +// void set_shells_visible(bool visible) +// %code%{ THIS->shell.is_visible = visible; %}; +// void set_extrusion_paths_colors(std::vector colors); +//}; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 160fd3e60c..0952513ca3 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -164,7 +164,7 @@ _constant() void export_gcode(char *path_template) %code%{ try { - THIS->export_gcode(path_template, nullptr, nullptr); + THIS->export_gcode(path_template, nullptr); } catch (std::exception& e) { croak("%s\n", e.what()); } diff --git a/xs/xsp/my.map b/xs/xsp/my.map index fd50d29751..44b9eaa893 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -191,9 +191,11 @@ GCode* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T -GCodePreviewData* O_OBJECT_SLIC3R -Ref O_OBJECT_SLIC3R_T -Clone O_OBJECT_SLIC3R_T +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//GCodePreviewData* O_OBJECT_SLIC3R +//Ref O_OBJECT_SLIC3R_T +//Clone O_OBJECT_SLIC3R_T +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ MotionPlanner* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index 7e277703b8..5ee142029b 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -155,9 +155,11 @@ %typemap{Ref}{simple}; %typemap{Clone}{simple}; -%typemap{GCodePreviewData*}; -%typemap{Ref}{simple}; -%typemap{Clone}{simple}; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//%typemap{GCodePreviewData*}; +//%typemap{Ref}{simple}; +//%typemap{Clone}{simple}; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ %typemap{Points}; %typemap{Pointfs}; From 277b340481617936f4c5c4ca82f3e8ca8e268826 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 7 May 2020 12:03:17 +0200 Subject: [PATCH 063/503] Attempt to fix build on OsX --- src/slic3r/GUI/DoubleSlider.cpp | 5 ++++- xs/src/perlglue.cpp | 2 -- xs/xsp/GCode.xsp | 4 ---- xs/xsp/my.map | 2 -- xs/xsp/typemap.xspt | 2 -- 5 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 3475f50246..f0940fbfdb 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -1,3 +1,6 @@ +#if ENABLE_GCODE_VIEWER +#include "libslic3r/libslic3r.h" +#endif // ENABLE_GCODE_VIEWER #include "wxExtensions.hpp" #if ENABLE_GCODE_VIEWER #include "libslic3r/GCode.hpp" @@ -1950,7 +1953,7 @@ std::string TickCodeInfo::get_color_for_tick(TickCode tick, const std::string& c if (mode == t_mode::SingleExtruder && code == ColorChangeCode && m_use_default_colors) { #if ENABLE_GCODE_VIEWER - const std::vector& colors = Slic3r::ColorPrintColors::get(); + const std::vector& colors = ColorPrintColors::get(); #else const std::vector& colors = GCodePreviewData::ColorPrintColors(); #endif // ENABLE_GCODE_VIEWER diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index 64cf9378fc..47961c6231 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -15,9 +15,7 @@ REGISTER_CLASS(Filler, "Filler"); REGISTER_CLASS(Flow, "Flow"); REGISTER_CLASS(CoolingBuffer, "GCode::CoolingBuffer"); REGISTER_CLASS(GCode, "GCode"); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ //REGISTER_CLASS(GCodePreviewData, "GCode::PreviewData"); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // REGISTER_CLASS(GCodeSender, "GCode::Sender"); REGISTER_CLASS(Layer, "Layer"); REGISTER_CLASS(SupportLayer, "Layer::Support"); diff --git a/xs/xsp/GCode.xsp b/xs/xsp/GCode.xsp index 5799cafdbd..1536c874b5 100644 --- a/xs/xsp/GCode.xsp +++ b/xs/xsp/GCode.xsp @@ -26,7 +26,6 @@ croak("%s\n", e.what()); } %}; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // void do_export_w_preview(Print *print, const char *path, GCodePreviewData *preview_data) // %code%{ // try { @@ -35,7 +34,6 @@ // croak("%s\n", e.what()); // } // %}; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Ref origin() %code{% RETVAL = &(THIS->origin()); %}; @@ -62,7 +60,6 @@ %code{% RETVAL = const_cast(static_cast(static_cast(&THIS->config()))); %}; }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ //%name{Slic3r::GCode::PreviewData} class GCodePreviewData { // GCodePreviewData(); // ~GCodePreviewData(); @@ -86,4 +83,3 @@ // %code%{ THIS->shell.is_visible = visible; %}; // void set_extrusion_paths_colors(std::vector colors); //}; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ diff --git a/xs/xsp/my.map b/xs/xsp/my.map index 44b9eaa893..7e51b237c0 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -191,11 +191,9 @@ GCode* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ //GCodePreviewData* O_OBJECT_SLIC3R //Ref O_OBJECT_SLIC3R_T //Clone O_OBJECT_SLIC3R_T -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ MotionPlanner* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index 5ee142029b..385b50f1aa 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -155,11 +155,9 @@ %typemap{Ref}{simple}; %typemap{Clone}{simple}; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ //%typemap{GCodePreviewData*}; //%typemap{Ref}{simple}; //%typemap{Clone}{simple}; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ %typemap{Points}; %typemap{Pointfs}; From 383d7f2d73066c1efdca893897b999f2d013fd06 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 7 May 2020 13:07:56 +0200 Subject: [PATCH 064/503] 2nd attempt to fix build on OsX --- src/slic3r/GUI/DoubleSlider.cpp | 5 ++--- src/slic3r/GUI/DoubleSlider.hpp | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index f0940fbfdb..4732ff2613 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -1,10 +1,9 @@ -#if ENABLE_GCODE_VIEWER #include "libslic3r/libslic3r.h" -#endif // ENABLE_GCODE_VIEWER -#include "wxExtensions.hpp" #if ENABLE_GCODE_VIEWER +#include "DoubleSlider.hpp" #include "libslic3r/GCode.hpp" #else +#include "wxExtensions.hpp" #include "libslic3r/GCode/PreviewData.hpp" #endif // ENABLE_GCODE_VIEWER #include "GUI.hpp" diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index bf8f54d6c9..36bff17e94 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -4,7 +4,9 @@ #include "libslic3r/CustomGCode.hpp" #include "wxExtensions.hpp" +#if !ENABLE_GCODE_VIEWER #include +#endif // !ENABLE_GCODE_VIEWER #include #include #include From c02a77d94215348eb89f4143269a8cd59160c1ca Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 11 May 2020 13:09:26 +0200 Subject: [PATCH 065/503] GCodeViewer -> Prototype for tool marker --- resources/shaders/gouraud_light.fs | 11 ++ resources/shaders/gouraud_light.vs | 38 ++++++ src/slic3r/GUI/GCodeViewer.cpp | 201 ++++++++++++++++++++++++----- src/slic3r/GUI/GCodeViewer.hpp | 33 +++-- src/slic3r/GUI/GUI_Preview.cpp | 3 +- 5 files changed, 244 insertions(+), 42 deletions(-) create mode 100644 resources/shaders/gouraud_light.fs create mode 100644 resources/shaders/gouraud_light.vs diff --git a/resources/shaders/gouraud_light.fs b/resources/shaders/gouraud_light.fs new file mode 100644 index 0000000000..1a58abc852 --- /dev/null +++ b/resources/shaders/gouraud_light.fs @@ -0,0 +1,11 @@ +#version 110 + +uniform vec4 uniform_color; + +// x = tainted, y = specular; +varying vec2 intensity; + +void main() +{ + gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); +} diff --git a/resources/shaders/gouraud_light.vs b/resources/shaders/gouraud_light.vs new file mode 100644 index 0000000000..d4f71938a9 --- /dev/null +++ b/resources/shaders/gouraud_light.vs @@ -0,0 +1,38 @@ +#version 110 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +// x = tainted, y = specular; +varying vec2 intensity; + +void main() +{ + // First transform the normal into camera space and normalize the result. + vec3 normal = normalize(gl_NormalMatrix * gl_Normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec3 position = (gl_ModelViewMatrix * gl_Vertex).xyz; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + gl_Position = ftransform(); +} diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 0893a7d465..269780d950 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -3,6 +3,8 @@ #if ENABLE_GCODE_VIEWER #include "libslic3r/Print.hpp" +#include "libslic3r/TriangleMesh.hpp" +#include "libslic3r/Geometry.hpp" #include "GUI_App.hpp" #include "PresetBundle.hpp" #include "Camera.hpp" @@ -146,20 +148,160 @@ GCodeViewer::Color GCodeViewer::Extrusions::Range::get_color_at(float value) con void GCodeViewer::SequentialView::Marker::init() { - if (m_initialized) - return; + Pointf3s vertices; + std::vector triangles; + // arrow tip + vertices.emplace_back(0.0, 0.0, 0.0); + vertices.emplace_back(0.5, -0.5, 1.0); + vertices.emplace_back(0.5, 0.5, 1.0); + vertices.emplace_back(-0.5, 0.5, 1.0); + vertices.emplace_back(-0.5, -0.5, 1.0); + + triangles.emplace_back(0, 1, 4); + triangles.emplace_back(0, 2, 1); + triangles.emplace_back(0, 3, 2); + triangles.emplace_back(0, 4, 3); + triangles.emplace_back(1, 2, 4); + triangles.emplace_back(2, 3, 4); + + // arrow stem + vertices.emplace_back(0.25, -0.25, 1.0); + vertices.emplace_back(0.25, 0.25, 1.0); + vertices.emplace_back(-0.25, 0.25, 1.0); + vertices.emplace_back(-0.25, -0.25, 1.0); + vertices.emplace_back(0.25, -0.25, 3.0); + vertices.emplace_back(0.25, 0.25, 3.0); + vertices.emplace_back(-0.25, 0.25, 3.0); + vertices.emplace_back(-0.25, -0.25, 3.0); + + triangles.emplace_back(5, 9, 8); + triangles.emplace_back(8, 9, 12); + triangles.emplace_back(6, 10, 5); + triangles.emplace_back(5, 10, 9); + triangles.emplace_back(7, 11, 6); + triangles.emplace_back(6, 11, 10); + triangles.emplace_back(8, 12, 7); + triangles.emplace_back(7, 12, 11); + triangles.emplace_back(9, 10, 12); + triangles.emplace_back(12, 10, 11); + + TriangleMesh mesh(vertices, triangles); + mesh.require_shared_vertices(); + + init_from_mesh(mesh); +} + +bool GCodeViewer::SequentialView::Marker::init_from_mesh(const TriangleMesh& mesh) +{ + auto get_normal = [](const std::array& triangle) { + return (triangle[1] - triangle[0]).cross(triangle[2] - triangle[0]).normalized(); + }; + + reset(); + + // vertex data -> load from mesh + std::vector vertices(6 * mesh.its.vertices.size()); + for (size_t i = 0; i < mesh.its.vertices.size(); ++i) { + ::memcpy(static_cast(&vertices[i * 6]), static_cast(mesh.its.vertices[i].data()), 3 * sizeof(float)); + } + + // indices/normals data -> load from mesh + std::vector indices(3 * mesh.its.indices.size()); + for (size_t i = 0; i < mesh.its.indices.size(); ++i) { + const stl_triangle_vertex_indices& triangle = mesh.its.indices[i]; + for (size_t j = 0; j < 3; ++j) { + indices[i * 3 + j] = static_cast(triangle[j]); + } + Vec3f normal = get_normal({ mesh.its.vertices[triangle[0]], mesh.its.vertices[triangle[1]], mesh.its.vertices[triangle[2]] }); + ::memcpy(static_cast(&vertices[3 + static_cast(triangle[0]) * 6]), static_cast(normal.data()), 3 * sizeof(float)); + ::memcpy(static_cast(&vertices[3 + static_cast(triangle[1]) * 6]), static_cast(normal.data()), 3 * sizeof(float)); + ::memcpy(static_cast(&vertices[3 + static_cast(triangle[2]) * 6]), static_cast(normal.data()), 3 * sizeof(float)); + } + + m_indices_count = static_cast(indices.size()); + + // vertex data -> send to gpu + glsafe(::glGenBuffers(1, &m_vbo_id)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id)); + glsafe(::glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + + // indices data -> send to gpu + glsafe(::glGenBuffers(1, &m_ibo_id)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo_id)); + glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + return init_shader(); } void GCodeViewer::SequentialView::Marker::render() const { - if (!m_initialized) + if (!m_visible || !m_shader.is_initialized()) return; + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + m_shader.start_using(); + GLint color_id = ::glGetUniformLocation(m_shader.get_shader_program_id(), "uniform_color"); + if (color_id >= 0) + glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)m_color.data())); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id)); + glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)0)); + glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float)))); + + glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); + + glsafe(::glPushMatrix()); + glsafe(::glMultMatrixf(m_world_transform.data())); + + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo_id)); + glsafe(::glDrawElements(GL_TRIANGLES, static_cast(m_indices_count), GL_UNSIGNED_INT, (const void*)0)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + glsafe(::glPopMatrix()); + + glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); + glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + + m_shader.stop_using(); + + glsafe(::glDisable(GL_BLEND)); } -const std::vector GCodeViewer::Extrusion_Role_Colors{ { +void GCodeViewer::SequentialView::Marker::reset() +{ + // release gpu memory + if (m_ibo_id > 0) { + glsafe(::glDeleteBuffers(1, &m_ibo_id)); + m_ibo_id = 0; + } + + if (m_vbo_id > 0) { + glsafe(::glDeleteBuffers(1, &m_vbo_id)); + m_vbo_id = 0; + } + + m_indices_count = 0; +} + +bool GCodeViewer::SequentialView::Marker::init_shader() +{ + if (!m_shader.init("gouraud_light.vs", "gouraud_light.fs")) { + BOOST_LOG_TRIVIAL(error) << "Unable to initialize gouraud_light shader: please, check that the files gouraud_light.vs and gouraud_light.fs are available"; + return false; + } + + return true; +} + +const std::vector GCodeViewer::Extrusion_Role_Colors {{ { 0.50f, 0.50f, 0.50f }, // erNone { 1.00f, 1.00f, 0.40f }, // erPerimeter { 1.00f, 0.65f, 0.00f }, // erExternalPerimeter @@ -178,13 +320,13 @@ const std::vector GCodeViewer::Extrusion_Role_Colors{ { { 0.00f, 0.00f, 0.00f } // erMixed }}; -const std::vector GCodeViewer::Travel_Colors{ { +const std::vector GCodeViewer::Travel_Colors {{ { 0.0f, 0.0f, 0.5f }, // Move { 0.0f, 0.5f, 0.0f }, // Extrude { 0.5f, 0.0f, 0.0f } // Retract }}; -const std::vector GCodeViewer::Range_Colors{ { +const std::vector GCodeViewer::Range_Colors {{ { 0.043f, 0.173f, 0.478f }, // bluish { 0.075f, 0.349f, 0.522f }, { 0.110f, 0.533f, 0.569f }, @@ -292,10 +434,13 @@ void GCodeViewer::render() const m_statistics.reset_opengl(); #endif // ENABLE_GCODE_VIEWER_STATISTICS + if (m_roles.empty()) + return; + glsafe(::glEnable(GL_DEPTH_TEST)); render_toolpaths(); - if (m_sequential_view.marker.visible) - m_sequential_view.marker.render(); + m_sequential_view.marker.set_world_transform(Geometry::assemble_transform(m_sequential_view.current_position.cast() + 0.5 * Vec3d::UnitZ(), { 0.0, 0.0, 0.0 }, { 4.0, 4.0, 4.0 }, { 1.0, 1.0, 1.0 }).cast()); + m_sequential_view.marker.render(); render_shells(); render_legend(); render_sequential_bar(); @@ -332,7 +477,8 @@ unsigned int GCodeViewer::get_options_visibility_flags() const flags = set_flag(flags, 5, is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Pause_Print)); flags = set_flag(flags, 6, is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Custom_GCode)); flags = set_flag(flags, 7, m_shells.visible); - flags = set_flag(flags, 8, is_legend_enabled()); + flags = set_flag(flags, 8, m_sequential_view.marker.is_visible()); + flags = set_flag(flags, 9, is_legend_enabled()); return flags; } @@ -350,7 +496,8 @@ void GCodeViewer::set_options_visibility_from_flags(unsigned int flags) set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Pause_Print, is_flag_set(5)); set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Custom_GCode, is_flag_set(6)); m_shells.visible = is_flag_set(7); - enable_legend(is_flag_set(8)); + m_sequential_view.marker.set_visible(is_flag_set(8)); + enable_legend(is_flag_set(9)); } bool GCodeViewer::init_shaders() @@ -706,7 +853,7 @@ void GCodeViewer::render_toolpaths() const { auto set_color = [](GLint current_program_id, const Color& color) { if (current_program_id > 0) { - GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1; + GLint color_id = ::glGetUniformLocation(current_program_id, "uniform_color"); if (color_id >= 0) { glsafe(::glUniform3fv(color_id, 1, (const GLfloat*)color.data())); return; @@ -737,10 +884,7 @@ void GCodeViewer::render_toolpaths() const GCodeProcessor::EMoveType type = buffer_type(i); buffer.shader.start_using(); - - GLint current_program_id; - glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id)); - + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.ibo_id)); switch (type) @@ -748,7 +892,7 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Tool_change: { Color color = { 1.0f, 1.0f, 1.0f }; - set_color(current_program_id, color); + set_color(static_cast(buffer.shader.get_shader_program_id()), color); for (const RenderPath& path : buffer.render_paths) { glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -764,7 +908,7 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Color_change: { Color color = { 1.0f, 0.0f, 0.0f }; - set_color(current_program_id, color); + set_color(static_cast(buffer.shader.get_shader_program_id()), color); for (const RenderPath& path : buffer.render_paths) { glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -780,7 +924,7 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Pause_Print: { Color color = { 0.0f, 1.0f, 0.0f }; - set_color(current_program_id, color); + set_color(static_cast(buffer.shader.get_shader_program_id()), color); for (const RenderPath& path : buffer.render_paths) { glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -796,7 +940,7 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Custom_GCode: { Color color = { 0.0f, 0.0f, 1.0f }; - set_color(current_program_id, color); + set_color(static_cast(buffer.shader.get_shader_program_id()), color); for (const RenderPath& path : buffer.render_paths) { glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -812,7 +956,7 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Retract: { Color color = { 1.0f, 0.0f, 1.0f }; - set_color(current_program_id, color); + set_color(static_cast(buffer.shader.get_shader_program_id()), color); for (const RenderPath& path : buffer.render_paths) { glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -828,7 +972,7 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Unretract: { Color color = { 0.0f, 1.0f, 1.0f }; - set_color(current_program_id, color); + set_color(static_cast(buffer.shader.get_shader_program_id()), color); for (const RenderPath& path : buffer.render_paths) { glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -845,7 +989,7 @@ void GCodeViewer::render_toolpaths() const { for (const RenderPath& path : buffer.render_paths) { - set_color(current_program_id, path.color); + set_color(static_cast(buffer.shader.get_shader_program_id()), path.color); glsafe(::glMultiDrawElements(GL_LINE_STRIP, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_line_strip_calls_count; @@ -858,7 +1002,7 @@ void GCodeViewer::render_toolpaths() const { for (const RenderPath& path : buffer.render_paths) { - set_color(current_program_id, path.color); + set_color(static_cast(buffer.shader.get_shader_program_id()), path.color); glsafe(::glMultiDrawElements(GL_LINE_STRIP, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_line_strip_calls_count; @@ -897,7 +1041,7 @@ void GCodeViewer::render_legend() const static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); static const ImU32 ICON_BORDER_COLOR = ImGui::GetColorU32(ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); - if (!m_legend_enabled || m_roles.empty()) + if (!m_legend_enabled) return; ImGuiWrapper& imgui = *wxGetApp().imgui(); @@ -1130,9 +1274,6 @@ void GCodeViewer::render_sequential_bar() const refresh_render_paths(true); }; - if (m_roles.empty()) - return; - if (m_sequential_view.last <= m_sequential_view.first) return; @@ -1204,9 +1345,6 @@ void GCodeViewer::render_sequential_bar() const ImGui::SameLine(); imgui.text(std::to_string(i_max)); - ImGui::Separator(); - ImGui::Checkbox(I18N::translate_utf8(L("Show marker")).c_str(), &m_sequential_view.marker.visible); - imgui.end(); ImGui::PopStyleVar(); } @@ -1217,9 +1355,6 @@ void GCodeViewer::render_statistics() const static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); static const float offset = 250.0f; - if (m_roles.empty()) - return; - ImGuiWrapper& imgui = *wxGetApp().imgui(); imgui.set_next_window_pos(0.5f * wxGetApp().plater()->get_current_canvas3D()->get_canvas_size().get_width(), 0.0f, ImGuiCond_Once, 0.5f, 0.0f); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 5354ae067f..d34e8ee4bd 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -9,7 +9,10 @@ #include namespace Slic3r { + class Print; +class TriangleMesh; + namespace GUI { class GCodeViewer @@ -73,8 +76,8 @@ class GCodeViewer struct IBuffer { unsigned int ibo_id{ 0 }; - Shader shader; size_t indices_count{ 0 }; + Shader shader; std::vector paths; std::vector render_paths; bool visible{ false }; @@ -147,19 +150,33 @@ class GCodeViewer struct SequentialView { - struct Marker + class Marker { - private: - bool m_initialized{ false }; + unsigned int m_vbo_id{ 0 }; + unsigned int m_ibo_id{ 0 }; + size_t m_indices_count{ 0 }; + Transform3f m_world_transform; + std::array m_color{ 1.0f, 1.0f, 1.0f, 1.0f }; + bool m_visible{ false }; + Shader m_shader; public: - unsigned int vbo_id{ 0 }; - unsigned int ibo_id{ 0 }; - bool visible{ false }; - Shader shader; + ~Marker() { reset(); } void init(); + bool init_from_mesh(const TriangleMesh& mesh); + + void set_world_transform(const Transform3f& transform) { m_world_transform = transform; } + + void set_color(const std::array& color) { m_color = color; } + + bool is_visible() const { return m_visible; } + void set_visible(bool visible) { m_visible = visible; } void render() const; + void reset(); + + private: + bool init_shader(); }; unsigned int first{ 0 }; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 4f47c2d18f..fc97796749 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -298,8 +298,9 @@ bool Preview::init(wxWindow* parent, Model* model) _L("Pause prints") + "|0|" + _L("Custom GCodes") + "|0|" + _L("Shells") + "|0|" + + _L("Tool marker") + "|1|" + _L("Legend") + "|1" - ); +); Slic3r::GUI::create_combochecklist(m_combochecklist_options, GUI::into_u8(_L("Options")), options_items); #else m_checkbox_travel = new wxCheckBox(this, wxID_ANY, _(L("Travel"))); From 769cca4b25410a2cdaf43af032ff301e443bbe43 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 11 May 2020 16:26:35 +0200 Subject: [PATCH 066/503] GCodeViewer -> Enhanced tool marker + refactoring (added new base class for OpenGL models) --- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/GCodeViewer.cpp | 129 +------------------ src/slic3r/GUI/GCodeViewer.hpp | 13 +- src/slic3r/GUI/GLModel.cpp | 229 +++++++++++++++++++++++++++++++++ src/slic3r/GUI/GLModel.hpp | 46 +++++++ 5 files changed, 286 insertions(+), 133 deletions(-) create mode 100644 src/slic3r/GUI/GLModel.cpp create mode 100644 src/slic3r/GUI/GLModel.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index fefc12ba87..b085fad456 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -55,6 +55,8 @@ set(SLIC3R_GUI_SOURCES GUI/Gizmos/GLGizmoHollow.hpp GUI/GLSelectionRectangle.cpp GUI/GLSelectionRectangle.hpp + GUI/GLModel.hpp + GUI/GLModel.cpp GUI/GLTexture.hpp GUI/GLTexture.cpp GUI/GLToolbar.hpp diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 269780d950..ad3985c625 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -3,7 +3,6 @@ #if ENABLE_GCODE_VIEWER #include "libslic3r/Print.hpp" -#include "libslic3r/TriangleMesh.hpp" #include "libslic3r/Geometry.hpp" #include "GUI_App.hpp" #include "PresetBundle.hpp" @@ -148,92 +147,8 @@ GCodeViewer::Color GCodeViewer::Extrusions::Range::get_color_at(float value) con void GCodeViewer::SequentialView::Marker::init() { - Pointf3s vertices; - std::vector triangles; - - // arrow tip - vertices.emplace_back(0.0, 0.0, 0.0); - vertices.emplace_back(0.5, -0.5, 1.0); - vertices.emplace_back(0.5, 0.5, 1.0); - vertices.emplace_back(-0.5, 0.5, 1.0); - vertices.emplace_back(-0.5, -0.5, 1.0); - - triangles.emplace_back(0, 1, 4); - triangles.emplace_back(0, 2, 1); - triangles.emplace_back(0, 3, 2); - triangles.emplace_back(0, 4, 3); - triangles.emplace_back(1, 2, 4); - triangles.emplace_back(2, 3, 4); - - // arrow stem - vertices.emplace_back(0.25, -0.25, 1.0); - vertices.emplace_back(0.25, 0.25, 1.0); - vertices.emplace_back(-0.25, 0.25, 1.0); - vertices.emplace_back(-0.25, -0.25, 1.0); - vertices.emplace_back(0.25, -0.25, 3.0); - vertices.emplace_back(0.25, 0.25, 3.0); - vertices.emplace_back(-0.25, 0.25, 3.0); - vertices.emplace_back(-0.25, -0.25, 3.0); - - triangles.emplace_back(5, 9, 8); - triangles.emplace_back(8, 9, 12); - triangles.emplace_back(6, 10, 5); - triangles.emplace_back(5, 10, 9); - triangles.emplace_back(7, 11, 6); - triangles.emplace_back(6, 11, 10); - triangles.emplace_back(8, 12, 7); - triangles.emplace_back(7, 12, 11); - triangles.emplace_back(9, 10, 12); - triangles.emplace_back(12, 10, 11); - - TriangleMesh mesh(vertices, triangles); - mesh.require_shared_vertices(); - - init_from_mesh(mesh); -} - -bool GCodeViewer::SequentialView::Marker::init_from_mesh(const TriangleMesh& mesh) -{ - auto get_normal = [](const std::array& triangle) { - return (triangle[1] - triangle[0]).cross(triangle[2] - triangle[0]).normalized(); - }; - - reset(); - - // vertex data -> load from mesh - std::vector vertices(6 * mesh.its.vertices.size()); - for (size_t i = 0; i < mesh.its.vertices.size(); ++i) { - ::memcpy(static_cast(&vertices[i * 6]), static_cast(mesh.its.vertices[i].data()), 3 * sizeof(float)); - } - - // indices/normals data -> load from mesh - std::vector indices(3 * mesh.its.indices.size()); - for (size_t i = 0; i < mesh.its.indices.size(); ++i) { - const stl_triangle_vertex_indices& triangle = mesh.its.indices[i]; - for (size_t j = 0; j < 3; ++j) { - indices[i * 3 + j] = static_cast(triangle[j]); - } - Vec3f normal = get_normal({ mesh.its.vertices[triangle[0]], mesh.its.vertices[triangle[1]], mesh.its.vertices[triangle[2]] }); - ::memcpy(static_cast(&vertices[3 + static_cast(triangle[0]) * 6]), static_cast(normal.data()), 3 * sizeof(float)); - ::memcpy(static_cast(&vertices[3 + static_cast(triangle[1]) * 6]), static_cast(normal.data()), 3 * sizeof(float)); - ::memcpy(static_cast(&vertices[3 + static_cast(triangle[2]) * 6]), static_cast(normal.data()), 3 * sizeof(float)); - } - - m_indices_count = static_cast(indices.size()); - - // vertex data -> send to gpu - glsafe(::glGenBuffers(1, &m_vbo_id)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id)); - glsafe(::glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - - // indices data -> send to gpu - glsafe(::glGenBuffers(1, &m_ibo_id)); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo_id)); - glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW)); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - - return init_shader(); + m_model.init_from(stilized_arrow(16, 0.5f, 1.0f, 0.25f, 2.0f)); + init_shader(); } void GCodeViewer::SequentialView::Marker::render() const @@ -249,56 +164,22 @@ void GCodeViewer::SequentialView::Marker::render() const if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)m_color.data())); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id)); - glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)0)); - glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float)))); - - glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); - glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); - glsafe(::glPushMatrix()); glsafe(::glMultMatrixf(m_world_transform.data())); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo_id)); - glsafe(::glDrawElements(GL_TRIANGLES, static_cast(m_indices_count), GL_UNSIGNED_INT, (const void*)0)); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + m_model.render(); glsafe(::glPopMatrix()); - glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); - - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - m_shader.stop_using(); glsafe(::glDisable(GL_BLEND)); } -void GCodeViewer::SequentialView::Marker::reset() +void GCodeViewer::SequentialView::Marker::init_shader() { - // release gpu memory - if (m_ibo_id > 0) { - glsafe(::glDeleteBuffers(1, &m_ibo_id)); - m_ibo_id = 0; - } - - if (m_vbo_id > 0) { - glsafe(::glDeleteBuffers(1, &m_vbo_id)); - m_vbo_id = 0; - } - - m_indices_count = 0; -} - -bool GCodeViewer::SequentialView::Marker::init_shader() -{ - if (!m_shader.init("gouraud_light.vs", "gouraud_light.fs")) { + if (!m_shader.init("gouraud_light.vs", "gouraud_light.fs")) BOOST_LOG_TRIVIAL(error) << "Unable to initialize gouraud_light shader: please, check that the files gouraud_light.vs and gouraud_light.fs are available"; - return false; - } - - return true; } const std::vector GCodeViewer::Extrusion_Role_Colors {{ diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index d34e8ee4bd..44f23b2852 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -5,6 +5,7 @@ #include "GLShader.hpp" #include "3DScene.hpp" #include "libslic3r/GCode/GCodeProcessor.hpp" +#include "GLModel.hpp" #include @@ -152,31 +153,25 @@ class GCodeViewer { class Marker { - unsigned int m_vbo_id{ 0 }; - unsigned int m_ibo_id{ 0 }; - size_t m_indices_count{ 0 }; + GL_Model m_model; Transform3f m_world_transform; std::array m_color{ 1.0f, 1.0f, 1.0f, 1.0f }; bool m_visible{ false }; Shader m_shader; public: - ~Marker() { reset(); } - void init(); - bool init_from_mesh(const TriangleMesh& mesh); void set_world_transform(const Transform3f& transform) { m_world_transform = transform; } - void set_color(const std::array& color) { m_color = color; } bool is_visible() const { return m_visible; } void set_visible(bool visible) { m_visible = visible; } + void render() const; - void reset(); private: - bool init_shader(); + void init_shader(); }; unsigned int first{ 0 }; diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp new file mode 100644 index 0000000000..336c699371 --- /dev/null +++ b/src/slic3r/GUI/GLModel.cpp @@ -0,0 +1,229 @@ +#include "libslic3r/libslic3r.h" +#include "GLModel.hpp" + +#include "3DScene.hpp" +#include "libslic3r/TriangleMesh.hpp" + +#include + +namespace Slic3r { +namespace GUI { + +bool GL_Model::init_from(const GLModelInitializationData& data) +{ + assert(!data.positions.empty() && !data.triangles.empty()); + assert(data.positions.size() == data.normals.size()); + + reset(); + + // vertices/normals data + std::vector vertices(6 * data.positions.size()); + for (size_t i = 0; i < data.positions.size(); ++i) { + ::memcpy(static_cast(&vertices[i * 6]), static_cast(data.positions[i].data()), 3 * sizeof(float)); + ::memcpy(static_cast(&vertices[3 + i * 6]), static_cast(data.normals[i].data()), 3 * sizeof(float)); + } + + // indices data + std::vector indices(3 * data.triangles.size()); + for (size_t i = 0; i < data.triangles.size(); ++i) { + for (size_t j = 0; j < 3; ++j) { + indices[i * 3 + j] = static_cast(data.triangles[i][j]); + } + } + + m_indices_count = static_cast(indices.size()); + + send_to_gpu(vertices, indices); + + return true; +} + +bool GL_Model::init_from(const TriangleMesh& mesh) +{ + auto get_normal = [](const std::array& triangle) { + return (triangle[1] - triangle[0]).cross(triangle[2] - triangle[0]).normalized(); + }; + + reset(); + + assert(!mesh.its.vertices.empty() && !mesh.its.indices.empty()); // call require_shared_vertices() before to pass the mesh to this method + + // vertices data -> load from mesh + std::vector vertices(6 * mesh.its.vertices.size()); + for (size_t i = 0; i < mesh.its.vertices.size(); ++i) { + ::memcpy(static_cast(&vertices[i * 6]), static_cast(mesh.its.vertices[i].data()), 3 * sizeof(float)); + } + + // indices/normals data -> load from mesh + std::vector indices(3 * mesh.its.indices.size()); + for (size_t i = 0; i < mesh.its.indices.size(); ++i) { + const stl_triangle_vertex_indices& triangle = mesh.its.indices[i]; + for (size_t j = 0; j < 3; ++j) { + indices[i * 3 + j] = static_cast(triangle[j]); + } + Vec3f normal = get_normal({ mesh.its.vertices[triangle[0]], mesh.its.vertices[triangle[1]], mesh.its.vertices[triangle[2]] }); + ::memcpy(static_cast(&vertices[3 + static_cast(triangle[0]) * 6]), static_cast(normal.data()), 3 * sizeof(float)); + ::memcpy(static_cast(&vertices[3 + static_cast(triangle[1]) * 6]), static_cast(normal.data()), 3 * sizeof(float)); + ::memcpy(static_cast(&vertices[3 + static_cast(triangle[2]) * 6]), static_cast(normal.data()), 3 * sizeof(float)); + } + + m_indices_count = static_cast(indices.size()); + + send_to_gpu(vertices, indices); + + return true; +} + +void GL_Model::reset() +{ + // release gpu memory + if (m_ibo_id > 0) { + glsafe(::glDeleteBuffers(1, &m_ibo_id)); + m_ibo_id = 0; + } + + if (m_vbo_id > 0) { + glsafe(::glDeleteBuffers(1, &m_vbo_id)); + m_vbo_id = 0; + } + + m_indices_count = 0; +} + +void GL_Model::render() const +{ + if (m_vbo_id == 0 || m_ibo_id == 0) + return; + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id)); + glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)0)); + glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float)))); + + glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); + + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo_id)); + glsafe(::glDrawElements(GL_TRIANGLES, static_cast(m_indices_count), GL_UNSIGNED_INT, (const void*)0)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); + glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); +} + +void GL_Model::send_to_gpu(const std::vector& vertices, const std::vector& indices) +{ + // vertex data -> send to gpu + glsafe(::glGenBuffers(1, &m_vbo_id)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id)); + glsafe(::glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + + // indices data -> send to gpu + glsafe(::glGenBuffers(1, &m_ibo_id)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo_id)); + glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); +} + +GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float tip_height, float stem_radius, float stem_height) +{ + GLModelInitializationData data; + + float angle_step = 2.0f * M_PI / static_cast(resolution); + std::vector cosines(resolution); + std::vector sines(resolution); + + for (int i = 0; i < resolution; ++i) + { + float angle = angle_step * static_cast(i); + cosines[i] = ::cos(angle); + sines[i] = -::sin(angle); + } + + // tip vertices/normals + data.positions.emplace_back(0.0f, 0.0f, 0.0f); + data.normals.emplace_back(-Vec3f::UnitZ()); + for (int i = 0; i < resolution; ++i) + { + data.positions.emplace_back(tip_radius * sines[i], tip_radius * cosines[i], tip_height); + data.normals.emplace_back(sines[i], cosines[i], 0.0f); + } + + // tip triangles + for (int i = 0; i < resolution; ++i) + { + int v3 = (i < resolution - 1) ? i + 2 : 1; + data.triangles.emplace_back(0, v3, i + 1); + } + + // tip cap outer perimeter vertices + for (int i = 0; i < resolution; ++i) + { + data.positions.emplace_back(tip_radius * sines[i], tip_radius * cosines[i], tip_height); + data.normals.emplace_back(Vec3f::UnitZ()); + } + + // tip cap inner perimeter vertices + for (int i = 0; i < resolution; ++i) + { + data.positions.emplace_back(stem_radius * sines[i], stem_radius * cosines[i], tip_height); + data.normals.emplace_back(Vec3f::UnitZ()); + } + + // tip cap triangles + for (int i = 0; i < resolution; ++i) + { + int v2 = (i < resolution - 1) ? i + resolution + 2 : resolution + 1; + int v3 = (i < resolution - 1) ? i + 2 * resolution + 2 : 2 * resolution + 1; + data.triangles.emplace_back(i + resolution + 1, v2, v3); + data.triangles.emplace_back(i + resolution + 1, v3, i + 2 * resolution + 1); + } + + // stem bottom vertices + for (int i = 0; i < resolution; ++i) + { + data.positions.emplace_back(stem_radius * sines[i], stem_radius * cosines[i], tip_height); + data.normals.emplace_back(sines[i], cosines[i], 0.0f); + } + + float total_height = tip_height + stem_height; + + // stem top vertices + for (int i = 0; i < resolution; ++i) + { + data.positions.emplace_back(stem_radius * sines[i], stem_radius * cosines[i], total_height); + data.normals.emplace_back(sines[i], cosines[i], 0.0f); + } + + // stem triangles + for (int i = 0; i < resolution; ++i) + { + int v2 = (i < resolution - 1) ? i + 3 * resolution + 2 : 3 * resolution + 1; + int v3 = (i < resolution - 1) ? i + 4 * resolution + 2 : 4 * resolution + 1; + data.triangles.emplace_back(i + 3 * resolution + 1, v2, v3); + data.triangles.emplace_back(i + 3 * resolution + 1, v3, i + 4 * resolution + 1); + } + + // stem cap vertices + data.positions.emplace_back(0.0f, 0.0f, total_height); + data.normals.emplace_back(Vec3f::UnitZ()); + for (int i = 0; i < resolution; ++i) + { + data.positions.emplace_back(stem_radius * sines[i], stem_radius * cosines[i], total_height); + data.normals.emplace_back(Vec3f::UnitZ()); + } + + // stem cap triangles + for (int i = 0; i < resolution; ++i) + { + int v3 = (i < resolution - 1) ? i + 5 * resolution + 3 : 5 * resolution + 2; + data.triangles.emplace_back(5 * resolution + 1, i + 5 * resolution + 2, v3); + } + + return data; +} + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp new file mode 100644 index 0000000000..f294531abb --- /dev/null +++ b/src/slic3r/GUI/GLModel.hpp @@ -0,0 +1,46 @@ +#ifndef slic3r_GLModel_hpp_ +#define slic3r_GLModel_hpp_ + +namespace Slic3r { + +class TriangleMesh; + +namespace GUI { + + struct GLModelInitializationData + { + std::vector positions; + std::vector normals; + std::vector triangles; + }; + + class GL_Model + { + unsigned int m_vbo_id{ 0 }; + unsigned int m_ibo_id{ 0 }; + size_t m_indices_count{ 0 }; + + public: + virtual ~GL_Model() { reset(); } + + bool init_from(const GLModelInitializationData& data); + bool init_from(const TriangleMesh& mesh); + void reset(); + void render() const; + + private: + void send_to_gpu(const std::vector& vertices, const std::vector& indices); + }; + + + // create an arrow with cylindrical stem and conical tip, with the given dimensions and resolution + // the arrow tip is at 0,0,0 + // the arrow has its axis of symmetry along the Z axis and is pointing downward + GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float tip_height, + float stem_radius, float stem_height); + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLModel_hpp_ + From b2f8f2bca6a3b2907870743180af05130a0352fc Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 11 May 2020 16:37:04 +0200 Subject: [PATCH 067/503] Added missing includes --- src/slic3r/GUI/GLModel.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index f294531abb..18e8bafbdd 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -1,6 +1,9 @@ #ifndef slic3r_GLModel_hpp_ #define slic3r_GLModel_hpp_ +#include "libslic3r/Point.hpp" +#include + namespace Slic3r { class TriangleMesh; From 8d5cea82f4e2035663812547d65a5bb1a63ea73a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 12 May 2020 11:33:50 +0200 Subject: [PATCH 068/503] Tech ENABLE_GCODE_VIEWER -> Bed axes rendered using the new OpenGL model class --- src/slic3r/GUI/3DBed.cpp | 74 +++++++++++++++++++++++++++++++++- src/slic3r/GUI/3DBed.hpp | 33 +++++++++++++++ src/slic3r/GUI/GCodeViewer.cpp | 6 ++- src/slic3r/GUI/GCodeViewer.hpp | 2 + src/slic3r/GUI/GLModel.cpp | 63 ++++++++++++++++------------- src/slic3r/GUI/GLModel.hpp | 13 ++++-- 6 files changed, 154 insertions(+), 37 deletions(-) diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 6c070ca99a..4ef8679603 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -5,15 +5,24 @@ #include "libslic3r/Polygon.hpp" #include "libslic3r/ClipperUtils.hpp" #include "libslic3r/BoundingBox.hpp" +#if ENABLE_GCODE_VIEWER +#include "libslic3r/Geometry.hpp" +#endif // ENABLE_GCODE_VIEWER #include "GUI_App.hpp" #include "PresetBundle.hpp" #include "GLCanvas3D.hpp" +#if ENABLE_GCODE_VIEWER +#include "3DScene.hpp" +#endif // ENABLE_GCODE_VIEWER #include #include #include +#if ENABLE_GCODE_VIEWER +#include +#endif // ENABLE_GCODE_VIEWER static const float GROUND_Z = -0.02f; @@ -119,13 +128,25 @@ const float* GeometryBuffer::get_vertices_data() const return (m_vertices.size() > 0) ? (const float*)m_vertices.data() : nullptr; } +#if ENABLE_GCODE_VIEWER +const float Bed3D::Axes::DefaultStemRadius = 0.5f; +const float Bed3D::Axes::DefaultStemLength = 25.0f; +const float Bed3D::Axes::DefaultTipRadius = 2.5f * Bed3D::Axes::DefaultStemRadius; +const float Bed3D::Axes::DefaultTipLength = 5.0f; +#else const double Bed3D::Axes::Radius = 0.5; const double Bed3D::Axes::ArrowBaseRadius = 2.5 * Bed3D::Axes::Radius; const double Bed3D::Axes::ArrowLength = 5.0; +#endif // ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER +void Bed3D::Axes::set_stem_length(float length) +{ + m_stem_length = length; + m_arrow.reset(); +} +#else Bed3D::Axes::Axes() -: origin(Vec3d::Zero()) -, length(25.0 * Vec3d::Ones()) { m_quadric = ::gluNewQuadric(); if (m_quadric != nullptr) @@ -137,9 +158,46 @@ Bed3D::Axes::~Axes() if (m_quadric != nullptr) ::gluDeleteQuadric(m_quadric); } +#endif // ENABLE_GCODE_VIEWER void Bed3D::Axes::render() const { +#if ENABLE_GCODE_VIEWER + auto render_axis = [this](const Transform3f& transform, GLint color_id, const std::array& color) { + if (color_id >= 0) + glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)color.data())); + + glsafe(::glPushMatrix()); + glsafe(::glMultMatrixf(transform.data())); + m_arrow.render(); + glsafe(::glPopMatrix()); + }; + + m_arrow.init_from(stilized_arrow(16, DefaultTipRadius, DefaultTipLength, DefaultStemRadius, m_stem_length)); + if (!m_shader.init("gouraud_light.vs", "gouraud_light.fs")) + BOOST_LOG_TRIVIAL(error) << "Unable to initialize gouraud_light shader: please, check that the files gouraud_light.vs and gouraud_light.fs are available"; + + if (!m_shader.is_initialized()) + return; + + glsafe(::glEnable(GL_DEPTH_TEST)); + + m_shader.start_using(); + GLint color_id = ::glGetUniformLocation(m_shader.get_shader_program_id(), "uniform_color"); + + // x axis + render_axis(Geometry::assemble_transform(m_origin, { 0.0, 0.5 * M_PI, 0.0f }).cast(), color_id, { 0.75f, 0.0f, 0.0f, 1.0f }); + + // y axis + render_axis(Geometry::assemble_transform(m_origin, { -0.5 * M_PI, 0.0, 0.0f }).cast(), color_id, { 0.0f, 0.75f, 0.0f, 1.0f }); + + // z axis + render_axis(Geometry::assemble_transform(m_origin).cast(), color_id, { 0.0f, 0.0f, 0.75f, 1.0f }); + + m_shader.stop_using(); + + glsafe(::glDisable(GL_DEPTH_TEST)); +#else if (m_quadric == nullptr) return; @@ -171,8 +229,10 @@ void Bed3D::Axes::render() const glsafe(::glDisable(GL_LIGHTING)); glsafe(::glDisable(GL_DEPTH_TEST)); +#endif // !ENABLE_GCODE_VIEWER } +#if !ENABLE_GCODE_VIEWER void Bed3D::Axes::render_axis(double length) const { ::gluQuadricOrientation(m_quadric, GLU_OUTSIDE); @@ -185,6 +245,7 @@ void Bed3D::Axes::render_axis(double length) const ::gluQuadricOrientation(m_quadric, GLU_INSIDE); ::gluDisk(m_quadric, 0.0, ArrowBaseRadius, 32, 1); } +#endif // !ENABLE_GCODE_VIEWER Bed3D::Bed3D() : m_type(Custom) @@ -242,8 +303,13 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c m_model.reset(); // Set the origin and size for rendering the coordinate system axes. +#if ENABLE_GCODE_VIEWER + m_axes.set_origin({ 0.0, 0.0, static_cast(GROUND_Z) }); + m_axes.set_stem_length(0.1f * static_cast(m_bounding_box.max_size())); +#else m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z); m_axes.length = 0.1 * m_bounding_box.max_size() * Vec3d::Ones(); +#endif // ENABLE_GCODE_VIEWER // Let the calee to update the UI. return true; @@ -290,7 +356,11 @@ void Bed3D::calc_bounding_boxes() const m_extended_bounding_box = m_bounding_box; // extend to contain axes +#if ENABLE_GCODE_VIEWER + m_extended_bounding_box.merge(m_axes.get_total_length() * Vec3d::Ones()); +#else m_extended_bounding_box.merge(m_axes.length + Axes::ArrowLength * Vec3d::Ones()); +#endif // ENABLE_GCODE_VIEWER // extend to contain model, if any if (!m_model.get_filename().empty()) diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index abdfca1fe0..440468233c 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -4,11 +4,16 @@ #include "GLTexture.hpp" #include "3DScene.hpp" #include "GLShader.hpp" +#if ENABLE_GCODE_VIEWER +#include "GLModel.hpp" +#endif // ENABLE_GCODE_VIEWER #include +#if !ENABLE_GCODE_VIEWER class GLUquadric; typedef class GLUquadric GLUquadricObj; +#endif // !ENABLE_GCODE_VIEWER namespace Slic3r { namespace GUI { @@ -45,22 +50,50 @@ public: class Bed3D { +#if ENABLE_GCODE_VIEWER + class Axes + { + static const float DefaultStemRadius; + static const float DefaultStemLength; + static const float DefaultTipRadius; + static const float DefaultTipLength; +#else struct Axes { static const double Radius; static const double ArrowBaseRadius; static const double ArrowLength; +#endif // ENABLE_GCODE_VIEWER + +#if ENABLE_GCODE_VIEWER + Vec3d m_origin{ Vec3d::Zero() }; + float m_stem_length{ DefaultStemLength }; + mutable GL_Model m_arrow; + mutable Shader m_shader; + + public: +#else Vec3d origin; Vec3d length; GLUquadricObj* m_quadric; +#endif // ENABLE_GCODE_VIEWER +#if !ENABLE_GCODE_VIEWER Axes(); ~Axes(); +#endif // !ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER + void set_origin(const Vec3d& origin) { m_origin = origin; } + void set_stem_length(float length); + float get_total_length() const { return m_stem_length + DefaultTipLength; } +#endif // ENABLE_GCODE_VIEWER void render() const; +#if !ENABLE_GCODE_VIEWER private: void render_axis(double length) const; +#endif // !ENABLE_GCODE_VIEWER }; public: diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index ad3985c625..525a2fd5a1 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -147,7 +147,7 @@ GCodeViewer::Color GCodeViewer::Extrusions::Range::get_color_at(float value) con void GCodeViewer::SequentialView::Marker::init() { - m_model.init_from(stilized_arrow(16, 0.5f, 1.0f, 0.25f, 2.0f)); + m_model.init_from(stilized_arrow(16, 2.0f, 4.0f, 1.0f, 8.0f)); init_shader(); } @@ -320,7 +320,7 @@ void GCodeViewer::render() const glsafe(::glEnable(GL_DEPTH_TEST)); render_toolpaths(); - m_sequential_view.marker.set_world_transform(Geometry::assemble_transform(m_sequential_view.current_position.cast() + 0.5 * Vec3d::UnitZ(), { 0.0, 0.0, 0.0 }, { 4.0, 4.0, 4.0 }, { 1.0, 1.0, 1.0 }).cast()); + m_sequential_view.marker.set_world_transform(Geometry::assemble_transform(m_sequential_view.current_position.cast() + (0.5 + 12.0) * Vec3d::UnitZ(), { M_PI, 0.0, 0.0 }).cast()); m_sequential_view.marker.render(); render_shells(); render_legend(); @@ -437,6 +437,8 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) ::memcpy(static_cast(&vertices_data[i * 3]), static_cast(move.position.data()), 3 * sizeof(float)); } + m_bounding_box.merge(m_bounding_box.max + m_sequential_view.marker.get_bounding_box().max[2] * Vec3d::UnitZ()); + #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.vertices_size = SLIC3R_STDVEC_MEMSIZE(vertices_data, float); m_statistics.vertices_gpu_size = vertices_data.size() * sizeof(float); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 44f23b2852..6f3ca47dba 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -162,6 +162,8 @@ class GCodeViewer public: void init(); + const BoundingBoxf3& get_bounding_box() const { return m_model.get_bounding_box(); } + void set_world_transform(const Transform3f& transform) { m_world_transform = transform; } void set_color(const std::array& color) { m_color = color; } diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 336c699371..4b2ce4e9e9 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -9,12 +9,14 @@ namespace Slic3r { namespace GUI { -bool GL_Model::init_from(const GLModelInitializationData& data) +void GL_Model::init_from(const GLModelInitializationData& data) { + assert(!data.positions.empty() && !data.triangles.empty()); assert(data.positions.size() == data.normals.size()); - reset(); + if (m_vbo_id > 0) // call reset() if you want to reuse this model + return; // vertices/normals data std::vector vertices(6 * data.positions.size()); @@ -32,19 +34,22 @@ bool GL_Model::init_from(const GLModelInitializationData& data) } m_indices_count = static_cast(indices.size()); + m_bounding_box = BoundingBoxf3(); + for (size_t i = 0; i < data.positions.size(); ++i) { + m_bounding_box.merge(data.positions[i].cast()); + } send_to_gpu(vertices, indices); - - return true; } -bool GL_Model::init_from(const TriangleMesh& mesh) +void GL_Model::init_from(const TriangleMesh& mesh) { auto get_normal = [](const std::array& triangle) { return (triangle[1] - triangle[0]).cross(triangle[2] - triangle[0]).normalized(); }; - reset(); + if (m_vbo_id > 0) // call reset() if you want to reuse this model + return; assert(!mesh.its.vertices.empty() && !mesh.its.indices.empty()); // call require_shared_vertices() before to pass the mesh to this method @@ -68,10 +73,9 @@ bool GL_Model::init_from(const TriangleMesh& mesh) } m_indices_count = static_cast(indices.size()); + m_bounding_box = mesh.bounding_box(); send_to_gpu(vertices, indices); - - return true; } void GL_Model::reset() @@ -88,6 +92,7 @@ void GL_Model::reset() } m_indices_count = 0; + m_bounding_box = BoundingBoxf3(); } void GL_Model::render() const @@ -142,12 +147,14 @@ GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float sines[i] = -::sin(angle); } + float total_height = tip_height + stem_height; + // tip vertices/normals - data.positions.emplace_back(0.0f, 0.0f, 0.0f); - data.normals.emplace_back(-Vec3f::UnitZ()); + data.positions.emplace_back(0.0f, 0.0f, total_height); + data.normals.emplace_back(Vec3f::UnitZ()); for (int i = 0; i < resolution; ++i) { - data.positions.emplace_back(tip_radius * sines[i], tip_radius * cosines[i], tip_height); + data.positions.emplace_back(tip_radius * sines[i], tip_radius * cosines[i], stem_height); data.normals.emplace_back(sines[i], cosines[i], 0.0f); } @@ -155,21 +162,21 @@ GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float for (int i = 0; i < resolution; ++i) { int v3 = (i < resolution - 1) ? i + 2 : 1; - data.triangles.emplace_back(0, v3, i + 1); + data.triangles.emplace_back(0, i + 1, v3); } // tip cap outer perimeter vertices for (int i = 0; i < resolution; ++i) { - data.positions.emplace_back(tip_radius * sines[i], tip_radius * cosines[i], tip_height); - data.normals.emplace_back(Vec3f::UnitZ()); + data.positions.emplace_back(tip_radius * sines[i], tip_radius * cosines[i], stem_height); + data.normals.emplace_back(-Vec3f::UnitZ()); } // tip cap inner perimeter vertices for (int i = 0; i < resolution; ++i) { - data.positions.emplace_back(stem_radius * sines[i], stem_radius * cosines[i], tip_height); - data.normals.emplace_back(Vec3f::UnitZ()); + data.positions.emplace_back(stem_radius * sines[i], stem_radius * cosines[i], stem_height); + data.normals.emplace_back(-Vec3f::UnitZ()); } // tip cap triangles @@ -177,23 +184,21 @@ GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float { int v2 = (i < resolution - 1) ? i + resolution + 2 : resolution + 1; int v3 = (i < resolution - 1) ? i + 2 * resolution + 2 : 2 * resolution + 1; - data.triangles.emplace_back(i + resolution + 1, v2, v3); - data.triangles.emplace_back(i + resolution + 1, v3, i + 2 * resolution + 1); + data.triangles.emplace_back(i + resolution + 1, v3, v2); + data.triangles.emplace_back(i + resolution + 1, i + 2 * resolution + 1, v3); } // stem bottom vertices for (int i = 0; i < resolution; ++i) { - data.positions.emplace_back(stem_radius * sines[i], stem_radius * cosines[i], tip_height); + data.positions.emplace_back(stem_radius * sines[i], stem_radius * cosines[i], stem_height); data.normals.emplace_back(sines[i], cosines[i], 0.0f); } - float total_height = tip_height + stem_height; - // stem top vertices for (int i = 0; i < resolution; ++i) { - data.positions.emplace_back(stem_radius * sines[i], stem_radius * cosines[i], total_height); + data.positions.emplace_back(stem_radius * sines[i], stem_radius * cosines[i], 0.0f); data.normals.emplace_back(sines[i], cosines[i], 0.0f); } @@ -202,24 +207,24 @@ GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float { int v2 = (i < resolution - 1) ? i + 3 * resolution + 2 : 3 * resolution + 1; int v3 = (i < resolution - 1) ? i + 4 * resolution + 2 : 4 * resolution + 1; - data.triangles.emplace_back(i + 3 * resolution + 1, v2, v3); - data.triangles.emplace_back(i + 3 * resolution + 1, v3, i + 4 * resolution + 1); + data.triangles.emplace_back(i + 3 * resolution + 1, v3, v2); + data.triangles.emplace_back(i + 3 * resolution + 1, i + 4 * resolution + 1, v3); } // stem cap vertices - data.positions.emplace_back(0.0f, 0.0f, total_height); - data.normals.emplace_back(Vec3f::UnitZ()); + data.positions.emplace_back(0.0f, 0.0f, 0.0f); + data.normals.emplace_back(-Vec3f::UnitZ()); for (int i = 0; i < resolution; ++i) { - data.positions.emplace_back(stem_radius * sines[i], stem_radius * cosines[i], total_height); - data.normals.emplace_back(Vec3f::UnitZ()); + data.positions.emplace_back(stem_radius* sines[i], stem_radius* cosines[i], 0.0f); + data.normals.emplace_back(-Vec3f::UnitZ()); } // stem cap triangles for (int i = 0; i < resolution; ++i) { int v3 = (i < resolution - 1) ? i + 5 * resolution + 3 : 5 * resolution + 2; - data.triangles.emplace_back(5 * resolution + 1, i + 5 * resolution + 2, v3); + data.triangles.emplace_back(5 * resolution + 1, v3, i + 5 * resolution + 2); } return data; diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index 18e8bafbdd..7b135ada6b 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -2,6 +2,7 @@ #define slic3r_GLModel_hpp_ #include "libslic3r/Point.hpp" +#include "libslic3r/BoundingBox.hpp" #include namespace Slic3r { @@ -23,22 +24,26 @@ namespace GUI { unsigned int m_ibo_id{ 0 }; size_t m_indices_count{ 0 }; + BoundingBoxf3 m_bounding_box; + public: virtual ~GL_Model() { reset(); } - bool init_from(const GLModelInitializationData& data); - bool init_from(const TriangleMesh& mesh); + void init_from(const GLModelInitializationData& data); + void init_from(const TriangleMesh& mesh); void reset(); void render() const; + const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; } + private: void send_to_gpu(const std::vector& vertices, const std::vector& indices); }; // create an arrow with cylindrical stem and conical tip, with the given dimensions and resolution - // the arrow tip is at 0,0,0 - // the arrow has its axis of symmetry along the Z axis and is pointing downward + // the origin of the arrow is in the center of the stem cap + // the arrow has its axis of symmetry along the Z axis and is pointing upward GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float tip_height, float stem_radius, float stem_height); From 58258df113249505ebe872bd1608fb08b6051884 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 12 May 2020 16:15:43 +0200 Subject: [PATCH 069/503] Tech ENABLE_GCODE_VIEWER -> Selection curved arrows rendered using the new OpenGL model class --- src/slic3r/GUI/3DScene.cpp | 2 + src/slic3r/GUI/3DScene.hpp | 2 + src/slic3r/GUI/GLModel.cpp | 184 +++++++++++++++++++++++++++++++---- src/slic3r/GUI/GLModel.hpp | 5 + src/slic3r/GUI/Selection.cpp | 54 +++++++++- src/slic3r/GUI/Selection.hpp | 9 ++ 6 files changed, 236 insertions(+), 20 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 6aaf0b500e..ef5e22c823 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -2002,6 +2002,7 @@ bool GLArrow::on_init() return true; } +#if !ENABLE_GCODE_VIEWER GLCurvedArrow::GLCurvedArrow(unsigned int resolution) : GLModel() , m_resolution(resolution) @@ -2115,6 +2116,7 @@ bool GLCurvedArrow::on_init() m_volume.indexed_vertex_array.finalize_geometry(true); return true; } +#endif // !ENABLE_GCODE_VIEWER bool GLBed::on_init_from_file(const std::string& filename) { diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 07c5cd53e3..d6ee72bdc1 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -648,6 +648,7 @@ protected: bool on_init() override; }; +#if !ENABLE_GCODE_VIEWER class GLCurvedArrow : public GLModel { unsigned int m_resolution; @@ -658,6 +659,7 @@ public: protected: bool on_init() override; }; +#endif // !ENABLE_GCODE_VIEWER class GLBed : public GLModel { diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 4b2ce4e9e9..6590da1409 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -11,7 +11,6 @@ namespace GUI { void GL_Model::init_from(const GLModelInitializationData& data) { - assert(!data.positions.empty() && !data.triangles.empty()); assert(data.positions.size() == data.normals.size()); @@ -134,9 +133,16 @@ void GL_Model::send_to_gpu(const std::vector& vertices, const std::vector GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float tip_height, float stem_radius, float stem_height) { + auto append_vertex = [](GLModelInitializationData& data, const Vec3f& position, const Vec3f& normal) { + data.positions.emplace_back(position); + data.normals.emplace_back(normal); + }; + + resolution = std::max(4, resolution); + GLModelInitializationData data; - float angle_step = 2.0f * M_PI / static_cast(resolution); + const float angle_step = 2.0f * M_PI / static_cast(resolution); std::vector cosines(resolution); std::vector sines(resolution); @@ -147,15 +153,13 @@ GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float sines[i] = -::sin(angle); } - float total_height = tip_height + stem_height; + const float total_height = tip_height + stem_height; // tip vertices/normals - data.positions.emplace_back(0.0f, 0.0f, total_height); - data.normals.emplace_back(Vec3f::UnitZ()); + append_vertex(data, { 0.0f, 0.0f, total_height }, Vec3f::UnitZ()); for (int i = 0; i < resolution; ++i) { - data.positions.emplace_back(tip_radius * sines[i], tip_radius * cosines[i], stem_height); - data.normals.emplace_back(sines[i], cosines[i], 0.0f); + append_vertex(data, { tip_radius * sines[i], tip_radius * cosines[i], stem_height }, { sines[i], cosines[i], 0.0f }); } // tip triangles @@ -168,15 +172,13 @@ GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float // tip cap outer perimeter vertices for (int i = 0; i < resolution; ++i) { - data.positions.emplace_back(tip_radius * sines[i], tip_radius * cosines[i], stem_height); - data.normals.emplace_back(-Vec3f::UnitZ()); + append_vertex(data, { tip_radius * sines[i], tip_radius * cosines[i], stem_height }, -Vec3f::UnitZ()); } // tip cap inner perimeter vertices for (int i = 0; i < resolution; ++i) { - data.positions.emplace_back(stem_radius * sines[i], stem_radius * cosines[i], stem_height); - data.normals.emplace_back(-Vec3f::UnitZ()); + append_vertex(data, { stem_radius * sines[i], stem_radius * cosines[i], stem_height }, -Vec3f::UnitZ()); } // tip cap triangles @@ -191,15 +193,13 @@ GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float // stem bottom vertices for (int i = 0; i < resolution; ++i) { - data.positions.emplace_back(stem_radius * sines[i], stem_radius * cosines[i], stem_height); - data.normals.emplace_back(sines[i], cosines[i], 0.0f); + append_vertex(data, { stem_radius * sines[i], stem_radius * cosines[i], stem_height }, { sines[i], cosines[i], 0.0f }); } // stem top vertices for (int i = 0; i < resolution; ++i) { - data.positions.emplace_back(stem_radius * sines[i], stem_radius * cosines[i], 0.0f); - data.normals.emplace_back(sines[i], cosines[i], 0.0f); + append_vertex(data, { stem_radius * sines[i], stem_radius * cosines[i], 0.0f }, { sines[i], cosines[i], 0.0f }); } // stem triangles @@ -212,12 +212,10 @@ GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float } // stem cap vertices - data.positions.emplace_back(0.0f, 0.0f, 0.0f); - data.normals.emplace_back(-Vec3f::UnitZ()); + append_vertex(data, Vec3f::Zero(), -Vec3f::UnitZ()); for (int i = 0; i < resolution; ++i) { - data.positions.emplace_back(stem_radius* sines[i], stem_radius* cosines[i], 0.0f); - data.normals.emplace_back(-Vec3f::UnitZ()); + append_vertex(data, { stem_radius * sines[i], stem_radius * cosines[i], 0.0f }, -Vec3f::UnitZ()); } // stem cap triangles @@ -230,5 +228,153 @@ GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float return data; } +GLModelInitializationData circular_arrow(int resolution, float radius, float tip_height, float tip_width, float stem_width, float thickness) +{ + auto append_vertex = [](GLModelInitializationData& data, const Vec3f& position, const Vec3f& normal) { + data.positions.emplace_back(position); + data.normals.emplace_back(normal); + }; + + resolution = std::max(2, resolution); + + GLModelInitializationData data; + + const float half_thickness = 0.5f * thickness; + const float half_stem_width = 0.5f * stem_width; + const float half_tip_width = 0.5f * tip_width; + + const float outer_radius = radius + half_stem_width; + const float inner_radius = radius - half_stem_width; + const float step_angle = 0.5f * PI / static_cast(resolution); + + // tip + // top face vertices + append_vertex(data, { 0.0f, outer_radius, half_thickness }, Vec3f::UnitZ()); + append_vertex(data, { 0.0f, radius + half_tip_width, half_thickness }, Vec3f::UnitZ()); + append_vertex(data, { -tip_height, radius, half_thickness }, Vec3f::UnitZ()); + append_vertex(data, { 0.0f, radius - half_tip_width, half_thickness }, Vec3f::UnitZ()); + append_vertex(data, { 0.0f, inner_radius, half_thickness }, Vec3f::UnitZ()); + + // top face triangles + data.triangles.emplace_back(0, 1, 2); + data.triangles.emplace_back(0, 2, 4); + data.triangles.emplace_back(4, 2, 3); + + // bottom face vertices + append_vertex(data, { 0.0f, outer_radius, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(data, { 0.0f, radius + half_tip_width, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(data, { -tip_height, radius, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(data, { 0.0f, radius - half_tip_width, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(data, { 0.0f, inner_radius, -half_thickness }, -Vec3f::UnitZ()); + + // bottom face triangles + data.triangles.emplace_back(5, 7, 6); + data.triangles.emplace_back(5, 9, 7); + data.triangles.emplace_back(9, 8, 7); + + // side faces vertices + append_vertex(data, { 0.0f, outer_radius, half_thickness }, Vec3f::UnitX()); + append_vertex(data, { 0.0f, radius + half_tip_width, half_thickness }, Vec3f::UnitY()); + append_vertex(data, { -tip_height, radius, half_thickness }, -Vec3f::UnitX()); + append_vertex(data, { 0.0f, radius - half_tip_width, half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { 0.0f, inner_radius, half_thickness }, Vec3f::UnitX()); + + append_vertex(data, { 0.0f, outer_radius, -half_thickness }, Vec3f::UnitX()); + append_vertex(data, { 0.0f, radius + half_tip_width, -half_thickness }, Vec3f::UnitY()); + append_vertex(data, { -tip_height, radius, -half_thickness }, -Vec3f::UnitX()); + append_vertex(data, { 0.0f, radius - half_tip_width, -half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { 0.0f, inner_radius, -half_thickness }, Vec3f::UnitX()); + + // side faces triangles + for (int i = 0; i < 4; ++i) + { + data.triangles.emplace_back(15 + i, 11 + i, 10 + i); + data.triangles.emplace_back(15 + i, 16 + i, 11 + i); + } + + // stem + // top face vertices + for (int i = 0; i <= resolution; ++i) + { + float angle = static_cast(i) * step_angle; + append_vertex(data, { inner_radius * ::sin(angle), inner_radius * ::cos(angle), half_thickness }, Vec3f::UnitZ()); + } + + for (int i = 0; i <= resolution; ++i) + { + float angle = static_cast(i) * step_angle; + append_vertex(data, { outer_radius * ::sin(angle), outer_radius * ::cos(angle), half_thickness }, Vec3f::UnitZ()); + } + + // top face triangles + for (int i = 0; i < resolution; ++i) + { + data.triangles.emplace_back(20 + i, 21 + i, 21 + resolution + i); + data.triangles.emplace_back(21 + i, 22 + resolution + i, 21 + resolution + i); + } + + // bottom face vertices + for (int i = 0; i <= resolution; ++i) + { + float angle = static_cast(i) * step_angle; + append_vertex(data, { inner_radius * ::sin(angle), inner_radius * ::cos(angle), -half_thickness }, -Vec3f::UnitZ()); + } + + for (int i = 0; i <= resolution; ++i) + { + float angle = static_cast(i) * step_angle; + append_vertex(data, { outer_radius * ::sin(angle), outer_radius * ::cos(angle), -half_thickness }, -Vec3f::UnitZ()); + } + + // bottom face triangles + for (int i = 0; i < resolution; ++i) + { + data.triangles.emplace_back(22 + 2 * resolution + i, 23 + 3 * resolution + i, 23 + 2 * resolution + i); + data.triangles.emplace_back(23 + 2 * resolution + i, 23 + 3 * resolution + i, 24 + 3 * resolution + i); + } + + // side faces vertices + for (int i = 0; i <= resolution; ++i) + { + float angle = static_cast(i) * step_angle; + float c = ::cos(angle); + float s = ::sin(angle); + append_vertex(data, { inner_radius * s, inner_radius * c, half_thickness }, { -s, -c, 0.0f}); + } + + for (int i = resolution; i >= 0; --i) + { + float angle = static_cast(i) * step_angle; + float c = ::cos(angle); + float s = ::sin(angle); + append_vertex(data, { outer_radius * s, outer_radius * c, half_thickness }, { s, c, 0.0f }); + } + + for (int i = 0; i <= resolution; ++i) + { + float angle = static_cast(i) * step_angle; + float c = ::cos(angle); + float s = ::sin(angle); + append_vertex(data, { inner_radius * s, inner_radius * c, -half_thickness }, { -s, -c, 0.0f }); + } + + for (int i = resolution; i >= 0; --i) + { + float angle = static_cast(i) * step_angle; + float c = ::cos(angle); + float s = ::sin(angle); + append_vertex(data, { outer_radius * s, outer_radius * c, -half_thickness }, { s, c, 0.0f }); + } + + // side faces triangles + for (int i = 0; i < 2 * resolution + 1; ++i) + { + data.triangles.emplace_back(20 + 6 * (resolution + 1) + i, 21 + 6 * (resolution + 1) + i, 21 + 4 * (resolution + 1) + i); + data.triangles.emplace_back(20 + 6 * (resolution + 1) + i, 21 + 4 * (resolution + 1) + i, 20 + 4 * (resolution + 1) + i); + } + + return data; +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index 7b135ada6b..c55931ef10 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -47,6 +47,11 @@ namespace GUI { GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float tip_height, float stem_radius, float stem_height); + // create an arrow whose stem is a quarter of circle, with the given dimensions and resolution + // the origin of the arrow is in the center of the circle + // the arrow is contained in the 1st quadrant and is pointing counterclockwise + GLModelInitializationData circular_arrow(int resolution, float radius, float tip_height, float tip_width, float stem_width, float thickness); + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 87f8b60ede..62a046a251 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -13,6 +13,9 @@ #include #include +#if ENABLE_GCODE_VIEWER +#include +#endif // ENABLE_GCODE_VIEWER static const float UNIFORM_SCALE_COLOR[3] = { 1.0f, 0.38f, 0.0f }; @@ -76,7 +79,9 @@ Selection::Selection() , m_mode(Instance) , m_type(Empty) , m_valid(false) +#if !ENABLE_GCODE_VIEWER , m_curved_arrow(16) +#endif // !ENABLE_GCODE_VIEWER , m_scale_factor(1.0f) { this->set_bounding_boxes_dirty(); @@ -109,10 +114,21 @@ bool Selection::init() m_arrow.set_scale(5.0 * Vec3d::Ones()); +#if ENABLE_GCODE_VIEWER + m_curved_arrow.init_from(circular_arrow(16, 10.0f, 5.0f, 10.0f, 5.0f, 1.0f)); + + if (!m_arrows_shader.init("gouraud_light.vs", "gouraud_light.fs")) + { + BOOST_LOG_TRIVIAL(error) << "Unable to initialize gouraud_light shader: please, check that the files gouraud_light.vs and gouraud_light.fs are available"; + return false; + } +#else if (!m_curved_arrow.init()) return false; m_curved_arrow.set_scale(5.0 * Vec3d::Ones()); +#endif //ENABLE_GCODE_VIEWER + return true; } @@ -1927,6 +1943,40 @@ void Selection::render_sidebar_position_hints(const std::string& sidebar_field) void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field) const { +#if ENABLE_GCODE_VIEWER + if (!m_arrows_shader.is_initialized()) + return; + + m_arrows_shader.start_using(); + GLint color_id = ::glGetUniformLocation(m_arrows_shader.get_shader_program_id(), "uniform_color"); + + if (boost::ends_with(sidebar_field, "x")) + { + if (color_id >= 0) + glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[0])); + + glsafe(::glRotated(90.0, 0.0, 1.0, 0.0)); + render_sidebar_rotation_hint(X); + } + else if (boost::ends_with(sidebar_field, "y")) + { + if (color_id >= 0) + glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[1])); + + glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0)); + render_sidebar_rotation_hint(Y); + } + else if (boost::ends_with(sidebar_field, "z")) + { + if (color_id >= 0) + glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[2])); + + render_sidebar_rotation_hint(Z); + } + + m_arrows_shader.stop_using(); + +#else if (boost::ends_with(sidebar_field, "x")) { glsafe(::glRotated(90.0, 0.0, 1.0, 0.0)); @@ -1939,6 +1989,7 @@ void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field) } else if (boost::ends_with(sidebar_field, "z")) render_sidebar_rotation_hint(Z); +#endif // ENABLE_GCODE_VIEWER } void Selection::render_sidebar_scale_hints(const std::string& sidebar_field) const @@ -2054,9 +2105,10 @@ void Selection::render_sidebar_position_hint(Axis axis) const void Selection::render_sidebar_rotation_hint(Axis axis) const { +#if !ENABLE_GCODE_VIEWER m_curved_arrow.set_color(AXES_COLOR[axis], 3); +#endif // !ENABLE_GCODE_VIEWER m_curved_arrow.render(); - glsafe(::glRotated(180.0, 0.0, 0.0, 1.0)); m_curved_arrow.render(); } diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index c27b4cc29d..321cb70e04 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -4,6 +4,10 @@ #include #include "libslic3r/Geometry.hpp" #include "3DScene.hpp" +#if ENABLE_GCODE_VIEWER +#include "GLModel.hpp" +#include "GLShader.hpp" +#endif // ENABLE_GCODE_VIEWER #if ENABLE_RENDER_SELECTION_CENTER class GLUquadric; @@ -201,7 +205,12 @@ private: GLUquadricObj* m_quadric; #endif // ENABLE_RENDER_SELECTION_CENTER mutable GLArrow m_arrow; +#if ENABLE_GCODE_VIEWER + GL_Model m_curved_arrow; + Shader m_arrows_shader; +#else mutable GLCurvedArrow m_curved_arrow; +#endif // ENABLE_GCODE_VIEWER mutable float m_scale_factor; From b59fc1e57d7e6f66cb580196c984f5d18cec7027 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 13 May 2020 09:07:06 +0200 Subject: [PATCH 070/503] Tech ENABLE_GCODE_VIEWER -> Selection straight arrows rendered using the new OpenGL model class --- src/slic3r/GUI/GLCanvas3D.cpp | 4 ++ src/slic3r/GUI/GLModel.cpp | 97 ++++++++++++++++++++++++++++++++++ src/slic3r/GUI/GLModel.hpp | 7 ++- src/slic3r/GUI/Selection.cpp | 98 ++++++++++++++++++++++++++++++----- src/slic3r/GUI/Selection.hpp | 11 +++- 5 files changed, 203 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index ba8d3b1ba6..1cbfac4e48 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5976,7 +5976,11 @@ void GLCanvas3D::_render_sla_slices() const void GLCanvas3D::_render_selection_sidebar_hints() const { +#if ENABLE_GCODE_VIEWER + m_selection.render_sidebar_hints(m_sidebar_field); +#else m_selection.render_sidebar_hints(m_sidebar_field, m_shader); +#endif // ENABLE_GCODE_VIEWER } void GLCanvas3D::_update_volumes_hover_state() const diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 6590da1409..ee98ce678a 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -376,5 +376,102 @@ GLModelInitializationData circular_arrow(int resolution, float radius, float tip return data; } +GLModelInitializationData straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness) +{ + auto append_vertex = [](GLModelInitializationData& data, const Vec3f& position, const Vec3f& normal) { + data.positions.emplace_back(position); + data.normals.emplace_back(normal); + }; + + GLModelInitializationData data; + + const float half_thickness = 0.5f * thickness; + const float half_stem_width = 0.5f * stem_width; + const float half_tip_width = 0.5f * tip_width; + const float total_height = tip_height + stem_height; + + // top face vertices + append_vertex(data, { half_stem_width, 0.0, half_thickness }, Vec3f::UnitZ()); + append_vertex(data, { half_stem_width, stem_height, half_thickness }, Vec3f::UnitZ()); + append_vertex(data, { half_tip_width, stem_height, half_thickness }, Vec3f::UnitZ()); + append_vertex(data, { 0.0, total_height, half_thickness }, Vec3f::UnitZ()); + append_vertex(data, { -half_tip_width, stem_height, half_thickness }, Vec3f::UnitZ()); + append_vertex(data, { -half_stem_width, stem_height, half_thickness }, Vec3f::UnitZ()); + append_vertex(data, { -half_stem_width, 0.0, half_thickness }, Vec3f::UnitZ()); + + // top face triangles + data.triangles.emplace_back(0, 1, 6); + data.triangles.emplace_back(6, 1, 5); + data.triangles.emplace_back(4, 5, 3); + data.triangles.emplace_back(5, 1, 3); + data.triangles.emplace_back(1, 2, 3); + + // bottom face vertices + append_vertex(data, { half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(data, { half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(data, { half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(data, { 0.0, total_height, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(data, { -half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(data, { -half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(data, { -half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitZ()); + + // bottom face triangles + data.triangles.emplace_back(7, 13, 8); + data.triangles.emplace_back(13, 12, 8); + data.triangles.emplace_back(12, 11, 10); + data.triangles.emplace_back(8, 12, 10); + data.triangles.emplace_back(9, 8, 10); + + // side faces vertices + append_vertex(data, { half_stem_width, 0.0, -half_thickness }, Vec3f::UnitX()); + append_vertex(data, { half_stem_width, stem_height, -half_thickness }, Vec3f::UnitX()); + append_vertex(data, { half_stem_width, 0.0, half_thickness }, Vec3f::UnitX()); + append_vertex(data, { half_stem_width, stem_height, half_thickness }, Vec3f::UnitX()); + + append_vertex(data, { half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { half_stem_width, stem_height, half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { half_tip_width, stem_height, half_thickness }, -Vec3f::UnitY()); + + Vec3f normal(tip_height, half_tip_width, 0.0f); + normal.normalize(); + append_vertex(data, { half_tip_width, stem_height, -half_thickness }, normal); + append_vertex(data, { 0.0, total_height, -half_thickness }, normal); + append_vertex(data, { half_tip_width, stem_height, half_thickness }, normal); + append_vertex(data, { 0.0, total_height, half_thickness }, normal); + + normal = Vec3f(-tip_height, half_tip_width, 0.0f); + normal.normalize(); + append_vertex(data, { 0.0, total_height, -half_thickness }, normal); + append_vertex(data, { -half_tip_width, stem_height, -half_thickness }, normal); + append_vertex(data, { 0.0, total_height, half_thickness }, normal); + append_vertex(data, { -half_tip_width, stem_height, half_thickness }, normal); + + append_vertex(data, { -half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { -half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { -half_tip_width, stem_height, half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { -half_stem_width, stem_height, half_thickness }, -Vec3f::UnitY()); + + append_vertex(data, { -half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitX()); + append_vertex(data, { -half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitX()); + append_vertex(data, { -half_stem_width, stem_height, half_thickness }, -Vec3f::UnitX()); + append_vertex(data, { -half_stem_width, 0.0, half_thickness }, -Vec3f::UnitX()); + + append_vertex(data, { -half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { -half_stem_width, 0.0, half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { half_stem_width, 0.0, half_thickness }, -Vec3f::UnitY()); + + // side face triangles + for (int i = 0; i < 7; ++i) + { + int ii = i * 4; + data.triangles.emplace_back(14 + ii, 15 + ii, 17 + ii); + data.triangles.emplace_back(14 + ii, 17 + ii, 16 + ii); + } + + return data; +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index c55931ef10..a11073b191 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -49,9 +49,14 @@ namespace GUI { // create an arrow whose stem is a quarter of circle, with the given dimensions and resolution // the origin of the arrow is in the center of the circle - // the arrow is contained in the 1st quadrant and is pointing counterclockwise + // the arrow is contained in the 1st quadrant of the XY plane and is pointing counterclockwise GLModelInitializationData circular_arrow(int resolution, float radius, float tip_height, float tip_width, float stem_width, float thickness); + // create an arrow with the given dimensions + // the origin of the arrow is in the center of the stem cap + // the arrow is contained in XY plane and has its main axis along the Y axis + GLModelInitializationData straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness); + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 62a046a251..3bab06b4d5 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -109,12 +109,8 @@ void Selection::set_volumes(GLVolumePtrs* volumes) // Init shall be called from the OpenGL render function, so that the OpenGL context is initialized! bool Selection::init() { - if (!m_arrow.init()) - return false; - - m_arrow.set_scale(5.0 * Vec3d::Ones()); - #if ENABLE_GCODE_VIEWER + m_arrow.init_from(straight_arrow(10.0f, 5.0f, 5.0f, 10.0f, 1.0f)); m_curved_arrow.init_from(circular_arrow(16, 10.0f, 5.0f, 10.0f, 5.0f, 1.0f)); if (!m_arrows_shader.init("gouraud_light.vs", "gouraud_light.fs")) @@ -123,6 +119,11 @@ bool Selection::init() return false; } #else + if (!m_arrow.init()) + return false; + + m_arrow.set_scale(5.0 * Vec3d::Ones()); + if (!m_curved_arrow.init()) return false; @@ -1243,16 +1244,28 @@ void Selection::render_center(bool gizmo_is_dragging) const } #endif // ENABLE_RENDER_SELECTION_CENTER +#if ENABLE_GCODE_VIEWER +void Selection::render_sidebar_hints(const std::string& sidebar_field) const +#else void Selection::render_sidebar_hints(const std::string& sidebar_field, const Shader& shader) const +#endif // ENABLE_GCODE_VIEWER { if (sidebar_field.empty()) return; if (!boost::starts_with(sidebar_field, "layer")) { +#if ENABLE_GCODE_VIEWER + if (!m_arrows_shader.is_initialized()) + return; + + m_arrows_shader.start_using(); + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); +#else shader.start_using(); glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); glsafe(::glEnable(GL_LIGHTING)); +#endif // ENABLE_GCODE_VIEWER } glsafe(::glEnable(GL_DEPTH_TEST)); @@ -1323,8 +1336,12 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field, const Sha if (!boost::starts_with(sidebar_field, "layer")) { +#if ENABLE_GCODE_VIEWER + m_arrows_shader.stop_using(); +#else glsafe(::glDisable(GL_LIGHTING)); shader.stop_using(); +#endif // ENABLE_GCODE_VIEWER } } @@ -1927,6 +1944,33 @@ void Selection::render_bounding_box(const BoundingBoxf3& box, float* color) cons void Selection::render_sidebar_position_hints(const std::string& sidebar_field) const { +#if ENABLE_GCODE_VIEWER + GLint color_id = ::glGetUniformLocation(m_arrows_shader.get_shader_program_id(), "uniform_color"); + + if (boost::ends_with(sidebar_field, "x")) + { + if (color_id >= 0) + glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[0])); + + glsafe(::glRotated(-90.0, 0.0, 0.0, 1.0)); + m_arrow.render(); + } + else if (boost::ends_with(sidebar_field, "y")) + { + if (color_id >= 0) + glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[1])); + + m_arrow.render(); + } + else if (boost::ends_with(sidebar_field, "z")) + { + if (color_id >= 0) + glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[2])); + + glsafe(::glRotated(90.0, 1.0, 0.0, 0.0)); + m_arrow.render(); + } +#else if (boost::ends_with(sidebar_field, "x")) { glsafe(::glRotated(-90.0, 0.0, 0.0, 1.0)); @@ -1939,15 +1983,12 @@ void Selection::render_sidebar_position_hints(const std::string& sidebar_field) glsafe(::glRotated(90.0, 1.0, 0.0, 0.0)); render_sidebar_position_hint(Z); } +#endif // ENABLE_GCODE_VIEWER } void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field) const { #if ENABLE_GCODE_VIEWER - if (!m_arrows_shader.is_initialized()) - return; - - m_arrows_shader.start_using(); GLint color_id = ::glGetUniformLocation(m_arrows_shader.get_shader_program_id(), "uniform_color"); if (boost::ends_with(sidebar_field, "x")) @@ -1973,9 +2014,6 @@ void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field) render_sidebar_rotation_hint(Z); } - - m_arrows_shader.stop_using(); - #else if (boost::ends_with(sidebar_field, "x")) { @@ -1996,6 +2034,7 @@ void Selection::render_sidebar_scale_hints(const std::string& sidebar_field) con { bool uniform_scale = requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling(); +#if ENABLE_GCODE_VIEWER if (boost::ends_with(sidebar_field, "x") || uniform_scale) { glsafe(::glPushMatrix()); @@ -2018,6 +2057,30 @@ void Selection::render_sidebar_scale_hints(const std::string& sidebar_field) con render_sidebar_scale_hint(Z); glsafe(::glPopMatrix()); } +#else + if (boost::ends_with(sidebar_field, "x") || uniform_scale) + { + glsafe(::glPushMatrix()); + glsafe(::glRotated(-90.0, 0.0, 0.0, 1.0)); + render_sidebar_scale_hint(X); + glsafe(::glPopMatrix()); + } + + if (boost::ends_with(sidebar_field, "y") || uniform_scale) + { + glsafe(::glPushMatrix()); + render_sidebar_scale_hint(Y); + glsafe(::glPopMatrix()); + } + + if (boost::ends_with(sidebar_field, "z") || uniform_scale) + { + glsafe(::glPushMatrix()); + glsafe(::glRotated(90.0, 1.0, 0.0, 0.0)); + render_sidebar_scale_hint(Z); + glsafe(::glPopMatrix()); + } +#endif // ENABLE_GCODE_VIEWER } void Selection::render_sidebar_size_hints(const std::string& sidebar_field) const @@ -2097,11 +2160,13 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co glsafe(::glDisable(GL_BLEND)); } +#if !ENABLE_GCODE_VIEWER void Selection::render_sidebar_position_hint(Axis axis) const { m_arrow.set_color(AXES_COLOR[axis], 3); m_arrow.render(); } +#endif // !ENABLE_GCODE_VIEWER void Selection::render_sidebar_rotation_hint(Axis axis) const { @@ -2115,7 +2180,14 @@ void Selection::render_sidebar_rotation_hint(Axis axis) const void Selection::render_sidebar_scale_hint(Axis axis) const { +#if ENABLE_GCODE_VIEWER + GLint color_id = ::glGetUniformLocation(m_arrows_shader.get_shader_program_id(), "uniform_color"); + + if (color_id >= 0) + glsafe(::glUniform4fv(color_id, 1, (requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling()) ? (const GLfloat*)UNIFORM_SCALE_COLOR : (const GLfloat*)AXES_COLOR[axis])); +#else m_arrow.set_color(((requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling()) ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis]), 3); +#endif // ENABLE_GCODE_VIEWER glsafe(::glTranslated(0.0, 5.0, 0.0)); m_arrow.render(); @@ -2125,9 +2197,11 @@ void Selection::render_sidebar_scale_hint(Axis axis) const m_arrow.render(); } +#if !ENABLE_GCODE_VIEWER void Selection::render_sidebar_size_hint(Axis axis, double length) const { } +#endif // !ENABLE_GCODE_VIEWER #ifndef NDEBUG static bool is_rotation_xy_synchronized(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to) diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 321cb70e04..53caf11060 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -204,11 +204,12 @@ private: #if ENABLE_RENDER_SELECTION_CENTER GLUquadricObj* m_quadric; #endif // ENABLE_RENDER_SELECTION_CENTER - mutable GLArrow m_arrow; #if ENABLE_GCODE_VIEWER + GL_Model m_arrow; GL_Model m_curved_arrow; Shader m_arrows_shader; #else + mutable GLArrow m_arrow; mutable GLCurvedArrow m_curved_arrow; #endif // ENABLE_GCODE_VIEWER @@ -325,7 +326,11 @@ public: #if ENABLE_RENDER_SELECTION_CENTER void render_center(bool gizmo_is_dragging) const; #endif // ENABLE_RENDER_SELECTION_CENTER +#if ENABLE_GCODE_VIEWER + void render_sidebar_hints(const std::string& sidebar_field) const; +#else void render_sidebar_hints(const std::string& sidebar_field, const Shader& shader) const; +#endif // ENABLE_GCODE_VIEWER bool requires_local_axes() const; @@ -368,10 +373,14 @@ private: void render_sidebar_scale_hints(const std::string& sidebar_field) const; void render_sidebar_size_hints(const std::string& sidebar_field) const; void render_sidebar_layers_hints(const std::string& sidebar_field) const; +#if !ENABLE_GCODE_VIEWER void render_sidebar_position_hint(Axis axis) const; +#endif // !ENABLE_GCODE_VIEWER void render_sidebar_rotation_hint(Axis axis) const; void render_sidebar_scale_hint(Axis axis) const; +#if !ENABLE_GCODE_VIEWER void render_sidebar_size_hint(Axis axis, double length) const; +#endif // !ENABLE_GCODE_VIEWER public: enum SyncRotationType { From 800a6c5e574ca9b35b4a1128860cb9b12d38ac13 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 13 May 2020 11:48:29 +0200 Subject: [PATCH 071/503] Tech ENABLE_GCODE_VIEWER -> Fixed normals in curved arrows model --- src/slic3r/GUI/GLModel.cpp | 105 ++++++++++++++++++++++++------------- 1 file changed, 68 insertions(+), 37 deletions(-) diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index ee98ce678a..e5b6cbdcb6 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -273,23 +273,36 @@ GLModelInitializationData circular_arrow(int resolution, float radius, float tip data.triangles.emplace_back(9, 8, 7); // side faces vertices + append_vertex(data, { 0.0f, outer_radius, -half_thickness }, Vec3f::UnitX()); + append_vertex(data, { 0.0f, radius + half_tip_width, -half_thickness }, Vec3f::UnitX()); append_vertex(data, { 0.0f, outer_radius, half_thickness }, Vec3f::UnitX()); - append_vertex(data, { 0.0f, radius + half_tip_width, half_thickness }, Vec3f::UnitY()); - append_vertex(data, { -tip_height, radius, half_thickness }, -Vec3f::UnitX()); - append_vertex(data, { 0.0f, radius - half_tip_width, half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { 0.0f, radius + half_tip_width, half_thickness }, Vec3f::UnitX()); + + Vec3f normal(-half_tip_width, tip_height, 0.0f); + normal.normalize(); + append_vertex(data, { 0.0f, radius + half_tip_width, -half_thickness }, normal); + append_vertex(data, { -tip_height, radius, -half_thickness }, normal); + append_vertex(data, { 0.0f, radius + half_tip_width, half_thickness }, normal); + append_vertex(data, { -tip_height, radius, half_thickness }, normal); + + normal = Vec3f(-half_tip_width, -tip_height, 0.0f); + normal.normalize(); + append_vertex(data, { -tip_height, radius, -half_thickness }, normal); + append_vertex(data, { 0.0f, radius - half_tip_width, -half_thickness }, normal); + append_vertex(data, { -tip_height, radius, half_thickness }, normal); + append_vertex(data, { 0.0f, radius - half_tip_width, half_thickness }, normal); + + append_vertex(data, { 0.0f, radius - half_tip_width, -half_thickness }, Vec3f::UnitX()); + append_vertex(data, { 0.0f, inner_radius, -half_thickness }, Vec3f::UnitX()); + append_vertex(data, { 0.0f, radius - half_tip_width, half_thickness }, Vec3f::UnitX()); append_vertex(data, { 0.0f, inner_radius, half_thickness }, Vec3f::UnitX()); - append_vertex(data, { 0.0f, outer_radius, -half_thickness }, Vec3f::UnitX()); - append_vertex(data, { 0.0f, radius + half_tip_width, -half_thickness }, Vec3f::UnitY()); - append_vertex(data, { -tip_height, radius, -half_thickness }, -Vec3f::UnitX()); - append_vertex(data, { 0.0f, radius - half_tip_width, -half_thickness }, -Vec3f::UnitY()); - append_vertex(data, { 0.0f, inner_radius, -half_thickness }, Vec3f::UnitX()); - - // side faces triangles + // side face triangles for (int i = 0; i < 4; ++i) { - data.triangles.emplace_back(15 + i, 11 + i, 10 + i); - data.triangles.emplace_back(15 + i, 16 + i, 11 + i); + int ii = i * 4; + data.triangles.emplace_back(10 + ii, 11 + ii, 13 + ii); + data.triangles.emplace_back(10 + ii, 13 + ii, 12 + ii); } // stem @@ -309,8 +322,8 @@ GLModelInitializationData circular_arrow(int resolution, float radius, float tip // top face triangles for (int i = 0; i < resolution; ++i) { - data.triangles.emplace_back(20 + i, 21 + i, 21 + resolution + i); - data.triangles.emplace_back(21 + i, 22 + resolution + i, 21 + resolution + i); + data.triangles.emplace_back(26 + i, 27 + i, 27 + resolution + i); + data.triangles.emplace_back(27 + i, 28 + resolution + i, 27 + resolution + i); } // bottom face vertices @@ -329,27 +342,11 @@ GLModelInitializationData circular_arrow(int resolution, float radius, float tip // bottom face triangles for (int i = 0; i < resolution; ++i) { - data.triangles.emplace_back(22 + 2 * resolution + i, 23 + 3 * resolution + i, 23 + 2 * resolution + i); - data.triangles.emplace_back(23 + 2 * resolution + i, 23 + 3 * resolution + i, 24 + 3 * resolution + i); - } - - // side faces vertices - for (int i = 0; i <= resolution; ++i) - { - float angle = static_cast(i) * step_angle; - float c = ::cos(angle); - float s = ::sin(angle); - append_vertex(data, { inner_radius * s, inner_radius * c, half_thickness }, { -s, -c, 0.0f}); - } - - for (int i = resolution; i >= 0; --i) - { - float angle = static_cast(i) * step_angle; - float c = ::cos(angle); - float s = ::sin(angle); - append_vertex(data, { outer_radius * s, outer_radius * c, half_thickness }, { s, c, 0.0f }); + data.triangles.emplace_back(28 + 2 * resolution + i, 29 + 3 * resolution + i, 29 + 2 * resolution + i); + data.triangles.emplace_back(29 + 2 * resolution + i, 29 + 3 * resolution + i, 30 + 3 * resolution + i); } + // side faces vertices and triangles for (int i = 0; i <= resolution; ++i) { float angle = static_cast(i) * step_angle; @@ -358,6 +355,31 @@ GLModelInitializationData circular_arrow(int resolution, float radius, float tip append_vertex(data, { inner_radius * s, inner_radius * c, -half_thickness }, { -s, -c, 0.0f }); } + for (int i = 0; i <= resolution; ++i) + { + float angle = static_cast(i) * step_angle; + float c = ::cos(angle); + float s = ::sin(angle); + append_vertex(data, { inner_radius * s, inner_radius * c, half_thickness }, { -s, -c, 0.0f }); + } + + int first_id = 26 + 4 * (resolution + 1); + for (int i = 0; i < resolution; ++i) + { + int ii = first_id + i; + data.triangles.emplace_back(ii, ii + 1, ii + resolution + 2); + data.triangles.emplace_back(ii, ii + resolution + 2, ii + resolution + 1); + } + + append_vertex(data, { inner_radius, 0.0f, -half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { outer_radius, 0.0f, -half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { inner_radius, 0.0f, half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { outer_radius, 0.0f, half_thickness }, -Vec3f::UnitY()); + + first_id = 26 + 6 * (resolution + 1); + data.triangles.emplace_back(first_id, first_id + 1, first_id + 3); + data.triangles.emplace_back(first_id, first_id + 3, first_id + 2); + for (int i = resolution; i >= 0; --i) { float angle = static_cast(i) * step_angle; @@ -366,11 +388,20 @@ GLModelInitializationData circular_arrow(int resolution, float radius, float tip append_vertex(data, { outer_radius * s, outer_radius * c, -half_thickness }, { s, c, 0.0f }); } - // side faces triangles - for (int i = 0; i < 2 * resolution + 1; ++i) + for (int i = resolution; i >= 0; --i) { - data.triangles.emplace_back(20 + 6 * (resolution + 1) + i, 21 + 6 * (resolution + 1) + i, 21 + 4 * (resolution + 1) + i); - data.triangles.emplace_back(20 + 6 * (resolution + 1) + i, 21 + 4 * (resolution + 1) + i, 20 + 4 * (resolution + 1) + i); + float angle = static_cast(i) * step_angle; + float c = ::cos(angle); + float s = ::sin(angle); + append_vertex(data, { outer_radius * s, outer_radius * c, +half_thickness }, { s, c, 0.0f }); + } + + first_id = 30 + 6 * (resolution + 1); + for (int i = 0; i < resolution; ++i) + { + int ii = first_id + i; + data.triangles.emplace_back(ii, ii + 1, ii + resolution + 2); + data.triangles.emplace_back(ii, ii + resolution + 2, ii + resolution + 1); } return data; From cd2e4002ed2b432ecd898db99d3306aa319bd94f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 13 May 2020 11:59:48 +0200 Subject: [PATCH 072/503] Tech ENABLE_GCODE_VIEWER -> Removed obsolete class GLArrow --- src/slic3r/GUI/3DScene.cpp | 7 ++++++- src/slic3r/GUI/3DScene.hpp | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index ef5e22c823..dc5376553f 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1944,6 +1944,9 @@ void GLModel::render() const glsafe(::glDisable(GL_BLEND)); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool GLArrow::on_init() { Pointf3s vertices; @@ -2002,7 +2005,9 @@ bool GLArrow::on_init() return true; } -#if !ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//#if !ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GLCurvedArrow::GLCurvedArrow(unsigned int resolution) : GLModel() , m_resolution(resolution) diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index d6ee72bdc1..d357fba6cf 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -642,13 +642,18 @@ protected: virtual bool on_init_from_file(const std::string& filename) { return false; } }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ class GLArrow : public GLModel { protected: bool on_init() override; }; -#if !ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//#if !ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ class GLCurvedArrow : public GLModel { unsigned int m_resolution; From 32529b66ac1945e406f4b7f4b795ee388a418e75 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 13 May 2020 13:55:00 +0200 Subject: [PATCH 073/503] Tech ENABLE_GCODE_VIEWER -> Small refactoring --- src/slic3r/GUI/3DScene.cpp | 5 ---- src/slic3r/GUI/3DScene.hpp | 5 ---- src/slic3r/GUI/GCodeViewer.cpp | 42 +++++++++++++++++----------------- 3 files changed, 21 insertions(+), 31 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index dc5376553f..29a4b84a30 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1944,9 +1944,7 @@ void GLModel::render() const glsafe(::glDisable(GL_BLEND)); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool GLArrow::on_init() { Pointf3s vertices; @@ -2005,9 +2003,6 @@ bool GLArrow::on_init() return true; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -//#if !ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GLCurvedArrow::GLCurvedArrow(unsigned int resolution) : GLModel() , m_resolution(resolution) diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index d357fba6cf..86072754d7 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -642,18 +642,13 @@ protected: virtual bool on_init_from_file(const std::string& filename) { return false; } }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ class GLArrow : public GLModel { protected: bool on_init() override; }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -//#if !ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ class GLCurvedArrow : public GLModel { unsigned int m_resolution; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 525a2fd5a1..aa188cae3f 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -978,14 +978,14 @@ void GCodeViewer::render_legend() const ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); switch (m_view_type) { - case EViewType::FeatureType: { imgui.text(I18N::translate_utf8(L("Feature type"))); break; } - case EViewType::Height: { imgui.text(I18N::translate_utf8(L("Height (mm)"))); break; } - case EViewType::Width: { imgui.text(I18N::translate_utf8(L("Width (mm)"))); break; } - case EViewType::Feedrate: { imgui.text(I18N::translate_utf8(L("Speed (mm/s)"))); break; } - case EViewType::FanSpeed: { imgui.text(I18N::translate_utf8(L("Fan Speed (%%)"))); break; } - case EViewType::VolumetricRate: { imgui.text(I18N::translate_utf8(L("Volumetric flow rate (mm³/s)"))); break; } - case EViewType::Tool: { imgui.text(I18N::translate_utf8(L("Tool"))); break; } - case EViewType::ColorPrint: { imgui.text(I18N::translate_utf8(L("Color Print"))); break; } + case EViewType::FeatureType: { imgui.text(_u8L("Feature type")); break; } + case EViewType::Height: { imgui.text(_u8L("Height (mm)")); break; } + case EViewType::Width: { imgui.text(_u8L("Width (mm)")); break; } + case EViewType::Feedrate: { imgui.text(_u8L("Speed (mm/s)")); break; } + case EViewType::FanSpeed: { imgui.text(_u8L("Fan Speed (%%)")); break; } + case EViewType::VolumetricRate: { imgui.text(_u8L("Volumetric flow rate (mm³/s)")); break; } + case EViewType::Tool: { imgui.text(_u8L("Tool")); break; } + case EViewType::ColorPrint: { imgui.text(_u8L("Color Print")); break; } default: { break; } } ImGui::PopStyleColor(); @@ -1001,7 +1001,7 @@ void GCodeViewer::render_legend() const if (!visible) ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f); - add_item(Extrusion_Role_Colors[static_cast(role)], I18N::translate_utf8(ExtrusionEntity::role_to_string(role)), [this, role]() { + add_item(Extrusion_Role_Colors[static_cast(role)], _u8L(ExtrusionEntity::role_to_string(role)), [this, role]() { if (role < erCount) { m_extrusions.role_visibility_flags = is_visible(role) ? m_extrusions.role_visibility_flags & ~(1 << role) : m_extrusions.role_visibility_flags | (1 << role); @@ -1031,7 +1031,7 @@ void GCodeViewer::render_legend() const if (it == m_extruder_ids.end()) continue; - add_item(m_tool_colors[i], (boost::format(I18N::translate_utf8(L("Extruder %d"))) % (i + 1)).str()); + add_item(m_tool_colors[i], (boost::format(_u8L("Extruder %d")) % (i + 1)).str()); } break; } @@ -1042,7 +1042,7 @@ void GCodeViewer::render_legend() const if (extruders_count == 1) { // single extruder use case if (custom_gcode_per_print_z.empty()) // no data to show - add_item(m_tool_colors.front(), I18N::translate_utf8(L("Default print color"))); + add_item(m_tool_colors.front(), _u8L("Default print color")); else { std::vector> cp_values; cp_values.reserve(custom_gcode_per_print_z.size()); @@ -1066,7 +1066,7 @@ void GCodeViewer::render_legend() const const int items_cnt = static_cast(cp_values.size()); if (items_cnt == 0) { // There is no one color change, but there are some pause print or custom Gcode - add_item(m_tool_colors.front(), I18N::translate_utf8(L("Default print color"))); + add_item(m_tool_colors.front(), _u8L("Default print color")); } else { for (int i = items_cnt; i >= 0; --i) { @@ -1074,14 +1074,14 @@ void GCodeViewer::render_legend() const std::string id_str = " (" + std::to_string(i + 1) + ")"; if (i == 0) { - add_item(m_tool_colors[i], (boost::format(I18N::translate_utf8(L("up to %.2f mm"))) % cp_values.front().first).str() + id_str); + add_item(m_tool_colors[i], (boost::format(_u8L("up to %.2f mm")) % cp_values.front().first).str() + id_str); break; } else if (i == items_cnt) { - add_item(m_tool_colors[i], (boost::format(I18N::translate_utf8(L("above %.2f mm"))) % cp_values[i - 1].second).str() + id_str); + add_item(m_tool_colors[i], (boost::format(_u8L("above %.2f mm")) % cp_values[i - 1].second).str() + id_str); continue; } - add_item(m_tool_colors[i], (boost::format(I18N::translate_utf8(L("%.2f - %.2f mm"))) % cp_values[i - 1].second % cp_values[i].first).str() + id_str); + add_item(m_tool_colors[i], (boost::format(_u8L("%.2f - %.2f mm")) % cp_values[i - 1].second% cp_values[i].first).str() + id_str); } } } @@ -1090,7 +1090,7 @@ void GCodeViewer::render_legend() const { // extruders for (unsigned int i = 0; i < (unsigned int)extruders_count; ++i) { - add_item(m_tool_colors[i], (boost::format(I18N::translate_utf8(L("Extruder %d"))) % (i + 1)).str()); + add_item(m_tool_colors[i], (boost::format(_u8L("Extruder %d")) % (i + 1)).str()); } // color changes @@ -1102,7 +1102,7 @@ void GCodeViewer::render_legend() const std::string id_str = " (" + std::to_string(color_change_idx--) + ")"; add_item(m_tool_colors[last_color_id--], - (boost::format(I18N::translate_utf8(L("Color change for Extruder %d at %.2f mm"))) % custom_gcode_per_print_z[i].extruder % custom_gcode_per_print_z[i].print_z).str() + id_str); + (boost::format(_u8L("Color change for Extruder %d at %.2f mm")) % custom_gcode_per_print_z[i].extruder % custom_gcode_per_print_z[i].print_z).str() + id_str); } } } @@ -1129,14 +1129,14 @@ void GCodeViewer::render_legend() const ImGui::Spacing(); ImGui::Spacing(); ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(I18N::translate_utf8(L("Travel"))); + imgui.text(_u8L("Travel")); ImGui::PopStyleColor(); ImGui::Separator(); // items - add_item(Travel_Colors[0], I18N::translate_utf8(L("Movement"))); - add_item(Travel_Colors[1], I18N::translate_utf8(L("Extrusion"))); - add_item(Travel_Colors[2], I18N::translate_utf8(L("Retraction"))); + add_item(Travel_Colors[0], _u8L("Movement")); + add_item(Travel_Colors[1], _u8L("Extrusion")); + add_item(Travel_Colors[2], _u8L("Retraction")); break; } From 5be901547e1c9cd641681f3c40c8aeabdbbe7332 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 15 May 2020 09:22:51 +0200 Subject: [PATCH 074/503] GCodeViewer -> Imgui slider for sequential view replaced by DoubleSlider::Control (wip) --- src/libslic3r/Print.hpp | 2 + src/libslic3r/Technologies.hpp | 1 + src/slic3r/GUI/BackgroundSlicingProcess.hpp | 2 + src/slic3r/GUI/DoubleSlider.cpp | 76 ++++-- src/slic3r/GUI/DoubleSlider.hpp | 8 +- src/slic3r/GUI/GCodeViewer.cpp | 14 + src/slic3r/GUI/GCodeViewer.hpp | 49 ++++ src/slic3r/GUI/GLCanvas3D.hpp | 6 + src/slic3r/GUI/GUI_Preview.cpp | 285 ++++++++++++++++++-- src/slic3r/GUI/GUI_Preview.hpp | 48 +++- src/slic3r/GUI/Plater.cpp | 21 ++ src/slic3r/GUI/Plater.hpp | 3 + 12 files changed, 463 insertions(+), 52 deletions(-) diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index c358cd9184..36ba68c45e 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -24,7 +24,9 @@ class Print; class PrintObject; class ModelObject; class GCode; +#if !ENABLE_GCODE_VIEWER class GCodePreviewData; +#endif // !ENABLE_GCODE_VIEWER enum class SlicingMode : uint32_t; // Print step IDs for keeping track of the print state. diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index c22e504dff..8ce01a3530 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -55,6 +55,7 @@ // Enable G-Code viewer #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) +#define ENABLE_GCODE_USE_WXWIDGETS_SLIDER (1 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index 91ebc1372c..22a99951ff 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -17,7 +17,9 @@ namespace Slic3r { class DynamicPrintConfig; +#if !ENABLE_GCODE_VIEWER class GCodePreviewData; +#endif // !ENABLE_GCODE_VIEWER class Model; class SLAPrint; class SL1Archive; diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 4732ff2613..021d73882e 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -20,7 +20,9 @@ #include #include #include +#if !ENABLE_GCODE_USE_WXWIDGETS_SLIDER #include +#endif // !ENABLE_GCODE_USE_WXWIDGETS_SLIDER #include #include @@ -408,22 +410,22 @@ void Control::render() // draw line draw_scroll_line(dc, lower_pos, higher_pos); - //draw color print ticks + // draw color print ticks draw_ticks(dc); // draw both sliders draw_thumbs(dc, lower_pos, higher_pos); - //draw lock/unlock + // draw lock/unlock draw_one_layer_icon(dc); - //draw revert bitmap (if it's shown) + // draw revert bitmap (if it's shown) draw_revert_icon(dc); - //draw cog bitmap (if it's shown) + // draw cog bitmap (if it's shown) draw_cog_icon(dc); - //draw mouse position + // draw mouse position draw_tick_on_mouse_position(dc); } @@ -535,10 +537,21 @@ wxString Control::get_label(int tick) const if (value >= m_values.size()) return "ErrVal"; - const wxString str = m_values.empty() ? - wxNumberFormatter::ToString(m_label_koef*value, 2, wxNumberFormatter::Style_None) : - wxNumberFormatter::ToString(m_values[value], 2, wxNumberFormatter::Style_None); - return format_wxstr("%1%\n(%2%)", str, m_values.empty() ? value : value+1); +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER + if (m_draw_mode == dmSequentialGCodeView) + return wxString::Format("%d", static_cast(m_values[value])); + else { + const wxString str = m_values.empty() ? + wxString::Format("%.*f", 2, m_label_koef * value) : + wxString::Format("%.*f", 2, m_values[value]); + return format_wxstr("%1%\n(%2%)", str, m_values.empty() ? value : value + 1); + } +#else + const wxString str = m_values.empty() ? + wxNumberFormatter::ToString(m_label_koef * value, 2, wxNumberFormatter::Style_None) : + wxNumberFormatter::ToString(m_values[value], 2, wxNumberFormatter::Style_None); + return format_wxstr("%1%\n(%2%)", str, m_values.empty() ? value : value + 1); +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER } void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_side/*=true*/) const @@ -779,6 +792,11 @@ void Control::draw_colored_band(wxDC& dc) void Control::draw_one_layer_icon(wxDC& dc) { +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER + if (m_draw_mode == dmSequentialGCodeView) + return; +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER + const wxBitmap& icon = m_is_one_layer ? m_focus == fiOneLayerIcon ? m_bmp_one_layer_lock_off.bmp() : m_bmp_one_layer_lock_on.bmp() : m_focus == fiOneLayerIcon ? m_bmp_one_layer_unlock_off.bmp() : m_bmp_one_layer_unlock_on.bmp(); @@ -1284,7 +1302,11 @@ void Control::OnWheel(wxMouseEvent& event) ssLower : ssHigher; } +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER + move_current_thumb((m_draw_mode == dmSequentialGCodeView) ? event.GetWheelRotation() < 0 : event.GetWheelRotation() > 0); +#else move_current_thumb(event.GetWheelRotation() > 0); +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER } void Control::OnKeyDown(wxKeyEvent &event) @@ -1306,20 +1328,34 @@ void Control::OnKeyDown(wxKeyEvent &event) UseDefaultColors(false); else if (is_horizontal()) { - if (key == WXK_LEFT || key == WXK_RIGHT) - move_current_thumb(key == WXK_LEFT); - else if (key == WXK_UP || key == WXK_DOWN) { - m_selection = key == WXK_UP ? ssHigher : ssLower; - Refresh(); +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER + if (m_is_focused) + { +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER + if (key == WXK_LEFT || key == WXK_RIGHT) + move_current_thumb(key == WXK_LEFT); + else if (key == WXK_UP || key == WXK_DOWN) { + m_selection = key == WXK_UP ? ssHigher : ssLower; + Refresh(); + } +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER + } +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER } - } else { - if (key == WXK_LEFT || key == WXK_RIGHT) { - m_selection = key == WXK_LEFT ? ssHigher : ssLower; - Refresh(); +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER + if (m_is_focused) + { +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER + if (key == WXK_LEFT || key == WXK_RIGHT) { + m_selection = key == WXK_LEFT ? ssHigher : ssLower; + Refresh(); + } + else if (key == WXK_UP || key == WXK_DOWN) + move_current_thumb(key == WXK_UP); +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER } - else if (key == WXK_UP || key == WXK_DOWN) - move_current_thumb(key == WXK_UP); +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER } event.Skip(); // !Needed to have EVT_CHAR generated as well diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index 36bff17e94..a17fb2b6fa 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -73,6 +73,9 @@ enum DrawMode dmRegular, dmSlaPrint, dmSequentialFffPrint, +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER + dmSequentialGCodeView, +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER }; using t_mode = CustomGCode::Mode; @@ -211,6 +214,9 @@ public: void SetTicksValues(const Slic3r::CustomGCode::Info &custom_gcode_per_print_z); void SetDrawMode(bool is_sla_print, bool is_sequential_print); +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER + void SetDrawMode(DrawMode mode) { m_draw_mode = mode; } +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER void SetManipulationMode(t_mode mode) { m_mode = mode; } t_mode GetManipulationMode() const { return m_mode; } @@ -223,7 +229,7 @@ public: bool is_higher_at_max() const { return m_higher_value == m_max_value; } bool is_full_span() const { return this->is_lower_at_min() && this->is_higher_at_max(); } - void OnPaint(wxPaintEvent& ) { render();} + void OnPaint(wxPaintEvent& ) { render(); } void OnLeftDown(wxMouseEvent& event); void OnMotion(wxMouseEvent& event); void OnLeftUp(wxMouseEvent& event); diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index aa188cae3f..acf1da4c71 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -324,7 +324,9 @@ void GCodeViewer::render() const m_sequential_view.marker.render(); render_shells(); render_legend(); +#if !ENABLE_GCODE_USE_WXWIDGETS_SLIDER render_sequential_bar(); +#endif // !ENABLE_GCODE_USE_WXWIDGETS_SLIDER #if ENABLE_GCODE_VIEWER_STATISTICS render_statistics(); #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -381,6 +383,16 @@ void GCodeViewer::set_options_visibility_from_flags(unsigned int flags) enable_legend(is_flag_set(9)); } +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER +void GCodeViewer::set_layers_z_range(const std::array& layers_z_range) +{ + bool keep_sequential_current = layers_z_range[1] <= m_layers_z_range[1]; + m_layers_z_range = layers_z_range; + refresh_render_paths(keep_sequential_current); + wxGetApp().plater()->update_preview_horz_slider(); +} +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER + bool GCodeViewer::init_shaders() { unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); @@ -1147,6 +1159,7 @@ void GCodeViewer::render_legend() const ImGui::PopStyleVar(); } +#if !ENABLE_GCODE_USE_WXWIDGETS_SLIDER void GCodeViewer::render_sequential_bar() const { static const float MARGIN = 125.0f; @@ -1231,6 +1244,7 @@ void GCodeViewer::render_sequential_bar() const imgui.end(); ImGui::PopStyleVar(); } +#endif // !ENABLE_GCODE_USE_WXWIDGETS_SLIDER #if ENABLE_GCODE_VIEWER_STATISTICS void GCodeViewer::render_statistics() const diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 6f3ca47dba..a4ac3358d2 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -149,6 +149,7 @@ class GCodeViewer void reset_ranges() { ranges.reset(); } }; +#if !ENABLE_GCODE_USE_WXWIDGETS_SLIDER struct SequentialView { class Marker @@ -182,6 +183,7 @@ class GCodeViewer Vec3f current_position{ Vec3f::Zero() }; Marker marker; }; +#endif // !ENABLE_GCODE_USE_WXWIDGETS_SLIDER #if ENABLE_GCODE_VIEWER_STATISTICS struct Statistics @@ -229,6 +231,42 @@ class GCodeViewer #endif // ENABLE_GCODE_VIEWER_STATISTICS public: +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER + struct SequentialView + { + class Marker + { + GL_Model m_model; + Transform3f m_world_transform; + std::array m_color{ 1.0f, 1.0f, 1.0f, 1.0f }; + bool m_visible{ false }; + Shader m_shader; + + public: + void init(); + + const BoundingBoxf3& get_bounding_box() const { return m_model.get_bounding_box(); } + + void set_world_transform(const Transform3f& transform) { m_world_transform = transform; } + void set_color(const std::array& color) { m_color = color; } + + bool is_visible() const { return m_visible; } + void set_visible(bool visible) { m_visible = visible; } + + void render() const; + + private: + void init_shader(); + }; + + unsigned int first{ 0 }; + unsigned int last{ 0 }; + unsigned int current{ 0 }; + Vec3f current_position{ Vec3f::Zero() }; + Marker marker; + }; +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER + enum class EViewType : unsigned char { FeatureType, @@ -284,6 +322,11 @@ public: const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; } const std::vector& get_layers_zs() const { return m_layers_zs; }; +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER + const SequentialView& get_sequential_view() const { return m_sequential_view; } + void update_sequential_view_current(unsigned int low, unsigned int high) { m_sequential_view.current = high; refresh_render_paths(true); } +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER + EViewType get_view_type() const { return m_view_type; } void set_view_type(EViewType type) { if (type == EViewType::Count) @@ -298,12 +341,16 @@ public: void set_toolpath_role_visibility_flags(unsigned int flags) { m_extrusions.role_visibility_flags = flags; } unsigned int get_options_visibility_flags() const; void set_options_visibility_from_flags(unsigned int flags); +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER + void set_layers_z_range(const std::array& layers_z_range); +#else void set_layers_z_range(const std::array& layers_z_range) { bool keep_sequential_current = layers_z_range[1] <= m_layers_z_range[1]; m_layers_z_range = layers_z_range; refresh_render_paths(keep_sequential_current); } +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER bool is_legend_enabled() const { return m_legend_enabled; } void enable_legend(bool enable) { m_legend_enabled = enable; } @@ -316,7 +363,9 @@ private: void render_toolpaths() const; void render_shells() const; void render_legend() const; +#if !ENABLE_GCODE_USE_WXWIDGETS_SLIDER void render_sequential_bar() const; +#endif // !ENABLE_GCODE_USE_WXWIDGETS_SLIDER #if ENABLE_GCODE_VIEWER_STATISTICS void render_statistics() const; #endif // ENABLE_GCODE_VIEWER_STATISTICS diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 3e7296e139..d4df60ad2a 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -42,7 +42,9 @@ namespace Slic3r { class Bed3D; struct Camera; class BackgroundSlicingProcess; +#if !ENABLE_GCODE_VIEWER class GCodePreviewData; +#endif // !ENABLE_GCODE_VIEWER struct ThumbnailData; struct SlicingParameters; enum LayerHeightEditActionType : unsigned int; @@ -551,6 +553,10 @@ public: #if ENABLE_GCODE_VIEWER void reset_gcode_toolpaths() { m_gcode_viewer.reset(); } +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER + const GCodeViewer::SequentialView& get_gcode_sequential_view() const { return m_gcode_viewer.get_sequential_view(); } + void update_gcode_sequential_view_current(unsigned int low, unsigned int high) { m_gcode_viewer.update_sequential_view_current(low, high); } +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER #endif // ENABLE_GCODE_VIEWER void toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index fc97796749..9547e0bf89 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -210,7 +210,9 @@ Preview::Preview( , m_number_extruders(1) , m_preferred_color_mode("feature") , m_loaded(false) +#if !ENABLE_GCODE_VIEWER , m_enabled(false) +#endif // !ENABLE_GCODE_VIEWER , m_schedule_background_process(schedule_background_process_func) #ifdef __linux__ , m_volumes_cleanup_required(false) @@ -245,8 +247,12 @@ bool Preview::init(wxWindow* parent, Model* model) m_canvas->enable_dynamic_background(true); m_canvas->enable_collapse_toolbar(true); +#if ENABLE_GCODE_VIEWER + m_double_slider_sizer = create_vert_slider_sizer(); +#else m_double_slider_sizer = new wxBoxSizer(wxHORIZONTAL); create_double_slider(); +#endif // ENABLE_GCODE_VIEWER m_label_view_type = new wxStaticText(this, wxID_ANY, _L("View")); @@ -315,15 +321,25 @@ bool Preview::init(wxWindow* parent, Model* model) top_sizer->Add(m_canvas_widget, 1, wxALL | wxEXPAND, 0); top_sizer->Add(m_double_slider_sizer, 0, wxEXPAND, 0); +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER + m_horz_slider = new DoubleSlider::Control(this, wxID_ANY, 0, 0, 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL); + m_horz_slider->SetDrawMode(DoubleSlider::dmSequentialGCodeView); + m_horz_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_horz_slider_scroll_changed, this); +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER + #if ENABLE_GCODE_VIEWER m_bottom_toolbar_sizer = new wxBoxSizer(wxHORIZONTAL); m_bottom_toolbar_sizer->AddSpacer(10); - m_bottom_toolbar_sizer->Add(m_label_view_type, 0, wxALIGN_CENTER_VERTICAL, 5); - m_bottom_toolbar_sizer->Add(m_choice_view_type, 0, wxEXPAND | wxALL, 5); + m_bottom_toolbar_sizer->Add(m_label_view_type, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5); + m_bottom_toolbar_sizer->Add(m_choice_view_type, 0, wxALIGN_CENTER_VERTICAL, 5); m_bottom_toolbar_sizer->AddSpacer(10); - m_bottom_toolbar_sizer->Add(m_label_show, 0, wxALIGN_CENTER_VERTICAL, 5); - m_bottom_toolbar_sizer->Add(m_combochecklist_options, 0, wxEXPAND | wxALL, 5); - m_bottom_toolbar_sizer->Add(m_combochecklist_features, 0, wxEXPAND | wxALL, 5); + m_bottom_toolbar_sizer->Add(m_label_show, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, 5); + m_bottom_toolbar_sizer->Add(m_combochecklist_options, 0, wxALIGN_CENTER_VERTICAL, 5); + m_bottom_toolbar_sizer->Add(m_combochecklist_features, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 5); +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER + m_bottom_toolbar_sizer->AddSpacer(10); + m_bottom_toolbar_sizer->Add(m_horz_slider, 1, wxALL | wxEXPAND, 5); +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER #else wxBoxSizer* bottom_sizer = new wxBoxSizer(wxHORIZONTAL); bottom_sizer->Add(m_label_view_type, 0, wxALIGN_CENTER_VERTICAL, 5); @@ -417,10 +433,12 @@ void Preview::set_number_extruders(unsigned int number_extruders) } } +#if !ENABLE_GCODE_VIEWER void Preview::set_enabled(bool enabled) { m_enabled = enabled; } +#endif // !ENABLE_GCODE_VIEWER void Preview::bed_shape_changed() { @@ -498,7 +516,14 @@ void Preview::refresh_print() void Preview::msw_rescale() { // rescale slider +#if ENABLE_GCODE_VIEWER + if (m_vert_slider != nullptr) m_vert_slider->msw_rescale(); +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER + if (m_horz_slider != nullptr) m_horz_slider->msw_rescale(); +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#else if (m_slider) m_slider->msw_rescale(); +#endif // ENABLE_GCODE_VIEWER // rescale warning legend on the canvas get_canvas3d()->msw_rescale(); @@ -509,14 +534,22 @@ void Preview::msw_rescale() void Preview::move_double_slider(wxKeyEvent& evt) { - if (m_slider) +#if ENABLE_GCODE_VIEWER + if (m_vert_slider != nullptr) m_vert_slider->OnKeyDown(evt); +#else + if (m_slider) m_slider->OnKeyDown(evt); +#endif // ENABLE_GCODE_VIEWER } void Preview::edit_double_slider(wxKeyEvent& evt) { - if (m_slider) +#if ENABLE_GCODE_VIEWER + if (m_vert_slider != nullptr) m_vert_slider->OnChar(evt); +#else + if (m_slider) m_slider->OnChar(evt); +#endif // ENABLE_GCODE_VIEWER } void Preview::bind_event_handlers() @@ -580,25 +613,35 @@ void Preview::show_hide_ui_elements(const std::string& what) } #endif // !ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER +void Preview::hide_vert_slider() +{ + m_double_slider_sizer->Hide((size_t)0); + Layout(); +} +#else void Preview::reset_sliders(bool reset_all) { m_enabled = false; -// reset_double_slider(); + // reset_double_slider(); if (reset_all) m_double_slider_sizer->Hide((size_t)0); else m_double_slider_sizer->GetItem(size_t(0))->GetSizer()->Hide(1); } +#endif // ENABLE_GCODE_VIEWER +#if !ENABLE_GCODE_VIEWER void Preview::update_sliders(const std::vector& layers_z, bool keep_z_range) { m_enabled = true; - update_double_slider(layers_z, keep_z_range); + m_double_slider_sizer->Show((size_t)0); Layout(); } +#endif // !ENABLE_GCODE_VIEWER void Preview::on_size(wxSizeEvent& evt) { @@ -705,32 +748,68 @@ void Preview::update_bottom_toolbar() combochecklist_set_flags(m_combochecklist_features, m_canvas->get_toolpath_role_visibility_flags()); combochecklist_set_flags(m_combochecklist_options, m_canvas->get_gcode_options_visibility_flags()); - m_bottom_toolbar_sizer->Show(m_combochecklist_features, + m_bottom_toolbar_sizer->Show(m_combochecklist_features, !m_canvas->is_gcode_legend_enabled() || m_canvas->get_gcode_view_type() != GCodeViewer::EViewType::FeatureType); m_bottom_toolbar_sizer->Layout(); + Refresh(); } #endif // ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER +wxBoxSizer* Preview::create_vert_slider_sizer() +{ + wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); + m_vert_slider = new DoubleSlider::Control(this, wxID_ANY, 0, 0, 0, 100); + + m_vert_slider->SetDrawMode(wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA, + wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_bool("complete_objects")); + + sizer->Add(m_vert_slider, 0, wxEXPAND, 0); + + // sizer, m_canvas_widget + m_canvas_widget->Bind(wxEVT_KEY_DOWN, &Preview::update_vert_slider_from_canvas, this); + m_canvas_widget->Bind(wxEVT_KEY_UP, [this](wxKeyEvent& event) { + if (event.GetKeyCode() == WXK_SHIFT) + m_vert_slider->UseDefaultColors(true); + event.Skip(); + }); + + m_vert_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_vert_slider_scroll_changed, this); + + Bind(DoubleSlider::wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) { + Model& model = wxGetApp().plater()->model(); + model.custom_gcode_per_print_z = m_vert_slider->GetTicksValues(); + m_schedule_background_process(); + + update_view_type(false); + + reload_print(); + }); + + return sizer; +} +#else void Preview::create_double_slider() { m_slider = new DoubleSlider::Control(this, wxID_ANY, 0, 0, 0, 100); + bool sla_print_technology = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA; bool sequential_print = wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_bool("complete_objects"); m_slider->SetDrawMode(sla_print_technology, sequential_print); m_double_slider_sizer->Add(m_slider, 0, wxEXPAND, 0); + // sizer, m_canvas_widget m_canvas_widget->Bind(wxEVT_KEY_DOWN, &Preview::update_double_slider_from_canvas, this); m_canvas_widget->Bind(wxEVT_KEY_UP, [this](wxKeyEvent& event) { if (event.GetKeyCode() == WXK_SHIFT) m_slider->UseDefaultColors(true); event.Skip(); - }); + }); m_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_sliders_scroll_changed, this); - Bind(DoubleSlider::wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) { Model& model = wxGetApp().plater()->model(); model.custom_gcode_per_print_z = m_slider->GetTicksValues(); @@ -739,8 +818,9 @@ void Preview::create_double_slider() update_view_type(false); reload_print(); - }); + }); } +#endif // ENABLE_GCODE_VIEWER // Find an index of a value in a sorted vector, which is in . // Returns -1 if there is no such member. @@ -769,8 +849,13 @@ static int find_close_layer_idx(const std::vector& zs, double &z, double return -1; } +#if ENABLE_GCODE_VIEWER +void Preview::check_vert_slider_values(std::vector& ticks_from_model, + const std::vector& layers_z) +#else void Preview::check_slider_values(std::vector& ticks_from_model, const std::vector& layers_z) +#endif // ENABLE_GCODE_VIEWER { // All ticks that would end up outside the slider range should be erased. // TODO: this should be placed into more appropriate part of code, @@ -787,12 +872,68 @@ void Preview::check_slider_values(std::vector& ticks_from_mod m_schedule_background_process(); } -void Preview::update_double_slider(const std::vector& layers_z, bool keep_z_range) +#if ENABLE_GCODE_VIEWER +void Preview::update_vert_slider(const std::vector& layers_z, bool keep_z_range) +{ + // Save the initial slider span. + double z_low = m_vert_slider->GetLowerValueD(); + double z_high = m_vert_slider->GetHigherValueD(); + bool was_empty = m_vert_slider->GetMaxValue() == 0; + + bool force_sliders_full_range = was_empty; + if (!keep_z_range) + { + bool span_changed = layers_z.empty() || std::abs(layers_z.back() - m_vert_slider->GetMaxValueD()) > DoubleSlider::epsilon()/*1e-6*/; + force_sliders_full_range |= span_changed; +} + bool snap_to_min = force_sliders_full_range || m_vert_slider->is_lower_at_min(); + bool snap_to_max = force_sliders_full_range || m_vert_slider->is_higher_at_max(); + + // Detect and set manipulation mode for double slider + update_vert_slider_mode(); + + CustomGCode::Info& ticks_info_from_model = wxGetApp().plater()->model().custom_gcode_per_print_z; + check_vert_slider_values(ticks_info_from_model.gcodes, layers_z); + + m_vert_slider->SetSliderValues(layers_z); + assert(m_vert_slider->GetMinValue() == 0); + m_vert_slider->SetMaxValue(layers_z.empty() ? 0 : layers_z.size() - 1); + + int idx_low = 0; + int idx_high = m_vert_slider->GetMaxValue(); + if (!layers_z.empty()) { + if (!snap_to_min) { + int idx_new = find_close_layer_idx(layers_z, z_low, DoubleSlider::epsilon()/*1e-6*/); + if (idx_new != -1) + idx_low = idx_new; + } + if (!snap_to_max) { + int idx_new = find_close_layer_idx(layers_z, z_high, DoubleSlider::epsilon()/*1e-6*/); + if (idx_new != -1) + idx_high = idx_new; + } + } + m_vert_slider->SetSelectionSpan(idx_low, idx_high); + + m_vert_slider->SetTicksValues(ticks_info_from_model); + + bool sla_print_technology = wxGetApp().plater()->printer_technology() == ptSLA; + bool sequential_print = wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_bool("complete_objects"); + m_vert_slider->SetDrawMode(sla_print_technology, sequential_print); + + m_vert_slider->SetExtruderColors(wxGetApp().plater()->get_extruder_colors_from_plater_config()); + + m_double_slider_sizer->Show((size_t)0); + Layout(); +} +#else +void Preview::update_double_slider(const std::vector & layers_z, bool keep_z_range) { // Save the initial slider span. double z_low = m_slider->GetLowerValueD(); double z_high = m_slider->GetHigherValueD(); bool was_empty = m_slider->GetMaxValue() == 0; + bool force_sliders_full_range = was_empty; if (!keep_z_range) { @@ -800,27 +941,27 @@ void Preview::update_double_slider(const std::vector& layers_z, bool kee force_sliders_full_range |= span_changed; } bool snap_to_min = force_sliders_full_range || m_slider->is_lower_at_min(); - bool snap_to_max = force_sliders_full_range || m_slider->is_higher_at_max(); + bool snap_to_max = force_sliders_full_range || m_slider->is_higher_at_max(); // Detect and set manipulation mode for double slider update_double_slider_mode(); - CustomGCode::Info &ticks_info_from_model = wxGetApp().plater()->model().custom_gcode_per_print_z; + CustomGCode::Info& ticks_info_from_model = wxGetApp().plater()->model().custom_gcode_per_print_z; check_slider_values(ticks_info_from_model.gcodes, layers_z); m_slider->SetSliderValues(layers_z); assert(m_slider->GetMinValue() == 0); m_slider->SetMaxValue(layers_z.empty() ? 0 : layers_z.size() - 1); - int idx_low = 0; + int idx_low = 0; int idx_high = m_slider->GetMaxValue(); - if (! layers_z.empty()) { - if (! snap_to_min) { + if (!layers_z.empty()) { + if (!snap_to_min) { int idx_new = find_close_layer_idx(layers_z, z_low, DoubleSlider::epsilon()/*1e-6*/); if (idx_new != -1) idx_low = idx_new; } - if (! snap_to_max) { + if (!snap_to_max) { int idx_new = find_close_layer_idx(layers_z, z_high, DoubleSlider::epsilon()/*1e-6*/); if (idx_new != -1) idx_high = idx_new; @@ -836,8 +977,13 @@ void Preview::update_double_slider(const std::vector& layers_z, bool kee m_slider->SetExtruderColors(wxGetApp().plater()->get_extruder_colors_from_plater_config()); } +#endif // ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER +void Preview::update_vert_slider_mode() +#else void Preview::update_double_slider_mode() +#endif // ENABLE_GCODE_VIEWER { // true -> single-extruder printer profile OR // multi-extruder printer profile , but whole model is printed by only one extruder @@ -886,16 +1032,68 @@ void Preview::update_double_slider_mode() } } +#if ENABLE_GCODE_VIEWER + m_vert_slider->SetModeAndOnlyExtruder(one_extruder_printed_model, only_extruder); +#else m_slider->SetModeAndOnlyExtruder(one_extruder_printed_model, only_extruder); +#endif // ENABLE_GCODE_VIEWER } +#if ENABLE_GCODE_VIEWER +void Preview::reset_vert_slider() +{ + m_vert_slider->SetHigherValue(0); + m_vert_slider->SetLowerValue(0); +} +#else void Preview::reset_double_slider() { m_slider->SetHigherValue(0); m_slider->SetLowerValue(0); } +#endif // ENABLE_GCODE_VIEWER -void Preview::update_double_slider_from_canvas(wxKeyEvent& event) +#if ENABLE_GCODE_VIEWER +void Preview::update_vert_slider_from_canvas(wxKeyEvent& event) +{ + if (event.HasModifiers()) { + event.Skip(); + return; + } + + const auto key = event.GetKeyCode(); + + if (key == 'U' || key == 'D') { + const int new_pos = key == 'U' ? m_vert_slider->GetHigherValue() + 1 : m_vert_slider->GetHigherValue() - 1; + m_vert_slider->SetHigherValue(new_pos); + if (event.ShiftDown() || m_vert_slider->is_one_layer()) m_vert_slider->SetLowerValue(m_vert_slider->GetHigherValue()); + } + else if (key == 'S') + m_vert_slider->ChangeOneLayerLock(); + else if (key == WXK_SHIFT) + m_vert_slider->UseDefaultColors(false); + else + event.Skip(); +} + +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER +void Preview::update_horz_slider() +{ + const GCodeViewer::SequentialView& view = m_canvas->get_gcode_sequential_view(); + std::vector values(view.last - view.first + 1); + unsigned int count = 0; + for (unsigned int i = view.first; i <= view.last; ++i) + { + values[count++] = static_cast(i + 1); + } + + m_horz_slider->SetSliderValues(values); + m_horz_slider->SetMaxValue(view.last - view.first); + m_horz_slider->SetSelectionSpan(0, view.current); +} +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#else +void Preview::update_double_slider_from_canvas(wxKeyEvent & event) { if (event.HasModifiers()) { event.Skip(); @@ -909,13 +1107,11 @@ void Preview::update_double_slider_from_canvas(wxKeyEvent& event) m_slider->SetHigherValue(new_pos); if (event.ShiftDown() || m_slider->is_one_layer()) m_slider->SetLowerValue(m_slider->GetHigherValue()); } -#if !ENABLE_GCODE_VIEWER else if (key == 'L') { m_checkbox_legend->SetValue(!m_checkbox_legend->GetValue()); auto evt = wxCommandEvent(); on_checkbox_legend(evt); } -#endif // !ENABLE_GCODE_VIEWER else if (key == 'S') m_slider->ChangeOneLayerLock(); else if (key == WXK_SHIFT) @@ -923,6 +1119,7 @@ void Preview::update_double_slider_from_canvas(wxKeyEvent& event) else event.Skip(); } +#endif // ENABLE_GCODE_VIEWER void Preview::load_print_as_fff(bool keep_z_range) { @@ -951,10 +1148,12 @@ void Preview::load_print_as_fff(bool keep_z_range) if (! has_layers) { +#if ENABLE_GCODE_VIEWER + hide_vert_slider(); +#else reset_sliders(true); -#if !ENABLE_GCODE_VIEWER m_canvas->reset_legend_texture(); -#endif // !ENABLE_GCODE_VIEWER +#endif // ENABLE_GCODE_VIEWER m_canvas_widget->Refresh(); return; } @@ -1046,10 +1245,18 @@ void Preview::load_print_as_fff(bool keep_z_range) #endif // !ENABLE_GCODE_VIEWER if (zs.empty()) { // all layers filtered out +#if ENABLE_GCODE_VIEWER + hide_vert_slider(); +#else reset_sliders(true); +#endif // ENABLE_GCODE_VIEWER m_canvas_widget->Refresh(); } else +#if ENABLE_GCODE_VIEWER + update_vert_slider(zs, keep_z_range); +#else update_sliders(zs, keep_z_range); +#endif // ENABLE_GCODE_VIEWER } } @@ -1077,7 +1284,11 @@ void Preview::load_print_as_sla() n_layers = (unsigned int)zs.size(); if (n_layers == 0) { +#if ENABLE_GCODE_VIEWER + hide_vert_slider(); +#else reset_sliders(true); +#endif // ENABLE_GCODE_VIEWER m_canvas_widget->Refresh(); } @@ -1092,13 +1303,21 @@ void Preview::load_print_as_sla() #endif // ENABLE_GCODE_VIEWER if (n_layers > 0) +#if ENABLE_GCODE_VIEWER + update_vert_slider(zs); +#else update_sliders(zs); +#endif // ENABLE_GCODE_VIEWER m_loaded = true; } } +#if ENABLE_GCODE_VIEWER +void Preview::on_vert_slider_scroll_changed(wxCommandEvent& event) +#else void Preview::on_sliders_scroll_changed(wxCommandEvent& event) +#endif // ENABLE_GCODE_VIEWER { if (IsShown()) { @@ -1106,7 +1325,7 @@ void Preview::on_sliders_scroll_changed(wxCommandEvent& event) if (tech == ptFFF) { #if ENABLE_GCODE_VIEWER - m_canvas->set_toolpaths_z_range({ m_slider->GetLowerValueD(), m_slider->GetHigherValueD() }); + m_canvas->set_toolpaths_z_range({ m_vert_slider->GetLowerValueD(), m_vert_slider->GetHigherValueD() }); m_canvas->set_as_dirty(); #else m_canvas->set_toolpaths_range(m_slider->GetLowerValueD() - 1e-6, m_slider->GetHigherValueD() + 1e-6); @@ -1116,13 +1335,27 @@ void Preview::on_sliders_scroll_changed(wxCommandEvent& event) } else if (tech == ptSLA) { +#if ENABLE_GCODE_VIEWER + m_canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), -m_vert_slider->GetLowerValueD())); + m_canvas->set_clipping_plane(1, ClippingPlane(-Vec3d::UnitZ(), m_vert_slider->GetHigherValueD())); + m_canvas->set_use_clipping_planes(m_vert_slider->GetHigherValue() != 0); +#else m_canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), -m_slider->GetLowerValueD())); m_canvas->set_clipping_plane(1, ClippingPlane(-Vec3d::UnitZ(), m_slider->GetHigherValueD())); m_canvas->set_use_clipping_planes(m_slider->GetHigherValue() != 0); +#endif // ENABLE_GCODE_VIEWER m_canvas->render(); } } } +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER +void Preview::on_horz_slider_scroll_changed(wxCommandEvent& event) +{ + m_canvas->update_gcode_sequential_view_current(static_cast(m_horz_slider->GetLowerValueD()), static_cast(m_horz_slider->GetHigherValueD())); + m_canvas->render(); +} +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index a11a474ccf..4df48a153e 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -24,7 +24,9 @@ namespace Slic3r { class DynamicPrintConfig; class Print; class BackgroundSlicingProcess; +#if !ENABLE_GCODE_VIEWER class GCodePreviewData; +#endif // !ENABLE_GCODE_VIEWER class Model; namespace DoubleSlider { @@ -100,9 +102,10 @@ class Preview : public wxPanel DynamicPrintConfig* m_config; BackgroundSlicingProcess* m_process; - GCodePreviewData* m_gcode_preview_data; #if ENABLE_GCODE_VIEWER GCodeProcessor::Result* m_gcode_result; +#else + GCodePreviewData* m_gcode_preview_data; #endif // ENABLE_GCODE_VIEWER #ifdef __linux__ @@ -118,9 +121,18 @@ class Preview : public wxPanel std::string m_preferred_color_mode; bool m_loaded; +#if !ENABLE_GCODE_VIEWER bool m_enabled; +#endif // !ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER + DoubleSlider::Control* m_vert_slider{ nullptr }; +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER + DoubleSlider::Control* m_horz_slider{ nullptr }; +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#else DoubleSlider::Control* m_slider {nullptr}; +#endif // ENABLE_GCODE_VIEWER public: #if ENABLE_GCODE_VIEWER @@ -138,7 +150,9 @@ Preview(wxWindow* parent, Model* model, DynamicPrintConfig* config, void set_as_dirty(); void set_number_extruders(unsigned int number_extruders); +#if !ENABLE_GCODE_VIEWER void set_enabled(bool enabled); +#endif // !ENABLE_GCODE_VIEWER void bed_shape_changed(); void select_view(const std::string& direction); void set_drop_target(wxDropTarget* target); @@ -157,6 +171,9 @@ Preview(wxWindow* parent, Model* model, DynamicPrintConfig* config, #if ENABLE_GCODE_VIEWER void update_bottom_toolbar(); +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER + void update_horz_slider(); +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER #endif // ENABLE_GCODE_VIEWER private: @@ -165,12 +182,14 @@ private: void bind_event_handlers(); void unbind_event_handlers(); -#if !ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER + void hide_vert_slider(); +#else void show_hide_ui_elements(const std::string& what); -#endif // !ENABLE_GCODE_VIEWER void reset_sliders(bool reset_all); void update_sliders(const std::vector& layers_z, bool keep_z_range = false); +#endif // ENABLE_GCODE_VIEWER void on_size(wxSizeEvent& evt); void on_choice_view_type(wxCommandEvent& evt); @@ -185,20 +204,39 @@ private: void on_checkbox_legend(wxCommandEvent& evt); #endif // ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER + // Create/Update/Reset double slider on 3dPreview + wxBoxSizer* create_vert_slider_sizer(); + void check_vert_slider_values(std::vector& ticks_from_model, + const std::vector& layers_z); + void reset_vert_slider(); + void update_vert_slider(const std::vector& layers_z, bool keep_z_range = false); + void update_vert_slider_mode(); + // update vertical DoubleSlider after keyDown in canvas + void update_vert_slider_from_canvas(wxKeyEvent& event); +#else // Create/Update/Reset double slider on 3dPreview void create_double_slider(); - void check_slider_values(std::vector &ticks_from_model, - const std::vector &layers_z); + void check_slider_values(std::vector& ticks_from_model, + const std::vector& layers_z); void reset_double_slider(); void update_double_slider(const std::vector& layers_z, bool keep_z_range = false); void update_double_slider_mode(); // update DoubleSlider after keyDown in canvas void update_double_slider_from_canvas(wxKeyEvent& event); +#endif // ENABLE_GCODE_VIEWER void load_print_as_fff(bool keep_z_range = false); void load_print_as_sla(); +#if ENABLE_GCODE_VIEWER + void on_vert_slider_scroll_changed(wxCommandEvent& event); +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER + void on_horz_slider_scroll_changed(wxCommandEvent& event); +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#else void on_sliders_scroll_changed(wxCommandEvent& event); +#endif // ENABLE_GCODE_VIEWER }; } // namespace GUI diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 8d505f2b65..4243b6ee84 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1641,6 +1641,9 @@ struct Plater::priv bool init_view_toolbar(); #if ENABLE_GCODE_VIEWER void update_preview_bottom_toolbar(); +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER + void update_preview_horz_slider(); +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER #endif // ENABLE_GCODE_VIEWER void reset_all_gizmos(); @@ -2591,8 +2594,10 @@ void Plater::priv::deselect_all() void Plater::priv::remove(size_t obj_idx) { +#if !ENABLE_GCODE_VIEWER // Prevent toolpaths preview from rendering while we modify the Print object preview->set_enabled(false); +#endif // !ENABLE_GCODE_VIEWER if (view3D->is_layers_editing_enabled()) view3D->enable_layers_editing(false); @@ -2622,8 +2627,10 @@ void Plater::priv::reset() set_project_filename(wxEmptyString); +#if !ENABLE_GCODE_VIEWER // Prevent toolpaths preview from rendering while we modify the Print object preview->set_enabled(false); +#endif // !ENABLE_GCODE_VIEWER if (view3D->is_layers_editing_enabled()) view3D->enable_layers_editing(false); @@ -3867,6 +3874,13 @@ void Plater::priv::update_preview_bottom_toolbar() { preview->update_bottom_toolbar(); } + +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER +void Plater::priv::update_preview_horz_slider() +{ + preview->update_horz_slider(); +} +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER #endif // ENABLE_GCODE_VIEWER bool Plater::priv::can_set_instance_to_object() const @@ -5463,6 +5477,13 @@ void Plater::update_preview_bottom_toolbar() { p->update_preview_bottom_toolbar(); } + +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER +void Plater::update_preview_horz_slider() +{ + p->update_preview_horz_slider(); +} +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER #endif // ENABLE_GCODE_VIEWER const Mouse3DController& Plater::get_mouse3d_controller() const diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 7a08c04efe..82c8bbe076 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -320,6 +320,9 @@ public: #if ENABLE_GCODE_VIEWER void update_preview_bottom_toolbar(); +#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER + void update_preview_horz_slider(); +#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER #endif // ENABLE_GCODE_VIEWER const Mouse3DController& get_mouse3d_controller() const; From a68eefbe4abc366dc5c80b315503b05fc321df64 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 15 May 2020 12:25:38 +0200 Subject: [PATCH 075/503] Tech ENABLE_GCODE_VIEWER -> Refactoring and code cleanup --- src/libslic3r/Technologies.hpp | 1 - src/slic3r/GUI/DoubleSlider.cpp | 32 +++--- src/slic3r/GUI/DoubleSlider.hpp | 8 +- src/slic3r/GUI/GCodeViewer.cpp | 94 +---------------- src/slic3r/GUI/GCodeViewer.hpp | 52 ---------- src/slic3r/GUI/GLCanvas3D.cpp | 8 ++ src/slic3r/GUI/GLCanvas3D.hpp | 6 +- src/slic3r/GUI/GUI_Preview.cpp | 175 ++++++++++++++++---------------- src/slic3r/GUI/GUI_Preview.hpp | 39 +++---- src/slic3r/GUI/Plater.cpp | 21 ++-- src/slic3r/GUI/Plater.hpp | 4 +- 11 files changed, 152 insertions(+), 288 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 8ce01a3530..c22e504dff 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -55,7 +55,6 @@ // Enable G-Code viewer #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) -#define ENABLE_GCODE_USE_WXWIDGETS_SLIDER (1 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 021d73882e..597b9ad607 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -20,9 +20,9 @@ #include #include #include -#if !ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#if !ENABLE_GCODE_VIEWER #include -#endif // !ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#endif // !ENABLE_GCODE_VIEWER #include #include @@ -537,7 +537,7 @@ wxString Control::get_label(int tick) const if (value >= m_values.size()) return "ErrVal"; -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#if ENABLE_GCODE_VIEWER if (m_draw_mode == dmSequentialGCodeView) return wxString::Format("%d", static_cast(m_values[value])); else { @@ -551,7 +551,7 @@ wxString Control::get_label(int tick) const wxNumberFormatter::ToString(m_label_koef * value, 2, wxNumberFormatter::Style_None) : wxNumberFormatter::ToString(m_values[value], 2, wxNumberFormatter::Style_None); return format_wxstr("%1%\n(%2%)", str, m_values.empty() ? value : value + 1); -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#endif // ENABLE_GCODE_VIEWER } void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_side/*=true*/) const @@ -792,10 +792,10 @@ void Control::draw_colored_band(wxDC& dc) void Control::draw_one_layer_icon(wxDC& dc) { -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#if ENABLE_GCODE_VIEWER if (m_draw_mode == dmSequentialGCodeView) return; -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#endif // ENABLE_GCODE_VIEWER const wxBitmap& icon = m_is_one_layer ? m_focus == fiOneLayerIcon ? m_bmp_one_layer_lock_off.bmp() : m_bmp_one_layer_lock_on.bmp() : @@ -1302,11 +1302,11 @@ void Control::OnWheel(wxMouseEvent& event) ssLower : ssHigher; } -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#if ENABLE_GCODE_VIEWER move_current_thumb((m_draw_mode == dmSequentialGCodeView) ? event.GetWheelRotation() < 0 : event.GetWheelRotation() > 0); #else move_current_thumb(event.GetWheelRotation() > 0); -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#endif // ENABLE_GCODE_VIEWER } void Control::OnKeyDown(wxKeyEvent &event) @@ -1328,34 +1328,34 @@ void Control::OnKeyDown(wxKeyEvent &event) UseDefaultColors(false); else if (is_horizontal()) { -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#if ENABLE_GCODE_VIEWER if (m_is_focused) { -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#endif // ENABLE_GCODE_VIEWER if (key == WXK_LEFT || key == WXK_RIGHT) move_current_thumb(key == WXK_LEFT); else if (key == WXK_UP || key == WXK_DOWN) { m_selection = key == WXK_UP ? ssHigher : ssLower; Refresh(); } -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#if ENABLE_GCODE_VIEWER } -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#endif // ENABLE_GCODE_VIEWER } else { -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#if ENABLE_GCODE_VIEWER if (m_is_focused) { -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#endif // ENABLE_GCODE_VIEWER if (key == WXK_LEFT || key == WXK_RIGHT) { m_selection = key == WXK_LEFT ? ssHigher : ssLower; Refresh(); } else if (key == WXK_UP || key == WXK_DOWN) move_current_thumb(key == WXK_UP); -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#if ENABLE_GCODE_VIEWER } -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#endif // ENABLE_GCODE_VIEWER } event.Skip(); // !Needed to have EVT_CHAR generated as well diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index a17fb2b6fa..fea1ba172e 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -73,9 +73,9 @@ enum DrawMode dmRegular, dmSlaPrint, dmSequentialFffPrint, -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#if ENABLE_GCODE_VIEWER dmSequentialGCodeView, -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#endif // ENABLE_GCODE_VIEWER }; using t_mode = CustomGCode::Mode; @@ -214,9 +214,9 @@ public: void SetTicksValues(const Slic3r::CustomGCode::Info &custom_gcode_per_print_z); void SetDrawMode(bool is_sla_print, bool is_sequential_print); -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#if ENABLE_GCODE_VIEWER void SetDrawMode(DrawMode mode) { m_draw_mode = mode; } -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#endif // ENABLE_GCODE_VIEWER void SetManipulationMode(t_mode mode) { m_mode = mode; } t_mode GetManipulationMode() const { return m_mode; } diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index acf1da4c71..c4bc391d18 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -324,9 +324,6 @@ void GCodeViewer::render() const m_sequential_view.marker.render(); render_shells(); render_legend(); -#if !ENABLE_GCODE_USE_WXWIDGETS_SLIDER - render_sequential_bar(); -#endif // !ENABLE_GCODE_USE_WXWIDGETS_SLIDER #if ENABLE_GCODE_VIEWER_STATISTICS render_statistics(); #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -383,15 +380,13 @@ void GCodeViewer::set_options_visibility_from_flags(unsigned int flags) enable_legend(is_flag_set(9)); } -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER void GCodeViewer::set_layers_z_range(const std::array& layers_z_range) { bool keep_sequential_current = layers_z_range[1] <= m_layers_z_range[1]; m_layers_z_range = layers_z_range; refresh_render_paths(keep_sequential_current); - wxGetApp().plater()->update_preview_horz_slider(); + wxGetApp().plater()->update_preview_moves_slider(); } -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER bool GCodeViewer::init_shaders() { @@ -1159,93 +1154,6 @@ void GCodeViewer::render_legend() const ImGui::PopStyleVar(); } -#if !ENABLE_GCODE_USE_WXWIDGETS_SLIDER -void GCodeViewer::render_sequential_bar() const -{ - static const float MARGIN = 125.0f; - static const float BUTTON_W = 50.0f; - - auto apply_button_action = [this](unsigned int value) { - m_sequential_view.current = std::clamp(value, m_sequential_view.first, m_sequential_view.last); - refresh_render_paths(true); - }; - - if (m_sequential_view.last <= m_sequential_view.first) - return; - - ImGuiWrapper& imgui = *wxGetApp().imgui(); - const ImGuiStyle& style = ImGui::GetStyle(); - - Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size(); - - float left = wxGetApp().plater()->get_view_toolbar().get_width(); - float width = static_cast(cnv_size.get_width()) - left; - - ImGui::SetNextWindowBgAlpha(0.5f); - imgui.set_next_window_pos(left, static_cast(cnv_size.get_height()), ImGuiCond_Always, 0.0f, 1.0f); - ImGui::SetNextWindowSize({ width, -1.0f }, ImGuiCond_Always); - ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - imgui.begin(std::string("Sequential"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); - - ImGui::SetCursorPosX(MARGIN); - imgui.disabled_begin(m_sequential_view.first == m_sequential_view.current); - if (ImGui::Button("< 1", { BUTTON_W, 0.0f })) - apply_button_action(m_sequential_view.current - 1); - imgui.disabled_end(); - - ImGui::SameLine(); - imgui.disabled_begin(m_sequential_view.current - m_sequential_view.first < 10); - if (ImGui::Button("< 10", { BUTTON_W, 0.0f })) - apply_button_action(m_sequential_view.current - 10); - imgui.disabled_end(); - - ImGui::SameLine(); - imgui.disabled_begin(m_sequential_view.current - m_sequential_view.first < 100); - if (ImGui::Button("< 100", { BUTTON_W, 0.0f })) - apply_button_action(m_sequential_view.current - 100); - imgui.disabled_end(); - - ImGui::SameLine(width - MARGIN - 3 * BUTTON_W - 2 * style.ItemSpacing.x - style.WindowPadding.x); - imgui.disabled_begin(m_sequential_view.last - m_sequential_view.current < 100); - if (ImGui::Button("> 100", { BUTTON_W, 0.0f })) - apply_button_action(m_sequential_view.current + 100); - imgui.disabled_end(); - - ImGui::SameLine(); - imgui.disabled_begin(m_sequential_view.last - m_sequential_view.current < 10); - if (ImGui::Button("> 10", { BUTTON_W, 0.0f })) - apply_button_action(m_sequential_view.current + 10); - imgui.disabled_end(); - - ImGui::SameLine(); - imgui.disabled_begin(m_sequential_view.last == m_sequential_view.current); - if (ImGui::Button("> 1", { BUTTON_W, 0.0f })) - apply_button_action(m_sequential_view.current + 1); - imgui.disabled_end(); - - int index = 1 + static_cast(m_sequential_view.current); - int i_min = 1 + static_cast(m_sequential_view.first); - int i_max = 1 + static_cast(m_sequential_view.last); - - std::string low_str = std::to_string(i_min); - ImGui::SetCursorPosX(MARGIN - style.ItemSpacing.x - ImGui::CalcTextSize(low_str.c_str()).x); - ImGui::AlignTextToFramePadding(); - imgui.text(low_str); - ImGui::SameLine(MARGIN); - ImGui::PushItemWidth(-MARGIN); - if (ImGui::SliderInt("##slider int", &index, i_min, i_max)) { - m_sequential_view.current = static_cast(index - 1); - refresh_render_paths(true); - } - ImGui::PopItemWidth(); - ImGui::SameLine(); - imgui.text(std::to_string(i_max)); - - imgui.end(); - ImGui::PopStyleVar(); -} -#endif // !ENABLE_GCODE_USE_WXWIDGETS_SLIDER - #if ENABLE_GCODE_VIEWER_STATISTICS void GCodeViewer::render_statistics() const { diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index a4ac3358d2..e53bcb0c62 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -149,42 +149,6 @@ class GCodeViewer void reset_ranges() { ranges.reset(); } }; -#if !ENABLE_GCODE_USE_WXWIDGETS_SLIDER - struct SequentialView - { - class Marker - { - GL_Model m_model; - Transform3f m_world_transform; - std::array m_color{ 1.0f, 1.0f, 1.0f, 1.0f }; - bool m_visible{ false }; - Shader m_shader; - - public: - void init(); - - const BoundingBoxf3& get_bounding_box() const { return m_model.get_bounding_box(); } - - void set_world_transform(const Transform3f& transform) { m_world_transform = transform; } - void set_color(const std::array& color) { m_color = color; } - - bool is_visible() const { return m_visible; } - void set_visible(bool visible) { m_visible = visible; } - - void render() const; - - private: - void init_shader(); - }; - - unsigned int first{ 0 }; - unsigned int last{ 0 }; - unsigned int current{ 0 }; - Vec3f current_position{ Vec3f::Zero() }; - Marker marker; - }; -#endif // !ENABLE_GCODE_USE_WXWIDGETS_SLIDER - #if ENABLE_GCODE_VIEWER_STATISTICS struct Statistics { @@ -231,7 +195,6 @@ class GCodeViewer #endif // ENABLE_GCODE_VIEWER_STATISTICS public: -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER struct SequentialView { class Marker @@ -265,7 +228,6 @@ public: Vec3f current_position{ Vec3f::Zero() }; Marker marker; }; -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER enum class EViewType : unsigned char { @@ -322,10 +284,8 @@ public: const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; } const std::vector& get_layers_zs() const { return m_layers_zs; }; -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER const SequentialView& get_sequential_view() const { return m_sequential_view; } void update_sequential_view_current(unsigned int low, unsigned int high) { m_sequential_view.current = high; refresh_render_paths(true); } -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER EViewType get_view_type() const { return m_view_type; } void set_view_type(EViewType type) { @@ -341,16 +301,7 @@ public: void set_toolpath_role_visibility_flags(unsigned int flags) { m_extrusions.role_visibility_flags = flags; } unsigned int get_options_visibility_flags() const; void set_options_visibility_from_flags(unsigned int flags); -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER void set_layers_z_range(const std::array& layers_z_range); -#else - void set_layers_z_range(const std::array& layers_z_range) - { - bool keep_sequential_current = layers_z_range[1] <= m_layers_z_range[1]; - m_layers_z_range = layers_z_range; - refresh_render_paths(keep_sequential_current); - } -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER bool is_legend_enabled() const { return m_legend_enabled; } void enable_legend(bool enable) { m_legend_enabled = enable; } @@ -363,9 +314,6 @@ private: void render_toolpaths() const; void render_shells() const; void render_legend() const; -#if !ENABLE_GCODE_USE_WXWIDGETS_SLIDER - void render_sequential_bar() const; -#endif // !ENABLE_GCODE_USE_WXWIDGETS_SLIDER #if ENABLE_GCODE_VIEWER_STATISTICS void render_statistics() const; #endif // ENABLE_GCODE_VIEWER_STATISTICS diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 073a9cd35e..7629bc969b 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1523,7 +1523,11 @@ wxDEFINE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent); +#if ENABLE_GCODE_VIEWER +wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_LAYERS_SLIDER, wxKeyEvent); +#else wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent); +#endif // ENABLE_GCODE_VIEWER wxDEFINE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent); wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); @@ -3405,7 +3409,11 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) keyCode == WXK_DOWN) { if (dynamic_cast(m_canvas->GetParent()) != nullptr) +#if ENABLE_GCODE_VIEWER + post_event(wxKeyEvent(EVT_GLCANVAS_MOVE_LAYERS_SLIDER, evt)); +#else post_event(wxKeyEvent(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, evt)); +#endif // ENABLE_GCODE_VIEWER } } } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index d4df60ad2a..43d37607c5 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -107,7 +107,11 @@ wxDECLARE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent); +#if ENABLE_GCODE_VIEWER +wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_LAYERS_SLIDER, wxKeyEvent); +#else wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent); +#endif // ENABLE_GCODE_VIEWER wxDECLARE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent); wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); @@ -553,10 +557,8 @@ public: #if ENABLE_GCODE_VIEWER void reset_gcode_toolpaths() { m_gcode_viewer.reset(); } -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER const GCodeViewer::SequentialView& get_gcode_sequential_view() const { return m_gcode_viewer.get_sequential_view(); } void update_gcode_sequential_view_current(unsigned int low, unsigned int high) { m_gcode_viewer.update_sequential_view_current(low, high); } -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER #endif // ENABLE_GCODE_VIEWER void toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 9547e0bf89..714ed3c9ef 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -183,9 +183,11 @@ Preview::Preview( #endif // ENABLE_GCODE_VIEWER : m_canvas_widget(nullptr) , m_canvas(nullptr) - , m_double_slider_sizer(nullptr) #if ENABLE_GCODE_VIEWER , m_bottom_toolbar_sizer(nullptr) + , m_layers_slider_sizer(nullptr) +#else + , m_double_slider_sizer(nullptr) #endif // ENABLE_GCODE_VIEWER , m_label_view_type(nullptr) , m_choice_view_type(nullptr) @@ -248,7 +250,7 @@ bool Preview::init(wxWindow* parent, Model* model) m_canvas->enable_collapse_toolbar(true); #if ENABLE_GCODE_VIEWER - m_double_slider_sizer = create_vert_slider_sizer(); + m_layers_slider_sizer = create_layers_slider_sizer(); #else m_double_slider_sizer = new wxBoxSizer(wxHORIZONTAL); create_double_slider(); @@ -319,15 +321,17 @@ bool Preview::init(wxWindow* parent, Model* model) wxBoxSizer* top_sizer = new wxBoxSizer(wxHORIZONTAL); top_sizer->Add(m_canvas_widget, 1, wxALL | wxEXPAND, 0); +#if ENABLE_GCODE_VIEWER + top_sizer->Add(m_layers_slider_sizer, 0, wxEXPAND, 0); +#else top_sizer->Add(m_double_slider_sizer, 0, wxEXPAND, 0); - -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER - m_horz_slider = new DoubleSlider::Control(this, wxID_ANY, 0, 0, 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL); - m_horz_slider->SetDrawMode(DoubleSlider::dmSequentialGCodeView); - m_horz_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_horz_slider_scroll_changed, this); -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#endif // ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER + m_moves_slider = new DoubleSlider::Control(this, wxID_ANY, 0, 0, 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL); + m_moves_slider->SetDrawMode(DoubleSlider::dmSequentialGCodeView); + m_moves_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_moves_slider_scroll_changed, this); + m_bottom_toolbar_sizer = new wxBoxSizer(wxHORIZONTAL); m_bottom_toolbar_sizer->AddSpacer(10); m_bottom_toolbar_sizer->Add(m_label_view_type, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5); @@ -336,10 +340,8 @@ bool Preview::init(wxWindow* parent, Model* model) m_bottom_toolbar_sizer->Add(m_label_show, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, 5); m_bottom_toolbar_sizer->Add(m_combochecklist_options, 0, wxALIGN_CENTER_VERTICAL, 5); m_bottom_toolbar_sizer->Add(m_combochecklist_features, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 5); -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER m_bottom_toolbar_sizer->AddSpacer(10); - m_bottom_toolbar_sizer->Add(m_horz_slider, 1, wxALL | wxEXPAND, 5); -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER + m_bottom_toolbar_sizer->Add(m_moves_slider, 1, wxALL | wxEXPAND, 5); #else wxBoxSizer* bottom_sizer = new wxBoxSizer(wxHORIZONTAL); bottom_sizer->Add(m_label_view_type, 0, wxALIGN_CENTER_VERTICAL, 5); @@ -517,10 +519,8 @@ void Preview::msw_rescale() { // rescale slider #if ENABLE_GCODE_VIEWER - if (m_vert_slider != nullptr) m_vert_slider->msw_rescale(); -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER - if (m_horz_slider != nullptr) m_horz_slider->msw_rescale(); -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER + if (m_layers_slider != nullptr) m_layers_slider->msw_rescale(); + if (m_moves_slider != nullptr) m_moves_slider->msw_rescale(); #else if (m_slider) m_slider->msw_rescale(); #endif // ENABLE_GCODE_VIEWER @@ -532,25 +532,31 @@ void Preview::msw_rescale() refresh_print(); } +#if ENABLE_GCODE_VIEWER +void Preview::move_layers_slider(wxKeyEvent& evt) +{ + if (m_layers_slider != nullptr) m_layers_slider->OnKeyDown(evt); +} +#else void Preview::move_double_slider(wxKeyEvent& evt) { -#if ENABLE_GCODE_VIEWER - if (m_vert_slider != nullptr) m_vert_slider->OnKeyDown(evt); -#else if (m_slider) m_slider->OnKeyDown(evt); -#endif // ENABLE_GCODE_VIEWER } +#endif // ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER +void Preview::edit_layers_slider(wxKeyEvent& evt) +{ + if (m_layers_slider != nullptr) m_layers_slider->OnChar(evt); +} +#else void Preview::edit_double_slider(wxKeyEvent& evt) { -#if ENABLE_GCODE_VIEWER - if (m_vert_slider != nullptr) m_vert_slider->OnChar(evt); -#else if (m_slider) m_slider->OnChar(evt); -#endif // ENABLE_GCODE_VIEWER } +#endif // ENABLE_GCODE_VIEWER void Preview::bind_event_handlers() { @@ -614,9 +620,9 @@ void Preview::show_hide_ui_elements(const std::string& what) #endif // !ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER -void Preview::hide_vert_slider() +void Preview::hide_layers_slider() { - m_double_slider_sizer->Hide((size_t)0); + m_layers_slider_sizer->Hide((size_t)0); Layout(); } #else @@ -756,29 +762,29 @@ void Preview::update_bottom_toolbar() #endif // ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER -wxBoxSizer* Preview::create_vert_slider_sizer() +wxBoxSizer* Preview::create_layers_slider_sizer() { wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); - m_vert_slider = new DoubleSlider::Control(this, wxID_ANY, 0, 0, 0, 100); + m_layers_slider = new DoubleSlider::Control(this, wxID_ANY, 0, 0, 0, 100); - m_vert_slider->SetDrawMode(wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA, + m_layers_slider->SetDrawMode(wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA, wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_bool("complete_objects")); - sizer->Add(m_vert_slider, 0, wxEXPAND, 0); + sizer->Add(m_layers_slider, 0, wxEXPAND, 0); // sizer, m_canvas_widget - m_canvas_widget->Bind(wxEVT_KEY_DOWN, &Preview::update_vert_slider_from_canvas, this); + m_canvas_widget->Bind(wxEVT_KEY_DOWN, &Preview::update_layers_slider_from_canvas, this); m_canvas_widget->Bind(wxEVT_KEY_UP, [this](wxKeyEvent& event) { if (event.GetKeyCode() == WXK_SHIFT) - m_vert_slider->UseDefaultColors(true); + m_layers_slider->UseDefaultColors(true); event.Skip(); }); - m_vert_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_vert_slider_scroll_changed, this); + m_layers_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_layers_slider_scroll_changed, this); Bind(DoubleSlider::wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) { Model& model = wxGetApp().plater()->model(); - model.custom_gcode_per_print_z = m_vert_slider->GetTicksValues(); + model.custom_gcode_per_print_z = m_layers_slider->GetTicksValues(); m_schedule_background_process(); update_view_type(false); @@ -850,8 +856,7 @@ static int find_close_layer_idx(const std::vector& zs, double &z, double } #if ENABLE_GCODE_VIEWER -void Preview::check_vert_slider_values(std::vector& ticks_from_model, - const std::vector& layers_z) +void Preview::check_layers_slider_values(std::vector& ticks_from_model, const std::vector& layers_z) #else void Preview::check_slider_values(std::vector& ticks_from_model, const std::vector& layers_z) @@ -873,34 +878,34 @@ void Preview::check_slider_values(std::vector& ticks_from_mod } #if ENABLE_GCODE_VIEWER -void Preview::update_vert_slider(const std::vector& layers_z, bool keep_z_range) +void Preview::update_layers_slider(const std::vector& layers_z, bool keep_z_range) { // Save the initial slider span. - double z_low = m_vert_slider->GetLowerValueD(); - double z_high = m_vert_slider->GetHigherValueD(); - bool was_empty = m_vert_slider->GetMaxValue() == 0; + double z_low = m_layers_slider->GetLowerValueD(); + double z_high = m_layers_slider->GetHigherValueD(); + bool was_empty = m_layers_slider->GetMaxValue() == 0; bool force_sliders_full_range = was_empty; if (!keep_z_range) { - bool span_changed = layers_z.empty() || std::abs(layers_z.back() - m_vert_slider->GetMaxValueD()) > DoubleSlider::epsilon()/*1e-6*/; + bool span_changed = layers_z.empty() || std::abs(layers_z.back() - m_layers_slider->GetMaxValueD()) > DoubleSlider::epsilon()/*1e-6*/; force_sliders_full_range |= span_changed; -} - bool snap_to_min = force_sliders_full_range || m_vert_slider->is_lower_at_min(); - bool snap_to_max = force_sliders_full_range || m_vert_slider->is_higher_at_max(); + } + bool snap_to_min = force_sliders_full_range || m_layers_slider->is_lower_at_min(); + bool snap_to_max = force_sliders_full_range || m_layers_slider->is_higher_at_max(); // Detect and set manipulation mode for double slider - update_vert_slider_mode(); + update_layers_slider_mode(); CustomGCode::Info& ticks_info_from_model = wxGetApp().plater()->model().custom_gcode_per_print_z; - check_vert_slider_values(ticks_info_from_model.gcodes, layers_z); + check_layers_slider_values(ticks_info_from_model.gcodes, layers_z); - m_vert_slider->SetSliderValues(layers_z); - assert(m_vert_slider->GetMinValue() == 0); - m_vert_slider->SetMaxValue(layers_z.empty() ? 0 : layers_z.size() - 1); + m_layers_slider->SetSliderValues(layers_z); + assert(m_layers_slider->GetMinValue() == 0); + m_layers_slider->SetMaxValue(layers_z.empty() ? 0 : layers_z.size() - 1); int idx_low = 0; - int idx_high = m_vert_slider->GetMaxValue(); + int idx_high = m_layers_slider->GetMaxValue(); if (!layers_z.empty()) { if (!snap_to_min) { int idx_new = find_close_layer_idx(layers_z, z_low, DoubleSlider::epsilon()/*1e-6*/); @@ -913,17 +918,15 @@ void Preview::update_vert_slider(const std::vector& layers_z, bool keep_ idx_high = idx_new; } } - m_vert_slider->SetSelectionSpan(idx_low, idx_high); - - m_vert_slider->SetTicksValues(ticks_info_from_model); + m_layers_slider->SetSelectionSpan(idx_low, idx_high); + m_layers_slider->SetTicksValues(ticks_info_from_model); bool sla_print_technology = wxGetApp().plater()->printer_technology() == ptSLA; bool sequential_print = wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_bool("complete_objects"); - m_vert_slider->SetDrawMode(sla_print_technology, sequential_print); + m_layers_slider->SetDrawMode(sla_print_technology, sequential_print); + m_layers_slider->SetExtruderColors(wxGetApp().plater()->get_extruder_colors_from_plater_config()); - m_vert_slider->SetExtruderColors(wxGetApp().plater()->get_extruder_colors_from_plater_config()); - - m_double_slider_sizer->Show((size_t)0); + m_layers_slider_sizer->Show((size_t)0); Layout(); } #else @@ -980,7 +983,7 @@ void Preview::update_double_slider(const std::vector & layers_z, bool ke #endif // ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER -void Preview::update_vert_slider_mode() +void Preview::update_layers_slider_mode() #else void Preview::update_double_slider_mode() #endif // ENABLE_GCODE_VIEWER @@ -1033,17 +1036,17 @@ void Preview::update_double_slider_mode() } #if ENABLE_GCODE_VIEWER - m_vert_slider->SetModeAndOnlyExtruder(one_extruder_printed_model, only_extruder); + m_layers_slider->SetModeAndOnlyExtruder(one_extruder_printed_model, only_extruder); #else m_slider->SetModeAndOnlyExtruder(one_extruder_printed_model, only_extruder); #endif // ENABLE_GCODE_VIEWER } #if ENABLE_GCODE_VIEWER -void Preview::reset_vert_slider() +void Preview::reset_layers_slider() { - m_vert_slider->SetHigherValue(0); - m_vert_slider->SetLowerValue(0); + m_layers_slider->SetHigherValue(0); + m_layers_slider->SetLowerValue(0); } #else void Preview::reset_double_slider() @@ -1054,7 +1057,7 @@ void Preview::reset_double_slider() #endif // ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER -void Preview::update_vert_slider_from_canvas(wxKeyEvent& event) +void Preview::update_layers_slider_from_canvas(wxKeyEvent& event) { if (event.HasModifiers()) { event.Skip(); @@ -1064,20 +1067,19 @@ void Preview::update_vert_slider_from_canvas(wxKeyEvent& event) const auto key = event.GetKeyCode(); if (key == 'U' || key == 'D') { - const int new_pos = key == 'U' ? m_vert_slider->GetHigherValue() + 1 : m_vert_slider->GetHigherValue() - 1; - m_vert_slider->SetHigherValue(new_pos); - if (event.ShiftDown() || m_vert_slider->is_one_layer()) m_vert_slider->SetLowerValue(m_vert_slider->GetHigherValue()); + const int new_pos = key == 'U' ? m_layers_slider->GetHigherValue() + 1 : m_layers_slider->GetHigherValue() - 1; + m_layers_slider->SetHigherValue(new_pos); + if (event.ShiftDown() || m_layers_slider->is_one_layer()) m_layers_slider->SetLowerValue(m_layers_slider->GetHigherValue()); } else if (key == 'S') - m_vert_slider->ChangeOneLayerLock(); + m_layers_slider->ChangeOneLayerLock(); else if (key == WXK_SHIFT) - m_vert_slider->UseDefaultColors(false); + m_layers_slider->UseDefaultColors(false); else event.Skip(); } -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER -void Preview::update_horz_slider() +void Preview::update_moves_slider() { const GCodeViewer::SequentialView& view = m_canvas->get_gcode_sequential_view(); std::vector values(view.last - view.first + 1); @@ -1087,11 +1089,10 @@ void Preview::update_horz_slider() values[count++] = static_cast(i + 1); } - m_horz_slider->SetSliderValues(values); - m_horz_slider->SetMaxValue(view.last - view.first); - m_horz_slider->SetSelectionSpan(0, view.current); + m_moves_slider->SetSliderValues(values); + m_moves_slider->SetMaxValue(view.last - view.first); + m_moves_slider->SetSelectionSpan(0, view.current); } -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER #else void Preview::update_double_slider_from_canvas(wxKeyEvent & event) { @@ -1149,7 +1150,7 @@ void Preview::load_print_as_fff(bool keep_z_range) if (! has_layers) { #if ENABLE_GCODE_VIEWER - hide_vert_slider(); + hide_layers_slider(); #else reset_sliders(true); m_canvas->reset_legend_texture(); @@ -1246,14 +1247,14 @@ void Preview::load_print_as_fff(bool keep_z_range) if (zs.empty()) { // all layers filtered out #if ENABLE_GCODE_VIEWER - hide_vert_slider(); + hide_layers_slider(); #else reset_sliders(true); #endif // ENABLE_GCODE_VIEWER m_canvas_widget->Refresh(); } else #if ENABLE_GCODE_VIEWER - update_vert_slider(zs, keep_z_range); + update_layers_slider(zs, keep_z_range); #else update_sliders(zs, keep_z_range); #endif // ENABLE_GCODE_VIEWER @@ -1285,7 +1286,7 @@ void Preview::load_print_as_sla() if (n_layers == 0) { #if ENABLE_GCODE_VIEWER - hide_vert_slider(); + hide_layers_slider(); #else reset_sliders(true); #endif // ENABLE_GCODE_VIEWER @@ -1304,7 +1305,7 @@ void Preview::load_print_as_sla() if (n_layers > 0) #if ENABLE_GCODE_VIEWER - update_vert_slider(zs); + update_layers_slider(zs); #else update_sliders(zs); #endif // ENABLE_GCODE_VIEWER @@ -1314,7 +1315,7 @@ void Preview::load_print_as_sla() } #if ENABLE_GCODE_VIEWER -void Preview::on_vert_slider_scroll_changed(wxCommandEvent& event) +void Preview::on_layers_slider_scroll_changed(wxCommandEvent& event) #else void Preview::on_sliders_scroll_changed(wxCommandEvent& event) #endif // ENABLE_GCODE_VIEWER @@ -1325,7 +1326,7 @@ void Preview::on_sliders_scroll_changed(wxCommandEvent& event) if (tech == ptFFF) { #if ENABLE_GCODE_VIEWER - m_canvas->set_toolpaths_z_range({ m_vert_slider->GetLowerValueD(), m_vert_slider->GetHigherValueD() }); + m_canvas->set_toolpaths_z_range({ m_layers_slider->GetLowerValueD(), m_layers_slider->GetHigherValueD() }); m_canvas->set_as_dirty(); #else m_canvas->set_toolpaths_range(m_slider->GetLowerValueD() - 1e-6, m_slider->GetHigherValueD() + 1e-6); @@ -1336,9 +1337,9 @@ void Preview::on_sliders_scroll_changed(wxCommandEvent& event) else if (tech == ptSLA) { #if ENABLE_GCODE_VIEWER - m_canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), -m_vert_slider->GetLowerValueD())); - m_canvas->set_clipping_plane(1, ClippingPlane(-Vec3d::UnitZ(), m_vert_slider->GetHigherValueD())); - m_canvas->set_use_clipping_planes(m_vert_slider->GetHigherValue() != 0); + m_canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), -m_layers_slider->GetLowerValueD())); + m_canvas->set_clipping_plane(1, ClippingPlane(-Vec3d::UnitZ(), m_layers_slider->GetHigherValueD())); + m_canvas->set_use_clipping_planes(m_layers_slider->GetHigherValue() != 0); #else m_canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), -m_slider->GetLowerValueD())); m_canvas->set_clipping_plane(1, ClippingPlane(-Vec3d::UnitZ(), m_slider->GetHigherValueD())); @@ -1349,13 +1350,13 @@ void Preview::on_sliders_scroll_changed(wxCommandEvent& event) } } -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER -void Preview::on_horz_slider_scroll_changed(wxCommandEvent& event) +#if ENABLE_GCODE_VIEWER +void Preview::on_moves_slider_scroll_changed(wxCommandEvent& event) { - m_canvas->update_gcode_sequential_view_current(static_cast(m_horz_slider->GetLowerValueD()), static_cast(m_horz_slider->GetHigherValueD())); + m_canvas->update_gcode_sequential_view_current(static_cast(m_moves_slider->GetLowerValueD()), static_cast(m_moves_slider->GetHigherValueD())); m_canvas->render(); } -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER +#endif // ENABLE_GCODE_VIEWER } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 4df48a153e..3f0d4b4e68 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -82,9 +82,11 @@ class Preview : public wxPanel { wxGLCanvas* m_canvas_widget; GLCanvas3D* m_canvas; - wxBoxSizer* m_double_slider_sizer; #if ENABLE_GCODE_VIEWER + wxBoxSizer* m_layers_slider_sizer; wxBoxSizer* m_bottom_toolbar_sizer; +#else + wxBoxSizer* m_double_slider_sizer; #endif // ENABLE_GCODE_VIEWER wxStaticText* m_label_view_type; wxChoice* m_choice_view_type; @@ -126,10 +128,8 @@ class Preview : public wxPanel #endif // !ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER - DoubleSlider::Control* m_vert_slider{ nullptr }; -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER - DoubleSlider::Control* m_horz_slider{ nullptr }; -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER + DoubleSlider::Control* m_layers_slider{ nullptr }; + DoubleSlider::Control* m_moves_slider{ nullptr }; #else DoubleSlider::Control* m_slider {nullptr}; #endif // ENABLE_GCODE_VIEWER @@ -162,8 +162,13 @@ Preview(wxWindow* parent, Model* model, DynamicPrintConfig* config, void refresh_print(); void msw_rescale(); +#if ENABLE_GCODE_VIEWER + void move_layers_slider(wxKeyEvent& evt); + void edit_layers_slider(wxKeyEvent& evt); +#else void move_double_slider(wxKeyEvent& evt); void edit_double_slider(wxKeyEvent& evt); +#endif // ENABLE_GCODE_VIEWER void update_view_type(bool slice_completed); @@ -171,9 +176,7 @@ Preview(wxWindow* parent, Model* model, DynamicPrintConfig* config, #if ENABLE_GCODE_VIEWER void update_bottom_toolbar(); -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER - void update_horz_slider(); -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER + void update_moves_slider(); #endif // ENABLE_GCODE_VIEWER private: @@ -183,7 +186,7 @@ private: void unbind_event_handlers(); #if ENABLE_GCODE_VIEWER - void hide_vert_slider(); + void hide_layers_slider(); #else void show_hide_ui_elements(const std::string& what); @@ -206,14 +209,14 @@ private: #if ENABLE_GCODE_VIEWER // Create/Update/Reset double slider on 3dPreview - wxBoxSizer* create_vert_slider_sizer(); - void check_vert_slider_values(std::vector& ticks_from_model, + wxBoxSizer* create_layers_slider_sizer(); + void check_layers_slider_values(std::vector& ticks_from_model, const std::vector& layers_z); - void reset_vert_slider(); - void update_vert_slider(const std::vector& layers_z, bool keep_z_range = false); - void update_vert_slider_mode(); + void reset_layers_slider(); + void update_layers_slider(const std::vector& layers_z, bool keep_z_range = false); + void update_layers_slider_mode(); // update vertical DoubleSlider after keyDown in canvas - void update_vert_slider_from_canvas(wxKeyEvent& event); + void update_layers_slider_from_canvas(wxKeyEvent& event); #else // Create/Update/Reset double slider on 3dPreview void create_double_slider(); @@ -230,10 +233,8 @@ private: void load_print_as_sla(); #if ENABLE_GCODE_VIEWER - void on_vert_slider_scroll_changed(wxCommandEvent& event); -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER - void on_horz_slider_scroll_changed(wxCommandEvent& event); -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER + void on_layers_slider_scroll_changed(wxCommandEvent& event); + void on_moves_slider_scroll_changed(wxCommandEvent& event); #else void on_sliders_scroll_changed(wxCommandEvent& event); #endif // ENABLE_GCODE_VIEWER diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index be5b5b3ab4..35455fa9c0 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1642,9 +1642,7 @@ struct Plater::priv bool init_view_toolbar(); #if ENABLE_GCODE_VIEWER void update_preview_bottom_toolbar(); -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER - void update_preview_horz_slider(); -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER + void update_preview_moves_slider(); #endif // ENABLE_GCODE_VIEWER void reset_all_gizmos(); @@ -1975,8 +1973,13 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) config->option("bed_custom_model")->value); }); preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); }); +#if ENABLE_GCODE_VIEWER + preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_MOVE_LAYERS_SLIDER, [this](wxKeyEvent& evt) { preview->move_layers_slider(evt); }); + preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_EDIT_COLOR_CHANGE, [this](wxKeyEvent& evt) { preview->edit_layers_slider(evt); }); +#else preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, [this](wxKeyEvent& evt) { preview->move_double_slider(evt); }); preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_EDIT_COLOR_CHANGE, [this](wxKeyEvent& evt) { preview->edit_double_slider(evt); }); +#endif // ENABLE_GCODE_VIEWER q->Bind(EVT_SLICING_COMPLETED, &priv::on_slicing_completed, this); q->Bind(EVT_PROCESS_COMPLETED, &priv::on_process_completed, this); @@ -3879,12 +3882,10 @@ void Plater::priv::update_preview_bottom_toolbar() preview->update_bottom_toolbar(); } -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER -void Plater::priv::update_preview_horz_slider() +void Plater::priv::update_preview_moves_slider() { - preview->update_horz_slider(); + preview->update_moves_slider(); } -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER #endif // ENABLE_GCODE_VIEWER bool Plater::priv::can_set_instance_to_object() const @@ -5482,12 +5483,10 @@ void Plater::update_preview_bottom_toolbar() p->update_preview_bottom_toolbar(); } -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER -void Plater::update_preview_horz_slider() +void Plater::update_preview_moves_slider() { - p->update_preview_horz_slider(); + p->update_preview_moves_slider(); } -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER #endif // ENABLE_GCODE_VIEWER const Mouse3DController& Plater::get_mouse3d_controller() const diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 82c8bbe076..21eba8ad13 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -320,9 +320,7 @@ public: #if ENABLE_GCODE_VIEWER void update_preview_bottom_toolbar(); -#if ENABLE_GCODE_USE_WXWIDGETS_SLIDER - void update_preview_horz_slider(); -#endif // ENABLE_GCODE_USE_WXWIDGETS_SLIDER + void update_preview_moves_slider(); #endif // ENABLE_GCODE_VIEWER const Mouse3DController& get_mouse3d_controller() const; From 2b536137d2830847b42732b294f37698a4484519 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 15 May 2020 17:57:47 +0200 Subject: [PATCH 076/503] Tech ENABLE_GCODE_VIEWER -> Adapting DoubleSlider::Control for sequential view --- src/slic3r/GUI/DoubleSlider.cpp | 75 ++++++++++++++++++++++++++++++--- src/slic3r/GUI/DoubleSlider.hpp | 4 ++ src/slic3r/GUI/GLCanvas3D.hpp | 2 +- 3 files changed, 73 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 597b9ad607..a6c9a5c772 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -838,8 +838,20 @@ void Control::draw_cog_icon(wxDC& dc) get_size(&width, &height); wxCoord x_draw, y_draw; - is_horizontal() ? x_draw = width-2 : x_draw = width - m_cog_icon_dim - 2; - is_horizontal() ? y_draw = height - m_cog_icon_dim - 2 : y_draw = height-2; +#if ENABLE_GCODE_VIEWER + if (m_draw_mode == dmSequentialGCodeView) + { + is_horizontal() ? x_draw = width - 2 : x_draw = 0.5 * width - 0.5 * m_cog_icon_dim; + is_horizontal() ? y_draw = 0.5 * height - 0.5 * m_cog_icon_dim : y_draw = height - 2; + } + else + { +#endif // ENABLE_GCODE_VIEWER + is_horizontal() ? x_draw = width - 2 : x_draw = width - m_cog_icon_dim - 2; + is_horizontal() ? y_draw = height - m_cog_icon_dim - 2 : y_draw = height - 2; +#if ENABLE_GCODE_VIEWER + } +#endif // ENABLE_GCODE_VIEWER dc.DrawBitmap(m_bmp_cog.bmp(), x_draw, y_draw); @@ -977,10 +989,19 @@ wxString Control::get_tooltip(int tick/*=-1*/) if (m_focus == fiRevertIcon) return _(L("Discard all custom changes")); if (m_focus == fiCogIcon) - return m_mode == t_mode::MultiAsSingle ? - GUI::from_u8((boost::format(_utf8(L("Jump to height %s or " - "Set extruder sequence for the entire print"))) % " (Shift + G)\n").str()) : - _(L("Jump to height")) + " (Shift + G)"; +#if ENABLE_GCODE_VIEWER + { + if (m_draw_mode == dmSequentialGCodeView) + return _L("Jump to move") + " (Shift + G)"; + else +#endif // ENABLE_GCODE_VIEWER + return m_mode == t_mode::MultiAsSingle ? + GUI::from_u8((boost::format(_utf8(L("Jump to height %s or " + "Set extruder sequence for the entire print"))) % " (Shift + G)\n").str()) : + _(L("Jump to height")) + " (Shift + G)"; +#if ENABLE_GCODE_VIEWER + } +#endif // ENABLE_GCODE_VIEWER if (m_focus == fiColorBand) return m_mode != t_mode::SingleExtruder ? "" : _(L("Edit current color - Right click the colored slider segment")); @@ -1230,7 +1251,11 @@ void Control::OnLeftUp(wxMouseEvent& event) if (m_mode == t_mode::MultiAsSingle && m_draw_mode == dmRegular) show_cog_icon_context_menu(); else +#if ENABLE_GCODE_VIEWER + jump_to_value(); +#else jump_to_print_z(); +#endif // ENABLE_GCODE_VIEWER break; case maOneLayerIconClick: switch_one_layer_mode(); @@ -1385,7 +1410,11 @@ void Control::OnChar(wxKeyEvent& event) m_ticks.suppress_minus(false); } if (key == 'G') +#if ENABLE_GCODE_VIEWER + jump_to_value(); +#else jump_to_print_z(); +#endif // ENABLE_GCODE_VIEWER } void Control::OnRightDown(wxMouseEvent& event) @@ -1571,7 +1600,11 @@ void Control::show_cog_icon_context_menu() wxMenu menu; append_menu_item(&menu, wxID_ANY, _(L("Jump to height")) + " (Shift+G)", "", - [this](wxCommandEvent&) { jump_to_print_z(); }, "", &menu); +#if ENABLE_GCODE_VIEWER + [this](wxCommandEvent&) { jump_to_value(); }, "", & menu); +#else + [this](wxCommandEvent&) { jump_to_print_z(); }, "", &menu); +#endif // ENABLE_GCODE_VIEWER append_menu_item(&menu, wxID_ANY, _(L("Set extruder sequence for the entire print")), "", [this](wxCommandEvent&) { edit_extruder_sequence(); }, "", &menu); @@ -1689,11 +1722,21 @@ static std::string get_pause_print_msg(const std::string& msg_in, double height) return into_u8(dlg.GetValue()); } +#if ENABLE_GCODE_VIEWER +static double get_value_to_jump(double active_value, double min_z, double max_z, DrawMode mode) +#else static double get_print_z_to_jump(double active_print_z, double min_z, double max_z) +#endif // ENABLE_GCODE_VIEWER { +#if ENABLE_GCODE_VIEWER + wxString msg_text = (mode == dmSequentialGCodeView) ? _L("Enter the move you want to jump to") + ":" : _L("Enter the height you want to jump to") + ":"; + wxString msg_header = (mode == dmSequentialGCodeView) ? _L("Jump to move") : _L("Jump to height"); + wxString msg_in = GUI::double_to_string(active_value); +#else wxString msg_text = _(L("Enter the height you want to jump to")) + ":"; wxString msg_header = _(L("Jump to height")); wxString msg_in = GUI::double_to_string(active_print_z); +#endif // ENABLE_GCODE_VIEWER // get custom gcode wxTextEntryDialog dlg(nullptr, msg_text, msg_header, msg_in, wxTextEntryDialogStyle); @@ -1902,6 +1945,23 @@ void Control::edit_extruder_sequence() post_ticks_changed_event(ToolChangeCode); } +#if ENABLE_GCODE_VIEWER +void Control::jump_to_value() +{ + double value = get_value_to_jump(m_values[m_selection == ssLower ? m_lower_value : m_higher_value], + m_values[m_min_value], m_values[m_max_value], m_draw_mode); + if (value < 0.0) + return; + + auto it = std::lower_bound(m_values.begin(), m_values.end(), value - epsilon()); + int tick_value = it - m_values.begin(); + + if (m_selection == ssLower) + SetLowerValue(tick_value); + else + SetHigherValue(tick_value); +} +#else void Control::jump_to_print_z() { double print_z = get_print_z_to_jump(m_values[m_selection == ssLower ? m_lower_value : m_higher_value], @@ -1917,6 +1977,7 @@ void Control::jump_to_print_z() else SetHigherValue(tick_value); } +#endif // ENABLE_GCODE_VIEWER void Control::post_ticks_changed_event(const std::string& gcode /*= ""*/) { diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index fea1ba172e..71949f7f3e 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -253,7 +253,11 @@ public: void discard_all_thicks(); void move_current_thumb_to_pos(wxPoint pos); void edit_extruder_sequence(); +#if ENABLE_GCODE_VIEWER + void jump_to_value(); +#else void jump_to_print_z(); +#endif // ENABLE_GCODE_VIEWER void show_add_context_menu(); void show_edit_context_menu(); void show_cog_icon_context_menu(); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 43d37607c5..2c059c648f 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -558,7 +558,7 @@ public: #if ENABLE_GCODE_VIEWER void reset_gcode_toolpaths() { m_gcode_viewer.reset(); } const GCodeViewer::SequentialView& get_gcode_sequential_view() const { return m_gcode_viewer.get_sequential_view(); } - void update_gcode_sequential_view_current(unsigned int low, unsigned int high) { m_gcode_viewer.update_sequential_view_current(low, high); } + void update_gcode_sequential_view_current(unsigned int first, unsigned int last) { m_gcode_viewer.update_sequential_view_current(first, last); } #endif // ENABLE_GCODE_VIEWER void toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1); From 163fbec8c84f6d314bd79a54a4741e050d52b3f3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 18 May 2020 13:24:07 +0200 Subject: [PATCH 077/503] GCodeViewer -> Completed implementation of slider for sequential view --- src/slic3r/GUI/GCodeViewer.cpp | 81 +++++++++++++++++--------------- src/slic3r/GUI/GCodeViewer.hpp | 20 ++++++-- src/slic3r/GUI/GUI_Preview.cpp | 84 ++++++++++++++++++++++++---------- src/slic3r/GUI/GUI_Preview.hpp | 15 ++++++ 4 files changed, 136 insertions(+), 64 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index c4bc391d18..dcb320766a 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -12,6 +12,7 @@ #include "DoubleSlider.hpp" #include "GLCanvas3D.hpp" #include "GLToolbar.hpp" +#include "GUI_Preview.hpp" #include "libslic3r/Model.hpp" #if ENABLE_GCODE_VIEWER_STATISTICS #include @@ -279,7 +280,7 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: } // update buffers' render paths - refresh_render_paths(false); + refresh_render_paths(false, false); #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.refresh_time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start_time).count(); @@ -349,16 +350,16 @@ unsigned int GCodeViewer::get_options_visibility_flags() const }; unsigned int flags = 0; - flags = set_flag(flags, 0, is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Travel)); - flags = set_flag(flags, 1, is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Retract)); - flags = set_flag(flags, 2, is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Unretract)); - flags = set_flag(flags, 3, is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Tool_change)); - flags = set_flag(flags, 4, is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Color_change)); - flags = set_flag(flags, 5, is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Pause_Print)); - flags = set_flag(flags, 6, is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Custom_GCode)); - flags = set_flag(flags, 7, m_shells.visible); - flags = set_flag(flags, 8, m_sequential_view.marker.is_visible()); - flags = set_flag(flags, 9, is_legend_enabled()); + flags = set_flag(flags, static_cast(Preview::OptionType::Travel), is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Travel)); + flags = set_flag(flags, static_cast(Preview::OptionType::Retractions), is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Retract)); + flags = set_flag(flags, static_cast(Preview::OptionType::Unretractions), is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Unretract)); + flags = set_flag(flags, static_cast(Preview::OptionType::ToolChanges), is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Tool_change)); + flags = set_flag(flags, static_cast(Preview::OptionType::ColorChanges), is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Color_change)); + flags = set_flag(flags, static_cast(Preview::OptionType::PausePrints), is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Pause_Print)); + flags = set_flag(flags, static_cast(Preview::OptionType::CustomGCodes), is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Custom_GCode)); + flags = set_flag(flags, static_cast(Preview::OptionType::Shells), m_shells.visible); + flags = set_flag(flags, static_cast(Preview::OptionType::ToolMarker), m_sequential_view.marker.is_visible()); + flags = set_flag(flags, static_cast(Preview::OptionType::Legend), is_legend_enabled()); return flags; } @@ -368,23 +369,24 @@ void GCodeViewer::set_options_visibility_from_flags(unsigned int flags) return (flags & (1 << flag)) != 0; }; - set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Travel, is_flag_set(0)); - set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Retract, is_flag_set(1)); - set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Unretract, is_flag_set(2)); - set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Tool_change, is_flag_set(3)); - set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Color_change, is_flag_set(4)); - set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Pause_Print, is_flag_set(5)); - set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Custom_GCode, is_flag_set(6)); - m_shells.visible = is_flag_set(7); - m_sequential_view.marker.set_visible(is_flag_set(8)); - enable_legend(is_flag_set(9)); + set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Travel, is_flag_set(static_cast(Preview::OptionType::Travel))); + set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Retract, is_flag_set(static_cast(Preview::OptionType::Retractions))); + set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Unretract, is_flag_set(static_cast(Preview::OptionType::Unretractions))); + set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Tool_change, is_flag_set(static_cast(Preview::OptionType::ToolChanges))); + set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Color_change, is_flag_set(static_cast(Preview::OptionType::ColorChanges))); + set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Pause_Print, is_flag_set(static_cast(Preview::OptionType::PausePrints))); + set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Custom_GCode, is_flag_set(static_cast(Preview::OptionType::CustomGCodes))); + m_shells.visible = is_flag_set(static_cast(Preview::OptionType::Shells)); + m_sequential_view.marker.set_visible(is_flag_set(static_cast(Preview::OptionType::ToolMarker))); + enable_legend(is_flag_set(static_cast(Preview::OptionType::Legend))); } void GCodeViewer::set_layers_z_range(const std::array& layers_z_range) { - bool keep_sequential_current = layers_z_range[1] <= m_layers_z_range[1]; + bool keep_sequential_current_first = layers_z_range[0] >= m_layers_z_range[0]; + bool keep_sequential_current_last = layers_z_range[1] <= m_layers_z_range[1]; m_layers_z_range = layers_z_range; - refresh_render_paths(keep_sequential_current); + refresh_render_paths(keep_sequential_current_first, keep_sequential_current_last); wxGetApp().plater()->update_preview_moves_slider(); } @@ -628,7 +630,7 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) } } -void GCodeViewer::refresh_render_paths(bool keep_sequential_current) const +void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const { #if ENABLE_GCODE_VIEWER_STATISTICS auto start_time = std::chrono::high_resolution_clock::now(); @@ -661,10 +663,12 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current) const m_statistics.render_paths_size = 0; #endif // ENABLE_GCODE_VIEWER_STATISTICS - m_sequential_view.first = m_vertices.vertices_count; - m_sequential_view.last = 0; - if (!keep_sequential_current) - m_sequential_view.current = m_vertices.vertices_count; + m_sequential_view.endpoints.first = m_vertices.vertices_count; + m_sequential_view.endpoints.last = 0; + if (!keep_sequential_current_first) + m_sequential_view.current.first = 0; + if (!keep_sequential_current_last) + m_sequential_view.current.last = m_vertices.vertices_count; // first pass: collect visible paths and update sequential view data std::vector> paths; @@ -690,22 +694,23 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current) const // store valid path paths.push_back({ &buffer, i }); - m_sequential_view.first = std::min(m_sequential_view.first, path.first.s_id); - m_sequential_view.last = std::max(m_sequential_view.last, path.last.s_id); + m_sequential_view.endpoints.first = std::min(m_sequential_view.endpoints.first, path.first.s_id); + m_sequential_view.endpoints.last = std::max(m_sequential_view.endpoints.last, path.last.s_id); } } // update current sequential position - m_sequential_view.current = keep_sequential_current ? std::clamp(m_sequential_view.current, m_sequential_view.first, m_sequential_view.last) : m_sequential_view.last; + m_sequential_view.current.first = keep_sequential_current_first ? std::clamp(m_sequential_view.current.first, m_sequential_view.endpoints.first, m_sequential_view.endpoints.last) : m_sequential_view.endpoints.first; + m_sequential_view.current.last = keep_sequential_current_last ? std::clamp(m_sequential_view.current.last, m_sequential_view.endpoints.first, m_sequential_view.endpoints.last) : m_sequential_view.endpoints.last; glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vertices.vbo_id)); size_t v_size = VBuffer::vertex_size_bytes(); - glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, static_cast(m_sequential_view.current * v_size), static_cast(v_size), static_cast(m_sequential_view.current_position.data()))); + glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, static_cast(m_sequential_view.current.last * v_size), static_cast(v_size), static_cast(m_sequential_view.current_position.data()))); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); // second pass: filter paths by sequential data for (auto&& [buffer, id] : paths) { const Path& path = buffer->paths[id]; - if ((m_sequential_view.current < path.first.s_id) || (path.last.s_id < m_sequential_view.first)) + if ((m_sequential_view.current.last <= path.first.s_id) || (path.last.s_id <= m_sequential_view.current.first)) continue; Color color; @@ -722,8 +727,12 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current) const it->color = color; } - it->sizes.push_back(std::min(m_sequential_view.current, path.last.s_id) - path.first.s_id + 1); - it->offsets.push_back(static_cast(path.first.i_id * sizeof(unsigned int))); + it->sizes.push_back(std::min(m_sequential_view.current.last, path.last.s_id) - std::max(m_sequential_view.current.first, path.first.s_id) + 1); + unsigned int delta_1st = 0; + if ((path.first.s_id < m_sequential_view.current.first) && (m_sequential_view.current.first <= path.last.s_id)) + delta_1st = m_sequential_view.current.first - path.first.s_id; + + it->offsets.push_back(static_cast((path.first.i_id + delta_1st) * sizeof(unsigned int))); } #if ENABLE_GCODE_VIEWER_STATISTICS @@ -1013,7 +1022,7 @@ void GCodeViewer::render_legend() const { m_extrusions.role_visibility_flags = is_visible(role) ? m_extrusions.role_visibility_flags & ~(1 << role) : m_extrusions.role_visibility_flags | (1 << role); // update buffers' render paths - refresh_render_paths(false); + refresh_render_paths(false, false); wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); wxGetApp().plater()->update_preview_bottom_toolbar(); } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index e53bcb0c62..688f1266b5 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -222,9 +222,14 @@ public: void init_shader(); }; - unsigned int first{ 0 }; - unsigned int last{ 0 }; - unsigned int current{ 0 }; + struct Endpoints + { + unsigned int first{ 0 }; + unsigned int last{ 0 }; + }; + + Endpoints endpoints; + Endpoints current; Vec3f current_position{ Vec3f::Zero() }; Marker marker; }; @@ -285,7 +290,12 @@ public: const std::vector& get_layers_zs() const { return m_layers_zs; }; const SequentialView& get_sequential_view() const { return m_sequential_view; } - void update_sequential_view_current(unsigned int low, unsigned int high) { m_sequential_view.current = high; refresh_render_paths(true); } + void update_sequential_view_current(unsigned int first, unsigned int last) + { + m_sequential_view.current.first = first; + m_sequential_view.current.last = last; + refresh_render_paths(true, true); + } EViewType get_view_type() const { return m_view_type; } void set_view_type(EViewType type) { @@ -310,7 +320,7 @@ private: bool init_shaders(); void load_toolpaths(const GCodeProcessor::Result& gcode_result); void load_shells(const Print& print, bool initialized); - void refresh_render_paths(bool keep_sequential_current) const; + void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const; void render_toolpaths() const; void render_shells() const; void render_legend() const; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 714ed3c9ef..65e1e50589 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -298,17 +298,17 @@ bool Preview::init(wxWindow* parent, Model* model) m_combochecklist_options = new wxComboCtrl(); m_combochecklist_options->Create(this, wxID_ANY, _L("Options"), wxDefaultPosition, wxDefaultSize, wxCB_READONLY); std::string options_items = GUI::into_u8( - _L("Travel") + "|0|" + - _L("Retractions") + "|0|" + - _L("Unretractions") + "|0|" + - _L("Tool changes") + "|0|" + - _L("Color changes") + "|0|" + - _L("Pause prints") + "|0|" + - _L("Custom GCodes") + "|0|" + - _L("Shells") + "|0|" + - _L("Tool marker") + "|1|" + - _L("Legend") + "|1" -); + get_option_type_string(OptionType::Travel) + "|0|" + + get_option_type_string(OptionType::Retractions) + "|0|" + + get_option_type_string(OptionType::Unretractions) + "|0|" + + get_option_type_string(OptionType::ToolChanges) + "|0|" + + get_option_type_string(OptionType::ColorChanges) + "|0|" + + get_option_type_string(OptionType::PausePrints) + "|0|" + + get_option_type_string(OptionType::CustomGCodes) + "|0|" + + get_option_type_string(OptionType::Shells) + "|0|" + + get_option_type_string(OptionType::ToolMarker) + "|0|" + + get_option_type_string(OptionType::Legend) + "|1" + ); Slic3r::GUI::create_combochecklist(m_combochecklist_options, GUI::into_u8(_L("Options")), options_items); #else m_checkbox_travel = new wxCheckBox(this, wxID_ANY, _(L("Travel"))); @@ -328,19 +328,19 @@ bool Preview::init(wxWindow* parent, Model* model) #endif // ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER - m_moves_slider = new DoubleSlider::Control(this, wxID_ANY, 0, 0, 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL); + m_moves_slider = new DoubleSlider::Control(this, wxID_ANY, 0, 0, 0, 100, wxDefaultPosition, wxSize(-1, 3 * GetTextExtent("m").y), wxSL_HORIZONTAL); m_moves_slider->SetDrawMode(DoubleSlider::dmSequentialGCodeView); m_moves_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_moves_slider_scroll_changed, this); m_bottom_toolbar_sizer = new wxBoxSizer(wxHORIZONTAL); - m_bottom_toolbar_sizer->AddSpacer(10); + m_bottom_toolbar_sizer->AddSpacer(5); m_bottom_toolbar_sizer->Add(m_label_view_type, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5); - m_bottom_toolbar_sizer->Add(m_choice_view_type, 0, wxALIGN_CENTER_VERTICAL, 5); - m_bottom_toolbar_sizer->AddSpacer(10); + m_bottom_toolbar_sizer->Add(m_choice_view_type, 0, wxALIGN_CENTER_VERTICAL, 0); + m_bottom_toolbar_sizer->AddSpacer(5); m_bottom_toolbar_sizer->Add(m_label_show, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, 5); - m_bottom_toolbar_sizer->Add(m_combochecklist_options, 0, wxALIGN_CENTER_VERTICAL, 5); + m_bottom_toolbar_sizer->Add(m_combochecklist_options, 0, wxALIGN_CENTER_VERTICAL, 0); m_bottom_toolbar_sizer->Add(m_combochecklist_features, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 5); - m_bottom_toolbar_sizer->AddSpacer(10); + m_bottom_toolbar_sizer->AddSpacer(5); m_bottom_toolbar_sizer->Add(m_moves_slider, 1, wxALL | wxEXPAND, 5); #else wxBoxSizer* bottom_sizer = new wxBoxSizer(wxHORIZONTAL); @@ -686,8 +686,27 @@ void Preview::on_combochecklist_features(wxCommandEvent& evt) #if ENABLE_GCODE_VIEWER void Preview::on_combochecklist_options(wxCommandEvent& evt) { - m_canvas->set_gcode_options_visibility_from_flags(Slic3r::GUI::combochecklist_get_flags(m_combochecklist_options)); - refresh_print(); + auto xor = [](unsigned int flags1, unsigned int flags2, unsigned int flag) { + auto is_flag_set = [](unsigned int flags, unsigned int flag) { + return (flags & (1 << flag)) != 0; + }; + return !is_flag_set(flags1, flag) != !is_flag_set(flags2, flag); + }; + + unsigned int curr_flags = m_canvas->get_gcode_options_visibility_flags(); + unsigned int new_flags = Slic3r::GUI::combochecklist_get_flags(m_combochecklist_options); + if (curr_flags == new_flags) + return; + + m_canvas->set_gcode_options_visibility_from_flags(new_flags); + + bool skip_refresh = xor(curr_flags, new_flags, static_cast(OptionType::Shells)) || + xor(curr_flags, new_flags, static_cast(OptionType::ToolMarker)); + + if (!skip_refresh) + refresh_print(); + else + m_canvas->set_as_dirty(); } #else void Preview::on_checkbox_travel(wxCommandEvent& evt) @@ -1082,16 +1101,16 @@ void Preview::update_layers_slider_from_canvas(wxKeyEvent& event) void Preview::update_moves_slider() { const GCodeViewer::SequentialView& view = m_canvas->get_gcode_sequential_view(); - std::vector values(view.last - view.first + 1); + std::vector values(view.endpoints.last - view.endpoints.first + 1); unsigned int count = 0; - for (unsigned int i = view.first; i <= view.last; ++i) + for (unsigned int i = view.endpoints.first; i <= view.endpoints.last; ++i) { values[count++] = static_cast(i + 1); } m_moves_slider->SetSliderValues(values); - m_moves_slider->SetMaxValue(view.last - view.first); - m_moves_slider->SetSelectionSpan(0, view.current); + m_moves_slider->SetMaxValue(view.endpoints.last - view.endpoints.first); + m_moves_slider->SetSelectionSpan(view.current.first - view.endpoints.first, view.current.last - view.endpoints.first); } #else void Preview::update_double_slider_from_canvas(wxKeyEvent & event) @@ -1356,6 +1375,25 @@ void Preview::on_moves_slider_scroll_changed(wxCommandEvent& event) m_canvas->update_gcode_sequential_view_current(static_cast(m_moves_slider->GetLowerValueD()), static_cast(m_moves_slider->GetHigherValueD())); m_canvas->render(); } + +wxString Preview::get_option_type_string(OptionType type) const +{ + switch (type) + { + case OptionType::Travel: { return _L("Travel"); } + case OptionType::Retractions: { return _L("Retractions"); } + case OptionType::Unretractions: { return _L("Unretractions"); } + case OptionType::ToolChanges: { return _L("Tool changes"); } + case OptionType::ColorChanges: { return _L("Color changes"); } + case OptionType::PausePrints: { return _L("Pause prints"); } + case OptionType::CustomGCodes: { return _L("Custom GCodes"); } + case OptionType::Shells: { return _L("Shells"); } + case OptionType::ToolMarker: { return _L("Tool marker"); } + case OptionType::Legend: { return _L("Legend"); } + default: { return ""; } + } +} + #endif // ENABLE_GCODE_VIEWER } // namespace GUI diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 3f0d4b4e68..9cf694ece3 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -136,6 +136,20 @@ class Preview : public wxPanel public: #if ENABLE_GCODE_VIEWER + enum class OptionType : unsigned int + { + Travel, + Retractions, + Unretractions, + ToolChanges, + ColorChanges, + PausePrints, + CustomGCodes, + Shells, + ToolMarker, + Legend + }; + Preview(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodeProcessor::Result* gcode_result, std::function schedule_background_process = []() {}); #else @@ -235,6 +249,7 @@ private: #if ENABLE_GCODE_VIEWER void on_layers_slider_scroll_changed(wxCommandEvent& event); void on_moves_slider_scroll_changed(wxCommandEvent& event); + wxString get_option_type_string(OptionType type) const; #else void on_sliders_scroll_changed(wxCommandEvent& event); #endif // ENABLE_GCODE_VIEWER From f4303fc419d8ed2a0ded1c1cec795284c49c51fe Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 18 May 2020 13:32:07 +0200 Subject: [PATCH 078/503] Attempt to fix build on OsX --- src/slic3r/GUI/GUI_Preview.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 65e1e50589..bf694b445f 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -686,7 +686,7 @@ void Preview::on_combochecklist_features(wxCommandEvent& evt) #if ENABLE_GCODE_VIEWER void Preview::on_combochecklist_options(wxCommandEvent& evt) { - auto xor = [](unsigned int flags1, unsigned int flags2, unsigned int flag) { + auto xored = [](unsigned int flags1, unsigned int flags2, unsigned int flag) { auto is_flag_set = [](unsigned int flags, unsigned int flag) { return (flags & (1 << flag)) != 0; }; @@ -700,8 +700,8 @@ void Preview::on_combochecklist_options(wxCommandEvent& evt) m_canvas->set_gcode_options_visibility_from_flags(new_flags); - bool skip_refresh = xor(curr_flags, new_flags, static_cast(OptionType::Shells)) || - xor(curr_flags, new_flags, static_cast(OptionType::ToolMarker)); + bool skip_refresh = xored(curr_flags, new_flags, static_cast(OptionType::Shells)) || + xored(curr_flags, new_flags, static_cast(OptionType::ToolMarker)); if (!skip_refresh) refresh_print(); From 053f509437693938283df052b2376893091bbb8d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 19 May 2020 10:04:14 +0200 Subject: [PATCH 079/503] GCodeViewer -> Fixed visibility of bottom toolbar --- src/slic3r/GUI/GUI_Preview.cpp | 94 +++++++++++++++++++++++++--------- src/slic3r/GUI/GUI_Preview.hpp | 3 +- 2 files changed, 72 insertions(+), 25 deletions(-) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index bf694b445f..fdbd396e26 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -184,8 +184,8 @@ Preview::Preview( : m_canvas_widget(nullptr) , m_canvas(nullptr) #if ENABLE_GCODE_VIEWER - , m_bottom_toolbar_sizer(nullptr) , m_layers_slider_sizer(nullptr) + , m_bottom_toolbar_panel(nullptr) #else , m_double_slider_sizer(nullptr) #endif // ENABLE_GCODE_VIEWER @@ -194,6 +194,7 @@ Preview::Preview( , m_label_show(nullptr) , m_combochecklist_features(nullptr) #if ENABLE_GCODE_VIEWER + , m_combochecklist_features_pos(0) , m_combochecklist_options(nullptr) #else , m_checkbox_travel(nullptr) @@ -251,14 +252,20 @@ bool Preview::init(wxWindow* parent, Model* model) #if ENABLE_GCODE_VIEWER m_layers_slider_sizer = create_layers_slider_sizer(); + + m_bottom_toolbar_panel = new wxPanel(this); + + m_label_view_type = new wxStaticText(m_bottom_toolbar_panel, wxID_ANY, _L("View")); + + m_choice_view_type = new wxChoice(m_bottom_toolbar_panel, wxID_ANY); #else m_double_slider_sizer = new wxBoxSizer(wxHORIZONTAL); create_double_slider(); -#endif // ENABLE_GCODE_VIEWER m_label_view_type = new wxStaticText(this, wxID_ANY, _L("View")); m_choice_view_type = new wxChoice(this, wxID_ANY); +#endif // ENABLE_GCODE_VIEWER m_choice_view_type->Append(_L("Feature type")); m_choice_view_type->Append(_L("Height")); m_choice_view_type->Append(_L("Width")); @@ -269,10 +276,18 @@ bool Preview::init(wxWindow* parent, Model* model) m_choice_view_type->Append(_L("Color Print")); m_choice_view_type->SetSelection(0); +#if ENABLE_GCODE_VIEWER + m_label_show = new wxStaticText(m_bottom_toolbar_panel, wxID_ANY, _L("Show")); +#else m_label_show = new wxStaticText(this, wxID_ANY, _L("Show")); +#endif // ENABLE_GCODE_VIEWER m_combochecklist_features = new wxComboCtrl(); +#if ENABLE_GCODE_VIEWER + m_combochecklist_features->Create(m_bottom_toolbar_panel, wxID_ANY, _L("Feature types"), wxDefaultPosition, wxDefaultSize, wxCB_READONLY); +#else m_combochecklist_features->Create(this, wxID_ANY, _L("Feature types"), wxDefaultPosition, wxDefaultSize, wxCB_READONLY); +#endif // ENABLE_GCODE_VIEWER std::string feature_items = GUI::into_u8( #if ENABLE_GCODE_VIEWER _L("Unknown") + "|1|" + @@ -296,7 +311,7 @@ bool Preview::init(wxWindow* parent, Model* model) #if ENABLE_GCODE_VIEWER m_combochecklist_options = new wxComboCtrl(); - m_combochecklist_options->Create(this, wxID_ANY, _L("Options"), wxDefaultPosition, wxDefaultSize, wxCB_READONLY); + m_combochecklist_options->Create(m_bottom_toolbar_panel, wxID_ANY, _L("Options"), wxDefaultPosition, wxDefaultSize, wxCB_READONLY); std::string options_items = GUI::into_u8( get_option_type_string(OptionType::Travel) + "|0|" + get_option_type_string(OptionType::Retractions) + "|0|" + @@ -328,20 +343,23 @@ bool Preview::init(wxWindow* parent, Model* model) #endif // ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER - m_moves_slider = new DoubleSlider::Control(this, wxID_ANY, 0, 0, 0, 100, wxDefaultPosition, wxSize(-1, 3 * GetTextExtent("m").y), wxSL_HORIZONTAL); + m_moves_slider = new DoubleSlider::Control(m_bottom_toolbar_panel, wxID_ANY, 0, 0, 0, 100, wxDefaultPosition, wxSize(-1, 3 * GetTextExtent("m").y), wxSL_HORIZONTAL); m_moves_slider->SetDrawMode(DoubleSlider::dmSequentialGCodeView); - m_moves_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_moves_slider_scroll_changed, this); - m_bottom_toolbar_sizer = new wxBoxSizer(wxHORIZONTAL); - m_bottom_toolbar_sizer->AddSpacer(5); - m_bottom_toolbar_sizer->Add(m_label_view_type, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5); - m_bottom_toolbar_sizer->Add(m_choice_view_type, 0, wxALIGN_CENTER_VERTICAL, 0); - m_bottom_toolbar_sizer->AddSpacer(5); - m_bottom_toolbar_sizer->Add(m_label_show, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, 5); - m_bottom_toolbar_sizer->Add(m_combochecklist_options, 0, wxALIGN_CENTER_VERTICAL, 0); - m_bottom_toolbar_sizer->Add(m_combochecklist_features, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 5); - m_bottom_toolbar_sizer->AddSpacer(5); - m_bottom_toolbar_sizer->Add(m_moves_slider, 1, wxALL | wxEXPAND, 5); + wxBoxSizer* bottom_toolbar_sizer = new wxBoxSizer(wxHORIZONTAL); + bottom_toolbar_sizer->AddSpacer(5); + bottom_toolbar_sizer->Add(m_label_view_type, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5); + bottom_toolbar_sizer->Add(m_choice_view_type, 0, wxALIGN_CENTER_VERTICAL, 0); + bottom_toolbar_sizer->AddSpacer(5); + bottom_toolbar_sizer->Add(m_label_show, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, 5); + bottom_toolbar_sizer->Add(m_combochecklist_options, 0, wxALIGN_CENTER_VERTICAL, 0); + // change the following number if editing the layout of the bottom toolbar sizer. It is used into update_bottom_toolbar() + m_combochecklist_features_pos = 6; + bottom_toolbar_sizer->Add(m_combochecklist_features, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 5); + bottom_toolbar_sizer->Hide(m_combochecklist_features); + bottom_toolbar_sizer->AddSpacer(5); + bottom_toolbar_sizer->Add(m_moves_slider, 1, wxALL | wxEXPAND, 0); + m_bottom_toolbar_panel->SetSizer(bottom_toolbar_sizer); #else wxBoxSizer* bottom_sizer = new wxBoxSizer(wxHORIZONTAL); bottom_sizer->Add(m_label_view_type, 0, wxALIGN_CENTER_VERTICAL, 5); @@ -364,8 +382,8 @@ bool Preview::init(wxWindow* parent, Model* model) wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); main_sizer->Add(top_sizer, 1, wxALL | wxEXPAND, 0); #if ENABLE_GCODE_VIEWER - main_sizer->Add(m_bottom_toolbar_sizer, 0, wxALL | wxEXPAND, 0); - main_sizer->Hide(m_bottom_toolbar_sizer); + main_sizer->Add(m_bottom_toolbar_panel, 0, wxALL | wxEXPAND, 0); + main_sizer->Hide(m_bottom_toolbar_panel); #else main_sizer->Add(bottom_sizer, 0, wxALL | wxEXPAND, 0); #endif // ENABLE_GCODE_VIEWER @@ -565,6 +583,7 @@ void Preview::bind_event_handlers() m_combochecklist_features->Bind(wxEVT_CHECKLISTBOX, &Preview::on_combochecklist_features, this); #if ENABLE_GCODE_VIEWER m_combochecklist_options->Bind(wxEVT_CHECKLISTBOX, &Preview::on_combochecklist_options, this); + m_moves_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_moves_slider_scroll_changed, this); #else m_checkbox_travel->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_travel, this); m_checkbox_retractions->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_retractions, this); @@ -581,6 +600,7 @@ void Preview::unbind_event_handlers() m_combochecklist_features->Unbind(wxEVT_CHECKLISTBOX, &Preview::on_combochecklist_features, this); #if ENABLE_GCODE_VIEWER m_combochecklist_options->Unbind(wxEVT_CHECKLISTBOX, &Preview::on_combochecklist_options, this); + m_moves_slider->Unbind(wxEVT_SCROLL_CHANGED, &Preview::on_moves_slider_scroll_changed, this); #else m_checkbox_travel->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_travel, this); m_checkbox_retractions->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_retractions, this); @@ -773,10 +793,33 @@ void Preview::update_bottom_toolbar() combochecklist_set_flags(m_combochecklist_features, m_canvas->get_toolpath_role_visibility_flags()); combochecklist_set_flags(m_combochecklist_options, m_canvas->get_gcode_options_visibility_flags()); - m_bottom_toolbar_sizer->Show(m_combochecklist_features, - !m_canvas->is_gcode_legend_enabled() || m_canvas->get_gcode_view_type() != GCodeViewer::EViewType::FeatureType); - m_bottom_toolbar_sizer->Layout(); - Refresh(); + // updates visibility of features combobox + if (m_bottom_toolbar_panel->IsShown()) + { + wxSizer* sizer = m_bottom_toolbar_panel->GetSizer(); + bool show = !m_canvas->is_gcode_legend_enabled() || m_canvas->get_gcode_view_type() != GCodeViewer::EViewType::FeatureType; + + if (show) + { + if (sizer->GetItem(m_combochecklist_features) == nullptr) + { + sizer->Insert(m_combochecklist_features_pos, m_combochecklist_features, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 5); + sizer->Show(m_combochecklist_features); + sizer->Layout(); + Refresh(); + } + } + else + { + if (sizer->GetItem(m_combochecklist_features) != nullptr) + { + sizer->Hide(m_combochecklist_features); + sizer->Detach(m_combochecklist_features); + sizer->Layout(); + Refresh(); + } + } + } } #endif // ENABLE_GCODE_VIEWER @@ -1243,8 +1286,9 @@ void Preview::load_print_as_fff(bool keep_z_range) #if ENABLE_GCODE_VIEWER m_canvas->load_gcode_preview(*m_gcode_result); m_canvas->refresh_gcode_preview(*m_gcode_result, colors); - GetSizer()->Show(m_bottom_toolbar_sizer); + GetSizer()->Show(m_bottom_toolbar_panel); GetSizer()->Layout(); + Refresh(); zs = m_canvas->get_gcode_layers_zs(); #else m_canvas->load_gcode_preview(*m_gcode_preview_data, colors); @@ -1254,8 +1298,9 @@ void Preview::load_print_as_fff(bool keep_z_range) // Load the initial preview based on slices, not the final G-code. m_canvas->load_preview(colors, color_print_values); #if ENABLE_GCODE_VIEWER - GetSizer()->Hide(m_bottom_toolbar_sizer); + GetSizer()->Hide(m_bottom_toolbar_panel); GetSizer()->Layout(); + Refresh(); zs = m_canvas->get_volumes_print_zs(true); #endif // ENABLE_GCODE_VIEWER } @@ -1316,8 +1361,9 @@ void Preview::load_print_as_sla() { m_canvas->load_sla_preview(); #if ENABLE_GCODE_VIEWER - GetSizer()->Hide(m_bottom_toolbar_sizer); + GetSizer()->Hide(m_bottom_toolbar_panel); GetSizer()->Layout(); + Refresh(); #else show_hide_ui_elements("none"); #endif // ENABLE_GCODE_VIEWER diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 9cf694ece3..64aa8f7e35 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -84,7 +84,7 @@ class Preview : public wxPanel GLCanvas3D* m_canvas; #if ENABLE_GCODE_VIEWER wxBoxSizer* m_layers_slider_sizer; - wxBoxSizer* m_bottom_toolbar_sizer; + wxPanel* m_bottom_toolbar_panel; #else wxBoxSizer* m_double_slider_sizer; #endif // ENABLE_GCODE_VIEWER @@ -93,6 +93,7 @@ class Preview : public wxPanel wxStaticText* m_label_show; wxComboCtrl* m_combochecklist_features; #if ENABLE_GCODE_VIEWER + size_t m_combochecklist_features_pos; wxComboCtrl* m_combochecklist_options; #else wxCheckBox* m_checkbox_travel; From 98c2e3c7b12b45bba6369330ad980bbe1c825077 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 19 May 2020 11:17:47 +0200 Subject: [PATCH 080/503] GCodeViewer -> New icons for thumbs of horizontal DoubleSlider::Control --- resources/icons/thumb_left.svg | 54 +++++++++++++++++++++++++++++++++ resources/icons/thumb_right.svg | 54 +++++++++++++++++++++++++++++++++ src/slic3r/GUI/DoubleSlider.cpp | 12 +++++++- src/slic3r/GUI/GUI_Preview.cpp | 2 +- 4 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 resources/icons/thumb_left.svg create mode 100644 resources/icons/thumb_right.svg diff --git a/resources/icons/thumb_left.svg b/resources/icons/thumb_left.svg new file mode 100644 index 0000000000..ef78bd1410 --- /dev/null +++ b/resources/icons/thumb_left.svg @@ -0,0 +1,54 @@ + +image/svg+xml + + + + + + diff --git a/resources/icons/thumb_right.svg b/resources/icons/thumb_right.svg new file mode 100644 index 0000000000..f3748525d2 --- /dev/null +++ b/resources/icons/thumb_right.svg @@ -0,0 +1,54 @@ + +image/svg+xml + + + + + + diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index a6c9a5c772..6173680817 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -64,8 +64,13 @@ Control::Control( wxWindow *parent, if (!is_osx) SetDoubleBuffered(true);// SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX +#if ENABLE_GCODE_VIEWER + m_bmp_thumb_higher = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "thumb_right") : ScalableBitmap(this, "thumb_up")); + m_bmp_thumb_lower = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "thumb_left") : ScalableBitmap(this, "thumb_down")); +#else m_bmp_thumb_higher = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "right_half_circle.png") : ScalableBitmap(this, "thumb_up")); m_bmp_thumb_lower = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "left_half_circle.png" ) : ScalableBitmap(this, "thumb_down")); +#endif // ENABLE_GCODE_VIEWER m_thumb_size = m_bmp_thumb_lower.GetBmpSize(); m_bmp_add_tick_on = ScalableBitmap(this, "colorchange_add"); @@ -576,6 +581,10 @@ void Control::draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider void Control::draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) { +#if ENABLE_GCODE_VIEWER + wxCoord x_draw = pos.x - int(0.5 * m_thumb_size.x); + wxCoord y_draw = pos.y - int(0.5 * m_thumb_size.y); +#else wxCoord x_draw, y_draw; if (selection == ssLower) { if (is_horizontal()) { @@ -587,7 +596,7 @@ void Control::draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider y_draw = pos.y - int(0.5*m_thumb_size.y); } } - else{ + else { if (is_horizontal()) { x_draw = pos.x; y_draw = pos.y - int(0.5*m_thumb_size.y); @@ -597,6 +606,7 @@ void Control::draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider y_draw = pos.y - int(0.5*m_thumb_size.y); } } +#endif // ENABLE_GCODE_VIEWER dc.DrawBitmap(selection == ssLower ? m_bmp_thumb_lower.bmp() : m_bmp_thumb_higher.bmp(), x_draw, y_draw); // Update thumb rect diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index fdbd396e26..1784dbccc1 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -343,7 +343,7 @@ bool Preview::init(wxWindow* parent, Model* model) #endif // ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER - m_moves_slider = new DoubleSlider::Control(m_bottom_toolbar_panel, wxID_ANY, 0, 0, 0, 100, wxDefaultPosition, wxSize(-1, 3 * GetTextExtent("m").y), wxSL_HORIZONTAL); + m_moves_slider = new DoubleSlider::Control(m_bottom_toolbar_panel, wxID_ANY, 0, 0, 0, 100, wxDefaultPosition, wxSize(-1, 4 * GetTextExtent("m").y), wxSL_HORIZONTAL); m_moves_slider->SetDrawMode(DoubleSlider::dmSequentialGCodeView); wxBoxSizer* bottom_toolbar_sizer = new wxBoxSizer(wxHORIZONTAL); From c7c87973b723a306b95558a476a5b27d8232800d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 20 May 2020 14:11:22 +0200 Subject: [PATCH 081/503] First installment of tech ENABLE_SHADERS_MANAGER, using class GLShadersManager as a central point to manage OpenGL shaders --- src/libslic3r/GCode/ToolOrdering.cpp | 4 +- src/libslic3r/Technologies.hpp | 3 + src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/3DBed.cpp | 75 +++++++ src/slic3r/GUI/3DBed.hpp | 18 ++ src/slic3r/GUI/GCodeViewer.cpp | 197 +++++++++++++++++- src/slic3r/GUI/GCodeViewer.hpp | 55 +++++ src/slic3r/GUI/GLCanvas3D.cpp | 192 +++++++++++++++++- src/slic3r/GUI/GLCanvas3D.hpp | 34 +++- src/slic3r/GUI/GLShader.cpp | 289 ++++++++++++++++++++++++++- src/slic3r/GUI/GLShader.hpp | 61 ++++++ src/slic3r/GUI/GLShadersManager.cpp | 76 +++++++ src/slic3r/GUI/GLShadersManager.hpp | 31 +++ src/slic3r/GUI/GUI_App.hpp | 10 + src/slic3r/GUI/OpenGLManager.cpp | 86 +++++++- src/slic3r/GUI/OpenGLManager.hpp | 22 +- src/slic3r/GUI/Selection.cpp | 263 +++++++++++++++++++++--- src/slic3r/GUI/Selection.hpp | 44 ++++ 18 files changed, 1415 insertions(+), 47 deletions(-) create mode 100644 src/slic3r/GUI/GLShadersManager.cpp create mode 100644 src/slic3r/GUI/GLShadersManager.hpp diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index db398f06c8..9c1a1900fd 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -400,7 +400,9 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_ // and maybe other problems. We will therefore go through layer_tools and detect and fix this. // So, if there is a non-object layer starting with different extruder than the last one ended with (or containing more than one extruder), // we'll mark it with has_wipe tower. - assert(! m_layer_tools.empty() && m_layer_tools.front().has_wipe_tower); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// assert(! m_layer_tools.empty() && m_layer_tools.front().has_wipe_tower); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (! m_layer_tools.empty() && m_layer_tools.front().has_wipe_tower) { for (size_t i = 0; i + 1 < m_layer_tools.size();) { const LayerTools < = m_layer_tools[i]; diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 3df9da961d..c274c1e841 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -44,6 +44,9 @@ // Enable G-Code viewer #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#define ENABLE_SHADERS_MANAGER (1 && ENABLE_GCODE_VIEWER) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index b085fad456..5d47f97589 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -23,6 +23,8 @@ set(SLIC3R_GUI_SOURCES GUI/3DScene.cpp GUI/3DScene.hpp GUI/format.hpp + GUI/GLShadersManager.hpp + GUI/GLShadersManager.cpp GUI/GLShader.cpp GUI/GLShader.hpp GUI/GLCanvas3D.hpp diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 4ef8679603..4bf8ce900e 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -15,6 +15,11 @@ #if ENABLE_GCODE_VIEWER #include "3DScene.hpp" #endif // ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//#if ENABLE_SHADERS_MANAGER +//#include "GLShader.hpp" +//#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include @@ -163,9 +168,17 @@ Bed3D::Axes::~Axes() void Bed3D::Axes::render() const { #if ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + auto render_axis = [this](const Transform3f& transform) { +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ auto render_axis = [this](const Transform3f& transform, GLint color_id, const std::array& color) { if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)color.data())); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glPushMatrix()); glsafe(::glMultMatrixf(transform.data())); @@ -174,14 +187,43 @@ void Bed3D::Axes::render() const }; m_arrow.init_from(stilized_arrow(16, DefaultTipRadius, DefaultTipLength, DefaultStemRadius, m_stem_length)); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); + if (shader == nullptr) + return; +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (!m_shader.init("gouraud_light.vs", "gouraud_light.fs")) BOOST_LOG_TRIVIAL(error) << "Unable to initialize gouraud_light shader: please, check that the files gouraud_light.vs and gouraud_light.fs are available"; if (!m_shader.is_initialized()) return; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glEnable(GL_DEPTH_TEST)); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader->start_using(); + + // x axis + shader->set_uniform("uniform_color", { 0.75f, 0.0f, 0.0f, 1.0f }); + render_axis(Geometry::assemble_transform(m_origin, { 0.0, 0.5 * M_PI, 0.0f }).cast()); + + // y axis + shader->set_uniform("uniform_color", { 0.0f, 0.75f, 0.0f, 1.0f }); + render_axis(Geometry::assemble_transform(m_origin, { -0.5 * M_PI, 0.0, 0.0f }).cast()); + + // z axis + shader->set_uniform("uniform_color", { 0.0f, 0.0f, 0.75f, 1.0f }); + render_axis(Geometry::assemble_transform(m_origin).cast()); + + shader->stop_using(); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_shader.start_using(); GLint color_id = ::glGetUniformLocation(m_shader.get_shader_program_id(), "uniform_color"); @@ -195,6 +237,9 @@ void Bed3D::Axes::render() const render_axis(Geometry::assemble_transform(m_origin).cast(), color_id, { 0.0f, 0.0f, 0.75f, 1.0f }); m_shader.stop_using(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glDisable(GL_DEPTH_TEST)); #else @@ -540,6 +585,16 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const if (m_triangles.get_vertices_count() > 0) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + GLShaderProgram* shader = wxGetApp().get_shader("printbed"); + if (shader != nullptr) + { + shader->start_using(); + shader->set_uniform("transparent_background", bottom); + shader->set_uniform("svg_source", boost::algorithm::iends_with(m_texture.get_source(), ".svg")); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (m_shader.get_shader_program_id() == 0) m_shader.init("printbed.vs", "printbed.fs"); @@ -548,6 +603,9 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const m_shader.start_using(); m_shader.set_uniform("transparent_background", bottom); m_shader.set_uniform("svg_source", boost::algorithm::iends_with(m_texture.get_source(), ".svg")); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (m_vbo_id == 0) { @@ -568,8 +626,17 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const unsigned int stride = m_triangles.get_vertex_data_size(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + GLint position_id = shader->get_attrib_location("v_position"); + GLint tex_coords_id = shader->get_attrib_location("v_tex_coords"); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GLint position_id = m_shader.get_attrib_location("v_position"); GLint tex_coords_id = m_shader.get_attrib_location("v_tex_coords"); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // show the temporary texture while no compressed data is available GLuint tex_id = (GLuint)m_temp_texture.get_id(); @@ -607,7 +674,15 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const glsafe(::glDisable(GL_BLEND)); glsafe(::glDepthMask(GL_TRUE)); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader->stop_using(); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_shader.stop_using(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } } } diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index 440468233c..9603015da8 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -3,7 +3,13 @@ #include "GLTexture.hpp" #include "3DScene.hpp" +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "GLShader.hpp" +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER #include "GLModel.hpp" #endif // ENABLE_GCODE_VIEWER @@ -69,7 +75,13 @@ class Bed3D Vec3d m_origin{ Vec3d::Zero() }; float m_stem_length{ DefaultStemLength }; mutable GL_Model m_arrow; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ mutable Shader m_shader; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ public: #else @@ -118,7 +130,13 @@ private: mutable GLBed m_model; // temporary texture shown until the main texture has still no levels compressed mutable GLTexture m_temp_texture; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ mutable Shader m_shader; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ mutable unsigned int m_vbo_id; Axes m_axes; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index dcb320766a..55f603829b 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -12,6 +12,11 @@ #include "DoubleSlider.hpp" #include "GLCanvas3D.hpp" #include "GLToolbar.hpp" +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//#if ENABLE_SHADERS_MANAGER +//#include "GLShader.hpp" +//#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "GUI_Preview.hpp" #include "libslic3r/Model.hpp" #if ENABLE_GCODE_VIEWER_STATISTICS @@ -107,6 +112,9 @@ void GCodeViewer::IBuffer::reset() render_paths = std::vector(); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool GCodeViewer::IBuffer::init_shader(const std::string& vertex_shader_src, const std::string& fragment_shader_src) { if (!shader.init(vertex_shader_src, fragment_shader_src)) { @@ -116,6 +124,9 @@ bool GCodeViewer::IBuffer::init_shader(const std::string& vertex_shader_src, con return true; } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void GCodeViewer::IBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsigned int i_id, unsigned int s_id) { @@ -149,11 +160,33 @@ GCodeViewer::Color GCodeViewer::Extrusions::Range::get_color_at(float value) con void GCodeViewer::SequentialView::Marker::init() { m_model.init_from(stilized_arrow(16, 2.0f, 4.0f, 1.0f, 8.0f)); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ init_shader(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } void GCodeViewer::SequentialView::Marker::render() const { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + if (!m_visible) + return; + + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); + if (shader == nullptr) + return; + + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + + shader->start_using(); + shader->set_uniform("uniform_color", m_color); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (!m_visible || !m_shader.is_initialized()) return; @@ -164,6 +197,9 @@ void GCodeViewer::SequentialView::Marker::render() const GLint color_id = ::glGetUniformLocation(m_shader.get_shader_program_id(), "uniform_color"); if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)m_color.data())); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glPushMatrix()); glsafe(::glMultMatrixf(m_world_transform.data())); @@ -172,16 +208,30 @@ void GCodeViewer::SequentialView::Marker::render() const glsafe(::glPopMatrix()); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader->stop_using(); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_shader.stop_using(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glDisable(GL_BLEND)); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void GCodeViewer::SequentialView::Marker::init_shader() { if (!m_shader.init("gouraud_light.vs", "gouraud_light.fs")) BOOST_LOG_TRIVIAL(error) << "Unable to initialize gouraud_light shader: please, check that the files gouraud_light.vs and gouraud_light.fs are available"; } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ const std::vector GCodeViewer::Extrusion_Role_Colors {{ { 0.50f, 0.50f, 0.50f }, // erNone @@ -390,6 +440,31 @@ void GCodeViewer::set_layers_z_range(const std::array& layers_z_range wxGetApp().plater()->update_preview_moves_slider(); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER +void GCodeViewer::init_shaders() +{ + unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); + unsigned char end_id = buffer_id(GCodeProcessor::EMoveType::Count); + + for (unsigned char i = begin_id; i < end_id; ++i) + { + switch (buffer_type(i)) + { + case GCodeProcessor::EMoveType::Tool_change: { m_buffers[i].shader = "toolchanges"; break; } + case GCodeProcessor::EMoveType::Color_change: { m_buffers[i].shader = "colorchanges"; break; } + case GCodeProcessor::EMoveType::Pause_Print: { m_buffers[i].shader = "pauses"; break; } + case GCodeProcessor::EMoveType::Custom_GCode: { m_buffers[i].shader = "customs"; break; } + case GCodeProcessor::EMoveType::Retract: { m_buffers[i].shader = "retractions"; break; } + case GCodeProcessor::EMoveType::Unretract: { m_buffers[i].shader = "unretractions"; break; } + case GCodeProcessor::EMoveType::Extrude: { m_buffers[i].shader = "extrusions"; break; } + case GCodeProcessor::EMoveType::Travel: { m_buffers[i].shader = "travels"; break; } + default: { break; } + } + } +} +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool GCodeViewer::init_shaders() { unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); @@ -424,6 +499,9 @@ bool GCodeViewer::init_shaders() return true; } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) { @@ -750,6 +828,9 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool void GCodeViewer::render_toolpaths() const { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ auto set_color = [](GLint current_program_id, const Color& color) { if (current_program_id > 0) { GLint color_id = ::glGetUniformLocation(current_program_id, "uniform_color"); @@ -760,6 +841,9 @@ void GCodeViewer::render_toolpaths() const } BOOST_LOG_TRIVIAL(error) << "Unable to find uniform_color uniform"; }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glCullFace(GL_BACK)); glsafe(::glLineWidth(3.0f)); @@ -779,11 +863,29 @@ void GCodeViewer::render_toolpaths() const if (!buffer.visible) continue; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + GLShaderProgram* shader = wxGetApp().get_shader(buffer.shader.c_str()); + if (shader != nullptr) { +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (buffer.shader.is_initialized()) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + GCodeProcessor::EMoveType type = buffer_type(i); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader->start_using(); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ buffer.shader.start_using(); - +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.ibo_id)); switch (type) @@ -791,7 +893,15 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Tool_change: { Color color = { 1.0f, 1.0f, 1.0f }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader->set_uniform("uniform_color", color); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ set_color(static_cast(buffer.shader.get_shader_program_id()), color); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ for (const RenderPath& path : buffer.render_paths) { glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -807,7 +917,15 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Color_change: { Color color = { 1.0f, 0.0f, 0.0f }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader->set_uniform("uniform_color", color); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ set_color(static_cast(buffer.shader.get_shader_program_id()), color); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ for (const RenderPath& path : buffer.render_paths) { glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -823,7 +941,15 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Pause_Print: { Color color = { 0.0f, 1.0f, 0.0f }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader->set_uniform("uniform_color", color); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ set_color(static_cast(buffer.shader.get_shader_program_id()), color); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ for (const RenderPath& path : buffer.render_paths) { glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -839,7 +965,15 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Custom_GCode: { Color color = { 0.0f, 0.0f, 1.0f }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader->set_uniform("uniform_color", color); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ set_color(static_cast(buffer.shader.get_shader_program_id()), color); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ for (const RenderPath& path : buffer.render_paths) { glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -855,7 +989,15 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Retract: { Color color = { 1.0f, 0.0f, 1.0f }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader->set_uniform("uniform_color", color); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ set_color(static_cast(buffer.shader.get_shader_program_id()), color); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ for (const RenderPath& path : buffer.render_paths) { glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -871,7 +1013,15 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Unretract: { Color color = { 0.0f, 1.0f, 1.0f }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader->set_uniform("uniform_color", color); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ set_color(static_cast(buffer.shader.get_shader_program_id()), color); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ for (const RenderPath& path : buffer.render_paths) { glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -888,7 +1038,15 @@ void GCodeViewer::render_toolpaths() const { for (const RenderPath& path : buffer.render_paths) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader->set_uniform("uniform_color", path.color); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ set_color(static_cast(buffer.shader.get_shader_program_id()), path.color); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glMultiDrawElements(GL_LINE_STRIP, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_line_strip_calls_count; @@ -901,7 +1059,15 @@ void GCodeViewer::render_toolpaths() const { for (const RenderPath& path : buffer.render_paths) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader->set_uniform("uniform_color", path.color); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ set_color(static_cast(buffer.shader.get_shader_program_id()), path.color); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glMultiDrawElements(GL_LINE_STRIP, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_line_strip_calls_count; @@ -913,7 +1079,15 @@ void GCodeViewer::render_toolpaths() const } glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader->stop_using(); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ buffer.shader.stop_using(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } } @@ -923,6 +1097,24 @@ void GCodeViewer::render_toolpaths() const void GCodeViewer::render_shells() const { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + if (!m_shells.visible || m_shells.volumes.empty()) + return; + + GLShaderProgram* shader = wxGetApp().get_shader("shells"); + if (shader == nullptr) + return; + +// glsafe(::glDepthMask(GL_FALSE)); + + shader->start_using(); + m_shells.volumes.render(GLVolumeCollection::Transparent, true, wxGetApp().plater()->get_camera().get_view_matrix()); + shader->stop_using(); + +// glsafe(::glDepthMask(GL_TRUE)); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (!m_shells.visible || m_shells.volumes.empty() || !m_shells.shader.is_initialized()) return; @@ -933,6 +1125,9 @@ void GCodeViewer::render_shells() const m_shells.shader.stop_using(); // glsafe(::glDepthMask(GL_TRUE)); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } void GCodeViewer::render_legend() const diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 688f1266b5..df28f4aa63 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -2,7 +2,13 @@ #define slic3r_GCodeViewer_hpp_ #if ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "GLShader.hpp" +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "3DScene.hpp" #include "libslic3r/GCode/GCodeProcessor.hpp" #include "GLModel.hpp" @@ -78,13 +84,27 @@ class GCodeViewer { unsigned int ibo_id{ 0 }; size_t indices_count{ 0 }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + std::string shader; +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Shader shader; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ std::vector paths; std::vector render_paths; bool visible{ false }; void reset(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool init_shader(const std::string& vertex_shader_src, const std::string& fragment_shader_src); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void add_path(const GCodeProcessor::MoveVertex& move, unsigned int i_id, unsigned int s_id); }; @@ -93,7 +113,13 @@ class GCodeViewer { GLVolumeCollection volumes; bool visible{ false }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Shader shader; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ }; // helper to render extrusion paths @@ -203,7 +229,13 @@ public: Transform3f m_world_transform; std::array m_color{ 1.0f, 1.0f, 1.0f, 1.0f }; bool m_visible{ false }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Shader m_shader; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ public: void init(); @@ -218,8 +250,14 @@ public: void render() const; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ private: void init_shader(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ }; struct Endpoints @@ -273,7 +311,16 @@ public: bool init() { set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Extrude, true); m_sequential_view.marker.init(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + init_shaders(); + return true; +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ return init_shaders(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } // extract rendering data from the given parameters @@ -317,7 +364,15 @@ public: void enable_legend(bool enable) { m_legend_enabled = enable; } private: +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + void init_shaders(); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool init_shaders(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void load_toolpaths(const GCodeProcessor::Result& gcode_result); void load_shells(const Print& print, bool initialized); void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 82c7576a25..c1c310b34c 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -156,10 +156,19 @@ GLCanvas3D::LayersEditing::~LayersEditing() const float GLCanvas3D::LayersEditing::THICKNESS_BAR_WIDTH = 70.0f; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER +void GLCanvas3D::LayersEditing::init() +{ +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename) { if (!m_shader.init(vertex_shader_filename, fragment_shader_filename)) return false; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glGenTextures(1, (GLuint*)&m_z_texture_id)); glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id)); @@ -170,7 +179,13 @@ bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1)); glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ return true; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } void GLCanvas3D::LayersEditing::set_config(const DynamicPrintConfig* config) @@ -203,7 +218,15 @@ void GLCanvas3D::LayersEditing::select_object(const Model &model, int object_id) bool GLCanvas3D::LayersEditing::is_allowed() const { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + return wxGetApp().get_shader("variable_layer_height") != nullptr && m_z_texture_id > 0; +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ return m_shader.is_initialized() && m_shader.get_shader()->shader_program_id > 0 && m_z_texture_id > 0; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } bool GLCanvas3D::LayersEditing::is_enabled() const @@ -361,7 +384,15 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas) bool GLCanvas3D::LayersEditing::is_initialized() const { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + return wxGetApp().get_shader("variable_layer_height") != nullptr; +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ return m_shader.is_initialized(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } std::string GLCanvas3D::LayersEditing::get_tooltip(const GLCanvas3D& canvas) const @@ -395,6 +426,21 @@ std::string GLCanvas3D::LayersEditing::get_tooltip(const GLCanvas3D& canvas) con void GLCanvas3D::LayersEditing::render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + GLShaderProgram* shader = wxGetApp().get_shader("variable_layer_height"); + if (shader == nullptr) + return; + + shader->start_using(); + + shader->set_uniform("z_to_texture_row", float(m_layers_texture.cells - 1) / (float(m_layers_texture.width) * m_object_max_z)); + shader->set_uniform("z_texture_row_to_normalized", 1.0f / (float)m_layers_texture.height); + shader->set_uniform("z_cursor", m_object_max_z * this->get_cursor_z_relative(canvas)); + shader->set_uniform("z_cursor_band_width", band_width); + shader->set_uniform("object_max_z", m_object_max_z); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_shader.start_using(); m_shader.set_uniform("z_to_texture_row", float(m_layers_texture.cells - 1) / (float(m_layers_texture.width) * m_object_max_z)); @@ -402,6 +448,9 @@ void GLCanvas3D::LayersEditing::render_active_object_annotations(const GLCanvas3 m_shader.set_uniform("z_cursor", m_object_max_z * this->get_cursor_z_relative(canvas)); m_shader.set_uniform("z_cursor_band_width", band_width); m_shader.set_uniform("object_max_z", m_object_max_z); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id)); @@ -421,7 +470,15 @@ void GLCanvas3D::LayersEditing::render_active_object_annotations(const GLCanvas3 glsafe(::glEnd()); glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader->stop_using(); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_shader.stop_using(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } void GLCanvas3D::LayersEditing::render_profile(const Rect& bar_rect) const @@ -455,6 +512,29 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G { assert(this->is_allowed()); assert(this->last_object_id != -1); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + GLShaderProgram* shader = wxGetApp().get_shader("variable_layer_height"); + assert(shader != nullptr); + + GLint current_program_id = 0; + glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id)); + if (shader->get_id() != static_cast(current_program_id)) + // The layer editing shader is not yet active. Activate it. + shader->start_using(); + else + // The layer editing shader was already active. + current_program_id = 0; + + const_cast(this)->generate_layer_height_texture(); + + // Uniforms were resolved, go ahead using the layer editing shader. + shader->set_uniform("z_to_texture_row", float(m_layers_texture.cells - 1) / (float(m_layers_texture.width) * float(m_object_max_z))); + shader->set_uniform("z_texture_row_to_normalized", 1.0f / float(m_layers_texture.height)); + shader->set_uniform("z_cursor", float(m_object_max_z) * float(this->get_cursor_z_relative(canvas))); + shader->set_uniform("z_cursor_band_width", float(this->band_width)); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GLint shader_id = m_shader.get_shader()->shader_program_id; assert(shader_id > 0); @@ -467,15 +547,16 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G // The layer editing shader was already active. current_program_id = -1; - GLint z_to_texture_row_id = ::glGetUniformLocation(shader_id, "z_to_texture_row"); - GLint z_texture_row_to_normalized_id = ::glGetUniformLocation(shader_id, "z_texture_row_to_normalized"); - GLint z_cursor_id = ::glGetUniformLocation(shader_id, "z_cursor"); - GLint z_cursor_band_width_id = ::glGetUniformLocation(shader_id, "z_cursor_band_width"); - GLint world_matrix_id = ::glGetUniformLocation(shader_id, "volume_world_matrix"); - GLint object_max_z_id = ::glGetUniformLocation(shader_id, "object_max_z"); + GLint z_to_texture_row_id = ::glGetUniformLocation(shader_id, "z_to_texture_row"); + GLint z_texture_row_to_normalized_id = ::glGetUniformLocation(shader_id, "z_texture_row_to_normalized"); + GLint z_cursor_id = ::glGetUniformLocation(shader_id, "z_cursor"); + GLint z_cursor_band_width_id = ::glGetUniformLocation(shader_id, "z_cursor_band_width"); + GLint world_matrix_id = ::glGetUniformLocation(shader_id, "volume_world_matrix"); + GLint object_max_z_id = ::glGetUniformLocation(shader_id, "object_max_z"); glcheck(); - if (z_to_texture_row_id != -1 && z_texture_row_to_normalized_id != -1 && z_cursor_id != -1 && z_cursor_band_width_id != -1 && world_matrix_id != -1) + + if (z_to_texture_row_id != -1 && z_texture_row_to_normalized_id != -1 && z_cursor_id != -1 && z_cursor_band_width_id != -1 && world_matrix_id != -1) { const_cast(this)->generate_layer_height_texture(); @@ -484,6 +565,9 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G glsafe(::glUniform1f(z_texture_row_to_normalized_id, GLfloat(1.0f / m_layers_texture.height))); glsafe(::glUniform1f(z_cursor_id, GLfloat(m_object_max_z) * GLfloat(this->get_cursor_z_relative(canvas)))); glsafe(::glUniform1f(z_cursor_band_width_id, GLfloat(this->band_width))); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // Initialize the layer height texture mapping. GLsizei w = (GLsizei)m_layers_texture.width; GLsizei h = (GLsizei)m_layers_texture.height; @@ -499,17 +583,29 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G // Render the object using the layer editing shader and texture. if (! glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier) continue; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader->set_uniform("volume_world_matrix", glvolume->world_matrix()); + shader->set_uniform("object_max_z", GLfloat(0)); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (world_matrix_id != -1) glsafe(::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)glvolume->world_matrix().cast().data())); if (object_max_z_id != -1) glsafe(::glUniform1f(object_max_z_id, GLfloat(0))); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glvolume->render(); } // Revert back to the previous shader. glBindTexture(GL_TEXTURE_2D, 0); if (current_program_id > 0) glsafe(::glUseProgram(current_program_id)); - } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + } else { // Something went wrong. Just render the object. @@ -522,6 +618,9 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G glvolume->render(); } } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } void GLCanvas3D::LayersEditing::adjust_layer_height_profile() @@ -1645,6 +1744,12 @@ bool GLCanvas3D::init() if (m_multisample_allowed) glsafe(::glEnable(GL_MULTISAMPLE)); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + if (m_main_toolbar.is_enabled()) + m_layers_editing.init(); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (!m_shader.init("gouraud.vs", "gouraud.fs")) { std::cout << "Unable to initialize gouraud shader: please, check that the files gouraud.vs and gouraud.fs are available" << std::endl; @@ -1656,6 +1761,9 @@ bool GLCanvas3D::init() std::cout << "Unable to initialize variable_layer_height shader: please, check that the files variable_layer_height.vs and variable_layer_height.fs are available" << std::endl; return false; } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER if (!m_main_toolbar.is_enabled()) @@ -4515,8 +4623,17 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool return ret; }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + static const std::array orange = { 0.923f, 0.504f, 0.264f, 1.0f }; + static const std::array gray = { 0.64f, 0.64f, 0.64f, 1.0f }; +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ static const GLfloat orange[] = { 0.923f, 0.504f, 0.264f, 1.0f }; static const GLfloat gray[] = { 0.64f, 0.64f, 0.64f, 1.0f }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GLVolumePtrs visible_volumes; @@ -4560,6 +4677,22 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool camera.apply_projection(box, near_z, far_z); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + GLShaderProgram* shader = wxGetApp().get_shader("gouraud"); + if (shader == nullptr) + return; + + if (transparent_background) + glsafe(::glClearColor(0.0f, 0.0f, 0.0f, 0.0f)); + + glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); + glsafe(::glEnable(GL_DEPTH_TEST)); + + shader->start_using(); + shader->set_uniform("print_box.volume_detection", 0); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (transparent_background) glsafe(::glClearColor(0.0f, 0.0f, 0.0f, 0.0f)); @@ -4575,24 +4708,44 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool if (print_box_detection_id != -1) glsafe(::glUniform1i(print_box_detection_id, 0)); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ for (const GLVolume* vol : visible_volumes) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader->set_uniform("uniform_color", (vol->printable && !vol->is_outside) ? orange : gray); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (vol->printable && !vol->is_outside) ? orange : gray)); else glsafe(::glColor4fv((vol->printable && !vol->is_outside) ? orange : gray)); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ vol->render(); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader->stop_using(); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_shader.stop_using(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glDisable(GL_DEPTH_TEST)); if (show_bed) _render_bed(!camera.is_looking_downward(), false); + // restore background color if (transparent_background) glsafe(::glClearColor(1.0f, 1.0f, 1.0f, 1.0f)); } @@ -5531,7 +5684,18 @@ void GLCanvas3D::_render_objects() const m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data()); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + GLShaderProgram* shader = wxGetApp().get_shader("gouraud"); + if (shader != nullptr) + { + shader->start_using(); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_shader.start_using(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (m_picking_enabled && !m_gizmos.is_dragging() && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f)) { int object_id = m_layers_editing.last_object_id; m_volumes.render(GLVolumeCollection::Opaque, false, wxGetApp().plater()->get_camera().get_view_matrix(), [object_id](const GLVolume& volume) { @@ -5540,7 +5704,8 @@ void GLCanvas3D::_render_objects() const }); // Let LayersEditing handle rendering of the active object using the layer height profile shader. m_layers_editing.render_volumes(*this, this->m_volumes); - } else { + } + else { // do not cull backfaces to show broken geometry, if any m_volumes.render(GLVolumeCollection::Opaque, m_picking_enabled, wxGetApp().plater()->get_camera().get_view_matrix(), [this](const GLVolume& volume) { return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0); @@ -5548,7 +5713,16 @@ void GLCanvas3D::_render_objects() const } m_volumes.render(GLVolumeCollection::Transparent, false, wxGetApp().plater()->get_camera().get_view_matrix()); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader->stop_using(); + } +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_shader.stop_using(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_camera_clipping_plane = ClippingPlane::ClipsNothing(); } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 9d77790619..635d23e544 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -7,7 +7,13 @@ #include "3DScene.hpp" #include "GLToolbar.hpp" +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "GLShader.hpp" +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "Event.hpp" #include "Selection.hpp" #include "Gizmos/GLGizmosManager.hpp" @@ -167,7 +173,13 @@ private: private: bool m_enabled; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Shader m_shader; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ unsigned int m_z_texture_id; // Not owned by LayersEditing. const DynamicPrintConfig *m_config; @@ -214,8 +226,16 @@ private: LayersEditing(); ~LayersEditing(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + void init(); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename); - void set_config(const DynamicPrintConfig* config); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + void set_config(const DynamicPrintConfig* config); void select_object(const Model &model, int object_id); bool is_allowed() const; @@ -457,7 +477,13 @@ private: WarningTexture m_warning_texture; wxTimer m_timer; LayersEditing m_layers_editing; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Shader m_shader; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Mouse m_mouse; mutable GLGizmosManager m_gizmos; mutable GLToolbar m_main_toolbar; @@ -587,7 +613,13 @@ public: void set_color_by(const std::string& value); void refresh_camera_scene_box(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ const Shader& get_shader() const { return m_shader; } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ BoundingBoxf3 volumes_bounding_box() const; BoundingBoxf3 scene_bounding_box() const; diff --git a/src/slic3r/GUI/GLShader.cpp b/src/slic3r/GUI/GLShader.cpp index c310760603..3b7c2abcc6 100644 --- a/src/slic3r/GUI/GLShader.cpp +++ b/src/slic3r/GUI/GLShader.cpp @@ -1,11 +1,289 @@ -#include - +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include "libslic3r/libslic3r.h" +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "GLShader.hpp" -#include "libslic3r/Utils.hpp" #include "3DScene.hpp" -#include +#include "libslic3r/Utils.hpp" +#include +#include + +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER +#include + +namespace Slic3r { + +GLShaderProgram::~GLShaderProgram() +{ + if (m_id > 0) + glsafe(::glDeleteProgram(m_id)); +} + +bool GLShaderProgram::init_from_files(const std::string& name, const ShaderFilenames& filenames) +{ + auto load_from_file = [](const std::string& filename) { + std::string path = resources_dir() + "/shaders/" + filename; + boost::nowide::ifstream s(path, boost::nowide::ifstream::binary); + if (!s.good()) + { + BOOST_LOG_TRIVIAL(error) << "Couldn't open file: '" << path << "'"; + return std::string(); + } + + s.seekg(0, s.end); + int file_length = static_cast(s.tellg()); + s.seekg(0, s.beg); + std::string source(file_length, '\0'); + s.read(source.data(), file_length); + if (!s.good()) + { + BOOST_LOG_TRIVIAL(error) << "Error while loading file: '" << path << "'"; + return std::string(); + } + + s.close(); + return source; + }; + + ShaderSources sources = {}; + for (size_t i = 0; i < static_cast(EShaderType::Count); ++i) { + sources[i] = filenames[i].empty() ? std::string() : load_from_file(filenames[i]); + } + + bool valid = (!sources[static_cast(EShaderType::Vertex)].empty() && !sources[static_cast(EShaderType::Fragment)].empty()) || + !sources[static_cast(EShaderType::Compute)].empty(); + + return valid ? init_from_texts(name, sources) : false; +} + +bool GLShaderProgram::init_from_texts(const std::string& name, const ShaderSources& sources) +{ + auto shader_type_as_string = [](EShaderType type) { + switch (type) + { + case EShaderType::Vertex: { return "vertex"; } + case EShaderType::Fragment: { return "fragment"; } + case EShaderType::Geometry: { return "geometry"; } + case EShaderType::TessEvaluation: { return "tesselation evaluation"; } + case EShaderType::TessControl: { return "tesselation control"; } + case EShaderType::Compute: { return "compute"; } + default: { return "unknown"; } + } + }; + + auto create_shader = [](EShaderType type) { + GLuint id = 0; + switch (type) + { + case EShaderType::Vertex: { id = ::glCreateShader(GL_VERTEX_SHADER); glcheck(); break; } + case EShaderType::Fragment: { id = ::glCreateShader(GL_FRAGMENT_SHADER); glcheck(); break; } + case EShaderType::Geometry: { id = ::glCreateShader(GL_GEOMETRY_SHADER); glcheck(); break; } + case EShaderType::TessEvaluation: { id = ::glCreateShader(GL_TESS_EVALUATION_SHADER); glcheck(); break; } + case EShaderType::TessControl: { id = ::glCreateShader(GL_TESS_CONTROL_SHADER); glcheck(); break; } + case EShaderType::Compute: { id = ::glCreateShader(GL_COMPUTE_SHADER); glcheck(); break; } + default: { break; } + } + + return (id == 0) ? std::make_pair(false, GLuint(0)) : std::make_pair(true, id); + }; + + auto release_shaders = [](const std::array(EShaderType::Count)>& shader_ids) { + for (size_t i = 0; i < static_cast(EShaderType::Count); ++i) { + if (shader_ids[i] > 0) + glsafe(::glDeleteShader(shader_ids[i])); + } + }; + + assert(m_id == 0); + + m_name = name; + + std::array(EShaderType::Count)> shader_ids = { 0 }; + + for (size_t i = 0; i < static_cast(EShaderType::Count); ++i) { + const std::string& source = sources[i]; + if (!source.empty()) + { + EShaderType type = static_cast(i); + auto [result, id] = create_shader(type); + if (result) + shader_ids[i] = id; + else + { + BOOST_LOG_TRIVIAL(error) << "glCreateShader() failed for " << shader_type_as_string(type) << " shader of shader program '" << name << "'"; + return false; + } + + const char* source_ptr = source.c_str(); + glsafe(::glShaderSource(id, 1, &source_ptr, nullptr)); + glsafe(::glCompileShader(id)); + GLint params; + glsafe(::glGetShaderiv(id, GL_COMPILE_STATUS, ¶ms)); + if (params == GL_FALSE) { + // Compilation failed. + glsafe(::glGetShaderiv(id, GL_INFO_LOG_LENGTH, ¶ms)); + std::vector msg(params); + glsafe(::glGetShaderInfoLog(id, params, ¶ms, msg.data())); + BOOST_LOG_TRIVIAL(error) << "Unable to compile " << shader_type_as_string(type) << " shader of shader program '" << name << "':\n" << msg.data(); + + // release shader + release_shaders(shader_ids); + return false; + } + } + } + + m_id = ::glCreateProgram(); + glcheck(); + if (m_id == 0) { + BOOST_LOG_TRIVIAL(error) << "glCreateProgram() failed for shader program '" << name << "'"; + + // release shaders + release_shaders(shader_ids); + return false; + } + + for (size_t i = 0; i < static_cast(EShaderType::Count); ++i) { + if (shader_ids[i] > 0) + glsafe(::glAttachShader(m_id, shader_ids[i])); + } + + glsafe(::glLinkProgram(m_id)); + GLint params; + glsafe(::glGetProgramiv(m_id, GL_LINK_STATUS, ¶ms)); + if (params == GL_FALSE) { + // Linking failed. + glsafe(::glGetProgramiv(m_id, GL_INFO_LOG_LENGTH, ¶ms)); + std::vector msg(params); + glsafe(::glGetProgramInfoLog(m_id, params, ¶ms, msg.data())); + BOOST_LOG_TRIVIAL(error) << "Unable to link shader program '" << name << "':\n" << msg.data(); + + // release shaders + release_shaders(shader_ids); + + // release shader program + glsafe(::glDeleteProgram(m_id)); + m_id = 0; + + return false; + } + + // release shaders, they are no more needed + release_shaders(shader_ids); + + return true; +} + +bool GLShaderProgram::start_using() const +{ + if (m_id == 0) + return false; + + glsafe(::glUseProgram(m_id)); + return true; +} + +void GLShaderProgram::stop_using() const +{ + glsafe(::glUseProgram(0)); +} + +bool GLShaderProgram::set_uniform(const char* name, int value) const +{ + int id = get_uniform_location(name); + if (id >= 0) { + glsafe(::glUniform1i(id, static_cast(value))); + return true; + } + return false; +} + +bool GLShaderProgram::set_uniform(const char* name, bool value) const +{ + return set_uniform(name, value ? 1 : 0); +} + +bool GLShaderProgram::set_uniform(const char* name, float value) const +{ + int id = get_uniform_location(name); + if (id >= 0) { + glsafe(::glUniform1f(id, static_cast(value))); + return true; + } + return false; +} + +bool GLShaderProgram::set_uniform(const char* name, const std::array& value) const +{ + int id = get_uniform_location(name); + if (id >= 0) { + glsafe(::glUniform3fv(id, 1, static_cast(value.data()))); + return true; + } + return false; +} + +bool GLShaderProgram::set_uniform(const char* name, const std::array& value) const +{ + int id = get_uniform_location(name); + if (id >= 0) { + glsafe(::glUniform4fv(id, 1, static_cast(value.data()))); + return true; + } + return false; +} + +bool GLShaderProgram::set_uniform(const char* name, const float* value, size_t size) const +{ + if (size == 1) + return set_uniform(name, value[0]); + else if (size < 5) + { + int id = get_uniform_location(name); + if (id >= 0) { + if (size == 2) + glsafe(::glUniform2fv(id, 1, static_cast(value))); + else if (size == 3) + glsafe(::glUniform3fv(id, 1, static_cast(value))); + else + glsafe(::glUniform4fv(id, 1, static_cast(value))); + + return true; + } + } + return false; +} + +bool GLShaderProgram::set_uniform(const char* name, const Transform3f& value) const +{ + int id = get_uniform_location(name); + if (id >= 0) { + glsafe(::glUniformMatrix4fv(id, 1, GL_FALSE, static_cast(value.matrix().data()))); + return true; + } + return false; +} + +bool GLShaderProgram::set_uniform(const char* name, const Transform3d& value) const +{ + return set_uniform(name, value.cast()); +} + +int GLShaderProgram::get_attrib_location(const char* name) const +{ + return (m_id > 0) ? ::glGetAttribLocation(m_id, name) : -1; +} + +int GLShaderProgram::get_uniform_location(const char* name) const +{ + return (m_id > 0) ? ::glGetUniformLocation(m_id, name) : -1; +} + +} // namespace Slic3r +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include #include #include @@ -364,3 +642,6 @@ void Shader::reset() } } // namespace Slic3r +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ diff --git a/src/slic3r/GUI/GLShader.hpp b/src/slic3r/GUI/GLShader.hpp index df2a23f15c..a4a8d32cb3 100644 --- a/src/slic3r/GUI/GLShader.hpp +++ b/src/slic3r/GUI/GLShader.hpp @@ -1,6 +1,64 @@ #ifndef slic3r_GLShader_hpp_ #define slic3r_GLShader_hpp_ +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER +#include +#include + +namespace Slic3r { + +class GLShaderProgram +{ +public: + enum class EShaderType + { + Vertex, + Fragment, + Geometry, + TessEvaluation, + TessControl, + Compute, + Count + }; + + typedef std::array(EShaderType::Count)> ShaderFilenames; + typedef std::array(EShaderType::Count)> ShaderSources; + +private: + std::string m_name; + unsigned int m_id{ 0 }; + +public: + ~GLShaderProgram(); + + bool init_from_files(const std::string& name, const ShaderFilenames& filenames); + bool init_from_texts(const std::string& name, const ShaderSources& sources); + + const std::string& get_name() const { return m_name; } + unsigned int get_id() const { return m_id; } + + bool start_using() const; + void stop_using() const; + + bool set_uniform(const char* name, int value) const; + bool set_uniform(const char* name, bool value) const; + bool set_uniform(const char* name, float value) const; + bool set_uniform(const char* name, const std::array& value) const; + bool set_uniform(const char* name, const std::array& value) const; + bool set_uniform(const char* name, const float* value, size_t size) const; + bool set_uniform(const char* name, const Transform3f& value) const; + bool set_uniform(const char* name, const Transform3d& value) const; + + // returns -1 if not found + int get_attrib_location(const char* name) const; + // returns -1 if not found + int get_uniform_location(const char* name) const; +}; + +} // namespace Slic3r +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "libslic3r/libslic3r.h" #include "libslic3r/Point.hpp" @@ -67,5 +125,8 @@ private: }; } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif /* slic3r_GLShader_hpp_ */ diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp new file mode 100644 index 0000000000..e4d6407224 --- /dev/null +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -0,0 +1,76 @@ +#include "libslic3r/libslic3r.h" +#include "GLShadersManager.hpp" + +#include +#include + +#if ENABLE_SHADERS_MANAGER + +namespace Slic3r { + +std::pair GLShadersManager::init() +{ + std::string error; + + auto append_shader = [this, &error](const std::string& name, const GLShaderProgram::ShaderFilenames& filenames) { + m_shaders.push_back(std::make_unique()); + if (!m_shaders.back()->init_from_files(name, filenames)) { + error += name + "\n"; + // if any error happens while initializating the shader, we remove it from the list + m_shaders.pop_back(); + return false; + } + return true; + }; + + assert(m_shaders.empty()); + + bool valid = true; + + // used to render bed axes, selection hints + valid &= append_shader("gouraud_light", { "gouraud_light.vs", "gouraud_light.fs" }); + // used to render printbed + valid &= append_shader("printbed", { "printbed.vs", "printbed.fs" }); + // used to render tool changes in gcode preview + valid &= append_shader("toolchanges", { "toolchanges.vs", "toolchanges.fs" }); + // used to render color changes in gcode preview + valid &= append_shader("colorchanges", { "colorchanges.vs", "colorchanges.fs" }); + // used to render pause prints in gcode preview + valid &= append_shader("pauses", { "pauses.vs", "pauses.fs" }); + // used to render custom gcode points in gcode preview + valid &= append_shader("customs", { "customs.vs", "customs.fs" }); + // used to render retractions in gcode preview + valid &= append_shader("retractions", { "retractions.vs", "retractions.fs" }); + // used to render unretractions in gcode preview + valid &= append_shader("unretractions", { "unretractions.vs", "unretractions.fs" }); + // used to render extrusion paths in gcode preview + valid &= append_shader("extrusions", { "extrusions.vs", "extrusions.fs" }); + // used to render travel paths in gcode preview + valid &= append_shader("travels", { "travels.vs", "travels.fs" }); + // used to render shells in gcode preview + valid &= append_shader("shells", { "shells.vs", "shells.fs" }); + // used to render objects in 3d editor + valid &= append_shader("gouraud", { "gouraud.vs", "gouraud.fs" }); + // used to render variable layers heights in 3d editor + valid &= append_shader("variable_layer_height", { "variable_layer_height.vs", "variable_layer_height.fs" }); + + return { valid, error }; +} + +void GLShadersManager::shutdown() +{ + for (std::unique_ptr& shader : m_shaders) + { + shader.reset(); + } +} + +GLShaderProgram* GLShadersManager::get_shader(const std::string& shader_name) +{ + auto it = std::find_if(m_shaders.begin(), m_shaders.end(), [shader_name](std::unique_ptr& p) { return p->get_name() == shader_name; }); + return (it != m_shaders.end()) ? it->get() : nullptr; +} + +} // namespace Slic3r + +#endif // ENABLE_SHADERS_MANAGER diff --git a/src/slic3r/GUI/GLShadersManager.hpp b/src/slic3r/GUI/GLShadersManager.hpp new file mode 100644 index 0000000000..0c624f9161 --- /dev/null +++ b/src/slic3r/GUI/GLShadersManager.hpp @@ -0,0 +1,31 @@ +#ifndef slic3r_GLShadersManager_hpp_ +#define slic3r_GLShadersManager_hpp_ + +#if ENABLE_SHADERS_MANAGER + +#include "GLShader.hpp" + +#include +#include +#include + +namespace Slic3r { + +class GLShadersManager +{ + std::vector> m_shaders; + +public: + std::pair init(); + // call this method before to release the OpenGL context + void shutdown(); + + // returns nullptr if not found + GLShaderProgram* get_shader(const std::string& shader_name); +}; + +} // namespace Slic3r + +#endif // ENABLE_SHADERS_MANAGER + +#endif // slic3r_GLShadersManager_hpp_ diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 984a1241e7..d0c1624d7c 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -217,6 +217,16 @@ public: void gcode_thumbnails_debug(); #endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + // returns nullptr if not found + GLShaderProgram* get_shader(const std::string& shader_name) { return m_opengl_mgr.get_shader(shader_name); } + +// // returns 0 if not found +// unsigned int get_shader_id(const std::string& shader_name) const { return m_opengl_mgr.get_shader_id(shader_name); } +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + private: bool on_init_inner(); void init_app_config(); diff --git a/src/slic3r/GUI/OpenGLManager.cpp b/src/slic3r/GUI/OpenGLManager.cpp index bdb005b1e2..e4674fffd1 100644 --- a/src/slic3r/GUI/OpenGLManager.cpp +++ b/src/slic3r/GUI/OpenGLManager.cpp @@ -28,6 +28,17 @@ namespace Slic3r { namespace GUI { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER +// A safe wrapper around glGetString to report a "N/A" string in case glGetString returns nullptr. +inline std::string gl_get_string_safe(GLenum param, const std::string& default_value) +{ + const char* value = (const char*)::glGetString(param); + return std::string((value != nullptr) ? value : default_value); +} +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + const std::string& OpenGLManager::GLInfo::get_version() const { if (!m_detected) @@ -85,6 +96,14 @@ float OpenGLManager::GLInfo::get_max_anisotropy() const void OpenGLManager::GLInfo::detect() const { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + m_version = gl_get_string_safe(GL_VERSION, "N/A"); + m_glsl_version = gl_get_string_safe(GL_SHADING_LANGUAGE_VERSION, "N/A"); + m_vendor = gl_get_string_safe(GL_VENDOR, "N/A"); + m_renderer = gl_get_string_safe(GL_RENDERER, "N/A"); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ const char* data = (const char*)::glGetString(GL_VERSION); if (data != nullptr) m_version = data; @@ -100,6 +119,9 @@ void OpenGLManager::GLInfo::detect() const data = (const char*)::glGetString(GL_RENDERER); if (data != nullptr) m_renderer = data; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_max_tex_size)); @@ -119,6 +141,13 @@ bool OpenGLManager::GLInfo::is_version_greater_or_equal_to(unsigned int major, u if (!m_detected) detect(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + if (m_version == "N/A") + return false; +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + std::vector tokens; boost::split(tokens, m_version, boost::is_any_of(" "), boost::token_compress_on); @@ -159,15 +188,34 @@ std::string OpenGLManager::GLInfo::to_string(bool format_as_html, bool extension std::string line_end = format_as_html ? "
" : "\n"; out << h2_start << "OpenGL installation" << h2_end << line_end; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + out << b_start << "GL version: " << b_end << m_version << line_end; + out << b_start << "Vendor: " << b_end << m_vendor << line_end; + out << b_start << "Renderer: " << b_end << m_renderer << line_end; + out << b_start << "GLSL version: " << b_end << m_glsl_version << line_end; +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ out << b_start << "GL version: " << b_end << (m_version.empty() ? "N/A" : m_version) << line_end; out << b_start << "Vendor: " << b_end << (m_vendor.empty() ? "N/A" : m_vendor) << line_end; out << b_start << "Renderer: " << b_end << (m_renderer.empty() ? "N/A" : m_renderer) << line_end; out << b_start << "GLSL version: " << b_end << (m_glsl_version.empty() ? "N/A" : m_glsl_version) << line_end; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (extensions) { std::vector extensions_list; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + std::string extensions_str = gl_get_string_safe(GL_EXTENSIONS, ""); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ std::string extensions_str = (const char*)::glGetString(GL_EXTENSIONS); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ boost::split(extensions_list, extensions_str, boost::is_any_of(" "), boost::token_compress_off); if (!extensions_list.empty()) @@ -199,6 +247,12 @@ OpenGLManager::OSInfo OpenGLManager::s_os_info; OpenGLManager::~OpenGLManager() { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + m_shaders_manager.shutdown(); +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + #if ENABLE_HACK_CLOSING_ON_OSX_10_9_5 #ifdef __APPLE__ // This is an ugly hack needed to solve the crash happening when closing the application on OSX 10.9.5 with newer wxWidgets @@ -240,19 +294,43 @@ bool OpenGLManager::init_gl() else s_framebuffers_type = EFramebufferType::Unknown; - if (! s_gl_info.is_version_greater_or_equal_to(2, 0)) { - // Complain about the OpenGL version. +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + bool valid_version = s_gl_info.is_version_greater_or_equal_to(2, 0); + if (!valid_version) { +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + if (!s_gl_info.is_version_greater_or_equal_to(2, 0)) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + + // Complain about the OpenGL version. wxString message = from_u8((boost::format( _utf8(L("PrusaSlicer requires OpenGL 2.0 capable graphics driver to run correctly, \n" "while OpenGL version %s, render %s, vendor %s was detected."))) % s_gl_info.get_version() % s_gl_info.get_renderer() % s_gl_info.get_vendor()).str()); - message += "\n"; + message += "\n"; message += _L("You may need to update your graphics card driver."); #ifdef _WIN32 - message += "\n"; + message += "\n"; message += _L("As a workaround, you may run PrusaSlicer with a software rendered 3D graphics by running prusa-slicer.exe with the --sw_renderer parameter."); #endif wxMessageBox(message, wxString("PrusaSlicer - ") + _L("Unsupported OpenGL version"), wxOK | wxICON_ERROR); } + +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + if (valid_version) { + // load shaders + auto [result, error] = m_shaders_manager.init(); + if (!result) { + wxString message = from_u8((boost::format( + _utf8(L("Unable to load the following shaders:\n%s"))) % error).str()); + wxMessageBox(message, wxString("PrusaSlicer - ") + _L("Error loading shaders"), wxOK | wxICON_ERROR); + } + } +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } return true; diff --git a/src/slic3r/GUI/OpenGLManager.hpp b/src/slic3r/GUI/OpenGLManager.hpp index 9d7ee5babb..c80deb6ef1 100644 --- a/src/slic3r/GUI/OpenGLManager.hpp +++ b/src/slic3r/GUI/OpenGLManager.hpp @@ -1,6 +1,12 @@ #ifndef slic3r_OpenGLManager_hpp_ #define slic3r_OpenGLManager_hpp_ +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER +#include "GLShadersManager.hpp" +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + class wxWindow; class wxGLCanvas; class wxGLContext; @@ -70,6 +76,11 @@ private: bool m_gl_initialized{ false }; wxGLContext* m_context{ nullptr }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + GLShadersManager m_shaders_manager; +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ static GLInfo s_gl_info; #if ENABLE_HACK_CLOSING_ON_OSX_10_9_5 #ifdef __APPLE__ @@ -86,9 +97,18 @@ public: ~OpenGLManager(); bool init_gl(); - wxGLContext* init_glcontext(wxGLCanvas& canvas); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + // returns nullptr if not found + GLShaderProgram* get_shader(const std::string& shader_name) { return m_shaders_manager.get_shader(shader_name); } + +// // returns 0 if not found +// unsigned int get_shader_id(const std::string& shader_name) const { return m_shaders_manager.get_shader_id(shader_name); } +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + static bool are_compressed_textures_supported() { return s_compressed_textures_supported; } static bool can_multisample() { return s_multisample == EMultisampleState::Enabled; } static bool are_framebuffers_supported() { return (s_framebuffers_type != EFramebufferType::Unknown); } diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 3bab06b4d5..982a16bf00 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -8,6 +8,11 @@ #include "GUI_ObjectList.hpp" #include "Gizmos/GLGizmoBase.hpp" #include "3DScene.hpp" +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//#if ENABLE_SHADERS_MANAGER +//#include "GLShader.hpp" +//#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "Camera.hpp" #include @@ -113,11 +118,17 @@ bool Selection::init() m_arrow.init_from(straight_arrow(10.0f, 5.0f, 5.0f, 10.0f, 1.0f)); m_curved_arrow.init_from(circular_arrow(16, 10.0f, 5.0f, 10.0f, 5.0f, 1.0f)); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (!m_arrows_shader.init("gouraud_light.vs", "gouraud_light.fs")) { BOOST_LOG_TRIVIAL(error) << "Unable to initialize gouraud_light shader: please, check that the files gouraud_light.vs and gouraud_light.fs are available"; return false; } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #else if (!m_arrow.init()) return false; @@ -1253,13 +1264,31 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field, const Sha if (sidebar_field.empty()) return; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + GLShaderProgram* shader = nullptr; +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + if (!boost::starts_with(sidebar_field, "layer")) { #if ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader = wxGetApp().get_shader("gouraud_light"); + if (shader == nullptr) + return; + + shader->start_using(); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (!m_arrows_shader.is_initialized()) return; m_arrows_shader.start_using(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); #else shader.start_using(); @@ -1322,13 +1351,46 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field, const Sha } if (boost::starts_with(sidebar_field, "position")) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + render_sidebar_position_hints(sidebar_field, *shader); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ render_sidebar_position_hints(sidebar_field); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ else if (boost::starts_with(sidebar_field, "rotation")) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + render_sidebar_rotation_hints(sidebar_field, *shader); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ render_sidebar_rotation_hints(sidebar_field); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_GCODE_VIEWER + else if (boost::starts_with(sidebar_field, "scale") || boost::starts_with(sidebar_field, "size")) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER +render_sidebar_scale_hints(sidebar_field, *shader); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + render_sidebar_scale_hints(sidebar_field); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ else if (boost::starts_with(sidebar_field, "scale")) render_sidebar_scale_hints(sidebar_field); else if (boost::starts_with(sidebar_field, "size")) render_sidebar_size_hints(sidebar_field); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ else if (boost::starts_with(sidebar_field, "layer")) render_sidebar_layers_hints(sidebar_field); @@ -1337,7 +1399,15 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field, const Sha if (!boost::starts_with(sidebar_field, "layer")) { #if ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader->stop_using(); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_arrows_shader.stop_using(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #else glsafe(::glDisable(GL_LIGHTING)); shader.stop_using(); @@ -1942,35 +2012,76 @@ void Selection::render_bounding_box(const BoundingBoxf3& box, float* color) cons glsafe(::glEnd()); } -void Selection::render_sidebar_position_hints(const std::string& sidebar_field) const -{ #if ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER +void Selection::render_sidebar_position_hints(const std::string& sidebar_field, GLShaderProgram& shader) const +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +void Selection::render_sidebar_position_hints(const std::string& sidebar_field) const +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +{ +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GLint color_id = ::glGetUniformLocation(m_arrows_shader.get_shader_program_id(), "uniform_color"); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (boost::ends_with(sidebar_field, "x")) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader.set_uniform("uniform_color", AXES_COLOR[0], 4); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[0])); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glRotated(-90.0, 0.0, 0.0, 1.0)); m_arrow.render(); } else if (boost::ends_with(sidebar_field, "y")) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader.set_uniform("uniform_color", AXES_COLOR[1], 4); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[1])); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_arrow.render(); } else if (boost::ends_with(sidebar_field, "z")) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader.set_uniform("uniform_color", AXES_COLOR[2], 4); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[2])); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glRotated(90.0, 1.0, 0.0, 0.0)); m_arrow.render(); } +} #else +void Selection::render_sidebar_position_hints(const std::string & sidebar_field) const +{ if (boost::ends_with(sidebar_field, "x")) { glsafe(::glRotated(-90.0, 0.0, 0.0, 1.0)); @@ -1983,38 +2094,79 @@ void Selection::render_sidebar_position_hints(const std::string& sidebar_field) glsafe(::glRotated(90.0, 1.0, 0.0, 0.0)); render_sidebar_position_hint(Z); } -#endif // ENABLE_GCODE_VIEWER } +#endif // ENABLE_GCODE_VIEWER -void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field) const -{ #if ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER +void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field, GLShaderProgram& shader) const +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field) const +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +{ +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GLint color_id = ::glGetUniformLocation(m_arrows_shader.get_shader_program_id(), "uniform_color"); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (boost::ends_with(sidebar_field, "x")) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader.set_uniform("uniform_color", AXES_COLOR[0], 4); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[0])); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glRotated(90.0, 0.0, 1.0, 0.0)); render_sidebar_rotation_hint(X); } else if (boost::ends_with(sidebar_field, "y")) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader.set_uniform("uniform_color", AXES_COLOR[1], 4); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[1])); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0)); render_sidebar_rotation_hint(Y); } else if (boost::ends_with(sidebar_field, "z")) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader.set_uniform("uniform_color", AXES_COLOR[2], 4); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[2])); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ render_sidebar_rotation_hint(Z); } +} #else +void Selection::render_sidebar_rotation_hints(const std::string & sidebar_field) const +{ if (boost::ends_with(sidebar_field, "x")) { glsafe(::glRotated(90.0, 0.0, 1.0, 0.0)); @@ -2027,37 +2179,74 @@ void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field) } else if (boost::ends_with(sidebar_field, "z")) render_sidebar_rotation_hint(Z); -#endif // ENABLE_GCODE_VIEWER } +#endif // ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER +void Selection::render_sidebar_scale_hints(const std::string& sidebar_field, GLShaderProgram& shader) const +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void Selection::render_sidebar_scale_hints(const std::string& sidebar_field) const +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { bool uniform_scale = requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling(); -#if ENABLE_GCODE_VIEWER if (boost::ends_with(sidebar_field, "x") || uniform_scale) { glsafe(::glPushMatrix()); glsafe(::glRotated(-90.0, 0.0, 0.0, 1.0)); - render_sidebar_scale_hint(X); - glsafe(::glPopMatrix()); - } - - if (boost::ends_with(sidebar_field, "y") || uniform_scale) - { - glsafe(::glPushMatrix()); - render_sidebar_scale_hint(Y); - glsafe(::glPopMatrix()); - } - - if (boost::ends_with(sidebar_field, "z") || uniform_scale) - { - glsafe(::glPushMatrix()); - glsafe(::glRotated(90.0, 1.0, 0.0, 0.0)); - render_sidebar_scale_hint(Z); - glsafe(::glPopMatrix()); - } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + render_sidebar_scale_hint(X, shader); #else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + render_sidebar_scale_hint(X); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + glsafe(::glPopMatrix()); + } + + if (boost::ends_with(sidebar_field, "y") || uniform_scale) + { + glsafe(::glPushMatrix()); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + render_sidebar_scale_hint(Y, shader); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + render_sidebar_scale_hint(Y); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + glsafe(::glPopMatrix()); + } + + if (boost::ends_with(sidebar_field, "z") || uniform_scale) + { + glsafe(::glPushMatrix()); + glsafe(::glRotated(90.0, 1.0, 0.0, 0.0)); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + render_sidebar_scale_hint(Z, shader); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + render_sidebar_scale_hint(Z); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + glsafe(::glPopMatrix()); + } +} +#else +void Selection::render_sidebar_scale_hints(const std::string & sidebar_field) const +{ + bool uniform_scale = requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling(); + if (boost::ends_with(sidebar_field, "x") || uniform_scale) { glsafe(::glPushMatrix()); @@ -2080,13 +2269,19 @@ void Selection::render_sidebar_scale_hints(const std::string& sidebar_field) con render_sidebar_scale_hint(Z); glsafe(::glPopMatrix()); } -#endif // ENABLE_GCODE_VIEWER } +#endif // ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void Selection::render_sidebar_size_hints(const std::string& sidebar_field) const { render_sidebar_scale_hints(sidebar_field); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) const { @@ -2178,13 +2373,29 @@ void Selection::render_sidebar_rotation_hint(Axis axis) const m_curved_arrow.render(); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER +void Selection::render_sidebar_scale_hint(Axis axis, GLShaderProgram& shader) const +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void Selection::render_sidebar_scale_hint(Axis axis) const +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { #if ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + shader.set_uniform("uniform_color", (requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling()) ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis], 4); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GLint color_id = ::glGetUniformLocation(m_arrows_shader.get_shader_program_id(), "uniform_color"); if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling()) ? (const GLfloat*)UNIFORM_SCALE_COLOR : (const GLfloat*)AXES_COLOR[axis])); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #else m_arrow.set_color(((requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling()) ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis]), 3); #endif // ENABLE_GCODE_VIEWER diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 53caf11060..7411fc97e5 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -6,7 +6,13 @@ #include "3DScene.hpp" #if ENABLE_GCODE_VIEWER #include "GLModel.hpp" +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "GLShader.hpp" +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER #if ENABLE_RENDER_SELECTION_CENTER @@ -15,7 +21,15 @@ typedef class GLUquadric GLUquadricObj; #endif // ENABLE_RENDER_SELECTION_CENTER namespace Slic3r { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER +class GLShaderProgram; +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ class Shader; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ namespace GUI { class TransformationType { @@ -207,7 +221,13 @@ private: #if ENABLE_GCODE_VIEWER GL_Model m_arrow; GL_Model m_curved_arrow; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Shader m_arrows_shader; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #else mutable GLArrow m_arrow; mutable GLCurvedArrow m_curved_arrow; @@ -368,16 +388,40 @@ private: void render_selected_volumes() const; void render_synchronized_volumes() const; void render_bounding_box(const BoundingBoxf3& box, float* color) const; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + void render_sidebar_position_hints(const std::string& sidebar_field, GLShaderProgram& shader) const; + void render_sidebar_rotation_hints(const std::string& sidebar_field, GLShaderProgram& shader) const; + void render_sidebar_scale_hints(const std::string& sidebar_field, GLShaderProgram& shader) const; +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void render_sidebar_position_hints(const std::string& sidebar_field) const; void render_sidebar_rotation_hints(const std::string& sidebar_field) const; void render_sidebar_scale_hints(const std::string& sidebar_field) const; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void render_sidebar_size_hints(const std::string& sidebar_field) const; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void render_sidebar_layers_hints(const std::string& sidebar_field) const; #if !ENABLE_GCODE_VIEWER void render_sidebar_position_hint(Axis axis) const; #endif // !ENABLE_GCODE_VIEWER void render_sidebar_rotation_hint(Axis axis) const; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_SHADERS_MANAGER + void render_sidebar_scale_hint(Axis axis, GLShaderProgram& shader) const; +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void render_sidebar_scale_hint(Axis axis) const; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_SHADERS_MANAGER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_GCODE_VIEWER void render_sidebar_size_hint(Axis axis, double length) const; #endif // !ENABLE_GCODE_VIEWER From cbfb09a241eeca1337a85bbb361518c304e29895 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 20 May 2020 17:03:53 +0200 Subject: [PATCH 082/503] Fixed build for all 4 cases of tech ENABLE_SHADERS_MANAGER and ENABLE_GCODE_VIEWER enabled/disabled and code cleanup --- src/libslic3r/GCode/ToolOrdering.cpp | 4 +- src/libslic3r/Technologies.hpp | 6 +- src/slic3r/GUI/3DBed.cpp | 29 ----- src/slic3r/GUI/3DBed.hpp | 12 -- src/slic3r/GUI/GCodeViewer.cpp | 81 ------------- src/slic3r/GUI/GCodeViewer.hpp | 32 ----- src/slic3r/GUI/GLCanvas3D.cpp | 68 +---------- src/slic3r/GUI/GLCanvas3D.hpp | 20 ---- src/slic3r/GUI/GLShader.cpp | 7 +- src/slic3r/GUI/GLShader.hpp | 6 +- src/slic3r/GUI/GUI_App.hpp | 5 - src/slic3r/GUI/OpenGLManager.cpp | 24 ---- src/slic3r/GUI/OpenGLManager.hpp | 9 -- src/slic3r/GUI/Selection.cpp | 171 +++++++-------------------- src/slic3r/GUI/Selection.hpp | 53 ++++----- 15 files changed, 72 insertions(+), 455 deletions(-) diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 9c1a1900fd..db398f06c8 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -400,9 +400,7 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_ // and maybe other problems. We will therefore go through layer_tools and detect and fix this. // So, if there is a non-object layer starting with different extruder than the last one ended with (or containing more than one extruder), // we'll mark it with has_wipe tower. -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -// assert(! m_layer_tools.empty() && m_layer_tools.front().has_wipe_tower); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + assert(! m_layer_tools.empty() && m_layer_tools.front().has_wipe_tower); if (! m_layer_tools.empty() && m_layer_tools.front().has_wipe_tower) { for (size_t i = 0; i + 1 < m_layer_tools.size();) { const LayerTools < = m_layer_tools[i]; diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index c274c1e841..d0673e2b4b 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -44,9 +44,9 @@ // Enable G-Code viewer #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#define ENABLE_SHADERS_MANAGER (1 && ENABLE_GCODE_VIEWER) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) +// Enable the OpenGL shaders manager +#define ENABLE_SHADERS_MANAGER (1 && ENABLE_2_3_0_ALPHA1) + #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 4bf8ce900e..73162645fc 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -15,11 +15,6 @@ #if ENABLE_GCODE_VIEWER #include "3DScene.hpp" #endif // ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -//#if ENABLE_SHADERS_MANAGER -//#include "GLShader.hpp" -//#endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include @@ -168,17 +163,13 @@ Bed3D::Axes::~Axes() void Bed3D::Axes::render() const { #if ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER auto render_axis = [this](const Transform3f& transform) { #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ auto render_axis = [this](const Transform3f& transform, GLint color_id, const std::array& color) { if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)color.data())); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glPushMatrix()); glsafe(::glMultMatrixf(transform.data())); @@ -187,25 +178,20 @@ void Bed3D::Axes::render() const }; m_arrow.init_from(stilized_arrow(16, DefaultTipRadius, DefaultTipLength, DefaultStemRadius, m_stem_length)); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) return; #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (!m_shader.init("gouraud_light.vs", "gouraud_light.fs")) BOOST_LOG_TRIVIAL(error) << "Unable to initialize gouraud_light shader: please, check that the files gouraud_light.vs and gouraud_light.fs are available"; if (!m_shader.is_initialized()) return; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glEnable(GL_DEPTH_TEST)); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader->start_using(); @@ -223,7 +209,6 @@ void Bed3D::Axes::render() const shader->stop_using(); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_shader.start_using(); GLint color_id = ::glGetUniformLocation(m_shader.get_shader_program_id(), "uniform_color"); @@ -237,9 +222,7 @@ void Bed3D::Axes::render() const render_axis(Geometry::assemble_transform(m_origin).cast(), color_id, { 0.0f, 0.0f, 0.75f, 1.0f }); m_shader.stop_using(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glDisable(GL_DEPTH_TEST)); #else @@ -585,7 +568,6 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const if (m_triangles.get_vertices_count() > 0) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER GLShaderProgram* shader = wxGetApp().get_shader("printbed"); if (shader != nullptr) @@ -594,7 +576,6 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const shader->set_uniform("transparent_background", bottom); shader->set_uniform("svg_source", boost::algorithm::iends_with(m_texture.get_source(), ".svg")); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (m_shader.get_shader_program_id() == 0) m_shader.init("printbed.vs", "printbed.fs"); @@ -603,9 +584,7 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const m_shader.start_using(); m_shader.set_uniform("transparent_background", bottom); m_shader.set_uniform("svg_source", boost::algorithm::iends_with(m_texture.get_source(), ".svg")); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (m_vbo_id == 0) { @@ -626,17 +605,13 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const unsigned int stride = m_triangles.get_vertex_data_size(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER GLint position_id = shader->get_attrib_location("v_position"); GLint tex_coords_id = shader->get_attrib_location("v_tex_coords"); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GLint position_id = m_shader.get_attrib_location("v_position"); GLint tex_coords_id = m_shader.get_attrib_location("v_tex_coords"); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // show the temporary texture while no compressed data is available GLuint tex_id = (GLuint)m_temp_texture.get_id(); @@ -674,15 +649,11 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const glsafe(::glDisable(GL_BLEND)); glsafe(::glDepthMask(GL_TRUE)); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader->stop_using(); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_shader.stop_using(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } } } diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index 9603015da8..d9a2c82620 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -3,13 +3,9 @@ #include "GLTexture.hpp" #include "3DScene.hpp" -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "GLShader.hpp" -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER #include "GLModel.hpp" #endif // ENABLE_GCODE_VIEWER @@ -75,13 +71,9 @@ class Bed3D Vec3d m_origin{ Vec3d::Zero() }; float m_stem_length{ DefaultStemLength }; mutable GL_Model m_arrow; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ mutable Shader m_shader; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ public: #else @@ -130,13 +122,9 @@ private: mutable GLBed m_model; // temporary texture shown until the main texture has still no levels compressed mutable GLTexture m_temp_texture; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ mutable Shader m_shader; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ mutable unsigned int m_vbo_id; Axes m_axes; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 55f603829b..a584dc7eb1 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -12,11 +12,6 @@ #include "DoubleSlider.hpp" #include "GLCanvas3D.hpp" #include "GLToolbar.hpp" -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -//#if ENABLE_SHADERS_MANAGER -//#include "GLShader.hpp" -//#endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "GUI_Preview.hpp" #include "libslic3r/Model.hpp" #if ENABLE_GCODE_VIEWER_STATISTICS @@ -112,9 +107,7 @@ void GCodeViewer::IBuffer::reset() render_paths = std::vector(); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool GCodeViewer::IBuffer::init_shader(const std::string& vertex_shader_src, const std::string& fragment_shader_src) { if (!shader.init(vertex_shader_src, fragment_shader_src)) { @@ -124,9 +117,7 @@ bool GCodeViewer::IBuffer::init_shader(const std::string& vertex_shader_src, con return true; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void GCodeViewer::IBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsigned int i_id, unsigned int s_id) { @@ -160,18 +151,13 @@ GCodeViewer::Color GCodeViewer::Extrusions::Range::get_color_at(float value) con void GCodeViewer::SequentialView::Marker::init() { m_model.init_from(stilized_arrow(16, 2.0f, 4.0f, 1.0f, 8.0f)); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ init_shader(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } void GCodeViewer::SequentialView::Marker::render() const { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER if (!m_visible) return; @@ -186,7 +172,6 @@ void GCodeViewer::SequentialView::Marker::render() const shader->start_using(); shader->set_uniform("uniform_color", m_color); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (!m_visible || !m_shader.is_initialized()) return; @@ -197,9 +182,7 @@ void GCodeViewer::SequentialView::Marker::render() const GLint color_id = ::glGetUniformLocation(m_shader.get_shader_program_id(), "uniform_color"); if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)m_color.data())); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glPushMatrix()); glsafe(::glMultMatrixf(m_world_transform.data())); @@ -208,30 +191,22 @@ void GCodeViewer::SequentialView::Marker::render() const glsafe(::glPopMatrix()); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader->stop_using(); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_shader.stop_using(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glDisable(GL_BLEND)); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void GCodeViewer::SequentialView::Marker::init_shader() { if (!m_shader.init("gouraud_light.vs", "gouraud_light.fs")) BOOST_LOG_TRIVIAL(error) << "Unable to initialize gouraud_light shader: please, check that the files gouraud_light.vs and gouraud_light.fs are available"; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ const std::vector GCodeViewer::Extrusion_Role_Colors {{ { 0.50f, 0.50f, 0.50f }, // erNone @@ -440,7 +415,6 @@ void GCodeViewer::set_layers_z_range(const std::array& layers_z_range wxGetApp().plater()->update_preview_moves_slider(); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER void GCodeViewer::init_shaders() { @@ -464,7 +438,6 @@ void GCodeViewer::init_shaders() } } #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool GCodeViewer::init_shaders() { unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); @@ -499,9 +472,7 @@ bool GCodeViewer::init_shaders() return true; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) { @@ -828,9 +799,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool void GCodeViewer::render_toolpaths() const { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ auto set_color = [](GLint current_program_id, const Color& color) { if (current_program_id > 0) { GLint color_id = ::glGetUniformLocation(current_program_id, "uniform_color"); @@ -841,9 +810,7 @@ void GCodeViewer::render_toolpaths() const } BOOST_LOG_TRIVIAL(error) << "Unable to find uniform_color uniform"; }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glCullFace(GL_BACK)); glsafe(::glLineWidth(3.0f)); @@ -863,28 +830,20 @@ void GCodeViewer::render_toolpaths() const if (!buffer.visible) continue; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER GLShaderProgram* shader = wxGetApp().get_shader(buffer.shader.c_str()); if (shader != nullptr) { #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (buffer.shader.is_initialized()) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GCodeProcessor::EMoveType type = buffer_type(i); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader->start_using(); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ buffer.shader.start_using(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.ibo_id)); @@ -893,15 +852,11 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Tool_change: { Color color = { 1.0f, 1.0f, 1.0f }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", color); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ set_color(static_cast(buffer.shader.get_shader_program_id()), color); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ for (const RenderPath& path : buffer.render_paths) { glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -917,15 +872,11 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Color_change: { Color color = { 1.0f, 0.0f, 0.0f }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", color); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ set_color(static_cast(buffer.shader.get_shader_program_id()), color); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ for (const RenderPath& path : buffer.render_paths) { glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -941,15 +892,11 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Pause_Print: { Color color = { 0.0f, 1.0f, 0.0f }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", color); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ set_color(static_cast(buffer.shader.get_shader_program_id()), color); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ for (const RenderPath& path : buffer.render_paths) { glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -965,15 +912,11 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Custom_GCode: { Color color = { 0.0f, 0.0f, 1.0f }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", color); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ set_color(static_cast(buffer.shader.get_shader_program_id()), color); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ for (const RenderPath& path : buffer.render_paths) { glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -989,15 +932,11 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Retract: { Color color = { 1.0f, 0.0f, 1.0f }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", color); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ set_color(static_cast(buffer.shader.get_shader_program_id()), color); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ for (const RenderPath& path : buffer.render_paths) { glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -1013,15 +952,11 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Unretract: { Color color = { 0.0f, 1.0f, 1.0f }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", color); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ set_color(static_cast(buffer.shader.get_shader_program_id()), color); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ for (const RenderPath& path : buffer.render_paths) { glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -1038,15 +973,11 @@ void GCodeViewer::render_toolpaths() const { for (const RenderPath& path : buffer.render_paths) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", path.color); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ set_color(static_cast(buffer.shader.get_shader_program_id()), path.color); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glMultiDrawElements(GL_LINE_STRIP, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_line_strip_calls_count; @@ -1059,15 +990,11 @@ void GCodeViewer::render_toolpaths() const { for (const RenderPath& path : buffer.render_paths) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", path.color); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ set_color(static_cast(buffer.shader.get_shader_program_id()), path.color); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glMultiDrawElements(GL_LINE_STRIP, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_line_strip_calls_count; @@ -1079,15 +1006,11 @@ void GCodeViewer::render_toolpaths() const } glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader->stop_using(); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ buffer.shader.stop_using(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } } @@ -1097,7 +1020,6 @@ void GCodeViewer::render_toolpaths() const void GCodeViewer::render_shells() const { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER if (!m_shells.visible || m_shells.volumes.empty()) return; @@ -1114,7 +1036,6 @@ void GCodeViewer::render_shells() const // glsafe(::glDepthMask(GL_TRUE)); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (!m_shells.visible || m_shells.volumes.empty() || !m_shells.shader.is_initialized()) return; @@ -1125,9 +1046,7 @@ void GCodeViewer::render_shells() const m_shells.shader.stop_using(); // glsafe(::glDepthMask(GL_TRUE)); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } void GCodeViewer::render_legend() const diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index df28f4aa63..c1f9cfdd7b 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -2,13 +2,9 @@ #define slic3r_GCodeViewer_hpp_ #if ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "GLShader.hpp" -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "3DScene.hpp" #include "libslic3r/GCode/GCodeProcessor.hpp" #include "GLModel.hpp" @@ -84,27 +80,19 @@ class GCodeViewer { unsigned int ibo_id{ 0 }; size_t indices_count{ 0 }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER std::string shader; #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Shader shader; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ std::vector paths; std::vector render_paths; bool visible{ false }; void reset(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool init_shader(const std::string& vertex_shader_src, const std::string& fragment_shader_src); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void add_path(const GCodeProcessor::MoveVertex& move, unsigned int i_id, unsigned int s_id); }; @@ -113,13 +101,9 @@ class GCodeViewer { GLVolumeCollection volumes; bool visible{ false }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Shader shader; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ }; // helper to render extrusion paths @@ -229,13 +213,9 @@ public: Transform3f m_world_transform; std::array m_color{ 1.0f, 1.0f, 1.0f, 1.0f }; bool m_visible{ false }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Shader m_shader; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ public: void init(); @@ -250,14 +230,10 @@ public: void render() const; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ private: void init_shader(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ }; struct Endpoints @@ -311,16 +287,12 @@ public: bool init() { set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Extrude, true); m_sequential_view.marker.init(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER init_shaders(); return true; #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ return init_shaders(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } // extract rendering data from the given parameters @@ -364,15 +336,11 @@ public: void enable_legend(bool enable) { m_legend_enabled = enable; } private: -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER void init_shaders(); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool init_shaders(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void load_toolpaths(const GCodeProcessor::Result& gcode_result); void load_shells(const Print& print, bool initialized); void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index c1c310b34c..51079c892f 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -156,19 +156,15 @@ GLCanvas3D::LayersEditing::~LayersEditing() const float GLCanvas3D::LayersEditing::THICKNESS_BAR_WIDTH = 70.0f; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER void GLCanvas3D::LayersEditing::init() { #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename) { if (!m_shader.init(vertex_shader_filename, fragment_shader_filename)) return false; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glGenTextures(1, (GLuint*)&m_z_texture_id)); glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id)); @@ -179,13 +175,9 @@ bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1)); glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ return true; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } void GLCanvas3D::LayersEditing::set_config(const DynamicPrintConfig* config) @@ -218,15 +210,11 @@ void GLCanvas3D::LayersEditing::select_object(const Model &model, int object_id) bool GLCanvas3D::LayersEditing::is_allowed() const { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER return wxGetApp().get_shader("variable_layer_height") != nullptr && m_z_texture_id > 0; #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ return m_shader.is_initialized() && m_shader.get_shader()->shader_program_id > 0 && m_z_texture_id > 0; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } bool GLCanvas3D::LayersEditing::is_enabled() const @@ -384,15 +372,11 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas) bool GLCanvas3D::LayersEditing::is_initialized() const { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER return wxGetApp().get_shader("variable_layer_height") != nullptr; #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ return m_shader.is_initialized(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } std::string GLCanvas3D::LayersEditing::get_tooltip(const GLCanvas3D& canvas) const @@ -426,7 +410,6 @@ std::string GLCanvas3D::LayersEditing::get_tooltip(const GLCanvas3D& canvas) con void GLCanvas3D::LayersEditing::render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER GLShaderProgram* shader = wxGetApp().get_shader("variable_layer_height"); if (shader == nullptr) @@ -440,7 +423,6 @@ void GLCanvas3D::LayersEditing::render_active_object_annotations(const GLCanvas3 shader->set_uniform("z_cursor_band_width", band_width); shader->set_uniform("object_max_z", m_object_max_z); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_shader.start_using(); m_shader.set_uniform("z_to_texture_row", float(m_layers_texture.cells - 1) / (float(m_layers_texture.width) * m_object_max_z)); @@ -448,9 +430,7 @@ void GLCanvas3D::LayersEditing::render_active_object_annotations(const GLCanvas3 m_shader.set_uniform("z_cursor", m_object_max_z * this->get_cursor_z_relative(canvas)); m_shader.set_uniform("z_cursor_band_width", band_width); m_shader.set_uniform("object_max_z", m_object_max_z); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id)); @@ -470,15 +450,11 @@ void GLCanvas3D::LayersEditing::render_active_object_annotations(const GLCanvas3 glsafe(::glEnd()); glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader->stop_using(); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_shader.stop_using(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } void GLCanvas3D::LayersEditing::render_profile(const Rect& bar_rect) const @@ -512,7 +488,6 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G { assert(this->is_allowed()); assert(this->last_object_id != -1); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER GLShaderProgram* shader = wxGetApp().get_shader("variable_layer_height"); assert(shader != nullptr); @@ -534,7 +509,6 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G shader->set_uniform("z_cursor", float(m_object_max_z) * float(this->get_cursor_z_relative(canvas))); shader->set_uniform("z_cursor_band_width", float(this->band_width)); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GLint shader_id = m_shader.get_shader()->shader_program_id; assert(shader_id > 0); @@ -565,9 +539,7 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G glsafe(::glUniform1f(z_texture_row_to_normalized_id, GLfloat(1.0f / m_layers_texture.height))); glsafe(::glUniform1f(z_cursor_id, GLfloat(m_object_max_z) * GLfloat(this->get_cursor_z_relative(canvas)))); glsafe(::glUniform1f(z_cursor_band_width_id, GLfloat(this->band_width))); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // Initialize the layer height texture mapping. GLsizei w = (GLsizei)m_layers_texture.width; GLsizei h = (GLsizei)m_layers_texture.height; @@ -583,28 +555,22 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G // Render the object using the layer editing shader and texture. if (! glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier) continue; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader->set_uniform("volume_world_matrix", glvolume->world_matrix()); shader->set_uniform("object_max_z", GLfloat(0)); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (world_matrix_id != -1) glsafe(::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)glvolume->world_matrix().cast().data())); if (object_max_z_id != -1) glsafe(::glUniform1f(object_max_z_id, GLfloat(0))); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glvolume->render(); } // Revert back to the previous shader. glBindTexture(GL_TEXTURE_2D, 0); if (current_program_id > 0) glsafe(::glUseProgram(current_program_id)); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } else { @@ -618,9 +584,7 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G glvolume->render(); } } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } void GLCanvas3D::LayersEditing::adjust_layer_height_profile() @@ -1744,12 +1708,10 @@ bool GLCanvas3D::init() if (m_multisample_allowed) glsafe(::glEnable(GL_MULTISAMPLE)); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER if (m_main_toolbar.is_enabled()) m_layers_editing.init(); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (!m_shader.init("gouraud.vs", "gouraud.fs")) { std::cout << "Unable to initialize gouraud shader: please, check that the files gouraud.vs and gouraud.fs are available" << std::endl; @@ -1761,9 +1723,7 @@ bool GLCanvas3D::init() std::cout << "Unable to initialize variable_layer_height shader: please, check that the files variable_layer_height.vs and variable_layer_height.fs are available" << std::endl; return false; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER if (!m_main_toolbar.is_enabled()) @@ -4623,17 +4583,13 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool return ret; }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER static const std::array orange = { 0.923f, 0.504f, 0.264f, 1.0f }; static const std::array gray = { 0.64f, 0.64f, 0.64f, 1.0f }; #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ static const GLfloat orange[] = { 0.923f, 0.504f, 0.264f, 1.0f }; static const GLfloat gray[] = { 0.64f, 0.64f, 0.64f, 1.0f }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GLVolumePtrs visible_volumes; @@ -4677,7 +4633,6 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool camera.apply_projection(box, near_z, far_z); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER GLShaderProgram* shader = wxGetApp().get_shader("gouraud"); if (shader == nullptr) @@ -4692,7 +4647,6 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool shader->start_using(); shader->set_uniform("print_box.volume_detection", 0); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (transparent_background) glsafe(::glClearColor(0.0f, 0.0f, 0.0f, 0.0f)); @@ -4708,37 +4662,27 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool if (print_box_detection_id != -1) glsafe(::glUniform1i(print_box_detection_id, 0)); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ for (const GLVolume* vol : visible_volumes) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", (vol->printable && !vol->is_outside) ? orange : gray); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (vol->printable && !vol->is_outside) ? orange : gray)); else glsafe(::glColor4fv((vol->printable && !vol->is_outside) ? orange : gray)); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ vol->render(); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader->stop_using(); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_shader.stop_using(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glDisable(GL_DEPTH_TEST)); @@ -5684,18 +5628,14 @@ void GLCanvas3D::_render_objects() const m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data()); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER GLShaderProgram* shader = wxGetApp().get_shader("gouraud"); if (shader != nullptr) { shader->start_using(); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_shader.start_using(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (m_picking_enabled && !m_gizmos.is_dragging() && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f)) { int object_id = m_layers_editing.last_object_id; m_volumes.render(GLVolumeCollection::Opaque, false, wxGetApp().plater()->get_camera().get_view_matrix(), [object_id](const GLVolume& volume) { @@ -5713,16 +5653,12 @@ void GLCanvas3D::_render_objects() const } m_volumes.render(GLVolumeCollection::Transparent, false, wxGetApp().plater()->get_camera().get_view_matrix()); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader->stop_using(); } #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_shader.stop_using(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_camera_clipping_plane = ClippingPlane::ClipsNothing(); } @@ -6142,8 +6078,12 @@ void GLCanvas3D::_render_selection_sidebar_hints() const { #if ENABLE_GCODE_VIEWER m_selection.render_sidebar_hints(m_sidebar_field); +#else +#if ENABLE_SHADERS_MANAGER + m_selection.render_sidebar_hints(m_sidebar_field); #else m_selection.render_sidebar_hints(m_sidebar_field, m_shader); +#endif // ENABLE_SHADERS_MANAGER #endif // ENABLE_GCODE_VIEWER } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 635d23e544..ff775a86e1 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -7,13 +7,9 @@ #include "3DScene.hpp" #include "GLToolbar.hpp" -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "GLShader.hpp" -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "Event.hpp" #include "Selection.hpp" #include "Gizmos/GLGizmosManager.hpp" @@ -173,13 +169,9 @@ private: private: bool m_enabled; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Shader m_shader; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ unsigned int m_z_texture_id; // Not owned by LayersEditing. const DynamicPrintConfig *m_config; @@ -226,15 +218,11 @@ private: LayersEditing(); ~LayersEditing(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER void init(); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void set_config(const DynamicPrintConfig* config); void select_object(const Model &model, int object_id); @@ -477,13 +465,9 @@ private: WarningTexture m_warning_texture; wxTimer m_timer; LayersEditing m_layers_editing; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Shader m_shader; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Mouse m_mouse; mutable GLGizmosManager m_gizmos; mutable GLToolbar m_main_toolbar; @@ -613,13 +597,9 @@ public: void set_color_by(const std::string& value); void refresh_camera_scene_box(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ const Shader& get_shader() const { return m_shader; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ BoundingBoxf3 volumes_bounding_box() const; BoundingBoxf3 scene_bounding_box() const; diff --git a/src/slic3r/GUI/GLShader.cpp b/src/slic3r/GUI/GLShader.cpp index 3b7c2abcc6..7aa2aff08a 100644 --- a/src/slic3r/GUI/GLShader.cpp +++ b/src/slic3r/GUI/GLShader.cpp @@ -1,6 +1,4 @@ -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "libslic3r/libslic3r.h" -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "GLShader.hpp" #include "3DScene.hpp" @@ -9,7 +7,6 @@ #include #include -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER #include @@ -283,7 +280,6 @@ int GLShaderProgram::get_uniform_location(const char* name) const } // namespace Slic3r #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include #include #include @@ -642,6 +638,5 @@ void Shader::reset() } } // namespace Slic3r -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ diff --git a/src/slic3r/GUI/GLShader.hpp b/src/slic3r/GUI/GLShader.hpp index a4a8d32cb3..23d04b22c8 100644 --- a/src/slic3r/GUI/GLShader.hpp +++ b/src/slic3r/GUI/GLShader.hpp @@ -1,7 +1,6 @@ #ifndef slic3r_GLShader_hpp_ #define slic3r_GLShader_hpp_ -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER #include #include @@ -58,7 +57,6 @@ public: } // namespace Slic3r #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "libslic3r/libslic3r.h" #include "libslic3r/Point.hpp" @@ -125,8 +123,8 @@ private: }; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + + #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif /* slic3r_GLShader_hpp_ */ diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index d0c1624d7c..c4082cbb21 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -217,15 +217,10 @@ public: void gcode_thumbnails_debug(); #endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER // returns nullptr if not found GLShaderProgram* get_shader(const std::string& shader_name) { return m_opengl_mgr.get_shader(shader_name); } - -// // returns 0 if not found -// unsigned int get_shader_id(const std::string& shader_name) const { return m_opengl_mgr.get_shader_id(shader_name); } #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ private: bool on_init_inner(); diff --git a/src/slic3r/GUI/OpenGLManager.cpp b/src/slic3r/GUI/OpenGLManager.cpp index e4674fffd1..deaa3cd199 100644 --- a/src/slic3r/GUI/OpenGLManager.cpp +++ b/src/slic3r/GUI/OpenGLManager.cpp @@ -28,7 +28,6 @@ namespace Slic3r { namespace GUI { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER // A safe wrapper around glGetString to report a "N/A" string in case glGetString returns nullptr. inline std::string gl_get_string_safe(GLenum param, const std::string& default_value) @@ -37,7 +36,6 @@ inline std::string gl_get_string_safe(GLenum param, const std::string& default_v return std::string((value != nullptr) ? value : default_value); } #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ const std::string& OpenGLManager::GLInfo::get_version() const { @@ -96,14 +94,12 @@ float OpenGLManager::GLInfo::get_max_anisotropy() const void OpenGLManager::GLInfo::detect() const { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER m_version = gl_get_string_safe(GL_VERSION, "N/A"); m_glsl_version = gl_get_string_safe(GL_SHADING_LANGUAGE_VERSION, "N/A"); m_vendor = gl_get_string_safe(GL_VENDOR, "N/A"); m_renderer = gl_get_string_safe(GL_RENDERER, "N/A"); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ const char* data = (const char*)::glGetString(GL_VERSION); if (data != nullptr) m_version = data; @@ -119,9 +115,7 @@ void OpenGLManager::GLInfo::detect() const data = (const char*)::glGetString(GL_RENDERER); if (data != nullptr) m_renderer = data; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_max_tex_size)); @@ -141,12 +135,10 @@ bool OpenGLManager::GLInfo::is_version_greater_or_equal_to(unsigned int major, u if (!m_detected) detect(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER if (m_version == "N/A") return false; #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ std::vector tokens; boost::split(tokens, m_version, boost::is_any_of(" "), boost::token_compress_on); @@ -188,34 +180,26 @@ std::string OpenGLManager::GLInfo::to_string(bool format_as_html, bool extension std::string line_end = format_as_html ? "
" : "\n"; out << h2_start << "OpenGL installation" << h2_end << line_end; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER out << b_start << "GL version: " << b_end << m_version << line_end; out << b_start << "Vendor: " << b_end << m_vendor << line_end; out << b_start << "Renderer: " << b_end << m_renderer << line_end; out << b_start << "GLSL version: " << b_end << m_glsl_version << line_end; #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ out << b_start << "GL version: " << b_end << (m_version.empty() ? "N/A" : m_version) << line_end; out << b_start << "Vendor: " << b_end << (m_vendor.empty() ? "N/A" : m_vendor) << line_end; out << b_start << "Renderer: " << b_end << (m_renderer.empty() ? "N/A" : m_renderer) << line_end; out << b_start << "GLSL version: " << b_end << (m_glsl_version.empty() ? "N/A" : m_glsl_version) << line_end; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (extensions) { std::vector extensions_list; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER std::string extensions_str = gl_get_string_safe(GL_EXTENSIONS, ""); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ std::string extensions_str = (const char*)::glGetString(GL_EXTENSIONS); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ boost::split(extensions_list, extensions_str, boost::is_any_of(" "), boost::token_compress_off); if (!extensions_list.empty()) @@ -247,11 +231,9 @@ OpenGLManager::OSInfo OpenGLManager::s_os_info; OpenGLManager::~OpenGLManager() { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER m_shaders_manager.shutdown(); #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_HACK_CLOSING_ON_OSX_10_9_5 #ifdef __APPLE__ @@ -294,16 +276,12 @@ bool OpenGLManager::init_gl() else s_framebuffers_type = EFramebufferType::Unknown; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER bool valid_version = s_gl_info.is_version_greater_or_equal_to(2, 0); if (!valid_version) { #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (!s_gl_info.is_version_greater_or_equal_to(2, 0)) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // Complain about the OpenGL version. wxString message = from_u8((boost::format( @@ -318,7 +296,6 @@ bool OpenGLManager::init_gl() wxMessageBox(message, wxString("PrusaSlicer - ") + _L("Unsupported OpenGL version"), wxOK | wxICON_ERROR); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER if (valid_version) { // load shaders @@ -330,7 +307,6 @@ bool OpenGLManager::init_gl() } } #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } return true; diff --git a/src/slic3r/GUI/OpenGLManager.hpp b/src/slic3r/GUI/OpenGLManager.hpp index c80deb6ef1..b645320240 100644 --- a/src/slic3r/GUI/OpenGLManager.hpp +++ b/src/slic3r/GUI/OpenGLManager.hpp @@ -1,11 +1,9 @@ #ifndef slic3r_OpenGLManager_hpp_ #define slic3r_OpenGLManager_hpp_ -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER #include "GLShadersManager.hpp" #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ class wxWindow; class wxGLCanvas; @@ -76,11 +74,9 @@ private: bool m_gl_initialized{ false }; wxGLContext* m_context{ nullptr }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER GLShadersManager m_shaders_manager; #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ static GLInfo s_gl_info; #if ENABLE_HACK_CLOSING_ON_OSX_10_9_5 #ifdef __APPLE__ @@ -99,15 +95,10 @@ public: bool init_gl(); wxGLContext* init_glcontext(wxGLCanvas& canvas); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER // returns nullptr if not found GLShaderProgram* get_shader(const std::string& shader_name) { return m_shaders_manager.get_shader(shader_name); } - -// // returns 0 if not found -// unsigned int get_shader_id(const std::string& shader_name) const { return m_shaders_manager.get_shader_id(shader_name); } #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ static bool are_compressed_textures_supported() { return s_compressed_textures_supported; } static bool can_multisample() { return s_multisample == EMultisampleState::Enabled; } diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 982a16bf00..d3e4e37ef8 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -8,11 +8,6 @@ #include "GUI_ObjectList.hpp" #include "Gizmos/GLGizmoBase.hpp" #include "3DScene.hpp" -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -//#if ENABLE_SHADERS_MANAGER -//#include "GLShader.hpp" -//#endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "Camera.hpp" #include @@ -118,17 +113,13 @@ bool Selection::init() m_arrow.init_from(straight_arrow(10.0f, 5.0f, 5.0f, 10.0f, 1.0f)); m_curved_arrow.init_from(circular_arrow(16, 10.0f, 5.0f, 10.0f, 5.0f, 1.0f)); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (!m_arrows_shader.init("gouraud_light.vs", "gouraud_light.fs")) { BOOST_LOG_TRIVIAL(error) << "Unable to initialize gouraud_light shader: please, check that the files gouraud_light.vs and gouraud_light.fs are available"; return false; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #else if (!m_arrow.init()) return false; @@ -1258,22 +1249,23 @@ void Selection::render_center(bool gizmo_is_dragging) const #if ENABLE_GCODE_VIEWER void Selection::render_sidebar_hints(const std::string& sidebar_field) const #else +#if ENABLE_SHADERS_MANAGER +void Selection::render_sidebar_hints(const std::string& sidebar_field) const +#else void Selection::render_sidebar_hints(const std::string& sidebar_field, const Shader& shader) const +#endif // ENABLE_SHADERS_MANAGER #endif // ENABLE_GCODE_VIEWER { if (sidebar_field.empty()) return; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER GLShaderProgram* shader = nullptr; #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (!boost::starts_with(sidebar_field, "layer")) { #if ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) @@ -1281,19 +1273,24 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field, const Sha shader->start_using(); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (!m_arrows_shader.is_initialized()) return; m_arrows_shader.start_using(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); +#else +#if ENABLE_SHADERS_MANAGER + shader = wxGetApp().get_shader("gouraud_light"); + if (shader == nullptr) + return; + + shader->start_using(); #else shader.start_using(); - glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); glsafe(::glEnable(GL_LIGHTING)); +#endif // ENABLE_SHADERS_MANAGER + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); #endif // ENABLE_GCODE_VIEWER } @@ -1351,46 +1348,35 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field, const Sha } if (boost::starts_with(sidebar_field, "position")) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER +#if ENABLE_GCODE_VIEWER render_sidebar_position_hints(sidebar_field, *shader); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ render_sidebar_position_hints(sidebar_field); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_GCODE_VIEWER +#else + render_sidebar_position_hints(sidebar_field); #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ else if (boost::starts_with(sidebar_field, "rotation")) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER +#if ENABLE_GCODE_VIEWER render_sidebar_rotation_hints(sidebar_field, *shader); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ render_sidebar_rotation_hints(sidebar_field); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_GCODE_VIEWER - else if (boost::starts_with(sidebar_field, "scale") || boost::starts_with(sidebar_field, "size")) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_SHADERS_MANAGER -render_sidebar_scale_hints(sidebar_field, *shader); -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - render_sidebar_scale_hints(sidebar_field); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - else if (boost::starts_with(sidebar_field, "scale")) - render_sidebar_scale_hints(sidebar_field); - else if (boost::starts_with(sidebar_field, "size")) - render_sidebar_size_hints(sidebar_field); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#else + render_sidebar_rotation_hints(sidebar_field); +#endif // ENABLE_SHADERS_MANAGER + else if (boost::starts_with(sidebar_field, "scale") || boost::starts_with(sidebar_field, "size")) +#if ENABLE_SHADERS_MANAGER +#if ENABLE_GCODE_VIEWER + render_sidebar_scale_hints(sidebar_field, *shader); +#else + render_sidebar_scale_hints(sidebar_field); +#endif // ENABLE_GCODE_VIEWER +#else + render_sidebar_scale_hints(sidebar_field); +#endif // ENABLE_SHADERS_MANAGER else if (boost::starts_with(sidebar_field, "layer")) render_sidebar_layers_hints(sidebar_field); @@ -1399,18 +1385,18 @@ render_sidebar_scale_hints(sidebar_field, *shader); if (!boost::starts_with(sidebar_field, "layer")) { #if ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader->stop_using(); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_arrows_shader.stop_using(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#else +#if ENABLE_SHADERS_MANAGER + shader->stop_using(); #else glsafe(::glDisable(GL_LIGHTING)); shader.stop_using(); +#endif // ENABLE_SHADERS_MANAGER #endif // ENABLE_GCODE_VIEWER } } @@ -2013,74 +1999,54 @@ void Selection::render_bounding_box(const BoundingBoxf3& box, float* color) cons } #if ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER void Selection::render_sidebar_position_hints(const std::string& sidebar_field, GLShaderProgram& shader) const #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void Selection::render_sidebar_position_hints(const std::string& sidebar_field) const -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GLint color_id = ::glGetUniformLocation(m_arrows_shader.get_shader_program_id(), "uniform_color"); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (boost::ends_with(sidebar_field, "x")) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader.set_uniform("uniform_color", AXES_COLOR[0], 4); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[0])); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glRotated(-90.0, 0.0, 0.0, 1.0)); m_arrow.render(); } else if (boost::ends_with(sidebar_field, "y")) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader.set_uniform("uniform_color", AXES_COLOR[1], 4); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[1])); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_arrow.render(); } else if (boost::ends_with(sidebar_field, "z")) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader.set_uniform("uniform_color", AXES_COLOR[2], 4); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[2])); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glRotated(90.0, 1.0, 0.0, 0.0)); m_arrow.render(); } } #else -void Selection::render_sidebar_position_hints(const std::string & sidebar_field) const +void Selection::render_sidebar_position_hints(const std::string& sidebar_field) const { if (boost::ends_with(sidebar_field, "x")) { @@ -2098,68 +2064,48 @@ void Selection::render_sidebar_position_hints(const std::string & sidebar_field) #endif // ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field, GLShaderProgram& shader) const #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field) const -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GLint color_id = ::glGetUniformLocation(m_arrows_shader.get_shader_program_id(), "uniform_color"); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (boost::ends_with(sidebar_field, "x")) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader.set_uniform("uniform_color", AXES_COLOR[0], 4); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[0])); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glRotated(90.0, 0.0, 1.0, 0.0)); render_sidebar_rotation_hint(X); } else if (boost::ends_with(sidebar_field, "y")) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader.set_uniform("uniform_color", AXES_COLOR[1], 4); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[1])); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0)); render_sidebar_rotation_hint(Y); } else if (boost::ends_with(sidebar_field, "z")) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader.set_uniform("uniform_color", AXES_COLOR[2], 4); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[2])); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ render_sidebar_rotation_hint(Z); } @@ -2183,15 +2129,11 @@ void Selection::render_sidebar_rotation_hints(const std::string & sidebar_field) #endif // ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER void Selection::render_sidebar_scale_hints(const std::string& sidebar_field, GLShaderProgram& shader) const #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void Selection::render_sidebar_scale_hints(const std::string& sidebar_field) const -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { bool uniform_scale = requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling(); @@ -2199,30 +2141,22 @@ void Selection::render_sidebar_scale_hints(const std::string& sidebar_field) con { glsafe(::glPushMatrix()); glsafe(::glRotated(-90.0, 0.0, 0.0, 1.0)); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER render_sidebar_scale_hint(X, shader); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ render_sidebar_scale_hint(X); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glPopMatrix()); } if (boost::ends_with(sidebar_field, "y") || uniform_scale) { glsafe(::glPushMatrix()); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER render_sidebar_scale_hint(Y, shader); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ render_sidebar_scale_hint(Y); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glPopMatrix()); } @@ -2230,15 +2164,11 @@ void Selection::render_sidebar_scale_hints(const std::string& sidebar_field) con { glsafe(::glPushMatrix()); glsafe(::glRotated(90.0, 1.0, 0.0, 0.0)); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER render_sidebar_scale_hint(Z, shader); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ render_sidebar_scale_hint(Z); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glPopMatrix()); } } @@ -2272,17 +2202,6 @@ void Selection::render_sidebar_scale_hints(const std::string & sidebar_field) co } #endif // ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if !ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -void Selection::render_sidebar_size_hints(const std::string& sidebar_field) const -{ - render_sidebar_scale_hints(sidebar_field); -} -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // !ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) const { static const double Margin = 10.0; @@ -2373,29 +2292,25 @@ void Selection::render_sidebar_rotation_hint(Axis axis) const m_curved_arrow.render(); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER +#if ENABLE_GCODE_VIEWER void Selection::render_sidebar_scale_hint(Axis axis, GLShaderProgram& shader) const #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void Selection::render_sidebar_scale_hint(Axis axis) const -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_GCODE_VIEWER +#else +void Selection::render_sidebar_scale_hint(Axis axis) const #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { #if ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER shader.set_uniform("uniform_color", (requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling()) ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis], 4); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GLint color_id = ::glGetUniformLocation(m_arrows_shader.get_shader_program_id(), "uniform_color"); if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling()) ? (const GLfloat*)UNIFORM_SCALE_COLOR : (const GLfloat*)AXES_COLOR[axis])); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #else m_arrow.set_color(((requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling()) ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis]), 3); #endif // ENABLE_GCODE_VIEWER @@ -2408,12 +2323,6 @@ void Selection::render_sidebar_scale_hint(Axis axis) const m_arrow.render(); } -#if !ENABLE_GCODE_VIEWER -void Selection::render_sidebar_size_hint(Axis axis, double length) const -{ -} -#endif // !ENABLE_GCODE_VIEWER - #ifndef NDEBUG static bool is_rotation_xy_synchronized(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to) { diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 7411fc97e5..2f8ffe58c8 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -6,13 +6,13 @@ #include "3DScene.hpp" #if ENABLE_GCODE_VIEWER #include "GLModel.hpp" -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "GLShader.hpp" -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#else +#if !ENABLE_SHADERS_MANAGER +#include "GLShader.hpp" +#endif // !ENABLE_SHADERS_MANAGER #endif // ENABLE_GCODE_VIEWER #if ENABLE_RENDER_SELECTION_CENTER @@ -21,15 +21,12 @@ typedef class GLUquadric GLUquadricObj; #endif // ENABLE_RENDER_SELECTION_CENTER namespace Slic3r { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_GCODE_VIEWER +class Shader; +#endif // !ENABLE_GCODE_VIEWER #if ENABLE_SHADERS_MANAGER class GLShaderProgram; -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -class Shader; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ namespace GUI { class TransformationType { @@ -221,13 +218,9 @@ private: #if ENABLE_GCODE_VIEWER GL_Model m_arrow; GL_Model m_curved_arrow; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Shader m_arrows_shader; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #else mutable GLArrow m_arrow; mutable GLCurvedArrow m_curved_arrow; @@ -348,8 +341,12 @@ public: #endif // ENABLE_RENDER_SELECTION_CENTER #if ENABLE_GCODE_VIEWER void render_sidebar_hints(const std::string& sidebar_field) const; +#else +#if ENABLE_SHADERS_MANAGER + void render_sidebar_hints(const std::string& sidebar_field) const; #else void render_sidebar_hints(const std::string& sidebar_field, const Shader& shader) const; +#endif // ENABLE_SHADERS_MANAGER #endif // ENABLE_GCODE_VIEWER bool requires_local_axes() const; @@ -388,43 +385,35 @@ private: void render_selected_volumes() const; void render_synchronized_volumes() const; void render_bounding_box(const BoundingBoxf3& box, float* color) const; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER +#if ENABLE_GCODE_VIEWER void render_sidebar_position_hints(const std::string& sidebar_field, GLShaderProgram& shader) const; void render_sidebar_rotation_hints(const std::string& sidebar_field, GLShaderProgram& shader) const; void render_sidebar_scale_hints(const std::string& sidebar_field, GLShaderProgram& shader) const; #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void render_sidebar_position_hints(const std::string& sidebar_field) const; void render_sidebar_rotation_hints(const std::string& sidebar_field) const; void render_sidebar_scale_hints(const std::string& sidebar_field) const; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_GCODE_VIEWER +#else + void render_sidebar_position_hints(const std::string& sidebar_field) const; + void render_sidebar_rotation_hints(const std::string& sidebar_field) const; + void render_sidebar_scale_hints(const std::string& sidebar_field) const; #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if !ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - void render_sidebar_size_hints(const std::string& sidebar_field) const; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // !ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void render_sidebar_layers_hints(const std::string& sidebar_field) const; #if !ENABLE_GCODE_VIEWER void render_sidebar_position_hint(Axis axis) const; #endif // !ENABLE_GCODE_VIEWER void render_sidebar_rotation_hint(Axis axis) const; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_SHADERS_MANAGER +#if ENABLE_GCODE_VIEWER void render_sidebar_scale_hint(Axis axis, GLShaderProgram& shader) const; #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void render_sidebar_scale_hint(Axis axis) const; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_GCODE_VIEWER +#else + void render_sidebar_scale_hint(Axis axis) const; #endif // ENABLE_SHADERS_MANAGER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if !ENABLE_GCODE_VIEWER - void render_sidebar_size_hint(Axis axis, double length) const; -#endif // !ENABLE_GCODE_VIEWER public: enum SyncRotationType { From 5aa8cc577988ec3f9b335aa6c609b1f5f6c0e88f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 21 May 2020 10:15:00 +0200 Subject: [PATCH 083/503] ENABLE_SHADERS_MANAGER -> Unified client code of new GLShadersManager and GLShaderProgram classes --- src/slic3r/GUI/3DBed.cpp | 19 +++++++++-- src/slic3r/GUI/3DScene.cpp | 40 +++++++++++++++++++++++ src/slic3r/GUI/3DScene.hpp | 6 ---- src/slic3r/GUI/GLCanvas3D.cpp | 18 ++++++----- src/slic3r/GUI/GLShader.cpp | 50 ++++++++++++++++++++--------- src/slic3r/GUI/GLShader.hpp | 4 ++- src/slic3r/GUI/GLShadersManager.cpp | 14 ++++++++ src/slic3r/GUI/GLShadersManager.hpp | 3 ++ src/slic3r/GUI/GUI_App.hpp | 4 +-- src/slic3r/GUI/OpenGLManager.hpp | 2 +- 10 files changed, 123 insertions(+), 37 deletions(-) diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 73162645fc..3fdcdd6fab 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -196,15 +196,18 @@ void Bed3D::Axes::render() const shader->start_using(); // x axis - shader->set_uniform("uniform_color", { 0.75f, 0.0f, 0.0f, 1.0f }); + std::array color = { 0.75f, 0.0f, 0.0f, 1.0f }; + shader->set_uniform("uniform_color", color); render_axis(Geometry::assemble_transform(m_origin, { 0.0, 0.5 * M_PI, 0.0f }).cast()); // y axis - shader->set_uniform("uniform_color", { 0.0f, 0.75f, 0.0f, 1.0f }); + color = { 0.0f, 0.75f, 0.0f, 1.0f }; + shader->set_uniform("uniform_color", color); render_axis(Geometry::assemble_transform(m_origin, { -0.5 * M_PI, 0.0, 0.0f }).cast()); // z axis - shader->set_uniform("uniform_color", { 0.0f, 0.0f, 0.75f, 1.0f }); + color = { 0.0f, 0.0f, 0.75f, 1.0f }; + shader->set_uniform("uniform_color", color); render_axis(Geometry::assemble_transform(m_origin).cast()); shader->stop_using(); @@ -676,9 +679,19 @@ void Bed3D::render_model() const if (!m_model.get_filename().empty()) { +#if ENABLE_SHADERS_MANAGER + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); + if (shader != nullptr) + { + shader->start_using(); + m_model.render(); + shader->stop_using(); + } +#else glsafe(::glEnable(GL_LIGHTING)); m_model.render(); glsafe(::glDisable(GL_LIGHTING)); +#endif // ENABLE_SHADERS_MANAGER } } diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 29a4b84a30..6e42e52a21 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1,6 +1,10 @@ #include #include "3DScene.hpp" +#if ENABLE_SHADERS_MANAGER +#include "GLShader.hpp" +#include "GUI_App.hpp" +#endif // ENABLE_SHADERS_MANAGER #include "libslic3r/ExtrusionEntity.hpp" #include "libslic3r/ExtrusionEntityCollection.hpp" @@ -640,6 +644,12 @@ GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCo void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function filter_func) const { +#if ENABLE_SHADERS_MANAGER + GLShaderProgram* shader = GUI::wxGetApp().get_current_shader(); + if (shader == nullptr) + return; +#endif // ENABLE_SHADERS_MANAGER + glsafe(::glEnable(GL_BLEND)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); @@ -650,6 +660,15 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); +#if ENABLE_SHADERS_MANAGER + shader->set_uniform("print_box.min", m_print_box_min, 3); + shader->set_uniform("print_box.max", m_print_box_max, 3); + shader->set_uniform("z_range", m_z_range, 2); + shader->set_uniform("clipping_plane", m_clipping_plane, 4); +#if ENABLE_SLOPE_RENDERING + shader->set_uniform("slope.z_range", m_slope.z_range); +#endif // ENABLE_SLOPE_RENDERING +#else GLint current_program_id; glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id)); GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1; @@ -684,11 +703,19 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab if (slope_z_range_id != -1) glsafe(::glUniform2fv(slope_z_range_id, 1, (const GLfloat*)m_slope.z_range.data())); #endif // ENABLE_SLOPE_RENDERING +#endif // ENABLE_SHADERS_MANAGER GLVolumeWithIdAndZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func); for (GLVolumeWithIdAndZ& volume : to_render) { volume.first->set_render_color(); #if ENABLE_SLOPE_RENDERING +#if ENABLE_SHADERS_MANAGER + shader->set_uniform("uniform_color", volume.first->render_color, 4); + shader->set_uniform("print_box.actived", volume.first->shader_outside_printer_detection_enabled); + shader->set_uniform("print_box.volume_world_matrix", volume.first->world_matrix()); + shader->set_uniform("slope.actived", m_slope.active && !volume.first->is_modifier && !volume.first->is_wipe_tower); + shader->set_uniform("slope.volume_world_normal_matrix", volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast()); +#else if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)volume.first->render_color)); else @@ -708,6 +735,7 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab Matrix3f normal_matrix = volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast(); glsafe(::glUniformMatrix3fv(slope_normal_matrix_id, 1, GL_FALSE, (const GLfloat*)normal_matrix.data())); } +#endif // ENABLE_SHADERS_MANAGER volume.first->render(); #else @@ -1912,6 +1940,12 @@ void GLModel::reset() void GLModel::render() const { +#if ENABLE_SHADERS_MANAGER + GLShaderProgram* shader = GUI::wxGetApp().get_current_shader(); + if (shader == nullptr) + return; +#endif // ENABLE_SHADERS_MANAGER + glsafe(::glEnable(GL_BLEND)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); @@ -1919,16 +1953,22 @@ void GLModel::render() const glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); +#if !ENABLE_SHADERS_MANAGER GLint current_program_id; glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id)); GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1; glcheck(); +#endif // !ENABLE_SHADERS_MANAGER #if ENABLE_SLOPE_RENDERING +#if ENABLE_SHADERS_MANAGER + shader->set_uniform("uniform_color", m_volume.render_color, 4); +#else if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)m_volume.render_color)); else glsafe(::glColor4fv(m_volume.render_color)); +#endif // ENABLE_SHADERS_MANAGER m_volume.render(); #else diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 86072754d7..fd74cd4912 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -26,12 +26,6 @@ inline void glAssertRecentCall() { } #endif namespace Slic3r { -namespace GUI { -class Bed3D; -struct Camera; -class GLToolbar; -} // namespace GUI - class SLAPrintObject; enum SLAPrintObjectStep : unsigned int; class DynamicPrintConfig; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 51079c892f..2eff9ac193 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -490,16 +490,16 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G assert(this->last_object_id != -1); #if ENABLE_SHADERS_MANAGER GLShaderProgram* shader = wxGetApp().get_shader("variable_layer_height"); - assert(shader != nullptr); + if (shader == nullptr) + return; - GLint current_program_id = 0; - glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id)); - if (shader->get_id() != static_cast(current_program_id)) + GLShaderProgram* current_shader = wxGetApp().get_current_shader(); + if (shader->get_id() != current_shader->get_id()) // The layer editing shader is not yet active. Activate it. shader->start_using(); else // The layer editing shader was already active. - current_program_id = 0; + current_shader = nullptr; const_cast(this)->generate_layer_height_texture(); @@ -529,7 +529,6 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G GLint object_max_z_id = ::glGetUniformLocation(shader_id, "object_max_z"); glcheck(); - if (z_to_texture_row_id != -1 && z_texture_row_to_normalized_id != -1 && z_cursor_id != -1 && z_cursor_band_width_id != -1 && world_matrix_id != -1) { const_cast(this)->generate_layer_height_texture(); @@ -568,9 +567,12 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G } // Revert back to the previous shader. glBindTexture(GL_TEXTURE_2D, 0); +#if ENABLE_SHADERS_MANAGER + if (current_shader != nullptr) + current_shader->start_using(); +#else if (current_program_id > 0) glsafe(::glUseProgram(current_program_id)); -#if !ENABLE_SHADERS_MANAGER } else { @@ -584,7 +586,7 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G glvolume->render(); } } -#endif // !ENABLE_SHADERS_MANAGER +#endif // ENABLE_SHADERS_MANAGER } void GLCanvas3D::LayersEditing::adjust_layer_height_profile() diff --git a/src/slic3r/GUI/GLShader.cpp b/src/slic3r/GUI/GLShader.cpp index 7aa2aff08a..d0b9267945 100644 --- a/src/slic3r/GUI/GLShader.cpp +++ b/src/slic3r/GUI/GLShader.cpp @@ -6,6 +6,7 @@ #include #include +#include #if ENABLE_SHADERS_MANAGER #include @@ -23,8 +24,7 @@ bool GLShaderProgram::init_from_files(const std::string& name, const ShaderFilen auto load_from_file = [](const std::string& filename) { std::string path = resources_dir() + "/shaders/" + filename; boost::nowide::ifstream s(path, boost::nowide::ifstream::binary); - if (!s.good()) - { + if (!s.good()) { BOOST_LOG_TRIVIAL(error) << "Couldn't open file: '" << path << "'"; return std::string(); } @@ -34,8 +34,7 @@ bool GLShaderProgram::init_from_files(const std::string& name, const ShaderFilen s.seekg(0, s.beg); std::string source(file_length, '\0'); s.read(source.data(), file_length); - if (!s.good()) - { + if (!s.good()) { BOOST_LOG_TRIVIAL(error) << "Error while loading file: '" << path << "'"; return std::string(); } @@ -49,8 +48,9 @@ bool GLShaderProgram::init_from_files(const std::string& name, const ShaderFilen sources[i] = filenames[i].empty() ? std::string() : load_from_file(filenames[i]); } - bool valid = (!sources[static_cast(EShaderType::Vertex)].empty() && !sources[static_cast(EShaderType::Fragment)].empty()) || - !sources[static_cast(EShaderType::Compute)].empty(); + bool valid = !sources[static_cast(EShaderType::Vertex)].empty() && !sources[static_cast(EShaderType::Fragment)].empty() && sources[static_cast(EShaderType::Compute)].empty(); + valid |= !sources[static_cast(EShaderType::Compute)].empty() && sources[static_cast(EShaderType::Vertex)].empty() && sources[static_cast(EShaderType::Fragment)].empty() && + sources[static_cast(EShaderType::Geometry)].empty() && sources[static_cast(EShaderType::TessEvaluation)].empty() && sources[static_cast(EShaderType::TessControl)].empty(); return valid ? init_from_texts(name, sources) : false; } @@ -107,9 +107,11 @@ bool GLShaderProgram::init_from_texts(const std::string& name, const ShaderSourc auto [result, id] = create_shader(type); if (result) shader_ids[i] = id; - else - { + else { BOOST_LOG_TRIVIAL(error) << "glCreateShader() failed for " << shader_type_as_string(type) << " shader of shader program '" << name << "'"; + + // release shaders + release_shaders(shader_ids); return false; } @@ -125,7 +127,7 @@ bool GLShaderProgram::init_from_texts(const std::string& name, const ShaderSourc glsafe(::glGetShaderInfoLog(id, params, ¶ms, msg.data())); BOOST_LOG_TRIVIAL(error) << "Unable to compile " << shader_type_as_string(type) << " shader of shader program '" << name << "':\n" << msg.data(); - // release shader + // release shaders release_shaders(shader_ids); return false; } @@ -173,13 +175,10 @@ bool GLShaderProgram::init_from_texts(const std::string& name, const ShaderSourc return true; } -bool GLShaderProgram::start_using() const +void GLShaderProgram::start_using() const { - if (m_id == 0) - return false; - + assert(m_id > 0); glsafe(::glUseProgram(m_id)); - return true; } void GLShaderProgram::stop_using() const @@ -212,6 +211,16 @@ bool GLShaderProgram::set_uniform(const char* name, float value) const return false; } +bool GLShaderProgram::set_uniform(const char* name, const std::array& value) const +{ + int id = get_uniform_location(name); + if (id >= 0) { + glsafe(::glUniform2fv(id, 1, static_cast(value.data()))); + return true; + } + return false; +} + bool GLShaderProgram::set_uniform(const char* name, const std::array& value) const { int id = get_uniform_location(name); @@ -236,8 +245,7 @@ bool GLShaderProgram::set_uniform(const char* name, const float* value, size_t s { if (size == 1) return set_uniform(name, value[0]); - else if (size < 5) - { + else if (size < 5) { int id = get_uniform_location(name); if (id >= 0) { if (size == 2) @@ -268,6 +276,16 @@ bool GLShaderProgram::set_uniform(const char* name, const Transform3d& value) co return set_uniform(name, value.cast()); } +bool GLShaderProgram::set_uniform(const char* name, const Matrix3f& value) const +{ + int id = get_uniform_location(name); + if (id >= 0) { + glsafe(::glUniformMatrix3fv(id, 1, GL_FALSE, static_cast(value.data()))); + return true; + } + return false; +} + int GLShaderProgram::get_attrib_location(const char* name) const { return (m_id > 0) ? ::glGetAttribLocation(m_id, name) : -1; diff --git a/src/slic3r/GUI/GLShader.hpp b/src/slic3r/GUI/GLShader.hpp index 23d04b22c8..6994b91caf 100644 --- a/src/slic3r/GUI/GLShader.hpp +++ b/src/slic3r/GUI/GLShader.hpp @@ -37,17 +37,19 @@ public: const std::string& get_name() const { return m_name; } unsigned int get_id() const { return m_id; } - bool start_using() const; + void start_using() const; void stop_using() const; bool set_uniform(const char* name, int value) const; bool set_uniform(const char* name, bool value) const; bool set_uniform(const char* name, float value) const; + bool set_uniform(const char* name, const std::array& value) const; bool set_uniform(const char* name, const std::array& value) const; bool set_uniform(const char* name, const std::array& value) const; bool set_uniform(const char* name, const float* value, size_t size) const; bool set_uniform(const char* name, const Transform3f& value) const; bool set_uniform(const char* name, const Transform3d& value) const; + bool set_uniform(const char* name, const Matrix3f& value) const; // returns -1 if not found int get_attrib_location(const char* name) const; diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index e4d6407224..feffaecdee 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -1,9 +1,12 @@ #include "libslic3r/libslic3r.h" #include "GLShadersManager.hpp" +#include "3DScene.hpp" #include #include +#include + #if ENABLE_SHADERS_MANAGER namespace Slic3r { @@ -71,6 +74,17 @@ GLShaderProgram* GLShadersManager::get_shader(const std::string& shader_name) return (it != m_shaders.end()) ? it->get() : nullptr; } +GLShaderProgram* GLShadersManager::get_current_shader() +{ + GLint id = 0; + glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, &id)); + if (id == 0) + return false; + + auto it = std::find_if(m_shaders.begin(), m_shaders.end(), [id](std::unique_ptr& p) { return p->get_id() == id; }); + return (it != m_shaders.end()) ? it->get() : nullptr; +} + } // namespace Slic3r #endif // ENABLE_SHADERS_MANAGER diff --git a/src/slic3r/GUI/GLShadersManager.hpp b/src/slic3r/GUI/GLShadersManager.hpp index 0c624f9161..f30472b123 100644 --- a/src/slic3r/GUI/GLShadersManager.hpp +++ b/src/slic3r/GUI/GLShadersManager.hpp @@ -22,6 +22,9 @@ public: // returns nullptr if not found GLShaderProgram* get_shader(const std::string& shader_name); + + // returns currently active shader, nullptr if none + GLShaderProgram* get_current_shader(); }; } // namespace Slic3r diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index c4082cbb21..9bd8697815 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -218,8 +218,8 @@ public: #endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG #if ENABLE_SHADERS_MANAGER - // returns nullptr if not found GLShaderProgram* get_shader(const std::string& shader_name) { return m_opengl_mgr.get_shader(shader_name); } + GLShaderProgram* get_current_shader() { return m_opengl_mgr.get_current_shader(); } #endif // ENABLE_SHADERS_MANAGER private: @@ -240,6 +240,6 @@ private: DECLARE_APP(GUI_App) } // GUI -} //Slic3r +} // Slic3r #endif // slic3r_GUI_App_hpp_ diff --git a/src/slic3r/GUI/OpenGLManager.hpp b/src/slic3r/GUI/OpenGLManager.hpp index b645320240..fb3b33494b 100644 --- a/src/slic3r/GUI/OpenGLManager.hpp +++ b/src/slic3r/GUI/OpenGLManager.hpp @@ -96,8 +96,8 @@ public: wxGLContext* init_glcontext(wxGLCanvas& canvas); #if ENABLE_SHADERS_MANAGER - // returns nullptr if not found GLShaderProgram* get_shader(const std::string& shader_name) { return m_shaders_manager.get_shader(shader_name); } + GLShaderProgram* get_current_shader() { return m_shaders_manager.get_current_shader(); } #endif // ENABLE_SHADERS_MANAGER static bool are_compressed_textures_supported() { return s_compressed_textures_supported; } From 3bbe2ef9600a03e7e4a4926109c465820274e343 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 21 May 2020 10:27:41 +0200 Subject: [PATCH 084/503] Fixed typo --- src/slic3r/GUI/GLShadersManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index feffaecdee..a3e48ca554 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -79,7 +79,7 @@ GLShaderProgram* GLShadersManager::get_current_shader() GLint id = 0; glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, &id)); if (id == 0) - return false; + return nullptr; auto it = std::find_if(m_shaders.begin(), m_shaders.end(), [id](std::unique_ptr& p) { return p->get_id() == id; }); return (it != m_shaders.end()) ? it->get() : nullptr; From 0d579f5467549ebd4680b0c53e0c8ed42834600f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 21 May 2020 12:13:24 +0200 Subject: [PATCH 085/503] ENABLE_SHADERS_MANAGER -> Small refactoring --- src/slic3r/GUI/GLShadersManager.cpp | 5 +- src/slic3r/GUI/Selection.cpp | 114 ++++++---------------------- src/slic3r/GUI/Selection.hpp | 20 ----- 3 files changed, 27 insertions(+), 112 deletions(-) diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index a3e48ca554..02d033b5a8 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -30,7 +30,7 @@ std::pair GLShadersManager::init() bool valid = true; - // used to render bed axes, selection hints + // used to render bed axes and model, selection hints, gcode sequential view marker model valid &= append_shader("gouraud_light", { "gouraud_light.vs", "gouraud_light.fs" }); // used to render printbed valid &= append_shader("printbed", { "printbed.vs", "printbed.fs" }); @@ -62,8 +62,7 @@ std::pair GLShadersManager::init() void GLShadersManager::shutdown() { - for (std::unique_ptr& shader : m_shaders) - { + for (std::unique_ptr& shader : m_shaders) { shader.reset(); } } diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index d3e4e37ef8..2949247b80 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1348,35 +1348,11 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field, const Sha } if (boost::starts_with(sidebar_field, "position")) -#if ENABLE_SHADERS_MANAGER -#if ENABLE_GCODE_VIEWER - render_sidebar_position_hints(sidebar_field, *shader); -#else render_sidebar_position_hints(sidebar_field); -#endif // ENABLE_GCODE_VIEWER -#else - render_sidebar_position_hints(sidebar_field); -#endif // ENABLE_SHADERS_MANAGER else if (boost::starts_with(sidebar_field, "rotation")) -#if ENABLE_SHADERS_MANAGER -#if ENABLE_GCODE_VIEWER - render_sidebar_rotation_hints(sidebar_field, *shader); -#else render_sidebar_rotation_hints(sidebar_field); -#endif // ENABLE_GCODE_VIEWER -#else - render_sidebar_rotation_hints(sidebar_field); -#endif // ENABLE_SHADERS_MANAGER else if (boost::starts_with(sidebar_field, "scale") || boost::starts_with(sidebar_field, "size")) -#if ENABLE_SHADERS_MANAGER -#if ENABLE_GCODE_VIEWER - render_sidebar_scale_hints(sidebar_field, *shader); -#else render_sidebar_scale_hints(sidebar_field); -#endif // ENABLE_GCODE_VIEWER -#else - render_sidebar_scale_hints(sidebar_field); -#endif // ENABLE_SHADERS_MANAGER else if (boost::starts_with(sidebar_field, "layer")) render_sidebar_layers_hints(sidebar_field); @@ -1999,12 +1975,16 @@ void Selection::render_bounding_box(const BoundingBoxf3& box, float* color) cons } #if ENABLE_GCODE_VIEWER -#if ENABLE_SHADERS_MANAGER -void Selection::render_sidebar_position_hints(const std::string& sidebar_field, GLShaderProgram& shader) const -#else void Selection::render_sidebar_position_hints(const std::string& sidebar_field) const -#endif // ENABLE_SHADERS_MANAGER { +#if ENABLE_SHADERS_MANAGER + auto set_color = [](Axis axis) { + GLShaderProgram* shader = wxGetApp().get_current_shader(); + if (shader != nullptr) + shader->set_uniform("uniform_color", AXES_COLOR[axis], 4); + }; +#endif // ENABLE_SHADERS_MANAGER + #if !ENABLE_SHADERS_MANAGER GLint color_id = ::glGetUniformLocation(m_arrows_shader.get_shader_program_id(), "uniform_color"); #endif // !ENABLE_SHADERS_MANAGER @@ -2012,7 +1992,7 @@ void Selection::render_sidebar_position_hints(const std::string& sidebar_field) if (boost::ends_with(sidebar_field, "x")) { #if ENABLE_SHADERS_MANAGER - shader.set_uniform("uniform_color", AXES_COLOR[0], 4); + set_color(X); #else if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[0])); @@ -2024,7 +2004,7 @@ void Selection::render_sidebar_position_hints(const std::string& sidebar_field) else if (boost::ends_with(sidebar_field, "y")) { #if ENABLE_SHADERS_MANAGER - shader.set_uniform("uniform_color", AXES_COLOR[1], 4); + set_color(Y); #else if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[1])); @@ -2035,7 +2015,7 @@ void Selection::render_sidebar_position_hints(const std::string& sidebar_field) else if (boost::ends_with(sidebar_field, "z")) { #if ENABLE_SHADERS_MANAGER - shader.set_uniform("uniform_color", AXES_COLOR[2], 4); + set_color(Z); #else if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[2])); @@ -2064,12 +2044,16 @@ void Selection::render_sidebar_position_hints(const std::string& sidebar_field) #endif // ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER -#if ENABLE_SHADERS_MANAGER -void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field, GLShaderProgram& shader) const -#else void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field) const -#endif // ENABLE_SHADERS_MANAGER { +#if ENABLE_SHADERS_MANAGER + auto set_color = [](Axis axis) { + GLShaderProgram* shader = wxGetApp().get_current_shader(); + if (shader != nullptr) + shader->set_uniform("uniform_color", AXES_COLOR[axis], 4); + }; +#endif // ENABLE_SHADERS_MANAGER + #if !ENABLE_SHADERS_MANAGER GLint color_id = ::glGetUniformLocation(m_arrows_shader.get_shader_program_id(), "uniform_color"); #endif // !ENABLE_SHADERS_MANAGER @@ -2077,7 +2061,7 @@ void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field) if (boost::ends_with(sidebar_field, "x")) { #if ENABLE_SHADERS_MANAGER - shader.set_uniform("uniform_color", AXES_COLOR[0], 4); + set_color(X); #else if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[0])); @@ -2089,7 +2073,7 @@ void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field) else if (boost::ends_with(sidebar_field, "y")) { #if ENABLE_SHADERS_MANAGER - shader.set_uniform("uniform_color", AXES_COLOR[1], 4); + set_color(Y); #else if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[1])); @@ -2101,7 +2085,7 @@ void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field) else if (boost::ends_with(sidebar_field, "z")) { #if ENABLE_SHADERS_MANAGER - shader.set_uniform("uniform_color", AXES_COLOR[2], 4); + set_color(Z); #else if (color_id >= 0) glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[2])); @@ -2128,52 +2112,7 @@ void Selection::render_sidebar_rotation_hints(const std::string & sidebar_field) } #endif // ENABLE_GCODE_VIEWER -#if ENABLE_GCODE_VIEWER -#if ENABLE_SHADERS_MANAGER -void Selection::render_sidebar_scale_hints(const std::string& sidebar_field, GLShaderProgram& shader) const -#else void Selection::render_sidebar_scale_hints(const std::string& sidebar_field) const -#endif // ENABLE_SHADERS_MANAGER -{ - bool uniform_scale = requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling(); - - if (boost::ends_with(sidebar_field, "x") || uniform_scale) - { - glsafe(::glPushMatrix()); - glsafe(::glRotated(-90.0, 0.0, 0.0, 1.0)); -#if ENABLE_SHADERS_MANAGER - render_sidebar_scale_hint(X, shader); -#else - render_sidebar_scale_hint(X); -#endif // ENABLE_SHADERS_MANAGER - glsafe(::glPopMatrix()); - } - - if (boost::ends_with(sidebar_field, "y") || uniform_scale) - { - glsafe(::glPushMatrix()); -#if ENABLE_SHADERS_MANAGER - render_sidebar_scale_hint(Y, shader); -#else - render_sidebar_scale_hint(Y); -#endif // ENABLE_SHADERS_MANAGER - glsafe(::glPopMatrix()); - } - - if (boost::ends_with(sidebar_field, "z") || uniform_scale) - { - glsafe(::glPushMatrix()); - glsafe(::glRotated(90.0, 1.0, 0.0, 0.0)); -#if ENABLE_SHADERS_MANAGER - render_sidebar_scale_hint(Z, shader); -#else - render_sidebar_scale_hint(Z); -#endif // ENABLE_SHADERS_MANAGER - glsafe(::glPopMatrix()); - } -} -#else -void Selection::render_sidebar_scale_hints(const std::string & sidebar_field) const { bool uniform_scale = requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling(); @@ -2200,7 +2139,6 @@ void Selection::render_sidebar_scale_hints(const std::string & sidebar_field) co glsafe(::glPopMatrix()); } } -#endif // ENABLE_GCODE_VIEWER void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) const { @@ -2293,18 +2231,16 @@ void Selection::render_sidebar_rotation_hint(Axis axis) const } #if ENABLE_SHADERS_MANAGER -#if ENABLE_GCODE_VIEWER -void Selection::render_sidebar_scale_hint(Axis axis, GLShaderProgram& shader) const -#else void Selection::render_sidebar_scale_hint(Axis axis) const -#endif // ENABLE_GCODE_VIEWER #else void Selection::render_sidebar_scale_hint(Axis axis) const #endif // ENABLE_SHADERS_MANAGER { #if ENABLE_GCODE_VIEWER #if ENABLE_SHADERS_MANAGER - shader.set_uniform("uniform_color", (requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling()) ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis], 4); + GLShaderProgram* shader = wxGetApp().get_current_shader(); + if (shader != nullptr) + shader->set_uniform("uniform_color", (requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling()) ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis], 4); #else GLint color_id = ::glGetUniformLocation(m_arrows_shader.get_shader_program_id(), "uniform_color"); diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 2f8ffe58c8..5541cc3fa9 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -385,35 +385,15 @@ private: void render_selected_volumes() const; void render_synchronized_volumes() const; void render_bounding_box(const BoundingBoxf3& box, float* color) const; -#if ENABLE_SHADERS_MANAGER -#if ENABLE_GCODE_VIEWER - void render_sidebar_position_hints(const std::string& sidebar_field, GLShaderProgram& shader) const; - void render_sidebar_rotation_hints(const std::string& sidebar_field, GLShaderProgram& shader) const; - void render_sidebar_scale_hints(const std::string& sidebar_field, GLShaderProgram& shader) const; -#else void render_sidebar_position_hints(const std::string& sidebar_field) const; void render_sidebar_rotation_hints(const std::string& sidebar_field) const; void render_sidebar_scale_hints(const std::string& sidebar_field) const; -#endif // ENABLE_GCODE_VIEWER -#else - void render_sidebar_position_hints(const std::string& sidebar_field) const; - void render_sidebar_rotation_hints(const std::string& sidebar_field) const; - void render_sidebar_scale_hints(const std::string& sidebar_field) const; -#endif // ENABLE_SHADERS_MANAGER void render_sidebar_layers_hints(const std::string& sidebar_field) const; #if !ENABLE_GCODE_VIEWER void render_sidebar_position_hint(Axis axis) const; #endif // !ENABLE_GCODE_VIEWER void render_sidebar_rotation_hint(Axis axis) const; -#if ENABLE_SHADERS_MANAGER -#if ENABLE_GCODE_VIEWER - void render_sidebar_scale_hint(Axis axis, GLShaderProgram& shader) const; -#else void render_sidebar_scale_hint(Axis axis) const; -#endif // ENABLE_GCODE_VIEWER -#else - void render_sidebar_scale_hint(Axis axis) const; -#endif // ENABLE_SHADERS_MANAGER public: enum SyncRotationType { From 4eb1b9432f9c90b6d57e96626d6b313e4b686944 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 21 May 2020 13:07:55 +0200 Subject: [PATCH 086/503] Fixed selection of thumbs into gcode sequential view slider --- src/slic3r/GUI/DoubleSlider.cpp | 7 +++++-- src/slic3r/GUI/DoubleSlider.hpp | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 6173680817..5c16e11eef 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -869,9 +869,12 @@ void Control::draw_cog_icon(wxDC& dc) m_rect_cog_icon = wxRect(x_draw, y_draw, m_cog_icon_dim, m_cog_icon_dim); } -void Control::update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection) +void Control::update_thumb_rect(const wxCoord begin_x, const wxCoord begin_y, const SelectedSlider& selection) { - const wxRect& rect = wxRect(begin_x, begin_y + (selection == ssLower ? int(m_thumb_size.y * 0.5) : 0), m_thumb_size.x, int(m_thumb_size.y*0.5)); + const wxRect rect = is_horizontal() ? + wxRect(begin_x + (selection == ssHigher ? m_thumb_size.x / 2 : 0), begin_y, m_thumb_size.x / 2, m_thumb_size.y) : + wxRect(begin_x, begin_y + (selection == ssLower ? m_thumb_size.y / 2 : 0), m_thumb_size.x, m_thumb_size.y / 2); + if (selection == ssLower) m_rect_lower_thumb = rect; else diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index 71949f7f3e..7fbcf607ea 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -283,7 +283,7 @@ protected: void draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_side = true) const; void draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const; - void update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection); + void update_thumb_rect(const wxCoord begin_x, const wxCoord begin_y, const SelectedSlider& selection); bool detect_selected_slider(const wxPoint& pt); void correct_lower_value(); void correct_higher_value(); From 8a9dbb3414d5f9179e4884ef9a6af05ff83a1bc3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 21 May 2020 13:19:07 +0200 Subject: [PATCH 087/503] ENABLE_SHADERS_MANAGER -> Fixed crash while rendering selection hints --- src/slic3r/GUI/GCodeViewer.cpp | 14 +++++++------- src/slic3r/GUI/Selection.cpp | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index a584dc7eb1..5d298c0ee1 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1108,14 +1108,14 @@ void GCodeViewer::render_legend() const ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); switch (m_view_type) { - case EViewType::FeatureType: { imgui.text(_u8L("Feature type")); break; } - case EViewType::Height: { imgui.text(_u8L("Height (mm)")); break; } - case EViewType::Width: { imgui.text(_u8L("Width (mm)")); break; } - case EViewType::Feedrate: { imgui.text(_u8L("Speed (mm/s)")); break; } - case EViewType::FanSpeed: { imgui.text(_u8L("Fan Speed (%%)")); break; } + case EViewType::FeatureType: { imgui.text(_u8L("Feature type")); break; } + case EViewType::Height: { imgui.text(_u8L("Height (mm)")); break; } + case EViewType::Width: { imgui.text(_u8L("Width (mm)")); break; } + case EViewType::Feedrate: { imgui.text(_u8L("Speed (mm/s)")); break; } + case EViewType::FanSpeed: { imgui.text(_u8L("Fan Speed (%%)")); break; } case EViewType::VolumetricRate: { imgui.text(_u8L("Volumetric flow rate (mm³/s)")); break; } - case EViewType::Tool: { imgui.text(_u8L("Tool")); break; } - case EViewType::ColorPrint: { imgui.text(_u8L("Color Print")); break; } + case EViewType::Tool: { imgui.text(_u8L("Tool")); break; } + case EViewType::ColorPrint: { imgui.text(_u8L("Color Print")); break; } default: { break; } } ImGui::PopStyleColor(); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 2949247b80..f890f0f012 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -17,7 +17,7 @@ #include #endif // ENABLE_GCODE_VIEWER -static const float UNIFORM_SCALE_COLOR[3] = { 1.0f, 0.38f, 0.0f }; +static const float UNIFORM_SCALE_COLOR[4] = { 0.923f, 0.504f, 0.264f, 1.0f }; namespace Slic3r { namespace GUI { From df010a1d4e26d8279fc61262481c0f33132a3841 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 22 May 2020 09:45:31 +0200 Subject: [PATCH 088/503] Added methods GUI_App::is_gl_version_greater_or_equal_to() and GUI_App::is_glsl_version_greater_or_equal_to() --- src/slic3r/GUI/GUI_App.hpp | 3 +++ src/slic3r/GUI/OpenGLManager.cpp | 25 +++++++++++++++++++------ src/slic3r/GUI/OpenGLManager.hpp | 1 + 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 9bd8697815..cbe2aafe16 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -222,6 +222,9 @@ public: GLShaderProgram* get_current_shader() { return m_opengl_mgr.get_current_shader(); } #endif // ENABLE_SHADERS_MANAGER + bool is_gl_version_greater_or_equal_to(unsigned int major, unsigned int minor) const { return m_opengl_mgr.get_gl_info().is_version_greater_or_equal_to(major, minor); } + bool is_glsl_version_greater_or_equal_to(unsigned int major, unsigned int minor) const { return m_opengl_mgr.get_gl_info().is_glsl_version_greater_or_equal_to(major, minor); } + private: bool on_init_inner(); void init_app_config(); diff --git a/src/slic3r/GUI/OpenGLManager.cpp b/src/slic3r/GUI/OpenGLManager.cpp index deaa3cd199..b21fd01439 100644 --- a/src/slic3r/GUI/OpenGLManager.cpp +++ b/src/slic3r/GUI/OpenGLManager.cpp @@ -130,18 +130,15 @@ void OpenGLManager::GLInfo::detect() const m_detected = true; } -bool OpenGLManager::GLInfo::is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const +static bool version_greater_or_equal_to(const std::string& version, unsigned int major, unsigned int minor) { - if (!m_detected) - detect(); - #if ENABLE_SHADERS_MANAGER - if (m_version == "N/A") + if (version == "N/A") return false; #endif // ENABLE_SHADERS_MANAGER std::vector tokens; - boost::split(tokens, m_version, boost::is_any_of(" "), boost::token_compress_on); + boost::split(tokens, version, boost::is_any_of(" "), boost::token_compress_on); if (tokens.empty()) return false; @@ -166,6 +163,22 @@ bool OpenGLManager::GLInfo::is_version_greater_or_equal_to(unsigned int major, u return gl_minor >= minor; } +bool OpenGLManager::GLInfo::is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const +{ + if (!m_detected) + detect(); + + return version_greater_or_equal_to(m_version, major, minor); +} + +bool OpenGLManager::GLInfo::is_glsl_version_greater_or_equal_to(unsigned int major, unsigned int minor) const +{ + if (!m_detected) + detect(); + + return version_greater_or_equal_to(m_glsl_version, major, minor); +} + std::string OpenGLManager::GLInfo::to_string(bool format_as_html, bool extensions) const { if (!m_detected) diff --git a/src/slic3r/GUI/OpenGLManager.hpp b/src/slic3r/GUI/OpenGLManager.hpp index fb3b33494b..e33df42491 100644 --- a/src/slic3r/GUI/OpenGLManager.hpp +++ b/src/slic3r/GUI/OpenGLManager.hpp @@ -45,6 +45,7 @@ public: float get_max_anisotropy() const; bool is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const; + bool is_glsl_version_greater_or_equal_to(unsigned int major, unsigned int minor) const; std::string to_string(bool format_as_html, bool extensions) const; From 082a30a5db051072c846a94a72270ed4cdcbd89e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 22 May 2020 09:49:42 +0200 Subject: [PATCH 089/503] ENABLE_SHADERS_MANAGER -> Added method GLShaderProgram::set_uniform(const char* name, double value) --- src/slic3r/GUI/GLShader.cpp | 5 +++++ src/slic3r/GUI/GLShader.hpp | 1 + 2 files changed, 6 insertions(+) diff --git a/src/slic3r/GUI/GLShader.cpp b/src/slic3r/GUI/GLShader.cpp index d0b9267945..38ded63326 100644 --- a/src/slic3r/GUI/GLShader.cpp +++ b/src/slic3r/GUI/GLShader.cpp @@ -211,6 +211,11 @@ bool GLShaderProgram::set_uniform(const char* name, float value) const return false; } +bool GLShaderProgram::set_uniform(const char* name, double value) const +{ + return set_uniform(name, static_cast(value)); +} + bool GLShaderProgram::set_uniform(const char* name, const std::array& value) const { int id = get_uniform_location(name); diff --git a/src/slic3r/GUI/GLShader.hpp b/src/slic3r/GUI/GLShader.hpp index 6994b91caf..91a1f66258 100644 --- a/src/slic3r/GUI/GLShader.hpp +++ b/src/slic3r/GUI/GLShader.hpp @@ -43,6 +43,7 @@ public: bool set_uniform(const char* name, int value) const; bool set_uniform(const char* name, bool value) const; bool set_uniform(const char* name, float value) const; + bool set_uniform(const char* name, double value) const; bool set_uniform(const char* name, const std::array& value) const; bool set_uniform(const char* name, const std::array& value) const; bool set_uniform(const char* name, const std::array& value) const; From f345e583582872c06a4421a377ac70d222ff9abb Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 22 May 2020 09:51:57 +0200 Subject: [PATCH 090/503] Fix in ENABLE_CAMERA_STATISTICS --- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 5f9325b14d..5c49eb8295 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2182,7 +2182,7 @@ void GLCanvas3D::render() #endif // ENABLE_RENDER_STATISTICS #if ENABLE_CAMERA_STATISTICS - m_camera.debug_render(); + camera.debug_render(); #endif // ENABLE_CAMERA_STATISTICS std::string tooltip; From 6e279cbec28b6faf2b65b1016341718e0a8bd287 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 22 May 2020 10:43:59 +0200 Subject: [PATCH 091/503] GCodeViewer -> Refactoring of sequential view marker positioning --- src/slic3r/GUI/GCodeViewer.cpp | 8 +++++++- src/slic3r/GUI/GCodeViewer.hpp | 6 ++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 5d298c0ee1..b727388464 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -156,6 +156,12 @@ void GCodeViewer::SequentialView::Marker::init() #endif // !ENABLE_SHADERS_MANAGER } +void GCodeViewer::SequentialView::Marker::set_world_position(const Vec3f& position) +{ + m_world_transform = (Geometry::assemble_transform(position.cast()) * Geometry::assemble_transform(m_model.get_bounding_box().size()[2] * Vec3d::UnitZ(), { M_PI, 0.0, 0.0 })).cast(); + m_world_bounding_box = m_model.get_bounding_box().transformed(m_world_transform.cast()); +} + void GCodeViewer::SequentialView::Marker::render() const { #if ENABLE_SHADERS_MANAGER @@ -346,7 +352,7 @@ void GCodeViewer::render() const glsafe(::glEnable(GL_DEPTH_TEST)); render_toolpaths(); - m_sequential_view.marker.set_world_transform(Geometry::assemble_transform(m_sequential_view.current_position.cast() + (0.5 + 12.0) * Vec3d::UnitZ(), { M_PI, 0.0, 0.0 }).cast()); + m_sequential_view.marker.set_world_position(m_sequential_view.current_position + m_sequential_view_marker_z_offset * Vec3f::UnitZ()); m_sequential_view.marker.render(); render_shells(); render_legend(); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index c1f9cfdd7b..45a87a0069 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -211,6 +211,7 @@ public: { GL_Model m_model; Transform3f m_world_transform; + BoundingBoxf3 m_world_bounding_box; std::array m_color{ 1.0f, 1.0f, 1.0f, 1.0f }; bool m_visible{ false }; #if !ENABLE_SHADERS_MANAGER @@ -220,9 +221,9 @@ public: public: void init(); - const BoundingBoxf3& get_bounding_box() const { return m_model.get_bounding_box(); } + const BoundingBoxf3& get_bounding_box() const { return m_world_bounding_box; } - void set_world_transform(const Transform3f& transform) { m_world_transform = transform; } + void set_world_position(const Vec3f& position); void set_color(const std::array& color) { m_color = color; } bool is_visible() const { return m_visible; } @@ -273,6 +274,7 @@ private: std::vector m_extruder_ids; mutable Extrusions m_extrusions; mutable SequentialView m_sequential_view; + float m_sequential_view_marker_z_offset{ 0.5f }; Shells m_shells; EViewType m_view_type{ EViewType::FeatureType }; bool m_legend_enabled{ true }; From 83ea38c2f31a452d8ad66730398887ed087f79d3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 22 May 2020 11:52:07 +0200 Subject: [PATCH 092/503] GCodeViewer -> Refactoring of options coloring + options added to legend --- src/slic3r/GUI/GCodeViewer.cpp | 74 +++++++++++++++++++++++++--------- src/slic3r/GUI/GCodeViewer.hpp | 11 +++++ 2 files changed, 67 insertions(+), 18 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index b727388464..453b9ecaeb 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -233,6 +233,15 @@ const std::vector GCodeViewer::Extrusion_Role_Colors {{ { 0.00f, 0.00f, 0.00f } // erMixed }}; +const std::vector GCodeViewer::Options_Colors {{ + { 1.00f, 0.00f, 1.00f }, // Retractions + { 0.00f, 1.00f, 1.00f }, // Unretractions + { 1.00f, 1.00f, 1.00f }, // ToolChanges + { 1.00f, 0.00f, 0.00f }, // ColorChanges + { 0.00f, 1.00f, 0.00f }, // PausePrints + { 0.00f, 0.00f, 1.00f } // CustomGCodes +}}; + const std::vector GCodeViewer::Travel_Colors {{ { 0.0f, 0.0f, 0.5f }, // Move { 0.0f, 0.5f, 0.0f }, // Extrude @@ -857,11 +866,10 @@ void GCodeViewer::render_toolpaths() const { case GCodeProcessor::EMoveType::Tool_change: { - Color color = { 1.0f, 1.0f, 1.0f }; #if ENABLE_SHADERS_MANAGER - shader->set_uniform("uniform_color", color); + shader->set_uniform("uniform_color", Options_Colors[static_cast(EOptionsColors::ToolChanges)]); #else - set_color(static_cast(buffer.shader.get_shader_program_id()), color); + set_color(static_cast(buffer.shader.get_shader_program_id()), Options_Colors[static_cast(EOptionsColors::ToolChanges)]); #endif // ENABLE_SHADERS_MANAGER for (const RenderPath& path : buffer.render_paths) { @@ -877,11 +885,10 @@ void GCodeViewer::render_toolpaths() const } case GCodeProcessor::EMoveType::Color_change: { - Color color = { 1.0f, 0.0f, 0.0f }; #if ENABLE_SHADERS_MANAGER - shader->set_uniform("uniform_color", color); + shader->set_uniform("uniform_color", Options_Colors[static_cast(EOptionsColors::ColorChanges)]); #else - set_color(static_cast(buffer.shader.get_shader_program_id()), color); + set_color(static_cast(buffer.shader.get_shader_program_id()), Options_Colors[static_cast(EOptionsColors::ColorChanges)]); #endif // ENABLE_SHADERS_MANAGER for (const RenderPath& path : buffer.render_paths) { @@ -897,11 +904,10 @@ void GCodeViewer::render_toolpaths() const } case GCodeProcessor::EMoveType::Pause_Print: { - Color color = { 0.0f, 1.0f, 0.0f }; #if ENABLE_SHADERS_MANAGER - shader->set_uniform("uniform_color", color); + shader->set_uniform("uniform_color", Options_Colors[static_cast(EOptionsColors::PausePrints)]); #else - set_color(static_cast(buffer.shader.get_shader_program_id()), color); + set_color(static_cast(buffer.shader.get_shader_program_id()), Options_Colors[static_cast(EOptionsColors::PausePrints)]); #endif // ENABLE_SHADERS_MANAGER for (const RenderPath& path : buffer.render_paths) { @@ -917,11 +923,10 @@ void GCodeViewer::render_toolpaths() const } case GCodeProcessor::EMoveType::Custom_GCode: { - Color color = { 0.0f, 0.0f, 1.0f }; #if ENABLE_SHADERS_MANAGER - shader->set_uniform("uniform_color", color); + shader->set_uniform("uniform_color", Options_Colors[static_cast(EOptionsColors::CustomGCodes)]); #else - set_color(static_cast(buffer.shader.get_shader_program_id()), color); + set_color(static_cast(buffer.shader.get_shader_program_id()), Options_Colors[static_cast(EOptionsColors::CustomGCodes)]); #endif // ENABLE_SHADERS_MANAGER for (const RenderPath& path : buffer.render_paths) { @@ -937,11 +942,10 @@ void GCodeViewer::render_toolpaths() const } case GCodeProcessor::EMoveType::Retract: { - Color color = { 1.0f, 0.0f, 1.0f }; #if ENABLE_SHADERS_MANAGER - shader->set_uniform("uniform_color", color); + shader->set_uniform("uniform_color", Options_Colors[static_cast(EOptionsColors::Retractions)]); #else - set_color(static_cast(buffer.shader.get_shader_program_id()), color); + set_color(static_cast(buffer.shader.get_shader_program_id()), Options_Colors[static_cast(EOptionsColors::Retractions)]); #endif // ENABLE_SHADERS_MANAGER for (const RenderPath& path : buffer.render_paths) { @@ -957,11 +961,10 @@ void GCodeViewer::render_toolpaths() const } case GCodeProcessor::EMoveType::Unretract: { - Color color = { 0.0f, 1.0f, 1.0f }; #if ENABLE_SHADERS_MANAGER - shader->set_uniform("uniform_color", color); + shader->set_uniform("uniform_color", Options_Colors[static_cast(EOptionsColors::Unretractions)]); #else - set_color(static_cast(buffer.shader.get_shader_program_id()), color); + set_color(static_cast(buffer.shader.get_shader_program_id()), Options_Colors[static_cast(EOptionsColors::Unretractions)]); #endif // ENABLE_SHADERS_MANAGER for (const RenderPath& path : buffer.render_paths) { @@ -1279,6 +1282,41 @@ void GCodeViewer::render_legend() const } } + auto any_option_visible = [this]() { + return m_buffers[buffer_id(GCodeProcessor::EMoveType::Color_change)].visible || + m_buffers[buffer_id(GCodeProcessor::EMoveType::Custom_GCode)].visible || + m_buffers[buffer_id(GCodeProcessor::EMoveType::Pause_Print)].visible || + m_buffers[buffer_id(GCodeProcessor::EMoveType::Retract)].visible || + m_buffers[buffer_id(GCodeProcessor::EMoveType::Tool_change)].visible || + m_buffers[buffer_id(GCodeProcessor::EMoveType::Unretract)].visible; + }; + + auto add_option = [this, add_item](GCodeProcessor::EMoveType move_type, EOptionsColors color, const std::string& text) { + const IBuffer& buffer = m_buffers[buffer_id(move_type)]; + if (buffer.visible && buffer.indices_count > 0) + add_item(Options_Colors[static_cast(color)], text); + }; + + // options + if (any_option_visible()) + { + // title + ImGui::Spacing(); + ImGui::Spacing(); + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(_u8L("Options")); + ImGui::PopStyleColor(); + ImGui::Separator(); + + // items + add_option(GCodeProcessor::EMoveType::Retract, EOptionsColors::Retractions, _u8L("Retractions")); + add_option(GCodeProcessor::EMoveType::Unretract, EOptionsColors::Unretractions, _u8L("Unretractions")); + add_option(GCodeProcessor::EMoveType::Tool_change, EOptionsColors::ToolChanges, _u8L("Tool changes")); + add_option(GCodeProcessor::EMoveType::Color_change, EOptionsColors::ColorChanges, _u8L("Color changes")); + add_option(GCodeProcessor::EMoveType::Pause_Print, EOptionsColors::PausePrints, _u8L("Pause prints")); + add_option(GCodeProcessor::EMoveType::Custom_GCode, EOptionsColors::CustomGCodes, _u8L("Custom GCodes")); + } + imgui.end(); ImGui::PopStyleVar(); } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 45a87a0069..72f45aedca 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -22,9 +22,20 @@ class GCodeViewer { using Color = std::array; static const std::vector Extrusion_Role_Colors; + static const std::vector Options_Colors; static const std::vector Travel_Colors; static const std::vector Range_Colors; + enum class EOptionsColors : unsigned char + { + Retractions, + Unretractions, + ToolChanges, + ColorChanges, + PausePrints, + CustomGCodes + }; + // buffer containing vertices data struct VBuffer { From 4d05ec085638d8c5cc94c1291e2c78fed1f6101e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 22 May 2020 13:21:43 +0200 Subject: [PATCH 093/503] GCodeViewer -> New shaders for options --- resources/shaders/colorchanges.fs | 45 ------------ resources/shaders/colorchanges.vs | 17 ----- resources/shaders/customs.fs | 45 ------------ resources/shaders/customs.vs | 17 ----- resources/shaders/options_110.fs | 8 +++ resources/shaders/options_110.vs | 11 +++ resources/shaders/options_120.fs | 15 ++++ resources/shaders/options_120.vs | 11 +++ resources/shaders/pauses.fs | 45 ------------ resources/shaders/pauses.vs | 17 ----- resources/shaders/retractions.fs | 45 ------------ resources/shaders/retractions.vs | 17 ----- resources/shaders/toolchanges.fs | 45 ------------ resources/shaders/toolchanges.vs | 17 ----- resources/shaders/unretractions.fs | 45 ------------ resources/shaders/unretractions.vs | 17 ----- src/slic3r/GUI/GCodeViewer.cpp | 108 +++++++++++++++++++++------- src/slic3r/GUI/GLShadersManager.cpp | 15 +--- 18 files changed, 132 insertions(+), 408 deletions(-) delete mode 100644 resources/shaders/colorchanges.fs delete mode 100644 resources/shaders/colorchanges.vs delete mode 100644 resources/shaders/customs.fs delete mode 100644 resources/shaders/customs.vs create mode 100644 resources/shaders/options_110.fs create mode 100644 resources/shaders/options_110.vs create mode 100644 resources/shaders/options_120.fs create mode 100644 resources/shaders/options_120.vs delete mode 100644 resources/shaders/pauses.fs delete mode 100644 resources/shaders/pauses.vs delete mode 100644 resources/shaders/retractions.fs delete mode 100644 resources/shaders/retractions.vs delete mode 100644 resources/shaders/toolchanges.fs delete mode 100644 resources/shaders/toolchanges.vs delete mode 100644 resources/shaders/unretractions.fs delete mode 100644 resources/shaders/unretractions.vs diff --git a/resources/shaders/colorchanges.fs b/resources/shaders/colorchanges.fs deleted file mode 100644 index fc81e487fb..0000000000 --- a/resources/shaders/colorchanges.fs +++ /dev/null @@ -1,45 +0,0 @@ -#version 110 - -#define INTENSITY_AMBIENT 0.3 -#define INTENSITY_CORRECTION 0.6 - -// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) -const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); -#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SHININESS 20.0 - -// normalized values for (1./1.43, 0.2/1.43, 1./1.43) -const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); -#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) - -uniform vec3 uniform_color; - -varying vec3 eye_position; -varying vec3 eye_normal; -//varying float world_normal_z; - -// x = tainted, y = specular; -vec2 intensity; - -void main() -{ - vec3 normal = normalize(eye_normal); - - // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. - // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. - float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); - - intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; - intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); - - // Perform the same lighting calculation for the 2nd light source (no specular applied). - NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); - intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; - -// // darkens fragments whose normal points downward -// if (world_normal_z < 0.0) -// intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); - - gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color * intensity.x, 1.0); -} diff --git a/resources/shaders/colorchanges.vs b/resources/shaders/colorchanges.vs deleted file mode 100644 index 3b78a59700..0000000000 --- a/resources/shaders/colorchanges.vs +++ /dev/null @@ -1,17 +0,0 @@ -#version 110 - -varying vec3 eye_position; -varying vec3 eye_normal; -//// world z component of the normal used to darken the lower side of the toolpaths -//varying float world_normal_z; - -void main() -{ - eye_position = (gl_ModelViewMatrix * gl_Vertex).xyz; - eye_normal = gl_NormalMatrix * vec3(0.0, 0.0, 1.0); -// eye_normal = gl_NormalMatrix * gl_Normal; -// world_normal_z = gl_Normal.z; - gl_Position = ftransform(); - - gl_PointSize = 15.0; -} diff --git a/resources/shaders/customs.fs b/resources/shaders/customs.fs deleted file mode 100644 index fc81e487fb..0000000000 --- a/resources/shaders/customs.fs +++ /dev/null @@ -1,45 +0,0 @@ -#version 110 - -#define INTENSITY_AMBIENT 0.3 -#define INTENSITY_CORRECTION 0.6 - -// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) -const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); -#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SHININESS 20.0 - -// normalized values for (1./1.43, 0.2/1.43, 1./1.43) -const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); -#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) - -uniform vec3 uniform_color; - -varying vec3 eye_position; -varying vec3 eye_normal; -//varying float world_normal_z; - -// x = tainted, y = specular; -vec2 intensity; - -void main() -{ - vec3 normal = normalize(eye_normal); - - // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. - // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. - float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); - - intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; - intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); - - // Perform the same lighting calculation for the 2nd light source (no specular applied). - NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); - intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; - -// // darkens fragments whose normal points downward -// if (world_normal_z < 0.0) -// intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); - - gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color * intensity.x, 1.0); -} diff --git a/resources/shaders/customs.vs b/resources/shaders/customs.vs deleted file mode 100644 index 3b78a59700..0000000000 --- a/resources/shaders/customs.vs +++ /dev/null @@ -1,17 +0,0 @@ -#version 110 - -varying vec3 eye_position; -varying vec3 eye_normal; -//// world z component of the normal used to darken the lower side of the toolpaths -//varying float world_normal_z; - -void main() -{ - eye_position = (gl_ModelViewMatrix * gl_Vertex).xyz; - eye_normal = gl_NormalMatrix * vec3(0.0, 0.0, 1.0); -// eye_normal = gl_NormalMatrix * gl_Normal; -// world_normal_z = gl_Normal.z; - gl_Position = ftransform(); - - gl_PointSize = 15.0; -} diff --git a/resources/shaders/options_110.fs b/resources/shaders/options_110.fs new file mode 100644 index 0000000000..3722058c87 --- /dev/null +++ b/resources/shaders/options_110.fs @@ -0,0 +1,8 @@ +#version 120 + +uniform vec3 uniform_color; + +void main() +{ + gl_FragColor = vec4(uniform_color, 1.0); +} diff --git a/resources/shaders/options_110.vs b/resources/shaders/options_110.vs new file mode 100644 index 0000000000..5361c88cec --- /dev/null +++ b/resources/shaders/options_110.vs @@ -0,0 +1,11 @@ +#version 110 + +uniform float zoom; +// x = min, y = max +uniform vec2 point_sizes; + +void main() +{ + gl_PointSize = clamp(zoom, point_sizes.x, point_sizes.y); + gl_Position = ftransform(); +} diff --git a/resources/shaders/options_120.fs b/resources/shaders/options_120.fs new file mode 100644 index 0000000000..090cde292f --- /dev/null +++ b/resources/shaders/options_120.fs @@ -0,0 +1,15 @@ +#version 120 + +uniform vec3 uniform_color; + +void main() +{ + vec2 pos = gl_PointCoord - vec2(0.5, 0.5); + float sq_radius = pos.x * pos.x + pos.y * pos.y; + if (sq_radius > 0.25) + discard; + else if (sq_radius > 0.180625) + gl_FragColor = vec4(0.5 * uniform_color, 1.0); + else + gl_FragColor = vec4(uniform_color, 1.0); +} diff --git a/resources/shaders/options_120.vs b/resources/shaders/options_120.vs new file mode 100644 index 0000000000..ebf7428c9a --- /dev/null +++ b/resources/shaders/options_120.vs @@ -0,0 +1,11 @@ +#version 120 + +uniform float zoom; +// x = min, y = max +uniform vec2 point_sizes; + +void main() +{ + gl_PointSize = clamp(zoom, point_sizes.x, point_sizes.y); + gl_Position = ftransform(); +} diff --git a/resources/shaders/pauses.fs b/resources/shaders/pauses.fs deleted file mode 100644 index fc81e487fb..0000000000 --- a/resources/shaders/pauses.fs +++ /dev/null @@ -1,45 +0,0 @@ -#version 110 - -#define INTENSITY_AMBIENT 0.3 -#define INTENSITY_CORRECTION 0.6 - -// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) -const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); -#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SHININESS 20.0 - -// normalized values for (1./1.43, 0.2/1.43, 1./1.43) -const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); -#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) - -uniform vec3 uniform_color; - -varying vec3 eye_position; -varying vec3 eye_normal; -//varying float world_normal_z; - -// x = tainted, y = specular; -vec2 intensity; - -void main() -{ - vec3 normal = normalize(eye_normal); - - // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. - // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. - float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); - - intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; - intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); - - // Perform the same lighting calculation for the 2nd light source (no specular applied). - NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); - intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; - -// // darkens fragments whose normal points downward -// if (world_normal_z < 0.0) -// intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); - - gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color * intensity.x, 1.0); -} diff --git a/resources/shaders/pauses.vs b/resources/shaders/pauses.vs deleted file mode 100644 index 3b78a59700..0000000000 --- a/resources/shaders/pauses.vs +++ /dev/null @@ -1,17 +0,0 @@ -#version 110 - -varying vec3 eye_position; -varying vec3 eye_normal; -//// world z component of the normal used to darken the lower side of the toolpaths -//varying float world_normal_z; - -void main() -{ - eye_position = (gl_ModelViewMatrix * gl_Vertex).xyz; - eye_normal = gl_NormalMatrix * vec3(0.0, 0.0, 1.0); -// eye_normal = gl_NormalMatrix * gl_Normal; -// world_normal_z = gl_Normal.z; - gl_Position = ftransform(); - - gl_PointSize = 15.0; -} diff --git a/resources/shaders/retractions.fs b/resources/shaders/retractions.fs deleted file mode 100644 index fc81e487fb..0000000000 --- a/resources/shaders/retractions.fs +++ /dev/null @@ -1,45 +0,0 @@ -#version 110 - -#define INTENSITY_AMBIENT 0.3 -#define INTENSITY_CORRECTION 0.6 - -// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) -const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); -#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SHININESS 20.0 - -// normalized values for (1./1.43, 0.2/1.43, 1./1.43) -const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); -#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) - -uniform vec3 uniform_color; - -varying vec3 eye_position; -varying vec3 eye_normal; -//varying float world_normal_z; - -// x = tainted, y = specular; -vec2 intensity; - -void main() -{ - vec3 normal = normalize(eye_normal); - - // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. - // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. - float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); - - intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; - intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); - - // Perform the same lighting calculation for the 2nd light source (no specular applied). - NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); - intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; - -// // darkens fragments whose normal points downward -// if (world_normal_z < 0.0) -// intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); - - gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color * intensity.x, 1.0); -} diff --git a/resources/shaders/retractions.vs b/resources/shaders/retractions.vs deleted file mode 100644 index 3b78a59700..0000000000 --- a/resources/shaders/retractions.vs +++ /dev/null @@ -1,17 +0,0 @@ -#version 110 - -varying vec3 eye_position; -varying vec3 eye_normal; -//// world z component of the normal used to darken the lower side of the toolpaths -//varying float world_normal_z; - -void main() -{ - eye_position = (gl_ModelViewMatrix * gl_Vertex).xyz; - eye_normal = gl_NormalMatrix * vec3(0.0, 0.0, 1.0); -// eye_normal = gl_NormalMatrix * gl_Normal; -// world_normal_z = gl_Normal.z; - gl_Position = ftransform(); - - gl_PointSize = 15.0; -} diff --git a/resources/shaders/toolchanges.fs b/resources/shaders/toolchanges.fs deleted file mode 100644 index fc81e487fb..0000000000 --- a/resources/shaders/toolchanges.fs +++ /dev/null @@ -1,45 +0,0 @@ -#version 110 - -#define INTENSITY_AMBIENT 0.3 -#define INTENSITY_CORRECTION 0.6 - -// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) -const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); -#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SHININESS 20.0 - -// normalized values for (1./1.43, 0.2/1.43, 1./1.43) -const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); -#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) - -uniform vec3 uniform_color; - -varying vec3 eye_position; -varying vec3 eye_normal; -//varying float world_normal_z; - -// x = tainted, y = specular; -vec2 intensity; - -void main() -{ - vec3 normal = normalize(eye_normal); - - // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. - // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. - float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); - - intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; - intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); - - // Perform the same lighting calculation for the 2nd light source (no specular applied). - NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); - intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; - -// // darkens fragments whose normal points downward -// if (world_normal_z < 0.0) -// intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); - - gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color * intensity.x, 1.0); -} diff --git a/resources/shaders/toolchanges.vs b/resources/shaders/toolchanges.vs deleted file mode 100644 index 3b78a59700..0000000000 --- a/resources/shaders/toolchanges.vs +++ /dev/null @@ -1,17 +0,0 @@ -#version 110 - -varying vec3 eye_position; -varying vec3 eye_normal; -//// world z component of the normal used to darken the lower side of the toolpaths -//varying float world_normal_z; - -void main() -{ - eye_position = (gl_ModelViewMatrix * gl_Vertex).xyz; - eye_normal = gl_NormalMatrix * vec3(0.0, 0.0, 1.0); -// eye_normal = gl_NormalMatrix * gl_Normal; -// world_normal_z = gl_Normal.z; - gl_Position = ftransform(); - - gl_PointSize = 15.0; -} diff --git a/resources/shaders/unretractions.fs b/resources/shaders/unretractions.fs deleted file mode 100644 index fc81e487fb..0000000000 --- a/resources/shaders/unretractions.fs +++ /dev/null @@ -1,45 +0,0 @@ -#version 110 - -#define INTENSITY_AMBIENT 0.3 -#define INTENSITY_CORRECTION 0.6 - -// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) -const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); -#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SHININESS 20.0 - -// normalized values for (1./1.43, 0.2/1.43, 1./1.43) -const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); -#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) - -uniform vec3 uniform_color; - -varying vec3 eye_position; -varying vec3 eye_normal; -//varying float world_normal_z; - -// x = tainted, y = specular; -vec2 intensity; - -void main() -{ - vec3 normal = normalize(eye_normal); - - // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. - // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. - float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); - - intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; - intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); - - // Perform the same lighting calculation for the 2nd light source (no specular applied). - NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); - intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; - -// // darkens fragments whose normal points downward -// if (world_normal_z < 0.0) -// intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); - - gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color * intensity.x, 1.0); -} diff --git a/resources/shaders/unretractions.vs b/resources/shaders/unretractions.vs deleted file mode 100644 index 3b78a59700..0000000000 --- a/resources/shaders/unretractions.vs +++ /dev/null @@ -1,17 +0,0 @@ -#version 110 - -varying vec3 eye_position; -varying vec3 eye_normal; -//// world z component of the normal used to darken the lower side of the toolpaths -//varying float world_normal_z; - -void main() -{ - eye_position = (gl_ModelViewMatrix * gl_Vertex).xyz; - eye_normal = gl_NormalMatrix * vec3(0.0, 0.0, 1.0); -// eye_normal = gl_NormalMatrix * gl_Normal; -// world_normal_z = gl_Normal.z; - gl_Position = ftransform(); - - gl_PointSize = 15.0; -} diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 453b9ecaeb..cf2f689cef 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -440,12 +440,12 @@ void GCodeViewer::init_shaders() { switch (buffer_type(i)) { - case GCodeProcessor::EMoveType::Tool_change: { m_buffers[i].shader = "toolchanges"; break; } - case GCodeProcessor::EMoveType::Color_change: { m_buffers[i].shader = "colorchanges"; break; } - case GCodeProcessor::EMoveType::Pause_Print: { m_buffers[i].shader = "pauses"; break; } - case GCodeProcessor::EMoveType::Custom_GCode: { m_buffers[i].shader = "customs"; break; } - case GCodeProcessor::EMoveType::Retract: { m_buffers[i].shader = "retractions"; break; } - case GCodeProcessor::EMoveType::Unretract: { m_buffers[i].shader = "unretractions"; break; } + case GCodeProcessor::EMoveType::Tool_change: { m_buffers[i].shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110"; break; } + case GCodeProcessor::EMoveType::Color_change: { m_buffers[i].shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110"; break; } + case GCodeProcessor::EMoveType::Pause_Print: { m_buffers[i].shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110"; break; } + case GCodeProcessor::EMoveType::Custom_GCode: { m_buffers[i].shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110"; break; } + case GCodeProcessor::EMoveType::Retract: { m_buffers[i].shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110"; break; } + case GCodeProcessor::EMoveType::Unretract: { m_buffers[i].shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110"; break; } case GCodeProcessor::EMoveType::Extrude: { m_buffers[i].shader = "extrusions"; break; } case GCodeProcessor::EMoveType::Travel: { m_buffers[i].shader = "travels"; break; } default: { break; } @@ -827,6 +827,12 @@ void GCodeViewer::render_toolpaths() const }; #endif // !ENABLE_SHADERS_MANAGER + bool is_glsl_120 = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20); + int detected_point_sizes[2]; + ::glGetIntegerv(GL_ALIASED_POINT_SIZE_RANGE, detected_point_sizes); + std::array point_sizes = { 2.0f, std::min(64.0f, static_cast(detected_point_sizes[1])) }; + double zoom = wxGetApp().plater()->get_camera().get_zoom(); + glsafe(::glCullFace(GL_BACK)); glsafe(::glLineWidth(3.0f)); @@ -868,94 +874,139 @@ void GCodeViewer::render_toolpaths() const { #if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", Options_Colors[static_cast(EOptionsColors::ToolChanges)]); + shader->set_uniform("zoom", zoom); + shader->set_uniform("point_sizes", point_sizes); + if (is_glsl_120) + { + glsafe(::glEnable(GL_POINT_SPRITE)); + glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); + } #else set_color(static_cast(buffer.shader.get_shader_program_id()), Options_Colors[static_cast(EOptionsColors::ToolChanges)]); #endif // ENABLE_SHADERS_MANAGER for (const RenderPath& path : buffer.render_paths) { - glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); - glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); - #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } + if (is_glsl_120) + { + glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); + glsafe(::glDisable(GL_POINT_SPRITE)); + } break; } case GCodeProcessor::EMoveType::Color_change: { #if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", Options_Colors[static_cast(EOptionsColors::ColorChanges)]); + shader->set_uniform("zoom", zoom); + shader->set_uniform("point_sizes", point_sizes); + if (is_glsl_120) + { + glsafe(::glEnable(GL_POINT_SPRITE)); + glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); + } #else set_color(static_cast(buffer.shader.get_shader_program_id()), Options_Colors[static_cast(EOptionsColors::ColorChanges)]); #endif // ENABLE_SHADERS_MANAGER for (const RenderPath& path : buffer.render_paths) { - glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); - glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); - #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } + if (is_glsl_120) + { + glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); + glsafe(::glDisable(GL_POINT_SPRITE)); + } break; } case GCodeProcessor::EMoveType::Pause_Print: { #if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", Options_Colors[static_cast(EOptionsColors::PausePrints)]); + shader->set_uniform("zoom", zoom); + shader->set_uniform("point_sizes", point_sizes); + if (is_glsl_120) + { + glsafe(::glEnable(GL_POINT_SPRITE)); + glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); + } #else set_color(static_cast(buffer.shader.get_shader_program_id()), Options_Colors[static_cast(EOptionsColors::PausePrints)]); #endif // ENABLE_SHADERS_MANAGER for (const RenderPath& path : buffer.render_paths) { - glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); - glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); - #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } + if (is_glsl_120) + { + glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); + glsafe(::glDisable(GL_POINT_SPRITE)); + } break; } case GCodeProcessor::EMoveType::Custom_GCode: { #if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", Options_Colors[static_cast(EOptionsColors::CustomGCodes)]); + shader->set_uniform("zoom", zoom); + shader->set_uniform("point_sizes", point_sizes); + if (is_glsl_120) + { + glsafe(::glEnable(GL_POINT_SPRITE)); + glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); + } #else set_color(static_cast(buffer.shader.get_shader_program_id()), Options_Colors[static_cast(EOptionsColors::CustomGCodes)]); #endif // ENABLE_SHADERS_MANAGER for (const RenderPath& path : buffer.render_paths) { - glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); - glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); - #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } + if (is_glsl_120) + { + glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); + glsafe(::glDisable(GL_POINT_SPRITE)); + } break; } case GCodeProcessor::EMoveType::Retract: { #if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", Options_Colors[static_cast(EOptionsColors::Retractions)]); + shader->set_uniform("zoom", zoom); + shader->set_uniform("point_sizes", point_sizes); + if (is_glsl_120) + { + glsafe(::glEnable(GL_POINT_SPRITE)); + glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); + } #else set_color(static_cast(buffer.shader.get_shader_program_id()), Options_Colors[static_cast(EOptionsColors::Retractions)]); #endif // ENABLE_SHADERS_MANAGER for (const RenderPath& path : buffer.render_paths) { - glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); - glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); - #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS + if (is_glsl_120) + { + glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); + glsafe(::glDisable(GL_POINT_SPRITE)); + } } break; } @@ -963,19 +1014,28 @@ void GCodeViewer::render_toolpaths() const { #if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", Options_Colors[static_cast(EOptionsColors::Unretractions)]); + shader->set_uniform("zoom", zoom); + shader->set_uniform("point_sizes", point_sizes); + if (is_glsl_120) + { + glsafe(::glEnable(GL_POINT_SPRITE)); + glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); + } #else set_color(static_cast(buffer.shader.get_shader_program_id()), Options_Colors[static_cast(EOptionsColors::Unretractions)]); #endif // ENABLE_SHADERS_MANAGER for (const RenderPath& path : buffer.render_paths) { - glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); - glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); - #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } + if (is_glsl_120) + { + glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); + glsafe(::glDisable(GL_POINT_SPRITE)); + } break; } case GCodeProcessor::EMoveType::Extrude: diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index 02d033b5a8..6df8c75208 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -34,18 +34,9 @@ std::pair GLShadersManager::init() valid &= append_shader("gouraud_light", { "gouraud_light.vs", "gouraud_light.fs" }); // used to render printbed valid &= append_shader("printbed", { "printbed.vs", "printbed.fs" }); - // used to render tool changes in gcode preview - valid &= append_shader("toolchanges", { "toolchanges.vs", "toolchanges.fs" }); - // used to render color changes in gcode preview - valid &= append_shader("colorchanges", { "colorchanges.vs", "colorchanges.fs" }); - // used to render pause prints in gcode preview - valid &= append_shader("pauses", { "pauses.vs", "pauses.fs" }); - // used to render custom gcode points in gcode preview - valid &= append_shader("customs", { "customs.vs", "customs.fs" }); - // used to render retractions in gcode preview - valid &= append_shader("retractions", { "retractions.vs", "retractions.fs" }); - // used to render unretractions in gcode preview - valid &= append_shader("unretractions", { "unretractions.vs", "unretractions.fs" }); + // used to render options in gcode preview + valid &= append_shader("options_110", { "options_110.vs", "options_110.fs" }); + valid &= append_shader("options_120", { "options_120.vs", "options_120.fs" }); // used to render extrusion paths in gcode preview valid &= append_shader("extrusions", { "extrusions.vs", "extrusions.fs" }); // used to render travel paths in gcode preview From 314995fa0b5a9a0d49178cf934a0ea418991f93d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 22 May 2020 16:08:02 +0200 Subject: [PATCH 094/503] ENABLE_SHADERS_MANAGER set as default --- resources/shaders/options_120.fs | 2 +- src/libslic3r/Technologies.hpp | 3 - src/slic3r/GUI/3DBed.cpp | 58 +---- src/slic3r/GUI/3DBed.hpp | 9 - src/slic3r/GUI/3DScene.cpp | 80 ------ src/slic3r/GUI/GCodeViewer.cpp | 150 +----------- src/slic3r/GUI/GCodeViewer.hpp | 29 --- src/slic3r/GUI/GLCanvas3D.cpp | 233 +++--------------- src/slic3r/GUI/GLCanvas3D.hpp | 17 +- src/slic3r/GUI/GLShader.cpp | 362 ---------------------------- src/slic3r/GUI/GLShader.hpp | 71 ------ src/slic3r/GUI/GLShadersManager.cpp | 3 - src/slic3r/GUI/GLShadersManager.hpp | 4 - src/slic3r/GUI/GUI_App.hpp | 2 - src/slic3r/GUI/OpenGLManager.cpp | 45 +--- src/slic3r/GUI/OpenGLManager.hpp | 6 - src/slic3r/GUI/Selection.cpp | 161 ++----------- src/slic3r/GUI/Selection.hpp | 20 -- 18 files changed, 57 insertions(+), 1198 deletions(-) diff --git a/resources/shaders/options_120.fs b/resources/shaders/options_120.fs index 090cde292f..5f699f9204 100644 --- a/resources/shaders/options_120.fs +++ b/resources/shaders/options_120.fs @@ -8,7 +8,7 @@ void main() float sq_radius = pos.x * pos.x + pos.y * pos.y; if (sq_radius > 0.25) discard; - else if (sq_radius > 0.180625) + else if ((sq_radius < 0.005625) || (sq_radius > 0.180625)) gl_FragColor = vec4(0.5 * uniform_color, 1.0); else gl_FragColor = vec4(uniform_color, 1.0); diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index d0673e2b4b..3df9da961d 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -46,7 +46,4 @@ #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) #define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) -// Enable the OpenGL shaders manager -#define ENABLE_SHADERS_MANAGER (1 && ENABLE_2_3_0_ALPHA1) - #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 3fdcdd6fab..b5a34b2ee7 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -163,14 +163,7 @@ Bed3D::Axes::~Axes() void Bed3D::Axes::render() const { #if ENABLE_GCODE_VIEWER -#if ENABLE_SHADERS_MANAGER auto render_axis = [this](const Transform3f& transform) { -#else - auto render_axis = [this](const Transform3f& transform, GLint color_id, const std::array& color) { - if (color_id >= 0) - glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)color.data())); -#endif // ENABLE_SHADERS_MANAGER - glsafe(::glPushMatrix()); glsafe(::glMultMatrixf(transform.data())); m_arrow.render(); @@ -178,21 +171,13 @@ void Bed3D::Axes::render() const }; m_arrow.init_from(stilized_arrow(16, DefaultTipRadius, DefaultTipLength, DefaultStemRadius, m_stem_length)); -#if ENABLE_SHADERS_MANAGER + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) return; -#else - if (!m_shader.init("gouraud_light.vs", "gouraud_light.fs")) - BOOST_LOG_TRIVIAL(error) << "Unable to initialize gouraud_light shader: please, check that the files gouraud_light.vs and gouraud_light.fs are available"; - - if (!m_shader.is_initialized()) - return; -#endif // ENABLE_SHADERS_MANAGER glsafe(::glEnable(GL_DEPTH_TEST)); -#if ENABLE_SHADERS_MANAGER shader->start_using(); // x axis @@ -211,21 +196,6 @@ void Bed3D::Axes::render() const render_axis(Geometry::assemble_transform(m_origin).cast()); shader->stop_using(); -#else - m_shader.start_using(); - GLint color_id = ::glGetUniformLocation(m_shader.get_shader_program_id(), "uniform_color"); - - // x axis - render_axis(Geometry::assemble_transform(m_origin, { 0.0, 0.5 * M_PI, 0.0f }).cast(), color_id, { 0.75f, 0.0f, 0.0f, 1.0f }); - - // y axis - render_axis(Geometry::assemble_transform(m_origin, { -0.5 * M_PI, 0.0, 0.0f }).cast(), color_id, { 0.0f, 0.75f, 0.0f, 1.0f }); - - // z axis - render_axis(Geometry::assemble_transform(m_origin).cast(), color_id, { 0.0f, 0.0f, 0.75f, 1.0f }); - - m_shader.stop_using(); -#endif // ENABLE_SHADERS_MANAGER glsafe(::glDisable(GL_DEPTH_TEST)); #else @@ -571,23 +541,12 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const if (m_triangles.get_vertices_count() > 0) { -#if ENABLE_SHADERS_MANAGER GLShaderProgram* shader = wxGetApp().get_shader("printbed"); if (shader != nullptr) { shader->start_using(); shader->set_uniform("transparent_background", bottom); shader->set_uniform("svg_source", boost::algorithm::iends_with(m_texture.get_source(), ".svg")); -#else - if (m_shader.get_shader_program_id() == 0) - m_shader.init("printbed.vs", "printbed.fs"); - - if (m_shader.is_initialized()) - { - m_shader.start_using(); - m_shader.set_uniform("transparent_background", bottom); - m_shader.set_uniform("svg_source", boost::algorithm::iends_with(m_texture.get_source(), ".svg")); -#endif // ENABLE_SHADERS_MANAGER if (m_vbo_id == 0) { @@ -608,13 +567,8 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const unsigned int stride = m_triangles.get_vertex_data_size(); -#if ENABLE_SHADERS_MANAGER GLint position_id = shader->get_attrib_location("v_position"); GLint tex_coords_id = shader->get_attrib_location("v_tex_coords"); -#else - GLint position_id = m_shader.get_attrib_location("v_position"); - GLint tex_coords_id = m_shader.get_attrib_location("v_tex_coords"); -#endif // ENABLE_SHADERS_MANAGER // show the temporary texture while no compressed data is available GLuint tex_id = (GLuint)m_temp_texture.get_id(); @@ -652,11 +606,7 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const glsafe(::glDisable(GL_BLEND)); glsafe(::glDepthMask(GL_TRUE)); -#if ENABLE_SHADERS_MANAGER shader->stop_using(); -#else - m_shader.stop_using(); -#endif // ENABLE_SHADERS_MANAGER } } } @@ -679,7 +629,6 @@ void Bed3D::render_model() const if (!m_model.get_filename().empty()) { -#if ENABLE_SHADERS_MANAGER GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader != nullptr) { @@ -687,11 +636,6 @@ void Bed3D::render_model() const m_model.render(); shader->stop_using(); } -#else - glsafe(::glEnable(GL_LIGHTING)); - m_model.render(); - glsafe(::glDisable(GL_LIGHTING)); -#endif // ENABLE_SHADERS_MANAGER } } diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index d9a2c82620..2487be2e47 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -3,9 +3,6 @@ #include "GLTexture.hpp" #include "3DScene.hpp" -#if !ENABLE_SHADERS_MANAGER -#include "GLShader.hpp" -#endif // !ENABLE_SHADERS_MANAGER #if ENABLE_GCODE_VIEWER #include "GLModel.hpp" #endif // ENABLE_GCODE_VIEWER @@ -71,9 +68,6 @@ class Bed3D Vec3d m_origin{ Vec3d::Zero() }; float m_stem_length{ DefaultStemLength }; mutable GL_Model m_arrow; -#if !ENABLE_SHADERS_MANAGER - mutable Shader m_shader; -#endif // !ENABLE_SHADERS_MANAGER public: #else @@ -122,9 +116,6 @@ private: mutable GLBed m_model; // temporary texture shown until the main texture has still no levels compressed mutable GLTexture m_temp_texture; -#if !ENABLE_SHADERS_MANAGER - mutable Shader m_shader; -#endif // !ENABLE_SHADERS_MANAGER mutable unsigned int m_vbo_id; Axes m_axes; diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 6e42e52a21..fb7d4c24cf 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1,10 +1,8 @@ #include #include "3DScene.hpp" -#if ENABLE_SHADERS_MANAGER #include "GLShader.hpp" #include "GUI_App.hpp" -#endif // ENABLE_SHADERS_MANAGER #include "libslic3r/ExtrusionEntity.hpp" #include "libslic3r/ExtrusionEntityCollection.hpp" @@ -644,11 +642,9 @@ GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCo void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function filter_func) const { -#if ENABLE_SHADERS_MANAGER GLShaderProgram* shader = GUI::wxGetApp().get_current_shader(); if (shader == nullptr) return; -#endif // ENABLE_SHADERS_MANAGER glsafe(::glEnable(GL_BLEND)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); @@ -660,7 +656,6 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); -#if ENABLE_SHADERS_MANAGER shader->set_uniform("print_box.min", m_print_box_min, 3); shader->set_uniform("print_box.max", m_print_box_max, 3); shader->set_uniform("z_range", m_z_range, 2); @@ -668,74 +663,16 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab #if ENABLE_SLOPE_RENDERING shader->set_uniform("slope.z_range", m_slope.z_range); #endif // ENABLE_SLOPE_RENDERING -#else - GLint current_program_id; - glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id)); - GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1; - GLint z_range_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "z_range") : -1; - GLint clipping_plane_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "clipping_plane") : -1; - - GLint print_box_min_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.min") : -1; - GLint print_box_max_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.max") : -1; - GLint print_box_active_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.actived") : -1; - GLint print_box_worldmatrix_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.volume_world_matrix") : -1; - -#if ENABLE_SLOPE_RENDERING - GLint slope_active_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "slope.actived") : -1; - GLint slope_normal_matrix_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "slope.volume_world_normal_matrix") : -1; - GLint slope_z_range_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "slope.z_range") : -1; -#endif // ENABLE_SLOPE_RENDERING - glcheck(); - - if (print_box_min_id != -1) - glsafe(::glUniform3fv(print_box_min_id, 1, (const GLfloat*)m_print_box_min)); - - if (print_box_max_id != -1) - glsafe(::glUniform3fv(print_box_max_id, 1, (const GLfloat*)m_print_box_max)); - - if (z_range_id != -1) - glsafe(::glUniform2fv(z_range_id, 1, (const GLfloat*)m_z_range)); - - if (clipping_plane_id != -1) - glsafe(::glUniform4fv(clipping_plane_id, 1, (const GLfloat*)m_clipping_plane)); - -#if ENABLE_SLOPE_RENDERING - if (slope_z_range_id != -1) - glsafe(::glUniform2fv(slope_z_range_id, 1, (const GLfloat*)m_slope.z_range.data())); -#endif // ENABLE_SLOPE_RENDERING -#endif // ENABLE_SHADERS_MANAGER GLVolumeWithIdAndZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func); for (GLVolumeWithIdAndZ& volume : to_render) { volume.first->set_render_color(); #if ENABLE_SLOPE_RENDERING -#if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", volume.first->render_color, 4); shader->set_uniform("print_box.actived", volume.first->shader_outside_printer_detection_enabled); shader->set_uniform("print_box.volume_world_matrix", volume.first->world_matrix()); shader->set_uniform("slope.actived", m_slope.active && !volume.first->is_modifier && !volume.first->is_wipe_tower); shader->set_uniform("slope.volume_world_normal_matrix", volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast()); -#else - if (color_id >= 0) - glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)volume.first->render_color)); - else - glsafe(::glColor4fv(volume.first->render_color)); - - if (print_box_active_id != -1) - glsafe(::glUniform1i(print_box_active_id, volume.first->shader_outside_printer_detection_enabled ? 1 : 0)); - - if (print_box_worldmatrix_id != -1) - glsafe(::glUniformMatrix4fv(print_box_worldmatrix_id, 1, GL_FALSE, (const GLfloat*)volume.first->world_matrix().cast().data())); - - if (slope_active_id != -1) - glsafe(::glUniform1i(slope_active_id, m_slope.active && !volume.first->is_modifier && !volume.first->is_wipe_tower ? 1 : 0)); - - if (slope_normal_matrix_id != -1) - { - Matrix3f normal_matrix = volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast(); - glsafe(::glUniformMatrix3fv(slope_normal_matrix_id, 1, GL_FALSE, (const GLfloat*)normal_matrix.data())); - } -#endif // ENABLE_SHADERS_MANAGER volume.first->render(); #else @@ -1940,11 +1877,9 @@ void GLModel::reset() void GLModel::render() const { -#if ENABLE_SHADERS_MANAGER GLShaderProgram* shader = GUI::wxGetApp().get_current_shader(); if (shader == nullptr) return; -#endif // ENABLE_SHADERS_MANAGER glsafe(::glEnable(GL_BLEND)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); @@ -1953,23 +1888,8 @@ void GLModel::render() const glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); -#if !ENABLE_SHADERS_MANAGER - GLint current_program_id; - glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id)); - GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1; - glcheck(); -#endif // !ENABLE_SHADERS_MANAGER - #if ENABLE_SLOPE_RENDERING -#if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", m_volume.render_color, 4); -#else - if (color_id >= 0) - glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)m_volume.render_color)); - else - glsafe(::glColor4fv(m_volume.render_color)); -#endif // ENABLE_SHADERS_MANAGER - m_volume.render(); #else m_volume.render(color_id, -1, -1); diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index cf2f689cef..c2e680b0eb 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -107,18 +107,6 @@ void GCodeViewer::IBuffer::reset() render_paths = std::vector(); } -#if !ENABLE_SHADERS_MANAGER -bool GCodeViewer::IBuffer::init_shader(const std::string& vertex_shader_src, const std::string& fragment_shader_src) -{ - if (!shader.init(vertex_shader_src, fragment_shader_src)) { - BOOST_LOG_TRIVIAL(error) << "Unable to initialize toolpaths shader: please, check that the files " << vertex_shader_src << " and " << fragment_shader_src << " are available"; - return false; - } - - return true; -} -#endif // !ENABLE_SHADERS_MANAGER - void GCodeViewer::IBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsigned int i_id, unsigned int s_id) { Path::Endpoint endpoint = { i_id, s_id, move.position }; @@ -151,9 +139,6 @@ GCodeViewer::Color GCodeViewer::Extrusions::Range::get_color_at(float value) con void GCodeViewer::SequentialView::Marker::init() { m_model.init_from(stilized_arrow(16, 2.0f, 4.0f, 1.0f, 8.0f)); -#if !ENABLE_SHADERS_MANAGER - init_shader(); -#endif // !ENABLE_SHADERS_MANAGER } void GCodeViewer::SequentialView::Marker::set_world_position(const Vec3f& position) @@ -164,7 +149,6 @@ void GCodeViewer::SequentialView::Marker::set_world_position(const Vec3f& positi void GCodeViewer::SequentialView::Marker::render() const { -#if ENABLE_SHADERS_MANAGER if (!m_visible) return; @@ -177,18 +161,6 @@ void GCodeViewer::SequentialView::Marker::render() const shader->start_using(); shader->set_uniform("uniform_color", m_color); -#else - if (!m_visible || !m_shader.is_initialized()) - return; - - glsafe(::glEnable(GL_BLEND)); - glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - - m_shader.start_using(); - GLint color_id = ::glGetUniformLocation(m_shader.get_shader_program_id(), "uniform_color"); - if (color_id >= 0) - glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)m_color.data())); -#endif // ENABLE_SHADERS_MANAGER glsafe(::glPushMatrix()); glsafe(::glMultMatrixf(m_world_transform.data())); @@ -197,23 +169,11 @@ void GCodeViewer::SequentialView::Marker::render() const glsafe(::glPopMatrix()); -#if ENABLE_SHADERS_MANAGER shader->stop_using(); -#else - m_shader.stop_using(); -#endif // ENABLE_SHADERS_MANAGER glsafe(::glDisable(GL_BLEND)); } -#if !ENABLE_SHADERS_MANAGER -void GCodeViewer::SequentialView::Marker::init_shader() -{ - if (!m_shader.init("gouraud_light.vs", "gouraud_light.fs")) - BOOST_LOG_TRIVIAL(error) << "Unable to initialize gouraud_light shader: please, check that the files gouraud_light.vs and gouraud_light.fs are available"; -} -#endif // !ENABLE_SHADERS_MANAGER - const std::vector GCodeViewer::Extrusion_Role_Colors {{ { 0.50f, 0.50f, 0.50f }, // erNone { 1.00f, 1.00f, 0.40f }, // erPerimeter @@ -430,7 +390,6 @@ void GCodeViewer::set_layers_z_range(const std::array& layers_z_range wxGetApp().plater()->update_preview_moves_slider(); } -#if ENABLE_SHADERS_MANAGER void GCodeViewer::init_shaders() { unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); @@ -452,42 +411,6 @@ void GCodeViewer::init_shaders() } } } -#else -bool GCodeViewer::init_shaders() -{ - unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); - unsigned char end_id = buffer_id(GCodeProcessor::EMoveType::Count); - - for (unsigned char i = begin_id; i < end_id; ++i) - { - std::string vertex_shader; - std::string fragment_shader; - - switch (buffer_type(i)) - { - case GCodeProcessor::EMoveType::Tool_change: { vertex_shader = "toolchanges.vs"; fragment_shader = "toolchanges.fs"; break; } - case GCodeProcessor::EMoveType::Color_change: { vertex_shader = "colorchanges.vs"; fragment_shader = "colorchanges.fs"; break; } - case GCodeProcessor::EMoveType::Pause_Print: { vertex_shader = "pauses.vs"; fragment_shader = "pauses.fs"; break; } - case GCodeProcessor::EMoveType::Custom_GCode: { vertex_shader = "customs.vs"; fragment_shader = "customs.fs"; break; } - case GCodeProcessor::EMoveType::Retract: { vertex_shader = "retractions.vs"; fragment_shader = "retractions.fs"; break; } - case GCodeProcessor::EMoveType::Unretract: { vertex_shader = "unretractions.vs"; fragment_shader = "unretractions.fs"; break; } - case GCodeProcessor::EMoveType::Extrude: { vertex_shader = "extrusions.vs"; fragment_shader = "extrusions.fs"; break; } - case GCodeProcessor::EMoveType::Travel: { vertex_shader = "travels.vs"; fragment_shader = "travels.fs"; break; } - default: { break; } - } - - if (vertex_shader.empty() || fragment_shader.empty() || !m_buffers[i].init_shader(vertex_shader, fragment_shader)) - return false; - } - - if (!m_shells.shader.init("shells.vs", "shells.fs")) { - BOOST_LOG_TRIVIAL(error) << "Unable to initialize shells shader: please, check that the files shells.vs and shells.fs are available"; - return false; - } - - return true; -} -#endif // ENABLE_SHADERS_MANAGER void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) { @@ -814,19 +737,6 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool void GCodeViewer::render_toolpaths() const { -#if !ENABLE_SHADERS_MANAGER - auto set_color = [](GLint current_program_id, const Color& color) { - if (current_program_id > 0) { - GLint color_id = ::glGetUniformLocation(current_program_id, "uniform_color"); - if (color_id >= 0) { - glsafe(::glUniform3fv(color_id, 1, (const GLfloat*)color.data())); - return; - } - } - BOOST_LOG_TRIVIAL(error) << "Unable to find uniform_color uniform"; - }; -#endif // !ENABLE_SHADERS_MANAGER - bool is_glsl_120 = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20); int detected_point_sizes[2]; ::glGetIntegerv(GL_ALIASED_POINT_SIZE_RANGE, detected_point_sizes); @@ -851,28 +761,18 @@ void GCodeViewer::render_toolpaths() const if (!buffer.visible) continue; -#if ENABLE_SHADERS_MANAGER GLShaderProgram* shader = wxGetApp().get_shader(buffer.shader.c_str()); if (shader != nullptr) { -#else - if (buffer.shader.is_initialized()) { -#endif // ENABLE_SHADERS_MANAGER + shader->start_using(); GCodeProcessor::EMoveType type = buffer_type(i); -#if ENABLE_SHADERS_MANAGER - shader->start_using(); -#else - buffer.shader.start_using(); -#endif // ENABLE_SHADERS_MANAGER - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.ibo_id)); switch (type) { case GCodeProcessor::EMoveType::Tool_change: { -#if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", Options_Colors[static_cast(EOptionsColors::ToolChanges)]); shader->set_uniform("zoom", zoom); shader->set_uniform("point_sizes", point_sizes); @@ -881,9 +781,6 @@ void GCodeViewer::render_toolpaths() const glsafe(::glEnable(GL_POINT_SPRITE)); glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); } -#else - set_color(static_cast(buffer.shader.get_shader_program_id()), Options_Colors[static_cast(EOptionsColors::ToolChanges)]); -#endif // ENABLE_SHADERS_MANAGER for (const RenderPath& path : buffer.render_paths) { glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); @@ -900,7 +797,6 @@ void GCodeViewer::render_toolpaths() const } case GCodeProcessor::EMoveType::Color_change: { -#if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", Options_Colors[static_cast(EOptionsColors::ColorChanges)]); shader->set_uniform("zoom", zoom); shader->set_uniform("point_sizes", point_sizes); @@ -909,9 +805,6 @@ void GCodeViewer::render_toolpaths() const glsafe(::glEnable(GL_POINT_SPRITE)); glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); } -#else - set_color(static_cast(buffer.shader.get_shader_program_id()), Options_Colors[static_cast(EOptionsColors::ColorChanges)]); -#endif // ENABLE_SHADERS_MANAGER for (const RenderPath& path : buffer.render_paths) { glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); @@ -928,7 +821,6 @@ void GCodeViewer::render_toolpaths() const } case GCodeProcessor::EMoveType::Pause_Print: { -#if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", Options_Colors[static_cast(EOptionsColors::PausePrints)]); shader->set_uniform("zoom", zoom); shader->set_uniform("point_sizes", point_sizes); @@ -937,9 +829,6 @@ void GCodeViewer::render_toolpaths() const glsafe(::glEnable(GL_POINT_SPRITE)); glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); } -#else - set_color(static_cast(buffer.shader.get_shader_program_id()), Options_Colors[static_cast(EOptionsColors::PausePrints)]); -#endif // ENABLE_SHADERS_MANAGER for (const RenderPath& path : buffer.render_paths) { glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); @@ -956,7 +845,6 @@ void GCodeViewer::render_toolpaths() const } case GCodeProcessor::EMoveType::Custom_GCode: { -#if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", Options_Colors[static_cast(EOptionsColors::CustomGCodes)]); shader->set_uniform("zoom", zoom); shader->set_uniform("point_sizes", point_sizes); @@ -965,9 +853,6 @@ void GCodeViewer::render_toolpaths() const glsafe(::glEnable(GL_POINT_SPRITE)); glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); } -#else - set_color(static_cast(buffer.shader.get_shader_program_id()), Options_Colors[static_cast(EOptionsColors::CustomGCodes)]); -#endif // ENABLE_SHADERS_MANAGER for (const RenderPath& path : buffer.render_paths) { glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); @@ -984,7 +869,6 @@ void GCodeViewer::render_toolpaths() const } case GCodeProcessor::EMoveType::Retract: { -#if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", Options_Colors[static_cast(EOptionsColors::Retractions)]); shader->set_uniform("zoom", zoom); shader->set_uniform("point_sizes", point_sizes); @@ -993,9 +877,6 @@ void GCodeViewer::render_toolpaths() const glsafe(::glEnable(GL_POINT_SPRITE)); glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); } -#else - set_color(static_cast(buffer.shader.get_shader_program_id()), Options_Colors[static_cast(EOptionsColors::Retractions)]); -#endif // ENABLE_SHADERS_MANAGER for (const RenderPath& path : buffer.render_paths) { glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); @@ -1012,7 +893,6 @@ void GCodeViewer::render_toolpaths() const } case GCodeProcessor::EMoveType::Unretract: { -#if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", Options_Colors[static_cast(EOptionsColors::Unretractions)]); shader->set_uniform("zoom", zoom); shader->set_uniform("point_sizes", point_sizes); @@ -1021,9 +901,6 @@ void GCodeViewer::render_toolpaths() const glsafe(::glEnable(GL_POINT_SPRITE)); glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); } -#else - set_color(static_cast(buffer.shader.get_shader_program_id()), Options_Colors[static_cast(EOptionsColors::Unretractions)]); -#endif // ENABLE_SHADERS_MANAGER for (const RenderPath& path : buffer.render_paths) { glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); @@ -1042,11 +919,7 @@ void GCodeViewer::render_toolpaths() const { for (const RenderPath& path : buffer.render_paths) { -#if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", path.color); -#else - set_color(static_cast(buffer.shader.get_shader_program_id()), path.color); -#endif // ENABLE_SHADERS_MANAGER glsafe(::glMultiDrawElements(GL_LINE_STRIP, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_line_strip_calls_count; @@ -1059,11 +932,7 @@ void GCodeViewer::render_toolpaths() const { for (const RenderPath& path : buffer.render_paths) { -#if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", path.color); -#else - set_color(static_cast(buffer.shader.get_shader_program_id()), path.color); -#endif // ENABLE_SHADERS_MANAGER glsafe(::glMultiDrawElements(GL_LINE_STRIP, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_line_strip_calls_count; @@ -1075,11 +944,7 @@ void GCodeViewer::render_toolpaths() const } glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); -#if ENABLE_SHADERS_MANAGER shader->stop_using(); -#else - buffer.shader.stop_using(); -#endif // ENABLE_SHADERS_MANAGER } } @@ -1089,7 +954,6 @@ void GCodeViewer::render_toolpaths() const void GCodeViewer::render_shells() const { -#if ENABLE_SHADERS_MANAGER if (!m_shells.visible || m_shells.volumes.empty()) return; @@ -1104,18 +968,6 @@ void GCodeViewer::render_shells() const shader->stop_using(); // glsafe(::glDepthMask(GL_TRUE)); -#else - if (!m_shells.visible || m_shells.volumes.empty() || !m_shells.shader.is_initialized()) - return; - -// glsafe(::glDepthMask(GL_FALSE)); - - m_shells.shader.start_using(); - m_shells.volumes.render(GLVolumeCollection::Transparent, true, wxGetApp().plater()->get_camera().get_view_matrix()); - m_shells.shader.stop_using(); - -// glsafe(::glDepthMask(GL_TRUE)); -#endif // ENABLE_SHADERS_MANAGER } void GCodeViewer::render_legend() const diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 72f45aedca..5ab9f68325 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -2,9 +2,6 @@ #define slic3r_GCodeViewer_hpp_ #if ENABLE_GCODE_VIEWER -#if !ENABLE_SHADERS_MANAGER -#include "GLShader.hpp" -#endif // !ENABLE_SHADERS_MANAGER #include "3DScene.hpp" #include "libslic3r/GCode/GCodeProcessor.hpp" #include "GLModel.hpp" @@ -91,19 +88,12 @@ class GCodeViewer { unsigned int ibo_id{ 0 }; size_t indices_count{ 0 }; -#if ENABLE_SHADERS_MANAGER std::string shader; -#else - Shader shader; -#endif // ENABLE_SHADERS_MANAGER std::vector paths; std::vector render_paths; bool visible{ false }; void reset(); -#if !ENABLE_SHADERS_MANAGER - bool init_shader(const std::string& vertex_shader_src, const std::string& fragment_shader_src); -#endif // !ENABLE_SHADERS_MANAGER void add_path(const GCodeProcessor::MoveVertex& move, unsigned int i_id, unsigned int s_id); }; @@ -112,9 +102,6 @@ class GCodeViewer { GLVolumeCollection volumes; bool visible{ false }; -#if !ENABLE_SHADERS_MANAGER - Shader shader; -#endif // !ENABLE_SHADERS_MANAGER }; // helper to render extrusion paths @@ -225,9 +212,6 @@ public: BoundingBoxf3 m_world_bounding_box; std::array m_color{ 1.0f, 1.0f, 1.0f, 1.0f }; bool m_visible{ false }; -#if !ENABLE_SHADERS_MANAGER - Shader m_shader; -#endif // !ENABLE_SHADERS_MANAGER public: void init(); @@ -241,11 +225,6 @@ public: void set_visible(bool visible) { m_visible = visible; } void render() const; - -#if !ENABLE_SHADERS_MANAGER - private: - void init_shader(); -#endif // !ENABLE_SHADERS_MANAGER }; struct Endpoints @@ -300,12 +279,8 @@ public: bool init() { set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Extrude, true); m_sequential_view.marker.init(); -#if ENABLE_SHADERS_MANAGER init_shaders(); return true; -#else - return init_shaders(); -#endif // ENABLE_SHADERS_MANAGER } // extract rendering data from the given parameters @@ -349,11 +324,7 @@ public: void enable_legend(bool enable) { m_legend_enabled = enable; } private: -#if ENABLE_SHADERS_MANAGER void init_shaders(); -#else - bool init_shaders(); -#endif // ENABLE_SHADERS_MANAGER void load_toolpaths(const GCodeProcessor::Result& gcode_result); void load_shells(const Print& print, bool initialized); void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 5c49eb8295..3a0958b109 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -156,16 +156,8 @@ GLCanvas3D::LayersEditing::~LayersEditing() const float GLCanvas3D::LayersEditing::THICKNESS_BAR_WIDTH = 70.0f; -#if ENABLE_SHADERS_MANAGER void GLCanvas3D::LayersEditing::init() { -#else -bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename) -{ - if (!m_shader.init(vertex_shader_filename, fragment_shader_filename)) - return false; -#endif // ENABLE_SHADERS_MANAGER - glsafe(::glGenTextures(1, (GLuint*)&m_z_texture_id)); glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id)); glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP)); @@ -174,10 +166,6 @@ bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST)); glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1)); glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); - -#if !ENABLE_SHADERS_MANAGER - return true; -#endif // !ENABLE_SHADERS_MANAGER } void GLCanvas3D::LayersEditing::set_config(const DynamicPrintConfig* config) @@ -210,11 +198,7 @@ void GLCanvas3D::LayersEditing::select_object(const Model &model, int object_id) bool GLCanvas3D::LayersEditing::is_allowed() const { -#if ENABLE_SHADERS_MANAGER return wxGetApp().get_shader("variable_layer_height") != nullptr && m_z_texture_id > 0; -#else - return m_shader.is_initialized() && m_shader.get_shader()->shader_program_id > 0 && m_z_texture_id > 0; -#endif // ENABLE_SHADERS_MANAGER } bool GLCanvas3D::LayersEditing::is_enabled() const @@ -372,11 +356,7 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas) bool GLCanvas3D::LayersEditing::is_initialized() const { -#if ENABLE_SHADERS_MANAGER return wxGetApp().get_shader("variable_layer_height") != nullptr; -#else - return m_shader.is_initialized(); -#endif // ENABLE_SHADERS_MANAGER } std::string GLCanvas3D::LayersEditing::get_tooltip(const GLCanvas3D& canvas) const @@ -410,7 +390,6 @@ std::string GLCanvas3D::LayersEditing::get_tooltip(const GLCanvas3D& canvas) con void GLCanvas3D::LayersEditing::render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const { -#if ENABLE_SHADERS_MANAGER GLShaderProgram* shader = wxGetApp().get_shader("variable_layer_height"); if (shader == nullptr) return; @@ -422,15 +401,6 @@ void GLCanvas3D::LayersEditing::render_active_object_annotations(const GLCanvas3 shader->set_uniform("z_cursor", m_object_max_z * this->get_cursor_z_relative(canvas)); shader->set_uniform("z_cursor_band_width", band_width); shader->set_uniform("object_max_z", m_object_max_z); -#else - m_shader.start_using(); - - m_shader.set_uniform("z_to_texture_row", float(m_layers_texture.cells - 1) / (float(m_layers_texture.width) * m_object_max_z)); - m_shader.set_uniform("z_texture_row_to_normalized", 1.0f / (float)m_layers_texture.height); - m_shader.set_uniform("z_cursor", m_object_max_z * this->get_cursor_z_relative(canvas)); - m_shader.set_uniform("z_cursor_band_width", band_width); - m_shader.set_uniform("object_max_z", m_object_max_z); -#endif // ENABLE_SHADERS_MANAGER glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id)); @@ -450,11 +420,7 @@ void GLCanvas3D::LayersEditing::render_active_object_annotations(const GLCanvas3 glsafe(::glEnd()); glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); -#if ENABLE_SHADERS_MANAGER shader->stop_using(); -#else - m_shader.stop_using(); -#endif // ENABLE_SHADERS_MANAGER } void GLCanvas3D::LayersEditing::render_profile(const Rect& bar_rect) const @@ -488,7 +454,6 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G { assert(this->is_allowed()); assert(this->last_object_id != -1); -#if ENABLE_SHADERS_MANAGER GLShaderProgram* shader = wxGetApp().get_shader("variable_layer_height"); if (shader == nullptr) return; @@ -508,85 +473,31 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G shader->set_uniform("z_texture_row_to_normalized", 1.0f / float(m_layers_texture.height)); shader->set_uniform("z_cursor", float(m_object_max_z) * float(this->get_cursor_z_relative(canvas))); shader->set_uniform("z_cursor_band_width", float(this->band_width)); -#else - GLint shader_id = m_shader.get_shader()->shader_program_id; - assert(shader_id > 0); - GLint current_program_id; - glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id)); - if (shader_id > 0 && shader_id != current_program_id) - // The layer editing shader is not yet active. Activate it. - glsafe(::glUseProgram(shader_id)); - else - // The layer editing shader was already active. - current_program_id = -1; + // Initialize the layer height texture mapping. + GLsizei w = (GLsizei)m_layers_texture.width; + GLsizei h = (GLsizei)m_layers_texture.height; + GLsizei half_w = w / 2; + GLsizei half_h = h / 2; + glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); + glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id)); + glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0)); + glsafe(::glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0)); + glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data())); + glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data() + m_layers_texture.width * m_layers_texture.height * 4)); + for (const GLVolume* glvolume : volumes.volumes) { + // Render the object using the layer editing shader and texture. + if (! glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier) + continue; - GLint z_to_texture_row_id = ::glGetUniformLocation(shader_id, "z_to_texture_row"); - GLint z_texture_row_to_normalized_id = ::glGetUniformLocation(shader_id, "z_texture_row_to_normalized"); - GLint z_cursor_id = ::glGetUniformLocation(shader_id, "z_cursor"); - GLint z_cursor_band_width_id = ::glGetUniformLocation(shader_id, "z_cursor_band_width"); - GLint world_matrix_id = ::glGetUniformLocation(shader_id, "volume_world_matrix"); - GLint object_max_z_id = ::glGetUniformLocation(shader_id, "object_max_z"); - glcheck(); - - if (z_to_texture_row_id != -1 && z_texture_row_to_normalized_id != -1 && z_cursor_id != -1 && z_cursor_band_width_id != -1 && world_matrix_id != -1) - { - const_cast(this)->generate_layer_height_texture(); - - // Uniforms were resolved, go ahead using the layer editing shader. - glsafe(::glUniform1f(z_to_texture_row_id, GLfloat(m_layers_texture.cells - 1) / (GLfloat(m_layers_texture.width) * GLfloat(m_object_max_z)))); - glsafe(::glUniform1f(z_texture_row_to_normalized_id, GLfloat(1.0f / m_layers_texture.height))); - glsafe(::glUniform1f(z_cursor_id, GLfloat(m_object_max_z) * GLfloat(this->get_cursor_z_relative(canvas)))); - glsafe(::glUniform1f(z_cursor_band_width_id, GLfloat(this->band_width))); -#endif // ENABLE_SHADERS_MANAGER - // Initialize the layer height texture mapping. - GLsizei w = (GLsizei)m_layers_texture.width; - GLsizei h = (GLsizei)m_layers_texture.height; - GLsizei half_w = w / 2; - GLsizei half_h = h / 2; - glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); - glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id)); - glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0)); - glsafe(::glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0)); - glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data())); - glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data() + m_layers_texture.width * m_layers_texture.height * 4)); - for (const GLVolume* glvolume : volumes.volumes) { - // Render the object using the layer editing shader and texture. - if (! glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier) - continue; -#if ENABLE_SHADERS_MANAGER - shader->set_uniform("volume_world_matrix", glvolume->world_matrix()); - shader->set_uniform("object_max_z", GLfloat(0)); -#else - if (world_matrix_id != -1) - glsafe(::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)glvolume->world_matrix().cast().data())); - if (object_max_z_id != -1) - glsafe(::glUniform1f(object_max_z_id, GLfloat(0))); -#endif // ENABLE_SHADERS_MANAGER - glvolume->render(); - } - // Revert back to the previous shader. - glBindTexture(GL_TEXTURE_2D, 0); -#if ENABLE_SHADERS_MANAGER - if (current_shader != nullptr) - current_shader->start_using(); -#else - if (current_program_id > 0) - glsafe(::glUseProgram(current_program_id)); + shader->set_uniform("volume_world_matrix", glvolume->world_matrix()); + shader->set_uniform("object_max_z", GLfloat(0)); + glvolume->render(); } - else - { - // Something went wrong. Just render the object. - assert(false); - for (const GLVolume* glvolume : volumes.volumes) { - // Render the object using the layer editing shader and texture. - if (!glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier) - continue; - glsafe(::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)glvolume->world_matrix().cast().data())); - glvolume->render(); - } - } -#endif // ENABLE_SHADERS_MANAGER + // Revert back to the previous shader. + glBindTexture(GL_TEXTURE_2D, 0); + if (current_shader != nullptr) + current_shader->start_using(); } void GLCanvas3D::LayersEditing::adjust_layer_height_profile() @@ -1710,22 +1621,8 @@ bool GLCanvas3D::init() if (m_multisample_allowed) glsafe(::glEnable(GL_MULTISAMPLE)); -#if ENABLE_SHADERS_MANAGER if (m_main_toolbar.is_enabled()) m_layers_editing.init(); -#else - if (!m_shader.init("gouraud.vs", "gouraud.fs")) - { - std::cout << "Unable to initialize gouraud shader: please, check that the files gouraud.vs and gouraud.fs are available" << std::endl; - return false; - } - - if (m_main_toolbar.is_enabled() && !m_layers_editing.init("variable_layer_height.vs", "variable_layer_height.fs")) - { - std::cout << "Unable to initialize variable_layer_height shader: please, check that the files variable_layer_height.vs and variable_layer_height.fs are available" << std::endl; - return false; - } -#endif // ENABLE_SHADERS_MANAGER #if ENABLE_GCODE_VIEWER if (!m_main_toolbar.is_enabled()) @@ -4585,13 +4482,8 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool return ret; }; -#if ENABLE_SHADERS_MANAGER static const std::array orange = { 0.923f, 0.504f, 0.264f, 1.0f }; static const std::array gray = { 0.64f, 0.64f, 0.64f, 1.0f }; -#else - static const GLfloat orange[] = { 0.923f, 0.504f, 0.264f, 1.0f }; - static const GLfloat gray[] = { 0.64f, 0.64f, 0.64f, 1.0f }; -#endif // ENABLE_SHADERS_MANAGER GLVolumePtrs visible_volumes; @@ -4635,7 +4527,6 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool camera.apply_projection(box, near_z, far_z); -#if ENABLE_SHADERS_MANAGER GLShaderProgram* shader = wxGetApp().get_shader("gouraud"); if (shader == nullptr) return; @@ -4648,43 +4539,14 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool shader->start_using(); shader->set_uniform("print_box.volume_detection", 0); -#else - if (transparent_background) - glsafe(::glClearColor(0.0f, 0.0f, 0.0f, 0.0f)); - - glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); - glsafe(::glEnable(GL_DEPTH_TEST)); - - m_shader.start_using(); - - GLint shader_id = m_shader.get_shader_program_id(); - GLint color_id = ::glGetUniformLocation(shader_id, "uniform_color"); - GLint print_box_detection_id = ::glGetUniformLocation(shader_id, "print_box.volume_detection"); - glcheck(); - - if (print_box_detection_id != -1) - glsafe(::glUniform1i(print_box_detection_id, 0)); -#endif // ENABLE_SHADERS_MANAGER for (const GLVolume* vol : visible_volumes) { -#if ENABLE_SHADERS_MANAGER shader->set_uniform("uniform_color", (vol->printable && !vol->is_outside) ? orange : gray); -#else - if (color_id >= 0) - glsafe(::glUniform4fv(color_id, 1, (vol->printable && !vol->is_outside) ? orange : gray)); - else - glsafe(::glColor4fv((vol->printable && !vol->is_outside) ? orange : gray)); -#endif // ENABLE_SHADERS_MANAGER - vol->render(); } -#if ENABLE_SHADERS_MANAGER shader->stop_using(); -#else - m_shader.stop_using(); -#endif // ENABLE_SHADERS_MANAGER glsafe(::glDisable(GL_DEPTH_TEST)); @@ -5610,13 +5472,11 @@ void GLCanvas3D::_render_objects() const m_camera_clipping_plane = m_gizmos.get_clipping_plane(); - if (m_picking_enabled) - { + if (m_picking_enabled) { // Update the layer editing selection to the first object selected, update the current object maximum Z. const_cast(m_layers_editing).select_object(*m_model, this->is_layers_editing_enabled() ? m_selection.get_object_idx() : -1); - if (m_config != nullptr) - { + if (m_config != nullptr) { const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false); m_volumes.set_print_box((float)bed_bb.min(0), (float)bed_bb.min(1), 0.0f, (float)bed_bb.max(0), (float)bed_bb.max(1), (float)m_config->opt_float("max_print_height")); m_volumes.check_outside_state(m_config, nullptr); @@ -5630,37 +5490,28 @@ void GLCanvas3D::_render_objects() const m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data()); -#if ENABLE_SHADERS_MANAGER GLShaderProgram* shader = wxGetApp().get_shader("gouraud"); - if (shader != nullptr) - { + if (shader != nullptr) { shader->start_using(); -#else - m_shader.start_using(); -#endif // ENABLE_SHADERS_MANAGER - if (m_picking_enabled && !m_gizmos.is_dragging() && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f)) { - int object_id = m_layers_editing.last_object_id; - m_volumes.render(GLVolumeCollection::Opaque, false, wxGetApp().plater()->get_camera().get_view_matrix(), [object_id](const GLVolume& volume) { - // Which volume to paint without the layer height profile shader? - return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id); - }); - // Let LayersEditing handle rendering of the active object using the layer height profile shader. - m_layers_editing.render_volumes(*this, this->m_volumes); - } - else { + + if (m_picking_enabled && !m_gizmos.is_dragging() && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f)) { + int object_id = m_layers_editing.last_object_id; + m_volumes.render(GLVolumeCollection::Opaque, false, wxGetApp().plater()->get_camera().get_view_matrix(), [object_id](const GLVolume& volume) { + // Which volume to paint without the layer height profile shader? + return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id); + }); + // Let LayersEditing handle rendering of the active object using the layer height profile shader. + m_layers_editing.render_volumes(*this, this->m_volumes); + } else { // do not cull backfaces to show broken geometry, if any m_volumes.render(GLVolumeCollection::Opaque, m_picking_enabled, wxGetApp().plater()->get_camera().get_view_matrix(), [this](const GLVolume& volume) { return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0); }); - } + } - m_volumes.render(GLVolumeCollection::Transparent, false, wxGetApp().plater()->get_camera().get_view_matrix()); -#if ENABLE_SHADERS_MANAGER - shader->stop_using(); + m_volumes.render(GLVolumeCollection::Transparent, false, wxGetApp().plater()->get_camera().get_view_matrix()); + shader->stop_using(); } -#else - m_shader.stop_using(); -#endif // ENABLE_SHADERS_MANAGER m_camera_clipping_plane = ClippingPlane::ClipsNothing(); } @@ -6078,15 +5929,7 @@ void GLCanvas3D::_render_sla_slices() const void GLCanvas3D::_render_selection_sidebar_hints() const { -#if ENABLE_GCODE_VIEWER m_selection.render_sidebar_hints(m_sidebar_field); -#else -#if ENABLE_SHADERS_MANAGER - m_selection.render_sidebar_hints(m_sidebar_field); -#else - m_selection.render_sidebar_hints(m_sidebar_field, m_shader); -#endif // ENABLE_SHADERS_MANAGER -#endif // ENABLE_GCODE_VIEWER } void GLCanvas3D::_update_volumes_hover_state() const diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index ff775a86e1..7b6f67c2b3 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -7,9 +7,6 @@ #include "3DScene.hpp" #include "GLToolbar.hpp" -#if !ENABLE_SHADERS_MANAGER -#include "GLShader.hpp" -#endif // !ENABLE_SHADERS_MANAGER #include "Event.hpp" #include "Selection.hpp" #include "Gizmos/GLGizmosManager.hpp" @@ -169,9 +166,6 @@ private: private: bool m_enabled; -#if !ENABLE_SHADERS_MANAGER - Shader m_shader; -#endif // !ENABLE_SHADERS_MANAGER unsigned int m_z_texture_id; // Not owned by LayersEditing. const DynamicPrintConfig *m_config; @@ -218,11 +212,8 @@ private: LayersEditing(); ~LayersEditing(); -#if ENABLE_SHADERS_MANAGER void init(); -#else - bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename); -#endif // ENABLE_SHADERS_MANAGER + void set_config(const DynamicPrintConfig* config); void select_object(const Model &model, int object_id); @@ -465,9 +456,6 @@ private: WarningTexture m_warning_texture; wxTimer m_timer; LayersEditing m_layers_editing; -#if !ENABLE_SHADERS_MANAGER - Shader m_shader; -#endif // !ENABLE_SHADERS_MANAGER Mouse m_mouse; mutable GLGizmosManager m_gizmos; mutable GLToolbar m_main_toolbar; @@ -597,9 +585,6 @@ public: void set_color_by(const std::string& value); void refresh_camera_scene_box(); -#if !ENABLE_SHADERS_MANAGER - const Shader& get_shader() const { return m_shader; } -#endif // !ENABLE_SHADERS_MANAGER BoundingBoxf3 volumes_bounding_box() const; BoundingBoxf3 scene_bounding_box() const; diff --git a/src/slic3r/GUI/GLShader.cpp b/src/slic3r/GUI/GLShader.cpp index 38ded63326..42e5b0a9f5 100644 --- a/src/slic3r/GUI/GLShader.cpp +++ b/src/slic3r/GUI/GLShader.cpp @@ -8,7 +8,6 @@ #include #include -#if ENABLE_SHADERS_MANAGER #include namespace Slic3r { @@ -302,364 +301,3 @@ int GLShaderProgram::get_uniform_location(const char* name) const } } // namespace Slic3r -#else -#include -#include -#include - -namespace Slic3r { - -GLShader::~GLShader() -{ - assert(fragment_program_id == 0); - assert(vertex_program_id == 0); - assert(shader_program_id == 0); -} - -// A safe wrapper around glGetString to report a "N/A" string in case glGetString returns nullptr. -inline std::string gl_get_string_safe(GLenum param) -{ - const char *value = (const char*)glGetString(param); - return std::string(value ? value : "N/A"); -} - -bool GLShader::load_from_text(const char *fragment_shader, const char *vertex_shader) -{ - std::string gl_version = gl_get_string_safe(GL_VERSION); - int major = atoi(gl_version.c_str()); - //int minor = atoi(gl_version.c_str() + gl_version.find('.') + 1); - if (major < 2) { - // Cannot create a shader object on OpenGL 1.x. - // Form an error message. - std::string gl_vendor = gl_get_string_safe(GL_VENDOR); - std::string gl_renderer = gl_get_string_safe(GL_RENDERER); - std::string glsl_version = gl_get_string_safe(GL_SHADING_LANGUAGE_VERSION); - last_error = "Your computer does not support OpenGL shaders.\n"; -#ifdef _WIN32 - if (gl_vendor == "Microsoft Corporation" && gl_renderer == "GDI Generic") { - last_error = "Windows is using a software OpenGL renderer.\n" - "You are either connected over remote desktop,\n" - "or a hardware acceleration is not available.\n"; - } -#endif - last_error += "GL version: " + gl_version + "\n"; - last_error += "vendor: " + gl_vendor + "\n"; - last_error += "renderer: " + gl_renderer + "\n"; - last_error += "GLSL version: " + glsl_version + "\n"; - return false; - } - - if (fragment_shader != nullptr) { - this->fragment_program_id = ::glCreateShader(GL_FRAGMENT_SHADER); - glcheck(); - if (this->fragment_program_id == 0) { - last_error = "glCreateShader(GL_FRAGMENT_SHADER) failed."; - return false; - } - GLint len = (GLint)strlen(fragment_shader); - glsafe(::glShaderSource(this->fragment_program_id, 1, &fragment_shader, &len)); - glsafe(::glCompileShader(this->fragment_program_id)); - GLint params; - glsafe(::glGetShaderiv(this->fragment_program_id, GL_COMPILE_STATUS, ¶ms)); - if (params == GL_FALSE) { - // Compilation failed. Get the log. - glsafe(::glGetShaderiv(this->fragment_program_id, GL_INFO_LOG_LENGTH, ¶ms)); - std::vector msg(params); - glsafe(::glGetShaderInfoLog(this->fragment_program_id, params, ¶ms, msg.data())); - this->last_error = std::string("Fragment shader compilation failed:\n") + msg.data(); - this->release(); - return false; - } - } - - if (vertex_shader != nullptr) { - this->vertex_program_id = ::glCreateShader(GL_VERTEX_SHADER); - glcheck(); - if (this->vertex_program_id == 0) { - last_error = "glCreateShader(GL_VERTEX_SHADER) failed."; - this->release(); - return false; - } - GLint len = (GLint)strlen(vertex_shader); - glsafe(::glShaderSource(this->vertex_program_id, 1, &vertex_shader, &len)); - glsafe(::glCompileShader(this->vertex_program_id)); - GLint params; - glsafe(::glGetShaderiv(this->vertex_program_id, GL_COMPILE_STATUS, ¶ms)); - if (params == GL_FALSE) { - // Compilation failed. Get the log. - glsafe(::glGetShaderiv(this->vertex_program_id, GL_INFO_LOG_LENGTH, ¶ms)); - std::vector msg(params); - glsafe(::glGetShaderInfoLog(this->vertex_program_id, params, ¶ms, msg.data())); - this->last_error = std::string("Vertex shader compilation failed:\n") + msg.data(); - this->release(); - return false; - } - } - - // Link shaders - this->shader_program_id = ::glCreateProgram(); - glcheck(); - if (this->shader_program_id == 0) { - last_error = "glCreateProgram() failed."; - this->release(); - return false; - } - - if (this->fragment_program_id) - glsafe(::glAttachShader(this->shader_program_id, this->fragment_program_id)); - if (this->vertex_program_id) - glsafe(::glAttachShader(this->shader_program_id, this->vertex_program_id)); - glsafe(::glLinkProgram(this->shader_program_id)); - - GLint params; - glsafe(::glGetProgramiv(this->shader_program_id, GL_LINK_STATUS, ¶ms)); - if (params == GL_FALSE) { - // Linking failed. Get the log. - glsafe(::glGetProgramiv(this->shader_program_id, GL_INFO_LOG_LENGTH, ¶ms)); - std::vector msg(params); - glsafe(::glGetProgramInfoLog(this->shader_program_id, params, ¶ms, msg.data())); - this->last_error = std::string("Shader linking failed:\n") + msg.data(); - this->release(); - return false; - } - - last_error.clear(); - return true; -} - -bool GLShader::load_from_file(const char* fragment_shader_filename, const char* vertex_shader_filename) -{ - const std::string& path = resources_dir() + "/shaders/"; - - boost::nowide::ifstream vs(path + std::string(vertex_shader_filename), boost::nowide::ifstream::binary); - if (!vs.good()) - return false; - - vs.seekg(0, vs.end); - int file_length = (int)vs.tellg(); - vs.seekg(0, vs.beg); - std::string vertex_shader(file_length, '\0'); - vs.read(vertex_shader.data(), file_length); - if (!vs.good()) - return false; - - vs.close(); - - boost::nowide::ifstream fs(path + std::string(fragment_shader_filename), boost::nowide::ifstream::binary); - if (!fs.good()) - return false; - - fs.seekg(0, fs.end); - file_length = (int)fs.tellg(); - fs.seekg(0, fs.beg); - std::string fragment_shader(file_length, '\0'); - fs.read(fragment_shader.data(), file_length); - if (!fs.good()) - return false; - - fs.close(); - - return load_from_text(fragment_shader.c_str(), vertex_shader.c_str()); -} - -void GLShader::release() -{ - if (this->shader_program_id) { - if (this->vertex_program_id) - glsafe(::glDetachShader(this->shader_program_id, this->vertex_program_id)); - if (this->fragment_program_id) - glsafe(::glDetachShader(this->shader_program_id, this->fragment_program_id)); - glsafe(::glDeleteProgram(this->shader_program_id)); - this->shader_program_id = 0; - } - - if (this->vertex_program_id) { - glsafe(::glDeleteShader(this->vertex_program_id)); - this->vertex_program_id = 0; - } - if (this->fragment_program_id) { - glsafe(::glDeleteShader(this->fragment_program_id)); - this->fragment_program_id = 0; - } -} - -void GLShader::enable() const -{ - glsafe(::glUseProgram(this->shader_program_id)); -} - -void GLShader::disable() const -{ - glsafe(::glUseProgram(0)); -} - -// Return shader vertex attribute ID -int GLShader::get_attrib_location(const char *name) const -{ - return this->shader_program_id ? glGetAttribLocation(this->shader_program_id, name) : -1; -} - -// Return shader uniform variable ID -int GLShader::get_uniform_location(const char *name) const -{ - return this->shader_program_id ? glGetUniformLocation(this->shader_program_id, name) : -1; -} - -bool GLShader::set_uniform(const char *name, float value) const -{ - int id = this->get_uniform_location(name); - if (id >= 0) { - glsafe(::glUniform1fARB(id, value)); - return true; - } - return false; -} - -bool GLShader::set_uniform(const char* name, const float* matrix) const -{ - int id = get_uniform_location(name); - if (id >= 0) - { - glsafe(::glUniformMatrix4fv(id, 1, GL_FALSE, (const GLfloat*)matrix)); - return true; - } - return false; -} - -bool GLShader::set_uniform(const char* name, int value) const -{ - int id = get_uniform_location(name); - if (id >= 0) - { - glsafe(::glUniform1i(id, value)); - return true; - } - return false; -} - -/* -# Set shader vector -sub SetVector -{ - my($self,$var,@values) = @_; - - my $id = $self->Map($var); - return 'Unable to map $var' if (!defined($id)); - - my $count = scalar(@values); - eval('glUniform'.$count.'fARB($id,@values)'); - - return ''; -} - -# Set shader 4x4 matrix -sub SetMatrix -{ - my($self,$var,$oga) = @_; - - my $id = $self->Map($var); - return 'Unable to map $var' if (!defined($id)); - - glUniformMatrix4fvARB_c($id,1,0,$oga->ptr()); - return ''; -} -*/ - -Shader::Shader() - : m_shader(nullptr) -{ -} - -Shader::~Shader() -{ - reset(); -} - -bool Shader::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename) -{ - if (is_initialized()) - return true; - - m_shader = new GLShader(); - if (m_shader != nullptr) - { - if (!m_shader->load_from_file(fragment_shader_filename.c_str(), vertex_shader_filename.c_str())) - { - std::cout << "Compilaton of shader failed:" << std::endl; - std::cout << m_shader->last_error << std::endl; - reset(); - return false; - } - } - - return true; -} - -bool Shader::is_initialized() const -{ - return (m_shader != nullptr); -} - -bool Shader::start_using() const -{ - if (is_initialized()) - { - m_shader->enable(); - return true; - } - else - return false; -} - -void Shader::stop_using() const -{ - if (m_shader != nullptr) - m_shader->disable(); -} - -int Shader::get_attrib_location(const std::string& name) const -{ - return (m_shader != nullptr) ? m_shader->get_attrib_location(name.c_str()) : -1; -} - -int Shader::get_uniform_location(const std::string& name) const -{ - return (m_shader != nullptr) ? m_shader->get_uniform_location(name.c_str()) : -1; -} - -void Shader::set_uniform(const std::string& name, float value) const -{ - if (m_shader != nullptr) - m_shader->set_uniform(name.c_str(), value); -} - -void Shader::set_uniform(const std::string& name, const float* matrix) const -{ - if (m_shader != nullptr) - m_shader->set_uniform(name.c_str(), matrix); -} - -void Shader::set_uniform(const std::string& name, bool value) const -{ - if (m_shader != nullptr) - m_shader->set_uniform(name.c_str(), value ? 1 : 0); -} - -unsigned int Shader::get_shader_program_id() const -{ - return (m_shader != nullptr) ? m_shader->shader_program_id : 0; -} - -void Shader::reset() -{ - if (m_shader != nullptr) - { - m_shader->release(); - delete m_shader; - m_shader = nullptr; - } -} - -} // namespace Slic3r - -#endif // ENABLE_SHADERS_MANAGER diff --git a/src/slic3r/GUI/GLShader.hpp b/src/slic3r/GUI/GLShader.hpp index 91a1f66258..e58437fbd1 100644 --- a/src/slic3r/GUI/GLShader.hpp +++ b/src/slic3r/GUI/GLShader.hpp @@ -1,7 +1,6 @@ #ifndef slic3r_GLShader_hpp_ #define slic3r_GLShader_hpp_ -#if ENABLE_SHADERS_MANAGER #include #include @@ -59,75 +58,5 @@ public: }; } // namespace Slic3r -#else -#include "libslic3r/libslic3r.h" -#include "libslic3r/Point.hpp" - -namespace Slic3r { - -class GLShader -{ -public: - GLShader() : - fragment_program_id(0), - vertex_program_id(0), - shader_program_id(0) - {} - ~GLShader(); - - bool load_from_text(const char *fragment_shader, const char *vertex_shader); - bool load_from_file(const char* fragment_shader_filename, const char* vertex_shader_filename); - - void release(); - - int get_attrib_location(const char *name) const; - int get_uniform_location(const char *name) const; - - bool set_uniform(const char *name, float value) const; - bool set_uniform(const char* name, const float* matrix) const; - bool set_uniform(const char* name, int value) const; - - void enable() const; - void disable() const; - - unsigned int fragment_program_id; - unsigned int vertex_program_id; - unsigned int shader_program_id; - std::string last_error; -}; - -class Shader -{ - GLShader* m_shader; - -public: - Shader(); - ~Shader(); - - bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename); - - bool is_initialized() const; - - bool start_using() const; - void stop_using() const; - - int get_attrib_location(const std::string& name) const; - int get_uniform_location(const std::string& name) const; - - void set_uniform(const std::string& name, float value) const; - void set_uniform(const std::string& name, const float* matrix) const; - void set_uniform(const std::string& name, bool value) const; - - const GLShader* get_shader() const { return m_shader; } - unsigned int get_shader_program_id() const; - -private: - void reset(); -}; - -} - - -#endif // ENABLE_SHADERS_MANAGER #endif /* slic3r_GLShader_hpp_ */ diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index 6df8c75208..dd77351bd0 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -7,8 +7,6 @@ #include -#if ENABLE_SHADERS_MANAGER - namespace Slic3r { std::pair GLShadersManager::init() @@ -77,4 +75,3 @@ GLShaderProgram* GLShadersManager::get_current_shader() } // namespace Slic3r -#endif // ENABLE_SHADERS_MANAGER diff --git a/src/slic3r/GUI/GLShadersManager.hpp b/src/slic3r/GUI/GLShadersManager.hpp index f30472b123..b2bbc140bd 100644 --- a/src/slic3r/GUI/GLShadersManager.hpp +++ b/src/slic3r/GUI/GLShadersManager.hpp @@ -1,8 +1,6 @@ #ifndef slic3r_GLShadersManager_hpp_ #define slic3r_GLShadersManager_hpp_ -#if ENABLE_SHADERS_MANAGER - #include "GLShader.hpp" #include @@ -29,6 +27,4 @@ public: } // namespace Slic3r -#endif // ENABLE_SHADERS_MANAGER - #endif // slic3r_GLShadersManager_hpp_ diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index cbe2aafe16..bdbc164acb 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -217,10 +217,8 @@ public: void gcode_thumbnails_debug(); #endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG -#if ENABLE_SHADERS_MANAGER GLShaderProgram* get_shader(const std::string& shader_name) { return m_opengl_mgr.get_shader(shader_name); } GLShaderProgram* get_current_shader() { return m_opengl_mgr.get_current_shader(); } -#endif // ENABLE_SHADERS_MANAGER bool is_gl_version_greater_or_equal_to(unsigned int major, unsigned int minor) const { return m_opengl_mgr.get_gl_info().is_version_greater_or_equal_to(major, minor); } bool is_glsl_version_greater_or_equal_to(unsigned int major, unsigned int minor) const { return m_opengl_mgr.get_gl_info().is_glsl_version_greater_or_equal_to(major, minor); } diff --git a/src/slic3r/GUI/OpenGLManager.cpp b/src/slic3r/GUI/OpenGLManager.cpp index b21fd01439..13c58f8472 100644 --- a/src/slic3r/GUI/OpenGLManager.cpp +++ b/src/slic3r/GUI/OpenGLManager.cpp @@ -28,14 +28,12 @@ namespace Slic3r { namespace GUI { -#if ENABLE_SHADERS_MANAGER // A safe wrapper around glGetString to report a "N/A" string in case glGetString returns nullptr. inline std::string gl_get_string_safe(GLenum param, const std::string& default_value) { const char* value = (const char*)::glGetString(param); return std::string((value != nullptr) ? value : default_value); } -#endif // ENABLE_SHADERS_MANAGER const std::string& OpenGLManager::GLInfo::get_version() const { @@ -94,28 +92,10 @@ float OpenGLManager::GLInfo::get_max_anisotropy() const void OpenGLManager::GLInfo::detect() const { -#if ENABLE_SHADERS_MANAGER m_version = gl_get_string_safe(GL_VERSION, "N/A"); m_glsl_version = gl_get_string_safe(GL_SHADING_LANGUAGE_VERSION, "N/A"); m_vendor = gl_get_string_safe(GL_VENDOR, "N/A"); m_renderer = gl_get_string_safe(GL_RENDERER, "N/A"); -#else - const char* data = (const char*)::glGetString(GL_VERSION); - if (data != nullptr) - m_version = data; - - data = (const char*)::glGetString(GL_SHADING_LANGUAGE_VERSION); - if (data != nullptr) - m_glsl_version = data; - - data = (const char*)::glGetString(GL_VENDOR); - if (data != nullptr) - m_vendor = data; - - data = (const char*)::glGetString(GL_RENDERER); - if (data != nullptr) - m_renderer = data; -#endif // ENABLE_SHADERS_MANAGER glsafe(::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_max_tex_size)); @@ -132,10 +112,8 @@ void OpenGLManager::GLInfo::detect() const static bool version_greater_or_equal_to(const std::string& version, unsigned int major, unsigned int minor) { -#if ENABLE_SHADERS_MANAGER if (version == "N/A") return false; -#endif // ENABLE_SHADERS_MANAGER std::vector tokens; boost::split(tokens, version, boost::is_any_of(" "), boost::token_compress_on); @@ -193,26 +171,15 @@ std::string OpenGLManager::GLInfo::to_string(bool format_as_html, bool extension std::string line_end = format_as_html ? "
" : "\n"; out << h2_start << "OpenGL installation" << h2_end << line_end; -#if ENABLE_SHADERS_MANAGER out << b_start << "GL version: " << b_end << m_version << line_end; out << b_start << "Vendor: " << b_end << m_vendor << line_end; out << b_start << "Renderer: " << b_end << m_renderer << line_end; out << b_start << "GLSL version: " << b_end << m_glsl_version << line_end; -#else - out << b_start << "GL version: " << b_end << (m_version.empty() ? "N/A" : m_version) << line_end; - out << b_start << "Vendor: " << b_end << (m_vendor.empty() ? "N/A" : m_vendor) << line_end; - out << b_start << "Renderer: " << b_end << (m_renderer.empty() ? "N/A" : m_renderer) << line_end; - out << b_start << "GLSL version: " << b_end << (m_glsl_version.empty() ? "N/A" : m_glsl_version) << line_end; -#endif // ENABLE_SHADERS_MANAGER if (extensions) { std::vector extensions_list; -#if ENABLE_SHADERS_MANAGER std::string extensions_str = gl_get_string_safe(GL_EXTENSIONS, ""); -#else - std::string extensions_str = (const char*)::glGetString(GL_EXTENSIONS); -#endif // ENABLE_SHADERS_MANAGER boost::split(extensions_list, extensions_str, boost::is_any_of(" "), boost::token_compress_off); if (!extensions_list.empty()) @@ -244,9 +211,7 @@ OpenGLManager::OSInfo OpenGLManager::s_os_info; OpenGLManager::~OpenGLManager() { -#if ENABLE_SHADERS_MANAGER m_shaders_manager.shutdown(); -#endif // ENABLE_SHADERS_MANAGER #if ENABLE_HACK_CLOSING_ON_OSX_10_9_5 #ifdef __APPLE__ @@ -289,13 +254,8 @@ bool OpenGLManager::init_gl() else s_framebuffers_type = EFramebufferType::Unknown; -#if ENABLE_SHADERS_MANAGER bool valid_version = s_gl_info.is_version_greater_or_equal_to(2, 0); if (!valid_version) { -#else - if (!s_gl_info.is_version_greater_or_equal_to(2, 0)) { -#endif // ENABLE_SHADERS_MANAGER - // Complain about the OpenGL version. wxString message = from_u8((boost::format( _utf8(L("PrusaSlicer requires OpenGL 2.0 capable graphics driver to run correctly, \n" @@ -309,7 +269,6 @@ bool OpenGLManager::init_gl() wxMessageBox(message, wxString("PrusaSlicer - ") + _L("Unsupported OpenGL version"), wxOK | wxICON_ERROR); } -#if ENABLE_SHADERS_MANAGER if (valid_version) { // load shaders auto [result, error] = m_shaders_manager.init(); @@ -319,7 +278,6 @@ bool OpenGLManager::init_gl() wxMessageBox(message, wxString("PrusaSlicer - ") + _L("Error loading shaders"), wxOK | wxICON_ERROR); } } -#endif // ENABLE_SHADERS_MANAGER } return true; @@ -327,8 +285,7 @@ bool OpenGLManager::init_gl() wxGLContext* OpenGLManager::init_glcontext(wxGLCanvas& canvas) { - if (m_context == nullptr) - { + if (m_context == nullptr) { m_context = new wxGLContext(&canvas); #if ENABLE_HACK_CLOSING_ON_OSX_10_9_5 diff --git a/src/slic3r/GUI/OpenGLManager.hpp b/src/slic3r/GUI/OpenGLManager.hpp index e33df42491..c89cdf3a61 100644 --- a/src/slic3r/GUI/OpenGLManager.hpp +++ b/src/slic3r/GUI/OpenGLManager.hpp @@ -1,9 +1,7 @@ #ifndef slic3r_OpenGLManager_hpp_ #define slic3r_OpenGLManager_hpp_ -#if ENABLE_SHADERS_MANAGER #include "GLShadersManager.hpp" -#endif // ENABLE_SHADERS_MANAGER class wxWindow; class wxGLCanvas; @@ -75,9 +73,7 @@ private: bool m_gl_initialized{ false }; wxGLContext* m_context{ nullptr }; -#if ENABLE_SHADERS_MANAGER GLShadersManager m_shaders_manager; -#endif // ENABLE_SHADERS_MANAGER static GLInfo s_gl_info; #if ENABLE_HACK_CLOSING_ON_OSX_10_9_5 #ifdef __APPLE__ @@ -96,10 +92,8 @@ public: bool init_gl(); wxGLContext* init_glcontext(wxGLCanvas& canvas); -#if ENABLE_SHADERS_MANAGER GLShaderProgram* get_shader(const std::string& shader_name) { return m_shaders_manager.get_shader(shader_name); } GLShaderProgram* get_current_shader() { return m_shaders_manager.get_current_shader(); } -#endif // ENABLE_SHADERS_MANAGER static bool are_compressed_textures_supported() { return s_compressed_textures_supported; } static bool can_multisample() { return s_multisample == EMultisampleState::Enabled; } diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index f890f0f012..cbc3942305 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -112,14 +112,6 @@ bool Selection::init() #if ENABLE_GCODE_VIEWER m_arrow.init_from(straight_arrow(10.0f, 5.0f, 5.0f, 10.0f, 1.0f)); m_curved_arrow.init_from(circular_arrow(16, 10.0f, 5.0f, 10.0f, 5.0f, 1.0f)); - -#if !ENABLE_SHADERS_MANAGER - if (!m_arrows_shader.init("gouraud_light.vs", "gouraud_light.fs")) - { - BOOST_LOG_TRIVIAL(error) << "Unable to initialize gouraud_light shader: please, check that the files gouraud_light.vs and gouraud_light.fs are available"; - return false; - } -#endif // !ENABLE_SHADERS_MANAGER #else if (!m_arrow.init()) return false; @@ -1246,76 +1238,40 @@ void Selection::render_center(bool gizmo_is_dragging) const } #endif // ENABLE_RENDER_SELECTION_CENTER -#if ENABLE_GCODE_VIEWER void Selection::render_sidebar_hints(const std::string& sidebar_field) const -#else -#if ENABLE_SHADERS_MANAGER -void Selection::render_sidebar_hints(const std::string& sidebar_field) const -#else -void Selection::render_sidebar_hints(const std::string& sidebar_field, const Shader& shader) const -#endif // ENABLE_SHADERS_MANAGER -#endif // ENABLE_GCODE_VIEWER { if (sidebar_field.empty()) return; -#if ENABLE_SHADERS_MANAGER GLShaderProgram* shader = nullptr; -#endif // ENABLE_SHADERS_MANAGER if (!boost::starts_with(sidebar_field, "layer")) { -#if ENABLE_GCODE_VIEWER -#if ENABLE_SHADERS_MANAGER shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) return; shader->start_using(); -#else - if (!m_arrows_shader.is_initialized()) - return; - - m_arrows_shader.start_using(); -#endif // ENABLE_SHADERS_MANAGER glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); -#else -#if ENABLE_SHADERS_MANAGER - shader = wxGetApp().get_shader("gouraud_light"); - if (shader == nullptr) - return; - - shader->start_using(); -#else - shader.start_using(); - glsafe(::glEnable(GL_LIGHTING)); -#endif // ENABLE_SHADERS_MANAGER - glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); -#endif // ENABLE_GCODE_VIEWER } glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glPushMatrix()); - if (!boost::starts_with(sidebar_field, "layer")) - { + if (!boost::starts_with(sidebar_field, "layer")) { const Vec3d& center = get_bounding_box().center(); - if (is_single_full_instance() && !wxGetApp().obj_manipul()->get_world_coordinates()) - { + if (is_single_full_instance() && !wxGetApp().obj_manipul()->get_world_coordinates()) { glsafe(::glTranslated(center(0), center(1), center(2))); - if (!boost::starts_with(sidebar_field, "position")) - { + if (!boost::starts_with(sidebar_field, "position")) { Transform3d orient_matrix = Transform3d::Identity(); if (boost::starts_with(sidebar_field, "scale")) orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); - else if (boost::starts_with(sidebar_field, "rotation")) - { + else if (boost::starts_with(sidebar_field, "rotation")) { if (boost::ends_with(sidebar_field, "x")) orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); - else if (boost::ends_with(sidebar_field, "y")) - { + else if (boost::ends_with(sidebar_field, "y")) { const Vec3d& rotation = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_rotation(); if (rotation(0) == 0.0) orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); @@ -1326,21 +1282,16 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field, const Sha glsafe(::glMultMatrixd(orient_matrix.data())); } - } - else if (is_single_volume() || is_single_modifier()) - { + } else if (is_single_volume() || is_single_modifier()) { glsafe(::glTranslated(center(0), center(1), center(2))); Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); if (!boost::starts_with(sidebar_field, "position")) orient_matrix = orient_matrix * (*m_volumes)[*m_list.begin()]->get_volume_transformation().get_matrix(true, false, true, true); glsafe(::glMultMatrixd(orient_matrix.data())); - } - else - { + } else { glsafe(::glTranslated(center(0), center(1), center(2))); - if (requires_local_axes()) - { + if (requires_local_axes()) { Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); glsafe(::glMultMatrixd(orient_matrix.data())); } @@ -1359,22 +1310,7 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field, const Sha glsafe(::glPopMatrix()); if (!boost::starts_with(sidebar_field, "layer")) - { -#if ENABLE_GCODE_VIEWER -#if ENABLE_SHADERS_MANAGER shader->stop_using(); -#else - m_arrows_shader.stop_using(); -#endif // ENABLE_SHADERS_MANAGER -#else -#if ENABLE_SHADERS_MANAGER - shader->stop_using(); -#else - glsafe(::glDisable(GL_LIGHTING)); - shader.stop_using(); -#endif // ENABLE_SHADERS_MANAGER -#endif // ENABLE_GCODE_VIEWER - } } bool Selection::requires_local_axes() const @@ -1977,50 +1913,21 @@ void Selection::render_bounding_box(const BoundingBoxf3& box, float* color) cons #if ENABLE_GCODE_VIEWER void Selection::render_sidebar_position_hints(const std::string& sidebar_field) const { -#if ENABLE_SHADERS_MANAGER auto set_color = [](Axis axis) { GLShaderProgram* shader = wxGetApp().get_current_shader(); if (shader != nullptr) shader->set_uniform("uniform_color", AXES_COLOR[axis], 4); }; -#endif // ENABLE_SHADERS_MANAGER -#if !ENABLE_SHADERS_MANAGER - GLint color_id = ::glGetUniformLocation(m_arrows_shader.get_shader_program_id(), "uniform_color"); -#endif // !ENABLE_SHADERS_MANAGER - - if (boost::ends_with(sidebar_field, "x")) - { -#if ENABLE_SHADERS_MANAGER + if (boost::ends_with(sidebar_field, "x")) { set_color(X); -#else - if (color_id >= 0) - glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[0])); -#endif // ENABLE_SHADERS_MANAGER - glsafe(::glRotated(-90.0, 0.0, 0.0, 1.0)); m_arrow.render(); - } - else if (boost::ends_with(sidebar_field, "y")) - { -#if ENABLE_SHADERS_MANAGER + } else if (boost::ends_with(sidebar_field, "y")) { set_color(Y); -#else - if (color_id >= 0) - glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[1])); -#endif // ENABLE_SHADERS_MANAGER - m_arrow.render(); - } - else if (boost::ends_with(sidebar_field, "z")) - { -#if ENABLE_SHADERS_MANAGER + } else if (boost::ends_with(sidebar_field, "z")) { set_color(Z); -#else - if (color_id >= 0) - glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[2])); -#endif // ENABLE_SHADERS_MANAGER - glsafe(::glRotated(90.0, 1.0, 0.0, 0.0)); m_arrow.render(); } @@ -2046,51 +1953,22 @@ void Selection::render_sidebar_position_hints(const std::string& sidebar_field) #if ENABLE_GCODE_VIEWER void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field) const { -#if ENABLE_SHADERS_MANAGER auto set_color = [](Axis axis) { GLShaderProgram* shader = wxGetApp().get_current_shader(); if (shader != nullptr) shader->set_uniform("uniform_color", AXES_COLOR[axis], 4); }; -#endif // ENABLE_SHADERS_MANAGER -#if !ENABLE_SHADERS_MANAGER - GLint color_id = ::glGetUniformLocation(m_arrows_shader.get_shader_program_id(), "uniform_color"); -#endif // !ENABLE_SHADERS_MANAGER - - if (boost::ends_with(sidebar_field, "x")) - { -#if ENABLE_SHADERS_MANAGER + if (boost::ends_with(sidebar_field, "x")) { set_color(X); -#else - if (color_id >= 0) - glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[0])); -#endif // ENABLE_SHADERS_MANAGER - glsafe(::glRotated(90.0, 0.0, 1.0, 0.0)); render_sidebar_rotation_hint(X); - } - else if (boost::ends_with(sidebar_field, "y")) - { -#if ENABLE_SHADERS_MANAGER + } else if (boost::ends_with(sidebar_field, "y")) { set_color(Y); -#else - if (color_id >= 0) - glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[1])); -#endif // ENABLE_SHADERS_MANAGER - glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0)); render_sidebar_rotation_hint(Y); - } - else if (boost::ends_with(sidebar_field, "z")) - { -#if ENABLE_SHADERS_MANAGER + } else if (boost::ends_with(sidebar_field, "z")) { set_color(Z); -#else - if (color_id >= 0) - glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)AXES_COLOR[2])); -#endif // ENABLE_SHADERS_MANAGER - render_sidebar_rotation_hint(Z); } } @@ -2230,23 +2108,12 @@ void Selection::render_sidebar_rotation_hint(Axis axis) const m_curved_arrow.render(); } -#if ENABLE_SHADERS_MANAGER void Selection::render_sidebar_scale_hint(Axis axis) const -#else -void Selection::render_sidebar_scale_hint(Axis axis) const -#endif // ENABLE_SHADERS_MANAGER { #if ENABLE_GCODE_VIEWER -#if ENABLE_SHADERS_MANAGER GLShaderProgram* shader = wxGetApp().get_current_shader(); if (shader != nullptr) shader->set_uniform("uniform_color", (requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling()) ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis], 4); -#else - GLint color_id = ::glGetUniformLocation(m_arrows_shader.get_shader_program_id(), "uniform_color"); - - if (color_id >= 0) - glsafe(::glUniform4fv(color_id, 1, (requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling()) ? (const GLfloat*)UNIFORM_SCALE_COLOR : (const GLfloat*)AXES_COLOR[axis])); -#endif // ENABLE_SHADERS_MANAGER #else m_arrow.set_color(((requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling()) ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis]), 3); #endif // ENABLE_GCODE_VIEWER diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 5541cc3fa9..34024875b3 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -6,13 +6,6 @@ #include "3DScene.hpp" #if ENABLE_GCODE_VIEWER #include "GLModel.hpp" -#if !ENABLE_SHADERS_MANAGER -#include "GLShader.hpp" -#endif // !ENABLE_SHADERS_MANAGER -#else -#if !ENABLE_SHADERS_MANAGER -#include "GLShader.hpp" -#endif // !ENABLE_SHADERS_MANAGER #endif // ENABLE_GCODE_VIEWER #if ENABLE_RENDER_SELECTION_CENTER @@ -24,9 +17,7 @@ namespace Slic3r { #if !ENABLE_GCODE_VIEWER class Shader; #endif // !ENABLE_GCODE_VIEWER -#if ENABLE_SHADERS_MANAGER class GLShaderProgram; -#endif // ENABLE_SHADERS_MANAGER namespace GUI { class TransformationType { @@ -218,9 +209,6 @@ private: #if ENABLE_GCODE_VIEWER GL_Model m_arrow; GL_Model m_curved_arrow; -#if !ENABLE_SHADERS_MANAGER - Shader m_arrows_shader; -#endif // !ENABLE_SHADERS_MANAGER #else mutable GLArrow m_arrow; mutable GLCurvedArrow m_curved_arrow; @@ -339,15 +327,7 @@ public: #if ENABLE_RENDER_SELECTION_CENTER void render_center(bool gizmo_is_dragging) const; #endif // ENABLE_RENDER_SELECTION_CENTER -#if ENABLE_GCODE_VIEWER void render_sidebar_hints(const std::string& sidebar_field) const; -#else -#if ENABLE_SHADERS_MANAGER - void render_sidebar_hints(const std::string& sidebar_field) const; -#else - void render_sidebar_hints(const std::string& sidebar_field, const Shader& shader) const; -#endif // ENABLE_SHADERS_MANAGER -#endif // ENABLE_GCODE_VIEWER bool requires_local_axes() const; From 43b78b630cee4979161a0d3757aead16b4c2b3cc Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 22 May 2020 16:37:53 +0200 Subject: [PATCH 095/503] GCodeViewer -> Enhanced legend icons --- src/slic3r/GUI/GCodeViewer.cpp | 67 +++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 18 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index c2e680b0eb..330a5a261b 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -987,12 +987,43 @@ void GCodeViewer::render_legend() const ImDrawList* draw_list = ImGui::GetWindowDrawList(); - auto add_item = [draw_list, &imgui](const Color& color, const std::string& label, std::function callback = nullptr) { + enum class EItemType : unsigned char + { + Rect, + Circle, + Line + }; + + auto add_item = [draw_list, &imgui](EItemType type, const Color& color, const std::string& label, std::function callback = nullptr) { float icon_size = ImGui::GetTextLineHeight(); ImVec2 pos = ImGui::GetCursorPos(); - draw_list->AddRect({ pos.x, pos.y }, { pos.x + icon_size, pos.y + icon_size }, ICON_BORDER_COLOR, 0.0f, 0); - draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, - ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f })); + switch (type) + { + default: + case EItemType::Rect: + { + draw_list->AddRect({ pos.x, pos.y }, { pos.x + icon_size, pos.y + icon_size }, ICON_BORDER_COLOR, 0.0f, 0); + draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, + ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f })); + break; + } + case EItemType::Circle: + { + draw_list->AddCircle({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, 0.5f * icon_size, ICON_BORDER_COLOR, 16); + draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, (0.5f * icon_size) - 2.0f, + ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); + draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, (0.5f * icon_size) - 3.0f, + ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); + draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, 1.5f, + ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); + break; + } + case EItemType::Line: + { + draw_list->AddLine({ pos.x + 1, pos.y + icon_size - 1}, { pos.x + icon_size - 1, pos.y + 1 }, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 3.0f); + break; + } + } // draw text ImGui::Dummy({ icon_size, icon_size }); @@ -1010,7 +1041,7 @@ void GCodeViewer::render_legend() const auto add_range_item = [this, draw_list, &imgui, add_item](int i, float value, unsigned int decimals) { char buf[1024]; ::sprintf(buf, "%.*f", decimals, value); - add_item(Range_Colors[i], buf); + add_item(EItemType::Rect, Range_Colors[i], buf); }; float step_size = range.step_size(); @@ -1052,7 +1083,7 @@ void GCodeViewer::render_legend() const if (!visible) ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f); - add_item(Extrusion_Role_Colors[static_cast(role)], _u8L(ExtrusionEntity::role_to_string(role)), [this, role]() { + add_item(EItemType::Rect, Extrusion_Role_Colors[static_cast(role)], _u8L(ExtrusionEntity::role_to_string(role)), [this, role]() { if (role < erCount) { m_extrusions.role_visibility_flags = is_visible(role) ? m_extrusions.role_visibility_flags & ~(1 << role) : m_extrusions.role_visibility_flags | (1 << role); @@ -1082,7 +1113,7 @@ void GCodeViewer::render_legend() const if (it == m_extruder_ids.end()) continue; - add_item(m_tool_colors[i], (boost::format(_u8L("Extruder %d")) % (i + 1)).str()); + add_item(EItemType::Rect, m_tool_colors[i], (boost::format(_u8L("Extruder %d")) % (i + 1)).str()); } break; } @@ -1093,7 +1124,7 @@ void GCodeViewer::render_legend() const if (extruders_count == 1) { // single extruder use case if (custom_gcode_per_print_z.empty()) // no data to show - add_item(m_tool_colors.front(), _u8L("Default print color")); + add_item(EItemType::Rect, m_tool_colors.front(), _u8L("Default print color")); else { std::vector> cp_values; cp_values.reserve(custom_gcode_per_print_z.size()); @@ -1117,7 +1148,7 @@ void GCodeViewer::render_legend() const const int items_cnt = static_cast(cp_values.size()); if (items_cnt == 0) { // There is no one color change, but there are some pause print or custom Gcode - add_item(m_tool_colors.front(), _u8L("Default print color")); + add_item(EItemType::Rect, m_tool_colors.front(), _u8L("Default print color")); } else { for (int i = items_cnt; i >= 0; --i) { @@ -1125,14 +1156,14 @@ void GCodeViewer::render_legend() const std::string id_str = " (" + std::to_string(i + 1) + ")"; if (i == 0) { - add_item(m_tool_colors[i], (boost::format(_u8L("up to %.2f mm")) % cp_values.front().first).str() + id_str); + add_item(EItemType::Rect, m_tool_colors[i], (boost::format(_u8L("up to %.2f mm")) % cp_values.front().first).str() + id_str); break; } else if (i == items_cnt) { - add_item(m_tool_colors[i], (boost::format(_u8L("above %.2f mm")) % cp_values[i - 1].second).str() + id_str); + add_item(EItemType::Rect, m_tool_colors[i], (boost::format(_u8L("above %.2f mm")) % cp_values[i - 1].second).str() + id_str); continue; } - add_item(m_tool_colors[i], (boost::format(_u8L("%.2f - %.2f mm")) % cp_values[i - 1].second% cp_values[i].first).str() + id_str); + add_item(EItemType::Rect, m_tool_colors[i], (boost::format(_u8L("%.2f - %.2f mm")) % cp_values[i - 1].second% cp_values[i].first).str() + id_str); } } } @@ -1141,7 +1172,7 @@ void GCodeViewer::render_legend() const { // extruders for (unsigned int i = 0; i < (unsigned int)extruders_count; ++i) { - add_item(m_tool_colors[i], (boost::format(_u8L("Extruder %d")) % (i + 1)).str()); + add_item(EItemType::Rect, m_tool_colors[i], (boost::format(_u8L("Extruder %d")) % (i + 1)).str()); } // color changes @@ -1152,7 +1183,7 @@ void GCodeViewer::render_legend() const // create label for color change item std::string id_str = " (" + std::to_string(color_change_idx--) + ")"; - add_item(m_tool_colors[last_color_id--], + add_item(EItemType::Rect, m_tool_colors[last_color_id--], (boost::format(_u8L("Color change for Extruder %d at %.2f mm")) % custom_gcode_per_print_z[i].extruder % custom_gcode_per_print_z[i].print_z).str() + id_str); } } @@ -1185,9 +1216,9 @@ void GCodeViewer::render_legend() const ImGui::Separator(); // items - add_item(Travel_Colors[0], _u8L("Movement")); - add_item(Travel_Colors[1], _u8L("Extrusion")); - add_item(Travel_Colors[2], _u8L("Retraction")); + add_item(EItemType::Line, Travel_Colors[0], _u8L("Movement")); + add_item(EItemType::Line, Travel_Colors[1], _u8L("Extrusion")); + add_item(EItemType::Line, Travel_Colors[2], _u8L("Retraction")); break; } @@ -1206,7 +1237,7 @@ void GCodeViewer::render_legend() const auto add_option = [this, add_item](GCodeProcessor::EMoveType move_type, EOptionsColors color, const std::string& text) { const IBuffer& buffer = m_buffers[buffer_id(move_type)]; if (buffer.visible && buffer.indices_count > 0) - add_item(Options_Colors[static_cast(color)], text); + add_item(EItemType::Circle, Options_Colors[static_cast(color)], text); }; // options From 1c826c063b4bf75a0ec1fff991e219deb6b1e8fa Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 25 May 2020 10:48:53 +0200 Subject: [PATCH 096/503] GCodeViewer refactoring and GLShaderProgram upgrade --- resources/shaders/options_120.fs | 5 +- src/slic3r/GUI/3DScene.cpp | 2 +- src/slic3r/GUI/GCodeViewer.cpp | 148 ++++++------------------------- src/slic3r/GUI/GLShader.cpp | 25 ++++++ src/slic3r/GUI/GLShader.hpp | 3 + 5 files changed, 59 insertions(+), 124 deletions(-) diff --git a/resources/shaders/options_120.fs b/resources/shaders/options_120.fs index 5f699f9204..236174f3a3 100644 --- a/resources/shaders/options_120.fs +++ b/resources/shaders/options_120.fs @@ -5,10 +5,11 @@ uniform vec3 uniform_color; void main() { vec2 pos = gl_PointCoord - vec2(0.5, 0.5); - float sq_radius = pos.x * pos.x + pos.y * pos.y; + float sq_radius = dot(pos, pos); if (sq_radius > 0.25) discard; - else if ((sq_radius < 0.005625) || (sq_radius > 0.180625)) + + if ((sq_radius < 0.005625) || (sq_radius > 0.180625)) gl_FragColor = vec4(0.5 * uniform_color, 1.0); else gl_FragColor = vec4(uniform_color, 1.0); diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index fb7d4c24cf..49b1c255b7 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -672,7 +672,7 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab shader->set_uniform("print_box.actived", volume.first->shader_outside_printer_detection_enabled); shader->set_uniform("print_box.volume_world_matrix", volume.first->world_matrix()); shader->set_uniform("slope.actived", m_slope.active && !volume.first->is_modifier && !volume.first->is_wipe_tower); - shader->set_uniform("slope.volume_world_normal_matrix", volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast()); + shader->set_uniform("slope.volume_world_normal_matrix", static_cast(volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast())); volume.first->render(); #else diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 330a5a261b..54c7d3bb5a 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -740,9 +740,29 @@ void GCodeViewer::render_toolpaths() const bool is_glsl_120 = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20); int detected_point_sizes[2]; ::glGetIntegerv(GL_ALIASED_POINT_SIZE_RANGE, detected_point_sizes); - std::array point_sizes = { 2.0f, std::min(64.0f, static_cast(detected_point_sizes[1])) }; + std::array point_sizes = { std::min(8.0f, static_cast(detected_point_sizes[1])), std::min(48.0f, static_cast(detected_point_sizes[1])) }; double zoom = wxGetApp().plater()->get_camera().get_zoom(); + auto render_options = [this, is_glsl_120, zoom, point_sizes](const IBuffer& buffer, EOptionsColors colors_id, GLShaderProgram& shader) { + shader.set_uniform("uniform_color", Options_Colors[static_cast(colors_id)]); + shader.set_uniform("zoom", zoom); + shader.set_uniform("point_sizes", point_sizes); + if (is_glsl_120) { + glsafe(::glEnable(GL_POINT_SPRITE)); + glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); + } + for (const RenderPath& path : buffer.render_paths) { + glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_multi_points_calls_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS + } + if (is_glsl_120) { + glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); + glsafe(::glDisable(GL_POINT_SPRITE)); + } + }; + glsafe(::glCullFace(GL_BACK)); glsafe(::glLineWidth(3.0f)); @@ -773,146 +793,32 @@ void GCodeViewer::render_toolpaths() const { case GCodeProcessor::EMoveType::Tool_change: { - shader->set_uniform("uniform_color", Options_Colors[static_cast(EOptionsColors::ToolChanges)]); - shader->set_uniform("zoom", zoom); - shader->set_uniform("point_sizes", point_sizes); - if (is_glsl_120) - { - glsafe(::glEnable(GL_POINT_SPRITE)); - glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); - } - for (const RenderPath& path : buffer.render_paths) - { - glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); -#if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.gl_multi_points_calls_count; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - } - if (is_glsl_120) - { - glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); - glsafe(::glDisable(GL_POINT_SPRITE)); - } + render_options(buffer, EOptionsColors::ToolChanges, *shader); break; } case GCodeProcessor::EMoveType::Color_change: { - shader->set_uniform("uniform_color", Options_Colors[static_cast(EOptionsColors::ColorChanges)]); - shader->set_uniform("zoom", zoom); - shader->set_uniform("point_sizes", point_sizes); - if (is_glsl_120) - { - glsafe(::glEnable(GL_POINT_SPRITE)); - glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); - } - for (const RenderPath& path : buffer.render_paths) - { - glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); -#if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.gl_multi_points_calls_count; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - } - if (is_glsl_120) - { - glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); - glsafe(::glDisable(GL_POINT_SPRITE)); - } + render_options(buffer, EOptionsColors::ColorChanges, *shader); break; } case GCodeProcessor::EMoveType::Pause_Print: { - shader->set_uniform("uniform_color", Options_Colors[static_cast(EOptionsColors::PausePrints)]); - shader->set_uniform("zoom", zoom); - shader->set_uniform("point_sizes", point_sizes); - if (is_glsl_120) - { - glsafe(::glEnable(GL_POINT_SPRITE)); - glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); - } - for (const RenderPath& path : buffer.render_paths) - { - glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); -#if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.gl_multi_points_calls_count; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - } - if (is_glsl_120) - { - glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); - glsafe(::glDisable(GL_POINT_SPRITE)); - } + render_options(buffer, EOptionsColors::PausePrints, *shader); break; } case GCodeProcessor::EMoveType::Custom_GCode: { - shader->set_uniform("uniform_color", Options_Colors[static_cast(EOptionsColors::CustomGCodes)]); - shader->set_uniform("zoom", zoom); - shader->set_uniform("point_sizes", point_sizes); - if (is_glsl_120) - { - glsafe(::glEnable(GL_POINT_SPRITE)); - glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); - } - for (const RenderPath& path : buffer.render_paths) - { - glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); -#if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.gl_multi_points_calls_count; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - } - if (is_glsl_120) - { - glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); - glsafe(::glDisable(GL_POINT_SPRITE)); - } + render_options(buffer, EOptionsColors::CustomGCodes, *shader); break; } case GCodeProcessor::EMoveType::Retract: { - shader->set_uniform("uniform_color", Options_Colors[static_cast(EOptionsColors::Retractions)]); - shader->set_uniform("zoom", zoom); - shader->set_uniform("point_sizes", point_sizes); - if (is_glsl_120) - { - glsafe(::glEnable(GL_POINT_SPRITE)); - glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); - } - for (const RenderPath& path : buffer.render_paths) - { - glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); -#if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.gl_multi_points_calls_count; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - if (is_glsl_120) - { - glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); - glsafe(::glDisable(GL_POINT_SPRITE)); - } - } + render_options(buffer, EOptionsColors::Retractions, *shader); break; } case GCodeProcessor::EMoveType::Unretract: { - shader->set_uniform("uniform_color", Options_Colors[static_cast(EOptionsColors::Unretractions)]); - shader->set_uniform("zoom", zoom); - shader->set_uniform("point_sizes", point_sizes); - if (is_glsl_120) - { - glsafe(::glEnable(GL_POINT_SPRITE)); - glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); - } - for (const RenderPath& path : buffer.render_paths) - { - glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); -#if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.gl_multi_points_calls_count; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - } - if (is_glsl_120) - { - glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); - glsafe(::glDisable(GL_POINT_SPRITE)); - } + render_options(buffer, EOptionsColors::Unretractions, *shader); break; } case GCodeProcessor::EMoveType::Extrude: diff --git a/src/slic3r/GUI/GLShader.cpp b/src/slic3r/GUI/GLShader.cpp index 42e5b0a9f5..a6d641f89f 100644 --- a/src/slic3r/GUI/GLShader.cpp +++ b/src/slic3r/GUI/GLShader.cpp @@ -215,6 +215,16 @@ bool GLShaderProgram::set_uniform(const char* name, double value) const return set_uniform(name, static_cast(value)); } +bool GLShaderProgram::set_uniform(const char* name, const std::array& value) const +{ + int id = get_uniform_location(name); + if (id >= 0) { + glsafe(::glUniform4iv(id, 1, static_cast(value.data()))); + return true; + } + return false; +} + bool GLShaderProgram::set_uniform(const char* name, const std::array& value) const { int id = get_uniform_location(name); @@ -290,6 +300,21 @@ bool GLShaderProgram::set_uniform(const char* name, const Matrix3f& value) const return false; } +bool GLShaderProgram::set_uniform(const char* name, const Vec3f& value) const +{ + int id = get_uniform_location(name); + if (id >= 0) { + glsafe(::glUniform3fv(id, 1, static_cast(value.data()))); + return true; + } + return false; +} + +bool GLShaderProgram::set_uniform(const char* name, const Vec3d& value) const +{ + return set_uniform(name, static_cast(value.cast())); +} + int GLShaderProgram::get_attrib_location(const char* name) const { return (m_id > 0) ? ::glGetAttribLocation(m_id, name) : -1; diff --git a/src/slic3r/GUI/GLShader.hpp b/src/slic3r/GUI/GLShader.hpp index e58437fbd1..521f6796f1 100644 --- a/src/slic3r/GUI/GLShader.hpp +++ b/src/slic3r/GUI/GLShader.hpp @@ -43,6 +43,7 @@ public: bool set_uniform(const char* name, bool value) const; bool set_uniform(const char* name, float value) const; bool set_uniform(const char* name, double value) const; + bool set_uniform(const char* name, const std::array& value) const; bool set_uniform(const char* name, const std::array& value) const; bool set_uniform(const char* name, const std::array& value) const; bool set_uniform(const char* name, const std::array& value) const; @@ -50,6 +51,8 @@ public: bool set_uniform(const char* name, const Transform3f& value) const; bool set_uniform(const char* name, const Transform3d& value) const; bool set_uniform(const char* name, const Matrix3f& value) const; + bool set_uniform(const char* name, const Vec3f& value) const; + bool set_uniform(const char* name, const Vec3d& value) const; // returns -1 if not found int get_attrib_location(const char* name) const; From 1af798dbd7ed9b4763bd28c9cd9e65911cd7a2bd Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 25 May 2020 11:16:40 +0200 Subject: [PATCH 097/503] DoubleSlider::Control thumb text rendered closer to the slider --- resources/shaders/options_120.fs | 1 + src/slic3r/GUI/DoubleSlider.cpp | 10 +++++----- src/slic3r/GUI/GUI_Preview.cpp | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/resources/shaders/options_120.fs b/resources/shaders/options_120.fs index 236174f3a3..1c53f6c725 100644 --- a/resources/shaders/options_120.fs +++ b/resources/shaders/options_120.fs @@ -1,3 +1,4 @@ +// version 120 is needed for gl_PointCoord #version 120 uniform vec3 uniform_color; diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 5c16e11eef..7415f333f8 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -566,12 +566,12 @@ void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_ dc.GetMultiLineTextExtent(label, &text_width, &text_height); wxPoint text_pos; if (right_side) - text_pos = is_horizontal() ? wxPoint(pos.x + 1, pos.y + m_thumb_size.x) : - wxPoint(pos.x + m_thumb_size.x+1, pos.y - 0.5*text_height - 1); + text_pos = is_horizontal() ? wxPoint(pos.x + 1, pos.y + m_thumb_size.x / 4) : + wxPoint(pos.x + m_thumb_size.x + 1, pos.y - 0.5 * text_height - 1); else - text_pos = is_horizontal() ? wxPoint(pos.x - text_width - 1, pos.y - m_thumb_size.x - text_height) : - wxPoint(pos.x - text_width - 1 - m_thumb_size.x, pos.y - 0.5*text_height + 1); - dc.DrawText(label, text_pos); + text_pos = is_horizontal() ? wxPoint(pos.x - text_width - 1, pos.y - m_thumb_size.x / 4 - text_height) : + wxPoint(pos.x - text_width - 1 - m_thumb_size.x, pos.y - 0.5 * text_height + 1); + dc.DrawText(label, text_pos); } void Control::draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 1784dbccc1..fdbd396e26 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -343,7 +343,7 @@ bool Preview::init(wxWindow* parent, Model* model) #endif // ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER - m_moves_slider = new DoubleSlider::Control(m_bottom_toolbar_panel, wxID_ANY, 0, 0, 0, 100, wxDefaultPosition, wxSize(-1, 4 * GetTextExtent("m").y), wxSL_HORIZONTAL); + m_moves_slider = new DoubleSlider::Control(m_bottom_toolbar_panel, wxID_ANY, 0, 0, 0, 100, wxDefaultPosition, wxSize(-1, 3 * GetTextExtent("m").y), wxSL_HORIZONTAL); m_moves_slider->SetDrawMode(DoubleSlider::dmSequentialGCodeView); wxBoxSizer* bottom_toolbar_sizer = new wxBoxSizer(wxHORIZONTAL); From 6810550a6cb5fcca497f6017e182cc88b4aaa7ba Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 25 May 2020 11:59:12 +0200 Subject: [PATCH 098/503] DoubleSlider::Control background color --- src/slic3r/GUI/DoubleSlider.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 7415f333f8..1682677df4 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -399,7 +399,11 @@ void Control::draw_focus_rect() void Control::render() { +#if ENABLE_GCODE_VIEWER + SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); +#else SetBackgroundColour(GetParent()->GetBackgroundColour()); +#endif // ENABLE_GCODE_VIEWER draw_focus_rect(); wxPaintDC dc(this); @@ -770,7 +774,11 @@ void Control::draw_colored_band(wxDC& dc) // don't color a band for MultiExtruder mode if (m_ticks.empty() || m_mode == t_mode::MultiExtruder) { +#if ENABLE_GCODE_VIEWER + draw_band(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW), main_band); +#else draw_band(dc, GetParent()->GetBackgroundColour(), main_band); +#endif // ENABLE_GCODE_VIEWER return; } From a63e5b352ee91276215fb0b087aa3dcde447f660 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 25 May 2020 12:08:09 +0200 Subject: [PATCH 099/503] ENABLE_GCODE_VIEWER -> Reduced vertical size of horizontal slider --- src/slic3r/GUI/GUI_Preview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index fdbd396e26..08cb3cdd76 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -343,7 +343,7 @@ bool Preview::init(wxWindow* parent, Model* model) #endif // ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER - m_moves_slider = new DoubleSlider::Control(m_bottom_toolbar_panel, wxID_ANY, 0, 0, 0, 100, wxDefaultPosition, wxSize(-1, 3 * GetTextExtent("m").y), wxSL_HORIZONTAL); + m_moves_slider = new DoubleSlider::Control(m_bottom_toolbar_panel, wxID_ANY, 0, 0, 0, 100, wxDefaultPosition, wxSize(-1, 2.5 * GetTextExtent("m").y), wxSL_HORIZONTAL); m_moves_slider->SetDrawMode(DoubleSlider::dmSequentialGCodeView); wxBoxSizer* bottom_toolbar_sizer = new wxBoxSizer(wxHORIZONTAL); From 2759380000f4407ea43e30deb7215dfbaf59b485 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 25 May 2020 13:53:41 +0200 Subject: [PATCH 100/503] DoubleSlider:Control platform dependent background color --- src/slic3r/GUI/DoubleSlider.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 1682677df4..087b1774e5 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -400,7 +400,11 @@ void Control::draw_focus_rect() void Control::render() { #if ENABLE_GCODE_VIEWER +#ifdef _WIN32 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); +#else + SetBackgroundColour(GetParent()->GetBackgroundColour()); +#endif // _WIN32 #else SetBackgroundColour(GetParent()->GetBackgroundColour()); #endif // ENABLE_GCODE_VIEWER @@ -570,10 +574,10 @@ void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_ dc.GetMultiLineTextExtent(label, &text_width, &text_height); wxPoint text_pos; if (right_side) - text_pos = is_horizontal() ? wxPoint(pos.x + 1, pos.y + m_thumb_size.x / 4) : + text_pos = is_horizontal() ? wxPoint(pos.x + 1, pos.y + m_thumb_size.x / 3) : wxPoint(pos.x + m_thumb_size.x + 1, pos.y - 0.5 * text_height - 1); else - text_pos = is_horizontal() ? wxPoint(pos.x - text_width - 1, pos.y - m_thumb_size.x / 4 - text_height) : + text_pos = is_horizontal() ? wxPoint(pos.x - text_width - 1, pos.y - m_thumb_size.x / 3 - text_height) : wxPoint(pos.x - text_width - 1 - m_thumb_size.x, pos.y - 0.5 * text_height + 1); dc.DrawText(label, text_pos); } @@ -775,7 +779,11 @@ void Control::draw_colored_band(wxDC& dc) if (m_ticks.empty() || m_mode == t_mode::MultiExtruder) { #if ENABLE_GCODE_VIEWER +#ifdef _WIN32 draw_band(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW), main_band); +#else + draw_band(dc, GetParent()->GetBackgroundColour(), main_band); +#endif // _WIN32 #else draw_band(dc, GetParent()->GetBackgroundColour(), main_band); #endif // ENABLE_GCODE_VIEWER From 1d317489fd096751335c633a85b2ff2ee1669e2d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 26 May 2020 08:16:08 +0200 Subject: [PATCH 101/503] GCodeViewer -> Temporary ImGui dialog for editing shaders parameters --- resources/shaders/options_120.fs | 27 +++++-- src/libslic3r/Technologies.hpp | 2 + src/slic3r/GUI/GCodeViewer.cpp | 127 ++++++++++++++++++++++++++++--- src/slic3r/GUI/GCodeViewer.hpp | 26 +++++-- src/slic3r/GUI/GLCanvas3D.cpp | 3 + 5 files changed, 165 insertions(+), 20 deletions(-) diff --git a/resources/shaders/options_120.fs b/resources/shaders/options_120.fs index 1c53f6c725..90d417b6e7 100644 --- a/resources/shaders/options_120.fs +++ b/resources/shaders/options_120.fs @@ -2,6 +2,26 @@ #version 120 uniform vec3 uniform_color; +uniform float percent_outline_radius; +uniform float percent_center_radius; + +vec4 hard_color(float sq_radius) +{ + if ((sq_radius < 0.005625) || (sq_radius > 0.180625)) + return vec4(0.5 * uniform_color, 1.0); + else + return vec4(uniform_color, 1.0); +} + +vec4 custom_color(float sq_radius) +{ + float in_radius = 0.5 * percent_center_radius; + float out_radius = 0.5 * (1.0 - percent_outline_radius); + if ((sq_radius < in_radius * in_radius) || (sq_radius > out_radius * out_radius)) + return vec4(0.5 * uniform_color, 1.0); + else + return vec4(uniform_color, 1.0); +} void main() { @@ -9,9 +29,6 @@ void main() float sq_radius = dot(pos, pos); if (sq_radius > 0.25) discard; - - if ((sq_radius < 0.005625) || (sq_radius > 0.180625)) - gl_FragColor = vec4(0.5 * uniform_color, 1.0); - else - gl_FragColor = vec4(uniform_color, 1.0); + + gl_FragColor = custom_color(sq_radius); } diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 3df9da961d..5631dadf34 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -45,5 +45,7 @@ // Enable G-Code viewer #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) #define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) +#define ENABLE_GCODE_VIEWER_SHADERS_EDITOR (1 && ENABLE_GCODE_VIEWER) + #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 54c7d3bb5a..6d41976941 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -221,6 +221,19 @@ const std::vector GCodeViewer::Range_Colors {{ { 0.761f, 0.322f, 0.235f } // reddish }}; +bool GCodeViewer::init() +{ + set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Extrude, true); + m_sequential_view.marker.init(); + init_shaders(); + + std::array point_sizes; + ::glGetIntegerv(GL_ALIASED_POINT_SIZE_RANGE, point_sizes.data()); + m_detected_point_sizes = { static_cast(point_sizes[0]), static_cast(point_sizes[1]) }; + + return true; +} + void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized) { // avoid processing if called with the same gcode_result @@ -328,6 +341,9 @@ void GCodeViewer::render() const #if ENABLE_GCODE_VIEWER_STATISTICS render_statistics(); #endif // ENABLE_GCODE_VIEWER_STATISTICS +#if ENABLE_GCODE_VIEWER_SHADERS_EDITOR + render_shaders_editor(); +#endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR } bool GCodeViewer::is_toolpath_move_type_visible(GCodeProcessor::EMoveType type) const @@ -737,30 +753,47 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool void GCodeViewer::render_toolpaths() const { +#if ENABLE_GCODE_VIEWER_SHADERS_EDITOR + bool is_glsl_120 = m_shaders_editor.glsl_version == 1 && wxGetApp().is_glsl_version_greater_or_equal_to(1, 20); + std::array point_sizes; + if (m_shaders_editor.size_dependent_on_zoom) + { + point_sizes = { std::min(static_cast(m_shaders_editor.sizes[0]), m_detected_point_sizes[1]), std::min(static_cast(m_shaders_editor.sizes[1]), m_detected_point_sizes[1]) }; + } + else + point_sizes = { static_cast(m_shaders_editor.fixed_size), static_cast(m_shaders_editor.fixed_size) }; +#else bool is_glsl_120 = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20); - int detected_point_sizes[2]; - ::glGetIntegerv(GL_ALIASED_POINT_SIZE_RANGE, detected_point_sizes); - std::array point_sizes = { std::min(8.0f, static_cast(detected_point_sizes[1])), std::min(48.0f, static_cast(detected_point_sizes[1])) }; + std::array point_sizes = { std::min(8.0f, m_detected_point_sizes[1]), std::min(48.0f, m_detected_point_sizes[1]) }; +#endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR double zoom = wxGetApp().plater()->get_camera().get_zoom(); auto render_options = [this, is_glsl_120, zoom, point_sizes](const IBuffer& buffer, EOptionsColors colors_id, GLShaderProgram& shader) { shader.set_uniform("uniform_color", Options_Colors[static_cast(colors_id)]); +#if ENABLE_GCODE_VIEWER_SHADERS_EDITOR + shader.set_uniform("zoom", m_shaders_editor.size_dependent_on_zoom ? zoom : 1.0f); + shader.set_uniform("percent_outline_radius", 0.01f * static_cast(m_shaders_editor.percent_outline)); + shader.set_uniform("percent_center_radius", 0.01f * static_cast(m_shaders_editor.percent_center)); +#else shader.set_uniform("zoom", zoom); + shader.set_uniform("percent_outline_radius", 0.15f); + shader.set_uniform("percent_center_radius", 0.15f); +#endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR shader.set_uniform("point_sizes", point_sizes); - if (is_glsl_120) { + glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); + if (is_glsl_120) glsafe(::glEnable(GL_POINT_SPRITE)); - glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); - } + for (const RenderPath& path : buffer.render_paths) { glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } - if (is_glsl_120) { - glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); + if (is_glsl_120) glsafe(::glDisable(GL_POINT_SPRITE)); - } + + glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); }; glsafe(::glCullFace(GL_BACK)); @@ -900,7 +933,11 @@ void GCodeViewer::render_legend() const Line }; +#if ENABLE_GCODE_VIEWER_SHADERS_EDITOR + auto add_item = [this, draw_list, &imgui](EItemType type, const Color& color, const std::string& label, std::function callback = nullptr) { +#else auto add_item = [draw_list, &imgui](EItemType type, const Color& color, const std::string& label, std::function callback = nullptr) { +#endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR float icon_size = ImGui::GetTextLineHeight(); ImVec2 pos = ImGui::GetCursorPos(); switch (type) @@ -915,6 +952,20 @@ void GCodeViewer::render_legend() const } case EItemType::Circle: { +#if ENABLE_GCODE_VIEWER_SHADERS_EDITOR + draw_list->AddCircle({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, 0.5f * icon_size, ICON_BORDER_COLOR, 16); + draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, (0.5f * icon_size) - 2.0f, + ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); + float radius = ((0.5f * icon_size) - 2.0f) * (1.0f - 0.01f * static_cast(m_shaders_editor.percent_outline)); + draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, radius, + ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); + if (m_shaders_editor.percent_center > 0) + { + radius = ((0.5f * icon_size) - 2.0f) * 0.01f * static_cast(m_shaders_editor.percent_center); + draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, radius, + ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); + } +#else draw_list->AddCircle({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, 0.5f * icon_size, ICON_BORDER_COLOR, 16); draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, (0.5f * icon_size) - 2.0f, ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); @@ -922,6 +973,7 @@ void GCodeViewer::render_legend() const ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, 1.5f, ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); +#endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR break; } case EItemType::Line: @@ -1266,6 +1318,63 @@ void GCodeViewer::render_statistics() const } #endif // ENABLE_GCODE_VIEWER_STATISTICS +#if ENABLE_GCODE_VIEWER_SHADERS_EDITOR +void GCodeViewer::render_shaders_editor() const +{ + auto set_shader = [this](const std::string& shader) { + unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); + unsigned char end_id = buffer_id(GCodeProcessor::EMoveType::Custom_GCode); + for (unsigned char i = begin_id; i <= end_id; ++i) { + m_buffers[i].shader = shader; + } + }; + + static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + + Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size(); + imgui.set_next_window_pos(static_cast(cnv_size.get_width()), 0.5f * static_cast(cnv_size.get_height()), ImGuiCond_Once, 1.0f, 0.5f); + imgui.begin(std::string("Shaders editor (DEV only)"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize); + + ImGui::RadioButton("glsl version 1.10 (low end PCs)", &m_shaders_editor.glsl_version, 0); + ImGui::RadioButton("glsl version 1.20 (default)", &m_shaders_editor.glsl_version, 1); + switch (m_shaders_editor.glsl_version) + { + case 0: { set_shader("options_110"); break; } + case 1: { set_shader("options_120"); break; } + } + + if (ImGui::CollapsingHeader("Options", ImGuiTreeNodeFlags_DefaultOpen)) + { + ImGui::Checkbox("size dependent on zoom", &m_shaders_editor.size_dependent_on_zoom); + if (m_shaders_editor.size_dependent_on_zoom) + { + if (ImGui::SliderInt("min size (min zoom)", &m_shaders_editor.sizes[0], 1, 100)) + { + if (m_shaders_editor.sizes[1] < m_shaders_editor.sizes[0]) + m_shaders_editor.sizes[1] = m_shaders_editor.sizes[0]; + } + ImGui::SliderInt("max size (max zoom)", &m_shaders_editor.sizes[1], 1, 100); + { + if (m_shaders_editor.sizes[1] < m_shaders_editor.sizes[0]) + m_shaders_editor.sizes[0] = m_shaders_editor.sizes[1]; + } + } + else + ImGui::SliderInt("fixed size", &m_shaders_editor.fixed_size, 1, 100); + + if (m_shaders_editor.glsl_version == 1) + { + ImGui::SliderInt("percent outline", &m_shaders_editor.percent_outline, 0, 50); + ImGui::SliderInt("percent center", &m_shaders_editor.percent_center, 0, 50); + } + } + + imgui.end(); +} +#endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR + bool GCodeViewer::is_travel_in_z_range(size_t id) const { const IBuffer& buffer = m_buffers[buffer_id(GCodeProcessor::EMoveType::Travel)]; diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 5ab9f68325..e7c620fc5e 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -202,6 +202,18 @@ class GCodeViewer }; #endif // ENABLE_GCODE_VIEWER_STATISTICS +#if ENABLE_GCODE_VIEWER_SHADERS_EDITOR + struct ShadersEditor + { + int glsl_version{ 1 }; + bool size_dependent_on_zoom{ true }; + int fixed_size{ 16 }; + std::array sizes{ 8, 64 }; + int percent_outline{ 15 }; + int percent_center{ 15 }; + }; +#endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR + public: struct SequentialView { @@ -271,17 +283,16 @@ private: #if ENABLE_GCODE_VIEWER_STATISTICS mutable Statistics m_statistics; #endif // ENABLE_GCODE_VIEWER_STATISTICS +#if ENABLE_GCODE_VIEWER_SHADERS_EDITOR + mutable ShadersEditor m_shaders_editor; +#endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR + std::array m_detected_point_sizes = { 0.0f, 0.0f }; public: GCodeViewer() = default; ~GCodeViewer() { reset(); } - bool init() { - set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Extrude, true); - m_sequential_view.marker.init(); - init_shaders(); - return true; - } + bool init(); // extract rendering data from the given parameters void load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized); @@ -334,6 +345,9 @@ private: #if ENABLE_GCODE_VIEWER_STATISTICS void render_statistics() const; #endif // ENABLE_GCODE_VIEWER_STATISTICS +#if ENABLE_GCODE_VIEWER_SHADERS_EDITOR + void render_shaders_editor() const; +#endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR bool is_visible(ExtrusionRole role) const { return role < erCount && (m_extrusions.role_visibility_flags & (1 << role)) != 0; } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index ee23783c38..0c0bad7a09 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3502,6 +3502,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) #ifdef SLIC3R_DEBUG_MOUSE_EVENTS printf((format_mouse_event_debug_message(evt) + " - Consumed by ImGUI\n").c_str()); #endif /* SLIC3R_DEBUG_MOUSE_EVENTS */ +#if ENABLE_GCODE_VIEWER + m_dirty = true; +#endif // ENABLE_GCODE_VIEWER // do not return if dragging or tooltip not empty to allow for tooltip update if (!m_mouse.dragging && m_tooltip.is_empty()) return; From 8f91b4f4f4a726fe82b961357e284888ea04121d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 26 May 2020 08:34:19 +0200 Subject: [PATCH 102/503] DoubleSlider::Control -> Tweaks to text position for horizontal case --- src/slic3r/GUI/DoubleSlider.cpp | 4 ++-- src/slic3r/GUI/GUI_Preview.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 087b1774e5..582a356754 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -574,10 +574,10 @@ void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_ dc.GetMultiLineTextExtent(label, &text_width, &text_height); wxPoint text_pos; if (right_side) - text_pos = is_horizontal() ? wxPoint(pos.x + 1, pos.y + m_thumb_size.x / 3) : + text_pos = is_horizontal() ? wxPoint(pos.x + 1, pos.y + m_thumb_size.x / 2 + 1) : wxPoint(pos.x + m_thumb_size.x + 1, pos.y - 0.5 * text_height - 1); else - text_pos = is_horizontal() ? wxPoint(pos.x - text_width - 1, pos.y - m_thumb_size.x / 3 - text_height) : + text_pos = is_horizontal() ? wxPoint(pos.x - text_width - 1, pos.y - m_thumb_size.x / 2 - text_height - 1) : wxPoint(pos.x - text_width - 1 - m_thumb_size.x, pos.y - 0.5 * text_height + 1); dc.DrawText(label, text_pos); } diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 08cb3cdd76..fdbd396e26 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -343,7 +343,7 @@ bool Preview::init(wxWindow* parent, Model* model) #endif // ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER - m_moves_slider = new DoubleSlider::Control(m_bottom_toolbar_panel, wxID_ANY, 0, 0, 0, 100, wxDefaultPosition, wxSize(-1, 2.5 * GetTextExtent("m").y), wxSL_HORIZONTAL); + m_moves_slider = new DoubleSlider::Control(m_bottom_toolbar_panel, wxID_ANY, 0, 0, 0, 100, wxDefaultPosition, wxSize(-1, 3 * GetTextExtent("m").y), wxSL_HORIZONTAL); m_moves_slider->SetDrawMode(DoubleSlider::dmSequentialGCodeView); wxBoxSizer* bottom_toolbar_sizer = new wxBoxSizer(wxHORIZONTAL); From aa04f0e555b1f8e073ec1383324d0e30b5c6da24 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 27 May 2020 08:06:02 +0200 Subject: [PATCH 103/503] ENABLE_GCODE_VIEWER -> Completed implementation of new GLModel class --- src/slic3r/GUI/3DBed.cpp | 29 +++++++++++-- src/slic3r/GUI/3DBed.hpp | 15 ++++++- src/slic3r/GUI/3DScene.cpp | 4 +- src/slic3r/GUI/3DScene.hpp | 4 +- src/slic3r/GUI/GCodeViewer.hpp | 2 +- src/slic3r/GUI/GLModel.cpp | 79 ++++++++++++++++++++++------------ src/slic3r/GUI/GLModel.hpp | 12 ++++-- src/slic3r/GUI/Selection.hpp | 4 +- 8 files changed, 105 insertions(+), 44 deletions(-) diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index b5a34b2ee7..450a538d04 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -358,14 +358,23 @@ void Bed3D::calc_bounding_boxes() const // extend to contain axes #if ENABLE_GCODE_VIEWER - m_extended_bounding_box.merge(m_axes.get_total_length() * Vec3d::Ones()); -#else - m_extended_bounding_box.merge(m_axes.length + Axes::ArrowLength * Vec3d::Ones()); -#endif // ENABLE_GCODE_VIEWER + m_extended_bounding_box.merge(m_axes.get_origin() + m_axes.get_total_length() * Vec3d::Ones()); + m_extended_bounding_box.merge(m_extended_bounding_box.min + Vec3d(-Axes::DefaultTipRadius, -Axes::DefaultTipRadius, m_extended_bounding_box.max(2))); + // extend to contain model, if any + BoundingBoxf3 model_bb = m_model.get_bounding_box(); + if (model_bb.defined) + { + model_bb.translate(m_model_offset); + m_extended_bounding_box.merge(model_bb); + } +#else + m_extended_bounding_box.merge(m_axes.get_total_length() * Vec3d::Ones()); + m_extended_bounding_box.merge(m_axes.length + Axes::ArrowLength * Vec3d::Ones()); // extend to contain model, if any if (!m_model.get_filename().empty()) m_extended_bounding_box.merge(m_model.get_transformed_bounding_box()); +#endif // ENABLE_GCODE_VIEWER } void Bed3D::calc_triangles(const ExPolygon& poly) @@ -621,7 +630,11 @@ void Bed3D::render_model() const // move the model so that its origin (0.0, 0.0, 0.0) goes into the bed shape center and a bit down to avoid z-fighting with the texture quad Vec3d shift = m_bounding_box.center(); shift(2) = -0.03; +#if ENABLE_GCODE_VIEWER + m_model_offset = shift; +#else m_model.set_offset(shift); +#endif // ENABLE_GCODE_VIEWER // update extended bounding box calc_bounding_boxes(); @@ -633,7 +646,15 @@ void Bed3D::render_model() const if (shader != nullptr) { shader->start_using(); +#if ENABLE_GCODE_VIEWER + shader->set_uniform("uniform_color", m_model_color); + ::glPushMatrix(); + ::glTranslated(m_model_offset(0), m_model_offset(1), m_model_offset(2)); +#endif // ENABLE_GCODE_VIEWER m_model.render(); +#if ENABLE_GCODE_VIEWER + ::glPopMatrix(); +#endif // ENABLE_GCODE_VIEWER shader->stop_using(); } } diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index 2487be2e47..b9e952c4a4 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -8,6 +8,9 @@ #endif // ENABLE_GCODE_VIEWER #include +#if ENABLE_GCODE_VIEWER +#include +#endif // ENABLE_GCODE_VIEWER #if !ENABLE_GCODE_VIEWER class GLUquadric; @@ -52,10 +55,13 @@ class Bed3D #if ENABLE_GCODE_VIEWER class Axes { + public: static const float DefaultStemRadius; static const float DefaultStemLength; static const float DefaultTipRadius; static const float DefaultTipLength; + + private: #else struct Axes { @@ -67,7 +73,7 @@ class Bed3D #if ENABLE_GCODE_VIEWER Vec3d m_origin{ Vec3d::Zero() }; float m_stem_length{ DefaultStemLength }; - mutable GL_Model m_arrow; + mutable GLModel m_arrow; public: #else @@ -82,6 +88,7 @@ class Bed3D #endif // !ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER + const Vec3d& get_origin() const { return m_origin; } void set_origin(const Vec3d& origin) { m_origin = origin; } void set_stem_length(float length); float get_total_length() const { return m_stem_length + DefaultTipLength; } @@ -113,7 +120,13 @@ private: GeometryBuffer m_triangles; GeometryBuffer m_gridlines; mutable GLTexture m_texture; +#if ENABLE_GCODE_VIEWER + mutable GLModel m_model; + mutable Vec3d m_model_offset{ Vec3d::Zero() }; + std::array m_model_color{ 0.235f, 0.235f, 0.235f, 1.0f }; +#else mutable GLBed m_model; +#endif // ENABLE_GCODE_VIEWER // temporary texture shown until the main texture has still no levels compressed mutable GLTexture m_temp_texture; mutable unsigned int m_vbo_id; diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 49b1c255b7..59be43271a 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1822,6 +1822,7 @@ void _3DScene::point3_to_verts(const Vec3crd& point, double width, double height thick_point_to_verts(point, width, height, volume); } +#if !ENABLE_GCODE_VIEWER GLModel::GLModel() : m_filename("") { @@ -1904,7 +1905,6 @@ void GLModel::render() const glsafe(::glDisable(GL_BLEND)); } -#if !ENABLE_GCODE_VIEWER bool GLArrow::on_init() { Pointf3s vertices; @@ -2076,7 +2076,6 @@ bool GLCurvedArrow::on_init() m_volume.indexed_vertex_array.finalize_geometry(true); return true; } -#endif // !ENABLE_GCODE_VIEWER bool GLBed::on_init_from_file(const std::string& filename) { @@ -2108,5 +2107,6 @@ bool GLBed::on_init_from_file(const std::string& filename) return true; } +#endif // !ENABLE_GCODE_VIEWER } // namespace Slic3r diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index fd74cd4912..d7bd0c1b3c 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -599,6 +599,7 @@ private: GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, const Transform3d& view_matrix, std::function filter_func = nullptr); +#if !ENABLE_GCODE_VIEWER class GLModel { protected: @@ -636,7 +637,6 @@ protected: virtual bool on_init_from_file(const std::string& filename) { return false; } }; -#if !ENABLE_GCODE_VIEWER class GLArrow : public GLModel { protected: @@ -653,13 +653,13 @@ public: protected: bool on_init() override; }; -#endif // !ENABLE_GCODE_VIEWER class GLBed : public GLModel { protected: bool on_init_from_file(const std::string& filename) override; }; +#endif // !ENABLE_GCODE_VIEWER struct _3DScene { diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index e7c620fc5e..1d940b66a7 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -219,7 +219,7 @@ public: { class Marker { - GL_Model m_model; + GLModel m_model; Transform3f m_world_transform; BoundingBoxf3 m_world_bounding_box; std::array m_color{ 1.0f, 1.0f, 1.0f, 1.0f }; diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index e5b6cbdcb6..e738aa3c49 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -3,13 +3,17 @@ #include "3DScene.hpp" #include "libslic3r/TriangleMesh.hpp" +#include "libslic3r/Model.hpp" + +#include +#include #include namespace Slic3r { namespace GUI { -void GL_Model::init_from(const GLModelInitializationData& data) +void GLModel::init_from(const GLModelInitializationData& data) { assert(!data.positions.empty() && !data.triangles.empty()); assert(data.positions.size() == data.normals.size()); @@ -20,8 +24,9 @@ void GL_Model::init_from(const GLModelInitializationData& data) // vertices/normals data std::vector vertices(6 * data.positions.size()); for (size_t i = 0; i < data.positions.size(); ++i) { - ::memcpy(static_cast(&vertices[i * 6]), static_cast(data.positions[i].data()), 3 * sizeof(float)); - ::memcpy(static_cast(&vertices[3 + i * 6]), static_cast(data.normals[i].data()), 3 * sizeof(float)); + size_t offset = i * 6; + ::memcpy(static_cast(&vertices[offset]), static_cast(data.positions[i].data()), 3 * sizeof(float)); + ::memcpy(static_cast(&vertices[3 + offset]), static_cast(data.normals[i].data()), 3 * sizeof(float)); } // indices data @@ -41,34 +46,26 @@ void GL_Model::init_from(const GLModelInitializationData& data) send_to_gpu(vertices, indices); } -void GL_Model::init_from(const TriangleMesh& mesh) +void GLModel::init_from(const TriangleMesh& mesh) { - auto get_normal = [](const std::array& triangle) { - return (triangle[1] - triangle[0]).cross(triangle[2] - triangle[0]).normalized(); - }; - if (m_vbo_id > 0) // call reset() if you want to reuse this model return; - assert(!mesh.its.vertices.empty() && !mesh.its.indices.empty()); // call require_shared_vertices() before to pass the mesh to this method + std::vector vertices = std::vector(18 * mesh.stl.stats.number_of_facets); + std::vector indices = std::vector(3 * mesh.stl.stats.number_of_facets); - // vertices data -> load from mesh - std::vector vertices(6 * mesh.its.vertices.size()); - for (size_t i = 0; i < mesh.its.vertices.size(); ++i) { - ::memcpy(static_cast(&vertices[i * 6]), static_cast(mesh.its.vertices[i].data()), 3 * sizeof(float)); - } - - // indices/normals data -> load from mesh - std::vector indices(3 * mesh.its.indices.size()); - for (size_t i = 0; i < mesh.its.indices.size(); ++i) { - const stl_triangle_vertex_indices& triangle = mesh.its.indices[i]; - for (size_t j = 0; j < 3; ++j) { - indices[i * 3 + j] = static_cast(triangle[j]); + unsigned int vertices_count = 0; + for (uint32_t i = 0; i < mesh.stl.stats.number_of_facets; ++i) { + const stl_facet& facet = mesh.stl.facet_start[i]; + for (uint32_t j = 0; j < 3; ++j) { + uint32_t offset = i * 18 + j * 6; + ::memcpy(static_cast(&vertices[offset]), static_cast(facet.vertex[j].data()), 3 * sizeof(float)); + ::memcpy(static_cast(&vertices[3 + offset]), static_cast(facet.normal.data()), 3 * sizeof(float)); } - Vec3f normal = get_normal({ mesh.its.vertices[triangle[0]], mesh.its.vertices[triangle[1]], mesh.its.vertices[triangle[2]] }); - ::memcpy(static_cast(&vertices[3 + static_cast(triangle[0]) * 6]), static_cast(normal.data()), 3 * sizeof(float)); - ::memcpy(static_cast(&vertices[3 + static_cast(triangle[1]) * 6]), static_cast(normal.data()), 3 * sizeof(float)); - ::memcpy(static_cast(&vertices[3 + static_cast(triangle[2]) * 6]), static_cast(normal.data()), 3 * sizeof(float)); + for (uint32_t j = 0; j < 3; ++j) { + indices[i * 3 + j] = vertices_count + j; + } + vertices_count += 3; } m_indices_count = static_cast(indices.size()); @@ -77,7 +74,32 @@ void GL_Model::init_from(const TriangleMesh& mesh) send_to_gpu(vertices, indices); } -void GL_Model::reset() +bool GLModel::init_from_file(const std::string& filename) +{ + if (!boost::filesystem::exists(filename)) + return false; + + if (!boost::algorithm::iends_with(filename, ".stl")) + return false; + + Model model; + try + { + model = Model::read_from_file(filename); + } + catch (std::exception&) + { + return false; + } + + init_from(model.mesh()); + + m_filename = filename; + + return true; +} + +void GLModel::reset() { // release gpu memory if (m_ibo_id > 0) { @@ -92,9 +114,10 @@ void GL_Model::reset() m_indices_count = 0; m_bounding_box = BoundingBoxf3(); + m_filename = std::string(); } -void GL_Model::render() const +void GLModel::render() const { if (m_vbo_id == 0 || m_ibo_id == 0) return; @@ -116,7 +139,7 @@ void GL_Model::render() const glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); } -void GL_Model::send_to_gpu(const std::vector& vertices, const std::vector& indices) +void GLModel::send_to_gpu(const std::vector& vertices, const std::vector& indices) { // vertex data -> send to gpu glsafe(::glGenBuffers(1, &m_vbo_id)); diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index a11073b191..0b4a69bdb0 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -4,6 +4,7 @@ #include "libslic3r/Point.hpp" #include "libslic3r/BoundingBox.hpp" #include +#include namespace Slic3r { @@ -18,24 +19,28 @@ namespace GUI { std::vector triangles; }; - class GL_Model + class GLModel { unsigned int m_vbo_id{ 0 }; unsigned int m_ibo_id{ 0 }; size_t m_indices_count{ 0 }; BoundingBoxf3 m_bounding_box; + std::string m_filename; public: - virtual ~GL_Model() { reset(); } + virtual ~GLModel() { reset(); } void init_from(const GLModelInitializationData& data); void init_from(const TriangleMesh& mesh); + bool init_from_file(const std::string& filename); void reset(); void render() const; const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; } + const std::string& get_filename() const { return m_filename; } + private: void send_to_gpu(const std::vector& vertices, const std::vector& indices); }; @@ -44,8 +49,7 @@ namespace GUI { // create an arrow with cylindrical stem and conical tip, with the given dimensions and resolution // the origin of the arrow is in the center of the stem cap // the arrow has its axis of symmetry along the Z axis and is pointing upward - GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float tip_height, - float stem_radius, float stem_height); + GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float tip_height, float stem_radius, float stem_height); // create an arrow whose stem is a quarter of circle, with the given dimensions and resolution // the origin of the arrow is in the center of the circle diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 34024875b3..c4ba30ed56 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -207,8 +207,8 @@ private: GLUquadricObj* m_quadric; #endif // ENABLE_RENDER_SELECTION_CENTER #if ENABLE_GCODE_VIEWER - GL_Model m_arrow; - GL_Model m_curved_arrow; + GLModel m_arrow; + GLModel m_curved_arrow; #else mutable GLArrow m_arrow; mutable GLCurvedArrow m_curved_arrow; From 94a4689b003b9e6e35487b09213d2dfd29c42954 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 27 May 2020 11:50:29 +0200 Subject: [PATCH 104/503] DoubleSlider::Control -> Change text position at the edges of horizontal slider --- src/slic3r/GUI/DoubleSlider.cpp | 33 +++++++++++++++++++++++++++------ src/slic3r/GUI/DoubleSlider.hpp | 4 ++-- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 582a356754..8cf4aea35d 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -272,14 +272,14 @@ wxCoord Control::get_position_from_value(const int value) return wxCoord(SLIDER_MARGIN + int(val*step + 0.5)); } -wxSize Control::get_size() +wxSize Control::get_size() const { int w, h; get_size(&w, &h); return wxSize(w, h); } -void Control::get_size(int *w, int *h) +void Control::get_size(int* w, int* h) const { GetSize(w, h); is_horizontal() ? *w -= m_lock_icon_dim : *h -= m_lock_icon_dim; @@ -574,11 +574,32 @@ void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_ dc.GetMultiLineTextExtent(label, &text_width, &text_height); wxPoint text_pos; if (right_side) - text_pos = is_horizontal() ? wxPoint(pos.x + 1, pos.y + m_thumb_size.x / 2 + 1) : - wxPoint(pos.x + m_thumb_size.x + 1, pos.y - 0.5 * text_height - 1); + { + if (is_horizontal()) + { + int width; + int height; + get_size(&width, &height); + + int x_right = pos.x + 1 + text_width; + int xx = (x_right < width) ? pos.x + 1 : pos.x - text_width - 1; + text_pos = wxPoint(xx, pos.y + m_thumb_size.x / 2 + 1); + } + else + text_pos = wxPoint(pos.x + m_thumb_size.x + 1, pos.y - 0.5 * text_height - 1); + } else - text_pos = is_horizontal() ? wxPoint(pos.x - text_width - 1, pos.y - m_thumb_size.x / 2 - text_height - 1) : - wxPoint(pos.x - text_width - 1 - m_thumb_size.x, pos.y - 0.5 * text_height + 1); + { + if (is_horizontal()) + { + int x = pos.x - text_width - 1; + int xx = (x > 0) ? x : pos.x + 1; + text_pos = wxPoint(xx, pos.y - m_thumb_size.x / 2 - text_height - 1); + } + else + text_pos = wxPoint(pos.x - text_width - 1 - m_thumb_size.x, pos.y - 0.5 * text_height + 1); + } + dc.DrawText(label, text_pos); } diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index a3d836c3f2..8aaba86a63 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -301,8 +301,8 @@ private: int get_value_from_position(const wxCoord x, const wxCoord y); int get_value_from_position(const wxPoint pos) { return get_value_from_position(pos.x, pos.y); } wxCoord get_position_from_value(const int value); - wxSize get_size(); - void get_size(int *w, int *h); + wxSize get_size() const; + void get_size(int* w, int* h) const; double get_double_value(const SelectedSlider& selection); wxString get_tooltip(int tick = -1); int get_edited_tick_for_position(wxPoint pos, const std::string& gcode = ColorChangeCode); From a0acf24ab8067e14acf977b920d93933f51efb4f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 27 May 2020 14:29:27 +0200 Subject: [PATCH 105/503] DoubleSlider::Control -> Fixed crash when pressing numpad [+] and [-] keys while the horizontal slider has focus --- src/slic3r/GUI/DoubleSlider.cpp | 38 +++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 8cf4aea35d..3374863b88 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -1387,6 +1387,22 @@ void Control::OnWheel(wxMouseEvent& event) void Control::OnKeyDown(wxKeyEvent &event) { const int key = event.GetKeyCode(); +#if ENABLE_GCODE_VIEWER + if (m_draw_mode != dmSequentialGCodeView && key == WXK_NUMPAD_ADD) { + // OnChar() is called immediately after OnKeyDown(), which can cause call of add_tick() twice. + // To avoid this case we should suppress second add_tick() call. + m_ticks.suppress_plus(true); + add_current_tick(true); + } + else if (m_draw_mode != dmSequentialGCodeView && (key == WXK_NUMPAD_SUBTRACT || key == WXK_DELETE || key == WXK_BACK)) { + // OnChar() is called immediately after OnKeyDown(), which can cause call of delete_tick() twice. + // To avoid this case we should suppress second delete_tick() call. + m_ticks.suppress_minus(true); + delete_current_tick(); + } + else if (m_draw_mode != dmSequentialGCodeView && event.GetKeyCode() == WXK_SHIFT) + UseDefaultColors(false); +#else if (key == WXK_NUMPAD_ADD) { // OnChar() is called immediately after OnKeyDown(), which can cause call of add_tick() twice. // To avoid this case we should suppress second add_tick() call. @@ -1401,6 +1417,7 @@ void Control::OnKeyDown(wxKeyEvent &event) } else if (event.GetKeyCode() == WXK_SHIFT) UseDefaultColors(false); +#endif // ENABLE_GCODE_VIEWER else if (is_horizontal()) { #if ENABLE_GCODE_VIEWER @@ -1451,14 +1468,21 @@ void Control::OnKeyUp(wxKeyEvent &event) void Control::OnChar(wxKeyEvent& event) { const int key = event.GetKeyCode(); - if (key == '+' && !m_ticks.suppressed_plus()) { - add_current_tick(true); - m_ticks.suppress_plus(false); - } - else if (key == '-' && !m_ticks.suppressed_minus()) { - delete_current_tick(); - m_ticks.suppress_minus(false); +#if ENABLE_GCODE_VIEWER + if (m_draw_mode != dmSequentialGCodeView) + { +#endif // ENABLE_GCODE_VIEWER + if (key == '+' && !m_ticks.suppressed_plus()) { + add_current_tick(true); + m_ticks.suppress_plus(false); + } + else if (key == '-' && !m_ticks.suppressed_minus()) { + delete_current_tick(); + m_ticks.suppress_minus(false); + } +#if ENABLE_GCODE_VIEWER } +#endif // ENABLE_GCODE_VIEWER if (key == 'G') #if ENABLE_GCODE_VIEWER jump_to_value(); From 100484dabe9909b62c9d6646c79f13443aa4727b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 27 May 2020 15:28:24 +0200 Subject: [PATCH 106/503] Added missing include --- src/slic3r/GUI/GCodeViewer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 6d41976941..2ba0a6e39d 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -5,6 +5,7 @@ #include "libslic3r/Print.hpp" #include "libslic3r/Geometry.hpp" #include "GUI_App.hpp" +#include "Plater.hpp" #include "PresetBundle.hpp" #include "Camera.hpp" #include "I18N.hpp" From e77fa3512ac0c9d697b9ad9620a5e955cf35209e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 27 May 2020 16:03:40 +0200 Subject: [PATCH 107/503] DoubleSlider::Control -> Shift and Ctrl used as accelerators for moving thumbs with arrows key and mouse wheel --- src/slic3r/GUI/DoubleSlider.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 45e242709d..384c984e52 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -1345,6 +1345,12 @@ void Control::move_current_thumb(const bool condition) if (is_horizontal()) delta *= -1; + // accelerators + if (wxGetKeyState(WXK_SHIFT) && wxGetKeyState(WXK_CONTROL)) + delta *= 10; + else if (wxGetKeyState(WXK_CONTROL)) + delta *= 5; + if (m_selection == ssLower) { m_lower_value -= delta; correct_lower_value(); From af3765c04c6ebba2c2b1e3961f96c4e37b7a4099 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 27 May 2020 16:14:14 +0200 Subject: [PATCH 108/503] Follow up of e77fa3512ac0c9d697b9ad9620a5e955cf35209e -> changed logic for DoubleSlider::Control accelerators --- src/slic3r/GUI/DoubleSlider.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 384c984e52..b68ff7e4e0 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -1346,10 +1346,13 @@ void Control::move_current_thumb(const bool condition) delta *= -1; // accelerators - if (wxGetKeyState(WXK_SHIFT) && wxGetKeyState(WXK_CONTROL)) - delta *= 10; - else if (wxGetKeyState(WXK_CONTROL)) - delta *= 5; + int accelerator = 0; + if (wxGetKeyState(WXK_SHIFT)) + accelerator += 5; + if (wxGetKeyState(WXK_CONTROL)) + accelerator += 5; + if (accelerator > 0) + delta *= accelerator; if (m_selection == ssLower) { m_lower_value -= delta; From 35190936a3fcca57376f30a9cafd8505c0ce508e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 27 May 2020 16:19:40 +0200 Subject: [PATCH 109/503] GCodeViewer -> Newer version of shader for options --- .../{options_120.fs => options_120_flat.fs} | 7 +- .../{options_120.vs => options_120_flat.vs} | 0 resources/shaders/options_120_solid.fs | 88 +++++++++++++++++++ resources/shaders/options_120_solid.vs | 14 +++ resources/shaders/shells.fs | 13 --- resources/shaders/shells.vs | 42 --------- src/libslic3r/Technologies.hpp | 2 +- src/slic3r/GUI/Camera.hpp | 1 + src/slic3r/GUI/GCodeViewer.cpp | 77 +++++++++------- src/slic3r/GUI/GCodeViewer.hpp | 8 +- src/slic3r/GUI/GLShader.cpp | 20 +++++ src/slic3r/GUI/GLShader.hpp | 2 + src/slic3r/GUI/GLShadersManager.cpp | 11 ++- 13 files changed, 187 insertions(+), 98 deletions(-) rename resources/shaders/{options_120.fs => options_120_flat.fs} (81%) rename resources/shaders/{options_120.vs => options_120_flat.vs} (100%) create mode 100644 resources/shaders/options_120_solid.fs create mode 100644 resources/shaders/options_120_solid.vs delete mode 100644 resources/shaders/shells.fs delete mode 100644 resources/shaders/shells.vs diff --git a/resources/shaders/options_120.fs b/resources/shaders/options_120_flat.fs similarity index 81% rename from resources/shaders/options_120.fs rename to resources/shaders/options_120_flat.fs index 90d417b6e7..656eccd1d4 100644 --- a/resources/shaders/options_120.fs +++ b/resources/shaders/options_120_flat.fs @@ -5,7 +5,7 @@ uniform vec3 uniform_color; uniform float percent_outline_radius; uniform float percent_center_radius; -vec4 hard_color(float sq_radius) +vec4 hardcoded_color(float sq_radius) { if ((sq_radius < 0.005625) || (sq_radius > 0.180625)) return vec4(0.5 * uniform_color, 1.0); @@ -13,7 +13,7 @@ vec4 hard_color(float sq_radius) return vec4(uniform_color, 1.0); } -vec4 custom_color(float sq_radius) +vec4 customizable_color(float sq_radius) { float in_radius = 0.5 * percent_center_radius; float out_radius = 0.5 * (1.0 - percent_outline_radius); @@ -30,5 +30,6 @@ void main() if (sq_radius > 0.25) discard; - gl_FragColor = custom_color(sq_radius); + gl_FragColor = customizable_color(sq_radius); +// gl_FragColor = hardcoded_color(sq_radius); } diff --git a/resources/shaders/options_120.vs b/resources/shaders/options_120_flat.vs similarity index 100% rename from resources/shaders/options_120.vs rename to resources/shaders/options_120_flat.vs diff --git a/resources/shaders/options_120_solid.fs b/resources/shaders/options_120_solid.fs new file mode 100644 index 0000000000..68d0bd4eec --- /dev/null +++ b/resources/shaders/options_120_solid.fs @@ -0,0 +1,88 @@ +// version 120 is needed for gl_PointCoord +#version 120 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +uniform vec3 uniform_color; +uniform float percent_outline_radius; +uniform float percent_center_radius; + +// x = width, y = height +uniform ivec2 viewport_sizes; +uniform vec2 z_range; +uniform mat4 inv_proj_matrix; + +varying vec3 eye_center; + +float radius = 0.5; +// x = tainted, y = specular; +vec2 intensity; + +vec3 eye_position_from_fragment() +{ + // Convert screen coordinates to normalized device coordinates (NDC) + vec4 ndc = vec4( + (gl_FragCoord.x / viewport_sizes.x - 0.5) * 2.0, + (gl_FragCoord.y / viewport_sizes.y - 0.5) * 2.0, + (gl_FragCoord.z - 0.5) * 2.0, + 1.0); + + // Convert NDC throuch inverse clip coordinates to view coordinates + vec4 clip = inv_proj_matrix * ndc; + return (clip / clip.w).xyz; +} + +vec3 eye_position_on_sphere(vec3 eye_fragment_position) +{ + vec3 eye_dir = normalize(eye_fragment_position); + float a = dot(eye_dir, eye_dir); + float b = 2.0 * dot(-eye_center, eye_dir); + float c = dot(eye_center, eye_center) - radius * radius; + float discriminant = b * b - 4 * a * c; + float t = -(b + sqrt(discriminant)) / (2.0 * a); + return t * eye_dir; +} + +vec4 on_sphere_color(vec3 eye_on_sphere_position) +{ + vec3 eye_normal = normalize(eye_on_sphere_position - eye_center); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_on_sphere_position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + return vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color.rgb * intensity.x, 1.0); +} + +void main() +{ + vec2 pos = gl_PointCoord - vec2(0.5, 0.5); + float sq_radius = dot(pos, pos); + if (sq_radius > 0.25) + discard; + + vec3 eye_on_sphere_position = eye_position_on_sphere(eye_position_from_fragment()); + +// gl_FragDepth = eye_on_sphere_position.z; +// gl_FragDepth = (eye_on_sphere_position.z - z_range.x) / (z_range.y - z_range.x); + gl_FragColor = on_sphere_color(eye_on_sphere_position); +} diff --git a/resources/shaders/options_120_solid.vs b/resources/shaders/options_120_solid.vs new file mode 100644 index 0000000000..0ad75003c9 --- /dev/null +++ b/resources/shaders/options_120_solid.vs @@ -0,0 +1,14 @@ +#version 120 + +uniform float zoom; +// x = min, y = max +uniform vec2 point_sizes; + +varying vec3 eye_center; + +void main() +{ + gl_PointSize = clamp(zoom, point_sizes.x, point_sizes.y); + eye_center = (gl_ModelViewMatrix * gl_Vertex).xyz; + gl_Position = ftransform(); +} diff --git a/resources/shaders/shells.fs b/resources/shaders/shells.fs deleted file mode 100644 index 0c3388df70..0000000000 --- a/resources/shaders/shells.fs +++ /dev/null @@ -1,13 +0,0 @@ -#version 110 - -const vec3 ZERO = vec3(0.0, 0.0, 0.0); - -uniform vec4 uniform_color; - -// x = tainted, y = specular; -varying vec2 intensity; - -void main() -{ - gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); -} diff --git a/resources/shaders/shells.vs b/resources/shaders/shells.vs deleted file mode 100644 index bb9c144e65..0000000000 --- a/resources/shaders/shells.vs +++ /dev/null @@ -1,42 +0,0 @@ -#version 110 - -#define INTENSITY_CORRECTION 0.6 - -// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) -const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); -#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SHININESS 20.0 - -// normalized values for (1./1.43, 0.2/1.43, 1./1.43) -const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); -#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) - -#define INTENSITY_AMBIENT 0.3 - -// x = tainted, y = specular; -varying vec2 intensity; - -void main() -{ - // First transform the normal into camera space and normalize the result. - vec3 normal = normalize(gl_NormalMatrix * gl_Normal); - - // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. - // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. - float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); - - intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; - intensity.y = 0.0; - - if (NdotL > 0.0) - { - vec3 position = (gl_ModelViewMatrix * gl_Vertex).xyz; - intensity.y += LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); - } - - // Perform the same lighting calculation for the 2nd light source (no specular applied). - intensity.x += max(dot(normal, LIGHT_FRONT_DIR), 0.0) * LIGHT_FRONT_DIFFUSE; - - gl_Position = ftransform(); -} diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 5631dadf34..984373ea4a 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -15,7 +15,7 @@ #define ENABLE_RENDER_STATISTICS 0 // Shows an imgui dialog with camera related data #define ENABLE_CAMERA_STATISTICS 0 -// Render the picking pass instead of the main scene (use [T] key to toggle between regular rendering and picking pass only rendering) +// Render the picking pass instead of the main scene (use [T] key to toggle between regular rendering and picking pass only rendering) #define ENABLE_RENDER_PICKING_PASS 0 // Enable extracting thumbnails from selected gcode and save them as png files #define ENABLE_THUMBNAIL_GENERATOR_DEBUG 0 diff --git a/src/slic3r/GUI/Camera.hpp b/src/slic3r/GUI/Camera.hpp index ece999c078..6e42562351 100644 --- a/src/slic3r/GUI/Camera.hpp +++ b/src/slic3r/GUI/Camera.hpp @@ -84,6 +84,7 @@ public: double get_near_z() const { return m_frustrum_zs.first; } double get_far_z() const { return m_frustrum_zs.second; } + const std::pair& get_z_range() const { return m_frustrum_zs; } double get_fov() const; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 6d41976941..978b4b95a6 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -415,12 +415,12 @@ void GCodeViewer::init_shaders() { switch (buffer_type(i)) { - case GCodeProcessor::EMoveType::Tool_change: { m_buffers[i].shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110"; break; } - case GCodeProcessor::EMoveType::Color_change: { m_buffers[i].shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110"; break; } - case GCodeProcessor::EMoveType::Pause_Print: { m_buffers[i].shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110"; break; } - case GCodeProcessor::EMoveType::Custom_GCode: { m_buffers[i].shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110"; break; } - case GCodeProcessor::EMoveType::Retract: { m_buffers[i].shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110"; break; } - case GCodeProcessor::EMoveType::Unretract: { m_buffers[i].shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110"; break; } + case GCodeProcessor::EMoveType::Tool_change: { m_buffers[i].shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120_solid" : "options_110"; break; } + case GCodeProcessor::EMoveType::Color_change: { m_buffers[i].shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120_solid" : "options_110"; break; } + case GCodeProcessor::EMoveType::Pause_Print: { m_buffers[i].shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120_solid" : "options_110"; break; } + case GCodeProcessor::EMoveType::Custom_GCode: { m_buffers[i].shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120_solid" : "options_110"; break; } + case GCodeProcessor::EMoveType::Retract: { m_buffers[i].shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120_solid" : "options_110"; break; } + case GCodeProcessor::EMoveType::Unretract: { m_buffers[i].shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120_solid" : "options_110"; break; } case GCodeProcessor::EMoveType::Extrude: { m_buffers[i].shader = "extrusions"; break; } case GCodeProcessor::EMoveType::Travel: { m_buffers[i].shader = "travels"; break; } default: { break; } @@ -754,21 +754,26 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool void GCodeViewer::render_toolpaths() const { #if ENABLE_GCODE_VIEWER_SHADERS_EDITOR - bool is_glsl_120 = m_shaders_editor.glsl_version == 1 && wxGetApp().is_glsl_version_greater_or_equal_to(1, 20); + bool is_glsl_120 = m_shaders_editor.shader_version >= 1 && wxGetApp().is_glsl_version_greater_or_equal_to(1, 20); std::array point_sizes; if (m_shaders_editor.size_dependent_on_zoom) - { point_sizes = { std::min(static_cast(m_shaders_editor.sizes[0]), m_detected_point_sizes[1]), std::min(static_cast(m_shaders_editor.sizes[1]), m_detected_point_sizes[1]) }; - } else point_sizes = { static_cast(m_shaders_editor.fixed_size), static_cast(m_shaders_editor.fixed_size) }; #else bool is_glsl_120 = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20); std::array point_sizes = { std::min(8.0f, m_detected_point_sizes[1]), std::min(48.0f, m_detected_point_sizes[1]) }; #endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR - double zoom = wxGetApp().plater()->get_camera().get_zoom(); + const Camera& camera = wxGetApp().plater()->get_camera(); + double zoom = camera.get_zoom(); + const std::array& viewport = camera.get_viewport(); + std::array viewport_sizes = { viewport[2], viewport[3] }; + const std::pair& camera_z_range = camera.get_z_range(); + std::array z_range = { static_cast(camera_z_range.first), static_cast(camera_z_range.second) }; - auto render_options = [this, is_glsl_120, zoom, point_sizes](const IBuffer& buffer, EOptionsColors colors_id, GLShaderProgram& shader) { + Transform3d inv_proj = camera.get_projection_matrix().inverse(); + + auto render_options = [this, is_glsl_120, zoom, viewport, inv_proj, viewport_sizes, z_range, point_sizes](const IBuffer& buffer, EOptionsColors colors_id, GLShaderProgram& shader) { shader.set_uniform("uniform_color", Options_Colors[static_cast(colors_id)]); #if ENABLE_GCODE_VIEWER_SHADERS_EDITOR shader.set_uniform("zoom", m_shaders_editor.size_dependent_on_zoom ? zoom : 1.0f); @@ -779,6 +784,9 @@ void GCodeViewer::render_toolpaths() const shader.set_uniform("percent_outline_radius", 0.15f); shader.set_uniform("percent_center_radius", 0.15f); #endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR + shader.set_uniform("viewport_sizes", viewport_sizes); + shader.set_uniform("inv_proj_matrix", inv_proj); + shader.set_uniform("z_range", z_range); shader.set_uniform("point_sizes", point_sizes); glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); if (is_glsl_120) @@ -896,7 +904,7 @@ void GCodeViewer::render_shells() const if (!m_shells.visible || m_shells.volumes.empty()) return; - GLShaderProgram* shader = wxGetApp().get_shader("shells"); + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) return; @@ -954,25 +962,25 @@ void GCodeViewer::render_legend() const { #if ENABLE_GCODE_VIEWER_SHADERS_EDITOR draw_list->AddCircle({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, 0.5f * icon_size, ICON_BORDER_COLOR, 16); - draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, (0.5f * icon_size) - 2.0f, - ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); - float radius = ((0.5f * icon_size) - 2.0f) * (1.0f - 0.01f * static_cast(m_shaders_editor.percent_outline)); - draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, radius, - ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); - if (m_shaders_editor.percent_center > 0) - { - radius = ((0.5f * icon_size) - 2.0f) * 0.01f * static_cast(m_shaders_editor.percent_center); - draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, radius, + if (m_shaders_editor.shader_version == 1) { + draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, (0.5f * icon_size) - 2.0f, ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); + float radius = ((0.5f * icon_size) - 2.0f) * (1.0f - 0.01f * static_cast(m_shaders_editor.percent_outline)); + draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, radius, + ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); + if (m_shaders_editor.percent_center > 0) { + radius = ((0.5f * icon_size) - 2.0f) * 0.01f * static_cast(m_shaders_editor.percent_center); + draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, radius, + ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); + } + } else { + draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, (0.5f * icon_size) - 2.0f, + ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); } #else draw_list->AddCircle({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, 0.5f * icon_size, ICON_BORDER_COLOR, 16); draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, (0.5f * icon_size) - 2.0f, - ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); - draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, (0.5f * icon_size) - 3.0f, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); - draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, 1.5f, - ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); #endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR break; } @@ -1195,7 +1203,11 @@ void GCodeViewer::render_legend() const auto add_option = [this, add_item](GCodeProcessor::EMoveType move_type, EOptionsColors color, const std::string& text) { const IBuffer& buffer = m_buffers[buffer_id(move_type)]; if (buffer.visible && buffer.indices_count > 0) - add_item(EItemType::Circle, Options_Colors[static_cast(color)], text); +#if ENABLE_GCODE_VIEWER_SHADERS_EDITOR + add_item((m_shaders_editor.shader_version == 0) ? EItemType::Rect : EItemType::Circle, Options_Colors[static_cast(color)], text); +#else + add_item((buffer.shader == "options_110") ? EItemType::Rect : EItemType::Circle, Options_Colors[static_cast(color)], text); +#endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR }; // options @@ -1337,12 +1349,15 @@ void GCodeViewer::render_shaders_editor() const imgui.set_next_window_pos(static_cast(cnv_size.get_width()), 0.5f * static_cast(cnv_size.get_height()), ImGuiCond_Once, 1.0f, 0.5f); imgui.begin(std::string("Shaders editor (DEV only)"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize); - ImGui::RadioButton("glsl version 1.10 (low end PCs)", &m_shaders_editor.glsl_version, 0); - ImGui::RadioButton("glsl version 1.20 (default)", &m_shaders_editor.glsl_version, 1); - switch (m_shaders_editor.glsl_version) + ImGui::RadioButton("glsl version 1.10 (low end PCs)", &m_shaders_editor.shader_version, 0); + ImGui::RadioButton("glsl version 1.20 flat (billboards)", &m_shaders_editor.shader_version, 1); + ImGui::RadioButton("glsl version 1.20 solid (spheres default)", &m_shaders_editor.shader_version, 2); + + switch (m_shaders_editor.shader_version) { case 0: { set_shader("options_110"); break; } - case 1: { set_shader("options_120"); break; } + case 1: { set_shader("options_120_flat"); break; } + case 2: { set_shader("options_120_solid"); break; } } if (ImGui::CollapsingHeader("Options", ImGuiTreeNodeFlags_DefaultOpen)) @@ -1364,7 +1379,7 @@ void GCodeViewer::render_shaders_editor() const else ImGui::SliderInt("fixed size", &m_shaders_editor.fixed_size, 1, 100); - if (m_shaders_editor.glsl_version == 1) + if (m_shaders_editor.shader_version == 1) { ImGui::SliderInt("percent outline", &m_shaders_editor.percent_outline, 0, 50); ImGui::SliderInt("percent center", &m_shaders_editor.percent_center, 0, 50); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 1d940b66a7..a2f9c14af7 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -205,12 +205,12 @@ class GCodeViewer #if ENABLE_GCODE_VIEWER_SHADERS_EDITOR struct ShadersEditor { - int glsl_version{ 1 }; + int shader_version{ 2 }; bool size_dependent_on_zoom{ true }; int fixed_size{ 16 }; - std::array sizes{ 8, 64 }; - int percent_outline{ 15 }; - int percent_center{ 15 }; + std::array sizes{ 3, 21 }; + int percent_outline{ 0 }; + int percent_center{ 33 }; }; #endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR diff --git a/src/slic3r/GUI/GLShader.cpp b/src/slic3r/GUI/GLShader.cpp index a6d641f89f..3c2612b45e 100644 --- a/src/slic3r/GUI/GLShader.cpp +++ b/src/slic3r/GUI/GLShader.cpp @@ -215,6 +215,26 @@ bool GLShaderProgram::set_uniform(const char* name, double value) const return set_uniform(name, static_cast(value)); } +bool GLShaderProgram::set_uniform(const char* name, const std::array& value) const +{ + int id = get_uniform_location(name); + if (id >= 0) { + glsafe(::glUniform2iv(id, 1, static_cast(value.data()))); + return true; + } + return false; +} + +bool GLShaderProgram::set_uniform(const char* name, const std::array& value) const +{ + int id = get_uniform_location(name); + if (id >= 0) { + glsafe(::glUniform3iv(id, 1, static_cast(value.data()))); + return true; + } + return false; +} + bool GLShaderProgram::set_uniform(const char* name, const std::array& value) const { int id = get_uniform_location(name); diff --git a/src/slic3r/GUI/GLShader.hpp b/src/slic3r/GUI/GLShader.hpp index 521f6796f1..a1160f8e98 100644 --- a/src/slic3r/GUI/GLShader.hpp +++ b/src/slic3r/GUI/GLShader.hpp @@ -43,6 +43,8 @@ public: bool set_uniform(const char* name, bool value) const; bool set_uniform(const char* name, float value) const; bool set_uniform(const char* name, double value) const; + bool set_uniform(const char* name, const std::array& value) const; + bool set_uniform(const char* name, const std::array& value) const; bool set_uniform(const char* name, const std::array& value) const; bool set_uniform(const char* name, const std::array& value) const; bool set_uniform(const char* name, const std::array& value) const; diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index dd77351bd0..4bebf7b984 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -1,6 +1,7 @@ #include "libslic3r/libslic3r.h" #include "GLShadersManager.hpp" #include "3DScene.hpp" +#include "GUI_App.hpp" #include #include @@ -28,19 +29,21 @@ std::pair GLShadersManager::init() bool valid = true; - // used to render bed axes and model, selection hints, gcode sequential view marker model + // used to render bed axes and model, selection hints, gcode sequential view marker model, preview shells valid &= append_shader("gouraud_light", { "gouraud_light.vs", "gouraud_light.fs" }); // used to render printbed valid &= append_shader("printbed", { "printbed.vs", "printbed.fs" }); // used to render options in gcode preview valid &= append_shader("options_110", { "options_110.vs", "options_110.fs" }); - valid &= append_shader("options_120", { "options_120.vs", "options_120.fs" }); + if (GUI::wxGetApp().is_glsl_version_greater_or_equal_to(1, 20)) + { + valid &= append_shader("options_120_flat", { "options_120_flat.vs", "options_120_flat.fs" }); + valid &= append_shader("options_120_solid", { "options_120_solid.vs", "options_120_solid.fs" }); + } // used to render extrusion paths in gcode preview valid &= append_shader("extrusions", { "extrusions.vs", "extrusions.fs" }); // used to render travel paths in gcode preview valid &= append_shader("travels", { "travels.vs", "travels.fs" }); - // used to render shells in gcode preview - valid &= append_shader("shells", { "shells.vs", "shells.fs" }); // used to render objects in 3d editor valid &= append_shader("gouraud", { "gouraud.vs", "gouraud.fs" }); // used to render variable layers heights in 3d editor From abd7d7480053060144faa155b08efbce89ff5ad8 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 27 May 2020 16:31:02 +0200 Subject: [PATCH 110/503] GCodeViewer -> Small refactoring --- src/slic3r/GUI/GCodeViewer.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 978b4b95a6..6dea0511bf 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -411,16 +411,17 @@ void GCodeViewer::init_shaders() unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); unsigned char end_id = buffer_id(GCodeProcessor::EMoveType::Count); + bool is_glsl_120 = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20); for (unsigned char i = begin_id; i < end_id; ++i) { switch (buffer_type(i)) { - case GCodeProcessor::EMoveType::Tool_change: { m_buffers[i].shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120_solid" : "options_110"; break; } - case GCodeProcessor::EMoveType::Color_change: { m_buffers[i].shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120_solid" : "options_110"; break; } - case GCodeProcessor::EMoveType::Pause_Print: { m_buffers[i].shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120_solid" : "options_110"; break; } - case GCodeProcessor::EMoveType::Custom_GCode: { m_buffers[i].shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120_solid" : "options_110"; break; } - case GCodeProcessor::EMoveType::Retract: { m_buffers[i].shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120_solid" : "options_110"; break; } - case GCodeProcessor::EMoveType::Unretract: { m_buffers[i].shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120_solid" : "options_110"; break; } + case GCodeProcessor::EMoveType::Tool_change: { m_buffers[i].shader = is_glsl_120 ? "options_120_solid" : "options_110"; break; } + case GCodeProcessor::EMoveType::Color_change: { m_buffers[i].shader = is_glsl_120 ? "options_120_solid" : "options_110"; break; } + case GCodeProcessor::EMoveType::Pause_Print: { m_buffers[i].shader = is_glsl_120 ? "options_120_solid" : "options_110"; break; } + case GCodeProcessor::EMoveType::Custom_GCode: { m_buffers[i].shader = is_glsl_120 ? "options_120_solid" : "options_110"; break; } + case GCodeProcessor::EMoveType::Retract: { m_buffers[i].shader = is_glsl_120 ? "options_120_solid" : "options_110"; break; } + case GCodeProcessor::EMoveType::Unretract: { m_buffers[i].shader = is_glsl_120 ? "options_120_solid" : "options_110"; break; } case GCodeProcessor::EMoveType::Extrude: { m_buffers[i].shader = "extrusions"; break; } case GCodeProcessor::EMoveType::Travel: { m_buffers[i].shader = "travels"; break; } default: { break; } From 0cb4a5ce56f60c07f2e190a9ad9ccdd38cf939cc Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 28 May 2020 07:06:54 +0200 Subject: [PATCH 111/503] GCodeViewer -> Improved depth detection in shader for options --- resources/shaders/options_120_solid.fs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/resources/shaders/options_120_solid.fs b/resources/shaders/options_120_solid.fs index 68d0bd4eec..b7770cf0db 100644 --- a/resources/shaders/options_120_solid.fs +++ b/resources/shaders/options_120_solid.fs @@ -21,7 +21,9 @@ uniform float percent_center_radius; // x = width, y = height uniform ivec2 viewport_sizes; -uniform vec2 z_range; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//uniform vec2 z_range; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ uniform mat4 inv_proj_matrix; varying vec3 eye_center; @@ -73,6 +75,15 @@ vec4 on_sphere_color(vec3 eye_on_sphere_position) return vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color.rgb * intensity.x, 1.0); } +float fragment_depth(vec3 eye_pos) +{ + // see: https://stackoverflow.com/questions/10264949/glsl-gl-fragcoord-z-calculation-and-setting-gl-fragdepth + vec4 clip_pos = gl_ProjectionMatrix * vec4(eye_pos, 1.0); + float ndc_depth = clip_pos.z / clip_pos.w; + + return (((gl_DepthRange.far - gl_DepthRange.near) * ndc_depth) + gl_DepthRange.near + gl_DepthRange.far) / 2.0; +} + void main() { vec2 pos = gl_PointCoord - vec2(0.5, 0.5); @@ -82,6 +93,7 @@ void main() vec3 eye_on_sphere_position = eye_position_on_sphere(eye_position_from_fragment()); + gl_FragDepth = fragment_depth(eye_on_sphere_position); // gl_FragDepth = eye_on_sphere_position.z; // gl_FragDepth = (eye_on_sphere_position.z - z_range.x) / (z_range.y - z_range.x); gl_FragColor = on_sphere_color(eye_on_sphere_position); From edaabf3fbde559698b3e25f547b0c4050bed8aef Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 28 May 2020 07:52:11 +0200 Subject: [PATCH 112/503] GCodeViewer -> Experimental hexagonal icons for toolpaths in legend --- src/slic3r/GUI/GCodeViewer.cpp | 68 +++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index c0acba344e..25432f1cf6 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -929,6 +929,8 @@ void GCodeViewer::render_legend() const ImGuiWrapper& imgui = *wxGetApp().imgui(); +#define USE_ICON_HEXAGON 1 + imgui.set_next_window_pos(0.0f, 0.0f, ImGuiCond_Always); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::SetNextWindowBgAlpha(0.6f); @@ -940,6 +942,7 @@ void GCodeViewer::render_legend() const { Rect, Circle, + Hexagon, Line }; @@ -963,22 +966,19 @@ void GCodeViewer::render_legend() const case EItemType::Circle: { #if ENABLE_GCODE_VIEWER_SHADERS_EDITOR - draw_list->AddCircle({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, 0.5f * icon_size, ICON_BORDER_COLOR, 16); + ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); + draw_list->AddCircle(center, 0.5f * icon_size, ICON_BORDER_COLOR, 16); if (m_shaders_editor.shader_version == 1) { - draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, (0.5f * icon_size) - 2.0f, + draw_list->AddCircleFilled(center, (0.5f * icon_size) - 2.0f, ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); float radius = ((0.5f * icon_size) - 2.0f) * (1.0f - 0.01f * static_cast(m_shaders_editor.percent_outline)); - draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, radius, - ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); + draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); if (m_shaders_editor.percent_center > 0) { radius = ((0.5f * icon_size) - 2.0f) * 0.01f * static_cast(m_shaders_editor.percent_center); - draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, radius, - ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); + draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); } - } else { - draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, (0.5f * icon_size) - 2.0f, - ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); - } + } else + draw_list->AddCircleFilled(center, (0.5f * icon_size) - 2.0f, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); #else draw_list->AddCircle({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, 0.5f * icon_size, ICON_BORDER_COLOR, 16); draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, (0.5f * icon_size) - 2.0f, @@ -986,6 +986,14 @@ void GCodeViewer::render_legend() const #endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR break; } + case EItemType::Hexagon: + { + ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); + draw_list->AddNgon(center, 0.5f * icon_size, ICON_BORDER_COLOR, 6); + draw_list->AddNgonFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, (0.5f * icon_size) - 2.0f, + ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 6); + break; + } case EItemType::Line: { draw_list->AddLine({ pos.x + 1, pos.y + icon_size - 1}, { pos.x + icon_size - 1, pos.y + 1 }, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 3.0f); @@ -1009,7 +1017,11 @@ void GCodeViewer::render_legend() const auto add_range_item = [this, draw_list, &imgui, add_item](int i, float value, unsigned int decimals) { char buf[1024]; ::sprintf(buf, "%.*f", decimals, value); +#if USE_ICON_HEXAGON + add_item(EItemType::Hexagon, Range_Colors[i], buf); +#else add_item(EItemType::Rect, Range_Colors[i], buf); +#endif // USE_ICON_HEXAGON }; float step_size = range.step_size(); @@ -1051,7 +1063,11 @@ void GCodeViewer::render_legend() const if (!visible) ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f); +#if USE_ICON_HEXAGON + add_item(EItemType::Hexagon, Extrusion_Role_Colors[static_cast(role)], _u8L(ExtrusionEntity::role_to_string(role)), [this, role]() { +#else add_item(EItemType::Rect, Extrusion_Role_Colors[static_cast(role)], _u8L(ExtrusionEntity::role_to_string(role)), [this, role]() { +#endif // USE_ICON_HEXAGON if (role < erCount) { m_extrusions.role_visibility_flags = is_visible(role) ? m_extrusions.role_visibility_flags & ~(1 << role) : m_extrusions.role_visibility_flags | (1 << role); @@ -1081,7 +1097,11 @@ void GCodeViewer::render_legend() const if (it == m_extruder_ids.end()) continue; +#if USE_ICON_HEXAGON + add_item(EItemType::Hexagon, m_tool_colors[i], (boost::format(_u8L("Extruder %d")) % (i + 1)).str()); +#else add_item(EItemType::Rect, m_tool_colors[i], (boost::format(_u8L("Extruder %d")) % (i + 1)).str()); +#endif // USE_ICON_HEXAGON } break; } @@ -1092,7 +1112,11 @@ void GCodeViewer::render_legend() const if (extruders_count == 1) { // single extruder use case if (custom_gcode_per_print_z.empty()) // no data to show +#if USE_ICON_HEXAGON + add_item(EItemType::Hexagon, m_tool_colors.front(), _u8L("Default print color")); +#else add_item(EItemType::Rect, m_tool_colors.front(), _u8L("Default print color")); +#endif // USE_ICON_HEXAGON else { std::vector> cp_values; cp_values.reserve(custom_gcode_per_print_z.size()); @@ -1116,7 +1140,11 @@ void GCodeViewer::render_legend() const const int items_cnt = static_cast(cp_values.size()); if (items_cnt == 0) { // There is no one color change, but there are some pause print or custom Gcode +#if USE_ICON_HEXAGON + add_item(EItemType::Hexagon, m_tool_colors.front(), _u8L("Default print color")); +#else add_item(EItemType::Rect, m_tool_colors.front(), _u8L("Default print color")); +#endif // USE_ICON_HEXAGON } else { for (int i = items_cnt; i >= 0; --i) { @@ -1124,14 +1152,26 @@ void GCodeViewer::render_legend() const std::string id_str = " (" + std::to_string(i + 1) + ")"; if (i == 0) { +#if USE_ICON_HEXAGON + add_item(EItemType::Hexagon, m_tool_colors[i], (boost::format(_u8L("up to %.2f mm")) % cp_values.front().first).str() + id_str); +#else add_item(EItemType::Rect, m_tool_colors[i], (boost::format(_u8L("up to %.2f mm")) % cp_values.front().first).str() + id_str); +#endif // USE_ICON_HEXAGON break; } else if (i == items_cnt) { +#if USE_ICON_HEXAGON + add_item(EItemType::Hexagon, m_tool_colors[i], (boost::format(_u8L("above %.2f mm")) % cp_values[i - 1].second).str() + id_str); +#else add_item(EItemType::Rect, m_tool_colors[i], (boost::format(_u8L("above %.2f mm")) % cp_values[i - 1].second).str() + id_str); +#endif // USE_ICON_HEXAGON continue; } +#if USE_ICON_HEXAGON + add_item(EItemType::Hexagon, m_tool_colors[i], (boost::format(_u8L("%.2f - %.2f mm")) % cp_values[i - 1].second% cp_values[i].first).str() + id_str); +#else add_item(EItemType::Rect, m_tool_colors[i], (boost::format(_u8L("%.2f - %.2f mm")) % cp_values[i - 1].second% cp_values[i].first).str() + id_str); +#endif // USE_ICON_HEXAGON } } } @@ -1140,7 +1180,11 @@ void GCodeViewer::render_legend() const { // extruders for (unsigned int i = 0; i < (unsigned int)extruders_count; ++i) { +#if USE_ICON_HEXAGON + add_item(EItemType::Hexagon, m_tool_colors[i], (boost::format(_u8L("Extruder %d")) % (i + 1)).str()); +#else add_item(EItemType::Rect, m_tool_colors[i], (boost::format(_u8L("Extruder %d")) % (i + 1)).str()); +#endif // USE_ICON_HEXAGON } // color changes @@ -1151,7 +1195,11 @@ void GCodeViewer::render_legend() const // create label for color change item std::string id_str = " (" + std::to_string(color_change_idx--) + ")"; +#if USE_ICON_HEXAGON + add_item(EItemType::Hexagon, m_tool_colors[last_color_id--], +#else add_item(EItemType::Rect, m_tool_colors[last_color_id--], +#endif // USE_ICON_HEXAGON (boost::format(_u8L("Color change for Extruder %d at %.2f mm")) % custom_gcode_per_print_z[i].extruder % custom_gcode_per_print_z[i].print_z).str() + id_str); } } From 9c8892c869a4e2421e3097fb588a7a8853b52516 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 28 May 2020 09:23:30 +0200 Subject: [PATCH 113/503] GCodeViewer -> Shaders code cleanup --- resources/shaders/extrusions.fs | 5 ----- resources/shaders/extrusions.vs | 4 ---- resources/shaders/options_120_solid.fs | 9 --------- resources/shaders/travels.fs | 5 ----- resources/shaders/travels.vs | 4 ---- src/slic3r/GUI/GCodeViewer.cpp | 13 ++++++++++--- 6 files changed, 10 insertions(+), 30 deletions(-) diff --git a/resources/shaders/extrusions.fs b/resources/shaders/extrusions.fs index fc81e487fb..65db0667a9 100644 --- a/resources/shaders/extrusions.fs +++ b/resources/shaders/extrusions.fs @@ -17,7 +17,6 @@ uniform vec3 uniform_color; varying vec3 eye_position; varying vec3 eye_normal; -//varying float world_normal_z; // x = tainted, y = specular; vec2 intensity; @@ -37,9 +36,5 @@ void main() NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; -// // darkens fragments whose normal points downward -// if (world_normal_z < 0.0) -// intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); - gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color * intensity.x, 1.0); } diff --git a/resources/shaders/extrusions.vs b/resources/shaders/extrusions.vs index d97adbabe0..8980f3944b 100644 --- a/resources/shaders/extrusions.vs +++ b/resources/shaders/extrusions.vs @@ -2,14 +2,10 @@ varying vec3 eye_position; varying vec3 eye_normal; -//// world z component of the normal used to darken the lower side of the toolpaths -//varying float world_normal_z; void main() { eye_position = (gl_ModelViewMatrix * gl_Vertex).xyz; eye_normal = gl_NormalMatrix * vec3(0.0, 0.0, 1.0); -// eye_normal = gl_NormalMatrix * gl_Normal; -// world_normal_z = gl_Normal.z; gl_Position = ftransform(); } diff --git a/resources/shaders/options_120_solid.fs b/resources/shaders/options_120_solid.fs index b7770cf0db..c7a57d547b 100644 --- a/resources/shaders/options_120_solid.fs +++ b/resources/shaders/options_120_solid.fs @@ -16,14 +16,9 @@ const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); #define INTENSITY_AMBIENT 0.3 uniform vec3 uniform_color; -uniform float percent_outline_radius; -uniform float percent_center_radius; // x = width, y = height uniform ivec2 viewport_sizes; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -//uniform vec2 z_range; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ uniform mat4 inv_proj_matrix; varying vec3 eye_center; @@ -77,10 +72,8 @@ vec4 on_sphere_color(vec3 eye_on_sphere_position) float fragment_depth(vec3 eye_pos) { - // see: https://stackoverflow.com/questions/10264949/glsl-gl-fragcoord-z-calculation-and-setting-gl-fragdepth vec4 clip_pos = gl_ProjectionMatrix * vec4(eye_pos, 1.0); float ndc_depth = clip_pos.z / clip_pos.w; - return (((gl_DepthRange.far - gl_DepthRange.near) * ndc_depth) + gl_DepthRange.near + gl_DepthRange.far) / 2.0; } @@ -94,7 +87,5 @@ void main() vec3 eye_on_sphere_position = eye_position_on_sphere(eye_position_from_fragment()); gl_FragDepth = fragment_depth(eye_on_sphere_position); -// gl_FragDepth = eye_on_sphere_position.z; -// gl_FragDepth = (eye_on_sphere_position.z - z_range.x) / (z_range.y - z_range.x); gl_FragColor = on_sphere_color(eye_on_sphere_position); } diff --git a/resources/shaders/travels.fs b/resources/shaders/travels.fs index fc81e487fb..65db0667a9 100644 --- a/resources/shaders/travels.fs +++ b/resources/shaders/travels.fs @@ -17,7 +17,6 @@ uniform vec3 uniform_color; varying vec3 eye_position; varying vec3 eye_normal; -//varying float world_normal_z; // x = tainted, y = specular; vec2 intensity; @@ -37,9 +36,5 @@ void main() NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; -// // darkens fragments whose normal points downward -// if (world_normal_z < 0.0) -// intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); - gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color * intensity.x, 1.0); } diff --git a/resources/shaders/travels.vs b/resources/shaders/travels.vs index d97adbabe0..8980f3944b 100644 --- a/resources/shaders/travels.vs +++ b/resources/shaders/travels.vs @@ -2,14 +2,10 @@ varying vec3 eye_position; varying vec3 eye_normal; -//// world z component of the normal used to darken the lower side of the toolpaths -//varying float world_normal_z; void main() { eye_position = (gl_ModelViewMatrix * gl_Vertex).xyz; eye_normal = gl_NormalMatrix * vec3(0.0, 0.0, 1.0); -// eye_normal = gl_NormalMatrix * gl_Normal; -// world_normal_z = gl_Normal.z; gl_Position = ftransform(); } diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 25432f1cf6..896a42b10f 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -771,11 +771,16 @@ void GCodeViewer::render_toolpaths() const const std::array& viewport = camera.get_viewport(); std::array viewport_sizes = { viewport[2], viewport[3] }; const std::pair& camera_z_range = camera.get_z_range(); - std::array z_range = { static_cast(camera_z_range.first), static_cast(camera_z_range.second) }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// std::array z_range = { static_cast(camera_z_range.first), static_cast(camera_z_range.second) }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Transform3d inv_proj = camera.get_projection_matrix().inverse(); - auto render_options = [this, is_glsl_120, zoom, viewport, inv_proj, viewport_sizes, z_range, point_sizes](const IBuffer& buffer, EOptionsColors colors_id, GLShaderProgram& shader) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + auto render_options = [this, is_glsl_120, zoom, viewport, inv_proj, viewport_sizes, point_sizes](const IBuffer& buffer, EOptionsColors colors_id, GLShaderProgram& shader) { +// auto render_options = [this, is_glsl_120, zoom, viewport, inv_proj, viewport_sizes, z_range, point_sizes](const IBuffer& buffer, EOptionsColors colors_id, GLShaderProgram& shader) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ shader.set_uniform("uniform_color", Options_Colors[static_cast(colors_id)]); #if ENABLE_GCODE_VIEWER_SHADERS_EDITOR shader.set_uniform("zoom", m_shaders_editor.size_dependent_on_zoom ? zoom : 1.0f); @@ -788,7 +793,9 @@ void GCodeViewer::render_toolpaths() const #endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR shader.set_uniform("viewport_sizes", viewport_sizes); shader.set_uniform("inv_proj_matrix", inv_proj); - shader.set_uniform("z_range", z_range); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// shader.set_uniform("z_range", z_range); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ shader.set_uniform("point_sizes", point_sizes); glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); if (is_glsl_120) From 2904ee6e1a122012a6d158137261482769aa8ca4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 28 May 2020 09:38:08 +0200 Subject: [PATCH 114/503] Added missing include --- src/slic3r/GUI/Camera.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index ac32767c4b..866c7e979d 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -5,6 +5,7 @@ #include "AppConfig.hpp" #if ENABLE_CAMERA_STATISTICS #include "Mouse3DController.hpp" +#include "Plater.hpp" #endif // ENABLE_CAMERA_STATISTICS #include From 96db6aaadb936c324f2f07369937a6fac8f8fc11 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 28 May 2020 11:14:56 +0200 Subject: [PATCH 115/503] Attempt to fix rambling crash on Mac Asan --- src/slic3r/GUI/GCodeViewer.cpp | 9 --------- src/slic3r/GUI/GUI_Preview.cpp | 4 ++++ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 896a42b10f..2cf2bb7057 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -771,16 +771,10 @@ void GCodeViewer::render_toolpaths() const const std::array& viewport = camera.get_viewport(); std::array viewport_sizes = { viewport[2], viewport[3] }; const std::pair& camera_z_range = camera.get_z_range(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -// std::array z_range = { static_cast(camera_z_range.first), static_cast(camera_z_range.second) }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Transform3d inv_proj = camera.get_projection_matrix().inverse(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ auto render_options = [this, is_glsl_120, zoom, viewport, inv_proj, viewport_sizes, point_sizes](const IBuffer& buffer, EOptionsColors colors_id, GLShaderProgram& shader) { -// auto render_options = [this, is_glsl_120, zoom, viewport, inv_proj, viewport_sizes, z_range, point_sizes](const IBuffer& buffer, EOptionsColors colors_id, GLShaderProgram& shader) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ shader.set_uniform("uniform_color", Options_Colors[static_cast(colors_id)]); #if ENABLE_GCODE_VIEWER_SHADERS_EDITOR shader.set_uniform("zoom", m_shaders_editor.size_dependent_on_zoom ? zoom : 1.0f); @@ -793,9 +787,6 @@ void GCodeViewer::render_toolpaths() const #endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR shader.set_uniform("viewport_sizes", viewport_sizes); shader.set_uniform("inv_proj_matrix", inv_proj); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -// shader.set_uniform("z_range", z_range); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ shader.set_uniform("point_sizes", point_sizes); glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); if (is_glsl_120) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index c673292efc..65791150a0 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -1144,6 +1144,10 @@ void Preview::update_layers_slider_from_canvas(wxKeyEvent& event) void Preview::update_moves_slider() { const GCodeViewer::SequentialView& view = m_canvas->get_gcode_sequential_view(); + // this should not be needed, but it is here to try to prevent rambling crashes on Mac Asan + if (view.endpoints.last < view.endpoints.first) + return; + std::vector values(view.endpoints.last - view.endpoints.first + 1); unsigned int count = 0; for (unsigned int i = view.endpoints.first; i <= view.endpoints.last; ++i) From dcec684cc7fe8979d25b726f6bdf560353139691 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 29 May 2020 12:29:04 +0200 Subject: [PATCH 116/503] ENABLE_GCODE_VIEWER -> Refactoring of shaders for options --- resources/shaders/options_110.fs | 2 +- resources/shaders/options_110.vs | 8 +-- resources/shaders/options_120_flat.fs | 20 +++--- resources/shaders/options_120_flat.vs | 8 +-- resources/shaders/options_120_solid.fs | 2 +- resources/shaders/options_120_solid.vs | 8 +-- src/slic3r/GUI/GCodeViewer.cpp | 87 +++++++++++++------------- src/slic3r/GUI/GCodeViewer.hpp | 4 +- 8 files changed, 67 insertions(+), 72 deletions(-) diff --git a/resources/shaders/options_110.fs b/resources/shaders/options_110.fs index 3722058c87..474e355e09 100644 --- a/resources/shaders/options_110.fs +++ b/resources/shaders/options_110.fs @@ -1,4 +1,4 @@ -#version 120 +#version 110 uniform vec3 uniform_color; diff --git a/resources/shaders/options_110.vs b/resources/shaders/options_110.vs index 5361c88cec..7592f86a42 100644 --- a/resources/shaders/options_110.vs +++ b/resources/shaders/options_110.vs @@ -1,11 +1,11 @@ #version 110 uniform float zoom; -// x = min, y = max -uniform vec2 point_sizes; +uniform float point_size; +uniform float near_plane_height; void main() { - gl_PointSize = clamp(zoom, point_sizes.x, point_sizes.y); - gl_Position = ftransform(); + gl_Position = ftransform(); + gl_PointSize = (gl_Position.w == 1.0) ? zoom * near_plane_height * point_size : near_plane_height * point_size / gl_Position.w; } diff --git a/resources/shaders/options_120_flat.fs b/resources/shaders/options_120_flat.fs index 656eccd1d4..e98e5693f3 100644 --- a/resources/shaders/options_120_flat.fs +++ b/resources/shaders/options_120_flat.fs @@ -5,31 +5,31 @@ uniform vec3 uniform_color; uniform float percent_outline_radius; uniform float percent_center_radius; -vec4 hardcoded_color(float sq_radius) +vec4 hardcoded_color(float sq_radius, vec3 color) { if ((sq_radius < 0.005625) || (sq_radius > 0.180625)) - return vec4(0.5 * uniform_color, 1.0); + return vec4(0.5 * color, 1.0); else - return vec4(uniform_color, 1.0); + return vec4(color, 1.0); } -vec4 customizable_color(float sq_radius) +vec4 customizable_color(float sq_radius, vec3 color) { float in_radius = 0.5 * percent_center_radius; float out_radius = 0.5 * (1.0 - percent_outline_radius); if ((sq_radius < in_radius * in_radius) || (sq_radius > out_radius * out_radius)) - return vec4(0.5 * uniform_color, 1.0); + return vec4(0.5 * color, 1.0); else - return vec4(uniform_color, 1.0); + return vec4(color, 1.0); } void main() { - vec2 pos = gl_PointCoord - vec2(0.5, 0.5); + vec2 pos = gl_PointCoord - vec2(0.5); float sq_radius = dot(pos, pos); if (sq_radius > 0.25) discard; - - gl_FragColor = customizable_color(sq_radius); -// gl_FragColor = hardcoded_color(sq_radius); + + gl_FragColor = customizable_color(sq_radius, uniform_color); +// gl_FragColor = hardcoded_color(sq_radius, uniform_color); } diff --git a/resources/shaders/options_120_flat.vs b/resources/shaders/options_120_flat.vs index ebf7428c9a..baf3cd3a7f 100644 --- a/resources/shaders/options_120_flat.vs +++ b/resources/shaders/options_120_flat.vs @@ -1,11 +1,11 @@ #version 120 uniform float zoom; -// x = min, y = max -uniform vec2 point_sizes; +uniform float point_size; +uniform float near_plane_height; void main() { - gl_PointSize = clamp(zoom, point_sizes.x, point_sizes.y); - gl_Position = ftransform(); + gl_Position = ftransform(); + gl_PointSize = (gl_Position.w == 1.0) ? zoom * near_plane_height * point_size : near_plane_height * point_size / gl_Position.w; } diff --git a/resources/shaders/options_120_solid.fs b/resources/shaders/options_120_solid.fs index c7a57d547b..18410b4062 100644 --- a/resources/shaders/options_120_solid.fs +++ b/resources/shaders/options_120_solid.fs @@ -79,7 +79,7 @@ float fragment_depth(vec3 eye_pos) void main() { - vec2 pos = gl_PointCoord - vec2(0.5, 0.5); + vec2 pos = gl_PointCoord - vec2(0.5); float sq_radius = dot(pos, pos); if (sq_radius > 0.25) discard; diff --git a/resources/shaders/options_120_solid.vs b/resources/shaders/options_120_solid.vs index 0ad75003c9..745ec8ddd1 100644 --- a/resources/shaders/options_120_solid.vs +++ b/resources/shaders/options_120_solid.vs @@ -1,14 +1,14 @@ #version 120 uniform float zoom; -// x = min, y = max -uniform vec2 point_sizes; +uniform float point_size; +uniform float near_plane_height; varying vec3 eye_center; void main() { - gl_PointSize = clamp(zoom, point_sizes.x, point_sizes.y); eye_center = (gl_ModelViewMatrix * gl_Vertex).xyz; - gl_Position = ftransform(); + gl_Position = ftransform(); + gl_PointSize = (gl_Position.w == 1.0) ? zoom * near_plane_height * point_size : near_plane_height * point_size / gl_Position.w; } diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index ed98996b41..24960c79fb 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -756,40 +756,36 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool void GCodeViewer::render_toolpaths() const { #if ENABLE_GCODE_VIEWER_SHADERS_EDITOR - bool is_glsl_120 = m_shaders_editor.shader_version >= 1 && wxGetApp().is_glsl_version_greater_or_equal_to(1, 20); - std::array point_sizes; - if (m_shaders_editor.size_dependent_on_zoom) - point_sizes = { std::min(static_cast(m_shaders_editor.sizes[0]), m_detected_point_sizes[1]), std::min(static_cast(m_shaders_editor.sizes[1]), m_detected_point_sizes[1]) }; - else - point_sizes = { static_cast(m_shaders_editor.fixed_size), static_cast(m_shaders_editor.fixed_size) }; + float point_size = m_shaders_editor.point_size; #else - bool is_glsl_120 = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20); - std::array point_sizes = { std::min(8.0f, m_detected_point_sizes[1]), std::min(48.0f, m_detected_point_sizes[1]) }; + float point_size = 1.0f; #endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR const Camera& camera = wxGetApp().plater()->get_camera(); double zoom = camera.get_zoom(); const std::array& viewport = camera.get_viewport(); std::array viewport_sizes = { viewport[2], viewport[3] }; + float near_plane_height = camera.get_type() == Camera::Perspective ? static_cast(viewport[3]) / (2.0f * static_cast(2.0 * std::tan(0.5 * Geometry::deg2rad(camera.get_fov())))) : + static_cast(viewport[3]) * 0.0005; Transform3d inv_proj = camera.get_projection_matrix().inverse(); - auto render_options = [this, is_glsl_120, zoom, viewport, inv_proj, viewport_sizes, point_sizes](const IBuffer& buffer, EOptionsColors colors_id, GLShaderProgram& shader) { + auto render_options = [this, zoom, inv_proj, viewport_sizes, point_size, near_plane_height](const IBuffer& buffer, EOptionsColors colors_id, GLShaderProgram& shader) { shader.set_uniform("uniform_color", Options_Colors[static_cast(colors_id)]); + shader.set_uniform("zoom", zoom); #if ENABLE_GCODE_VIEWER_SHADERS_EDITOR - shader.set_uniform("zoom", m_shaders_editor.size_dependent_on_zoom ? zoom : 1.0f); shader.set_uniform("percent_outline_radius", 0.01f * static_cast(m_shaders_editor.percent_outline)); shader.set_uniform("percent_center_radius", 0.01f * static_cast(m_shaders_editor.percent_center)); #else - shader.set_uniform("zoom", zoom); shader.set_uniform("percent_outline_radius", 0.15f); shader.set_uniform("percent_center_radius", 0.15f); #endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR shader.set_uniform("viewport_sizes", viewport_sizes); shader.set_uniform("inv_proj_matrix", inv_proj); - shader.set_uniform("point_sizes", point_sizes); + shader.set_uniform("point_size", point_size); + shader.set_uniform("near_plane_height", near_plane_height); + glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); - if (is_glsl_120) - glsafe(::glEnable(GL_POINT_SPRITE)); + glsafe(::glEnable(GL_POINT_SPRITE)); for (const RenderPath& path : buffer.render_paths) { glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); @@ -797,9 +793,8 @@ void GCodeViewer::render_toolpaths() const ++m_statistics.gl_multi_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } - if (is_glsl_120) - glsafe(::glDisable(GL_POINT_SPRITE)); - + + glsafe(::glDisable(GL_POINT_SPRITE)); glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); }; @@ -964,31 +959,49 @@ void GCodeViewer::render_legend() const { #if ENABLE_GCODE_VIEWER_SHADERS_EDITOR ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); - draw_list->AddCircle(center, 0.5f * icon_size, ICON_BORDER_COLOR, 16); if (m_shaders_editor.shader_version == 1) { - draw_list->AddCircleFilled(center, (0.5f * icon_size) - 2.0f, + draw_list->AddCircleFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); - float radius = ((0.5f * icon_size) - 2.0f) * (1.0f - 0.01f * static_cast(m_shaders_editor.percent_outline)); + float radius = 0.5f * icon_size * (1.0f - 0.01f * static_cast(m_shaders_editor.percent_outline)); draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); if (m_shaders_editor.percent_center > 0) { - radius = ((0.5f * icon_size) - 2.0f) * 0.01f * static_cast(m_shaders_editor.percent_center); + radius = 0.5f * icon_size * 0.01f * static_cast(m_shaders_editor.percent_center); draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); } - } else - draw_list->AddCircleFilled(center, (0.5f * icon_size) - 2.0f, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); + } + else + draw_list->AddCircleFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); + +// ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); +// draw_list->AddCircle(center, 0.5f * icon_size, ICON_BORDER_COLOR, 16); +// if (m_shaders_editor.shader_version == 1) { +// draw_list->AddCircleFilled(center, (0.5f * icon_size) - 2.0f, +// ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); +// float radius = ((0.5f * icon_size) - 2.0f) * (1.0f - 0.01f * static_cast(m_shaders_editor.percent_outline)); +// draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); +// if (m_shaders_editor.percent_center > 0) { +// radius = ((0.5f * icon_size) - 2.0f) * 0.01f * static_cast(m_shaders_editor.percent_center); +// draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); +// } +// } else +// draw_list->AddCircleFilled(center, (0.5f * icon_size) - 2.0f, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); #else - draw_list->AddCircle({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, 0.5f * icon_size, ICON_BORDER_COLOR, 16); - draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, (0.5f * icon_size) - 2.0f, + draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, 0.5f * icon_size, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); + +// draw_list->AddCircle({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, 0.5f * icon_size, ICON_BORDER_COLOR, 16); +// draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, (0.5f * icon_size) - 2.0f, +// ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); #endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR break; } case EItemType::Hexagon: { ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); - draw_list->AddNgon(center, 0.5f * icon_size, ICON_BORDER_COLOR, 6); - draw_list->AddNgonFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, (0.5f * icon_size) - 2.0f, - ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 6); + draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 6); +// draw_list->AddNgon(center, 0.5f * icon_size, ICON_BORDER_COLOR, 6); +// draw_list->AddNgonFilled(center, (0.5f * icon_size) - 2.0f, +// ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 6); break; } case EItemType::Line: @@ -1409,23 +1422,7 @@ void GCodeViewer::render_shaders_editor() const if (ImGui::CollapsingHeader("Options", ImGuiTreeNodeFlags_DefaultOpen)) { - ImGui::Checkbox("size dependent on zoom", &m_shaders_editor.size_dependent_on_zoom); - if (m_shaders_editor.size_dependent_on_zoom) - { - if (ImGui::SliderInt("min size (min zoom)", &m_shaders_editor.sizes[0], 1, 100)) - { - if (m_shaders_editor.sizes[1] < m_shaders_editor.sizes[0]) - m_shaders_editor.sizes[1] = m_shaders_editor.sizes[0]; - } - ImGui::SliderInt("max size (max zoom)", &m_shaders_editor.sizes[1], 1, 100); - { - if (m_shaders_editor.sizes[1] < m_shaders_editor.sizes[0]) - m_shaders_editor.sizes[0] = m_shaders_editor.sizes[1]; - } - } - else - ImGui::SliderInt("fixed size", &m_shaders_editor.fixed_size, 1, 100); - + ImGui::SliderFloat("point size", &m_shaders_editor.point_size, 0.5f, 1.5f, "%.1f"); if (m_shaders_editor.shader_version == 1) { ImGui::SliderInt("percent outline", &m_shaders_editor.percent_outline, 0, 50); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index a2f9c14af7..6f24eef435 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -206,9 +206,7 @@ class GCodeViewer struct ShadersEditor { int shader_version{ 2 }; - bool size_dependent_on_zoom{ true }; - int fixed_size{ 16 }; - std::array sizes{ 3, 21 }; + float point_size{ 1.0f }; int percent_outline{ 0 }; int percent_center{ 33 }; }; From 707268d41dc737a1aa621ba0ad2cbc1cf63da3d1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 1 Jun 2020 08:55:44 +0200 Subject: [PATCH 117/503] ENABLE_GCODE_VIEWER -> Improvements in shaders for options --- resources/shaders/options_120_flat.fs | 26 ++++++++------------- resources/shaders/options_120_solid.fs | 31 +++++++++++++------------- src/slic3r/GUI/GCodeViewer.cpp | 7 +++--- 3 files changed, 27 insertions(+), 37 deletions(-) diff --git a/resources/shaders/options_120_flat.fs b/resources/shaders/options_120_flat.fs index e98e5693f3..5bcc718761 100644 --- a/resources/shaders/options_120_flat.fs +++ b/resources/shaders/options_120_flat.fs @@ -5,31 +5,23 @@ uniform vec3 uniform_color; uniform float percent_outline_radius; uniform float percent_center_radius; -vec4 hardcoded_color(float sq_radius, vec3 color) +vec4 hardcoded_color(float radius, vec3 color) { - if ((sq_radius < 0.005625) || (sq_radius > 0.180625)) - return vec4(0.5 * color, 1.0); - else - return vec4(color, 1.0); + return ((radius < 0.15) || (radius > 0.85)) ? vec4(0.5 * color, 1.0) : vec4(color, 1.0); } -vec4 customizable_color(float sq_radius, vec3 color) +vec4 customizable_color(float radius, vec3 color) { - float in_radius = 0.5 * percent_center_radius; - float out_radius = 0.5 * (1.0 - percent_outline_radius); - if ((sq_radius < in_radius * in_radius) || (sq_radius > out_radius * out_radius)) - return vec4(0.5 * color, 1.0); - else - return vec4(color, 1.0); + return ((radius < percent_center_radius) || (radius > 1.0 - percent_outline_radius)) ? + vec4(0.5 * color, 1.0) : vec4(color, 1.0); } void main() { - vec2 pos = gl_PointCoord - vec2(0.5); - float sq_radius = dot(pos, pos); - if (sq_radius > 0.25) + vec2 pos = (gl_PointCoord - 0.5) * 2.0; + float radius = length(pos); + if (radius > 1.0) discard; - gl_FragColor = customizable_color(sq_radius, uniform_color); -// gl_FragColor = hardcoded_color(sq_radius, uniform_color); + gl_FragColor = customizable_color(radius, uniform_color); } diff --git a/resources/shaders/options_120_solid.fs b/resources/shaders/options_120_solid.fs index 18410b4062..ebe7135274 100644 --- a/resources/shaders/options_120_solid.fs +++ b/resources/shaders/options_120_solid.fs @@ -17,28 +17,26 @@ const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); uniform vec3 uniform_color; -// x = width, y = height -uniform ivec2 viewport_sizes; +uniform ivec4 viewport; +uniform float point_size; uniform mat4 inv_proj_matrix; varying vec3 eye_center; - -float radius = 0.5; // x = tainted, y = specular; vec2 intensity; +float radius = 0.5 * point_size; + vec3 eye_position_from_fragment() { // Convert screen coordinates to normalized device coordinates (NDC) - vec4 ndc = vec4( - (gl_FragCoord.x / viewport_sizes.x - 0.5) * 2.0, - (gl_FragCoord.y / viewport_sizes.y - 0.5) * 2.0, - (gl_FragCoord.z - 0.5) * 2.0, - 1.0); - + vec4 ndc = vec4((gl_FragCoord.x / viewport.z - 0.5) * 2.0, + (gl_FragCoord.y / viewport.w - 0.5) * 2.0, + (gl_FragCoord.z - 0.5) * 2.0, + gl_FragCoord.w); // Convert NDC throuch inverse clip coordinates to view coordinates vec4 clip = inv_proj_matrix * ndc; - return (clip / clip.w).xyz; + return clip.xyz; } vec3 eye_position_on_sphere(vec3 eye_fragment_position) @@ -67,21 +65,22 @@ vec4 on_sphere_color(vec3 eye_on_sphere_position) NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; - return vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color.rgb * intensity.x, 1.0); + return vec4(intensity + uniform_color.rgb * intensity.x, 1.0); +// return vec4(vec3(intensity.y) + uniform_color.rgb * intensity.x, 1.0); } float fragment_depth(vec3 eye_pos) { vec4 clip_pos = gl_ProjectionMatrix * vec4(eye_pos, 1.0); float ndc_depth = clip_pos.z / clip_pos.w; - return (((gl_DepthRange.far - gl_DepthRange.near) * ndc_depth) + gl_DepthRange.near + gl_DepthRange.far) / 2.0; + return ((gl_DepthRange.far - gl_DepthRange.near) * ndc_depth + gl_DepthRange.near + gl_DepthRange.far) / 2.0; } void main() { - vec2 pos = gl_PointCoord - vec2(0.5); - float sq_radius = dot(pos, pos); - if (sq_radius > 0.25) + vec2 pos = (gl_PointCoord - 0.5) * 2.0; + float radius = length(pos); + if (radius > 1.0) discard; vec3 eye_on_sphere_position = eye_position_on_sphere(eye_position_from_fragment()); diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 24960c79fb..3709aaa888 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -763,13 +763,12 @@ void GCodeViewer::render_toolpaths() const const Camera& camera = wxGetApp().plater()->get_camera(); double zoom = camera.get_zoom(); const std::array& viewport = camera.get_viewport(); - std::array viewport_sizes = { viewport[2], viewport[3] }; float near_plane_height = camera.get_type() == Camera::Perspective ? static_cast(viewport[3]) / (2.0f * static_cast(2.0 * std::tan(0.5 * Geometry::deg2rad(camera.get_fov())))) : static_cast(viewport[3]) * 0.0005; Transform3d inv_proj = camera.get_projection_matrix().inverse(); - auto render_options = [this, zoom, inv_proj, viewport_sizes, point_size, near_plane_height](const IBuffer& buffer, EOptionsColors colors_id, GLShaderProgram& shader) { + auto render_options = [this, zoom, inv_proj, viewport, point_size, near_plane_height](const IBuffer& buffer, EOptionsColors colors_id, GLShaderProgram& shader) { shader.set_uniform("uniform_color", Options_Colors[static_cast(colors_id)]); shader.set_uniform("zoom", zoom); #if ENABLE_GCODE_VIEWER_SHADERS_EDITOR @@ -779,7 +778,7 @@ void GCodeViewer::render_toolpaths() const shader.set_uniform("percent_outline_radius", 0.15f); shader.set_uniform("percent_center_radius", 0.15f); #endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR - shader.set_uniform("viewport_sizes", viewport_sizes); + shader.set_uniform("viewport", viewport); shader.set_uniform("inv_proj_matrix", inv_proj); shader.set_uniform("point_size", point_size); shader.set_uniform("near_plane_height", near_plane_height); @@ -1422,7 +1421,7 @@ void GCodeViewer::render_shaders_editor() const if (ImGui::CollapsingHeader("Options", ImGuiTreeNodeFlags_DefaultOpen)) { - ImGui::SliderFloat("point size", &m_shaders_editor.point_size, 0.5f, 1.5f, "%.1f"); + ImGui::SliderFloat("point size", &m_shaders_editor.point_size, 0.5f, 3.0f, "%.1f"); if (m_shaders_editor.shader_version == 1) { ImGui::SliderInt("percent outline", &m_shaders_editor.percent_outline, 0, 50); From 7b33e780a2cf86b819dd6839844142111904efad Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 1 Jun 2020 09:11:16 +0200 Subject: [PATCH 118/503] Follow-up of 707268d41dc737a1aa621ba0ad2cbc1cf63da3d1 -> Fixed typo --- resources/shaders/options_120_solid.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/shaders/options_120_solid.fs b/resources/shaders/options_120_solid.fs index ebe7135274..912b809e07 100644 --- a/resources/shaders/options_120_solid.fs +++ b/resources/shaders/options_120_solid.fs @@ -65,7 +65,7 @@ vec4 on_sphere_color(vec3 eye_on_sphere_position) NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; - return vec4(intensity + uniform_color.rgb * intensity.x, 1.0); + return vec4(intensity.y + uniform_color.rgb * intensity.x, 1.0); // return vec4(vec3(intensity.y) + uniform_color.rgb * intensity.x, 1.0); } From ca17948f87d68e616a0dc888e906462a48b7699d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 8 Jun 2020 09:12:20 +0200 Subject: [PATCH 119/503] ENABLE_GCODE_VIEWER_AS_STATE -> Load gcode from file and process it --- src/libslic3r/GCode/GCodeProcessor.cpp | 18 +++------ src/libslic3r/GCode/GCodeProcessor.hpp | 5 ++- src/libslic3r/Technologies.hpp | 2 - src/slic3r/GUI/GCodeViewer.cpp | 23 ++++++++---- src/slic3r/GUI/GLCanvas3D.hpp | 8 ---- src/slic3r/GUI/GUI_App.cpp | 2 - src/slic3r/GUI/GUI_App.hpp | 2 - src/slic3r/GUI/GUI_Preview.cpp | 14 ------- src/slic3r/GUI/MainFrame.cpp | 51 ++------------------------ src/slic3r/GUI/MainFrame.hpp | 8 ---- src/slic3r/GUI/Plater.cpp | 35 +++++------------- src/slic3r/GUI/Plater.hpp | 16 ++++---- 12 files changed, 46 insertions(+), 138 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index b43f1466c1..c74820a04c 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -37,7 +37,7 @@ void GCodeProcessor::CpColor::reset() current = 0; } -unsigned int GCodeProcessor::Result::id = 0; +unsigned int GCodeProcessor::s_result_id = 0; void GCodeProcessor::apply_config(const PrintConfig& config) { @@ -84,19 +84,19 @@ void GCodeProcessor::reset() m_cp_color.reset(); m_result.reset(); + m_result.id = ++s_result_id; } void GCodeProcessor::process_file(const std::string& filename) { + m_result.id = ++s_result_id; m_result.moves.emplace_back(MoveVertex()); m_parser.parse_file(filename, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { process_gcode_line(line); }); } void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) { -/* - std::cout << line.raw() << std::endl; -*/ +/* std::cout << line.raw() << std::endl; */ // update start position m_start_position = m_end_position; @@ -296,24 +296,18 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) } else if (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f || delta_pos[Z] != 0.0f) type = EMoveType::Travel; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE - if (type == EMoveType::Extrude && (m_width == 0.0f || m_height == 0.0f)) - { - if (m_extrusion_role != erCustom) - { + if (type == EMoveType::Extrude && (m_width == 0.0f || m_height == 0.0f)) { + if (m_extrusion_role != erCustom) { m_width = 0.5f; m_height = 0.5f; } type = EMoveType::Travel; } #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (type == EMoveType::Extrude && (m_width == 0.0f || m_height == 0.0f || !is_valid_extrusion_role(m_extrusion_role))) type = EMoveType::Travel; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ return type; }; diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 4f6cf7430f..e8c43350c0 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -104,9 +104,9 @@ namespace Slic3r { struct Result { - static unsigned int id; + unsigned int id; std::vector moves; - void reset() { ++id; moves = std::vector(); } + void reset() { moves = std::vector(); } }; private: @@ -134,6 +134,7 @@ namespace Slic3r { CpColor m_cp_color; Result m_result; + static unsigned int s_result_id; public: GCodeProcessor() { reset(); } diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 6b60517f61..31cd28b0d2 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -55,9 +55,7 @@ #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) #define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_SHADERS_EDITOR (1 && ENABLE_GCODE_VIEWER) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #define ENABLE_GCODE_VIEWER_AS_STATE (1 && ENABLE_GCODE_VIEWER) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 3709aaa888..31065d230d 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -5,6 +5,9 @@ #include "libslic3r/Print.hpp" #include "libslic3r/Geometry.hpp" #include "GUI_App.hpp" +#if ENABLE_GCODE_VIEWER_AS_STATE +#include "MainFrame.hpp" +#endif // ENABLE_GCODE_VIEWER_AS_STATE #include "Plater.hpp" #include "PresetBundle.hpp" #include "Camera.hpp" @@ -443,13 +446,19 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) return; // vertex data / bounding box -> extract from result - std::vector vertices_data(m_vertices.vertices_count * 3); - for (size_t i = 0; i < m_vertices.vertices_count; ++i) { - const GCodeProcessor::MoveVertex& move = gcode_result.moves[i]; - if (move.type == GCodeProcessor::EMoveType::Extrude) - m_bounding_box.merge(move.position.cast()); - ::memcpy(static_cast(&vertices_data[i * 3]), static_cast(move.position.data()), 3 * sizeof(float)); - } + std::vector vertices_data(m_vertices.vertices_count * 3); + for (size_t i = 0; i < m_vertices.vertices_count; ++i) { + const GCodeProcessor::MoveVertex& move = gcode_result.moves[i]; +#if ENABLE_GCODE_VIEWER_AS_STATE + if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) { +#endif // ENABLE_GCODE_VIEWER_AS_STATE + if (move.type == GCodeProcessor::EMoveType::Extrude && move.width != 0.0f && move.height != 0.0f) + m_bounding_box.merge(move.position.cast()); +#if ENABLE_GCODE_VIEWER_AS_STATE + } +#endif // ENABLE_GCODE_VIEWER_AS_STATE + ::memcpy(static_cast(&vertices_data[i * 3]), static_cast(move.position.data()), 3 * sizeof(float)); + } m_bounding_box.merge(m_bounding_box.max + m_sequential_view.marker.get_bounding_box().max[2] * Vec3d::UnitZ()); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 810a99f211..c8b7d3231c 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -127,9 +127,7 @@ class GLCanvas3D static const double DefaultCameraZoomToBoxMarginFactor; public: -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ struct GCodePreviewVolumeIndex { enum EType @@ -156,9 +154,7 @@ public: void reset() { first_volumes.clear(); } }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ private: class LayersEditing @@ -514,13 +510,9 @@ private: bool m_reload_delayed; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GCodePreviewVolumeIndex m_gcode_preview_volume_index; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_RENDER_PICKING_PASS bool m_show_picking_texture; diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index ad26582986..d7f019e41b 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -760,7 +760,6 @@ void GUI_App::import_model(wxWindow *parent, wxArrayString& input_files) const dialog.GetPaths(input_files); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE void GUI_App::load_gcode(wxWindow* parent, wxString& input_file) const { @@ -774,7 +773,6 @@ void GUI_App::load_gcode(wxWindow* parent, wxString& input_file) const input_file = dialog.GetPath(); } #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool GUI_App::switch_language() { diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 11f3a7c3cc..ad874bd1ef 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -156,11 +156,9 @@ public: void keyboard_shortcuts(); void load_project(wxWindow *parent, wxString& input_file) const; void import_model(wxWindow *parent, wxArrayString& input_files) const; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE void load_gcode(wxWindow* parent, wxString& input_file) const; #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ static bool catch_error(std::function cb, const std::string& err); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 6b3866ca47..51e8e81876 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -13,11 +13,9 @@ #include "PresetBundle.hpp" #include "DoubleSlider.hpp" #include "Plater.hpp" -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE #include "MainFrame.hpp" #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include #include @@ -1193,13 +1191,11 @@ void Preview::update_double_slider_from_canvas(wxKeyEvent & event) void Preview::load_print_as_fff(bool keep_z_range) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE if (wxGetApp().mainframe == nullptr) // avoid proessing while mainframe is being constructed return; #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (m_loaded || m_process->current_printer_technology() != ptFFF) return; @@ -1224,23 +1220,17 @@ void Preview::load_print_as_fff(bool keep_z_range) } } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer && !has_layers) #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (! has_layers) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { #if ENABLE_GCODE_VIEWER hide_layers_slider(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GetSizer()->Hide(m_bottom_toolbar_panel); GetSizer()->Layout(); Refresh(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #else reset_sliders(true); m_canvas->reset_legend_texture(); @@ -1270,15 +1260,11 @@ void Preview::load_print_as_fff(bool keep_z_range) #if ENABLE_GCODE_VIEWER GCodeViewer::EViewType gcode_view_type = m_canvas->get_gcode_view_preview_type(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE bool gcode_preview_data_valid = !m_gcode_result->moves.empty(); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool gcode_preview_data_valid = print->is_step_done(psGCodeExport); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #else bool gcode_preview_data_valid = print->is_step_done(psGCodeExport) && ! m_gcode_preview_data->empty(); #endif // ENABLE_GCODE_VIEWER diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 89be99e06c..f09f611e6e 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -81,7 +81,6 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S // initialize tabpanel and menubar init_tabpanel(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE init_editor_menubar(); init_gcodeviewer_menubar(); @@ -99,11 +98,8 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S SetAcceleratorTable(accel); #endif // _WIN32 #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ init_menubar(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // set default tooltip timer in msec // SetAutoPop supposedly accepts long integers but some bug doesn't allow for larger values @@ -248,7 +244,6 @@ void MainFrame::shutdown() } #endif // _WIN32 -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE if (m_plater != nullptr) { m_plater->stop_jobs(); @@ -263,7 +258,6 @@ void MainFrame::shutdown() m_plater->reset_canvas_volumes(); } #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (m_plater) m_plater->stop_jobs(); @@ -275,9 +269,7 @@ void MainFrame::shutdown() // Cleanup of canvases' volumes needs to be done here or a crash may happen on some Linux Debian flavours // see: https://github.com/prusa3d/PrusaSlicer/issues/3964 if (m_plater) m_plater->reset_canvas_volumes(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // Weird things happen as the Paint messages are floating around the windows being destructed. // Avoid the Paint messages by hiding the main window. @@ -288,9 +280,8 @@ void MainFrame::shutdown() if (m_settings_dialog) m_settings_dialog->Destroy(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (m_plater != nullptr) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + // restore sidebar if it was hidden when switching to gcode viewer mode if (m_restore_from_gcode_viewer.collapsed_sidebar) m_plater->collapse_sidebar(false); // Stop the background thread (Windows and Linux). @@ -298,9 +289,7 @@ void MainFrame::shutdown() m_plater->get_mouse3d_controller().shutdown(); // Store the device parameter database back to appconfig. m_plater->get_mouse3d_controller().save_config(*wxGetApp().app_config); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // Stop the background thread of the removable drive manager, so that no new updates will be sent to the Plater. wxGetApp().removable_drive_manager()->shutdown(); @@ -642,7 +631,6 @@ void MainFrame::on_sys_color_changed() msw_rescale_menu(menu_bar->GetMenu(id)); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE #ifdef _MSC_VER // \xA0 is a non-breaking space. It is entered here to spoil the automatic accelerators, @@ -716,11 +704,8 @@ static void add_common_view_menu_items(wxMenu* view_menu, MainFrame* mainFrame, void MainFrame::init_editor_menubar() #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void MainFrame::init_menubar() -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { #ifdef __APPLE__ wxMenuBar::SetAutoWindowMenu(false); @@ -880,21 +865,17 @@ void MainFrame::init_menubar() append_menu_item(fileMenu, wxID_ANY, _L("&Repair STL file") + dots, _L("Automatically repair an STL file"), [this](wxCommandEvent&) { repair_stl(); }, "wrench", nullptr, [this]() {return true; }, this); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_ANY, _L("&G-code preview"), _L("Switch to G-code preview mode"), [this](wxCommandEvent&) { set_mode(EMode::GCodeViewer); }); #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_EXIT, _L("&Quit"), wxString::Format(_L("Quit %s"), SLIC3R_APP_NAME), [this](wxCommandEvent&) { Close(false); }); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #ifdef _MSC_VER // \xA0 is a non-breaking space. It is entered here to spoil the automatic accelerators, // as the simple numeric accelerators spoil all numeric data entry. @@ -904,9 +885,7 @@ void MainFrame::init_menubar() wxString sep = " - "; wxString sep_space = ""; #endif -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // Edit menu wxMenu* editMenu = nullptr; @@ -990,9 +969,7 @@ void MainFrame::init_menubar() [this](){return can_change_view(); }, this); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if _WIN32 // This is needed on Windows to fake the CTRL+# of the window menu when using the numpad wxAcceleratorEntry entries[6]; @@ -1005,9 +982,7 @@ void MainFrame::init_menubar() wxAcceleratorTable accel(6, entries); SetAcceleratorTable(accel); #endif // _WIN32 -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ windowMenu->AppendSeparator(); append_menu_item(windowMenu, wxID_ANY, _L("Print &Host Upload Queue") + "\tCtrl+J", _L("Display the Print Host Upload Queue window"), @@ -1019,11 +994,9 @@ void MainFrame::init_menubar() wxMenu* viewMenu = nullptr; if (m_plater) { viewMenu = new wxMenu(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE add_common_view_menu_items(viewMenu, this, std::bind(&MainFrame::can_change_view, this)); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // The camera control accelerators are captured by GLCanvas3D::on_char(). append_menu_item(viewMenu, wxID_ANY, _L("Iso") + sep + "&0", _L("Iso View"), [this](wxCommandEvent&) { select_view("iso"); }, "", nullptr, [this](){return can_change_view(); }, this); @@ -1042,9 +1015,7 @@ void MainFrame::init_menubar() "", nullptr, [this](){return can_change_view(); }, this); append_menu_item(viewMenu, wxID_ANY, _L("Right") + sep + "&6", _L("Right View"), [this](wxCommandEvent&) { select_view("right"); }, "", nullptr, [this](){return can_change_view(); }, this); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ viewMenu->AppendSeparator(); #if ENABLE_SLOPE_RENDERING wxMenu* options_menu = new wxMenu(); @@ -1066,11 +1037,9 @@ void MainFrame::init_menubar() } // Help menu -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE auto helpMenu = generate_help_menu(); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ auto helpMenu = new wxMenu(); { append_menu_item(helpMenu, wxID_ANY, _L("Prusa 3D &Drivers"), _L("Open the Prusa3D drivers download page in your browser"), @@ -1105,14 +1074,11 @@ void MainFrame::init_menubar() [this](wxCommandEvent&) { wxGetApp().gcode_thumbnails_debug(); }); #endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // menubar // assign menubar to frame after appending items, otherwise special items // will not be handled correctly -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE m_editor_menubar = new wxMenuBar(); m_editor_menubar->Append(fileMenu, _L("&File")); @@ -1124,7 +1090,6 @@ void MainFrame::init_menubar() m_editor_menubar->Append(helpMenu, _L("&Help")); SetMenuBar(m_editor_menubar); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ auto menubar = new wxMenuBar(); menubar->Append(fileMenu, _(L("&File"))); if (editMenu) menubar->Append(editMenu, _(L("&Edit"))); @@ -1134,9 +1099,7 @@ void MainFrame::init_menubar() wxGetApp().add_config_menu(menubar); menubar->Append(helpMenu, _(L("&Help"))); SetMenuBar(menubar); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #ifdef __APPLE__ // This fixes a bug on Mac OS where the quit command doesn't emit window close events @@ -1150,18 +1113,13 @@ void MainFrame::init_menubar() #endif if (plater()->printer_technology() == ptSLA) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE update_editor_menubar(); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ update_menubar(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE void MainFrame::init_gcodeviewer_menubar() { @@ -1204,6 +1162,7 @@ void MainFrame::set_mode(EMode mode) case EMode::Editor: { m_plater->reset(); +// m_plater->reset_last_loaded_gcode(); // switch view m_plater->select_view_3D("3D"); @@ -1229,6 +1188,7 @@ void MainFrame::set_mode(EMode mode) case EMode::GCodeViewer: { m_plater->reset(); + m_plater->reset_last_loaded_gcode(); // reinitialize undo/redo stack m_plater->clear_undo_redo_stack_main(); @@ -1258,17 +1218,12 @@ void MainFrame::set_mode(EMode mode) } } #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE void MainFrame::update_editor_menubar() #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void MainFrame::update_menubar() -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { const bool is_fff = plater()->printer_technology() == ptFFF; diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 8a68fa36fd..c51567dd31 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -68,7 +68,6 @@ class MainFrame : public DPIFrame wxString m_qs_last_input_file = wxEmptyString; wxString m_qs_last_output_file = wxEmptyString; wxString m_last_config = wxEmptyString; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE wxMenuBar* m_editor_menubar{ nullptr }; wxMenuBar* m_gcodeviewer_menubar{ nullptr }; @@ -81,7 +80,6 @@ class MainFrame : public DPIFrame RestoreFromGCodeViewer m_restore_from_gcode_viewer; #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if 0 wxMenuItem* m_menu_item_repeat { nullptr }; // doesn't used now @@ -135,7 +133,6 @@ class MainFrame : public DPIFrame slDlg, } m_layout; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE public: enum class EMode : unsigned char @@ -147,7 +144,6 @@ public: private: EMode m_mode{ EMode::Editor }; #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ protected: virtual void on_dpi_changed(const wxRect &suggested_rect); @@ -167,7 +163,6 @@ public: void init_tabpanel(); void create_preset_tabs(); void add_created_tab(Tab* panel); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE void init_editor_menubar(); void update_editor_menubar(); @@ -176,12 +171,9 @@ public: EMode get_mode() const { return m_mode; } void set_mode(EMode mode); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void init_menubar(); void update_menubar(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void update_ui_from_settings(); bool is_loaded() const { return m_loaded; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index cb306caba1..7e9779f17e 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -33,17 +33,13 @@ #include "libslic3r/Format/STL.hpp" #include "libslic3r/Format/AMF.hpp" #include "libslic3r/Format/3mf.hpp" -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE #include "libslic3r/GCode/GCodeProcessor.hpp" #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_GCODE_VIEWER #include "libslic3r/GCode/PreviewData.hpp" #endif // !ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "libslic3r/GCode/ThumbnailData.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/SLA/Hollowing.hpp" @@ -2735,11 +2731,9 @@ void Plater::priv::reset() model.custom_gcode_per_print_z.gcodes.clear(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE gcode_result.reset(); #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } void Plater::priv::mirror(Axis axis) @@ -4550,7 +4544,6 @@ void Plater::extract_config_from_project() load_files(input_paths, false, true); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE void Plater::load_gcode() { @@ -4563,25 +4556,27 @@ void Plater::load_gcode() void Plater::load_gcode(const wxString& filename) { - if (filename.empty()) + if (filename.empty() || m_last_loaded_gcode == filename) return; -// p->gcode_result.reset(); + m_last_loaded_gcode = filename; + + // cleanup view before to start loading/processing + p->gcode_result.reset(); + p->preview->reload_print(false); + p->get_current_canvas3D()->render(); wxBusyCursor wait; - wxBusyInfo info(_L("Processing GCode") + "...", get_current_canvas3D()->get_wxglcanvas()); +// wxBusyInfo info(_L("Processing GCode") + "...", get_current_canvas3D()->get_wxglcanvas()); GCodeProcessor processor; // processor.apply_config(config); processor.process_file(filename.ToUTF8().data()); p->gcode_result = std::move(processor.extract_result()); -// select_view_3D("Preview"); - p->preview->load_print(false); -// p->preview->load_gcode_preview(); + p->preview->reload_print(false); } #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ std::vector Plater::load_files(const std::vector& input_files, bool load_model, bool load_config, bool imperial_units /*= false*/) { return p->load_files(input_files, load_model, load_config, imperial_units); } @@ -5513,16 +5508,12 @@ void Plater::set_printer_technology(PrinterTechnology printer_technology) p->label_btn_export = printer_technology == ptFFF ? L("Export G-code") : L("Export"); p->label_btn_send = printer_technology == ptFFF ? L("Send G-code") : L("Send to printer"); - if (wxGetApp().mainframe) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + if (wxGetApp().mainframe != nullptr) #if ENABLE_GCODE_VIEWER_AS_STATE wxGetApp().mainframe->update_editor_menubar(); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ wxGetApp().mainframe->update_menubar(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ p->update_main_toolbar_tooltips(); @@ -5674,28 +5665,24 @@ bool Plater::init_view_toolbar() return p->init_view_toolbar(); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE void Plater::enable_view_toolbar(bool enable) { p->view_toolbar.set_enabled(enable); } #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool Plater::init_collapse_toolbar() { return p->init_collapse_toolbar(); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE void Plater::enable_collapse_toolbar(bool enable) { p->collapse_toolbar.set_enabled(enable); } #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ const Camera& Plater::get_camera() const { @@ -5819,11 +5806,9 @@ bool Plater::can_undo() const { return p->undo_redo_stack().has_undo_snapshot(); bool Plater::can_redo() const { return p->undo_redo_stack().has_redo_snapshot(); } bool Plater::can_reload_from_disk() const { return p->can_reload_from_disk(); } const UndoRedo::Stack& Plater::undo_redo_stack_main() const { return p->undo_redo_stack_main(); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE void Plater::clear_undo_redo_stack_main() { p->undo_redo_stack_main().clear(); } #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void Plater::enter_gizmos_stack() { p->enter_gizmos_stack(); } void Plater::leave_gizmos_stack() { p->leave_gizmos_stack(); } bool Plater::inside_snapshot_capture() { return p->inside_snapshot_capture(); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 92b46c9373..fdead2a1cc 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -173,12 +173,10 @@ public: void add_model(bool imperial_units = false); void import_sl1_archive(); void extract_config_from_project(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE void load_gcode(); void load_gcode(const wxString& filename); #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ std::vector load_files(const std::vector& input_files, bool load_model = true, bool load_config = true, bool imperial_units = false); // To be called when providing a list of files to the GUI slic3r on command line. @@ -258,11 +256,9 @@ public: bool search_string_getter(int idx, const char** label, const char** tooltip); // For the memory statistics. const Slic3r::UndoRedo::Stack& undo_redo_stack_main() const; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE void clear_undo_redo_stack_main(); #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // Enter / leave the Gizmos specific Undo / Redo stack. To be used by the SLA support point editing gizmo. void enter_gizmos_stack(); void leave_gizmos_stack(); @@ -326,17 +322,13 @@ public: void sys_color_changed(); bool init_view_toolbar(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE void enable_view_toolbar(bool enable); #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool init_collapse_toolbar(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_AS_STATE void enable_collapse_toolbar(bool enable); #endif // ENABLE_GCODE_VIEWER_AS_STATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ const Camera& get_camera() const; Camera& get_camera(); @@ -360,6 +352,10 @@ public: void update_preview_moves_slider(); #endif // ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER_AS_STATE + void reset_last_loaded_gcode() { m_last_loaded_gcode = ""; } +#endif // ENABLE_GCODE_VIEWER_AS_STATE + const Mouse3DController& get_mouse3d_controller() const; Mouse3DController& get_mouse3d_controller(); @@ -414,6 +410,10 @@ private: bool m_tracking_popup_menu = false; wxString m_tracking_popup_menu_error_message; +#if ENABLE_GCODE_VIEWER_AS_STATE + wxString m_last_loaded_gcode; +#endif // ENABLE_GCODE_VIEWER_AS_STATE + void suppress_snapshots(); void allow_snapshots(); From 70478f226f739f08dba2fac59e44b0ddd9e009e5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 8 Jun 2020 12:27:32 +0200 Subject: [PATCH 120/503] ENABLE_GCODE_VIEWER_AS_STATE -> Use default printbed in gcode viewer --- src/slic3r/GUI/GCodeViewer.cpp | 17 +++++++++++++++- src/slic3r/GUI/GLCanvas3D.cpp | 37 +++++++++++++++++++++++++--------- src/slic3r/GUI/GLCanvas3D.hpp | 3 +++ src/slic3r/GUI/MainFrame.cpp | 11 +++++++++- src/slic3r/GUI/Plater.cpp | 37 ++++++++++++++++++---------------- src/slic3r/GUI/Plater.hpp | 3 +++ 6 files changed, 79 insertions(+), 29 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 31065d230d..281ccfa67d 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -251,6 +251,19 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& load_toolpaths(gcode_result); load_shells(print, initialized); + +#if ENABLE_GCODE_VIEWER_AS_STATE + // adjust printbed size + const double margin = 10.0; + Vec2d min(m_bounding_box.min(0) - margin, m_bounding_box.min(1) - margin); + Vec2d max(m_bounding_box.max(0) + margin, m_bounding_box.max(1) + margin); + Pointfs bed_shape = { + { min(0), min(1) }, + { max(0), min(1) }, + { max(0), max(1) }, + { min(0), max(1) } }; + wxGetApp().plater()->set_bed_shape(bed_shape, "", ""); +#endif // ENABLE_GCODE_VIEWER_AS_STATE } void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std::vector& str_tool_colors) @@ -450,7 +463,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) for (size_t i = 0; i < m_vertices.vertices_count; ++i) { const GCodeProcessor::MoveVertex& move = gcode_result.moves[i]; #if ENABLE_GCODE_VIEWER_AS_STATE - if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) { + if (wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer) + m_bounding_box.merge(move.position.cast()); + else { #endif // ENABLE_GCODE_VIEWER_AS_STATE if (move.type == GCodeProcessor::EMoveType::Extrude && move.width != 0.0f && move.height != 0.0f) m_bounding_box.merge(move.position.cast()); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 304bc9fe7c..29d3b9aa57 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1930,6 +1930,13 @@ void GLCanvas3D::zoom_to_selection() _zoom_to_box(m_selection.get_bounding_box()); } +#if ENABLE_GCODE_VIEWER_AS_STATE +void GLCanvas3D::zoom_to_gcode() +{ + _zoom_to_box(m_gcode_viewer.get_bounding_box(), 1.05); +} +#endif // ENABLE_GCODE_VIEWER_AS_STATE + void GLCanvas3D::select_view(const std::string& direction) { wxGetApp().plater()->get_camera().select_view(direction); @@ -2706,7 +2713,10 @@ static void load_gcode_retractions(const GCodePreviewData::Retraction& retractio void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result) { m_gcode_viewer.load(gcode_result, *this->fff_print(), m_initialized); - _show_warning_texture_if_needed(WarningTexture::ToolpathOutside); +#if ENABLE_GCODE_VIEWER_AS_STATE + if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) +#endif // ENABLE_GCODE_VIEWER_AS_STATE + _show_warning_texture_if_needed(WarningTexture::ToolpathOutside); } void GLCanvas3D::refresh_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector& str_tool_colors) @@ -5351,8 +5361,7 @@ static BoundingBoxf3 print_volume(const DynamicPrintConfig& config) BoundingBoxf3 ret; const ConfigOptionPoints* opt = dynamic_cast(config.option("bed_shape")); - if (opt != nullptr) - { + if (opt != nullptr) { BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values)); ret = BoundingBoxf3(Vec3d(unscale(bed_box_2D.min(0)) - tolerance_x, unscale(bed_box_2D.min(1)) - tolerance_y, 0.0), Vec3d(unscale(bed_box_2D.max(0)) + tolerance_x, unscale(bed_box_2D.max(1)) + tolerance_y, config.opt_float("max_print_height"))); // Allow the objects to protrude below the print bed @@ -5365,14 +5374,22 @@ static BoundingBoxf3 print_volume(const DynamicPrintConfig& config) void GLCanvas3D::_render_background() const { #if ENABLE_GCODE_VIEWER - bool use_error_color = m_dynamic_background_enabled; - if (!m_volumes.empty()) - use_error_color &= _is_any_volume_outside(); - else - { - BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); - use_error_color &= (test_volume.radius() > 0.0) ? !test_volume.contains(m_gcode_viewer.get_bounding_box()) : false; +#if ENABLE_GCODE_VIEWER_AS_STATE + bool use_error_color = false; + if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) { + use_error_color = m_dynamic_background_enabled; +#else + bool use_error_color = m_dynamic_background_enabled; +#endif // ENABLE_GCODE_VIEWER_AS_STATE + if (!m_volumes.empty()) + use_error_color &= _is_any_volume_outside(); + else { + BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); + use_error_color &= (test_volume.radius() > 0.0) ? !test_volume.contains(m_gcode_viewer.get_bounding_box()) : false; + } +#if ENABLE_GCODE_VIEWER_AS_STATE } +#endif // ENABLE_GCODE_VIEWER_AS_STATE #endif // ENABLE_GCODE_VIEWER glsafe(::glPushMatrix()); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index c8b7d3231c..782a9425d7 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -624,6 +624,9 @@ public: void zoom_to_bed(); void zoom_to_volumes(); void zoom_to_selection(); +#if ENABLE_GCODE_VIEWER_AS_STATE + void zoom_to_gcode(); +#endif // ENABLE_GCODE_VIEWER_AS_STATE void select_view(const std::string& direction); void update_volumes_colors_by_extruder(); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index f09f611e6e..f9e757251f 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1162,10 +1162,14 @@ void MainFrame::set_mode(EMode mode) case EMode::Editor: { m_plater->reset(); -// m_plater->reset_last_loaded_gcode(); // switch view m_plater->select_view_3D("3D"); + m_plater->select_view("iso"); + + // switch printbed + m_plater->set_bed_shape(); + // switch menubar SetMenuBar(m_editor_menubar); @@ -1196,6 +1200,11 @@ void MainFrame::set_mode(EMode mode) // switch view m_plater->select_view_3D("Preview"); + m_plater->select_view("iso"); + + // switch printbed + m_plater->set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, "", ""); + // switch menubar SetMenuBar(m_gcodeviewer_menubar); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 7e9779f17e..e5bbf73c61 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2014,21 +2014,11 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLTOOLBAR_SPLIT_OBJECTS, &priv::on_action_split_objects, this); view3D_canvas->Bind(EVT_GLTOOLBAR_SPLIT_VOLUMES, &priv::on_action_split_volumes, this); view3D_canvas->Bind(EVT_GLTOOLBAR_LAYERSEDITING, &priv::on_action_layersediting, this); - view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&) - { - set_bed_shape(config->option("bed_shape")->values, - config->option("bed_custom_texture")->value, - config->option("bed_custom_model")->value); - }); + view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [q](SimpleEvent&) { q->set_bed_shape(); }); // Preview events: preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); }); - preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&) - { - set_bed_shape(config->option("bed_shape")->values, - config->option("bed_custom_texture")->value, - config->option("bed_custom_model")->value); - }); + preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [q](SimpleEvent&) { q->set_bed_shape(); }); preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); }); #if ENABLE_GCODE_VIEWER preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_MOVE_LAYERS_SLIDER, [this](wxKeyEvent& evt) { preview->move_layers_slider(evt); }); @@ -4567,14 +4557,16 @@ void Plater::load_gcode(const wxString& filename) p->get_current_canvas3D()->render(); wxBusyCursor wait; -// wxBusyInfo info(_L("Processing GCode") + "...", get_current_canvas3D()->get_wxglcanvas()); + // process gcode GCodeProcessor processor; // processor.apply_config(config); processor.process_file(filename.ToUTF8().data()); p->gcode_result = std::move(processor.extract_result()); + // show results p->preview->reload_print(false); + p->preview->get_canvas3d()->zoom_to_gcode(); } #endif // ENABLE_GCODE_VIEWER_AS_STATE @@ -5318,9 +5310,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config) } if (bed_shape_changed) - p->set_bed_shape(p->config->option("bed_shape")->values, - p->config->option("bed_custom_texture")->value, - p->config->option("bed_custom_model")->value); + set_bed_shape(); if (update_scheduled) update(); @@ -5331,11 +5321,24 @@ void Plater::on_config_change(const DynamicPrintConfig &config) void Plater::set_bed_shape() const { - p->set_bed_shape(p->config->option("bed_shape")->values, +#if ENABLE_GCODE_VIEWER_AS_STATE + set_bed_shape(p->config->option("bed_shape")->values, + p->config->option("bed_custom_texture")->value, + p->config->option("bed_custom_model")->value); +#else + p->set_bed_shape(p->config->option("bed_shape")->values, p->config->option("bed_custom_texture")->value, p->config->option("bed_custom_model")->value); +#endif // ENABLE_GCODE_VIEWER_AS_STATE } +#if ENABLE_GCODE_VIEWER_AS_STATE +void Plater::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model) const +{ + p->set_bed_shape(shape, custom_texture, custom_model); +} +#endif // ENABLE_GCODE_VIEWER_AS_STATE + void Plater::force_filament_colors_update() { bool update_scheduled = false; diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index fdead2a1cc..9759a7ffc3 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -360,6 +360,9 @@ public: Mouse3DController& get_mouse3d_controller(); void set_bed_shape() const; +#if ENABLE_GCODE_VIEWER_AS_STATE + void set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model) const; +#endif // ENABLE_GCODE_VIEWER_AS_STATE // ROII wrapper for suppressing the Undo / Redo snapshot to be taken. class SuppressSnapshots From ea0e9a5873aecdc9799d39bc276e034b38126ff1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 8 Jun 2020 13:17:07 +0200 Subject: [PATCH 121/503] Follow-up of 70478f226f739f08dba2fac59e44b0ddd9e009e5 -> Fixed printbed for regular gcode preview --- src/slic3r/GUI/GCodeViewer.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 281ccfa67d..61d432b9a8 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -253,16 +253,18 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& load_shells(print, initialized); #if ENABLE_GCODE_VIEWER_AS_STATE - // adjust printbed size - const double margin = 10.0; - Vec2d min(m_bounding_box.min(0) - margin, m_bounding_box.min(1) - margin); - Vec2d max(m_bounding_box.max(0) + margin, m_bounding_box.max(1) + margin); - Pointfs bed_shape = { - { min(0), min(1) }, - { max(0), min(1) }, - { max(0), max(1) }, - { min(0), max(1) } }; - wxGetApp().plater()->set_bed_shape(bed_shape, "", ""); + if (wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer) { + // adjust printbed size + const double margin = 10.0; + Vec2d min(m_bounding_box.min(0) - margin, m_bounding_box.min(1) - margin); + Vec2d max(m_bounding_box.max(0) + margin, m_bounding_box.max(1) + margin); + Pointfs bed_shape = { + { min(0), min(1) }, + { max(0), min(1) }, + { max(0), max(1) }, + { min(0), max(1) } }; + wxGetApp().plater()->set_bed_shape(bed_shape, "", ""); + } #endif // ENABLE_GCODE_VIEWER_AS_STATE } From 9f94f89808d9651438f51cf45beffb02eaea0f9a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 8 Jun 2020 14:37:40 +0200 Subject: [PATCH 122/503] ENABLE_GCODE_VIEWER_AS_STATE -> Smoother transition between states --- src/slic3r/GUI/MainFrame.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index f9e757251f..efc3efa8a3 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1163,6 +1163,8 @@ void MainFrame::set_mode(EMode mode) { m_plater->reset(); + m_plater->Freeze(); + // switch view m_plater->select_view_3D("3D"); m_plater->select_view("iso"); @@ -1187,6 +1189,8 @@ void MainFrame::set_mode(EMode mode) m_restore_from_gcode_viewer.collapsed_sidebar = false; } + m_plater->Thaw(); + break; } case EMode::GCodeViewer: @@ -1194,6 +1198,8 @@ void MainFrame::set_mode(EMode mode) m_plater->reset(); m_plater->reset_last_loaded_gcode(); + m_plater->Freeze(); + // reinitialize undo/redo stack m_plater->clear_undo_redo_stack_main(); m_plater->take_snapshot(_L("New Project")); @@ -1222,6 +1228,8 @@ void MainFrame::set_mode(EMode mode) m_restore_from_gcode_viewer.collapsed_sidebar = true; } + m_plater->Thaw(); + break; } } From d358fe85fa03f6bdc807c14978d592bb066682da Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 9 Jun 2020 08:12:51 +0200 Subject: [PATCH 123/503] GCodeViewer -> Show tool marker position when enabled --- src/slic3r/GUI/GCodeViewer.cpp | 38 +++++++++++++++++++++++++++++++--- src/slic3r/GUI/GCodeViewer.hpp | 3 ++- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 61d432b9a8..0042340992 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -146,8 +146,9 @@ void GCodeViewer::SequentialView::Marker::init() } void GCodeViewer::SequentialView::Marker::set_world_position(const Vec3f& position) -{ - m_world_transform = (Geometry::assemble_transform(position.cast()) * Geometry::assemble_transform(m_model.get_bounding_box().size()[2] * Vec3d::UnitZ(), { M_PI, 0.0, 0.0 })).cast(); +{ + m_world_position = position; + m_world_transform = (Geometry::assemble_transform((position + m_z_offset * Vec3f::UnitZ()).cast()) * Geometry::assemble_transform(m_model.get_bounding_box().size()[2] * Vec3d::UnitZ(), { M_PI, 0.0, 0.0 })).cast(); m_world_bounding_box = m_model.get_bounding_box().transformed(m_world_transform.cast()); } @@ -176,6 +177,37 @@ void GCodeViewer::SequentialView::Marker::render() const shader->stop_using(); glsafe(::glDisable(GL_BLEND)); + + static float last_window_width = 0.0f; + static size_t last_text_length = 0; + static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size(); + imgui.set_next_window_pos(static_cast(cnv_size.get_width()), static_cast(cnv_size.get_height()), ImGuiCond_Always, 1.0f, 1.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::SetNextWindowBgAlpha(0.25f); + imgui.begin(std::string("ToolPosition"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(_u8L("Tool position") + ":"); + ImGui::PopStyleColor(); + ImGui::SameLine(); + char buf[1024]; + sprintf(buf, "X: %.2f, Y: %.2f, Z: %.2f", m_world_position(0), m_world_position(1), m_world_position(2)); + imgui.text(std::string(buf)); + + // force extra frame to automatically update window size + float width = ImGui::GetWindowWidth(); + size_t length = strlen(buf); + if (width != last_window_width || length != last_text_length) { + last_window_width = width; + last_text_length = length; + wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); + wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); + } + + imgui.end(); + ImGui::PopStyleVar(); } const std::vector GCodeViewer::Extrusion_Role_Colors {{ @@ -353,7 +385,7 @@ void GCodeViewer::render() const glsafe(::glEnable(GL_DEPTH_TEST)); render_toolpaths(); - m_sequential_view.marker.set_world_position(m_sequential_view.current_position + m_sequential_view_marker_z_offset * Vec3f::UnitZ()); + m_sequential_view.marker.set_world_position(m_sequential_view.current_position); m_sequential_view.marker.render(); render_shells(); render_legend(); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 6f24eef435..413f4ef4ad 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -218,8 +218,10 @@ public: class Marker { GLModel m_model; + Vec3f m_world_position; Transform3f m_world_transform; BoundingBoxf3 m_world_bounding_box; + float m_z_offset{ 0.5f }; std::array m_color{ 1.0f, 1.0f, 1.0f, 1.0f }; bool m_visible{ false }; @@ -274,7 +276,6 @@ private: std::vector m_extruder_ids; mutable Extrusions m_extrusions; mutable SequentialView m_sequential_view; - float m_sequential_view_marker_z_offset{ 0.5f }; Shells m_shells; EViewType m_view_type{ EViewType::FeatureType }; bool m_legend_enabled{ true }; From 345c01c54ff1ef85fbb98a09864aaa124d8fa0a2 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 9 Jun 2020 08:37:24 +0200 Subject: [PATCH 124/503] ENABLE_GCODE_VIEWER -> Updated keyboard shortcuts dialog --- src/slic3r/GUI/KBShortcutsDialog.cpp | 30 ++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 556b610e91..51ba06ba45 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -1,3 +1,4 @@ +#include "libslic3r/libslic3r.h" #include "KBShortcutsDialog.hpp" #include "I18N.hpp" #include "libslic3r/Utils.hpp" @@ -29,7 +30,7 @@ namespace Slic3r { namespace GUI { KBShortcutsDialog::KBShortcutsDialog() - : DPIDialog(NULL, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _(L("Keyboard Shortcuts")), + : DPIDialog(NULL, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _L("Keyboard Shortcuts"), #if ENABLE_SCROLLABLE wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) #else @@ -146,7 +147,7 @@ void KBShortcutsDialog::fill_shortcuts() { "?", L("Show keyboard shortcuts list") } }; - m_full_shortcuts.push_back(std::make_pair(_(L("Commands")), commands_shortcuts)); + m_full_shortcuts.push_back(std::make_pair(_L("Commands"), commands_shortcuts)); Shortcuts plater_shortcuts = { { "A", L("Arrange") }, @@ -186,7 +187,7 @@ void KBShortcutsDialog::fill_shortcuts() #endif // ENABLE_RENDER_PICKING_PASS }; - m_full_shortcuts.push_back(std::make_pair(_(L("Plater")), plater_shortcuts)); + m_full_shortcuts.push_back(std::make_pair(_L("Plater"), plater_shortcuts)); Shortcuts gizmos_shortcuts = { { "Shift+", L("Press to snap by 5% in Gizmo scale\nor to snap by 1mm in Gizmo move") }, @@ -195,7 +196,7 @@ void KBShortcutsDialog::fill_shortcuts() { alt, L("Press to scale (in Gizmo scale) or rotate (in Gizmo rotate)\nselected objects around their own center") }, }; - m_full_shortcuts.push_back(std::make_pair(_(L("Gizmos")), gizmos_shortcuts)); + m_full_shortcuts.push_back(std::make_pair(_L("Gizmos"), gizmos_shortcuts)); Shortcuts preview_shortcuts = { { L("Arrow Up"), L("Upper Layer") }, @@ -205,7 +206,7 @@ void KBShortcutsDialog::fill_shortcuts() { "L", L("Show/Hide Legend") } }; - m_full_shortcuts.push_back(std::make_pair(_(L("Preview")), preview_shortcuts)); + m_full_shortcuts.push_back(std::make_pair(_L("Preview"), preview_shortcuts)); Shortcuts layers_slider_shortcuts = { { L("Arrow Up"), L("Move current slider thumb Up") }, @@ -213,10 +214,23 @@ void KBShortcutsDialog::fill_shortcuts() { L("Arrow Left"), L("Set upper thumb to current slider thumb") }, { L("Arrow Right"), L("Set lower thumb to current slider thumb") }, { "+", L("Add color change marker for current layer") }, - { "-", L("Delete color change marker for current layer") } + { "-", L("Delete color change marker for current layer") }, + { "Shift+", L("Press to speed up 5 times while moving thumb\nwith arrow keys or mouse wheel") }, + { ctrl, L("Press to speed up 5 times while moving thumb\nwith arrow keys or mouse wheel") }, }; - m_full_shortcuts.push_back(std::make_pair(_(L("Layers Slider")), layers_slider_shortcuts)); + m_full_shortcuts.push_back(std::make_pair(_L("Layers Slider"), layers_slider_shortcuts)); + +#if ENABLE_GCODE_VIEWER + Shortcuts sequential_slider_shortcuts = { + { L("Arrow Left"), L("Move current slider thumb Left") }, + { L("Arrow Right"), L("Move current slider thumb Right") }, + { "Shift+", L("Press to speed up 5 times while moving thumb\nwith arrow keys or mouse wheel") }, + { ctrl, L("Press to speed up 5 times while moving thumb\nwith arrow keys or mouse wheel") }, + }; + + m_full_shortcuts.push_back(std::make_pair(_L("Sequential Slider"), sequential_slider_shortcuts)); +#endif // ENABLE_GCODE_VIEWER } wxPanel* KBShortcutsDialog::create_header(wxWindow* parent, const wxFont& bold_font) @@ -239,7 +253,7 @@ wxPanel* KBShortcutsDialog::create_header(wxWindow* parent, const wxFont& bold_f sizer->Add(m_header_bitmap, 0, wxEXPAND | wxLEFT | wxRIGHT, 10); // text - wxStaticText* text = new wxStaticText(panel, wxID_ANY, _(L("Keyboard shortcuts"))); + wxStaticText* text = new wxStaticText(panel, wxID_ANY, _L("Keyboard shortcuts")); text->SetFont(header_font); sizer->Add(text, 0, wxALIGN_CENTER_VERTICAL); From 48cc358b7271b70f6c5d1ca2aa15ba42ab37eac8 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 9 Jun 2020 11:44:25 +0200 Subject: [PATCH 125/503] Fixed build on Mac --- src/slic3r/GUI/MainFrame.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index efc3efa8a3..ba4568bbce 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1104,7 +1104,11 @@ void MainFrame::init_menubar() #ifdef __APPLE__ // This fixes a bug on Mac OS where the quit command doesn't emit window close events // wx bug: https://trac.wxwidgets.org/ticket/18328 +#if ENABLE_GCODE_VIEWER_AS_STATE + wxMenu* apple_menu = m_editor_menubar->OSXGetAppleMenu(); +#else wxMenu *apple_menu = menubar->OSXGetAppleMenu(); +#endif // ENABLE_GCODE_VIEWER_AS_STATE if (apple_menu != nullptr) { apple_menu->Bind(wxEVT_MENU, [this](wxCommandEvent &) { Close(); From 4c51a258ef59cab6ce344b52badef40082522b58 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 9 Jun 2020 12:44:22 +0200 Subject: [PATCH 126/503] GCodeViewer -> Fixed bottom panel not disappearing when switching to gcode viewer from preview --- src/slic3r/GUI/Plater.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index e5bbf73c61..0420c1d031 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2708,6 +2708,10 @@ void Plater::priv::reset() if (view3D->is_layers_editing_enabled()) view3D->enable_layers_editing(false); +#if ENABLE_GCODE_VIEWER_AS_STATE + gcode_result.reset(); +#endif // ENABLE_GCODE_VIEWER_AS_STATE + // Stop and reset the Print content. this->background_process.reset(); model.clear_objects(); @@ -2720,10 +2724,6 @@ void Plater::priv::reset() this->sidebar->show_sliced_info_sizer(false); model.custom_gcode_per_print_z.gcodes.clear(); - -#if ENABLE_GCODE_VIEWER_AS_STATE - gcode_result.reset(); -#endif // ENABLE_GCODE_VIEWER_AS_STATE } void Plater::priv::mirror(Axis axis) From aa14b4263841d6e3276ad64c31518190d875bfad Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 12 Jun 2020 09:01:20 +0200 Subject: [PATCH 127/503] GCodeProcessor -> Added processing of gcode lines G0 --- resources/shaders/options_120_solid.fs | 1 - src/libslic3r/GCode/GCodeProcessor.cpp | 6 ++++++ src/libslic3r/GCode/GCodeProcessor.hpp | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/resources/shaders/options_120_solid.fs b/resources/shaders/options_120_solid.fs index 912b809e07..4719ff96a6 100644 --- a/resources/shaders/options_120_solid.fs +++ b/resources/shaders/options_120_solid.fs @@ -66,7 +66,6 @@ vec4 on_sphere_color(vec3 eye_on_sphere_position) intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; return vec4(intensity.y + uniform_color.rgb * intensity.x, 1.0); -// return vec4(vec3(intensity.y) + uniform_color.rgb * intensity.x, 1.0); } float fragment_depth(vec3 eye_pos) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index c74820a04c..d561647262 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -110,6 +110,7 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) { switch (::atoi(&cmd[1])) { + case 0: { process_G0(line); break; } // Move case 1: { process_G1(line); break; } // Move case 10: { process_G10(line); break; } // Retract case 11: { process_G11(line); break; } // Unretract @@ -263,6 +264,11 @@ void GCodeProcessor::process_tags(const std::string& comment) } } +void GCodeProcessor::process_G0(const GCodeReader::GCodeLine& line) +{ + process_G1(line); +} + void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) { auto absolute_position = [this](Axis axis, const GCodeReader::GCodeLine& lineG1) diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index e8c43350c0..bc49245848 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -155,6 +155,7 @@ namespace Slic3r { void process_tags(const std::string& comment); // Move + void process_G0(const GCodeReader::GCodeLine& line); void process_G1(const GCodeReader::GCodeLine& line); // Retract From 43e6e4f18c89257d2df547253fda600ed093d324 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 16 Jun 2020 12:57:49 +0200 Subject: [PATCH 128/503] Code refactoring: - PresetCombpBoxes are extracted to the separate file. - All preset icons are moved to the PresetComboBox from Preset and PresetBundle - First "steps" to add physical printers to the printers list on the sidebar. --- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/GUI_ObjectList.cpp | 3 - src/slic3r/GUI/MainFrame.cpp | 12 - src/slic3r/GUI/Plater.cpp | 212 ++------ src/slic3r/GUI/Plater.hpp | 41 +- src/slic3r/GUI/Preset.cpp | 410 ++++---------- src/slic3r/GUI/Preset.hpp | 228 ++++++-- src/slic3r/GUI/PresetBundle.cpp | 270 +--------- src/slic3r/GUI/PresetBundle.hpp | 22 - src/slic3r/GUI/PresetComboBoxes.cpp | 794 ++++++++++++++++++++++++++++ src/slic3r/GUI/PresetComboBoxes.hpp | 179 +++++++ src/slic3r/GUI/Tab.cpp | 46 +- src/slic3r/GUI/Tab.hpp | 7 +- src/slic3r/GUI/wxExtensions.cpp | 88 --- src/slic3r/GUI/wxExtensions.hpp | 31 -- 15 files changed, 1304 insertions(+), 1041 deletions(-) create mode 100644 src/slic3r/GUI/PresetComboBoxes.cpp create mode 100644 src/slic3r/GUI/PresetComboBoxes.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 7e02c0fdd7..98389e7dae 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -80,6 +80,8 @@ set(SLIC3R_GUI_SOURCES GUI/MainFrame.cpp GUI/MainFrame.hpp GUI/Plater.cpp + GUI/PresetComboBoxes.hpp + GUI/PresetComboBoxes.cpp GUI/Plater.hpp GUI/GUI_ObjectList.cpp GUI/GUI_ObjectList.hpp diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index c11ed66ab8..2f201180a8 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -88,9 +88,6 @@ ObjectList::ObjectList(wxWindow* parent) : { // Fill CATEGORY_ICON { - // Note: `this` isn't passed to create_scaled_bitmap() here because of bugs in the widget, - // see note in PresetBundle::load_compatible_bitmaps() - // ptFFF CATEGORY_ICON[L("Layers and Perimeters")] = create_scaled_bitmap("layers"); CATEGORY_ICON[L("Infill")] = create_scaled_bitmap("infill"); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index ef837c200d..d4ce21fc00 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -74,11 +74,6 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S SLIC3R_VERSION + _(L(" - Remember to check for updates at http://github.com/prusa3d/PrusaSlicer/releases"))); - /* Load default preset bitmaps before a tabpanel initialization, - * but after filling of an em_unit value - */ - wxGetApp().preset_bundle->load_default_preset_bitmaps(); - // initialize tabpanel and menubar init_tabpanel(); init_menubar(); @@ -540,11 +535,6 @@ void MainFrame::on_dpi_changed(const wxRect &suggested_rect) wxGetApp().update_fonts(); this->SetFont(this->normal_font()); - /* Load default preset bitmaps before a tabpanel initialization, - * but after filling of an em_unit value - */ - wxGetApp().preset_bundle->load_default_preset_bitmaps(); - // update Plater wxGetApp().plater()->msw_rescale(); @@ -586,8 +576,6 @@ void MainFrame::on_sys_color_changed() // update label colors in respect to the system mode wxGetApp().init_label_colours(); - wxGetApp().preset_bundle->load_default_preset_bitmaps(); - // update Plater wxGetApp().plater()->sys_color_changed(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 8c3a90370a..339badc96d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -77,6 +76,7 @@ #include "../Utils/UndoRedo.hpp" #include "RemovableDriveManager.hpp" #include "InstanceCheck.hpp" +#include "PresetComboBoxes.hpp" #ifdef __APPLE__ #include "Gizmos/GLGizmosManager.hpp" @@ -253,153 +253,6 @@ void SlicedInfo::SetTextAndShow(SlicedInfoIdx idx, const wxString& text, const w info_vec[idx].second->Show(show); } -PresetComboBox::PresetComboBox(wxWindow *parent, Preset::Type preset_type) : -PresetBitmapComboBox(parent, wxSize(15 * wxGetApp().em_unit(), -1)), - preset_type(preset_type), - last_selected(wxNOT_FOUND), - m_em_unit(wxGetApp().em_unit()) -{ - SetFont(wxGetApp().normal_font()); -#ifdef _WIN32 - // Workaround for ignoring CBN_EDITCHANGE events, which are processed after the content of the combo box changes, so that - // the index of the item inside CBN_EDITCHANGE may no more be valid. - EnableTextChangedEvents(false); -#endif /* _WIN32 */ - Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &evt) { - auto selected_item = evt.GetSelection(); - - auto marker = reinterpret_cast(this->GetClientData(selected_item)); - if (marker >= LABEL_ITEM_MARKER && marker < LABEL_ITEM_MAX) { - this->SetSelection(this->last_selected); - evt.StopPropagation(); - if (marker >= LABEL_ITEM_WIZARD_PRINTERS) { - ConfigWizard::StartPage sp = ConfigWizard::SP_WELCOME; - switch (marker) { - case LABEL_ITEM_WIZARD_PRINTERS: sp = ConfigWizard::SP_PRINTERS; break; - case LABEL_ITEM_WIZARD_FILAMENTS: sp = ConfigWizard::SP_FILAMENTS; break; - case LABEL_ITEM_WIZARD_MATERIALS: sp = ConfigWizard::SP_MATERIALS; break; - } - wxTheApp->CallAfter([sp]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, sp); }); - } - } else if ( this->last_selected != selected_item || - wxGetApp().get_tab(this->preset_type)->get_presets()->current_is_dirty() ) { - this->last_selected = selected_item; - evt.SetInt(this->preset_type); - evt.Skip(); - } else { - evt.StopPropagation(); - } - }); - - if (preset_type == Slic3r::Preset::TYPE_FILAMENT) - { - Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &event) { - PresetBundle* preset_bundle = wxGetApp().preset_bundle; - const Preset* selected_preset = preset_bundle->filaments.find_preset(preset_bundle->filament_presets[extruder_idx]); - // Wide icons are shown if the currently selected preset is not compatible with the current printer, - // and red flag is drown in front of the selected preset. - bool wide_icons = selected_preset != nullptr && !selected_preset->is_compatible; - float scale = m_em_unit*0.1f; - - int shifl_Left = wide_icons ? int(scale * 16 + 0.5) : 0; -#if defined(wxBITMAPCOMBOBOX_OWNERDRAWN_BASED) - shifl_Left += int(scale * 4 + 0.5f); // IMAGE_SPACING_RIGHT = 4 for wxBitmapComboBox -> Space left of image -#endif - int icon_right_pos = shifl_Left + int(scale * (24+4) + 0.5); - int mouse_pos = event.GetLogicalPosition(wxClientDC(this)).x; - if (mouse_pos < shifl_Left || mouse_pos > icon_right_pos ) { - // Let the combo box process the mouse click. - event.Skip(); - return; - } - - // Swallow the mouse click and open the color picker. - - // get current color - DynamicPrintConfig* cfg = wxGetApp().get_tab(Preset::TYPE_PRINTER)->get_config(); - auto colors = static_cast(cfg->option("extruder_colour")->clone()); - wxColour clr(colors->values[extruder_idx]); - if (!clr.IsOk()) - clr = wxColour(0,0,0); // Don't set alfa to transparence - - auto data = new wxColourData(); - data->SetChooseFull(1); - data->SetColour(clr); - - wxColourDialog dialog(this, data); - dialog.CenterOnParent(); - if (dialog.ShowModal() == wxID_OK) - { - colors->values[extruder_idx] = dialog.GetColourData().GetColour().GetAsString(wxC2S_HTML_SYNTAX).ToStdString(); - - DynamicPrintConfig cfg_new = *cfg; - cfg_new.set_key_value("extruder_colour", colors); - - wxGetApp().get_tab(Preset::TYPE_PRINTER)->load_config(cfg_new); - preset_bundle->update_plater_filament_ui(extruder_idx, this); - wxGetApp().plater()->on_config_change(cfg_new); - } - }); - } - - edit_btn = new ScalableButton(parent, wxID_ANY, "cog"); - edit_btn->SetToolTip(_L("Click to edit preset")); - - edit_btn->Bind(wxEVT_BUTTON, ([preset_type, this](wxCommandEvent) - { - Tab* tab = wxGetApp().get_tab(preset_type); - if (!tab) - return; - - int page_id = wxGetApp().tab_panel()->FindPage(tab); - if (page_id == wxNOT_FOUND) - return; - - wxGetApp().tab_panel()->SetSelection(page_id); - - // Switch to Settings NotePad - wxGetApp().mainframe->select_tab(); - - /* In a case of a multi-material printing, for editing another Filament Preset - * it's needed to select this preset for the "Filament settings" Tab - */ - if (preset_type == Preset::TYPE_FILAMENT && wxGetApp().extruders_edited_cnt() > 1) - { - const std::string& selected_preset = GetString(GetSelection()).ToUTF8().data(); - - // Call select_preset() only if there is new preset and not just modified - if ( !boost::algorithm::ends_with(selected_preset, Preset::suffix_modified()) ) - { - const std::string& preset_name = wxGetApp().preset_bundle->filaments.get_preset_name_by_alias(selected_preset); - tab->select_preset(/*selected_preset*/preset_name); - } - } - })); -} - -PresetComboBox::~PresetComboBox() -{ - if (edit_btn) - edit_btn->Destroy(); -} - - -void PresetComboBox::set_label_marker(int item, LabelItemType label_item_type) -{ - this->SetClientData(item, (void*)label_item_type); -} - -void PresetComboBox::check_selection(int selection) -{ - this->last_selected = selection; -} - -void PresetComboBox::msw_rescale() -{ - m_em_unit = wxGetApp().em_unit(); - edit_btn->msw_rescale(); -} - // Frequently changed parameters class FreqChangedParams : public OG_Settings @@ -697,12 +550,12 @@ struct Sidebar::priv ModeSizer *mode_sizer; wxFlexGridSizer *sizer_presets; - PresetComboBox *combo_print; - std::vector combos_filament; + PlaterPresetComboBox *combo_print; + std::vector combos_filament; wxBoxSizer *sizer_filaments; - PresetComboBox *combo_sla_print; - PresetComboBox *combo_sla_material; - PresetComboBox *combo_printer; + PlaterPresetComboBox *combo_sla_print; + PlaterPresetComboBox *combo_sla_material; + PlaterPresetComboBox *combo_printer; wxBoxSizer *sizer_params; FreqChangedParams *frequently_changed_parameters{ nullptr }; @@ -801,10 +654,10 @@ Sidebar::Sidebar(Plater *parent) p->sizer_filaments = new wxBoxSizer(wxVERTICAL); - auto init_combo = [this](PresetComboBox **combo, wxString label, Preset::Type preset_type, bool filament) { + auto init_combo = [this](PlaterPresetComboBox **combo, wxString label, Preset::Type preset_type, bool filament) { auto *text = new wxStaticText(p->presets_panel, wxID_ANY, label + " :"); text->SetFont(wxGetApp().small_font()); - *combo = new PresetComboBox(p->presets_panel, preset_type); + *combo = new PlaterPresetComboBox(p->presets_panel, preset_type); auto combo_and_btn_sizer = new wxBoxSizer(wxHORIZONTAL); combo_and_btn_sizer->Add(*combo, 1, wxEXPAND); @@ -941,8 +794,8 @@ Sidebar::Sidebar(Plater *parent) Sidebar::~Sidebar() {} -void Sidebar::init_filament_combo(PresetComboBox **combo, const int extr_idx) { - *combo = new PresetComboBox(p->presets_panel, Slic3r::Preset::TYPE_FILAMENT); +void Sidebar::init_filament_combo(PlaterPresetComboBox **combo, const int extr_idx) { + *combo = new PlaterPresetComboBox(p->presets_panel, Slic3r::Preset::TYPE_FILAMENT); // # copy icons from first choice // $choice->SetItemBitmap($_, $choices->[0]->GetItemBitmap($_)) for 0..$#presets; @@ -977,18 +830,18 @@ void Sidebar::update_all_preset_comboboxes() // Update the print choosers to only contain the compatible presets, update the dirty flags. if (print_tech == ptFFF) - preset_bundle.prints.update_plater_ui(p->combo_print); + p->combo_print->update(); else { - preset_bundle.sla_prints.update_plater_ui(p->combo_sla_print); - preset_bundle.sla_materials.update_plater_ui(p->combo_sla_material); + p->combo_sla_print->update(); + p->combo_sla_material->update(); } // Update the printer choosers, update the dirty flags. - preset_bundle.printers.update_plater_ui(p->combo_printer); + p->combo_printer->update(); // Update the filament choosers to only contain the compatible presets, update the color preview, // update the dirty flags. if (print_tech == ptFFF) { - for (size_t i = 0; i < p->combos_filament.size(); ++i) - preset_bundle.update_plater_filament_ui(i, p->combos_filament[i]); + for (PlaterPresetComboBox* cb : p->combos_filament) + cb->update(); } } @@ -1010,23 +863,22 @@ void Sidebar::update_presets(Preset::Type preset_type) preset_bundle.set_filament_preset(0, name); } - for (size_t i = 0; i < filament_cnt; i++) { - preset_bundle.update_plater_filament_ui(i, p->combos_filament[i]); - } + for (size_t i = 0; i < filament_cnt; i++) + p->combos_filament[i]->update(); break; } case Preset::TYPE_PRINT: - preset_bundle.prints.update_plater_ui(p->combo_print); + p->combo_print->update(); break; case Preset::TYPE_SLA_PRINT: - preset_bundle.sla_prints.update_plater_ui(p->combo_sla_print); + p->combo_sla_print->update(); break; case Preset::TYPE_SLA_MATERIAL: - preset_bundle.sla_materials.update_plater_ui(p->combo_sla_material); + p->combo_sla_material->update(); break; case Preset::TYPE_PRINTER: @@ -1062,18 +914,14 @@ void Sidebar::msw_rescale() p->mode_sizer->msw_rescale(); - // Rescale preset comboboxes in respect to the current em_unit ... - for (PresetComboBox* combo : std::vector { p->combo_print, + for (PlaterPresetComboBox* combo : std::vector { p->combo_print, p->combo_sla_print, p->combo_sla_material, p->combo_printer } ) combo->msw_rescale(); - for (PresetComboBox* combo : p->combos_filament) + for (PlaterPresetComboBox* combo : p->combos_filament) combo->msw_rescale(); - // ... then refill them and set min size to correct layout of the sidebar - update_all_preset_comboboxes(); - p->frequently_changed_parameters->msw_rescale(); p->object_list->msw_rescale(); p->object_manipulation->msw_rescale(); @@ -1096,12 +944,12 @@ void Sidebar::sys_color_changed() { // Update preset comboboxes in respect to the system color ... // combo->msw_rescale() updates icon on button, so use it - for (PresetComboBox* combo : std::vector{ p->combo_print, + for (PlaterPresetComboBox* combo : std::vector{ p->combo_print, p->combo_sla_print, p->combo_sla_material, p->combo_printer }) combo->msw_rescale(); - for (PresetComboBox* combo : p->combos_filament) + for (PlaterPresetComboBox* combo : p->combos_filament) combo->msw_rescale(); // ... then refill them and set min size to correct layout of the sidebar @@ -1458,7 +1306,7 @@ void Sidebar::update_ui_from_settings() update_sliced_info_sizer(); } -std::vector& Sidebar::combos_filament() +std::vector& Sidebar::combos_filament() { return p->combos_filament; } @@ -3339,7 +3187,7 @@ void Plater::priv::set_current_panel(wxPanel* panel) void Plater::priv::on_select_preset(wxCommandEvent &evt) { auto preset_type = static_cast(evt.GetInt()); - auto *combo = static_cast(evt.GetEventObject()); + auto *combo = static_cast(evt.GetEventObject()); // see https://github.com/prusa3d/PrusaSlicer/issues/3889 // Under OSX: in case of use of a same names written in different case (like "ENDER" and "Ender"), @@ -3368,7 +3216,7 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt) // TODO: ? if (preset_type == Preset::TYPE_FILAMENT && sidebar->is_multifilament()) { // Only update the plater UI for the 2nd and other filaments. - wxGetApp().preset_bundle->update_plater_filament_ui(idx, combo); + combo->update(); } else { wxWindowUpdateLocker noUpdates(sidebar->presets_panel()); @@ -5154,12 +5002,12 @@ void Plater::on_extruders_change(size_t num_extruders) size_t i = choices.size(); while ( i < num_extruders ) { - PresetComboBox* choice/*{ nullptr }*/; + PlaterPresetComboBox* choice/*{ nullptr }*/; sidebar().init_filament_combo(&choice, i); choices.push_back(choice); // initialize selection - wxGetApp().preset_bundle->update_plater_filament_ui(i, choice); + choice->update(); ++i; } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 5d60e006b0..d2acc7632e 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -6,14 +6,12 @@ #include #include -#include #include "Preset.hpp" #include "Selection.hpp" #include "libslic3r/BoundingBox.hpp" #include "Jobs/Job.hpp" -#include "wxExtensions.hpp" #include "Search.hpp" class wxButton; @@ -50,46 +48,13 @@ class Mouse3DController; struct Camera; class Bed3D; class GLToolbar; +class PlaterPresetComboBox; using t_optgroups = std::vector >; class Plater; enum class ActionButtonType : int; -class PresetComboBox : public PresetBitmapComboBox -{ -public: - PresetComboBox(wxWindow *parent, Preset::Type preset_type); - ~PresetComboBox(); - - ScalableButton* edit_btn { nullptr }; - - enum LabelItemType { - LABEL_ITEM_MARKER = 0xffffff01, - LABEL_ITEM_WIZARD_PRINTERS, - LABEL_ITEM_WIZARD_FILAMENTS, - LABEL_ITEM_WIZARD_MATERIALS, - - LABEL_ITEM_MAX, - }; - - void set_label_marker(int item, LabelItemType label_item_type = LABEL_ITEM_MARKER); - void set_extruder_idx(const int extr_idx) { extruder_idx = extr_idx; } - int get_extruder_idx() const { return extruder_idx; } - int em_unit() const { return m_em_unit; } - void check_selection(int selection); - - void msw_rescale(); - -private: - typedef std::size_t Marker; - - Preset::Type preset_type; - int last_selected; - int extruder_idx = -1; - int m_em_unit; -}; - class Sidebar : public wxPanel { ConfigOptionMode m_mode; @@ -101,7 +66,7 @@ public: Sidebar &operator=(const Sidebar &) = delete; ~Sidebar(); - void init_filament_combo(PresetComboBox **combo, const int extr_idx); + void init_filament_combo(PlaterPresetComboBox **combo, const int extr_idx); void remove_unused_filament_combos(const size_t current_extruder_count); void update_all_preset_comboboxes(); void update_presets(Slic3r::Preset::Type preset_type); @@ -139,7 +104,7 @@ public: void update_searcher(); void update_ui_from_settings(); - std::vector& combos_filament(); + std::vector& combos_filament(); Search::OptionsSearcher& get_searcher(); std::string& get_search_line(); diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index d810c399d5..883dc438ab 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -2,9 +2,7 @@ #include "Preset.hpp" #include "AppConfig.hpp" -#include "BitmapCache.hpp" #include "I18N.hpp" -#include "wxExtensions.hpp" #ifdef _MSC_VER #define WIN32_LEAN_AND_MEAN @@ -30,15 +28,9 @@ #include #include -#include -#include -#include -#include - #include "libslic3r/libslic3r.h" #include "libslic3r/Utils.hpp" #include "libslic3r/PlaceholderParser.hpp" -#include "Plater.hpp" using boost::property_tree::ptree; @@ -590,10 +582,7 @@ const std::vector& Preset::sla_printer_options() PresetCollection::PresetCollection(Preset::Type type, const std::vector &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name) : m_type(type), m_edited_preset(type, "", false), - m_idx_selected(0), - m_bitmap_main_frame(new wxBitmap), - m_bitmap_add(new wxBitmap), - m_bitmap_cache(new GUI::BitmapCache) + m_idx_selected(0) { // Insert just the default preset. this->add_default_preset(keys, defaults, default_name); @@ -602,12 +591,6 @@ PresetCollection::PresetCollection(Preset::Type type, const std::vectorget_selected_idx() == size_t(-1)) @@ -1119,279 +1092,15 @@ size_t PresetCollection::update_compatible_internal(const PresetWithVendorProfil // Delete the current preset, activate the first visible preset. //void PresetCollection::delete_current_preset(); -// Update the wxChoice UI component from this list of presets. -// Hide the -void PresetCollection::update_plater_ui(GUI::PresetComboBox *ui) -{ - if (ui == nullptr) - return; - - // Otherwise fill in the list from scratch. - ui->Freeze(); - ui->Clear(); - size_t selected_preset_item = INT_MAX; // some value meaning that no one item is selected - - const Preset &selected_preset = this->get_selected_preset(); - // Show wide icons if the currently selected preset is not compatible with the current printer, - // and draw a red flag in front of the selected preset. - bool wide_icons = ! selected_preset.is_compatible && m_bitmap_incompatible != nullptr; - - /* It's supposed that standard size of an icon is 16px*16px for 100% scaled display. - * So set sizes for solid_colored icons used for filament preset - * and scale them in respect to em_unit value - */ - const float scale_f = ui->em_unit() * 0.1f; - const int icon_height = 16 * scale_f + 0.5f; - const int icon_width = 16 * scale_f + 0.5f; - const int thin_space_icon_width = 4 * scale_f + 0.5f; - const int wide_space_icon_width = 6 * scale_f + 0.5f; - - std::map nonsys_presets; - wxString selected = ""; - wxString tooltip = ""; - if (!this->m_presets.front().is_visible) - ui->set_label_marker(ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap)); - for (size_t i = this->m_presets.front().is_visible ? 0 : m_num_default_presets; i < this->m_presets.size(); ++ i) { - const Preset &preset = this->m_presets[i]; - if (! preset.is_visible || (! preset.is_compatible && i != m_idx_selected)) - continue; - - std::string bitmap_key = ""; - // !!! Temporary solution, till refactoring: create and use "sla_printer" icon instead of m_bitmap_main_frame - wxBitmap main_bmp = m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap; - if (m_type == Preset::TYPE_PRINTER && preset.printer_technology()==ptSLA ) { - bitmap_key = "sla_printer"; - main_bmp = create_scaled_bitmap("sla_printer"); - } - - // If the filament preset is not compatible and there is a "red flag" icon loaded, show it left - // to the filament color image. - if (wide_icons) - bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt"; - bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst"; - wxBitmap *bmp = m_bitmap_cache->find(bitmap_key); - if (bmp == nullptr) { - // Create the bitmap with color bars. - std::vector bmps; - if (wide_icons) - // Paint a red flag for incompatible presets. - bmps.emplace_back(preset.is_compatible ? m_bitmap_cache->mkclear(icon_width, icon_height) : *m_bitmap_incompatible); - // Paint the color bars. - bmps.emplace_back(m_bitmap_cache->mkclear(thin_space_icon_width, icon_height)); - bmps.emplace_back(main_bmp); - // Paint a lock at the system presets. - bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width, icon_height)); - bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmap_lock : m_bitmap_cache->mkclear(icon_width, icon_height)); - bmp = m_bitmap_cache->insert(bitmap_key, bmps); - } - - const std::string name = preset.alias.empty() ? preset.name : preset.alias; - if (preset.is_default || preset.is_system) { - ui->Append(wxString::FromUTF8((/*preset.*/name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), - (bmp == 0) ? main_bmp : *bmp); - if (i == m_idx_selected || - // just in case: mark selected_preset_item as a first added element - selected_preset_item == INT_MAX) { - selected_preset_item = ui->GetCount() - 1; - tooltip = wxString::FromUTF8(preset.name.c_str()); - } - } - else - { - nonsys_presets.emplace(wxString::FromUTF8((/*preset.*/name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), bmp/*preset.is_compatible*/); - if (i == m_idx_selected) { - selected = wxString::FromUTF8((/*preset.*/name + (preset.is_dirty ? g_suffix_modified : "")).c_str()); - tooltip = wxString::FromUTF8(preset.name.c_str()); - } - } - if (i + 1 == m_num_default_presets) - ui->set_label_marker(ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap)); - } - if (!nonsys_presets.empty()) - { - ui->set_label_marker(ui->Append(PresetCollection::separator(L("User presets")), wxNullBitmap)); - for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { - ui->Append(it->first, *it->second); - if (it->first == selected || - // just in case: mark selected_preset_item as a first added element - selected_preset_item == INT_MAX) - selected_preset_item = ui->GetCount() - 1; - } - } - if (m_type == Preset::TYPE_PRINTER || m_type == Preset::TYPE_SLA_MATERIAL) { - std::string bitmap_key = ""; - // If the filament preset is not compatible and there is a "red flag" icon loaded, show it left - // to the filament color image. - if (wide_icons) - bitmap_key += "wide,"; - bitmap_key += "edit_preset_list"; - wxBitmap *bmp = m_bitmap_cache->find(bitmap_key); - if (bmp == nullptr) { - // Create the bitmap with color bars. - std::vector bmps; - if (wide_icons) - // Paint a red flag for incompatible presets. - bmps.emplace_back(m_bitmap_cache->mkclear(icon_width, icon_height)); - // Paint the color bars. - bmps.emplace_back(m_bitmap_cache->mkclear(thin_space_icon_width, icon_height)); - bmps.emplace_back(*m_bitmap_main_frame); - // Paint a lock at the system presets. - bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width, icon_height)); -// bmps.emplace_back(m_bitmap_add ? *m_bitmap_add : wxNullBitmap); - bmps.emplace_back(create_scaled_bitmap("edit_uni")); - bmp = m_bitmap_cache->insert(bitmap_key, bmps); - } - if (m_type == Preset::TYPE_SLA_MATERIAL) - ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add/Remove materials")), *bmp), GUI::PresetComboBox::LABEL_ITEM_WIZARD_MATERIALS); - else - ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add/Remove printers")), *bmp), GUI::PresetComboBox::LABEL_ITEM_WIZARD_PRINTERS); - } - - /* But, if selected_preset_item is still equal to INT_MAX, it means that - * there is no presets added to the list. - * So, select last combobox item ("Add/Remove preset") - */ - if (selected_preset_item == INT_MAX) - selected_preset_item = ui->GetCount() - 1; - - ui->SetSelection(selected_preset_item); - ui->SetToolTip(tooltip.IsEmpty() ? ui->GetString(selected_preset_item) : tooltip); - ui->check_selection(selected_preset_item); - ui->Thaw(); - - // Update control min size after rescale (changed Display DPI under MSW) - if (ui->GetMinWidth() != 20 * ui->em_unit()) - ui->SetMinSize(wxSize(20 * ui->em_unit(), ui->GetSize().GetHeight())); -} - -size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible, const int em/* = 10*/) -{ - if (ui == nullptr) - return 0; - ui->Freeze(); - ui->Clear(); - size_t selected_preset_item = INT_MAX; // some value meaning that no one item is selected - - /* It's supposed that standard size of an icon is 16px*16px for 100% scaled display. - * So set sizes for solid_colored(empty) icons used for preset - * and scale them in respect to em_unit value - */ - const float scale_f = em * 0.1f; - const int icon_height = 16 * scale_f + 0.5f; - const int icon_width = 16 * scale_f + 0.5f; - - std::map nonsys_presets; - wxString selected = ""; - if (!this->m_presets.front().is_visible) - ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap); - for (size_t i = this->m_presets.front().is_visible ? 0 : m_num_default_presets; i < this->m_presets.size(); ++i) { - const Preset &preset = this->m_presets[i]; - if (! preset.is_visible || (! show_incompatible && ! preset.is_compatible && i != m_idx_selected)) - continue; - std::string bitmap_key = "tab"; - - // !!! Temporary solution, till refactoring: create and use "sla_printer" icon instead of m_bitmap_main_frame - wxBitmap main_bmp = m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap; - if (m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA) { - bitmap_key = "sla_printer"; - main_bmp = create_scaled_bitmap("sla_printer"); - } - - bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt"; - bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst"; - wxBitmap *bmp = m_bitmap_cache->find(bitmap_key); - if (bmp == nullptr) { - // Create the bitmap with color bars. - std::vector bmps; - const wxBitmap* tmp_bmp = preset.is_compatible ? m_bitmap_compatible : m_bitmap_incompatible; - bmps.emplace_back((tmp_bmp == 0) ? main_bmp : *tmp_bmp); - // Paint a lock at the system presets. - bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmap_lock : m_bitmap_cache->mkclear(icon_width, icon_height)); - bmp = m_bitmap_cache->insert(bitmap_key, bmps); - } - - if (preset.is_default || preset.is_system) { - ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), - (bmp == 0) ? main_bmp : *bmp); - if (i == m_idx_selected || - // just in case: mark selected_preset_item as a first added element - selected_preset_item == INT_MAX) - selected_preset_item = ui->GetCount() - 1; - } - else - { - nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), bmp/*preset.is_compatible*/); - if (i == m_idx_selected) - selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()); - } - if (i + 1 == m_num_default_presets) - ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap); - } - if (!nonsys_presets.empty()) - { - ui->Append(PresetCollection::separator(L("User presets")), wxNullBitmap); - for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { - ui->Append(it->first, *it->second); - if (it->first == selected || - // just in case: mark selected_preset_item as a first added element - selected_preset_item == INT_MAX) - selected_preset_item = ui->GetCount() - 1; - } - } - if (m_type == Preset::TYPE_PRINTER) { - wxBitmap *bmp = m_bitmap_cache->find("edit_printer_list"); - if (bmp == nullptr) { - // Create the bitmap with color bars. - std::vector bmps; - bmps.emplace_back(*m_bitmap_main_frame); -// bmps.emplace_back(m_bitmap_add ? *m_bitmap_add : wxNullBitmap); - bmps.emplace_back(create_scaled_bitmap("edit_uni")); - bmp = m_bitmap_cache->insert("add_printer_tab", bmps); - } - ui->Append(PresetCollection::separator("Add a new printer"), *bmp); - } - - /* But, if selected_preset_item is still equal to INT_MAX, it means that - * there is no presets added to the list. - * So, select last combobox item ("Add/Remove preset") - */ - if (selected_preset_item == INT_MAX) - selected_preset_item = ui->GetCount() - 1; - - ui->SetSelection(selected_preset_item); - ui->SetToolTip(ui->GetString(selected_preset_item)); - ui->Thaw(); - return selected_preset_item; -} - -// Update a dirty floag of the current preset, update the labels of the UI component accordingly. +// Update a dirty flag of the current preset // Return true if the dirty flag changed. -bool PresetCollection::update_dirty_ui(wxBitmapComboBox *ui) +bool PresetCollection::update_dirty() { - wxWindowUpdateLocker noUpdates(ui); - // 1) Update the dirty flag of the current preset. bool was_dirty = this->get_selected_preset().is_dirty; bool is_dirty = current_is_dirty(); this->get_selected_preset().is_dirty = is_dirty; this->get_edited_preset().is_dirty = is_dirty; - // 2) Update the labels. - for (unsigned int ui_id = 0; ui_id < ui->GetCount(); ++ ui_id) { - std::string old_label = ui->GetString(ui_id).utf8_str().data(); - std::string preset_name = Preset::remove_suffix_modified(old_label); - const Preset *preset = this->find_preset(preset_name, false); -// The old_label could be the "----- system presets ------" or the "------- user presets --------" separator. -// assert(preset != nullptr); - if (preset != nullptr) { - std::string new_label = preset->is_dirty ? preset->name + g_suffix_modified : preset->name; - if (old_label != new_label) - ui->SetString(ui_id, wxString::FromUTF8(new_label.c_str())); - } - } -#ifdef __APPLE__ - // wxWidgets on OSX do not upload the text of the combo box line automatically. - // Force it to update by re-selecting. - ui->SetSelection(ui->GetSelection()); -#endif /* __APPLE __ */ + return was_dirty != is_dirty; } @@ -1605,16 +1314,6 @@ std::string PresetCollection::path_from_name(const std::string &new_name) const return (boost::filesystem::path(m_dir_path) / file_name).make_preferred().string(); } -void PresetCollection::clear_bitmap_cache() -{ - m_bitmap_cache->clear(); -} - -wxString PresetCollection::separator(const std::string &label) -{ - return wxString::FromUTF8(PresetCollection::separator_head()) + _(label) + wxString::FromUTF8(PresetCollection::separator_tail()); -} - const Preset& PrinterPresetCollection::default_preset_for(const DynamicPrintConfig &config) const { const ConfigOptionEnumGeneric *opt_printer_technology = config.opt("printer_technology"); @@ -1631,7 +1330,108 @@ const Preset* PrinterPresetCollection::find_by_model_id(const std::string &model return it != cend() ? &*it : nullptr; } +/* +PhysicalPrinter& PhysicalPrinterCollection::load_external_printer( + // Path to the profile source file (a G-code, an AMF or 3MF file, a config file) + const std::string& path, + // Name of the profile, derived from the source file name. + const std::string& name, + // Original name of the profile, extracted from the loaded config. Empty, if the name has not been stored. + const std::string& original_name, + // Config to initialize the preset from. + const DynamicPrintConfig& config, + // Select the preset after loading? + bool select) +{ + // Load the preset over a default preset, so that the missing fields are filled in from the default preset. + DynamicPrintConfig cfg(this->default_printer().config); + cfg.apply_only(config, cfg.keys(), true); + // Is there a preset already loaded with the name stored inside the config? + std::deque::iterator it = this->find_printer_internal(original_name); + bool found = it != m_printers.end() && it->name == original_name; + if (!found) { + // Try to match the original_name against the "renamed_from" profile names of loaded system profiles. + / * + it = this->find_preset_renamed(original_name); + found = it != m_presets.end(); + * / + } + if (found) { + if (profile_print_params_same(it->config, cfg)) { + // The preset exists and it matches the values stored inside config. + if (select) + this->select_printer(it - m_printers.begin()); + return *it; + } + if (profile_host_params_same_or_anonymized(it->config, cfg) == ProfileHostParams::Anonymized) { + // The project being loaded is anonymized. Replace the empty host keys of the loaded profile with the data from the original profile. + // See "Octoprint Settings when Opening a .3MF file" GH issue #3244 + auto opt_update = [it, &cfg](const std::string& opt_key) { + auto opt = it->config.option(opt_key); + if (opt != nullptr) + cfg.set_key_value(opt_key, opt->clone()); + }; + opt_update("print_host"); + opt_update("printhost_apikey"); + opt_update("printhost_cafile"); + } + } + // The external preset does not match an internal preset, load the external preset. + std::string new_name; + for (size_t idx = 0;; ++idx) { + std::string suffix; + if (original_name.empty()) { + if (idx > 0) + suffix = " (" + std::to_string(idx) + ")"; + } + else { + if (idx == 0) + suffix = " (" + original_name + ")"; + else + suffix = " (" + original_name + "-" + std::to_string(idx) + ")"; + } + new_name = name + suffix; + it = this->find_printer_internal(new_name); + if (it == m_printers.end() || it->name != new_name) + // Unique profile name. Insert a new profile. + break; + if (profile_print_params_same(it->config, cfg)) { + // The preset exists and it matches the values stored inside config. + if (select) + this->select_printer(it - m_printers.begin()); + return *it; + } + // Form another profile name. + } + // Insert a new profile. + PhysicalPrinter& printer = this->load_printer(path, new_name, std::move(cfg), select); + return printer; +} + +void PhysicalPrinterCollection::save_printer(const std::string& new_name) +{ + // 1) Find the printer with a new_name or create a new one, + // initialize it with the edited config. + auto it = this->find_printer_internal(new_name); + if (it != m_printers.end() && it->name == new_name) { + // Preset with the same name found. + PhysicalPrinter& printer = *it; + // Overwriting an existing preset. + printer.config = std::move(m_edited_printer.config); + } + else { + // Creating a new printer. + PhysicalPrinter& printer = *m_printers.insert(it, m_edited_printer); + std::string old_name = printer.name; + printer.name = new_name; + } + // 2) Activate the saved preset. + this->select_printer_by_name(new_name, true); + // 3) Store the active preset to disk. + this->get_selected_preset().save(); +} +*/ namespace PresetUtils { const VendorProfile::PrinterModel* system_printer_model(const Preset &preset) { diff --git a/src/slic3r/GUI/Preset.hpp b/src/slic3r/GUI/Preset.hpp index dc00780918..8a8fa024b6 100644 --- a/src/slic3r/GUI/Preset.hpp +++ b/src/slic3r/GUI/Preset.hpp @@ -24,11 +24,6 @@ namespace Slic3r { class AppConfig; class PresetBundle; -namespace GUI { - class BitmapCache; - class PresetComboBox; -} - enum ConfigFileType { CONFIG_FILE_TYPE_UNKNOWN, @@ -322,18 +317,6 @@ public: // returns true if the preset was deleted successfully. bool delete_preset(const std::string& name); - // Load default bitmap to be placed at the wxBitmapComboBox of a MainFrame. - void load_bitmap_default(const std::string &file_name); - - // Load "add new printer" bitmap to be placed at the wxBitmapComboBox of a MainFrame. - void load_bitmap_add(const std::string &file_name); - - // Compatible & incompatible marks, to be placed at the wxBitmapComboBox items. - void set_bitmap_compatible (const wxBitmap *bmp) { m_bitmap_compatible = bmp; } - void set_bitmap_incompatible(const wxBitmap *bmp) { m_bitmap_incompatible = bmp; } - void set_bitmap_lock (const wxBitmap *bmp) { m_bitmap_lock = bmp; } - void set_bitmap_lock_open (const wxBitmap *bmp) { m_bitmap_lock_open = bmp; } - // Enable / disable the "- default -" preset. void set_default_suppressed(bool default_suppressed); bool is_default_suppressed() const { return m_default_suppressed; } @@ -446,18 +429,9 @@ public: // Return a sorted list of system preset names. std::vector system_preset_names() const; - // Update the choice UI from the list of presets. - // If show_incompatible, all presets are shown, otherwise only the compatible presets are shown. - // If an incompatible preset is selected, it is shown as well. - size_t update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible, const int em = 10); - // Update the choice UI from the list of presets. - // Only the compatible presets are shown. - // If an incompatible preset is selected, it is shown as well. - void update_plater_ui(GUI::PresetComboBox *ui); - - // Update a dirty floag of the current preset, update the labels of the UI component accordingly. + // Update a dirty flag of the current preset // Return true if the dirty flag changed. - bool update_dirty_ui(wxBitmapComboBox *ui); + bool update_dirty(); // Select a profile by its name. Return true if the selection changed. // Without force, the selection is only updated if the index changes. @@ -467,16 +441,7 @@ public: // Generate a file path from a profile name. Add the ".ini" suffix if it is missing. std::string path_from_name(const std::string &new_name) const; - void clear_bitmap_cache(); - -#ifdef __linux__ - static const char* separator_head() { return "------- "; } - static const char* separator_tail() { return " -------"; } -#else /* __linux__ */ - static const char* separator_head() { return "————— "; } - static const char* separator_tail() { return " —————"; } -#endif /* __linux__ */ - static wxString separator(const std::string &label); + size_t num_default_presets() { return m_num_default_presets; } protected: // Select a preset, if it exists. If it does not exist, select an invalid (-1) index. @@ -547,23 +512,10 @@ private: // Is the "- default -" preset suppressed? bool m_default_suppressed = true; size_t m_num_default_presets = 0; - // Compatible & incompatible marks, to be placed at the wxBitmapComboBox items of a Plater. - // These bitmaps are not owned by PresetCollection, but by a PresetBundle. - const wxBitmap *m_bitmap_compatible = nullptr; - const wxBitmap *m_bitmap_incompatible = nullptr; - const wxBitmap *m_bitmap_lock = nullptr; - const wxBitmap *m_bitmap_lock_open = nullptr; - // Marks placed at the wxBitmapComboBox of a MainFrame. - // These bitmaps are owned by PresetCollection. - wxBitmap *m_bitmap_main_frame; - // "Add printer profile" icon, owned by PresetCollection. - wxBitmap *m_bitmap_add; + // Path to the directory to store the config files into. std::string m_dir_path; - // Caching color bitmaps for the filament combo box. - GUI::BitmapCache *m_bitmap_cache = nullptr; - // to access select_preset_by_name_strict() friend class PresetBundle; }; @@ -585,6 +537,178 @@ namespace PresetUtils { const VendorProfile::PrinterModel* system_printer_model(const Preset &preset); } // namespace PresetUtils + +////////////////////////////////////////////////////////////////////// + +class PhysicalPrinter +{ +public: + PhysicalPrinter(const std::string& name) : name(name) {} + + // Name of the Physical Printer, usually derived form the file name. + std::string name; + // File name of the Physical Printer. + std::string file; + // Name of the related Printer preset + std::string preset_name; + + // Has this profile been loaded? + bool loaded = false; + + // Configuration data, loaded from a file, or set from the defaults. + DynamicPrintConfig config; + + void save() { this->config.save(this->file); } + + // Return a printer technology, return ptFFF if the printer technology is not set. + static PrinterTechnology printer_technology(const DynamicPrintConfig& cfg) { + auto* opt = cfg.option>("printer_technology"); + // The following assert may trigger when importing some legacy profile, + // but it is safer to keep it here to capture the cases where the "printer_technology" key is queried, where it should not. + return (opt == nullptr) ? ptFFF : opt->value; + } + PrinterTechnology printer_technology() const { return printer_technology(this->config); } + + // Sort lexicographically by a preset name. The preset name shall be unique across a single PresetCollection. + bool operator<(const Preset& other) const { return this->name < other.name; } + +protected: + friend class PhysicalPrinterCollection; +}; +/* +// Collections of presets of the same type (one of the Print, Filament or Printer type). +class PhysicalPrinterCollection +{ +public: + // Initialize the PresetCollection with the "- default -" preset. + PhysicalPrinterCollection(const std::vector& keys) : m_idx_selected(0) {} + ~PhysicalPrinterCollection() {} + + typedef std::deque::iterator Iterator; + typedef std::deque::const_iterator ConstIterator; + Iterator begin() { return m_printers.begin(); } + ConstIterator begin() const { return m_printers.cbegin(); } + ConstIterator cbegin() const { return m_printers.cbegin(); } + Iterator end() { return m_printers.end(); } + ConstIterator end() const { return m_printers.cend(); } + ConstIterator cend() const { return m_printers.cend(); } + + void reset(bool delete_files) {}; + + const std::deque& operator()() const { return m_printers; } + + // Load ini files of the particular type from the provided directory path. + void load_printers(const std::string& dir_path, const std::string& subdir){}; + + // Load a preset from an already parsed config file, insert it into the sorted sequence of presets + // and select it, losing previous modifications. + PhysicalPrinter& load_printer(const std::string& path, const std::string& name, const DynamicPrintConfig& config, bool select = true); + PhysicalPrinter& load_printer(const std::string& path, const std::string& name, DynamicPrintConfig&& config, bool select = true); + + PhysicalPrinter& load_external_printer( + // Path to the profile source file (a G-code, an AMF or 3MF file, a config file) + const std::string& path, + // Name of the profile, derived from the source file name. + const std::string& name, + // Original name of the profile, extracted from the loaded config. Empty, if the name has not been stored. + const std::string& original_name, + // Config to initialize the preset from. + const DynamicPrintConfig& config, + // Select the preset after loading? + bool select = true); + + // Save the printer under a new name. If the name is different from the old one, + // a new printer is stored into the list of printers. + // ? New printer is activated. + void save_printer(const std::string& new_name); + + // Delete the current preset, activate the first visible preset. + // returns true if the preset was deleted successfully. + bool delete_current_printer() {return true;} + // Delete the current preset, activate the first visible preset. + // returns true if the preset was deleted successfully. + bool delete_printer(const std::string& name) { return true; } + + // Select a printer. If an invalid index is provided, the first visible printer is selected. + PhysicalPrinter& select_printer(size_t idx); + // Return the selected preset, without the user modifications applied. + PhysicalPrinter& get_selected_preset() { return m_printers[m_idx_selected]; } + const PhysicalPrinter& get_selected_preset() const { return m_printers[m_idx_selected]; } + size_t get_selected_idx() const { return m_idx_selected; } + // Returns the name of the selected preset, or an empty string if no preset is selected. + std::string get_selected_preset_name() const { return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_preset().name; } + PhysicalPrinter& get_edited_preset() { return m_edited_printer; } + const PhysicalPrinter& get_edited_preset() const { return m_edited_printer; } + + // Return a preset possibly with modifications. + PhysicalPrinter& default_printer(size_t idx = 0) { return m_printers[idx]; } + const PhysicalPrinter& default_printer(size_t idx = 0) const { return m_printers[idx]; } + + // used to update preset_choice from Tab + const std::deque& get_presets() const { return m_printers; } + size_t get_idx_selected() { return m_idx_selected; } + + // Return a preset by an index. If the preset is active, a temporary copy is returned. + PhysicalPrinter& printer(size_t idx) { return (idx == m_idx_selected) ? m_edited_printer : m_printers[idx]; } + const PhysicalPrinter& printer(size_t idx) const { return const_cast(this)->printer(idx); } + + // Return a preset by its name. If the preset is active, a temporary copy is returned. + // If a preset is not found by its name, null is returned. + PhysicalPrinter* find_printer(const std::string& name, bool first_visible_if_not_found = false); + const PhysicalPrinter* find_printer(const std::string& name, bool first_visible_if_not_found = false) const + { + return const_cast(this)->find_printer(name, first_visible_if_not_found); + } + + // Return number of presets including the "- default -" preset. + size_t size() const { return m_printers.size(); } + + // Select a profile by its name. Return true if the selection changed. + // Without force, the selection is only updated if the index changes. + // With force, the changes are reverted if the new index is the same as the old index. + bool select_printer_by_name(const std::string& name, bool force) {}; + + // Generate a file path from a profile name. Add the ".ini" suffix if it is missing. + std::string path_from_name(const std::string& new_name) const; + +private: +// PhysicalPrinterCollection(); + PhysicalPrinterCollection(const PhysicalPrinterCollection& other); + PhysicalPrinterCollection& operator=(const PhysicalPrinterCollection& other); + + // Find a preset position in the sorted list of presets. + // The "-- default -- " preset is always the first, so it needs + // to be handled differently. + // If a preset does not exist, an iterator is returned indicating where to insert a preset with the same name. + std::deque::iterator find_printer_internal(const std::string& name) + { + PhysicalPrinter key(name); + auto it = std::lower_bound(m_printers.begin()+0, m_printers.end(), key); + return it; + } + std::deque::const_iterator find_printer_internal(const std::string& name) const + { + return const_cast(this)->find_printer_internal(name); + } + + static std::vector dirty_options(const Preset* edited, const Preset* reference, const bool is_printer_type = false); + + // List of presets, starting with the "- default -" preset. + // Use deque to force the container to allocate an object per each entry, + // so that the addresses of the presets don't change during resizing of the container. + std::deque m_printers; + // Initially this printer contains a copy of the selected printer. Later on, this copy may be modified by the user. + PhysicalPrinter m_edited_printer; + // Selected preset. + size_t m_idx_selected; + + // Path to the directory to store the config files into. + std::string m_dir_path; +}; + +////////////////////////////////////////////////////////////////////// +*/ + } // namespace Slic3r #endif /* slic3r_Preset_hpp_ */ diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp index ba806a0b2d..024884b00a 100644 --- a/src/slic3r/GUI/PresetBundle.cpp +++ b/src/slic3r/GUI/PresetBundle.cpp @@ -1,12 +1,10 @@ #include #include "PresetBundle.hpp" -#include "BitmapCache.hpp" #include "Plater.hpp" -#include "I18N.hpp" -#include "wxExtensions.hpp" #include +#include #include #include #include @@ -21,16 +19,13 @@ #include #include -#include #include -#include -#include -#include #include "libslic3r/libslic3r.h" #include "libslic3r/Utils.hpp" #include "libslic3r/Model.hpp" #include "GUI_App.hpp" +#include "libslic3r/CustomGCode.hpp" // Store the print/filament/printer presets into a "presets" subdirectory of the Slic3rPE config dir. @@ -52,12 +47,7 @@ PresetBundle::PresetBundle() : filaments(Preset::TYPE_FILAMENT, Preset::filament_options(), static_cast(FullPrintConfig::defaults())), sla_materials(Preset::TYPE_SLA_MATERIAL, Preset::sla_material_options(), static_cast(SLAFullPrintConfig::defaults())), sla_prints(Preset::TYPE_SLA_PRINT, Preset::sla_print_options(), static_cast(SLAFullPrintConfig::defaults())), - printers(Preset::TYPE_PRINTER, Preset::printer_options(), static_cast(FullPrintConfig::defaults()), "- default FFF -"), - m_bitmapCompatible(new wxBitmap), - m_bitmapIncompatible(new wxBitmap), - m_bitmapLock(new wxBitmap), - m_bitmapLockOpen(new wxBitmap), - m_bitmapCache(new GUI::BitmapCache) + printers(Preset::TYPE_PRINTER, Preset::printer_options(), static_cast(FullPrintConfig::defaults()), "- default FFF -") { if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr) wxImage::AddHandler(new wxPNGHandler); @@ -112,16 +102,6 @@ PresetBundle::PresetBundle() : preset.inherits(); } - // Load the default preset bitmaps. - // #ys_FIXME_to_delete we'll load them later, using em_unit() -// this->prints .load_bitmap_default("cog"); -// this->sla_prints .load_bitmap_default("package_green.png"); -// this->filaments .load_bitmap_default("spool.png"); -// this->sla_materials.load_bitmap_default("package_green.png"); -// this->printers .load_bitmap_default("printer_empty.png"); -// this->printers .load_bitmap_add("add.png"); -// this->load_compatible_bitmaps(); - // Re-activate the default presets, so their "edited" preset copies will be updated with the additional configuration values above. this->prints .select_preset(0); this->sla_prints .select_preset(0); @@ -134,20 +114,6 @@ PresetBundle::PresetBundle() : PresetBundle::~PresetBundle() { - assert(m_bitmapCompatible != nullptr); - assert(m_bitmapIncompatible != nullptr); - assert(m_bitmapLock != nullptr); - assert(m_bitmapLockOpen != nullptr); - delete m_bitmapCompatible; - m_bitmapCompatible = nullptr; - delete m_bitmapIncompatible; - m_bitmapIncompatible = nullptr; - delete m_bitmapLock; - m_bitmapLock = nullptr; - delete m_bitmapLockOpen; - m_bitmapLockOpen = nullptr; - delete m_bitmapCache; - m_bitmapCache = nullptr; } void PresetBundle::reset(bool delete_files) @@ -486,36 +452,6 @@ void PresetBundle::export_selections(AppConfig &config) config.set("presets", "printer", printers.get_selected_preset_name()); } -void PresetBundle::load_compatible_bitmaps() -{ - *m_bitmapCompatible = create_scaled_bitmap("flag_green"); - *m_bitmapIncompatible = create_scaled_bitmap("flag_red"); - *m_bitmapLock = create_scaled_bitmap("lock_closed"); - *m_bitmapLockOpen = create_scaled_bitmap("lock_open"); - - prints .set_bitmap_compatible(m_bitmapCompatible); - filaments .set_bitmap_compatible(m_bitmapCompatible); - sla_prints .set_bitmap_compatible(m_bitmapCompatible); - sla_materials.set_bitmap_compatible(m_bitmapCompatible); - - prints .set_bitmap_incompatible(m_bitmapIncompatible); - filaments .set_bitmap_incompatible(m_bitmapIncompatible); - sla_prints .set_bitmap_incompatible(m_bitmapIncompatible); - sla_materials.set_bitmap_incompatible(m_bitmapIncompatible); - - prints .set_bitmap_lock(m_bitmapLock); - filaments .set_bitmap_lock(m_bitmapLock); - sla_prints .set_bitmap_lock(m_bitmapLock); - sla_materials.set_bitmap_lock(m_bitmapLock); - printers .set_bitmap_lock(m_bitmapLock); - - prints .set_bitmap_lock_open(m_bitmapLock); - filaments .set_bitmap_lock_open(m_bitmapLock); - sla_prints .set_bitmap_lock_open(m_bitmapLock); - sla_materials.set_bitmap_lock_open(m_bitmapLock); - printers .set_bitmap_lock_open(m_bitmapLock); -} - DynamicPrintConfig PresetBundle::full_config() const { return (this->printers.get_edited_preset().printer_technology() == ptFFF) ? @@ -886,7 +822,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool // 4) Load the project config values (the per extruder wipe matrix etc). this->project_config.apply_only(config, s_project_options); - update_custom_gcode_per_print_z_from_config(GUI::wxGetApp().plater()->model().custom_gcode_per_print_z, &this->project_config); + CustomGCode::update_custom_gcode_per_print_z_from_config(GUI::wxGetApp().plater()->model().custom_gcode_per_print_z, &this->project_config); break; } @@ -1544,207 +1480,11 @@ void PresetBundle::export_configbundle(const std::string &path, bool export_syst // an optional "(modified)" suffix will be removed from the filament name. void PresetBundle::set_filament_preset(size_t idx, const std::string &name) { - if (name.find_first_of(PresetCollection::separator_head()) == 0) - return; - - if (idx >= filament_presets.size()) + if (idx >= filament_presets.size()) filament_presets.resize(idx + 1, filaments.default_preset().name); filament_presets[idx] = Preset::remove_suffix_modified(name); } -void PresetBundle::load_default_preset_bitmaps() -{ - // Clear bitmap cache, before load new scaled default preset bitmaps - m_bitmapCache->clear(); - this->prints.clear_bitmap_cache(); - this->sla_prints.clear_bitmap_cache(); - this->filaments.clear_bitmap_cache(); - this->sla_materials.clear_bitmap_cache(); - this->printers.clear_bitmap_cache(); - - this->prints.load_bitmap_default("cog"); - this->sla_prints.load_bitmap_default("cog"); - this->filaments.load_bitmap_default("spool.png"); - this->sla_materials.load_bitmap_default("resin"); - this->printers.load_bitmap_default("printer"); - this->printers.load_bitmap_add("add.png"); - this->load_compatible_bitmaps(); -} - -void PresetBundle::update_plater_filament_ui(unsigned int idx_extruder, GUI::PresetComboBox *ui) -{ - if (ui == nullptr || this->printers.get_edited_preset().printer_technology() == ptSLA || - this->filament_presets.size() <= idx_extruder ) - return; - - unsigned char rgb[3]; - std::string extruder_color = this->printers.get_edited_preset().config.opt_string("extruder_colour", idx_extruder); - if (!m_bitmapCache->parse_color(extruder_color, rgb)) - // Extruder color is not defined. - extruder_color.clear(); - - // Fill in the list from scratch. - ui->Freeze(); - ui->Clear(); - size_t selected_preset_item = INT_MAX; // some value meaning that no one item is selected - - const Preset *selected_preset = this->filaments.find_preset(this->filament_presets[idx_extruder]); - // Show wide icons if the currently selected preset is not compatible with the current printer, - // and draw a red flag in front of the selected preset. - bool wide_icons = selected_preset != nullptr && ! selected_preset->is_compatible && m_bitmapIncompatible != nullptr; - assert(selected_preset != nullptr); - std::map nonsys_presets; - wxString selected_str = ""; - if (!this->filaments().front().is_visible) - ui->set_label_marker(ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap)); - - /* It's supposed that standard size of an icon is 16px*16px for 100% scaled display. - * So set sizes for solid_colored icons used for filament preset - * and scale them in respect to em_unit value - */ - const float scale_f = ui->em_unit() * 0.1f; - - // To avoid the errors of number rounding for different combination of monitor configuration, - // let use scaled 8px, as a smallest icon unit - const int icon_unit = 8 * scale_f + 0.5f; - const int normal_icon_width = 2 * icon_unit; //16 * scale_f + 0.5f; - const int thin_icon_width = icon_unit; //8 * scale_f + 0.5f; - const int wide_icon_width = 3 * icon_unit; //24 * scale_f + 0.5f; - - const int space_icon_width = 2 * scale_f + 0.5f; - - // To avoid asserts, each added bitmap to wxBitmapCombobox should be the same size, so - // set a bitmap height to m_bitmapLock->GetHeight() - // - // To avoid asserts, each added bitmap to wxBitmapCombobox should be the same size. - // But for some display scaling (for example 125% or 175%) normal_icon_width differs from icon width. - // So: - // for nonsystem presets set a width of empty bitmap to m_bitmapLock->GetWidth() - // for compatible presets set a width of empty bitmap to m_bitmapIncompatible->GetWidth() - // - // Note, under OSX we should use a Scaled Height/Width because of Retina scale -#ifdef __APPLE__ - const int icon_height = m_bitmapLock->GetScaledHeight(); - const int lock_icon_width = m_bitmapLock->GetScaledWidth(); - const int flag_icon_width = m_bitmapIncompatible->GetScaledWidth(); -#else - const int icon_height = m_bitmapLock->GetHeight(); - const int lock_icon_width = m_bitmapLock->GetWidth(); - const int flag_icon_width = m_bitmapIncompatible->GetWidth(); -#endif - - wxString tooltip = ""; - - for (int i = this->filaments().front().is_visible ? 0 : 1; i < int(this->filaments().size()); ++i) { - const Preset &preset = this->filaments.preset(i); - bool selected = this->filament_presets[idx_extruder] == preset.name; - if (! preset.is_visible || (! preset.is_compatible && ! selected)) - continue; - // Assign an extruder color to the selected item if the extruder color is defined. - std::string filament_rgb = preset.config.opt_string("filament_colour", 0); - std::string extruder_rgb = (selected && !extruder_color.empty()) ? extruder_color : filament_rgb; - bool single_bar = filament_rgb == extruder_rgb; - std::string bitmap_key = single_bar ? filament_rgb : filament_rgb + extruder_rgb; - // If the filament preset is not compatible and there is a "red flag" icon loaded, show it left - // to the filament color image. - if (wide_icons) - bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt"; - bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst"; - if (preset.is_dirty) - bitmap_key += ",drty"; - wxBitmap *bitmap = m_bitmapCache->find(bitmap_key); - if (bitmap == nullptr) { - // Create the bitmap with color bars. - std::vector bmps; - if (wide_icons) - // Paint a red flag for incompatible presets. - bmps.emplace_back(preset.is_compatible ? m_bitmapCache->mkclear(flag_icon_width, icon_height) : *m_bitmapIncompatible); - // Paint the color bars. - m_bitmapCache->parse_color(filament_rgb, rgb); - bmps.emplace_back(m_bitmapCache->mksolid(single_bar ? wide_icon_width : normal_icon_width, icon_height, rgb)); - if (! single_bar) { - m_bitmapCache->parse_color(extruder_rgb, rgb); - bmps.emplace_back(m_bitmapCache->mksolid(thin_icon_width, icon_height, rgb)); - } - // Paint a lock at the system presets. - bmps.emplace_back(m_bitmapCache->mkclear(space_icon_width, icon_height)); - bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmapLock : m_bitmapCache->mkclear(lock_icon_width, icon_height)); -// (preset.is_dirty ? *m_bitmapLockOpen : *m_bitmapLock) : m_bitmapCache->mkclear(16, 16)); - bitmap = m_bitmapCache->insert(bitmap_key, bmps); - } - - const std::string name = preset.alias.empty() ? preset.name : preset.alias; - if (preset.is_default || preset.is_system) { - ui->Append(wxString::FromUTF8((/*preset.*/name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), - (bitmap == 0) ? wxNullBitmap : *bitmap); - if (selected || - // just in case: mark selected_preset_item as a first added element - selected_preset_item == INT_MAX ) { - selected_preset_item = ui->GetCount() - 1; - tooltip = wxString::FromUTF8(preset.name.c_str()); - } - } - else - { - nonsys_presets.emplace(wxString::FromUTF8((/*preset.*/name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), - (bitmap == 0) ? &wxNullBitmap : bitmap); - if (selected) { - selected_str = wxString::FromUTF8((/*preset.*/name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()); - tooltip = wxString::FromUTF8(preset.name.c_str()); - } - } - if (preset.is_default) - ui->set_label_marker(ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap)); - } - - if (!nonsys_presets.empty()) - { - ui->set_label_marker(ui->Append(PresetCollection::separator(L("User presets")), wxNullBitmap)); - for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { - ui->Append(it->first, *it->second); - if (it->first == selected_str || - // just in case: mark selected_preset_item as a first added element - selected_preset_item == INT_MAX) { - selected_preset_item = ui->GetCount() - 1; - } - } - } - - std::string bitmap_key = ""; - if (wide_icons) - bitmap_key += "wide,"; - bitmap_key += "edit_preset_list"; - wxBitmap* bmp = m_bitmapCache->find(bitmap_key); - if (bmp == nullptr) { - // Create the bitmap with color bars. - std::vector bmps; - if (wide_icons) - // Paint a red flag for incompatible presets. - bmps.emplace_back(m_bitmapCache->mkclear(flag_icon_width, icon_height)); - // Paint the color bars + a lock at the system presets. - bmps.emplace_back(m_bitmapCache->mkclear(wide_icon_width+space_icon_width, icon_height)); - bmps.emplace_back(create_scaled_bitmap("edit_uni")); - bmp = m_bitmapCache->insert(bitmap_key, bmps); - } - ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add/Remove filaments")), *bmp), GUI::PresetComboBox::LABEL_ITEM_WIZARD_FILAMENTS); - - /* But, if selected_preset_item is still equal to INT_MAX, it means that - * there is no presets added to the list. - * So, select last combobox item ("Add/Remove filaments") - */ - if (selected_preset_item == INT_MAX) - selected_preset_item = ui->GetCount() - 1; - - ui->SetSelection(selected_preset_item); - ui->SetToolTip(tooltip.IsEmpty() ? ui->GetString(selected_preset_item) : tooltip); - ui->check_selection(selected_preset_item); - ui->Thaw(); - - // Update control min size after rescale (changed Display DPI under MSW) - if (ui->GetMinWidth() != 20 * ui->em_unit()) - ui->SetMinSize(wxSize(20 * ui->em_unit(), ui->GetSize().GetHeight())); -} - void PresetBundle::set_default_suppressed(bool default_suppressed) { prints.set_default_suppressed(default_suppressed); diff --git a/src/slic3r/GUI/PresetBundle.hpp b/src/slic3r/GUI/PresetBundle.hpp index bf1bba21db..7d137bb7ad 100644 --- a/src/slic3r/GUI/PresetBundle.hpp +++ b/src/slic3r/GUI/PresetBundle.hpp @@ -5,7 +5,6 @@ #include "Preset.hpp" #include -#include #include #include @@ -13,10 +12,6 @@ class wxWindow; namespace Slic3r { -namespace GUI { - class BitmapCache; -}; - // Bundle of Print + Filament + Printer presets. class PresetBundle { @@ -110,9 +105,6 @@ public: // Export a config bundle file containing all the presets and the names of the active presets. void export_configbundle(const std::string &path, bool export_system_settings = false); - // Update a filament selection combo box on the plater for an idx_extruder. - void update_plater_filament_ui(unsigned int idx_extruder, GUI::PresetComboBox *ui); - // Enable / disable the "- default -" preset. void set_default_suppressed(bool default_suppressed); @@ -132,8 +124,6 @@ public: void update_compatible(PresetSelectCompatibleType select_other_print_if_incompatible, PresetSelectCompatibleType select_other_filament_if_incompatible); void update_compatible(PresetSelectCompatibleType select_other_if_incompatible) { this->update_compatible(select_other_if_incompatible, select_other_if_incompatible); } - void load_default_preset_bitmaps(); - // Set the is_visible flag for printer vendors, printer models and printer variants // based on the user configuration. // If the "vendor" section is missing, enable all models and variants of the particular vendor. @@ -163,21 +153,9 @@ private: // If it is not an external config, then the config will be stored into the user profile directory. void load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config); void load_config_file_config_bundle(const std::string &path, const boost::property_tree::ptree &tree); - void load_compatible_bitmaps(); DynamicPrintConfig full_fff_config() const; DynamicPrintConfig full_sla_config() const; - - // Indicator, that the preset is compatible with the selected printer. - wxBitmap *m_bitmapCompatible; - // Indicator, that the preset is NOT compatible with the selected printer. - wxBitmap *m_bitmapIncompatible; - // Indicator, that the preset is system and not modified. - wxBitmap *m_bitmapLock; - // Indicator, that the preset is system and user modified. - wxBitmap *m_bitmapLockOpen; - // Caching color bitmaps for the filament combo box. - GUI::BitmapCache *m_bitmapCache; }; } // namespace Slic3r diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp new file mode 100644 index 0000000000..380edb48ad --- /dev/null +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -0,0 +1,794 @@ +#include "PresetComboBoxes.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "libslic3r/libslic3r.h" +#include "libslic3r/PrintConfig.hpp" + +#include "GUI.hpp" +#include "GUI_App.hpp" +#include "Plater.hpp" +#include "MainFrame.hpp" +#include "format.hpp" +#include "Tab.hpp" +#include "PresetBundle.hpp" +#include "PrintHostDialogs.hpp" +#include "ConfigWizard.hpp" +#include "../Utils/ASCIIFolding.hpp" +#include "../Utils/PrintHost.hpp" +#include "../Utils/FixModelByWin10.hpp" +#include "../Utils/UndoRedo.hpp" +#include "RemovableDriveManager.hpp" +#include "BitmapCache.hpp" + +using Slic3r::GUI::format_wxstr; + +static const std::pair THUMBNAIL_SIZE_3MF = { 256, 256 }; + +namespace Slic3r { +namespace GUI { + +// --------------------------------- +// *** PresetComboBox *** +// --------------------------------- + +/* For PresetComboBox we use bitmaps that are created from images that are already scaled appropriately for Retina + * (Contrary to the intuition, the `scale` argument for Bitmap's constructor doesn't mean + * "please scale this to such and such" but rather + * "the wxImage is already sized for backing scale such and such". ) + * Unfortunately, the constructor changes the size of wxBitmap too. + * Thus We need to use unscaled size value for bitmaps that we use + * to avoid scaled size of control items. + * For this purpose control drawing methods and + * control size calculation methods (virtual) are overridden. + **/ + +PresetComboBox::PresetComboBox(wxWindow* parent, Preset::Type preset_type, const wxSize& size) : + wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, size, 0, nullptr, wxCB_READONLY), + m_type(preset_type), + m_last_selected(wxNOT_FOUND), + m_em_unit(wxGetApp().em_unit()), + m_preset_bundle(wxGetApp().preset_bundle), + m_bitmap_cache(new BitmapCache) +{ + SetFont(wxGetApp().normal_font()); +#ifdef _WIN32 + // Workaround for ignoring CBN_EDITCHANGE events, which are processed after the content of the combo box changes, so that + // the index of the item inside CBN_EDITCHANGE may no more be valid. + EnableTextChangedEvents(false); +#endif /* _WIN32 */ + + switch (m_type) + { + case Preset::TYPE_PRINT: { + m_collection = &m_preset_bundle->prints; + m_main_bitmap_name = "cog"; + break; + } + case Preset::TYPE_FILAMENT: { + m_collection = &m_preset_bundle->filaments; + m_main_bitmap_name = "spool"; + break; + } + case Preset::TYPE_SLA_PRINT: { + m_collection = &m_preset_bundle->sla_prints; + m_main_bitmap_name = "cog"; + break; + } + case Preset::TYPE_SLA_MATERIAL: { + m_collection = &m_preset_bundle->sla_materials; + m_main_bitmap_name = "resin"; + break; + } + case Preset::TYPE_PRINTER: { + m_collection = &m_preset_bundle->printers; + m_main_bitmap_name = "printer"; + break; + } + default: break; + } + + m_bitmapCompatible = ScalableBitmap(nullptr, "flag_green"); + m_bitmapIncompatible = ScalableBitmap(nullptr, "flag_red"); + m_bitmapLock = ScalableBitmap(nullptr, "lock_closed"); + + // parameters for an icon's drawing + fill_width_height(); +} + +PresetComboBox::~PresetComboBox() +{ + delete m_bitmap_cache; + m_bitmap_cache = nullptr; +} + +void PresetComboBox::set_label_marker(int item, LabelItemType label_item_type) +{ + this->SetClientData(item, (void*)label_item_type); +} + +void PresetComboBox::msw_rescale() +{ + m_em_unit = wxGetApp().em_unit(); + + m_bitmapLock.msw_rescale(); + m_bitmapIncompatible.msw_rescale(); + m_bitmapCompatible.msw_rescale(); + + // parameters for an icon's drawing + fill_width_height(); + + // update the control to redraw the icons + update(); +} + +void PresetComboBox::fill_width_height() +{ + // To avoid asserts, each added bitmap to wxBitmapCombobox should be the same size, so + // set a bitmap's height to m_bitmapLock->GetHeight() and norm_icon_width to m_bitmapLock->GetWidth() + icon_height = m_bitmapLock.GetBmpHeight(); + norm_icon_width = m_bitmapLock.GetBmpWidth(); + + /* It's supposed that standard size of an icon is 16px*16px for 100% scaled display. + * So set sizes for solid_colored icons used for filament preset + * and scale them in respect to em_unit value + */ + const float scale_f = (float)m_em_unit * 0.1f; + + thin_icon_width = lroundf(8 * scale_f); // analogue to 8px; + wide_icon_width = norm_icon_width + thin_icon_width; + + space_icon_width = lroundf(2 * scale_f); + thin_space_icon_width = 2 * space_icon_width; + wide_space_icon_width = 3 * space_icon_width; +} + +wxString PresetComboBox::separator(const std::string& label) +{ + return wxString::FromUTF8(separator_head()) + _(label) + wxString::FromUTF8(separator_tail()); +} + +#ifdef __APPLE__ +bool PresetComboBox::OnAddBitmap(const wxBitmap& bitmap) +{ + if (bitmap.IsOk()) + { + // we should use scaled! size values of bitmap + int width = (int)bitmap.GetScaledWidth(); + int height = (int)bitmap.GetScaledHeight(); + + if (m_usedImgSize.x < 0) + { + // If size not yet determined, get it from this image. + m_usedImgSize.x = width; + m_usedImgSize.y = height; + + // Adjust control size to vertically fit the bitmap + wxWindow* ctrl = GetControl(); + ctrl->InvalidateBestSize(); + wxSize newSz = ctrl->GetBestSize(); + wxSize sz = ctrl->GetSize(); + if (newSz.y > sz.y) + ctrl->SetSize(sz.x, newSz.y); + else + DetermineIndent(); + } + + wxCHECK_MSG(width == m_usedImgSize.x && height == m_usedImgSize.y, + false, + "you can only add images of same size"); + + return true; + } + + return false; +} + +void PresetComboBox::OnDrawItem(wxDC& dc, + const wxRect& rect, + int item, + int flags) const +{ + const wxBitmap& bmp = *(wxBitmap*)m_bitmaps[item]; + if (bmp.IsOk()) + { + // we should use scaled! size values of bitmap + wxCoord w = bmp.GetScaledWidth(); + wxCoord h = bmp.GetScaledHeight(); + + const int imgSpacingLeft = 4; + + // Draw the image centered + dc.DrawBitmap(bmp, + rect.x + (m_usedImgSize.x - w) / 2 + imgSpacingLeft, + rect.y + (rect.height - h) / 2, + true); + } + + wxString text = GetString(item); + if (!text.empty()) + dc.DrawText(text, + rect.x + m_imgAreaWidth + 1, + rect.y + (rect.height - dc.GetCharHeight()) / 2); +} +#endif + + +// --------------------------------- +// *** PlaterPresetComboBox *** +// --------------------------------- + +PlaterPresetComboBox::PlaterPresetComboBox(wxWindow *parent, Preset::Type preset_type) : + PresetComboBox(parent, preset_type, wxSize(15 * wxGetApp().em_unit(), -1)) +{ + Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &evt) { + auto selected_item = evt.GetSelection(); + + auto marker = reinterpret_cast(this->GetClientData(selected_item)); + if (marker >= LABEL_ITEM_MARKER && marker < LABEL_ITEM_MAX) { + this->SetSelection(this->m_last_selected); + evt.StopPropagation(); + if (marker == LABEL_ITEM_PHYSICAL_PRINTERS) + { + PhysicalPrinterDialog dlg; + dlg.ShowModal(); + return; + } + if (marker >= LABEL_ITEM_WIZARD_PRINTERS) { + ConfigWizard::StartPage sp = ConfigWizard::SP_WELCOME; + switch (marker) { + case LABEL_ITEM_WIZARD_PRINTERS: sp = ConfigWizard::SP_PRINTERS; break; + case LABEL_ITEM_WIZARD_FILAMENTS: sp = ConfigWizard::SP_FILAMENTS; break; + case LABEL_ITEM_WIZARD_MATERIALS: sp = ConfigWizard::SP_MATERIALS; break; + default: break; + } + wxTheApp->CallAfter([sp]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, sp); }); + } + } else if ( this->m_last_selected != selected_item || m_collection->current_is_dirty() ) { + this->m_last_selected = selected_item; + evt.SetInt(this->m_type); + evt.Skip(); + } else { + evt.StopPropagation(); + } + }); + + if (m_type == Preset::TYPE_FILAMENT) + { + Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &event) { + const Preset* selected_preset = m_collection->find_preset(m_preset_bundle->filament_presets[m_extruder_idx]); + // Wide icons are shown if the currently selected preset is not compatible with the current printer, + // and red flag is drown in front of the selected preset. + bool wide_icons = selected_preset != nullptr && !selected_preset->is_compatible; + float scale = m_em_unit*0.1f; + + int shifl_Left = wide_icons ? int(scale * 16 + 0.5) : 0; +#if defined(wxBITMAPCOMBOBOX_OWNERDRAWN_BASED) + shifl_Left += int(scale * 4 + 0.5f); // IMAGE_SPACING_RIGHT = 4 for wxBitmapComboBox -> Space left of image +#endif + int icon_right_pos = shifl_Left + int(scale * (24+4) + 0.5); + int mouse_pos = event.GetLogicalPosition(wxClientDC(this)).x; + if (mouse_pos < shifl_Left || mouse_pos > icon_right_pos ) { + // Let the combo box process the mouse click. + event.Skip(); + return; + } + + // Swallow the mouse click and open the color picker. + + // get current color + DynamicPrintConfig* cfg = wxGetApp().get_tab(Preset::TYPE_PRINTER)->get_config(); + auto colors = static_cast(cfg->option("extruder_colour")->clone()); + wxColour clr(colors->values[m_extruder_idx]); + if (!clr.IsOk()) + clr = wxColour(0,0,0); // Don't set alfa to transparence + + auto data = new wxColourData(); + data->SetChooseFull(1); + data->SetColour(clr); + + wxColourDialog dialog(this, data); + dialog.CenterOnParent(); + if (dialog.ShowModal() == wxID_OK) + { + colors->values[m_extruder_idx] = dialog.GetColourData().GetColour().GetAsString(wxC2S_HTML_SYNTAX).ToStdString(); + + DynamicPrintConfig cfg_new = *cfg; + cfg_new.set_key_value("extruder_colour", colors); + + wxGetApp().get_tab(Preset::TYPE_PRINTER)->load_config(cfg_new); + this->update(); + wxGetApp().plater()->on_config_change(cfg_new); + } + }); + } + + edit_btn = new ScalableButton(parent, wxID_ANY, "cog"); + edit_btn->SetToolTip(_L("Click to edit preset")); + + edit_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent) + { + Tab* tab = wxGetApp().get_tab(m_type); + if (!tab) + return; + + int page_id = wxGetApp().tab_panel()->FindPage(tab); + if (page_id == wxNOT_FOUND) + return; + + wxGetApp().tab_panel()->SetSelection(page_id); + + // Switch to Settings NotePad + wxGetApp().mainframe->select_tab(); + + /* In a case of a multi-material printing, for editing another Filament Preset + * it's needed to select this preset for the "Filament settings" Tab + */ + if (m_type == Preset::TYPE_FILAMENT && wxGetApp().extruders_edited_cnt() > 1) + { + const std::string& selected_preset = GetString(GetSelection()).ToUTF8().data(); + + // Call select_preset() only if there is new preset and not just modified + if ( !boost::algorithm::ends_with(selected_preset, Preset::suffix_modified()) ) + { + const std::string& preset_name = wxGetApp().preset_bundle->filaments.get_preset_name_by_alias(selected_preset); + tab->select_preset(preset_name); + } + } + }); +} + +PlaterPresetComboBox::~PlaterPresetComboBox() +{ + if (edit_btn) + edit_btn->Destroy(); +} + +// Only the compatible presets are shown. +// If an incompatible preset is selected, it is shown as well. +void PlaterPresetComboBox::update() +{ + if (m_type == Preset::TYPE_FILAMENT && + (m_collection->get_edited_preset().printer_technology() == ptSLA || + m_preset_bundle->filament_presets.size() <= m_extruder_idx) ) + return; + + // Otherwise fill in the list from scratch. + this->Freeze(); + this->Clear(); + size_t selected_preset_item = INT_MAX; // some value meaning that no one item is selected + + const Preset* selected_filament_preset; + std::string extruder_color; + if (m_type == Preset::TYPE_FILAMENT) + { + unsigned char rgb[3]; + extruder_color = m_preset_bundle->printers.get_edited_preset().config.opt_string("extruder_colour", (unsigned int)m_extruder_idx); + if (!m_bitmap_cache->parse_color(extruder_color, rgb)) + // Extruder color is not defined. + extruder_color.clear(); + selected_filament_preset = m_collection->find_preset(m_preset_bundle->filament_presets[m_extruder_idx]); + assert(selected_filament_preset); + } + + const Preset& selected_preset = m_type == Preset::TYPE_FILAMENT ? *selected_filament_preset : m_collection->get_selected_preset(); + // Show wide icons if the currently selected preset is not compatible with the current printer, + // and draw a red flag in front of the selected preset. + bool wide_icons = !selected_preset.is_compatible; + + std::map nonsys_presets; + std::map physical_printers; + + wxString selected = ""; + wxString tooltip = ""; + const std::deque& presets = m_collection->get_presets(); + + if (!presets.front().is_visible) + this->set_label_marker(this->Append(separator(L("System presets")), wxNullBitmap)); + + for (size_t i = presets.front().is_visible ? 0 : m_collection->num_default_presets(); i < presets.size(); ++i) + { + const Preset& preset = presets[i]; + bool is_selected = m_type == Preset::TYPE_FILAMENT ? + m_preset_bundle->filament_presets[m_extruder_idx] == preset.name : + i == m_collection->get_selected_idx(); + + if (!preset.is_visible || (!preset.is_compatible && !is_selected)) + continue; + + std::string bitmap_key, filament_rgb, extruder_rgb; + bool single_bar = false; + if (m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA) + bitmap_key = "sla_printer"; + else if (m_type == Preset::TYPE_FILAMENT) + { + // Assign an extruder color to the selected item if the extruder color is defined. + filament_rgb = preset.config.opt_string("filament_colour", 0); + extruder_rgb = (selected && !extruder_color.empty()) ? extruder_color : filament_rgb; + single_bar = filament_rgb == extruder_rgb; + + bitmap_key = single_bar ? filament_rgb : filament_rgb + extruder_rgb; + } + wxBitmap main_bmp = create_scaled_bitmap(m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name); + + // If the filament preset is not compatible and there is a "red flag" icon loaded, show it left + // to the filament color image. + if (wide_icons) + bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt"; + bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst"; + bitmap_key += "-h" + std::to_string(icon_height); + + wxBitmap* bmp = m_bitmap_cache->find(bitmap_key); + if (bmp == nullptr) { + // Create the bitmap with color bars. + std::vector bmps; + if (wide_icons) + // Paint a red flag for incompatible presets. + bmps.emplace_back(preset.is_compatible ? m_bitmap_cache->mkclear(norm_icon_width, icon_height) : m_bitmapIncompatible.bmp()); + + if (m_type == Preset::TYPE_FILAMENT) + { + unsigned char rgb[3]; + // Paint the color bars. + m_bitmap_cache->parse_color(filament_rgb, rgb); + bmps.emplace_back(m_bitmap_cache->mksolid(single_bar ? wide_icon_width : norm_icon_width, icon_height, rgb)); + if (!single_bar) { + m_bitmap_cache->parse_color(extruder_rgb, rgb); + bmps.emplace_back(m_bitmap_cache->mksolid(thin_icon_width, icon_height, rgb)); + } + // Paint a lock at the system presets. + bmps.emplace_back(m_bitmap_cache->mkclear(space_icon_width, icon_height)); + } + else + { + // Paint the color bars. + bmps.emplace_back(m_bitmap_cache->mkclear(thin_space_icon_width, icon_height)); + bmps.emplace_back(main_bmp); + // Paint a lock at the system presets. + bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width, icon_height)); + } + bmps.emplace_back((preset.is_system || preset.is_default) ? m_bitmapLock.bmp() : m_bitmap_cache->mkclear(norm_icon_width, icon_height)); + bmp = m_bitmap_cache->insert(bitmap_key, bmps); + } + + const std::string name = preset.alias.empty() ? preset.name : preset.alias; + if (preset.is_default || preset.is_system) { + Append(wxString::FromUTF8((name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), + !bmp ? main_bmp : *bmp); + if (is_selected || + // just in case: mark selected_preset_item as a first added element + selected_preset_item == INT_MAX) { + selected_preset_item = GetCount() - 1; + tooltip = wxString::FromUTF8(preset.name.c_str()); + } + } + else + { + nonsys_presets.emplace(wxString::FromUTF8((name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), bmp); + if (is_selected) { + selected = wxString::FromUTF8((name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()); + tooltip = wxString::FromUTF8(preset.name.c_str()); + } + } + if (i + 1 == m_collection->num_default_presets()) + set_label_marker(Append(separator(L("System presets")), wxNullBitmap)); + } + if (!nonsys_presets.empty()) + { + set_label_marker(Append(separator(L("User presets")), wxNullBitmap)); + for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { + Append(it->first, *it->second); + if (it->first == selected || + // just in case: mark selected_preset_item as a first added element + selected_preset_item == INT_MAX) + selected_preset_item = GetCount() - 1; + } + } + if (!physical_printers.empty()) + { + set_label_marker(Append(separator(L("Physical printers")), wxNullBitmap)); + for (std::map::iterator it = physical_printers.begin(); it != physical_printers.end(); ++it) { + Append(it->first, *it->second); + if (it->first == selected || + // just in case: mark selected_preset_item as a first added element + selected_preset_item == INT_MAX) + selected_preset_item = GetCount() - 1; + } + } + + if (m_type == Preset::TYPE_PRINTER || m_type == Preset::TYPE_SLA_MATERIAL) { + std::string bitmap_key = ""; + // If the filament preset is not compatible and there is a "red flag" icon loaded, show it left + // to the filament color image. + if (wide_icons) + bitmap_key += "wide,"; + bitmap_key += "edit_preset_list"; + bitmap_key += "-h" + std::to_string(icon_height); + + wxBitmap* bmp = m_bitmap_cache->find(bitmap_key); + if (bmp == nullptr) { + // Create the bitmap with color bars.update_plater_ui + std::vector bmps; + if (wide_icons) + // Paint a red flag for incompatible presets. + bmps.emplace_back(m_bitmap_cache->mkclear(norm_icon_width, icon_height)); + // Paint the color bars. + bmps.emplace_back(m_bitmap_cache->mkclear(thin_space_icon_width, icon_height)); + bmps.emplace_back(create_scaled_bitmap(m_main_bitmap_name)); + // Paint a lock at the system presets. + bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width, icon_height)); + bmps.emplace_back(create_scaled_bitmap("edit_uni")); + bmp = m_bitmap_cache->insert(bitmap_key, bmps); + } + if (m_type == Preset::TYPE_SLA_MATERIAL) + set_label_marker(Append(separator(L("Add/Remove materials")), *bmp), LABEL_ITEM_WIZARD_MATERIALS); + else + set_label_marker(Append(separator(L("Add/Remove printers")), *bmp), LABEL_ITEM_WIZARD_PRINTERS); + } + if (m_type == Preset::TYPE_PRINTER) { + std::string bitmap_key = ""; + if (wide_icons) + bitmap_key += "wide,"; + bitmap_key += "edit_preset_list"; + bitmap_key += "-h" + std::to_string(icon_height); + + wxBitmap* bmp = m_bitmap_cache->find(bitmap_key); + if (bmp == nullptr) { + // Create the bitmap with color bars. + std::vector bmps; + if (wide_icons) + // Paint a red flag for incompatible presets. + bmps.emplace_back(m_bitmap_cache->mkclear(norm_icon_width, icon_height)); + // Paint the color bars. + bmps.emplace_back(m_bitmap_cache->mkclear(thin_space_icon_width, icon_height)); + bmps.emplace_back(create_scaled_bitmap("printer")); + // Paint a lock at the system presets. + bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width, icon_height)); + bmps.emplace_back(create_scaled_bitmap("edit_uni")); + bmp = m_bitmap_cache->insert(bitmap_key, bmps); + } + set_label_marker(Append(separator(L("Add physical printer")), *bmp), LABEL_ITEM_PHYSICAL_PRINTERS); + } + + /* But, if selected_preset_item is still equal to INT_MAX, it means that + * there is no presets added to the list. + * So, select last combobox item ("Add/Remove preset") + */ + if (selected_preset_item == INT_MAX) + selected_preset_item = GetCount() - 1; + + SetSelection(selected_preset_item); + SetToolTip(tooltip.IsEmpty() ? GetString(selected_preset_item) : tooltip); + m_last_selected = selected_preset_item; + Thaw(); + + // Update control min size after rescale (changed Display DPI under MSW) + if (GetMinWidth() != 20 * m_em_unit) + SetMinSize(wxSize(20 * m_em_unit, GetSize().GetHeight())); +} + +void PlaterPresetComboBox::msw_rescale() +{ + PresetComboBox::msw_rescale(); + edit_btn->msw_rescale(); +} + + +// --------------------------------- +// *** PlaterPresetComboBox *** +// --------------------------------- + +TabPresetComboBox::TabPresetComboBox(wxWindow* parent, Preset::Type preset_type) : + PresetComboBox(parent, preset_type, wxSize(35 * wxGetApp().em_unit(), -1)) +{ + Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) { + // see https://github.com/prusa3d/PrusaSlicer/issues/3889 + // Under OSX: in case of use of a same names written in different case (like "ENDER" and "Ender") + // m_presets_choice->GetSelection() will return first item, because search in PopupListCtrl is case-insensitive. + // So, use GetSelection() from event parameter + auto selected_item = evt.GetSelection(); + + auto marker = reinterpret_cast(this->GetClientData(selected_item)); + if (marker >= LABEL_ITEM_MARKER && marker < LABEL_ITEM_MAX) { + this->SetSelection(this->m_last_selected); + if (marker == LABEL_ITEM_WIZARD_PRINTERS) + wxTheApp->CallAfter([]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, ConfigWizard::SP_PRINTERS); }); + } + else if (m_last_selected != selected_item || m_collection->current_is_dirty()) { + std::string selected_string = this->GetString(selected_item).ToUTF8().data(); + Tab* tab = wxGetApp().get_tab(this->m_type); + assert (tab); + tab->select_preset(selected_string); + } + + evt.StopPropagation(); + }); +} + +// Update the choice UI from the list of presets. +// If show_incompatible, all presets are shown, otherwise only the compatible presets are shown. +// If an incompatible preset is selected, it is shown as well. +void TabPresetComboBox::update() +{ + Freeze(); + Clear(); + size_t selected_preset_item = INT_MAX; // some value meaning that no one item is selected + + const std::deque& presets = m_collection->get_presets(); + + std::map nonsys_presets; + wxString selected = ""; + if (!presets.front().is_visible) + set_label_marker(Append(separator(L("System presets")), wxNullBitmap)); + int idx_selected = m_collection->get_selected_idx(); + for (size_t i = presets.front().is_visible ? 0 : m_collection->num_default_presets(); i < presets.size(); ++i) { + const Preset& preset = presets[i]; + if (!preset.is_visible || (!show_incompatible && !preset.is_compatible && i != idx_selected)) + continue; + + std::string bitmap_key = "tab"; + wxBitmap main_bmp = create_scaled_bitmap(m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name); + if (m_type == Preset::TYPE_PRINTER) { + bitmap_key += "_printer"; + if (preset.printer_technology() == ptSLA) + bitmap_key += "_sla"; + } + bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt"; + bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst"; + bitmap_key += "-h" + std::to_string(icon_height); + + wxBitmap* bmp = m_bitmap_cache->find(bitmap_key); + if (bmp == nullptr) { + // Create the bitmap with color bars. + std::vector bmps; + bmps.emplace_back(m_type == Preset::TYPE_PRINTER ? main_bmp : preset.is_compatible ? m_bitmapCompatible.bmp() : m_bitmapIncompatible.bmp()); + // Paint a lock at the system presets. + bmps.emplace_back((preset.is_system || preset.is_default) ? m_bitmapLock.bmp() : m_bitmap_cache->mkclear(norm_icon_width, icon_height)); + bmp = m_bitmap_cache->insert(bitmap_key, bmps); + } + + if (preset.is_default || preset.is_system) { + Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), + (bmp == 0) ? main_bmp : *bmp); + if (i == idx_selected || + // just in case: mark selected_preset_item as a first added element + selected_preset_item == INT_MAX) + selected_preset_item = GetCount() - 1; + } + else + { + nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), bmp); + if (i == idx_selected) + selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()); + } + if (i + 1 == m_collection->num_default_presets()) + set_label_marker(Append(separator(L("System presets")), wxNullBitmap)); + } + if (!nonsys_presets.empty()) + { + set_label_marker(Append(separator(L("User presets")), wxNullBitmap)); + for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { + Append(it->first, *it->second); + if (it->first == selected || + // just in case: mark selected_preset_item as a first added element + selected_preset_item == INT_MAX) + selected_preset_item = GetCount() - 1; + } + } + if (m_type == Preset::TYPE_PRINTER) { + std::string bitmap_key = "edit_preset_list"; + bitmap_key += "-h" + std::to_string(icon_height); + + wxBitmap* bmp = m_bitmap_cache->find(bitmap_key); + if (bmp == nullptr) { + // Create the bitmap with color bars. + std::vector bmps; + bmps.emplace_back(create_scaled_bitmap(m_main_bitmap_name)); + bmps.emplace_back(create_scaled_bitmap("edit_uni")); + bmp = m_bitmap_cache->insert(bitmap_key, bmps); + } + set_label_marker(Append(separator(L("Add/Remove printers")), *bmp), LABEL_ITEM_WIZARD_PRINTERS); + } + + /* But, if selected_preset_item is still equal to INT_MAX, it means that + * there is no presets added to the list. + * So, select last combobox item ("Add/Remove preset") + */ + if (selected_preset_item == INT_MAX) + selected_preset_item = GetCount() - 1; + + SetSelection(selected_preset_item); + SetToolTip(GetString(selected_preset_item)); + Thaw(); + + m_last_selected = selected_preset_item; +} + +void TabPresetComboBox::msw_rescale() +{ + PresetComboBox::msw_rescale(); + wxSize sz = wxSize(35 * m_em_unit, -1); + SetMinSize(sz); + SetSize(sz); +} + +void TabPresetComboBox::update_dirty() +{ + // 1) Update the dirty flag of the current preset. + m_collection->update_dirty(); + + // 2) Update the labels. + wxWindowUpdateLocker noUpdates(this); + for (unsigned int ui_id = 0; ui_id < GetCount(); ++ui_id) { + std::string old_label = GetString(ui_id).utf8_str().data(); + std::string preset_name = Preset::remove_suffix_modified(old_label); + const Preset* preset = m_collection->find_preset(preset_name, false); + if (preset) { + std::string new_label = preset->is_dirty ? preset->name + Preset::suffix_modified() : preset->name; + if (old_label != new_label) + SetString(ui_id, wxString::FromUTF8(new_label.c_str())); + } + } +#ifdef __APPLE__ + // wxWidgets on OSX do not upload the text of the combo box line automatically. + // Force it to update by re-selecting. + SetSelection(GetSelection()); +#endif /* __APPLE __ */ +} + + +//------------------------------------------ +// PhysicalPrinterDialog +//------------------------------------------ + + +PhysicalPrinterDialog::PhysicalPrinterDialog() + : DPIDialog(NULL, wxID_ANY, _L("Search"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +{ + SetFont(wxGetApp().normal_font()); + SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + + int border = 10; + int em = em_unit(); + + printer_text = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); + printer_presets = new PlaterPresetComboBox(this, Preset::TYPE_PRINTER); + + wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); + + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + + topSizer->Add(printer_text , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(printer_presets , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(btns , 0, wxEXPAND | wxALL, border); + + SetSizer(topSizer); + topSizer->SetSizeHints(this); +} + +void PhysicalPrinterDialog::on_dpi_changed(const wxRect& suggested_rect) +{ + const int& em = em_unit(); + + msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL }); + + const wxSize& size = wxSize(40 * em, 30 * em); + SetMinSize(size); + + Fit(); + Refresh(); +} + + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp new file mode 100644 index 0000000000..63110e4329 --- /dev/null +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -0,0 +1,179 @@ +#ifndef slic3r_PresetComboBoxes_hpp_ +#define slic3r_PresetComboBoxes_hpp_ + +#include + +#include +#include +#include + +#include "Preset.hpp" +#include "wxExtensions.hpp" +#include "GUI_Utils.hpp" + +class wxString; +class wxTextCtrl; + +namespace Slic3r { + +namespace GUI { + +class BitmapCache; + + +// --------------------------------- +// *** PresetComboBox *** +// --------------------------------- + +// BitmapComboBox used to presets list on Sidebar and Tabs +class PresetComboBox : public wxBitmapComboBox +{ +public: + PresetComboBox(wxWindow* parent, Preset::Type preset_type, const wxSize& size = wxDefaultSize); + ~PresetComboBox(); + + enum LabelItemType { + LABEL_ITEM_MARKER = 0xffffff01, + LABEL_ITEM_PHYSICAL_PRINTERS, + LABEL_ITEM_WIZARD_PRINTERS, + LABEL_ITEM_WIZARD_FILAMENTS, + LABEL_ITEM_WIZARD_MATERIALS, + + LABEL_ITEM_MAX, + }; + + void set_label_marker(int item, LabelItemType label_item_type = LABEL_ITEM_MARKER); + int em_unit() const { return m_em_unit; } + + virtual void update() {}; + virtual void msw_rescale(); + +protected: + typedef std::size_t Marker; + + Preset::Type m_type; + std::string m_main_bitmap_name; + + PresetBundle* m_preset_bundle {nullptr}; + PresetCollection* m_collection {nullptr}; + + // Caching color bitmaps for the filament combo box. + BitmapCache* m_bitmap_cache {nullptr}; + // Indicator, that the preset is compatible with the selected printer. + ScalableBitmap m_bitmapCompatible; + // Indicator, that the preset is NOT compatible with the selected printer. + ScalableBitmap m_bitmapIncompatible; + // Indicator, that the preset is system and not modified. + ScalableBitmap m_bitmapLock; + + int m_last_selected; + int m_em_unit; + + // parameters for an icon's drawing + int icon_height; + int norm_icon_width; + int thin_icon_width; + int wide_icon_width; + int space_icon_width; + int thin_space_icon_width; + int wide_space_icon_width; + +#ifdef __linux__ + static const char* separator_head() { return "------- "; } + static const char* separator_tail() { return " -------"; } +#else // __linux__ + static const char* separator_head() { return "————— "; } + static const char* separator_tail() { return " —————"; } +#endif // __linux__ + static wxString separator(const std::string& label); + +#ifdef __APPLE__ + /* For PresetComboBox we use bitmaps that are created from images that are already scaled appropriately for Retina + * (Contrary to the intuition, the `scale` argument for Bitmap's constructor doesn't mean + * "please scale this to such and such" but rather + * "the wxImage is already sized for backing scale such and such". ) + * Unfortunately, the constructor changes the size of wxBitmap too. + * Thus We need to use unscaled size value for bitmaps that we use + * to avoid scaled size of control items. + * For this purpose control drawing methods and + * control size calculation methods (virtual) are overridden. + **/ + virtual bool OnAddBitmap(const wxBitmap& bitmap) override; + virtual void OnDrawItem(wxDC& dc, const wxRect& rect, int item, int flags) const override; +#endif + +private: + void fill_width_height(); +}; + + +// --------------------------------- +// *** PlaterPresetComboBox *** +// --------------------------------- + +class PlaterPresetComboBox : public PresetComboBox +{ +public: + PlaterPresetComboBox(wxWindow *parent, Preset::Type preset_type); + ~PlaterPresetComboBox(); + + ScalableButton* edit_btn { nullptr }; + + void set_extruder_idx(const int extr_idx) { m_extruder_idx = extr_idx; } + int get_extruder_idx() const { return m_extruder_idx; } + + void update() override; + void msw_rescale() override; + +private: + int m_extruder_idx = -1; +}; + + +// --------------------------------- +// *** PlaterPresetComboBox *** +// --------------------------------- + +class TabPresetComboBox : public PresetComboBox +{ +public: + TabPresetComboBox(wxWindow *parent, Preset::Type preset_type); + ~TabPresetComboBox() {} + void set_show_incompatible_presets(bool show_incompatible_presets) { + show_incompatible = show_incompatible_presets; + } + + void update() override; + void update_dirty(); + void msw_rescale() override; + +private: + bool show_incompatible{false}; +}; + + +//------------------------------------------ +// PhysicalPrinterDialog +//------------------------------------------ + +class PhysicalPrinterDialog : public DPIDialog +{ + std::string printer_name; + std::string preset_name; + + wxTextCtrl* printer_text { nullptr }; + PresetComboBox* printer_presets; + +public: + PhysicalPrinterDialog(); + ~PhysicalPrinterDialog() {} + +protected: + void on_dpi_changed(const wxRect& suggested_rect) override; + void on_sys_color_changed() override {}; +}; + +} // namespace GUI +} // namespace Slic3r + +#endif diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 84bc5a5726..b128ec03d4 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -27,6 +27,7 @@ #include #include "wxExtensions.hpp" +#include "PresetComboBoxes.hpp" #include #include "GUI_App.hpp" @@ -160,10 +161,7 @@ void Tab::create_preset_tab() #endif //__WXOSX__ // preset chooser - m_presets_choice = new PresetBitmapComboBox(panel, wxSize(35 * m_em_unit, -1)); - - // search combox -// m_search = new Search::SearchCtrl(panel); + m_presets_choice = new TabPresetComboBox(panel, m_type); auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); @@ -278,35 +276,6 @@ void Tab::create_preset_tab() m_treectrl->Bind(wxEVT_TREE_SEL_CHANGED, &Tab::OnTreeSelChange, this); m_treectrl->Bind(wxEVT_KEY_DOWN, &Tab::OnKeyDown, this); - m_presets_choice->Bind(wxEVT_COMBOBOX, ([this](wxCommandEvent e) { - //! Because of The MSW and GTK version of wxBitmapComboBox derived from wxComboBox, - //! but the OSX version derived from wxOwnerDrawnCombo, instead of: - //! select_preset(m_presets_choice->GetStringSelection().ToUTF8().data()); - //! we doing next: - // int selected_item = m_presets_choice->GetSelection(); - - // see https://github.com/prusa3d/PrusaSlicer/issues/3889 - // Under OSX: in case of use of a same names written in different case (like "ENDER" and "Ender") - // m_presets_choice->GetSelection() will return first item, because search in PopupListCtrl is case-insensitive. - // So, use GetSelection() from event parameter - int selected_item = e.GetSelection(); - if (m_selected_preset_item == size_t(selected_item) && !m_presets->current_is_dirty()) - return; - if (selected_item >= 0) { - std::string selected_string = m_presets_choice->GetString(selected_item).ToUTF8().data(); - if (selected_string.find(PresetCollection::separator_head()) == 0 - /*selected_string == "------- System presets -------" || - selected_string == "------- User presets -------"*/) { - m_presets_choice->SetSelection(m_selected_preset_item); - if (wxString::FromUTF8(selected_string.c_str()) == PresetCollection::separator(L("Add a new printer"))) - wxTheApp->CallAfter([]() { wxGetApp().run_wizard(ConfigWizard::RR_USER); }); - return; - } - m_selected_preset_item = selected_item; - select_preset(selected_string); - } - })); - m_btn_save_preset->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { save_preset(); })); m_btn_delete_preset->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { delete_preset(); })); m_btn_hide_incompatible_presets->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { @@ -778,14 +747,14 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/) // comparing the selected preset config with $self->{config}. void Tab::update_dirty() { - m_presets->update_dirty_ui(m_presets_choice); + m_presets_choice->update_dirty(); on_presets_changed(); update_changed_ui(); } void Tab::update_tab_ui() { - m_selected_preset_item = m_presets->update_tab_ui(m_presets_choice, m_show_incompatible_presets, m_em_unit); + m_presets_choice->update(); } // Load a provied DynamicConfig into the tab, modifying the active preset. @@ -850,12 +819,10 @@ void Tab::msw_rescale() m_em_unit = wxGetApp().em_unit(); m_mode_sizer->msw_rescale(); + m_presets_choice->msw_rescale(); - m_presets_choice->SetSize(35 * m_em_unit, -1); m_treectrl->SetMinSize(wxSize(20 * m_em_unit, -1)); - update_tab_ui(); - // rescale buttons and cached bitmaps for (const auto btn : m_scaled_buttons) btn->msw_rescale(); @@ -963,7 +930,7 @@ void Tab::load_key_value(const std::string& opt_key, const boost::any& value, bo // Don't select another profile if this profile happens to become incompatible. m_preset_bundle->update_compatible(PresetSelectCompatibleType::Never); } - m_presets->update_dirty_ui(m_presets_choice); + m_presets_choice->update_dirty(); on_presets_changed(); update(); } @@ -3360,6 +3327,7 @@ void Tab::delete_preset() void Tab::toggle_show_hide_incompatible() { m_show_incompatible_presets = !m_show_incompatible_presets; + m_presets_choice->set_show_incompatible_presets(m_show_incompatible_presets); update_show_hide_incompatible_button(); update_tab_ui(); } diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 5805809bfb..a0bf536c19 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -39,6 +39,8 @@ namespace Slic3r { namespace GUI { +class TabPresetComboBox; + // Single Tab page containing a{ vsizer } of{ optgroups } // package Slic3r::GUI::Tab::Page; using ConfigOptionsGroupShp = std::shared_ptr; @@ -113,7 +115,7 @@ protected: Preset::Type m_type; std::string m_name; const wxString m_title; - PresetBitmapComboBox* m_presets_choice; + TabPresetComboBox* m_presets_choice; ScalableButton* m_search_btn; ScalableButton* m_btn_save_preset; ScalableButton* m_btn_delete_preset; @@ -206,8 +208,6 @@ protected: bool m_is_nonsys_values{ true }; bool m_postpone_update_ui {false}; - size_t m_selected_preset_item{ 0 }; - void set_type(); int m_em_unit; @@ -320,7 +320,6 @@ public: DynamicPrintConfig* get_config() { return m_config; } PresetCollection* get_presets() { return m_presets; } - size_t get_selected_preset_item() { return m_selected_preset_item; } void on_value_change(const std::string& opt_key, const boost::any& value); diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index ad9f0a121e..39b3e154b4 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -300,94 +300,6 @@ void wxCheckListBoxComboPopup::OnListBoxSelection(wxCommandEvent& evt) } -namespace Slic3r { -namespace GUI { - -// *** PresetBitmapComboBox *** - -/* For PresetBitmapComboBox we use bitmaps that are created from images that are already scaled appropriately for Retina - * (Contrary to the intuition, the `scale` argument for Bitmap's constructor doesn't mean - * "please scale this to such and such" but rather - * "the wxImage is already sized for backing scale such and such". ) - * Unfortunately, the constructor changes the size of wxBitmap too. - * Thus We need to use unscaled size value for bitmaps that we use - * to avoid scaled size of control items. - * For this purpose control drawing methods and - * control size calculation methods (virtual) are overridden. - **/ - -PresetBitmapComboBox::PresetBitmapComboBox(wxWindow* parent, const wxSize& size) : - wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, size, 0, nullptr, wxCB_READONLY) -{} - -#ifdef __APPLE__ -bool PresetBitmapComboBox::OnAddBitmap(const wxBitmap& bitmap) -{ - if (bitmap.IsOk()) - { - // we should use scaled! size values of bitmap - int width = (int)bitmap.GetScaledWidth(); - int height = (int)bitmap.GetScaledHeight(); - - if (m_usedImgSize.x < 0) - { - // If size not yet determined, get it from this image. - m_usedImgSize.x = width; - m_usedImgSize.y = height; - - // Adjust control size to vertically fit the bitmap - wxWindow* ctrl = GetControl(); - ctrl->InvalidateBestSize(); - wxSize newSz = ctrl->GetBestSize(); - wxSize sz = ctrl->GetSize(); - if (newSz.y > sz.y) - ctrl->SetSize(sz.x, newSz.y); - else - DetermineIndent(); - } - - wxCHECK_MSG(width == m_usedImgSize.x && height == m_usedImgSize.y, - false, - "you can only add images of same size"); - - return true; - } - - return false; -} - -void PresetBitmapComboBox::OnDrawItem(wxDC& dc, - const wxRect& rect, - int item, - int flags) const -{ - const wxBitmap& bmp = *(wxBitmap*)m_bitmaps[item]; - if (bmp.IsOk()) - { - // we should use scaled! size values of bitmap - wxCoord w = bmp.GetScaledWidth(); - wxCoord h = bmp.GetScaledHeight(); - - const int imgSpacingLeft = 4; - - // Draw the image centered - dc.DrawBitmap(bmp, - rect.x + (m_usedImgSize.x - w) / 2 + imgSpacingLeft, - rect.y + (rect.height - h) / 2, - true); - } - - wxString text = GetString(item); - if (!text.empty()) - dc.DrawText(text, - rect.x + m_imgAreaWidth + 1, - rect.y + (rect.height - dc.GetCharHeight()) / 2); -} -#endif -} -} - - // *** wxDataViewTreeCtrlComboPopup *** const unsigned int wxDataViewTreeCtrlComboPopup::DefaultWidth = 270; diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 569257e1b4..17fe8992c5 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -95,37 +95,6 @@ public: void OnListBoxSelection(wxCommandEvent& evt); }; -namespace Slic3r { -namespace GUI { -// *** PresetBitmapComboBox *** - -// BitmapComboBox used to presets list on Sidebar and Tabs -class PresetBitmapComboBox: public wxBitmapComboBox -{ -public: - PresetBitmapComboBox(wxWindow* parent, const wxSize& size = wxDefaultSize); - ~PresetBitmapComboBox() {} - -#ifdef __APPLE__ -protected: - /* For PresetBitmapComboBox we use bitmaps that are created from images that are already scaled appropriately for Retina - * (Contrary to the intuition, the `scale` argument for Bitmap's constructor doesn't mean - * "please scale this to such and such" but rather - * "the wxImage is already sized for backing scale such and such". ) - * Unfortunately, the constructor changes the size of wxBitmap too. - * Thus We need to use unscaled size value for bitmaps that we use - * to avoid scaled size of control items. - * For this purpose control drawing methods and - * control size calculation methods (virtual) are overridden. - **/ - virtual bool OnAddBitmap(const wxBitmap& bitmap) override; - virtual void OnDrawItem(wxDC& dc, const wxRect& rect, int item, int flags) const override; -#endif -}; - -} -} - // *** wxDataViewTreeCtrlComboBox *** From 19c4f3260429ba6db33411fd78b6bfc74ac2b8e9 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 16 Jun 2020 16:58:41 +0200 Subject: [PATCH 129/503] Preset and PresetBundle are moved to the _libslic3r_ folder --- src/libslic3r/CMakeLists.txt | 4 ++++ src/{slic3r/GUI => libslic3r}/Preset.cpp | 23 +++++++++++++------ src/{slic3r/GUI => libslic3r}/Preset.hpp | 14 +++-------- .../GUI => libslic3r}/PresetBundle.cpp | 17 +++----------- .../GUI => libslic3r}/PresetBundle.hpp | 3 +-- src/slic3r/CMakeLists.txt | 4 ---- src/slic3r/Config/Snapshot.cpp | 3 +-- src/slic3r/GUI/3DBed.cpp | 2 +- src/slic3r/GUI/ConfigManipulation.cpp | 2 +- src/slic3r/GUI/ConfigWizard_private.hpp | 2 +- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- src/slic3r/GUI/GUI_App.cpp | 4 ++-- src/slic3r/GUI/GUI_App.hpp | 2 +- src/slic3r/GUI/GUI_ObjectLayers.cpp | 2 +- src/slic3r/GUI/GUI_ObjectList.cpp | 2 +- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 2 +- src/slic3r/GUI/GUI_ObjectSettings.cpp | 2 +- src/slic3r/GUI/GUI_Preview.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 2 +- src/slic3r/GUI/I18N.hpp | 2 +- src/slic3r/GUI/Jobs/SLAImportJob.cpp | 2 +- src/slic3r/GUI/MainFrame.cpp | 2 +- src/slic3r/GUI/Mouse3DController.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 4 +++- src/slic3r/GUI/Plater.hpp | 2 +- src/slic3r/GUI/PresetComboBoxes.cpp | 2 +- src/slic3r/GUI/PresetComboBoxes.hpp | 2 +- src/slic3r/GUI/PresetHints.cpp | 1 - src/slic3r/GUI/PresetHints.hpp | 2 +- src/slic3r/GUI/Search.cpp | 2 +- src/slic3r/GUI/Search.hpp | 2 +- src/slic3r/GUI/Tab.cpp | 3 +-- src/slic3r/GUI/Tab.hpp | 2 +- src/slic3r/Utils/PresetUpdater.cpp | 2 +- 38 files changed, 61 insertions(+), 73 deletions(-) rename src/{slic3r/GUI => libslic3r}/Preset.cpp (99%) rename src/{slic3r/GUI => libslic3r}/Preset.hpp (99%) rename src/{slic3r/GUI => libslic3r}/PresetBundle.cpp (99%) rename src/{slic3r/GUI => libslic3r}/PresetBundle.hpp (99%) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 1a58bdbbd9..1605c52cde 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -147,6 +147,10 @@ add_library(libslic3r STATIC PolygonTrimmer.hpp Polyline.cpp Polyline.hpp + Preset.cpp + Preset.hpp + PresetBundle.cpp + PresetBundle.hpp Print.cpp Print.hpp PrintBase.cpp diff --git a/src/slic3r/GUI/Preset.cpp b/src/libslic3r/Preset.cpp similarity index 99% rename from src/slic3r/GUI/Preset.cpp rename to src/libslic3r/Preset.cpp index 883dc438ab..e46cd6c822 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1,8 +1,7 @@ #include #include "Preset.hpp" -#include "AppConfig.hpp" -#include "I18N.hpp" +#include "slic3r/GUI/AppConfig.hpp" #ifdef _MSC_VER #define WIN32_LEAN_AND_MEAN @@ -10,6 +9,16 @@ #include #endif /* _MSC_VER */ +// instead of #include "slic3r/GUI/I18N.hpp" : +#ifndef L +// !!! If you needed to translate some string, +// !!! please use _L(string) +// !!! _() - is a standard wxWidgets macro to translate +// !!! L() is used only for marking localizable string +// !!! It will be used in "xgettext" to create a Locating Message Catalog. +#define L(s) s +#endif /* L */ + #include #include #include @@ -28,9 +37,9 @@ #include #include -#include "libslic3r/libslic3r.h" -#include "libslic3r/Utils.hpp" -#include "libslic3r/PlaceholderParser.hpp" +#include "libslic3r.h" +#include "Utils.hpp" +#include "PlaceholderParser.hpp" using boost::property_tree::ptree; @@ -237,9 +246,9 @@ const std::string& Preset::suffix_modified() return g_suffix_modified; } -void Preset::update_suffix_modified() +void Preset::update_suffix_modified(const std::string& new_suffix_modified) { - g_suffix_modified = (" (" + _(L("modified")) + ")").ToUTF8().data(); + g_suffix_modified = new_suffix_modified; } // Remove an optional "(modified)" suffix from a name. // This converts a UI name to a unique preset identifier. diff --git a/src/slic3r/GUI/Preset.hpp b/src/libslic3r/Preset.hpp similarity index 99% rename from src/slic3r/GUI/Preset.hpp rename to src/libslic3r/Preset.hpp index 8a8fa024b6..b0af2f142b 100644 --- a/src/slic3r/GUI/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -8,16 +8,8 @@ #include #include -#include "libslic3r/libslic3r.h" -#include "libslic3r/PrintConfig.hpp" -#include "libslic3r/Semver.hpp" - -class wxBitmap; -class wxBitmapComboBox; -class wxChoice; -class wxItemContainer; -class wxString; -class wxWindow; +#include "PrintConfig.hpp" +#include "Semver.hpp" namespace Slic3r { @@ -231,7 +223,7 @@ public: static const std::vector& sla_material_options(); static const std::vector& sla_print_options(); - static void update_suffix_modified(); + static void update_suffix_modified(const std::string& new_suffix_modified); static const std::string& suffix_modified(); static std::string remove_suffix_modified(const std::string& name); static void normalize(DynamicPrintConfig &config); diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp similarity index 99% rename from src/slic3r/GUI/PresetBundle.cpp rename to src/libslic3r/PresetBundle.cpp index 024884b00a..9da7731a45 100644 --- a/src/slic3r/GUI/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -1,7 +1,9 @@ #include #include "PresetBundle.hpp" -#include "Plater.hpp" +#include "libslic3r.h" +#include "Utils.hpp" +#include "Model.hpp" #include #include @@ -19,14 +21,6 @@ #include #include -#include - -#include "libslic3r/libslic3r.h" -#include "libslic3r/Utils.hpp" -#include "libslic3r/Model.hpp" -#include "GUI_App.hpp" -#include "libslic3r/CustomGCode.hpp" - // Store the print/filament/printer presets into a "presets" subdirectory of the Slic3rPE config dir. // This breaks compatibility with the upstream Slic3r if the --datadir is used to switch between the two versions. @@ -49,9 +43,6 @@ PresetBundle::PresetBundle() : sla_prints(Preset::TYPE_SLA_PRINT, Preset::sla_print_options(), static_cast(SLAFullPrintConfig::defaults())), printers(Preset::TYPE_PRINTER, Preset::printer_options(), static_cast(FullPrintConfig::defaults()), "- default FFF -") { - if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr) - wxImage::AddHandler(new wxPNGHandler); - // The following keys are handled by the UI, they do not have a counterpart in any StaticPrintConfig derived classes, // therefore they need to be handled differently. As they have no counterpart in StaticPrintConfig, they are not being // initialized based on PrintConfigDef(), but to empty values (zeros, empty vectors, empty strings). @@ -822,8 +813,6 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool // 4) Load the project config values (the per extruder wipe matrix etc). this->project_config.apply_only(config, s_project_options); - CustomGCode::update_custom_gcode_per_print_z_from_config(GUI::wxGetApp().plater()->model().custom_gcode_per_print_z, &this->project_config); - break; } case ptSLA: diff --git a/src/slic3r/GUI/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp similarity index 99% rename from src/slic3r/GUI/PresetBundle.hpp rename to src/libslic3r/PresetBundle.hpp index 7d137bb7ad..19d4093d63 100644 --- a/src/slic3r/GUI/PresetBundle.hpp +++ b/src/libslic3r/PresetBundle.hpp @@ -1,14 +1,13 @@ #ifndef slic3r_PresetBundle_hpp_ #define slic3r_PresetBundle_hpp_ -#include "AppConfig.hpp" #include "Preset.hpp" #include #include #include -class wxWindow; +#include "slic3r/GUI/AppConfig.hpp" namespace Slic3r { diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 98389e7dae..49e0692858 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -61,10 +61,6 @@ set(SLIC3R_GUI_SOURCES GUI/GLToolbar.cpp GUI/Preferences.cpp GUI/Preferences.hpp - GUI/Preset.cpp - GUI/Preset.hpp - GUI/PresetBundle.cpp - GUI/PresetBundle.hpp GUI/PresetHints.cpp GUI/PresetHints.hpp GUI/GUI.cpp diff --git a/src/slic3r/Config/Snapshot.cpp b/src/slic3r/Config/Snapshot.cpp index 2264afa7d2..f7d313418d 100644 --- a/src/slic3r/Config/Snapshot.cpp +++ b/src/slic3r/Config/Snapshot.cpp @@ -1,6 +1,5 @@ #include "Snapshot.hpp" #include "../GUI/AppConfig.hpp" -#include "../GUI/PresetBundle.hpp" #include @@ -11,7 +10,7 @@ #include #include - +#include "libslic3r/PresetBundle.hpp" #include "libslic3r/libslic3r.h" #include "libslic3r/Time.hpp" #include "libslic3r/Config.hpp" diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 6c070ca99a..f2f9f63012 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -7,7 +7,7 @@ #include "libslic3r/BoundingBox.hpp" #include "GUI_App.hpp" -#include "PresetBundle.hpp" +#include "libslic3r/PresetBundle.hpp" #include "GLCanvas3D.hpp" #include diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index a0df4c6598..cd8463a772 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -2,7 +2,7 @@ #include "ConfigManipulation.hpp" #include "I18N.hpp" #include "GUI_App.hpp" -#include "PresetBundle.hpp" +#include "libslic3r/PresetBundle.hpp" #include diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index c99c5952b9..be2919861f 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -20,9 +20,9 @@ #include #include "libslic3r/PrintConfig.hpp" +#include "libslic3r/PresetBundle.hpp" #include "slic3r/Utils/PresetUpdater.hpp" #include "AppConfig.hpp" -#include "PresetBundle.hpp" #include "BedShapeDialog.hpp" #include "GUI.hpp" #include "wxExtensions.hpp" diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index b4e672c4fb..b5cd9fb2a1 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -13,11 +13,11 @@ #include "libslic3r/Utils.hpp" #include "libslic3r/Technologies.hpp" #include "libslic3r/Tesselate.hpp" +#include "libslic3r/PresetBundle.hpp" #include "slic3r/GUI/3DScene.hpp" #include "slic3r/GUI/BackgroundSlicingProcess.hpp" #include "slic3r/GUI/GLShader.hpp" #include "slic3r/GUI/GUI.hpp" -#include "slic3r/GUI/PresetBundle.hpp" #include "slic3r/GUI/Tab.hpp" #include "slic3r/GUI/GUI_Preview.hpp" #include "slic3r/GUI/OpenGLManager.hpp" diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 3c000f62e5..157875e708 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -31,11 +31,11 @@ #include "libslic3r/Utils.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/I18N.hpp" +#include "libslic3r/PresetBundle.hpp" #include "GUI.hpp" #include "GUI_Utils.hpp" #include "AppConfig.hpp" -#include "PresetBundle.hpp" #include "3DScene.hpp" #include "MainFrame.hpp" #include "Plater.hpp" @@ -935,7 +935,7 @@ bool GUI_App::load_language(wxString language, bool initial) m_imgui->set_language(into_u8(language_info->CanonicalName)); //FIXME This is a temporary workaround, the correct solution is to switch to "C" locale during file import / export only. wxSetlocale(LC_NUMERIC, "C"); - Preset::update_suffix_modified(); + Preset::update_suffix_modified((" (" + _L("modified") + ")").ToUTF8().data()); return true; } diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index c2b257f458..23567695cd 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -3,10 +3,10 @@ #include #include -#include "Preset.hpp" #include "ImGuiWrapper.hpp" #include "ConfigWizard.hpp" #include "OpenGLManager.hpp" +#include "libslic3r/Preset.hpp" #include #include diff --git a/src/slic3r/GUI/GUI_ObjectLayers.cpp b/src/slic3r/GUI/GUI_ObjectLayers.cpp index b1a5512d4b..90a725fbfd 100644 --- a/src/slic3r/GUI/GUI_ObjectLayers.cpp +++ b/src/slic3r/GUI/GUI_ObjectLayers.cpp @@ -3,7 +3,7 @@ #include "OptionsGroup.hpp" #include "GUI_App.hpp" -#include "PresetBundle.hpp" +#include "libslic3r/PresetBundle.hpp" #include "libslic3r/Model.hpp" #include "GLCanvas3D.hpp" #include "Plater.hpp" diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 2f201180a8..b87565b03a 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1,4 +1,5 @@ #include "libslic3r/libslic3r.h" +#include "libslic3r/PresetBundle.hpp" #include "GUI_ObjectList.hpp" #include "GUI_ObjectManipulation.hpp" #include "GUI_ObjectLayers.hpp" @@ -7,7 +8,6 @@ #include "Plater.hpp" #include "OptionsGroup.hpp" -#include "PresetBundle.hpp" #include "Tab.hpp" #include "wxExtensions.hpp" #include "libslic3r/Model.hpp" diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 2c35fc316d..7243e8c73a 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -6,7 +6,7 @@ #include "OptionsGroup.hpp" #include "GUI_App.hpp" #include "wxExtensions.hpp" -#include "PresetBundle.hpp" +#include "libslic3r/PresetBundle.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/Geometry.hpp" #include "Selection.hpp" diff --git a/src/slic3r/GUI/GUI_ObjectSettings.cpp b/src/slic3r/GUI/GUI_ObjectSettings.cpp index ef78123a4c..398cd51d45 100644 --- a/src/slic3r/GUI/GUI_ObjectSettings.cpp +++ b/src/slic3r/GUI/GUI_ObjectSettings.cpp @@ -4,8 +4,8 @@ #include "OptionsGroup.hpp" #include "GUI_App.hpp" #include "wxExtensions.hpp" -#include "PresetBundle.hpp" #include "Plater.hpp" +#include "libslic3r/PresetBundle.hpp" #include "libslic3r/Model.hpp" #include diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index c1e8b4c33c..f068ef37dc 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -8,7 +8,7 @@ #include "BackgroundSlicingProcess.hpp" #include "OpenGLManager.hpp" #include "GLCanvas3D.hpp" -#include "PresetBundle.hpp" +#include "libslic3r/PresetBundle.hpp" #include "DoubleSlider.hpp" #include "Plater.hpp" diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index cd42857247..7aa5168459 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -6,9 +6,9 @@ #include #include "slic3r/GUI/GUI_App.hpp" -#include "slic3r/GUI/PresetBundle.hpp" #include "slic3r/GUI/Camera.hpp" #include "slic3r/GUI/Plater.hpp" +#include "libslic3r/PresetBundle.hpp" #include "libslic3r/Model.hpp" diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 658db64cab..273384da2e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -9,7 +9,7 @@ #include "slic3r/GUI/GUI_ObjectSettings.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp" #include "slic3r/GUI/Plater.hpp" -#include "slic3r/GUI/PresetBundle.hpp" +#include "libslic3r/PresetBundle.hpp" #include "libslic3r/Model.hpp" diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 908fe27b11..2856bb35de 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -16,7 +16,7 @@ #include "slic3r/GUI/GUI_ObjectSettings.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp" #include "slic3r/GUI/Plater.hpp" -#include "slic3r/GUI/PresetBundle.hpp" +#include "libslic3r/PresetBundle.hpp" #include "libslic3r/SLAPrint.hpp" diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 051e9cf880..6742f5cdef 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -8,7 +8,7 @@ #include "slic3r/GUI/Camera.hpp" #include "slic3r/GUI/Plater.hpp" -#include "slic3r/GUI/PresetBundle.hpp" +#include "libslic3r/PresetBundle.hpp" #include diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 511c68735c..c33ba2850e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -5,7 +5,6 @@ #include "slic3r/GUI/Camera.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/GUI_ObjectManipulation.hpp" -#include "slic3r/GUI/PresetBundle.hpp" #include "slic3r/GUI/Plater.hpp" #include "slic3r/Utils/UndoRedo.hpp" @@ -19,6 +18,7 @@ #include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp" #include "libslic3r/Model.hpp" +#include "libslic3r/PresetBundle.hpp" #include diff --git a/src/slic3r/GUI/I18N.hpp b/src/slic3r/GUI/I18N.hpp index 25e46930ba..7bad6880e9 100644 --- a/src/slic3r/GUI/I18N.hpp +++ b/src/slic3r/GUI/I18N.hpp @@ -12,7 +12,7 @@ #ifndef L // !!! If you needed to translate some wxString, -// !!! please use _(L(string)) +// !!! please use _L(string) // !!! _() - is a standard wxWidgets macro to translate // !!! L() is used only for marking localizable string // !!! It will be used in "xgettext" to create a Locating Message Catalog. diff --git a/src/slic3r/GUI/Jobs/SLAImportJob.cpp b/src/slic3r/GUI/Jobs/SLAImportJob.cpp index 4e9f08ff23..cc779df2ad 100644 --- a/src/slic3r/GUI/Jobs/SLAImportJob.cpp +++ b/src/slic3r/GUI/Jobs/SLAImportJob.cpp @@ -4,11 +4,11 @@ #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/AppConfig.hpp" #include "slic3r/GUI/Plater.hpp" -#include "slic3r/GUI/PresetBundle.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp" #include "slic3r/Utils/SLAImport.hpp" #include "libslic3r/Model.hpp" +#include "libslic3r/PresetBundle.hpp" #include #include diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index d4ce21fc00..08caf299b5 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -15,9 +15,9 @@ #include "libslic3r/Print.hpp" #include "libslic3r/Polygon.hpp" #include "libslic3r/SLAPrint.hpp" +#include "libslic3r/PresetBundle.hpp" #include "Tab.hpp" -#include "PresetBundle.hpp" #include "ProgressStatusBar.hpp" #include "3DScene.hpp" #include "AppConfig.hpp" diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index baa9356b69..91d2414d51 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -1,9 +1,9 @@ #include "libslic3r/libslic3r.h" +#include "libslic3r/PresetBundle.hpp" #include "Mouse3DController.hpp" #include "Camera.hpp" #include "GUI_App.hpp" -#include "PresetBundle.hpp" #include "AppConfig.hpp" #include "GLCanvas3D.hpp" #include "Plater.hpp" diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 339badc96d..a78683bd4e 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -43,6 +43,7 @@ #include "libslic3r/PrintConfig.hpp" #include "libslic3r/SLAPrint.hpp" #include "libslic3r/Utils.hpp" +#include "libslic3r/PresetBundle.hpp" #include "GUI.hpp" #include "GUI_App.hpp" @@ -65,7 +66,6 @@ #include "Jobs/ArrangeJob.hpp" #include "Jobs/RotoptimizeJob.hpp" #include "Jobs/SLAImportJob.hpp" -#include "PresetBundle.hpp" #include "BackgroundSlicingProcess.hpp" #include "ProgressStatusBar.hpp" #include "PrintHostDialogs.hpp" @@ -2120,6 +2120,8 @@ std::vector Plater::priv::load_files(const std::vector& input_ if (!config.empty()) { Preset::normalize(config); wxGetApp().preset_bundle->load_config_model(filename.string(), std::move(config)); + if (printer_technology == ptFFF) + CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, &wxGetApp().preset_bundle->project_config); wxGetApp().load_current_presets(); is_project_file = true; } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index d2acc7632e..38fc679825 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -7,9 +7,9 @@ #include -#include "Preset.hpp" #include "Selection.hpp" +#include "libslic3r/Preset.hpp" #include "libslic3r/BoundingBox.hpp" #include "Jobs/Job.hpp" #include "Search.hpp" diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 380edb48ad..51b5a0c8de 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -15,6 +15,7 @@ #include "libslic3r/libslic3r.h" #include "libslic3r/PrintConfig.hpp" +#include "libslic3r/PresetBundle.hpp" #include "GUI.hpp" #include "GUI_App.hpp" @@ -22,7 +23,6 @@ #include "MainFrame.hpp" #include "format.hpp" #include "Tab.hpp" -#include "PresetBundle.hpp" #include "PrintHostDialogs.hpp" #include "ConfigWizard.hpp" #include "../Utils/ASCIIFolding.hpp" diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index 63110e4329..3d9a13490f 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -7,7 +7,7 @@ #include #include -#include "Preset.hpp" +#include "libslic3r/Preset.hpp" #include "wxExtensions.hpp" #include "GUI_Utils.hpp" diff --git a/src/slic3r/GUI/PresetHints.cpp b/src/slic3r/GUI/PresetHints.cpp index 24afeb526e..c40c4c6acb 100644 --- a/src/slic3r/GUI/PresetHints.cpp +++ b/src/slic3r/GUI/PresetHints.cpp @@ -4,7 +4,6 @@ #include "libslic3r/Slicing.hpp" #include "libslic3r/libslic3r.h" -#include "PresetBundle.hpp" #include "PresetHints.hpp" #include diff --git a/src/slic3r/GUI/PresetHints.hpp b/src/slic3r/GUI/PresetHints.hpp index be049c2c87..a61310f408 100644 --- a/src/slic3r/GUI/PresetHints.hpp +++ b/src/slic3r/GUI/PresetHints.hpp @@ -3,7 +3,7 @@ #include -#include "PresetBundle.hpp" +#include "libslic3r/PresetBundle.hpp" namespace Slic3r { diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index 613a39ccef..242e3d7256 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -9,10 +9,10 @@ #include "wx/dataview.h" #include "libslic3r/PrintConfig.hpp" +#include "libslic3r/PresetBundle.hpp" #include "GUI_App.hpp" #include "Plater.hpp" #include "Tab.hpp" -#include "PresetBundle.hpp" #define FTS_FUZZY_MATCH_IMPLEMENTATION #include "fts_fuzzy_match.h" diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp index 9701e68088..8202222e9d 100644 --- a/src/slic3r/GUI/Search.hpp +++ b/src/slic3r/GUI/Search.hpp @@ -14,8 +14,8 @@ #include #include "GUI_Utils.hpp" -#include "Preset.hpp" #include "wxExtensions.hpp" +#include "libslic3r/Preset.hpp" namespace Slic3r { diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index b128ec03d4..88c11030d6 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1,8 +1,8 @@ // #include "libslic3r/GCodeSender.hpp" #include "slic3r/Utils/Serial.hpp" #include "Tab.hpp" -#include "PresetBundle.hpp" #include "PresetHints.hpp" +#include "libslic3r/PresetBundle.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/Model.hpp" @@ -32,7 +32,6 @@ #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" -#include "ConfigWizard.hpp" #include "Plater.hpp" #include "MainFrame.hpp" #include "format.hpp" diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index a0bf536c19..bc15efa359 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -33,8 +33,8 @@ #include "Event.hpp" #include "wxExtensions.hpp" #include "ConfigManipulation.hpp" -#include "Preset.hpp" #include "OptionsGroup.hpp" +#include "libslic3r/Preset.hpp" namespace Slic3r { namespace GUI { diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index c32613c468..dec2518580 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -19,9 +19,9 @@ #include "libslic3r/libslic3r.h" #include "libslic3r/format.hpp" #include "libslic3r/Utils.hpp" +#include "libslic3r/PresetBundle.hpp" #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/I18N.hpp" -#include "slic3r/GUI/PresetBundle.hpp" #include "slic3r/GUI/UpdateDialogs.hpp" #include "slic3r/GUI/ConfigWizard.hpp" #include "slic3r/GUI/GUI_App.hpp" From 7c7dcab03299932cab681fe82996ac96e579f0df Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 18 Jun 2020 11:39:25 +0200 Subject: [PATCH 130/503] First filling of the PhysicalPrinterDialog + Fixed scaling of the icons for the BitmapComboBoxes + Fixed calling of the blinking icons on the Tabs --- src/slic3r/GUI/Field.cpp | 1 + src/slic3r/GUI/PresetComboBoxes.cpp | 46 +++++++++++++++++------------ src/slic3r/GUI/PresetComboBoxes.hpp | 7 ++--- src/slic3r/GUI/Tab.cpp | 4 ++- 4 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 3a06c3056e..8ab82e20d8 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -295,6 +295,7 @@ void Field::msw_rescale(bool rescale_sidetext) { m_Undo_to_sys_btn->msw_rescale(); m_Undo_btn->msw_rescale(); + m_blinking_bmp->msw_rescale(); // update em_unit value m_em_unit = em_unit(m_parent); diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 51b5a0c8de..6c38c866d1 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -58,7 +58,7 @@ PresetComboBox::PresetComboBox(wxWindow* parent, Preset::Type preset_type, const wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, size, 0, nullptr, wxCB_READONLY), m_type(preset_type), m_last_selected(wxNOT_FOUND), - m_em_unit(wxGetApp().em_unit()), + m_em_unit(em_unit(this)), m_preset_bundle(wxGetApp().preset_bundle), m_bitmap_cache(new BitmapCache) { @@ -99,9 +99,9 @@ PresetComboBox::PresetComboBox(wxWindow* parent, Preset::Type preset_type, const default: break; } - m_bitmapCompatible = ScalableBitmap(nullptr, "flag_green"); - m_bitmapIncompatible = ScalableBitmap(nullptr, "flag_red"); - m_bitmapLock = ScalableBitmap(nullptr, "lock_closed"); + m_bitmapCompatible = ScalableBitmap(this, "flag_green"); + m_bitmapIncompatible = ScalableBitmap(this, "flag_red"); + m_bitmapLock = ScalableBitmap(this, "lock_closed"); // parameters for an icon's drawing fill_width_height(); @@ -120,7 +120,7 @@ void PresetComboBox::set_label_marker(int item, LabelItemType label_item_type) void PresetComboBox::msw_rescale() { - m_em_unit = wxGetApp().em_unit(); + m_em_unit = em_unit(this); m_bitmapLock.msw_rescale(); m_bitmapIncompatible.msw_rescale(); @@ -241,7 +241,7 @@ PlaterPresetComboBox::PlaterPresetComboBox(wxWindow *parent, Preset::Type preset evt.StopPropagation(); if (marker == LABEL_ITEM_PHYSICAL_PRINTERS) { - PhysicalPrinterDialog dlg; + PhysicalPrinterDialog dlg(_L("New Physical Printer"), this->m_last_selected); dlg.ShowModal(); return; } @@ -360,7 +360,7 @@ PlaterPresetComboBox::~PlaterPresetComboBox() void PlaterPresetComboBox::update() { if (m_type == Preset::TYPE_FILAMENT && - (m_collection->get_edited_preset().printer_technology() == ptSLA || + (m_preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA || m_preset_bundle->filament_presets.size() <= m_extruder_idx) ) return; @@ -586,13 +586,13 @@ void PlaterPresetComboBox::msw_rescale() // --------------------------------- -// *** PlaterPresetComboBox *** +// *** TabPresetComboBox *** // --------------------------------- -TabPresetComboBox::TabPresetComboBox(wxWindow* parent, Preset::Type preset_type) : +TabPresetComboBox::TabPresetComboBox(wxWindow* parent, Preset::Type preset_type, bool is_from_physical_printer/* = false*/) : PresetComboBox(parent, preset_type, wxSize(35 * wxGetApp().em_unit(), -1)) { - Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) { + Bind(wxEVT_COMBOBOX, [this, is_from_physical_printer](wxCommandEvent& evt) { // see https://github.com/prusa3d/PrusaSlicer/issues/3889 // Under OSX: in case of use of a same names written in different case (like "ENDER" and "Ender") // m_presets_choice->GetSelection() will return first item, because search in PopupListCtrl is case-insensitive. @@ -603,9 +603,16 @@ TabPresetComboBox::TabPresetComboBox(wxWindow* parent, Preset::Type preset_type) if (marker >= LABEL_ITEM_MARKER && marker < LABEL_ITEM_MAX) { this->SetSelection(this->m_last_selected); if (marker == LABEL_ITEM_WIZARD_PRINTERS) - wxTheApp->CallAfter([]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, ConfigWizard::SP_PRINTERS); }); + wxTheApp->CallAfter([this, is_from_physical_printer]() { + wxGetApp().run_wizard(ConfigWizard::RR_USER, ConfigWizard::SP_PRINTERS); + if (is_from_physical_printer) + update(); + }); } - else if (m_last_selected != selected_item || m_collection->current_is_dirty()) { + else if ( is_from_physical_printer) { + // do nothing + } + else if (m_last_selected != selected_item || m_collection->current_is_dirty() ) { std::string selected_string = this->GetString(selected_item).ToUTF8().data(); Tab* tab = wxGetApp().get_tab(this->m_type); assert (tab); @@ -638,7 +645,7 @@ void TabPresetComboBox::update() continue; std::string bitmap_key = "tab"; - wxBitmap main_bmp = create_scaled_bitmap(m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name); + wxBitmap main_bmp = create_scaled_bitmap(m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name, this); if (m_type == Preset::TYPE_PRINTER) { bitmap_key += "_printer"; if (preset.printer_technology() == ptSLA) @@ -694,8 +701,8 @@ void TabPresetComboBox::update() if (bmp == nullptr) { // Create the bitmap with color bars. std::vector bmps; - bmps.emplace_back(create_scaled_bitmap(m_main_bitmap_name)); - bmps.emplace_back(create_scaled_bitmap("edit_uni")); + bmps.emplace_back(create_scaled_bitmap(m_main_bitmap_name, this)); + bmps.emplace_back(create_scaled_bitmap("edit_uni", this)); bmp = m_bitmap_cache->insert(bitmap_key, bmps); } set_label_marker(Append(separator(L("Add/Remove printers")), *bmp), LABEL_ITEM_WIZARD_PRINTERS); @@ -753,8 +760,8 @@ void TabPresetComboBox::update_dirty() //------------------------------------------ -PhysicalPrinterDialog::PhysicalPrinterDialog() - : DPIDialog(NULL, wxID_ANY, _L("Search"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +PhysicalPrinterDialog::PhysicalPrinterDialog(const wxString& printer_name, int last_selected_preset) + : DPIDialog(NULL, wxID_ANY, _L("PhysicalPrinter"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { SetFont(wxGetApp().normal_font()); SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); @@ -762,8 +769,9 @@ PhysicalPrinterDialog::PhysicalPrinterDialog() int border = 10; int em = em_unit(); - printer_text = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); - printer_presets = new PlaterPresetComboBox(this, Preset::TYPE_PRINTER); + printer_text = new wxTextCtrl(this, wxID_ANY, printer_name, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); + printer_presets = new TabPresetComboBox(this, Preset::TYPE_PRINTER, true); + printer_presets->update(); wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index 3d9a13490f..38b98d6586 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -43,7 +43,6 @@ public: }; void set_label_marker(int item, LabelItemType label_item_type = LABEL_ITEM_MARKER); - int em_unit() const { return m_em_unit; } virtual void update() {}; virtual void msw_rescale(); @@ -131,13 +130,13 @@ private: // --------------------------------- -// *** PlaterPresetComboBox *** +// *** TabPresetComboBox *** // --------------------------------- class TabPresetComboBox : public PresetComboBox { public: - TabPresetComboBox(wxWindow *parent, Preset::Type preset_type); + TabPresetComboBox(wxWindow *parent, Preset::Type preset_type, bool is_from_physical_printer = false); ~TabPresetComboBox() {} void set_show_incompatible_presets(bool show_incompatible_presets) { show_incompatible = show_incompatible_presets; @@ -165,7 +164,7 @@ class PhysicalPrinterDialog : public DPIDialog PresetComboBox* printer_presets; public: - PhysicalPrinterDialog(); + PhysicalPrinterDialog(const wxString& printer_name, int last_selected_preset); ~PhysicalPrinterDialog() {} protected: diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 88c11030d6..bb19e139d2 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -815,7 +815,7 @@ void Tab::update_visibility() void Tab::msw_rescale() { - m_em_unit = wxGetApp().em_unit(); + m_em_unit = em_unit(m_parent); m_mode_sizer->msw_rescale(); m_presets_choice->msw_rescale(); @@ -827,6 +827,8 @@ void Tab::msw_rescale() btn->msw_rescale(); for (const auto bmp : m_scaled_bitmaps) bmp->msw_rescale(); + for (const auto ikon : m_blinking_ikons) + ikon.second->msw_rescale(); for (ScalableBitmap& bmp : m_mode_bitmap_cache) bmp.msw_rescale(); From eb215fe994be09ae3ccaddba044a52e4169b5906 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 19 Jun 2020 15:32:44 +0200 Subject: [PATCH 131/503] ENABLE_GCODE_VIEWER_AS_STATE -> Removed tabs from gcode viewer state --- src/libslic3r/Technologies.hpp | 2 +- src/slic3r/GUI/3DBed.cpp | 16 ++++++++++++++ src/slic3r/GUI/MainFrame.cpp | 40 +++++++++++++++++++++++++++++++--- src/slic3r/GUI/MainFrame.hpp | 3 +++ 4 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 41b204d654..b04e78c4ed 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -60,7 +60,7 @@ // Enable G-Code viewer #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) #define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) -#define ENABLE_GCODE_VIEWER_SHADERS_EDITOR (1 && ENABLE_GCODE_VIEWER) +#define ENABLE_GCODE_VIEWER_SHADERS_EDITOR (0 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_AS_STATE (1 && ENABLE_GCODE_VIEWER) diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 450a538d04..16ab95d6c4 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -694,7 +694,11 @@ void Bed3D::render_default(bool bottom) const { // draw background glsafe(::glDepthMask(GL_FALSE)); +#if ENABLE_LAYOUT_NO_RESTART + glsafe(::glColor4fv(m_model_color.data())); +#else glsafe(::glColor4f(0.35f, 0.35f, 0.35f, 0.4f)); +#endif // ENABLE_LAYOUT_NO_RESTART glsafe(::glNormal3d(0.0f, 0.0f, 1.0f)); glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_vertices_data())); glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount)); @@ -702,11 +706,23 @@ void Bed3D::render_default(bool bottom) const } // draw grid +#if ENABLE_LAYOUT_NO_RESTART + glsafe(::glLineWidth(1.5f * m_scale_factor)); +#else glsafe(::glLineWidth(3.0f * m_scale_factor)); +#endif // ENABLE_LAYOUT_NO_RESTART if (has_model && !bottom) +#if ENABLE_LAYOUT_NO_RESTART + glsafe(::glColor4f(0.9f, 0.9f, 0.9f, 1.0f)); +#else glsafe(::glColor4f(0.75f, 0.75f, 0.75f, 1.0f)); +#endif // ENABLE_LAYOUT_NO_RESTART else +#if ENABLE_LAYOUT_NO_RESTART + glsafe(::glColor4f(0.9f, 0.9f, 0.9f, 0.6f)); +#else glsafe(::glColor4f(0.2f, 0.2f, 0.2f, 0.4f)); +#endif //ENABLE_LAYOUT_NO_RESTART glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_gridlines.get_vertices_data())); glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)m_gridlines.get_vertices_count())); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 00cc7d7bb2..7f591bedd8 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -323,9 +323,16 @@ void MainFrame::update_layout() Layout(); }; +#if ENABLE_GCODE_VIEWER_AS_STATE + ESettingsLayout layout = (m_mode == EMode::GCodeViewer) ? ESettingsLayout::GCodeViewer : + (wxGetApp().app_config->get("old_settings_layout_mode") == "1" ? ESettingsLayout::Old : + wxGetApp().app_config->get("new_settings_layout_mode") == "1" ? ESettingsLayout::New : + wxGetApp().app_config->get("dlg_settings_layout_mode") == "1" ? ESettingsLayout::Dlg : ESettingsLayout::Old); +#else ESettingsLayout layout = wxGetApp().app_config->get("old_settings_layout_mode") == "1" ? ESettingsLayout::Old : wxGetApp().app_config->get("new_settings_layout_mode") == "1" ? ESettingsLayout::New : wxGetApp().app_config->get("dlg_settings_layout_mode") == "1" ? ESettingsLayout::Dlg : ESettingsLayout::Old; +#endif // ENABLE_GCODE_VIEWER_AS_STATE if (m_layout == layout) return; @@ -377,6 +384,14 @@ void MainFrame::update_layout() m_plater->Show(); break; } +#if ENABLE_GCODE_VIEWER_AS_STATE + case ESettingsLayout::GCodeViewer: + { + GetSizer()->Add(m_plater, 1, wxEXPAND); + m_plater->Show(); + break; + } +#endif // ENABLE_GCODE_VIEWER_AS_STATE } //#ifdef __APPLE__ @@ -1082,15 +1097,16 @@ void MainFrame::init_menubar() #endif m_menu_item_reslice_now = append_menu_item(fileMenu, wxID_ANY, _L("(Re)Slice No&w") + "\tCtrl+R", _L("Start new slicing process"), [this](wxCommandEvent&) { reslice_now(); }, "re_slice", nullptr, - [this](){return m_plater != nullptr && can_reslice(); }, this); + [this]() { return m_plater != nullptr && can_reslice(); }, this); fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_ANY, _L("&Repair STL file") + dots, _L("Automatically repair an STL file"), [this](wxCommandEvent&) { repair_stl(); }, "wrench", nullptr, - [this]() {return true; }, this); + [this]() { return true; }, this); #if ENABLE_GCODE_VIEWER_AS_STATE fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_ANY, _L("&G-code preview"), _L("Switch to G-code preview mode"), - [this](wxCommandEvent&) { set_mode(EMode::GCodeViewer); }); + [this](wxCommandEvent&) { set_mode(EMode::GCodeViewer); }, "", nullptr, + [this]() { return m_plater != nullptr && m_plater->printer_technology() != ptSLA; }, this); #endif // ENABLE_GCODE_VIEWER_AS_STATE fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_EXIT, _L("&Quit"), wxString::Format(_L("Quit %s"), SLIC3R_APP_NAME), @@ -1381,16 +1397,30 @@ void MainFrame::init_gcodeviewer_menubar() void MainFrame::set_mode(EMode mode) { + if (m_mode == mode) + return; + + wxBusyCursor busy; + m_mode = mode; switch (m_mode) { default: case EMode::Editor: { +#if ENABLE_LAYOUT_NO_RESTART + update_layout(); + select_tab(0); +#endif // ENABLE_LAYOUT_NO_RESTART + m_plater->reset(); m_plater->Freeze(); + // reinitialize undo/redo stack + m_plater->clear_undo_redo_stack_main(); + m_plater->take_snapshot(_L("New Project")); + // switch view m_plater->select_view_3D("3D"); m_plater->select_view("iso"); @@ -1421,6 +1451,10 @@ void MainFrame::set_mode(EMode mode) } case EMode::GCodeViewer: { +#if ENABLE_LAYOUT_NO_RESTART + update_layout(); +#endif // ENABLE_LAYOUT_NO_RESTART + m_plater->reset(); m_plater->reset_last_loaded_gcode(); diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 9ec2d991a5..931dd87b28 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -138,6 +138,9 @@ class MainFrame : public DPIFrame Old, New, Dlg, +#if ENABLE_GCODE_VIEWER_AS_STATE + GCodeViewer +#endif // ENABLE_GCODE_VIEWER_AS_STATE }; ESettingsLayout m_layout{ ESettingsLayout::Unknown }; From 2a90cd2849b91111f1463d70e15fb755da38e193 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 22 Jun 2020 09:10:41 +0200 Subject: [PATCH 132/503] GCodeViewer -> Do not show modifier shells --- src/slic3r/GUI/GCodeViewer.cpp | 39 +++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 215faee594..cccb31969c 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -282,7 +282,10 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& reset(); load_toolpaths(gcode_result); - load_shells(print, initialized); +#if ENABLE_GCODE_VIEWER_AS_STATE + if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) +#endif // ENABLE_GCODE_VIEWER_AS_STATE + load_shells(print, initialized); #if ENABLE_GCODE_VIEWER_AS_STATE if (wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer) { @@ -493,21 +496,21 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) return; // vertex data / bounding box -> extract from result - std::vector vertices_data(m_vertices.vertices_count * 3); - for (size_t i = 0; i < m_vertices.vertices_count; ++i) { - const GCodeProcessor::MoveVertex& move = gcode_result.moves[i]; + std::vector vertices_data(m_vertices.vertices_count * 3); + for (size_t i = 0; i < m_vertices.vertices_count; ++i) { + const GCodeProcessor::MoveVertex& move = gcode_result.moves[i]; #if ENABLE_GCODE_VIEWER_AS_STATE - if (wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer) + if (wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer) + m_bounding_box.merge(move.position.cast()); + else { +#endif // ENABLE_GCODE_VIEWER_AS_STATE + if (move.type == GCodeProcessor::EMoveType::Extrude && move.width != 0.0f && move.height != 0.0f) m_bounding_box.merge(move.position.cast()); - else { -#endif // ENABLE_GCODE_VIEWER_AS_STATE - if (move.type == GCodeProcessor::EMoveType::Extrude && move.width != 0.0f && move.height != 0.0f) - m_bounding_box.merge(move.position.cast()); #if ENABLE_GCODE_VIEWER_AS_STATE - } -#endif // ENABLE_GCODE_VIEWER_AS_STATE - ::memcpy(static_cast(&vertices_data[i * 3]), static_cast(move.position.data()), 3 * sizeof(float)); } +#endif // ENABLE_GCODE_VIEWER_AS_STATE + ::memcpy(static_cast(&vertices_data[i * 3]), static_cast(move.position.data()), 3 * sizeof(float)); + } m_bounding_box.merge(m_bounding_box.max + m_sequential_view.marker.get_bounding_box().max[2] * Vec3d::UnitZ()); @@ -684,6 +687,18 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) } } + // remove modifiers + while (true) { + GLVolumePtrs::iterator it = std::find_if(m_shells.volumes.volumes.begin(), m_shells.volumes.volumes.end(), [](GLVolume* volume) { return volume->is_modifier; }); + if (it != m_shells.volumes.volumes.end()) + { + delete (*it); + m_shells.volumes.volumes.erase(it); + } + else + break; + } + for (GLVolume* volume : m_shells.volumes.volumes) { volume->zoom_to_volumes = false; From dc6f97a6ad473c9f137ebf30b753b298b64a9f56 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 22 Jun 2020 11:49:58 +0200 Subject: [PATCH 133/503] ENABLE_GCODE_VIEWER_AS_STATE -> Fixed toolpaths visualization when switching between states and when exporting g-code --- src/slic3r/GUI/GUI_Preview.cpp | 6 ++---- src/slic3r/GUI/MainFrame.cpp | 2 ++ src/slic3r/GUI/Plater.cpp | 26 ++++++++++++++++++++++++++ src/slic3r/GUI/Plater.hpp | 2 ++ 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 05168fc333..6ba3835075 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -510,11 +510,9 @@ void Preview::reload_print(bool keep_volumes) !keep_volumes) { m_canvas->reset_volumes(); -#if ENABLE_GCODE_VIEWER - m_canvas->reset_gcode_toolpaths(); -#else +#if !ENABLE_GCODE_VIEWER m_canvas->reset_legend_texture(); -#endif // ENABLE_GCODE_VIEWER +#endif // !ENABLE_GCODE_VIEWER m_loaded = false; #ifdef __linux__ m_volumes_cleanup_required = false; diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 7f591bedd8..2e25752a1a 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1414,6 +1414,7 @@ void MainFrame::set_mode(EMode mode) #endif // ENABLE_LAYOUT_NO_RESTART m_plater->reset(); + m_plater->reset_gcode_toolpaths(); m_plater->Freeze(); @@ -1457,6 +1458,7 @@ void MainFrame::set_mode(EMode mode) m_plater->reset(); m_plater->reset_last_loaded_gcode(); + m_plater->reset_gcode_toolpaths(); m_plater->Freeze(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index ae4c4ba000..18c185551d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1701,6 +1701,10 @@ struct Plater::priv void update_preview_moves_slider(); #endif // ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER + void reset_gcode_toolpaths(); +#endif // ENABLE_GCODE_VIEWER + void reset_all_gizmos(); void update_ui_from_settings(); void update_main_toolbar_tooltips(); @@ -4008,6 +4012,13 @@ void Plater::priv::update_preview_moves_slider() } #endif // ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER +void Plater::priv::reset_gcode_toolpaths() +{ + preview->get_canvas3d()->reset_gcode_toolpaths(); +} +#endif // ENABLE_GCODE_VIEWER + bool Plater::priv::can_set_instance_to_object() const { const int obj_idx = get_selected_object_idx(); @@ -5060,6 +5071,9 @@ void Plater::reslice() if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) != 0) return; +#if ENABLE_GCODE_VIEWER + bool clean_gcode_toolpaths = true; +#endif // ENABLE_GCODE_VIEWER if (p->background_process.running()) { if (wxGetApp().get_mode() == comSimple) @@ -5072,6 +5086,13 @@ void Plater::reslice() } else if (!p->background_process.empty() && !p->background_process.idle()) p->show_action_buttons(true); +#if ENABLE_GCODE_VIEWER + else + clean_gcode_toolpaths = false; + + if (clean_gcode_toolpaths) + reset_gcode_toolpaths(); +#endif // ENABLE_GCODE_VIEWER // update type of preview p->preview->update_view_type(true); @@ -5750,6 +5771,11 @@ void Plater::update_preview_moves_slider() { p->update_preview_moves_slider(); } + +void Plater::reset_gcode_toolpaths() +{ + p->reset_gcode_toolpaths(); +} #endif // ENABLE_GCODE_VIEWER const Mouse3DController& Plater::get_mouse3d_controller() const diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 9759a7ffc3..8d0c935a4c 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -350,6 +350,8 @@ public: #if ENABLE_GCODE_VIEWER void update_preview_bottom_toolbar(); void update_preview_moves_slider(); + + void reset_gcode_toolpaths(); #endif // ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER_AS_STATE From 88670b48fd66b8f7561d7a4b54ba71460e4fbf19 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 22 Jun 2020 12:10:18 +0200 Subject: [PATCH 134/503] ENABLE_GCODE_VIEWER_AS_STATE -> Added dialog informing user that all objects will be removed when switching to g-code viewer mode --- src/slic3r/GUI/MainFrame.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 2e25752a1a..366dc44711 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1105,7 +1105,10 @@ void MainFrame::init_menubar() #if ENABLE_GCODE_VIEWER_AS_STATE fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_ANY, _L("&G-code preview"), _L("Switch to G-code preview mode"), - [this](wxCommandEvent&) { set_mode(EMode::GCodeViewer); }, "", nullptr, + [this](wxCommandEvent&) { + if (m_plater->model().objects.empty() || wxMessageDialog((wxWindow*)this, _L("Switching to G-code preview mode will remove all objects, continue?"), wxString(SLIC3R_APP_NAME) + " - " + _L("Switch to G-code preview mode"), wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES) + set_mode(EMode::GCodeViewer); + }, "", nullptr, [this]() { return m_plater != nullptr && m_plater->printer_technology() != ptSLA; }, this); #endif // ENABLE_GCODE_VIEWER_AS_STATE fileMenu->AppendSeparator(); From 7207f215e98289bdc39c157193698b987e720a97 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 22 Jun 2020 12:43:52 +0200 Subject: [PATCH 135/503] ENABLE_GCODE_VIEWER_AS_STATE -> Do not show warning texture in gcode viewer mode --- src/slic3r/GUI/GLCanvas3D.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e7be2c4019..5c72ae0fa7 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7110,8 +7110,18 @@ void GLCanvas3D::_show_warning_texture_if_needed(WarningTexture::Warning warning show = _is_any_volume_outside(); else { - BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); - show = (test_volume.radius() > 0.0) ? !test_volume.contains(m_gcode_viewer.get_bounding_box()) : false; +#if ENABLE_GCODE_VIEWER_AS_STATE + if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) + { + BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); + const BoundingBoxf3& paths_volume = m_gcode_viewer.get_bounding_box(); + if (test_volume.radius() > 0.0 && paths_volume.radius() > 0.0) + show = !test_volume.contains(paths_volume); + } +#else + BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); + show = (test_volume.radius() > 0.0) ? !test_volume.contains(m_gcode_viewer.get_bounding_box()) : false; +#endif // ENABLE_GCODE_VIEWER_AS_STATE } _set_warning_texture(warning, show); #else From 289f7a14a016b102c91dc7bb15dbaccc07683892 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 22 Jun 2020 14:06:41 +0200 Subject: [PATCH 136/503] Follow-up of dc6f97a6ad473c9f137ebf30b753b298b64a9f56 -> Fixed toolpaths visualization when new slicing is required --- src/slic3r/GUI/GUI_Preview.cpp | 12 +++++++++--- src/slic3r/GUI/GUI_Preview.hpp | 2 +- src/slic3r/GUI/Plater.cpp | 5 ++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 6ba3835075..48d6e930b0 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -510,9 +510,11 @@ void Preview::reload_print(bool keep_volumes) !keep_volumes) { m_canvas->reset_volumes(); -#if !ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER + m_canvas->reset_gcode_toolpaths(); +#else m_canvas->reset_legend_texture(); -#endif // !ENABLE_GCODE_VIEWER +#endif // ENABLE_GCODE_VIEWER m_loaded = false; #ifdef __linux__ m_volumes_cleanup_required = false; @@ -761,7 +763,7 @@ void Preview::on_checkbox_legend(wxCommandEvent& evt) } #endif // ENABLE_GCODE_VIEWER -void Preview::update_view_type(bool slice_completed) +void Preview::update_view_type(bool keep_volumes) { const DynamicPrintConfig& config = wxGetApp().preset_bundle->project_config; @@ -785,7 +787,11 @@ void Preview::update_view_type(bool slice_completed) m_preferred_color_mode = "feature"; } +#if ENABLE_GCODE_VIEWER + reload_print(keep_volumes); +#else reload_print(); +#endif // ENABLE_GCODE_VIEWER } #if ENABLE_GCODE_VIEWER diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 291ee4156a..bf174c2e09 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -186,7 +186,7 @@ Preview(wxWindow* parent, Model* model, DynamicPrintConfig* config, void edit_double_slider(wxKeyEvent& evt); #endif // ENABLE_GCODE_VIEWER - void update_view_type(bool slice_completed); + void update_view_type(bool keep_volumes); bool is_loaded() const { return m_loaded; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 18c185551d..70ee1a7589 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5092,10 +5092,13 @@ void Plater::reslice() if (clean_gcode_toolpaths) reset_gcode_toolpaths(); -#endif // ENABLE_GCODE_VIEWER + // update type of preview + p->preview->update_view_type(!clean_gcode_toolpaths); +#else // update type of preview p->preview->update_view_type(true); +#endif // ENABLE_GCODE_VIEWER } void Plater::reslice_SLA_supports(const ModelObject &object, bool postpone_error_messages) From ca7dce9f0296da29f1d52009526d5019903d474a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 22 Jun 2020 15:42:27 +0200 Subject: [PATCH 137/503] Follow-up of dc6f97a6ad473c9f137ebf30b753b298b64a9f56 -> Fixed toolpaths visualization when editing config data --- src/slic3r/GUI/GUI_Preview.cpp | 6 ++---- src/slic3r/GUI/Plater.cpp | 10 ++++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 48d6e930b0..2ddb59b863 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -510,11 +510,9 @@ void Preview::reload_print(bool keep_volumes) !keep_volumes) { m_canvas->reset_volumes(); -#if ENABLE_GCODE_VIEWER - m_canvas->reset_gcode_toolpaths(); -#else +#if !ENABLE_GCODE_VIEWER m_canvas->reset_legend_texture(); -#endif // ENABLE_GCODE_VIEWER +#endif // !ENABLE_GCODE_VIEWER m_loaded = false; #ifdef __linux__ m_volumes_cleanup_required = false; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 70ee1a7589..bd19c94c79 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2861,10 +2861,20 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool this->sidebar->show_sliced_info_sizer(false); // Reset preview canvases. If the print has been invalidated, the preview canvases will be cleared. // Otherwise they will be just refreshed. +#if ENABLE_GCODE_VIEWER + if (this->preview != nullptr) + { + // If the preview is not visible, the following line just invalidates the preview, + // but the G-code paths or SLA preview are calculated first once the preview is made visible. + this->preview->get_canvas3d()->reset_gcode_toolpaths(); + this->preview->reload_print(); + } +#else if (this->preview != nullptr) // If the preview is not visible, the following line just invalidates the preview, // but the G-code paths or SLA preview are calculated first once the preview is made visible. this->preview->reload_print(); +#endif // ENABLE_GCODE_VIEWER // In FDM mode, we need to reload the 3D scene because of the wipe tower preview box. // In SLA mode, we need to reload the 3D scene every time to show the support structures. if (this->printer_technology == ptSLA || (this->printer_technology == ptFFF && this->config->opt_bool("wipe_tower"))) From 779dcd58c8687725cff75ec7b3540679c2c9631f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 23 Jun 2020 09:01:28 +0200 Subject: [PATCH 138/503] GCodeViewer -> Line width of toolpaths dependent on zoom --- src/slic3r/GUI/GCodeViewer.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index cccb31969c..3bba4a7723 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -870,8 +870,12 @@ void GCodeViewer::render_toolpaths() const glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); }; + auto line_width = [zoom]() { + return (zoom < 5.0) ? 1.0 : (1.0 + 5.0 * (zoom - 5.0) / (100.0 - 5.0)); + }; + glsafe(::glCullFace(GL_BACK)); - glsafe(::glLineWidth(3.0f)); + glsafe(::glLineWidth(static_cast(line_width()))); unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); unsigned char end_id = buffer_id(GCodeProcessor::EMoveType::Count); From 7e815b47277fbdea481ce5d807c3eb76d588eb6b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 23 Jun 2020 14:31:08 +0200 Subject: [PATCH 139/503] GCodeViewer -> Fixed sequential view endpoints when moving the vertical slider thumb --- src/slic3r/GUI/GUI_Preview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 2ddb59b863..530165001f 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -1440,7 +1440,7 @@ void Preview::on_sliders_scroll_changed(wxCommandEvent& event) #if ENABLE_GCODE_VIEWER void Preview::on_moves_slider_scroll_changed(wxCommandEvent& event) { - m_canvas->update_gcode_sequential_view_current(static_cast(m_moves_slider->GetLowerValueD()), static_cast(m_moves_slider->GetHigherValueD())); + m_canvas->update_gcode_sequential_view_current(static_cast(m_moves_slider->GetLowerValueD() - 1.0), static_cast(m_moves_slider->GetHigherValueD() - 1.0)); m_canvas->render(); } From 81a7b7782b3b9c7cc371011a60d45afcea4983ce Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 23 Jun 2020 15:22:52 +0200 Subject: [PATCH 140/503] GCodeViewer -> Some refactoring --- src/slic3r/GUI/GCodeViewer.cpp | 149 ++++++++++----------------------- 1 file changed, 46 insertions(+), 103 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 3bba4a7723..415b61aeb9 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -44,8 +44,7 @@ std::vector> decode_colors(const std::vector & static const float INV_255 = 1.0f / 255.0f; std::vector> output(colors.size(), { 0.0f, 0.0f, 0.0f }); - for (size_t i = 0; i < colors.size(); ++i) - { + for (size_t i = 0; i < colors.size(); ++i) { const std::string& color = colors[i]; const char* c = color.data() + 1; if ((color.size() == 7) && (color.front() == '#')) { @@ -293,11 +292,10 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& const double margin = 10.0; Vec2d min(m_bounding_box.min(0) - margin, m_bounding_box.min(1) - margin); Vec2d max(m_bounding_box.max(0) + margin, m_bounding_box.max(1) + margin); - Pointfs bed_shape = { - { min(0), min(1) }, - { max(0), min(1) }, - { max(0), max(1) }, - { min(0), max(1) } }; + Pointfs bed_shape = { { min(0), min(1) }, + { max(0), min(1) }, + { max(0), max(1) }, + { min(0), max(1) } }; wxGetApp().plater()->set_bed_shape(bed_shape, "", ""); } #endif // ENABLE_GCODE_VIEWER_AS_STATE @@ -317,8 +315,7 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: // update ranges for coloring / legend m_extrusions.reset_ranges(); - for (size_t i = 0; i < m_vertices.vertices_count; ++i) - { + for (size_t i = 0; i < m_vertices.vertices_count; ++i) { // skip first vertex if (i == 0) continue; @@ -466,8 +463,7 @@ void GCodeViewer::init_shaders() unsigned char end_id = buffer_id(GCodeProcessor::EMoveType::Count); bool is_glsl_120 = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20); - for (unsigned char i = begin_id; i < end_id; ++i) - { + for (unsigned char i = begin_id; i < end_id; ++i) { switch (buffer_type(i)) { case GCodeProcessor::EMoveType::Tool_change: { m_buffers[i].shader = is_glsl_120 ? "options_120_solid" : "options_110"; break; } @@ -530,8 +526,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) // indices data -> extract from result std::vector> indices(m_buffers.size()); - for (size_t i = 0; i < m_vertices.vertices_count; ++i) - { + for (size_t i = 0; i < m_vertices.vertices_count; ++i) { // skip first vertex if (i == 0) continue; @@ -560,10 +555,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) case GCodeProcessor::EMoveType::Travel: { if (prev.type != curr.type || !buffer.paths.back().matches(curr)) { - buffer.add_path(curr, static_cast(buffer_indices.size()), static_cast(i)); + buffer.add_path(curr, static_cast(buffer_indices.size()), static_cast(i - 1)); Path& last_path = buffer.paths.back(); last_path.first.position = prev.position; - last_path.first.s_id = static_cast(i - 1); buffer_indices.push_back(static_cast(i - 1)); } @@ -571,23 +565,18 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) buffer_indices.push_back(static_cast(i)); break; } - default: - { - break; - } + default: { break; } } } #if ENABLE_GCODE_VIEWER_STATISTICS - for (IBuffer& buffer : m_buffers) - { + for (IBuffer& buffer : m_buffers) { m_statistics.paths_size += SLIC3R_STDVEC_MEMSIZE(buffer.paths, Path); } #endif // ENABLE_GCODE_VIEWER_STATISTICS // indices data -> send data to gpu - for (size_t i = 0; i < m_buffers.size(); ++i) - { + for (size_t i = 0; i < m_buffers.size(); ++i) { IBuffer& buffer = m_buffers[i]; std::vector& buffer_indices = indices[i]; buffer.indices_count = buffer_indices.size(); @@ -605,8 +594,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) } // layers zs / roles / extruder ids / cp color ids -> extract from result - for (size_t i = 0; i < m_vertices.vertices_count; ++i) - { + for (size_t i = 0; i < m_vertices.vertices_count; ++i) { const GCodeProcessor::MoveVertex& move = gcode_result.moves[i]; if (move.type == GCodeProcessor::EMoveType::Extrude) m_layers_zs.emplace_back(static_cast(move.position[2])); @@ -655,8 +643,7 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) // adds objects' volumes int object_id = 0; - for (const PrintObject* obj : print.objects()) - { + for (const PrintObject* obj : print.objects()) { const ModelObject* model_obj = obj->model_object(); std::vector instance_ids(model_obj->instances.size()); @@ -690,8 +677,7 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) // remove modifiers while (true) { GLVolumePtrs::iterator it = std::find_if(m_shells.volumes.volumes.begin(), m_shells.volumes.volumes.end(), [](GLVolume* volume) { return volume->is_modifier; }); - if (it != m_shells.volumes.volumes.end()) - { + if (it != m_shells.volumes.volumes.end()) { delete (*it); m_shells.volumes.volumes.erase(it); } @@ -699,8 +685,7 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) break; } - for (GLVolume* volume : m_shells.volumes.volumes) - { + for (GLVolume* volume : m_shells.volumes.volumes) { volume->zoom_to_volumes = false; volume->color[3] = 0.25f; volume->force_native_color = true; @@ -841,8 +826,8 @@ void GCodeViewer::render_toolpaths() const Transform3d inv_proj = camera.get_projection_matrix().inverse(); - auto render_options = [this, zoom, inv_proj, viewport, point_size, near_plane_height](const IBuffer& buffer, EOptionsColors colors_id, GLShaderProgram& shader) { - shader.set_uniform("uniform_color", Options_Colors[static_cast(colors_id)]); + auto render_as_points = [this, zoom, inv_proj, viewport, point_size, near_plane_height](const IBuffer& buffer, EOptionsColors color_id, GLShaderProgram& shader) { + shader.set_uniform("uniform_color", Options_Colors[static_cast(color_id)]); shader.set_uniform("zoom", zoom); #if ENABLE_GCODE_VIEWER_SHADERS_EDITOR shader.set_uniform("percent_outline_radius", 0.01f * static_cast(m_shaders_editor.percent_outline)); @@ -870,6 +855,18 @@ void GCodeViewer::render_toolpaths() const glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); }; + auto render_as_lines = [this](const IBuffer& buffer, GLShaderProgram& shader) { + for (const RenderPath& path : buffer.render_paths) + { + shader.set_uniform("uniform_color", path.color); +// glsafe(::glMultiDrawElements(GL_LINES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); + glsafe(::glMultiDrawElements(GL_LINE_STRIP, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_multi_line_strip_calls_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS + } + }; + auto line_width = [zoom]() { return (zoom < 5.0) ? 1.0 : (1.0 + 5.0 * (zoom - 5.0) / (100.0 - 5.0)); }; @@ -902,62 +899,14 @@ void GCodeViewer::render_toolpaths() const switch (type) { - case GCodeProcessor::EMoveType::Tool_change: - { - render_options(buffer, EOptionsColors::ToolChanges, *shader); - break; - } - case GCodeProcessor::EMoveType::Color_change: - { - render_options(buffer, EOptionsColors::ColorChanges, *shader); - break; - } - case GCodeProcessor::EMoveType::Pause_Print: - { - render_options(buffer, EOptionsColors::PausePrints, *shader); - break; - } - case GCodeProcessor::EMoveType::Custom_GCode: - { - render_options(buffer, EOptionsColors::CustomGCodes, *shader); - break; - } - case GCodeProcessor::EMoveType::Retract: - { - render_options(buffer, EOptionsColors::Retractions, *shader); - break; - } - case GCodeProcessor::EMoveType::Unretract: - { - render_options(buffer, EOptionsColors::Unretractions, *shader); - break; - } + case GCodeProcessor::EMoveType::Tool_change: { render_as_points(buffer, EOptionsColors::ToolChanges, *shader); break; } + case GCodeProcessor::EMoveType::Color_change: { render_as_points(buffer, EOptionsColors::ColorChanges, *shader); break; } + case GCodeProcessor::EMoveType::Pause_Print: { render_as_points(buffer, EOptionsColors::PausePrints, *shader); break; } + case GCodeProcessor::EMoveType::Custom_GCode: { render_as_points(buffer, EOptionsColors::CustomGCodes, *shader); break; } + case GCodeProcessor::EMoveType::Retract: { render_as_points(buffer, EOptionsColors::Retractions, *shader); break; } + case GCodeProcessor::EMoveType::Unretract: { render_as_points(buffer, EOptionsColors::Unretractions, *shader); break; } case GCodeProcessor::EMoveType::Extrude: - { - for (const RenderPath& path : buffer.render_paths) - { - shader->set_uniform("uniform_color", path.color); - glsafe(::glMultiDrawElements(GL_LINE_STRIP, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); -#if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.gl_multi_line_strip_calls_count; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - - } - break; - } - case GCodeProcessor::EMoveType::Travel: - { - for (const RenderPath& path : buffer.render_paths) - { - shader->set_uniform("uniform_color", path.color); - glsafe(::glMultiDrawElements(GL_LINE_STRIP, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); -#if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.gl_multi_line_strip_calls_count; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - - } - break; - } + case GCodeProcessor::EMoveType::Travel: { render_as_lines(buffer, *shader); break; } } glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); @@ -1090,8 +1039,7 @@ void GCodeViewer::render_legend() const // draw text ImGui::Dummy({ icon_size, icon_size }); ImGui::SameLine(); - if (callback != nullptr) - { + if (callback != nullptr) { if (ImGui::MenuItem(label.c_str())) callback(); } @@ -1114,8 +1062,7 @@ void GCodeViewer::render_legend() const if (step_size == 0.0f) // single item use case add_range_item(0, range.min, decimals); - else - { + else { for (int i = static_cast(Range_Colors.size()) - 1; i >= 0; --i) { add_range_item(i, range.min + static_cast(i) * step_size, decimals); } @@ -1297,8 +1244,7 @@ void GCodeViewer::render_legend() const } // travel paths - if (m_buffers[buffer_id(GCodeProcessor::EMoveType::Travel)].visible) - { + if (m_buffers[buffer_id(GCodeProcessor::EMoveType::Travel)].visible) { switch (m_view_type) { case EViewType::Feedrate: @@ -1347,8 +1293,7 @@ void GCodeViewer::render_legend() const }; // options - if (any_option_visible()) - { + if (any_option_visible()) { // title ImGui::Spacing(); ImGui::Spacing(); @@ -1386,19 +1331,19 @@ void GCodeViewer::render_statistics() const imgui.text(std::string("Load time:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.load_time) + "ms"); + imgui.text(std::to_string(m_statistics.load_time) + " ms"); ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); imgui.text(std::string("Resfresh time:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.refresh_time) + "ms"); + imgui.text(std::to_string(m_statistics.refresh_time) + " ms"); ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); imgui.text(std::string("Resfresh paths time:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.refresh_paths_time) + "ms"); + imgui.text(std::to_string(m_statistics.refresh_paths_time) + " ms"); ImGui::Separator(); @@ -1496,11 +1441,9 @@ void GCodeViewer::render_shaders_editor() const case 2: { set_shader("options_120_solid"); break; } } - if (ImGui::CollapsingHeader("Options", ImGuiTreeNodeFlags_DefaultOpen)) - { + if (ImGui::CollapsingHeader("Options", ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::SliderFloat("point size", &m_shaders_editor.point_size, 0.5f, 3.0f, "%.1f"); - if (m_shaders_editor.shader_version == 1) - { + if (m_shaders_editor.shader_version == 1) { ImGui::SliderInt("percent outline", &m_shaders_editor.percent_outline, 0, 50); ImGui::SliderInt("percent center", &m_shaders_editor.percent_center, 0, 50); } From 02624689ce345dc20d24194d4c6b4d8c438d7907 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 24 Jun 2020 08:50:01 +0200 Subject: [PATCH 141/503] Physical Printers. - save/load printers - consistency between selection on Tab and Plater --- src/libslic3r/Preset.cpp | 250 ++++++++++++-------- src/libslic3r/Preset.hpp | 109 ++++----- src/libslic3r/PresetBundle.cpp | 24 +- src/libslic3r/PresetBundle.hpp | 1 + src/libslic3r/PrintConfig.cpp | 20 ++ src/slic3r/GUI/Plater.cpp | 30 ++- src/slic3r/GUI/PresetComboBoxes.cpp | 342 ++++++++++++++++++++++++---- src/slic3r/GUI/PresetComboBoxes.hpp | 37 ++- src/slic3r/GUI/Tab.cpp | 10 + 9 files changed, 600 insertions(+), 223 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index e46cd6c822..94c9577dfb 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1339,108 +1339,178 @@ const Preset* PrinterPresetCollection::find_by_model_id(const std::string &model return it != cend() ? &*it : nullptr; } -/* -PhysicalPrinter& PhysicalPrinterCollection::load_external_printer( - // Path to the profile source file (a G-code, an AMF or 3MF file, a config file) - const std::string& path, - // Name of the profile, derived from the source file name. - const std::string& name, - // Original name of the profile, extracted from the loaded config. Empty, if the name has not been stored. - const std::string& original_name, - // Config to initialize the preset from. - const DynamicPrintConfig& config, - // Select the preset after loading? - bool select) -{ - // Load the preset over a default preset, so that the missing fields are filled in from the default preset. - DynamicPrintConfig cfg(this->default_printer().config); - cfg.apply_only(config, cfg.keys(), true); - // Is there a preset already loaded with the name stored inside the config? - std::deque::iterator it = this->find_printer_internal(original_name); - bool found = it != m_printers.end() && it->name == original_name; - if (!found) { - // Try to match the original_name against the "renamed_from" profile names of loaded system profiles. - / * - it = this->find_preset_renamed(original_name); - found = it != m_presets.end(); - * / - } - if (found) { - if (profile_print_params_same(it->config, cfg)) { - // The preset exists and it matches the values stored inside config. - if (select) - this->select_printer(it - m_printers.begin()); - return *it; - } - if (profile_host_params_same_or_anonymized(it->config, cfg) == ProfileHostParams::Anonymized) { - // The project being loaded is anonymized. Replace the empty host keys of the loaded profile with the data from the original profile. - // See "Octoprint Settings when Opening a .3MF file" GH issue #3244 - auto opt_update = [it, &cfg](const std::string& opt_key) { - auto opt = it->config.option(opt_key); - if (opt != nullptr) - cfg.set_key_value(opt_key, opt->clone()); - }; - opt_update("print_host"); - opt_update("printhost_apikey"); - opt_update("printhost_cafile"); - } - } - // The external preset does not match an internal preset, load the external preset. - std::string new_name; - for (size_t idx = 0;; ++idx) { - std::string suffix; - if (original_name.empty()) { - if (idx > 0) - suffix = " (" + std::to_string(idx) + ")"; - } - else { - if (idx == 0) - suffix = " (" + original_name + ")"; - else - suffix = " (" + original_name + "-" + std::to_string(idx) + ")"; - } - new_name = name + suffix; - it = this->find_printer_internal(new_name); - if (it == m_printers.end() || it->name != new_name) - // Unique profile name. Insert a new profile. - break; - if (profile_print_params_same(it->config, cfg)) { - // The preset exists and it matches the values stored inside config. - if (select) - this->select_printer(it - m_printers.begin()); - return *it; - } - // Form another profile name. - } - // Insert a new profile. - PhysicalPrinter& printer = this->load_printer(path, new_name, std::move(cfg), select); - return printer; +// ------------------------- +// *** PhysicalPrinter *** +// ------------------------- + +const std::vector& PhysicalPrinter::printer_options() +{ + static std::vector s_opts; + if (s_opts.empty()) { + s_opts = { + "preset_name", + "printer_technology", + "host_type", + "print_host", + "printhost_apikey", + "printhost_cafile", + "login", + "password" + }; + } + return s_opts; } -void PhysicalPrinterCollection::save_printer(const std::string& new_name) +const std::string& PhysicalPrinter::get_preset_name() +{ + return config.opt_string("preset_name"); +} + +void PhysicalPrinter::update_from_preset(const Preset& preset) +{ + config.apply_only(preset.config, printer_options(), false); + // add preset name to the options list + config.set_key_value("preset_name", new ConfigOptionString(preset.name)); +} + +void PhysicalPrinter::update_from_config(const DynamicPrintConfig& new_config) +{ + config.apply_only(new_config, printer_options(), false); +} + +PhysicalPrinter::PhysicalPrinter(const std::string& name, const Preset& preset) : + name(name) +{ + update_from_preset(preset); +} + + +// ----------------------------------- +// *** PhysicalPrinterCollection *** +// ----------------------------------- + +PhysicalPrinterCollection::PhysicalPrinterCollection( const std::vector& keys) +{ +} + +// Load all presets found in dir_path. +// Throws an exception on error. +void PhysicalPrinterCollection::load_printers(const std::string& dir_path, const std::string& subdir) +{ + boost::filesystem::path dir = boost::filesystem::canonical(boost::filesystem::path(dir_path) / subdir).make_preferred(); + m_dir_path = dir.string(); + std::string errors_cummulative; + // Store the loaded printers into a new vector, otherwise the binary search for already existing presets would be broken. + std::deque printers_loaded; + for (auto& dir_entry : boost::filesystem::directory_iterator(dir)) + if (Slic3r::is_ini_file(dir_entry)) { + std::string name = dir_entry.path().filename().string(); + // Remove the .ini suffix. + name.erase(name.size() - 4); + if (this->find_printer(name, false)) { + // This happens when there's is a preset (most likely legacy one) with the same name as a system preset + // that's already been loaded from a bundle. + BOOST_LOG_TRIVIAL(warning) << "Preset already present, not loading: " << name; + continue; + } + try { + PhysicalPrinter printer(name); + printer.file = dir_entry.path().string(); + // Load the preset file, apply preset values on top of defaults. + try { + DynamicPrintConfig config; + config.load_from_ini(printer.file); + printer.update_from_config(config); + printer.loaded = true; + } + catch (const std::ifstream::failure& err) { + throw std::runtime_error(std::string("The selected preset cannot be loaded: ") + printer.file + "\n\tReason: " + err.what()); + } + catch (const std::runtime_error& err) { + throw std::runtime_error(std::string("Failed loading the preset file: ") + printer.file + "\n\tReason: " + err.what()); + } + printers_loaded.emplace_back(printer); + } + catch (const std::runtime_error& err) { + errors_cummulative += err.what(); + errors_cummulative += "\n"; + } + } + m_printers.insert(m_printers.end(), std::make_move_iterator(printers_loaded.begin()), std::make_move_iterator(printers_loaded.end())); + std::sort(m_printers.begin(), m_printers.end()); +//! this->select_preset(first_visible_idx()); + if (!errors_cummulative.empty()) + throw std::runtime_error(errors_cummulative); +} + +PhysicalPrinter* PhysicalPrinterCollection::find_printer( const std::string& name, bool first_visible_if_not_found) +{ + PhysicalPrinter key(name); + auto it = this->find_printer_internal(name); + // Ensure that a temporary copy is returned if the preset found is currently selected. + return (it != m_printers.end() && it->name == key.name) ? &this->printer(it - m_printers.begin()) : + first_visible_if_not_found ? &this->printer(0) : nullptr; +} + +// Generate a file path from a profile name. Add the ".ini" suffix if it is missing. +std::string PhysicalPrinterCollection::path_from_name(const std::string& new_name) const +{ + std::string file_name = boost::iends_with(new_name, ".ini") ? new_name : (new_name + ".ini"); + return (boost::filesystem::path(m_dir_path) / file_name).make_preferred().string(); +} + +void PhysicalPrinterCollection::save_printer(const PhysicalPrinter& edited_printer) { // 1) Find the printer with a new_name or create a new one, // initialize it with the edited config. - auto it = this->find_printer_internal(new_name); - if (it != m_printers.end() && it->name == new_name) { - // Preset with the same name found. - PhysicalPrinter& printer = *it; + auto it = this->find_printer_internal(edited_printer.name); + if (it != m_printers.end() && it->name == edited_printer.name) { + // Printer with the same name found. // Overwriting an existing preset. - printer.config = std::move(m_edited_printer.config); + it->config = std::move(edited_printer.config); } else { // Creating a new printer. - PhysicalPrinter& printer = *m_printers.insert(it, m_edited_printer); - std::string old_name = printer.name; - printer.name = new_name; + it = m_printers.insert(it, edited_printer); } - // 2) Activate the saved preset. - this->select_printer_by_name(new_name, true); - // 3) Store the active preset to disk. - this->get_selected_preset().save(); + assert(it != m_printers.end()); + + // 2) Save printer + PhysicalPrinter& printer = *it; + if (printer.file.empty()) + printer.file = this->path_from_name(printer.name); + printer.save(); + + // update idx_selected + m_idx_selected = it - m_printers.begin(); } -*/ + +bool PhysicalPrinterCollection::delete_printer(const std::string& name) +{ + auto it = this->find_printer_internal(name); + + const PhysicalPrinter& printer = *it; + if (it == m_printers.end()) + return false; + + // Erase the preset file. + boost::nowide::remove(printer.file.c_str()); + m_printers.erase(it); + return true; +} + +PhysicalPrinter& PhysicalPrinterCollection::select_printer_by_name(const std::string& name) +{ + auto it = this->find_printer_internal(name); + assert(it != m_printers.end()); + + // update idx_selected + m_idx_selected = it - m_printers.begin(); + return *it; +} + + namespace PresetUtils { const VendorProfile::PrinterModel* system_printer_model(const Preset &preset) { diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index b0af2f142b..c08a1a0fbc 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -535,14 +535,14 @@ namespace PresetUtils { class PhysicalPrinter { public: - PhysicalPrinter(const std::string& name) : name(name) {} + PhysicalPrinter() {} + PhysicalPrinter(const std::string& name) : name(name){} + PhysicalPrinter(const std::string& name, const Preset& preset); // Name of the Physical Printer, usually derived form the file name. std::string name; // File name of the Physical Printer. std::string file; - // Name of the related Printer preset - std::string preset_name; // Has this profile been loaded? bool loaded = false; @@ -550,7 +550,13 @@ public: // Configuration data, loaded from a file, or set from the defaults. DynamicPrintConfig config; + static const std::vector& printer_options(); + const std::string& get_preset_name(); + void save() { this->config.save(this->file); } + void save_to(const std::string& file_name) const { this->config.save(file_name); } + void update_from_preset(const Preset& preset); + void update_from_config(const DynamicPrintConfig &new_config); // Return a printer technology, return ptFFF if the printer technology is not set. static PrinterTechnology printer_technology(const DynamicPrintConfig& cfg) { @@ -562,18 +568,22 @@ public: PrinterTechnology printer_technology() const { return printer_technology(this->config); } // Sort lexicographically by a preset name. The preset name shall be unique across a single PresetCollection. - bool operator<(const Preset& other) const { return this->name < other.name; } + bool operator<(const PhysicalPrinter& other) const { return this->name < other.name; } protected: friend class PhysicalPrinterCollection; }; -/* -// Collections of presets of the same type (one of the Print, Filament or Printer type). + + +// --------------------------------- +// *** PhysicalPrinterCollection *** +// --------------------------------- + +// Collections of physical printers class PhysicalPrinterCollection { public: - // Initialize the PresetCollection with the "- default -" preset. - PhysicalPrinterCollection(const std::vector& keys) : m_idx_selected(0) {} + PhysicalPrinterCollection(const std::vector& keys); ~PhysicalPrinterCollection() {} typedef std::deque::iterator Iterator; @@ -585,63 +595,39 @@ public: ConstIterator end() const { return m_printers.cend(); } ConstIterator cend() const { return m_printers.cend(); } + bool empty() const {return m_printers.empty(); } + void reset(bool delete_files) {}; const std::deque& operator()() const { return m_printers; } // Load ini files of the particular type from the provided directory path. - void load_printers(const std::string& dir_path, const std::string& subdir){}; - - // Load a preset from an already parsed config file, insert it into the sorted sequence of presets - // and select it, losing previous modifications. - PhysicalPrinter& load_printer(const std::string& path, const std::string& name, const DynamicPrintConfig& config, bool select = true); - PhysicalPrinter& load_printer(const std::string& path, const std::string& name, DynamicPrintConfig&& config, bool select = true); - - PhysicalPrinter& load_external_printer( - // Path to the profile source file (a G-code, an AMF or 3MF file, a config file) - const std::string& path, - // Name of the profile, derived from the source file name. - const std::string& name, - // Original name of the profile, extracted from the loaded config. Empty, if the name has not been stored. - const std::string& original_name, - // Config to initialize the preset from. - const DynamicPrintConfig& config, - // Select the preset after loading? - bool select = true); + void load_printers(const std::string& dir_path, const std::string& subdir); // Save the printer under a new name. If the name is different from the old one, // a new printer is stored into the list of printers. - // ? New printer is activated. - void save_printer(const std::string& new_name); + // New printer is activated. + void save_printer(const PhysicalPrinter& printer); // Delete the current preset, activate the first visible preset. // returns true if the preset was deleted successfully. - bool delete_current_printer() {return true;} - // Delete the current preset, activate the first visible preset. - // returns true if the preset was deleted successfully. - bool delete_printer(const std::string& name) { return true; } + bool delete_printer(const std::string& name); - // Select a printer. If an invalid index is provided, the first visible printer is selected. - PhysicalPrinter& select_printer(size_t idx); // Return the selected preset, without the user modifications applied. - PhysicalPrinter& get_selected_preset() { return m_printers[m_idx_selected]; } - const PhysicalPrinter& get_selected_preset() const { return m_printers[m_idx_selected]; } + PhysicalPrinter& get_selected_printer() { return m_printers[m_idx_selected]; } + const PhysicalPrinter& get_selected_printer() const { return m_printers[m_idx_selected]; } size_t get_selected_idx() const { return m_idx_selected; } // Returns the name of the selected preset, or an empty string if no preset is selected. - std::string get_selected_preset_name() const { return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_preset().name; } - PhysicalPrinter& get_edited_preset() { return m_edited_printer; } - const PhysicalPrinter& get_edited_preset() const { return m_edited_printer; } + std::string get_selected_printer_name() const { return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_printer().name; } + DynamicPrintConfig* get_selected_printer_config() { return (m_idx_selected == size_t(-1)) ? nullptr : &(this->get_selected_printer().config); } - // Return a preset possibly with modifications. - PhysicalPrinter& default_printer(size_t idx = 0) { return m_printers[idx]; } - const PhysicalPrinter& default_printer(size_t idx = 0) const { return m_printers[idx]; } + // select printer with name and return reference on it + PhysicalPrinter& select_printer_by_name(const std::string& name); + bool has_selection() const { return m_idx_selected != size_t(-1); } + void unselect_printer() { m_idx_selected = size_t(-1); } - // used to update preset_choice from Tab - const std::deque& get_presets() const { return m_printers; } - size_t get_idx_selected() { return m_idx_selected; } - - // Return a preset by an index. If the preset is active, a temporary copy is returned. - PhysicalPrinter& printer(size_t idx) { return (idx == m_idx_selected) ? m_edited_printer : m_printers[idx]; } + // Return a printer by an index. If the printer is active, a temporary copy is returned. + PhysicalPrinter& printer(size_t idx) { return m_printers[idx]; } const PhysicalPrinter& printer(size_t idx) const { return const_cast(this)->printer(idx); } // Return a preset by its name. If the preset is active, a temporary copy is returned. @@ -652,20 +638,10 @@ public: return const_cast(this)->find_printer(name, first_visible_if_not_found); } - // Return number of presets including the "- default -" preset. - size_t size() const { return m_printers.size(); } - - // Select a profile by its name. Return true if the selection changed. - // Without force, the selection is only updated if the index changes. - // With force, the changes are reverted if the new index is the same as the old index. - bool select_printer_by_name(const std::string& name, bool force) {}; - // Generate a file path from a profile name. Add the ".ini" suffix if it is missing. std::string path_from_name(const std::string& new_name) const; private: -// PhysicalPrinterCollection(); - PhysicalPrinterCollection(const PhysicalPrinterCollection& other); PhysicalPrinterCollection& operator=(const PhysicalPrinterCollection& other); // Find a preset position in the sorted list of presets. @@ -674,8 +650,8 @@ private: // If a preset does not exist, an iterator is returned indicating where to insert a preset with the same name. std::deque::iterator find_printer_internal(const std::string& name) { - PhysicalPrinter key(name); - auto it = std::lower_bound(m_printers.begin()+0, m_printers.end(), key); + PhysicalPrinter printer(name); + auto it = std::lower_bound(m_printers.begin(), m_printers.end(), printer); return it; } std::deque::const_iterator find_printer_internal(const std::string& name) const @@ -683,23 +659,18 @@ private: return const_cast(this)->find_printer_internal(name); } - static std::vector dirty_options(const Preset* edited, const Preset* reference, const bool is_printer_type = false); - - // List of presets, starting with the "- default -" preset. + // List of printers // Use deque to force the container to allocate an object per each entry, // so that the addresses of the presets don't change during resizing of the container. std::deque m_printers; - // Initially this printer contains a copy of the selected printer. Later on, this copy may be modified by the user. - PhysicalPrinter m_edited_printer; - // Selected preset. - size_t m_idx_selected; + + // Selected printer. + size_t m_idx_selected = size_t(-1); // Path to the directory to store the config files into. std::string m_dir_path; }; -////////////////////////////////////////////////////////////////////// -*/ } // namespace Slic3r diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 9da7731a45..7c4fa1cb25 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -41,7 +41,8 @@ PresetBundle::PresetBundle() : filaments(Preset::TYPE_FILAMENT, Preset::filament_options(), static_cast(FullPrintConfig::defaults())), sla_materials(Preset::TYPE_SLA_MATERIAL, Preset::sla_material_options(), static_cast(SLAFullPrintConfig::defaults())), sla_prints(Preset::TYPE_SLA_PRINT, Preset::sla_print_options(), static_cast(SLAFullPrintConfig::defaults())), - printers(Preset::TYPE_PRINTER, Preset::printer_options(), static_cast(FullPrintConfig::defaults()), "- default FFF -") + printers(Preset::TYPE_PRINTER, Preset::printer_options(), static_cast(FullPrintConfig::defaults()), "- default FFF -"), + physical_printers(PhysicalPrinter::printer_options()) { // The following keys are handled by the UI, they do not have a counterpart in any StaticPrintConfig derived classes, // therefore they need to be handled differently. As they have no counterpart in StaticPrintConfig, they are not being @@ -139,14 +140,16 @@ void PresetBundle::setup_directories() data_dir / "presets" / "filament", data_dir / "presets" / "sla_print", data_dir / "presets" / "sla_material", - data_dir / "presets" / "printer" + data_dir / "presets" / "printer", + data_dir / "presets" / "physical_printer" #else // Store the print/filament/printer presets at the same location as the upstream Slic3r. data_dir / "print", data_dir / "filament", data_dir / "sla_print", data_dir / "sla_material", - data_dir / "printer" + data_dir / "printer", + data_dir / "physical_printer" #endif }; for (const boost::filesystem::path &path : paths) { @@ -196,6 +199,11 @@ void PresetBundle::load_presets(AppConfig &config, const std::string &preferred_ } catch (const std::runtime_error &err) { errors_cummulative += err.what(); } + try { + this->physical_printers.load_printers(dir_user_presets, "physical_printer"); + } catch (const std::runtime_error &err) { + errors_cummulative += err.what(); + } this->update_multi_material_filament_presets(); this->update_compatible(PresetSelectCompatibleType::Never); if (! errors_cummulative.empty()) @@ -422,6 +430,14 @@ void PresetBundle::load_selections(AppConfig &config, const std::string &preferr // exist. this->update_compatible(PresetSelectCompatibleType::Always); this->update_multi_material_filament_presets(); + + // Parse the initial physical printer name. + std::string initial_physical_printer_name = remove_ini_suffix(config.get("extras", "physical_printer")); + + // Activate physical printer from the config + const PhysicalPrinter* initial_physical_printer = physical_printers.find_printer(initial_physical_printer_name); + if (initial_physical_printer) + physical_printers.select_printer_by_name(initial_physical_printer_name); } // Export selections (current print, current filaments, current printer) into config.ini @@ -441,6 +457,8 @@ void PresetBundle::export_selections(AppConfig &config) config.set("presets", "sla_print", sla_prints.get_selected_preset_name()); config.set("presets", "sla_material", sla_materials.get_selected_preset_name()); config.set("presets", "printer", printers.get_selected_preset_name()); + + config.set("extras", "physical_printer", physical_printers.get_selected_printer_name()); } DynamicPrintConfig PresetBundle::full_config() const diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp index 19d4093d63..2906584d33 100644 --- a/src/libslic3r/PresetBundle.hpp +++ b/src/libslic3r/PresetBundle.hpp @@ -39,6 +39,7 @@ public: PresetCollection& materials(PrinterTechnology pt) { return pt == ptFFF ? this->filaments : this->sla_materials; } const PresetCollection& materials(PrinterTechnology pt) const { return pt == ptFFF ? this->filaments : this->sla_materials; } PrinterPresetCollection printers; + PhysicalPrinterCollection physical_printers; // Filament preset names for a multi-extruder or multi-material print. // extruders.size() should be the same as printers.get_edited_preset().config.nozzle_diameter.size() std::vector filament_presets; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index d6a23d75d3..93ef170fb3 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -130,6 +130,26 @@ void PrintConfigDef::init_common_params() def->min = 0; def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(0.2)); + + // Options used by physical printers + + def = this->add("login", coString); + def->label = L("Login"); +// def->tooltip = L(""); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionString("")); + + def = this->add("password", coString); + def->label = L("Password"); +// def->tooltip = L(""); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionString("")); + + def = this->add("preset_name", coString); + def->label = L("Printer preset name"); + def->tooltip = L("Related printer preset name"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionString("")); } void PrintConfigDef::init_fff_params() diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a78683bd4e..048e17926e 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -942,8 +942,6 @@ void Sidebar::msw_rescale() void Sidebar::sys_color_changed() { - // Update preset comboboxes in respect to the system color ... - // combo->msw_rescale() updates icon on button, so use it for (PlaterPresetComboBox* combo : std::vector{ p->combo_print, p->combo_sla_print, p->combo_sla_material, @@ -952,12 +950,8 @@ void Sidebar::sys_color_changed() for (PlaterPresetComboBox* combo : p->combos_filament) combo->msw_rescale(); - // ... then refill them and set min size to correct layout of the sidebar - update_all_preset_comboboxes(); - p->object_list->sys_color_changed(); p->object_manipulation->sys_color_changed(); -// p->object_settings->msw_rescale(); p->object_layers->sys_color_changed(); // btn...->msw_rescale() updates icon on button, so use it @@ -3208,12 +3202,23 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt) //! instead of //! combo->GetStringSelection().ToUTF8().data()); - const std::string preset_name = wxGetApp().preset_bundle->get_preset_name_by_alias(preset_type, + std::string preset_name = wxGetApp().preset_bundle->get_preset_name_by_alias(preset_type, Preset::remove_suffix_modified(combo->GetString(selection).ToUTF8().data())); if (preset_type == Preset::TYPE_FILAMENT) { wxGetApp().preset_bundle->set_filament_preset(idx, preset_name); } + + if (preset_type == Preset::TYPE_PRINTER) { + if(combo->is_selected_physical_printer()) { + // Select related printer preset on the Printer Settings Tab + const std::string printer_name = combo->GetString(selection).ToUTF8().data(); + PhysicalPrinter& printer = wxGetApp().preset_bundle->physical_printers.select_printer_by_name(printer_name); + preset_name = wxGetApp().preset_bundle->get_preset_name_by_alias(preset_type, printer.get_preset_name()); + } + else + wxGetApp().preset_bundle->physical_printers.unselect_printer(); + } // TODO: ? if (preset_type == Preset::TYPE_FILAMENT && sidebar->is_multifilament()) { @@ -3974,7 +3979,12 @@ void Plater::priv::show_action_buttons(const bool ready_to_slice) const this->ready_to_slice = ready_to_slice; wxWindowUpdateLocker noUpdater(sidebar); - const auto prin_host_opt = config->option("print_host"); + + DynamicPrintConfig* selected_printer_config = wxGetApp().preset_bundle->physical_printers.get_selected_printer_config(); + if (!selected_printer_config) + selected_printer_config = config; + + const auto prin_host_opt = selected_printer_config->option("print_host"); const bool send_gcode_shown = prin_host_opt != nullptr && !prin_host_opt->value.empty(); // when a background processing is ON, export_btn and/or send_btn are showing @@ -4893,7 +4903,9 @@ void Plater::send_gcode() { if (p->model.objects.empty()) { return; } - PrintHostJob upload_job(p->config); + // if physical_printer is selected, send gcode for this printer + DynamicPrintConfig* physical_printer_config = wxGetApp().preset_bundle->physical_printers.get_selected_printer_config(); + PrintHostJob upload_job(physical_printer_config ? physical_printer_config : p->config); if (upload_job.empty()) { return; } // Obtain default output path diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 6c38c866d1..ce25d56901 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -31,6 +31,7 @@ #include "../Utils/UndoRedo.hpp" #include "RemovableDriveManager.hpp" #include "BitmapCache.hpp" +#include "BonjourDialog.hpp" using Slic3r::GUI::format_wxstr; @@ -241,8 +242,9 @@ PlaterPresetComboBox::PlaterPresetComboBox(wxWindow *parent, Preset::Type preset evt.StopPropagation(); if (marker == LABEL_ITEM_PHYSICAL_PRINTERS) { - PhysicalPrinterDialog dlg(_L("New Physical Printer"), this->m_last_selected); - dlg.ShowModal(); + PhysicalPrinterDialog dlg(wxEmptyString); + if (dlg.ShowModal() == wxID_OK) + this->update(); return; } if (marker >= LABEL_ITEM_WIZARD_PRINTERS) { @@ -255,7 +257,7 @@ PlaterPresetComboBox::PlaterPresetComboBox(wxWindow *parent, Preset::Type preset } wxTheApp->CallAfter([sp]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, sp); }); } - } else if ( this->m_last_selected != selected_item || m_collection->current_is_dirty() ) { + } else if (marker == LABEL_ITEM_PHYSICAL_PRINTER || this->m_last_selected != selected_item || m_collection->current_is_dirty() ) { this->m_last_selected = selected_item; evt.SetInt(this->m_type); evt.Skip(); @@ -319,6 +321,16 @@ PlaterPresetComboBox::PlaterPresetComboBox(wxWindow *parent, Preset::Type preset edit_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent) { + // In a case of a physical printer, for its editing open PhysicalPrinterDialog + if (m_type == Preset::TYPE_PRINTER && this->is_selected_physical_printer()) + { + PhysicalPrinterDialog dlg(this->GetString(this->GetSelection())); + if (dlg.ShowModal() == wxID_OK) { + update(); + return; + } + } + Tab* tab = wxGetApp().get_tab(m_type); if (!tab) return; @@ -355,6 +367,13 @@ PlaterPresetComboBox::~PlaterPresetComboBox() edit_btn->Destroy(); } +bool PlaterPresetComboBox::is_selected_physical_printer() +{ + auto selected_item = this->GetSelection(); + auto marker = reinterpret_cast(this->GetClientData(selected_item)); + return marker == LABEL_ITEM_PHYSICAL_PRINTER; +} + // Only the compatible presets are shown. // If an incompatible preset is selected, it is shown as well. void PlaterPresetComboBox::update() @@ -388,7 +407,6 @@ void PlaterPresetComboBox::update() bool wide_icons = !selected_preset.is_compatible; std::map nonsys_presets; - std::map physical_printers; wxString selected = ""; wxString tooltip = ""; @@ -400,9 +418,11 @@ void PlaterPresetComboBox::update() for (size_t i = presets.front().is_visible ? 0 : m_collection->num_default_presets(); i < presets.size(); ++i) { const Preset& preset = presets[i]; - bool is_selected = m_type == Preset::TYPE_FILAMENT ? - m_preset_bundle->filament_presets[m_extruder_idx] == preset.name : - i == m_collection->get_selected_idx(); + bool is_selected = m_type == Preset::TYPE_FILAMENT ? + m_preset_bundle->filament_presets[m_extruder_idx] == preset.name : + // The case, when some physical printer is selected + m_type == Preset::TYPE_PRINTER && m_preset_bundle->physical_printers.has_selection() ? false : + i == m_collection->get_selected_idx(); if (!preset.is_visible || (!preset.is_compatible && !is_selected)) continue; @@ -495,17 +515,6 @@ void PlaterPresetComboBox::update() selected_preset_item = GetCount() - 1; } } - if (!physical_printers.empty()) - { - set_label_marker(Append(separator(L("Physical printers")), wxNullBitmap)); - for (std::map::iterator it = physical_printers.begin(); it != physical_printers.end(); ++it) { - Append(it->first, *it->second); - if (it->first == selected || - // just in case: mark selected_preset_item as a first added element - selected_preset_item == INT_MAX) - selected_preset_item = GetCount() - 1; - } - } if (m_type == Preset::TYPE_PRINTER || m_type == Preset::TYPE_SLA_MATERIAL) { std::string bitmap_key = ""; @@ -536,8 +545,45 @@ void PlaterPresetComboBox::update() else set_label_marker(Append(separator(L("Add/Remove printers")), *bmp), LABEL_ITEM_WIZARD_PRINTERS); } - if (m_type == Preset::TYPE_PRINTER) { - std::string bitmap_key = ""; + + if (m_type == Preset::TYPE_PRINTER) + { + // add Physical printers, if any exists + if (!m_preset_bundle->physical_printers.empty()) { + set_label_marker(Append(separator(L("Physical printers")), wxNullBitmap)); + const PhysicalPrinterCollection& ph_printers = m_preset_bundle->physical_printers; + + for (PhysicalPrinterCollection::ConstIterator it = ph_printers.begin(); it != ph_printers.end(); ++it) { + std::string bitmap_key = it->printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; + if (wide_icons) + bitmap_key += "wide,"; + bitmap_key += "-h" + std::to_string(icon_height); + + wxBitmap* bmp = m_bitmap_cache->find(bitmap_key); + if (bmp == nullptr) { + // Create the bitmap with color bars. + std::vector bmps; + if (wide_icons) + // Paint a red flag for incompatible presets. + bmps.emplace_back(m_bitmap_cache->mkclear(norm_icon_width, icon_height)); + // Paint the color bars. + bmps.emplace_back(m_bitmap_cache->mkclear(thin_space_icon_width, icon_height)); + bmps.emplace_back(create_scaled_bitmap(it->printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name)); + // Paint a lock at the system presets. + bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width+norm_icon_width, icon_height)); + bmp = m_bitmap_cache->insert(bitmap_key, bmps); + } + + set_label_marker(Append(wxString::FromUTF8((it->name).c_str()), *bmp), LABEL_ITEM_PHYSICAL_PRINTER); + if (ph_printers.has_selection() && it->name == ph_printers.get_selected_printer_name() || + // just in case: mark selected_preset_item as a first added element + selected_preset_item == INT_MAX) + selected_preset_item = GetCount() - 1; + } + } + + // add LABEL_ITEM_PHYSICAL_PRINTERS + std::string bitmap_key; if (wide_icons) bitmap_key += "wide,"; bitmap_key += "edit_preset_list"; @@ -589,10 +635,10 @@ void PlaterPresetComboBox::msw_rescale() // *** TabPresetComboBox *** // --------------------------------- -TabPresetComboBox::TabPresetComboBox(wxWindow* parent, Preset::Type preset_type, bool is_from_physical_printer/* = false*/) : +TabPresetComboBox::TabPresetComboBox(wxWindow* parent, Preset::Type preset_type) : PresetComboBox(parent, preset_type, wxSize(35 * wxGetApp().em_unit(), -1)) { - Bind(wxEVT_COMBOBOX, [this, is_from_physical_printer](wxCommandEvent& evt) { + Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) { // see https://github.com/prusa3d/PrusaSlicer/issues/3889 // Under OSX: in case of use of a same names written in different case (like "ENDER" and "Ender") // m_presets_choice->GetSelection() will return first item, because search in PopupListCtrl is case-insensitive. @@ -603,21 +649,17 @@ TabPresetComboBox::TabPresetComboBox(wxWindow* parent, Preset::Type preset_type, if (marker >= LABEL_ITEM_MARKER && marker < LABEL_ITEM_MAX) { this->SetSelection(this->m_last_selected); if (marker == LABEL_ITEM_WIZARD_PRINTERS) - wxTheApp->CallAfter([this, is_from_physical_printer]() { + wxTheApp->CallAfter([this]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, ConfigWizard::SP_PRINTERS); - if (is_from_physical_printer) + + // update combobox if its parent is a PhysicalPrinterDialog + PhysicalPrinterDialog* parent = dynamic_cast(this->GetParent()); + if (parent != nullptr) update(); }); } - else if ( is_from_physical_printer) { - // do nothing - } - else if (m_last_selected != selected_item || m_collection->current_is_dirty() ) { - std::string selected_string = this->GetString(selected_item).ToUTF8().data(); - Tab* tab = wxGetApp().get_tab(this->m_type); - assert (tab); - tab->select_preset(selected_string); - } + else if (on_selection_changed && (m_last_selected != selected_item || m_collection->current_is_dirty()) ) + on_selection_changed(selected_item); evt.StopPropagation(); }); @@ -760,35 +802,212 @@ void TabPresetComboBox::update_dirty() //------------------------------------------ -PhysicalPrinterDialog::PhysicalPrinterDialog(const wxString& printer_name, int last_selected_preset) +PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) : DPIDialog(NULL, wxID_ANY, _L("PhysicalPrinter"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { SetFont(wxGetApp().normal_font()); SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); int border = 10; - int em = em_unit(); - printer_text = new wxTextCtrl(this, wxID_ANY, printer_name, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); - printer_presets = new TabPresetComboBox(this, Preset::TYPE_PRINTER, true); - printer_presets->update(); + m_printer_presets = new TabPresetComboBox(this, Preset::TYPE_PRINTER); + m_printer_presets->set_selection_changed_function([this](int selection) { + std::string selected_string = Preset::remove_suffix_modified(m_printer_presets->GetString(selection).ToUTF8().data()); + Preset* preset = wxGetApp().preset_bundle->printers.find_preset(selected_string); + assert(preset); + Preset& edited_preset = wxGetApp().preset_bundle->printers.get_edited_preset(); + if (preset->name == edited_preset.name) + preset = &edited_preset; + m_printer.update_from_preset(*preset); + + // update values + m_optgroup->reload_config(); + update_octoprint_visible(); + }); + m_printer_presets->update(); + + wxString preset_name = m_printer_presets->GetString(m_printer_presets->GetSelection()); + + if (printer_name.IsEmpty()) + printer_name = preset_name + " - "+_L("Physical Printer"); + m_printer_name = new wxTextCtrl(this, wxID_ANY, printer_name, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); + + PhysicalPrinterCollection& printers = wxGetApp().preset_bundle->physical_printers; + PhysicalPrinter* printer = printers.find_printer(into_u8(printer_name)); + if (!printer) { + const Preset& preset = wxGetApp().preset_bundle->printers.get_edited_preset(); + printer = new PhysicalPrinter(into_u8(printer_name), preset); + } + assert(printer); + m_printer = *printer; + + m_config = &m_printer.config; + + m_optgroup = new ConfigOptionsGroup(this, _L("Print Host upload"), m_config); + build_printhost_settings(m_optgroup); + m_optgroup->reload_config(); wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); + wxButton* btnOK = static_cast(this->FindWindowById(wxID_OK, this)); + btnOK->Bind(wxEVT_BUTTON, &PhysicalPrinterDialog::OnOK, this); wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - topSizer->Add(printer_text , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); - topSizer->Add(printer_presets , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); - topSizer->Add(btns , 0, wxEXPAND | wxALL, border); + topSizer->Add(m_printer_name , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(m_printer_presets , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(m_optgroup->sizer , 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(btns , 0, wxEXPAND | wxALL, border); SetSizer(topSizer); topSizer->SetSizeHints(this); } +void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgroup) +{ + m_optgroup->append_single_option_line("host_type"); + + auto create_sizer_with_btn = [this](wxWindow* parent, ScalableButton** btn, const std::string& icon_name, const wxString& label) { + *btn = new ScalableButton(parent, wxID_ANY, icon_name, label, wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT); + (*btn)->SetFont(wxGetApp().normal_font()); + + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(*btn); + return sizer; + }; + + auto printhost_browse = [=](wxWindow* parent) + { + auto sizer = create_sizer_with_btn(parent, &m_printhost_browse_btn, "browse", _L("Browse") + " " + dots); + m_printhost_browse_btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent& e) { + BonjourDialog dialog(this, Preset::printer_technology(m_printer.config)); + if (dialog.show_and_lookup()) { + m_optgroup->set_value("print_host", std::move(dialog.get_selected()), true); + m_optgroup->get_field("print_host")->field_changed(); + } + }); + + return sizer; + }; + + auto print_host_test = [=](wxWindow* parent) { + auto sizer = create_sizer_with_btn(parent, &m_printhost_test_btn, "test", _L("Test")); + + m_printhost_test_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) { + std::unique_ptr host(PrintHost::get_print_host(m_config)); + if (!host) { + const wxString text = _L("Could not get a valid Printer Host reference"); + show_error(this, text); + return; + } + wxString msg; + if (host->test(msg)) { + show_info(this, host->get_test_ok_msg(), _L("Success!")); + } + else { + show_error(this, host->get_test_failed_msg(msg)); + } + }); + + return sizer; + }; + + // Set a wider width for a better alignment + Option option = m_optgroup->get_option("print_host"); + option.opt.width = Field::def_width_wider(); + Line host_line = m_optgroup->create_single_option_line(option); + host_line.append_widget(printhost_browse); + host_line.append_widget(print_host_test); + m_optgroup->append_line(host_line); + option = m_optgroup->get_option("printhost_apikey"); + option.opt.width = Field::def_width_wider(); + m_optgroup->append_single_option_line(option); + + const auto ca_file_hint = _u8L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate."); + + if (Http::ca_file_supported()) { + option = m_optgroup->get_option("printhost_cafile"); + option.opt.width = Field::def_width_wider(); + Line cafile_line = m_optgroup->create_single_option_line(option); + + auto printhost_cafile_browse = [=](wxWindow* parent) { + auto sizer = create_sizer_with_btn(parent, &m_printhost_cafile_browse_btn, "browse", _L("Browse") + " " + dots); + m_printhost_cafile_browse_btn->Bind(wxEVT_BUTTON, [this, m_optgroup](wxCommandEvent e) { + static const auto filemasks = _L("Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*"); + wxFileDialog openFileDialog(this, _L("Open CA certificate file"), "", "", filemasks, wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if (openFileDialog.ShowModal() != wxID_CANCEL) { + m_optgroup->set_value("printhost_cafile", std::move(openFileDialog.GetPath()), true); + m_optgroup->get_field("printhost_cafile")->field_changed(); + } + }); + + return sizer; + }; + + cafile_line.append_widget(printhost_cafile_browse); + m_optgroup->append_line(cafile_line); + + Line cafile_hint{ "", "" }; + cafile_hint.full_width = 1; + cafile_hint.widget = [this, ca_file_hint](wxWindow* parent) { + auto txt = new wxStaticText(parent, wxID_ANY, ca_file_hint); + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(txt); + return sizer; + }; + m_optgroup->append_line(cafile_hint); + } + else { + Line line{ "", "" }; + line.full_width = 1; + + line.widget = [ca_file_hint](wxWindow* parent) { + std::string info = _u8L("HTTPS CA File") + ":\n\t" + + (boost::format(_u8L("On this system, %s uses HTTPS certificates from the system Certificate Store or Keychain.")) % SLIC3R_APP_NAME).str() + + "\n\t" + _u8L("To use a custom CA file, please import your CA file into Certificate Store / Keychain."); + + auto txt = new wxStaticText(parent, wxID_ANY, from_u8((boost::format("%1%\n\n\t%2%") % info % ca_file_hint).str())); + txt->SetFont(wxGetApp().normal_font()); + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(txt, 1, wxEXPAND); + return sizer; + }; + + m_optgroup->append_line(line); + } + + for (const std::string& opt_key : std::vector{ "login", "password" }) { + option = m_optgroup->get_option(opt_key); + option.opt.width = Field::def_width_wider(); + m_optgroup->append_single_option_line(option); + } + + update_octoprint_visible(); +} + +void PhysicalPrinterDialog::update_octoprint_visible() +{ + const PrinterTechnology tech = Preset::printer_technology(m_printer.config); + // Only offer the host type selection for FFF, for SLA it's always the SL1 printer (at the moment) + Field* host_type = m_optgroup->get_field("host_type"); + if (tech == ptFFF) + host_type->enable(); + else { + host_type->set_value(int(PrintHostType::htOctoPrint), false); + host_type->disable(); + } +} + void PhysicalPrinterDialog::on_dpi_changed(const wxRect& suggested_rect) { const int& em = em_unit(); + m_printhost_browse_btn->msw_rescale(); + m_printhost_test_btn->msw_rescale(); + if (m_printhost_cafile_browse_btn) + m_printhost_cafile_browse_btn->msw_rescale(); + + m_optgroup->msw_rescale(); + msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL }); const wxSize& size = wxSize(40 * em, 30 * em); @@ -798,5 +1017,46 @@ void PhysicalPrinterDialog::on_dpi_changed(const wxRect& suggested_rect) Refresh(); } +void PhysicalPrinterDialog::OnOK(wxEvent& event) +{ + wxString printer_name = m_printer_name->GetValue(); + if (printer_name.IsEmpty()) { + show_error(this, _L("The supplied name is empty. It can't be saved.")); + return; + } + + PhysicalPrinterCollection& printers = wxGetApp().preset_bundle->physical_printers; + const PhysicalPrinter* existing = printers.find_printer(into_u8(printer_name)); + if (existing && into_u8(printer_name) != printers.get_selected_printer_name()) + { + wxString msg_text = from_u8((boost::format(_u8L("Printer with name \"%1%\" already exists.")) % printer_name).str()); + msg_text += "\n" + _L("Replace?"); + wxMessageDialog dialog(nullptr, msg_text, _L("Warning"), wxICON_WARNING | wxYES | wxNO); + + if (dialog.ShowModal() == wxID_NO) + return; + + // Remove the printer from the list. + printers.delete_printer(into_u8(printer_name)); + } + + //upadte printer name, if it was changed + m_printer.name = into_u8(printer_name); + + // save new physical printer + printers.save_printer(m_printer); + + // update selection on the tab only when it was changed + if (m_printer.get_preset_name() != wxGetApp().preset_bundle->printers.get_selected_preset_name()) { + Tab* tab = wxGetApp().get_tab(Preset::TYPE_PRINTER); + if (tab) { + wxString preset_name = m_printer_presets->GetString(m_printer_presets->GetSelection()); + tab->select_preset(into_u8(preset_name)); + } + } + + event.Skip(); +} + }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index 38b98d6586..e1597bcfc6 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -13,6 +13,7 @@ class wxString; class wxTextCtrl; +class ScalableButton; namespace Slic3r { @@ -33,7 +34,8 @@ public: ~PresetComboBox(); enum LabelItemType { - LABEL_ITEM_MARKER = 0xffffff01, + LABEL_ITEM_PHYSICAL_PRINTER = 0xffffff01, + LABEL_ITEM_MARKER, LABEL_ITEM_PHYSICAL_PRINTERS, LABEL_ITEM_WIZARD_PRINTERS, LABEL_ITEM_WIZARD_FILAMENTS, @@ -121,11 +123,13 @@ public: void set_extruder_idx(const int extr_idx) { m_extruder_idx = extr_idx; } int get_extruder_idx() const { return m_extruder_idx; } + bool is_selected_physical_printer(); + void update() override; void msw_rescale() override; private: - int m_extruder_idx = -1; + int m_extruder_idx = -1; }; @@ -135,8 +139,11 @@ private: class TabPresetComboBox : public PresetComboBox { + bool show_incompatible {false}; + std::function on_selection_changed { nullptr }; + public: - TabPresetComboBox(wxWindow *parent, Preset::Type preset_type, bool is_from_physical_printer = false); + TabPresetComboBox(wxWindow *parent, Preset::Type preset_type); ~TabPresetComboBox() {} void set_show_incompatible_presets(bool show_incompatible_presets) { show_incompatible = show_incompatible_presets; @@ -146,25 +153,33 @@ public: void update_dirty(); void msw_rescale() override; -private: - bool show_incompatible{false}; + void set_selection_changed_function(std::function sel_changed) { on_selection_changed = sel_changed; } }; //------------------------------------------ // PhysicalPrinterDialog //------------------------------------------ - +class ConfigOptionsGroup; class PhysicalPrinterDialog : public DPIDialog { - std::string printer_name; - std::string preset_name; + PhysicalPrinter m_printer; + DynamicPrintConfig* m_config { nullptr }; - wxTextCtrl* printer_text { nullptr }; - PresetComboBox* printer_presets; + wxTextCtrl* m_printer_name { nullptr }; + TabPresetComboBox* m_printer_presets { nullptr }; + ConfigOptionsGroup* m_optgroup { nullptr }; + + ScalableButton* m_printhost_browse_btn; + ScalableButton* m_printhost_test_btn; + ScalableButton* m_printhost_cafile_browse_btn {nullptr}; + + void build_printhost_settings(ConfigOptionsGroup* optgroup); + void update_octoprint_visible(); + void OnOK(wxEvent& event); public: - PhysicalPrinterDialog(const wxString& printer_name, int last_selected_preset); + PhysicalPrinterDialog(wxString printer_name); ~PhysicalPrinterDialog() {} protected: diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index bb19e139d2..da5d51dc54 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -161,6 +161,13 @@ void Tab::create_preset_tab() // preset chooser m_presets_choice = new TabPresetComboBox(panel, m_type); + m_presets_choice->set_selection_changed_function([this](int selection) { + // unselect pthysical printer, if it was selected + m_preset_bundle->physical_printers.unselect_printer(); + // select preset + std::string selected_string = m_presets_choice->GetString(selection).ToUTF8().data(); + select_preset(selected_string); + }); auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); @@ -3022,6 +3029,9 @@ void Tab::select_preset(std::string preset_name, bool delete_current) if (canceled) { update_tab_ui(); + // unselect physical printer selection to the correct synchronization of the printer presets between Tab and Plater + if (m_type == Preset::TYPE_PRINTER) + m_preset_bundle->physical_printers.unselect_printer(); // Trigger the on_presets_changed event so that we also restore the previous value in the plater selector, // if this action was initiated from the plater. on_presets_changed(); From 89035febfaf926394eaef82db007694dc87c9713 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 24 Jun 2020 09:20:04 +0200 Subject: [PATCH 142/503] Fixed includes --- src/slic3r/Utils/FixModelByWin10.cpp | 2 +- xs/xsp/Model.xsp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/Utils/FixModelByWin10.cpp b/src/slic3r/Utils/FixModelByWin10.cpp index 0de526432b..a3683a84d3 100644 --- a/src/slic3r/Utils/FixModelByWin10.cpp +++ b/src/slic3r/Utils/FixModelByWin10.cpp @@ -30,10 +30,10 @@ #include "libslic3r/Model.hpp" #include "libslic3r/Print.hpp" +#include "libslic3r/PresetBundle.hpp" #include "libslic3r/Format/3mf.hpp" #include "../GUI/GUI.hpp" #include "../GUI/I18N.hpp" -#include "../GUI/PresetBundle.hpp" #include #include diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 4fb35578db..844b7c95ec 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -12,7 +12,7 @@ #include "libslic3r/Format/OBJ.hpp" #include "libslic3r/Format/PRUS.hpp" #include "libslic3r/Format/STL.hpp" -#include "slic3r/GUI/PresetBundle.hpp" +#include "libslic3r/PresetBundle.hpp" %} %name{Slic3r::Model} class Model { From 8ac839f427ef2c0d1470de51e8d9515689296e83 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 24 Jun 2020 12:28:00 +0200 Subject: [PATCH 143/503] Physical printers: Delete selected printer + Added context menu for the cog-button near the printer presets --- src/libslic3r/Preset.cpp | 21 +++++++-- src/libslic3r/Preset.hpp | 3 ++ src/slic3r/GUI/PresetComboBoxes.cpp | 72 +++++++++++++++++++++-------- src/slic3r/GUI/PresetComboBoxes.hpp | 2 + 4 files changed, 76 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 94c9577dfb..e17130f69c 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1489,17 +1489,32 @@ void PhysicalPrinterCollection::save_printer(const PhysicalPrinter& edited_print bool PhysicalPrinterCollection::delete_printer(const std::string& name) { auto it = this->find_printer_internal(name); + if (it == m_printers.end()) + return false; const PhysicalPrinter& printer = *it; - if (it == m_printers.end()) - return false; - // Erase the preset file. boost::nowide::remove(printer.file.c_str()); m_printers.erase(it); return true; } +bool PhysicalPrinterCollection::delete_selected_printer() +{ + if (!has_selection()) + return false; + const PhysicalPrinter& printer = this->get_selected_printer(); + + // Erase the preset file. + boost::nowide::remove(printer.file.c_str()); + // Remove the preset from the list. + m_printers.erase(m_printers.begin() + m_idx_selected); + // unselect all printers + unselect_printer(); + + return true; +} + PhysicalPrinter& PhysicalPrinterCollection::select_printer_by_name(const std::string& name) { auto it = this->find_printer_internal(name); diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index c08a1a0fbc..a5837a9fe0 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -612,6 +612,9 @@ public: // Delete the current preset, activate the first visible preset. // returns true if the preset was deleted successfully. bool delete_printer(const std::string& name); + // Delete the selected preset + // returns true if the preset was deleted successfully. + bool delete_selected_printer(); // Return the selected preset, without the user modifications applied. PhysicalPrinter& get_selected_printer() { return m_printers[m_idx_selected]; } diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index ce25d56901..e9da7dc9d8 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "libslic3r/libslic3r.h" #include "libslic3r/PrintConfig.hpp" @@ -322,28 +323,14 @@ PlaterPresetComboBox::PlaterPresetComboBox(wxWindow *parent, Preset::Type preset edit_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent) { // In a case of a physical printer, for its editing open PhysicalPrinterDialog - if (m_type == Preset::TYPE_PRINTER && this->is_selected_physical_printer()) - { - PhysicalPrinterDialog dlg(this->GetString(this->GetSelection())); - if (dlg.ShowModal() == wxID_OK) { - update(); - return; - } + if (m_type == Preset::TYPE_PRINTER && this->is_selected_physical_printer()) { + this->show_edit_menu(); + return; } - Tab* tab = wxGetApp().get_tab(m_type); - if (!tab) + if (!switch_to_tab()) return; - int page_id = wxGetApp().tab_panel()->FindPage(tab); - if (page_id == wxNOT_FOUND) - return; - - wxGetApp().tab_panel()->SetSelection(page_id); - - // Switch to Settings NotePad - wxGetApp().mainframe->select_tab(); - /* In a case of a multi-material printing, for editing another Filament Preset * it's needed to select this preset for the "Filament settings" Tab */ @@ -355,7 +342,7 @@ PlaterPresetComboBox::PlaterPresetComboBox(wxWindow *parent, Preset::Type preset if ( !boost::algorithm::ends_with(selected_preset, Preset::suffix_modified()) ) { const std::string& preset_name = wxGetApp().preset_bundle->filaments.get_preset_name_by_alias(selected_preset); - tab->select_preset(preset_name); + wxGetApp().get_tab(m_type)->select_preset(preset_name); } } }); @@ -374,6 +361,53 @@ bool PlaterPresetComboBox::is_selected_physical_printer() return marker == LABEL_ITEM_PHYSICAL_PRINTER; } +bool PlaterPresetComboBox::switch_to_tab() +{ + Tab* tab = wxGetApp().get_tab(m_type); + if (!tab) + return false; + + int page_id = wxGetApp().tab_panel()->FindPage(tab); + if (page_id == wxNOT_FOUND) + return false; + + wxGetApp().tab_panel()->SetSelection(page_id); + // Switch to Settings NotePad + wxGetApp().mainframe->select_tab(); + return true; +} + +void PlaterPresetComboBox::show_edit_menu() +{ + wxMenu* menu = new wxMenu(); + + append_menu_item(menu, wxID_ANY, _L("Edit related printer profile"), "", + [this](wxCommandEvent&) { this->switch_to_tab(); }, "cog", menu, []() { return true; }, wxGetApp().plater()); + + append_menu_item(menu, wxID_ANY, _L("Edit physical printer"), "", + [this](wxCommandEvent&) { + PhysicalPrinterDialog dlg(this->GetString(this->GetSelection())); + if (dlg.ShowModal() == wxID_OK) + update(); + }, "cog", menu, []() { return true; }, wxGetApp().plater()); + + append_menu_item(menu, wxID_ANY, _L("Delete physical printer"), "", + [this](wxCommandEvent&) { + const std::string& printer_name = m_preset_bundle->physical_printers.get_selected_printer_name(); + if (printer_name.empty()) + return; + + const wxString msg = from_u8((boost::format(_u8L("Are you sure you want to delete \"%1%\" printer?")) % printer_name).str()); + if (wxMessageDialog(this, msg, _L("Delete Physical Printer"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION).ShowModal() != wxID_YES) + return; + + m_preset_bundle->physical_printers.delete_selected_printer(); + update(); + }, "cross", menu, []() { return true; }, wxGetApp().plater()); + + wxGetApp().plater()->PopupMenu(menu); +} + // Only the compatible presets are shown. // If an incompatible preset is selected, it is shown as well. void PlaterPresetComboBox::update() diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index e1597bcfc6..9261e92ef4 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -124,6 +124,8 @@ public: int get_extruder_idx() const { return m_extruder_idx; } bool is_selected_physical_printer(); + bool switch_to_tab(); + void show_edit_menu(); void update() override; void msw_rescale() override; From 648ecb47c23ac44cb10155796c8c979d1019a0eb Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 24 Jun 2020 16:57:09 +0200 Subject: [PATCH 144/503] GCodeViewer -> Fixed incorrect detection of out of printbed for toolpaths --- src/slic3r/GUI/GCodeViewer.cpp | 26 +++++++++++++------------- src/slic3r/GUI/GCodeViewer.hpp | 18 ++++++++++++------ src/slic3r/GUI/GLCanvas3D.cpp | 10 +++++----- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 415b61aeb9..4b308e7030 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -148,7 +148,6 @@ void GCodeViewer::SequentialView::Marker::set_world_position(const Vec3f& positi { m_world_position = position; m_world_transform = (Geometry::assemble_transform((position + m_z_offset * Vec3f::UnitZ()).cast()) * Geometry::assemble_transform(m_model.get_bounding_box().size()[2] * Vec3d::UnitZ(), { M_PI, 0.0, 0.0 })).cast(); - m_world_bounding_box = m_model.get_bounding_box().transformed(m_world_transform.cast()); } void GCodeViewer::SequentialView::Marker::render() const @@ -288,10 +287,10 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& #if ENABLE_GCODE_VIEWER_AS_STATE if (wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer) { - // adjust printbed size + // adjust printbed size in dependence of toolpaths bbox const double margin = 10.0; - Vec2d min(m_bounding_box.min(0) - margin, m_bounding_box.min(1) - margin); - Vec2d max(m_bounding_box.max(0) + margin, m_bounding_box.max(1) + margin); + Vec2d min(m_paths_bounding_box.min(0) - margin, m_paths_bounding_box.min(1) - margin); + Vec2d max(m_paths_bounding_box.max(0) + margin, m_paths_bounding_box.max(1) + margin); Pointfs bed_shape = { { min(0), min(1) }, { max(0), min(1) }, { max(0), max(1) }, @@ -359,7 +358,8 @@ void GCodeViewer::reset() buffer.reset(); } - m_bounding_box = BoundingBoxf3(); + m_paths_bounding_box = BoundingBoxf3(); + m_max_bounding_box = BoundingBoxf3(); m_tool_colors = std::vector(); m_extruder_ids = std::vector(); m_extrusions.reset_role_visibility_flags(); @@ -497,18 +497,19 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) const GCodeProcessor::MoveVertex& move = gcode_result.moves[i]; #if ENABLE_GCODE_VIEWER_AS_STATE if (wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer) - m_bounding_box.merge(move.position.cast()); + // for the gcode viewer we need all moves to correctly size the printbed + m_paths_bounding_box.merge(move.position.cast()); else { #endif // ENABLE_GCODE_VIEWER_AS_STATE if (move.type == GCodeProcessor::EMoveType::Extrude && move.width != 0.0f && move.height != 0.0f) - m_bounding_box.merge(move.position.cast()); + m_paths_bounding_box.merge(move.position.cast()); #if ENABLE_GCODE_VIEWER_AS_STATE } #endif // ENABLE_GCODE_VIEWER_AS_STATE ::memcpy(static_cast(&vertices_data[i * 3]), static_cast(move.position.data()), 3 * sizeof(float)); } - m_bounding_box.merge(m_bounding_box.max + m_sequential_view.marker.get_bounding_box().max[2] * Vec3d::UnitZ()); + m_max_bounding_box.merge(m_paths_bounding_box.max + m_sequential_view.marker.get_bounding_box().max[2] * Vec3d::UnitZ()); #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.vertices_size = SLIC3R_STDVEC_MEMSIZE(vertices_data, float); @@ -578,7 +579,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) // indices data -> send data to gpu for (size_t i = 0; i < m_buffers.size(); ++i) { IBuffer& buffer = m_buffers[i]; - std::vector& buffer_indices = indices[i]; + const std::vector& buffer_indices = indices[i]; buffer.indices_count = buffer_indices.size(); #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.indices_size += SLIC3R_STDVEC_MEMSIZE(buffer_indices, unsigned int); @@ -859,7 +860,6 @@ void GCodeViewer::render_toolpaths() const for (const RenderPath& path : buffer.render_paths) { shader.set_uniform("uniform_color", path.color); -// glsafe(::glMultiDrawElements(GL_LINES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); glsafe(::glMultiDrawElements(GL_LINE_STRIP, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_line_strip_calls_count; @@ -878,8 +878,8 @@ void GCodeViewer::render_toolpaths() const unsigned char end_id = buffer_id(GCodeProcessor::EMoveType::Count); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vertices.vbo_id)); - glsafe(::glVertexPointer(3, GL_FLOAT, VBuffer::vertex_size_bytes(), (const void*)0)); - glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + glsafe(::glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, VBuffer::vertex_size_bytes(), (const void*)0)); + glsafe(::glEnableVertexAttribArray(0)); for (unsigned char i = begin_id; i < end_id; ++i) { const IBuffer& buffer = m_buffers[i]; @@ -914,7 +914,7 @@ void GCodeViewer::render_toolpaths() const } } - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + glsafe(::glDisableVertexAttribArray(0)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 413f4ef4ad..5c60863058 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -43,8 +43,8 @@ class GCodeViewer void reset(); - static size_t vertex_size() { return 3; } - static size_t vertex_size_bytes() { return vertex_size() * sizeof(float); } + static size_t vertex_size_floats() { return 3; } + static size_t vertex_size_bytes() { return vertex_size_floats() * sizeof(float); } }; // Used to identify different toolpath sub-types inside a IBuffer @@ -160,11 +160,14 @@ class GCodeViewer #if ENABLE_GCODE_VIEWER_STATISTICS struct Statistics { + // times long long load_time{ 0 }; long long refresh_time{ 0 }; long long refresh_paths_time{ 0 }; + // opengl calls long long gl_multi_points_calls_count{ 0 }; long long gl_multi_line_strip_calls_count{ 0 }; + // memory long long results_size{ 0 }; long long vertices_size{ 0 }; long long vertices_gpu_size{ 0 }; @@ -220,7 +223,6 @@ public: GLModel m_model; Vec3f m_world_position; Transform3f m_world_transform; - BoundingBoxf3 m_world_bounding_box; float m_z_offset{ 0.5f }; std::array m_color{ 1.0f, 1.0f, 1.0f, 1.0f }; bool m_visible{ false }; @@ -228,7 +230,7 @@ public: public: void init(); - const BoundingBoxf3& get_bounding_box() const { return m_world_bounding_box; } + const BoundingBoxf3& get_bounding_box() const { return m_model.get_bounding_box(); } void set_world_position(const Vec3f& position); void set_color(const std::array& color) { m_color = color; } @@ -268,7 +270,10 @@ private: unsigned int m_last_result_id{ 0 }; VBuffer m_vertices; mutable std::vector m_buffers{ static_cast(GCodeProcessor::EMoveType::Extrude) }; - BoundingBoxf3 m_bounding_box; + // bounding box of toolpaths + BoundingBoxf3 m_paths_bounding_box; + // bounding box of toolpaths + marker tools + BoundingBoxf3 m_max_bounding_box; std::vector m_tool_colors; std::vector m_layers_zs; std::array m_layers_z_range; @@ -303,7 +308,8 @@ public: bool has_data() const { return !m_roles.empty(); } - const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; } + const BoundingBoxf3& get_paths_bounding_box() const { return m_paths_bounding_box; } + const BoundingBoxf3& get_max_bounding_box() const { return m_max_bounding_box; } const std::vector& get_layers_zs() const { return m_layers_zs; }; const SequentialView& get_sequential_view() const { return m_sequential_view; } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 5c72ae0fa7..0ee30a8084 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1933,7 +1933,7 @@ void GLCanvas3D::zoom_to_selection() #if ENABLE_GCODE_VIEWER_AS_STATE void GLCanvas3D::zoom_to_gcode() { - _zoom_to_box(m_gcode_viewer.get_bounding_box(), 1.05); + _zoom_to_box(m_gcode_viewer.get_paths_bounding_box(), 1.05); } #endif // ENABLE_GCODE_VIEWER_AS_STATE @@ -3108,7 +3108,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) if (!m_volumes.empty()) zoom_to_volumes(); else - _zoom_to_box(m_gcode_viewer.get_bounding_box()); + _zoom_to_box(m_gcode_viewer.get_paths_bounding_box()); } break; @@ -5189,7 +5189,7 @@ BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_be #if ENABLE_GCODE_VIEWER if (!m_main_toolbar.is_enabled()) - bb.merge(m_gcode_viewer.get_bounding_box()); + bb.merge(m_gcode_viewer.get_max_bounding_box()); #endif // ENABLE_GCODE_VIEWER return bb; @@ -5385,7 +5385,7 @@ void GLCanvas3D::_render_background() const use_error_color &= _is_any_volume_outside(); else { BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); - use_error_color &= (test_volume.radius() > 0.0) ? !test_volume.contains(m_gcode_viewer.get_bounding_box()) : false; + use_error_color &= (test_volume.radius() > 0.0) ? !test_volume.contains(m_gcode_viewer.get_paths_bounding_box()) : false; } #if ENABLE_GCODE_VIEWER_AS_STATE } @@ -7114,7 +7114,7 @@ void GLCanvas3D::_show_warning_texture_if_needed(WarningTexture::Warning warning if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) { BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); - const BoundingBoxf3& paths_volume = m_gcode_viewer.get_bounding_box(); + const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box(); if (test_volume.radius() > 0.0 && paths_volume.radius() > 0.0) show = !test_volume.contains(paths_volume); } From eb683616193f03991cff08dbaf85bb1fe348cd09 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 25 Jun 2020 08:14:45 +0200 Subject: [PATCH 145/503] Follow-up of 648ecb47c23ac44cb10155796c8c979d1019a0eb -> Fixed calculation of max bounding box --- src/slic3r/GUI/GCodeViewer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 4b308e7030..05a8c20bba 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -509,7 +509,8 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) ::memcpy(static_cast(&vertices_data[i * 3]), static_cast(move.position.data()), 3 * sizeof(float)); } - m_max_bounding_box.merge(m_paths_bounding_box.max + m_sequential_view.marker.get_bounding_box().max[2] * Vec3d::UnitZ()); + m_max_bounding_box = m_paths_bounding_box; + m_max_bounding_box.merge(m_paths_bounding_box.max + m_sequential_view.marker.get_bounding_box().size()[2] * Vec3d::UnitZ()); #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.vertices_size = SLIC3R_STDVEC_MEMSIZE(vertices_data, float); From 1a2926050fee0b5ab118054aaa4ccb65614d2063 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 25 Jun 2020 12:58:59 +0200 Subject: [PATCH 146/503] PhysicalPrinter. PhysicalPrinterDialog improvements --- src/libslic3r/Preset.cpp | 3 +- src/libslic3r/PrintConfig.cpp | 11 +++++++ src/libslic3r/PrintConfig.hpp | 13 ++++++++ src/slic3r/GUI/Field.cpp | 2 ++ src/slic3r/GUI/Field.hpp | 2 ++ src/slic3r/GUI/GUI.cpp | 2 ++ src/slic3r/GUI/OptionsGroup.cpp | 19 +++++++----- src/slic3r/GUI/OptionsGroup.hpp | 7 +++++ src/slic3r/GUI/PresetComboBoxes.cpp | 46 +++++++++++++++++++++-------- src/slic3r/GUI/PresetComboBoxes.hpp | 2 +- 10 files changed, 85 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index e17130f69c..abc508b489 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1354,7 +1354,8 @@ const std::vector& PhysicalPrinter::printer_options() "host_type", "print_host", "printhost_apikey", - "printhost_cafile", + "printhost_cafile", + "authorization_type", "login", "password" }; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 93ef170fb3..01b17ec0de 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -150,6 +150,17 @@ void PrintConfigDef::init_common_params() def->tooltip = L("Related printer preset name"); def->mode = comAdvanced; def->set_default_value(new ConfigOptionString("")); + + def = this->add("authorization_type", coEnum); + def->label = L("Authorization Type"); +// def->tooltip = L(""); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("key"); + def->enum_values.push_back("user"); + def->enum_labels.push_back("KeyPassword"); + def->enum_labels.push_back("UserPassword"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionEnum(atKeyPassword)); } void PrintConfigDef::init_fff_params() diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index f28ef2a228..9b5c47512e 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -33,6 +33,10 @@ enum PrintHostType { htOctoPrint, htDuet, htFlashAir, htAstroBox }; +enum AuthorizationType { + atKeyPassword, atUserPassword +}; + enum InfillPattern : int { ipRectilinear, ipMonotonous, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipCount, @@ -109,6 +113,15 @@ template<> inline const t_config_enum_values& ConfigOptionEnum::g return keys_map; } +template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { + static t_config_enum_values keys_map; + if (keys_map.empty()) { + keys_map["key"] = atKeyPassword; + keys_map["user"] = atUserPassword; + } + return keys_map; +} + template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { static t_config_enum_values keys_map; if (keys_map.empty()) { diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 8ab82e20d8..9cb3d726de 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -1080,6 +1080,8 @@ boost::any& Choice::get_value() m_value = static_cast(ret_enum); else if (m_opt_id.compare("support_pillar_connection_mode") == 0) m_value = static_cast(ret_enum); + else if (m_opt_id == "authorization_type") + m_value = static_cast(ret_enum); } else if (m_opt.gui_type == "f_enum_open") { const int ret_enum = field->GetSelection(); diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index 484b2059f0..1a49977565 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -151,6 +151,8 @@ public: virtual wxSizer* getSizer() { return nullptr; } virtual wxWindow* getWindow() { return nullptr; } + wxStaticText* getLabel() { return m_Label; } + bool is_matched(const std::string& string, const std::string& pattern); void get_value_by_opt_type(wxString& str, const bool check_value = true); diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index b9516b12f2..88c4576681 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -194,6 +194,8 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); else if(opt_key.compare("support_pillar_connection_mode") == 0) config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); + else if(opt_key == "authorization_type") + config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); } break; case coPoints:{ diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 819c214a85..1bebb8827a 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -729,31 +729,34 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config opt_key == "fill_pattern" ) { ret = static_cast(config.option>(opt_key)->value); } - else if (opt_key.compare("ironing_type") == 0 ) { + else if (opt_key == "ironing_type") { ret = static_cast(config.option>(opt_key)->value); } - else if (opt_key.compare("gcode_flavor") == 0 ) { + else if (opt_key == "gcode_flavor") { ret = static_cast(config.option>(opt_key)->value); } - else if (opt_key.compare("support_material_pattern") == 0) { + else if (opt_key == "support_material_pattern") { ret = static_cast(config.option>(opt_key)->value); } - else if (opt_key.compare("seam_position") == 0) { + else if (opt_key == "seam_position") { ret = static_cast(config.option>(opt_key)->value); } - else if (opt_key.compare("host_type") == 0) { + else if (opt_key == "host_type") { ret = static_cast(config.option>(opt_key)->value); } - else if (opt_key.compare("display_orientation") == 0) { + else if (opt_key == "display_orientation") { ret = static_cast(config.option>(opt_key)->value); } - else if (opt_key.compare("support_pillar_connection_mode") == 0) { + else if (opt_key == "support_pillar_connection_mode") { ret = static_cast(config.option>(opt_key)->value); } + else if (opt_key == "authorization_type") { + ret = static_cast(config.option>(opt_key)->value); + } } break; case coPoints: - if (opt_key.compare("bed_shape") == 0) + if (opt_key == "bed_shape") ret = config.option(opt_key)->values; else ret = config.option(opt_key)->get_at(idx); diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp index 2e6f9aa0f4..edd4a15bc9 100644 --- a/src/slic3r/GUI/OptionsGroup.hpp +++ b/src/slic3r/GUI/OptionsGroup.hpp @@ -149,6 +149,13 @@ public: return true; } + void show_field(const t_config_option_key& opt_key, bool show = true) { + Field* field = get_field(opt_key); + field->getWindow()->Show(show); + field->getLabel()->Show(show); + } + void hide_field(const t_config_option_key& opt_key) { show_field(opt_key, false); } + void set_name(const wxString& new_name) { stb->SetLabel(new_name); } diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index e9da7dc9d8..b39582ee0f 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -837,7 +837,7 @@ void TabPresetComboBox::update_dirty() PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) - : DPIDialog(NULL, wxID_ANY, _L("PhysicalPrinter"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) + : DPIDialog(NULL, wxID_ANY, _L("Physical Printer"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), -1), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { SetFont(wxGetApp().normal_font()); SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); @@ -856,7 +856,7 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) // update values m_optgroup->reload_config(); - update_octoprint_visible(); + update(); }); m_printer_presets->update(); @@ -898,6 +898,11 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgroup) { + m_optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { + if (opt_key == "authorization_type") + this->update(); + }; + m_optgroup->append_single_option_line("host_type"); auto create_sizer_with_btn = [this](wxWindow* parent, ScalableButton** btn, const std::string& icon_name, const wxString& label) { @@ -952,6 +957,9 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr host_line.append_widget(printhost_browse); host_line.append_widget(print_host_test); m_optgroup->append_line(host_line); + + m_optgroup->append_single_option_line("authorization_type"); + option = m_optgroup->get_option("printhost_apikey"); option.opt.width = Field::def_width_wider(); m_optgroup->append_single_option_line(option); @@ -999,7 +1007,8 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr (boost::format(_u8L("On this system, %s uses HTTPS certificates from the system Certificate Store or Keychain.")) % SLIC3R_APP_NAME).str() + "\n\t" + _u8L("To use a custom CA file, please import your CA file into Certificate Store / Keychain."); - auto txt = new wxStaticText(parent, wxID_ANY, from_u8((boost::format("%1%\n\n\t%2%") % info % ca_file_hint).str())); + //auto txt = new wxStaticText(parent, wxID_ANY, from_u8((boost::format("%1%\n\n\t%2%") % info % ca_file_hint).str())); + auto txt = new wxStaticText(parent, wxID_ANY, from_u8((boost::format("%1%\n\t%2%") % info % ca_file_hint).str())); txt->SetFont(wxGetApp().normal_font()); auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(txt, 1, wxEXPAND); @@ -1015,20 +1024,33 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr m_optgroup->append_single_option_line(option); } - update_octoprint_visible(); + update(); } -void PhysicalPrinterDialog::update_octoprint_visible() +void PhysicalPrinterDialog::update() { const PrinterTechnology tech = Preset::printer_technology(m_printer.config); // Only offer the host type selection for FFF, for SLA it's always the SL1 printer (at the moment) - Field* host_type = m_optgroup->get_field("host_type"); - if (tech == ptFFF) - host_type->enable(); - else { - host_type->set_value(int(PrintHostType::htOctoPrint), false); - host_type->disable(); + if (tech == ptFFF) { + m_optgroup->show_field("host_type"); + m_optgroup->hide_field("authorization_type"); + for (const std::string& opt_key : std::vector{ "login", "password" }) + m_optgroup->hide_field(opt_key); } + else { + m_optgroup->set_value("host_type", int(PrintHostType::htOctoPrint), false); + m_optgroup->hide_field("host_type"); + + m_optgroup->show_field("authorization_type"); + + AuthorizationType auth_type = m_config->option>("authorization_type")->value; + m_optgroup->show_field("printhost_apikey", auth_type == AuthorizationType::atKeyPassword); + + for (const std::string& opt_key : std::vector{ "login", "password" }) + m_optgroup->show_field(opt_key, auth_type == AuthorizationType::atUserPassword); + } + + this->Layout(); } void PhysicalPrinterDialog::on_dpi_changed(const wxRect& suggested_rect) @@ -1044,7 +1066,7 @@ void PhysicalPrinterDialog::on_dpi_changed(const wxRect& suggested_rect) msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL }); - const wxSize& size = wxSize(40 * em, 30 * em); + const wxSize& size = wxSize(45 * em, 35 * em); SetMinSize(size); Fit(); diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index 9261e92ef4..196c4368e8 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -177,7 +177,7 @@ class PhysicalPrinterDialog : public DPIDialog ScalableButton* m_printhost_cafile_browse_btn {nullptr}; void build_printhost_settings(ConfigOptionsGroup* optgroup); - void update_octoprint_visible(); + void update(); void OnOK(wxEvent& event); public: From d96b5f360661feaa2f964d9f4b18d1e586d2607b Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 26 Jun 2020 09:58:39 +0200 Subject: [PATCH 147/503] PhysicalPrinter : Next improvements: * Create full printer name as a PrinterName + RelatedPresetName * Added printer model to the PhysicalPrinter.config => Enable to select just between presets with same printer model * When physical printer is selected and create new preset ask if should we use this preset for selected ph_printer or just to switch for it --- src/libslic3r/Preset.cpp | 33 +++++- src/libslic3r/Preset.hpp | 24 +++- src/slic3r/GUI/PresetComboBoxes.cpp | 175 ++++++++++++++++++++-------- src/slic3r/GUI/PresetComboBoxes.hpp | 9 ++ src/slic3r/GUI/Tab.cpp | 33 +++++- src/slic3r/GUI/Tab.hpp | 1 + src/slic3r/GUI/wxExtensions.cpp | 7 +- src/slic3r/GUI/wxExtensions.hpp | 4 +- 8 files changed, 225 insertions(+), 61 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index abc508b489..9af3dacf0a 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -1351,6 +1352,7 @@ const std::vector& PhysicalPrinter::printer_options() s_opts = { "preset_name", "printer_technology", + "printer_model", "host_type", "print_host", "printhost_apikey", @@ -1363,21 +1365,28 @@ const std::vector& PhysicalPrinter::printer_options() return s_opts; } -const std::string& PhysicalPrinter::get_preset_name() +const std::string& PhysicalPrinter::get_preset_name() const { return config.opt_string("preset_name"); } +const std::string& PhysicalPrinter::get_printer_model() const +{ + return config.opt_string("printer_model"); +} + void PhysicalPrinter::update_from_preset(const Preset& preset) { config.apply_only(preset.config, printer_options(), false); // add preset name to the options list config.set_key_value("preset_name", new ConfigOptionString(preset.name)); + update_full_name(); } void PhysicalPrinter::update_from_config(const DynamicPrintConfig& new_config) { config.apply_only(new_config, printer_options(), false); + update_full_name(); } PhysicalPrinter::PhysicalPrinter(const std::string& name, const Preset& preset) : @@ -1386,6 +1395,24 @@ PhysicalPrinter::PhysicalPrinter(const std::string& name, const Preset& preset) update_from_preset(preset); } +void PhysicalPrinter::set_name(const std::string& name) +{ + this->name = name; + update_full_name(); +} + +void PhysicalPrinter::update_full_name() +{ + full_name = name + " * " + get_preset_name(); +} + +std::string PhysicalPrinter::get_short_name(std::string full_name) +{ + int pos = full_name.find_first_of(" * "); + boost::erase_tail(full_name, full_name.length() - pos); + return full_name; +} + // ----------------------------------- // *** PhysicalPrinterCollection *** @@ -1470,6 +1497,7 @@ void PhysicalPrinterCollection::save_printer(const PhysicalPrinter& edited_print // Printer with the same name found. // Overwriting an existing preset. it->config = std::move(edited_printer.config); + it->full_name = edited_printer.full_name; } else { // Creating a new printer. @@ -1516,8 +1544,9 @@ bool PhysicalPrinterCollection::delete_selected_printer() return true; } -PhysicalPrinter& PhysicalPrinterCollection::select_printer_by_name(const std::string& name) +PhysicalPrinter& PhysicalPrinterCollection::select_printer_by_name(std::string name) { + name = PhysicalPrinter::get_short_name(name); auto it = this->find_printer_internal(name); assert(it != m_printers.end()); diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index a5837a9fe0..6eb1fd2db3 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -538,20 +538,24 @@ public: PhysicalPrinter() {} PhysicalPrinter(const std::string& name) : name(name){} PhysicalPrinter(const std::string& name, const Preset& preset); + void set_name(const std::string &name); + void update_full_name(); // Name of the Physical Printer, usually derived form the file name. std::string name; + // Full name of the Physical Printer, included related preset name + std::string full_name; // File name of the Physical Printer. std::string file; + // Configuration data, loaded from a file, or set from the defaults. + DynamicPrintConfig config; // Has this profile been loaded? bool loaded = false; - // Configuration data, loaded from a file, or set from the defaults. - DynamicPrintConfig config; - static const std::vector& printer_options(); - const std::string& get_preset_name(); + const std::string& get_preset_name() const; + const std::string& get_printer_model() const; void save() { this->config.save(this->file); } void save_to(const std::string& file_name) const { this->config.save(file_name); } @@ -570,6 +574,9 @@ public: // Sort lexicographically by a preset name. The preset name shall be unique across a single PresetCollection. bool operator<(const PhysicalPrinter& other) const { return this->name < other.name; } + // get printer name from the full name uncluded preset name + static std::string get_short_name(std::string full_name); + protected: friend class PhysicalPrinterCollection; }; @@ -622,10 +629,17 @@ public: size_t get_selected_idx() const { return m_idx_selected; } // Returns the name of the selected preset, or an empty string if no preset is selected. std::string get_selected_printer_name() const { return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_printer().name; } + // Returns the full name of the selected preset, or an empty string if no preset is selected. + std::string get_selected_full_printer_name() const { return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_printer().full_name; } + // Returns the printer model of the selected preset, or an empty string if no preset is selected. + std::string get_selected_printer_model() const { return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_printer().get_printer_model(); } + // Returns the printer model of the selected preset, or an empty string if no preset is selected. + std::string get_selected_printer_preset_name() const { return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_printer().get_preset_name(); } + // Returns the config of the selected preset, or nullptr if no preset is selected. DynamicPrintConfig* get_selected_printer_config() { return (m_idx_selected == size_t(-1)) ? nullptr : &(this->get_selected_printer().config); } // select printer with name and return reference on it - PhysicalPrinter& select_printer_by_name(const std::string& name); + PhysicalPrinter& select_printer_by_name(std::string name); bool has_selection() const { return m_idx_selected != size_t(-1); } void unselect_printer() { m_idx_selected = size_t(-1); } diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index b39582ee0f..88dd4b739a 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -104,6 +104,7 @@ PresetComboBox::PresetComboBox(wxWindow* parent, Preset::Type preset_type, const m_bitmapCompatible = ScalableBitmap(this, "flag_green"); m_bitmapIncompatible = ScalableBitmap(this, "flag_red"); m_bitmapLock = ScalableBitmap(this, "lock_closed"); + m_bitmapLockDisabled = ScalableBitmap(this, "lock_closed", 16, true); // parameters for an icon's drawing fill_width_height(); @@ -125,6 +126,7 @@ void PresetComboBox::msw_rescale() m_em_unit = em_unit(this); m_bitmapLock.msw_rescale(); + m_bitmapLockDisabled.msw_rescale(); m_bitmapIncompatible.msw_rescale(); m_bitmapCompatible.msw_rescale(); @@ -241,6 +243,7 @@ PlaterPresetComboBox::PlaterPresetComboBox(wxWindow *parent, Preset::Type preset if (marker >= LABEL_ITEM_MARKER && marker < LABEL_ITEM_MAX) { this->SetSelection(this->m_last_selected); evt.StopPropagation(); + /* if (marker == LABEL_ITEM_PHYSICAL_PRINTERS) { PhysicalPrinterDialog dlg(wxEmptyString); @@ -258,6 +261,19 @@ PlaterPresetComboBox::PlaterPresetComboBox(wxWindow *parent, Preset::Type preset } wxTheApp->CallAfter([sp]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, sp); }); } + */ + if (marker == LABEL_ITEM_WIZARD_PRINTERS) + show_add_menu(); + else + { + ConfigWizard::StartPage sp = ConfigWizard::SP_WELCOME; + switch (marker) { + case LABEL_ITEM_WIZARD_FILAMENTS: sp = ConfigWizard::SP_FILAMENTS; break; + case LABEL_ITEM_WIZARD_MATERIALS: sp = ConfigWizard::SP_MATERIALS; break; + default: break; + } + wxTheApp->CallAfter([sp]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, sp); }); + } } else if (marker == LABEL_ITEM_PHYSICAL_PRINTER || this->m_last_selected != selected_item || m_collection->current_is_dirty() ) { this->m_last_selected = selected_item; evt.SetInt(this->m_type); @@ -377,6 +393,25 @@ bool PlaterPresetComboBox::switch_to_tab() return true; } +void PlaterPresetComboBox::show_add_menu() +{ + wxMenu* menu = new wxMenu(); + + append_menu_item(menu, wxID_ANY, _L("Add/Remove logical printers"), "", + [this](wxCommandEvent&) { + wxTheApp->CallAfter([]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, ConfigWizard::SP_PRINTERS); }); + }, "edit_uni", menu, []() { return true; }, wxGetApp().plater()); + + append_menu_item(menu, wxID_ANY, _L("Add physical printer"), "", + [this](wxCommandEvent&) { + PhysicalPrinterDialog dlg(wxEmptyString); + if (dlg.ShowModal() == wxID_OK) + update(); + }, "edit_uni", menu, []() { return true; }, wxGetApp().plater()); + + wxGetApp().plater()->PopupMenu(menu); +} + void PlaterPresetComboBox::show_edit_menu() { wxMenu* menu = new wxMenu(); @@ -393,7 +428,7 @@ void PlaterPresetComboBox::show_edit_menu() append_menu_item(menu, wxID_ANY, _L("Delete physical printer"), "", [this](wxCommandEvent&) { - const std::string& printer_name = m_preset_bundle->physical_printers.get_selected_printer_name(); + const std::string& printer_name = m_preset_bundle->physical_printers.get_selected_full_printer_name(); if (printer_name.empty()) return; @@ -550,36 +585,6 @@ void PlaterPresetComboBox::update() } } - if (m_type == Preset::TYPE_PRINTER || m_type == Preset::TYPE_SLA_MATERIAL) { - std::string bitmap_key = ""; - // If the filament preset is not compatible and there is a "red flag" icon loaded, show it left - // to the filament color image. - if (wide_icons) - bitmap_key += "wide,"; - bitmap_key += "edit_preset_list"; - bitmap_key += "-h" + std::to_string(icon_height); - - wxBitmap* bmp = m_bitmap_cache->find(bitmap_key); - if (bmp == nullptr) { - // Create the bitmap with color bars.update_plater_ui - std::vector bmps; - if (wide_icons) - // Paint a red flag for incompatible presets. - bmps.emplace_back(m_bitmap_cache->mkclear(norm_icon_width, icon_height)); - // Paint the color bars. - bmps.emplace_back(m_bitmap_cache->mkclear(thin_space_icon_width, icon_height)); - bmps.emplace_back(create_scaled_bitmap(m_main_bitmap_name)); - // Paint a lock at the system presets. - bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width, icon_height)); - bmps.emplace_back(create_scaled_bitmap("edit_uni")); - bmp = m_bitmap_cache->insert(bitmap_key, bmps); - } - if (m_type == Preset::TYPE_SLA_MATERIAL) - set_label_marker(Append(separator(L("Add/Remove materials")), *bmp), LABEL_ITEM_WIZARD_MATERIALS); - else - set_label_marker(Append(separator(L("Add/Remove printers")), *bmp), LABEL_ITEM_WIZARD_PRINTERS); - } - if (m_type == Preset::TYPE_PRINTER) { // add Physical printers, if any exists @@ -608,7 +613,7 @@ void PlaterPresetComboBox::update() bmp = m_bitmap_cache->insert(bitmap_key, bmps); } - set_label_marker(Append(wxString::FromUTF8((it->name).c_str()), *bmp), LABEL_ITEM_PHYSICAL_PRINTER); + set_label_marker(Append(wxString::FromUTF8((it->full_name).c_str()), *bmp), LABEL_ITEM_PHYSICAL_PRINTER); if (ph_printers.has_selection() && it->name == ph_printers.get_selected_printer_name() || // just in case: mark selected_preset_item as a first added element selected_preset_item == INT_MAX) @@ -616,6 +621,7 @@ void PlaterPresetComboBox::update() } } +/* // add LABEL_ITEM_PHYSICAL_PRINTERS std::string bitmap_key; if (wide_icons) @@ -639,6 +645,37 @@ void PlaterPresetComboBox::update() bmp = m_bitmap_cache->insert(bitmap_key, bmps); } set_label_marker(Append(separator(L("Add physical printer")), *bmp), LABEL_ITEM_PHYSICAL_PRINTERS); +*/ + } + + if (m_type == Preset::TYPE_PRINTER || m_type == Preset::TYPE_SLA_MATERIAL) { + std::string bitmap_key = ""; + // If the filament preset is not compatible and there is a "red flag" icon loaded, show it left + // to the filament color image. + if (wide_icons) + bitmap_key += "wide,"; + bitmap_key += "edit_preset_list"; + bitmap_key += "-h" + std::to_string(icon_height); + + wxBitmap* bmp = m_bitmap_cache->find(bitmap_key); + if (bmp == nullptr) { + // Create the bitmap with color bars.update_plater_ui + std::vector bmps; + if (wide_icons) + // Paint a red flag for incompatible presets. + bmps.emplace_back(m_bitmap_cache->mkclear(norm_icon_width, icon_height)); + // Paint the color bars. + bmps.emplace_back(m_bitmap_cache->mkclear(thin_space_icon_width, icon_height)); + bmps.emplace_back(create_scaled_bitmap(m_main_bitmap_name)); + // Paint a lock at the system presets. + bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width, icon_height)); + bmps.emplace_back(create_scaled_bitmap("edit_uni")); + bmp = m_bitmap_cache->insert(bitmap_key, bmps); + } + if (m_type == Preset::TYPE_SLA_MATERIAL) + set_label_marker(Append(separator(L("Add/Remove materials")), *bmp), LABEL_ITEM_WIZARD_MATERIALS); + else + set_label_marker(Append(separator(L("Add/Remove printers")), *bmp), LABEL_ITEM_WIZARD_PRINTERS); } /* But, if selected_preset_item is still equal to INT_MAX, it means that @@ -680,7 +717,7 @@ TabPresetComboBox::TabPresetComboBox(wxWindow* parent, Preset::Type preset_type) auto selected_item = evt.GetSelection(); auto marker = reinterpret_cast(this->GetClientData(selected_item)); - if (marker >= LABEL_ITEM_MARKER && marker < LABEL_ITEM_MAX) { + if (marker >= LABEL_ITEM_DISABLED && marker < LABEL_ITEM_MAX) { this->SetSelection(this->m_last_selected); if (marker == LABEL_ITEM_WIZARD_PRINTERS) wxTheApp->CallAfter([this]() { @@ -710,7 +747,7 @@ void TabPresetComboBox::update() const std::deque& presets = m_collection->get_presets(); - std::map nonsys_presets; + std::map> nonsys_presets; wxString selected = ""; if (!presets.front().is_visible) set_label_marker(Append(separator(L("System presets")), wxNullBitmap)); @@ -719,13 +756,24 @@ void TabPresetComboBox::update() const Preset& preset = presets[i]; if (!preset.is_visible || (!show_incompatible && !preset.is_compatible && i != idx_selected)) continue; + + // marker used for disable incompatible printer models for the selected physical printer + bool is_enabled = true; + // check this value just for printer presets, when physical printer is selected + if (m_type == Preset::TYPE_PRINTER && m_preset_bundle->physical_printers.has_selection()) { + is_enabled = m_enable_all ? true : + preset.name == m_preset_bundle->physical_printers.get_selected_printer_preset_name() || + preset.config.opt_string("printer_model") == m_preset_bundle->physical_printers.get_selected_printer_model(); + } std::string bitmap_key = "tab"; - wxBitmap main_bmp = create_scaled_bitmap(m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name, this); + wxBitmap main_bmp = create_scaled_bitmap(m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name, this, 16, !is_enabled); if (m_type == Preset::TYPE_PRINTER) { bitmap_key += "_printer"; if (preset.printer_technology() == ptSLA) bitmap_key += "_sla"; + if (!is_enabled) + bitmap_key += "_disabled"; } bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt"; bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst"; @@ -737,13 +785,14 @@ void TabPresetComboBox::update() std::vector bmps; bmps.emplace_back(m_type == Preset::TYPE_PRINTER ? main_bmp : preset.is_compatible ? m_bitmapCompatible.bmp() : m_bitmapIncompatible.bmp()); // Paint a lock at the system presets. - bmps.emplace_back((preset.is_system || preset.is_default) ? m_bitmapLock.bmp() : m_bitmap_cache->mkclear(norm_icon_width, icon_height)); + bmps.emplace_back((preset.is_system || preset.is_default) ? (is_enabled ? m_bitmapLock.bmp() : m_bitmapLockDisabled.bmp()) : m_bitmap_cache->mkclear(norm_icon_width, icon_height)); bmp = m_bitmap_cache->insert(bitmap_key, bmps); } if (preset.is_default || preset.is_system) { - Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), - (bmp == 0) ? main_bmp : *bmp); + int item_id = Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), !bmp ? main_bmp : *bmp); + if (!is_enabled) + set_label_marker(item_id, LABEL_ITEM_DISABLED); if (i == idx_selected || // just in case: mark selected_preset_item as a first added element selected_preset_item == INT_MAX) @@ -751,7 +800,8 @@ void TabPresetComboBox::update() } else { - nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), bmp); + std::pair pair(bmp, is_enabled); + nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), std::pair(bmp, is_enabled)); if (i == idx_selected) selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()); } @@ -761,8 +811,11 @@ void TabPresetComboBox::update() if (!nonsys_presets.empty()) { set_label_marker(Append(separator(L("User presets")), wxNullBitmap)); - for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { - Append(it->first, *it->second); + for (std::map>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { + int item_id = Append(it->first, *it->second.first); + bool is_enabled = it->second.second; + if (!is_enabled) + set_label_marker(item_id, LABEL_ITEM_DISABLED); if (it->first == selected || // just in case: mark selected_preset_item as a first added element selected_preset_item == INT_MAX) @@ -845,6 +898,17 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) int border = 10; m_printer_presets = new TabPresetComboBox(this, Preset::TYPE_PRINTER); + + if (printer_name.IsEmpty()) { + // if printer_name is empty it means that new printer is created, so enable all items in the preset list + m_printer_presets->set_enable_all(); + printer_name = _L("My Printer Device"); + } + else { + std::string full_name = into_u8(printer_name); + printer_name = from_u8(PhysicalPrinter::get_short_name(full_name)); + } + m_printer_presets->set_selection_changed_function([this](int selection) { std::string selected_string = Preset::remove_suffix_modified(m_printer_presets->GetString(selection).ToUTF8().data()); Preset* preset = wxGetApp().preset_bundle->printers.find_preset(selected_string); @@ -854,17 +918,22 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) preset = &edited_preset; m_printer.update_from_preset(*preset); + update_printer_name(); + // update values m_optgroup->reload_config(); update(); }); m_printer_presets->update(); - wxString preset_name = m_printer_presets->GetString(m_printer_presets->GetSelection()); + wxStaticText* label_top = new wxStaticText(this, wxID_ANY, _("Descriptive name for the printer device") + ":"); + m_printer_name = new wxTextCtrl(this, wxID_ANY, printer_name, wxDefaultPosition, wxDefaultSize); + m_printer_name->Bind(wxEVT_TEXT, [this](wxEvent&) { this->update_printer_name(); }); - if (printer_name.IsEmpty()) - printer_name = preset_name + " - "+_L("Physical Printer"); - m_printer_name = new wxTextCtrl(this, wxID_ANY, printer_name, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); + wxStaticText* label_bottom = new wxStaticText(this, wxID_ANY, _("This printer name will be shown in the presets list") + ":"); + m_full_printer_name = new wxStaticText(this, wxID_ANY, ""); + + update_printer_name(); PhysicalPrinterCollection& printers = wxGetApp().preset_bundle->physical_printers; PhysicalPrinter* printer = printers.find_printer(into_u8(printer_name)); @@ -887,8 +956,11 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + topSizer->Add(label_top , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); topSizer->Add(m_printer_name , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); topSizer->Add(m_printer_presets , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(label_bottom , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(m_full_printer_name , 0, wxEXPAND | wxLEFT | wxRIGHT, border); topSizer->Add(m_optgroup->sizer , 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); topSizer->Add(btns , 0, wxEXPAND | wxALL, border); @@ -1053,6 +1125,15 @@ void PhysicalPrinterDialog::update() this->Layout(); } +void PhysicalPrinterDialog::update_printer_name() +{ + wxString printer_name = m_printer_name->GetValue(); + wxString preset_name = m_printer_presets->GetString(m_printer_presets->GetSelection()); + + m_full_printer_name->SetLabelText("\t" + printer_name + " * " + preset_name); + this->Layout(); +} + void PhysicalPrinterDialog::on_dpi_changed(const wxRect& suggested_rect) { const int& em = em_unit(); @@ -1096,8 +1177,8 @@ void PhysicalPrinterDialog::OnOK(wxEvent& event) printers.delete_printer(into_u8(printer_name)); } - //upadte printer name, if it was changed - m_printer.name = into_u8(printer_name); + //update printer name, if it was changed + m_printer.set_name(into_u8(printer_name)); // save new physical printer printers.save_printer(m_printer); diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index 196c4368e8..653c0e540c 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -13,6 +13,7 @@ class wxString; class wxTextCtrl; +class wxStatictext; class ScalableButton; namespace Slic3r { @@ -35,6 +36,7 @@ public: enum LabelItemType { LABEL_ITEM_PHYSICAL_PRINTER = 0xffffff01, + LABEL_ITEM_DISABLED, LABEL_ITEM_MARKER, LABEL_ITEM_PHYSICAL_PRINTERS, LABEL_ITEM_WIZARD_PRINTERS, @@ -66,6 +68,8 @@ protected: ScalableBitmap m_bitmapIncompatible; // Indicator, that the preset is system and not modified. ScalableBitmap m_bitmapLock; + // Disabled analogue of the m_bitmapLock . + ScalableBitmap m_bitmapLockDisabled; int m_last_selected; int m_em_unit; @@ -125,6 +129,7 @@ public: bool is_selected_physical_printer(); bool switch_to_tab(); + void show_add_menu(); void show_edit_menu(); void update() override; @@ -142,6 +147,7 @@ private: class TabPresetComboBox : public PresetComboBox { bool show_incompatible {false}; + bool m_enable_all {false}; std::function on_selection_changed { nullptr }; public: @@ -156,6 +162,7 @@ public: void msw_rescale() override; void set_selection_changed_function(std::function sel_changed) { on_selection_changed = sel_changed; } + void set_enable_all(bool enable=true) { m_enable_all = enable; } }; @@ -169,6 +176,7 @@ class PhysicalPrinterDialog : public DPIDialog DynamicPrintConfig* m_config { nullptr }; wxTextCtrl* m_printer_name { nullptr }; + wxStaticText* m_full_printer_name { nullptr }; TabPresetComboBox* m_printer_presets { nullptr }; ConfigOptionsGroup* m_optgroup { nullptr }; @@ -178,6 +186,7 @@ class PhysicalPrinterDialog : public DPIDialog void build_printhost_settings(ConfigOptionsGroup* optgroup); void update(); + void update_printer_name(); void OnOK(wxEvent& event); public: diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index da5d51dc54..595283e983 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -162,10 +162,9 @@ void Tab::create_preset_tab() // preset chooser m_presets_choice = new TabPresetComboBox(panel, m_type); m_presets_choice->set_selection_changed_function([this](int selection) { - // unselect pthysical printer, if it was selected - m_preset_bundle->physical_printers.unselect_printer(); - // select preset std::string selected_string = m_presets_choice->GetString(selection).ToUTF8().data(); + update_physical_printers(selected_string); + // select preset select_preset(selected_string); }); @@ -763,6 +762,32 @@ void Tab::update_tab_ui() m_presets_choice->update(); } +void Tab::update_physical_printers(std::string preset_name) +{ + if (m_type == Preset::TYPE_PRINTER && m_preset_bundle->physical_printers.has_selection()) + { + std::string printer_name = m_preset_bundle->physical_printers.get_selected_full_printer_name(); + wxString msg_text = from_u8((boost::format(_u8L("You have selected physical printer \"%1%\".")) % printer_name).str()); + msg_text += "\n\n" + _L("Would you like to change related preset for this printer?") + "\n\n" + + _L("Select YES if you want to change related preset for this printer \n" + "or NO to switch to the another preset (logical printer)."); + wxMessageDialog dialog(nullptr, msg_text, _L("Warning"), wxICON_WARNING | wxYES | wxNO); + + if (dialog.ShowModal() == wxID_YES) { + preset_name = Preset::remove_suffix_modified(preset_name); + Preset* preset = m_presets->find_preset(preset_name); + assert(preset); + Preset& edited_preset = m_presets->get_edited_preset(); + if (preset->name == edited_preset.name) + preset = &edited_preset; + m_preset_bundle->physical_printers.get_selected_printer().update_from_preset(*preset); + } + else + // unselect physical printer, if it was selected + m_preset_bundle->physical_printers.unselect_printer(); + } +} + // Load a provied DynamicConfig into the tab, modifying the active preset. // This could be used for example by setting a Wipe Tower position by interactive manipulation in the 3D view. void Tab::load_config(const DynamicPrintConfig& config) @@ -3269,6 +3294,8 @@ void Tab::save_preset(std::string name /*= ""*/, bool detach) // Mark the print & filament enabled if they are compatible with the currently selected preset. // If saving the preset changes compatibility with other presets, keep the now incompatible dependent presets selected, however with a "red flag" icon showing that they are no more compatible. m_preset_bundle->update_compatible(PresetSelectCompatibleType::Never); + //update physical printer's related printer preset if it's needed + update_physical_printers(name); // Add the new item into the UI component, remove dirty flags and activate the saved item. update_tab_ui(); // Update the selection boxes at the plater. diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index bc15efa359..69720ff65e 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -306,6 +306,7 @@ public: void load_initial_data(); void update_dirty(); void update_tab_ui(); + void update_physical_printers(std::string preset_name); void load_config(const DynamicPrintConfig& config); virtual void reload_config(); void update_mode(); diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 39b3e154b4..67b5a18f79 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -731,11 +731,12 @@ void MenuWithSeparators::SetSecondSeparator() // ---------------------------------------------------------------------------- ScalableBitmap::ScalableBitmap( wxWindow *parent, const std::string& icon_name/* = ""*/, - const int px_cnt/* = 16*/): + const int px_cnt/* = 16*/, + const bool grayscale/* = false*/): m_parent(parent), m_icon_name(icon_name), m_px_cnt(px_cnt) { - m_bmp = create_scaled_bitmap(icon_name, parent, px_cnt); + m_bmp = create_scaled_bitmap(icon_name, parent, px_cnt, grayscale); } wxSize ScalableBitmap::GetBmpSize() const @@ -768,7 +769,7 @@ int ScalableBitmap::GetBmpHeight() const void ScalableBitmap::msw_rescale() { - m_bmp = create_scaled_bitmap(m_icon_name, m_parent, m_px_cnt); + m_bmp = create_scaled_bitmap(m_icon_name, m_parent, m_px_cnt, m_grayscale); } // ---------------------------------------------------------------------------- diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 17fe8992c5..9be3361bda 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -130,7 +130,8 @@ public: ScalableBitmap() {}; ScalableBitmap( wxWindow *parent, const std::string& icon_name = "", - const int px_cnt = 16); + const int px_cnt = 16, + const bool grayscale = false); ~ScalableBitmap() {} @@ -151,6 +152,7 @@ private: wxBitmap m_bmp = wxBitmap(); std::string m_icon_name = ""; int m_px_cnt {16}; + bool m_grayscale {false}; }; From 6d4a0d91fce7bc8e85302d9b0f8d14b882c0cb9c Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 26 Jun 2020 16:58:53 +0200 Subject: [PATCH 148/503] Fixed typo in PresetComboBox.hpp and added missed include in libslic3r.h --- src/libslic3r/libslic3r.h | 1 + src/slic3r/GUI/PresetComboBoxes.hpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index db375ec14f..3ea70d9985 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -5,6 +5,7 @@ // this needs to be included early for MSVC (listing it in Build.PL is not enough) #include +#include #include #include #include diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index 653c0e540c..dce22bc82b 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -13,7 +13,7 @@ class wxString; class wxTextCtrl; -class wxStatictext; +class wxStaticText; class ScalableButton; namespace Slic3r { From 69de5c8c9fdb4e51e3b6d489f36b0b6227360770 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 29 Jun 2020 14:00:08 +0200 Subject: [PATCH 149/503] GCodeViewer -> Pass vertex normal to shaders for toolpaths --- resources/shaders/extrusions.fs | 40 --- resources/shaders/extrusions.vs | 11 - resources/shaders/options_120_solid.fs | 2 +- resources/shaders/toolpaths.fs | 31 ++ resources/shaders/toolpaths.vs | 21 ++ resources/shaders/travels.fs | 40 --- resources/shaders/travels.vs | 11 - src/libslic3r/GCode/GCodeProcessor.cpp | 12 + src/libslic3r/GCode/GCodeProcessor.hpp | 5 + src/libslic3r/Technologies.hpp | 4 +- src/slic3r/GUI/GCodeViewer.cpp | 410 +++++++++++++++++-------- src/slic3r/GUI/GCodeViewer.hpp | 100 ++++-- src/slic3r/GUI/GLShadersManager.cpp | 9 +- 13 files changed, 431 insertions(+), 265 deletions(-) delete mode 100644 resources/shaders/extrusions.fs delete mode 100644 resources/shaders/extrusions.vs create mode 100644 resources/shaders/toolpaths.fs create mode 100644 resources/shaders/toolpaths.vs delete mode 100644 resources/shaders/travels.fs delete mode 100644 resources/shaders/travels.vs diff --git a/resources/shaders/extrusions.fs b/resources/shaders/extrusions.fs deleted file mode 100644 index 65db0667a9..0000000000 --- a/resources/shaders/extrusions.fs +++ /dev/null @@ -1,40 +0,0 @@ -#version 110 - -#define INTENSITY_AMBIENT 0.3 -#define INTENSITY_CORRECTION 0.6 - -// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) -const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); -#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SHININESS 20.0 - -// normalized values for (1./1.43, 0.2/1.43, 1./1.43) -const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); -#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) - -uniform vec3 uniform_color; - -varying vec3 eye_position; -varying vec3 eye_normal; - -// x = tainted, y = specular; -vec2 intensity; - -void main() -{ - vec3 normal = normalize(eye_normal); - - // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. - // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. - float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); - - intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; - intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); - - // Perform the same lighting calculation for the 2nd light source (no specular applied). - NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); - intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; - - gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color * intensity.x, 1.0); -} diff --git a/resources/shaders/extrusions.vs b/resources/shaders/extrusions.vs deleted file mode 100644 index 8980f3944b..0000000000 --- a/resources/shaders/extrusions.vs +++ /dev/null @@ -1,11 +0,0 @@ -#version 110 - -varying vec3 eye_position; -varying vec3 eye_normal; - -void main() -{ - eye_position = (gl_ModelViewMatrix * gl_Vertex).xyz; - eye_normal = gl_NormalMatrix * vec3(0.0, 0.0, 1.0); - gl_Position = ftransform(); -} diff --git a/resources/shaders/options_120_solid.fs b/resources/shaders/options_120_solid.fs index 4719ff96a6..4480d7b147 100644 --- a/resources/shaders/options_120_solid.fs +++ b/resources/shaders/options_120_solid.fs @@ -84,6 +84,6 @@ void main() vec3 eye_on_sphere_position = eye_position_on_sphere(eye_position_from_fragment()); - gl_FragDepth = fragment_depth(eye_on_sphere_position); +// gl_FragDepth = fragment_depth(eye_on_sphere_position); gl_FragColor = on_sphere_color(eye_on_sphere_position); } diff --git a/resources/shaders/toolpaths.fs b/resources/shaders/toolpaths.fs new file mode 100644 index 0000000000..13f60c0a82 --- /dev/null +++ b/resources/shaders/toolpaths.fs @@ -0,0 +1,31 @@ +#version 110 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); + +// x = ambient, y = top diffuse, z = front diffuse, w = global +uniform vec4 light_intensity; +uniform vec3 uniform_color; + +varying vec3 eye_position; +varying vec3 eye_normal; + +float intensity; + +void main() +{ + vec3 normal = normalize(eye_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. Take the abs value to light the lines no matter in which direction the normal points. + float NdotL = abs(dot(normal, LIGHT_TOP_DIR)); + + intensity = light_intensity.x + NdotL * light_intensity.y; + + // Perform the same lighting calculation for the 2nd light source. + NdotL = abs(dot(normal, LIGHT_FRONT_DIR)); + intensity += NdotL * light_intensity.z; + + gl_FragColor = vec4(uniform_color * light_intensity.w * intensity, 1.0); +} diff --git a/resources/shaders/toolpaths.vs b/resources/shaders/toolpaths.vs new file mode 100644 index 0000000000..34d141bfe1 --- /dev/null +++ b/resources/shaders/toolpaths.vs @@ -0,0 +1,21 @@ +#version 110 + +varying vec3 eye_position; +varying vec3 eye_normal; + +vec3 world_normal() +{ + // the world normal is always parallel to the world XY plane + // the x component is stored into gl_Vertex.w + float x = gl_Vertex.w; + float y = sqrt(1.0 - x * x); + return vec3(x, y, 0.0); +} + +void main() +{ + vec4 world_position = vec4(gl_Vertex.xyz, 1.0); + gl_Position = gl_ModelViewProjectionMatrix * world_position; + eye_position = (gl_ModelViewMatrix * world_position).xyz; + eye_normal = gl_NormalMatrix * world_normal(); +} diff --git a/resources/shaders/travels.fs b/resources/shaders/travels.fs deleted file mode 100644 index 65db0667a9..0000000000 --- a/resources/shaders/travels.fs +++ /dev/null @@ -1,40 +0,0 @@ -#version 110 - -#define INTENSITY_AMBIENT 0.3 -#define INTENSITY_CORRECTION 0.6 - -// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) -const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); -#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SHININESS 20.0 - -// normalized values for (1./1.43, 0.2/1.43, 1./1.43) -const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); -#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) - -uniform vec3 uniform_color; - -varying vec3 eye_position; -varying vec3 eye_normal; - -// x = tainted, y = specular; -vec2 intensity; - -void main() -{ - vec3 normal = normalize(eye_normal); - - // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. - // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. - float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); - - intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; - intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); - - // Perform the same lighting calculation for the 2nd light source (no specular applied). - NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); - intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; - - gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color * intensity.x, 1.0); -} diff --git a/resources/shaders/travels.vs b/resources/shaders/travels.vs deleted file mode 100644 index 8980f3944b..0000000000 --- a/resources/shaders/travels.vs +++ /dev/null @@ -1,11 +0,0 @@ -#version 110 - -varying vec3 eye_position; -varying vec3 eye_normal; - -void main() -{ - eye_position = (gl_ModelViewMatrix * gl_Vertex).xyz; - eye_normal = gl_NormalMatrix * vec3(0.0, 0.0, 1.0); - gl_Position = ftransform(); -} diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index d561647262..14f29b56b6 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -7,6 +7,10 @@ #if ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER_STATISTICS +#include +#endif // ENABLE_GCODE_VIEWER_STATISTICS + static const float INCHES_TO_MM = 25.4f; static const float MMMIN_TO_MMSEC = 1.0f / 60.0f; @@ -89,9 +93,17 @@ void GCodeProcessor::reset() void GCodeProcessor::process_file(const std::string& filename) { +#if ENABLE_GCODE_VIEWER_STATISTICS + auto start_time = std::chrono::high_resolution_clock::now(); +#endif // ENABLE_GCODE_VIEWER_STATISTICS + m_result.id = ++s_result_id; m_result.moves.emplace_back(MoveVertex()); m_parser.parse_file(filename, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { process_gcode_line(line); }); + +#if ENABLE_GCODE_VIEWER_STATISTICS + m_result.time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start_time).count(); +#endif // ENABLE_GCODE_VIEWER_STATISTICS } void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index bc49245848..3f596c9c2b 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -106,7 +106,12 @@ namespace Slic3r { { unsigned int id; std::vector moves; +#if ENABLE_GCODE_VIEWER_STATISTICS + long long time{ 0 }; + void reset() { time = 0; moves = std::vector(); } +#else void reset() { moves = std::vector(); } +#endif // ENABLE_GCODE_VIEWER_STATISTICS }; private: diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index b04e78c4ed..fb84efe5ab 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -59,8 +59,8 @@ // Enable G-Code viewer #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) -#define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) -#define ENABLE_GCODE_VIEWER_SHADERS_EDITOR (0 && ENABLE_GCODE_VIEWER) +#define ENABLE_GCODE_VIEWER_STATISTICS (1 && ENABLE_GCODE_VIEWER) +#define ENABLE_GCODE_VIEWER_SHADERS_EDITOR (1 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_AS_STATE (1 && ENABLE_GCODE_VIEWER) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 05a8c20bba..6d6ba557e2 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -64,12 +64,23 @@ std::vector> decode_colors(const std::vector & void GCodeViewer::VBuffer::reset() { // release gpu memory - if (vbo_id > 0) { - glsafe(::glDeleteBuffers(1, &vbo_id)); - vbo_id = 0; + if (id > 0) { + glsafe(::glDeleteBuffers(1, &id)); + id = 0; } - vertices_count = 0; + count = 0; +} + +void GCodeViewer::IBuffer::reset() +{ + // release gpu memory + if (id > 0) { + glsafe(::glDeleteBuffers(1, &id)); + id = 0; + } + + count = 0; } bool GCodeViewer::Path::matches(const GCodeProcessor::MoveVertex& move) const @@ -96,21 +107,18 @@ bool GCodeViewer::Path::matches(const GCodeProcessor::MoveVertex& move) const } } -void GCodeViewer::IBuffer::reset() +void GCodeViewer::TBuffer::reset() { // release gpu memory - if (ibo_id > 0) { - glsafe(::glDeleteBuffers(1, &ibo_id)); - ibo_id = 0; - } + vertices.reset(); + indices.reset(); // release cpu memory - indices_count = 0; paths = std::vector(); render_paths = std::vector(); } -void GCodeViewer::IBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsigned int i_id, unsigned int s_id) +void GCodeViewer::TBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsigned int i_id, unsigned int s_id) { Path::Endpoint endpoint = { i_id, s_id, move.position }; paths.push_back({ move.type, move.extrusion_role, endpoint, endpoint, move.delta_extruder, move.height, move.width, move.feedrate, move.fan_speed, move.volumetric_rate(), move.extruder_id, move.cp_color_id }); @@ -209,7 +217,7 @@ void GCodeViewer::SequentialView::Marker::render() const } const std::vector GCodeViewer::Extrusion_Role_Colors {{ - { 0.50f, 0.50f, 0.50f }, // erNone + { 0.75f, 0.75f, 0.75f }, // erNone { 1.00f, 1.00f, 0.40f }, // erPerimeter { 1.00f, 0.65f, 0.00f }, // erExternalPerimeter { 0.00f, 0.00f, 1.00f }, // erOverhangPerimeter @@ -257,6 +265,29 @@ const std::vector GCodeViewer::Range_Colors {{ bool GCodeViewer::init() { + for (size_t i = 0; i < m_buffers.size(); ++i) + { + switch (buffer_type(i)) + { + case GCodeProcessor::EMoveType::Tool_change: + case GCodeProcessor::EMoveType::Color_change: + case GCodeProcessor::EMoveType::Pause_Print: + case GCodeProcessor::EMoveType::Custom_GCode: + case GCodeProcessor::EMoveType::Retract: + case GCodeProcessor::EMoveType::Unretract: + { + m_buffers[i].vertices.format = VBuffer::EFormat::Position; + break; + } + case GCodeProcessor::EMoveType::Extrude: + case GCodeProcessor::EMoveType::Travel: + { + m_buffers[i].vertices.format = VBuffer::EFormat::PositionNormal; + break; + } + } + } + set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Extrude, true); m_sequential_view.marker.init(); init_shaders(); @@ -306,7 +337,7 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: auto start_time = std::chrono::high_resolution_clock::now(); #endif // ENABLE_GCODE_VIEWER_STATISTICS - if (m_vertices.vertices_count == 0) + if (m_vertices_count == 0) return; // update tool colors @@ -314,7 +345,7 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: // update ranges for coloring / legend m_extrusions.reset_ranges(); - for (size_t i = 0; i < m_vertices.vertices_count; ++i) { + for (size_t i = 0; i < m_vertices_count; ++i) { // skip first vertex if (i == 0) continue; @@ -342,19 +373,18 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: } } - // update buffers' render paths - refresh_render_paths(false, false); - #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.refresh_time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start_time).count(); #endif // ENABLE_GCODE_VIEWER_STATISTICS + + // update buffers' render paths + refresh_render_paths(false, false); } void GCodeViewer::reset() { - m_vertices.reset(); - - for (IBuffer& buffer : m_buffers) { + m_vertices_count = 0; + for (TBuffer& buffer : m_buffers) { buffer.reset(); } @@ -472,8 +502,8 @@ void GCodeViewer::init_shaders() case GCodeProcessor::EMoveType::Custom_GCode: { m_buffers[i].shader = is_glsl_120 ? "options_120_solid" : "options_110"; break; } case GCodeProcessor::EMoveType::Retract: { m_buffers[i].shader = is_glsl_120 ? "options_120_solid" : "options_110"; break; } case GCodeProcessor::EMoveType::Unretract: { m_buffers[i].shader = is_glsl_120 ? "options_120_solid" : "options_110"; break; } - case GCodeProcessor::EMoveType::Extrude: { m_buffers[i].shader = "extrusions"; break; } - case GCodeProcessor::EMoveType::Travel: { m_buffers[i].shader = "travels"; break; } + case GCodeProcessor::EMoveType::Extrude: { m_buffers[i].shader = "toolpaths"; break; } + case GCodeProcessor::EMoveType::Travel: { m_buffers[i].shader = "toolpaths"; break; } default: { break; } } } @@ -484,16 +514,15 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) #if ENABLE_GCODE_VIEWER_STATISTICS auto start_time = std::chrono::high_resolution_clock::now(); m_statistics.results_size = SLIC3R_STDVEC_MEMSIZE(gcode_result.moves, GCodeProcessor::MoveVertex); + m_statistics.results_time = gcode_result.time; #endif // ENABLE_GCODE_VIEWER_STATISTICS - // vertex data - m_vertices.vertices_count = gcode_result.moves.size(); - if (m_vertices.vertices_count == 0) + // vertices data + m_vertices_count = gcode_result.moves.size(); + if (m_vertices_count == 0) return; - // vertex data / bounding box -> extract from result - std::vector vertices_data(m_vertices.vertices_count * 3); - for (size_t i = 0; i < m_vertices.vertices_count; ++i) { + for (size_t i = 0; i < m_vertices_count; ++i) { const GCodeProcessor::MoveVertex& move = gcode_result.moves[i]; #if ENABLE_GCODE_VIEWER_AS_STATE if (wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer) @@ -506,29 +535,16 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) #if ENABLE_GCODE_VIEWER_AS_STATE } #endif // ENABLE_GCODE_VIEWER_AS_STATE - ::memcpy(static_cast(&vertices_data[i * 3]), static_cast(move.position.data()), 3 * sizeof(float)); } + // max bounding box m_max_bounding_box = m_paths_bounding_box; m_max_bounding_box.merge(m_paths_bounding_box.max + m_sequential_view.marker.get_bounding_box().size()[2] * Vec3d::UnitZ()); -#if ENABLE_GCODE_VIEWER_STATISTICS - m_statistics.vertices_size = SLIC3R_STDVEC_MEMSIZE(vertices_data, float); - m_statistics.vertices_gpu_size = vertices_data.size() * sizeof(float); -#endif // ENABLE_GCODE_VIEWER_STATISTICS - - // vertex data -> send to gpu - glsafe(::glGenBuffers(1, &m_vertices.vbo_id)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vertices.vbo_id)); - glsafe(::glBufferData(GL_ARRAY_BUFFER, vertices_data.size() * sizeof(float), vertices_data.data(), GL_STATIC_DRAW)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - - // vertex data -> no more needed, free ram - vertices_data = std::vector(); - - // indices data -> extract from result + // toolpaths data -> extract from result + std::vector> vertices(m_buffers.size()); std::vector> indices(m_buffers.size()); - for (size_t i = 0; i < m_vertices.vertices_count; ++i) { + for (size_t i = 0; i < m_vertices_count; ++i) { // skip first vertex if (i == 0) continue; @@ -537,7 +553,8 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i]; unsigned char id = buffer_id(curr.type); - IBuffer& buffer = m_buffers[id]; + TBuffer& buffer = m_buffers[id]; + std::vector& buffer_vertices = vertices[id]; std::vector& buffer_indices = indices[id]; switch (curr.type) @@ -549,54 +566,103 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) case GCodeProcessor::EMoveType::Retract: case GCodeProcessor::EMoveType::Unretract: { + for (int j = 0; j < 3; ++j) { + buffer_vertices.push_back(curr.position[j]); + } buffer.add_path(curr, static_cast(buffer_indices.size()), static_cast(i)); - buffer_indices.push_back(static_cast(i)); + buffer_indices.push_back(static_cast(buffer_indices.size())); break; } case GCodeProcessor::EMoveType::Extrude: case GCodeProcessor::EMoveType::Travel: { + // x component of the normal to the current segment (the normal is parallel to the XY plane) + float normal_x = (curr.position - prev.position).normalized()[1]; + if (prev.type != curr.type || !buffer.paths.back().matches(curr)) { - buffer.add_path(curr, static_cast(buffer_indices.size()), static_cast(i - 1)); + // add starting vertex position + for (int j = 0; j < 3; ++j) { + buffer_vertices.push_back(prev.position[j]); + } + // add starting vertex normal x component + buffer_vertices.push_back(normal_x); + // add starting index + buffer_indices.push_back(buffer_indices.size()); + buffer.add_path(curr, static_cast(buffer_indices.size() - 1), static_cast(i - 1)); Path& last_path = buffer.paths.back(); last_path.first.position = prev.position; - buffer_indices.push_back(static_cast(i - 1)); } - - buffer.paths.back().last = { static_cast(buffer_indices.size()), static_cast(i), curr.position }; - buffer_indices.push_back(static_cast(i)); + + Path& last_path = buffer.paths.back(); + if (last_path.first.i_id != last_path.last.i_id) + { + // add previous vertex position + for (int j = 0; j < 3; ++j) { + buffer_vertices.push_back(prev.position[j]); + } + // add previous vertex normal x component + buffer_vertices.push_back(normal_x); + // add previous index + buffer_indices.push_back(buffer_indices.size()); + } + + // add current vertex position + for (int j = 0; j < 3; ++j) { + buffer_vertices.push_back(curr.position[j]); + } + // add current vertex normal x component + buffer_vertices.push_back(normal_x); + // add current index + buffer_indices.push_back(buffer_indices.size()); + last_path.last = { static_cast(buffer_indices.size() - 1), static_cast(i), curr.position }; break; } default: { break; } } } + // toolpaths data -> send data to gpu + for (size_t i = 0; i < m_buffers.size(); ++i) { + TBuffer& buffer = m_buffers[i]; + + // vertices + const std::vector& buffer_vertices = vertices[i]; + buffer.vertices.count = buffer_vertices.size() / buffer.vertices.vertex_size_floats(); #if ENABLE_GCODE_VIEWER_STATISTICS - for (IBuffer& buffer : m_buffers) { - m_statistics.paths_size += SLIC3R_STDVEC_MEMSIZE(buffer.paths, Path); - } + m_statistics.vertices_gpu_size = buffer_vertices.size() * sizeof(float); #endif // ENABLE_GCODE_VIEWER_STATISTICS - // indices data -> send data to gpu - for (size_t i = 0; i < m_buffers.size(); ++i) { - IBuffer& buffer = m_buffers[i]; + glsafe(::glGenBuffers(1, &buffer.vertices.id)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vertices.id)); + glsafe(::glBufferData(GL_ARRAY_BUFFER, buffer_vertices.size() * sizeof(float), buffer_vertices.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + + // indices const std::vector& buffer_indices = indices[i]; - buffer.indices_count = buffer_indices.size(); + buffer.indices.count = buffer_indices.size(); #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.indices_size += SLIC3R_STDVEC_MEMSIZE(buffer_indices, unsigned int); - m_statistics.indices_gpu_size += buffer.indices_count * sizeof(unsigned int); + m_statistics.indices_gpu_size += buffer.indices.count * sizeof(unsigned int); #endif // ENABLE_GCODE_VIEWER_STATISTICS - if (buffer.indices_count > 0) { - glsafe(::glGenBuffers(1, &buffer.ibo_id)); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.ibo_id)); - glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, buffer.indices_count * sizeof(unsigned int), buffer_indices.data(), GL_STATIC_DRAW)); + if (buffer.indices.count > 0) { + glsafe(::glGenBuffers(1, &buffer.indices.id)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.indices.id)); + glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, buffer.indices.count * sizeof(unsigned int), buffer_indices.data(), GL_STATIC_DRAW)); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); } } +#if ENABLE_GCODE_VIEWER_STATISTICS + for (const TBuffer& buffer : m_buffers) { + m_statistics.paths_size += SLIC3R_STDVEC_MEMSIZE(buffer.paths, Path); + } + m_statistics.travel_segments_count = indices[buffer_id(GCodeProcessor::EMoveType::Travel)].size() / 2; + m_statistics.extrude_segments_count = indices[buffer_id(GCodeProcessor::EMoveType::Extrude)].size() / 2; +#endif // ENABLE_GCODE_VIEWER_STATISTICS + // layers zs / roles / extruder ids / cp color ids -> extract from result - for (size_t i = 0; i < m_vertices.vertices_count; ++i) { + for (size_t i = 0; i < m_vertices_count; ++i) { const GCodeProcessor::MoveVertex& move = gcode_result.moves[i]; if (move.type == GCodeProcessor::EMoveType::Extrude) m_layers_zs.emplace_back(static_cast(move.position[2])); @@ -728,18 +794,18 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool m_statistics.render_paths_size = 0; #endif // ENABLE_GCODE_VIEWER_STATISTICS - m_sequential_view.endpoints.first = m_vertices.vertices_count; + m_sequential_view.endpoints.first = m_vertices_count; m_sequential_view.endpoints.last = 0; if (!keep_sequential_current_first) m_sequential_view.current.first = 0; if (!keep_sequential_current_last) - m_sequential_view.current.last = m_vertices.vertices_count; + m_sequential_view.current.last = m_vertices_count; // first pass: collect visible paths and update sequential view data - std::vector> paths; - for (IBuffer& buffer : m_buffers) { + std::vector> paths; + for (TBuffer& buffer : m_buffers) { // reset render paths - buffer.render_paths = std::vector(); + buffer.render_paths.clear(); if (!buffer.visible) continue; @@ -767,23 +833,43 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool // update current sequential position m_sequential_view.current.first = keep_sequential_current_first ? std::clamp(m_sequential_view.current.first, m_sequential_view.endpoints.first, m_sequential_view.endpoints.last) : m_sequential_view.endpoints.first; m_sequential_view.current.last = keep_sequential_current_last ? std::clamp(m_sequential_view.current.last, m_sequential_view.endpoints.first, m_sequential_view.endpoints.last) : m_sequential_view.endpoints.last; - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vertices.vbo_id)); - size_t v_size = VBuffer::vertex_size_bytes(); - glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, static_cast(m_sequential_view.current.last * v_size), static_cast(v_size), static_cast(m_sequential_view.current_position.data()))); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - // second pass: filter paths by sequential data + // get the world position from gpu + bool found = false; + for (const TBuffer& buffer : m_buffers) { + // searches the path containing the current position + for (const Path& path : buffer.paths) { + if (path.first.s_id <= m_sequential_view.current.last && m_sequential_view.current.last <= path.last.s_id) { + size_t offset = m_sequential_view.current.last - path.first.s_id; + if (offset > 0 && (path.type == GCodeProcessor::EMoveType::Travel || path.type == GCodeProcessor::EMoveType::Extrude)) + offset = 1 + 2 * (offset - 1); + + offset += path.first.i_id; + + // gets the position from the vertices buffer on gpu + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vertices.id)); + glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, static_cast(offset * buffer.vertices.vertex_size_bytes()), static_cast(3 * sizeof(float)), static_cast(m_sequential_view.current_position.data()))); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + found = true; + break; + } + } + if (found) + break; + } + + // second pass: filter paths by sequential data and collect them by color for (auto&& [buffer, id] : paths) { const Path& path = buffer->paths[id]; - if ((m_sequential_view.current.last <= path.first.s_id) || (path.last.s_id <= m_sequential_view.current.first)) + if (m_sequential_view.current.last <= path.first.s_id || path.last.s_id <= m_sequential_view.current.first) continue; Color color; switch (path.type) { case GCodeProcessor::EMoveType::Extrude: { color = extrusion_color(path); break; } - case GCodeProcessor::EMoveType::Travel: { color = (m_view_type == EViewType::Feedrate || m_view_type == EViewType::Tool || m_view_type == EViewType::ColorPrint) ? extrusion_color(path) : travel_color(path); break; } - default: { color = { 0.0f, 0.0f, 0.0f }; break; } + case GCodeProcessor::EMoveType::Travel: { color = (m_view_type == EViewType::Feedrate || m_view_type == EViewType::Tool || m_view_type == EViewType::ColorPrint) ? extrusion_color(path) : travel_color(path); break; } + default: { color = { 0.0f, 0.0f, 0.0f }; break; } } auto it = std::find_if(buffer->render_paths.begin(), buffer->render_paths.end(), [color](const RenderPath& path) { return path.color == color; }); @@ -792,7 +878,11 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool it->color = color; } - it->sizes.push_back(std::min(m_sequential_view.current.last, path.last.s_id) - std::max(m_sequential_view.current.first, path.first.s_id) + 1); + unsigned int size = std::min(m_sequential_view.current.last, path.last.s_id) - std::max(m_sequential_view.current.first, path.first.s_id) + 1; + if (path.type == GCodeProcessor::EMoveType::Extrude || path.type == GCodeProcessor::EMoveType::Travel) + size = 2 * (size - 1); + + it->sizes.push_back(size); unsigned int delta_1st = 0; if ((path.first.s_id < m_sequential_view.current.first) && (m_sequential_view.current.first <= path.last.s_id)) delta_1st = m_sequential_view.current.first - path.first.s_id; @@ -801,14 +891,13 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool } #if ENABLE_GCODE_VIEWER_STATISTICS - for (const IBuffer& buffer : m_buffers) { + for (const TBuffer& buffer : m_buffers) { m_statistics.render_paths_size += SLIC3R_STDVEC_MEMSIZE(buffer.render_paths, RenderPath); for (const RenderPath& path : buffer.render_paths) { m_statistics.render_paths_size += SLIC3R_STDVEC_MEMSIZE(path.sizes, unsigned int); m_statistics.render_paths_size += SLIC3R_STDVEC_MEMSIZE(path.offsets, size_t); } } - m_statistics.refresh_paths_time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start_time).count(); #endif // ENABLE_GCODE_VIEWER_STATISTICS } @@ -816,9 +905,9 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool void GCodeViewer::render_toolpaths() const { #if ENABLE_GCODE_VIEWER_SHADERS_EDITOR - float point_size = m_shaders_editor.point_size; + float point_size = m_shaders_editor.points.point_size; #else - float point_size = 1.0f; + float point_size = 0.8f; #endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR const Camera& camera = wxGetApp().plater()->get_camera(); double zoom = camera.get_zoom(); @@ -828,12 +917,12 @@ void GCodeViewer::render_toolpaths() const Transform3d inv_proj = camera.get_projection_matrix().inverse(); - auto render_as_points = [this, zoom, inv_proj, viewport, point_size, near_plane_height](const IBuffer& buffer, EOptionsColors color_id, GLShaderProgram& shader) { + auto render_as_points = [this, zoom, inv_proj, viewport, point_size, near_plane_height](const TBuffer& buffer, EOptionsColors color_id, GLShaderProgram& shader) { shader.set_uniform("uniform_color", Options_Colors[static_cast(color_id)]); shader.set_uniform("zoom", zoom); #if ENABLE_GCODE_VIEWER_SHADERS_EDITOR - shader.set_uniform("percent_outline_radius", 0.01f * static_cast(m_shaders_editor.percent_outline)); - shader.set_uniform("percent_center_radius", 0.01f * static_cast(m_shaders_editor.percent_center)); + shader.set_uniform("percent_outline_radius", 0.01f * static_cast(m_shaders_editor.points.percent_outline)); + shader.set_uniform("percent_center_radius", 0.01f * static_cast(m_shaders_editor.points.percent_center)); #else shader.set_uniform("percent_outline_radius", 0.15f); shader.set_uniform("percent_center_radius", 0.15f); @@ -852,16 +941,16 @@ void GCodeViewer::render_toolpaths() const ++m_statistics.gl_multi_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } - + glsafe(::glDisable(GL_POINT_SPRITE)); glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); }; - auto render_as_lines = [this](const IBuffer& buffer, GLShaderProgram& shader) { + auto render_as_lines = [this](const TBuffer& buffer, GLShaderProgram& shader) { for (const RenderPath& path : buffer.render_paths) { shader.set_uniform("uniform_color", path.color); - glsafe(::glMultiDrawElements(GL_LINE_STRIP, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); + glsafe(::glMultiDrawElements(GL_LINES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_line_strip_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -878,27 +967,25 @@ void GCodeViewer::render_toolpaths() const unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); unsigned char end_id = buffer_id(GCodeProcessor::EMoveType::Count); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vertices.vbo_id)); - glsafe(::glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, VBuffer::vertex_size_bytes(), (const void*)0)); - glsafe(::glEnableVertexAttribArray(0)); - for (unsigned char i = begin_id; i < end_id; ++i) { - const IBuffer& buffer = m_buffers[i]; - if (buffer.ibo_id == 0) - continue; - + const TBuffer& buffer = m_buffers[i]; if (!buffer.visible) continue; + if (buffer.vertices.id == 0 || buffer.indices.id == 0) + continue; + GLShaderProgram* shader = wxGetApp().get_shader(buffer.shader.c_str()); if (shader != nullptr) { shader->start_using(); - GCodeProcessor::EMoveType type = buffer_type(i); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vertices.id)); + glsafe(::glVertexAttribPointer(0, buffer.vertices.vertex_size_floats(), GL_FLOAT, GL_FALSE, buffer.vertices.vertex_size_bytes(), (const void*)0)); + glsafe(::glEnableVertexAttribArray(0)); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.ibo_id)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.indices.id)); - switch (type) + switch (buffer_type(i)) { case GCodeProcessor::EMoveType::Tool_change: { render_as_points(buffer, EOptionsColors::ToolChanges, *shader); break; } case GCodeProcessor::EMoveType::Color_change: { render_as_points(buffer, EOptionsColors::ColorChanges, *shader); break; } @@ -907,16 +994,33 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Retract: { render_as_points(buffer, EOptionsColors::Retractions, *shader); break; } case GCodeProcessor::EMoveType::Unretract: { render_as_points(buffer, EOptionsColors::Unretractions, *shader); break; } case GCodeProcessor::EMoveType::Extrude: - case GCodeProcessor::EMoveType::Travel: { render_as_lines(buffer, *shader); break; } + case GCodeProcessor::EMoveType::Travel: + { + std::array light_intensity; +#if ENABLE_GCODE_VIEWER_SHADERS_EDITOR + light_intensity[0] = m_shaders_editor.lines.lights.ambient; + light_intensity[1] = m_shaders_editor.lines.lights.top_diffuse; + light_intensity[2] = m_shaders_editor.lines.lights.front_diffuse; + light_intensity[3] = m_shaders_editor.lines.lights.global; +#else + light_intensity[0] = 0.25f; + light_intensity[1] = 0.7f; + light_intensity[2] = 0.75f; + light_intensity[3] = 0.75f; +#endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR + shader->set_uniform("light_intensity", light_intensity); + render_as_lines(buffer, *shader); break; + } } glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + glsafe(::glDisableVertexAttribArray(0)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + shader->stop_using(); } } - - glsafe(::glDisableVertexAttribArray(0)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); } void GCodeViewer::render_shells() const @@ -985,13 +1089,13 @@ void GCodeViewer::render_legend() const { #if ENABLE_GCODE_VIEWER_SHADERS_EDITOR ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); - if (m_shaders_editor.shader_version == 1) { + if (m_shaders_editor.points.shader_version == 1) { draw_list->AddCircleFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); - float radius = 0.5f * icon_size * (1.0f - 0.01f * static_cast(m_shaders_editor.percent_outline)); + float radius = 0.5f * icon_size * (1.0f - 0.01f * static_cast(m_shaders_editor.points.percent_outline)); draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); - if (m_shaders_editor.percent_center > 0) { - radius = 0.5f * icon_size * 0.01f * static_cast(m_shaders_editor.percent_center); + if (m_shaders_editor.points.percent_center > 0) { + radius = 0.5f * icon_size * 0.01f * static_cast(m_shaders_editor.points.percent_center); draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); } } @@ -1284,10 +1388,10 @@ void GCodeViewer::render_legend() const }; auto add_option = [this, add_item](GCodeProcessor::EMoveType move_type, EOptionsColors color, const std::string& text) { - const IBuffer& buffer = m_buffers[buffer_id(move_type)]; - if (buffer.visible && buffer.indices_count > 0) + const TBuffer& buffer = m_buffers[buffer_id(move_type)]; + if (buffer.visible && buffer.indices.count > 0) #if ENABLE_GCODE_VIEWER_SHADERS_EDITOR - add_item((m_shaders_editor.shader_version == 0) ? EItemType::Rect : EItemType::Circle, Options_Colors[static_cast(color)], text); + add_item((m_shaders_editor.points.shader_version == 0) ? EItemType::Rect : EItemType::Circle, Options_Colors[static_cast(color)], text); #else add_item((buffer.shader == "options_110") ? EItemType::Rect : EItemType::Circle, Options_Colors[static_cast(color)], text); #endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR @@ -1320,14 +1424,22 @@ void GCodeViewer::render_legend() const void GCodeViewer::render_statistics() const { static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); - static const float offset = 250.0f; + static const float offset = 230.0f; ImGuiWrapper& imgui = *wxGetApp().imgui(); imgui.set_next_window_pos(0.5f * wxGetApp().plater()->get_current_canvas3D()->get_canvas_size().get_width(), 0.0f, ImGuiCond_Once, 0.5f, 0.0f); - imgui.begin(std::string("Statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + imgui.begin(std::string("GCodeViewer Statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize); ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(std::string("GCodeProcessor time:")); + ImGui::PopStyleColor(); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.results_time) + " ms"); + + ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); imgui.text(std::string("Load time:")); ImGui::PopStyleColor(); @@ -1370,12 +1482,6 @@ void GCodeViewer::render_statistics() const ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(std::string("Vertices CPU:")); - ImGui::PopStyleColor(); - ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.vertices_size) + " bytes"); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); imgui.text(std::string("Indices CPU:")); ImGui::PopStyleColor(); @@ -1408,6 +1514,20 @@ void GCodeViewer::render_statistics() const ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.indices_gpu_size) + " bytes"); + ImGui::Separator(); + + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(std::string("Travel segments:")); + ImGui::PopStyleColor(); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.travel_segments_count)); + + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(std::string("Extrude segments:")); + ImGui::PopStyleColor(); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.extrude_segments_count)); + imgui.end(); } #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -1429,34 +1549,58 @@ void GCodeViewer::render_shaders_editor() const Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size(); imgui.set_next_window_pos(static_cast(cnv_size.get_width()), 0.5f * static_cast(cnv_size.get_height()), ImGuiCond_Once, 1.0f, 0.5f); + imgui.begin(std::string("Shaders editor (DEV only)"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize); - ImGui::RadioButton("glsl version 1.10 (low end PCs)", &m_shaders_editor.shader_version, 0); - ImGui::RadioButton("glsl version 1.20 flat (billboards)", &m_shaders_editor.shader_version, 1); - ImGui::RadioButton("glsl version 1.20 solid (spheres default)", &m_shaders_editor.shader_version, 2); + if (ImGui::CollapsingHeader("Points", ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::TreeNode("GLSL version")) { + ImGui::RadioButton("1.10 (low end PCs)", &m_shaders_editor.points.shader_version, 0); + ImGui::RadioButton("1.20 flat (billboards)", &m_shaders_editor.points.shader_version, 1); + ImGui::RadioButton("1.20 solid (spheres default)", &m_shaders_editor.points.shader_version, 2); + ImGui::TreePop(); + } - switch (m_shaders_editor.shader_version) - { - case 0: { set_shader("options_110"); break; } - case 1: { set_shader("options_120_flat"); break; } - case 2: { set_shader("options_120_solid"); break; } + switch (m_shaders_editor.points.shader_version) + { + case 0: { set_shader("options_110"); break; } + case 1: { set_shader("options_120_flat"); break; } + case 2: { set_shader("options_120_solid"); break; } + } + + if (ImGui::TreeNode("Options")) { + ImGui::SliderFloat("point size", &m_shaders_editor.points.point_size, 0.5f, 3.0f, "%.2f"); + if (m_shaders_editor.points.shader_version == 1) { + ImGui::SliderInt("% outline", &m_shaders_editor.points.percent_outline, 0, 50); + ImGui::SliderInt("% center", &m_shaders_editor.points.percent_center, 0, 50); + } + ImGui::TreePop(); + } } - if (ImGui::CollapsingHeader("Options", ImGuiTreeNodeFlags_DefaultOpen)) { - ImGui::SliderFloat("point size", &m_shaders_editor.point_size, 0.5f, 3.0f, "%.1f"); - if (m_shaders_editor.shader_version == 1) { - ImGui::SliderInt("percent outline", &m_shaders_editor.percent_outline, 0, 50); - ImGui::SliderInt("percent center", &m_shaders_editor.percent_center, 0, 50); + if (ImGui::CollapsingHeader("Lines", ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::TreeNode("Lights")) { + ImGui::SliderFloat("ambient", &m_shaders_editor.lines.lights.ambient, 0.0f, 1.0f, "%.2f"); + ImGui::SliderFloat("top diffuse", &m_shaders_editor.lines.lights.top_diffuse, 0.0f, 1.0f, "%.2f"); + ImGui::SliderFloat("front diffuse", &m_shaders_editor.lines.lights.front_diffuse, 0.0f, 1.0f, "%.2f"); + ImGui::SliderFloat("global", &m_shaders_editor.lines.lights.global, 0.0f, 1.0f, "%.2f"); + ImGui::TreePop(); } } + ImGui::SetWindowSize(ImVec2(std::max(ImGui::GetWindowWidth(), 600.0f), -1.0f), ImGuiCond_Always); + if (ImGui::GetWindowPos().x + ImGui::GetWindowWidth() > static_cast(cnv_size.get_width())) { + ImGui::SetWindowPos(ImVec2(static_cast(cnv_size.get_width()) - ImGui::GetWindowWidth(), ImGui::GetWindowPos().y), ImGuiCond_Always); + wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); + wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); + } + imgui.end(); } #endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR bool GCodeViewer::is_travel_in_z_range(size_t id) const { - const IBuffer& buffer = m_buffers[buffer_id(GCodeProcessor::EMoveType::Travel)]; + const TBuffer& buffer = m_buffers[buffer_id(GCodeProcessor::EMoveType::Travel)]; if (id >= buffer.paths.size()) return false; diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 5c60863058..5cddeaa75a 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -33,18 +33,47 @@ class GCodeViewer CustomGCodes }; - // buffer containing vertices data + // vbo buffer containing vertices data for a specific toolpath type struct VBuffer { - unsigned int vbo_id{ 0 }; - size_t vertices_count{ 0 }; + enum class EFormat : unsigned char + { + Position, + PositionNormal + }; - size_t data_size_bytes() { return vertices_count * vertex_size_bytes(); } + EFormat format{ EFormat::Position }; + // vbo id + unsigned int id{ 0 }; + // count of vertices, updated after data are sent to gpu + size_t count{ 0 }; + + size_t data_size_bytes() const { return count * vertex_size_bytes(); } + size_t vertex_size_floats() const + { + switch (format) + { + // vertex format: 3 floats -> position.x|position.y|position.z + case EFormat::Position: { return 3; } + // vertex format: 4 floats -> position.x|position.y|position.z|normal.x + case EFormat::PositionNormal: { return 4; } + default: { return 0; } + } + } + size_t vertex_size_bytes() const { return vertex_size_floats() * sizeof(float); } void reset(); + }; - static size_t vertex_size_floats() { return 3; } - static size_t vertex_size_bytes() { return vertex_size_floats() * sizeof(float); } + // ibo buffer containing indices data for a specific toolpath type + struct IBuffer + { + // ibo id + unsigned int id{ 0 }; + // count of indices, updated after data are sent to gpu + size_t count{ 0 }; + + void reset(); }; // Used to identify different toolpath sub-types inside a IBuffer @@ -52,9 +81,9 @@ class GCodeViewer { struct Endpoint { - // index into the buffer indices ibo + // index into the indices buffer unsigned int i_id{ 0u }; - // sequential id (same as index into the vertices vbo) + // sequential id unsigned int s_id{ 0u }; Vec3f position{ Vec3f::Zero() }; }; @@ -83,11 +112,12 @@ class GCodeViewer std::vector offsets; // use size_t because we need the pointer's size (used in the call glMultiDrawElements()) }; - // buffer containing indices data and shader for a specific toolpath type - struct IBuffer + // buffer containing data for rendering a specific toolpath type + struct TBuffer { - unsigned int ibo_id{ 0 }; - size_t indices_count{ 0 }; + VBuffer vertices; + IBuffer indices; + std::string shader; std::vector paths; std::vector render_paths; @@ -161,6 +191,7 @@ class GCodeViewer struct Statistics { // times + long long results_time{ 0 }; long long load_time{ 0 }; long long refresh_time{ 0 }; long long refresh_paths_time{ 0 }; @@ -169,20 +200,24 @@ class GCodeViewer long long gl_multi_line_strip_calls_count{ 0 }; // memory long long results_size{ 0 }; - long long vertices_size{ 0 }; long long vertices_gpu_size{ 0 }; long long indices_size{ 0 }; long long indices_gpu_size{ 0 }; long long paths_size{ 0 }; long long render_paths_size{ 0 }; + // others + long long travel_segments_count{ 0 }; + long long extrude_segments_count{ 0 }; void reset_all() { reset_times(); reset_opengl(); reset_sizes(); + reset_counters(); } void reset_times() { + results_time = 0; load_time = 0; refresh_time = 0; refresh_paths_time = 0; @@ -195,23 +230,46 @@ class GCodeViewer void reset_sizes() { results_size = 0; - vertices_size = 0; vertices_gpu_size = 0; indices_size = 0; indices_gpu_size = 0; - paths_size = 0; + paths_size = 0; render_paths_size = 0; } + + void reset_counters() { + travel_segments_count = 0; + extrude_segments_count = 0; + } }; #endif // ENABLE_GCODE_VIEWER_STATISTICS #if ENABLE_GCODE_VIEWER_SHADERS_EDITOR struct ShadersEditor { - int shader_version{ 2 }; - float point_size{ 1.0f }; - int percent_outline{ 0 }; - int percent_center{ 33 }; + struct Points + { + int shader_version{ 2 }; + float point_size{ 0.8f }; + int percent_outline{ 0 }; + int percent_center{ 33 }; + }; + + struct Lines + { + struct Lights + { + float ambient{ 0.25f }; + float top_diffuse{ 0.7f }; + float front_diffuse{ 0.75f }; + float global{ 0.75f }; + }; + + Lights lights; + }; + + Points points; + Lines lines; }; #endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR @@ -268,8 +326,8 @@ public: private: unsigned int m_last_result_id{ 0 }; - VBuffer m_vertices; - mutable std::vector m_buffers{ static_cast(GCodeProcessor::EMoveType::Extrude) }; + size_t m_vertices_count{ 0 }; + mutable std::vector m_buffers{ static_cast(GCodeProcessor::EMoveType::Extrude) }; // bounding box of toolpaths BoundingBoxf3 m_paths_bounding_box; // bounding box of toolpaths + marker tools diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index 4bebf7b984..cb47d79618 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -35,15 +35,12 @@ std::pair GLShadersManager::init() valid &= append_shader("printbed", { "printbed.vs", "printbed.fs" }); // used to render options in gcode preview valid &= append_shader("options_110", { "options_110.vs", "options_110.fs" }); - if (GUI::wxGetApp().is_glsl_version_greater_or_equal_to(1, 20)) - { + if (GUI::wxGetApp().is_glsl_version_greater_or_equal_to(1, 20)) { valid &= append_shader("options_120_flat", { "options_120_flat.vs", "options_120_flat.fs" }); valid &= append_shader("options_120_solid", { "options_120_solid.vs", "options_120_solid.fs" }); } - // used to render extrusion paths in gcode preview - valid &= append_shader("extrusions", { "extrusions.vs", "extrusions.fs" }); - // used to render travel paths in gcode preview - valid &= append_shader("travels", { "travels.vs", "travels.fs" }); + // used to render extrusion and travel paths in gcode preview + valid &= append_shader("toolpaths", { "toolpaths.vs", "toolpaths.fs" }); // used to render objects in 3d editor valid &= append_shader("gouraud", { "gouraud.vs", "gouraud.fs" }); // used to render variable layers heights in 3d editor From feb4857cf8d83829b844d30b86c6eeb9170454be Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 30 Jun 2020 12:53:42 +0200 Subject: [PATCH 150/503] Fixed height of features type combo popup when building against wxWidgets 3.1.3 --- src/slic3r/GUI/wxExtensions.cpp | 12 ++++-------- src/slic3r/GUI/wxExtensions.hpp | 1 - 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 74d790df86..c42933b9b3 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -174,7 +174,6 @@ wxMenuItem* append_menu_check_item(wxMenu* menu, int id, const wxString& string, const unsigned int wxCheckListBoxComboPopup::DefaultWidth = 200; const unsigned int wxCheckListBoxComboPopup::DefaultHeight = 200; -const unsigned int wxCheckListBoxComboPopup::DefaultItemHeight = 18; bool wxCheckListBoxComboPopup::Create(wxWindow* parent) { @@ -202,20 +201,17 @@ wxSize wxCheckListBoxComboPopup::GetAdjustedSize(int minWidth, int prefHeight, i // and set height dinamically in dependence of items count wxComboCtrl* cmb = GetComboCtrl(); - if (cmb != nullptr) - { + if (cmb != nullptr) { wxSize size = GetComboCtrl()->GetSize(); unsigned int count = GetCount(); - if (count > 0) - { + if (count > 0) { int max_width = size.x; - for (unsigned int i = 0; i < count; ++i) - { + for (unsigned int i = 0; i < count; ++i) { max_width = std::max(max_width, 60 + GetTextExtent(GetString(i)).x); } size.SetWidth(max_width); - size.SetHeight(4 + count * (2 + GetTextExtent(GetString(0)).y)); + size.SetHeight(count * cmb->GetCharHeight()); } else size.SetHeight(DefaultHeight); diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 569257e1b4..254dbfad3a 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -63,7 +63,6 @@ class wxCheckListBoxComboPopup : public wxCheckListBox, public wxComboPopup { static const unsigned int DefaultWidth; static const unsigned int DefaultHeight; - static const unsigned int DefaultItemHeight; wxString m_text; From 0b88e86634bd291f524c24912061c9c1e9e71ef3 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 30 Jun 2020 14:12:47 +0200 Subject: [PATCH 151/503] PhysicalPrinter improvements: * implemented PresetForPrinter class --- src/libslic3r/Preset.cpp | 9 ++ src/libslic3r/Preset.hpp | 1 + src/slic3r/GUI/PresetComboBoxes.cpp | 154 ++++++++++++++++++++++------ src/slic3r/GUI/PresetComboBoxes.hpp | 51 +++++++-- 4 files changed, 175 insertions(+), 40 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 9af3dacf0a..ec3e933384 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1375,6 +1375,15 @@ const std::string& PhysicalPrinter::get_printer_model() const return config.opt_string("printer_model"); } +bool PhysicalPrinter::has_empty_config() const +{ + return config.opt_string("print_host" ).empty() && + config.opt_string("printhost_apikey").empty() && + config.opt_string("printhost_cafile").empty() && + config.opt_string("login" ).empty() && + config.opt_string("password" ).empty(); +} + void PhysicalPrinter::update_from_preset(const Preset& preset) { config.apply_only(preset.config, printer_options(), false); diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index 6eb1fd2db3..a076a9a217 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -556,6 +556,7 @@ public: static const std::vector& printer_options(); const std::string& get_preset_name() const; const std::string& get_printer_model() const; + bool has_empty_config() const; void save() { this->config.save(this->file); } void save_to(const std::string& file_name) const { this->config.save(file_name); } diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 88dd4b739a..dc5365a139 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -884,6 +884,76 @@ void TabPresetComboBox::update_dirty() } +//------------------------------------------ +// PresetForPrinter +//------------------------------------------ + +PresetForPrinter::PresetForPrinter(PhysicalPrinterDialog* parent, bool is_all_enable) : + m_parent(parent) +{ + m_sizer = new wxBoxSizer(wxVERTICAL); + + m_delete_preset_btn = new ScalableButton(parent, wxID_ANY, "cross", "", wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT); + m_delete_preset_btn->SetFont(wxGetApp().normal_font()); + m_delete_preset_btn->SetToolTip(_L("Delete this preset from this printer device")); + m_delete_preset_btn->Bind(wxEVT_BUTTON, &PresetForPrinter::DeletePreset, this); + + m_presets_list = new TabPresetComboBox(parent, Preset::TYPE_PRINTER); + + if (is_all_enable) + m_presets_list->set_enable_all(); + + m_presets_list->set_selection_changed_function([this](int selection) { + std::string selected_string = Preset::remove_suffix_modified(m_presets_list->GetString(selection).ToUTF8().data()); + Preset* preset = wxGetApp().preset_bundle->printers.find_preset(selected_string); + assert(preset); + Preset& edited_preset = wxGetApp().preset_bundle->printers.get_edited_preset(); + if (preset->name == edited_preset.name) + preset = &edited_preset; + + // if created physical printer doesn't have any settings, use the settings from the selected preset + if (m_parent->get_printer()->has_empty_config()) { + // update Print Host upload from the selected preset + m_parent->get_printer()->update_from_preset(*preset); + // update values in parent (PhysicalPrinterDialog) + m_parent->update(); + } + + update_full_printer_name(); + }); + + m_full_printer_name = new wxStaticText(parent, wxID_ANY, ""); + + m_presets_list->update(); +} + +PresetForPrinter::~PresetForPrinter() +{ + m_presets_list->Destroy(); + m_delete_preset_btn->Destroy(); + m_full_printer_name->Destroy(); +} + +void PresetForPrinter::DeletePreset(wxEvent& event) +{ + +} + +void PresetForPrinter::update_full_printer_name() +{ + wxString printer_name = m_parent->get_printer_name(); + wxString preset_name = m_presets_list->GetString(m_presets_list->GetSelection()); + + m_full_printer_name->SetLabelText(printer_name + " * " + preset_name); +} + +void PresetForPrinter::msw_rescale() +{ + m_presets_list->msw_rescale(); + m_delete_preset_btn->msw_rescale(); +} + + //------------------------------------------ // PhysicalPrinterDialog //------------------------------------------ @@ -896,44 +966,31 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); int border = 10; + m_info_string = _("This printer name will be shown in the presets list") + ":\n"; - m_printer_presets = new TabPresetComboBox(this, Preset::TYPE_PRINTER); + TabPresetComboBox* printer_presets = new TabPresetComboBox(this, Preset::TYPE_PRINTER); if (printer_name.IsEmpty()) { - // if printer_name is empty it means that new printer is created, so enable all items in the preset list - m_printer_presets->set_enable_all(); printer_name = _L("My Printer Device"); + // if printer_name is empty it means that new printer is created, so enable all items in the preset list + m_presets.emplace_back(new PresetForPrinter(this, true)); } else { std::string full_name = into_u8(printer_name); printer_name = from_u8(PhysicalPrinter::get_short_name(full_name)); } - m_printer_presets->set_selection_changed_function([this](int selection) { - std::string selected_string = Preset::remove_suffix_modified(m_printer_presets->GetString(selection).ToUTF8().data()); - Preset* preset = wxGetApp().preset_bundle->printers.find_preset(selected_string); - assert(preset); - Preset& edited_preset = wxGetApp().preset_bundle->printers.get_edited_preset(); - if (preset->name == edited_preset.name) - preset = &edited_preset; - m_printer.update_from_preset(*preset); - - update_printer_name(); - - // update values - m_optgroup->reload_config(); - update(); - }); - m_printer_presets->update(); - wxStaticText* label_top = new wxStaticText(this, wxID_ANY, _("Descriptive name for the printer device") + ":"); + + m_add_preset_btn = new ScalableButton(this, wxID_ANY, "add_copies", "", wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT); + m_add_preset_btn->SetFont(wxGetApp().normal_font()); + m_add_preset_btn->SetToolTip(_L("Add preset for this printer device")); + m_add_preset_btn->Bind(wxEVT_BUTTON, &PhysicalPrinterDialog::AddPreset, this); + m_printer_name = new wxTextCtrl(this, wxID_ANY, printer_name, wxDefaultPosition, wxDefaultSize); - m_printer_name->Bind(wxEVT_TEXT, [this](wxEvent&) { this->update_printer_name(); }); + m_printer_name->Bind(wxEVT_TEXT, [this](wxEvent&) { this->update_full_printer_names(); }); - wxStaticText* label_bottom = new wxStaticText(this, wxID_ANY, _("This printer name will be shown in the presets list") + ":"); - m_full_printer_name = new wxStaticText(this, wxID_ANY, ""); - - update_printer_name(); + update_full_printer_names(); PhysicalPrinterCollection& printers = wxGetApp().preset_bundle->physical_printers; PhysicalPrinter* printer = printers.find_printer(into_u8(printer_name)); @@ -948,19 +1005,27 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) m_optgroup = new ConfigOptionsGroup(this, _L("Print Host upload"), m_config); build_printhost_settings(m_optgroup); - m_optgroup->reload_config(); + //m_optgroup->reload_config(); wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); wxButton* btnOK = static_cast(this->FindWindowById(wxID_OK, this)); btnOK->Bind(wxEVT_BUTTON, &PhysicalPrinterDialog::OnOK, this); + wxBoxSizer* nameSizer = new wxBoxSizer(wxHORIZONTAL); + nameSizer->Add(m_printer_name, 1, wxEXPAND); + nameSizer->Add(m_add_preset_btn, 0, wxEXPAND | wxLEFT, border); + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); topSizer->Add(label_top , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); - topSizer->Add(m_printer_name , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(nameSizer , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + for (PresetForPrinter* preset : m_presets) + topSizer->Add(preset->sizer(), 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);; + /* topSizer->Add(m_printer_presets , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); topSizer->Add(label_bottom , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); topSizer->Add(m_full_printer_name , 0, wxEXPAND | wxLEFT | wxRIGHT, border); + */ topSizer->Add(m_optgroup->sizer , 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); topSizer->Add(btns , 0, wxEXPAND | wxALL, border); @@ -968,6 +1033,14 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) topSizer->SetSizeHints(this); } +PhysicalPrinterDialog::~PhysicalPrinterDialog() +{ + for (PresetForPrinter* preset : m_presets) { + delete preset; + preset = nullptr; + } +} + void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgroup) { m_optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { @@ -1101,6 +1174,8 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr void PhysicalPrinterDialog::update() { + m_optgroup->reload_config(); + const PrinterTechnology tech = Preset::printer_technology(m_printer.config); // Only offer the host type selection for FFF, for SLA it's always the SL1 printer (at the moment) if (tech == ptFFF) { @@ -1125,12 +1200,17 @@ void PhysicalPrinterDialog::update() this->Layout(); } -void PhysicalPrinterDialog::update_printer_name() -{ - wxString printer_name = m_printer_name->GetValue(); - wxString preset_name = m_printer_presets->GetString(m_printer_presets->GetSelection()); - m_full_printer_name->SetLabelText("\t" + printer_name + " * " + preset_name); +wxString PhysicalPrinterDialog::get_printer_name() +{ + return m_info_string + m_printer_name->GetValue() + "\t"; +} + +void PhysicalPrinterDialog::update_full_printer_names() +{ + for (PresetForPrinter* preset : m_presets) + preset->update_full_printer_name(); + this->Layout(); } @@ -1147,6 +1227,9 @@ void PhysicalPrinterDialog::on_dpi_changed(const wxRect& suggested_rect) msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL }); + for (PresetForPrinter* preset : m_presets) + preset->msw_rescale(); + const wxSize& size = wxSize(45 * em, 35 * em); SetMinSize(size); @@ -1184,6 +1267,7 @@ void PhysicalPrinterDialog::OnOK(wxEvent& event) printers.save_printer(m_printer); // update selection on the tab only when it was changed + /* if (m_printer.get_preset_name() != wxGetApp().preset_bundle->printers.get_selected_preset_name()) { Tab* tab = wxGetApp().get_tab(Preset::TYPE_PRINTER); if (tab) { @@ -1191,9 +1275,15 @@ void PhysicalPrinterDialog::OnOK(wxEvent& event) tab->select_preset(into_u8(preset_name)); } } + */ event.Skip(); } +void PhysicalPrinterDialog::AddPreset(wxEvent& event) +{ + +} + }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index dce22bc82b..3d5ae298f0 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -166,32 +166,67 @@ public: }; +//------------------------------------------ +// PresetForPrinter +//------------------------------------------ +class PhysicalPrinterDialog; +class PresetForPrinter +{ + PhysicalPrinterDialog* m_parent { nullptr }; + + TabPresetComboBox* m_presets_list { nullptr }; + ScalableButton* m_delete_preset_btn { nullptr }; + wxStaticText* m_full_printer_name { nullptr }; + + wxBoxSizer* m_sizer { nullptr }; + + void DeletePreset(wxEvent& event); + +public: + PresetForPrinter(PhysicalPrinterDialog* parent, bool is_all_enable); + ~PresetForPrinter(); + + wxBoxSizer* sizer() { return m_sizer; } + void update_full_printer_name(); + + void msw_rescale(); + void on_sys_color_changed() {}; +}; + + //------------------------------------------ // PhysicalPrinterDialog //------------------------------------------ + class ConfigOptionsGroup; class PhysicalPrinterDialog : public DPIDialog { PhysicalPrinter m_printer; DynamicPrintConfig* m_config { nullptr }; + wxString m_info_string; wxTextCtrl* m_printer_name { nullptr }; - wxStaticText* m_full_printer_name { nullptr }; - TabPresetComboBox* m_printer_presets { nullptr }; + std::vector m_presets; + ConfigOptionsGroup* m_optgroup { nullptr }; - ScalableButton* m_printhost_browse_btn; - ScalableButton* m_printhost_test_btn; - ScalableButton* m_printhost_cafile_browse_btn {nullptr}; + ScalableButton* m_add_preset_btn {nullptr}; + ScalableButton* m_printhost_browse_btn {nullptr}; + ScalableButton* m_printhost_test_btn {nullptr}; + ScalableButton* m_printhost_cafile_browse_btn {nullptr}; void build_printhost_settings(ConfigOptionsGroup* optgroup); - void update(); - void update_printer_name(); void OnOK(wxEvent& event); + void AddPreset(wxEvent& event); public: PhysicalPrinterDialog(wxString printer_name); - ~PhysicalPrinterDialog() {} + ~PhysicalPrinterDialog(); + + void update(); + wxString get_printer_name(); + void update_full_printer_names(); + PhysicalPrinter* get_printer() {return &m_printer; } protected: void on_dpi_changed(const wxRect& suggested_rect) override; From 0b1086f390bbc534f777c9e28480a6b463337be8 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 3 Jul 2020 12:17:12 +0200 Subject: [PATCH 152/503] GCodeViewer -> Export of extrude toolpaths to obj files --- src/slic3r/GUI/3DScene.cpp | 2 + src/slic3r/GUI/3DScene.hpp | 2 + src/slic3r/GUI/GCodeViewer.cpp | 287 ++++++++++++++++++++++++++++++++- src/slic3r/GUI/GCodeViewer.hpp | 5 +- src/slic3r/GUI/GLCanvas3D.cpp | 8 + src/slic3r/GUI/MainFrame.cpp | 6 +- 6 files changed, 300 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index f53e4a55c2..c9c7b72f37 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1017,6 +1017,7 @@ bool GLVolumeCollection::has_toolpaths_to_export() const return false; } +#if !ENABLE_GCODE_VIEWER void GLVolumeCollection::export_toolpaths_to_obj(const char* filename) const { if (filename == nullptr) @@ -1298,6 +1299,7 @@ void GLVolumeCollection::export_toolpaths_to_obj(const char* filename) const fclose(fp); } +#endif // !ENABLE_GCODE_VIEWER // caller is responsible for supplying NO lines with zero length static void thick_lines_to_indexed_vertex_array( diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 1b4d0fc4f2..7e8ae6fe33 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -597,8 +597,10 @@ public: std::string log_memory_info() const; bool has_toolpaths_to_export() const; +#if !ENABLE_GCODE_VIEWER // Export the geometry of the GLVolumes toolpaths of this collection into the file with the given path, in obj format void export_toolpaths_to_obj(const char* filename) const; +#endif // !ENABLE_GCODE_VIEWER private: GLVolumeCollection(const GLVolumeCollection &other); diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 6d6ba557e2..688134f7c9 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -487,6 +488,284 @@ void GCodeViewer::set_layers_z_range(const std::array& layers_z_range wxGetApp().plater()->update_preview_moves_slider(); } +void GCodeViewer::export_toolpaths_to_obj(const char* filename) const +{ + if (filename == nullptr) + return; + + if (!has_data()) + return; + + wxBusyCursor busy; + + // the data needed is contained into the Extrude TBuffer + const TBuffer& buffer = m_buffers[buffer_id(GCodeProcessor::EMoveType::Extrude)]; + if (buffer.vertices.id == 0 || buffer.indices.id == 0) + return; + + // collect color information to generate materials + std::vector colors; + for (const RenderPath& path : buffer.render_paths) { + colors.push_back(path.color); + } + + // save materials file + boost::filesystem::path mat_filename(filename); + mat_filename.replace_extension("mtl"); + FILE* fp = boost::nowide::fopen(mat_filename.string().c_str(), "w"); + if (fp == nullptr) { + BOOST_LOG_TRIVIAL(error) << "GCodeViewer::export_toolpaths_to_obj: Couldn't open " << mat_filename.string().c_str() << " for writing"; + return; + } + + fprintf(fp, "# G-Code Toolpaths Materials\n"); + fprintf(fp, "# Generated by %s based on Slic3r\n", SLIC3R_BUILD_ID); + + unsigned int colors_count = 1; + for (const Color& color : colors) + { + fprintf(fp, "\nnewmtl material_%d\n", colors_count++); + fprintf(fp, "Ka 1 1 1\n"); + fprintf(fp, "Kd %f %f %f\n", color[0], color[1], color[2]); + fprintf(fp, "Ks 0 0 0\n"); + } + + fclose(fp); + + // save geometry file + fp = boost::nowide::fopen(filename, "w"); + if (fp == nullptr) { + BOOST_LOG_TRIVIAL(error) << "GCodeViewer::export_toolpaths_to_obj: Couldn't open " << filename << " for writing"; + return; + } + + fprintf(fp, "# G-Code Toolpaths\n"); + fprintf(fp, "# Generated by %s based on Slic3r\n", SLIC3R_BUILD_ID); + fprintf(fp, "\nmtllib ./%s\n", mat_filename.filename().string().c_str()); + + // get vertices data from vertex buffer on gpu + size_t floats_per_vertex = buffer.vertices.vertex_size_floats(); + std::vector vertices = std::vector(buffer.vertices.count * floats_per_vertex); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vertices.id)); + glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, 0, buffer.vertices.data_size_bytes(), vertices.data())); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + + auto get_vertex = [&vertices, floats_per_vertex](size_t id) { + // extract vertex from vector of floats + size_t base_id = id * floats_per_vertex; + return Vec3f(vertices[base_id + 0], vertices[base_id + 1], vertices[base_id + 2]); + }; + + struct Segment + { + Vec3f v1; + Vec3f v2; + Vec3f dir; + Vec3f right; + Vec3f up; + Vec3f rl_displacement; + Vec3f tb_displacement; + float length; + }; + + auto generate_segment = [get_vertex](size_t start_id, float half_width, float half_height) { + auto local_basis = [](const Vec3f& dir) { + // calculate local basis (dir, right, up) on given segment + std::array ret; + ret[0] = dir.normalized(); + if (std::abs(ret[0][2]) < EPSILON) { + // segment parallel to XY plane + ret[1] = { ret[0][1], -ret[0][0], 0.0f }; + ret[2] = Vec3f::UnitZ(); + } + else if (std::abs(std::abs(ret[0].dot(Vec3f::UnitZ())) - 1.0f) < EPSILON) { + // segment parallel to Z axis + ret[1] = Vec3f::UnitX(); + ret[2] = Vec3f::UnitY(); + } + else { + ret[0] = dir.normalized(); + ret[1] = ret[0].cross(Vec3f::UnitZ()).normalized(); + ret[2] = ret[1].cross(ret[0]); + } + return ret; + }; + + Vec3f v1 = get_vertex(start_id); + Vec3f v2 = get_vertex(start_id + 1); + float length = (v2 - v1).norm(); + const auto&& [dir, right, up] = local_basis(v2 - v1); + return Segment({ v1, v2, dir, right, up, half_width * right, half_height * up, length }); + }; + + size_t out_vertices_count = 0; + + for (size_t i = 0; i < buffer.render_paths.size(); ++i) { + // get paths segments from buffer paths + const RenderPath& render_path = buffer.render_paths[i]; + const Path& path = buffer.paths[render_path.path_id]; + float half_width = 0.5f * path.width; + float half_height = 0.5f * path.height; + + // generates vertices/normals/triangles + std::vector out_vertices; + std::vector out_normals; + using Triangle = std::array; + std::vector out_triangles; + for (size_t j = 0; j < render_path.offsets.size(); ++j) { + unsigned int start = static_cast(render_path.offsets[j] / sizeof(unsigned int)); + unsigned int end = start + render_path.sizes[j]; + + for (size_t k = start; k < end; k += 2) { + Segment curr = generate_segment(k, half_width, half_height); + + if (k == start) { + // starting endpoint vertices/normals + out_vertices.push_back(curr.v1 + curr.rl_displacement); out_normals.push_back(curr.right); // right + out_vertices.push_back(curr.v1 + curr.tb_displacement); out_normals.push_back(curr.up); // top + out_vertices.push_back(curr.v1 - curr.rl_displacement); out_normals.push_back(-curr.right); // left + out_vertices.push_back(curr.v1 - curr.tb_displacement); out_normals.push_back(-curr.up); // bottom + out_vertices_count += 4; + + // starting cap triangles + size_t base_id = out_vertices_count - 4 + 1; + out_triangles.push_back({ base_id + 0, base_id + 1, base_id + 2 }); + out_triangles.push_back({ base_id + 0, base_id + 2, base_id + 3 }); + } + else { + // for the endpoint shared by the current and the previous segments + // we keep the top and bottom vertices of the previous vertices + // and add new left/right vertices for the current segment + out_vertices.push_back(curr.v1 + curr.rl_displacement); out_normals.push_back(curr.right); // right + out_vertices.push_back(curr.v1 - curr.rl_displacement); out_normals.push_back(-curr.right); // left + out_vertices_count += 2; + + Segment prev = generate_segment(k - 2, half_width, half_height); + Vec3f med_dir = (prev.dir + curr.dir).normalized(); + float disp = half_width * ::tan(::acos(std::clamp(curr.dir.dot(med_dir), -1.0f, 1.0f))); + Vec3f disp_vec = disp * prev.dir; + + bool is_right_turn = prev.up.dot(prev.dir.cross(curr.dir)) <= 0.0f; + if (prev.dir.dot(curr.dir) < 0.7071068f) { + // if the angle between two consecutive segments is greater than 45 degrees + // we add a cap in the outside corner + // and displace the vertices in the inside corner to the same position, if possible + if (is_right_turn) { + // corner cap triangles (left) + size_t base_id = out_vertices_count - 6 + 1; + out_triangles.push_back({ base_id + 5, base_id + 2, base_id + 1 }); + out_triangles.push_back({ base_id + 5, base_id + 3, base_id + 2 }); + + // update right vertices + if (disp < prev.length) { + base_id = out_vertices.size() - 6; + out_vertices[base_id + 0] -= disp_vec; + out_vertices[base_id + 4] = out_vertices[base_id + 0]; + } + } + else { + // corner cap triangles (right) + size_t base_id = out_vertices_count - 6 + 1; + out_triangles.push_back({ base_id + 0, base_id + 4, base_id + 1 }); + out_triangles.push_back({ base_id + 0, base_id + 3, base_id + 4 }); + + // update left vertices + if (disp < prev.length) { + base_id = out_vertices.size() - 6; + out_vertices[base_id + 2] -= disp_vec; + out_vertices[base_id + 5] = out_vertices[base_id + 2]; + } + } + } + else { + // if the angle between two consecutive segments is lesser than 45 degrees + // displace the vertices to the same position + if (is_right_turn) { + size_t base_id = out_vertices.size() - 6; + // right + out_vertices[base_id + 0] -= disp_vec; + out_vertices[base_id + 4] = out_vertices[base_id + 0]; + // left + out_vertices[base_id + 2] += disp_vec; + out_vertices[base_id + 5] = out_vertices[base_id + 2]; + } + else { + size_t base_id = out_vertices.size() - 6; + // right + out_vertices[base_id + 0] += disp_vec; + out_vertices[base_id + 4] = out_vertices[base_id + 0]; + // left + out_vertices[base_id + 2] -= disp_vec; + out_vertices[base_id + 5] = out_vertices[base_id + 2]; + } + } + } + + // current second endpoint vertices/normals + out_vertices.push_back(curr.v2 + curr.rl_displacement); out_normals.push_back(curr.right); // right + out_vertices.push_back(curr.v2 + curr.tb_displacement); out_normals.push_back(curr.up); // top + out_vertices.push_back(curr.v2 - curr.rl_displacement); out_normals.push_back(-curr.right); // left + out_vertices.push_back(curr.v2 - curr.tb_displacement); out_normals.push_back(-curr.up); // bottom + out_vertices_count += 4; + + // sides triangles + if (k == start) { + size_t base_id = out_vertices_count - 8 + 1; + out_triangles.push_back({ base_id + 0, base_id + 4, base_id + 5 }); + out_triangles.push_back({ base_id + 0, base_id + 5, base_id + 1 }); + out_triangles.push_back({ base_id + 1, base_id + 5, base_id + 6 }); + out_triangles.push_back({ base_id + 1, base_id + 6, base_id + 2 }); + out_triangles.push_back({ base_id + 2, base_id + 6, base_id + 7 }); + out_triangles.push_back({ base_id + 2, base_id + 7, base_id + 3 }); + out_triangles.push_back({ base_id + 3, base_id + 7, base_id + 4 }); + out_triangles.push_back({ base_id + 3, base_id + 4, base_id + 0 }); + } + else { + size_t base_id = out_vertices_count - 10 + 1; + out_triangles.push_back({ base_id + 4, base_id + 6, base_id + 7 }); + out_triangles.push_back({ base_id + 4, base_id + 7, base_id + 1 }); + out_triangles.push_back({ base_id + 1, base_id + 7, base_id + 8 }); + out_triangles.push_back({ base_id + 1, base_id + 8, base_id + 5 }); + out_triangles.push_back({ base_id + 5, base_id + 8, base_id + 9 }); + out_triangles.push_back({ base_id + 5, base_id + 9, base_id + 3 }); + out_triangles.push_back({ base_id + 3, base_id + 9, base_id + 6 }); + out_triangles.push_back({ base_id + 3, base_id + 6, base_id + 4 }); + } + + if (k + 2 == end) { + // ending cap triangles + size_t base_id = out_vertices_count - 4 + 1; + out_triangles.push_back({ base_id + 0, base_id + 2, base_id + 1 }); + out_triangles.push_back({ base_id + 0, base_id + 3, base_id + 2 }); + } + } + } + + // save to file + fprintf(fp, "\n# vertices path %lld\n", i + 1); + for (const Vec3f& v : out_vertices) { + fprintf(fp, "v %g %g %g\n", v[0], v[1], v[2]); + } + + fprintf(fp, "\n# normals path %lld\n", i + 1); + for (const Vec3f& n : out_normals) { + fprintf(fp, "vn %g %g %g\n", n[0], n[1], n[2]); + } + + fprintf(fp, "\n# material path %lld\n", i + 1); + fprintf(fp, "usemtl material_%lld\n", i + 1); + + fprintf(fp, "\n# triangles path %lld\n", i + 1); + for (const Triangle& t : out_triangles) { + fprintf(fp, "f %lld//%lld %lld//%lld %lld//%lld\n", t[0], t[0], t[1], t[1], t[2], t[2]); + } + + } + +// fprintf(fp, "\n#vertices count %lld\n", out_vertices_count); + fclose(fp); +} + void GCodeViewer::init_shaders() { unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); @@ -641,7 +920,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) const std::vector& buffer_indices = indices[i]; buffer.indices.count = buffer_indices.size(); #if ENABLE_GCODE_VIEWER_STATISTICS - m_statistics.indices_size += SLIC3R_STDVEC_MEMSIZE(buffer_indices, unsigned int); m_statistics.indices_gpu_size += buffer.indices.count * sizeof(unsigned int); #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -876,6 +1154,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool if (it == buffer->render_paths.end()) { it = buffer->render_paths.insert(buffer->render_paths.end(), RenderPath()); it->color = color; + it->path_id = id; } unsigned int size = std::min(m_sequential_view.current.last, path.last.s_id) - std::max(m_sequential_view.current.first, path.first.s_id) + 1; @@ -1482,12 +1761,6 @@ void GCodeViewer::render_statistics() const ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(std::string("Indices CPU:")); - ImGui::PopStyleColor(); - ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.indices_size) + " bytes"); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); imgui.text(std::string("Paths CPU:")); ImGui::PopStyleColor(); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 5cddeaa75a..137ae89af7 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -108,6 +108,7 @@ class GCodeViewer struct RenderPath { Color color; + size_t path_id; std::vector sizes; std::vector offsets; // use size_t because we need the pointer's size (used in the call glMultiDrawElements()) }; @@ -201,7 +202,6 @@ class GCodeViewer // memory long long results_size{ 0 }; long long vertices_gpu_size{ 0 }; - long long indices_size{ 0 }; long long indices_gpu_size{ 0 }; long long paths_size{ 0 }; long long render_paths_size{ 0 }; @@ -231,7 +231,6 @@ class GCodeViewer void reset_sizes() { results_size = 0; vertices_gpu_size = 0; - indices_size = 0; indices_gpu_size = 0; paths_size = 0; render_paths_size = 0; @@ -397,6 +396,8 @@ public: bool is_legend_enabled() const { return m_legend_enabled; } void enable_legend(bool enable) { m_legend_enabled = enable; } + void export_toolpaths_to_obj(const char* filename) const; + private: void init_shaders(); void load_toolpaths(const GCodeProcessor::Result& gcode_result); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 0ee30a8084..f8ff9406f3 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4349,12 +4349,20 @@ void GLCanvas3D::update_tooltip_for_settings_item_in_main_toolbar() bool GLCanvas3D::has_toolpaths_to_export() const { +#if ENABLE_GCODE_VIEWER + return m_gcode_viewer.has_data(); +#else return m_volumes.has_toolpaths_to_export(); +#endif // ENABLE_GCODE_VIEWER } void GLCanvas3D::export_toolpaths_to_obj(const char* filename) const { +#if ENABLE_GCODE_VIEWER + m_gcode_viewer.export_toolpaths_to_obj(filename); +#else m_volumes.export_toolpaths_to_obj(filename); +#endif // ENABLE_GCODE_VIEWER } void GLCanvas3D::mouse_up_cleanup() diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 5e26979c3c..9c98f0f2e5 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1373,9 +1373,13 @@ void MainFrame::init_gcodeviewer_menubar() wxMenu* fileMenu = new wxMenu; { append_menu_item(fileMenu, wxID_ANY, _L("&Open G-code") + dots + "\tCtrl+O", _L("Open a G-code file"), - [this](wxCommandEvent&) { if (m_plater) m_plater->load_gcode(); }, "open", nullptr, + [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->load_gcode(); }, "open", nullptr, [this]() {return m_plater != nullptr; }, this); fileMenu->AppendSeparator(); + append_menu_item(fileMenu, wxID_ANY, _L("Export &toolpaths as OBJ") + dots, _L("Export toolpaths as OBJ"), + [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->export_toolpaths_to_obj(); }, "export_plater", nullptr, + [this]() {return can_export_toolpaths(); }, this); + fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_ANY, _L("Exit &G-code preview"), _L("Switch to editor mode"), [this](wxCommandEvent&) { set_mode(EMode::Editor); }); fileMenu->AppendSeparator(); From 2a78799f7e0dbdd2d381dbb4482b318264d7f81b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 3 Jul 2020 13:04:52 +0200 Subject: [PATCH 153/503] GCodeViewer -> Fixed layout when switching to/from gcode viewer state --- src/slic3r/GUI/MainFrame.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 9c98f0f2e5..eba169dc96 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -389,7 +389,7 @@ void MainFrame::update_layout() #if ENABLE_GCODE_VIEWER_AS_STATE case ESettingsLayout::GCodeViewer: { - GetSizer()->Add(m_plater, 1, wxEXPAND); + m_main_sizer->Add(m_plater, 1, wxEXPAND); m_plater->Show(); break; } From 73b885fc3711e82ae4c2fabf2464a06714dda435 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 8 Jul 2020 13:33:50 +0200 Subject: [PATCH 154/503] GCodeViewer -> Added imgui dialog for estimated printing times --- src/libslic3r/GCode.cpp | 17 +- src/libslic3r/GCodeTimeEstimator.cpp | 33 ++ src/libslic3r/GCodeTimeEstimator.hpp | 8 + src/libslic3r/Print.hpp | 14 + src/libslic3r/Technologies.hpp | 4 +- src/slic3r/GUI/GCodeViewer.cpp | 379 +++++++++++++------ src/slic3r/GUI/GCodeViewer.hpp | 7 +- src/slic3r/GUI/GLCanvas3D.cpp | 68 ++-- src/slic3r/GUI/GLShadersManager.cpp | 2 +- src/slic3r/GUI/GUI_Preview.cpp | 5 +- src/slic3r/GUI/GUI_Preview.hpp | 3 +- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 3 +- src/slic3r/GUI/ImGuiWrapper.cpp | 36 +- src/slic3r/GUI/ImGuiWrapper.hpp | 7 + src/slic3r/GUI/KBShortcutsDialog.cpp | 5 +- src/slic3r/GUI/MainFrame.cpp | 4 + src/slic3r/GUI/Plater.cpp | 26 +- 17 files changed, 438 insertions(+), 183 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 8de64544c8..7bfb73aa37 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1050,10 +1050,19 @@ namespace DoExport { print_statistics.clear(); print_statistics.estimated_normal_print_time = normal_time_estimator.get_time_dhm/*s*/(); print_statistics.estimated_silent_print_time = silent_time_estimator_enabled ? silent_time_estimator.get_time_dhm/*s*/() : "N/A"; - print_statistics.estimated_normal_custom_gcode_print_times = normal_time_estimator.get_custom_gcode_times_dhm(true); - if (silent_time_estimator_enabled) - print_statistics.estimated_silent_custom_gcode_print_times = silent_time_estimator.get_custom_gcode_times_dhm(true); - print_statistics.total_toolchanges = std::max(0, wipe_tower_data.number_of_toolchanges); +#if ENABLE_GCODE_VIEWER + print_statistics.estimated_normal_custom_gcode_print_times_str = normal_time_estimator.get_custom_gcode_times_dhm(true); + print_statistics.estimated_normal_custom_gcode_print_times = normal_time_estimator.get_custom_gcode_times(true); + if (silent_time_estimator_enabled) { + print_statistics.estimated_silent_custom_gcode_print_times_str = silent_time_estimator.get_custom_gcode_times_dhm(true); + print_statistics.estimated_silent_custom_gcode_print_times = silent_time_estimator.get_custom_gcode_times(true); + } +#else + print_statistics.estimated_normal_custom_gcode_print_times = normal_time_estimator.get_custom_gcode_times_dhm(true); + if (silent_time_estimator_enabled) + print_statistics.estimated_silent_custom_gcode_print_times = silent_time_estimator.get_custom_gcode_times_dhm(true); +#endif // ENABLE_GCODE_VIEWER + print_statistics.total_toolchanges = std::max(0, wipe_tower_data.number_of_toolchanges); if (! extruders.empty()) { std::pair out_filament_used_mm ("; filament used [mm] = ", 0); std::pair out_filament_used_cm3("; filament used [cm3] = ", 0); diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp index 9e8137ef0e..d67db84819 100644 --- a/src/libslic3r/GCodeTimeEstimator.cpp +++ b/src/libslic3r/GCodeTimeEstimator.cpp @@ -678,6 +678,21 @@ namespace Slic3r { return _get_time_minutes(get_time()); } +#if ENABLE_GCODE_VIEWER + std::vector>> GCodeTimeEstimator::get_custom_gcode_times(bool include_remaining) const + { + std::vector>> ret; + + float total_time = 0.0f; + for (const auto& [type, time] : m_custom_gcode_times) { + float remaining = include_remaining ? m_time - total_time : 0.0f; + ret.push_back({ type, { time, remaining } }); + total_time += time; + } + + return ret; + } +#else std::vector> GCodeTimeEstimator::get_custom_gcode_times() const { return m_custom_gcode_times; @@ -721,7 +736,24 @@ namespace Slic3r { } return ret; } +#endif // ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER + std::vector>> GCodeTimeEstimator::get_custom_gcode_times_dhm(bool include_remaining) const + { + std::vector>> ret; + + float total_time = 0.0f; + for (const auto& [type, time] : m_custom_gcode_times) { + std::string duration = _get_time_dhm(time); + std::string remaining = include_remaining ? _get_time_dhm(m_time - total_time) : ""; + ret.push_back({ type, { duration, remaining } }); + total_time += time; + } + + return ret; + } +#else std::vector> GCodeTimeEstimator::get_custom_gcode_times_dhm(bool include_remaining) const { std::vector> ret; @@ -742,6 +774,7 @@ namespace Slic3r { return ret; } +#endif // ENABLE_GCODE_VIEWER // Return an estimate of the memory consumed by the time estimator. size_t GCodeTimeEstimator::memory_used() const diff --git a/src/libslic3r/GCodeTimeEstimator.hpp b/src/libslic3r/GCodeTimeEstimator.hpp index 63e11c4faa..cfa12b40be 100644 --- a/src/libslic3r/GCodeTimeEstimator.hpp +++ b/src/libslic3r/GCodeTimeEstimator.hpp @@ -358,6 +358,9 @@ namespace Slic3r { std::string get_time_minutes() const; // Returns the estimated time, in seconds, for each custom gcode +#if ENABLE_GCODE_VIEWER + std::vector>> get_custom_gcode_times(bool include_remaining) const; +#else std::vector> get_custom_gcode_times() const; // Returns the estimated time, in format DDd HHh MMm SSs, for each color @@ -367,10 +370,15 @@ namespace Slic3r { // Returns the estimated time, in minutes (integer), for each color // If include_remaining==true the strings will be formatted as: "time for color (remaining time at color start)" std::vector get_color_times_minutes(bool include_remaining) const; +#endif // ENABLE_GCODE_VIEWER // Returns the estimated time, in format DDd HHh MMm, for each custom_gcode // If include_remaining==true the strings will be formatted as: "time for custom_gcode (remaining time at color start)" +#if ENABLE_GCODE_VIEWER + std::vector>> get_custom_gcode_times_dhm(bool include_remaining) const; +#else std::vector> get_custom_gcode_times_dhm(bool include_remaining) const; +#endif // ENABLE_GCODE_VIEWER // Return an estimate of the memory consumed by the time estimator. size_t memory_used() const; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index e4f4c60f57..eb9a4fb4b7 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -305,8 +305,15 @@ struct PrintStatistics PrintStatistics() { clear(); } std::string estimated_normal_print_time; std::string estimated_silent_print_time; +#if ENABLE_GCODE_VIEWER + std::vector>> estimated_normal_custom_gcode_print_times; + std::vector>> estimated_silent_custom_gcode_print_times; + std::vector>> estimated_normal_custom_gcode_print_times_str; + std::vector>> estimated_silent_custom_gcode_print_times_str; +#else std::vector> estimated_normal_custom_gcode_print_times; std::vector> estimated_silent_custom_gcode_print_times; +#endif // ENABLE_GCODE_VIEWER double total_used_filament; double total_extruded_volume; double total_cost; @@ -326,8 +333,15 @@ struct PrintStatistics void clear() { estimated_normal_print_time.clear(); estimated_silent_print_time.clear(); +#if ENABLE_GCODE_VIEWER + estimated_normal_custom_gcode_print_times_str.clear(); + estimated_silent_custom_gcode_print_times_str.clear(); estimated_normal_custom_gcode_print_times.clear(); estimated_silent_custom_gcode_print_times.clear(); +#else + estimated_normal_custom_gcode_print_times.clear(); + estimated_silent_custom_gcode_print_times.clear(); +#endif //ENABLE_GCODE_VIEWER total_used_filament = 0.; total_extruded_volume = 0.; total_cost = 0.; diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index fb84efe5ab..b04e78c4ed 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -59,8 +59,8 @@ // Enable G-Code viewer #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) -#define ENABLE_GCODE_VIEWER_STATISTICS (1 && ENABLE_GCODE_VIEWER) -#define ENABLE_GCODE_VIEWER_SHADERS_EDITOR (1 && ENABLE_GCODE_VIEWER) +#define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) +#define ENABLE_GCODE_VIEWER_SHADERS_EDITOR (0 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_AS_STATE (1 && ENABLE_GCODE_VIEWER) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 688134f7c9..26dc765db4 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -4,6 +4,8 @@ #if ENABLE_GCODE_VIEWER #include "libslic3r/Print.hpp" #include "libslic3r/Geometry.hpp" +#include "libslic3r/Model.hpp" +#include "libslic3r/Utils.hpp" #include "GUI_App.hpp" #if ENABLE_GCODE_VIEWER_AS_STATE #include "MainFrame.hpp" @@ -17,10 +19,7 @@ #include "GLCanvas3D.hpp" #include "GLToolbar.hpp" #include "GUI_Preview.hpp" -#include "libslic3r/Model.hpp" -#if ENABLE_GCODE_VIEWER_STATISTICS #include -#endif // ENABLE_GCODE_VIEWER_STATISTICS #include #include @@ -187,15 +186,14 @@ void GCodeViewer::SequentialView::Marker::render() const static float last_window_width = 0.0f; static size_t last_text_length = 0; - static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); ImGuiWrapper& imgui = *wxGetApp().imgui(); Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size(); - imgui.set_next_window_pos(static_cast(cnv_size.get_width()), static_cast(cnv_size.get_height()), ImGuiCond_Always, 1.0f, 1.0f); + imgui.set_next_window_pos(0.5f * static_cast(cnv_size.get_width()), static_cast(cnv_size.get_height()), ImGuiCond_Always, 0.5f, 1.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::SetNextWindowBgAlpha(0.25f); imgui.begin(std::string("ToolPosition"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); imgui.text(_u8L("Tool position") + ":"); ImGui::PopStyleColor(); ImGui::SameLine(); @@ -270,6 +268,7 @@ bool GCodeViewer::init() { switch (buffer_type(i)) { + default: { break; } case GCodeProcessor::EMoveType::Tool_change: case GCodeProcessor::EMoveType::Color_change: case GCodeProcessor::EMoveType::Pause_Print: @@ -420,6 +419,7 @@ void GCodeViewer::render() const m_sequential_view.marker.render(); render_shells(); render_legend(); + render_time_estimate(); #if ENABLE_GCODE_VIEWER_STATISTICS render_statistics(); #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -458,6 +458,7 @@ unsigned int GCodeViewer::get_options_visibility_flags() const flags = set_flag(flags, static_cast(Preview::OptionType::Shells), m_shells.visible); flags = set_flag(flags, static_cast(Preview::OptionType::ToolMarker), m_sequential_view.marker.is_visible()); flags = set_flag(flags, static_cast(Preview::OptionType::Legend), is_legend_enabled()); + flags = set_flag(flags, static_cast(Preview::OptionType::TimeEstimate), is_time_estimate_enabled()); return flags; } @@ -477,6 +478,7 @@ void GCodeViewer::set_options_visibility_from_flags(unsigned int flags) m_shells.visible = is_flag_set(static_cast(Preview::OptionType::Shells)); m_sequential_view.marker.set_visible(is_flag_set(static_cast(Preview::OptionType::ToolMarker))); enable_legend(is_flag_set(static_cast(Preview::OptionType::Legend))); + enable_time_estimate(is_flag_set(static_cast(Preview::OptionType::TimeEstimate))); } void GCodeViewer::set_layers_z_range(const std::array& layers_z_range) @@ -591,8 +593,8 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const return ret; }; - Vec3f v1 = get_vertex(start_id); - Vec3f v2 = get_vertex(start_id + 1); + Vec3f v1 = get_vertex(start_id) - half_height * Vec3f::UnitZ(); + Vec3f v2 = get_vertex(start_id + 1) - half_height * Vec3f::UnitZ(); float length = (v2 - v1).norm(); const auto&& [dir, right, up] = local_basis(v2 - v1); return Segment({ v1, v2, dir, right, up, half_width * right, half_height * up, length }); @@ -605,7 +607,8 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const const RenderPath& render_path = buffer.render_paths[i]; const Path& path = buffer.paths[render_path.path_id]; float half_width = 0.5f * path.width; - float half_height = 0.5f * path.height; + // clamp height to avoid artifacts due to z-fighting when importing the obj file into blender and similar + float half_height = std::max(0.5f * path.height, 0.005f); // generates vertices/normals/triangles std::vector out_vertices; @@ -657,7 +660,7 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const out_triangles.push_back({ base_id + 5, base_id + 3, base_id + 2 }); // update right vertices - if (disp < prev.length) { + if (disp < prev.length && disp < curr.length) { base_id = out_vertices.size() - 6; out_vertices[base_id + 0] -= disp_vec; out_vertices[base_id + 4] = out_vertices[base_id + 0]; @@ -670,7 +673,7 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const out_triangles.push_back({ base_id + 0, base_id + 3, base_id + 4 }); // update left vertices - if (disp < prev.length) { + if (disp < prev.length && disp < curr.length) { base_id = out_vertices.size() - 6; out_vertices[base_id + 2] -= disp_vec; out_vertices[base_id + 5] = out_vertices[base_id + 2]; @@ -742,27 +745,25 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const } // save to file - fprintf(fp, "\n# vertices path %lld\n", i + 1); + fprintf(fp, "\n# vertices path %zu\n", i + 1); for (const Vec3f& v : out_vertices) { fprintf(fp, "v %g %g %g\n", v[0], v[1], v[2]); } - fprintf(fp, "\n# normals path %lld\n", i + 1); + fprintf(fp, "\n# normals path %zu\n", i + 1); for (const Vec3f& n : out_normals) { fprintf(fp, "vn %g %g %g\n", n[0], n[1], n[2]); } - fprintf(fp, "\n# material path %lld\n", i + 1); - fprintf(fp, "usemtl material_%lld\n", i + 1); + fprintf(fp, "\n# material path %zu\n", i + 1); + fprintf(fp, "usemtl material_%zu\n", i + 1); - fprintf(fp, "\n# triangles path %lld\n", i + 1); + fprintf(fp, "\n# triangles path %zu\n", i + 1); for (const Triangle& t : out_triangles) { - fprintf(fp, "f %lld//%lld %lld//%lld %lld//%lld\n", t[0], t[0], t[1], t[1], t[2], t[2]); + fprintf(fp, "f %zu//%zu %zu//%zu %zu//%zu\n", t[0], t[0], t[1], t[1], t[2], t[2]); } - } -// fprintf(fp, "\n#vertices count %lld\n", out_vertices_count); fclose(fp); } @@ -775,12 +776,12 @@ void GCodeViewer::init_shaders() for (unsigned char i = begin_id; i < end_id; ++i) { switch (buffer_type(i)) { - case GCodeProcessor::EMoveType::Tool_change: { m_buffers[i].shader = is_glsl_120 ? "options_120_solid" : "options_110"; break; } - case GCodeProcessor::EMoveType::Color_change: { m_buffers[i].shader = is_glsl_120 ? "options_120_solid" : "options_110"; break; } - case GCodeProcessor::EMoveType::Pause_Print: { m_buffers[i].shader = is_glsl_120 ? "options_120_solid" : "options_110"; break; } - case GCodeProcessor::EMoveType::Custom_GCode: { m_buffers[i].shader = is_glsl_120 ? "options_120_solid" : "options_110"; break; } - case GCodeProcessor::EMoveType::Retract: { m_buffers[i].shader = is_glsl_120 ? "options_120_solid" : "options_110"; break; } - case GCodeProcessor::EMoveType::Unretract: { m_buffers[i].shader = is_glsl_120 ? "options_120_solid" : "options_110"; break; } + case GCodeProcessor::EMoveType::Tool_change: { m_buffers[i].shader = is_glsl_120 ? "options_120_flat" : "options_110"; break; } + case GCodeProcessor::EMoveType::Color_change: { m_buffers[i].shader = is_glsl_120 ? "options_120_flat" : "options_110"; break; } + case GCodeProcessor::EMoveType::Pause_Print: { m_buffers[i].shader = is_glsl_120 ? "options_120_flat" : "options_110"; break; } + case GCodeProcessor::EMoveType::Custom_GCode: { m_buffers[i].shader = is_glsl_120 ? "options_120_flat" : "options_110"; break; } + case GCodeProcessor::EMoveType::Retract: { m_buffers[i].shader = is_glsl_120 ? "options_120_flat" : "options_110"; break; } + case GCodeProcessor::EMoveType::Unretract: { m_buffers[i].shader = is_glsl_120 ? "options_120_flat" : "options_110"; break; } case GCodeProcessor::EMoveType::Extrude: { m_buffers[i].shader = "toolpaths"; break; } case GCodeProcessor::EMoveType::Travel: { m_buffers[i].shader = "toolpaths"; break; } default: { break; } @@ -1137,7 +1138,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool } // second pass: filter paths by sequential data and collect them by color - for (auto&& [buffer, id] : paths) { + for (const auto& [buffer, id] : paths) { const Path& path = buffer->paths[id]; if (m_sequential_view.current.last <= path.first.s_id || path.last.s_id <= m_sequential_view.current.first) continue; @@ -1203,8 +1204,8 @@ void GCodeViewer::render_toolpaths() const shader.set_uniform("percent_outline_radius", 0.01f * static_cast(m_shaders_editor.points.percent_outline)); shader.set_uniform("percent_center_radius", 0.01f * static_cast(m_shaders_editor.points.percent_center)); #else - shader.set_uniform("percent_outline_radius", 0.15f); - shader.set_uniform("percent_center_radius", 0.15f); + shader.set_uniform("percent_outline_radius", 0.0f); + shader.set_uniform("percent_center_radius", 0.33f); #endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR shader.set_uniform("viewport", viewport); shader.set_uniform("inv_proj_matrix", inv_proj); @@ -1266,6 +1267,7 @@ void GCodeViewer::render_toolpaths() const switch (buffer_type(i)) { + default: { break; } case GCodeProcessor::EMoveType::Tool_change: { render_as_points(buffer, EOptionsColors::ToolChanges, *shader); break; } case GCodeProcessor::EMoveType::Color_change: { render_as_points(buffer, EOptionsColors::ColorChanges, *shader); break; } case GCodeProcessor::EMoveType::Pause_Print: { render_as_points(buffer, EOptionsColors::PausePrints, *shader); break; } @@ -1320,18 +1322,15 @@ void GCodeViewer::render_shells() const // glsafe(::glDepthMask(GL_TRUE)); } +#define USE_ICON_HEXAGON 1 + void GCodeViewer::render_legend() const { - static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); - static const ImU32 ICON_BORDER_COLOR = ImGui::GetColorU32(ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); - if (!m_legend_enabled) return; ImGuiWrapper& imgui = *wxGetApp().imgui(); -#define USE_ICON_HEXAGON 1 - imgui.set_next_window_pos(0.0f, 0.0f, ImGuiCond_Always); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::SetNextWindowBgAlpha(0.6f); @@ -1347,19 +1346,14 @@ void GCodeViewer::render_legend() const Line }; -#if ENABLE_GCODE_VIEWER_SHADERS_EDITOR auto add_item = [this, draw_list, &imgui](EItemType type, const Color& color, const std::string& label, std::function callback = nullptr) { -#else - auto add_item = [draw_list, &imgui](EItemType type, const Color& color, const std::string& label, std::function callback = nullptr) { -#endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR float icon_size = ImGui::GetTextLineHeight(); - ImVec2 pos = ImGui::GetCursorPos(); + ImVec2 pos = ImGui::GetCursorScreenPos(); switch (type) { default: case EItemType::Rect: { - draw_list->AddRect({ pos.x, pos.y }, { pos.x + icon_size, pos.y + icon_size }, ICON_BORDER_COLOR, 0.0f, 0); draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f })); break; @@ -1380,37 +1374,26 @@ void GCodeViewer::render_legend() const } else draw_list->AddCircleFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); - -// ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); -// draw_list->AddCircle(center, 0.5f * icon_size, ICON_BORDER_COLOR, 16); -// if (m_shaders_editor.shader_version == 1) { -// draw_list->AddCircleFilled(center, (0.5f * icon_size) - 2.0f, -// ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); -// float radius = ((0.5f * icon_size) - 2.0f) * (1.0f - 0.01f * static_cast(m_shaders_editor.percent_outline)); -// draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); -// if (m_shaders_editor.percent_center > 0) { -// radius = ((0.5f * icon_size) - 2.0f) * 0.01f * static_cast(m_shaders_editor.percent_center); -// draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); -// } -// } else -// draw_list->AddCircleFilled(center, (0.5f * icon_size) - 2.0f, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); #else - draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, 0.5f * icon_size, - ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); - -// draw_list->AddCircle({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, 0.5f * icon_size, ICON_BORDER_COLOR, 16); -// draw_list->AddCircleFilled({ 0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size) }, (0.5f * icon_size) - 2.0f, -// ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); + ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); + if (m_buffers[buffer_id(GCodeProcessor::EMoveType::Retract)].shader == "options_120_flat") { + draw_list->AddCircleFilled(center, 0.5f * icon_size, + ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); + float radius = 0.5f * icon_size; + draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); + radius = 0.5f * icon_size * 0.01f * 33.0f; + draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); + } + else + draw_list->AddCircleFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); #endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR + break; } case EItemType::Hexagon: { ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 6); -// draw_list->AddNgon(center, 0.5f * icon_size, ICON_BORDER_COLOR, 6); -// draw_list->AddNgonFilled(center, (0.5f * icon_size) - 2.0f, -// ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 6); break; } case EItemType::Line: @@ -1454,21 +1437,18 @@ void GCodeViewer::render_legend() const }; // extrusion paths -> title - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); switch (m_view_type) { - case EViewType::FeatureType: { imgui.text(_u8L("Feature type")); break; } - case EViewType::Height: { imgui.text(_u8L("Height (mm)")); break; } - case EViewType::Width: { imgui.text(_u8L("Width (mm)")); break; } - case EViewType::Feedrate: { imgui.text(_u8L("Speed (mm/s)")); break; } - case EViewType::FanSpeed: { imgui.text(_u8L("Fan Speed (%%)")); break; } - case EViewType::VolumetricRate: { imgui.text(_u8L("Volumetric flow rate (mm³/s)")); break; } - case EViewType::Tool: { imgui.text(_u8L("Tool")); break; } - case EViewType::ColorPrint: { imgui.text(_u8L("Color Print")); break; } - default: { break; } + case EViewType::FeatureType: { imgui.title(_u8L("Feature type")); break; } + case EViewType::Height: { imgui.title(_u8L("Height (mm)")); break; } + case EViewType::Width: { imgui.title(_u8L("Width (mm)")); break; } + case EViewType::Feedrate: { imgui.title(_u8L("Speed (mm/s)")); break; } + case EViewType::FanSpeed: { imgui.title(_u8L("Fan Speed (%%)")); break; } + case EViewType::VolumetricRate: { imgui.title(_u8L("Volumetric flow rate (mm³/s)")); break; } + case EViewType::Tool: { imgui.title(_u8L("Tool")); break; } + case EViewType::ColorPrint: { imgui.title(_u8L("Color Print")); break; } + default: { break; } } - ImGui::PopStyleColor(); - ImGui::Separator(); // extrusion paths -> items switch (m_view_type) @@ -1566,28 +1546,26 @@ void GCodeViewer::render_legend() const else { for (int i = items_cnt; i >= 0; --i) { // create label for color change item - std::string id_str = " (" + std::to_string(i + 1) + ")"; - if (i == 0) { #if USE_ICON_HEXAGON - add_item(EItemType::Hexagon, m_tool_colors[i], (boost::format(_u8L("up to %.2f mm")) % cp_values.front().first).str() + id_str); + add_item(EItemType::Hexagon, m_tool_colors[i], (boost::format(_u8L("up to %.2f mm")) % cp_values.front().first).str()); #else - add_item(EItemType::Rect, m_tool_colors[i], (boost::format(_u8L("up to %.2f mm")) % cp_values.front().first).str() + id_str); + add_item(EItemType::Rect, m_tool_colors[i], (boost::format(_u8L("up to %.2f mm")) % cp_values.front().first).str()); #endif // USE_ICON_HEXAGON break; } else if (i == items_cnt) { #if USE_ICON_HEXAGON - add_item(EItemType::Hexagon, m_tool_colors[i], (boost::format(_u8L("above %.2f mm")) % cp_values[i - 1].second).str() + id_str); + add_item(EItemType::Hexagon, m_tool_colors[i], (boost::format(_u8L("above %.2f mm")) % cp_values[i - 1].second).str()); #else - add_item(EItemType::Rect, m_tool_colors[i], (boost::format(_u8L("above %.2f mm")) % cp_values[i - 1].second).str() + id_str); + add_item(EItemType::Rect, m_tool_colors[i], (boost::format(_u8L("above %.2f mm")) % cp_values[i - 1].second).str()); #endif // USE_ICON_HEXAGON continue; } #if USE_ICON_HEXAGON - add_item(EItemType::Hexagon, m_tool_colors[i], (boost::format(_u8L("%.2f - %.2f mm")) % cp_values[i - 1].second% cp_values[i].first).str() + id_str); + add_item(EItemType::Hexagon, m_tool_colors[i], (boost::format(_u8L("%.2f - %.2f mm")) % cp_values[i - 1].second % cp_values[i].first).str()); #else - add_item(EItemType::Rect, m_tool_colors[i], (boost::format(_u8L("%.2f - %.2f mm")) % cp_values[i - 1].second% cp_values[i].first).str() + id_str); + add_item(EItemType::Rect, m_tool_colors[i], (boost::format(_u8L("%.2f - %.2f mm")) % cp_values[i - 1].second % cp_values[i].first).str()); #endif // USE_ICON_HEXAGON } } @@ -1609,15 +1587,12 @@ void GCodeViewer::render_legend() const size_t last_color_id = m_tool_colors.size() - 1; for (int i = static_cast(custom_gcode_per_print_z.size()) - 1; i >= 0; --i) { if (custom_gcode_per_print_z[i].type == ColorChange) { - // create label for color change item - std::string id_str = " (" + std::to_string(color_change_idx--) + ")"; - #if USE_ICON_HEXAGON add_item(EItemType::Hexagon, m_tool_colors[last_color_id--], #else add_item(EItemType::Rect, m_tool_colors[last_color_id--], #endif // USE_ICON_HEXAGON - (boost::format(_u8L("Color change for Extruder %d at %.2f mm")) % custom_gcode_per_print_z[i].extruder % custom_gcode_per_print_z[i].print_z).str() + id_str); + (boost::format(_u8L("Color change for Extruder %d at %.2f mm")) % custom_gcode_per_print_z[i].extruder % custom_gcode_per_print_z[i].print_z).str()); } } } @@ -1641,11 +1616,7 @@ void GCodeViewer::render_legend() const { // title ImGui::Spacing(); - ImGui::Spacing(); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(_u8L("Travel")); - ImGui::PopStyleColor(); - ImGui::Separator(); + imgui.title(_u8L("Travel")); // items add_item(EItemType::Line, Travel_Colors[0], _u8L("Movement")); @@ -1657,13 +1628,18 @@ void GCodeViewer::render_legend() const } } - auto any_option_visible = [this]() { - return m_buffers[buffer_id(GCodeProcessor::EMoveType::Color_change)].visible || - m_buffers[buffer_id(GCodeProcessor::EMoveType::Custom_GCode)].visible || - m_buffers[buffer_id(GCodeProcessor::EMoveType::Pause_Print)].visible || - m_buffers[buffer_id(GCodeProcessor::EMoveType::Retract)].visible || - m_buffers[buffer_id(GCodeProcessor::EMoveType::Tool_change)].visible || - m_buffers[buffer_id(GCodeProcessor::EMoveType::Unretract)].visible; + auto any_option_available = [this]() { + auto available = [this](GCodeProcessor::EMoveType type) { + const TBuffer& buffer = m_buffers[buffer_id(type)]; + return buffer.visible && buffer.indices.count > 0; + }; + + return available(GCodeProcessor::EMoveType::Color_change) || + available(GCodeProcessor::EMoveType::Custom_GCode) || + available(GCodeProcessor::EMoveType::Pause_Print) || + available(GCodeProcessor::EMoveType::Retract) || + available(GCodeProcessor::EMoveType::Tool_change) || + available(GCodeProcessor::EMoveType::Unretract); }; auto add_option = [this, add_item](GCodeProcessor::EMoveType move_type, EOptionsColors color, const std::string& text) { @@ -1677,14 +1653,10 @@ void GCodeViewer::render_legend() const }; // options - if (any_option_visible()) { + if (any_option_available()) { // title ImGui::Spacing(); - ImGui::Spacing(); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(_u8L("Options")); - ImGui::PopStyleColor(); - ImGui::Separator(); + imgui.title(_u8L("Options")); // items add_option(GCodeProcessor::EMoveType::Retract, EOptionsColors::Retractions, _u8L("Retractions")); @@ -1699,10 +1671,175 @@ void GCodeViewer::render_legend() const ImGui::PopStyleVar(); } +void GCodeViewer::render_time_estimate() const +{ + static const std::vector Columns_Headers = { + _u8L("Operation"), + _u8L("Remaining"), + _u8L("Duration") + }; + + if (!m_time_estimate_enabled) + return; + + const PrintStatistics& ps = wxGetApp().plater()->fff_print().print_statistics(); + if (ps.estimated_normal_print_time == "N/A" && ps.estimated_silent_print_time == "N/A") + return; + + int columns_count = 1; + if (ps.estimated_silent_print_time != "N/A") + ++columns_count; + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size(); + imgui.set_next_window_pos(static_cast(cnv_size.get_width()), static_cast(cnv_size.get_height()), ImGuiCond_Always, 1.0f, 1.0f); + ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(-1.0f, 0.5f * static_cast(cnv_size.get_height()))); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::SetNextWindowBgAlpha(0.6f); + imgui.begin(std::string("Time_estimate"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove); + + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + float icon_size = ImGui::GetTextLineHeight(); + + using Time = std::pair; + using TimesList = std::vector>; + using Headers = std::vector; + using Offsets = std::array; + + auto add_mode = [this, &imgui, icon_size, draw_list](const std::string& mode, const std::string& time, const TimesList& times, const Headers& headers) { + auto add_partial_times = [this, &imgui, icon_size, draw_list](const TimesList& times, const Headers& headers) { + auto add_color = [this, &imgui, icon_size, draw_list](int id, Offsets& offsets, const Time& time) { + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); + std::string text = _u8L("Color"); + if (m_view_type != EViewType::ColorPrint) + text += " " + std::to_string(id); + imgui.text(text); + ImGui::PopStyleColor(); + ImGui::SameLine(); + + if (m_view_type == EViewType::ColorPrint) { + const Color& color = m_tool_colors[id - 1]; + ImVec2 pos = ImGui::GetCursorScreenPos(); +#if USE_ICON_HEXAGON + ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); + draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 6); +#else + draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, + ImGui::GetColorU32({ m_tool_colors[i][0], m_tool_colors[i][1], m_tool_colors[i][2], 1.0f })); +#endif // USE_ICON_HEXAGON + } + ImGui::SameLine(offsets[0]); + imgui.text(short_time(get_time_dhms(time.second))); + ImGui::SameLine(offsets[1]); + imgui.text(short_time(get_time_dhms(time.first))); + }; + auto calc_offsets = [this, icon_size](const TimesList& times, const Headers& headers, int color_change_count) { + Offsets ret = { ImGui::CalcTextSize(headers[0].c_str()).x, ImGui::CalcTextSize(headers[1].c_str()).x }; + for (const auto& [type, time] : times) { + std::string label; + switch (type) + { + case CustomGCode::PausePrint: + { + label = _u8L("Pause"); + break; + } + case CustomGCode::ColorChange: + { + label = _u8L("Color"); + if (m_view_type != EViewType::ColorPrint) + label += " " + std::to_string(color_change_count); + break; + } + default: { break; } + } + + ret[0] = std::max(ret[0], ImGui::CalcTextSize(label.c_str()).x); + ret[1] = std::max(ret[1], ImGui::CalcTextSize(short_time(get_time_dhms(time.second)).c_str()).x); + } + + const ImGuiStyle& style = ImGui::GetStyle(); + ret[0] += icon_size + style.ItemSpacing.x; + ret[1] += ret[0] + style.ItemSpacing.x; + return ret; + }; + + if (times.empty()) + return; + + int color_change_count = 0; + for (auto time : times) { + if (time.first == CustomGCode::ColorChange) + ++color_change_count; + } + + Offsets offsets = calc_offsets(times, headers, color_change_count); + + ImGui::Spacing(); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); + imgui.text(headers[0]); + ImGui::SameLine(offsets[0]); + imgui.text(headers[1]); + ImGui::SameLine(offsets[1]); + imgui.text(headers[2]); + ImGui::PopStyleColor(); + + int last_color_id = color_change_count; + + for (int i = static_cast(times.size()) - 1; i >= 0; --i) { + const auto& [type, time] = times[i]; + + switch (type) + { + case CustomGCode::PausePrint: + { + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); + imgui.text(_u8L("Pause")); + ImGui::PopStyleColor(); + ImGui::SameLine(offsets[0]); + imgui.text(short_time(get_time_dhms(time.second - time.first))); + + add_color(last_color_id, offsets, time); + break; + } + case CustomGCode::ColorChange: + { + add_color(color_change_count, offsets, time); + last_color_id = color_change_count--; + break; + } + default: { break; } + } + } + }; + + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); + imgui.text(mode + ":"); + ImGui::PopStyleColor(); + ImGui::SameLine(); + imgui.text(time); + add_partial_times(times, headers); + }; + + // title + imgui.title(_u8L("Estimated printing time")); + + // times + if (ps.estimated_normal_print_time != "N/A") + add_mode(_u8L("Normal mode"), ps.estimated_normal_print_time, ps.estimated_normal_custom_gcode_print_times, Columns_Headers); + + if (ps.estimated_silent_print_time != "N/A") { + ImGui::Separator(); + add_mode(_u8L("Stealth mode"), ps.estimated_silent_print_time, ps.estimated_silent_custom_gcode_print_times, Columns_Headers); + } + + imgui.end(); + ImGui::PopStyleVar(); +} + #if ENABLE_GCODE_VIEWER_STATISTICS void GCodeViewer::render_statistics() const { - static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); static const float offset = 230.0f; ImGuiWrapper& imgui = *wxGetApp().imgui(); @@ -1711,7 +1848,7 @@ void GCodeViewer::render_statistics() const imgui.begin(std::string("GCodeViewer Statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize); ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); imgui.text(std::string("GCodeProcessor time:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); @@ -1719,19 +1856,19 @@ void GCodeViewer::render_statistics() const ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); imgui.text(std::string("Load time:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.load_time) + " ms"); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); imgui.text(std::string("Resfresh time:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.refresh_time) + " ms"); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); imgui.text(std::string("Resfresh paths time:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); @@ -1739,13 +1876,13 @@ void GCodeViewer::render_statistics() const ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); imgui.text(std::string("Multi GL_POINTS calls:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.gl_multi_points_calls_count)); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); imgui.text(std::string("Multi GL_LINE_STRIP calls:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); @@ -1753,7 +1890,7 @@ void GCodeViewer::render_statistics() const ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); imgui.text(std::string("GCodeProcessor results:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); @@ -1761,13 +1898,13 @@ void GCodeViewer::render_statistics() const ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); imgui.text(std::string("Paths CPU:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.paths_size) + " bytes"); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); imgui.text(std::string("Render paths CPU:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); @@ -1775,13 +1912,13 @@ void GCodeViewer::render_statistics() const ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); imgui.text(std::string("Vertices GPU:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.vertices_gpu_size) + " bytes"); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); imgui.text(std::string("Indices GPU:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); @@ -1789,13 +1926,13 @@ void GCodeViewer::render_statistics() const ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); imgui.text(std::string("Travel segments:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.travel_segments_count)); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); imgui.text(std::string("Extrude segments:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); @@ -1816,8 +1953,6 @@ void GCodeViewer::render_shaders_editor() const } }; - static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); - ImGuiWrapper& imgui = *wxGetApp().imgui(); Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size(); @@ -1828,8 +1963,8 @@ void GCodeViewer::render_shaders_editor() const if (ImGui::CollapsingHeader("Points", ImGuiTreeNodeFlags_DefaultOpen)) { if (ImGui::TreeNode("GLSL version")) { ImGui::RadioButton("1.10 (low end PCs)", &m_shaders_editor.points.shader_version, 0); - ImGui::RadioButton("1.20 flat (billboards)", &m_shaders_editor.points.shader_version, 1); - ImGui::RadioButton("1.20 solid (spheres default)", &m_shaders_editor.points.shader_version, 2); + ImGui::RadioButton("1.20 flat (billboards) [default]", &m_shaders_editor.points.shader_version, 1); + ImGui::RadioButton("1.20 solid (spheres)", &m_shaders_editor.points.shader_version, 2); ImGui::TreePop(); } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 137ae89af7..90155c7281 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -248,7 +248,7 @@ class GCodeViewer { struct Points { - int shader_version{ 2 }; + int shader_version{ 1 }; float point_size{ 0.8f }; int percent_outline{ 0 }; int percent_center{ 33 }; @@ -341,6 +341,7 @@ private: Shells m_shells; EViewType m_view_type{ EViewType::FeatureType }; bool m_legend_enabled{ true }; + bool m_time_estimate_enabled{ true }; #if ENABLE_GCODE_VIEWER_STATISTICS mutable Statistics m_statistics; #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -396,6 +397,9 @@ public: bool is_legend_enabled() const { return m_legend_enabled; } void enable_legend(bool enable) { m_legend_enabled = enable; } + bool is_time_estimate_enabled() const { return m_time_estimate_enabled; } + void enable_time_estimate(bool enable) { m_time_estimate_enabled = enable; } + void export_toolpaths_to_obj(const char* filename) const; private: @@ -406,6 +410,7 @@ private: void render_toolpaths() const; void render_shells() const; void render_legend() const; + void render_time_estimate() const; #if ENABLE_GCODE_VIEWER_STATISTICS void render_statistics() const; #endif // ENABLE_GCODE_VIEWER_STATISTICS diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index f8ff9406f3..8c69142485 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -219,8 +219,6 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const if (!m_enabled) return; - static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); - const Size& cnv_size = canvas.get_canvas_size(); float canvas_w = (float)cnv_size.get_width(); float canvas_h = (float)cnv_size.get_height(); @@ -228,50 +226,50 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const ImGuiWrapper& imgui = *wxGetApp().imgui(); imgui.set_next_window_pos(canvas_w - imgui.get_style_scaling() * THICKNESS_BAR_WIDTH, canvas_h, ImGuiCond_Always, 1.0f, 1.0f); - imgui.begin(_(L("Variable layer height")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse); + imgui.begin(_L("Variable layer height"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(_(L("Left mouse button:"))); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); + imgui.text(_L("Left mouse button:")); ImGui::PopStyleColor(); ImGui::SameLine(); - imgui.text(_(L("Add detail"))); + imgui.text(_L("Add detail")); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(_(L("Right mouse button:"))); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); + imgui.text(_L("Right mouse button:")); ImGui::PopStyleColor(); ImGui::SameLine(); - imgui.text(_(L("Remove detail"))); + imgui.text(_L("Remove detail")); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(_(L("Shift + Left mouse button:"))); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); + imgui.text(_L("Shift + Left mouse button:")); ImGui::PopStyleColor(); ImGui::SameLine(); - imgui.text(_(L("Reset to base"))); + imgui.text(_L("Reset to base")); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(_(L("Shift + Right mouse button:"))); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); + imgui.text(_L("Shift + Right mouse button:")); ImGui::PopStyleColor(); ImGui::SameLine(); - imgui.text(_(L("Smoothing"))); + imgui.text(_L("Smoothing")); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(_(L("Mouse wheel:"))); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); + imgui.text(_L("Mouse wheel:")); ImGui::PopStyleColor(); ImGui::SameLine(); - imgui.text(_(L("Increase/decrease edit area"))); + imgui.text(_L("Increase/decrease edit area")); ImGui::Separator(); - if (imgui.button(_(L("Adaptive")))) + if (imgui.button(_L("Adaptive"))) wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, m_adaptive_quality)); ImGui::SameLine(); float text_align = ImGui::GetCursorPosX(); ImGui::AlignTextToFramePadding(); - imgui.text(_(L("Quality / Speed"))); + imgui.text(_L("Quality / Speed")); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); - ImGui::TextUnformatted(_(L("Higher print quality versus higher print speed.")).ToUTF8()); + ImGui::TextUnformatted(_L("Higher print quality versus higher print speed.").ToUTF8()); ImGui::EndTooltip(); } @@ -282,13 +280,13 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const ImGui::SliderFloat("", &m_adaptive_quality, 0.0f, 1.f, "%.2f"); ImGui::Separator(); - if (imgui.button(_(L("Smooth")))) + if (imgui.button(_L("Smooth"))) wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), HeightProfileSmoothEvent(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, m_smooth_params)); ImGui::SameLine(); ImGui::SetCursorPosX(text_align); ImGui::AlignTextToFramePadding(); - imgui.text(_(L("Radius"))); + imgui.text(_L("Radius")); ImGui::SameLine(); ImGui::SetCursorPosX(widget_align); ImGui::PushItemWidth(imgui.get_style_scaling() * 120.0f); @@ -298,7 +296,7 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const ImGui::SetCursorPosX(text_align); ImGui::AlignTextToFramePadding(); - imgui.text(_(L("Keep min"))); + imgui.text(_L("Keep min")); ImGui::SameLine(); if (ImGui::GetCursorPosX() < widget_align) // because of line lenght after localization ImGui::SetCursorPosX(widget_align); @@ -307,7 +305,7 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const imgui.checkbox("##2", m_smooth_params.keep_min); ImGui::Separator(); - if (imgui.button(_(L("Reset")))) + if (imgui.button(_L("Reset"))) wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE)); imgui.end(); @@ -3078,8 +3076,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) #if ENABLE_GCODE_VIEWER case 'L': case 'l': { - if (!m_main_toolbar.is_enabled()) - { + if (!m_main_toolbar.is_enabled()) { m_gcode_viewer.enable_legend(!m_gcode_viewer.is_legend_enabled()); m_dirty = true; wxGetApp().plater()->update_preview_bottom_toolbar(); @@ -3090,13 +3087,24 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) case 'O': case 'o': { _update_camera_zoom(-1.0); break; } #if ENABLE_RENDER_PICKING_PASS - case 'T': - case 't': { + case 'P': + case 'p': { m_show_picking_texture = !m_show_picking_texture; - m_dirty = true; + m_dirty = true; break; } #endif // ENABLE_RENDER_PICKING_PASS +#if ENABLE_GCODE_VIEWER + case 'T': + case 't': { + if (!m_main_toolbar.is_enabled()) { + m_gcode_viewer.enable_time_estimate(!m_gcode_viewer.is_time_estimate_enabled()); + m_dirty = true; + wxGetApp().plater()->update_preview_bottom_toolbar(); + } + break; + } +#endif // ENABLE_GCODE_VIEWER case 'Z': #if ENABLE_GCODE_VIEWER case 'z': diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index cb47d79618..e62a81d39b 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -69,7 +69,7 @@ GLShaderProgram* GLShadersManager::get_current_shader() if (id == 0) return nullptr; - auto it = std::find_if(m_shaders.begin(), m_shaders.end(), [id](std::unique_ptr& p) { return p->get_id() == id; }); + auto it = std::find_if(m_shaders.begin(), m_shaders.end(), [id](std::unique_ptr& p) { return static_cast(p->get_id()) == id; }); return (it != m_shaders.end()) ? it->get() : nullptr; } diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 530165001f..50a820cfaa 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -323,7 +323,8 @@ bool Preview::init(wxWindow* parent, Model* model) get_option_type_string(OptionType::CustomGCodes) + "|0|" + get_option_type_string(OptionType::Shells) + "|0|" + get_option_type_string(OptionType::ToolMarker) + "|0|" + - get_option_type_string(OptionType::Legend) + "|1" + get_option_type_string(OptionType::Legend) + "|1|" + + get_option_type_string(OptionType::TimeEstimate) + "|1" ); Slic3r::GUI::create_combochecklist(m_combochecklist_options, GUI::into_u8(_L("Options")), options_items); #else @@ -1458,10 +1459,10 @@ wxString Preview::get_option_type_string(OptionType type) const case OptionType::Shells: { return _L("Shells"); } case OptionType::ToolMarker: { return _L("Tool marker"); } case OptionType::Legend: { return _L("Legend"); } + case OptionType::TimeEstimate: { return _L("Estimated printing time"); } default: { return ""; } } } - #endif // ENABLE_GCODE_VIEWER } // namespace GUI diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index bf174c2e09..ff3bf41371 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -149,7 +149,8 @@ public: CustomGCodes, Shells, ToolMarker, - Legend + Legend, + TimeEstimate }; Preview(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process, diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index cd42857247..9aaded6e3c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -650,8 +650,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l window_width = std::max(window_width, button_width); auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) { - static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); m_imgui->text(caption); ImGui::PopStyleColor(); ImGui::SameLine(caption_max); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 51a9a6d4eb..2c463dc2a5 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -44,6 +44,12 @@ static const std::map font_icons = { {ImGui::MaterialIconMarker , "resin" } }; +const ImVec4 ImGuiWrapper::COL_WINDOW_BACKGROND = { 0.133f, 0.133f, 0.133f, 0.8f }; +const ImVec4 ImGuiWrapper::COL_GREY_DARK = { 0.333f, 0.333f, 0.333f, 1.0f }; +const ImVec4 ImGuiWrapper::COL_GREY_LIGHT = { 0.4f, 0.4f, 0.4f, 1.0f }; +const ImVec4 ImGuiWrapper::COL_ORANGE_DARK = { 0.757f, 0.404f, 0.216f, 1.0f }; +const ImVec4 ImGuiWrapper::COL_ORANGE_LIGHT = { 1.0f, 0.49f, 0.216f, 1.0f }; + ImGuiWrapper::ImGuiWrapper() : m_glyph_ranges(nullptr) , m_font_cjk(false) @@ -751,6 +757,22 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co check_box(_L("Search in English"), view_params.english); } +void ImGuiWrapper::title(const std::string& str) +{ + ImGuiWindow* window = ImGui::GetCurrentWindow(); + const float frame_height = ImGui::CalcTextSize(str.c_str(), nullptr, false).y; + + ImRect frame_bb; + frame_bb.Min = { window->WorkRect.Min.x, window->DC.CursorPos.y }; + frame_bb.Max = { window->WorkRect.Max.x, window->DC.CursorPos.y + frame_height }; + + frame_bb.Min.x -= IM_FLOOR(window->WindowPadding.x * 0.5f - 1.0f); + frame_bb.Max.x += IM_FLOOR(window->WindowPadding.x * 0.5f); + + window->DrawList->AddRectFilled(frame_bb.Min, frame_bb.Max, ImGui::GetColorU32(COL_ORANGE_DARK), 0.0f, 0); + text(str); +} + void ImGuiWrapper::disabled_begin(bool disabled) { wxCHECK_RET(!m_disabled, "ImGUI: Unbalanced disabled_begin() call"); @@ -970,20 +992,10 @@ void ImGuiWrapper::init_style() { ImGuiStyle &style = ImGui::GetStyle(); - auto set_color = [&](ImGuiCol_ col, unsigned hex_color) { - style.Colors[col] = ImVec4( - ((hex_color >> 24) & 0xff) / 255.0f, - ((hex_color >> 16) & 0xff) / 255.0f, - ((hex_color >> 8) & 0xff) / 255.0f, - (hex_color & 0xff) / 255.0f); + auto set_color = [&](ImGuiCol_ entity, ImVec4 color) { + style.Colors[entity] = color; }; - static const unsigned COL_WINDOW_BACKGROND = 0x222222cc; - static const unsigned COL_GREY_DARK = 0x555555ff; - static const unsigned COL_GREY_LIGHT = 0x666666ff; - static const unsigned COL_ORANGE_DARK = 0xc16737ff; - static const unsigned COL_ORANGE_LIGHT = 0xff7d38ff; - // Window style.WindowRounding = 4.0f; set_color(ImGuiCol_WindowBg, COL_WINDOW_BACKGROND); diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index bf542e1381..f79bd3fbc8 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -80,6 +80,7 @@ public: bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected, int& mouse_wheel); void search_list(const ImVec2& size, bool (*items_getter)(int, const char** label, const char** tooltip), char* search_str, Search::OptionViewParameters& view_params, int& selected, bool& edited, int& mouse_wheel, bool is_localized); + void title(const std::string& str); void disabled_begin(bool disabled); void disabled_end(); @@ -89,6 +90,12 @@ public: bool want_text_input() const; bool want_any_input() const; + static const ImVec4 COL_WINDOW_BACKGROND; + static const ImVec4 COL_GREY_DARK; + static const ImVec4 COL_GREY_LIGHT; + static const ImVec4 COL_ORANGE_DARK; + static const ImVec4 COL_ORANGE_LIGHT; + private: void init_font(bool compress); void init_input(); diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 51ba06ba45..66e5ac4878 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -183,7 +183,7 @@ void KBShortcutsDialog::fill_shortcuts() #endif // __linux__ #if ENABLE_RENDER_PICKING_PASS // Don't localize debugging texts. - { "T", "Toggle picking pass texture rendering on/off" }, + { "P", "Toggle picking pass texture rendering on/off" }, #endif // ENABLE_RENDER_PICKING_PASS }; @@ -203,7 +203,8 @@ void KBShortcutsDialog::fill_shortcuts() { L("Arrow Down"), L("Lower Layer") }, { "U", L("Upper Layer") }, { "D", L("Lower Layer") }, - { "L", L("Show/Hide Legend") } + { "L", L("Show/Hide Legend") }, + { "T", L("Show/Hide Estimated printing time") } }; m_full_shortcuts.push_back(std::make_pair(_L("Preview"), preview_shortcuts)); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index eba169dc96..59b8e56f34 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -355,6 +355,10 @@ void MainFrame::update_layout() // Set new settings switch (m_layout) { + case ESettingsLayout::Unknown: + { + break; + } case ESettingsLayout::Old: { m_plater->Reparent(m_tabpanel); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 36fa83470a..d0b52426c2 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1330,8 +1330,12 @@ void Sidebar::update_sliced_info_sizer() wxString str_color = _L("Color"); wxString str_pause = _L("Pause"); - auto fill_labels = [str_color, str_pause](const std::vector>& times, - wxString& new_label, wxString& info_text) +#if ENABLE_GCODE_VIEWER + auto fill_labels = [str_color, str_pause](const std::vector>>& times, +#else + auto fill_labels = [str_color, str_pause](const std::vector>& times, +#endif // ENABLE_GCODE_VIEWER + wxString& new_label, wxString& info_text) { int color_change_count = 0; for (auto time : times) @@ -1348,19 +1352,31 @@ void Sidebar::update_sliced_info_sizer() if (i != (int)times.size() - 1 && times[i].first == CustomGCode::PausePrint) new_label += format_wxstr(" -> %1%", str_pause); +#if ENABLE_GCODE_VIEWER + info_text += format_wxstr("\n%1% (%2%)", times[i].second.first, times[i].second.second); +#else info_text += format_wxstr("\n%1%", times[i].second); +#endif // ENABLE_GCODE_VIEWER } }; if (ps.estimated_normal_print_time != "N/A") { new_label += format_wxstr("\n - %1%", _L("normal mode")); info_text += format_wxstr("\n%1%", ps.estimated_normal_print_time); +#if ENABLE_GCODE_VIEWER + fill_labels(ps.estimated_normal_custom_gcode_print_times_str, new_label, info_text); +#else fill_labels(ps.estimated_normal_custom_gcode_print_times, new_label, info_text); +#endif // ENABLE_GCODE_VIEWER } if (ps.estimated_silent_print_time != "N/A") { new_label += format_wxstr("\n - %1%", _L("stealth mode")); info_text += format_wxstr("\n%1%", ps.estimated_silent_print_time); +#if ENABLE_GCODE_VIEWER + fill_labels(ps.estimated_silent_custom_gcode_print_times_str, new_label, info_text); +#else fill_labels(ps.estimated_silent_custom_gcode_print_times, new_label, info_text); +#endif // ENABLE_GCODE_VIEWER } p->sliced_info->SetTextAndShow(siEstimatedTime, info_text, new_label); } @@ -2709,6 +2725,9 @@ void Plater::priv::reset() if (view3D->is_layers_editing_enabled()) view3D->enable_layers_editing(false); +#if ENABLE_GCODE_VIEWER + reset_gcode_toolpaths(); +#endif // ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER_AS_STATE gcode_result.reset(); #endif // ENABLE_GCODE_VIEWER_AS_STATE @@ -2859,8 +2878,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool // Reset preview canvases. If the print has been invalidated, the preview canvases will be cleared. // Otherwise they will be just refreshed. #if ENABLE_GCODE_VIEWER - if (this->preview != nullptr) - { + if (this->preview != nullptr) { // If the preview is not visible, the following line just invalidates the preview, // but the G-code paths or SLA preview are calculated first once the preview is made visible. this->preview->get_canvas3d()->reset_gcode_toolpaths(); From 6fbb3db79c1a2b87a873120606bdf2bdc8cacc65 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 8 Jul 2020 14:43:14 +0200 Subject: [PATCH 155/503] Fixed build when ENABLE_GCODE_VIEWER is disabled --- src/slic3r/GUI/3DBed.cpp | 5 ++--- src/slic3r/GUI/MainFrame.cpp | 2 ++ src/slic3r/GUI/Selection.cpp | 8 ++++++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 16ab95d6c4..ca075fb372 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -369,7 +369,6 @@ void Bed3D::calc_bounding_boxes() const m_extended_bounding_box.merge(model_bb); } #else - m_extended_bounding_box.merge(m_axes.get_total_length() * Vec3d::Ones()); m_extended_bounding_box.merge(m_axes.length + Axes::ArrowLength * Vec3d::Ones()); // extend to contain model, if any if (!m_model.get_filename().empty()) @@ -694,11 +693,11 @@ void Bed3D::render_default(bool bottom) const { // draw background glsafe(::glDepthMask(GL_FALSE)); -#if ENABLE_LAYOUT_NO_RESTART +#if ENABLE_GCODE_VIEWER glsafe(::glColor4fv(m_model_color.data())); #else glsafe(::glColor4f(0.35f, 0.35f, 0.35f, 0.4f)); -#endif // ENABLE_LAYOUT_NO_RESTART +#endif // ENABLE_GCODE_VIEWER glsafe(::glNormal3d(0.0f, 0.0f, 1.0f)); glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_vertices_data())); glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount)); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 59b8e56f34..0d2c17dce7 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -482,9 +482,11 @@ void MainFrame::shutdown() #endif // ENABLE_LAYOUT_NO_RESTART if (m_plater != nullptr) { +#if ENABLE_GCODE_VIEWER_AS_STATE // restore sidebar if it was hidden when switching to gcode viewer mode if (m_restore_from_gcode_viewer.collapsed_sidebar) m_plater->collapse_sidebar(false); +#endif // ENABLE_GCODE_VIEWER_AS_STATE // Stop the background thread (Windows and Linux). // Disconnect from a 3DConnextion driver (OSX). m_plater->get_mouse3d_controller().shutdown(); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index add09a59de..f429c6a958 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -2039,11 +2039,19 @@ void Selection::render_sidebar_scale_hints(const std::string& sidebar_field) con shader->set_uniform("uniform_color", uniform_scale ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis], 4); glsafe(::glTranslated(0.0, 5.0, 0.0)); +#if ENABLE_GCODE_VIEWER m_arrow.render(); +#else + m_arrow->render(); +#endif // ENABLE_GCODE_VIEWER glsafe(::glTranslated(0.0, -10.0, 0.0)); glsafe(::glRotated(180.0, 0.0, 0.0, 1.0)); +#if ENABLE_GCODE_VIEWER m_arrow.render(); +#else + m_arrow->render(); +#endif // ENABLE_GCODE_VIEWER }; if (boost::ends_with(sidebar_field, "x") || uniform_scale) From 431cfcc671f2cf8b861814e24c5afd5c1c7ec9d7 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 9 Jul 2020 15:57:35 +0200 Subject: [PATCH 156/503] GCodeViewer -> Reworked layout of imgui dialog for estimated printing times --- src/slic3r/GUI/GCodeViewer.cpp | 254 +++++++++++++++++++------------- src/slic3r/GUI/ImGuiWrapper.cpp | 7 + 2 files changed, 160 insertions(+), 101 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 26dc765db4..3ceaf86fa2 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -40,23 +40,28 @@ static GCodeProcessor::EMoveType buffer_type(unsigned char id) { return static_cast(static_cast(GCodeProcessor::EMoveType::Retract) + id); } -std::vector> decode_colors(const std::vector & colors) { +std::array decode_color(const std::string& color) { static const float INV_255 = 1.0f / 255.0f; + std::array ret; + const char* c = color.data() + 1; + if ((color.size() == 7) && (color.front() == '#')) { + for (size_t j = 0; j < 3; ++j) { + int digit1 = hex_digit_to_int(*c++); + int digit2 = hex_digit_to_int(*c++); + if ((digit1 == -1) || (digit2 == -1)) + break; + + ret[j] = float(digit1 * 16 + digit2) * INV_255; + } + } + return ret; +} + +std::vector> decode_colors(const std::vector& colors) { std::vector> output(colors.size(), { 0.0f, 0.0f, 0.0f }); for (size_t i = 0; i < colors.size(); ++i) { - const std::string& color = colors[i]; - const char* c = color.data() + 1; - if ((color.size() == 7) && (color.front() == '#')) { - for (size_t j = 0; j < 3; ++j) { - int digit1 = hex_digit_to_int(*c++); - int digit2 = hex_digit_to_int(*c++); - if ((digit1 == -1) || (digit2 == -1)) - break; - - output[i][j] = float(digit1 * 16 + digit2) * INV_255; - } - } + output[i] = decode_color(colors[i]); } return output; } @@ -1575,6 +1580,11 @@ void GCodeViewer::render_legend() const { // extruders for (unsigned int i = 0; i < (unsigned int)extruders_count; ++i) { + // shows only extruders actually used + auto it = std::find(m_extruder_ids.begin(), m_extruder_ids.end(), static_cast(i)); + if (it == m_extruder_ids.end()) + continue; + #if USE_ICON_HEXAGON add_item(EItemType::Hexagon, m_tool_colors[i], (boost::format(_u8L("Extruder %d")) % (i + 1)).str()); #else @@ -1671,14 +1681,9 @@ void GCodeViewer::render_legend() const ImGui::PopStyleVar(); } + void GCodeViewer::render_time_estimate() const { - static const std::vector Columns_Headers = { - _u8L("Operation"), - _u8L("Remaining"), - _u8L("Duration") - }; - if (!m_time_estimate_enabled) return; @@ -1686,94 +1691,87 @@ void GCodeViewer::render_time_estimate() const if (ps.estimated_normal_print_time == "N/A" && ps.estimated_silent_print_time == "N/A") return; - int columns_count = 1; - if (ps.estimated_silent_print_time != "N/A") - ++columns_count; - ImGuiWrapper& imgui = *wxGetApp().imgui(); - Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size(); - imgui.set_next_window_pos(static_cast(cnv_size.get_width()), static_cast(cnv_size.get_height()), ImGuiCond_Always, 1.0f, 1.0f); - ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(-1.0f, 0.5f * static_cast(cnv_size.get_height()))); - ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - ImGui::SetNextWindowBgAlpha(0.6f); - imgui.begin(std::string("Time_estimate"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove); - - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - float icon_size = ImGui::GetTextLineHeight(); using Time = std::pair; using TimesList = std::vector>; - using Headers = std::vector; - using Offsets = std::array; - auto add_mode = [this, &imgui, icon_size, draw_list](const std::string& mode, const std::string& time, const TimesList& times, const Headers& headers) { - auto add_partial_times = [this, &imgui, icon_size, draw_list](const TimesList& times, const Headers& headers) { - auto add_color = [this, &imgui, icon_size, draw_list](int id, Offsets& offsets, const Time& time) { - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - std::string text = _u8L("Color"); - if (m_view_type != EViewType::ColorPrint) - text += " " + std::to_string(id); - imgui.text(text); - ImGui::PopStyleColor(); - ImGui::SameLine(); + // helper structure containig the data needed to render the items + struct Item + { + CustomGCode::Type type; + int extruder_id; + Color color; + Time time; + }; + using Items = std::vector; - if (m_view_type == EViewType::ColorPrint) { - const Color& color = m_tool_colors[id - 1]; - ImVec2 pos = ImGui::GetCursorScreenPos(); -#if USE_ICON_HEXAGON - ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); - draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 6); -#else - draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, - ImGui::GetColorU32({ m_tool_colors[i][0], m_tool_colors[i][1], m_tool_colors[i][2], 1.0f })); -#endif // USE_ICON_HEXAGON - } - ImGui::SameLine(offsets[0]); - imgui.text(short_time(get_time_dhms(time.second))); - ImGui::SameLine(offsets[1]); - imgui.text(short_time(get_time_dhms(time.first))); + auto append_mode = [this, &imgui](const std::string& time_str, const Items& items) { + auto append_partial_times = [this, &imgui](const Items& items) { + using Headers = std::vector; + const Headers headers = { + _u8L("Event"), + _u8L("Remaining"), + _u8L("Duration") }; - auto calc_offsets = [this, icon_size](const TimesList& times, const Headers& headers, int color_change_count) { + using Offsets = std::array; + auto calc_offsets = [this, &headers](const Items& items) { Offsets ret = { ImGui::CalcTextSize(headers[0].c_str()).x, ImGui::CalcTextSize(headers[1].c_str()).x }; - for (const auto& [type, time] : times) { + for (const Item& item : items) { std::string label; - switch (type) + switch (item.type) { - case CustomGCode::PausePrint: - { - label = _u8L("Pause"); - break; - } + case CustomGCode::PausePrint: { label = _u8L("Pause"); break; } case CustomGCode::ColorChange: { - label = _u8L("Color"); - if (m_view_type != EViewType::ColorPrint) - label += " " + std::to_string(color_change_count); + int extruders_count = wxGetApp().extruders_edited_cnt(); + label = (extruders_count > 1) ? _u8L("[XX] Color") : _u8L("Color"); break; } default: { break; } } ret[0] = std::max(ret[0], ImGui::CalcTextSize(label.c_str()).x); - ret[1] = std::max(ret[1], ImGui::CalcTextSize(short_time(get_time_dhms(time.second)).c_str()).x); + ret[1] = std::max(ret[1], ImGui::CalcTextSize(short_time(get_time_dhms(item.time.second)).c_str()).x); } const ImGuiStyle& style = ImGui::GetStyle(); - ret[0] += icon_size + style.ItemSpacing.x; + ret[0] += ImGui::GetTextLineHeight() + 2.0f * style.ItemSpacing.x; ret[1] += ret[0] + style.ItemSpacing.x; return ret; }; + auto append_color = [this, &imgui](int id, int extruder_id, const Color& color, Offsets& offsets, const Time& time) { + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); + int extruders_count = wxGetApp().extruders_edited_cnt(); + std::string text; + if (extruders_count > 1) + text = "[" + std::to_string(extruder_id) + "] "; + text += _u8L("Color"); + imgui.text(text); + ImGui::PopStyleColor(); + ImGui::SameLine(); - if (times.empty()) + float icon_size = ImGui::GetTextLineHeight(); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + ImVec2 pos = ImGui::GetCursorScreenPos(); + pos.x -= 0.5f * ImGui::GetStyle().ItemSpacing.x; +#if USE_ICON_HEXAGON + ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); + draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 6); +#else + draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, + ImGui::GetColorU32({ m_tool_colors[i][0], m_tool_colors[i][1], m_tool_colors[i][2], 1.0f })); +#endif // USE_ICON_HEXAGON + ImGui::SameLine(offsets[0]); + imgui.text(short_time(get_time_dhms(time.second))); + ImGui::SameLine(offsets[1]); + imgui.text(short_time(get_time_dhms(time.first))); + }; + + if (items.empty()) return; - int color_change_count = 0; - for (auto time : times) { - if (time.first == CustomGCode::ColorChange) - ++color_change_count; - } - - Offsets offsets = calc_offsets(times, headers, color_change_count); + Offsets offsets = calc_offsets(items); ImGui::Spacing(); ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); @@ -1783,13 +1781,11 @@ void GCodeViewer::render_time_estimate() const ImGui::SameLine(offsets[1]); imgui.text(headers[2]); ImGui::PopStyleColor(); + ImGui::Separator(); - int last_color_id = color_change_count; - - for (int i = static_cast(times.size()) - 1; i >= 0; --i) { - const auto& [type, time] = times[i]; - - switch (type) + unsigned int last_color_id = 0; + for (const Item& item : items) { + switch (item.type) { case CustomGCode::PausePrint: { @@ -1797,15 +1793,13 @@ void GCodeViewer::render_time_estimate() const imgui.text(_u8L("Pause")); ImGui::PopStyleColor(); ImGui::SameLine(offsets[0]); - imgui.text(short_time(get_time_dhms(time.second - time.first))); - - add_color(last_color_id, offsets, time); + imgui.text(short_time(get_time_dhms(item.time.second - item.time.first))); break; } case CustomGCode::ColorChange: { - add_color(color_change_count, offsets, time); - last_color_id = color_change_count--; + append_color(last_color_id, item.extruder_id, item.color, offsets, item.time); + ++last_color_id; break; } default: { break; } @@ -1814,24 +1808,82 @@ void GCodeViewer::render_time_estimate() const }; ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(mode + ":"); + imgui.text(_u8L("Time") + ":"); ImGui::PopStyleColor(); ImGui::SameLine(); - imgui.text(time); - add_partial_times(times, headers); + imgui.text(time_str); + append_partial_times(items); }; + auto generate_items = [this](const TimesList& times) { + std::vector items; + + std::vector custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes; + int extruders_count = wxGetApp().extruders_edited_cnt(); + std::vector last_color(extruders_count); + for (int i = 0; i < extruders_count; ++i) { + last_color[i] = m_tool_colors[i]; + } + int last_extruder_id = 1; + for (const auto& time_rec : times) { + switch (time_rec.first) + { + case CustomGCode::PausePrint: + { + auto it = std::find_if(custom_gcode_per_print_z.begin(), custom_gcode_per_print_z.end(), [time_rec](const CustomGCode::Item& item) { return item.type == time_rec.first; }); + if (it != custom_gcode_per_print_z.end()) { + items.push_back({ CustomGCode::ColorChange, it->extruder, last_color[it->extruder - 1], time_rec.second }); + items.push_back({ time_rec.first, it->extruder, last_color[it->extruder - 1], time_rec.second }); + custom_gcode_per_print_z.erase(it); + } + break; + } + case CustomGCode::ColorChange: + { + auto it = std::find_if(custom_gcode_per_print_z.begin(), custom_gcode_per_print_z.end(), [time_rec](const CustomGCode::Item& item) { return item.type == time_rec.first; }); + if (it != custom_gcode_per_print_z.end()) { + items.push_back({ time_rec.first, it->extruder, last_color[it->extruder - 1], time_rec.second }); + last_color[it->extruder - 1] = decode_color(it->color); + last_extruder_id = it->extruder; + custom_gcode_per_print_z.erase(it); + } + else + items.push_back({ time_rec.first, last_extruder_id, last_color[last_extruder_id - 1], time_rec.second }); + + break; + } + default: { break; } + } + } + + return items; + }; + + Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size(); + imgui.set_next_window_pos(static_cast(cnv_size.get_width()), static_cast(cnv_size.get_height()), ImGuiCond_Always, 1.0f, 1.0f); + ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(-1.0f, 0.5f * static_cast(cnv_size.get_height()))); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::SetNextWindowBgAlpha(0.6f); + imgui.begin(std::string("Time_estimate_2"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove); + // title imgui.title(_u8L("Estimated printing time")); - // times - if (ps.estimated_normal_print_time != "N/A") - add_mode(_u8L("Normal mode"), ps.estimated_normal_print_time, ps.estimated_normal_custom_gcode_print_times, Columns_Headers); - - if (ps.estimated_silent_print_time != "N/A") { - ImGui::Separator(); - add_mode(_u8L("Stealth mode"), ps.estimated_silent_print_time, ps.estimated_silent_custom_gcode_print_times, Columns_Headers); + // mode tabs + ImGui::BeginTabBar("mode_tabs"); + if (ps.estimated_normal_print_time != "N/A") { + if (ImGui::BeginTabItem(_u8L("Normal").c_str())) { + append_mode(ps.estimated_normal_print_time, generate_items(ps.estimated_normal_custom_gcode_print_times)); + ImGui::EndTabItem(); + } } + if (ps.estimated_silent_print_time != "N/A") { + if (ImGui::BeginTabItem(_u8L("Stealth").c_str())) { + append_mode(ps.estimated_silent_print_time, generate_items(ps.estimated_silent_custom_gcode_print_times)); + ImGui::EndTabItem(); + } + } + ImGui::EndTabBar(); imgui.end(); ImGui::PopStyleVar(); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 2c463dc2a5..1253c047ef 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -1028,6 +1028,13 @@ void ImGuiWrapper::init_style() // Separator set_color(ImGuiCol_Separator, COL_ORANGE_LIGHT); + + // Tabs + set_color(ImGuiCol_Tab, COL_ORANGE_DARK); + set_color(ImGuiCol_TabHovered, COL_ORANGE_LIGHT); + set_color(ImGuiCol_TabActive, COL_ORANGE_LIGHT); + set_color(ImGuiCol_TabUnfocused, COL_GREY_DARK); + set_color(ImGuiCol_TabUnfocusedActive, COL_GREY_LIGHT); } void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) From 13a8ed0bd03aa508487dc22705f909b4563572b9 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 10 Jul 2020 13:20:03 +0200 Subject: [PATCH 157/503] GCodeViewer -> Reworked layout of color print legend to make it consistent for the single extruder and multi extruders cases --- src/slic3r/GUI/GCodeViewer.cpp | 239 ++++++++++++++++++--------------- 1 file changed, 133 insertions(+), 106 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 3ceaf86fa2..f22a691b1f 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1351,7 +1351,7 @@ void GCodeViewer::render_legend() const Line }; - auto add_item = [this, draw_list, &imgui](EItemType type, const Color& color, const std::string& label, std::function callback = nullptr) { + auto append_item = [this, draw_list, &imgui](EItemType type, const Color& color, const std::string& label, std::function callback = nullptr) { float icon_size = ImGui::GetTextLineHeight(); ImVec2 pos = ImGui::GetCursorScreenPos(); switch (type) @@ -1419,28 +1419,75 @@ void GCodeViewer::render_legend() const imgui.text(label); }; - auto add_range = [this, draw_list, &imgui, add_item](const Extrusions::Range& range, unsigned int decimals) { - auto add_range_item = [this, draw_list, &imgui, add_item](int i, float value, unsigned int decimals) { + auto append_range = [this, draw_list, &imgui, append_item](const Extrusions::Range& range, unsigned int decimals) { + auto append_range_item = [this, draw_list, &imgui, append_item](int i, float value, unsigned int decimals) { char buf[1024]; ::sprintf(buf, "%.*f", decimals, value); #if USE_ICON_HEXAGON - add_item(EItemType::Hexagon, Range_Colors[i], buf); + append_item(EItemType::Hexagon, Range_Colors[i], buf); #else - add_item(EItemType::Rect, Range_Colors[i], buf); + append_item(EItemType::Rect, Range_Colors[i], buf); #endif // USE_ICON_HEXAGON }; float step_size = range.step_size(); if (step_size == 0.0f) // single item use case - add_range_item(0, range.min, decimals); + append_range_item(0, range.min, decimals); else { for (int i = static_cast(Range_Colors.size()) - 1; i >= 0; --i) { - add_range_item(i, range.min + static_cast(i) * step_size, decimals); + append_range_item(i, range.min + static_cast(i) * step_size, decimals); } } }; + auto color_print_ranges = [this](unsigned char extruder_id, const std::vector& custom_gcode_per_print_z) { + std::vector>> ret; + ret.reserve(custom_gcode_per_print_z.size()); + + for (const auto& item : custom_gcode_per_print_z) { + if (extruder_id + 1 != static_cast(item.extruder)) + continue; + + if (item.type != ColorChange) + continue; + + auto lower_b = std::lower_bound(m_layers_zs.begin(), m_layers_zs.end(), item.print_z - Slic3r::DoubleSlider::epsilon()); + + if (lower_b == m_layers_zs.end()) + continue; + + double current_z = *lower_b; + double previous_z = lower_b == m_layers_zs.begin() ? 0.0 : *(--lower_b); + + // to avoid duplicate values, check adding values + if (ret.empty() || !(ret.back().second.first == previous_z && ret.back().second.second == current_z)) + ret.push_back({ decode_color(item.color), { previous_z, current_z } }); + } + + return ret; + }; + + auto upto_label = [](double z) { + char buf[64]; + ::sprintf(buf, "%.2f", z); + return _u8L("up to") + " " + std::string(buf) + " " + _u8L("mm"); + }; + + auto above_label = [](double z) { + char buf[64]; + ::sprintf(buf, "%.2f", z); + return _u8L("above") + " " + std::string(buf) + " " + _u8L("mm"); + }; + + auto fromto_label = [](double z1, double z2) { + char buf1[64]; + ::sprintf(buf1, "%.2f", z1); + char buf2[64]; + ::sprintf(buf2, "%.2f", z2); + return _u8L("from") + " " + std::string(buf1) + " " + _u8L("to") + " " + std::string(buf2) + " " + _u8L("mm"); + }; + // extrusion paths -> title switch (m_view_type) { @@ -1466,9 +1513,9 @@ void GCodeViewer::render_legend() const ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f); #if USE_ICON_HEXAGON - add_item(EItemType::Hexagon, Extrusion_Role_Colors[static_cast(role)], _u8L(ExtrusionEntity::role_to_string(role)), [this, role]() { + append_item(EItemType::Hexagon, Extrusion_Role_Colors[static_cast(role)], _u8L(ExtrusionEntity::role_to_string(role)), [this, role]() { #else - add_item(EItemType::Rect, Extrusion_Role_Colors[static_cast(role)], _u8L(ExtrusionEntity::role_to_string(role)), [this, role]() { + append_item(EItemType::Rect, Extrusion_Role_Colors[static_cast(role)], _u8L(ExtrusionEntity::role_to_string(role)), [this, role]() { #endif // USE_ICON_HEXAGON if (role < erCount) { @@ -1485,24 +1532,19 @@ void GCodeViewer::render_legend() const } break; } - case EViewType::Height: { add_range(m_extrusions.ranges.height, 3); break; } - case EViewType::Width: { add_range(m_extrusions.ranges.width, 3); break; } - case EViewType::Feedrate: { add_range(m_extrusions.ranges.feedrate, 1); break; } - case EViewType::FanSpeed: { add_range(m_extrusions.ranges.fan_speed, 0); break; } - case EViewType::VolumetricRate: { add_range(m_extrusions.ranges.volumetric_rate, 3); break; } + case EViewType::Height: { append_range(m_extrusions.ranges.height, 3); break; } + case EViewType::Width: { append_range(m_extrusions.ranges.width, 3); break; } + case EViewType::Feedrate: { append_range(m_extrusions.ranges.feedrate, 1); break; } + case EViewType::FanSpeed: { append_range(m_extrusions.ranges.fan_speed, 0); break; } + case EViewType::VolumetricRate: { append_range(m_extrusions.ranges.volumetric_rate, 3); break; } case EViewType::Tool: { - size_t tools_count = m_tool_colors.size(); - for (size_t i = 0; i < tools_count; ++i) { - // shows only extruders actually used - auto it = std::find(m_extruder_ids.begin(), m_extruder_ids.end(), static_cast(i)); - if (it == m_extruder_ids.end()) - continue; - + // shows only extruders actually used + for (unsigned char i : m_extruder_ids) { #if USE_ICON_HEXAGON - add_item(EItemType::Hexagon, m_tool_colors[i], (boost::format(_u8L("Extruder %d")) % (i + 1)).str()); + append_item(EItemType::Hexagon, m_tool_colors[i], _u8L("Extruder") + " " + std::to_string(i + 1)); #else - add_item(EItemType::Rect, m_tool_colors[i], (boost::format(_u8L("Extruder %d")) % (i + 1)).str()); + append_item(EItemType::Rect, m_tool_colors[i], _u8L("Extruder") + " " + std::to_string(i + 1)); #endif // USE_ICON_HEXAGON } break; @@ -1512,97 +1554,85 @@ void GCodeViewer::render_legend() const const std::vector& custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes; const int extruders_count = wxGetApp().extruders_edited_cnt(); if (extruders_count == 1) { // single extruder use case - if (custom_gcode_per_print_z.empty()) - // no data to show + std::vector>> cp_values = color_print_ranges(0, custom_gcode_per_print_z); + const int items_cnt = static_cast(cp_values.size()); + if (items_cnt == 0) { // There are no color changes, but there are some pause print or custom Gcode #if USE_ICON_HEXAGON - add_item(EItemType::Hexagon, m_tool_colors.front(), _u8L("Default print color")); + append_item(EItemType::Hexagon, m_tool_colors.front(), _u8L("Default color")); #else - add_item(EItemType::Rect, m_tool_colors.front(), _u8L("Default print color")); + append_item(EItemType::Rect, m_tool_colors.front(), _u8L("Default color")); #endif // USE_ICON_HEXAGON + } else { - std::vector> cp_values; - cp_values.reserve(custom_gcode_per_print_z.size()); - - for (auto custom_code : custom_gcode_per_print_z) { - if (custom_code.type != ColorChange) - continue; - - auto lower_b = std::lower_bound(m_layers_zs.begin(), m_layers_zs.end(), custom_code.print_z - Slic3r::DoubleSlider::epsilon()); - - if (lower_b == m_layers_zs.end()) - continue; - - double current_z = *lower_b; - double previous_z = lower_b == m_layers_zs.begin() ? 0.0 : *(--lower_b); - - // to avoid duplicate values, check adding values - if (cp_values.empty() || !(cp_values.back().first == previous_z && cp_values.back().second == current_z)) - cp_values.emplace_back(std::make_pair(previous_z, current_z)); - } - - const int items_cnt = static_cast(cp_values.size()); - if (items_cnt == 0) { // There is no one color change, but there are some pause print or custom Gcode + for (int i = items_cnt; i >= 0; --i) { + // create label for color change item + if (i == 0) { #if USE_ICON_HEXAGON - add_item(EItemType::Hexagon, m_tool_colors.front(), _u8L("Default print color")); + append_item(EItemType::Hexagon, m_tool_colors[0], upto_label(cp_values.front().second.first)); #else - add_item(EItemType::Rect, m_tool_colors.front(), _u8L("Default print color")); -#endif // USE_ICON_HEXAGON - } - else { - for (int i = items_cnt; i >= 0; --i) { - // create label for color change item - if (i == 0) { -#if USE_ICON_HEXAGON - add_item(EItemType::Hexagon, m_tool_colors[i], (boost::format(_u8L("up to %.2f mm")) % cp_values.front().first).str()); -#else - add_item(EItemType::Rect, m_tool_colors[i], (boost::format(_u8L("up to %.2f mm")) % cp_values.front().first).str()); -#endif // USE_ICON_HEXAGON - break; - } - else if (i == items_cnt) { -#if USE_ICON_HEXAGON - add_item(EItemType::Hexagon, m_tool_colors[i], (boost::format(_u8L("above %.2f mm")) % cp_values[i - 1].second).str()); -#else - add_item(EItemType::Rect, m_tool_colors[i], (boost::format(_u8L("above %.2f mm")) % cp_values[i - 1].second).str()); -#endif // USE_ICON_HEXAGON - continue; - } -#if USE_ICON_HEXAGON - add_item(EItemType::Hexagon, m_tool_colors[i], (boost::format(_u8L("%.2f - %.2f mm")) % cp_values[i - 1].second % cp_values[i].first).str()); -#else - add_item(EItemType::Rect, m_tool_colors[i], (boost::format(_u8L("%.2f - %.2f mm")) % cp_values[i - 1].second % cp_values[i].first).str()); + append_item(EItemType::Rect, m_tool_colors[0], upto_label(cp_values.front().second.first); #endif // USE_ICON_HEXAGON + break; } + else if (i == items_cnt) { +#if USE_ICON_HEXAGON + append_item(EItemType::Hexagon, cp_values[i - 1].first, above_label(cp_values[i - 1].second.second)); +#else + append_item(EItemType::Rect, cp_values[i - 1].first, above_label(cp_values[i - 1].second.second); +#endif // USE_ICON_HEXAGON + continue; + } +#if USE_ICON_HEXAGON + append_item(EItemType::Hexagon, cp_values[i - 1].first, fromto_label(cp_values[i - 1].second.second, cp_values[i].second.first)); +#else + append_item(EItemType::Rect, cp_values[i - 1].first, fromto_label(cp_values[i - 1].second.second, cp_values[i].second.first)); +#endif // USE_ICON_HEXAGON } } } else // multi extruder use case { - // extruders - for (unsigned int i = 0; i < (unsigned int)extruders_count; ++i) { - // shows only extruders actually used - auto it = std::find(m_extruder_ids.begin(), m_extruder_ids.end(), static_cast(i)); - if (it == m_extruder_ids.end()) - continue; - + // shows only extruders actually used + for (unsigned char i : m_extruder_ids) { + std::vector>> cp_values = color_print_ranges(i, custom_gcode_per_print_z); + const int items_cnt = static_cast(cp_values.size()); + if (items_cnt == 0) { // There are no color changes, but there are some pause print or custom Gcode #if USE_ICON_HEXAGON - add_item(EItemType::Hexagon, m_tool_colors[i], (boost::format(_u8L("Extruder %d")) % (i + 1)).str()); + append_item(EItemType::Hexagon, m_tool_colors[i], _u8L("Extruder") + " " + std::to_string(i + 1) + " " + _u8L("default color")); #else - add_item(EItemType::Rect, m_tool_colors[i], (boost::format(_u8L("Extruder %d")) % (i + 1)).str()); + append_item(EItemType::Rect, m_tool_colors[i], _u8L("Extruder") + " " + std::to_string(i + 1) + " " + _u8L("default color")); #endif // USE_ICON_HEXAGON - } - - // color changes - int color_change_idx = 1 + static_cast(m_tool_colors.size()) - extruders_count; - size_t last_color_id = m_tool_colors.size() - 1; - for (int i = static_cast(custom_gcode_per_print_z.size()) - 1; i >= 0; --i) { - if (custom_gcode_per_print_z[i].type == ColorChange) { + } + else { + for (int j = items_cnt; j >= 0; --j) { + // create label for color change item + std::string label = _u8L("Extruder") + " " + std::to_string(i + 1); + if (j == 0) { + label += " " + upto_label(cp_values.front().second.first); #if USE_ICON_HEXAGON - add_item(EItemType::Hexagon, m_tool_colors[last_color_id--], + append_item(EItemType::Hexagon, m_tool_colors[i], label); #else - add_item(EItemType::Rect, m_tool_colors[last_color_id--], + append_item(EItemType::Rect, m_tool_colors[i], label); #endif // USE_ICON_HEXAGON - (boost::format(_u8L("Color change for Extruder %d at %.2f mm")) % custom_gcode_per_print_z[i].extruder % custom_gcode_per_print_z[i].print_z).str()); + break; + } + else if (j == items_cnt) { + label += " " + above_label(cp_values[j - 1].second.second); +#if USE_ICON_HEXAGON + append_item(EItemType::Hexagon, cp_values[j - 1].first, label); +#else + append_item(EItemType::Rect, cp_values[j - 1].first, label); +#endif // USE_ICON_HEXAGON + continue; + } + + label += " " + fromto_label(cp_values[j - 1].second.second, cp_values[j].second.first); +#if USE_ICON_HEXAGON + append_item(EItemType::Hexagon, cp_values[j - 1].first, label); +#else + append_item(EItemType::Rect, cp_values[j - 1].first, label); +#endif // USE_ICON_HEXAGON + } } } } @@ -1629,9 +1659,9 @@ void GCodeViewer::render_legend() const imgui.title(_u8L("Travel")); // items - add_item(EItemType::Line, Travel_Colors[0], _u8L("Movement")); - add_item(EItemType::Line, Travel_Colors[1], _u8L("Extrusion")); - add_item(EItemType::Line, Travel_Colors[2], _u8L("Retraction")); + append_item(EItemType::Line, Travel_Colors[0], _u8L("Movement")); + append_item(EItemType::Line, Travel_Colors[1], _u8L("Extrusion")); + append_item(EItemType::Line, Travel_Colors[2], _u8L("Retraction")); break; } @@ -1652,13 +1682,13 @@ void GCodeViewer::render_legend() const available(GCodeProcessor::EMoveType::Unretract); }; - auto add_option = [this, add_item](GCodeProcessor::EMoveType move_type, EOptionsColors color, const std::string& text) { + auto add_option = [this, append_item](GCodeProcessor::EMoveType move_type, EOptionsColors color, const std::string& text) { const TBuffer& buffer = m_buffers[buffer_id(move_type)]; if (buffer.visible && buffer.indices.count > 0) #if ENABLE_GCODE_VIEWER_SHADERS_EDITOR - add_item((m_shaders_editor.points.shader_version == 0) ? EItemType::Rect : EItemType::Circle, Options_Colors[static_cast(color)], text); + append_item((m_shaders_editor.points.shader_version == 0) ? EItemType::Rect : EItemType::Circle, Options_Colors[static_cast(color)], text); #else - add_item((buffer.shader == "options_110") ? EItemType::Rect : EItemType::Circle, Options_Colors[static_cast(color)], text); + append_item((buffer.shader == "options_110") ? EItemType::Rect : EItemType::Circle, Options_Colors[static_cast(color)], text); #endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR }; @@ -1681,7 +1711,6 @@ void GCodeViewer::render_legend() const ImGui::PopStyleVar(); } - void GCodeViewer::render_time_estimate() const { if (!m_time_estimate_enabled) @@ -1724,8 +1753,7 @@ void GCodeViewer::render_time_estimate() const case CustomGCode::PausePrint: { label = _u8L("Pause"); break; } case CustomGCode::ColorChange: { - int extruders_count = wxGetApp().extruders_edited_cnt(); - label = (extruders_count > 1) ? _u8L("[XX] Color") : _u8L("Color"); + label = (wxGetApp().extruders_edited_cnt() > 1) ? _u8L("[XX] Color") : _u8L("Color"); break; } default: { break; } @@ -1742,9 +1770,8 @@ void GCodeViewer::render_time_estimate() const }; auto append_color = [this, &imgui](int id, int extruder_id, const Color& color, Offsets& offsets, const Time& time) { ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - int extruders_count = wxGetApp().extruders_edited_cnt(); std::string text; - if (extruders_count > 1) + if (wxGetApp().extruders_edited_cnt() > 1) text = "[" + std::to_string(extruder_id) + "] "; text += _u8L("Color"); imgui.text(text); From 755fdb5ab478042a2a7edf9318ab46defb637444 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 10 Jul 2020 15:31:56 +0200 Subject: [PATCH 158/503] GCodeViewer -> Refactoring of data shown into estimated printing time dialog --- src/slic3r/GUI/GCodeViewer.cpp | 73 +++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index f22a691b1f..aaabd62220 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1728,9 +1728,16 @@ void GCodeViewer::render_time_estimate() const // helper structure containig the data needed to render the items struct Item { - CustomGCode::Type type; + enum class EType : unsigned char + { + Print, + ColorChange, + Pause + }; + EType type; int extruder_id; - Color color; + Color color1; + Color color2; Time time; }; using Items = std::vector; @@ -1750,13 +1757,9 @@ void GCodeViewer::render_time_estimate() const std::string label; switch (item.type) { - case CustomGCode::PausePrint: { label = _u8L("Pause"); break; } - case CustomGCode::ColorChange: - { - label = (wxGetApp().extruders_edited_cnt() > 1) ? _u8L("[XX] Color") : _u8L("Color"); - break; - } - default: { break; } + case Item::EType::Print: { label = _u8L("Print"); break; } + case Item::EType::Pause: { label = _u8L("Pause"); break; } + case Item::EType::ColorChange: { label = _u8L("Color change"); break; } } ret[0] = std::max(ret[0], ImGui::CalcTextSize(label.c_str()).x); @@ -1764,17 +1767,13 @@ void GCodeViewer::render_time_estimate() const } const ImGuiStyle& style = ImGui::GetStyle(); - ret[0] += ImGui::GetTextLineHeight() + 2.0f * style.ItemSpacing.x; + ret[0] += 2.0f * (ImGui::GetTextLineHeight() + style.ItemSpacing.x); ret[1] += ret[0] + style.ItemSpacing.x; return ret; }; - auto append_color = [this, &imgui](int id, int extruder_id, const Color& color, Offsets& offsets, const Time& time) { + auto append_color = [this, &imgui](const Color& color1, const Color& color2, Offsets& offsets, const Time& time) { ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - std::string text; - if (wxGetApp().extruders_edited_cnt() > 1) - text = "[" + std::to_string(extruder_id) + "] "; - text += _u8L("Color"); - imgui.text(text); + imgui.text(_u8L("Color change")); ImGui::PopStyleColor(); ImGui::SameLine(); @@ -1784,15 +1783,18 @@ void GCodeViewer::render_time_estimate() const pos.x -= 0.5f * ImGui::GetStyle().ItemSpacing.x; #if USE_ICON_HEXAGON ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); - draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 6); + draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color1[0], color1[1], color1[2], 1.0f }), 6); + center.x += icon_size; + draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color2[0], color2[1], color2[2], 1.0f }), 6); #else draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, - ImGui::GetColorU32({ m_tool_colors[i][0], m_tool_colors[i][1], m_tool_colors[i][2], 1.0f })); + ImGui::GetColorU32({ color1[0], color1[1], color1[2], 1.0f })); + pos.x += icon_size; + draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, + ImGui::GetColorU32({ color2[0], color2[1], color2[2], 1.0f })); #endif // USE_ICON_HEXAGON ImGui::SameLine(offsets[0]); - imgui.text(short_time(get_time_dhms(time.second))); - ImGui::SameLine(offsets[1]); - imgui.text(short_time(get_time_dhms(time.first))); + imgui.text(short_time(get_time_dhms(time.second - time.first))); }; if (items.empty()) @@ -1810,11 +1812,21 @@ void GCodeViewer::render_time_estimate() const ImGui::PopStyleColor(); ImGui::Separator(); - unsigned int last_color_id = 0; for (const Item& item : items) { switch (item.type) { - case CustomGCode::PausePrint: + case Item::EType::Print: + { + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); + imgui.text(_u8L("Print")); + ImGui::PopStyleColor(); + ImGui::SameLine(offsets[0]); + imgui.text(short_time(get_time_dhms(item.time.second))); + ImGui::SameLine(offsets[1]); + imgui.text(short_time(get_time_dhms(item.time.first))); + break; + } + case Item::EType::Pause: { ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); imgui.text(_u8L("Pause")); @@ -1823,13 +1835,11 @@ void GCodeViewer::render_time_estimate() const imgui.text(short_time(get_time_dhms(item.time.second - item.time.first))); break; } - case CustomGCode::ColorChange: + case Item::EType::ColorChange: { - append_color(last_color_id, item.extruder_id, item.color, offsets, item.time); - ++last_color_id; + append_color(item.color1, item.color2, offsets, item.time); break; } - default: { break; } } } }; @@ -1859,8 +1869,8 @@ void GCodeViewer::render_time_estimate() const { auto it = std::find_if(custom_gcode_per_print_z.begin(), custom_gcode_per_print_z.end(), [time_rec](const CustomGCode::Item& item) { return item.type == time_rec.first; }); if (it != custom_gcode_per_print_z.end()) { - items.push_back({ CustomGCode::ColorChange, it->extruder, last_color[it->extruder - 1], time_rec.second }); - items.push_back({ time_rec.first, it->extruder, last_color[it->extruder - 1], time_rec.second }); + items.push_back({ Item::EType::Print, it->extruder, Color(), Color(), time_rec.second }); + items.push_back({ Item::EType::Pause, it->extruder, Color(), Color(), time_rec.second }); custom_gcode_per_print_z.erase(it); } break; @@ -1869,13 +1879,14 @@ void GCodeViewer::render_time_estimate() const { auto it = std::find_if(custom_gcode_per_print_z.begin(), custom_gcode_per_print_z.end(), [time_rec](const CustomGCode::Item& item) { return item.type == time_rec.first; }); if (it != custom_gcode_per_print_z.end()) { - items.push_back({ time_rec.first, it->extruder, last_color[it->extruder - 1], time_rec.second }); + items.push_back({ Item::EType::Print, it->extruder, Color(), Color(), time_rec.second }); + items.push_back({ Item::EType::ColorChange, it->extruder, last_color[it->extruder - 1], decode_color(it->color), time_rec.second }); last_color[it->extruder - 1] = decode_color(it->color); last_extruder_id = it->extruder; custom_gcode_per_print_z.erase(it); } else - items.push_back({ time_rec.first, last_extruder_id, last_color[last_extruder_id - 1], time_rec.second }); + items.push_back({ Item::EType::Print, last_extruder_id, Color(), Color(), time_rec.second }); break; } From c5197f33502effe0bd19a257db6235ca8df41edd Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 14 Jul 2020 15:34:08 +0200 Subject: [PATCH 159/503] PhysicalPrinterDialog is completed --- src/libslic3r/Preset.cpp | 82 +++++++++++++-- src/libslic3r/Preset.hpp | 28 +++-- src/slic3r/GUI/PresetComboBoxes.cpp | 156 ++++++++++++++++++++++------ src/slic3r/GUI/PresetComboBoxes.hpp | 13 ++- 4 files changed, 230 insertions(+), 49 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index ec3e933384..c9f5bd0af0 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1345,6 +1345,11 @@ const Preset* PrinterPresetCollection::find_by_model_id(const std::string &model // *** PhysicalPrinter *** // ------------------------- +std::string PhysicalPrinter::separator() +{ + return " * "; +} + const std::vector& PhysicalPrinter::printer_options() { static std::vector s_opts; @@ -1370,9 +1375,9 @@ const std::string& PhysicalPrinter::get_preset_name() const return config.opt_string("preset_name"); } -const std::string& PhysicalPrinter::get_printer_model() const +const std::set& PhysicalPrinter::get_preset_names() const { - return config.opt_string("printer_model"); + return preset_names; } bool PhysicalPrinter::has_empty_config() const @@ -1384,20 +1389,62 @@ bool PhysicalPrinter::has_empty_config() const config.opt_string("password" ).empty(); } +void PhysicalPrinter::update_preset_names_in_config() +{ + if (!preset_names.empty()) { + std::string name; + for (auto el : preset_names) + name += el + ";"; + name.pop_back(); + config.set_key_value("preset_name", new ConfigOptionString(name)); + } +} + +void PhysicalPrinter::save(const std::string& file_name_from, const std::string& file_name_to) +{ + // rename the file + boost::nowide::rename(file_name_from.data(), file_name_to.data()); + this->file = file_name_to; + // save configuration + this->config.save(this->file); +} + void PhysicalPrinter::update_from_preset(const Preset& preset) { config.apply_only(preset.config, printer_options(), false); - // add preset name to the options list - config.set_key_value("preset_name", new ConfigOptionString(preset.name)); + // add preset names to the options list + auto ret = preset_names.emplace(preset.name); + update_preset_names_in_config(); + update_full_name(); } void PhysicalPrinter::update_from_config(const DynamicPrintConfig& new_config) { config.apply_only(new_config, printer_options(), false); + + std::string str = config.opt_string("preset_name"); + std::set values{}; + if (!str.empty()) { + boost::split(values, str, boost::is_any_of(";")); + for (const std::string& val : values) + preset_names.emplace(val); + } + preset_names = values; + update_full_name(); } +void PhysicalPrinter::reset_presets() +{ + return preset_names.clear(); +} + +bool PhysicalPrinter::add_preset(const std::string& preset_name) +{ + return preset_names.emplace(preset_name).second; +} + PhysicalPrinter::PhysicalPrinter(const std::string& name, const Preset& preset) : name(name) { @@ -1412,16 +1459,23 @@ void PhysicalPrinter::set_name(const std::string& name) void PhysicalPrinter::update_full_name() { - full_name = name + " * " + get_preset_name(); + full_name = name + separator() + get_preset_name(); } std::string PhysicalPrinter::get_short_name(std::string full_name) { - int pos = full_name.find_first_of(" * "); + int pos = full_name.find(separator()); boost::erase_tail(full_name, full_name.length() - pos); return full_name; } +std::string PhysicalPrinter::get_preset_name(std::string full_name) +{ + int pos = full_name.find(separator()); + boost::erase_head(full_name, pos + 2); + return full_name; +} + // ----------------------------------- // *** PhysicalPrinterCollection *** @@ -1497,15 +1551,18 @@ std::string PhysicalPrinterCollection::path_from_name(const std::string& new_nam return (boost::filesystem::path(m_dir_path) / file_name).make_preferred().string(); } -void PhysicalPrinterCollection::save_printer(const PhysicalPrinter& edited_printer) +void PhysicalPrinterCollection::save_printer(const PhysicalPrinter& edited_printer, const std::string& renamed_from) { + std::string name = renamed_from.empty() ? edited_printer.name : renamed_from; // 1) Find the printer with a new_name or create a new one, // initialize it with the edited config. - auto it = this->find_printer_internal(edited_printer.name); - if (it != m_printers.end() && it->name == edited_printer.name) { + auto it = this->find_printer_internal(name); + if (it != m_printers.end() && it->name == name) { // Printer with the same name found. // Overwriting an existing preset. it->config = std::move(edited_printer.config); + it->name = edited_printer.name; + it->preset_names = edited_printer.preset_names; it->full_name = edited_printer.full_name; } else { @@ -1518,7 +1575,12 @@ void PhysicalPrinterCollection::save_printer(const PhysicalPrinter& edited_print PhysicalPrinter& printer = *it; if (printer.file.empty()) printer.file = this->path_from_name(printer.name); - printer.save(); + + if (printer.file == this->path_from_name(printer.name)) + printer.save(); + else + // if printer was renamed, we should rename a file and than save the config + printer.save(printer.file, this->path_from_name(printer.name)); // update idx_selected m_idx_selected = it - m_printers.begin(); diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index a076a9a217..d583bed20d 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -549,20 +549,33 @@ public: std::string file; // Configuration data, loaded from a file, or set from the defaults. DynamicPrintConfig config; + // set of presets used with this physical printer + std::set preset_names; + + static std::string separator(); // Has this profile been loaded? bool loaded = false; - static const std::vector& printer_options(); - const std::string& get_preset_name() const; - const std::string& get_printer_model() const; + static const std::vector& printer_options(); + const std::string& get_preset_name() const; + + const std::set& get_preset_names() const; + bool has_empty_config() const; + void update_preset_names_in_config(); void save() { this->config.save(this->file); } - void save_to(const std::string& file_name) const { this->config.save(file_name); } + void save(const std::string& file_name_from, const std::string& file_name_to); + void update_from_preset(const Preset& preset); void update_from_config(const DynamicPrintConfig &new_config); + // add preset to the preset_names + // return false, if preset with this name is already exist in the set + bool add_preset(const std::string& preset_name); + void reset_presets(); + // Return a printer technology, return ptFFF if the printer technology is not set. static PrinterTechnology printer_technology(const DynamicPrintConfig& cfg) { auto* opt = cfg.option>("printer_technology"); @@ -578,6 +591,9 @@ public: // get printer name from the full name uncluded preset name static std::string get_short_name(std::string full_name); + // get preset name from the full name uncluded printer name + static std::string get_preset_name(std::string full_name); + protected: friend class PhysicalPrinterCollection; }; @@ -615,7 +631,7 @@ public: // Save the printer under a new name. If the name is different from the old one, // a new printer is stored into the list of printers. // New printer is activated. - void save_printer(const PhysicalPrinter& printer); + void save_printer(const PhysicalPrinter& printer, const std::string& renamed_from); // Delete the current preset, activate the first visible preset. // returns true if the preset was deleted successfully. @@ -633,8 +649,6 @@ public: // Returns the full name of the selected preset, or an empty string if no preset is selected. std::string get_selected_full_printer_name() const { return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_printer().full_name; } // Returns the printer model of the selected preset, or an empty string if no preset is selected. - std::string get_selected_printer_model() const { return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_printer().get_printer_model(); } - // Returns the printer model of the selected preset, or an empty string if no preset is selected. std::string get_selected_printer_preset_name() const { return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_printer().get_preset_name(); } // Returns the config of the selected preset, or nullptr if no preset is selected. DynamicPrintConfig* get_selected_printer_config() { return (m_idx_selected == size_t(-1)) ? nullptr : &(this->get_selected_printer().config); } diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index dc5365a139..1473bf6dab 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -41,6 +41,8 @@ static const std::pair THUMBNAIL_SIZE_3MF = { 256, 2 namespace Slic3r { namespace GUI { +#define BORDER_W 10 + // --------------------------------- // *** PresetComboBox *** // --------------------------------- @@ -762,8 +764,8 @@ void TabPresetComboBox::update() // check this value just for printer presets, when physical printer is selected if (m_type == Preset::TYPE_PRINTER && m_preset_bundle->physical_printers.has_selection()) { is_enabled = m_enable_all ? true : - preset.name == m_preset_bundle->physical_printers.get_selected_printer_preset_name() || - preset.config.opt_string("printer_model") == m_preset_bundle->physical_printers.get_selected_printer_model(); + preset.name == m_preset_bundle->physical_printers.get_selected_printer_preset_name()/* || + preset.config.opt_string("printer_model") == m_preset_bundle->physical_printers.get_selected_printer_model()*/; } std::string bitmap_key = "tab"; @@ -888,7 +890,7 @@ void TabPresetComboBox::update_dirty() // PresetForPrinter //------------------------------------------ -PresetForPrinter::PresetForPrinter(PhysicalPrinterDialog* parent, bool is_all_enable) : +PresetForPrinter::PresetForPrinter(PhysicalPrinterDialog* parent, const std::string& preset_name) : m_parent(parent) { m_sizer = new wxBoxSizer(wxVERTICAL); @@ -899,8 +901,7 @@ PresetForPrinter::PresetForPrinter(PhysicalPrinterDialog* parent, bool is_all_en m_delete_preset_btn->Bind(wxEVT_BUTTON, &PresetForPrinter::DeletePreset, this); m_presets_list = new TabPresetComboBox(parent, Preset::TYPE_PRINTER); - - if (is_all_enable) + if (preset_name.empty()) m_presets_list->set_enable_all(); m_presets_list->set_selection_changed_function([this](int selection) { @@ -921,22 +922,37 @@ PresetForPrinter::PresetForPrinter(PhysicalPrinterDialog* parent, bool is_all_en update_full_printer_name(); }); + m_presets_list->update(); + m_presets_list->SetStringSelection(from_u8(preset_name)); + + m_info_line = new wxStaticText(parent, wxID_ANY, _L("This printer will be shown in the presets list as") + ":"); m_full_printer_name = new wxStaticText(parent, wxID_ANY, ""); + m_full_printer_name->SetFont(wxGetApp().bold_font()); - m_presets_list->update(); + wxBoxSizer* preset_sizer = new wxBoxSizer(wxHORIZONTAL); + preset_sizer->Add(m_presets_list , 1, wxEXPAND); + preset_sizer->Add(m_delete_preset_btn , 0, wxEXPAND | wxLEFT, BORDER_W); + + wxBoxSizer* name_sizer = new wxBoxSizer(wxHORIZONTAL); + name_sizer->Add(m_info_line, 0, wxEXPAND); + name_sizer->Add(m_full_printer_name, 0, wxEXPAND | wxLEFT, BORDER_W); + + m_sizer->Add(preset_sizer , 0, wxEXPAND); + m_sizer->Add(name_sizer, 0, wxEXPAND); } PresetForPrinter::~PresetForPrinter() { m_presets_list->Destroy(); m_delete_preset_btn->Destroy(); + m_info_line->Destroy(); m_full_printer_name->Destroy(); } void PresetForPrinter::DeletePreset(wxEvent& event) { - + m_parent->DeletePreset(this); } void PresetForPrinter::update_full_printer_name() @@ -947,6 +963,22 @@ void PresetForPrinter::update_full_printer_name() m_full_printer_name->SetLabelText(printer_name + " * " + preset_name); } +std::string PresetForPrinter::get_preset_name() +{ + return into_u8(m_presets_list->GetString(m_presets_list->GetSelection())); +} + +void PresetForPrinter::DisableDeleteBtn() +{ + m_delete_preset_btn->Enable(false); +} + +void PresetForPrinter::EnableDeleteBtn() +{ + if (!m_delete_preset_btn->IsEnabled()) + m_delete_preset_btn->Enable(); +} + void PresetForPrinter::msw_rescale() { m_presets_list->msw_rescale(); @@ -965,22 +997,17 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) SetFont(wxGetApp().normal_font()); SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - int border = 10; - m_info_string = _("This printer name will be shown in the presets list") + ":\n"; - - TabPresetComboBox* printer_presets = new TabPresetComboBox(this, Preset::TYPE_PRINTER); - if (printer_name.IsEmpty()) { printer_name = _L("My Printer Device"); // if printer_name is empty it means that new printer is created, so enable all items in the preset list - m_presets.emplace_back(new PresetForPrinter(this, true)); + m_presets.emplace_back(new PresetForPrinter(this, "")); } else { std::string full_name = into_u8(printer_name); printer_name = from_u8(PhysicalPrinter::get_short_name(full_name)); } - wxStaticText* label_top = new wxStaticText(this, wxID_ANY, _("Descriptive name for the printer device") + ":"); + wxStaticText* label_top = new wxStaticText(this, wxID_ANY, _L("Descriptive name for the printer device") + ":"); m_add_preset_btn = new ScalableButton(this, wxID_ANY, "add_copies", "", wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT); m_add_preset_btn->SetFont(wxGetApp().normal_font()); @@ -990,17 +1017,26 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) m_printer_name = new wxTextCtrl(this, wxID_ANY, printer_name, wxDefaultPosition, wxDefaultSize); m_printer_name->Bind(wxEVT_TEXT, [this](wxEvent&) { this->update_full_printer_names(); }); - update_full_printer_names(); - PhysicalPrinterCollection& printers = wxGetApp().preset_bundle->physical_printers; PhysicalPrinter* printer = printers.find_printer(into_u8(printer_name)); if (!printer) { const Preset& preset = wxGetApp().preset_bundle->printers.get_edited_preset(); printer = new PhysicalPrinter(into_u8(printer_name), preset); } + else + { + const std::set& preset_names = printer->get_preset_names(); + for (const std::string& preset_name : preset_names) + m_presets.emplace_back(new PresetForPrinter(this, preset_name)); + } assert(printer); m_printer = *printer; + if (m_presets.size() == 1) + m_presets.front()->DisableDeleteBtn(); + + update_full_printer_names(); + m_config = &m_printer.config; m_optgroup = new ConfigOptionsGroup(this, _L("Print Host upload"), m_config); @@ -1013,21 +1049,19 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) wxBoxSizer* nameSizer = new wxBoxSizer(wxHORIZONTAL); nameSizer->Add(m_printer_name, 1, wxEXPAND); - nameSizer->Add(m_add_preset_btn, 0, wxEXPAND | wxLEFT, border); + nameSizer->Add(m_add_preset_btn, 0, wxEXPAND | wxLEFT, BORDER_W); + + m_presets_sizer = new wxBoxSizer(wxVERTICAL); + for (PresetForPrinter* preset : m_presets) + m_presets_sizer->Add(preset->sizer(), 1, wxEXPAND | wxTOP, BORDER_W); wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - topSizer->Add(label_top , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); - topSizer->Add(nameSizer , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); - for (PresetForPrinter* preset : m_presets) - topSizer->Add(preset->sizer(), 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);; - /* - topSizer->Add(m_printer_presets , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); - topSizer->Add(label_bottom , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); - topSizer->Add(m_full_printer_name , 0, wxEXPAND | wxLEFT | wxRIGHT, border); - */ - topSizer->Add(m_optgroup->sizer , 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); - topSizer->Add(btns , 0, wxEXPAND | wxALL, border); + topSizer->Add(label_top , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W); + topSizer->Add(nameSizer , 0, wxEXPAND | wxLEFT | wxRIGHT, BORDER_W); + topSizer->Add(m_presets_sizer , 0, wxEXPAND | wxLEFT | wxRIGHT, BORDER_W); + topSizer->Add(m_optgroup->sizer , 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W); + topSizer->Add(btns , 0, wxEXPAND | wxALL, BORDER_W); SetSizer(topSizer); topSizer->SetSizeHints(this); @@ -1203,7 +1237,7 @@ void PhysicalPrinterDialog::update() wxString PhysicalPrinterDialog::get_printer_name() { - return m_info_string + m_printer_name->GetValue() + "\t"; + return m_printer_name->GetValue(); } void PhysicalPrinterDialog::update_full_printer_names() @@ -1260,11 +1294,39 @@ void PhysicalPrinterDialog::OnOK(wxEvent& event) printers.delete_printer(into_u8(printer_name)); } + std::set repeat_presets; + m_printer.reset_presets(); + for (PresetForPrinter* preset : m_presets) { + if (!m_printer.add_preset(preset->get_preset_name())) + repeat_presets.emplace(preset->get_preset_name()); + } + // update preset_names in printer config + m_printer.update_preset_names_in_config(); + + if (!repeat_presets.empty()) + { + wxString repeatable_presets = "\n"; + for (const std::string& preset_name : repeat_presets) + repeatable_presets += " " + from_u8(preset_name) + "\n"; + repeatable_presets += "\n"; + + wxString msg_text = from_u8((boost::format(_u8L("Next printer preset(s) is(are) duplicated:%1%" + "It(they) will be added just once for the printer \"%2%\".")) % repeatable_presets % printer_name).str()); + wxMessageDialog dialog(nullptr, msg_text, _L("Infornation"), wxICON_INFORMATION | wxOK); + dialog.ShowModal(); + } + + std::string renamed_from; + // temporary save previous printer name if it was edited + if (m_printer.name != _u8L("My Printer Device") && + m_printer.name != into_u8(printer_name)) + renamed_from = m_printer.name; + //update printer name, if it was changed m_printer.set_name(into_u8(printer_name)); // save new physical printer - printers.save_printer(m_printer); + printers.save_printer(m_printer, renamed_from); // update selection on the tab only when it was changed /* @@ -1282,7 +1344,41 @@ void PhysicalPrinterDialog::OnOK(wxEvent& event) void PhysicalPrinterDialog::AddPreset(wxEvent& event) { + // if printer_name is empty it means that new printer is created, so enable all items in the preset list + m_presets.emplace_back(new PresetForPrinter(this, "")); + // enable DELETE button for the first preset, if was disabled + m_presets.front()->EnableDeleteBtn(); + m_presets_sizer->Add(m_presets.back()->sizer(), 1, wxEXPAND | wxTOP, BORDER_W); + update_full_printer_names(); + + this->Fit(); +} + +void PhysicalPrinterDialog::DeletePreset(PresetForPrinter* preset_for_printer) +{ + if (m_presets.size() == 1) { + wxString msg_text = _L("It's not possible to delete last related preset for the printer."); + wxMessageDialog dialog(nullptr, msg_text, _L("Infornation"), wxICON_INFORMATION | wxOK); + dialog.ShowModal(); + return; + } + + assert(preset_for_printer); + auto it = std::find(m_presets.begin(), m_presets.end(), preset_for_printer); + if (it == m_presets.end()) + return; + + const int remove_id = it - m_presets.begin(); + m_presets_sizer->Remove(remove_id); + delete preset_for_printer; + m_presets.erase(it); + + if (m_presets.size() == 1) + m_presets.front()->DisableDeleteBtn(); + + this->Layout(); + this->Fit(); } diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index 3d5ae298f0..c818b6b911 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -15,6 +15,7 @@ class wxString; class wxTextCtrl; class wxStaticText; class ScalableButton; +class wxBoxSizer; namespace Slic3r { @@ -169,6 +170,7 @@ public: //------------------------------------------ // PresetForPrinter //------------------------------------------ +static std::string g_info_string = " (modified)"; class PhysicalPrinterDialog; class PresetForPrinter { @@ -176,6 +178,7 @@ class PresetForPrinter TabPresetComboBox* m_presets_list { nullptr }; ScalableButton* m_delete_preset_btn { nullptr }; + wxStaticText* m_info_line { nullptr }; wxStaticText* m_full_printer_name { nullptr }; wxBoxSizer* m_sizer { nullptr }; @@ -183,11 +186,14 @@ class PresetForPrinter void DeletePreset(wxEvent& event); public: - PresetForPrinter(PhysicalPrinterDialog* parent, bool is_all_enable); + PresetForPrinter(PhysicalPrinterDialog* parent, const std::string& preset_name); ~PresetForPrinter(); wxBoxSizer* sizer() { return m_sizer; } void update_full_printer_name(); + std::string get_preset_name(); + void DisableDeleteBtn(); + void EnableDeleteBtn(); void msw_rescale(); void on_sys_color_changed() {}; @@ -203,7 +209,6 @@ class PhysicalPrinterDialog : public DPIDialog { PhysicalPrinter m_printer; DynamicPrintConfig* m_config { nullptr }; - wxString m_info_string; wxTextCtrl* m_printer_name { nullptr }; std::vector m_presets; @@ -215,6 +220,8 @@ class PhysicalPrinterDialog : public DPIDialog ScalableButton* m_printhost_test_btn {nullptr}; ScalableButton* m_printhost_cafile_browse_btn {nullptr}; + wxBoxSizer* m_presets_sizer {nullptr}; + void build_printhost_settings(ConfigOptionsGroup* optgroup); void OnOK(wxEvent& event); void AddPreset(wxEvent& event); @@ -228,6 +235,8 @@ public: void update_full_printer_names(); PhysicalPrinter* get_printer() {return &m_printer; } + void DeletePreset(PresetForPrinter* preset_for_printer); + protected: void on_dpi_changed(const wxRect& suggested_rect) override; void on_sys_color_changed() override {}; From 3a88e698969bdcdf5cd04225bedff96215cf5fa6 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 16 Jul 2020 11:09:21 +0200 Subject: [PATCH 160/503] ENABLE_GCODE_VIEWER -> Integration of time estimator into GCodeProcessor --- src/libslic3r/GCode.cpp | 1246 ++++++++++++------------ src/libslic3r/GCode/GCodeProcessor.cpp | 821 +++++++++++++++- src/libslic3r/GCode/GCodeProcessor.hpp | 193 +++- src/libslic3r/GCodeTimeEstimator.cpp | 16 - src/libslic3r/GCodeTimeEstimator.hpp | 4 - src/libslic3r/Print.cpp | 18 +- src/libslic3r/Print.hpp | 12 +- src/slic3r/GUI/GCodeViewer.cpp | 3 + src/slic3r/GUI/Plater.cpp | 22 +- 9 files changed, 1631 insertions(+), 704 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 7bfb73aa37..47448954c8 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -48,662 +48,684 @@ using namespace std::literals::string_view_literals; namespace Slic3r { -//! macro used to mark string used at localization, -//! return same string + //! macro used to mark string used at localization, + //! return same string #define L(s) (s) #define _(s) Slic3r::I18N::translate(s) // Only add a newline in case the current G-code does not end with a newline. -static inline void check_add_eol(std::string &gcode) -{ - if (! gcode.empty() && gcode.back() != '\n') - gcode += '\n'; -} - - -// Return true if tch_prefix is found in custom_gcode -static bool custom_gcode_changes_tool(const std::string& custom_gcode, const std::string& tch_prefix, unsigned next_extruder) -{ - bool ok = false; - size_t from_pos = 0; - size_t pos = 0; - while ((pos = custom_gcode.find(tch_prefix, from_pos)) != std::string::npos) { - if (pos+1 == custom_gcode.size()) - break; - from_pos = pos+1; - // only whitespace is allowed before the command - while (--pos < custom_gcode.size() && custom_gcode[pos] != '\n') { - if (! std::isspace(custom_gcode[pos])) - goto NEXT; - } - { - // we should also check that the extruder changes to what was expected - std::istringstream ss(custom_gcode.substr(from_pos, std::string::npos)); - unsigned num = 0; - if (ss >> num) - ok = (num == next_extruder); - } -NEXT: ; + static inline void check_add_eol(std::string& gcode) + { + if (!gcode.empty() && gcode.back() != '\n') + gcode += '\n'; } - return ok; -} -void AvoidCrossingPerimeters::init_external_mp(const Print &print) -{ - m_external_mp = Slic3r::make_unique(union_ex(this->collect_contours_all_layers(print.objects()))); -} -// Plan a travel move while minimizing the number of perimeter crossings. -// point is in unscaled coordinates, in the coordinate system of the current active object -// (set by gcodegen.set_origin()). -Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &point) -{ - // If use_external, then perform the path planning in the world coordinate system (correcting for the gcodegen offset). - // Otherwise perform the path planning in the coordinate system of the active object. - bool use_external = this->use_external_mp || this->use_external_mp_once; - Point scaled_origin = use_external ? Point::new_scale(gcodegen.origin()(0), gcodegen.origin()(1)) : Point(0, 0); - Polyline result = (use_external ? m_external_mp.get() : m_layer_mp.get())-> - shortest_path(gcodegen.last_pos() + scaled_origin, point + scaled_origin); - if (use_external) - result.translate(- scaled_origin); - return result; -} - -// Collect outer contours of all objects over all layers. -// Discard objects only containing thin walls (offset would fail on an empty polygon). -// Used by avoid crossing perimeters feature. -Polygons AvoidCrossingPerimeters::collect_contours_all_layers(const PrintObjectPtrs& objects) -{ - Polygons islands; - for (const PrintObject *object : objects) { - // Reducing all the object slices into the Z projection in a logarithimc fashion. - // First reduce to half the number of layers. - std::vector polygons_per_layer((object->layers().size() + 1) / 2); - tbb::parallel_for(tbb::blocked_range(0, object->layers().size() / 2), - [&object, &polygons_per_layer](const tbb::blocked_range &range) { - for (size_t i = range.begin(); i < range.end(); ++ i) { - const Layer* layer1 = object->layers()[i * 2]; - const Layer* layer2 = object->layers()[i * 2 + 1]; - Polygons polys; - polys.reserve(layer1->lslices.size() + layer2->lslices.size()); - for (const ExPolygon &expoly : layer1->lslices) - //FIXME no holes? - polys.emplace_back(expoly.contour); - for (const ExPolygon &expoly : layer2->lslices) - //FIXME no holes? - polys.emplace_back(expoly.contour); - polygons_per_layer[i] = union_(polys); - } - }); - if (object->layers().size() & 1) { - const Layer *layer = object->layers().back(); - Polygons polys; - polys.reserve(layer->lslices.size()); - for (const ExPolygon &expoly : layer->lslices) - //FIXME no holes? - polys.emplace_back(expoly.contour); - polygons_per_layer.back() = union_(polys); - } - // Now reduce down to a single layer. - size_t cnt = polygons_per_layer.size(); - while (cnt > 1) { - tbb::parallel_for(tbb::blocked_range(0, cnt / 2), - [&polygons_per_layer](const tbb::blocked_range &range) { - for (size_t i = range.begin(); i < range.end(); ++ i) { - Polygons polys; - polys.reserve(polygons_per_layer[i * 2].size() + polygons_per_layer[i * 2 + 1].size()); - polygons_append(polys, polygons_per_layer[i * 2]); - polygons_append(polys, polygons_per_layer[i * 2 + 1]); - polygons_per_layer[i * 2] = union_(polys); - } - }); - for (size_t i = 0; i < cnt / 2; ++ i) - polygons_per_layer[i] = std::move(polygons_per_layer[i * 2]); - if (cnt & 1) - polygons_per_layer[cnt / 2] = std::move(polygons_per_layer[cnt - 1]); - cnt = (cnt + 1) / 2; - } - // And collect copies of the objects. - for (const PrintInstance &instance : object->instances()) { - // All the layers were reduced to the 1st item of polygons_per_layer. - size_t i = islands.size(); - polygons_append(islands, polygons_per_layer.front()); - for (; i < islands.size(); ++ i) - islands[i].translate(instance.shift); - } - } - return islands; -} - -std::string OozePrevention::pre_toolchange(GCode &gcodegen) -{ - std::string gcode; - - // move to the nearest standby point - if (!this->standby_points.empty()) { - // get current position in print coordinates - Vec3d writer_pos = gcodegen.writer().get_position(); - Point pos = Point::new_scale(writer_pos(0), writer_pos(1)); - - // find standby point - Point standby_point; - pos.nearest_point(this->standby_points, &standby_point); - - /* We don't call gcodegen.travel_to() because we don't need retraction (it was already - triggered by the caller) nor avoid_crossing_perimeters and also because the coordinates - of the destination point must not be transformed by origin nor current extruder offset. */ - gcode += gcodegen.writer().travel_to_xy(unscale(standby_point), - "move to standby position"); - } - - if (gcodegen.config().standby_temperature_delta.value != 0) { - // we assume that heating is always slower than cooling, so no need to block - gcode += gcodegen.writer().set_temperature - (this->_get_temp(gcodegen) + gcodegen.config().standby_temperature_delta.value, false, gcodegen.writer().extruder()->id()); - } - - return gcode; -} - -std::string OozePrevention::post_toolchange(GCode &gcodegen) -{ - return (gcodegen.config().standby_temperature_delta.value != 0) ? - gcodegen.writer().set_temperature(this->_get_temp(gcodegen), true, gcodegen.writer().extruder()->id()) : - std::string(); -} - -int -OozePrevention::_get_temp(GCode &gcodegen) -{ - return (gcodegen.layer() != NULL && gcodegen.layer()->id() == 0) - ? gcodegen.config().first_layer_temperature.get_at(gcodegen.writer().extruder()->id()) - : gcodegen.config().temperature.get_at(gcodegen.writer().extruder()->id()); -} - -std::string Wipe::wipe(GCode &gcodegen, bool toolchange) -{ - std::string gcode; - - /* Reduce feedrate a bit; travel speed is often too high to move on existing material. - Too fast = ripping of existing material; too slow = short wipe path, thus more blob. */ - double wipe_speed = gcodegen.writer().config.travel_speed.value * 0.8; - - // get the retraction length - double length = toolchange - ? gcodegen.writer().extruder()->retract_length_toolchange() - : gcodegen.writer().extruder()->retract_length(); - // Shorten the retraction length by the amount already retracted before wipe. - length *= (1. - gcodegen.writer().extruder()->retract_before_wipe()); - - if (length > 0) { - /* Calculate how long we need to travel in order to consume the required - amount of retraction. In other words, how far do we move in XY at wipe_speed - for the time needed to consume retract_length at retract_speed? */ - double wipe_dist = scale_(length / gcodegen.writer().extruder()->retract_speed() * wipe_speed); - - /* Take the stored wipe path and replace first point with the current actual position - (they might be different, for example, in case of loop clipping). */ - Polyline wipe_path; - wipe_path.append(gcodegen.last_pos()); - wipe_path.append( - this->path.points.begin() + 1, - this->path.points.end() - ); - - wipe_path.clip_end(wipe_path.length() - wipe_dist); - - // subdivide the retraction in segments - if (! wipe_path.empty()) { - for (const Line &line : wipe_path.lines()) { - double segment_length = line.length(); - /* Reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one - due to rounding (TODO: test and/or better math for this) */ - double dE = length * (segment_length / wipe_dist) * 0.95; - //FIXME one shall not generate the unnecessary G1 Fxxx commands, here wipe_speed is a constant inside this cycle. - // Is it here for the cooling markers? Or should it be outside of the cycle? - gcode += gcodegen.writer().set_speed(wipe_speed*60, "", gcodegen.enable_cooling_markers() ? ";_WIPE" : ""); - gcode += gcodegen.writer().extrude_to_xy( - gcodegen.point_to_gcode(line.b), - -dE, - "wipe and retract" - ); + // Return true if tch_prefix is found in custom_gcode + static bool custom_gcode_changes_tool(const std::string& custom_gcode, const std::string& tch_prefix, unsigned next_extruder) + { + bool ok = false; + size_t from_pos = 0; + size_t pos = 0; + while ((pos = custom_gcode.find(tch_prefix, from_pos)) != std::string::npos) { + if (pos + 1 == custom_gcode.size()) + break; + from_pos = pos + 1; + // only whitespace is allowed before the command + while (--pos < custom_gcode.size() && custom_gcode[pos] != '\n') { + if (!std::isspace(custom_gcode[pos])) + goto NEXT; } - gcodegen.set_last_pos(wipe_path.points.back()); + { + // we should also check that the extruder changes to what was expected + std::istringstream ss(custom_gcode.substr(from_pos, std::string::npos)); + unsigned num = 0; + if (ss >> num) + ok = (num == next_extruder); + } + NEXT:; } - - // prevent wiping again on same path - this->reset_path(); - } - - return gcode; -} - -static inline Point wipe_tower_point_to_object_point(GCode &gcodegen, const Vec2f &wipe_tower_pt) -{ - return Point(scale_(wipe_tower_pt.x() - gcodegen.origin()(0)), scale_(wipe_tower_pt.y() - gcodegen.origin()(1))); -} - -std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z) const -{ - if (new_extruder_id != -1 && new_extruder_id != tcr.new_tool) - throw std::invalid_argument("Error: WipeTowerIntegration::append_tcr was asked to do a toolchange it didn't expect."); - - std::string gcode; - - // Toolchangeresult.gcode assumes the wipe tower corner is at the origin (except for priming lines) - // We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position - float alpha = m_wipe_tower_rotation/180.f * float(M_PI); - Vec2f start_pos = tcr.start_pos; - Vec2f end_pos = tcr.end_pos; - if (!tcr.priming) { - start_pos = Eigen::Rotation2Df(alpha) * start_pos; - start_pos += m_wipe_tower_pos; - end_pos = Eigen::Rotation2Df(alpha) * end_pos; - end_pos += m_wipe_tower_pos; + return ok; } - Vec2f wipe_tower_offset = tcr.priming ? Vec2f::Zero() : m_wipe_tower_pos; - float wipe_tower_rotation = tcr.priming ? 0.f : alpha; - - std::string tcr_rotated_gcode = post_process_wipe_tower_moves(tcr, wipe_tower_offset, wipe_tower_rotation); - - if (!tcr.priming) { - // Move over the wipe tower. - // Retract for a tool change, using the toolchange retract value and setting the priming extra length. - gcode += gcodegen.retract(true); - gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true; - gcode += gcodegen.travel_to( - wipe_tower_point_to_object_point(gcodegen, start_pos), - erMixed, - "Travel to a Wipe Tower"); - gcode += gcodegen.unretract(); + void AvoidCrossingPerimeters::init_external_mp(const Print& print) + { + m_external_mp = Slic3r::make_unique(union_ex(this->collect_contours_all_layers(print.objects()))); } - double current_z = gcodegen.writer().get_position().z(); - if (z == -1.) // in case no specific z was provided, print at current_z pos - z = current_z; - if (! is_approx(z, current_z)) { - gcode += gcodegen.writer().retract(); - gcode += gcodegen.writer().travel_to_z(z, "Travel down to the last wipe tower layer."); - gcode += gcodegen.writer().unretract(); + // Plan a travel move while minimizing the number of perimeter crossings. + // point is in unscaled coordinates, in the coordinate system of the current active object + // (set by gcodegen.set_origin()). + Polyline AvoidCrossingPerimeters::travel_to(const GCode& gcodegen, const Point& point) + { + // If use_external, then perform the path planning in the world coordinate system (correcting for the gcodegen offset). + // Otherwise perform the path planning in the coordinate system of the active object. + bool use_external = this->use_external_mp || this->use_external_mp_once; + Point scaled_origin = use_external ? Point::new_scale(gcodegen.origin()(0), gcodegen.origin()(1)) : Point(0, 0); + Polyline result = (use_external ? m_external_mp.get() : m_layer_mp.get())-> + shortest_path(gcodegen.last_pos() + scaled_origin, point + scaled_origin); + if (use_external) + result.translate(-scaled_origin); + return result; } - - // Process the end filament gcode. - std::string end_filament_gcode_str; - if (gcodegen.writer().extruder() != nullptr) { - // Process the custom end_filament_gcode in case of single_extruder_multi_material. - unsigned int old_extruder_id = gcodegen.writer().extruder()->id(); - const std::string &end_filament_gcode = gcodegen.config().end_filament_gcode.get_at(old_extruder_id); - if (gcodegen.writer().extruder() != nullptr && ! end_filament_gcode.empty()) { - end_filament_gcode_str = gcodegen.placeholder_parser_process("end_filament_gcode", end_filament_gcode, old_extruder_id); - check_add_eol(end_filament_gcode_str); + // Collect outer contours of all objects over all layers. + // Discard objects only containing thin walls (offset would fail on an empty polygon). + // Used by avoid crossing perimeters feature. + Polygons AvoidCrossingPerimeters::collect_contours_all_layers(const PrintObjectPtrs& objects) + { + Polygons islands; + for (const PrintObject* object : objects) { + // Reducing all the object slices into the Z projection in a logarithimc fashion. + // First reduce to half the number of layers. + std::vector polygons_per_layer((object->layers().size() + 1) / 2); + tbb::parallel_for(tbb::blocked_range(0, object->layers().size() / 2), + [&object, &polygons_per_layer](const tbb::blocked_range& range) { + for (size_t i = range.begin(); i < range.end(); ++i) { + const Layer* layer1 = object->layers()[i * 2]; + const Layer* layer2 = object->layers()[i * 2 + 1]; + Polygons polys; + polys.reserve(layer1->lslices.size() + layer2->lslices.size()); + for (const ExPolygon& expoly : layer1->lslices) + //FIXME no holes? + polys.emplace_back(expoly.contour); + for (const ExPolygon& expoly : layer2->lslices) + //FIXME no holes? + polys.emplace_back(expoly.contour); + polygons_per_layer[i] = union_(polys); + } + }); + if (object->layers().size() & 1) { + const Layer* layer = object->layers().back(); + Polygons polys; + polys.reserve(layer->lslices.size()); + for (const ExPolygon& expoly : layer->lslices) + //FIXME no holes? + polys.emplace_back(expoly.contour); + polygons_per_layer.back() = union_(polys); + } + // Now reduce down to a single layer. + size_t cnt = polygons_per_layer.size(); + while (cnt > 1) { + tbb::parallel_for(tbb::blocked_range(0, cnt / 2), + [&polygons_per_layer](const tbb::blocked_range& range) { + for (size_t i = range.begin(); i < range.end(); ++i) { + Polygons polys; + polys.reserve(polygons_per_layer[i * 2].size() + polygons_per_layer[i * 2 + 1].size()); + polygons_append(polys, polygons_per_layer[i * 2]); + polygons_append(polys, polygons_per_layer[i * 2 + 1]); + polygons_per_layer[i * 2] = union_(polys); + } + }); + for (size_t i = 0; i < cnt / 2; ++i) + polygons_per_layer[i] = std::move(polygons_per_layer[i * 2]); + if (cnt & 1) + polygons_per_layer[cnt / 2] = std::move(polygons_per_layer[cnt - 1]); + cnt = (cnt + 1) / 2; + } + // And collect copies of the objects. + for (const PrintInstance& instance : object->instances()) { + // All the layers were reduced to the 1st item of polygons_per_layer. + size_t i = islands.size(); + polygons_append(islands, polygons_per_layer.front()); + for (; i < islands.size(); ++i) + islands[i].translate(instance.shift); + } } + return islands; } - // Process the custom toolchange_gcode. If it is empty, provide a simple Tn command to change the filament. - // Otherwise, leave control to the user completely. - std::string toolchange_gcode_str; - if (true /*gcodegen.writer().extruder() != nullptr*/) { - const std::string& toolchange_gcode = gcodegen.config().toolchange_gcode.value; - if (!toolchange_gcode.empty()) { + std::string OozePrevention::pre_toolchange(GCode& gcodegen) + { + std::string gcode; + + // move to the nearest standby point + if (!this->standby_points.empty()) { + // get current position in print coordinates + Vec3d writer_pos = gcodegen.writer().get_position(); + Point pos = Point::new_scale(writer_pos(0), writer_pos(1)); + + // find standby point + Point standby_point; + pos.nearest_point(this->standby_points, &standby_point); + + /* We don't call gcodegen.travel_to() because we don't need retraction (it was already + triggered by the caller) nor avoid_crossing_perimeters and also because the coordinates + of the destination point must not be transformed by origin nor current extruder offset. */ + gcode += gcodegen.writer().travel_to_xy(unscale(standby_point), + "move to standby position"); + } + + if (gcodegen.config().standby_temperature_delta.value != 0) { + // we assume that heating is always slower than cooling, so no need to block + gcode += gcodegen.writer().set_temperature + (this->_get_temp(gcodegen) + gcodegen.config().standby_temperature_delta.value, false, gcodegen.writer().extruder()->id()); + } + + return gcode; + } + + std::string OozePrevention::post_toolchange(GCode& gcodegen) + { + return (gcodegen.config().standby_temperature_delta.value != 0) ? + gcodegen.writer().set_temperature(this->_get_temp(gcodegen), true, gcodegen.writer().extruder()->id()) : + std::string(); + } + + int + OozePrevention::_get_temp(GCode& gcodegen) + { + return (gcodegen.layer() != NULL && gcodegen.layer()->id() == 0) + ? gcodegen.config().first_layer_temperature.get_at(gcodegen.writer().extruder()->id()) + : gcodegen.config().temperature.get_at(gcodegen.writer().extruder()->id()); + } + + std::string Wipe::wipe(GCode& gcodegen, bool toolchange) + { + std::string gcode; + + /* Reduce feedrate a bit; travel speed is often too high to move on existing material. + Too fast = ripping of existing material; too slow = short wipe path, thus more blob. */ + double wipe_speed = gcodegen.writer().config.travel_speed.value * 0.8; + + // get the retraction length + double length = toolchange + ? gcodegen.writer().extruder()->retract_length_toolchange() + : gcodegen.writer().extruder()->retract_length(); + // Shorten the retraction length by the amount already retracted before wipe. + length *= (1. - gcodegen.writer().extruder()->retract_before_wipe()); + + if (length > 0) { + /* Calculate how long we need to travel in order to consume the required + amount of retraction. In other words, how far do we move in XY at wipe_speed + for the time needed to consume retract_length at retract_speed? */ + double wipe_dist = scale_(length / gcodegen.writer().extruder()->retract_speed() * wipe_speed); + + /* Take the stored wipe path and replace first point with the current actual position + (they might be different, for example, in case of loop clipping). */ + Polyline wipe_path; + wipe_path.append(gcodegen.last_pos()); + wipe_path.append( + this->path.points.begin() + 1, + this->path.points.end() + ); + + wipe_path.clip_end(wipe_path.length() - wipe_dist); + + // subdivide the retraction in segments + if (!wipe_path.empty()) { + for (const Line& line : wipe_path.lines()) { + double segment_length = line.length(); + /* Reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one + due to rounding (TODO: test and/or better math for this) */ + double dE = length * (segment_length / wipe_dist) * 0.95; + //FIXME one shall not generate the unnecessary G1 Fxxx commands, here wipe_speed is a constant inside this cycle. + // Is it here for the cooling markers? Or should it be outside of the cycle? + gcode += gcodegen.writer().set_speed(wipe_speed * 60, "", gcodegen.enable_cooling_markers() ? ";_WIPE" : ""); + gcode += gcodegen.writer().extrude_to_xy( + gcodegen.point_to_gcode(line.b), + -dE, + "wipe and retract" + ); + } + gcodegen.set_last_pos(wipe_path.points.back()); + } + + // prevent wiping again on same path + this->reset_path(); + } + + return gcode; + } + + static inline Point wipe_tower_point_to_object_point(GCode& gcodegen, const Vec2f& wipe_tower_pt) + { + return Point(scale_(wipe_tower_pt.x() - gcodegen.origin()(0)), scale_(wipe_tower_pt.y() - gcodegen.origin()(1))); + } + + std::string WipeTowerIntegration::append_tcr(GCode& gcodegen, const WipeTower::ToolChangeResult& tcr, int new_extruder_id, double z) const + { + if (new_extruder_id != -1 && new_extruder_id != tcr.new_tool) + throw std::invalid_argument("Error: WipeTowerIntegration::append_tcr was asked to do a toolchange it didn't expect."); + + std::string gcode; + + // Toolchangeresult.gcode assumes the wipe tower corner is at the origin (except for priming lines) + // We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position + float alpha = m_wipe_tower_rotation / 180.f * float(M_PI); + Vec2f start_pos = tcr.start_pos; + Vec2f end_pos = tcr.end_pos; + if (!tcr.priming) { + start_pos = Eigen::Rotation2Df(alpha) * start_pos; + start_pos += m_wipe_tower_pos; + end_pos = Eigen::Rotation2Df(alpha) * end_pos; + end_pos += m_wipe_tower_pos; + } + + Vec2f wipe_tower_offset = tcr.priming ? Vec2f::Zero() : m_wipe_tower_pos; + float wipe_tower_rotation = tcr.priming ? 0.f : alpha; + + std::string tcr_rotated_gcode = post_process_wipe_tower_moves(tcr, wipe_tower_offset, wipe_tower_rotation); + + if (!tcr.priming) { + // Move over the wipe tower. + // Retract for a tool change, using the toolchange retract value and setting the priming extra length. + gcode += gcodegen.retract(true); + gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true; + gcode += gcodegen.travel_to( + wipe_tower_point_to_object_point(gcodegen, start_pos), + erMixed, + "Travel to a Wipe Tower"); + gcode += gcodegen.unretract(); + } + + double current_z = gcodegen.writer().get_position().z(); + if (z == -1.) // in case no specific z was provided, print at current_z pos + z = current_z; + if (!is_approx(z, current_z)) { + gcode += gcodegen.writer().retract(); + gcode += gcodegen.writer().travel_to_z(z, "Travel down to the last wipe tower layer."); + gcode += gcodegen.writer().unretract(); + } + + + // Process the end filament gcode. + std::string end_filament_gcode_str; + if (gcodegen.writer().extruder() != nullptr) { + // Process the custom end_filament_gcode in case of single_extruder_multi_material. + unsigned int old_extruder_id = gcodegen.writer().extruder()->id(); + const std::string& end_filament_gcode = gcodegen.config().end_filament_gcode.get_at(old_extruder_id); + if (gcodegen.writer().extruder() != nullptr && !end_filament_gcode.empty()) { + end_filament_gcode_str = gcodegen.placeholder_parser_process("end_filament_gcode", end_filament_gcode, old_extruder_id); + check_add_eol(end_filament_gcode_str); + } + } + + // Process the custom toolchange_gcode. If it is empty, provide a simple Tn command to change the filament. + // Otherwise, leave control to the user completely. + std::string toolchange_gcode_str; + if (true /*gcodegen.writer().extruder() != nullptr*/) { + const std::string& toolchange_gcode = gcodegen.config().toolchange_gcode.value; + if (!toolchange_gcode.empty()) { + DynamicConfig config; + int previous_extruder_id = gcodegen.writer().extruder() ? (int)gcodegen.writer().extruder()->id() : -1; + config.set_key_value("previous_extruder", new ConfigOptionInt(previous_extruder_id)); + config.set_key_value("next_extruder", new ConfigOptionInt((int)new_extruder_id)); + config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index)); + config.set_key_value("layer_z", new ConfigOptionFloat(tcr.print_z)); + toolchange_gcode_str = gcodegen.placeholder_parser_process("toolchange_gcode", toolchange_gcode, new_extruder_id, &config); + check_add_eol(toolchange_gcode_str); + } + + std::string toolchange_command; + if (tcr.priming || (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id))) + toolchange_command = gcodegen.writer().toolchange(new_extruder_id); + if (!custom_gcode_changes_tool(toolchange_gcode_str, gcodegen.writer().toolchange_prefix(), new_extruder_id)) + toolchange_gcode_str += toolchange_command; + else { + // We have informed the m_writer about the current extruder_id, we can ignore the generated G-code. + } + } + + gcodegen.placeholder_parser().set("current_extruder", new_extruder_id); + + // Process the start filament gcode. + std::string start_filament_gcode_str; + const std::string& start_filament_gcode = gcodegen.config().start_filament_gcode.get_at(new_extruder_id); + if (!start_filament_gcode.empty()) { + // Process the start_filament_gcode for the active filament only. DynamicConfig config; - int previous_extruder_id = gcodegen.writer().extruder() ? (int)gcodegen.writer().extruder()->id() : -1; - config.set_key_value("previous_extruder", new ConfigOptionInt(previous_extruder_id)); - config.set_key_value("next_extruder", new ConfigOptionInt((int)new_extruder_id)); - config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index)); - config.set_key_value("layer_z", new ConfigOptionFloat(tcr.print_z)); - toolchange_gcode_str = gcodegen.placeholder_parser_process("toolchange_gcode", toolchange_gcode, new_extruder_id, &config); - check_add_eol(toolchange_gcode_str); + config.set_key_value("filament_extruder_id", new ConfigOptionInt(new_extruder_id)); + start_filament_gcode_str = gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id, &config); + check_add_eol(start_filament_gcode_str); } - std::string toolchange_command; - if (tcr.priming || (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id))) - toolchange_command = gcodegen.writer().toolchange(new_extruder_id); - if (! custom_gcode_changes_tool(toolchange_gcode_str, gcodegen.writer().toolchange_prefix(), new_extruder_id)) - toolchange_gcode_str += toolchange_command; - else { - // We have informed the m_writer about the current extruder_id, we can ignore the generated G-code. - } - } - - gcodegen.placeholder_parser().set("current_extruder", new_extruder_id); - - // Process the start filament gcode. - std::string start_filament_gcode_str; - const std::string &start_filament_gcode = gcodegen.config().start_filament_gcode.get_at(new_extruder_id); - if (! start_filament_gcode.empty()) { - // Process the start_filament_gcode for the active filament only. + // Insert the end filament, toolchange, and start filament gcode into the generated gcode. DynamicConfig config; - config.set_key_value("filament_extruder_id", new ConfigOptionInt(new_extruder_id)); - start_filament_gcode_str = gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id, &config); - check_add_eol(start_filament_gcode_str); + config.set_key_value("end_filament_gcode", new ConfigOptionString(end_filament_gcode_str)); + config.set_key_value("toolchange_gcode", new ConfigOptionString(toolchange_gcode_str)); + config.set_key_value("start_filament_gcode", new ConfigOptionString(start_filament_gcode_str)); + std::string tcr_gcode, tcr_escaped_gcode = gcodegen.placeholder_parser_process("tcr_rotated_gcode", tcr_rotated_gcode, new_extruder_id, &config); + unescape_string_cstyle(tcr_escaped_gcode, tcr_gcode); + gcode += tcr_gcode; + check_add_eol(toolchange_gcode_str); + + + // A phony move to the end position at the wipe tower. + gcodegen.writer().travel_to_xy(end_pos.cast()); + gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos)); + if (!is_approx(z, current_z)) { + gcode += gcodegen.writer().retract(); + gcode += gcodegen.writer().travel_to_z(current_z, "Travel back up to the topmost object layer."); + gcode += gcodegen.writer().unretract(); + } + + else { + // Prepare a future wipe. + gcodegen.m_wipe.path.points.clear(); + if (new_extruder_id >= 0) { + // Start the wipe at the current position. + gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, end_pos)); + // Wipe end point: Wipe direction away from the closer tower edge to the further tower edge. + gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, + Vec2f((std::abs(m_left - end_pos.x()) < std::abs(m_right - end_pos.x())) ? m_right : m_left, + end_pos.y()))); + } + } + + // Let the planner know we are traveling between objects. + gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true; + return gcode; } - // Insert the end filament, toolchange, and start filament gcode into the generated gcode. - DynamicConfig config; - config.set_key_value("end_filament_gcode", new ConfigOptionString(end_filament_gcode_str)); - config.set_key_value("toolchange_gcode", new ConfigOptionString(toolchange_gcode_str)); - config.set_key_value("start_filament_gcode", new ConfigOptionString(start_filament_gcode_str)); - std::string tcr_gcode, tcr_escaped_gcode = gcodegen.placeholder_parser_process("tcr_rotated_gcode", tcr_rotated_gcode, new_extruder_id, &config); - unescape_string_cstyle(tcr_escaped_gcode, tcr_gcode); - gcode += tcr_gcode; - check_add_eol(toolchange_gcode_str); + // This function postprocesses gcode_original, rotates and moves all G1 extrusions and returns resulting gcode + // Starting position has to be supplied explicitely (otherwise it would fail in case first G1 command only contained one coordinate) + std::string WipeTowerIntegration::post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const + { + Vec2f extruder_offset = m_extruder_offsets[tcr.initial_tool].cast(); + std::istringstream gcode_str(tcr.gcode); + std::string gcode_out; + std::string line; + Vec2f pos = tcr.start_pos; + Vec2f transformed_pos = pos; + Vec2f old_pos(-1000.1f, -1000.1f); - // A phony move to the end position at the wipe tower. - gcodegen.writer().travel_to_xy(end_pos.cast()); - gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos)); - if (! is_approx(z, current_z)) { - gcode += gcodegen.writer().retract(); - gcode += gcodegen.writer().travel_to_z(current_z, "Travel back up to the topmost object layer."); - gcode += gcodegen.writer().unretract(); + while (gcode_str) { + std::getline(gcode_str, line); // we read the gcode line by line + + // All G1 commands should be translated and rotated. X and Y coords are + // only pushed to the output when they differ from last time. + // WT generator can override this by appending the never_skip_tag + if (line.find("G1 ") == 0) { + bool never_skip = false; + auto it = line.find(WipeTower::never_skip_tag()); + if (it != std::string::npos) { + // remove the tag and remember we saw it + never_skip = true; + line.erase(it, it + WipeTower::never_skip_tag().size()); + } + std::ostringstream line_out; + std::istringstream line_str(line); + line_str >> std::noskipws; // don't skip whitespace + char ch = 0; + while (line_str >> ch) { + if (ch == 'X' || ch == 'Y') + line_str >> (ch == 'X' ? pos.x() : pos.y()); + else + line_out << ch; + } + + transformed_pos = Eigen::Rotation2Df(angle) * pos + translation; + + if (transformed_pos != old_pos || never_skip) { + line = line_out.str(); + std::ostringstream oss; + oss << std::fixed << std::setprecision(3) << "G1 "; + if (transformed_pos.x() != old_pos.x() || never_skip) + oss << " X" << transformed_pos.x() - extruder_offset.x(); + if (transformed_pos.y() != old_pos.y() || never_skip) + oss << " Y" << transformed_pos.y() - extruder_offset.y(); + oss << " "; + line.replace(line.find("G1 "), 3, oss.str()); + old_pos = transformed_pos; + } + } + + gcode_out += line + "\n"; + + // If this was a toolchange command, we should change current extruder offset + if (line == "[toolchange_gcode]") { + extruder_offset = m_extruder_offsets[tcr.new_tool].cast(); + + // If the extruder offset changed, add an extra move so everything is continuous + if (extruder_offset != m_extruder_offsets[tcr.initial_tool].cast()) { + std::ostringstream oss; + oss << std::fixed << std::setprecision(3) + << "G1 X" << transformed_pos.x() - extruder_offset.x() + << " Y" << transformed_pos.y() - extruder_offset.y() + << "\n"; + gcode_out += oss.str(); + } + } + } + return gcode_out; } - else { + + std::string WipeTowerIntegration::prime(GCode& gcodegen) + { + assert(m_layer_idx == 0); + std::string gcode; + + + // Disable linear advance for the wipe tower operations. + //gcode += (gcodegen.config().gcode_flavor == gcfRepRap ? std::string("M572 D0 S0\n") : std::string("M900 K0\n")); + + for (const WipeTower::ToolChangeResult& tcr : m_priming) { + if (!tcr.extrusions.empty()) + gcode += append_tcr(gcodegen, tcr, tcr.new_tool); + + + // Let the tool change be executed by the wipe tower class. + // Inform the G-code writer about the changes done behind its back. + //gcode += tcr.gcode; + // Let the m_writer know the current extruder_id, but ignore the generated G-code. + // unsigned int current_extruder_id = tcr.extrusions.back().tool; + // gcodegen.writer().toolchange(current_extruder_id); + // gcodegen.placeholder_parser().set("current_extruder", current_extruder_id); + + } + + // A phony move to the end position at the wipe tower. + /* gcodegen.writer().travel_to_xy(Vec2d(m_priming.back().end_pos.x, m_priming.back().end_pos.y)); + gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, m_priming.back().end_pos)); // Prepare a future wipe. gcodegen.m_wipe.path.points.clear(); - if (new_extruder_id >= 0) { - // Start the wipe at the current position. - gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, end_pos)); - // Wipe end point: Wipe direction away from the closer tower edge to the further tower edge. - gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, - Vec2f((std::abs(m_left - end_pos.x()) < std::abs(m_right - end_pos.x())) ? m_right : m_left, - end_pos.y()))); + // Start the wipe at the current position. + gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, m_priming.back().end_pos)); + // Wipe end point: Wipe direction away from the closer tower edge to the further tower edge. + gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, + WipeTower::xy((std::abs(m_left - m_priming.back().end_pos.x) < std::abs(m_right - m_priming.back().end_pos.x)) ? m_right : m_left, + m_priming.back().end_pos.y)));*/ + + return gcode; + } + + std::string WipeTowerIntegration::tool_change(GCode& gcodegen, int extruder_id, bool finish_layer) + { + std::string gcode; + assert(m_layer_idx >= 0); + if (!m_brim_done || gcodegen.writer().need_toolchange(extruder_id) || finish_layer) { + if (m_layer_idx < (int)m_tool_changes.size()) { + if (!(size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size())) + throw std::runtime_error("Wipe tower generation failed, possibly due to empty first layer."); + + + // Calculate where the wipe tower layer will be printed. -1 means that print z will not change, + // resulting in a wipe tower with sparse layers. + double wipe_tower_z = -1; + bool ignore_sparse = false; + if (gcodegen.config().wipe_tower_no_sparse_layers.value) { + wipe_tower_z = m_last_wipe_tower_print_z; + ignore_sparse = (m_brim_done && m_tool_changes[m_layer_idx].size() == 1 && m_tool_changes[m_layer_idx].front().initial_tool == m_tool_changes[m_layer_idx].front().new_tool); + if (m_tool_change_idx == 0 && !ignore_sparse) + wipe_tower_z = m_last_wipe_tower_print_z + m_tool_changes[m_layer_idx].front().layer_height; + } + + if (!ignore_sparse) { + gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id, wipe_tower_z); + m_last_wipe_tower_print_z = wipe_tower_z; + } + } + m_brim_done = true; } + return gcode; } - // Let the planner know we are traveling between objects. - gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true; - return gcode; -} - -// This function postprocesses gcode_original, rotates and moves all G1 extrusions and returns resulting gcode -// Starting position has to be supplied explicitely (otherwise it would fail in case first G1 command only contained one coordinate) -std::string WipeTowerIntegration::post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const -{ - Vec2f extruder_offset = m_extruder_offsets[tcr.initial_tool].cast(); - - std::istringstream gcode_str(tcr.gcode); - std::string gcode_out; - std::string line; - Vec2f pos = tcr.start_pos; - Vec2f transformed_pos = pos; - Vec2f old_pos(-1000.1f, -1000.1f); - - while (gcode_str) { - std::getline(gcode_str, line); // we read the gcode line by line - - // All G1 commands should be translated and rotated. X and Y coords are - // only pushed to the output when they differ from last time. - // WT generator can override this by appending the never_skip_tag - if (line.find("G1 ") == 0) { - bool never_skip = false; - auto it = line.find(WipeTower::never_skip_tag()); - if (it != std::string::npos) { - // remove the tag and remember we saw it - never_skip = true; - line.erase(it, it+WipeTower::never_skip_tag().size()); - } - std::ostringstream line_out; - std::istringstream line_str(line); - line_str >> std::noskipws; // don't skip whitespace - char ch = 0; - while (line_str >> ch) { - if (ch == 'X' || ch =='Y') - line_str >> (ch == 'X' ? pos.x() : pos.y()); - else - line_out << ch; - } - - transformed_pos = Eigen::Rotation2Df(angle) * pos + translation; - - if (transformed_pos != old_pos || never_skip) { - line = line_out.str(); - std::ostringstream oss; - oss << std::fixed << std::setprecision(3) << "G1 "; - if (transformed_pos.x() != old_pos.x() || never_skip) - oss << " X" << transformed_pos.x() - extruder_offset.x(); - if (transformed_pos.y() != old_pos.y() || never_skip) - oss << " Y" << transformed_pos.y() - extruder_offset.y(); - oss << " "; - line.replace(line.find("G1 "), 3, oss.str()); - old_pos = transformed_pos; - } - } - - gcode_out += line + "\n"; - - // If this was a toolchange command, we should change current extruder offset - if (line == "[toolchange_gcode]") { - extruder_offset = m_extruder_offsets[tcr.new_tool].cast(); - - // If the extruder offset changed, add an extra move so everything is continuous - if (extruder_offset != m_extruder_offsets[tcr.initial_tool].cast()) { - std::ostringstream oss; - oss << std::fixed << std::setprecision(3) - << "G1 X" << transformed_pos.x() - extruder_offset.x() - << " Y" << transformed_pos.y() - extruder_offset.y() - << "\n"; - gcode_out += oss.str(); - } - } + // Print is finished. Now it remains to unload the filament safely with ramming over the wipe tower. + std::string WipeTowerIntegration::finalize(GCode& gcodegen) + { + std::string gcode; + if (std::abs(gcodegen.writer().get_position()(2) - m_final_purge.print_z) > EPSILON) + gcode += gcodegen.change_layer(m_final_purge.print_z); + gcode += append_tcr(gcodegen, m_final_purge, -1); + return gcode; } - return gcode_out; -} - - -std::string WipeTowerIntegration::prime(GCode &gcodegen) -{ - assert(m_layer_idx == 0); - std::string gcode; - - - // Disable linear advance for the wipe tower operations. - //gcode += (gcodegen.config().gcode_flavor == gcfRepRap ? std::string("M572 D0 S0\n") : std::string("M900 K0\n")); - - for (const WipeTower::ToolChangeResult& tcr : m_priming) { - if (!tcr.extrusions.empty()) - gcode += append_tcr(gcodegen, tcr, tcr.new_tool); - - - // Let the tool change be executed by the wipe tower class. - // Inform the G-code writer about the changes done behind its back. - //gcode += tcr.gcode; - // Let the m_writer know the current extruder_id, but ignore the generated G-code. - // unsigned int current_extruder_id = tcr.extrusions.back().tool; - // gcodegen.writer().toolchange(current_extruder_id); - // gcodegen.placeholder_parser().set("current_extruder", current_extruder_id); - - } - - // A phony move to the end position at the wipe tower. - /* gcodegen.writer().travel_to_xy(Vec2d(m_priming.back().end_pos.x, m_priming.back().end_pos.y)); - gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, m_priming.back().end_pos)); - // Prepare a future wipe. - gcodegen.m_wipe.path.points.clear(); - // Start the wipe at the current position. - gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, m_priming.back().end_pos)); - // Wipe end point: Wipe direction away from the closer tower edge to the further tower edge. - gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, - WipeTower::xy((std::abs(m_left - m_priming.back().end_pos.x) < std::abs(m_right - m_priming.back().end_pos.x)) ? m_right : m_left, - m_priming.back().end_pos.y)));*/ - - return gcode; -} - -std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id, bool finish_layer) -{ - std::string gcode; - assert(m_layer_idx >= 0); - if (! m_brim_done || gcodegen.writer().need_toolchange(extruder_id) || finish_layer) { - if (m_layer_idx < (int)m_tool_changes.size()) { - if (! (size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size())) - throw std::runtime_error("Wipe tower generation failed, possibly due to empty first layer."); - - - // Calculate where the wipe tower layer will be printed. -1 means that print z will not change, - // resulting in a wipe tower with sparse layers. - double wipe_tower_z = -1; - bool ignore_sparse = false; - if (gcodegen.config().wipe_tower_no_sparse_layers.value) { - wipe_tower_z = m_last_wipe_tower_print_z; - ignore_sparse = (m_brim_done && m_tool_changes[m_layer_idx].size() == 1 && m_tool_changes[m_layer_idx].front().initial_tool == m_tool_changes[m_layer_idx].front().new_tool); - if (m_tool_change_idx == 0 && ! ignore_sparse) - wipe_tower_z = m_last_wipe_tower_print_z + m_tool_changes[m_layer_idx].front().layer_height; - } - - if (! ignore_sparse) { - gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id, wipe_tower_z); - m_last_wipe_tower_print_z = wipe_tower_z; - } - } - m_brim_done = true; - } - return gcode; -} - -// Print is finished. Now it remains to unload the filament safely with ramming over the wipe tower. -std::string WipeTowerIntegration::finalize(GCode &gcodegen) -{ - std::string gcode; - if (std::abs(gcodegen.writer().get_position()(2) - m_final_purge.print_z) > EPSILON) - gcode += gcodegen.change_layer(m_final_purge.print_z); - gcode += append_tcr(gcodegen, m_final_purge, -1); - return gcode; -} #if ENABLE_GCODE_VIEWER -const std::vector ColorPrintColors::Colors = { "#C0392B", "#E67E22", "#F1C40F", "#27AE60", "#1ABC9C", "#2980B9", "#9B59B6" }; + const std::vector ColorPrintColors::Colors = { "#C0392B", "#E67E22", "#F1C40F", "#27AE60", "#1ABC9C", "#2980B9", "#9B59B6" }; #endif // ENABLE_GCODE_VIEWER #define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id()) -// Collect pairs of object_layer + support_layer sorted by print_z. -// object_layer & support_layer are considered to be on the same print_z, if they are not further than EPSILON. -std::vector GCode::collect_layers_to_print(const PrintObject &object) -{ - std::vector layers_to_print; - layers_to_print.reserve(object.layers().size() + object.support_layers().size()); + // Collect pairs of object_layer + support_layer sorted by print_z. + // object_layer & support_layer are considered to be on the same print_z, if they are not further than EPSILON. + std::vector GCode::collect_layers_to_print(const PrintObject& object) + { + std::vector layers_to_print; + layers_to_print.reserve(object.layers().size() + object.support_layers().size()); - // Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um. - // This is the same logic as in support generator. - //FIXME should we use the printing extruders instead? - double gap_over_supports = object.config().support_material_contact_distance; - // FIXME should we test object.config().support_material_synchronize_layers ? Currently the support layers are synchronized with object layers iff soluble supports. - assert(! object.config().support_material || gap_over_supports != 0. || object.config().support_material_synchronize_layers); - if (gap_over_supports != 0.) { - gap_over_supports = std::max(0., gap_over_supports); - // Not a soluble support, - double support_layer_height_min = 1000000.; - for (auto lh : object.print()->config().min_layer_height.values) - support_layer_height_min = std::min(support_layer_height_min, std::max(0.01, lh)); - gap_over_supports += support_layer_height_min; - } + // Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um. + // This is the same logic as in support generator. + //FIXME should we use the printing extruders instead? + double gap_over_supports = object.config().support_material_contact_distance; + // FIXME should we test object.config().support_material_synchronize_layers ? Currently the support layers are synchronized with object layers iff soluble supports. + assert(!object.config().support_material || gap_over_supports != 0. || object.config().support_material_synchronize_layers); + if (gap_over_supports != 0.) { + gap_over_supports = std::max(0., gap_over_supports); + // Not a soluble support, + double support_layer_height_min = 1000000.; + for (auto lh : object.print()->config().min_layer_height.values) + support_layer_height_min = std::min(support_layer_height_min, std::max(0.01, lh)); + gap_over_supports += support_layer_height_min; + } - // Pair the object layers with the support layers by z. - size_t idx_object_layer = 0; - size_t idx_support_layer = 0; - const LayerToPrint* last_extrusion_layer = nullptr; - while (idx_object_layer < object.layers().size() || idx_support_layer < object.support_layers().size()) { - LayerToPrint layer_to_print; - layer_to_print.object_layer = (idx_object_layer < object.layers().size()) ? object.layers()[idx_object_layer ++] : nullptr; - layer_to_print.support_layer = (idx_support_layer < object.support_layers().size()) ? object.support_layers()[idx_support_layer ++] : nullptr; - if (layer_to_print.object_layer && layer_to_print.support_layer) { - if (layer_to_print.object_layer->print_z < layer_to_print.support_layer->print_z - EPSILON) { - layer_to_print.support_layer = nullptr; - -- idx_support_layer; - } else if (layer_to_print.support_layer->print_z < layer_to_print.object_layer->print_z - EPSILON) { - layer_to_print.object_layer = nullptr; - -- idx_object_layer; + // Pair the object layers with the support layers by z. + size_t idx_object_layer = 0; + size_t idx_support_layer = 0; + const LayerToPrint* last_extrusion_layer = nullptr; + while (idx_object_layer < object.layers().size() || idx_support_layer < object.support_layers().size()) { + LayerToPrint layer_to_print; + layer_to_print.object_layer = (idx_object_layer < object.layers().size()) ? object.layers()[idx_object_layer++] : nullptr; + layer_to_print.support_layer = (idx_support_layer < object.support_layers().size()) ? object.support_layers()[idx_support_layer++] : nullptr; + if (layer_to_print.object_layer && layer_to_print.support_layer) { + if (layer_to_print.object_layer->print_z < layer_to_print.support_layer->print_z - EPSILON) { + layer_to_print.support_layer = nullptr; + --idx_support_layer; + } + else if (layer_to_print.support_layer->print_z < layer_to_print.object_layer->print_z - EPSILON) { + layer_to_print.object_layer = nullptr; + --idx_object_layer; + } + } + + layers_to_print.emplace_back(layer_to_print); + + // In case there are extrusions on this layer, check there is a layer to lay it on. + if ((layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions()) + // Allow empty support layers, as the support generator may produce no extrusions for non-empty support regions. + || (layer_to_print.support_layer /* && layer_to_print.support_layer->has_extrusions() */)) { + double support_contact_z = (last_extrusion_layer && last_extrusion_layer->support_layer) + ? gap_over_supports + : 0.; + double maximal_print_z = (last_extrusion_layer ? last_extrusion_layer->print_z() : 0.) + + layer_to_print.layer()->height + + support_contact_z; + // Negative support_contact_z is not taken into account, it can result in false positives in cases + // where previous layer has object extrusions too (https://github.com/prusa3d/PrusaSlicer/issues/2752) + + // Only check this layer in case it has some extrusions. + bool has_extrusions = (layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions()) + || (layer_to_print.support_layer && layer_to_print.support_layer->has_extrusions()); + + if (has_extrusions && layer_to_print.print_z() > maximal_print_z + 2. * EPSILON) + throw std::runtime_error(_(L("Empty layers detected, the output would not be printable.")) + "\n\n" + + _(L("Object name")) + ": " + object.model_object()->name + "\n" + _(L("Print z")) + ": " + + std::to_string(layers_to_print.back().print_z()) + "\n\n" + _(L("This is " + "usually caused by negligibly small extrusions or by a faulty model. Try to repair " + "the model or change its orientation on the bed."))); + // Remember last layer with extrusions. + last_extrusion_layer = &layers_to_print.back(); } } - layers_to_print.emplace_back(layer_to_print); - - // In case there are extrusions on this layer, check there is a layer to lay it on. - if ((layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions()) - // Allow empty support layers, as the support generator may produce no extrusions for non-empty support regions. - || (layer_to_print.support_layer /* && layer_to_print.support_layer->has_extrusions() */)) { - double support_contact_z = (last_extrusion_layer && last_extrusion_layer->support_layer) - ? gap_over_supports - : 0.; - double maximal_print_z = (last_extrusion_layer ? last_extrusion_layer->print_z() : 0.) - + layer_to_print.layer()->height - + support_contact_z; - // Negative support_contact_z is not taken into account, it can result in false positives in cases - // where previous layer has object extrusions too (https://github.com/prusa3d/PrusaSlicer/issues/2752) - - // Only check this layer in case it has some extrusions. - bool has_extrusions = (layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions()) - || (layer_to_print.support_layer && layer_to_print.support_layer->has_extrusions()); - - if (has_extrusions && layer_to_print.print_z() > maximal_print_z + 2. * EPSILON) - throw std::runtime_error(_(L("Empty layers detected, the output would not be printable.")) + "\n\n" + - _(L("Object name")) + ": " + object.model_object()->name + "\n" + _(L("Print z")) + ": " + - std::to_string(layers_to_print.back().print_z()) + "\n\n" + _(L("This is " - "usually caused by negligibly small extrusions or by a faulty model. Try to repair " - "the model or change its orientation on the bed."))); - // Remember last layer with extrusions. - last_extrusion_layer = &layers_to_print.back(); - } + return layers_to_print; } - return layers_to_print; -} + // Prepare for non-sequential printing of multiple objects: Support resp. object layers with nearly identical print_z + // will be printed for all objects at once. + // Return a list of items. + std::vector>> GCode::collect_layers_to_print(const Print& print) + { + struct OrderingItem { + coordf_t print_z; + size_t object_idx; + size_t layer_idx; + }; -// Prepare for non-sequential printing of multiple objects: Support resp. object layers with nearly identical print_z -// will be printed for all objects at once. -// Return a list of items. -std::vector>> GCode::collect_layers_to_print(const Print &print) -{ - struct OrderingItem { - coordf_t print_z; - size_t object_idx; - size_t layer_idx; - }; - - std::vector> per_object(print.objects().size(), std::vector()); - std::vector ordering; - for (size_t i = 0; i < print.objects().size(); ++i) { - per_object[i] = collect_layers_to_print(*print.objects()[i]); - OrderingItem ordering_item; - ordering_item.object_idx = i; - ordering.reserve(ordering.size() + per_object[i].size()); - const LayerToPrint &front = per_object[i].front(); - for (const LayerToPrint <p : per_object[i]) { - ordering_item.print_z = ltp.print_z(); - ordering_item.layer_idx = <p - &front; - ordering.emplace_back(ordering_item); + std::vector> per_object(print.objects().size(), std::vector()); + std::vector ordering; + for (size_t i = 0; i < print.objects().size(); ++i) { + per_object[i] = collect_layers_to_print(*print.objects()[i]); + OrderingItem ordering_item; + ordering_item.object_idx = i; + ordering.reserve(ordering.size() + per_object[i].size()); + const LayerToPrint& front = per_object[i].front(); + for (const LayerToPrint& ltp : per_object[i]) { + ordering_item.print_z = ltp.print_z(); + ordering_item.layer_idx = <p - &front; + ordering.emplace_back(ordering_item); + } } - } - std::sort(ordering.begin(), ordering.end(), [](const OrderingItem &oi1, const OrderingItem &oi2) { return oi1.print_z < oi2.print_z; }); + std::sort(ordering.begin(), ordering.end(), [](const OrderingItem& oi1, const OrderingItem& oi2) { return oi1.print_z < oi2.print_z; }); - std::vector>> layers_to_print; - // Merge numerically very close Z values. - for (size_t i = 0; i < ordering.size();) { - // Find the last layer with roughly the same print_z. - size_t j = i + 1; - coordf_t zmax = ordering[i].print_z + EPSILON; - for (; j < ordering.size() && ordering[j].print_z <= zmax; ++ j) ; - // Merge into layers_to_print. - std::pair> merged; - // Assign an average print_z to the set of layers with nearly equal print_z. - merged.first = 0.5 * (ordering[i].print_z + ordering[j-1].print_z); - merged.second.assign(print.objects().size(), LayerToPrint()); - for (; i < j; ++i) { - const OrderingItem &oi = ordering[i]; - assert(merged.second[oi.object_idx].layer() == nullptr); - merged.second[oi.object_idx] = std::move(per_object[oi.object_idx][oi.layer_idx]); + std::vector>> layers_to_print; + // Merge numerically very close Z values. + for (size_t i = 0; i < ordering.size();) { + // Find the last layer with roughly the same print_z. + size_t j = i + 1; + coordf_t zmax = ordering[i].print_z + EPSILON; + for (; j < ordering.size() && ordering[j].print_z <= zmax; ++j); + // Merge into layers_to_print. + std::pair> merged; + // Assign an average print_z to the set of layers with nearly equal print_z. + merged.first = 0.5 * (ordering[i].print_z + ordering[j - 1].print_z); + merged.second.assign(print.objects().size(), LayerToPrint()); + for (; i < j; ++i) { + const OrderingItem& oi = ordering[i]; + assert(merged.second[oi.object_idx].layer() == nullptr); + merged.second[oi.object_idx] = std::move(per_object[oi.object_idx][oi.layer_idx]); + } + layers_to_print.emplace_back(std::move(merged)); } - layers_to_print.emplace_back(std::move(merged)); - } - return layers_to_print; -} + return layers_to_print; + } #if ENABLE_GCODE_VIEWER +// free functions called by GCode::do_export() +namespace DoExport { + static void update_print_stats_estimated_times( + const GCodeProcessor& processor, + const bool silent_time_estimator_enabled, + PrintStatistics& print_statistics) + { + print_statistics.estimated_normal_print_time = processor.get_time_dhm(GCodeProcessor::ETimeMode::Normal); + print_statistics.estimated_normal_custom_gcode_print_times = processor.get_custom_gcode_times(GCodeProcessor::ETimeMode::Normal, true); + if (silent_time_estimator_enabled) { + print_statistics.estimated_silent_print_time = processor.get_time_dhm(GCodeProcessor::ETimeMode::Stealth); + print_statistics.estimated_silent_custom_gcode_print_times = processor.get_custom_gcode_times(GCodeProcessor::ETimeMode::Stealth, true); + } + else { + print_statistics.estimated_silent_print_time = "N/A"; + print_statistics.estimated_silent_custom_gcode_print_times.clear(); + } + } + +} // namespace DoExport + void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb) #else void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb) @@ -768,6 +790,8 @@ void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_ m_processor.process_file(path_tmp); if (result != nullptr) *result = std::move(m_processor.extract_result()); + + DoExport::update_print_stats_estimated_times(m_processor, m_silent_time_estimator_enabled, print->m_print_statistics); #endif // ENABLE_GCODE_VIEWER GCodeTimeEstimator::PostProcessData normal_data = m_normal_time_estimator.get_post_process_data(); @@ -883,10 +907,11 @@ namespace DoExport { } #if ENABLE_GCODE_VIEWER - static void init_gcode_processor(const PrintConfig& config, GCodeProcessor& processor) + static void init_gcode_processor(const PrintConfig& config, GCodeProcessor& processor, bool silent_time_estimator_enabled) { processor.reset(); processor.apply_config(config); + processor.enable_stealth_time_estimator(silent_time_estimator_enabled); } #else static void init_gcode_analyzer(const PrintConfig &config, GCodeAnalyzer &analyzer) @@ -1036,7 +1061,7 @@ namespace DoExport { } // Fill in print_statistics and return formatted string containing filament statistics to be inserted into G-code comment section. - static std::string update_print_stats_and_format_filament_stats( + static std::string update_print_stats_and_format_filament_stats( const GCodeTimeEstimator &normal_time_estimator, const GCodeTimeEstimator &silent_time_estimator, const bool silent_time_estimator_enabled, @@ -1044,20 +1069,19 @@ namespace DoExport { const WipeTowerData &wipe_tower_data, const std::vector &extruders, PrintStatistics &print_statistics) - { + { std::string filament_stats_string_out; print_statistics.clear(); - print_statistics.estimated_normal_print_time = normal_time_estimator.get_time_dhm/*s*/(); - print_statistics.estimated_silent_print_time = silent_time_estimator_enabled ? silent_time_estimator.get_time_dhm/*s*/() : "N/A"; #if ENABLE_GCODE_VIEWER + print_statistics.estimated_normal_print_time_str = normal_time_estimator.get_time_dhm/*s*/(); + print_statistics.estimated_silent_print_time_str = silent_time_estimator_enabled ? silent_time_estimator.get_time_dhm/*s*/() : "N/A"; print_statistics.estimated_normal_custom_gcode_print_times_str = normal_time_estimator.get_custom_gcode_times_dhm(true); - print_statistics.estimated_normal_custom_gcode_print_times = normal_time_estimator.get_custom_gcode_times(true); - if (silent_time_estimator_enabled) { + if (silent_time_estimator_enabled) print_statistics.estimated_silent_custom_gcode_print_times_str = silent_time_estimator.get_custom_gcode_times_dhm(true); - print_statistics.estimated_silent_custom_gcode_print_times = silent_time_estimator.get_custom_gcode_times(true); - } #else + print_statistics.estimated_normal_print_time = normal_time_estimator.get_time_dhm/*s*/(); + print_statistics.estimated_silent_print_time = silent_time_estimator_enabled ? silent_time_estimator.get_time_dhm/*s*/() : "N/A"; print_statistics.estimated_normal_custom_gcode_print_times = normal_time_estimator.get_custom_gcode_times_dhm(true); if (silent_time_estimator_enabled) print_statistics.estimated_silent_custom_gcode_print_times = silent_time_estimator.get_custom_gcode_times_dhm(true); @@ -1156,7 +1180,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu // modifies the following: m_normal_time_estimator, m_silent_time_estimator, m_silent_time_estimator_enabled); #if ENABLE_GCODE_VIEWER - DoExport::init_gcode_processor(print.config(), m_processor); + DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled); #else DoExport::init_gcode_analyzer(print.config(), m_analyzer); #endif // ENABLE_GCODE_VIEWER diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 14f29b56b6..7174e5e36d 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1,9 +1,11 @@ #include "libslic3r/libslic3r.h" +#include "libslic3r/Utils.hpp" #include "GCodeProcessor.hpp" #include #include +#include #if ENABLE_GCODE_VIEWER @@ -14,20 +16,65 @@ static const float INCHES_TO_MM = 25.4f; static const float MMMIN_TO_MMSEC = 1.0f / 60.0f; -static bool is_valid_extrusion_role(int value) -{ - return ((int)Slic3r::erNone <= value) && (value <= (int)Slic3r::erMixed); -} +static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2 namespace Slic3r { -const std::string GCodeProcessor::Extrusion_Role_Tag = "_PROCESSOR_EXTRUSION_ROLE:"; -const std::string GCodeProcessor::Width_Tag = "_PROCESSOR_WIDTH:"; -const std::string GCodeProcessor::Height_Tag = "_PROCESSOR_HEIGHT:"; -const std::string GCodeProcessor::Mm3_Per_Mm_Tag = "_PROCESSOR_MM3_PER_MM:"; -const std::string GCodeProcessor::Color_Change_Tag = "_PROCESSOR_COLOR_CHANGE"; -const std::string GCodeProcessor::Pause_Print_Tag = "_PROCESSOR_PAUSE_PRINT"; -const std::string GCodeProcessor::Custom_Code_Tag = "_PROCESSOR_CUSTOM_CODE"; +const std::string GCodeProcessor::Extrusion_Role_Tag = "PrusaSlicer__EXTRUSION_ROLE:"; +const std::string GCodeProcessor::Width_Tag = "PrusaSlicer__WIDTH:"; +const std::string GCodeProcessor::Height_Tag = "PrusaSlicer__HEIGHT:"; +const std::string GCodeProcessor::Mm3_Per_Mm_Tag = "PrusaSlicer__MM3_PER_MM:"; +const std::string GCodeProcessor::Color_Change_Tag = "PrusaSlicer__COLOR_CHANGE"; +const std::string GCodeProcessor::Pause_Print_Tag = "PrusaSlicer__PAUSE_PRINT"; +const std::string GCodeProcessor::Custom_Code_Tag = "PrusaSlicer__CUSTOM_CODE"; + +static bool is_valid_extrusion_role(int value) +{ + return (static_cast(erNone) <= value) && (value <= static_cast(erMixed)); +} + +static void set_option_value(ConfigOptionFloats& option, size_t id, float value) +{ + if (id < option.values.size()) + option.values[id] = static_cast(value); +}; + +static float get_option_value(const ConfigOptionFloats& option, size_t id) +{ + return option.values.empty() ? 0.0f : + ((id < option.values.size()) ? static_cast(option.values[id]) : static_cast(option.values.back())); +} + +static float estimated_acceleration_distance(float initial_rate, float target_rate, float acceleration) +{ + return (acceleration == 0.0f) ? 0.0f : (sqr(target_rate) - sqr(initial_rate)) / (2.0f * acceleration); +} + +static float intersection_distance(float initial_rate, float final_rate, float acceleration, float distance) +{ + return (acceleration == 0.0f) ? 0.0f : (2.0f * acceleration * distance - sqr(initial_rate) + sqr(final_rate)) / (4.0f * acceleration); +} + +static float speed_from_distance(float initial_feedrate, float distance, float acceleration) +{ + // to avoid invalid negative numbers due to numerical errors + float value = std::max(0.0f, sqr(initial_feedrate) + 2.0f * acceleration * distance); + return ::sqrt(value); +} + +// Calculates the maximum allowable speed at this point when you must be able to reach target_velocity using the +// acceleration within the allotted distance. +static float max_allowable_speed(float acceleration, float target_velocity, float distance) +{ + // to avoid invalid negative numbers due to numerical errors + float value = std::max(0.0f, sqr(target_velocity) - 2.0f * acceleration * distance); + return std::sqrt(value); +} + +static float acceleration_time_from_distance(float initial_feedrate, float distance, float acceleration) +{ + return (acceleration != 0.0f) ? (speed_from_distance(initial_feedrate, distance, acceleration) - initial_feedrate) / acceleration : 0.0f; +} void GCodeProcessor::CachedPosition::reset() { @@ -41,6 +88,208 @@ void GCodeProcessor::CpColor::reset() current = 0; } +float GCodeProcessor::Trapezoid::acceleration_time(float entry_feedrate, float acceleration) const +{ + return acceleration_time_from_distance(entry_feedrate, accelerate_until, acceleration); +} + +float GCodeProcessor::Trapezoid::cruise_time() const +{ + return (cruise_feedrate != 0.0f) ? cruise_distance() / cruise_feedrate : 0.0f; +} + +float GCodeProcessor::Trapezoid::deceleration_time(float distance, float acceleration) const +{ + return acceleration_time_from_distance(cruise_feedrate, (distance - decelerate_after), -acceleration); +} + +float GCodeProcessor::Trapezoid::cruise_distance() const +{ + return decelerate_after - accelerate_until; +} + +void GCodeProcessor::TimeBlock::calculate_trapezoid() +{ + trapezoid.cruise_feedrate = feedrate_profile.cruise; + + float accelerate_distance = std::max(0.0f, estimated_acceleration_distance(feedrate_profile.entry, feedrate_profile.cruise, acceleration)); + float decelerate_distance = std::max(0.0f, estimated_acceleration_distance(feedrate_profile.cruise, feedrate_profile.exit, -acceleration)); + float cruise_distance = distance - accelerate_distance - decelerate_distance; + + // Not enough space to reach the nominal feedrate. + // This means no cruising, and we'll have to use intersection_distance() to calculate when to abort acceleration + // and start braking in order to reach the exit_feedrate exactly at the end of this block. + if (cruise_distance < 0.0f) { + accelerate_distance = std::clamp(intersection_distance(feedrate_profile.entry, feedrate_profile.exit, acceleration, distance), 0.0f, distance); + cruise_distance = 0.0f; + trapezoid.cruise_feedrate = speed_from_distance(feedrate_profile.entry, accelerate_distance, acceleration); + } + + trapezoid.accelerate_until = accelerate_distance; + trapezoid.decelerate_after = accelerate_distance + cruise_distance; +} + +float GCodeProcessor::TimeBlock::time() const +{ + return trapezoid.acceleration_time(feedrate_profile.entry, acceleration) + + trapezoid.cruise_time() + + trapezoid.deceleration_time(distance, acceleration); +} + +void GCodeProcessor::TimeMachine::State::reset() +{ + feedrate = 0.0f; + safe_feedrate = 0.0f; + axis_feedrate = { 0.0f, 0.0f, 0.0f, 0.0f }; + abs_axis_feedrate = { 0.0f, 0.0f, 0.0f, 0.0f }; +} + +void GCodeProcessor::TimeMachine::CustomGCodeTime::reset() +{ + needed = false; + cache = 0.0f; + times = std::vector>(); +} + +void GCodeProcessor::TimeMachine::reset() +{ + enabled = false; + acceleration = 0.0f; + extrude_factor_override_percentage = 1.0f; + time = 0.0f; + curr.reset(); + prev.reset(); + gcode_time.reset(); + blocks = std::vector(); +} + +void GCodeProcessor::TimeMachine::simulate_st_synchronize(float additional_time) +{ + if (!enabled) + return; + + time += additional_time; + gcode_time.cache += additional_time; + calculate_time(); +} + +static void planner_forward_pass_kernel(GCodeProcessor::TimeBlock& prev, GCodeProcessor::TimeBlock& curr) +{ + // If the previous block is an acceleration block, but it is not long enough to complete the + // full speed change within the block, we need to adjust the entry speed accordingly. Entry + // speeds have already been reset, maximized, and reverse planned by reverse planner. + // If nominal length is true, max junction speed is guaranteed to be reached. No need to recheck. + if (!prev.flags.nominal_length) { + if (prev.feedrate_profile.entry < curr.feedrate_profile.entry) { + float entry_speed = std::min(curr.feedrate_profile.entry, max_allowable_speed(-prev.acceleration, prev.feedrate_profile.entry, prev.distance)); + + // Check for junction speed change + if (curr.feedrate_profile.entry != entry_speed) { + curr.feedrate_profile.entry = entry_speed; + curr.flags.recalculate = true; + } + } + } +} + +void planner_reverse_pass_kernel(GCodeProcessor::TimeBlock& curr, GCodeProcessor::TimeBlock& next) +{ + // If entry speed is already at the maximum entry speed, no need to recheck. Block is cruising. + // If not, block in state of acceleration or deceleration. Reset entry speed to maximum and + // check for maximum allowable speed reductions to ensure maximum possible planned speed. + if (curr.feedrate_profile.entry != curr.max_entry_speed) { + // If nominal length true, max junction speed is guaranteed to be reached. Only compute + // for max allowable speed if block is decelerating and nominal length is false. + if (!curr.flags.nominal_length && curr.max_entry_speed > next.feedrate_profile.entry) + curr.feedrate_profile.entry = std::min(curr.max_entry_speed, max_allowable_speed(-curr.acceleration, next.feedrate_profile.entry, curr.distance)); + else + curr.feedrate_profile.entry = curr.max_entry_speed; + + curr.flags.recalculate = true; + } +} + +static void recalculate_trapezoids(std::vector& blocks) +{ + GCodeProcessor::TimeBlock* curr = nullptr; + GCodeProcessor::TimeBlock* next = nullptr; + + for (size_t i = 0; i < blocks.size(); ++i) { + GCodeProcessor::TimeBlock& b = blocks[i]; + + curr = next; + next = &b; + + if (curr != nullptr) { + // Recalculate if current block entry or exit junction speed has changed. + if (curr->flags.recalculate || next->flags.recalculate) { + // NOTE: Entry and exit factors always > 0 by all previous logic operations. + GCodeProcessor::TimeBlock block = *curr; + block.feedrate_profile.exit = next->feedrate_profile.entry; + block.calculate_trapezoid(); + curr->trapezoid = block.trapezoid; + curr->flags.recalculate = false; // Reset current only to ensure next trapezoid is computed + } + } + } + + // Last/newest block in buffer. Always recalculated. + if (next != nullptr) { + GCodeProcessor::TimeBlock block = *next; + block.feedrate_profile.exit = next->safe_feedrate; + block.calculate_trapezoid(); + next->trapezoid = block.trapezoid; + next->flags.recalculate = false; + } +} + +void GCodeProcessor::TimeMachine::calculate_time(size_t keep_last_n_blocks) +{ + if (!enabled || blocks.size() < 2) + return; + + assert(keep_last_n_blocks <= blocks.size()); + + // forward_pass + for (size_t i = 0; i + 1 < blocks.size(); ++i) { + planner_forward_pass_kernel(blocks[i], blocks[i + 1]); + } + + // reverse_pass + for (int i = static_cast(blocks.size()) - 1; i > 0; --i) + planner_reverse_pass_kernel(blocks[i - 1], blocks[i]); + + recalculate_trapezoids(blocks); + + size_t n_blocks_process = blocks.size() - keep_last_n_blocks; +// m_g1_times.reserve(m_g1_times.size() + n_blocks_process); + for (size_t i = 0; i < n_blocks_process; ++i) { + float block_time = blocks[i].time(); + time += block_time; + gcode_time.cache += block_time; + +// if (block.g1_line_id >= 0) +// m_g1_times.emplace_back(block.g1_line_id, time); + } + + if (keep_last_n_blocks) + blocks.erase(blocks.begin(), blocks.begin() + n_blocks_process); + else + blocks.clear(); +} + +void GCodeProcessor::TimeProcessor::reset() +{ + extruder_unloaded = true; + machine_limits = MachineEnvelopeConfig(); + filament_load_times = std::vector(); + filament_unload_times = std::vector(); + for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + machines[i].reset(); + } + machines[static_cast(ETimeMode::Normal)].enabled = true; +} + unsigned int GCodeProcessor::s_result_id = 0; void GCodeProcessor::apply_config(const PrintConfig& config) @@ -61,6 +310,28 @@ void GCodeProcessor::apply_config(const PrintConfig& config) for (size_t id = 0; id < extruders_count; ++id) { m_extruders_color[id] = static_cast(id); } + + m_time_processor.machine_limits = reinterpret_cast(config); + // Filament load / unload times are not specific to a firmware flavor. Let anybody use it if they find it useful. + // As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they + // are considered to be active for the single extruder multi-material printers only. + m_time_processor.filament_load_times.clear(); + for (double d : config.filament_load_time.values) { + m_time_processor.filament_load_times.push_back(static_cast(d)); + } + m_time_processor.filament_unload_times.clear(); + for (double d : config.filament_unload_time.values) { + m_time_processor.filament_unload_times.push_back(static_cast(d)); + } + for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i); + m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION; + } +} + +void GCodeProcessor::enable_stealth_time_estimator(bool enabled) +{ + m_time_processor.machines[static_cast(ETimeMode::Stealth)].enabled = enabled; } void GCodeProcessor::reset() @@ -71,9 +342,9 @@ void GCodeProcessor::reset() m_extruder_offsets = std::vector(1, Vec3f::Zero()); m_flavor = gcfRepRap; - std::fill(m_start_position.begin(), m_start_position.end(), 0.0f); - std::fill(m_end_position.begin(), m_end_position.end(), 0.0f); - std::fill(m_origin.begin(), m_origin.end(), 0.0f); + m_start_position = { 0.0f, 0.0f, 0.0f, 0.0f }; + m_end_position = { 0.0f, 0.0f, 0.0f, 0.0f }; + m_origin = { 0.0f, 0.0f, 0.0f, 0.0f }; m_cached_position.reset(); m_feedrate = 0.0f; @@ -87,6 +358,8 @@ void GCodeProcessor::reset() m_extruders_color = ExtrudersColor(); m_cp_color.reset(); + m_time_processor.reset(); + m_result.reset(); m_result.id = ++s_result_id; } @@ -101,11 +374,43 @@ void GCodeProcessor::process_file(const std::string& filename) m_result.moves.emplace_back(MoveVertex()); m_parser.parse_file(filename, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { process_gcode_line(line); }); + // process the remaining time blocks + for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + TimeMachine& machine = m_time_processor.machines[i]; + TimeMachine::CustomGCodeTime& gcode_time = machine.gcode_time; + machine.calculate_time(); + if (gcode_time.needed && gcode_time.cache != 0.0f) + gcode_time.times.push_back({ CustomGCode::ColorChange, gcode_time.cache }); + } + #if ENABLE_GCODE_VIEWER_STATISTICS m_result.time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start_time).count(); #endif // ENABLE_GCODE_VIEWER_STATISTICS } +std::string GCodeProcessor::get_time_dhm(ETimeMode mode) const +{ + std::string ret = "N/A"; + if (mode < ETimeMode::Count) + ret = short_time(get_time_dhms(m_time_processor.machines[static_cast(mode)].time)); + return ret; +} + +std::vector>> GCodeProcessor::get_custom_gcode_times(ETimeMode mode, bool include_remaining) const +{ + std::vector>> ret; + if (mode < ETimeMode::Count) { + const TimeMachine& machine = m_time_processor.machines[static_cast(mode)]; + float total_time = 0.0f; + for (const auto& [type, time] : machine.gcode_time.times) { + float remaining = include_remaining ? machine.time - total_time : 0.0f; + ret.push_back({ type, { time, remaining } }); + total_time += time; + } + } + return ret; +} + void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) { /* std::cout << line.raw() << std::endl; */ @@ -126,6 +431,8 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) case 1: { process_G1(line); break; } // Move case 10: { process_G10(line); break; } // Retract case 11: { process_G11(line); break; } // Unretract + case 20: { process_G20(line); break; } // Set Units to Inches + case 21: { process_G21(line); break; } // Set Units to Millimeters case 22: { process_G22(line); break; } // Firmware controlled retract case 23: { process_G23(line); break; } // Firmware controlled unretract case 90: { process_G90(line); break; } // Set to Absolute Positioning @@ -139,6 +446,7 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) { switch (::atoi(&cmd[1])) { + case 1: { process_M1(line); break; } // Sleep or Conditional stop case 82: { process_M82(line); break; } // Set extruder to absolute mode case 83: { process_M83(line); break; } // Set extruder to relative mode case 106: { process_M106(line); break; } // Set fan speed @@ -146,8 +454,15 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) case 108: { process_M108(line); break; } // Set tool (Sailfish) case 132: { process_M132(line); break; } // Recall stored home offsets case 135: { process_M135(line); break; } // Set tool (MakerWare) + case 201: { process_M201(line); break; } // Set max printing acceleration + case 203: { process_M203(line); break; } // Set maximum feedrate + case 204: { process_M204(line); break; } // Set default acceleration + case 205: { process_M205(line); break; } // Advanced settings + case 221: { process_M221(line); break; } // Set extrude factor override percentage case 401: { process_M401(line); break; } // Repetier: Store x, y and z position case 402: { process_M402(line); break; } // Repetier: Go to stored position + case 566: { process_M566(line); break; } // Set allowable instantaneous speed change + case 702: { process_M702(line); break; } // Unload the current filament into the MK3 MMU2 unit at the end of print. default: { break; } } break; @@ -160,8 +475,7 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) default: { break; } } } - else - { + else { std::string comment = line.comment(); if (comment.length() > 1) // process tags embedded into comments @@ -179,8 +493,7 @@ void GCodeProcessor::process_tags(const std::string& comment) int role = std::stoi(comment.substr(pos + Extrusion_Role_Tag.length())); if (is_valid_extrusion_role(role)) m_extrusion_role = static_cast(role); - else - { + else { // todo: show some error ? } } @@ -247,11 +560,12 @@ void GCodeProcessor::process_tags(const std::string& comment) if (m_cp_color.counter == UCHAR_MAX) m_cp_color.counter = 0; - if (m_extruder_id == extruder_id) - { + if (m_extruder_id == extruder_id) { m_cp_color.current = m_extruders_color[extruder_id]; store_move_vertex(EMoveType::Color_change); } + + process_custom_gcode_time(CustomGCode::ColorChange); } catch (...) { @@ -265,6 +579,7 @@ void GCodeProcessor::process_tags(const std::string& comment) pos = comment.find(Pause_Print_Tag); if (pos != comment.npos) { store_move_vertex(EMoveType::Pause_Print); + process_custom_gcode_time(CustomGCode::PausePrint); return; } @@ -306,12 +621,14 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) type = EMoveType::Travel; else type = EMoveType::Retract; - } else if (delta_pos[E] > 0.0f) { + } + else if (delta_pos[E] > 0.0f) { if (delta_pos[X] == 0.0f && delta_pos[Y] == 0.0f && delta_pos[Z] == 0.0f) type = EMoveType::Unretract; else if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f)) type = EMoveType::Extrude; - } else if (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f || delta_pos[Z] != 0.0f) + } + else if (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f || delta_pos[Z] != 0.0f) type = EMoveType::Travel; #if ENABLE_GCODE_VIEWER_AS_STATE @@ -351,7 +668,165 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) if (max_abs_delta == 0.0f) return; - // store g1 move + // time estimate section + auto move_length = [](const AxisCoords& delta_pos) { + float sq_xyz_length = sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z]); + return (sq_xyz_length > 0.0f) ? std::sqrt(sq_xyz_length) : std::abs(delta_pos[E]); + }; + + auto is_extruder_only_move = [](const AxisCoords& delta_pos) { + return (delta_pos[X] == 0.0f) && (delta_pos[Y] == 0.0f) && (delta_pos[Z] == 0.0f) && (delta_pos[E] != 0.0f); + }; + + float distance = move_length(delta_pos); + assert(distance != 0.0f); + float inv_distance = 1.0f / distance; + + for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + TimeMachine& machine = m_time_processor.machines[i]; + if (!machine.enabled) + continue; + + TimeMachine::State& curr = machine.curr; + TimeMachine::State& prev = machine.prev; + std::vector& blocks = machine.blocks; + + curr.feedrate = (delta_pos[E] == 0.0f) ? + minimum_travel_feedrate(static_cast(i), m_feedrate) : + minimum_feedrate(static_cast(i), m_feedrate); + + TimeBlock block; + block.distance = distance; + + // calculates block cruise feedrate + float min_feedrate_factor = 1.0f; + for (unsigned char a = X; a <= E; ++a) { + curr.axis_feedrate[a] = curr.feedrate * delta_pos[a] * inv_distance; + if (a == E) + curr.axis_feedrate[a] *= machine.extrude_factor_override_percentage; + + curr.abs_axis_feedrate[a] = std::abs(curr.axis_feedrate[a]); + if (curr.abs_axis_feedrate[a] != 0.0f) { + float axis_max_feedrate = get_axis_max_feedrate(static_cast(i), static_cast(a)); + if (axis_max_feedrate != 0.0f) + min_feedrate_factor = std::min(min_feedrate_factor, axis_max_feedrate / curr.abs_axis_feedrate[a]); + } + } + + block.feedrate_profile.cruise = min_feedrate_factor * curr.feedrate; + + if (min_feedrate_factor < 1.0f) { + for (unsigned char a = X; a <= E; ++a) { + curr.axis_feedrate[a] *= min_feedrate_factor; + curr.abs_axis_feedrate[a] *= min_feedrate_factor; + } + } + + // calculates block acceleration + float acceleration = is_extruder_only_move(delta_pos) ? + get_retract_acceleration(static_cast(i)) : + get_acceleration(static_cast(i)); + + for (unsigned char a = X; a <= E; ++a) { + float axis_max_acceleration = get_axis_max_acceleration(static_cast(i), static_cast(a)); + if (acceleration * std::abs(delta_pos[a]) * inv_distance > axis_max_acceleration) + acceleration = axis_max_acceleration; + } + + block.acceleration = acceleration; + + // calculates block exit feedrate + curr.safe_feedrate = block.feedrate_profile.cruise; + + for (unsigned char a = X; a <= E; ++a) { + float axis_max_jerk = get_axis_max_jerk(static_cast(i), static_cast(a)); + if (curr.abs_axis_feedrate[a] > axis_max_jerk) + curr.safe_feedrate = std::min(curr.safe_feedrate, axis_max_jerk); + } + + block.feedrate_profile.exit = curr.safe_feedrate; + + static const float PREVIOUS_FEEDRATE_THRESHOLD = 0.0001f; + + // calculates block entry feedrate + float vmax_junction = curr.safe_feedrate; + if (!blocks.empty() && prev.feedrate > PREVIOUS_FEEDRATE_THRESHOLD) { + bool prev_speed_larger = prev.feedrate > block.feedrate_profile.cruise; + float smaller_speed_factor = prev_speed_larger ? (block.feedrate_profile.cruise / prev.feedrate) : (prev.feedrate / block.feedrate_profile.cruise); + // 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_profile.cruise : prev.feedrate; + + float v_factor = 1.0f; + bool limited = false; + + for (unsigned char a = X; a <= E; ++a) { + // 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_entry = curr.axis_feedrate[a]; + + if (prev_speed_larger) + v_exit *= smaller_speed_factor; + + if (limited) { + v_exit *= v_factor; + v_entry *= v_factor; + } + + // Calculate the jerk depending on whether the axis is coasting in the same direction or reversing a direction. + float jerk = + (v_exit > v_entry) ? + (((v_entry > 0.0f) || (v_exit < 0.0f)) ? + // coasting + (v_exit - v_entry) : + // axis reversal + std::max(v_exit, -v_entry)) : + // v_exit <= v_entry + (((v_entry < 0.0f) || (v_exit > 0.0f)) ? + // coasting + (v_entry - v_exit) : + // axis reversal + std::max(-v_exit, v_entry)); + + float axis_max_jerk = get_axis_max_jerk(static_cast(i), static_cast(a)); + if (jerk > axis_max_jerk) { + v_factor *= axis_max_jerk / jerk; + limited = true; + } + } + + if (limited) + vmax_junction *= v_factor; + + // Now the transition velocity is known, which maximizes the shared exit / entry velocity while + // respecting the jerk factors, it may be possible, that applying separate safe exit / entry velocities will achieve faster prints. + 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. + if ((prev.safe_feedrate > vmax_junction_threshold) && (curr.safe_feedrate > vmax_junction_threshold)) + vmax_junction = curr.safe_feedrate; + } + + float v_allowable = max_allowable_speed(-acceleration, curr.safe_feedrate, block.distance); + block.feedrate_profile.entry = std::min(vmax_junction, v_allowable); + + block.max_entry_speed = vmax_junction; + block.flags.nominal_length = (block.feedrate_profile.cruise <= v_allowable); + block.flags.recalculate = true; + block.safe_feedrate = curr.safe_feedrate; + + // calculates block trapezoid + block.calculate_trapezoid(); + + // updates previous + prev = curr; + + blocks.push_back(block); + + if (blocks.size() > TimeProcessor::Planner::refresh_threshold) + machine.calculate_time(TimeProcessor::Planner::queue_size); + } + + // store move store_move_vertex(move_type(delta_pos)); } @@ -367,6 +842,16 @@ void GCodeProcessor::process_G11(const GCodeReader::GCodeLine& line) store_move_vertex(EMoveType::Unretract); } +void GCodeProcessor::process_G20(const GCodeReader::GCodeLine& line) +{ + m_units = EUnits::Inches; +} + +void GCodeProcessor::process_G21(const GCodeReader::GCodeLine& line) +{ + m_units = EUnits::Millimeters; +} + void GCodeProcessor::process_G22(const GCodeReader::GCodeLine& line) { // stores retract move @@ -391,32 +876,34 @@ void GCodeProcessor::process_G91(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_G92(const GCodeReader::GCodeLine& line) { - float lengthsScaleFactor = (m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f; - bool anyFound = false; + float lengths_scale_factor = (m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f; + bool any_found = false; if (line.has_x()) { - m_origin[X] = m_end_position[X] - line.x() * lengthsScaleFactor; - anyFound = true; + m_origin[X] = m_end_position[X] - line.x() * lengths_scale_factor; + any_found = true; } if (line.has_y()) { - m_origin[Y] = m_end_position[Y] - line.y() * lengthsScaleFactor; - anyFound = true; + m_origin[Y] = m_end_position[Y] - line.y() * lengths_scale_factor; + any_found = true; } if (line.has_z()) { - m_origin[Z] = m_end_position[Z] - line.z() * lengthsScaleFactor; - anyFound = true; + m_origin[Z] = m_end_position[Z] - line.z() * lengths_scale_factor; + any_found = true; } if (line.has_e()) { // extruder coordinate can grow to the point where its float representation does not allow for proper addition with small increments, // we set the value taken from the G92 line as the new current position for it - m_end_position[E] = line.e() * lengthsScaleFactor; - anyFound = true; + m_end_position[E] = line.e() * lengths_scale_factor; + any_found = true; } + else + simulate_st_synchronize(); - if (!anyFound && !line.has_unknown_axis()) { + if (!any_found && !line.has_unknown_axis()) { // The G92 may be called for axes that PrusaSlicer does not recognize, for example see GH issue #3510, // where G92 A0 B0 is called although the extruder axis is till E. for (unsigned char a = X; a <= E; ++a) { @@ -425,6 +912,11 @@ void GCodeProcessor::process_G92(const GCodeReader::GCodeLine& line) } } +void GCodeProcessor::process_M1(const GCodeReader::GCodeLine& line) +{ + simulate_st_synchronize(); +} + void GCodeProcessor::process_M82(const GCodeReader::GCodeLine& line) { m_e_local_positioning_type = EPositioningType::Absolute; @@ -501,6 +993,117 @@ void GCodeProcessor::process_M135(const GCodeReader::GCodeLine& line) process_T(cmd.substr(pos)); } +void GCodeProcessor::process_M201(const GCodeReader::GCodeLine& line) +{ + // see http://reprap.org/wiki/G-code#M201:_Set_max_printing_acceleration + float factor = (m_flavor != gcfRepRap && m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f; + + for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + if (line.has_x()) + set_option_value(m_time_processor.machine_limits.machine_max_acceleration_x, i, line.x() * factor); + + if (line.has_y() && i < m_time_processor.machine_limits.machine_max_acceleration_y.values.size()) + set_option_value(m_time_processor.machine_limits.machine_max_acceleration_y, i, line.y() * factor); + + if (line.has_z() && i < m_time_processor.machine_limits.machine_max_acceleration_z.values.size()) + set_option_value(m_time_processor.machine_limits.machine_max_acceleration_z, i, line.z() * factor); + + if (line.has_e() && i < m_time_processor.machine_limits.machine_max_acceleration_e.values.size()) + set_option_value(m_time_processor.machine_limits.machine_max_acceleration_e, i, line.e() * factor); + } +} + +void GCodeProcessor::process_M203(const GCodeReader::GCodeLine& line) +{ + // see http://reprap.org/wiki/G-code#M203:_Set_maximum_feedrate + if (m_flavor == gcfRepetier) + return; + + // see http://reprap.org/wiki/G-code#M203:_Set_maximum_feedrate + // http://smoothieware.org/supported-g-codes + float factor = (m_flavor == gcfMarlin || m_flavor == gcfSmoothie) ? 1.0f : MMMIN_TO_MMSEC; + + for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + if (line.has_x()) + set_option_value(m_time_processor.machine_limits.machine_max_feedrate_x, i, line.x() * factor); + + if (line.has_y()) + set_option_value(m_time_processor.machine_limits.machine_max_feedrate_y, i, line.y() * factor); + + if (line.has_z()) + set_option_value(m_time_processor.machine_limits.machine_max_feedrate_z, i, line.z() * factor); + + if (line.has_e()) + set_option_value(m_time_processor.machine_limits.machine_max_feedrate_e, i, line.e() * factor); + } +} + +void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line) +{ + float value; + for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + if (line.has_value('S', value)) { + // Legacy acceleration format. This format is used by the legacy Marlin, MK2 or MK3 firmware, + // and it is also generated by Slic3r to control acceleration per extrusion type + // (there is a separate acceleration settings in Slicer for perimeter, first layer etc). + set_acceleration(static_cast(i), value); + if (line.has_value('T', value)) + set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value); + } + else { + // New acceleration format, compatible with the upstream Marlin. + if (line.has_value('P', value)) + set_acceleration(static_cast(i), value); + if (line.has_value('R', value)) + set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value); + if (line.has_value('T', value)) { + // Interpret the T value as the travel acceleration in the new Marlin format. + //FIXME Prusa3D firmware currently does not support travel acceleration value independent from the extruding acceleration value. + // set_travel_acceleration(value); + } + } + } +} + +void GCodeProcessor::process_M205(const GCodeReader::GCodeLine& line) +{ + for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + if (line.has_x()) { + float max_jerk = line.x(); + set_option_value(m_time_processor.machine_limits.machine_max_jerk_x, i, max_jerk); + set_option_value(m_time_processor.machine_limits.machine_max_jerk_y, i, max_jerk); + } + + if (line.has_y()) + set_option_value(m_time_processor.machine_limits.machine_max_jerk_y, i, line.y()); + + if (line.has_z()) + set_option_value(m_time_processor.machine_limits.machine_max_jerk_z, i, line.z()); + + if (line.has_e()) + set_option_value(m_time_processor.machine_limits.machine_max_jerk_e, i, line.e()); + + float value; + if (line.has_value('S', value)) + set_option_value(m_time_processor.machine_limits.machine_min_extruding_rate, i, value); + + if (line.has_value('T', value)) + set_option_value(m_time_processor.machine_limits.machine_min_travel_rate, i, value); + } +} + +void GCodeProcessor::process_M221(const GCodeReader::GCodeLine& line) +{ + float value_s; + float value_t; + if (line.has_value('S', value_s) && !line.has_value('T', value_t)) { + value_s *= 0.01f; + for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + m_time_processor.machines[i].extrude_factor_override_percentage = value_s; + } + } +} + void GCodeProcessor::process_M401(const GCodeReader::GCodeLine& line) { if (m_flavor != gcfRepetier) @@ -544,6 +1147,34 @@ void GCodeProcessor::process_M402(const GCodeReader::GCodeLine& line) m_feedrate = p; } +void GCodeProcessor::process_M566(const GCodeReader::GCodeLine& line) +{ + for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + if (line.has_x()) + set_option_value(m_time_processor.machine_limits.machine_max_jerk_x, i, line.x() * MMMIN_TO_MMSEC); + + if (line.has_y()) + set_option_value(m_time_processor.machine_limits.machine_max_jerk_y, i, line.y() * MMMIN_TO_MMSEC); + + if (line.has_z()) + set_option_value(m_time_processor.machine_limits.machine_max_jerk_z, i, line.z() * MMMIN_TO_MMSEC); + + if (line.has_e()) + set_option_value(m_time_processor.machine_limits.machine_max_jerk_e, i, line.e() * MMMIN_TO_MMSEC); + } +} + +void GCodeProcessor::process_M702(const GCodeReader::GCodeLine& line) +{ + if (line.has('C')) { + // MK3 MMU2 specific M code: + // M702 C is expected to be sent by the custom end G-code when finalizing a print. + // The MK3 unit shall unload and park the active filament into the MMU2 unit. + m_time_processor.extruder_unloaded = true; + simulate_st_synchronize(get_filament_unload_time(m_extruder_id)); + } +} + void GCodeProcessor::process_T(const GCodeReader::GCodeLine& line) { process_T(line.cmd()); @@ -560,8 +1191,16 @@ void GCodeProcessor::process_T(const std::string& command) if (id >= extruders_count) BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid toolchange, maybe from a custom gcode."; else { + unsigned char old_extruder_id = m_extruder_id; m_extruder_id = id; m_cp_color.current = m_extruders_color[id]; + // Specific to the MK3 MMU2: + // The initial value of extruder_unloaded is set to true indicating + // that the filament is parked in the MMU2 unit and there is nothing to be unloaded yet. + float extra_time = get_filament_unload_time(static_cast(old_extruder_id)); + m_time_processor.extruder_unloaded = false; + extra_time += get_filament_load_time(static_cast(m_extruder_id)); + simulate_st_synchronize(extra_time); } // store tool change move @@ -593,6 +1232,120 @@ void GCodeProcessor::store_move_vertex(EMoveType type) m_result.moves.emplace_back(vertex); } +float GCodeProcessor::minimum_feedrate(ETimeMode mode, float feedrate) const +{ + if (m_time_processor.machine_limits.machine_min_extruding_rate.empty()) + return feedrate; + + return std::max(feedrate, get_option_value(m_time_processor.machine_limits.machine_min_extruding_rate, static_cast(mode))); +} + +float GCodeProcessor::minimum_travel_feedrate(ETimeMode mode, float feedrate) const +{ + if (m_time_processor.machine_limits.machine_min_travel_rate.empty()) + return feedrate; + + return std::max(feedrate, get_option_value(m_time_processor.machine_limits.machine_min_travel_rate, static_cast(mode))); +} + +float GCodeProcessor::get_axis_max_feedrate(ETimeMode mode, Axis axis) const +{ + switch (axis) + { + case X: { return get_option_value(m_time_processor.machine_limits.machine_max_feedrate_x, static_cast(mode)); } + case Y: { return get_option_value(m_time_processor.machine_limits.machine_max_feedrate_y, static_cast(mode)); } + case Z: { return get_option_value(m_time_processor.machine_limits.machine_max_feedrate_z, static_cast(mode)); } + case E: { return get_option_value(m_time_processor.machine_limits.machine_max_feedrate_e, static_cast(mode)); } + default: { return 0.0f; } + } +} + +float GCodeProcessor::get_axis_max_acceleration(ETimeMode mode, Axis axis) const +{ + switch (axis) + { + case X: { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_x, static_cast(mode)); } + case Y: { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_y, static_cast(mode)); } + case Z: { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_z, static_cast(mode)); } + case E: { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_e, static_cast(mode)); } + default: { return 0.0f; } + } +} + +float GCodeProcessor::get_axis_max_jerk(ETimeMode mode, Axis axis) const +{ + switch (axis) + { + case X: { return get_option_value(m_time_processor.machine_limits.machine_max_jerk_x, static_cast(mode)); } + case Y: { return get_option_value(m_time_processor.machine_limits.machine_max_jerk_y, static_cast(mode)); } + case Z: { return get_option_value(m_time_processor.machine_limits.machine_max_jerk_z, static_cast(mode)); } + case E: { return get_option_value(m_time_processor.machine_limits.machine_max_jerk_e, static_cast(mode)); } + default: { return 0.0f; } + } +} + +float GCodeProcessor::get_retract_acceleration(ETimeMode mode) const +{ + return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, static_cast(mode)); +} + +float GCodeProcessor::get_acceleration(ETimeMode mode) const +{ + size_t id = static_cast(mode); + return (id < m_time_processor.machines.size()) ? m_time_processor.machines[id].acceleration : DEFAULT_ACCELERATION; +} + +void GCodeProcessor::set_acceleration(ETimeMode mode, float value) +{ + size_t id = static_cast(mode); + if (id < m_time_processor.machines.size()) { + float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, id); + m_time_processor.machines[id].acceleration = (max_acceleration == 0.0f) ? value : std::min(value, max_acceleration); + } +} + +float GCodeProcessor::get_filament_load_time(size_t extruder_id) +{ + return (m_time_processor.filament_load_times.empty() || m_time_processor.extruder_unloaded) ? + 0.0f : + ((extruder_id < m_time_processor.filament_load_times.size()) ? + m_time_processor.filament_load_times[extruder_id] : m_time_processor.filament_load_times.front()); +} + +float GCodeProcessor::get_filament_unload_time(size_t extruder_id) +{ + return (m_time_processor.filament_unload_times.empty() || m_time_processor.extruder_unloaded) ? + 0.0f : + ((extruder_id < m_time_processor.filament_unload_times.size()) ? + m_time_processor.filament_unload_times[extruder_id] : m_time_processor.filament_unload_times.front()); +} + +void GCodeProcessor::process_custom_gcode_time(CustomGCode::Type code) +{ + for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + TimeMachine& machine = m_time_processor.machines[i]; + if (!machine.enabled) + continue; + + TimeMachine::CustomGCodeTime& gcode_time = machine.gcode_time; + gcode_time.needed = true; + //FIXME this simulates st_synchronize! is it correct? + // The estimated time may be longer than the real print time. + machine.simulate_st_synchronize(); + if (gcode_time.cache != 0.0f) { + gcode_time.times.push_back({ code, gcode_time.cache }); + gcode_time.cache = 0.0f; + } + } +} + +void GCodeProcessor::simulate_st_synchronize(float additional_time) +{ + for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + m_time_processor.machines[i].simulate_st_synchronize(additional_time); + } +} + } /* namespace Slic3r */ #endif // ENABLE_GCODE_VIEWER diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 3f596c9c2b..9878eea9dd 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -5,6 +5,8 @@ #include "libslic3r/GCodeReader.hpp" #include "libslic3r/Point.hpp" #include "libslic3r/ExtrusionEntity.hpp" +#include "libslic3r/PrintConfig.hpp" +#include "libslic3r/CustomGCode.hpp" #include #include @@ -41,7 +43,7 @@ namespace Slic3r { struct CachedPosition { AxisCoords position; // mm - float feedrate; // mm/s + float feedrate; // mm/s void reset(); }; @@ -54,6 +56,118 @@ namespace Slic3r { void reset(); }; + public: + struct FeedrateProfile + { + float entry{ 0.0f }; // mm/s + float cruise{ 0.0f }; // mm/s + float exit{ 0.0f }; // mm/s + }; + + struct Trapezoid + { + float accelerate_until{ 0.0f }; // mm + float decelerate_after{ 0.0f }; // mm + float cruise_feedrate{ 0.0f }; // mm/sec + + float acceleration_time(float entry_feedrate, float acceleration) const; + float cruise_time() const; + float deceleration_time(float distance, float acceleration) const; + float cruise_distance() const; + }; + + struct TimeBlock + { + struct Flags + { + bool recalculate{ false }; + bool nominal_length{ false }; + }; + + float distance{ 0.0f }; // mm + float acceleration{ 0.0f }; // mm/s^2 + float max_entry_speed{ 0.0f }; // mm/s + float safe_feedrate{ 0.0f }; // mm/s + Flags flags; + FeedrateProfile feedrate_profile; + Trapezoid trapezoid; + + // Calculates this block's trapezoid + void calculate_trapezoid(); + + float time() const; + }; + + enum class ETimeMode : unsigned char + { + Normal, + Stealth, + Count + }; + + private: + struct TimeMachine + { + struct State + { + float feedrate; // mm/s + float safe_feedrate; // mm/s + AxisCoords axis_feedrate; // mm/s + AxisCoords abs_axis_feedrate; // mm/s + + void reset(); + }; + + struct CustomGCodeTime + { + bool needed; + float cache; + std::vector> times; + + void reset(); + }; + + bool enabled; + float acceleration; // mm/s^2 + float extrude_factor_override_percentage; + float time; // s + State curr; + State prev; + CustomGCodeTime gcode_time; + std::vector blocks; + + void reset(); + + // Simulates firmware st_synchronize() call + void simulate_st_synchronize(float additional_time = 0.0f); + void calculate_time(size_t keep_last_n_blocks = 0); + }; + + struct TimeProcessor + { + struct Planner + { + // Size of the firmware planner queue. The old 8-bit Marlins usually just managed 16 trapezoidal blocks. + // Let's be conservative and plan for newer boards with more memory. + static constexpr size_t queue_size = 64; + // The firmware recalculates last planner_queue_size trapezoidal blocks each time a new block is added. + // We are not simulating the firmware exactly, we calculate a sequence of blocks once a reasonable number of blocks accumulate. + static constexpr size_t refresh_threshold = queue_size * 4; + }; + + // extruder_id is currently used to correctly calculate filament load / unload times into the total print time. + // This is currently only really used by the MK3 MMU2: + // extruder_unloaded = true means no filament is loaded yet, all the filaments are parked in the MK3 MMU2 unit. + bool extruder_unloaded; + MachineEnvelopeConfig machine_limits; + // Additional load / unload times for a filament exchange sequence. + std::vector filament_load_times; + std::vector filament_unload_times; + std::array(ETimeMode::Count)> machines; + + void reset(); + }; + public: enum class EMoveType : unsigned char { @@ -85,21 +199,6 @@ namespace Slic3r { float time{ 0.0f }; // s float volumetric_rate() const { return feedrate * mm3_per_mm; } - - std::string to_string() const - { - std::string str = std::to_string((int)type); - str += ", " + std::to_string((int)extrusion_role); - str += ", " + Slic3r::to_string((Vec3d)position.cast()); - str += ", " + std::to_string(extruder_id); - str += ", " + std::to_string(cp_color_id); - str += ", " + std::to_string(feedrate); - str += ", " + std::to_string(width); - str += ", " + std::to_string(height); - str += ", " + std::to_string(mm3_per_mm); - str += ", " + std::to_string(fan_speed); - return str; - } }; struct Result @@ -124,13 +223,13 @@ namespace Slic3r { GCodeFlavor m_flavor; AxisCoords m_start_position; // mm - AxisCoords m_end_position; // mm - AxisCoords m_origin; // mm + AxisCoords m_end_position; // mm + AxisCoords m_origin; // mm CachedPosition m_cached_position; - float m_feedrate; // mm/s - float m_width; // mm - float m_height; // mm + float m_feedrate; // mm/s + float m_width; // mm + float m_height; // mm float m_mm3_per_mm; float m_fan_speed; // percentage ExtrusionRole m_extrusion_role; @@ -138,6 +237,8 @@ namespace Slic3r { ExtrudersColor m_extruders_color; CpColor m_cp_color; + TimeProcessor m_time_processor; + Result m_result; static unsigned int s_result_id; @@ -145,6 +246,7 @@ namespace Slic3r { GCodeProcessor() { reset(); } void apply_config(const PrintConfig& config); + void enable_stealth_time_estimator(bool enabled); void reset(); const Result& get_result() const { return m_result; } @@ -153,6 +255,9 @@ namespace Slic3r { // Process the gcode contained in the file with the given filename void process_file(const std::string& filename); + std::string get_time_dhm(ETimeMode mode) const; + std::vector>> get_custom_gcode_times(ETimeMode mode, bool include_remaining) const; + private: void process_gcode_line(const GCodeReader::GCodeLine& line); @@ -169,6 +274,12 @@ namespace Slic3r { // Unretract void process_G11(const GCodeReader::GCodeLine& line); + // Set Units to Inches + void process_G20(const GCodeReader::GCodeLine& line); + + // Set Units to Millimeters + void process_G21(const GCodeReader::GCodeLine& line); + // Firmware controlled Retract void process_G22(const GCodeReader::GCodeLine& line); @@ -184,6 +295,9 @@ namespace Slic3r { // Set Position void process_G92(const GCodeReader::GCodeLine& line); + // Sleep or Conditional stop + void process_M1(const GCodeReader::GCodeLine& line); + // Set extruder to absolute mode void process_M82(const GCodeReader::GCodeLine& line); @@ -205,17 +319,54 @@ namespace Slic3r { // Set tool (MakerWare) void process_M135(const GCodeReader::GCodeLine& line); + // Set max printing acceleration + void process_M201(const GCodeReader::GCodeLine& line); + + // Set maximum feedrate + void process_M203(const GCodeReader::GCodeLine& line); + + // Set default acceleration + void process_M204(const GCodeReader::GCodeLine& line); + + // Advanced settings + void process_M205(const GCodeReader::GCodeLine& line); + + // Set extrude factor override percentage + void process_M221(const GCodeReader::GCodeLine& line); + // Repetier: Store x, y and z position void process_M401(const GCodeReader::GCodeLine& line); // Repetier: Go to stored position void process_M402(const GCodeReader::GCodeLine& line); + // Set allowable instantaneous speed change + void process_M566(const GCodeReader::GCodeLine& line); + + // Unload the current filament into the MK3 MMU2 unit at the end of print. + void process_M702(const GCodeReader::GCodeLine& line); + // Processes T line (Select Tool) void process_T(const GCodeReader::GCodeLine& line); void process_T(const std::string& command); void store_move_vertex(EMoveType type); + + float minimum_feedrate(ETimeMode mode, float feedrate) const; + float minimum_travel_feedrate(ETimeMode mode, float feedrate) const; + float get_axis_max_feedrate(ETimeMode mode, Axis axis) const; + float get_axis_max_acceleration(ETimeMode mode, Axis axis) const; + float get_axis_max_jerk(ETimeMode mode, Axis axis) const; + float get_retract_acceleration(ETimeMode mode) const; + float get_acceleration(ETimeMode mode) const; + void set_acceleration(ETimeMode mode, float value); + float get_filament_load_time(size_t extruder_id); + float get_filament_unload_time(size_t extruder_id); + + void process_custom_gcode_time(CustomGCode::Type code); + + // Simulates firmware st_synchronize() call + void simulate_st_synchronize(float additional_time = 0.0f); }; } /* namespace Slic3r */ diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp index d67db84819..bc3adefc08 100644 --- a/src/libslic3r/GCodeTimeEstimator.cpp +++ b/src/libslic3r/GCodeTimeEstimator.cpp @@ -678,21 +678,6 @@ namespace Slic3r { return _get_time_minutes(get_time()); } -#if ENABLE_GCODE_VIEWER - std::vector>> GCodeTimeEstimator::get_custom_gcode_times(bool include_remaining) const - { - std::vector>> ret; - - float total_time = 0.0f; - for (const auto& [type, time] : m_custom_gcode_times) { - float remaining = include_remaining ? m_time - total_time : 0.0f; - ret.push_back({ type, { time, remaining } }); - total_time += time; - } - - return ret; - } -#else std::vector> GCodeTimeEstimator::get_custom_gcode_times() const { return m_custom_gcode_times; @@ -736,7 +721,6 @@ namespace Slic3r { } return ret; } -#endif // ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER std::vector>> GCodeTimeEstimator::get_custom_gcode_times_dhm(bool include_remaining) const diff --git a/src/libslic3r/GCodeTimeEstimator.hpp b/src/libslic3r/GCodeTimeEstimator.hpp index cfa12b40be..ce6b2f4af0 100644 --- a/src/libslic3r/GCodeTimeEstimator.hpp +++ b/src/libslic3r/GCodeTimeEstimator.hpp @@ -358,9 +358,6 @@ namespace Slic3r { std::string get_time_minutes() const; // Returns the estimated time, in seconds, for each custom gcode -#if ENABLE_GCODE_VIEWER - std::vector>> get_custom_gcode_times(bool include_remaining) const; -#else std::vector> get_custom_gcode_times() const; // Returns the estimated time, in format DDd HHh MMm SSs, for each color @@ -370,7 +367,6 @@ namespace Slic3r { // Returns the estimated time, in minutes (integer), for each color // If include_remaining==true the strings will be formatted as: "time for color (remaining time at color start)" std::vector get_color_times_minutes(bool include_remaining) const; -#endif // ENABLE_GCODE_VIEWER // Returns the estimated time, in format DDd HHh MMm, for each custom_gcode // If include_remaining==true the strings will be formatted as: "time for custom_gcode (remaining time at color start)" diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 5a1a9868d7..34fab6f307 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -2190,18 +2190,24 @@ std::string Print::output_filename(const std::string &filename_base) const DynamicConfig PrintStatistics::config() const { DynamicConfig config; +#if ENABLE_GCODE_VIEWER + config.set_key_value("print_time", new ConfigOptionString(this->estimated_normal_print_time_str)); + config.set_key_value("normal_print_time", new ConfigOptionString(this->estimated_normal_print_time_str)); + config.set_key_value("silent_print_time", new ConfigOptionString(this->estimated_silent_print_time_str)); +#else std::string normal_print_time = short_time(this->estimated_normal_print_time); std::string silent_print_time = short_time(this->estimated_silent_print_time); config.set_key_value("print_time", new ConfigOptionString(normal_print_time)); config.set_key_value("normal_print_time", new ConfigOptionString(normal_print_time)); config.set_key_value("silent_print_time", new ConfigOptionString(silent_print_time)); - config.set_key_value("used_filament", new ConfigOptionFloat (this->total_used_filament / 1000.)); - config.set_key_value("extruded_volume", new ConfigOptionFloat (this->total_extruded_volume)); - config.set_key_value("total_cost", new ConfigOptionFloat (this->total_cost)); +#endif // ENABLE_GCODE_VIEWER + config.set_key_value("used_filament", new ConfigOptionFloat(this->total_used_filament / 1000.)); + config.set_key_value("extruded_volume", new ConfigOptionFloat(this->total_extruded_volume)); + config.set_key_value("total_cost", new ConfigOptionFloat(this->total_cost)); config.set_key_value("total_toolchanges", new ConfigOptionInt(this->total_toolchanges)); - config.set_key_value("total_weight", new ConfigOptionFloat (this->total_weight)); - config.set_key_value("total_wipe_tower_cost", new ConfigOptionFloat (this->total_wipe_tower_cost)); - config.set_key_value("total_wipe_tower_filament", new ConfigOptionFloat (this->total_wipe_tower_filament)); + config.set_key_value("total_weight", new ConfigOptionFloat(this->total_weight)); + config.set_key_value("total_wipe_tower_cost", new ConfigOptionFloat(this->total_wipe_tower_cost)); + config.set_key_value("total_wipe_tower_filament", new ConfigOptionFloat(this->total_wipe_tower_filament)); return config; } diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index eb9a4fb4b7..b46ec42174 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -303,14 +303,18 @@ private: struct PrintStatistics { PrintStatistics() { clear(); } +#if ENABLE_GCODE_VIEWER std::string estimated_normal_print_time; std::string estimated_silent_print_time; -#if ENABLE_GCODE_VIEWER + std::string estimated_normal_print_time_str; + std::string estimated_silent_print_time_str; std::vector>> estimated_normal_custom_gcode_print_times; std::vector>> estimated_silent_custom_gcode_print_times; std::vector>> estimated_normal_custom_gcode_print_times_str; std::vector>> estimated_silent_custom_gcode_print_times_str; #else + std::string estimated_normal_print_time; + std::string estimated_silent_print_time; std::vector> estimated_normal_custom_gcode_print_times; std::vector> estimated_silent_custom_gcode_print_times; #endif // ENABLE_GCODE_VIEWER @@ -331,14 +335,12 @@ struct PrintStatistics std::string finalize_output_path(const std::string &path_in) const; void clear() { - estimated_normal_print_time.clear(); - estimated_silent_print_time.clear(); #if ENABLE_GCODE_VIEWER estimated_normal_custom_gcode_print_times_str.clear(); estimated_silent_custom_gcode_print_times_str.clear(); - estimated_normal_custom_gcode_print_times.clear(); - estimated_silent_custom_gcode_print_times.clear(); #else + estimated_normal_print_time.clear(); + estimated_silent_print_time.clear(); estimated_normal_custom_gcode_print_times.clear(); estimated_silent_custom_gcode_print_times.clear(); #endif //ENABLE_GCODE_VIEWER diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index aaabd62220..27b0db83f5 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1720,6 +1720,9 @@ void GCodeViewer::render_time_estimate() const if (ps.estimated_normal_print_time == "N/A" && ps.estimated_silent_print_time == "N/A") return; + if (ps.estimated_normal_print_time.empty() && ps.estimated_silent_print_time.empty()) + return; + ImGuiWrapper& imgui = *wxGetApp().imgui(); using Time = std::pair; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 0981740ebf..9c22a6d60a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1322,7 +1322,11 @@ void Sidebar::update_sliced_info_sizer() wxString::Format("%.2f", ps.total_cost); p->sliced_info->SetTextAndShow(siCost, info_text, new_label); +#if ENABLE_GCODE_VIEWER + if (ps.estimated_normal_print_time_str == "N/A" && ps.estimated_silent_print_time_str == "N/A") +#else if (ps.estimated_normal_print_time == "N/A" && ps.estimated_silent_print_time == "N/A") +#endif // ENABLE_GCODE_VIEWER p->sliced_info->SetTextAndShow(siEstimatedTime, "N/A"); else { new_label = _L("Estimated printing time") +":"; @@ -1360,21 +1364,25 @@ void Sidebar::update_sliced_info_sizer() } }; +#if ENABLE_GCODE_VIEWER + if (ps.estimated_normal_print_time_str != "N/A") { + new_label += format_wxstr("\n - %1%", _L("normal mode")); + info_text += format_wxstr("\n%1%", ps.estimated_normal_print_time_str); + fill_labels(ps.estimated_normal_custom_gcode_print_times_str, new_label, info_text); + } + if (ps.estimated_silent_print_time_str != "N/A") { + new_label += format_wxstr("\n - %1%", _L("stealth mode")); + info_text += format_wxstr("\n%1%", ps.estimated_silent_print_time_str); + fill_labels(ps.estimated_silent_custom_gcode_print_times_str, new_label, info_text); +#else if (ps.estimated_normal_print_time != "N/A") { new_label += format_wxstr("\n - %1%", _L("normal mode")); info_text += format_wxstr("\n%1%", ps.estimated_normal_print_time); -#if ENABLE_GCODE_VIEWER - fill_labels(ps.estimated_normal_custom_gcode_print_times_str, new_label, info_text); -#else fill_labels(ps.estimated_normal_custom_gcode_print_times, new_label, info_text); -#endif // ENABLE_GCODE_VIEWER } if (ps.estimated_silent_print_time != "N/A") { new_label += format_wxstr("\n - %1%", _L("stealth mode")); info_text += format_wxstr("\n%1%", ps.estimated_silent_print_time); -#if ENABLE_GCODE_VIEWER - fill_labels(ps.estimated_silent_custom_gcode_print_times_str, new_label, info_text); -#else fill_labels(ps.estimated_silent_custom_gcode_print_times, new_label, info_text); #endif // ENABLE_GCODE_VIEWER } From f7164db68e760c9df9a937b5b3674c9f8bcd3be8 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 17 Jul 2020 08:27:23 +0200 Subject: [PATCH 161/503] GCodeViewer -> Added estimated printing times for move types --- src/libslic3r/GCode.cpp | 28 +---- src/libslic3r/GCode/GCodeProcessor.cpp | 51 ++++++++- src/libslic3r/GCode/GCodeProcessor.hpp | 36 +++--- src/libslic3r/Print.hpp | 19 +++- src/slic3r/GUI/GCodeViewer.cpp | 148 ++++++++++++++++++------- 5 files changed, 195 insertions(+), 87 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 47448954c8..9e365f9558 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -705,27 +705,6 @@ namespace Slic3r { } #if ENABLE_GCODE_VIEWER -// free functions called by GCode::do_export() -namespace DoExport { - static void update_print_stats_estimated_times( - const GCodeProcessor& processor, - const bool silent_time_estimator_enabled, - PrintStatistics& print_statistics) - { - print_statistics.estimated_normal_print_time = processor.get_time_dhm(GCodeProcessor::ETimeMode::Normal); - print_statistics.estimated_normal_custom_gcode_print_times = processor.get_custom_gcode_times(GCodeProcessor::ETimeMode::Normal, true); - if (silent_time_estimator_enabled) { - print_statistics.estimated_silent_print_time = processor.get_time_dhm(GCodeProcessor::ETimeMode::Stealth); - print_statistics.estimated_silent_custom_gcode_print_times = processor.get_custom_gcode_times(GCodeProcessor::ETimeMode::Stealth, true); - } - else { - print_statistics.estimated_silent_print_time = "N/A"; - print_statistics.estimated_silent_custom_gcode_print_times.clear(); - } - } - -} // namespace DoExport - void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb) #else void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb) @@ -787,11 +766,12 @@ void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_ } #if ENABLE_GCODE_VIEWER + print->m_print_statistics.clear_time_estimates(); m_processor.process_file(path_tmp); - if (result != nullptr) + if (result != nullptr) { *result = std::move(m_processor.extract_result()); - - DoExport::update_print_stats_estimated_times(m_processor, m_silent_time_estimator_enabled, print->m_print_statistics); + m_processor.update_print_stats_estimated_times(print->m_print_statistics); + } #endif // ENABLE_GCODE_VIEWER GCodeTimeEstimator::PostProcessData normal_data = m_normal_time_estimator.get_post_process_data(); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 7174e5e36d..7f8efc53fc 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1,5 +1,6 @@ #include "libslic3r/libslic3r.h" #include "libslic3r/Utils.hpp" +#include "libslic3r/Print.hpp" #include "GCodeProcessor.hpp" #include @@ -161,6 +162,7 @@ void GCodeProcessor::TimeMachine::reset() prev.reset(); gcode_time.reset(); blocks = std::vector(); + std::fill(moves_time.begin(), moves_time.end(), 0.0f); } void GCodeProcessor::TimeMachine::simulate_st_synchronize(float additional_time) @@ -264,9 +266,11 @@ void GCodeProcessor::TimeMachine::calculate_time(size_t keep_last_n_blocks) size_t n_blocks_process = blocks.size() - keep_last_n_blocks; // m_g1_times.reserve(m_g1_times.size() + n_blocks_process); for (size_t i = 0; i < n_blocks_process; ++i) { - float block_time = blocks[i].time(); + const TimeBlock& block = blocks[i]; + float block_time = block.time(); time += block_time; gcode_time.cache += block_time; + moves_time[static_cast(block.move_type)] += block_time; // if (block.g1_line_id >= 0) // m_g1_times.emplace_back(block.g1_line_id, time); @@ -388,12 +392,31 @@ void GCodeProcessor::process_file(const std::string& filename) #endif // ENABLE_GCODE_VIEWER_STATISTICS } +void GCodeProcessor::update_print_stats_estimated_times(PrintStatistics& print_statistics) +{ + print_statistics.estimated_normal_print_time = get_time(GCodeProcessor::ETimeMode::Normal); + print_statistics.estimated_normal_custom_gcode_print_times = get_custom_gcode_times(GCodeProcessor::ETimeMode::Normal, true); + print_statistics.estimated_normal_moves_times = get_moves_time(GCodeProcessor::ETimeMode::Normal); + if (m_time_processor.machines[static_cast(GCodeProcessor::ETimeMode::Stealth)].enabled) { + print_statistics.estimated_silent_print_time = get_time(GCodeProcessor::ETimeMode::Stealth); + print_statistics.estimated_silent_custom_gcode_print_times = get_custom_gcode_times(GCodeProcessor::ETimeMode::Stealth, true); + print_statistics.estimated_silent_moves_times = get_moves_time(GCodeProcessor::ETimeMode::Stealth); + } + else { + print_statistics.estimated_silent_print_time = 0.0f; + print_statistics.estimated_silent_custom_gcode_print_times.clear(); + print_statistics.estimated_silent_moves_times.clear(); + } +} + +float GCodeProcessor::get_time(ETimeMode mode) const +{ + return (mode < ETimeMode::Count) ? m_time_processor.machines[static_cast(mode)].time : 0.0f; +} + std::string GCodeProcessor::get_time_dhm(ETimeMode mode) const { - std::string ret = "N/A"; - if (mode < ETimeMode::Count) - ret = short_time(get_time_dhms(m_time_processor.machines[static_cast(mode)].time)); - return ret; + return (mode < ETimeMode::Count) ? short_time(get_time_dhms(m_time_processor.machines[static_cast(mode)].time)) : std::string("N/A"); } std::vector>> GCodeProcessor::get_custom_gcode_times(ETimeMode mode, bool include_remaining) const @@ -411,6 +434,19 @@ std::vector>> GCodeProcesso return ret; } +std::vector> GCodeProcessor::get_moves_time(ETimeMode mode) const +{ + std::vector> ret; + if (mode < ETimeMode::Count) { + for (size_t i = 0; i < m_time_processor.machines[static_cast(mode)].moves_time.size(); ++i) { + float time = m_time_processor.machines[static_cast(mode)].moves_time[i]; + if (time > 0.0f) + ret.push_back({ static_cast(i), time }); + } + } + return ret; +} + void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) { /* std::cout << line.raw() << std::endl; */ @@ -668,6 +704,8 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) if (max_abs_delta == 0.0f) return; + EMoveType type = move_type(delta_pos); + // time estimate section auto move_length = [](const AxisCoords& delta_pos) { float sq_xyz_length = sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z]); @@ -696,6 +734,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) minimum_feedrate(static_cast(i), m_feedrate); TimeBlock block; + block.move_type = type; block.distance = distance; // calculates block cruise feedrate @@ -827,7 +866,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) } // store move - store_move_vertex(move_type(delta_pos)); + store_move_vertex(type); } void GCodeProcessor::process_G10(const GCodeReader::GCodeLine& line) diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 9878eea9dd..214ed3d911 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -13,6 +13,8 @@ namespace Slic3r { + struct PrintStatistics; + class GCodeProcessor { public: @@ -57,6 +59,20 @@ namespace Slic3r { }; public: + enum class EMoveType : unsigned char + { + Noop, + Retract, + Unretract, + Tool_change, + Color_change, + Pause_Print, + Custom_GCode, + Travel, + Extrude, + Count + }; + struct FeedrateProfile { float entry{ 0.0f }; // mm/s @@ -84,6 +100,7 @@ namespace Slic3r { bool nominal_length{ false }; }; + EMoveType move_type{ EMoveType::Noop }; float distance{ 0.0f }; // mm float acceleration{ 0.0f }; // mm/s^2 float max_entry_speed{ 0.0f }; // mm/s @@ -135,6 +152,7 @@ namespace Slic3r { State prev; CustomGCodeTime gcode_time; std::vector blocks; + std::array(EMoveType::Count)> moves_time; void reset(); @@ -169,20 +187,6 @@ namespace Slic3r { }; public: - enum class EMoveType : unsigned char - { - Noop, - Retract, - Unretract, - Tool_change, - Color_change, - Pause_Print, - Custom_GCode, - Travel, - Extrude, - Count - }; - struct MoveVertex { EMoveType type{ EMoveType::Noop }; @@ -255,9 +259,13 @@ namespace Slic3r { // Process the gcode contained in the file with the given filename void process_file(const std::string& filename); + void update_print_stats_estimated_times(PrintStatistics& print_statistics); + float get_time(ETimeMode mode) const; std::string get_time_dhm(ETimeMode mode) const; std::vector>> get_custom_gcode_times(ETimeMode mode, bool include_remaining) const; + std::vector> get_moves_time(ETimeMode mode) const; + private: void process_gcode_line(const GCodeReader::GCodeLine& line); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 4a0657061a..748411cd77 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -304,14 +304,16 @@ struct PrintStatistics { PrintStatistics() { clear(); } #if ENABLE_GCODE_VIEWER - std::string estimated_normal_print_time; - std::string estimated_silent_print_time; + float estimated_normal_print_time; + float estimated_silent_print_time; std::string estimated_normal_print_time_str; std::string estimated_silent_print_time_str; std::vector>> estimated_normal_custom_gcode_print_times; std::vector>> estimated_silent_custom_gcode_print_times; std::vector>> estimated_normal_custom_gcode_print_times_str; std::vector>> estimated_silent_custom_gcode_print_times_str; + std::vector> estimated_normal_moves_times; + std::vector> estimated_silent_moves_times; #else std::string estimated_normal_print_time; std::string estimated_silent_print_time; @@ -336,6 +338,8 @@ struct PrintStatistics void clear() { #if ENABLE_GCODE_VIEWER + estimated_normal_print_time_str.clear(); + estimated_silent_print_time_str.clear(); estimated_normal_custom_gcode_print_times_str.clear(); estimated_silent_custom_gcode_print_times_str.clear(); #else @@ -353,6 +357,17 @@ struct PrintStatistics total_wipe_tower_filament = 0.; filament_stats.clear(); } + +#if ENABLE_GCODE_VIEWER + void clear_time_estimates() { + estimated_normal_print_time = 0.0f; + estimated_silent_print_time = 0.0f; + estimated_normal_custom_gcode_print_times.clear(); + estimated_silent_custom_gcode_print_times.clear(); + estimated_normal_moves_times.clear(); + estimated_silent_moves_times.clear(); + } +#endif //ENABLE_GCODE_VIEWER }; typedef std::vector PrintObjectPtrs; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 27b0db83f5..db95c2b224 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1717,19 +1717,16 @@ void GCodeViewer::render_time_estimate() const return; const PrintStatistics& ps = wxGetApp().plater()->fff_print().print_statistics(); - if (ps.estimated_normal_print_time == "N/A" && ps.estimated_silent_print_time == "N/A") - return; - - if (ps.estimated_normal_print_time.empty() && ps.estimated_silent_print_time.empty()) + if (ps.estimated_normal_print_time <= 0.0f && ps.estimated_silent_print_time <= 0.0f) return; ImGuiWrapper& imgui = *wxGetApp().imgui(); - using Time = std::pair; - using TimesList = std::vector>; + using Times = std::pair; + using TimesList = std::vector>; - // helper structure containig the data needed to render the items - struct Item + // helper structure containig the data needed to render the time items + struct PartialTime { enum class EType : unsigned char { @@ -1741,12 +1738,13 @@ void GCodeViewer::render_time_estimate() const int extruder_id; Color color1; Color color2; - Time time; + Times times; }; - using Items = std::vector; + using PartialTimes = std::vector; - auto append_mode = [this, &imgui](const std::string& time_str, const Items& items) { - auto append_partial_times = [this, &imgui](const Items& items) { + auto append_mode = [this, &imgui](float total_time, const PartialTimes& items, + const std::vector>& moves_time) { + auto append_partial_times = [this, &imgui](const PartialTimes& items) { using Headers = std::vector; const Headers headers = { _u8L("Event"), @@ -1754,19 +1752,19 @@ void GCodeViewer::render_time_estimate() const _u8L("Duration") }; using Offsets = std::array; - auto calc_offsets = [this, &headers](const Items& items) { + auto calc_offsets = [this, &headers](const PartialTimes& items) { Offsets ret = { ImGui::CalcTextSize(headers[0].c_str()).x, ImGui::CalcTextSize(headers[1].c_str()).x }; - for (const Item& item : items) { + for (const PartialTime& item : items) { std::string label; switch (item.type) { - case Item::EType::Print: { label = _u8L("Print"); break; } - case Item::EType::Pause: { label = _u8L("Pause"); break; } - case Item::EType::ColorChange: { label = _u8L("Color change"); break; } + case PartialTime::EType::Print: { label = _u8L("Print"); break; } + case PartialTime::EType::Pause: { label = _u8L("Pause"); break; } + case PartialTime::EType::ColorChange: { label = _u8L("Color change"); break; } } ret[0] = std::max(ret[0], ImGui::CalcTextSize(label.c_str()).x); - ret[1] = std::max(ret[1], ImGui::CalcTextSize(short_time(get_time_dhms(item.time.second)).c_str()).x); + ret[1] = std::max(ret[1], ImGui::CalcTextSize(short_time(get_time_dhms(item.times.second)).c_str()).x); } const ImGuiStyle& style = ImGui::GetStyle(); @@ -1774,7 +1772,7 @@ void GCodeViewer::render_time_estimate() const ret[1] += ret[0] + style.ItemSpacing.x; return ret; }; - auto append_color = [this, &imgui](const Color& color1, const Color& color2, Offsets& offsets, const Time& time) { + auto append_color = [this, &imgui](const Color& color1, const Color& color2, Offsets& offsets, const Times& times) { ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); imgui.text(_u8L("Color change")); ImGui::PopStyleColor(); @@ -1797,7 +1795,7 @@ void GCodeViewer::render_time_estimate() const ImGui::GetColorU32({ color2[0], color2[1], color2[2], 1.0f })); #endif // USE_ICON_HEXAGON ImGui::SameLine(offsets[0]); - imgui.text(short_time(get_time_dhms(time.second - time.first))); + imgui.text(short_time(get_time_dhms(times.second - times.first))); }; if (items.empty()) @@ -1815,48 +1813,116 @@ void GCodeViewer::render_time_estimate() const ImGui::PopStyleColor(); ImGui::Separator(); - for (const Item& item : items) { + for (const PartialTime& item : items) { switch (item.type) { - case Item::EType::Print: + case PartialTime::EType::Print: { ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); imgui.text(_u8L("Print")); ImGui::PopStyleColor(); ImGui::SameLine(offsets[0]); - imgui.text(short_time(get_time_dhms(item.time.second))); + imgui.text(short_time(get_time_dhms(item.times.second))); ImGui::SameLine(offsets[1]); - imgui.text(short_time(get_time_dhms(item.time.first))); + imgui.text(short_time(get_time_dhms(item.times.first))); break; } - case Item::EType::Pause: + case PartialTime::EType::Pause: { ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); imgui.text(_u8L("Pause")); ImGui::PopStyleColor(); ImGui::SameLine(offsets[0]); - imgui.text(short_time(get_time_dhms(item.time.second - item.time.first))); + imgui.text(short_time(get_time_dhms(item.times.second - item.times.first))); break; } - case Item::EType::ColorChange: + case PartialTime::EType::ColorChange: { - append_color(item.color1, item.color2, offsets, item.time); + append_color(item.color1, item.color2, offsets, item.times); break; } } } }; + auto append_move_times = [this, &imgui](float total_time, const std::vector>& moves_time) { + using Headers = std::vector; + const Headers headers = { + _u8L("Type"), + _u8L("Time"), + _u8L("Percentage") + }; + auto move_type_label = [](GCodeProcessor::EMoveType type) { + switch (type) + { + case GCodeProcessor::EMoveType::Noop: { return _u8L("Noop"); } + case GCodeProcessor::EMoveType::Retract: { return _u8L("Retraction"); } + case GCodeProcessor::EMoveType::Unretract: { return _u8L("Unretraction"); } + case GCodeProcessor::EMoveType::Tool_change: { return _u8L("Tool change"); } + case GCodeProcessor::EMoveType::Color_change: { return _u8L("Color change"); } + case GCodeProcessor::EMoveType::Pause_Print: { return _u8L("Pause print"); } + case GCodeProcessor::EMoveType::Custom_GCode: { return _u8L("Custom GCode"); } + case GCodeProcessor::EMoveType::Travel: { return _u8L("Travel"); } + case GCodeProcessor::EMoveType::Extrude: { return _u8L("Extrusion"); } + default: { return _u8L("Unknown"); } + } + }; + using Offsets = std::array; + auto calc_offsets = [this, &headers, move_type_label](const std::vector>& moves_time) { + Offsets ret = { ImGui::CalcTextSize(headers[0].c_str()).x, ImGui::CalcTextSize(headers[1].c_str()).x }; + for (const auto& [type, time] : moves_time) { + ret[0] = std::max(ret[0], ImGui::CalcTextSize(move_type_label(type).c_str()).x); + ret[1] = std::max(ret[1], ImGui::CalcTextSize(short_time(get_time_dhms(time)).c_str()).x); + } + + const ImGuiStyle& style = ImGui::GetStyle(); + ret[0] += 2.0f * style.ItemSpacing.x; + ret[1] += ret[0] + style.ItemSpacing.x; + return ret; + }; + + + if (moves_time.empty()) + return; + + if (!ImGui::CollapsingHeader(_u8L("Moves Time").c_str())) + return; + + Offsets offsets = calc_offsets(moves_time); + + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); + imgui.text(headers[0]); + ImGui::SameLine(offsets[0]); + imgui.text(headers[1]); + ImGui::SameLine(offsets[1]); + imgui.text(headers[2]); + ImGui::PopStyleColor(); + ImGui::Separator(); + + for (const auto& [type, time] : moves_time) { + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); + imgui.text(move_type_label(type)); + ImGui::PopStyleColor(); + ImGui::SameLine(offsets[0]); + imgui.text(short_time(get_time_dhms(time))); + ImGui::SameLine(offsets[1]); + char buf[64]; + ::sprintf(buf, "%.2f%%", 100.0f * time / total_time); + ImGui::TextUnformatted(buf); + } + }; + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); imgui.text(_u8L("Time") + ":"); ImGui::PopStyleColor(); ImGui::SameLine(); - imgui.text(time_str); + imgui.text(short_time(get_time_dhms(total_time))); append_partial_times(items); + append_move_times(total_time, moves_time); }; - auto generate_items = [this](const TimesList& times) { - std::vector items; + auto generate_partial_times = [this](const TimesList& times) { + PartialTimes items; std::vector custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes; int extruders_count = wxGetApp().extruders_edited_cnt(); @@ -1872,8 +1938,8 @@ void GCodeViewer::render_time_estimate() const { auto it = std::find_if(custom_gcode_per_print_z.begin(), custom_gcode_per_print_z.end(), [time_rec](const CustomGCode::Item& item) { return item.type == time_rec.first; }); if (it != custom_gcode_per_print_z.end()) { - items.push_back({ Item::EType::Print, it->extruder, Color(), Color(), time_rec.second }); - items.push_back({ Item::EType::Pause, it->extruder, Color(), Color(), time_rec.second }); + items.push_back({ PartialTime::EType::Print, it->extruder, Color(), Color(), time_rec.second }); + items.push_back({ PartialTime::EType::Pause, it->extruder, Color(), Color(), time_rec.second }); custom_gcode_per_print_z.erase(it); } break; @@ -1882,14 +1948,14 @@ void GCodeViewer::render_time_estimate() const { auto it = std::find_if(custom_gcode_per_print_z.begin(), custom_gcode_per_print_z.end(), [time_rec](const CustomGCode::Item& item) { return item.type == time_rec.first; }); if (it != custom_gcode_per_print_z.end()) { - items.push_back({ Item::EType::Print, it->extruder, Color(), Color(), time_rec.second }); - items.push_back({ Item::EType::ColorChange, it->extruder, last_color[it->extruder - 1], decode_color(it->color), time_rec.second }); + items.push_back({ PartialTime::EType::Print, it->extruder, Color(), Color(), time_rec.second }); + items.push_back({ PartialTime::EType::ColorChange, it->extruder, last_color[it->extruder - 1], decode_color(it->color), time_rec.second }); last_color[it->extruder - 1] = decode_color(it->color); last_extruder_id = it->extruder; custom_gcode_per_print_z.erase(it); } else - items.push_back({ Item::EType::Print, last_extruder_id, Color(), Color(), time_rec.second }); + items.push_back({ PartialTime::EType::Print, last_extruder_id, Color(), Color(), time_rec.second }); break; } @@ -1905,22 +1971,22 @@ void GCodeViewer::render_time_estimate() const ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(-1.0f, 0.5f * static_cast(cnv_size.get_height()))); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::SetNextWindowBgAlpha(0.6f); - imgui.begin(std::string("Time_estimate_2"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove); + imgui.begin(std::string("Time_estimate"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove); // title imgui.title(_u8L("Estimated printing time")); // mode tabs ImGui::BeginTabBar("mode_tabs"); - if (ps.estimated_normal_print_time != "N/A") { + if (ps.estimated_normal_print_time > 0.0f) { if (ImGui::BeginTabItem(_u8L("Normal").c_str())) { - append_mode(ps.estimated_normal_print_time, generate_items(ps.estimated_normal_custom_gcode_print_times)); + append_mode(ps.estimated_normal_print_time, generate_partial_times(ps.estimated_normal_custom_gcode_print_times), ps.estimated_normal_moves_times); ImGui::EndTabItem(); } } - if (ps.estimated_silent_print_time != "N/A") { + if (ps.estimated_silent_print_time > 0.0f) { if (ImGui::BeginTabItem(_u8L("Stealth").c_str())) { - append_mode(ps.estimated_silent_print_time, generate_items(ps.estimated_silent_custom_gcode_print_times)); + append_mode(ps.estimated_silent_print_time, generate_partial_times(ps.estimated_silent_custom_gcode_print_times), ps.estimated_silent_moves_times); ImGui::EndTabItem(); } } From b03ae392c5020828e8e96f44f635f77f045ca073 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 17 Jul 2020 10:50:16 +0200 Subject: [PATCH 162/503] GCodeViewer -> Added estimated printing times for extrusion roles --- src/libslic3r/GCode/GCodeProcessor.cpp | 19 +++ src/libslic3r/GCode/GCodeProcessor.hpp | 3 + src/libslic3r/Print.hpp | 4 + src/slic3r/GUI/GCodeViewer.cpp | 172 +++++++++++++++++-------- 4 files changed, 144 insertions(+), 54 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 7f8efc53fc..67ed7c699b 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -163,6 +163,7 @@ void GCodeProcessor::TimeMachine::reset() gcode_time.reset(); blocks = std::vector(); std::fill(moves_time.begin(), moves_time.end(), 0.0f); + std::fill(roles_time.begin(), roles_time.end(), 0.0f); } void GCodeProcessor::TimeMachine::simulate_st_synchronize(float additional_time) @@ -271,6 +272,7 @@ void GCodeProcessor::TimeMachine::calculate_time(size_t keep_last_n_blocks) time += block_time; gcode_time.cache += block_time; moves_time[static_cast(block.move_type)] += block_time; + roles_time[static_cast(block.role)] += block_time; // if (block.g1_line_id >= 0) // m_g1_times.emplace_back(block.g1_line_id, time); @@ -397,15 +399,18 @@ void GCodeProcessor::update_print_stats_estimated_times(PrintStatistics& print_s print_statistics.estimated_normal_print_time = get_time(GCodeProcessor::ETimeMode::Normal); print_statistics.estimated_normal_custom_gcode_print_times = get_custom_gcode_times(GCodeProcessor::ETimeMode::Normal, true); print_statistics.estimated_normal_moves_times = get_moves_time(GCodeProcessor::ETimeMode::Normal); + print_statistics.estimated_normal_roles_times = get_roles_time(GCodeProcessor::ETimeMode::Normal); if (m_time_processor.machines[static_cast(GCodeProcessor::ETimeMode::Stealth)].enabled) { print_statistics.estimated_silent_print_time = get_time(GCodeProcessor::ETimeMode::Stealth); print_statistics.estimated_silent_custom_gcode_print_times = get_custom_gcode_times(GCodeProcessor::ETimeMode::Stealth, true); print_statistics.estimated_silent_moves_times = get_moves_time(GCodeProcessor::ETimeMode::Stealth); + print_statistics.estimated_silent_roles_times = get_roles_time(GCodeProcessor::ETimeMode::Stealth); } else { print_statistics.estimated_silent_print_time = 0.0f; print_statistics.estimated_silent_custom_gcode_print_times.clear(); print_statistics.estimated_silent_moves_times.clear(); + print_statistics.estimated_silent_roles_times.clear(); } } @@ -447,6 +452,19 @@ std::vector> GCodeProcessor::get_mov return ret; } +std::vector> GCodeProcessor::get_roles_time(ETimeMode mode) const +{ + std::vector> ret; + if (mode < ETimeMode::Count) { + for (size_t i = 0; i < m_time_processor.machines[static_cast(mode)].roles_time.size(); ++i) { + float time = m_time_processor.machines[static_cast(mode)].roles_time[i]; + if (time > 0.0f) + ret.push_back({ static_cast(i), time }); + } + } + return ret; +} + void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) { /* std::cout << line.raw() << std::endl; */ @@ -735,6 +753,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) TimeBlock block; block.move_type = type; + block.role = m_extrusion_role; block.distance = distance; // calculates block cruise feedrate diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 214ed3d911..d19d363f95 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -101,6 +101,7 @@ namespace Slic3r { }; EMoveType move_type{ EMoveType::Noop }; + ExtrusionRole role{ erNone }; float distance{ 0.0f }; // mm float acceleration{ 0.0f }; // mm/s^2 float max_entry_speed{ 0.0f }; // mm/s @@ -153,6 +154,7 @@ namespace Slic3r { CustomGCodeTime gcode_time; std::vector blocks; std::array(EMoveType::Count)> moves_time; + std::array(ExtrusionRole::erCount)> roles_time; void reset(); @@ -265,6 +267,7 @@ namespace Slic3r { std::vector>> get_custom_gcode_times(ETimeMode mode, bool include_remaining) const; std::vector> get_moves_time(ETimeMode mode) const; + std::vector> get_roles_time(ETimeMode mode) const; private: void process_gcode_line(const GCodeReader::GCodeLine& line); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 748411cd77..e2b7ab5467 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -314,6 +314,8 @@ struct PrintStatistics std::vector>> estimated_silent_custom_gcode_print_times_str; std::vector> estimated_normal_moves_times; std::vector> estimated_silent_moves_times; + std::vector> estimated_normal_roles_times; + std::vector> estimated_silent_roles_times; #else std::string estimated_normal_print_time; std::string estimated_silent_print_time; @@ -366,6 +368,8 @@ struct PrintStatistics estimated_silent_custom_gcode_print_times.clear(); estimated_normal_moves_times.clear(); estimated_silent_moves_times.clear(); + estimated_normal_roles_times.clear(); + estimated_silent_roles_times.clear(); } #endif //ENABLE_GCODE_VIEWER }; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index db95c2b224..df29af67b8 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1724,6 +1724,8 @@ void GCodeViewer::render_time_estimate() const using Times = std::pair; using TimesList = std::vector>; + using Headers = std::vector; + using ColumnOffsets = std::array; // helper structure containig the data needed to render the time items struct PartialTime @@ -1743,17 +1745,14 @@ void GCodeViewer::render_time_estimate() const using PartialTimes = std::vector; auto append_mode = [this, &imgui](float total_time, const PartialTimes& items, - const std::vector>& moves_time) { - auto append_partial_times = [this, &imgui](const PartialTimes& items) { - using Headers = std::vector; - const Headers headers = { - _u8L("Event"), - _u8L("Remaining"), - _u8L("Duration") - }; - using Offsets = std::array; + const Headers& partial_times_headers, + const std::vector>& moves_time, + const Headers& moves_headers, + const std::vector>& roles_time, + const Headers& roles_headers) { + auto append_partial_times = [this, &imgui](const PartialTimes& items, const Headers& headers) { auto calc_offsets = [this, &headers](const PartialTimes& items) { - Offsets ret = { ImGui::CalcTextSize(headers[0].c_str()).x, ImGui::CalcTextSize(headers[1].c_str()).x }; + ColumnOffsets ret = { ImGui::CalcTextSize(headers[0].c_str()).x, ImGui::CalcTextSize(headers[1].c_str()).x }; for (const PartialTime& item : items) { std::string label; switch (item.type) @@ -1772,7 +1771,7 @@ void GCodeViewer::render_time_estimate() const ret[1] += ret[0] + style.ItemSpacing.x; return ret; }; - auto append_color = [this, &imgui](const Color& color1, const Color& color2, Offsets& offsets, const Times& times) { + auto append_color = [this, &imgui](const Color& color1, const Color& color2, ColumnOffsets& offsets, const Times& times) { ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); imgui.text(_u8L("Color change")); ImGui::PopStyleColor(); @@ -1801,7 +1800,7 @@ void GCodeViewer::render_time_estimate() const if (items.empty()) return; - Offsets offsets = calc_offsets(items); + ColumnOffsets offsets = calc_offsets(items); ImGui::Spacing(); ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); @@ -1845,42 +1844,25 @@ void GCodeViewer::render_time_estimate() const } }; - auto append_move_times = [this, &imgui](float total_time, const std::vector>& moves_time) { - using Headers = std::vector; - const Headers headers = { - _u8L("Type"), - _u8L("Time"), - _u8L("Percentage") - }; - auto move_type_label = [](GCodeProcessor::EMoveType type) { - switch (type) - { - case GCodeProcessor::EMoveType::Noop: { return _u8L("Noop"); } - case GCodeProcessor::EMoveType::Retract: { return _u8L("Retraction"); } - case GCodeProcessor::EMoveType::Unretract: { return _u8L("Unretraction"); } - case GCodeProcessor::EMoveType::Tool_change: { return _u8L("Tool change"); } - case GCodeProcessor::EMoveType::Color_change: { return _u8L("Color change"); } - case GCodeProcessor::EMoveType::Pause_Print: { return _u8L("Pause print"); } - case GCodeProcessor::EMoveType::Custom_GCode: { return _u8L("Custom GCode"); } - case GCodeProcessor::EMoveType::Travel: { return _u8L("Travel"); } - case GCodeProcessor::EMoveType::Extrude: { return _u8L("Extrusion"); } - default: { return _u8L("Unknown"); } - } - }; - using Offsets = std::array; - auto calc_offsets = [this, &headers, move_type_label](const std::vector>& moves_time) { - Offsets ret = { ImGui::CalcTextSize(headers[0].c_str()).x, ImGui::CalcTextSize(headers[1].c_str()).x }; - for (const auto& [type, time] : moves_time) { - ret[0] = std::max(ret[0], ImGui::CalcTextSize(move_type_label(type).c_str()).x); - ret[1] = std::max(ret[1], ImGui::CalcTextSize(short_time(get_time_dhms(time)).c_str()).x); - } - - const ImGuiStyle& style = ImGui::GetStyle(); - ret[0] += 2.0f * style.ItemSpacing.x; - ret[1] += ret[0] + style.ItemSpacing.x; - return ret; - }; + auto move_type_label = [](GCodeProcessor::EMoveType type) { + switch (type) + { + case GCodeProcessor::EMoveType::Noop: { return _u8L("Noop"); } + case GCodeProcessor::EMoveType::Retract: { return _u8L("Retraction"); } + case GCodeProcessor::EMoveType::Unretract: { return _u8L("Unretraction"); } + case GCodeProcessor::EMoveType::Tool_change: { return _u8L("Tool change"); } + case GCodeProcessor::EMoveType::Color_change: { return _u8L("Color change"); } + case GCodeProcessor::EMoveType::Pause_Print: { return _u8L("Pause print"); } + case GCodeProcessor::EMoveType::Custom_GCode: { return _u8L("Custom GCode"); } + case GCodeProcessor::EMoveType::Travel: { return _u8L("Travel"); } + case GCodeProcessor::EMoveType::Extrude: { return _u8L("Extrusion"); } + default: { return _u8L("Unknown"); } + } + }; + auto append_move_times = [this, &imgui, move_type_label](float total_time, + const std::vector>& moves_time, + const Headers& headers, const ColumnOffsets& offsets) { if (moves_time.empty()) return; @@ -1888,8 +1870,6 @@ void GCodeViewer::render_time_estimate() const if (!ImGui::CollapsingHeader(_u8L("Moves Time").c_str())) return; - Offsets offsets = calc_offsets(moves_time); - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); imgui.text(headers[0]); ImGui::SameLine(offsets[0]); @@ -1899,7 +1879,10 @@ void GCodeViewer::render_time_estimate() const ImGui::PopStyleColor(); ImGui::Separator(); - for (const auto& [type, time] : moves_time) { + std::vector> sorted_moves_time(moves_time); + std::sort(sorted_moves_time.begin(), sorted_moves_time.end(), [](const auto& p1, const auto& p2) { return p2.second < p1.second; }); + + for (const auto& [type, time] : sorted_moves_time) { ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); imgui.text(move_type_label(type)); ImGui::PopStyleColor(); @@ -1912,13 +1895,72 @@ void GCodeViewer::render_time_estimate() const } }; + auto append_role_times = [this, &imgui](float total_time, + const std::vector>& roles_time, + const Headers& headers, const ColumnOffsets& offsets) { + + if (roles_time.empty()) + return; + + if (!ImGui::CollapsingHeader(_u8L("Features Time").c_str())) + return; + + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); + imgui.text(headers[0]); + ImGui::SameLine(offsets[0]); + imgui.text(headers[1]); + ImGui::SameLine(offsets[1]); + imgui.text(headers[2]); + ImGui::PopStyleColor(); + ImGui::Separator(); + + std::vector> sorted_roles_time(roles_time); + std::sort(sorted_roles_time.begin(), sorted_roles_time.end(), [](const auto& p1, const auto& p2) { return p2.second < p1.second; }); + + for (const auto& [role, time] : sorted_roles_time) { + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); + imgui.text(_u8L(ExtrusionEntity::role_to_string(role))); + ImGui::PopStyleColor(); + ImGui::SameLine(offsets[0]); + imgui.text(short_time(get_time_dhms(time))); + ImGui::SameLine(offsets[1]); + char buf[64]; + ::sprintf(buf, "%.2f%%", 100.0f * time / total_time); + ImGui::TextUnformatted(buf); + } + }; + + auto calc_common_offsets = [move_type_label]( + const std::vector>& moves_time, const Headers& moves_headers, + const std::vector>& roles_time, const Headers& roles_headers) { + ColumnOffsets ret = { std::max(ImGui::CalcTextSize(moves_headers[0].c_str()).x, ImGui::CalcTextSize(roles_headers[0].c_str()).x), + std::max(ImGui::CalcTextSize(moves_headers[1].c_str()).x, ImGui::CalcTextSize(roles_headers[1].c_str()).x) }; + + for (const auto& [type, time] : moves_time) { + ret[0] = std::max(ret[0], ImGui::CalcTextSize(move_type_label(type).c_str()).x); + ret[1] = std::max(ret[1], ImGui::CalcTextSize(short_time(get_time_dhms(time)).c_str()).x); + } + + for (const auto& [role, time] : roles_time) { + ret[0] = std::max(ret[0], ImGui::CalcTextSize(_u8L(ExtrusionEntity::role_to_string(role)).c_str()).x); + ret[1] = std::max(ret[1], ImGui::CalcTextSize(short_time(get_time_dhms(time)).c_str()).x); + } + + const ImGuiStyle& style = ImGui::GetStyle(); + ret[0] += 2.0f * style.ItemSpacing.x; + ret[1] += ret[0] + style.ItemSpacing.x; + return ret; + }; + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); imgui.text(_u8L("Time") + ":"); ImGui::PopStyleColor(); ImGui::SameLine(); imgui.text(short_time(get_time_dhms(total_time))); - append_partial_times(items); - append_move_times(total_time, moves_time); + append_partial_times(items, partial_times_headers); + ColumnOffsets common_offsets = calc_common_offsets(moves_time, moves_headers, roles_time, roles_headers); + append_move_times(total_time, moves_time, moves_headers, common_offsets); + append_role_times(total_time, roles_time, roles_headers, common_offsets); }; auto generate_partial_times = [this](const TimesList& times) { @@ -1966,6 +2008,22 @@ void GCodeViewer::render_time_estimate() const return items; }; + const Headers partial_times_headers = { + _u8L("Event"), + _u8L("Remaining"), + _u8L("Duration") + }; + const Headers moves_headers = { + _u8L("Type"), + _u8L("Time"), + _u8L("Percentage") + }; + const Headers roles_headers = { + _u8L("Feature"), + _u8L("Time"), + _u8L("Percentage") + }; + Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size(); imgui.set_next_window_pos(static_cast(cnv_size.get_width()), static_cast(cnv_size.get_height()), ImGuiCond_Always, 1.0f, 1.0f); ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(-1.0f, 0.5f * static_cast(cnv_size.get_height()))); @@ -1980,13 +2038,19 @@ void GCodeViewer::render_time_estimate() const ImGui::BeginTabBar("mode_tabs"); if (ps.estimated_normal_print_time > 0.0f) { if (ImGui::BeginTabItem(_u8L("Normal").c_str())) { - append_mode(ps.estimated_normal_print_time, generate_partial_times(ps.estimated_normal_custom_gcode_print_times), ps.estimated_normal_moves_times); + append_mode(ps.estimated_normal_print_time, + generate_partial_times(ps.estimated_normal_custom_gcode_print_times), partial_times_headers, + ps.estimated_normal_moves_times, moves_headers, + ps.estimated_normal_roles_times, roles_headers); ImGui::EndTabItem(); } } if (ps.estimated_silent_print_time > 0.0f) { if (ImGui::BeginTabItem(_u8L("Stealth").c_str())) { - append_mode(ps.estimated_silent_print_time, generate_partial_times(ps.estimated_silent_custom_gcode_print_times), ps.estimated_silent_moves_times); + append_mode(ps.estimated_silent_print_time, + generate_partial_times(ps.estimated_silent_custom_gcode_print_times), partial_times_headers, + ps.estimated_silent_moves_times, moves_headers, + ps.estimated_silent_roles_times, roles_headers); ImGui::EndTabItem(); } } From 0df1d117804264f7b9f30f9f7e5a6367314f1cd5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 17 Jul 2020 11:08:34 +0200 Subject: [PATCH 163/503] GCodeViewer -> Attempt to fix rendering of toolpaths on Mac --- src/slic3r/GUI/GCodeViewer.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index df29af67b8..27bd8b6727 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1221,7 +1221,13 @@ void GCodeViewer::render_toolpaths() const glsafe(::glEnable(GL_POINT_SPRITE)); for (const RenderPath& path : buffer.render_paths) { +#ifdef __APPLE__ + for (size_t i = 0; i < path.sizes.size(); ++i) { + glsafe(::glDrawElements(GL_POINTS, (GLsizei)path.sizes[i], GL_UNSIGNED_INT, (const void*)path.offsets[i])); + } +#else glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); +#endif // __APPLE__ #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -1235,7 +1241,13 @@ void GCodeViewer::render_toolpaths() const for (const RenderPath& path : buffer.render_paths) { shader.set_uniform("uniform_color", path.color); +#ifdef __APPLE__ + for (size_t i = 0; i < path.sizes.size(); ++i) { + glsafe(::glDrawElements(GL_LINES, (GLsizei)path.sizes[i], GL_UNSIGNED_INT, (const void*)path.offsets[i])); + } +#else glsafe(::glMultiDrawElements(GL_LINES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); +#endif // __APPLE__ #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_line_strip_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS From a35f72442e3d44156f88efa58d16222a6a0b7ee0 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 17 Jul 2020 12:10:55 +0200 Subject: [PATCH 164/503] GCodeViewer -> 2nd attempt to fix rendering of toolpaths on Mac --- src/slic3r/GUI/GCodeViewer.cpp | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 27bd8b6727..0ab43a98c7 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1221,13 +1221,7 @@ void GCodeViewer::render_toolpaths() const glsafe(::glEnable(GL_POINT_SPRITE)); for (const RenderPath& path : buffer.render_paths) { -#ifdef __APPLE__ - for (size_t i = 0; i < path.sizes.size(); ++i) { - glsafe(::glDrawElements(GL_POINTS, (GLsizei)path.sizes[i], GL_UNSIGNED_INT, (const void*)path.offsets[i])); - } -#else glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); -#endif // __APPLE__ #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -1241,13 +1235,7 @@ void GCodeViewer::render_toolpaths() const for (const RenderPath& path : buffer.render_paths) { shader.set_uniform("uniform_color", path.color); -#ifdef __APPLE__ - for (size_t i = 0; i < path.sizes.size(); ++i) { - glsafe(::glDrawElements(GL_LINES, (GLsizei)path.sizes[i], GL_UNSIGNED_INT, (const void*)path.offsets[i])); - } -#else glsafe(::glMultiDrawElements(GL_LINES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); -#endif // __APPLE__ #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_line_strip_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -1255,7 +1243,11 @@ void GCodeViewer::render_toolpaths() const }; auto line_width = [zoom]() { - return (zoom < 5.0) ? 1.0 : (1.0 + 5.0 * (zoom - 5.0) / (100.0 - 5.0)); +#ifdef WIN32 + return (zoom < 5.0) ? 1.0 : (1.0 + 5.0 * (zoom - 5.0) / (100.0 - 5.0)); +#else + return 3.0f; +#endif // WIN32 }; glsafe(::glCullFace(GL_BACK)); From 5eac36a31066df16fc581206951fbfe675e177ab Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 17 Jul 2020 14:32:38 +0200 Subject: [PATCH 165/503] Update for PresetComboBoxes All "Printer-PresetName" pairs are like a separated items now + some code refactoring for PresetComboBoxes::update() --- src/libslic3r/Preset.cpp | 60 ++-- src/libslic3r/Preset.hpp | 41 ++- src/libslic3r/PresetBundle.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 21 +- src/slic3r/GUI/PresetComboBoxes.cpp | 514 ++++++++++++++++++---------- src/slic3r/GUI/PresetComboBoxes.hpp | 49 ++- src/slic3r/GUI/Tab.cpp | 67 +++- src/slic3r/GUI/Tab.hpp | 5 +- 8 files changed, 489 insertions(+), 270 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index c9f5bd0af0..ad7615f6d1 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1370,11 +1370,6 @@ const std::vector& PhysicalPrinter::printer_options() return s_opts; } -const std::string& PhysicalPrinter::get_preset_name() const -{ - return config.opt_string("preset_name"); -} - const std::set& PhysicalPrinter::get_preset_names() const { return preset_names; @@ -1415,8 +1410,6 @@ void PhysicalPrinter::update_from_preset(const Preset& preset) // add preset names to the options list auto ret = preset_names.emplace(preset.name); update_preset_names_in_config(); - - update_full_name(); } void PhysicalPrinter::update_from_config(const DynamicPrintConfig& new_config) @@ -1431,8 +1424,6 @@ void PhysicalPrinter::update_from_config(const DynamicPrintConfig& new_config) preset_names.emplace(val); } preset_names = values; - - update_full_name(); } void PhysicalPrinter::reset_presets() @@ -1454,26 +1445,26 @@ PhysicalPrinter::PhysicalPrinter(const std::string& name, const Preset& preset) void PhysicalPrinter::set_name(const std::string& name) { this->name = name; - update_full_name(); } -void PhysicalPrinter::update_full_name() +std::string PhysicalPrinter::get_full_name(std::string preset_name) const { - full_name = name + separator() + get_preset_name(); + return name + separator() + preset_name; } std::string PhysicalPrinter::get_short_name(std::string full_name) { int pos = full_name.find(separator()); - boost::erase_tail(full_name, full_name.length() - pos); + if (pos > 0) + boost::erase_tail(full_name, full_name.length() - pos); return full_name; } -std::string PhysicalPrinter::get_preset_name(std::string full_name) +std::string PhysicalPrinter::get_preset_name(std::string name) { - int pos = full_name.find(separator()); - boost::erase_head(full_name, pos + 2); - return full_name; + int pos = name.find(separator()); + boost::erase_head(name, pos + 3); + return Preset::remove_suffix_modified(name); } @@ -1563,7 +1554,6 @@ void PhysicalPrinterCollection::save_printer(const PhysicalPrinter& edited_print it->config = std::move(edited_printer.config); it->name = edited_printer.name; it->preset_names = edited_printer.preset_names; - it->full_name = edited_printer.full_name; } else { // Creating a new printer. @@ -1615,17 +1605,43 @@ bool PhysicalPrinterCollection::delete_selected_printer() return true; } -PhysicalPrinter& PhysicalPrinterCollection::select_printer_by_name(std::string name) +std::string PhysicalPrinterCollection::get_selected_full_printer_name() const { - name = PhysicalPrinter::get_short_name(name); - auto it = this->find_printer_internal(name); + return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_printer().get_full_name(m_selected_preset); +} + +PhysicalPrinter& PhysicalPrinterCollection::select_printer_by_name(const std::string& full_name) +{ + std::string printer_name = PhysicalPrinter::get_short_name(full_name); + auto it = this->find_printer_internal(printer_name); assert(it != m_printers.end()); // update idx_selected - m_idx_selected = it - m_printers.begin(); + m_idx_selected = it - m_printers.begin(); + // update name of the currently selected preset + m_selected_preset = it->get_preset_name(full_name); + if (m_selected_preset.empty()) + m_selected_preset = *it->preset_names.begin(); return *it; } +bool PhysicalPrinterCollection::has_selection() const +{ + return m_idx_selected != size_t(-1); +} + +void PhysicalPrinterCollection::unselect_printer() +{ + m_idx_selected = size_t(-1); + m_selected_preset.clear(); +} + +bool PhysicalPrinterCollection::is_selected(PhysicalPrinterCollection::ConstIterator it, const std::string& preset_name) const +{ + return m_idx_selected == it - m_printers.begin() && + m_selected_preset == preset_name; +} + namespace PresetUtils { const VendorProfile::PrinterModel* system_printer_model(const Preset &preset) diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index d583bed20d..d30ea70590 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -539,12 +539,9 @@ public: PhysicalPrinter(const std::string& name) : name(name){} PhysicalPrinter(const std::string& name, const Preset& preset); void set_name(const std::string &name); - void update_full_name(); // Name of the Physical Printer, usually derived form the file name. std::string name; - // Full name of the Physical Printer, included related preset name - std::string full_name; // File name of the Physical Printer. std::string file; // Configuration data, loaded from a file, or set from the defaults. @@ -558,8 +555,6 @@ public: bool loaded = false; static const std::vector& printer_options(); - const std::string& get_preset_name() const; - const std::set& get_preset_names() const; bool has_empty_config() const; @@ -588,6 +583,9 @@ public: // Sort lexicographically by a preset name. The preset name shall be unique across a single PresetCollection. bool operator<(const PhysicalPrinter& other) const { return this->name < other.name; } + // get full printer name included a name of the preset + std::string get_full_name(std::string preset_name) const; + // get printer name from the full name uncluded preset name static std::string get_short_name(std::string full_name); @@ -641,22 +639,29 @@ public: bool delete_selected_printer(); // Return the selected preset, without the user modifications applied. - PhysicalPrinter& get_selected_printer() { return m_printers[m_idx_selected]; } - const PhysicalPrinter& get_selected_printer() const { return m_printers[m_idx_selected]; } - size_t get_selected_idx() const { return m_idx_selected; } + PhysicalPrinter& get_selected_printer() { return m_printers[m_idx_selected]; } + const PhysicalPrinter& get_selected_printer() const { return m_printers[m_idx_selected]; } + + size_t get_selected_idx() const { return m_idx_selected; } // Returns the name of the selected preset, or an empty string if no preset is selected. - std::string get_selected_printer_name() const { return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_printer().name; } - // Returns the full name of the selected preset, or an empty string if no preset is selected. - std::string get_selected_full_printer_name() const { return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_printer().full_name; } + std::string get_selected_printer_name() const { return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_printer().name; } + // Returns the config of the selected printer, or nullptr if no printer is selected. + DynamicPrintConfig* get_selected_printer_config() { return (m_idx_selected == size_t(-1)) ? nullptr : &(this->get_selected_printer().config); } + // Returns the config of the selected printer, or nullptr if no printer is selected. + PrinterTechnology get_selected_printer_technology() { return (m_idx_selected == size_t(-1)) ? PrinterTechnology::ptAny : this->get_selected_printer().printer_technology(); } + + // Each physical printer can have a several related preset, + // so, use the next functions to get an exact names of selections in the list: + // Returns the full name of the selected printer, or an empty string if no preset is selected. + std::string get_selected_full_printer_name() const; // Returns the printer model of the selected preset, or an empty string if no preset is selected. - std::string get_selected_printer_preset_name() const { return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_printer().get_preset_name(); } - // Returns the config of the selected preset, or nullptr if no preset is selected. - DynamicPrintConfig* get_selected_printer_config() { return (m_idx_selected == size_t(-1)) ? nullptr : &(this->get_selected_printer().config); } + std::string get_selected_printer_preset_name() const { return (m_idx_selected == size_t(-1)) ? std::string() : m_selected_preset; } // select printer with name and return reference on it - PhysicalPrinter& select_printer_by_name(std::string name); - bool has_selection() const { return m_idx_selected != size_t(-1); } - void unselect_printer() { m_idx_selected = size_t(-1); } + PhysicalPrinter& select_printer_by_name(const std::string& full_name); + bool has_selection() const; + void unselect_printer() ; + bool is_selected(ConstIterator it, const std::string &preset_name) const; // Return a printer by an index. If the printer is active, a temporary copy is returned. PhysicalPrinter& printer(size_t idx) { return m_printers[idx]; } @@ -698,6 +703,8 @@ private: // Selected printer. size_t m_idx_selected = size_t(-1); + // The name of the preset which is currently select for this printer + std::string m_selected_preset; // Path to the directory to store the config files into. std::string m_dir_path; diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 7c4fa1cb25..7969966e5e 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -458,7 +458,7 @@ void PresetBundle::export_selections(AppConfig &config) config.set("presets", "sla_material", sla_materials.get_selected_preset_name()); config.set("presets", "printer", printers.get_selected_preset_name()); - config.set("extras", "physical_printer", physical_printers.get_selected_printer_name()); + config.set("extras", "physical_printer", physical_printers.get_selected_full_printer_name()); } DynamicPrintConfig PresetBundle::full_config() const diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 048e17926e..5673eece71 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3208,24 +3208,21 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt) if (preset_type == Preset::TYPE_FILAMENT) { wxGetApp().preset_bundle->set_filament_preset(idx, preset_name); } - - if (preset_type == Preset::TYPE_PRINTER) { - if(combo->is_selected_physical_printer()) { - // Select related printer preset on the Printer Settings Tab - const std::string printer_name = combo->GetString(selection).ToUTF8().data(); - PhysicalPrinter& printer = wxGetApp().preset_bundle->physical_printers.select_printer_by_name(printer_name); - preset_name = wxGetApp().preset_bundle->get_preset_name_by_alias(preset_type, printer.get_preset_name()); - } - else - wxGetApp().preset_bundle->physical_printers.unselect_printer(); - } + bool select_preset = !combo->selection_is_changed_according_to_physical_printers(); // TODO: ? if (preset_type == Preset::TYPE_FILAMENT && sidebar->is_multifilament()) { // Only update the plater UI for the 2nd and other filaments. combo->update(); } - else { + else if (select_preset) { + if (preset_type == Preset::TYPE_PRINTER) { + PhysicalPrinterCollection& physical_printers = wxGetApp().preset_bundle->physical_printers; + if(combo->is_selected_physical_printer()) + preset_name = physical_printers.get_selected_printer_preset_name(); + else + physical_printers.unselect_printer(); + } wxWindowUpdateLocker noUpdates(sidebar->presets_panel()); wxGetApp().get_tab(preset_type)->select_preset(preset_name); } diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 1473bf6dab..e08cf101d6 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -63,8 +63,7 @@ PresetComboBox::PresetComboBox(wxWindow* parent, Preset::Type preset_type, const m_type(preset_type), m_last_selected(wxNOT_FOUND), m_em_unit(em_unit(this)), - m_preset_bundle(wxGetApp().preset_bundle), - m_bitmap_cache(new BitmapCache) + m_preset_bundle(wxGetApp().preset_bundle) { SetFont(wxGetApp().normal_font()); #ifdef _WIN32 @@ -105,17 +104,31 @@ PresetComboBox::PresetComboBox(wxWindow* parent, Preset::Type preset_type, const m_bitmapCompatible = ScalableBitmap(this, "flag_green"); m_bitmapIncompatible = ScalableBitmap(this, "flag_red"); - m_bitmapLock = ScalableBitmap(this, "lock_closed"); - m_bitmapLockDisabled = ScalableBitmap(this, "lock_closed", 16, true); // parameters for an icon's drawing fill_width_height(); + + Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) { + // see https://github.com/prusa3d/PrusaSlicer/issues/3889 + // Under OSX: in case of use of a same names written in different case (like "ENDER" and "Ender") + // m_presets_choice->GetSelection() will return first item, because search in PopupListCtrl is case-insensitive. + // So, use GetSelection() from event parameter + auto selected_item = evt.GetSelection(); + + auto marker = reinterpret_cast(this->GetClientData(selected_item)); + if (marker >= LABEL_ITEM_DISABLED && marker < LABEL_ITEM_MAX) + this->SetSelection(this->m_last_selected); + else if (on_selection_changed && (m_last_selected != selected_item || m_collection->current_is_dirty())) { + m_last_selected = selected_item; + on_selection_changed(selected_item); + evt.StopPropagation(); + } + evt.Skip(); + }); } PresetComboBox::~PresetComboBox() { - delete m_bitmap_cache; - m_bitmap_cache = nullptr; } void PresetComboBox::set_label_marker(int item, LabelItemType label_item_type) @@ -123,12 +136,96 @@ void PresetComboBox::set_label_marker(int item, LabelItemType label_item_type) this->SetClientData(item, (void*)label_item_type); } +void PresetComboBox::update(const std::string& select_preset_name) +{ + Freeze(); + Clear(); + size_t selected_preset_item = INT_MAX; // some value meaning that no one item is selected + + const std::deque& presets = m_collection->get_presets(); + + std::map> nonsys_presets; + wxString selected = ""; + if (!presets.front().is_visible) + set_label_marker(Append(separator(L("System presets")), wxNullBitmap)); + + for (size_t i = presets.front().is_visible ? 0 : m_collection->num_default_presets(); i < presets.size(); ++i) + { + const Preset& preset = presets[i]; + if (!preset.is_visible || !preset.is_compatible) + continue; + + // marker used for disable incompatible printer models for the selected physical printer + bool is_enabled = true; + + std::string bitmap_key = "tab"; + std::string main_icon_name = m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; + + if (m_type == Preset::TYPE_PRINTER) { + bitmap_key += "_printer"; + if (preset.printer_technology() == ptSLA) + bitmap_key += "_sla"; + if (!is_enabled) + bitmap_key += "_disabled"; + } + bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt"; + bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst"; + + wxBitmap* bmp = get_bmp(bitmap_key, main_icon_name, "lock_closed", is_enabled, preset.is_compatible, preset.is_system || preset.is_default); + assert(bmp); + + if (preset.is_default || preset.is_system) { + int item_id = Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), *bmp); + if (!is_enabled) + set_label_marker(item_id, LABEL_ITEM_DISABLED); + if (preset.name == select_preset_name ||//i == idx_selected || + // just in case: mark selected_preset_item as a first added element + selected_preset_item == INT_MAX) + selected_preset_item = GetCount() - 1; + } + else + { + std::pair pair(bmp, is_enabled); + nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), std::pair(bmp, is_enabled)); + if (preset.name == select_preset_name) + selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()); + } + if (i + 1 == m_collection->num_default_presets()) + set_label_marker(Append(separator(L("System presets")), wxNullBitmap)); + } + if (!nonsys_presets.empty()) + { + set_label_marker(Append(separator(L("User presets")), wxNullBitmap)); + for (std::map>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { + int item_id = Append(it->first, *it->second.first); + bool is_enabled = it->second.second; + if (!is_enabled) + set_label_marker(item_id, LABEL_ITEM_DISABLED); + if (it->first == selected || + // just in case: mark selected_preset_item as a first added element + selected_preset_item == INT_MAX) + selected_preset_item = GetCount() - 1; + } + } + + /* But, if selected_preset_item is still equal to INT_MAX, it means that + * there is no presets added to the list. + * So, select last combobox item ("Add/Remove preset") + */ + if (selected_preset_item == INT_MAX) + selected_preset_item = GetCount() - 1; + + SetSelection(selected_preset_item); + SetToolTip(GetString(selected_preset_item)); + Thaw(); + + m_last_selected = selected_preset_item; +} + void PresetComboBox::msw_rescale() { m_em_unit = em_unit(this); - m_bitmapLock.msw_rescale(); - m_bitmapLockDisabled.msw_rescale(); m_bitmapIncompatible.msw_rescale(); m_bitmapCompatible.msw_rescale(); @@ -142,9 +239,9 @@ void PresetComboBox::msw_rescale() void PresetComboBox::fill_width_height() { // To avoid asserts, each added bitmap to wxBitmapCombobox should be the same size, so - // set a bitmap's height to m_bitmapLock->GetHeight() and norm_icon_width to m_bitmapLock->GetWidth() - icon_height = m_bitmapLock.GetBmpHeight(); - norm_icon_width = m_bitmapLock.GetBmpWidth(); + // set a bitmap's height to m_bitmapCompatible->GetHeight() and norm_icon_width to m_bitmapCompatible->GetWidth() + icon_height = m_bitmapCompatible.GetBmpHeight(); + norm_icon_width = m_bitmapCompatible.GetBmpWidth(); /* It's supposed that standard size of an icon is 16px*16px for 100% scaled display. * So set sizes for solid_colored icons used for filament preset @@ -165,6 +262,110 @@ wxString PresetComboBox::separator(const std::string& label) return wxString::FromUTF8(separator_head()) + _(label) + wxString::FromUTF8(separator_tail()); } +wxBitmap* PresetComboBox::get_bmp( std::string bitmap_key, bool wide_icons, const std::string& main_icon_name, + bool is_compatible/* = true*/, bool is_system/* = false*/, bool is_single_bar/* = false*/, + std::string filament_rgb/* = ""*/, std::string extruder_rgb/* = ""*/) +{ + bitmap_key += ",h" + std::to_string(icon_height); + + wxBitmap* bmp = bitmap_cache().find(bitmap_key); + if (bmp == nullptr) { + // Create the bitmap with color bars. + std::vector bmps; + if (wide_icons) + // Paint a red flag for incompatible presets. + bmps.emplace_back(is_compatible ? bitmap_cache().mkclear(norm_icon_width, icon_height) : m_bitmapIncompatible.bmp()); + + if (m_type == Preset::TYPE_FILAMENT) + { + unsigned char rgb[3]; + // Paint the color bars. + bitmap_cache().parse_color(filament_rgb, rgb); + bmps.emplace_back(bitmap_cache().mksolid(is_single_bar ? wide_icon_width : norm_icon_width, icon_height, rgb)); + if (!is_single_bar) { + bitmap_cache().parse_color(extruder_rgb, rgb); + bmps.emplace_back(bitmap_cache().mksolid(thin_icon_width, icon_height, rgb)); + } + // Paint a lock at the system presets. + bmps.emplace_back(bitmap_cache().mkclear(space_icon_width, icon_height)); + } + else + { + // Paint the color bars. + bmps.emplace_back(bitmap_cache().mkclear(thin_space_icon_width, icon_height)); + bmps.emplace_back(create_scaled_bitmap(main_icon_name)); + // Paint a lock at the system presets. + bmps.emplace_back(bitmap_cache().mkclear(wide_space_icon_width, icon_height)); + } + bmps.emplace_back(is_system ? create_scaled_bitmap("lock_closed") : bitmap_cache().mkclear(norm_icon_width, icon_height)); + bmp = bitmap_cache().insert(bitmap_key, bmps); + } + + return bmp; +} + +wxBitmap* PresetComboBox::get_bmp( std::string bitmap_key, const std::string& main_icon_name, const std::string& next_icon_name, + bool is_enabled/* = true*/, bool is_compatible/* = true*/, bool is_system/* = false*/) +{ + bitmap_key += ",h" + std::to_string(icon_height); + + wxBitmap* bmp = bitmap_cache().find(bitmap_key); + if (bmp == nullptr) { + // Create the bitmap with color bars. + std::vector bmps; + bmps.emplace_back(m_type == Preset::TYPE_PRINTER ? create_scaled_bitmap(main_icon_name, this, 16, !is_enabled) : + is_compatible ? m_bitmapCompatible.bmp() : m_bitmapIncompatible.bmp()); + // Paint a lock at the system presets. + bmps.emplace_back(is_system ? create_scaled_bitmap(next_icon_name, this, 16, !is_enabled) : bitmap_cache().mkclear(norm_icon_width, icon_height)); + bmp = bitmap_cache().insert(bitmap_key, bmps); + } + + return bmp; +} + +bool PresetComboBox::is_selected_physical_printer() +{ + auto selected_item = this->GetSelection(); + auto marker = reinterpret_cast(this->GetClientData(selected_item)); + return marker == LABEL_ITEM_PHYSICAL_PRINTER; +} + +bool PresetComboBox::selection_is_changed_according_to_physical_printers() +{ + if (m_type != Preset::TYPE_PRINTER || !is_selected_physical_printer()) + return false; + + PhysicalPrinterCollection& physical_printers = m_preset_bundle->physical_printers; + + std::string selected_string = this->GetString(this->GetSelection()).ToUTF8().data(); + + std::string old_printer_full_name, old_printer_preset; + if (physical_printers.has_selection()) { + old_printer_full_name = physical_printers.get_selected_full_printer_name(); + old_printer_preset = physical_printers.get_selected_printer_preset_name(); + } + else + old_printer_preset = m_collection->get_edited_preset().name; + // Select related printer preset on the Printer Settings Tab + physical_printers.select_printer_by_name(selected_string); + std::string preset_name = physical_printers.get_selected_printer_preset_name(); + + // if new preset wasn't selected, there is no need to call update preset selection + if (old_printer_preset == preset_name) { + // we need just to update according Plater<->Tab PresetComboBox + if (dynamic_cast(this)!=nullptr) + wxGetApp().get_tab(m_type)->update_preset_choice(); + else if (dynamic_cast(this)!=nullptr) + wxGetApp().sidebar().update_presets(m_type); + return true; + } + + Tab* tab = wxGetApp().get_tab(Preset::TYPE_PRINTER); + if (tab) + tab->select_preset(preset_name, false, old_printer_full_name); + return true; +} + #ifdef __APPLE__ bool PresetComboBox::OnAddBitmap(const wxBitmap& bitmap) { @@ -245,25 +446,6 @@ PlaterPresetComboBox::PlaterPresetComboBox(wxWindow *parent, Preset::Type preset if (marker >= LABEL_ITEM_MARKER && marker < LABEL_ITEM_MAX) { this->SetSelection(this->m_last_selected); evt.StopPropagation(); - /* - if (marker == LABEL_ITEM_PHYSICAL_PRINTERS) - { - PhysicalPrinterDialog dlg(wxEmptyString); - if (dlg.ShowModal() == wxID_OK) - this->update(); - return; - } - if (marker >= LABEL_ITEM_WIZARD_PRINTERS) { - ConfigWizard::StartPage sp = ConfigWizard::SP_WELCOME; - switch (marker) { - case LABEL_ITEM_WIZARD_PRINTERS: sp = ConfigWizard::SP_PRINTERS; break; - case LABEL_ITEM_WIZARD_FILAMENTS: sp = ConfigWizard::SP_FILAMENTS; break; - case LABEL_ITEM_WIZARD_MATERIALS: sp = ConfigWizard::SP_MATERIALS; break; - default: break; - } - wxTheApp->CallAfter([sp]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, sp); }); - } - */ if (marker == LABEL_ITEM_WIZARD_PRINTERS) show_add_menu(); else @@ -372,13 +554,6 @@ PlaterPresetComboBox::~PlaterPresetComboBox() edit_btn->Destroy(); } -bool PlaterPresetComboBox::is_selected_physical_printer() -{ - auto selected_item = this->GetSelection(); - auto marker = reinterpret_cast(this->GetClientData(selected_item)); - return marker == LABEL_ITEM_PHYSICAL_PRINTER; -} - bool PlaterPresetComboBox::switch_to_tab() { Tab* tab = wxGetApp().get_tab(m_type); @@ -465,7 +640,7 @@ void PlaterPresetComboBox::update() { unsigned char rgb[3]; extruder_color = m_preset_bundle->printers.get_edited_preset().config.opt_string("extruder_colour", (unsigned int)m_extruder_idx); - if (!m_bitmap_cache->parse_color(extruder_color, rgb)) + if (!bitmap_cache().parse_color(extruder_color, rgb)) // Extruder color is not defined. extruder_color.clear(); selected_filament_preset = m_collection->find_preset(m_preset_bundle->filament_presets[m_extruder_idx]); @@ -499,64 +674,33 @@ void PlaterPresetComboBox::update() continue; std::string bitmap_key, filament_rgb, extruder_rgb; + std::string bitmap_type_name = bitmap_key = m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; + bool single_bar = false; - if (m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA) - bitmap_key = "sla_printer"; - else if (m_type == Preset::TYPE_FILAMENT) + if (m_type == Preset::TYPE_FILAMENT) { // Assign an extruder color to the selected item if the extruder color is defined. filament_rgb = preset.config.opt_string("filament_colour", 0); extruder_rgb = (selected && !extruder_color.empty()) ? extruder_color : filament_rgb; single_bar = filament_rgb == extruder_rgb; - bitmap_key = single_bar ? filament_rgb : filament_rgb + extruder_rgb; + bitmap_key += single_bar ? filament_rgb : filament_rgb + extruder_rgb; } - wxBitmap main_bmp = create_scaled_bitmap(m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name); // If the filament preset is not compatible and there is a "red flag" icon loaded, show it left // to the filament color image. if (wide_icons) bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt"; bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst"; - bitmap_key += "-h" + std::to_string(icon_height); - wxBitmap* bmp = m_bitmap_cache->find(bitmap_key); - if (bmp == nullptr) { - // Create the bitmap with color bars. - std::vector bmps; - if (wide_icons) - // Paint a red flag for incompatible presets. - bmps.emplace_back(preset.is_compatible ? m_bitmap_cache->mkclear(norm_icon_width, icon_height) : m_bitmapIncompatible.bmp()); - - if (m_type == Preset::TYPE_FILAMENT) - { - unsigned char rgb[3]; - // Paint the color bars. - m_bitmap_cache->parse_color(filament_rgb, rgb); - bmps.emplace_back(m_bitmap_cache->mksolid(single_bar ? wide_icon_width : norm_icon_width, icon_height, rgb)); - if (!single_bar) { - m_bitmap_cache->parse_color(extruder_rgb, rgb); - bmps.emplace_back(m_bitmap_cache->mksolid(thin_icon_width, icon_height, rgb)); - } - // Paint a lock at the system presets. - bmps.emplace_back(m_bitmap_cache->mkclear(space_icon_width, icon_height)); - } - else - { - // Paint the color bars. - bmps.emplace_back(m_bitmap_cache->mkclear(thin_space_icon_width, icon_height)); - bmps.emplace_back(main_bmp); - // Paint a lock at the system presets. - bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width, icon_height)); - } - bmps.emplace_back((preset.is_system || preset.is_default) ? m_bitmapLock.bmp() : m_bitmap_cache->mkclear(norm_icon_width, icon_height)); - bmp = m_bitmap_cache->insert(bitmap_key, bmps); - } + wxBitmap* bmp = get_bmp(bitmap_key, wide_icons, bitmap_type_name, + preset.is_compatible, preset.is_system || preset.is_default, + single_bar, filament_rgb, extruder_rgb); + assert(bmp); const std::string name = preset.alias.empty() ? preset.name : preset.alias; if (preset.is_default || preset.is_system) { - Append(wxString::FromUTF8((name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), - !bmp ? main_bmp : *bmp); + Append(wxString::FromUTF8((name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), *bmp); if (is_selected || // just in case: mark selected_preset_item as a first added element selected_preset_item == INT_MAX) { @@ -595,59 +739,26 @@ void PlaterPresetComboBox::update() const PhysicalPrinterCollection& ph_printers = m_preset_bundle->physical_printers; for (PhysicalPrinterCollection::ConstIterator it = ph_printers.begin(); it != ph_printers.end(); ++it) { - std::string bitmap_key = it->printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; - if (wide_icons) - bitmap_key += "wide,"; - bitmap_key += "-h" + std::to_string(icon_height); - - wxBitmap* bmp = m_bitmap_cache->find(bitmap_key); - if (bmp == nullptr) { - // Create the bitmap with color bars. - std::vector bmps; + for (const std::string preset_name : it->get_preset_names()) { + Preset* preset = m_collection->find_preset(preset_name); + if (!preset) + continue; + std::string main_icon_name, bitmap_key = main_icon_name = preset->printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; if (wide_icons) - // Paint a red flag for incompatible presets. - bmps.emplace_back(m_bitmap_cache->mkclear(norm_icon_width, icon_height)); - // Paint the color bars. - bmps.emplace_back(m_bitmap_cache->mkclear(thin_space_icon_width, icon_height)); - bmps.emplace_back(create_scaled_bitmap(it->printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name)); - // Paint a lock at the system presets. - bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width+norm_icon_width, icon_height)); - bmp = m_bitmap_cache->insert(bitmap_key, bmps); - } + bitmap_key += ",cmpt"; + bitmap_key += ",nsyst"; - set_label_marker(Append(wxString::FromUTF8((it->full_name).c_str()), *bmp), LABEL_ITEM_PHYSICAL_PRINTER); - if (ph_printers.has_selection() && it->name == ph_printers.get_selected_printer_name() || - // just in case: mark selected_preset_item as a first added element - selected_preset_item == INT_MAX) - selected_preset_item = GetCount() - 1; + wxBitmap* bmp = get_bmp(bitmap_key, wide_icons, main_icon_name); + assert(bmp); + + set_label_marker(Append(wxString::FromUTF8((it->get_full_name(preset_name) + (preset->is_dirty ? Preset::suffix_modified() : "")).c_str()), *bmp), LABEL_ITEM_PHYSICAL_PRINTER); + if (ph_printers.is_selected(it, preset_name) || + // just in case: mark selected_preset_item as a first added element + selected_preset_item == INT_MAX) + selected_preset_item = GetCount() - 1; + } } } - -/* - // add LABEL_ITEM_PHYSICAL_PRINTERS - std::string bitmap_key; - if (wide_icons) - bitmap_key += "wide,"; - bitmap_key += "edit_preset_list"; - bitmap_key += "-h" + std::to_string(icon_height); - - wxBitmap* bmp = m_bitmap_cache->find(bitmap_key); - if (bmp == nullptr) { - // Create the bitmap with color bars. - std::vector bmps; - if (wide_icons) - // Paint a red flag for incompatible presets. - bmps.emplace_back(m_bitmap_cache->mkclear(norm_icon_width, icon_height)); - // Paint the color bars. - bmps.emplace_back(m_bitmap_cache->mkclear(thin_space_icon_width, icon_height)); - bmps.emplace_back(create_scaled_bitmap("printer")); - // Paint a lock at the system presets. - bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width, icon_height)); - bmps.emplace_back(create_scaled_bitmap("edit_uni")); - bmp = m_bitmap_cache->insert(bitmap_key, bmps); - } - set_label_marker(Append(separator(L("Add physical printer")), *bmp), LABEL_ITEM_PHYSICAL_PRINTERS); -*/ } if (m_type == Preset::TYPE_PRINTER || m_type == Preset::TYPE_SLA_MATERIAL) { @@ -657,23 +768,10 @@ void PlaterPresetComboBox::update() if (wide_icons) bitmap_key += "wide,"; bitmap_key += "edit_preset_list"; - bitmap_key += "-h" + std::to_string(icon_height); - wxBitmap* bmp = m_bitmap_cache->find(bitmap_key); - if (bmp == nullptr) { - // Create the bitmap with color bars.update_plater_ui - std::vector bmps; - if (wide_icons) - // Paint a red flag for incompatible presets. - bmps.emplace_back(m_bitmap_cache->mkclear(norm_icon_width, icon_height)); - // Paint the color bars. - bmps.emplace_back(m_bitmap_cache->mkclear(thin_space_icon_width, icon_height)); - bmps.emplace_back(create_scaled_bitmap(m_main_bitmap_name)); - // Paint a lock at the system presets. - bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width, icon_height)); - bmps.emplace_back(create_scaled_bitmap("edit_uni")); - bmp = m_bitmap_cache->insert(bitmap_key, bmps); - } + wxBitmap* bmp = get_bmp(bitmap_key, wide_icons, "edit_uni"); + assert(bmp); + if (m_type == Preset::TYPE_SLA_MATERIAL) set_label_marker(Append(separator(L("Add/Remove materials")), *bmp), LABEL_ITEM_WIZARD_MATERIALS); else @@ -731,8 +829,10 @@ TabPresetComboBox::TabPresetComboBox(wxWindow* parent, Preset::Type preset_type) update(); }); } - else if (on_selection_changed && (m_last_selected != selected_item || m_collection->current_is_dirty()) ) + else if (on_selection_changed && (m_last_selected != selected_item || m_collection->current_is_dirty()) ) { + m_last_selected = selected_item; on_selection_changed(selected_item); + } evt.StopPropagation(); }); @@ -754,7 +854,12 @@ void TabPresetComboBox::update() if (!presets.front().is_visible) set_label_marker(Append(separator(L("System presets")), wxNullBitmap)); int idx_selected = m_collection->get_selected_idx(); - for (size_t i = presets.front().is_visible ? 0 : m_collection->num_default_presets(); i < presets.size(); ++i) { + + std::string sel_preset_name = m_preset_bundle->physical_printers.get_selected_printer_preset_name(); + PrinterTechnology proper_pt = (m_type == Preset::TYPE_PRINTER && m_preset_bundle->physical_printers.has_selection()) ? + m_collection->find_preset(sel_preset_name)->printer_technology() : ptAny; + for (size_t i = presets.front().is_visible ? 0 : m_collection->num_default_presets(); i < presets.size(); ++i) + { const Preset& preset = presets[i]; if (!preset.is_visible || (!show_incompatible && !preset.is_compatible && i != idx_selected)) continue; @@ -762,14 +867,12 @@ void TabPresetComboBox::update() // marker used for disable incompatible printer models for the selected physical printer bool is_enabled = true; // check this value just for printer presets, when physical printer is selected - if (m_type == Preset::TYPE_PRINTER && m_preset_bundle->physical_printers.has_selection()) { - is_enabled = m_enable_all ? true : - preset.name == m_preset_bundle->physical_printers.get_selected_printer_preset_name()/* || - preset.config.opt_string("printer_model") == m_preset_bundle->physical_printers.get_selected_printer_model()*/; - } + if (m_type == Preset::TYPE_PRINTER && m_preset_bundle->physical_printers.has_selection()) + is_enabled = m_enable_all ? true : preset.printer_technology() == proper_pt;//m_preset_bundle->physical_printers.get_selected_printer_technology(); std::string bitmap_key = "tab"; - wxBitmap main_bmp = create_scaled_bitmap(m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name, this, 16, !is_enabled); + std::string main_icon_name = m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; + if (m_type == Preset::TYPE_PRINTER) { bitmap_key += "_printer"; if (preset.printer_technology() == ptSLA) @@ -779,20 +882,12 @@ void TabPresetComboBox::update() } bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt"; bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst"; - bitmap_key += "-h" + std::to_string(icon_height); - wxBitmap* bmp = m_bitmap_cache->find(bitmap_key); - if (bmp == nullptr) { - // Create the bitmap with color bars. - std::vector bmps; - bmps.emplace_back(m_type == Preset::TYPE_PRINTER ? main_bmp : preset.is_compatible ? m_bitmapCompatible.bmp() : m_bitmapIncompatible.bmp()); - // Paint a lock at the system presets. - bmps.emplace_back((preset.is_system || preset.is_default) ? (is_enabled ? m_bitmapLock.bmp() : m_bitmapLockDisabled.bmp()) : m_bitmap_cache->mkclear(norm_icon_width, icon_height)); - bmp = m_bitmap_cache->insert(bitmap_key, bmps); - } + wxBitmap* bmp = get_bmp(bitmap_key, main_icon_name, "lock_closed", is_enabled, preset.is_compatible, preset.is_system || preset.is_default); + assert(bmp); if (preset.is_default || preset.is_system) { - int item_id = Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), !bmp ? main_bmp : *bmp); + int item_id = Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), *bmp); if (!is_enabled) set_label_marker(item_id, LABEL_ITEM_DISABLED); if (i == idx_selected || @@ -824,18 +919,38 @@ void TabPresetComboBox::update() selected_preset_item = GetCount() - 1; } } - if (m_type == Preset::TYPE_PRINTER) { - std::string bitmap_key = "edit_preset_list"; - bitmap_key += "-h" + std::to_string(icon_height); - wxBitmap* bmp = m_bitmap_cache->find(bitmap_key); - if (bmp == nullptr) { - // Create the bitmap with color bars. - std::vector bmps; - bmps.emplace_back(create_scaled_bitmap(m_main_bitmap_name, this)); - bmps.emplace_back(create_scaled_bitmap("edit_uni", this)); - bmp = m_bitmap_cache->insert(bitmap_key, bmps); + if (m_type == Preset::TYPE_PRINTER) + { + // add Physical printers, if any exists + if (!m_preset_bundle->physical_printers.empty()) { + set_label_marker(Append(separator(L("Physical printers")), wxNullBitmap)); + const PhysicalPrinterCollection& ph_printers = m_preset_bundle->physical_printers; + + for (PhysicalPrinterCollection::ConstIterator it = ph_printers.begin(); it != ph_printers.end(); ++it) { + for (const std::string preset_name : it->get_preset_names()) { + Preset* preset = m_collection->find_preset(preset_name); + if (!preset) + continue; + std::string main_icon_name = preset->printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; + std::string bitmap_key = main_icon_name + ",nsyst"; + + wxBitmap* bmp = get_bmp(bitmap_key, main_icon_name, "", true, true, false); + assert(bmp); + + set_label_marker(Append(wxString::FromUTF8((it->get_full_name(preset_name) + (preset->is_dirty ? Preset::suffix_modified() : "")).c_str()), *bmp), LABEL_ITEM_PHYSICAL_PRINTER); + if (ph_printers.is_selected(it, preset_name) || + // just in case: mark selected_preset_item as a first added element + selected_preset_item == INT_MAX) + selected_preset_item = GetCount() - 1; + } + } } + + // add "Add/Remove printers" item + wxBitmap* bmp = get_bmp("edit_preset_list", m_main_bitmap_name, "edit_uni", true, true, true); + assert(bmp); + set_label_marker(Append(separator(L("Add/Remove printers")), *bmp), LABEL_ITEM_WIZARD_PRINTERS); } @@ -869,11 +984,26 @@ void TabPresetComboBox::update_dirty() // 2) Update the labels. wxWindowUpdateLocker noUpdates(this); for (unsigned int ui_id = 0; ui_id < GetCount(); ++ui_id) { + auto marker = reinterpret_cast(this->GetClientData(ui_id)); + if (marker >= LABEL_ITEM_MARKER) + continue; + std::string old_label = GetString(ui_id).utf8_str().data(); std::string preset_name = Preset::remove_suffix_modified(old_label); + std::string ph_printer_name; + + if (marker == LABEL_ITEM_PHYSICAL_PRINTER) { + ph_printer_name = PhysicalPrinter::get_short_name(preset_name); + preset_name = PhysicalPrinter::get_preset_name(preset_name); + } + const Preset* preset = m_collection->find_preset(preset_name, false); if (preset) { std::string new_label = preset->is_dirty ? preset->name + Preset::suffix_modified() : preset->name; + + if (marker == LABEL_ITEM_PHYSICAL_PRINTER) + new_label = ph_printer_name + PhysicalPrinter::separator() + new_label; + if (old_label != new_label) SetString(ui_id, wxString::FromUTF8(new_label.c_str())); } @@ -885,6 +1015,12 @@ void TabPresetComboBox::update_dirty() #endif /* __APPLE __ */ } +void TabPresetComboBox::update_physical_printers( const std::string& preset_name) +{ + if (m_type == Preset::TYPE_PRINTER && update_ph_printers) + update_ph_printers(preset_name); +} + //------------------------------------------ // PresetForPrinter @@ -900,9 +1036,7 @@ PresetForPrinter::PresetForPrinter(PhysicalPrinterDialog* parent, const std::str m_delete_preset_btn->SetToolTip(_L("Delete this preset from this printer device")); m_delete_preset_btn->Bind(wxEVT_BUTTON, &PresetForPrinter::DeletePreset, this); - m_presets_list = new TabPresetComboBox(parent, Preset::TYPE_PRINTER); - if (preset_name.empty()) - m_presets_list->set_enable_all(); + m_presets_list = new PresetComboBox(parent, Preset::TYPE_PRINTER); m_presets_list->set_selection_changed_function([this](int selection) { std::string selected_string = Preset::remove_suffix_modified(m_presets_list->GetString(selection).ToUTF8().data()); @@ -922,8 +1056,7 @@ PresetForPrinter::PresetForPrinter(PhysicalPrinterDialog* parent, const std::str update_full_printer_name(); }); - m_presets_list->update(); - m_presets_list->SetStringSelection(from_u8(preset_name)); + m_presets_list->update(preset_name); m_info_line = new wxStaticText(parent, wxID_ANY, _L("This printer will be shown in the presets list as") + ":"); @@ -997,11 +1130,10 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) SetFont(wxGetApp().normal_font()); SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - if (printer_name.IsEmpty()) { - printer_name = _L("My Printer Device"); - // if printer_name is empty it means that new printer is created, so enable all items in the preset list - m_presets.emplace_back(new PresetForPrinter(this, "")); - } + m_default_name = _L("My Printer Device"); + + if (printer_name.IsEmpty()) + printer_name = m_default_name; else { std::string full_name = into_u8(printer_name); printer_name = from_u8(PhysicalPrinter::get_short_name(full_name)); @@ -1022,6 +1154,8 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) if (!printer) { const Preset& preset = wxGetApp().preset_bundle->printers.get_edited_preset(); printer = new PhysicalPrinter(into_u8(printer_name), preset); + // if printer_name is empty it means that new printer is created, so enable all items in the preset list + m_presets.emplace_back(new PresetForPrinter(this, preset.name)); } else { @@ -1275,7 +1409,11 @@ void PhysicalPrinterDialog::OnOK(wxEvent& event) { wxString printer_name = m_printer_name->GetValue(); if (printer_name.IsEmpty()) { - show_error(this, _L("The supplied name is empty. It can't be saved.")); + warning_catcher(this, _L("The supplied name is empty. It can't be saved.")); + return; + } + if (printer_name == m_default_name) { + warning_catcher(this, _L("You should to change a name of your printer device. It can't be saved.")); return; } @@ -1311,9 +1449,10 @@ void PhysicalPrinterDialog::OnOK(wxEvent& event) repeatable_presets += "\n"; wxString msg_text = from_u8((boost::format(_u8L("Next printer preset(s) is(are) duplicated:%1%" - "It(they) will be added just once for the printer \"%2%\".")) % repeatable_presets % printer_name).str()); - wxMessageDialog dialog(nullptr, msg_text, _L("Infornation"), wxICON_INFORMATION | wxOK); - dialog.ShowModal(); + "Should I add it(they) just once for the printer \"%2%\" and close the Editing Dialog?")) % repeatable_presets % printer_name).str()); + wxMessageDialog dialog(nullptr, msg_text, _L("Warning"), wxICON_WARNING | wxYES | wxNO); + if (dialog.ShowModal() == wxID_NO) + return; } std::string renamed_from; @@ -1344,8 +1483,7 @@ void PhysicalPrinterDialog::OnOK(wxEvent& event) void PhysicalPrinterDialog::AddPreset(wxEvent& event) { - // if printer_name is empty it means that new printer is created, so enable all items in the preset list - m_presets.emplace_back(new PresetForPrinter(this, "")); + m_presets.emplace_back(new PresetForPrinter(this)); // enable DELETE button for the first preset, if was disabled m_presets.front()->EnableDeleteBtn(); diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index c818b6b911..1f5ef026da 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -10,6 +10,7 @@ #include "libslic3r/Preset.hpp" #include "wxExtensions.hpp" #include "GUI_Utils.hpp" +#include "BitmapCache.hpp" class wxString; class wxTextCtrl; @@ -21,8 +22,6 @@ namespace Slic3r { namespace GUI { -class BitmapCache; - // --------------------------------- // *** PresetComboBox *** @@ -49,11 +48,22 @@ public: void set_label_marker(int item, LabelItemType label_item_type = LABEL_ITEM_MARKER); - virtual void update() {}; + void set_selection_changed_function(std::function sel_changed) { on_selection_changed = sel_changed; } + + bool is_selected_physical_printer(); + + // Return true, if physical printer was selected + // and next internal selection was accomplished + bool selection_is_changed_according_to_physical_printers(); + + void update(const std::string& select_preset); + + virtual void update(){}; virtual void msw_rescale(); protected: typedef std::size_t Marker; + std::function on_selection_changed { nullptr }; Preset::Type m_type; std::string m_main_bitmap_name; @@ -61,16 +71,16 @@ protected: PresetBundle* m_preset_bundle {nullptr}; PresetCollection* m_collection {nullptr}; - // Caching color bitmaps for the filament combo box. - BitmapCache* m_bitmap_cache {nullptr}; + // Caching bitmaps for the all bitmaps, used in preset comboboxes + static BitmapCache& bitmap_cache() { + static BitmapCache bmps; + return bmps; + } + // Indicator, that the preset is compatible with the selected printer. ScalableBitmap m_bitmapCompatible; // Indicator, that the preset is NOT compatible with the selected printer. ScalableBitmap m_bitmapIncompatible; - // Indicator, that the preset is system and not modified. - ScalableBitmap m_bitmapLock; - // Disabled analogue of the m_bitmapLock . - ScalableBitmap m_bitmapLockDisabled; int m_last_selected; int m_em_unit; @@ -93,6 +103,13 @@ protected: #endif // __linux__ static wxString separator(const std::string& label); + wxBitmap* get_bmp( std::string bitmap_key, bool wide_icons, const std::string& main_icon_name, + bool is_compatible = true, bool is_system = false, bool is_single_bar = false, + std::string filament_rgb = "", std::string extruder_rgb = ""); + + wxBitmap* get_bmp( std::string bitmap_key, const std::string& main_icon_name, const std::string& next_icon_name, + bool is_enabled = true, bool is_compatible = true, bool is_system = false); + #ifdef __APPLE__ /* For PresetComboBox we use bitmaps that are created from images that are already scaled appropriately for Retina * (Contrary to the intuition, the `scale` argument for Bitmap's constructor doesn't mean @@ -128,7 +145,6 @@ public: void set_extruder_idx(const int extr_idx) { m_extruder_idx = extr_idx; } int get_extruder_idx() const { return m_extruder_idx; } - bool is_selected_physical_printer(); bool switch_to_tab(); void show_add_menu(); void show_edit_menu(); @@ -149,7 +165,8 @@ class TabPresetComboBox : public PresetComboBox { bool show_incompatible {false}; bool m_enable_all {false}; - std::function on_selection_changed { nullptr }; + + std::function update_ph_printers { nullptr }; public: TabPresetComboBox(wxWindow *parent, Preset::Type preset_type); @@ -157,12 +174,15 @@ public: void set_show_incompatible_presets(bool show_incompatible_presets) { show_incompatible = show_incompatible_presets; } + void set_update_physical_printers_function(std::function update_fn) { + update_ph_printers = update_fn; + } void update() override; void update_dirty(); + void update_physical_printers(const std::string& preset_name); void msw_rescale() override; - void set_selection_changed_function(std::function sel_changed) { on_selection_changed = sel_changed; } void set_enable_all(bool enable=true) { m_enable_all = enable; } }; @@ -176,7 +196,7 @@ class PresetForPrinter { PhysicalPrinterDialog* m_parent { nullptr }; - TabPresetComboBox* m_presets_list { nullptr }; + PresetComboBox* m_presets_list { nullptr }; ScalableButton* m_delete_preset_btn { nullptr }; wxStaticText* m_info_line { nullptr }; wxStaticText* m_full_printer_name { nullptr }; @@ -186,7 +206,7 @@ class PresetForPrinter void DeletePreset(wxEvent& event); public: - PresetForPrinter(PhysicalPrinterDialog* parent, const std::string& preset_name); + PresetForPrinter(PhysicalPrinterDialog* parent, const std::string& preset_name = ""); ~PresetForPrinter(); wxBoxSizer* sizer() { return m_sizer; } @@ -208,6 +228,7 @@ class ConfigOptionsGroup; class PhysicalPrinterDialog : public DPIDialog { PhysicalPrinter m_printer; + wxString m_default_name; DynamicPrintConfig* m_config { nullptr }; wxTextCtrl* m_printer_name { nullptr }; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 595283e983..dbea9b3f5c 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -162,10 +162,20 @@ void Tab::create_preset_tab() // preset chooser m_presets_choice = new TabPresetComboBox(panel, m_type); m_presets_choice->set_selection_changed_function([this](int selection) { - std::string selected_string = m_presets_choice->GetString(selection).ToUTF8().data(); - update_physical_printers(selected_string); - // select preset - select_preset(selected_string); + if (!m_presets_choice->selection_is_changed_according_to_physical_printers()) + { + // for the printer presets set callback for the updating of the physical printers + if (m_type == Preset::TYPE_PRINTER) + m_presets_choice->set_update_physical_printers_function([this](std::string preset_name) { update_physical_printers(preset_name);}); + + // select preset + select_preset(m_presets_choice->GetString(selection).ToUTF8().data()); + + // Disable callback for the updating of the physical printers to avoid a case, + // when select_preset is called from the others than this place + if (m_type == Preset::TYPE_PRINTER) + m_presets_choice->set_update_physical_printers_function(nullptr); + } }); auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); @@ -764,9 +774,11 @@ void Tab::update_tab_ui() void Tab::update_physical_printers(std::string preset_name) { - if (m_type == Preset::TYPE_PRINTER && m_preset_bundle->physical_printers.has_selection()) + PhysicalPrinterCollection& physical_printers = wxGetApp().preset_bundle->physical_printers; + if (physical_printers.has_selection() && + Preset::remove_suffix_modified(preset_name) != physical_printers.get_selected_printer_preset_name()) { - std::string printer_name = m_preset_bundle->physical_printers.get_selected_full_printer_name(); + std::string printer_name = physical_printers.get_selected_full_printer_name(); wxString msg_text = from_u8((boost::format(_u8L("You have selected physical printer \"%1%\".")) % printer_name).str()); msg_text += "\n\n" + _L("Would you like to change related preset for this printer?") + "\n\n" + _L("Select YES if you want to change related preset for this printer \n" @@ -780,12 +792,14 @@ void Tab::update_physical_printers(std::string preset_name) Preset& edited_preset = m_presets->get_edited_preset(); if (preset->name == edited_preset.name) preset = &edited_preset; - m_preset_bundle->physical_printers.get_selected_printer().update_from_preset(*preset); + physical_printers.get_selected_printer().update_from_preset(*preset); + physical_printers.select_printer_by_name(physical_printers.get_selected_printer().get_full_name(preset_name)); + return; } - else - // unselect physical printer, if it was selected - m_preset_bundle->physical_printers.unselect_printer(); } + + // unselect physical printer, if it was selected + m_preset_bundle->physical_printers.unselect_printer(); } // Load a provied DynamicConfig into the tab, modifying the active preset. @@ -2148,11 +2162,10 @@ void TabPrinter::build_fff() line.append_widget(serial_test); optgroup->append_line(line); } -#endif optgroup = page->new_optgroup(L("Print Host upload")); build_printhost(optgroup.get()); - +#endif optgroup = page->new_optgroup(L("Firmware")); optgroup->append_single_option_line("gcode_flavor"); optgroup->append_single_option_line("silent_mode"); @@ -2310,8 +2323,10 @@ void TabPrinter::build_sla() optgroup->append_single_option_line("min_initial_exposure_time"); optgroup->append_single_option_line("max_initial_exposure_time"); + /* optgroup = page->new_optgroup(L("Print Host upload")); build_printhost(optgroup.get()); + */ const int notes_field_height = 25; // 250 @@ -2699,11 +2714,13 @@ void TabPrinter::update_fff() m_serial_test_btn->Disable(); } + /* { std::unique_ptr host(PrintHost::get_print_host(m_config)); m_print_host_test_btn->Enable(!m_config->opt_string("print_host").empty() && host->can_test()); m_printhost_browse_btn->Enable(host->has_auto_discovery()); } + */ bool have_multiple_extruders = m_extruders_count > 1; get_field("toolchange_gcode")->toggle(have_multiple_extruders); @@ -2942,10 +2959,15 @@ void Tab::update_page_tree_visibility() } +void Tab::update_preset_choice() +{ + m_presets_choice->update(); +} + // Called by the UI combo box when the user switches profiles, and also to delete the current profile. // Select a preset by a name.If !defined(name), then the default preset is selected. // If the current profile is modified, user is asked to save the changes. -void Tab::select_preset(std::string preset_name, bool delete_current) +void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, const std::string& last_selected_ph_printer_name/* =""*/) { if (preset_name.empty()) { if (delete_current) { @@ -3054,9 +3076,22 @@ void Tab::select_preset(std::string preset_name, bool delete_current) if (canceled) { update_tab_ui(); + /* // unselect physical printer selection to the correct synchronization of the printer presets between Tab and Plater if (m_type == Preset::TYPE_PRINTER) m_preset_bundle->physical_printers.unselect_printer(); + */ + + + // Check if preset really was changed. + // If preset selection was canceled and previously was selected physical printer, we should select it back + if (m_type == Preset::TYPE_PRINTER && !last_selected_ph_printer_name.empty()) { + if (m_presets->get_edited_preset().name == PhysicalPrinter::get_preset_name(last_selected_ph_printer_name)) { + m_preset_bundle->physical_printers.select_printer_by_name(last_selected_ph_printer_name); + m_presets_choice->update(); + } + } + // Trigger the on_presets_changed event so that we also restore the previous value in the plater selector, // if this action was initiated from the plater. on_presets_changed(); @@ -3098,6 +3133,10 @@ void Tab::select_preset(std::string preset_name, bool delete_current) else if (printer_technology == ptSLA && m_dependent_tabs.front() != Preset::Type::TYPE_SLA_PRINT) m_dependent_tabs = { Preset::Type::TYPE_SLA_PRINT, Preset::Type::TYPE_SLA_MATERIAL }; } + + //update physical printer's related printer preset if it's needed + m_presets_choice->update_physical_printers(preset_name); + load_current_preset(); } } @@ -3295,7 +3334,7 @@ void Tab::save_preset(std::string name /*= ""*/, bool detach) // If saving the preset changes compatibility with other presets, keep the now incompatible dependent presets selected, however with a "red flag" icon showing that they are no more compatible. m_preset_bundle->update_compatible(PresetSelectCompatibleType::Never); //update physical printer's related printer preset if it's needed - update_physical_printers(name); + m_presets_choice->update_physical_printers(name); // Add the new item into the UI component, remove dirty flags and activate the saved item. update_tab_ui(); // Update the selection boxes at the plater. diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 69720ff65e..82df31b590 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -275,8 +275,9 @@ public: void load_current_preset(); void rebuild_page_tree(); void update_page_tree_visibility(); - // Select a new preset, possibly delete the current one. - void select_preset(std::string preset_name = "", bool delete_current = false); + void update_preset_choice(); + // Select a new preset, possibly delete the current one. + void select_preset(std::string preset_name = "", bool delete_current = false, const std::string& last_selected_ph_printer_name = ""); bool may_discard_current_dirty_preset(PresetCollection* presets = nullptr, const std::string& new_printer_name = ""); bool may_switch_to_SLA_preset(); From 087c83c95877bc0c09ef55ba24919436617b5d7a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 17 Jul 2020 14:58:58 +0200 Subject: [PATCH 166/503] GCodeViewer -> 3rd attempt to fix rendering of toolpaths on Mac --- src/slic3r/GUI/GCodeViewer.cpp | 43 +++++++++++++++++----------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 0ab43a98c7..a697306cfd 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1232,8 +1232,7 @@ void GCodeViewer::render_toolpaths() const }; auto render_as_lines = [this](const TBuffer& buffer, GLShaderProgram& shader) { - for (const RenderPath& path : buffer.render_paths) - { + for (const RenderPath& path : buffer.render_paths) { shader.set_uniform("uniform_color", path.color); glsafe(::glMultiDrawElements(GL_LINES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS @@ -1242,16 +1241,11 @@ void GCodeViewer::render_toolpaths() const } }; - auto line_width = [zoom]() { -#ifdef WIN32 + auto line_width = [](double zoom) { return (zoom < 5.0) ? 1.0 : (1.0 + 5.0 * (zoom - 5.0) / (100.0 - 5.0)); -#else - return 3.0f; -#endif // WIN32 }; - glsafe(::glCullFace(GL_BACK)); - glsafe(::glLineWidth(static_cast(line_width()))); + glsafe(::glLineWidth(static_cast(line_width(zoom)))); unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); unsigned char end_id = buffer_id(GCodeProcessor::EMoveType::Count); @@ -1269,8 +1263,12 @@ void GCodeViewer::render_toolpaths() const shader->start_using(); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vertices.id)); - glsafe(::glVertexAttribPointer(0, buffer.vertices.vertex_size_floats(), GL_FLOAT, GL_FALSE, buffer.vertices.vertex_size_bytes(), (const void*)0)); - glsafe(::glEnableVertexAttribArray(0)); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + glsafe(::glVertexPointer(buffer.vertices.vertex_size_floats(), GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)0)); + glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); +// glsafe(::glVertexAttribPointer(0, buffer.vertices.vertex_size_floats(), GL_FLOAT, GL_FALSE, buffer.vertices.vertex_size_bytes(), (const void*)0)); +// glsafe(::glEnableVertexAttribArray(0)); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.indices.id)); @@ -1286,26 +1284,27 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Extrude: case GCodeProcessor::EMoveType::Travel: { - std::array light_intensity; #if ENABLE_GCODE_VIEWER_SHADERS_EDITOR - light_intensity[0] = m_shaders_editor.lines.lights.ambient; - light_intensity[1] = m_shaders_editor.lines.lights.top_diffuse; - light_intensity[2] = m_shaders_editor.lines.lights.front_diffuse; - light_intensity[3] = m_shaders_editor.lines.lights.global; + std::array light_intensity = { + m_shaders_editor.lines.lights.ambient, + m_shaders_editor.lines.lights.top_diffuse, + m_shaders_editor.lines.lights.front_diffuse, + m_shaders_editor.lines.lights.global }; #else - light_intensity[0] = 0.25f; - light_intensity[1] = 0.7f; - light_intensity[2] = 0.75f; - light_intensity[3] = 0.75f; + std::array light_intensity = { 0.25f, 0.7f, 0.75f, 0.75f }; #endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR shader->set_uniform("light_intensity", light_intensity); - render_as_lines(buffer, *shader); break; + render_as_lines(buffer, *shader); + break; } } glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - glsafe(::glDisableVertexAttribArray(0)); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); +// glsafe(::glDisableVertexAttribArray(0)); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); shader->stop_using(); From afd9429e6d9b56a26db85270f498cf287e45864d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 17 Jul 2020 15:18:29 +0200 Subject: [PATCH 167/503] Code cleanup --- src/slic3r/GUI/GCodeViewer.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index a697306cfd..3edf0d907c 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1263,12 +1263,8 @@ void GCodeViewer::render_toolpaths() const shader->start_using(); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vertices.id)); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glVertexPointer(buffer.vertices.vertex_size_floats(), GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)0)); glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); -// glsafe(::glVertexAttribPointer(0, buffer.vertices.vertex_size_floats(), GL_FLOAT, GL_FALSE, buffer.vertices.vertex_size_bytes(), (const void*)0)); -// glsafe(::glEnableVertexAttribArray(0)); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.indices.id)); @@ -1301,10 +1297,7 @@ void GCodeViewer::render_toolpaths() const glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); -// glsafe(::glDisableVertexAttribArray(0)); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); shader->stop_using(); From 51f0fd8912ef2f368c20e99d41131f2e7f87e16b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 20 Jul 2020 09:45:49 +0200 Subject: [PATCH 168/503] GCodeViewer -> Added visualization of percentage in estimated printing time dialog --- src/libslic3r/Print.hpp | 2 + src/slic3r/GUI/GCodeViewer.cpp | 88 +++++++++++++++------------------ src/slic3r/GUI/ImGuiWrapper.cpp | 4 +- src/slic3r/GUI/MainFrame.cpp | 4 ++ 4 files changed, 48 insertions(+), 50 deletions(-) diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index e2b7ab5467..7a0ecdf170 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -340,6 +340,7 @@ struct PrintStatistics void clear() { #if ENABLE_GCODE_VIEWER + clear_time_estimates(); estimated_normal_print_time_str.clear(); estimated_silent_print_time_str.clear(); estimated_normal_custom_gcode_print_times_str.clear(); @@ -461,6 +462,7 @@ public: const Polygon& first_layer_convex_hull() const { return m_first_layer_convex_hull; } const PrintStatistics& print_statistics() const { return m_print_statistics; } + PrintStatistics& print_statistics() { return m_print_statistics; } // Wipe tower support. bool has_wipe_tower() const; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 3edf0d907c..07cfb396e8 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1740,14 +1740,25 @@ void GCodeViewer::render_time_estimate() const }; using PartialTimes = std::vector; - auto append_mode = [this, &imgui](float total_time, const PartialTimes& items, + auto append_headers = [&imgui](const Headers& headers, const ColumnOffsets& offsets) { + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); + imgui.text(headers[0]); + ImGui::SameLine(offsets[0]); + imgui.text(headers[1]); + ImGui::SameLine(offsets[1]); + imgui.text(headers[2]); + ImGui::PopStyleColor(); + ImGui::Separator(); + }; + + auto append_mode = [this, &imgui, append_headers](float total_time, const PartialTimes& items, const Headers& partial_times_headers, const std::vector>& moves_time, const Headers& moves_headers, const std::vector>& roles_time, const Headers& roles_headers) { - auto append_partial_times = [this, &imgui](const PartialTimes& items, const Headers& headers) { - auto calc_offsets = [this, &headers](const PartialTimes& items) { + auto append_partial_times = [this, &imgui, append_headers](const PartialTimes& items, const Headers& headers) { + auto calc_offsets = [this, &headers](const PartialTimes& items) { ColumnOffsets ret = { ImGui::CalcTextSize(headers[0].c_str()).x, ImGui::CalcTextSize(headers[1].c_str()).x }; for (const PartialTime& item : items) { std::string label; @@ -1799,14 +1810,7 @@ void GCodeViewer::render_time_estimate() const ColumnOffsets offsets = calc_offsets(items); ImGui::Spacing(); - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(headers[0]); - ImGui::SameLine(offsets[0]); - imgui.text(headers[1]); - ImGui::SameLine(offsets[1]); - imgui.text(headers[2]); - ImGui::PopStyleColor(); - ImGui::Separator(); + append_headers(headers, offsets); for (const PartialTime& item : items) { switch (item.type) @@ -1856,7 +1860,26 @@ void GCodeViewer::render_time_estimate() const } }; - auto append_move_times = [this, &imgui, move_type_label](float total_time, + auto append_time_item = [&imgui] (const std::string& label, float time, float percentage, const ImVec4& color, const ColumnOffsets& offsets) { + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); + imgui.text(label); + ImGui::PopStyleColor(); + ImGui::SameLine(offsets[0]); + imgui.text(short_time(get_time_dhms(time))); + ImGui::SameLine(offsets[1]); + char buf[64]; + ::sprintf(buf, "%.2f%%", 100.0f * percentage); + ImGuiWindow* window = ImGui::GetCurrentWindow(); + ImRect frame_bb; + frame_bb.Min = { ImGui::GetCursorScreenPos().x, window->DC.CursorPos.y }; + frame_bb.Max = { frame_bb.Min.x + percentage * (window->WorkRect.Max.x - frame_bb.Min.x), window->DC.CursorPos.y + ImGui::CalcTextSize(buf, nullptr, false).y }; + frame_bb.Min.x -= IM_FLOOR(window->WindowPadding.x * 0.5f - 1.0f); + frame_bb.Max.x += IM_FLOOR(window->WindowPadding.x * 0.5f); + window->DrawList->AddRectFilled(frame_bb.Min, frame_bb.Max, ImGui::GetColorU32({ color.x, color.y, color.z, 1.0f }), 0.0f, 0); + ImGui::TextUnformatted(buf); + }; + + auto append_move_times = [this, &imgui, move_type_label, append_headers, append_time_item](float total_time, const std::vector>& moves_time, const Headers& headers, const ColumnOffsets& offsets) { @@ -1866,32 +1889,17 @@ void GCodeViewer::render_time_estimate() const if (!ImGui::CollapsingHeader(_u8L("Moves Time").c_str())) return; - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(headers[0]); - ImGui::SameLine(offsets[0]); - imgui.text(headers[1]); - ImGui::SameLine(offsets[1]); - imgui.text(headers[2]); - ImGui::PopStyleColor(); - ImGui::Separator(); + append_headers(headers, offsets); std::vector> sorted_moves_time(moves_time); std::sort(sorted_moves_time.begin(), sorted_moves_time.end(), [](const auto& p1, const auto& p2) { return p2.second < p1.second; }); for (const auto& [type, time] : sorted_moves_time) { - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(move_type_label(type)); - ImGui::PopStyleColor(); - ImGui::SameLine(offsets[0]); - imgui.text(short_time(get_time_dhms(time))); - ImGui::SameLine(offsets[1]); - char buf[64]; - ::sprintf(buf, "%.2f%%", 100.0f * time / total_time); - ImGui::TextUnformatted(buf); + append_time_item(move_type_label(type), time, time / total_time, ImGuiWrapper::COL_ORANGE_LIGHT, offsets); } }; - auto append_role_times = [this, &imgui](float total_time, + auto append_role_times = [this, &imgui, append_headers, append_time_item](float total_time, const std::vector>& roles_time, const Headers& headers, const ColumnOffsets& offsets) { @@ -1901,28 +1909,14 @@ void GCodeViewer::render_time_estimate() const if (!ImGui::CollapsingHeader(_u8L("Features Time").c_str())) return; - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(headers[0]); - ImGui::SameLine(offsets[0]); - imgui.text(headers[1]); - ImGui::SameLine(offsets[1]); - imgui.text(headers[2]); - ImGui::PopStyleColor(); - ImGui::Separator(); + append_headers(headers, offsets); std::vector> sorted_roles_time(roles_time); std::sort(sorted_roles_time.begin(), sorted_roles_time.end(), [](const auto& p1, const auto& p2) { return p2.second < p1.second; }); for (const auto& [role, time] : sorted_roles_time) { - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(_u8L(ExtrusionEntity::role_to_string(role))); - ImGui::PopStyleColor(); - ImGui::SameLine(offsets[0]); - imgui.text(short_time(get_time_dhms(time))); - ImGui::SameLine(offsets[1]); - char buf[64]; - ::sprintf(buf, "%.2f%%", 100.0f * time / total_time); - ImGui::TextUnformatted(buf); + Color color = Extrusion_Role_Colors[static_cast(role)]; + append_time_item(_u8L(ExtrusionEntity::role_to_string(role)), time, time / total_time, { 0.666f * color[0], 0.666f * color[1], 0.666f * color[2], 1.0f}, offsets); } }; diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 1253c047ef..b4e8c6d0f9 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -760,12 +760,10 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co void ImGuiWrapper::title(const std::string& str) { ImGuiWindow* window = ImGui::GetCurrentWindow(); - const float frame_height = ImGui::CalcTextSize(str.c_str(), nullptr, false).y; ImRect frame_bb; frame_bb.Min = { window->WorkRect.Min.x, window->DC.CursorPos.y }; - frame_bb.Max = { window->WorkRect.Max.x, window->DC.CursorPos.y + frame_height }; - + frame_bb.Max = { window->WorkRect.Max.x, window->DC.CursorPos.y + ImGui::CalcTextSize(str.c_str(), nullptr, false).y }; frame_bb.Min.x -= IM_FLOOR(window->WindowPadding.x * 0.5f - 1.0f); frame_bb.Max.x += IM_FLOOR(window->WindowPadding.x * 0.5f); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index eda980a93b..d917e82b4d 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1428,6 +1428,8 @@ void MainFrame::set_mode(EMode mode) select_tab(0); #endif // ENABLE_LAYOUT_NO_RESTART + m_plater->fff_print().print_statistics().clear_time_estimates(); + m_plater->reset(); m_plater->reset_gcode_toolpaths(); @@ -1471,6 +1473,8 @@ void MainFrame::set_mode(EMode mode) update_layout(); #endif // ENABLE_LAYOUT_NO_RESTART + m_plater->fff_print().print_statistics().clear_time_estimates(); + m_plater->reset(); m_plater->reset_last_loaded_gcode(); m_plater->reset_gcode_toolpaths(); From 4700579589377f8036ec442913e3b4b6a273987b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 20 Jul 2020 12:25:00 +0200 Subject: [PATCH 169/503] GCodeViewer -> Estimated printing time dialog hidden by defaul --- src/slic3r/GUI/GCodeViewer.cpp | 8 ++++++ src/slic3r/GUI/GCodeViewer.hpp | 4 +-- src/slic3r/GUI/GLCanvas3D.hpp | 1 + src/slic3r/GUI/Plater.cpp | 45 +++++++++++++++++++--------------- 4 files changed, 36 insertions(+), 22 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 07cfb396e8..2d2bb512d9 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -495,6 +495,14 @@ void GCodeViewer::set_layers_z_range(const std::array& layers_z_range wxGetApp().plater()->update_preview_moves_slider(); } +void GCodeViewer::enable_time_estimate(bool enable) +{ + m_time_estimate_enabled = enable; + wxGetApp().update_ui_from_settings(); + wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); + wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); +} + void GCodeViewer::export_toolpaths_to_obj(const char* filename) const { if (filename == nullptr) diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 90155c7281..564b625699 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -341,7 +341,7 @@ private: Shells m_shells; EViewType m_view_type{ EViewType::FeatureType }; bool m_legend_enabled{ true }; - bool m_time_estimate_enabled{ true }; + bool m_time_estimate_enabled{ false }; #if ENABLE_GCODE_VIEWER_STATISTICS mutable Statistics m_statistics; #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -398,7 +398,7 @@ public: void enable_legend(bool enable) { m_legend_enabled = enable; } bool is_time_estimate_enabled() const { return m_time_estimate_enabled; } - void enable_time_estimate(bool enable) { m_time_estimate_enabled = enable; } + void enable_time_estimate(bool enable); void export_toolpaths_to_obj(const char* filename) const; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 782a9425d7..0b5a357fdb 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -558,6 +558,7 @@ public: void reset_gcode_toolpaths() { m_gcode_viewer.reset(); } const GCodeViewer::SequentialView& get_gcode_sequential_view() const { return m_gcode_viewer.get_sequential_view(); } void update_gcode_sequential_view_current(unsigned int first, unsigned int last) { m_gcode_viewer.update_sequential_view_current(first, last); } + bool is_time_estimate_enabled() const { return m_gcode_viewer.is_time_estimate_enabled(); } #endif // ENABLE_GCODE_VIEWER void toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index db19ff39b6..39003e9aaa 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1323,13 +1323,14 @@ void Sidebar::update_sliced_info_sizer() p->sliced_info->SetTextAndShow(siCost, info_text, new_label); #if ENABLE_GCODE_VIEWER - if (ps.estimated_normal_print_time_str == "N/A" && ps.estimated_silent_print_time_str == "N/A") + if (p->plater->get_current_canvas3D()->is_time_estimate_enabled() || (ps.estimated_normal_print_time_str == "N/A" && ps.estimated_silent_print_time_str == "N/A")) #else + if (ps.estimated_normal_print_time == "N/A" && ps.estimated_silent_print_time == "N/A") #endif // ENABLE_GCODE_VIEWER p->sliced_info->SetTextAndShow(siEstimatedTime, "N/A"); else { - new_label = _L("Estimated printing time") +":"; + new_label = _L("Estimated printing time") + ":"; info_text = ""; wxString str_color = _L("Color"); wxString str_pause = _L("Pause"); @@ -1340,29 +1341,29 @@ void Sidebar::update_sliced_info_sizer() auto fill_labels = [str_color, str_pause](const std::vector>& times, #endif // ENABLE_GCODE_VIEWER wxString& new_label, wxString& info_text) - { - int color_change_count = 0; - for (auto time : times) - if (time.first == CustomGCode::ColorChange) - color_change_count++; - - for (int i = (int)times.size() - 1; i >= 0; --i) { - if (i == 0 || times[i - 1].first == CustomGCode::PausePrint) - new_label += format_wxstr("\n - %1%%2%", str_color + " ", color_change_count); - else if (times[i - 1].first == CustomGCode::ColorChange) - new_label += format_wxstr("\n - %1%%2%", str_color + " ", color_change_count--); + int color_change_count = 0; + for (auto time : times) + if (time.first == CustomGCode::ColorChange) + color_change_count++; - if (i != (int)times.size() - 1 && times[i].first == CustomGCode::PausePrint) - new_label += format_wxstr(" -> %1%", str_pause); + for (int i = (int)times.size() - 1; i >= 0; --i) + { + if (i == 0 || times[i - 1].first == CustomGCode::PausePrint) + new_label += format_wxstr("\n - %1%%2%", str_color + " ", color_change_count); + else if (times[i - 1].first == CustomGCode::ColorChange) + new_label += format_wxstr("\n - %1%%2%", str_color + " ", color_change_count--); + + if (i != (int)times.size() - 1 && times[i].first == CustomGCode::PausePrint) + new_label += format_wxstr(" -> %1%", str_pause); #if ENABLE_GCODE_VIEWER - info_text += format_wxstr("\n%1% (%2%)", times[i].second.first, times[i].second.second); + info_text += format_wxstr("\n%1% (%2%)", times[i].second.first, times[i].second.second); #else - info_text += format_wxstr("\n%1%", times[i].second); + info_text += format_wxstr("\n%1%", times[i].second); #endif // ENABLE_GCODE_VIEWER - } - }; + } + }; #if ENABLE_GCODE_VIEWER if (ps.estimated_normal_print_time_str != "N/A") { @@ -1386,7 +1387,7 @@ void Sidebar::update_sliced_info_sizer() fill_labels(ps.estimated_silent_custom_gcode_print_times, new_label, info_text); #endif // ENABLE_GCODE_VIEWER } - p->sliced_info->SetTextAndShow(siEstimatedTime, info_text, new_label); + p->sliced_info->SetTextAndShow(siEstimatedTime, info_text, new_label); } // if there is a wipe tower, insert number of toolchanges info into the array: @@ -2180,6 +2181,10 @@ void Plater::priv::select_view_3D(const std::string& name) set_current_panel(view3D); else if (name == "Preview") set_current_panel(preview); + +#if ENABLE_GCODE_VIEWER + wxGetApp().update_ui_from_settings(); +#endif // ENABLE_GCODE_VIEWER } void Plater::priv::select_next_view_3D() From 2de442b617a315437bf5c2a4aba1bbe435bc99aa Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 20 Jul 2020 13:31:39 +0200 Subject: [PATCH 170/503] Pull request #4235 - Fix tick/untick ironing feature in preview by rongith --- src/libslic3r/ExtrusionEntity.cpp | 2 +- src/libslic3r/PrintConfig.cpp | 2 +- src/slic3r/GUI/GUI_Preview.cpp | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index c482f7edba..69b3a6455d 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -312,8 +312,8 @@ std::string ExtrusionEntity::role_to_string(ExtrusionRole role) case erOverhangPerimeter : return L("Overhang perimeter"); case erInternalInfill : return L("Internal infill"); case erSolidInfill : return L("Solid infill"); - case erIroning : return L("Ironing"); case erTopSolidInfill : return L("Top solid infill"); + case erIroning : return L("Ironing"); case erBridgeInfill : return L("Bridge infill"); case erGapFill : return L("Gap fill"); case erSkirt : return L("Skirt"); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index d6a23d75d3..0c1c828f7e 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1122,7 +1122,7 @@ void PrintConfigDef::init_fff_params() def = this->add("ironing_spacing", coFloat); def->label = L("Spacing between ironing passes"); - def->tooltip = L("Distance between ironing lins"); + def->tooltip = L("Distance between ironing lines"); def->sidetext = L("mm"); def->min = 0; def->mode = comExpert; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index c1e8b4c33c..b4606ab7f0 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -250,6 +250,7 @@ bool Preview::init(wxWindow* parent, Model* model) _(L("Internal infill")) + "|" + _(L("Solid infill")) + "|" + _(L("Top solid infill")) + "|" + + _(L("Ironing")) + "|" + _(L("Bridge infill")) + "|" + _(L("Gap fill")) + "|" + _(L("Skirt")) + "|" + From 72ec414f1e4492d67465efe368a4a224a21ed7eb Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 20 Jul 2020 14:56:09 +0200 Subject: [PATCH 171/503] PhysicalPrinters improvements: - Added possibility to correct delete presets considering with the physical printers - Smart switching to the printer preset if physical printer was selected --- src/libslic3r/Preset.cpp | 55 +++++++++- src/libslic3r/Preset.hpp | 9 +- src/slic3r/GUI/PresetComboBoxes.cpp | 154 ++++++++++++++++++++++++---- src/slic3r/GUI/PresetComboBoxes.hpp | 32 +++++- src/slic3r/GUI/Tab.cpp | 141 ++++++++++++++----------- src/slic3r/GUI/Tab.hpp | 2 +- 6 files changed, 302 insertions(+), 91 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index ad7615f6d1..a55dc24cac 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1436,6 +1436,11 @@ bool PhysicalPrinter::add_preset(const std::string& preset_name) return preset_names.emplace(preset_name).second; } +bool PhysicalPrinter::delete_preset(const std::string& preset_name) +{ + return preset_names.erase(preset_name) > 0; +} + PhysicalPrinter::PhysicalPrinter(const std::string& name, const Preset& preset) : name(name) { @@ -1521,7 +1526,6 @@ void PhysicalPrinterCollection::load_printers(const std::string& dir_path, const } m_printers.insert(m_printers.end(), std::make_move_iterator(printers_loaded.begin()), std::make_move_iterator(printers_loaded.end())); std::sort(m_printers.begin(), m_printers.end()); -//! this->select_preset(first_visible_idx()); if (!errors_cummulative.empty()) throw std::runtime_error(errors_cummulative); } @@ -1542,8 +1546,11 @@ std::string PhysicalPrinterCollection::path_from_name(const std::string& new_nam return (boost::filesystem::path(m_dir_path) / file_name).make_preferred().string(); } -void PhysicalPrinterCollection::save_printer(const PhysicalPrinter& edited_printer, const std::string& renamed_from) +void PhysicalPrinterCollection::save_printer(PhysicalPrinter& edited_printer, const std::string& renamed_from/* = ""*/) { + // controll and update preset_names in edited_printer config + edited_printer.update_preset_names_in_config(); + std::string name = renamed_from.empty() ? edited_printer.name : renamed_from; // 1) Find the printer with a new_name or create a new one, // initialize it with the edited config. @@ -1605,6 +1612,33 @@ bool PhysicalPrinterCollection::delete_selected_printer() return true; } +bool PhysicalPrinterCollection::delete_preset_from_printers( const std::string& preset_name, bool first_check /*=true*/) +{ + if (first_check) { + for (auto printer: m_printers) + if (printer.preset_names.size()==1 && *printer.preset_names.begin() == preset_name) + return false; + } + + std::vector printers_for_delete; + for (PhysicalPrinter& printer : m_printers) + if (printer.preset_names.size() == 1 && *printer.preset_names.begin() == preset_name) + printers_for_delete.emplace_back(printer.name); + else if (printer.delete_preset(preset_name)) { + if (printer.name == get_selected_printer_name() && + preset_name == get_selected_printer_preset_name()) + select_printer(printer); + save_printer(printer); + } + + if (!printers_for_delete.empty()) { + for (const std::string& printer_name : printers_for_delete) + delete_printer(printer_name); + unselect_printer(); + } + return true; +} + std::string PhysicalPrinterCollection::get_selected_full_printer_name() const { return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_printer().get_full_name(m_selected_preset); @@ -1625,6 +1659,23 @@ PhysicalPrinter& PhysicalPrinterCollection::select_printer_by_name(const std::st return *it; } +PhysicalPrinter& PhysicalPrinterCollection::select_printer(const std::string& printer_name) +{ + auto it = this->find_printer_internal(printer_name); + assert(it != m_printers.end()); + + // update idx_selected + m_idx_selected = it - m_printers.begin(); + // update name of the currently selected preset + m_selected_preset = *it->preset_names.begin(); + return *it; +} + +PhysicalPrinter& PhysicalPrinterCollection::select_printer(const PhysicalPrinter& printer) +{ + return select_printer(printer.name); +} + bool PhysicalPrinterCollection::has_selection() const { return m_idx_selected != size_t(-1); diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index d30ea70590..e8afb0f6fd 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -569,6 +569,7 @@ public: // add preset to the preset_names // return false, if preset with this name is already exist in the set bool add_preset(const std::string& preset_name); + bool delete_preset(const std::string& preset_name); void reset_presets(); // Return a printer technology, return ptFFF if the printer technology is not set. @@ -629,7 +630,7 @@ public: // Save the printer under a new name. If the name is different from the old one, // a new printer is stored into the list of printers. // New printer is activated. - void save_printer(const PhysicalPrinter& printer, const std::string& renamed_from); + void save_printer(PhysicalPrinter& printer, const std::string& renamed_from = ""); // Delete the current preset, activate the first visible preset. // returns true if the preset was deleted successfully. @@ -637,6 +638,10 @@ public: // Delete the selected preset // returns true if the preset was deleted successfully. bool delete_selected_printer(); + // Delete preset_name preset from all printers: + // If there is last preset for the printer and first_check== false, then delete this printer + // returns true if all presets were deleted successfully. + bool delete_preset_from_printers(const std::string& preset_name, bool first_check = true); // Return the selected preset, without the user modifications applied. PhysicalPrinter& get_selected_printer() { return m_printers[m_idx_selected]; } @@ -659,6 +664,8 @@ public: // select printer with name and return reference on it PhysicalPrinter& select_printer_by_name(const std::string& full_name); + PhysicalPrinter& select_printer(const std::string &printer_name); + PhysicalPrinter& select_printer(const PhysicalPrinter& printer); bool has_selection() const; void unselect_printer() ; bool is_selected(ConstIterator it, const std::string &preset_name) const; diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index e08cf101d6..bc1f48dd64 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -357,6 +357,8 @@ bool PresetComboBox::selection_is_changed_according_to_physical_printers() wxGetApp().get_tab(m_type)->update_preset_choice(); else if (dynamic_cast(this)!=nullptr) wxGetApp().sidebar().update_presets(m_type); + + this->update(); return true; } @@ -614,6 +616,8 @@ void PlaterPresetComboBox::show_edit_menu() return; m_preset_bundle->physical_printers.delete_selected_printer(); + + wxGetApp().get_tab(m_type)->update_preset_choice(); update(); }, "cross", menu, []() { return true; }, wxGetApp().plater()); @@ -855,9 +859,16 @@ void TabPresetComboBox::update() set_label_marker(Append(separator(L("System presets")), wxNullBitmap)); int idx_selected = m_collection->get_selected_idx(); - std::string sel_preset_name = m_preset_bundle->physical_printers.get_selected_printer_preset_name(); - PrinterTechnology proper_pt = (m_type == Preset::TYPE_PRINTER && m_preset_bundle->physical_printers.has_selection()) ? - m_collection->find_preset(sel_preset_name)->printer_technology() : ptAny; + PrinterTechnology proper_pt = ptAny; + if (m_type == Preset::TYPE_PRINTER && m_preset_bundle->physical_printers.has_selection()) { + std::string sel_preset_name = m_preset_bundle->physical_printers.get_selected_printer_preset_name(); + Preset* preset = m_collection->find_preset(sel_preset_name); + if (preset) + proper_pt = preset->printer_technology(); + else + m_preset_bundle->physical_printers.unselect_printer(); + } + for (size_t i = presets.front().is_visible ? 0 : m_collection->num_default_presets(); i < presets.size(); ++i) { const Preset& preset = presets[i]; @@ -868,7 +879,7 @@ void TabPresetComboBox::update() bool is_enabled = true; // check this value just for printer presets, when physical printer is selected if (m_type == Preset::TYPE_PRINTER && m_preset_bundle->physical_printers.has_selection()) - is_enabled = m_enable_all ? true : preset.printer_technology() == proper_pt;//m_preset_bundle->physical_printers.get_selected_printer_technology(); + is_enabled = m_enable_all ? true : preset.printer_technology() == proper_pt; std::string bitmap_key = "tab"; std::string main_icon_name = m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; @@ -1017,8 +1028,49 @@ void TabPresetComboBox::update_dirty() void TabPresetComboBox::update_physical_printers( const std::string& preset_name) { - if (m_type == Preset::TYPE_PRINTER && update_ph_printers) - update_ph_printers(preset_name); + if (m_type != Preset::TYPE_PRINTER || !m_allow_to_update_physical_printers) + return; + + m_allow_to_update_physical_printers = false; + + PhysicalPrinterCollection& physical_printers = m_preset_bundle->physical_printers; + if (!physical_printers.has_selection()) + return; + + std::string printer_preset_name = physical_printers.get_selected_printer_preset_name(); + + if (Preset::remove_suffix_modified(preset_name) == printer_preset_name) { + if (!this->is_selected_physical_printer()) + physical_printers.unselect_printer(); + } + else + { + ChangePresetForPhysicalPrinterDialog dlg(Preset::remove_suffix_modified(preset_name)); + if(dlg.ShowModal() == wxID_OK) + { + if (dlg.m_selection == ChangePresetForPhysicalPrinterDialog::Switch) + // unselect physical printer, if it was selected + m_preset_bundle->physical_printers.unselect_printer(); + else + { + PhysicalPrinter printer = physical_printers.get_selected_printer(); + + if (dlg.m_selection == ChangePresetForPhysicalPrinterDialog::ChangePreset) + printer.delete_preset(printer_preset_name); + + if (printer.add_preset(preset_name)) + physical_printers.save_printer(printer); + else { + wxMessageDialog dialog(nullptr, _L("This preset is already exist for this physical printer. Please, select another one."), _L("Information"), wxICON_INFORMATION | wxOK); + dialog.ShowModal(); + } + + physical_printers.select_printer_by_name(printer.get_full_name(preset_name)); + } + } + else + wxGetApp().get_tab(Preset::TYPE_PRINTER)->select_preset(printer_preset_name); + } } @@ -1123,7 +1175,6 @@ void PresetForPrinter::msw_rescale() // PhysicalPrinterDialog //------------------------------------------ - PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) : DPIDialog(NULL, wxID_ANY, _L("Physical Printer"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), -1), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { @@ -1427,9 +1478,6 @@ void PhysicalPrinterDialog::OnOK(wxEvent& event) if (dialog.ShowModal() == wxID_NO) return; - - // Remove the printer from the list. - printers.delete_printer(into_u8(printer_name)); } std::set repeat_presets; @@ -1438,8 +1486,6 @@ void PhysicalPrinterDialog::OnOK(wxEvent& event) if (!m_printer.add_preset(preset->get_preset_name())) repeat_presets.emplace(preset->get_preset_name()); } - // update preset_names in printer config - m_printer.update_preset_names_in_config(); if (!repeat_presets.empty()) { @@ -1467,16 +1513,10 @@ void PhysicalPrinterDialog::OnOK(wxEvent& event) // save new physical printer printers.save_printer(m_printer, renamed_from); - // update selection on the tab only when it was changed - /* - if (m_printer.get_preset_name() != wxGetApp().preset_bundle->printers.get_selected_preset_name()) { - Tab* tab = wxGetApp().get_tab(Preset::TYPE_PRINTER); - if (tab) { - wxString preset_name = m_printer_presets->GetString(m_printer_presets->GetSelection()); - tab->select_preset(into_u8(preset_name)); - } - } - */ + printers.select_printer(m_printer); + + // refresh preset list on Printer Settings Tab + wxGetApp().get_tab(Preset::TYPE_PRINTER)->update_preset_choice(); event.Skip(); } @@ -1520,4 +1560,74 @@ void PhysicalPrinterDialog::DeletePreset(PresetForPrinter* preset_for_printer) } +//----------------------------------------------- +// ChangePresetForPhysicalPrinterDialog +//----------------------------------------------- + +ChangePresetForPhysicalPrinterDialog::ChangePresetForPhysicalPrinterDialog(const std::string& preset_name) + : DPIDialog(nullptr, wxID_ANY, _L("Warning"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), -1), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING/* | wxRESIZE_BORDER*/) +{ + SetFont(wxGetApp().normal_font()); + SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + + PhysicalPrinterCollection& printers = wxGetApp().preset_bundle->physical_printers; + std::string printer_name = printers.get_selected_printer_name(); + std::string old_preset_name = printers.get_selected_printer_preset_name(); + + wxString msg_text = from_u8((boost::format(_u8L("You have selected physical printer \"%1%\"\n" + "with related printer preset \"%2%\"")) % + printer_name % old_preset_name).str()); + wxStaticText* label_top = new wxStaticText(this, wxID_ANY, msg_text); + label_top->SetFont(wxGetApp().bold_font()); + + wxString choices[] = { from_u8((boost::format(_u8L("Just switch to \"%1%\"")) % preset_name).str()), + from_u8((boost::format(_u8L("Change \"%1%\" to \"%2%\" for this physical printer")) % old_preset_name % preset_name).str()), + from_u8((boost::format(_u8L("Add \"%1%\" as a next preset for the the physical printer")) % preset_name).str()) }; + + wxRadioBox* selection_type_box = new wxRadioBox(this, wxID_ANY, _L("What would you like to do?"), wxDefaultPosition, wxDefaultSize, WXSIZEOF(choices), choices, + 3, wxRA_SPECIFY_ROWS); + selection_type_box->SetFont(wxGetApp().normal_font()); + selection_type_box->SetSelection(0); + + selection_type_box->Bind(wxEVT_RADIOBOX, [this](wxCommandEvent& e) { + int selection = e.GetSelection(); + m_selection = (SelectionType)selection; + }); + + auto radio_sizer = new wxBoxSizer(wxHORIZONTAL); + radio_sizer->Add(selection_type_box, 1, wxALIGN_CENTER_VERTICAL); + + wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); + wxButton* btnOK = static_cast(this->FindWindowById(wxID_OK, this)); + btnOK->Bind(wxEVT_BUTTON, &ChangePresetForPhysicalPrinterDialog::OnOK, this); + + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + + topSizer->Add(label_top, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W); + topSizer->Add(radio_sizer, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W); + topSizer->Add(btns, 0, wxEXPAND | wxALL, BORDER_W); + + SetSizer(topSizer); + topSizer->SetSizeHints(this); +} + +void ChangePresetForPhysicalPrinterDialog::on_dpi_changed(const wxRect& suggested_rect) +{ + const int& em = em_unit(); + + msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL }); + + const wxSize& size = wxSize(45 * em, 35 * em); + SetMinSize(size); + + Fit(); + Refresh(); +} + +void ChangePresetForPhysicalPrinterDialog::OnOK(wxEvent& event) +{ + event.Skip(); +} + + }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index 1f5ef026da..1cea97e41e 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -166,7 +166,7 @@ class TabPresetComboBox : public PresetComboBox bool show_incompatible {false}; bool m_enable_all {false}; - std::function update_ph_printers { nullptr }; + bool m_allow_to_update_physical_printers {false}; public: TabPresetComboBox(wxWindow *parent, Preset::Type preset_type); @@ -174,8 +174,8 @@ public: void set_show_incompatible_presets(bool show_incompatible_presets) { show_incompatible = show_incompatible_presets; } - void set_update_physical_printers_function(std::function update_fn) { - update_ph_printers = update_fn; + void allow_to_update_physical_printers() { + m_allow_to_update_physical_printers = m_type == Preset::TYPE_PRINTER; } void update() override; @@ -263,6 +263,32 @@ protected: void on_sys_color_changed() override {}; }; + +//------------------------------------------------ +// ChangePresetForPhysicalPrinterDialog +//------------------------------------------------ + +class ChangePresetForPhysicalPrinterDialog : public DPIDialog +{ + void OnOK(wxEvent& event); + +public: + + enum SelectionType + { + Switch, + ChangePreset, + AddPreset + } m_selection {Switch}; + + ChangePresetForPhysicalPrinterDialog(const std::string& preset_name); + ~ChangePresetForPhysicalPrinterDialog() {} + +protected: + void on_dpi_changed(const wxRect& suggested_rect) override; + void on_sys_color_changed() override {}; +}; + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index dbea9b3f5c..e498f56b77 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -164,17 +164,14 @@ void Tab::create_preset_tab() m_presets_choice->set_selection_changed_function([this](int selection) { if (!m_presets_choice->selection_is_changed_according_to_physical_printers()) { - // for the printer presets set callback for the updating of the physical printers + // For the printer presets allow to update a physical printer if it is needed. + // After call of the update_physical_printers() this possibility will be disabled again to avoid a case, + // when select_preset is called from the others than this place if (m_type == Preset::TYPE_PRINTER) - m_presets_choice->set_update_physical_printers_function([this](std::string preset_name) { update_physical_printers(preset_name);}); + m_presets_choice->allow_to_update_physical_printers(); // select preset select_preset(m_presets_choice->GetString(selection).ToUTF8().data()); - - // Disable callback for the updating of the physical printers to avoid a case, - // when select_preset is called from the others than this place - if (m_type == Preset::TYPE_PRINTER) - m_presets_choice->set_update_physical_printers_function(nullptr); } }); @@ -772,36 +769,6 @@ void Tab::update_tab_ui() m_presets_choice->update(); } -void Tab::update_physical_printers(std::string preset_name) -{ - PhysicalPrinterCollection& physical_printers = wxGetApp().preset_bundle->physical_printers; - if (physical_printers.has_selection() && - Preset::remove_suffix_modified(preset_name) != physical_printers.get_selected_printer_preset_name()) - { - std::string printer_name = physical_printers.get_selected_full_printer_name(); - wxString msg_text = from_u8((boost::format(_u8L("You have selected physical printer \"%1%\".")) % printer_name).str()); - msg_text += "\n\n" + _L("Would you like to change related preset for this printer?") + "\n\n" + - _L("Select YES if you want to change related preset for this printer \n" - "or NO to switch to the another preset (logical printer)."); - wxMessageDialog dialog(nullptr, msg_text, _L("Warning"), wxICON_WARNING | wxYES | wxNO); - - if (dialog.ShowModal() == wxID_YES) { - preset_name = Preset::remove_suffix_modified(preset_name); - Preset* preset = m_presets->find_preset(preset_name); - assert(preset); - Preset& edited_preset = m_presets->get_edited_preset(); - if (preset->name == edited_preset.name) - preset = &edited_preset; - physical_printers.get_selected_printer().update_from_preset(*preset); - physical_printers.select_printer_by_name(physical_printers.get_selected_printer().get_full_name(preset_name)); - return; - } - } - - // unselect physical printer, if it was selected - m_preset_bundle->physical_printers.unselect_printer(); -} - // Load a provied DynamicConfig into the tab, modifying the active preset. // This could be used for example by setting a Wipe Tower position by interactive manipulation in the 3D view. void Tab::load_config(const DynamicPrintConfig& config) @@ -2822,7 +2789,8 @@ void Tab::load_current_preset() { const Preset& preset = m_presets->get_edited_preset(); - (preset.is_default || preset.is_system) ? m_btn_delete_preset->Disable() : m_btn_delete_preset->Enable(true); +// (preset.is_default || preset.is_system) ? m_btn_delete_preset->Disable() : m_btn_delete_preset->Enable(true); + update_delete_preset_btn(); update(); if (m_type == Slic3r::Preset::TYPE_PRINTER) { @@ -2959,9 +2927,23 @@ void Tab::update_page_tree_visibility() } +void Tab::update_delete_preset_btn() +{ + if (m_type == Preset::TYPE_PRINTER && m_presets_choice->is_selected_physical_printer() && + m_preset_bundle->physical_printers.has_selection()) { + // we can't delete last preset from the physical printer + m_btn_delete_preset->Enable(m_preset_bundle->physical_printers.get_selected_printer().preset_names.size() > 1); + } + else { + const Preset& preset = m_presets->get_edited_preset(); + m_btn_delete_preset->Enable(!preset.is_default && !preset.is_system); + } +} + void Tab::update_preset_choice() { m_presets_choice->update(); + update_delete_preset_btn(); } // Called by the UI combo box when the user switches profiles, and also to delete the current profile. @@ -2971,16 +2953,55 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, { if (preset_name.empty()) { if (delete_current) { - // Find an alternate preset to be selected after the current preset is deleted. - const std::deque &presets = this->m_presets->get_presets(); - size_t idx_current = this->m_presets->get_idx_selected(); - // Find the next visible preset. - size_t idx_new = idx_current + 1; - if (idx_new < presets.size()) - for (; idx_new < presets.size() && ! presets[idx_new].is_visible; ++ idx_new) ; - if (idx_new == presets.size()) - for (idx_new = idx_current - 1; idx_new > 0 && ! presets[idx_new].is_visible; -- idx_new); - preset_name = presets[idx_new].name; + PhysicalPrinterCollection& physical_printers = m_preset_bundle->physical_printers; + if (m_presets_choice->is_selected_physical_printer()) { + PhysicalPrinter& printer = physical_printers.get_selected_printer(); + + if (printer.preset_names.size()==1) { + wxMessageDialog dialog(nullptr, _L("It's a last for this physical printer. We can't delete it"), _L("Information"), wxICON_INFORMATION | wxOK); + dialog.ShowModal(); + } + else { + // just delete this preset from the current physical printer + printer.delete_preset(m_presets->get_edited_preset().name); + // select first from the possible presets for this printer + physical_printers.select_printer(printer); + + preset_name = physical_printers.get_selected_printer_preset_name(); + // revert delete_current value to avoid deleting of the new selected preset + delete_current = false; + } + } + else { + // Check preset for delete in physical printers + // Ask a customer about next action , if there is a printer with just one preset and this preset is equal to delete + if (m_type == Preset::TYPE_PRINTER && !physical_printers.empty() ) + { + // try to delete selected preset from the all printers it has + if (!physical_printers.delete_preset_from_printers(m_presets->get_edited_preset().name)) + { + wxMessageDialog dialog(nullptr, _L("There is/are a physical printer(s), which has/have one and only this printer preset.\n" + "This/Those printer(s) will be deletede after deleting of the selected preset.\n" + "Are you sure you want to delete the selected preset?"), _L("Warning"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION); + if (dialog.ShowModal() == wxID_NO) + return; + + // delete selected preset from printers and printer, if it's needed + physical_printers.delete_preset_from_printers(m_presets->get_edited_preset().name, false); + } + } + + // Find an alternate preset to be selected after the current preset is deleted. + const std::deque &presets = this->m_presets->get_presets(); + size_t idx_current = this->m_presets->get_idx_selected(); + // Find the next visible preset. + size_t idx_new = idx_current + 1; + if (idx_new < presets.size()) + for (; idx_new < presets.size() && ! presets[idx_new].is_visible; ++ idx_new) ; + if (idx_new == presets.size()) + for (idx_new = idx_current - 1; idx_new > 0 && ! presets[idx_new].is_visible; -- idx_new); + preset_name = presets[idx_new].name; + } } else { // If no name is provided, select the "-- default --" preset. preset_name = m_presets->default_preset().name; @@ -3075,23 +3096,16 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, } if (canceled) { - update_tab_ui(); - /* - // unselect physical printer selection to the correct synchronization of the printer presets between Tab and Plater - if (m_type == Preset::TYPE_PRINTER) - m_preset_bundle->physical_printers.unselect_printer(); - */ - - - // Check if preset really was changed. - // If preset selection was canceled and previously was selected physical printer, we should select it back - if (m_type == Preset::TYPE_PRINTER && !last_selected_ph_printer_name.empty()) { - if (m_presets->get_edited_preset().name == PhysicalPrinter::get_preset_name(last_selected_ph_printer_name)) { + if (m_type == Preset::TYPE_PRINTER) { + if (!last_selected_ph_printer_name.empty() && + m_presets->get_edited_preset().name == PhysicalPrinter::get_preset_name(last_selected_ph_printer_name)) { + // If preset selection was canceled and previously was selected physical printer, we should select it back m_preset_bundle->physical_printers.select_printer_by_name(last_selected_ph_printer_name); - m_presets_choice->update(); } } + update_tab_ui(); + // Trigger the on_presets_changed event so that we also restore the previous value in the plater selector, // if this action was initiated from the plater. on_presets_changed(); @@ -3334,6 +3348,7 @@ void Tab::save_preset(std::string name /*= ""*/, bool detach) // If saving the preset changes compatibility with other presets, keep the now incompatible dependent presets selected, however with a "red flag" icon showing that they are no more compatible. m_preset_bundle->update_compatible(PresetSelectCompatibleType::Never); //update physical printer's related printer preset if it's needed + m_presets_choice->allow_to_update_physical_printers(); m_presets_choice->update_physical_printers(name); // Add the new item into the UI component, remove dirty flags and activate the saved item. update_tab_ui(); @@ -3389,7 +3404,9 @@ void Tab::delete_preset() // Don't let the user delete the ' - default - ' configuration. std::string action = current_preset.is_external ? _utf8(L("remove")) : _utf8(L("delete")); // TRN remove/delete - const wxString msg = from_u8((boost::format(_utf8(L("Are you sure you want to %1% the selected preset?"))) % action).str()); + const wxString msg = m_presets_choice->is_selected_physical_printer() ? + from_u8((boost::format(_utf8(L("Are you sure you want to delete \"%1%\" preset from the physical printer?"))) % current_preset.name).str()) : + from_u8((boost::format(_utf8(L("Are you sure you want to %1% the selected preset?"))) % action).str()); action = current_preset.is_external ? _utf8(L("Remove")) : _utf8(L("Delete")); // TRN Remove/Delete wxString title = from_u8((boost::format(_utf8(L("%1% Preset"))) % action).str()); //action + _(L(" Preset")); diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 82df31b590..57129955b0 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -275,6 +275,7 @@ public: void load_current_preset(); void rebuild_page_tree(); void update_page_tree_visibility(); + void update_delete_preset_btn(); void update_preset_choice(); // Select a new preset, possibly delete the current one. void select_preset(std::string preset_name = "", bool delete_current = false, const std::string& last_selected_ph_printer_name = ""); @@ -307,7 +308,6 @@ public: void load_initial_data(); void update_dirty(); void update_tab_ui(); - void update_physical_printers(std::string preset_name); void load_config(const DynamicPrintConfig& config); virtual void reload_config(); void update_mode(); From 3d990a918904fb1ab10afa4f95f2885350821994 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 20 Jul 2020 16:27:39 +0200 Subject: [PATCH 172/503] First try to convert a user printer profiles to the physical printers --- src/libslic3r/Preset.cpp | 33 +++++++++++++++++++++++++++-- src/libslic3r/Preset.hpp | 1 + src/libslic3r/PresetBundle.cpp | 1 + src/slic3r/GUI/PresetComboBoxes.cpp | 6 ++++++ src/slic3r/GUI/PresetComboBoxes.hpp | 9 +++----- 5 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index a55dc24cac..401de0fc3f 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1481,7 +1481,7 @@ PhysicalPrinterCollection::PhysicalPrinterCollection( const std::vectorfind_printer(name, false)) { // This happens when there's is a preset (most likely legacy one) with the same name as a system preset // that's already been loaded from a bundle. - BOOST_LOG_TRIVIAL(warning) << "Preset already present, not loading: " << name; + BOOST_LOG_TRIVIAL(warning) << "Printer already present, not loading: " << name; continue; } try { @@ -1530,6 +1530,35 @@ void PhysicalPrinterCollection::load_printers(const std::string& dir_path, const throw std::runtime_error(errors_cummulative); } +// if there is saved user presets, contains information about "Print Host upload", +// Create default printers with this presets +// Throws an exception on error. +void PhysicalPrinterCollection::load_printers(const PrinterPresetCollection& printer_presets, std::string def_printer_name/* = ""*/) +{ + if (def_printer_name.empty()) + def_printer_name = "Printer"; + + int cnt=0; + std::string errors_cummulative; + // Store the loaded printers into a new vector + std::deque printers_loaded; + for (const Preset& preset: printer_presets) { + const DynamicPrintConfig& config = preset.config; + if (!config.opt_string("print_host").empty() || + !config.opt_string("printhost_apikey").empty() || + !config.opt_string("printhost_cafile").empty() ) { + PhysicalPrinter printer((boost::format("%1% %2%") % def_printer_name % ++cnt ).str(), preset); + printer.loaded = true; + printers_loaded.emplace_back(printer); + + save_printer(printer); + } + } + + if (!errors_cummulative.empty()) + throw std::runtime_error(errors_cummulative); +} + PhysicalPrinter* PhysicalPrinterCollection::find_printer( const std::string& name, bool first_visible_if_not_found) { PhysicalPrinter key(name); diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index e8afb0f6fd..02f831136f 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -626,6 +626,7 @@ public: // Load ini files of the particular type from the provided directory path. void load_printers(const std::string& dir_path, const std::string& subdir); + void load_printers(const PrinterPresetCollection &printer_presets, std::string def_printer_name = ""); // Save the printer under a new name. If the name is different from the old one, // a new printer is stored into the list of printers. diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 7969966e5e..0e215a2ae7 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -201,6 +201,7 @@ void PresetBundle::load_presets(AppConfig &config, const std::string &preferred_ } try { this->physical_printers.load_printers(dir_user_presets, "physical_printer"); + this->physical_printers.load_printers(this->printers); } catch (const std::runtime_error &err) { errors_cummulative += err.what(); } diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index bc1f48dd64..2d74344bd3 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -131,6 +131,12 @@ PresetComboBox::~PresetComboBox() { } +BitmapCache& PresetComboBox::bitmap_cache() +{ + static BitmapCache bmps; + return bmps; +} + void PresetComboBox::set_label_marker(int item, LabelItemType label_item_type) { this->SetClientData(item, (void*)label_item_type); diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index 1cea97e41e..89d043f7bb 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -10,7 +10,7 @@ #include "libslic3r/Preset.hpp" #include "wxExtensions.hpp" #include "GUI_Utils.hpp" -#include "BitmapCache.hpp" +//#include "BitmapCache.hpp" class wxString; class wxTextCtrl; @@ -22,7 +22,7 @@ namespace Slic3r { namespace GUI { - +class BitmapCache; // --------------------------------- // *** PresetComboBox *** // --------------------------------- @@ -72,10 +72,7 @@ protected: PresetCollection* m_collection {nullptr}; // Caching bitmaps for the all bitmaps, used in preset comboboxes - static BitmapCache& bitmap_cache() { - static BitmapCache bmps; - return bmps; - } + static BitmapCache& bitmap_cache(); // Indicator, that the preset is compatible with the selected printer. ScalableBitmap m_bitmapCompatible; From d910f7934b4b267b90a9766da4cbb3cc2c69b7af Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 21 Jul 2020 08:43:20 +0200 Subject: [PATCH 173/503] Empty layer check fix The test gave false positive in case there were supposed to be both object and support extrusions on the first layer --- src/libslic3r/GCode.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 7fc531b92f..35dc5a53bd 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -617,10 +617,12 @@ std::vector GCode::collect_layers_to_print(const PrintObjec layers_to_print.emplace_back(layer_to_print); + bool has_extrusions = (layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions()) + || (layer_to_print.support_layer && layer_to_print.support_layer->has_extrusions()); + // Check that there are extrusions on the very first layer. if (layers_to_print.size() == 1u) { - if ((layer_to_print.object_layer && ! layer_to_print.object_layer->has_extrusions()) - || (layer_to_print.support_layer && ! layer_to_print.support_layer->has_extrusions())) + if (! has_extrusions) throw std::runtime_error(_(L("There is an object with no extrusions on the first layer."))); } @@ -637,10 +639,6 @@ std::vector GCode::collect_layers_to_print(const PrintObjec // Negative support_contact_z is not taken into account, it can result in false positives in cases // where previous layer has object extrusions too (https://github.com/prusa3d/PrusaSlicer/issues/2752) - // Only check this layer in case it has some extrusions. - bool has_extrusions = (layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions()) - || (layer_to_print.support_layer && layer_to_print.support_layer->has_extrusions()); - if (has_extrusions && layer_to_print.print_z() > maximal_print_z + 2. * EPSILON) { const_cast(object.print())->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, _(L("Empty layers detected, the output would not be printable.")) + "\n\n" + From 6e8006524009cd10322cc3418cf4500ceb833978 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 21 Jul 2020 09:08:48 +0200 Subject: [PATCH 174/503] Added well-known metadata to 3mf export --- src/libslic3r/Format/3mf.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 657e9ec983..edf55ba37e 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -4,6 +4,7 @@ #include "../GCode.hpp" #include "../Geometry.hpp" #include "../GCode/ThumbnailData.hpp" +#include "../Time.hpp" #include "../I18N.hpp" @@ -1991,7 +1992,7 @@ namespace Slic3r { bool _add_content_types_file_to_archive(mz_zip_archive& archive); bool _add_thumbnail_file_to_archive(mz_zip_archive& archive, const ThumbnailData& thumbnail_data); bool _add_relationships_file_to_archive(mz_zip_archive& archive); - bool _add_model_file_to_archive(mz_zip_archive& archive, const Model& model, IdToObjectDataMap &objects_data); + bool _add_model_file_to_archive(const std::string& filename, mz_zip_archive& archive, const Model& model, IdToObjectDataMap& objects_data); bool _add_object_to_model_stream(std::stringstream& stream, unsigned int& object_id, ModelObject& object, BuildItemsList& build_items, VolumeToOffsetsMap& volumes_offsets); bool _add_mesh_to_object_stream(std::stringstream& stream, ModelObject& object, VolumeToOffsetsMap& volumes_offsets); bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items); @@ -2054,7 +2055,7 @@ namespace Slic3r { // Adds model file ("3D/3dmodel.model"). // This is the one and only file that contains all the geometry (vertices and triangles) of all ModelVolumes. IdToObjectDataMap objects_data; - if (!_add_model_file_to_archive(archive, model, objects_data)) + if (!_add_model_file_to_archive(filename, archive, model, objects_data)) { close_zip_writer(&archive); boost::filesystem::remove(filename); @@ -2203,7 +2204,7 @@ namespace Slic3r { return true; } - bool _3MF_Exporter::_add_model_file_to_archive(mz_zip_archive& archive, const Model& model, IdToObjectDataMap &objects_data) + bool _3MF_Exporter::_add_model_file_to_archive(const std::string& filename, mz_zip_archive& archive, const Model& model, IdToObjectDataMap& objects_data) { std::stringstream stream; // https://en.cppreference.com/w/cpp/types/numeric_limits/max_digits10 @@ -2214,6 +2215,19 @@ namespace Slic3r { stream << "\n"; stream << "<" << MODEL_TAG << " unit=\"millimeter\" xml:lang=\"en-US\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\" xmlns:slic3rpe=\"http://schemas.slic3r.org/3mf/2017/06\">\n"; stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_3MF_VERSION << "\">" << VERSION_3MF << "\n"; + std::string name = boost::filesystem::path(filename).stem().string(); + stream << " <" << METADATA_TAG << " name=\"Title\">" << name << "\n"; + stream << " <" << METADATA_TAG << " name=\"Designer\">" << "\n"; + stream << " <" << METADATA_TAG << " name=\"Description\">" << name << "\n"; + stream << " <" << METADATA_TAG << " name=\"Copyright\">" << "\n"; + stream << " <" << METADATA_TAG << " name=\"LicenseTerms\">" << "\n"; + stream << " <" << METADATA_TAG << " name=\"Rating\">" << "\n"; + std::string date = Slic3r::Utils::utc_timestamp(Slic3r::Utils::get_current_time_utc()); + // keep only the date part of the string + date = date.substr(0, 10); + stream << " <" << METADATA_TAG << " name=\"CreationDate\">" << date << "\n"; + stream << " <" << METADATA_TAG << " name=\"ModificationDate\">" << date << "\n"; + stream << " <" << METADATA_TAG << " name=\"Application\">" << SLIC3R_APP_KEY << "-" << SLIC3R_VERSION << "\n"; stream << " <" << RESOURCES_TAG << ">\n"; // Instance transformations, indexed by the 3MF object ID (which is a linear serialization of all instances of all ModelObjects). From dc59e86d2c10fac0e58694a37ff77f72790da0eb Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 21 Jul 2020 09:34:54 +0200 Subject: [PATCH 175/503] ENABLE_GCODE_VIEWER -> Partial refactoring in preparation for removal of old time estimator --- src/libslic3r/GCode.cpp | 2 ++ src/libslic3r/GCodeTimeEstimator.cpp | 2 ++ src/libslic3r/GCodeTimeEstimator.hpp | 2 ++ src/libslic3r/Print.cpp | 6 ++++++ src/libslic3r/Print.hpp | 6 ++++++ src/libslic3r/Technologies.hpp | 1 + src/slic3r/GUI/Plater.cpp | 27 +++++++++++++++++++++++++++ 7 files changed, 46 insertions(+) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 56cf357b7a..a4f25fa14c 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1065,11 +1065,13 @@ namespace DoExport { print_statistics.clear(); #if ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR print_statistics.estimated_normal_print_time_str = normal_time_estimator.get_time_dhm/*s*/(); print_statistics.estimated_silent_print_time_str = silent_time_estimator_enabled ? silent_time_estimator.get_time_dhm/*s*/() : "N/A"; print_statistics.estimated_normal_custom_gcode_print_times_str = normal_time_estimator.get_custom_gcode_times_dhm(true); if (silent_time_estimator_enabled) print_statistics.estimated_silent_custom_gcode_print_times_str = silent_time_estimator.get_custom_gcode_times_dhm(true); +#endif // ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR #else print_statistics.estimated_normal_print_time = normal_time_estimator.get_time_dhm/*s*/(); print_statistics.estimated_silent_print_time = silent_time_estimator_enabled ? silent_time_estimator.get_time_dhm/*s*/() : "N/A"; diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp index bc3adefc08..4bfe322ce2 100644 --- a/src/libslic3r/GCodeTimeEstimator.cpp +++ b/src/libslic3r/GCodeTimeEstimator.cpp @@ -723,6 +723,7 @@ namespace Slic3r { } #if ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR std::vector>> GCodeTimeEstimator::get_custom_gcode_times_dhm(bool include_remaining) const { std::vector>> ret; @@ -737,6 +738,7 @@ namespace Slic3r { return ret; } +#endif // ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR #else std::vector> GCodeTimeEstimator::get_custom_gcode_times_dhm(bool include_remaining) const { diff --git a/src/libslic3r/GCodeTimeEstimator.hpp b/src/libslic3r/GCodeTimeEstimator.hpp index ce6b2f4af0..3b92f02692 100644 --- a/src/libslic3r/GCodeTimeEstimator.hpp +++ b/src/libslic3r/GCodeTimeEstimator.hpp @@ -371,7 +371,9 @@ namespace Slic3r { // Returns the estimated time, in format DDd HHh MMm, for each custom_gcode // If include_remaining==true the strings will be formatted as: "time for custom_gcode (remaining time at color start)" #if ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR std::vector>> get_custom_gcode_times_dhm(bool include_remaining) const; +#endif // ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR #else std::vector> get_custom_gcode_times_dhm(bool include_remaining) const; #endif // ENABLE_GCODE_VIEWER diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 34fab6f307..a48d6f602f 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -2191,9 +2191,15 @@ DynamicConfig PrintStatistics::config() const { DynamicConfig config; #if ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR config.set_key_value("print_time", new ConfigOptionString(this->estimated_normal_print_time_str)); config.set_key_value("normal_print_time", new ConfigOptionString(this->estimated_normal_print_time_str)); config.set_key_value("silent_print_time", new ConfigOptionString(this->estimated_silent_print_time_str)); +#else + config.set_key_value("print_time", new ConfigOptionString(short_time(get_time_dhms(this->estimated_normal_print_time)))); + config.set_key_value("normal_print_time", new ConfigOptionString(short_time(get_time_dhms(this->estimated_normal_print_time)))); + config.set_key_value("silent_print_time", new ConfigOptionString(short_time(get_time_dhms(this->estimated_silent_print_time)))); +#endif // ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR #else std::string normal_print_time = short_time(this->estimated_normal_print_time); std::string silent_print_time = short_time(this->estimated_silent_print_time); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 7a0ecdf170..064145a2e7 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -306,12 +306,16 @@ struct PrintStatistics #if ENABLE_GCODE_VIEWER float estimated_normal_print_time; float estimated_silent_print_time; +#if ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR std::string estimated_normal_print_time_str; std::string estimated_silent_print_time_str; +#endif // ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR std::vector>> estimated_normal_custom_gcode_print_times; std::vector>> estimated_silent_custom_gcode_print_times; +#if ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR std::vector>> estimated_normal_custom_gcode_print_times_str; std::vector>> estimated_silent_custom_gcode_print_times_str; +#endif // ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR std::vector> estimated_normal_moves_times; std::vector> estimated_silent_moves_times; std::vector> estimated_normal_roles_times; @@ -341,10 +345,12 @@ struct PrintStatistics void clear() { #if ENABLE_GCODE_VIEWER clear_time_estimates(); +#if ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR estimated_normal_print_time_str.clear(); estimated_silent_print_time_str.clear(); estimated_normal_custom_gcode_print_times_str.clear(); estimated_silent_custom_gcode_print_times_str.clear(); +#endif // ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR #else estimated_normal_print_time.clear(); estimated_silent_print_time.clear(); diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index b04e78c4ed..06d217812b 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -62,6 +62,7 @@ #define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_SHADERS_EDITOR (0 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_AS_STATE (1 && ENABLE_GCODE_VIEWER) +#define ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR (1 && ENABLE_GCODE_VIEWER) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 39003e9aaa..f4c017adde 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1323,7 +1323,11 @@ void Sidebar::update_sliced_info_sizer() p->sliced_info->SetTextAndShow(siCost, info_text, new_label); #if ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR if (p->plater->get_current_canvas3D()->is_time_estimate_enabled() || (ps.estimated_normal_print_time_str == "N/A" && ps.estimated_silent_print_time_str == "N/A")) +#else + if (p->plater->get_current_canvas3D()->is_time_estimate_enabled() || (ps.estimated_normal_print_time <= 0.0f && ps.estimated_silent_print_time <= 0.0f)) +#endif // ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR #else if (ps.estimated_normal_print_time == "N/A" && ps.estimated_silent_print_time == "N/A") @@ -1336,7 +1340,11 @@ void Sidebar::update_sliced_info_sizer() wxString str_pause = _L("Pause"); #if ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR auto fill_labels = [str_color, str_pause](const std::vector>>& times, +#else + auto fill_labels = [str_color, str_pause](const std::vector>>& times, +#endif // ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR #else auto fill_labels = [str_color, str_pause](const std::vector>& times, #endif // ENABLE_GCODE_VIEWER @@ -1358,7 +1366,11 @@ void Sidebar::update_sliced_info_sizer() new_label += format_wxstr(" -> %1%", str_pause); #if ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR info_text += format_wxstr("\n%1% (%2%)", times[i].second.first, times[i].second.second); +#else + info_text += format_wxstr("\n%1% (%2%)", short_time(get_time_dhms(times[i].second.first)), short_time(get_time_dhms(times[i].second.second))); +#endif // ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR #else info_text += format_wxstr("\n%1%", times[i].second); #endif // ENABLE_GCODE_VIEWER @@ -1366,6 +1378,7 @@ void Sidebar::update_sliced_info_sizer() }; #if ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR if (ps.estimated_normal_print_time_str != "N/A") { new_label += format_wxstr("\n - %1%", _L("normal mode")); info_text += format_wxstr("\n%1%", ps.estimated_normal_print_time_str); @@ -1375,6 +1388,17 @@ void Sidebar::update_sliced_info_sizer() new_label += format_wxstr("\n - %1%", _L("stealth mode")); info_text += format_wxstr("\n%1%", ps.estimated_silent_print_time_str); fill_labels(ps.estimated_silent_custom_gcode_print_times_str, new_label, info_text); +#else + if (ps.estimated_normal_print_time > 0.0f) { + new_label += format_wxstr("\n - %1%", _L("normal mode")); + info_text += format_wxstr("\n%1%", short_time(get_time_dhms(ps.estimated_normal_print_time))); + fill_labels(ps.estimated_normal_custom_gcode_print_times, new_label, info_text); + } + if (ps.estimated_silent_print_time > 0.0f) { + new_label += format_wxstr("\n - %1%", _L("stealth mode")); + info_text += format_wxstr("\n%1%", short_time(get_time_dhms(ps.estimated_silent_print_time))); + fill_labels(ps.estimated_silent_custom_gcode_print_times, new_label, info_text); +#endif // ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR #else if (ps.estimated_normal_print_time != "N/A") { new_label += format_wxstr("\n - %1%", _L("normal mode")); @@ -1397,6 +1421,9 @@ void Sidebar::update_sliced_info_sizer() p->sliced_info->SetTextAndShow(siMateril_unit, "N/A"); } } +#if ENABLE_GCODE_VIEWER + Layout(); +#endif // ENABLE_GCODE_VIEWER } void Sidebar::show_sliced_info_sizer(const bool show) From 6057fb95958dd03dc8de8d0a92cd2017fbb40c6b Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 21 Jul 2020 09:44:07 +0200 Subject: [PATCH 176/503] GUI_objectList: fixed typo in assert --- src/slic3r/GUI/GUI_ObjectList.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index c11ed66ab8..d44db72b10 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -350,7 +350,7 @@ void ObjectList::get_selection_indexes(std::vector& obj_idxs, std::vectorGetItemType(item); - assert(type & itObject | itInstance | itInstanceRoot); + assert(type & (itObject | itInstance | itInstanceRoot)); obj_idxs.emplace_back(type & itObject ? m_objects_model->GetIdByItem(item) : m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item))); From f7ceffb46e5abd687625d4ef730b8003b6cadcc7 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 21 Jul 2020 15:33:28 +0200 Subject: [PATCH 177/503] Fixed back-end warning infrastructure: The Print / PrintObject should have been derived from ObjectBase, not from ObjectID. --- src/libslic3r/ObjectID.hpp | 2 ++ src/libslic3r/Print.hpp | 2 +- src/libslic3r/PrintBase.cpp | 4 ++-- src/libslic3r/PrintBase.hpp | 12 ++++++------ src/libslic3r/SLAPrint.hpp | 2 +- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/ObjectID.hpp b/src/libslic3r/ObjectID.hpp index 484d1173ba..920f512de3 100644 --- a/src/libslic3r/ObjectID.hpp +++ b/src/libslic3r/ObjectID.hpp @@ -42,6 +42,8 @@ private: // Base for Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial to provide a unique ID // to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject). +// Also base for Print, PrintObject, SLAPrint, SLAPrintObject to provide a unique ID for matching Model / ModelObject +// with their corresponding Print / PrintObject objects by the notification center at the UI when processing back-end warnings. // Achtung! The s_last_id counter is not thread safe, so it is expected, that the ObjectBase derived instances // are only instantiated from the main thread. class ObjectBase diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index bf541e1222..05929dd2ef 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -399,7 +399,7 @@ public: // in the notification center. const PrintObject* get_object(ObjectID object_id) const { auto it = std::find_if(m_objects.begin(), m_objects.end(), - [object_id](const PrintObject *obj) { return *static_cast(obj) == object_id; }); + [object_id](const PrintObject *obj) { return obj->id() == object_id; }); return (it == m_objects.end()) ? nullptr : *it; } const PrintRegionPtrs& regions() const { return m_regions; } diff --git a/src/libslic3r/PrintBase.cpp b/src/libslic3r/PrintBase.cpp index 14339f3c62..ab6ca3d350 100644 --- a/src/libslic3r/PrintBase.cpp +++ b/src/libslic3r/PrintBase.cpp @@ -93,7 +93,7 @@ void PrintBase::status_update_warnings(ObjectID object_id, int step, PrintStateB if (this->m_status_callback) m_status_callback(SlicingStatus(*this, step)); else if (! message.empty()) - printf("%s warning: %s\n", (object_id == ObjectID(*this)) ? "print" : "print object", message.c_str()); + printf("%s warning: %s\n", (object_id == this->id()) ? "print" : "print object", message.c_str()); } tbb::mutex& PrintObjectBase::state_mutex(PrintBase *print) @@ -108,7 +108,7 @@ std::function PrintObjectBase::cancel_callback(PrintBase *print) void PrintObjectBase::status_update_warnings(PrintBase *print, int step, PrintStateBase::WarningLevel warning_level, const std::string &message) { - print->status_update_warnings(*this, step, warning_level, message); + print->status_update_warnings(this->id(), step, warning_level, message); } } // namespace Slic3r diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index d7f3483e87..5e94e011a7 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -304,7 +304,7 @@ private: class PrintBase; -class PrintObjectBase : public ObjectID +class PrintObjectBase : public ObjectBase { public: const ModelObject* model_object() const { return m_model_object; } @@ -335,7 +335,7 @@ protected: * The PrintBase class will abstract this flow for different technologies. * */ -class PrintBase : public ObjectID +class PrintBase : public ObjectBase { public: PrintBase() : m_placeholder_parser(&m_full_print_config) { this->restart(); } @@ -386,9 +386,9 @@ public: struct SlicingStatus { SlicingStatus(int percent, const std::string &text, unsigned int flags = 0) : percent(percent), text(text), flags(flags) {} SlicingStatus(const PrintBase &print, int warning_step) : - flags(UPDATE_PRINT_STEP_WARNINGS), warning_object_id(print), warning_step(warning_step) {} + flags(UPDATE_PRINT_STEP_WARNINGS), warning_object_id(print.id()), warning_step(warning_step) {} SlicingStatus(const PrintObjectBase &print_object, int warning_step) : - flags(UPDATE_PRINT_OBJECT_STEP_WARNINGS), warning_object_id(print_object), warning_step(warning_step) {} + flags(UPDATE_PRINT_OBJECT_STEP_WARNINGS), warning_object_id(print_object.id()), warning_step(warning_step) {} int percent { -1 }; std::string text; // Bitmap of flags. @@ -508,7 +508,7 @@ protected: PrintStateBase::TimeStamp set_done(PrintStepEnum step) { std::pair status = m_state.set_done(step, this->state_mutex(), [this](){ this->throw_if_canceled(); }); if (status.second) - this->status_update_warnings(*this, static_cast(step), PrintStateBase::WarningLevel::NON_CRITICAL, std::string()); + this->status_update_warnings(this->id(), static_cast(step), PrintStateBase::WarningLevel::NON_CRITICAL, std::string()); return status.first; } bool invalidate_step(PrintStepEnum step) @@ -530,7 +530,7 @@ protected: std::pair active_step = m_state.active_step_add_warning(warning_level, message, message_id, this->state_mutex()); if (active_step.second) // Update UI. - this->status_update_warnings(*this, static_cast(active_step.first), warning_level, message); + this->status_update_warnings(this->id(), static_cast(active_step.first), warning_level, message); } private: diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 573fc2b0c8..9d41586ee6 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -433,7 +433,7 @@ public: // in the notification center. const SLAPrintObject* get_object(ObjectID object_id) const { auto it = std::find_if(m_objects.begin(), m_objects.end(), - [object_id](const SLAPrintObject *obj) { return *static_cast(obj) == object_id; }); + [object_id](const SLAPrintObject *obj) { return obj->id() == object_id; }); return (it == m_objects.end()) ? nullptr : *it; } From 42677107a57a0ad7319d856dcfcdc520148d8924 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 21 Jul 2020 16:00:03 +0200 Subject: [PATCH 178/503] ENABLE_GCODE_VIEWER -> Fixed scene update when opening a gcode file --- src/slic3r/GUI/Plater.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f4c017adde..ef113b7b96 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4650,6 +4650,7 @@ void Plater::load_gcode(const wxString& filename) // cleanup view before to start loading/processing p->gcode_result.reset(); + reset_gcode_toolpaths(); p->preview->reload_print(false); p->get_current_canvas3D()->render(); From 435355adfe425243e2a5780f1869cff17eeaf05a Mon Sep 17 00:00:00 2001 From: rongith Date: Wed, 13 May 2020 16:12:58 +0200 Subject: [PATCH 179/503] Temporary ironing icon to avoid crashes on GTK Ironing type and spacing can be set per-object --- src/libslic3r/PrintConfig.cpp | 2 ++ src/slic3r/GUI/GUI_ObjectList.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 0c1c828f7e..a25292298c 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1099,6 +1099,7 @@ void PrintConfigDef::init_fff_params() def = this->add("ironing_type", coEnum); def->label = L("Ironing Type"); + def->category = L("Ironing"); def->tooltip = L("Ironing Type"); def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); def->enum_values.push_back("top"); @@ -1122,6 +1123,7 @@ void PrintConfigDef::init_fff_params() def = this->add("ironing_spacing", coFloat); def->label = L("Spacing between ironing passes"); + def->category = L("Ironing"); def->tooltip = L("Distance between ironing lines"); def->sidetext = L("mm"); def->min = 0; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index d44db72b10..a47c8f6711 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -94,6 +94,7 @@ ObjectList::ObjectList(wxWindow* parent) : // ptFFF CATEGORY_ICON[L("Layers and Perimeters")] = create_scaled_bitmap("layers"); CATEGORY_ICON[L("Infill")] = create_scaled_bitmap("infill"); + CATEGORY_ICON[L("Ironing")] = create_scaled_bitmap("infill"); // FIXME when the ironing icon is available CATEGORY_ICON[L("Support material")] = create_scaled_bitmap("support"); CATEGORY_ICON[L("Speed")] = create_scaled_bitmap("time"); CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap("funnel"); @@ -644,6 +645,7 @@ void ObjectList::msw_rescale_icons() // ptFFF CATEGORY_ICON[L("Layers and Perimeters")] = create_scaled_bitmap("layers"); CATEGORY_ICON[L("Infill")] = create_scaled_bitmap("infill"); + CATEGORY_ICON[L("Ironing")] = create_scaled_bitmap("infill"); // FIXME when the ironing icon is available CATEGORY_ICON[L("Support material")] = create_scaled_bitmap("support"); CATEGORY_ICON[L("Speed")] = create_scaled_bitmap("time"); CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap("funnel"); From 6d28d68e4a87e3e53baf4d5c84d19a8bf4e56a61 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 21 Jul 2020 16:21:18 +0200 Subject: [PATCH 180/503] PhysicalPrinter : Implemented synchronizations from user printer profiles with "Print Host upload" information to the new physical printers --- src/libslic3r/Preset.cpp | 102 ++++++++++++++++++++++------ src/libslic3r/Preset.hpp | 11 ++- src/libslic3r/PresetBundle.cpp | 4 +- src/slic3r/GUI/GUI_App.cpp | 44 ++++++++++++ src/slic3r/GUI/GUI_App.hpp | 1 + src/slic3r/GUI/PresetComboBoxes.cpp | 5 +- 6 files changed, 138 insertions(+), 29 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 401de0fc3f..ae74bffd75 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1370,6 +1370,37 @@ const std::vector& PhysicalPrinter::printer_options() return s_opts; } +const std::vector& PhysicalPrinter::print_host_options() +{ + static std::vector s_opts; + if (s_opts.empty()) { + s_opts = { + "print_host", + "printhost_apikey", + "printhost_cafile" + }; + } + return s_opts; +} + +bool PhysicalPrinter::has_print_host_information(const PrinterPresetCollection& printer_presets) +{ + for (const Preset& preset : printer_presets) + if (has_print_host_information(preset.config)) + return true; + + return false; +} + +bool PhysicalPrinter::has_print_host_information(const DynamicPrintConfig& config) +{ + for (const std::string& opt : print_host_options()) + if (!config.opt_string(opt).empty()) + return true; + + return false; +} + const std::set& PhysicalPrinter::get_preset_names() const { return preset_names; @@ -1532,42 +1563,69 @@ void PhysicalPrinterCollection::load_printers(const std::string& dir_path, const // if there is saved user presets, contains information about "Print Host upload", // Create default printers with this presets -// Throws an exception on error. -void PhysicalPrinterCollection::load_printers(const PrinterPresetCollection& printer_presets, std::string def_printer_name/* = ""*/) +// Note! "Print Host upload" options will be cleared after physical printer creations +void PhysicalPrinterCollection::load_printers_from_presets(PrinterPresetCollection& printer_presets, std::string def_printer_name) { - if (def_printer_name.empty()) - def_printer_name = "Printer"; - int cnt=0; - std::string errors_cummulative; - // Store the loaded printers into a new vector - std::deque printers_loaded; - for (const Preset& preset: printer_presets) { - const DynamicPrintConfig& config = preset.config; - if (!config.opt_string("print_host").empty() || - !config.opt_string("printhost_apikey").empty() || - !config.opt_string("printhost_cafile").empty() ) { - PhysicalPrinter printer((boost::format("%1% %2%") % def_printer_name % ++cnt ).str(), preset); - printer.loaded = true; - printers_loaded.emplace_back(printer); + for (Preset& preset: printer_presets) { + DynamicPrintConfig& config = preset.config; + const std::vector& options = PhysicalPrinter::print_host_options(); - save_printer(printer); + for(const std::string& option : options) { + if (!config.opt_string(option).empty()) { + // check if printer with those "Print Host upload" options already exist + PhysicalPrinter* existed_printer = find_printer_with_same_config(config); + if (existed_printer) + // just add preset for this printer + existed_printer->add_preset(preset.name); + else { + // create new printer from this preset + PhysicalPrinter printer((boost::format("%1% %2%") % def_printer_name % ++cnt ).str(), preset); + printer.loaded = true; + save_printer(printer); + } + + // erase "Print Host upload" information from the preset + for (const std::string& opt : options) + config.opt_string(opt).clear(); + // save changes for preset + preset.save(); + + // update those changes for edited preset if it's equal to the preset + Preset& edited = printer_presets.get_edited_preset(); + if (preset.name == edited.name) { + for (const std::string& opt : options) + edited.config.opt_string(opt).clear(); + } + + break; + } } } - - if (!errors_cummulative.empty()) - throw std::runtime_error(errors_cummulative); } PhysicalPrinter* PhysicalPrinterCollection::find_printer( const std::string& name, bool first_visible_if_not_found) { - PhysicalPrinter key(name); auto it = this->find_printer_internal(name); // Ensure that a temporary copy is returned if the preset found is currently selected. - return (it != m_printers.end() && it->name == key.name) ? &this->printer(it - m_printers.begin()) : + return (it != m_printers.end() && it->name == name) ? &this->printer(it - m_printers.begin()) : first_visible_if_not_found ? &this->printer(0) : nullptr; } +PhysicalPrinter* PhysicalPrinterCollection::find_printer_with_same_config(const DynamicPrintConfig& config) +{ + for (const PhysicalPrinter& printer :*this) { + bool is_equal = true; + for (const std::string& opt : PhysicalPrinter::print_host_options()) + if (is_equal && printer.config.opt_string(opt) != config.opt_string(opt)) + is_equal = false; + + if (is_equal) + return find_printer(printer.name); + } + return nullptr; +} + // Generate a file path from a profile name. Add the ".ini" suffix if it is missing. std::string PhysicalPrinterCollection::path_from_name(const std::string& new_name) const { diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index 02f831136f..98b805b4e4 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -549,12 +549,15 @@ public: // set of presets used with this physical printer std::set preset_names; - static std::string separator(); - // Has this profile been loaded? bool loaded = false; + static std::string separator(); static const std::vector& printer_options(); + static const std::vector& print_host_options(); + static bool has_print_host_information(const PrinterPresetCollection& printer_presets); + static bool has_print_host_information(const DynamicPrintConfig& config); + const std::set& get_preset_names() const; bool has_empty_config() const; @@ -626,7 +629,7 @@ public: // Load ini files of the particular type from the provided directory path. void load_printers(const std::string& dir_path, const std::string& subdir); - void load_printers(const PrinterPresetCollection &printer_presets, std::string def_printer_name = ""); + void load_printers_from_presets(PrinterPresetCollection &printer_presets, std::string def_printer_name); // Save the printer under a new name. If the name is different from the old one, // a new printer is stored into the list of printers. @@ -704,6 +707,8 @@ private: return const_cast(this)->find_printer_internal(name); } + PhysicalPrinter* find_printer_with_same_config( const DynamicPrintConfig &config); + // List of printers // Use deque to force the container to allocate an object per each entry, // so that the addresses of the presets don't change during resizing of the container. diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 0e215a2ae7..d074c77d7d 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -201,7 +201,6 @@ void PresetBundle::load_presets(AppConfig &config, const std::string &preferred_ } try { this->physical_printers.load_printers(dir_user_presets, "physical_printer"); - this->physical_printers.load_printers(this->printers); } catch (const std::runtime_error &err) { errors_cummulative += err.what(); } @@ -436,8 +435,7 @@ void PresetBundle::load_selections(AppConfig &config, const std::string &preferr std::string initial_physical_printer_name = remove_ini_suffix(config.get("extras", "physical_printer")); // Activate physical printer from the config - const PhysicalPrinter* initial_physical_printer = physical_printers.find_printer(initial_physical_printer_name); - if (initial_physical_printer) + if (!initial_physical_printer_name.empty()) physical_printers.select_printer_by_name(initial_physical_printer_name); } diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 089a38c6f7..f7689c6e95 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -28,6 +28,9 @@ #include #include +#include +#include + #include "libslic3r/Utils.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/I18N.hpp" @@ -629,6 +632,43 @@ void GUI_App::set_auto_toolbar_icon_scale(float scale) const app_config->set("auto_toolbar_size", val); } +// check user printer_presets for the containing information about "Print Host upload" +void GUI_App::check_printer_presets() +{ + if (!PhysicalPrinter::has_print_host_information(preset_bundle->printers)) + return; + + wxString msg_text = _L("You have presets with saved options for \"Print Host upload\".\n" + "But from this version of PrusaSlicer we don't show/use this information in Printer Settings.\n" + "Now, this information will be exposed in physical printers settings.") + "\n\n" + + _L("Enter the name for the Printer device used by defaul during its creation.\n" + "Note: This name can be changed later from the physical printers settings") + ":"; + wxString msg_header = _L("Name for printer device"); + + // get custom gcode + wxTextEntryDialog dlg(nullptr, msg_text, msg_header, _L("Printer"), wxTextEntryDialogStyle); + + // detect TextCtrl and OK button + wxTextCtrl* textctrl{ nullptr }; + wxWindowList& dlg_items = dlg.GetChildren(); + for (auto item : dlg_items) { + textctrl = dynamic_cast(item); + if (textctrl) + break; + } + + if (textctrl) { + textctrl->SetSelection(0, textctrl->GetLastPosition()); + + wxButton* btn_OK = static_cast(dlg.FindWindowById(wxID_OK)); + btn_OK->Bind(wxEVT_UPDATE_UI, [textctrl](wxUpdateUIEvent& evt) { + evt.Enable(!textctrl->IsEmpty()); + }, btn_OK->GetId()); + } + if (dlg.ShowModal() == wxID_OK) + preset_bundle->physical_printers.load_printers_from_presets(preset_bundle->printers, into_u8(dlg.GetValue())); +} + void GUI_App::recreate_GUI(const wxString& msg_name) { mainframe->shutdown(); @@ -1171,6 +1211,10 @@ bool GUI_App::checked_tab(Tab* tab) // Update UI / Tabs to reflect changes in the currently loaded presets void GUI_App::load_current_presets() { + // check printer_presets for the containing information about "Print Host upload" + // and create physical printer from it, if any exists + check_printer_presets(); + PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); this->plater()->set_printer_technology(printer_technology); for (Tab *tab : tabs_list) diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 23567695cd..db551610b7 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -150,6 +150,7 @@ public: wxSize get_min_size() const; float toolbar_icon_scale(const bool is_limited = false) const; void set_auto_toolbar_icon_scale(float scale) const; + void check_printer_presets(); void recreate_GUI(const wxString& message); void system_info(); diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 2d74344bd3..e527ef9c90 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -359,8 +359,11 @@ bool PresetComboBox::selection_is_changed_according_to_physical_printers() // if new preset wasn't selected, there is no need to call update preset selection if (old_printer_preset == preset_name) { // we need just to update according Plater<->Tab PresetComboBox - if (dynamic_cast(this)!=nullptr) + if (dynamic_cast(this)!=nullptr) { wxGetApp().get_tab(m_type)->update_preset_choice(); + // Synchronize config.ini with the current selections. + m_preset_bundle->export_selections(*wxGetApp().app_config); + } else if (dynamic_cast(this)!=nullptr) wxGetApp().sidebar().update_presets(m_type); From 8f90fe16095225062c6a738bebc994714778023d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 22 Jul 2020 10:37:25 +0200 Subject: [PATCH 181/503] Code cleanup and small refactoring --- src/libslic3r/GCode.cpp | 146 ++----------------------- src/libslic3r/GCode/GCodeProcessor.cpp | 15 +-- 2 files changed, 16 insertions(+), 145 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index d492acf2e8..8a835e07d5 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -579,7 +579,6 @@ namespace Slic3r { #define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id()) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // Collect pairs of object_layer + support_layer sorted by print_z. // object_layer & support_layer are considered to be on the same print_z, if they are not further than EPSILON. std::vector GCode::collect_layers_to_print(const PrintObject& object) @@ -663,127 +662,6 @@ std::vector GCode::collect_layers_to_print(const PrintObjec return layers_to_print; } - -// // Collect pairs of object_layer + support_layer sorted by print_z. -// // object_layer & support_layer are considered to be on the same print_z, if they are not further than EPSILON. -// std::vector GCode::collect_layers_to_print(const PrintObject& object) -// { -// std::vector layers_to_print; -// layers_to_print.reserve(object.layers().size() + object.support_layers().size()); -// -// // Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um. -// // This is the same logic as in support generator. -// //FIXME should we use the printing extruders instead? -// double gap_over_supports = object.config().support_material_contact_distance; -// // FIXME should we test object.config().support_material_synchronize_layers ? Currently the support layers are synchronized with object layers iff soluble supports. -// assert(!object.config().support_material || gap_over_supports != 0. || object.config().support_material_synchronize_layers); -// if (gap_over_supports != 0.) { -// gap_over_supports = std::max(0., gap_over_supports); -// // Not a soluble support, -// double support_layer_height_min = 1000000.; -// for (auto lh : object.print()->config().min_layer_height.values) -// support_layer_height_min = std::min(support_layer_height_min, std::max(0.01, lh)); -// gap_over_supports += support_layer_height_min; -// } -// -// // Pair the object layers with the support layers by z. -// size_t idx_object_layer = 0; -// size_t idx_support_layer = 0; -// const LayerToPrint* last_extrusion_layer = nullptr; -// while (idx_object_layer < object.layers().size() || idx_support_layer < object.support_layers().size()) { -// LayerToPrint layer_to_print; -// layer_to_print.object_layer = (idx_object_layer < object.layers().size()) ? object.layers()[idx_object_layer++] : nullptr; -// layer_to_print.support_layer = (idx_support_layer < object.support_layers().size()) ? object.support_layers()[idx_support_layer++] : nullptr; -// if (layer_to_print.object_layer && layer_to_print.support_layer) { -// if (layer_to_print.object_layer->print_z < layer_to_print.support_layer->print_z - EPSILON) { -// layer_to_print.support_layer = nullptr; -// --idx_support_layer; -// } -// else if (layer_to_print.support_layer->print_z < layer_to_print.object_layer->print_z - EPSILON) { -// layer_to_print.object_layer = nullptr; -// --idx_object_layer; -// } -// } -// -//<<<<<<< HEAD -// layers_to_print.emplace_back(layer_to_print); -// -// // Check that there are extrusions on the very first layer. -// if (layers_to_print.size() == 1u) { -// if ((layer_to_print.object_layer && !layer_to_print.object_layer->has_extrusions()) -// || (layer_to_print.support_layer && !layer_to_print.support_layer->has_extrusions())) -// throw std::runtime_error(_(L("There is an object with no extrusions on the first layer."))); -//======= -// layers_to_print.emplace_back(layer_to_print); -// -// bool has_extrusions = (layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions()) -// || (layer_to_print.support_layer && layer_to_print.support_layer->has_extrusions()); -// -// // Check that there are extrusions on the very first layer. -// if (layers_to_print.size() == 1u) { -// if (! has_extrusions) -// throw std::runtime_error(_(L("There is an object with no extrusions on the first layer."))); -// } -// -// // In case there are extrusions on this layer, check there is a layer to lay it on. -// if ((layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions()) -// // Allow empty support layers, as the support generator may produce no extrusions for non-empty support regions. -// || (layer_to_print.support_layer /* && layer_to_print.support_layer->has_extrusions() */)) { -// double support_contact_z = (last_extrusion_layer && last_extrusion_layer->support_layer) -// ? gap_over_supports -// : 0.; -// double maximal_print_z = (last_extrusion_layer ? last_extrusion_layer->print_z() : 0.) -// + layer_to_print.layer()->height -// + support_contact_z; -// // Negative support_contact_z is not taken into account, it can result in false positives in cases -// // where previous layer has object extrusions too (https://github.com/prusa3d/PrusaSlicer/issues/2752) -// -// if (has_extrusions && layer_to_print.print_z() > maximal_print_z + 2. * EPSILON) { -// const_cast(object.print())->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, -// _(L("Empty layers detected, the output would not be printable.")) + "\n\n" + -// _(L("Object name")) + ": " + object.model_object()->name + "\n" + _(L("Print z")) + ": " + -// std::to_string(layers_to_print.back().print_z()) + "\n\n" + _(L("This is " -// "usually caused by negligibly small extrusions or by a faulty model. Try to repair " -// "the model or change its orientation on the bed."))); -//>>>>>>> b587289c141022323753fa1810552964de0b1356 -// } -// -// // In case there are extrusions on this layer, check there is a layer to lay it on. -// if ((layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions()) -// // Allow empty support layers, as the support generator may produce no extrusions for non-empty support regions. -// || (layer_to_print.support_layer /* && layer_to_print.support_layer->has_extrusions() */)) { -// double support_contact_z = (last_extrusion_layer && last_extrusion_layer->support_layer) -// ? gap_over_supports -// : 0.; -// double maximal_print_z = (last_extrusion_layer ? last_extrusion_layer->print_z() : 0.) -// + layer_to_print.layer()->height -// + support_contact_z; -// // Negative support_contact_z is not taken into account, it can result in false positives in cases -// // where previous layer has object extrusions too (https://github.com/prusa3d/PrusaSlicer/issues/2752) -// -// // Only check this layer in case it has some extrusions. -// bool has_extrusions = (layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions()) -// || (layer_to_print.support_layer && layer_to_print.support_layer->has_extrusions()); -// -// if (has_extrusions && layer_to_print.print_z() > maximal_print_z + 2. * EPSILON) { -// const_cast(object.print())->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, -// _(L("Empty layers detected, the output would not be printable.")) + "\n\n" + -// _(L("Object name")) + ": " + object.model_object()->name + "\n" + _(L("Print z")) + ": " + -// std::to_string(layers_to_print.back().print_z()) + "\n\n" + _(L("This is " -// "usually caused by negligibly small extrusions or by a faulty model. Try to repair " -// "the model or change its orientation on the bed."))); -// } -// -// // Remember last layer with extrusions. -// if (has_extrusions) -// last_extrusion_layer = &layers_to_print.back(); -// } -// } -// -// return layers_to_print; -// } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - // Prepare for non-sequential printing of multiple objects: Support resp. object layers with nearly identical print_z // will be printed for all objects at once. // Return a list of items. @@ -3332,36 +3210,34 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, // adds analyzer tags and updates analyzer's tracking data #if !ENABLE_GCODE_VIEWER - if (m_enable_analyzer) - { + if (m_enable_analyzer) { #endif // !ENABLE_GCODE_VIEWER - // PrusaMultiMaterial::Writer may generate GCodeAnalyzer::Height_Tag and GCodeAnalyzer::Width_Tag lines without updating m_last_height and m_last_width - // so, if the last role was erWipeTower we force export of GCodeAnalyzer::Height_Tag and GCodeAnalyzer::Width_Tag lines #if ENABLE_GCODE_VIEWER + // PrusaMultiMaterial::Writer may generate GCodeProcessor::Height_Tag and GCodeProcessor::Width_Tag lines without updating m_last_height and m_last_width + // so, if the last role was erWipeTower we force export of GCodeProcessor::Height_Tag and GCodeProcessor::Width_Tag lines bool last_was_wipe_tower = (m_last_processor_extrusion_role == erWipeTower); #else + // PrusaMultiMaterial::Writer may generate GCodeAnalyzer::Height_Tag and GCodeAnalyzer::Width_Tag lines without updating m_last_height and m_last_width + // so, if the last role was erWipeTower we force export of GCodeAnalyzer::Height_Tag and GCodeAnalyzer::Width_Tag lines bool last_was_wipe_tower = (m_last_analyzer_extrusion_role == erWipeTower); #endif // ENABLE_GCODE_VIEWER char buf[64]; #if ENABLE_GCODE_VIEWER - if (path.role() != m_last_processor_extrusion_role) - { + if (path.role() != m_last_processor_extrusion_role) { m_last_processor_extrusion_role = path.role(); sprintf(buf, ";%s%d\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), int(m_last_processor_extrusion_role)); gcode += buf; } #else - if (path.role() != m_last_analyzer_extrusion_role) - { + if (path.role() != m_last_analyzer_extrusion_role) { m_last_analyzer_extrusion_role = path.role(); sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), int(m_last_analyzer_extrusion_role)); gcode += buf; } #endif // ENABLE_GCODE_VIEWER - if (last_was_wipe_tower || (m_last_mm3_per_mm != path.mm3_per_mm)) - { + if (last_was_wipe_tower || (m_last_mm3_per_mm != path.mm3_per_mm)) { m_last_mm3_per_mm = path.mm3_per_mm; #if ENABLE_GCODE_VIEWER sprintf(buf, ";%s%f\n", GCodeProcessor::Mm3_Per_Mm_Tag.c_str(), m_last_mm3_per_mm); @@ -3372,8 +3248,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, #endif // ENABLE_GCODE_VIEWER } - if (last_was_wipe_tower || (m_last_width != path.width)) - { + if (last_was_wipe_tower || (m_last_width != path.width)) { m_last_width = path.width; #if ENABLE_GCODE_VIEWER sprintf(buf, ";%s%f\n", GCodeProcessor::Width_Tag.c_str(), m_last_width); @@ -3384,8 +3259,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, #endif // ENABLE_GCODE_VIEWER } - if (last_was_wipe_tower || (m_last_height != path.height)) - { + if (last_was_wipe_tower || (m_last_height != path.height)) { m_last_height = path.height; #if ENABLE_GCODE_VIEWER sprintf(buf, ";%s%f\n", GCodeProcessor::Height_Tag.c_str(), m_last_height); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 67ed7c699b..83cbb876ef 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -671,15 +671,12 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) EMoveType type = EMoveType::Noop; if (delta_pos[E] < 0.0f) { - if (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f || delta_pos[Z] != 0.0f) - type = EMoveType::Travel; - else - type = EMoveType::Retract; - } + type = (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f || delta_pos[Z] != 0.0f) ? EMoveType::Travel : EMoveType::Retract; + } else if (delta_pos[E] > 0.0f) { if (delta_pos[X] == 0.0f && delta_pos[Y] == 0.0f && delta_pos[Z] == 0.0f) type = EMoveType::Unretract; - else if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f)) + else if (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f) type = EMoveType::Extrude; } else if (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f || delta_pos[Z] != 0.0f) @@ -730,8 +727,8 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) return (sq_xyz_length > 0.0f) ? std::sqrt(sq_xyz_length) : std::abs(delta_pos[E]); }; - auto is_extruder_only_move = [](const AxisCoords& delta_pos) { - return (delta_pos[X] == 0.0f) && (delta_pos[Y] == 0.0f) && (delta_pos[Z] == 0.0f) && (delta_pos[E] != 0.0f); + auto is_extrusion_only_move = [](const AxisCoords& delta_pos) { + return delta_pos[X] == 0.0f && delta_pos[Y] == 0.0f && delta_pos[Z] == 0.0f && delta_pos[E] != 0.0f; }; float distance = move_length(delta_pos); @@ -781,7 +778,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) } // calculates block acceleration - float acceleration = is_extruder_only_move(delta_pos) ? + float acceleration = is_extrusion_only_move(delta_pos) ? get_retract_acceleration(static_cast(i)) : get_acceleration(static_cast(i)); From f7119c42f46b76aea67d914f48be663e4ab0a742 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 22 Jul 2020 13:23:44 +0200 Subject: [PATCH 182/503] PresetComboBox class:: code refactoring --- src/slic3r/GUI/PresetComboBoxes.cpp | 165 ++++++++++------------------ src/slic3r/GUI/PresetComboBoxes.hpp | 4 + 2 files changed, 65 insertions(+), 104 deletions(-) diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index e527ef9c90..4cf9ac40b6 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -142,11 +142,36 @@ void PresetComboBox::set_label_marker(int item, LabelItemType label_item_type) this->SetClientData(item, (void*)label_item_type); } +void PresetComboBox::invalidate_selection() +{ + m_last_selected = INT_MAX; // this value means that no one item is selected +} + +void PresetComboBox::validate_selection(bool predicate/*=false*/) +{ + if (predicate || + // just in case: mark m_last_selected as a first added element + m_last_selected == INT_MAX) + m_last_selected = GetCount() - 1; +} + +void PresetComboBox::update_selection() +{ + /* If selected_preset_item is still equal to INT_MAX, it means that + * there is no presets added to the list. + * So, select last combobox item ("Add/Remove preset") + */ + validate_selection(); + + SetSelection(m_last_selected); + SetToolTip(GetString(m_last_selected)); +} + void PresetComboBox::update(const std::string& select_preset_name) { Freeze(); Clear(); - size_t selected_preset_item = INT_MAX; // some value meaning that no one item is selected + invalidate_selection(); const std::deque& presets = m_collection->get_presets(); @@ -164,18 +189,13 @@ void PresetComboBox::update(const std::string& select_preset_name) // marker used for disable incompatible printer models for the selected physical printer bool is_enabled = true; - std::string bitmap_key = "tab"; - std::string main_icon_name = m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; - + std::string bitmap_key = "cb"; if (m_type == Preset::TYPE_PRINTER) { bitmap_key += "_printer"; if (preset.printer_technology() == ptSLA) bitmap_key += "_sla"; - if (!is_enabled) - bitmap_key += "_disabled"; } - bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt"; - bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst"; + std::string main_icon_name = m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; wxBitmap* bmp = get_bmp(bitmap_key, main_icon_name, "lock_closed", is_enabled, preset.is_compatible, preset.is_system || preset.is_default); assert(bmp); @@ -184,10 +204,7 @@ void PresetComboBox::update(const std::string& select_preset_name) int item_id = Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), *bmp); if (!is_enabled) set_label_marker(item_id, LABEL_ITEM_DISABLED); - if (preset.name == select_preset_name ||//i == idx_selected || - // just in case: mark selected_preset_item as a first added element - selected_preset_item == INT_MAX) - selected_preset_item = GetCount() - 1; + validate_selection(preset.name == select_preset_name); } else { @@ -207,25 +224,12 @@ void PresetComboBox::update(const std::string& select_preset_name) bool is_enabled = it->second.second; if (!is_enabled) set_label_marker(item_id, LABEL_ITEM_DISABLED); - if (it->first == selected || - // just in case: mark selected_preset_item as a first added element - selected_preset_item == INT_MAX) - selected_preset_item = GetCount() - 1; + validate_selection(it->first == selected); } } - /* But, if selected_preset_item is still equal to INT_MAX, it means that - * there is no presets added to the list. - * So, select last combobox item ("Add/Remove preset") - */ - if (selected_preset_item == INT_MAX) - selected_preset_item = GetCount() - 1; - - SetSelection(selected_preset_item); - SetToolTip(GetString(selected_preset_item)); + update_selection(); Thaw(); - - m_last_selected = selected_preset_item; } void PresetComboBox::msw_rescale() @@ -272,6 +276,12 @@ wxBitmap* PresetComboBox::get_bmp( std::string bitmap_key, bool wide_icons, con bool is_compatible/* = true*/, bool is_system/* = false*/, bool is_single_bar/* = false*/, std::string filament_rgb/* = ""*/, std::string extruder_rgb/* = ""*/) { + // If the filament preset is not compatible and there is a "red flag" icon loaded, show it left + // to the filament color image. + if (wide_icons) + bitmap_key += is_compatible ? ",cmpt" : ",ncmpt"; + + bitmap_key += is_system ? ",syst" : ",nsyst"; bitmap_key += ",h" + std::to_string(icon_height); wxBitmap* bmp = bitmap_cache().find(bitmap_key); @@ -313,6 +323,9 @@ wxBitmap* PresetComboBox::get_bmp( std::string bitmap_key, bool wide_icons, con wxBitmap* PresetComboBox::get_bmp( std::string bitmap_key, const std::string& main_icon_name, const std::string& next_icon_name, bool is_enabled/* = true*/, bool is_compatible/* = true*/, bool is_system/* = false*/) { + bitmap_key += !is_enabled ? "_disabled" : ""; + bitmap_key += is_compatible ? ",cmpt" : ",ncmpt"; + bitmap_key += is_system ? ",syst" : ",nsyst"; bitmap_key += ",h" + std::to_string(icon_height); wxBitmap* bmp = bitmap_cache().find(bitmap_key); @@ -645,7 +658,7 @@ void PlaterPresetComboBox::update() // Otherwise fill in the list from scratch. this->Freeze(); this->Clear(); - size_t selected_preset_item = INT_MAX; // some value meaning that no one item is selected + invalidate_selection(); const Preset* selected_filament_preset; std::string extruder_color; @@ -700,12 +713,6 @@ void PlaterPresetComboBox::update() bitmap_key += single_bar ? filament_rgb : filament_rgb + extruder_rgb; } - // If the filament preset is not compatible and there is a "red flag" icon loaded, show it left - // to the filament color image. - if (wide_icons) - bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt"; - bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst"; - wxBitmap* bmp = get_bmp(bitmap_key, wide_icons, bitmap_type_name, preset.is_compatible, preset.is_system || preset.is_default, single_bar, filament_rgb, extruder_rgb); @@ -714,12 +721,9 @@ void PlaterPresetComboBox::update() const std::string name = preset.alias.empty() ? preset.name : preset.alias; if (preset.is_default || preset.is_system) { Append(wxString::FromUTF8((name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), *bmp); - if (is_selected || - // just in case: mark selected_preset_item as a first added element - selected_preset_item == INT_MAX) { - selected_preset_item = GetCount() - 1; + validate_selection(is_selected); + if (is_selected) tooltip = wxString::FromUTF8(preset.name.c_str()); - } } else { @@ -737,10 +741,7 @@ void PlaterPresetComboBox::update() set_label_marker(Append(separator(L("User presets")), wxNullBitmap)); for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { Append(it->first, *it->second); - if (it->first == selected || - // just in case: mark selected_preset_item as a first added element - selected_preset_item == INT_MAX) - selected_preset_item = GetCount() - 1; + validate_selection(it->first == selected); } } @@ -757,32 +758,18 @@ void PlaterPresetComboBox::update() if (!preset) continue; std::string main_icon_name, bitmap_key = main_icon_name = preset->printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; - if (wide_icons) - bitmap_key += ",cmpt"; - bitmap_key += ",nsyst"; - - wxBitmap* bmp = get_bmp(bitmap_key, wide_icons, main_icon_name); + wxBitmap* bmp = get_bmp(main_icon_name, wide_icons, main_icon_name); assert(bmp); set_label_marker(Append(wxString::FromUTF8((it->get_full_name(preset_name) + (preset->is_dirty ? Preset::suffix_modified() : "")).c_str()), *bmp), LABEL_ITEM_PHYSICAL_PRINTER); - if (ph_printers.is_selected(it, preset_name) || - // just in case: mark selected_preset_item as a first added element - selected_preset_item == INT_MAX) - selected_preset_item = GetCount() - 1; + validate_selection(ph_printers.is_selected(it, preset_name)); } } } } if (m_type == Preset::TYPE_PRINTER || m_type == Preset::TYPE_SLA_MATERIAL) { - std::string bitmap_key = ""; - // If the filament preset is not compatible and there is a "red flag" icon loaded, show it left - // to the filament color image. - if (wide_icons) - bitmap_key += "wide,"; - bitmap_key += "edit_preset_list"; - - wxBitmap* bmp = get_bmp(bitmap_key, wide_icons, "edit_uni"); + wxBitmap* bmp = get_bmp("edit_preset_list", wide_icons, "edit_uni"); assert(bmp); if (m_type == Preset::TYPE_SLA_MATERIAL) @@ -791,18 +778,12 @@ void PlaterPresetComboBox::update() set_label_marker(Append(separator(L("Add/Remove printers")), *bmp), LABEL_ITEM_WIZARD_PRINTERS); } - /* But, if selected_preset_item is still equal to INT_MAX, it means that - * there is no presets added to the list. - * So, select last combobox item ("Add/Remove preset") - */ - if (selected_preset_item == INT_MAX) - selected_preset_item = GetCount() - 1; - - SetSelection(selected_preset_item); - SetToolTip(tooltip.IsEmpty() ? GetString(selected_preset_item) : tooltip); - m_last_selected = selected_preset_item; + update_selection(); Thaw(); + if (!tooltip.IsEmpty()) + SetToolTip(tooltip); + // Update control min size after rescale (changed Display DPI under MSW) if (GetMinWidth() != 20 * m_em_unit) SetMinSize(wxSize(20 * m_em_unit, GetSize().GetHeight())); @@ -858,7 +839,7 @@ void TabPresetComboBox::update() { Freeze(); Clear(); - size_t selected_preset_item = INT_MAX; // some value meaning that no one item is selected + invalidate_selection(); const std::deque& presets = m_collection->get_presets(); @@ -890,18 +871,13 @@ void TabPresetComboBox::update() if (m_type == Preset::TYPE_PRINTER && m_preset_bundle->physical_printers.has_selection()) is_enabled = m_enable_all ? true : preset.printer_technology() == proper_pt; - std::string bitmap_key = "tab"; - std::string main_icon_name = m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; - + std::string bitmap_key = "tab"; if (m_type == Preset::TYPE_PRINTER) { bitmap_key += "_printer"; if (preset.printer_technology() == ptSLA) bitmap_key += "_sla"; - if (!is_enabled) - bitmap_key += "_disabled"; } - bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt"; - bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst"; + std::string main_icon_name = m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; wxBitmap* bmp = get_bmp(bitmap_key, main_icon_name, "lock_closed", is_enabled, preset.is_compatible, preset.is_system || preset.is_default); assert(bmp); @@ -910,10 +886,7 @@ void TabPresetComboBox::update() int item_id = Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), *bmp); if (!is_enabled) set_label_marker(item_id, LABEL_ITEM_DISABLED); - if (i == idx_selected || - // just in case: mark selected_preset_item as a first added element - selected_preset_item == INT_MAX) - selected_preset_item = GetCount() - 1; + validate_selection(i == idx_selected); } else { @@ -933,10 +906,7 @@ void TabPresetComboBox::update() bool is_enabled = it->second.second; if (!is_enabled) set_label_marker(item_id, LABEL_ITEM_DISABLED); - if (it->first == selected || - // just in case: mark selected_preset_item as a first added element - selected_preset_item == INT_MAX) - selected_preset_item = GetCount() - 1; + validate_selection(it->first == selected); } } @@ -953,39 +923,26 @@ void TabPresetComboBox::update() if (!preset) continue; std::string main_icon_name = preset->printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; - std::string bitmap_key = main_icon_name + ",nsyst"; - wxBitmap* bmp = get_bmp(bitmap_key, main_icon_name, "", true, true, false); + wxBitmap* bmp = get_bmp(main_icon_name, main_icon_name, "", true, true, false); assert(bmp); set_label_marker(Append(wxString::FromUTF8((it->get_full_name(preset_name) + (preset->is_dirty ? Preset::suffix_modified() : "")).c_str()), *bmp), LABEL_ITEM_PHYSICAL_PRINTER); - if (ph_printers.is_selected(it, preset_name) || - // just in case: mark selected_preset_item as a first added element - selected_preset_item == INT_MAX) - selected_preset_item = GetCount() - 1; + validate_selection(ph_printers.is_selected(it, preset_name)); } } } // add "Add/Remove printers" item - wxBitmap* bmp = get_bmp("edit_preset_list", m_main_bitmap_name, "edit_uni", true, true, true); + std::string icon_name = "edit_uni"; + wxBitmap* bmp = get_bmp("edit_preset_list, tab,", icon_name, ""); assert(bmp); set_label_marker(Append(separator(L("Add/Remove printers")), *bmp), LABEL_ITEM_WIZARD_PRINTERS); } - /* But, if selected_preset_item is still equal to INT_MAX, it means that - * there is no presets added to the list. - * So, select last combobox item ("Add/Remove preset") - */ - if (selected_preset_item == INT_MAX) - selected_preset_item = GetCount() - 1; - - SetSelection(selected_preset_item); - SetToolTip(GetString(selected_preset_item)); + update_selection(); Thaw(); - - m_last_selected = selected_preset_item; } void TabPresetComboBox::msw_rescale() diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index 89d043f7bb..9a70818e15 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -91,6 +91,10 @@ protected: int thin_space_icon_width; int wide_space_icon_width; + void invalidate_selection(); + void validate_selection(bool predicate = false); + void update_selection(); + #ifdef __linux__ static const char* separator_head() { return "------- "; } static const char* separator_tail() { return " -------"; } From 2f43c1f3fa7473f5fe6c5a6b8baa7766fbf0756c Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 22 Jul 2020 15:52:01 +0200 Subject: [PATCH 183/503] Fixed update of the TreeCtrls and "revert to system values" buttons on preset tabs, if application was run in New or Dlg mode --- src/slic3r/GUI/MainFrame.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 10d9d800f9..64a1319d44 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1527,6 +1527,7 @@ void MainFrame::load_config(const DynamicPrintConfig& config) void MainFrame::select_tab(size_t tab/* = size_t(-1)*/) { + bool tabpanel_was_hidden = false; #if ENABLE_LAYOUT_NO_RESTART if (m_layout == ESettingsLayout::Dlg) { #else @@ -1557,6 +1558,7 @@ void MainFrame::select_tab(size_t tab/* = size_t(-1)*/) if (m_settings_dialog.IsShown()) m_settings_dialog.SetFocus(); else { + tabpanel_was_hidden = true; m_tabpanel->Show(); m_settings_dialog.Show(); } @@ -1576,6 +1578,7 @@ void MainFrame::select_tab(size_t tab/* = size_t(-1)*/) #if ENABLE_LAYOUT_NO_RESTART else if (m_layout == ESettingsLayout::New) { m_main_sizer->Show(m_plater, tab == 0); + tabpanel_was_hidden = !m_main_sizer->IsShown(m_tabpanel); m_main_sizer->Show(m_tabpanel, tab != 0); #else else if (m_layout == slNew) { @@ -1589,6 +1592,14 @@ void MainFrame::select_tab(size_t tab/* = size_t(-1)*/) Layout(); } + // When we run application in ESettingsLayout::New or ESettingsLayout::Dlg mode, tabpanel is hidden from the very beginning + // and as a result Tab::update_changed_tree_ui() function couldn't update m_is_nonsys_values values, + // which are used for update TreeCtrl and "revert_buttons". + // So, force the call of this function for Tabs, if tab panel was hidden + if (tabpanel_was_hidden) + for (auto tab : wxGetApp().tabs_list) + tab->update_changed_tree_ui(); + // when tab == -1, it means we should show the last selected tab #if ENABLE_LAYOUT_NO_RESTART m_tabpanel->SetSelection(tab == (size_t)(-1) ? m_last_selected_tab : (m_layout == ESettingsLayout::Dlg && tab != 0) ? tab - 1 : tab); From 299b783601c0584992aafb00689718c1ab51627e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 22 Jul 2020 16:28:34 +0200 Subject: [PATCH 184/503] PhysicalPrinterDialog: Select first related preset for the printer, if printer was just created or previously selected preset doesn't exist now --- src/slic3r/GUI/PresetComboBoxes.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 4cf9ac40b6..2c2b1d7a3d 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -1479,10 +1479,12 @@ void PhysicalPrinterDialog::OnOK(wxEvent& event) // save new physical printer printers.save_printer(m_printer, renamed_from); - printers.select_printer(m_printer); - - // refresh preset list on Printer Settings Tab - wxGetApp().get_tab(Preset::TYPE_PRINTER)->update_preset_choice(); + if (m_printer.preset_names.find(printers.get_selected_printer_preset_name()) == m_printer.preset_names.end()) { + // select first preset for this printer + printers.select_printer(m_printer); + // refresh preset list on Printer Settings Tab + wxGetApp().get_tab(Preset::TYPE_PRINTER)->select_preset(printers.get_selected_printer_preset_name()); + } event.Skip(); } From a4c12b90f19786c00efb909819718f4801c47170 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 23 Jul 2020 12:17:18 +0200 Subject: [PATCH 185/503] PhysicalPrinterCollection: Use select_preset() instead of select_preset_by_name() + changed signature for select_preset() --- src/libslic3r/Preset.cpp | 31 +++++++++++------------------ src/libslic3r/Preset.hpp | 14 ++++++------- src/libslic3r/PresetBundle.cpp | 2 +- src/slic3r/GUI/PresetComboBoxes.cpp | 4 ++-- src/slic3r/GUI/Tab.cpp | 2 +- 5 files changed, 23 insertions(+), 30 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index ae74bffd75..7e30831fe5 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1731,34 +1731,27 @@ std::string PhysicalPrinterCollection::get_selected_full_printer_name() const return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_printer().get_full_name(m_selected_preset); } -PhysicalPrinter& PhysicalPrinterCollection::select_printer_by_name(const std::string& full_name) +void PhysicalPrinterCollection::select_printer(const std::string& full_name) { std::string printer_name = PhysicalPrinter::get_short_name(full_name); auto it = this->find_printer_internal(printer_name); - assert(it != m_printers.end()); + if (it == m_printers.end()) { + unselect_printer(); + return; + } // update idx_selected - m_idx_selected = it - m_printers.begin(); + m_idx_selected = it - m_printers.begin(); + // update name of the currently selected preset - m_selected_preset = it->get_preset_name(full_name); - if (m_selected_preset.empty()) + if (printer_name == full_name) + // use first preset in the list m_selected_preset = *it->preset_names.begin(); - return *it; + else + m_selected_preset = it->get_preset_name(full_name); } -PhysicalPrinter& PhysicalPrinterCollection::select_printer(const std::string& printer_name) -{ - auto it = this->find_printer_internal(printer_name); - assert(it != m_printers.end()); - - // update idx_selected - m_idx_selected = it - m_printers.begin(); - // update name of the currently selected preset - m_selected_preset = *it->preset_names.begin(); - return *it; -} - -PhysicalPrinter& PhysicalPrinterCollection::select_printer(const PhysicalPrinter& printer) +void PhysicalPrinterCollection::select_printer(const PhysicalPrinter& printer) { return select_printer(printer.name); } diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index 98b805b4e4..6b5a2a5112 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -666,13 +666,13 @@ public: // Returns the printer model of the selected preset, or an empty string if no preset is selected. std::string get_selected_printer_preset_name() const { return (m_idx_selected == size_t(-1)) ? std::string() : m_selected_preset; } - // select printer with name and return reference on it - PhysicalPrinter& select_printer_by_name(const std::string& full_name); - PhysicalPrinter& select_printer(const std::string &printer_name); - PhysicalPrinter& select_printer(const PhysicalPrinter& printer); - bool has_selection() const; - void unselect_printer() ; - bool is_selected(ConstIterator it, const std::string &preset_name) const; + // Select printer by the full printer name, which contains name of printer, separator and name of selected preset + // If full_name doesn't contain name of selected preset, then select first preset in the list for this printer + void select_printer(const std::string& full_name); + void select_printer(const PhysicalPrinter& printer); + bool has_selection() const; + void unselect_printer() ; + bool is_selected(ConstIterator it, const std::string &preset_name) const; // Return a printer by an index. If the printer is active, a temporary copy is returned. PhysicalPrinter& printer(size_t idx) { return m_printers[idx]; } diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index d074c77d7d..108985704c 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -436,7 +436,7 @@ void PresetBundle::load_selections(AppConfig &config, const std::string &preferr // Activate physical printer from the config if (!initial_physical_printer_name.empty()) - physical_printers.select_printer_by_name(initial_physical_printer_name); + physical_printers.select_printer(initial_physical_printer_name); } // Export selections (current print, current filaments, current printer) into config.ini diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 2c2b1d7a3d..f6a2a036bc 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -366,7 +366,7 @@ bool PresetComboBox::selection_is_changed_according_to_physical_printers() else old_printer_preset = m_collection->get_edited_preset().name; // Select related printer preset on the Printer Settings Tab - physical_printers.select_printer_by_name(selected_string); + physical_printers.select_printer(selected_string); std::string preset_name = physical_printers.get_selected_printer_preset_name(); // if new preset wasn't selected, there is no need to call update preset selection @@ -1031,7 +1031,7 @@ void TabPresetComboBox::update_physical_printers( const std::string& preset_name dialog.ShowModal(); } - physical_printers.select_printer_by_name(printer.get_full_name(preset_name)); + physical_printers.select_printer(printer.get_full_name(preset_name)); } } else diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index e498f56b77..dd63dc1412 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3100,7 +3100,7 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, if (!last_selected_ph_printer_name.empty() && m_presets->get_edited_preset().name == PhysicalPrinter::get_preset_name(last_selected_ph_printer_name)) { // If preset selection was canceled and previously was selected physical printer, we should select it back - m_preset_bundle->physical_printers.select_printer_by_name(last_selected_ph_printer_name); + m_preset_bundle->physical_printers.select_printer(last_selected_ph_printer_name); } } From 257e77ed407a13cd43eb9f233ee40d53d29c701e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 23 Jul 2020 12:44:08 +0200 Subject: [PATCH 186/503] Fixed a typo --- src/slic3r/GUI/GUI_App.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index f7689c6e95..1b7278bd7f 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -641,7 +641,7 @@ void GUI_App::check_printer_presets() wxString msg_text = _L("You have presets with saved options for \"Print Host upload\".\n" "But from this version of PrusaSlicer we don't show/use this information in Printer Settings.\n" "Now, this information will be exposed in physical printers settings.") + "\n\n" + - _L("Enter the name for the Printer device used by defaul during its creation.\n" + _L("Enter the name for the Printer device used by default during its creation.\n" "Note: This name can be changed later from the physical printers settings") + ":"; wxString msg_header = _L("Name for printer device"); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index dd63dc1412..3f566eacb3 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2981,7 +2981,7 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, if (!physical_printers.delete_preset_from_printers(m_presets->get_edited_preset().name)) { wxMessageDialog dialog(nullptr, _L("There is/are a physical printer(s), which has/have one and only this printer preset.\n" - "This/Those printer(s) will be deletede after deleting of the selected preset.\n" + "This/Those printer(s) will be deleted after deleting of the selected preset.\n" "Are you sure you want to delete the selected preset?"), _L("Warning"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION); if (dialog.ShowModal() == wxID_NO) return; From fd50c3d262942338eac2abaab0c3e107d512a7ae Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 24 Jul 2020 11:21:49 +0200 Subject: [PATCH 187/503] Fixed a bug in selection from the 3D scene. Steps to the reproduce a crash: 1. In SLA mode add some object with several instances 2. Slice 3. Back to 3Dview scene, select all using Ctrl+A 4. Press "Delete" --- src/slic3r/GUI/GUI_ObjectList.cpp | 5 +++-- src/slic3r/GUI/Selection.cpp | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index a47c8f6711..f3ff264ced 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2364,8 +2364,9 @@ void ObjectList::del_layers_from_object(const int obj_idx) bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, const int type) { - if (obj_idx == 1000) - // Cannot delete a wipe tower. + assert(idx >= 0); + if (obj_idx == 1000 || idx<0) + // Cannot delete a wipe tower or volume with negative id return false; ModelObject* object = (*m_objects)[obj_idx]; diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 69550748d8..d76c2c7121 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1605,10 +1605,11 @@ void Selection::update_type() if ((*m_volumes)[i]->volume_idx() < 0) ++sla_volumes_count; } - unsigned int volumes_count = model_volumes_count + sla_volumes_count; + // Note: sla_volumes_count is a count of the selected sla_volumes per object instead of per instance, like a model_volumes_count is + unsigned int instances_count = (unsigned int)model_object->instances.size(); unsigned int selected_instances_count = (unsigned int)m_cache.content.begin()->second.size(); - if (volumes_count * instances_count == (unsigned int)m_list.size()) + if (model_volumes_count * instances_count + sla_volumes_count == (unsigned int)m_list.size()) { m_type = SingleFullObject; // ensures the correct mode is selected @@ -1616,7 +1617,7 @@ void Selection::update_type() } else if (selected_instances_count == 1) { - if (volumes_count == (unsigned int)m_list.size()) + if (model_volumes_count + sla_volumes_count == (unsigned int)m_list.size()) { m_type = SingleFullInstance; // ensures the correct mode is selected @@ -1639,7 +1640,7 @@ void Selection::update_type() requires_disable = true; } } - else if ((selected_instances_count > 1) && (selected_instances_count * volumes_count == (unsigned int)m_list.size())) + else if ((selected_instances_count > 1) && (selected_instances_count * model_volumes_count + sla_volumes_count == (unsigned int)m_list.size())) { m_type = MultipleFullInstance; // ensures the correct mode is selected From 0280a2a15bcb10d3ba53692ab60a33713390aba8 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 24 Jul 2020 13:02:46 +0200 Subject: [PATCH 188/503] Hot fix for the last commit --- src/slic3r/GUI/Selection.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index d76c2c7121..e9250fe9e4 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1595,17 +1595,17 @@ void Selection::update_type() } else { + unsigned int sla_volumes_count = 0; + // Note: sla_volumes_count is a count of the selected sla_volumes per object instead of per instance, like a model_volumes_count is + for (unsigned int i : m_list) { + if ((*m_volumes)[i]->volume_idx() < 0) + ++sla_volumes_count; + } + if (m_cache.content.size() == 1) // single object { const ModelObject* model_object = m_model->objects[m_cache.content.begin()->first]; unsigned int model_volumes_count = (unsigned int)model_object->volumes.size(); - unsigned int sla_volumes_count = 0; - for (unsigned int i : m_list) - { - if ((*m_volumes)[i]->volume_idx() < 0) - ++sla_volumes_count; - } - // Note: sla_volumes_count is a count of the selected sla_volumes per object instead of per instance, like a model_volumes_count is unsigned int instances_count = (unsigned int)model_object->instances.size(); unsigned int selected_instances_count = (unsigned int)m_cache.content.begin()->second.size(); @@ -1657,7 +1657,7 @@ void Selection::update_type() unsigned int instances_count = (unsigned int)model_object->instances.size(); sels_cntr += volumes_count * instances_count; } - if (sels_cntr == (unsigned int)m_list.size()) + if (sels_cntr + sla_volumes_count == (unsigned int)m_list.size()) { m_type = MultipleFullObject; // ensures the correct mode is selected From b155c3c4f829f3e77321939ebe356969ee35ce5f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 24 Jul 2020 16:34:25 +0200 Subject: [PATCH 189/503] PhysicalPrinterDialog :: next improvement --- src/slic3r/CMakeLists.txt | 4 +- src/slic3r/GUI/PhysicalPrinterDialog.cpp | 556 +++++++++++++++++++++++ src/slic3r/GUI/PhysicalPrinterDialog.hpp | 105 +++++ src/slic3r/GUI/PresetComboBoxes.cpp | 539 ++-------------------- src/slic3r/GUI/PresetComboBoxes.hpp | 89 +--- 5 files changed, 702 insertions(+), 591 deletions(-) create mode 100644 src/slic3r/GUI/PhysicalPrinterDialog.cpp create mode 100644 src/slic3r/GUI/PhysicalPrinterDialog.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 49e0692858..20ea4e33a9 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -76,9 +76,11 @@ set(SLIC3R_GUI_SOURCES GUI/MainFrame.cpp GUI/MainFrame.hpp GUI/Plater.cpp + GUI/Plater.hpp GUI/PresetComboBoxes.hpp GUI/PresetComboBoxes.cpp - GUI/Plater.hpp + GUI/PhysicalPrinterDialog.hpp + GUI/PhysicalPrinterDialog.cpp GUI/GUI_ObjectList.cpp GUI/GUI_ObjectList.hpp GUI/GUI_ObjectManipulation.cpp diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.cpp b/src/slic3r/GUI/PhysicalPrinterDialog.cpp new file mode 100644 index 0000000000..7d3c92c138 --- /dev/null +++ b/src/slic3r/GUI/PhysicalPrinterDialog.cpp @@ -0,0 +1,556 @@ +#include "PhysicalPrinterDialog.hpp" +#include "PresetComboBoxes.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "libslic3r/libslic3r.h" +#include "libslic3r/PrintConfig.hpp" +#include "libslic3r/PresetBundle.hpp" + +#include "GUI.hpp" +#include "GUI_App.hpp" +#include "MainFrame.hpp" +#include "format.hpp" +#include "Tab.hpp" +#include "wxExtensions.hpp" +#include "PrintHostDialogs.hpp" +#include "../Utils/ASCIIFolding.hpp" +#include "../Utils/PrintHost.hpp" +#include "../Utils/FixModelByWin10.hpp" +#include "../Utils/UndoRedo.hpp" +#include "RemovableDriveManager.hpp" +#include "BitmapCache.hpp" +#include "BonjourDialog.hpp" + +using Slic3r::GUI::format_wxstr; + +//static const std::pair THUMBNAIL_SIZE_3MF = { 256, 256 }; + +namespace Slic3r { +namespace GUI { + +#define BORDER_W 10 + +//------------------------------------------ +// PresetForPrinter +//------------------------------------------ + +PresetForPrinter::PresetForPrinter(PhysicalPrinterDialog* parent, const std::string& preset_name) : + m_parent(parent) +{ + m_sizer = new wxBoxSizer(wxVERTICAL); + + m_delete_preset_btn = new ScalableButton(parent, wxID_ANY, "cross", "", wxDefaultSize, wxDefaultPosition, /*wxBU_LEFT | */wxBU_EXACTFIT); + m_delete_preset_btn->SetFont(wxGetApp().normal_font()); + m_delete_preset_btn->SetToolTip(_L("Delete this preset from this printer device")); + m_delete_preset_btn->Bind(wxEVT_BUTTON, &PresetForPrinter::DeletePreset, this); + + m_presets_list = new PresetComboBox(parent, Preset::TYPE_PRINTER); + m_presets_list->set_printer_technology(parent->get_printer_technology()); + + m_presets_list->set_selection_changed_function([this](int selection) { + std::string selected_string = Preset::remove_suffix_modified(m_presets_list->GetString(selection).ToUTF8().data()); + Preset* preset = wxGetApp().preset_bundle->printers.find_preset(selected_string); + assert(preset); + Preset& edited_preset = wxGetApp().preset_bundle->printers.get_edited_preset(); + if (preset->name == edited_preset.name) + preset = &edited_preset; + + // if created physical printer doesn't have any settings, use the settings from the selected preset + if (m_parent->get_printer()->has_empty_config()) { + // update Print Host upload from the selected preset + m_parent->get_printer()->update_from_preset(*preset); + // update values in parent (PhysicalPrinterDialog) + m_parent->update(); + } + + // update PrinterTechnology if it was changed + if (m_presets_list->set_printer_technology(preset->printer_technology())) + m_parent->set_printer_technology(preset->printer_technology()); + + update_full_printer_name(); + }); + m_presets_list->update(preset_name); + + m_info_line = new wxStaticText(parent, wxID_ANY, _L("This printer will be shown in the presets list as") + ":"); + + m_full_printer_name = new wxStaticText(parent, wxID_ANY, ""); + m_full_printer_name->SetFont(wxGetApp().bold_font()); + + wxBoxSizer* preset_sizer = new wxBoxSizer(wxHORIZONTAL); + preset_sizer->Add(m_presets_list , 1, wxEXPAND); + preset_sizer->Add(m_delete_preset_btn , 0, wxEXPAND | wxLEFT, BORDER_W); + + wxBoxSizer* name_sizer = new wxBoxSizer(wxHORIZONTAL); + name_sizer->Add(m_info_line, 0, wxEXPAND); + name_sizer->Add(m_full_printer_name, 0, wxEXPAND | wxLEFT, BORDER_W); + + m_sizer->Add(preset_sizer , 0, wxEXPAND); + m_sizer->Add(name_sizer, 0, wxEXPAND); +} + +PresetForPrinter::~PresetForPrinter() +{ + m_presets_list->Destroy(); + m_delete_preset_btn->Destroy(); + m_info_line->Destroy(); + m_full_printer_name->Destroy(); +} + +void PresetForPrinter::DeletePreset(wxEvent& event) +{ + m_parent->DeletePreset(this); +} + +void PresetForPrinter::update_full_printer_name() +{ + wxString printer_name = m_parent->get_printer_name(); + wxString preset_name = m_presets_list->GetString(m_presets_list->GetSelection()); + + m_full_printer_name->SetLabelText(printer_name + " * " + preset_name); +} + +std::string PresetForPrinter::get_preset_name() +{ + return into_u8(m_presets_list->GetString(m_presets_list->GetSelection())); +} + +void PresetForPrinter::SuppressDelete() +{ + m_delete_preset_btn->Enable(false); + + // this case means that now we have only one related preset for the printer + // So, allow any selection + m_presets_list->set_printer_technology(ptAny); + m_presets_list->update(); +} + +void PresetForPrinter::AllowDelete() +{ + if (!m_delete_preset_btn->IsEnabled()) + m_delete_preset_btn->Enable(); + + m_presets_list->set_printer_technology(m_parent->get_printer_technology()); + m_presets_list->update(); +} + +void PresetForPrinter::msw_rescale() +{ + m_presets_list->msw_rescale(); + m_delete_preset_btn->msw_rescale(); +} + + +//------------------------------------------ +// PhysicalPrinterDialog +//------------------------------------------ + +PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) + : DPIDialog(NULL, wxID_ANY, _L("Physical Printer"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), -1), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +{ + SetFont(wxGetApp().normal_font()); + SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + + m_default_name = _L("My Printer Device"); + + if (printer_name.IsEmpty()) + printer_name = m_default_name; + else { + std::string full_name = into_u8(printer_name); + printer_name = from_u8(PhysicalPrinter::get_short_name(full_name)); + } + + wxStaticText* label_top = new wxStaticText(this, wxID_ANY, _L("Descriptive name for the printer device") + ":"); + + m_add_preset_btn = new ScalableButton(this, wxID_ANY, "add_copies", "", wxDefaultSize, wxDefaultPosition, /*wxBU_LEFT | */wxBU_EXACTFIT); + m_add_preset_btn->SetFont(wxGetApp().normal_font()); + m_add_preset_btn->SetToolTip(_L("Add preset for this printer device")); + m_add_preset_btn->Bind(wxEVT_BUTTON, &PhysicalPrinterDialog::AddPreset, this); + + m_printer_name = new wxTextCtrl(this, wxID_ANY, printer_name, wxDefaultPosition, wxDefaultSize); + m_printer_name->Bind(wxEVT_TEXT, [this](wxEvent&) { this->update_full_printer_names(); }); + + PhysicalPrinterCollection& printers = wxGetApp().preset_bundle->physical_printers; + PhysicalPrinter* printer = printers.find_printer(into_u8(printer_name)); + if (!printer) { + const Preset& preset = wxGetApp().preset_bundle->printers.get_edited_preset(); + printer = new PhysicalPrinter(into_u8(printer_name), preset); + // if printer_name is empty it means that new printer is created, so enable all items in the preset list + m_presets.emplace_back(new PresetForPrinter(this, preset.name)); + } + else + { + const std::set& preset_names = printer->get_preset_names(); + for (const std::string& preset_name : preset_names) + m_presets.emplace_back(new PresetForPrinter(this, preset_name)); + } + assert(printer); + m_printer = *printer; + + if (m_presets.size() == 1) + m_presets.front()->SuppressDelete(); + + update_full_printer_names(); + + m_config = &m_printer.config; + + m_optgroup = new ConfigOptionsGroup(this, _L("Print Host upload"), m_config); + build_printhost_settings(m_optgroup); + //m_optgroup->reload_config(); + + wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); + wxButton* btnOK = static_cast(this->FindWindowById(wxID_OK, this)); + btnOK->Bind(wxEVT_BUTTON, &PhysicalPrinterDialog::OnOK, this); + + wxBoxSizer* nameSizer = new wxBoxSizer(wxHORIZONTAL); + nameSizer->Add(m_printer_name, 1, wxEXPAND); + nameSizer->Add(m_add_preset_btn, 0, wxEXPAND | wxLEFT, BORDER_W); + + m_presets_sizer = new wxBoxSizer(wxVERTICAL); + for (PresetForPrinter* preset : m_presets) + m_presets_sizer->Add(preset->sizer(), 1, wxEXPAND | wxTOP, BORDER_W); + + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + + topSizer->Add(label_top , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W); + topSizer->Add(nameSizer , 0, wxEXPAND | wxLEFT | wxRIGHT, BORDER_W); + topSizer->Add(m_presets_sizer , 0, wxEXPAND | wxLEFT | wxRIGHT, BORDER_W); + topSizer->Add(m_optgroup->sizer , 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W); + topSizer->Add(btns , 0, wxEXPAND | wxALL, BORDER_W); + + SetSizer(topSizer); + topSizer->SetSizeHints(this); +} + +PhysicalPrinterDialog::~PhysicalPrinterDialog() +{ + for (PresetForPrinter* preset : m_presets) { + delete preset; + preset = nullptr; + } +} + +void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgroup) +{ + m_optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { + if (opt_key == "authorization_type") + this->update(); + }; + + m_optgroup->append_single_option_line("host_type"); + + auto create_sizer_with_btn = [this](wxWindow* parent, ScalableButton** btn, const std::string& icon_name, const wxString& label) { + *btn = new ScalableButton(parent, wxID_ANY, icon_name, label, wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT); + (*btn)->SetFont(wxGetApp().normal_font()); + + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(*btn); + return sizer; + }; + + auto printhost_browse = [=](wxWindow* parent) + { + auto sizer = create_sizer_with_btn(parent, &m_printhost_browse_btn, "browse", _L("Browse") + " " + dots); + m_printhost_browse_btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent& e) { + BonjourDialog dialog(this, Preset::printer_technology(m_printer.config)); + if (dialog.show_and_lookup()) { + m_optgroup->set_value("print_host", std::move(dialog.get_selected()), true); + m_optgroup->get_field("print_host")->field_changed(); + } + }); + + return sizer; + }; + + auto print_host_test = [=](wxWindow* parent) { + auto sizer = create_sizer_with_btn(parent, &m_printhost_test_btn, "test", _L("Test")); + + m_printhost_test_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) { + std::unique_ptr host(PrintHost::get_print_host(m_config)); + if (!host) { + const wxString text = _L("Could not get a valid Printer Host reference"); + show_error(this, text); + return; + } + wxString msg; + if (host->test(msg)) { + show_info(this, host->get_test_ok_msg(), _L("Success!")); + } + else { + show_error(this, host->get_test_failed_msg(msg)); + } + }); + + return sizer; + }; + + // Set a wider width for a better alignment + Option option = m_optgroup->get_option("print_host"); + option.opt.width = Field::def_width_wider(); + Line host_line = m_optgroup->create_single_option_line(option); + host_line.append_widget(printhost_browse); + host_line.append_widget(print_host_test); + m_optgroup->append_line(host_line); + + m_optgroup->append_single_option_line("authorization_type"); + + option = m_optgroup->get_option("printhost_apikey"); + option.opt.width = Field::def_width_wider(); + m_optgroup->append_single_option_line(option); + + const auto ca_file_hint = _u8L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate."); + + if (Http::ca_file_supported()) { + option = m_optgroup->get_option("printhost_cafile"); + option.opt.width = Field::def_width_wider(); + Line cafile_line = m_optgroup->create_single_option_line(option); + + auto printhost_cafile_browse = [=](wxWindow* parent) { + auto sizer = create_sizer_with_btn(parent, &m_printhost_cafile_browse_btn, "browse", _L("Browse") + " " + dots); + m_printhost_cafile_browse_btn->Bind(wxEVT_BUTTON, [this, m_optgroup](wxCommandEvent e) { + static const auto filemasks = _L("Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*"); + wxFileDialog openFileDialog(this, _L("Open CA certificate file"), "", "", filemasks, wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if (openFileDialog.ShowModal() != wxID_CANCEL) { + m_optgroup->set_value("printhost_cafile", std::move(openFileDialog.GetPath()), true); + m_optgroup->get_field("printhost_cafile")->field_changed(); + } + }); + + return sizer; + }; + + cafile_line.append_widget(printhost_cafile_browse); + m_optgroup->append_line(cafile_line); + + Line cafile_hint{ "", "" }; + cafile_hint.full_width = 1; + cafile_hint.widget = [this, ca_file_hint](wxWindow* parent) { + auto txt = new wxStaticText(parent, wxID_ANY, ca_file_hint); + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(txt); + return sizer; + }; + m_optgroup->append_line(cafile_hint); + } + else { + Line line{ "", "" }; + line.full_width = 1; + + line.widget = [ca_file_hint](wxWindow* parent) { + std::string info = _u8L("HTTPS CA File") + ":\n\t" + + (boost::format(_u8L("On this system, %s uses HTTPS certificates from the system Certificate Store or Keychain.")) % SLIC3R_APP_NAME).str() + + "\n\t" + _u8L("To use a custom CA file, please import your CA file into Certificate Store / Keychain."); + + //auto txt = new wxStaticText(parent, wxID_ANY, from_u8((boost::format("%1%\n\n\t%2%") % info % ca_file_hint).str())); + auto txt = new wxStaticText(parent, wxID_ANY, from_u8((boost::format("%1%\n\t%2%") % info % ca_file_hint).str())); + txt->SetFont(wxGetApp().normal_font()); + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(txt, 1, wxEXPAND); + return sizer; + }; + + m_optgroup->append_line(line); + } + + for (const std::string& opt_key : std::vector{ "login", "password" }) { + option = m_optgroup->get_option(opt_key); + option.opt.width = Field::def_width_wider(); + m_optgroup->append_single_option_line(option); + } + + update(); +} + +void PhysicalPrinterDialog::update() +{ + m_optgroup->reload_config(); + + const PrinterTechnology tech = Preset::printer_technology(*m_config); + // Only offer the host type selection for FFF, for SLA it's always the SL1 printer (at the moment) + if (tech == ptFFF) { + m_optgroup->show_field("host_type"); + m_optgroup->hide_field("authorization_type"); + for (const std::string& opt_key : std::vector{ "login", "password" }) + m_optgroup->hide_field(opt_key); + } + else { + m_optgroup->set_value("host_type", int(PrintHostType::htOctoPrint), false); + m_optgroup->hide_field("host_type"); + + m_optgroup->show_field("authorization_type"); + + AuthorizationType auth_type = m_config->option>("authorization_type")->value; + m_optgroup->show_field("printhost_apikey", auth_type == AuthorizationType::atKeyPassword); + + for (const std::string& opt_key : std::vector{ "login", "password" }) + m_optgroup->show_field(opt_key, auth_type == AuthorizationType::atUserPassword); + } + + this->Layout(); +} + + +wxString PhysicalPrinterDialog::get_printer_name() +{ + return m_printer_name->GetValue(); +} + +void PhysicalPrinterDialog::update_full_printer_names() +{ + for (PresetForPrinter* preset : m_presets) + preset->update_full_printer_name(); + + this->Layout(); +} + +void PhysicalPrinterDialog::set_printer_technology(PrinterTechnology pt) +{ + m_config->set_key_value("printer_technology", new ConfigOptionEnum(pt)); + update(); +} + +PrinterTechnology PhysicalPrinterDialog::get_printer_technology() +{ + return m_printer.printer_technology(); +} + +void PhysicalPrinterDialog::on_dpi_changed(const wxRect& suggested_rect) +{ + const int& em = em_unit(); + + m_printhost_browse_btn->msw_rescale(); + m_printhost_test_btn->msw_rescale(); + if (m_printhost_cafile_browse_btn) + m_printhost_cafile_browse_btn->msw_rescale(); + + m_optgroup->msw_rescale(); + + msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL }); + + for (PresetForPrinter* preset : m_presets) + preset->msw_rescale(); + + const wxSize& size = wxSize(45 * em, 35 * em); + SetMinSize(size); + + Fit(); + Refresh(); +} + +void PhysicalPrinterDialog::OnOK(wxEvent& event) +{ + wxString printer_name = m_printer_name->GetValue(); + if (printer_name.IsEmpty()) { + warning_catcher(this, _L("The supplied name is empty. It can't be saved.")); + return; + } + if (printer_name == m_default_name) { + warning_catcher(this, _L("You should to change a name of your printer device. It can't be saved.")); + return; + } + + PhysicalPrinterCollection& printers = wxGetApp().preset_bundle->physical_printers; + const PhysicalPrinter* existing = printers.find_printer(into_u8(printer_name)); + if (existing && into_u8(printer_name) != printers.get_selected_printer_name()) + { + wxString msg_text = from_u8((boost::format(_u8L("Printer with name \"%1%\" already exists.")) % printer_name).str()); + msg_text += "\n" + _L("Replace?"); + wxMessageDialog dialog(nullptr, msg_text, _L("Warning"), wxICON_WARNING | wxYES | wxNO); + + if (dialog.ShowModal() == wxID_NO) + return; + } + + std::set repeat_presets; + m_printer.reset_presets(); + for (PresetForPrinter* preset : m_presets) { + if (!m_printer.add_preset(preset->get_preset_name())) + repeat_presets.emplace(preset->get_preset_name()); + } + + if (!repeat_presets.empty()) + { + wxString repeatable_presets = "\n"; + for (const std::string& preset_name : repeat_presets) + repeatable_presets += " " + from_u8(preset_name) + "\n"; + repeatable_presets += "\n"; + + wxString msg_text = from_u8((boost::format(_u8L("Next printer preset(s) is(are) duplicated:%1%" + "Should I add it(they) just once for the printer \"%2%\" and close the Editing Dialog?")) % repeatable_presets % printer_name).str()); + wxMessageDialog dialog(nullptr, msg_text, _L("Warning"), wxICON_WARNING | wxYES | wxNO); + if (dialog.ShowModal() == wxID_NO) + return; + } + + std::string renamed_from; + // temporary save previous printer name if it was edited + if (m_printer.name != _u8L("My Printer Device") && + m_printer.name != into_u8(printer_name)) + renamed_from = m_printer.name; + + //update printer name, if it was changed + m_printer.set_name(into_u8(printer_name)); + + // save new physical printer + printers.save_printer(m_printer, renamed_from); + + if (m_printer.preset_names.find(printers.get_selected_printer_preset_name()) == m_printer.preset_names.end()) { + // select first preset for this printer + printers.select_printer(m_printer); + // refresh preset list on Printer Settings Tab + wxGetApp().get_tab(Preset::TYPE_PRINTER)->select_preset(printers.get_selected_printer_preset_name()); + } + + event.Skip(); +} + +void PhysicalPrinterDialog::AddPreset(wxEvent& event) +{ + m_presets.emplace_back(new PresetForPrinter(this)); + // enable DELETE button for the first preset, if was disabled + m_presets.front()->AllowDelete(); + + m_presets_sizer->Add(m_presets.back()->sizer(), 1, wxEXPAND | wxTOP, BORDER_W); + update_full_printer_names(); + + this->Fit(); +} + +void PhysicalPrinterDialog::DeletePreset(PresetForPrinter* preset_for_printer) +{ + if (m_presets.size() == 1) { + wxString msg_text = _L("It's not possible to delete last related preset for the printer."); + wxMessageDialog dialog(nullptr, msg_text, _L("Infornation"), wxICON_INFORMATION | wxOK); + dialog.ShowModal(); + return; + } + + assert(preset_for_printer); + auto it = std::find(m_presets.begin(), m_presets.end(), preset_for_printer); + if (it == m_presets.end()) + return; + + const int remove_id = it - m_presets.begin(); + m_presets_sizer->Remove(remove_id); + delete preset_for_printer; + m_presets.erase(it); + + if (m_presets.size() == 1) + m_presets.front()->SuppressDelete(); + + this->Layout(); + this->Fit(); +} + + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.hpp b/src/slic3r/GUI/PhysicalPrinterDialog.hpp new file mode 100644 index 0000000000..3d0cf2d9f2 --- /dev/null +++ b/src/slic3r/GUI/PhysicalPrinterDialog.hpp @@ -0,0 +1,105 @@ +#ifndef slic3r_PhysicalPrinterDialog_hpp_ +#define slic3r_PhysicalPrinterDialog_hpp_ + +#include + +#include + +#include "libslic3r/Preset.hpp" +#include "GUI_Utils.hpp" + +class wxString; +class wxTextCtrl; +class wxStaticText; +class ScalableButton; +class wxBoxSizer; + +namespace Slic3r { + +namespace GUI { + +class PresetComboBox; + +//------------------------------------------ +// PresetForPrinter +//------------------------------------------ +//static std::string g_info_string = " (modified)"; +class PhysicalPrinterDialog; +class PresetForPrinter +{ + PhysicalPrinterDialog* m_parent { nullptr }; + + PresetComboBox* m_presets_list { nullptr }; + ScalableButton* m_delete_preset_btn { nullptr }; + wxStaticText* m_info_line { nullptr }; + wxStaticText* m_full_printer_name { nullptr }; + + wxBoxSizer* m_sizer { nullptr }; + + void DeletePreset(wxEvent& event); + +public: + PresetForPrinter(PhysicalPrinterDialog* parent, const std::string& preset_name = ""); + ~PresetForPrinter(); + + wxBoxSizer* sizer() { return m_sizer; } + void update_full_printer_name(); + std::string get_preset_name(); + void SuppressDelete(); + void AllowDelete(); + + void msw_rescale(); + void on_sys_color_changed() {}; +}; + + +//------------------------------------------ +// PhysicalPrinterDialog +//------------------------------------------ + +class ConfigOptionsGroup; +class PhysicalPrinterDialog : public DPIDialog +{ + PhysicalPrinter m_printer; + wxString m_default_name; + DynamicPrintConfig* m_config { nullptr }; + + wxTextCtrl* m_printer_name { nullptr }; + std::vector m_presets; + + ConfigOptionsGroup* m_optgroup { nullptr }; + + ScalableButton* m_add_preset_btn {nullptr}; + ScalableButton* m_printhost_browse_btn {nullptr}; + ScalableButton* m_printhost_test_btn {nullptr}; + ScalableButton* m_printhost_cafile_browse_btn {nullptr}; + + wxBoxSizer* m_presets_sizer {nullptr}; + + void build_printhost_settings(ConfigOptionsGroup* optgroup); + void OnOK(wxEvent& event); + void AddPreset(wxEvent& event); + +public: + PhysicalPrinterDialog(wxString printer_name); + ~PhysicalPrinterDialog(); + + void update(); + wxString get_printer_name(); + void update_full_printer_names(); + PhysicalPrinter* get_printer() {return &m_printer; } + void set_printer_technology(PrinterTechnology pt); + PrinterTechnology get_printer_technology(); + + void DeletePreset(PresetForPrinter* preset_for_printer); + +protected: + void on_dpi_changed(const wxRect& suggested_rect) override; + void on_sys_color_changed() override {}; +}; + + +} // namespace GUI +} // namespace Slic3r + +#endif diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index f6a2a036bc..3edc3947a1 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -24,20 +24,15 @@ #include "MainFrame.hpp" #include "format.hpp" #include "Tab.hpp" -#include "PrintHostDialogs.hpp" #include "ConfigWizard.hpp" #include "../Utils/ASCIIFolding.hpp" -#include "../Utils/PrintHost.hpp" #include "../Utils/FixModelByWin10.hpp" #include "../Utils/UndoRedo.hpp" -#include "RemovableDriveManager.hpp" #include "BitmapCache.hpp" -#include "BonjourDialog.hpp" +#include "PhysicalPrinterDialog.hpp" using Slic3r::GUI::format_wxstr; -static const std::pair THUMBNAIL_SIZE_3MF = { 256, 256 }; - namespace Slic3r { namespace GUI { @@ -142,6 +137,15 @@ void PresetComboBox::set_label_marker(int item, LabelItemType label_item_type) this->SetClientData(item, (void*)label_item_type); } +bool PresetComboBox::set_printer_technology(PrinterTechnology pt) +{ + if (printer_technology != pt) { + printer_technology = pt; + return true; + } + return false; +} + void PresetComboBox::invalidate_selection() { m_last_selected = INT_MAX; // this value means that no one item is selected @@ -187,7 +191,7 @@ void PresetComboBox::update(const std::string& select_preset_name) continue; // marker used for disable incompatible printer models for the selected physical printer - bool is_enabled = true; + bool is_enabled = m_type == Preset::TYPE_PRINTER && printer_technology != ptAny ? preset.printer_technology() == printer_technology : true; std::string bitmap_key = "cb"; if (m_type == Preset::TYPE_PRINTER) { @@ -204,13 +208,13 @@ void PresetComboBox::update(const std::string& select_preset_name) int item_id = Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), *bmp); if (!is_enabled) set_label_marker(item_id, LABEL_ITEM_DISABLED); - validate_selection(preset.name == select_preset_name); + validate_selection(preset.name == select_preset_name || (select_preset_name.empty() && is_enabled)); } else { std::pair pair(bmp, is_enabled); nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), std::pair(bmp, is_enabled)); - if (preset.name == select_preset_name) + if (preset.name == select_preset_name || (select_preset_name.empty() && is_enabled)) selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()); } if (i + 1 == m_collection->num_default_presets()) @@ -232,6 +236,11 @@ void PresetComboBox::update(const std::string& select_preset_name) Thaw(); } +void PresetComboBox::update() +{ + this->update(into_u8(this->GetString(this->GetSelection()))); +} + void PresetComboBox::msw_rescale() { m_em_unit = em_unit(this); @@ -547,7 +556,7 @@ PlaterPresetComboBox::PlaterPresetComboBox(wxWindow *parent, Preset::Type preset edit_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent) { // In a case of a physical printer, for its editing open PhysicalPrinterDialog - if (m_type == Preset::TYPE_PRINTER && this->is_selected_physical_printer()) { + if (m_type == Preset::TYPE_PRINTER/* && this->is_selected_physical_printer()*/) { this->show_edit_menu(); return; } @@ -598,7 +607,7 @@ void PlaterPresetComboBox::show_add_menu() { wxMenu* menu = new wxMenu(); - append_menu_item(menu, wxID_ANY, _L("Add/Remove logical printers"), "", + append_menu_item(menu, wxID_ANY, _L("Add/Remove presets"), "", [this](wxCommandEvent&) { wxTheApp->CallAfter([]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, ConfigWizard::SP_PRINTERS); }); }, "edit_uni", menu, []() { return true; }, wxGetApp().plater()); @@ -617,9 +626,10 @@ void PlaterPresetComboBox::show_edit_menu() { wxMenu* menu = new wxMenu(); - append_menu_item(menu, wxID_ANY, _L("Edit related printer profile"), "", + append_menu_item(menu, wxID_ANY, _L("Edit preset"), "", [this](wxCommandEvent&) { this->switch_to_tab(); }, "cog", menu, []() { return true; }, wxGetApp().plater()); + if (this->is_selected_physical_printer()) { append_menu_item(menu, wxID_ANY, _L("Edit physical printer"), "", [this](wxCommandEvent&) { PhysicalPrinterDialog dlg(this->GetString(this->GetSelection())); @@ -642,6 +652,12 @@ void PlaterPresetComboBox::show_edit_menu() wxGetApp().get_tab(m_type)->update_preset_choice(); update(); }, "cross", menu, []() { return true; }, wxGetApp().plater()); + } + else + append_menu_item(menu, wxID_ANY, _L("Add/Remove presets"), "", + [this](wxCommandEvent&) { + wxTheApp->CallAfter([]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, ConfigWizard::SP_PRINTERS); }); + }, "edit_uni", menu, []() { return true; }, wxGetApp().plater()); wxGetApp().plater()->PopupMenu(menu); } @@ -768,7 +784,7 @@ void PlaterPresetComboBox::update() } } - if (m_type == Preset::TYPE_PRINTER || m_type == Preset::TYPE_SLA_MATERIAL) { + if (/*m_type == Preset::TYPE_PRINTER || */m_type == Preset::TYPE_SLA_MATERIAL) { wxBitmap* bmp = get_bmp("edit_preset_list", wide_icons, "edit_uni"); assert(bmp); @@ -867,9 +883,6 @@ void TabPresetComboBox::update() // marker used for disable incompatible printer models for the selected physical printer bool is_enabled = true; - // check this value just for printer presets, when physical printer is selected - if (m_type == Preset::TYPE_PRINTER && m_preset_bundle->physical_printers.has_selection()) - is_enabled = m_enable_all ? true : preset.printer_technology() == proper_pt; std::string bitmap_key = "tab"; if (m_type == Preset::TYPE_PRINTER) { @@ -1040,494 +1053,6 @@ void TabPresetComboBox::update_physical_printers( const std::string& preset_name } -//------------------------------------------ -// PresetForPrinter -//------------------------------------------ - -PresetForPrinter::PresetForPrinter(PhysicalPrinterDialog* parent, const std::string& preset_name) : - m_parent(parent) -{ - m_sizer = new wxBoxSizer(wxVERTICAL); - - m_delete_preset_btn = new ScalableButton(parent, wxID_ANY, "cross", "", wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT); - m_delete_preset_btn->SetFont(wxGetApp().normal_font()); - m_delete_preset_btn->SetToolTip(_L("Delete this preset from this printer device")); - m_delete_preset_btn->Bind(wxEVT_BUTTON, &PresetForPrinter::DeletePreset, this); - - m_presets_list = new PresetComboBox(parent, Preset::TYPE_PRINTER); - - m_presets_list->set_selection_changed_function([this](int selection) { - std::string selected_string = Preset::remove_suffix_modified(m_presets_list->GetString(selection).ToUTF8().data()); - Preset* preset = wxGetApp().preset_bundle->printers.find_preset(selected_string); - assert(preset); - Preset& edited_preset = wxGetApp().preset_bundle->printers.get_edited_preset(); - if (preset->name == edited_preset.name) - preset = &edited_preset; - - // if created physical printer doesn't have any settings, use the settings from the selected preset - if (m_parent->get_printer()->has_empty_config()) { - // update Print Host upload from the selected preset - m_parent->get_printer()->update_from_preset(*preset); - // update values in parent (PhysicalPrinterDialog) - m_parent->update(); - } - - update_full_printer_name(); - }); - m_presets_list->update(preset_name); - - m_info_line = new wxStaticText(parent, wxID_ANY, _L("This printer will be shown in the presets list as") + ":"); - - m_full_printer_name = new wxStaticText(parent, wxID_ANY, ""); - m_full_printer_name->SetFont(wxGetApp().bold_font()); - - wxBoxSizer* preset_sizer = new wxBoxSizer(wxHORIZONTAL); - preset_sizer->Add(m_presets_list , 1, wxEXPAND); - preset_sizer->Add(m_delete_preset_btn , 0, wxEXPAND | wxLEFT, BORDER_W); - - wxBoxSizer* name_sizer = new wxBoxSizer(wxHORIZONTAL); - name_sizer->Add(m_info_line, 0, wxEXPAND); - name_sizer->Add(m_full_printer_name, 0, wxEXPAND | wxLEFT, BORDER_W); - - m_sizer->Add(preset_sizer , 0, wxEXPAND); - m_sizer->Add(name_sizer, 0, wxEXPAND); -} - -PresetForPrinter::~PresetForPrinter() -{ - m_presets_list->Destroy(); - m_delete_preset_btn->Destroy(); - m_info_line->Destroy(); - m_full_printer_name->Destroy(); -} - -void PresetForPrinter::DeletePreset(wxEvent& event) -{ - m_parent->DeletePreset(this); -} - -void PresetForPrinter::update_full_printer_name() -{ - wxString printer_name = m_parent->get_printer_name(); - wxString preset_name = m_presets_list->GetString(m_presets_list->GetSelection()); - - m_full_printer_name->SetLabelText(printer_name + " * " + preset_name); -} - -std::string PresetForPrinter::get_preset_name() -{ - return into_u8(m_presets_list->GetString(m_presets_list->GetSelection())); -} - -void PresetForPrinter::DisableDeleteBtn() -{ - m_delete_preset_btn->Enable(false); -} - -void PresetForPrinter::EnableDeleteBtn() -{ - if (!m_delete_preset_btn->IsEnabled()) - m_delete_preset_btn->Enable(); -} - -void PresetForPrinter::msw_rescale() -{ - m_presets_list->msw_rescale(); - m_delete_preset_btn->msw_rescale(); -} - - -//------------------------------------------ -// PhysicalPrinterDialog -//------------------------------------------ - -PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) - : DPIDialog(NULL, wxID_ANY, _L("Physical Printer"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), -1), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) -{ - SetFont(wxGetApp().normal_font()); - SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - - m_default_name = _L("My Printer Device"); - - if (printer_name.IsEmpty()) - printer_name = m_default_name; - else { - std::string full_name = into_u8(printer_name); - printer_name = from_u8(PhysicalPrinter::get_short_name(full_name)); - } - - wxStaticText* label_top = new wxStaticText(this, wxID_ANY, _L("Descriptive name for the printer device") + ":"); - - m_add_preset_btn = new ScalableButton(this, wxID_ANY, "add_copies", "", wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT); - m_add_preset_btn->SetFont(wxGetApp().normal_font()); - m_add_preset_btn->SetToolTip(_L("Add preset for this printer device")); - m_add_preset_btn->Bind(wxEVT_BUTTON, &PhysicalPrinterDialog::AddPreset, this); - - m_printer_name = new wxTextCtrl(this, wxID_ANY, printer_name, wxDefaultPosition, wxDefaultSize); - m_printer_name->Bind(wxEVT_TEXT, [this](wxEvent&) { this->update_full_printer_names(); }); - - PhysicalPrinterCollection& printers = wxGetApp().preset_bundle->physical_printers; - PhysicalPrinter* printer = printers.find_printer(into_u8(printer_name)); - if (!printer) { - const Preset& preset = wxGetApp().preset_bundle->printers.get_edited_preset(); - printer = new PhysicalPrinter(into_u8(printer_name), preset); - // if printer_name is empty it means that new printer is created, so enable all items in the preset list - m_presets.emplace_back(new PresetForPrinter(this, preset.name)); - } - else - { - const std::set& preset_names = printer->get_preset_names(); - for (const std::string& preset_name : preset_names) - m_presets.emplace_back(new PresetForPrinter(this, preset_name)); - } - assert(printer); - m_printer = *printer; - - if (m_presets.size() == 1) - m_presets.front()->DisableDeleteBtn(); - - update_full_printer_names(); - - m_config = &m_printer.config; - - m_optgroup = new ConfigOptionsGroup(this, _L("Print Host upload"), m_config); - build_printhost_settings(m_optgroup); - //m_optgroup->reload_config(); - - wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); - wxButton* btnOK = static_cast(this->FindWindowById(wxID_OK, this)); - btnOK->Bind(wxEVT_BUTTON, &PhysicalPrinterDialog::OnOK, this); - - wxBoxSizer* nameSizer = new wxBoxSizer(wxHORIZONTAL); - nameSizer->Add(m_printer_name, 1, wxEXPAND); - nameSizer->Add(m_add_preset_btn, 0, wxEXPAND | wxLEFT, BORDER_W); - - m_presets_sizer = new wxBoxSizer(wxVERTICAL); - for (PresetForPrinter* preset : m_presets) - m_presets_sizer->Add(preset->sizer(), 1, wxEXPAND | wxTOP, BORDER_W); - - wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - - topSizer->Add(label_top , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W); - topSizer->Add(nameSizer , 0, wxEXPAND | wxLEFT | wxRIGHT, BORDER_W); - topSizer->Add(m_presets_sizer , 0, wxEXPAND | wxLEFT | wxRIGHT, BORDER_W); - topSizer->Add(m_optgroup->sizer , 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W); - topSizer->Add(btns , 0, wxEXPAND | wxALL, BORDER_W); - - SetSizer(topSizer); - topSizer->SetSizeHints(this); -} - -PhysicalPrinterDialog::~PhysicalPrinterDialog() -{ - for (PresetForPrinter* preset : m_presets) { - delete preset; - preset = nullptr; - } -} - -void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgroup) -{ - m_optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { - if (opt_key == "authorization_type") - this->update(); - }; - - m_optgroup->append_single_option_line("host_type"); - - auto create_sizer_with_btn = [this](wxWindow* parent, ScalableButton** btn, const std::string& icon_name, const wxString& label) { - *btn = new ScalableButton(parent, wxID_ANY, icon_name, label, wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT); - (*btn)->SetFont(wxGetApp().normal_font()); - - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(*btn); - return sizer; - }; - - auto printhost_browse = [=](wxWindow* parent) - { - auto sizer = create_sizer_with_btn(parent, &m_printhost_browse_btn, "browse", _L("Browse") + " " + dots); - m_printhost_browse_btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent& e) { - BonjourDialog dialog(this, Preset::printer_technology(m_printer.config)); - if (dialog.show_and_lookup()) { - m_optgroup->set_value("print_host", std::move(dialog.get_selected()), true); - m_optgroup->get_field("print_host")->field_changed(); - } - }); - - return sizer; - }; - - auto print_host_test = [=](wxWindow* parent) { - auto sizer = create_sizer_with_btn(parent, &m_printhost_test_btn, "test", _L("Test")); - - m_printhost_test_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) { - std::unique_ptr host(PrintHost::get_print_host(m_config)); - if (!host) { - const wxString text = _L("Could not get a valid Printer Host reference"); - show_error(this, text); - return; - } - wxString msg; - if (host->test(msg)) { - show_info(this, host->get_test_ok_msg(), _L("Success!")); - } - else { - show_error(this, host->get_test_failed_msg(msg)); - } - }); - - return sizer; - }; - - // Set a wider width for a better alignment - Option option = m_optgroup->get_option("print_host"); - option.opt.width = Field::def_width_wider(); - Line host_line = m_optgroup->create_single_option_line(option); - host_line.append_widget(printhost_browse); - host_line.append_widget(print_host_test); - m_optgroup->append_line(host_line); - - m_optgroup->append_single_option_line("authorization_type"); - - option = m_optgroup->get_option("printhost_apikey"); - option.opt.width = Field::def_width_wider(); - m_optgroup->append_single_option_line(option); - - const auto ca_file_hint = _u8L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate."); - - if (Http::ca_file_supported()) { - option = m_optgroup->get_option("printhost_cafile"); - option.opt.width = Field::def_width_wider(); - Line cafile_line = m_optgroup->create_single_option_line(option); - - auto printhost_cafile_browse = [=](wxWindow* parent) { - auto sizer = create_sizer_with_btn(parent, &m_printhost_cafile_browse_btn, "browse", _L("Browse") + " " + dots); - m_printhost_cafile_browse_btn->Bind(wxEVT_BUTTON, [this, m_optgroup](wxCommandEvent e) { - static const auto filemasks = _L("Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*"); - wxFileDialog openFileDialog(this, _L("Open CA certificate file"), "", "", filemasks, wxFD_OPEN | wxFD_FILE_MUST_EXIST); - if (openFileDialog.ShowModal() != wxID_CANCEL) { - m_optgroup->set_value("printhost_cafile", std::move(openFileDialog.GetPath()), true); - m_optgroup->get_field("printhost_cafile")->field_changed(); - } - }); - - return sizer; - }; - - cafile_line.append_widget(printhost_cafile_browse); - m_optgroup->append_line(cafile_line); - - Line cafile_hint{ "", "" }; - cafile_hint.full_width = 1; - cafile_hint.widget = [this, ca_file_hint](wxWindow* parent) { - auto txt = new wxStaticText(parent, wxID_ANY, ca_file_hint); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(txt); - return sizer; - }; - m_optgroup->append_line(cafile_hint); - } - else { - Line line{ "", "" }; - line.full_width = 1; - - line.widget = [ca_file_hint](wxWindow* parent) { - std::string info = _u8L("HTTPS CA File") + ":\n\t" + - (boost::format(_u8L("On this system, %s uses HTTPS certificates from the system Certificate Store or Keychain.")) % SLIC3R_APP_NAME).str() + - "\n\t" + _u8L("To use a custom CA file, please import your CA file into Certificate Store / Keychain."); - - //auto txt = new wxStaticText(parent, wxID_ANY, from_u8((boost::format("%1%\n\n\t%2%") % info % ca_file_hint).str())); - auto txt = new wxStaticText(parent, wxID_ANY, from_u8((boost::format("%1%\n\t%2%") % info % ca_file_hint).str())); - txt->SetFont(wxGetApp().normal_font()); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(txt, 1, wxEXPAND); - return sizer; - }; - - m_optgroup->append_line(line); - } - - for (const std::string& opt_key : std::vector{ "login", "password" }) { - option = m_optgroup->get_option(opt_key); - option.opt.width = Field::def_width_wider(); - m_optgroup->append_single_option_line(option); - } - - update(); -} - -void PhysicalPrinterDialog::update() -{ - m_optgroup->reload_config(); - - const PrinterTechnology tech = Preset::printer_technology(m_printer.config); - // Only offer the host type selection for FFF, for SLA it's always the SL1 printer (at the moment) - if (tech == ptFFF) { - m_optgroup->show_field("host_type"); - m_optgroup->hide_field("authorization_type"); - for (const std::string& opt_key : std::vector{ "login", "password" }) - m_optgroup->hide_field(opt_key); - } - else { - m_optgroup->set_value("host_type", int(PrintHostType::htOctoPrint), false); - m_optgroup->hide_field("host_type"); - - m_optgroup->show_field("authorization_type"); - - AuthorizationType auth_type = m_config->option>("authorization_type")->value; - m_optgroup->show_field("printhost_apikey", auth_type == AuthorizationType::atKeyPassword); - - for (const std::string& opt_key : std::vector{ "login", "password" }) - m_optgroup->show_field(opt_key, auth_type == AuthorizationType::atUserPassword); - } - - this->Layout(); -} - - -wxString PhysicalPrinterDialog::get_printer_name() -{ - return m_printer_name->GetValue(); -} - -void PhysicalPrinterDialog::update_full_printer_names() -{ - for (PresetForPrinter* preset : m_presets) - preset->update_full_printer_name(); - - this->Layout(); -} - -void PhysicalPrinterDialog::on_dpi_changed(const wxRect& suggested_rect) -{ - const int& em = em_unit(); - - m_printhost_browse_btn->msw_rescale(); - m_printhost_test_btn->msw_rescale(); - if (m_printhost_cafile_browse_btn) - m_printhost_cafile_browse_btn->msw_rescale(); - - m_optgroup->msw_rescale(); - - msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL }); - - for (PresetForPrinter* preset : m_presets) - preset->msw_rescale(); - - const wxSize& size = wxSize(45 * em, 35 * em); - SetMinSize(size); - - Fit(); - Refresh(); -} - -void PhysicalPrinterDialog::OnOK(wxEvent& event) -{ - wxString printer_name = m_printer_name->GetValue(); - if (printer_name.IsEmpty()) { - warning_catcher(this, _L("The supplied name is empty. It can't be saved.")); - return; - } - if (printer_name == m_default_name) { - warning_catcher(this, _L("You should to change a name of your printer device. It can't be saved.")); - return; - } - - PhysicalPrinterCollection& printers = wxGetApp().preset_bundle->physical_printers; - const PhysicalPrinter* existing = printers.find_printer(into_u8(printer_name)); - if (existing && into_u8(printer_name) != printers.get_selected_printer_name()) - { - wxString msg_text = from_u8((boost::format(_u8L("Printer with name \"%1%\" already exists.")) % printer_name).str()); - msg_text += "\n" + _L("Replace?"); - wxMessageDialog dialog(nullptr, msg_text, _L("Warning"), wxICON_WARNING | wxYES | wxNO); - - if (dialog.ShowModal() == wxID_NO) - return; - } - - std::set repeat_presets; - m_printer.reset_presets(); - for (PresetForPrinter* preset : m_presets) { - if (!m_printer.add_preset(preset->get_preset_name())) - repeat_presets.emplace(preset->get_preset_name()); - } - - if (!repeat_presets.empty()) - { - wxString repeatable_presets = "\n"; - for (const std::string& preset_name : repeat_presets) - repeatable_presets += " " + from_u8(preset_name) + "\n"; - repeatable_presets += "\n"; - - wxString msg_text = from_u8((boost::format(_u8L("Next printer preset(s) is(are) duplicated:%1%" - "Should I add it(they) just once for the printer \"%2%\" and close the Editing Dialog?")) % repeatable_presets % printer_name).str()); - wxMessageDialog dialog(nullptr, msg_text, _L("Warning"), wxICON_WARNING | wxYES | wxNO); - if (dialog.ShowModal() == wxID_NO) - return; - } - - std::string renamed_from; - // temporary save previous printer name if it was edited - if (m_printer.name != _u8L("My Printer Device") && - m_printer.name != into_u8(printer_name)) - renamed_from = m_printer.name; - - //update printer name, if it was changed - m_printer.set_name(into_u8(printer_name)); - - // save new physical printer - printers.save_printer(m_printer, renamed_from); - - if (m_printer.preset_names.find(printers.get_selected_printer_preset_name()) == m_printer.preset_names.end()) { - // select first preset for this printer - printers.select_printer(m_printer); - // refresh preset list on Printer Settings Tab - wxGetApp().get_tab(Preset::TYPE_PRINTER)->select_preset(printers.get_selected_printer_preset_name()); - } - - event.Skip(); -} - -void PhysicalPrinterDialog::AddPreset(wxEvent& event) -{ - m_presets.emplace_back(new PresetForPrinter(this)); - // enable DELETE button for the first preset, if was disabled - m_presets.front()->EnableDeleteBtn(); - - m_presets_sizer->Add(m_presets.back()->sizer(), 1, wxEXPAND | wxTOP, BORDER_W); - update_full_printer_names(); - - this->Fit(); -} - -void PhysicalPrinterDialog::DeletePreset(PresetForPrinter* preset_for_printer) -{ - if (m_presets.size() == 1) { - wxString msg_text = _L("It's not possible to delete last related preset for the printer."); - wxMessageDialog dialog(nullptr, msg_text, _L("Infornation"), wxICON_INFORMATION | wxOK); - dialog.ShowModal(); - return; - } - - assert(preset_for_printer); - auto it = std::find(m_presets.begin(), m_presets.end(), preset_for_printer); - if (it == m_presets.end()) - return; - - const int remove_id = it - m_presets.begin(); - m_presets_sizer->Remove(remove_id); - delete preset_for_printer; - m_presets.erase(it); - - if (m_presets.size() == 1) - m_presets.front()->DisableDeleteBtn(); - - this->Layout(); - this->Fit(); -} - - //----------------------------------------------- // ChangePresetForPhysicalPrinterDialog //----------------------------------------------- @@ -1548,9 +1073,9 @@ ChangePresetForPhysicalPrinterDialog::ChangePresetForPhysicalPrinterDialog(const wxStaticText* label_top = new wxStaticText(this, wxID_ANY, msg_text); label_top->SetFont(wxGetApp().bold_font()); - wxString choices[] = { from_u8((boost::format(_u8L("Just switch to \"%1%\"")) % preset_name).str()), - from_u8((boost::format(_u8L("Change \"%1%\" to \"%2%\" for this physical printer")) % old_preset_name % preset_name).str()), - from_u8((boost::format(_u8L("Add \"%1%\" as a next preset for the the physical printer")) % preset_name).str()) }; + wxString choices[] = { from_u8((boost::format(_u8L("Change \"%1%\" to \"%2%\" for this physical printer")) % old_preset_name % preset_name).str()), + from_u8((boost::format(_u8L("Add \"%1%\" as a next preset for the the physical printer")) % preset_name).str()), + from_u8((boost::format(_u8L("Just switch to \"%1%\"")) % preset_name).str()) }; wxRadioBox* selection_type_box = new wxRadioBox(this, wxID_ANY, _L("What would you like to do?"), wxDefaultPosition, wxDefaultSize, WXSIZEOF(choices), choices, 3, wxRA_SPECIFY_ROWS); diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index 9a70818e15..6b8017f311 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -1,16 +1,12 @@ #ifndef slic3r_PresetComboBoxes_hpp_ #define slic3r_PresetComboBoxes_hpp_ -#include - -#include #include #include #include "libslic3r/Preset.hpp" #include "wxExtensions.hpp" #include "GUI_Utils.hpp" -//#include "BitmapCache.hpp" class wxString; class wxTextCtrl; @@ -23,6 +19,7 @@ namespace Slic3r { namespace GUI { class BitmapCache; + // --------------------------------- // *** PresetComboBox *** // --------------------------------- @@ -47,8 +44,9 @@ public: }; void set_label_marker(int item, LabelItemType label_item_type = LABEL_ITEM_MARKER); + bool set_printer_technology(PrinterTechnology pt); - void set_selection_changed_function(std::function sel_changed) { on_selection_changed = sel_changed; } + void set_selection_changed_function(std::function sel_changed) { on_selection_changed = sel_changed; } bool is_selected_physical_printer(); @@ -58,7 +56,7 @@ public: void update(const std::string& select_preset); - virtual void update(){}; + virtual void update(); virtual void msw_rescale(); protected: @@ -91,6 +89,8 @@ protected: int thin_space_icon_width; int wide_space_icon_width; + PrinterTechnology printer_technology {ptAny}; + void invalidate_selection(); void validate_selection(bool predicate = false); void update_selection(); @@ -188,83 +188,6 @@ public: }; -//------------------------------------------ -// PresetForPrinter -//------------------------------------------ -static std::string g_info_string = " (modified)"; -class PhysicalPrinterDialog; -class PresetForPrinter -{ - PhysicalPrinterDialog* m_parent { nullptr }; - - PresetComboBox* m_presets_list { nullptr }; - ScalableButton* m_delete_preset_btn { nullptr }; - wxStaticText* m_info_line { nullptr }; - wxStaticText* m_full_printer_name { nullptr }; - - wxBoxSizer* m_sizer { nullptr }; - - void DeletePreset(wxEvent& event); - -public: - PresetForPrinter(PhysicalPrinterDialog* parent, const std::string& preset_name = ""); - ~PresetForPrinter(); - - wxBoxSizer* sizer() { return m_sizer; } - void update_full_printer_name(); - std::string get_preset_name(); - void DisableDeleteBtn(); - void EnableDeleteBtn(); - - void msw_rescale(); - void on_sys_color_changed() {}; -}; - - -//------------------------------------------ -// PhysicalPrinterDialog -//------------------------------------------ - -class ConfigOptionsGroup; -class PhysicalPrinterDialog : public DPIDialog -{ - PhysicalPrinter m_printer; - wxString m_default_name; - DynamicPrintConfig* m_config { nullptr }; - - wxTextCtrl* m_printer_name { nullptr }; - std::vector m_presets; - - ConfigOptionsGroup* m_optgroup { nullptr }; - - ScalableButton* m_add_preset_btn {nullptr}; - ScalableButton* m_printhost_browse_btn {nullptr}; - ScalableButton* m_printhost_test_btn {nullptr}; - ScalableButton* m_printhost_cafile_browse_btn {nullptr}; - - wxBoxSizer* m_presets_sizer {nullptr}; - - void build_printhost_settings(ConfigOptionsGroup* optgroup); - void OnOK(wxEvent& event); - void AddPreset(wxEvent& event); - -public: - PhysicalPrinterDialog(wxString printer_name); - ~PhysicalPrinterDialog(); - - void update(); - wxString get_printer_name(); - void update_full_printer_names(); - PhysicalPrinter* get_printer() {return &m_printer; } - - void DeletePreset(PresetForPrinter* preset_for_printer); - -protected: - void on_dpi_changed(const wxRect& suggested_rect) override; - void on_sys_color_changed() override {}; -}; - - //------------------------------------------------ // ChangePresetForPhysicalPrinterDialog //------------------------------------------------ From 953d1417a0a751ffcb9fc5c93d9e42a203a0cda4 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 11 Jun 2020 13:09:34 +0200 Subject: [PATCH 190/503] TriangleSelector: draft of interface --- src/libslic3r/Model.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 66 ++++++++++++++++++ src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp | 71 ++++++++++++++++++++ 3 files changed, 138 insertions(+) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index e5930fb8ac..be298ae4bb 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -394,6 +394,7 @@ enum class ModelVolumeType : int { }; enum class FacetSupportType : int8_t { + // Maximum is 3. The value is serialized in TriangleSelector into 2 bits! NONE = 0, ENFORCER = 1, BLOCKER = 2 diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index cd42857247..00e236b64b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -805,6 +805,10 @@ void GLGizmoFdmSupports::on_set_state() activate_internal_undo_redo_stack(true); }); } + + TriangleSelector ts{TriangleMesh()}; + ts.test(); + } if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off // we are actually shutting down @@ -854,5 +858,67 @@ void GLGizmoFdmSupports::on_save(cereal::BinaryOutputArchive&) const +void TriangleSelector::test() +{ + DivisionNode node; + while (true) { + std::cout << "Zadej pocet stran a spec stranu: "; + int num; + int spec; + std::cin >> num >> spec; + node.set_division(num, spec); + std::cout << node.number_of_split_sides() << " " + << (node.number_of_split_sides()==1 ? node.side_to_split() : node.side_to_keep()) + << std::endl << std::endl; + } +} + + +void TriangleSelector::DivisionNode::set_division(int sides_to_split, int special_side_idx) +{ + assert(sides_to_split >=0 && sides_to_split <= 3); + assert(special_side_idx >=-1 && special_side_idx < 3); + + // If splitting one or two sides, second argument must be provided. + assert(sides_to_split != 1 || special_side_idx != -1); + assert(sides_to_split != 2 || special_side_idx != -1); + + division_type = sides_to_split | (special_side_idx != -1 ? (special_side_idx << 2) : 0 ); +} + + + +void TriangleSelector::DivisionNode::set_type(FacetSupportType type) +{ + // If this is not a leaf-node, this makes no sense and + // the bits are used for storing index of an edge. + assert(number_of_split_sides() == 0); + division_type = type | (type << 2); +} + + + +int TriangleSelector::DivisionNode::side_to_keep() const +{ + assert(number_of_split_sides() == 2); + return (division_type & 0b1100) >> 2; +} + + + +int TriangleSelector::DivisionNode::side_to_split() const +{ + assert(number_of_split_sides() == 1); + return (division_type & 0b1100) >> 2; +} + + + +FacetSupportType TriangleSelector::DivisionNode::get_type() const +{ + assert(number_of_split_sides() == 0); // this must be leaf + return (division_type & 0b1100) >> 2; +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index c4f5b153ec..f815a80633 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -19,6 +19,77 @@ namespace GUI { enum class SLAGizmoEventType : unsigned char; class ClippingPlane; + +// Following class holds information about selected triangles. It also has power +// to recursively subdivide the triangles and make the selection finer. +class TriangleSelector { +public: + void test(); + explicit TriangleSelector(const TriangleMesh& mesh) {} + + // Select all triangles inside the circle, subdivide where needed. + void select_patch(const Vec3f& hit, // point where to start + int facet_idx, // facet that point belongs to + const Vec3f& dir, // direction of the ray + float radius_sqr, // squared radius of the cursor + bool enforcer); // enforcer or blocker? + + void unselect_all(); + + // Remove all unnecessary data (such as vertices that are not needed + // because the selection has been made larger. + void garbage_collect(); + +private: + // A struct to hold information about how a triangle was divided. + struct DivisionNode { + // Index of triangle this describes. + int triangle_idx; + + // Bitmask encoding which sides are split. + unsigned char division_type; + // bits 0 and 1 : 00 - no division + // 01 - one-edge split + // 10 - two-edge split + // 11 - three-edge split + // bits 2 and 3 : decimal 0, 1 or 2 identifying the special edge (one that + // splits in one-edge split or one that stays in two-edge split). + + // Pointers to children nodes (not all are always used). + std::array children; + + // Set the division type. + void set_division(int sides_to_split, int special_side_idx = -1); + + // Helpers that decode the division_type bitmask. + int number_of_split_sides() const { return division_type & 0b11; } + int side_to_keep() const; + int side_to_split() const; + }; + + // Triangle and pointer to how it's divided (nullptr = not divided). + // The ptr is nullptr for all new triangles, it is only valid for + // the original (undivided) triangles. + struct Triangle { + stl_triangle_vertex_indices verts_idxs; + DivisionNode* div_info; + }; + + // Lists of vertices and triangles, both original and new + std::vector m_vertices; + std::vector m_triangles; + + // Number of original vertices and triangles. + int m_orig_size_vertices; + int m_orig_size_indices; + + // Limits for stopping the recursion. + float m_max_edge_length; + int m_max_recursion_depth; +}; + + + class GLGizmoFdmSupports : public GLGizmoBase { private: From d2b2446b077fb90b7211cc4a2a3e20e1f02e7fdc Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 16 Jun 2020 16:10:26 +0200 Subject: [PATCH 191/503] TriangleSelector: first partially working implementation --- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 500 ++++++++++++++----- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp | 65 ++- 2 files changed, 422 insertions(+), 143 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 00e236b64b..1b045f38e2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -16,7 +16,6 @@ namespace Slic3r { namespace GUI { -static constexpr size_t MaxVertexBuffers = 50; GLGizmoFdmSupports::GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) @@ -90,12 +89,13 @@ void GLGizmoFdmSupports::set_fdm_support_data(ModelObject* model_object, const S void GLGizmoFdmSupports::on_render() const { - const Selection& selection = m_parent.get_selection(); + //const Selection& selection = m_parent.get_selection(); glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_DEPTH_TEST)); - render_triangles(selection); + //render_triangles(selection); + m_triangle_selector->render(); m_c->object_clipper()->render_cut(); render_cursor_circle(); @@ -146,13 +146,13 @@ void GLGizmoFdmSupports::render_triangles(const Selection& selection) const glsafe(::glMultMatrixd(trafo_matrix.data())); // Now render both enforcers and blockers. - for (int i=0; i<2; ++i) { - glsafe(::glColor4f(i ? 1.f : 0.2f, 0.2f, i ? 0.2f : 1.0f, 0.5f)); - for (const GLIndexedVertexArray& iva : m_ivas[mesh_id][i]) { - if (iva.has_VBOs()) - iva.render(); - } - } + //for (int i=0; i<2; ++i) { + // glsafe(::glColor4f(i ? 1.f : 0.2f, 0.2f, i ? 0.2f : 1.0f, 0.5f)); + // for (const GLIndexedVertexArray& iva : m_ivas[mesh_id][i]) { + if (m_iva.has_VBOs()) + m_iva.render(); + // } + //} glsafe(::glPopMatrix()); if (is_left_handed) glsafe(::glFrontFace(GL_CCW)); @@ -209,7 +209,8 @@ void GLGizmoFdmSupports::render_cursor_circle() const void GLGizmoFdmSupports::update_model_object() const { - ModelObject* mo = m_c->selection_info()->model_object(); + return; + /*ModelObject* mo = m_c->selection_info()->model_object(); int idx = -1; for (ModelVolume* mv : mo->volumes) { ++idx; @@ -217,7 +218,7 @@ void GLGizmoFdmSupports::update_model_object() const continue; for (int i=0; im_supported_facets.set_facet(i, m_selected_facets[idx][i]); - } + }*/ } @@ -226,13 +227,15 @@ void GLGizmoFdmSupports::update_from_model_object() wxBusyCursor wait; const ModelObject* mo = m_c->selection_info()->model_object(); - size_t num_of_volumes = 0; + /*size_t num_of_volumes = 0; for (const ModelVolume* mv : mo->volumes) if (mv->is_model_part()) ++num_of_volumes; - m_selected_facets.resize(num_of_volumes); + m_selected_facets.resize(num_of_volumes);*/ - m_ivas.clear(); + m_triangle_selector = std::make_unique(mo->volumes.front()->mesh()); + + /*m_ivas.clear(); m_ivas.resize(num_of_volumes); for (size_t i=0; ivolumes) { if (! mv->is_model_part()) continue; ++mesh_id; if (mesh_id == closest_hit_mesh_id) { - mesh = &mv->mesh(); + //mesh = &mv->mesh(); break; } } - bool update_both = false; + // FIXME: just for now, only process first mesh + if (mesh_id != 0) + return false; const Transform3d& trafo_matrix = trafo_matrices[mesh_id]; @@ -426,80 +435,10 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous const float avg_scaling = (sf(0) + sf(1) + sf(2))/3.; const float limit = pow(m_cursor_radius/avg_scaling , 2.f); - const std::pair& hit_and_facet = { closest_hit, closest_facet }; - // Calculate direction from camera to the hit (in mesh coords): - Vec3f dir = ((trafo_matrix.inverse() * camera.get_position()).cast() - hit_and_facet.first).normalized(); + Vec3f dir = ((trafo_matrix.inverse() * camera.get_position()).cast() - closest_hit).normalized(); - // A lambda to calculate distance from the centerline: - auto squared_distance_from_line = [&hit_and_facet, &dir](const Vec3f& point) -> float { - Vec3f diff = hit_and_facet.first - point; - return (diff - diff.dot(dir) * dir).squaredNorm(); - }; - - // A lambda to determine whether this facet is potentionally visible (still can be obscured) - auto faces_camera = [&dir, &mesh](const size_t& facet) -> bool { - return (mesh->stl.facet_start[facet].normal.dot(dir) > 0.); - }; - // Now start with the facet the pointer points to and check all adjacent facets. - std::vector facets_to_select{hit_and_facet.second}; - std::vector visited(m_selected_facets[mesh_id].size(), false); // keep track of facets we already processed - size_t facet_idx = 0; // index into facets_to_select - while (facet_idx < facets_to_select.size()) { - size_t facet = facets_to_select[facet_idx]; - if (! visited[facet]) { - // check all three vertices and in case they're close enough, - // add neighboring facets to be proccessed later - for (size_t i=0; i<3; ++i) { - float dist = squared_distance_from_line( - mesh->its.vertices[mesh->its.indices[facet](i)]); - if (dist < limit) { - for (int n=0; n<3; ++n) { - if (faces_camera(mesh->stl.neighbors_start[facet].neighbor[n])) - facets_to_select.push_back(mesh->stl.neighbors_start[facet].neighbor[n]); - } - } - } - visited[facet] = true; - } - ++facet_idx; - } - - std::vector new_facets; - new_facets.reserve(facets_to_select.size()); - - // Now just select all facets that passed and remember which - // ones have really changed state. - for (size_t next_facet : facets_to_select) { - FacetSupportType& facet = m_selected_facets[mesh_id][next_facet]; - - if (facet != new_state) { - if (facet != FacetSupportType::NONE) { - // this triangle is currently in the other VBA. - // Both VBAs need to be refreshed. - update_both = true; - } - facet = new_state; - new_facets.push_back(next_facet); - } - } - - if (! new_facets.empty()) { - if (new_state != FacetSupportType::NONE) { - // append triangles into the respective VBA - update_vertex_buffers(mesh, mesh_id, new_state, &new_facets); - if (update_both) { - auto other = new_state == FacetSupportType::ENFORCER - ? FacetSupportType::BLOCKER - : FacetSupportType::ENFORCER; - update_vertex_buffers(mesh, mesh_id, other); // regenerate the other VBA - } - } - else { - update_vertex_buffers(mesh, mesh_id, FacetSupportType::ENFORCER); - update_vertex_buffers(mesh, mesh_id, FacetSupportType::BLOCKER); - } - } + m_triangle_selector->select_patch(closest_hit, closest_facet, dir, limit, new_state); return true; } @@ -529,7 +468,7 @@ void GLGizmoFdmSupports::update_vertex_buffers(const TriangleMesh* mesh, FacetSupportType type, const std::vector* new_facets) { - std::vector& ivas = m_ivas[mesh_id][type == FacetSupportType::ENFORCER ? 0 : 1]; + //std::vector& ivas = m_ivas[mesh_id][type == FacetSupportType::ENFORCER ? 0 : 1]; // lambda to push facet into vertex buffer auto push_facet = [this, &mesh, &mesh_id](size_t idx, GLIndexedVertexArray& iva) { @@ -543,24 +482,26 @@ void GLGizmoFdmSupports::update_vertex_buffers(const TriangleMesh* mesh, }; - if (ivas.size() == MaxVertexBuffers || ! new_facets) { + //if (ivas.size() == MaxVertexBuffers || ! new_facets) { // If there are too many or they should be regenerated, make one large // GLVertexBufferArray. - ivas.clear(); // destructors release geometry - ivas.push_back(GLIndexedVertexArray()); + //ivas.clear(); // destructors release geometry + //ivas.push_back(GLIndexedVertexArray()); + + m_iva.release_geometry(); + m_iva.clear(); bool pushed = false; for (size_t facet_idx=0; facet_idxselection_info()->model_object(); @@ -615,6 +558,7 @@ void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool overwr update_model_object(); m_parent.set_as_dirty(); m_setting_angle = false; +*/ } @@ -670,7 +614,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l ImGui::SameLine(); if (m_imgui->button(m_desc.at("remove_all"))) { - ModelObject* mo = m_c->selection_info()->model_object(); + /*ModelObject* mo = m_c->selection_info()->model_object(); int idx = -1; for (ModelVolume* mv : mo->volumes) { ++idx; @@ -681,7 +625,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l update_vertex_buffers(&mv->mesh(), idx, FacetSupportType::BLOCKER); m_parent.set_as_dirty(); } - } + }*/ } const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; @@ -805,10 +749,6 @@ void GLGizmoFdmSupports::on_set_state() activate_internal_undo_redo_stack(true); }); } - - TriangleSelector ts{TriangleMesh()}; - ts.test(); - } if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off // we are actually shutting down @@ -818,7 +758,7 @@ void GLGizmoFdmSupports::on_set_state() } activate_internal_undo_redo_stack(false); m_old_mo_id = -1; - m_ivas.clear(); + m_iva.release_geometry(); m_selected_facets.clear(); } m_old_state = m_state; @@ -858,22 +798,6 @@ void GLGizmoFdmSupports::on_save(cereal::BinaryOutputArchive&) const -void TriangleSelector::test() -{ - DivisionNode node; - while (true) { - std::cout << "Zadej pocet stran a spec stranu: "; - int num; - int spec; - std::cin >> num >> spec; - node.set_division(num, spec); - std::cout << node.number_of_split_sides() << " " - << (node.number_of_split_sides()==1 ? node.side_to_split() : node.side_to_keep()) - << std::endl << std::endl; - } -} - - void TriangleSelector::DivisionNode::set_division(int sides_to_split, int special_side_idx) { assert(sides_to_split >=0 && sides_to_split <= 3); @@ -883,17 +807,17 @@ void TriangleSelector::DivisionNode::set_division(int sides_to_split, int specia assert(sides_to_split != 1 || special_side_idx != -1); assert(sides_to_split != 2 || special_side_idx != -1); - division_type = sides_to_split | (special_side_idx != -1 ? (special_side_idx << 2) : 0 ); + division_type = sides_to_split | ((special_side_idx != -1 ? special_side_idx : 0 ) <<2); } -void TriangleSelector::DivisionNode::set_type(FacetSupportType type) +void TriangleSelector::DivisionNode::set_state(FacetSupportType type) { // If this is not a leaf-node, this makes no sense and // the bits are used for storing index of an edge. assert(number_of_split_sides() == 0); - division_type = type | (type << 2); + division_type = (int8_t(type) << 2); } @@ -914,10 +838,326 @@ int TriangleSelector::DivisionNode::side_to_split() const -FacetSupportType TriangleSelector::DivisionNode::get_type() const +FacetSupportType TriangleSelector::DivisionNode::get_state() const { assert(number_of_split_sides() == 0); // this must be leaf - return (division_type & 0b1100) >> 2; + return FacetSupportType((division_type & 0b1100) >> 2); +} + + + +void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, const Vec3f& dir, + float radius_sqr, FacetSupportType new_state) +{ + assert(facet_start < m_orig_size_indices); + + // Save current cursor center, squared radius and camera direction, + // so we don't have to pass it around. + m_cursor = {hit, dir, radius_sqr}; + + // Now start with the facet the pointer points to and check all adjacent facets. + std::vector facets_to_check{facet_start}; + std::vector visited(m_orig_size_indices, false); // keep track of facets we already processed + int facet_idx = 0; // index into facets_to_check + while (facet_idx < int(facets_to_check.size())) { + int facet = facets_to_check[facet_idx]; + if (! visited[facet]) { + int num_of_inside_vertices = vertices_inside(facet); + // select the facet... + select_triangle(facet, new_state, num_of_inside_vertices, facet == facet_start); + + // ...and add neighboring facets to be proccessed later + for (int n=0; n<3; ++n) { + if (faces_camera(m_mesh->stl.neighbors_start[facet].neighbor[n])) + facets_to_check.push_back(m_mesh->stl.neighbors_start[facet].neighbor[n]); + } + } + visited[facet] = true; + ++facet_idx; + } +} + + + +// Selects either the whole triangle (discarding any children it has), or divides +// the triangle recursively, selecting just subtriangles truly inside the circle. +// This is done by an actual recursive call. +void TriangleSelector::select_triangle(int facet_idx, FacetSupportType type, + int num_of_inside_vertices, bool cursor_inside) +{ + assert(facet_idx < m_triangles.size()); +//cursor_inside=false; + if (num_of_inside_vertices == -1) + num_of_inside_vertices = vertices_inside(facet_idx); + + if (num_of_inside_vertices == 0 && ! cursor_inside) + return; // FIXME: just an edge can be inside + + if (num_of_inside_vertices == 3) { + // dump any subdivision and select whole triangle + undivide_triangle(facet_idx); + m_triangles[facet_idx].div_info->set_state(type); + } else { + // the triangle is partially inside, let's recursively divide it + // (if not already) and try selecting its children. + split_triangle(facet_idx); + assert(facet_idx < m_triangles.size()); + int num_of_children = m_triangles[facet_idx].div_info->number_of_split_sides() + 1; + if (num_of_children != 1) { + for (int i=0; ichildren[i], type, -1, cursor_inside); + } + } + } + //if (m_triangles[facet_idx].div_info->number_of_split_sides() != 0) + // remove_needless(m_triangles[facet_idx].div_info->children[0]); +} + + +bool TriangleSelector::split_triangle(int facet_idx) +{ + if (m_triangles[facet_idx].div_info->number_of_split_sides() != 0) { + // The triangle was divided already. + return 0; + } + + FacetSupportType old_type = m_triangles[facet_idx].div_info->get_state(); + + const double limit_squared = 4; + + stl_triangle_vertex_indices& facet = m_triangles[facet_idx].verts_idxs; + const stl_vertex* pts[3] = { &m_vertices[facet[0]], &m_vertices[facet[1]], &m_vertices[facet[2]]}; + double sides[3] = { (*pts[2]-*pts[1]).squaredNorm(), (*pts[0]-*pts[2]).squaredNorm(), (*pts[1]-*pts[0]).squaredNorm() }; + + std::vector sides_to_split; + int side_to_keep = -1; + for (int pt_idx = 0; pt_idx<3; ++pt_idx) { + if (sides[pt_idx] > limit_squared) + sides_to_split.push_back(pt_idx); + else + side_to_keep = pt_idx; + } + if (sides_to_split.empty()) { + m_triangles[facet_idx].div_info->set_division(0); + return 0; + } + + // indices of triangle vertices + std::vector verts_idxs; + int idx = sides_to_split.size() == 2 ? side_to_keep : sides_to_split[0]; + for (int j=0; j<3; ++j) { + verts_idxs.push_back(facet[idx++]); + if (idx == 3) + idx = 0; + } + + + if (sides_to_split.size() == 1) { + m_vertices.emplace_back((m_vertices[verts_idxs[1]] + m_vertices[verts_idxs[2]])/2.); + verts_idxs.insert(verts_idxs.begin()+2, m_vertices.size() - 1); + + m_triangles.emplace_back(verts_idxs[0], verts_idxs[1], verts_idxs[2]); + m_triangles.emplace_back(verts_idxs[2], verts_idxs[3], verts_idxs[0]); + } + + if (sides_to_split.size() == 2) { + m_vertices.emplace_back((m_vertices[verts_idxs[0]] + m_vertices[verts_idxs[1]])/2.); + verts_idxs.insert(verts_idxs.begin()+1, m_vertices.size() - 1); + + m_vertices.emplace_back((m_vertices[verts_idxs[0]] + m_vertices[verts_idxs[3]])/2.); + verts_idxs.insert(verts_idxs.begin()+4, m_vertices.size() - 1); + + m_triangles.emplace_back(verts_idxs[0], verts_idxs[1], verts_idxs[4]); + m_triangles.emplace_back(verts_idxs[1], verts_idxs[2], verts_idxs[4]); + m_triangles.emplace_back(verts_idxs[2], verts_idxs[3], verts_idxs[4]); + } + + if (sides_to_split.size() == 3) { + m_vertices.emplace_back((m_vertices[verts_idxs[0]] + m_vertices[verts_idxs[1]])/2.); + verts_idxs.insert(verts_idxs.begin()+1, m_vertices.size() - 1); + m_vertices.emplace_back((m_vertices[verts_idxs[2]] + m_vertices[verts_idxs[3]])/2.); + verts_idxs.insert(verts_idxs.begin()+3, m_vertices.size() - 1); + m_vertices.emplace_back((m_vertices[verts_idxs[4]] + m_vertices[verts_idxs[0]])/2.); + verts_idxs.insert(verts_idxs.begin()+5, m_vertices.size() - 1); + + m_triangles.emplace_back(verts_idxs[0], verts_idxs[1], verts_idxs[5]); + m_triangles.emplace_back(verts_idxs[1], verts_idxs[2], verts_idxs[3]); + m_triangles.emplace_back(verts_idxs[3], verts_idxs[4], verts_idxs[5]); + m_triangles.emplace_back(verts_idxs[1], verts_idxs[3], verts_idxs[5]); + } + + // Save how the triangle was split. Second argument makes sense only for one + // or two split sides, otherwise the value is ignored. + m_triangles[facet_idx].div_info->set_division(sides_to_split.size(), + sides_to_split.size() == 2 ? side_to_keep : sides_to_split[0]); + + // And save the children. All children should start in the same state as the triangle we just split. + assert(! sides_to_split.empty() && int(sides_to_split.size()) <= 3); + for (int i=0; i<=int(sides_to_split.size()); ++i) { + m_triangles[facet_idx].div_info->children[i] = m_triangles.size()-1-i; + m_triangles[m_triangles.size()-1-i].div_info->parent = facet_idx; + m_triangles[m_triangles[facet_idx].div_info->children[i]].div_info->set_state(old_type); + } + + +#ifndef NDEBUG + int split_sides = m_triangles[facet_idx].div_info->number_of_split_sides(); + if (split_sides != 0) { + // check that children are range + for (int i=0; i<=split_sides; ++i) + assert(m_triangles[facet_idx].div_info->children[i] >= 0 && m_triangles[facet_idx].div_info->children[i] < int(m_triangles.size())); + + } +#endif + + return 1; +} + + +// Calculate distance of a point from a line. +bool TriangleSelector::is_point_inside_cursor(const Vec3f& point) const +{ + Vec3f diff = m_cursor.center - point; + return (diff - diff.dot(m_cursor.dir) * m_cursor.dir).squaredNorm() < m_cursor.radius_sqr; +} + + + +// Determine whether this facet is potentially visible (still can be obscured). +bool TriangleSelector::faces_camera(int facet) const +{ + assert(facet < m_orig_size_indices); + // The normal is cached in mesh->stl, use it. + return (m_mesh->stl.facet_start[facet].normal.dot(m_cursor.dir) > 0.); +} + + + +// How many vertices of a triangle are inside the circle? +int TriangleSelector::vertices_inside(int facet_idx) const +{ + int inside = 0; + for (size_t i=0; i<3; ++i) { + if (is_point_inside_cursor(m_vertices[m_triangles[facet_idx].verts_idxs[i]])) + ++inside; + } + return inside; +} + + +// Is mouse pointer inside a triangle? +/*bool TriangleSelector::is_pointer_inside_triangle(int facet_idx) const +{ + +}*/ + + + +// Recursively remove all subtriangles. +void TriangleSelector::undivide_triangle(int facet_idx) +{ + assert(facet_idx < m_triangles.size()); + auto& dn_ptr = m_triangles[facet_idx].div_info; + assert(dn_ptr); + + if (dn_ptr->number_of_split_sides() != 0) { + for (int i=0; i<=dn_ptr->number_of_split_sides(); ++i) { + undivide_triangle(dn_ptr->children[i]); + m_triangles[dn_ptr->children[i]].div_info->valid = false; + } + } + + dn_ptr->set_division(0); // not split +} + + +void TriangleSelector::remove_needless(int child_facet) +{ + assert(m_triangles[child_facet].div_info->number_of_split_sides() == 0); + int parent = m_triangles[child_facet].div_info->parent; + if (parent == -1) + return; // root + // Check type of all valid children. + FacetSupportType type = m_triangles[m_triangles[parent].div_info->children[0]].div_info->get_state(); + for (int i=0; i<=m_triangles[parent].div_info->number_of_split_sides(); ++i) + if (m_triangles[m_triangles[parent].div_info->children[0]].div_info->get_state() != type) + return; // not all children are the same + + // All children are the same, let's kill them. + undivide_triangle(parent); + m_triangles[parent].div_info->set_state(type); + + // And not try the same for grandparent. + remove_needless(parent); +} + + +TriangleSelector::TriangleSelector(const TriangleMesh& mesh) +{ + for (const stl_vertex& vert : mesh.its.vertices) + m_vertices.push_back(vert); + for (const stl_triangle_vertex_indices& ind : mesh.its.indices) + m_triangles.emplace_back(Triangle(ind[0], ind[1], ind[2])); + m_orig_size_vertices = m_vertices.size(); + m_orig_size_indices = m_triangles.size(); + m_mesh = &mesh; +} + + +void TriangleSelector::render() const +{ + ::glColor3f(0.f, 0.f, 1.f); + ::glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); + + Vec3d offset = wxGetApp().model().objects.front()->instances.front()->get_transformation().get_offset(); + ::glTranslatef(offset.x(), offset.y(), offset.z()); + ::glScalef(1.01f, 1.01f, 1.01f); + + ::glBegin( GL_TRIANGLES); + + for (int tr_id=0; tr_idvalid) { + for (int i=0; i<3; ++i) + ::glVertex3f(m_vertices[tr.verts_idxs[i]][0], m_vertices[tr.verts_idxs[i]][1], m_vertices[tr.verts_idxs[i]][2]); + } + } + ::glEnd(); + ::glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + + ::glBegin( GL_TRIANGLES); + for (int tr_id=0; tr_idvalid) + continue; + + if (tr.div_info->number_of_split_sides() == 0) { + if (tr.div_info->get_state() == FacetSupportType::ENFORCER) + ::glColor4f(0.f, 0.f, 1.f, 0.2f); + else if (tr.div_info->get_state() == FacetSupportType::BLOCKER) + ::glColor4f(1.f, 0.f, 0.f, 0.2f); + else + continue; + } + else + continue; + + + if (tr.div_info->valid) { + for (int i=0; i<3; ++i) + ::glVertex3f(m_vertices[tr.verts_idxs[i]][0], m_vertices[tr.verts_idxs[i]][1], m_vertices[tr.verts_idxs[i]][2]); + } + } + ::glEnd(); +} + + +TriangleSelector::DivisionNode::DivisionNode() +{ + set_division(0); + set_state(FacetSupportType::NONE); } } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index f815a80633..07c2e86d52 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -24,15 +24,17 @@ class ClippingPlane; // to recursively subdivide the triangles and make the selection finer. class TriangleSelector { public: - void test(); - explicit TriangleSelector(const TriangleMesh& mesh) {} + void render() const; + // Create new object on a TriangleMesh. The referenced mesh must + // stay valid, a ptr to it is saved and used. + explicit TriangleSelector(const TriangleMesh& mesh); // Select all triangles inside the circle, subdivide where needed. void select_patch(const Vec3f& hit, // point where to start - int facet_idx, // facet that point belongs to + int facet_start, // facet that point belongs to const Vec3f& dir, // direction of the ray float radius_sqr, // squared radius of the cursor - bool enforcer); // enforcer or blocker? + FacetSupportType new_state); // enforcer or blocker? void unselect_all(); @@ -43,11 +45,15 @@ public: private: // A struct to hold information about how a triangle was divided. struct DivisionNode { + DivisionNode(); // Index of triangle this describes. - int triangle_idx; + bool valid{true}; + + // Index of parent triangle (-1: original) + int parent{-1}; // Bitmask encoding which sides are split. - unsigned char division_type; + int8_t division_type; // bits 0 and 1 : 00 - no division // 01 - one-edge split // 10 - two-edge split @@ -55,29 +61,37 @@ private: // bits 2 and 3 : decimal 0, 1 or 2 identifying the special edge (one that // splits in one-edge split or one that stays in two-edge split). - // Pointers to children nodes (not all are always used). - std::array children; + // Children triangles (0 = no child) + std::array children; // Set the division type. void set_division(int sides_to_split, int special_side_idx = -1); + void set_state(FacetSupportType state); // Helpers that decode the division_type bitmask. int number_of_split_sides() const { return division_type & 0b11; } int side_to_keep() const; int side_to_split() const; + + FacetSupportType get_state() const; }; // Triangle and pointer to how it's divided (nullptr = not divided). // The ptr is nullptr for all new triangles, it is only valid for // the original (undivided) triangles. struct Triangle { + Triangle(int a, int b, int c) + : verts_idxs{stl_triangle_vertex_indices(a, b, c)}, + div_info{std::make_unique()} + {} stl_triangle_vertex_indices verts_idxs; - DivisionNode* div_info; + std::unique_ptr div_info; }; // Lists of vertices and triangles, both original and new std::vector m_vertices; std::vector m_triangles; + const TriangleMesh* m_mesh; // Number of original vertices and triangles. int m_orig_size_vertices; @@ -86,6 +100,32 @@ private: // Limits for stopping the recursion. float m_max_edge_length; int m_max_recursion_depth; + + // Caches for cursor position, radius and direction. + struct Cursor { + Vec3f center; + Vec3f dir; + float radius_sqr; + }; + + Cursor m_cursor; + + // Private functions: + void select_triangle(int facet_idx, FacetSupportType type, + int num_of_inside_vertices = -1, + bool cursor_inside = false); + + bool is_point_inside_cursor(const Vec3f& point) const; + + int vertices_inside(int facet_idx) const; + + bool faces_camera(int facet) const; + + void undivide_triangle(int facet_idx); + + bool split_triangle(int facet_idx); + + void remove_needless(int child_facet); }; @@ -107,10 +147,7 @@ private: // individual facets (one of the enum values above). std::vector> m_selected_facets; - // Vertex buffer arrays for each model-part volume. There is a vector of - // arrays so that adding triangles can be done without regenerating all - // other triangles. Enforcers and blockers are of course separate. - std::vector, 2>> m_ivas; + GLIndexedVertexArray m_iva; void update_vertex_buffers(const TriangleMesh* mesh, int mesh_id, @@ -148,6 +185,8 @@ private: bool m_setting_angle = false; bool m_internal_stack_active = false; bool m_schedule_update = false; + + std::unique_ptr m_triangle_selector; // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. From c3db84e3821d7c94fb4f615cbc7bedf10695a59b Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 19 Jun 2020 09:20:58 +0200 Subject: [PATCH 192/503] TriangleSelector: Improvements --- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 254 ++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp | 70 ++--- 2 files changed, 176 insertions(+), 148 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 1b045f38e2..1c77f437c0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -436,9 +436,11 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous const float limit = pow(m_cursor_radius/avg_scaling , 2.f); // Calculate direction from camera to the hit (in mesh coords): - Vec3f dir = ((trafo_matrix.inverse() * camera.get_position()).cast() - closest_hit).normalized(); + Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast(); + Vec3f dir = (closest_hit - camera_pos).normalized(); - m_triangle_selector->select_patch(closest_hit, closest_facet, dir, limit, new_state); + m_triangle_selector->select_patch(closest_hit, closest_facet, camera_pos, + dir, limit, new_state); return true; } @@ -567,6 +569,12 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l if (! m_c->selection_info()->model_object()) return; + m_imgui->begin(std::string("TriangleSelector DEBUG"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); + static float edge_limit = 1.f; + m_imgui->slider_float("Edge limit (mm): ", &edge_limit, 0.1f, 8.f); + m_triangle_selector->set_edge_limit(edge_limit); + m_imgui->end(); + const float approx_height = m_imgui->scaled(18.0f); y = std::min(y, bottom_limit - approx_height); m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); @@ -798,7 +806,7 @@ void GLGizmoFdmSupports::on_save(cereal::BinaryOutputArchive&) const -void TriangleSelector::DivisionNode::set_division(int sides_to_split, int special_side_idx) +void TriangleSelector::Triangle::set_division(int sides_to_split, int special_side_idx) { assert(sides_to_split >=0 && sides_to_split <= 3); assert(special_side_idx >=-1 && special_side_idx < 3); @@ -812,48 +820,49 @@ void TriangleSelector::DivisionNode::set_division(int sides_to_split, int specia -void TriangleSelector::DivisionNode::set_state(FacetSupportType type) +void TriangleSelector::Triangle::set_state(FacetSupportType type) { // If this is not a leaf-node, this makes no sense and // the bits are used for storing index of an edge. - assert(number_of_split_sides() == 0); + assert(! is_split()); division_type = (int8_t(type) << 2); } -int TriangleSelector::DivisionNode::side_to_keep() const +int TriangleSelector::Triangle::side_to_keep() const { assert(number_of_split_sides() == 2); - return (division_type & 0b1100) >> 2; + return division_type >> 2; } -int TriangleSelector::DivisionNode::side_to_split() const +int TriangleSelector::Triangle::side_to_split() const { assert(number_of_split_sides() == 1); - return (division_type & 0b1100) >> 2; + return division_type >> 2; } -FacetSupportType TriangleSelector::DivisionNode::get_state() const +FacetSupportType TriangleSelector::Triangle::get_state() const { - assert(number_of_split_sides() == 0); // this must be leaf - return FacetSupportType((division_type & 0b1100) >> 2); + assert(! is_split()); // this must be leaf + return FacetSupportType(division_type >> 2); } -void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, const Vec3f& dir, +void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, + const Vec3f& source, const Vec3f& dir, float radius_sqr, FacetSupportType new_state) { assert(facet_start < m_orig_size_indices); // Save current cursor center, squared radius and camera direction, // so we don't have to pass it around. - m_cursor = {hit, dir, radius_sqr}; + m_cursor = {hit, source, dir, radius_sqr}; // Now start with the facet the pointer points to and check all adjacent facets. std::vector facets_to_check{facet_start}; @@ -862,14 +871,12 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, const Vec while (facet_idx < int(facets_to_check.size())) { int facet = facets_to_check[facet_idx]; if (! visited[facet]) { - int num_of_inside_vertices = vertices_inside(facet); - // select the facet... - select_triangle(facet, new_state, num_of_inside_vertices, facet == facet_start); - - // ...and add neighboring facets to be proccessed later - for (int n=0; n<3; ++n) { - if (faces_camera(m_mesh->stl.neighbors_start[facet].neighbor[n])) - facets_to_check.push_back(m_mesh->stl.neighbors_start[facet].neighbor[n]); + if (select_triangle(facet, new_state, facet == facet_start)) { + // add neighboring facets to list to be proccessed later + for (int n=0; n<3; ++n) { + if (faces_camera(m_mesh->stl.neighbors_start[facet].neighbor[n])) + facets_to_check.push_back(m_mesh->stl.neighbors_start[facet].neighbor[n]); + } } } visited[facet] = true; @@ -879,53 +886,71 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, const Vec -// Selects either the whole triangle (discarding any children it has), or divides +// Selects either the whole triangle (discarding any children it had), or divides // the triangle recursively, selecting just subtriangles truly inside the circle. -// This is done by an actual recursive call. -void TriangleSelector::select_triangle(int facet_idx, FacetSupportType type, - int num_of_inside_vertices, bool cursor_inside) +// This is done by an actual recursive call. Returns false if the triangle is +// outside the cursor. +bool TriangleSelector::select_triangle(int facet_idx, FacetSupportType type, bool cursor_inside) { - assert(facet_idx < m_triangles.size()); -//cursor_inside=false; - if (num_of_inside_vertices == -1) - num_of_inside_vertices = vertices_inside(facet_idx); + bool out = false; + assert(facet_idx < int(m_triangles.size())); + + Triangle& tr = m_triangles[facet_idx]; + if (! tr.valid) + return false; + + cursor_inside = is_pointer_in_triangle(facet_idx); + + int num_of_inside_vertices = vertices_inside(facet_idx); if (num_of_inside_vertices == 0 && ! cursor_inside) - return; // FIXME: just an edge can be inside + return out; // FIXME: just an edge can be inside if (num_of_inside_vertices == 3) { // dump any subdivision and select whole triangle undivide_triangle(facet_idx); - m_triangles[facet_idx].div_info->set_state(type); + tr.set_state(type); } else { // the triangle is partially inside, let's recursively divide it // (if not already) and try selecting its children. + + + if (! tr.is_split() && tr.get_state() == type) { + // This is leaf triangle that is already of correct type as a whole. + // No need to split, all children would end up selected anyway. + return true; + } + split_triangle(facet_idx); - assert(facet_idx < m_triangles.size()); - int num_of_children = m_triangles[facet_idx].div_info->number_of_split_sides() + 1; + assert(facet_idx < int(m_triangles.size())); + int num_of_children = tr.number_of_split_sides() + 1; if (num_of_children != 1) { - for (int i=0; ichildren[i], type, -1, cursor_inside); - } + for (int i=0; inumber_of_split_sides() != 0) - // remove_needless(m_triangles[facet_idx].div_info->children[0]); + + // In case that all siblings are leafs and have the same state now, + // they may be removed and substituted by the parent triangle. + //remove_if_needless(facet_idx); + return true; } bool TriangleSelector::split_triangle(int facet_idx) { - if (m_triangles[facet_idx].div_info->number_of_split_sides() != 0) { + if (m_triangles[facet_idx].is_split()) { // The triangle was divided already. - return 0; + return false; } - FacetSupportType old_type = m_triangles[facet_idx].div_info->get_state(); + Triangle& tr = m_triangles[facet_idx]; - const double limit_squared = 4; + FacetSupportType old_type = tr.get_state(); - stl_triangle_vertex_indices& facet = m_triangles[facet_idx].verts_idxs; + const double limit_squared = m_edge_limit_sqr; + + stl_triangle_vertex_indices& facet = tr.verts_idxs; const stl_vertex* pts[3] = { &m_vertices[facet[0]], &m_vertices[facet[1]], &m_vertices[facet[2]]}; double sides[3] = { (*pts[2]-*pts[1]).squaredNorm(), (*pts[0]-*pts[2]).squaredNorm(), (*pts[1]-*pts[0]).squaredNorm() }; @@ -938,8 +963,8 @@ bool TriangleSelector::split_triangle(int facet_idx) side_to_keep = pt_idx; } if (sides_to_split.empty()) { - m_triangles[facet_idx].div_info->set_division(0); - return 0; + tr.set_division(0); + return false; } // indices of triangle vertices @@ -988,29 +1013,18 @@ bool TriangleSelector::split_triangle(int facet_idx) // Save how the triangle was split. Second argument makes sense only for one // or two split sides, otherwise the value is ignored. - m_triangles[facet_idx].div_info->set_division(sides_to_split.size(), + tr.set_division(sides_to_split.size(), sides_to_split.size() == 2 ? side_to_keep : sides_to_split[0]); // And save the children. All children should start in the same state as the triangle we just split. assert(! sides_to_split.empty() && int(sides_to_split.size()) <= 3); for (int i=0; i<=int(sides_to_split.size()); ++i) { - m_triangles[facet_idx].div_info->children[i] = m_triangles.size()-1-i; - m_triangles[m_triangles.size()-1-i].div_info->parent = facet_idx; - m_triangles[m_triangles[facet_idx].div_info->children[i]].div_info->set_state(old_type); + tr.children[i] = m_triangles.size()-1-i; + m_triangles[tr.children[i]].parent = facet_idx; + m_triangles[tr.children[i]].set_state(old_type); } - -#ifndef NDEBUG - int split_sides = m_triangles[facet_idx].div_info->number_of_split_sides(); - if (split_sides != 0) { - // check that children are range - for (int i=0; i<=split_sides; ++i) - assert(m_triangles[facet_idx].div_info->children[i] >= 0 && m_triangles[facet_idx].div_info->children[i] < int(m_triangles.size())); - - } -#endif - - return 1; + return true; } @@ -1022,6 +1036,29 @@ bool TriangleSelector::is_point_inside_cursor(const Vec3f& point) const } +// Is pointer in a triangle? +bool TriangleSelector::is_pointer_in_triangle(int facet_idx) const +{ + auto signed_volume_sign = [](const Vec3f& a, const Vec3f& b, + const Vec3f& c, const Vec3f& d) -> bool { + return ((b-a).cross(c-a)).dot(d-a) > 0.; + }; + + const Vec3f& p1 = m_vertices[m_triangles[facet_idx].verts_idxs[0]]; + const Vec3f& p2 = m_vertices[m_triangles[facet_idx].verts_idxs[1]]; + const Vec3f& p3 = m_vertices[m_triangles[facet_idx].verts_idxs[2]]; + const Vec3f& q1 = m_cursor.center + m_cursor.dir; + const Vec3f q2 = m_cursor.center - m_cursor.dir; + + if (signed_volume_sign(q1,p1,p2,p3) != signed_volume_sign(q2,p1,p2,p3)) { + bool pos = signed_volume_sign(q1,q2,p1,p2); + if (signed_volume_sign(q1,q2,p2,p3) == pos && signed_volume_sign(q1,q2,p3,p1) == pos) + return true; + } + return false; +} + + // Determine whether this facet is potentially visible (still can be obscured). bool TriangleSelector::faces_camera(int facet) const @@ -1056,39 +1093,41 @@ int TriangleSelector::vertices_inside(int facet_idx) const // Recursively remove all subtriangles. void TriangleSelector::undivide_triangle(int facet_idx) { - assert(facet_idx < m_triangles.size()); - auto& dn_ptr = m_triangles[facet_idx].div_info; - assert(dn_ptr); + assert(facet_idx < int(m_triangles.size())); + Triangle& tr = m_triangles[facet_idx]; - if (dn_ptr->number_of_split_sides() != 0) { - for (int i=0; i<=dn_ptr->number_of_split_sides(); ++i) { - undivide_triangle(dn_ptr->children[i]); - m_triangles[dn_ptr->children[i]].div_info->valid = false; + if (tr.is_split()) { + for (int i=0; i<=tr.number_of_split_sides(); ++i) { + undivide_triangle(tr.children[i]); + m_triangles[tr.children[i]].valid = false; } } - dn_ptr->set_division(0); // not split + tr.set_division(0); // not split } -void TriangleSelector::remove_needless(int child_facet) +void TriangleSelector::remove_if_needless(int child_facet) { - assert(m_triangles[child_facet].div_info->number_of_split_sides() == 0); - int parent = m_triangles[child_facet].div_info->parent; + if (m_triangles[child_facet].is_split() || ! m_triangles[child_facet].valid) + return; + int parent = m_triangles[child_facet].parent; if (parent == -1) return; // root - // Check type of all valid children. - FacetSupportType type = m_triangles[m_triangles[parent].div_info->children[0]].div_info->get_state(); - for (int i=0; i<=m_triangles[parent].div_info->number_of_split_sides(); ++i) - if (m_triangles[m_triangles[parent].div_info->children[0]].div_info->get_state() != type) + FacetSupportType child_type = m_triangles[child_facet].get_state(); + + // Check type of all valid children, if they're same, they are needless. + for (int i=0; i<=m_triangles[parent].number_of_split_sides(); ++i) + if (m_triangles[m_triangles[parent].children[i]].is_split() + || m_triangles[m_triangles[parent].children[i]].get_state() != child_type) return; // not all children are the same - // All children are the same, let's kill them. + // All children are the same, kill them. undivide_triangle(parent); - m_triangles[parent].div_info->set_state(type); + m_triangles[parent].set_state(child_type); - // And not try the same for grandparent. - remove_needless(parent); + // And now try the same for parent (which has just become leaf). + remove_if_needless(parent); } @@ -1114,51 +1153,40 @@ void TriangleSelector::render() const ::glScalef(1.01f, 1.01f, 1.01f); ::glBegin( GL_TRIANGLES); - - for (int tr_id=0; tr_idvalid) { - for (int i=0; i<3; ++i) - ::glVertex3f(m_vertices[tr.verts_idxs[i]][0], m_vertices[tr.verts_idxs[i]][1], m_vertices[tr.verts_idxs[i]][2]); - } + if (! tr.valid) + continue; + + if (tr_id == m_orig_size_indices-1) + ::glColor3f(1.f, 0.f, 0.f); + + for (int i=0; i<3; ++i) + ::glVertex3f(m_vertices[tr.verts_idxs[i]][0], + m_vertices[tr.verts_idxs[i]][1], + m_vertices[tr.verts_idxs[i]][2]); } ::glEnd(); + ::glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); ::glBegin( GL_TRIANGLES); - for (int tr_id=0; tr_idvalid) + for (const Triangle& tr : m_triangles) { + if (! tr.valid || tr.is_split() || tr.get_state() == FacetSupportType::NONE) continue; - if (tr.div_info->number_of_split_sides() == 0) { - if (tr.div_info->get_state() == FacetSupportType::ENFORCER) - ::glColor4f(0.f, 0.f, 1.f, 0.2f); - else if (tr.div_info->get_state() == FacetSupportType::BLOCKER) - ::glColor4f(1.f, 0.f, 0.f, 0.2f); - else - continue; - } + if (tr.get_state() == FacetSupportType::ENFORCER) + ::glColor4f(0.f, 0.f, 1.f, 0.2f); else - continue; + ::glColor4f(1.f, 0.f, 0.f, 0.2f); - - if (tr.div_info->valid) { - for (int i=0; i<3; ++i) - ::glVertex3f(m_vertices[tr.verts_idxs[i]][0], m_vertices[tr.verts_idxs[i]][1], m_vertices[tr.verts_idxs[i]][2]); - } + for (int i=0; i<3; ++i) + ::glVertex3f(m_vertices[tr.verts_idxs[i]][0], + m_vertices[tr.verts_idxs[i]][1], + m_vertices[tr.verts_idxs[i]][2]); } ::glEnd(); } - -TriangleSelector::DivisionNode::DivisionNode() -{ - set_division(0); - set_state(FacetSupportType::NONE); -} - } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index 07c2e86d52..3e1b076c53 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -24,16 +24,18 @@ class ClippingPlane; // to recursively subdivide the triangles and make the selection finer. class TriangleSelector { public: + void set_edge_limit(float edge_limit) { m_edge_limit_sqr = std::pow(edge_limit, 2.f); } void render() const; // Create new object on a TriangleMesh. The referenced mesh must // stay valid, a ptr to it is saved and used. explicit TriangleSelector(const TriangleMesh& mesh); // Select all triangles inside the circle, subdivide where needed. - void select_patch(const Vec3f& hit, // point where to start - int facet_start, // facet that point belongs to - const Vec3f& dir, // direction of the ray - float radius_sqr, // squared radius of the cursor + void select_patch(const Vec3f& hit, // point where to start + int facet_start, // facet that point belongs to + const Vec3f& source, // camera position (mesh coords) + const Vec3f& dir, // direction of the ray (mesh coords) + float radius_sqr, // squared radius of the cursor FacetSupportType new_state); // enforcer or blocker? void unselect_all(); @@ -43,49 +45,44 @@ public: void garbage_collect(); private: - // A struct to hold information about how a triangle was divided. - struct DivisionNode { - DivisionNode(); - // Index of triangle this describes. + // Triangle and info about how it's split. + struct Triangle { + public: + Triangle(int a, int b, int c) + : verts_idxs{stl_triangle_vertex_indices(a, b, c)}, + division_type{0} + {} + stl_triangle_vertex_indices verts_idxs; + + // Is this triangle valid or marked to remove? bool valid{true}; // Index of parent triangle (-1: original) int parent{-1}; - // Bitmask encoding which sides are split. - int8_t division_type; - // bits 0 and 1 : 00 - no division - // 01 - one-edge split - // 10 - two-edge split - // 11 - three-edge split - // bits 2 and 3 : decimal 0, 1 or 2 identifying the special edge (one that - // splits in one-edge split or one that stays in two-edge split). - // Children triangles (0 = no child) std::array children; // Set the division type. void set_division(int sides_to_split, int special_side_idx = -1); - void set_state(FacetSupportType state); - // Helpers that decode the division_type bitmask. + // Get/set current state. + void set_state(FacetSupportType state); + FacetSupportType get_state() const; + + // Get info on how it's split. + bool is_split() const { return number_of_split_sides() != 0; } int number_of_split_sides() const { return division_type & 0b11; } int side_to_keep() const; int side_to_split() const; - FacetSupportType get_state() const; - }; - - // Triangle and pointer to how it's divided (nullptr = not divided). - // The ptr is nullptr for all new triangles, it is only valid for - // the original (undivided) triangles. - struct Triangle { - Triangle(int a, int b, int c) - : verts_idxs{stl_triangle_vertex_indices(a, b, c)}, - div_info{std::make_unique()} - {} - stl_triangle_vertex_indices verts_idxs; - std::unique_ptr div_info; + private: + // Bitmask encoding which sides are split. + int8_t division_type; + // bits 0, 1 : decimal 0, 1, 2 or 3 (how many sides are split) + // bits 2, 3 (non-leaf): decimal 0, 1 or 2 identifying the special edge + // (one that splits in one-edge split or one that stays in two-edge split). + // bits 2, 3 (leaf): FacetSupportType value }; // Lists of vertices and triangles, both original and new @@ -93,6 +90,8 @@ private: std::vector m_triangles; const TriangleMesh* m_mesh; + float m_edge_limit_sqr = 1.f; + // Number of original vertices and triangles. int m_orig_size_vertices; int m_orig_size_indices; @@ -104,6 +103,7 @@ private: // Caches for cursor position, radius and direction. struct Cursor { Vec3f center; + Vec3f source; Vec3f dir; float radius_sqr; }; @@ -111,8 +111,7 @@ private: Cursor m_cursor; // Private functions: - void select_triangle(int facet_idx, FacetSupportType type, - int num_of_inside_vertices = -1, + bool select_triangle(int facet_idx, FacetSupportType type, bool cursor_inside = false); bool is_point_inside_cursor(const Vec3f& point) const; @@ -125,7 +124,8 @@ private: bool split_triangle(int facet_idx); - void remove_needless(int child_facet); + void remove_if_needless(int child_facet); + bool is_pointer_in_triangle(int facet_idx) const; }; From bed28bb2fff974cd2097b47d404b3cbeb69d570b Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 22 Jun 2020 11:45:51 +0200 Subject: [PATCH 193/503] TriangleSelector: even more progress --- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 36 ++++++++++++++------ src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp | 7 +--- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 1c77f437c0..95f46b3fe6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -859,6 +859,7 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, float radius_sqr, FacetSupportType new_state) { assert(facet_start < m_orig_size_indices); + assert(is_approx(dir.norm(), 1.f)); // Save current cursor center, squared radius and camera direction, // so we don't have to pass it around. @@ -892,7 +893,6 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, // outside the cursor. bool TriangleSelector::select_triangle(int facet_idx, FacetSupportType type, bool cursor_inside) { - bool out = false; assert(facet_idx < int(m_triangles.size())); Triangle& tr = m_triangles[facet_idx]; @@ -903,10 +903,12 @@ bool TriangleSelector::select_triangle(int facet_idx, FacetSupportType type, boo int num_of_inside_vertices = vertices_inside(facet_idx); - if (num_of_inside_vertices == 0 && ! cursor_inside) - return out; // FIXME: just an edge can be inside + if (num_of_inside_vertices == 0 + && ! cursor_inside + && ! is_edge_inside_cursor(facet_idx)) + return false; - if (num_of_inside_vertices == 3) { + if (vertices_inside(facet_idx) == 3) { // dump any subdivision and select whole triangle undivide_triangle(facet_idx); tr.set_state(type); @@ -914,7 +916,6 @@ bool TriangleSelector::select_triangle(int facet_idx, FacetSupportType type, boo // the triangle is partially inside, let's recursively divide it // (if not already) and try selecting its children. - if (! tr.is_split() && tr.get_state() == type) { // This is leaf triangle that is already of correct type as a whole. // No need to split, all children would end up selected anyway. @@ -1065,11 +1066,10 @@ bool TriangleSelector::faces_camera(int facet) const { assert(facet < m_orig_size_indices); // The normal is cached in mesh->stl, use it. - return (m_mesh->stl.facet_start[facet].normal.dot(m_cursor.dir) > 0.); + return (m_mesh->stl.facet_start[facet].normal.dot(m_cursor.dir) < 0.); } - // How many vertices of a triangle are inside the circle? int TriangleSelector::vertices_inside(int facet_idx) const { @@ -1082,11 +1082,27 @@ int TriangleSelector::vertices_inside(int facet_idx) const } -// Is mouse pointer inside a triangle? -/*bool TriangleSelector::is_pointer_inside_triangle(int facet_idx) const +// Is edge inside cursor? +bool TriangleSelector::is_edge_inside_cursor(int facet_idx) const { + Vec3f pts[3]; + for (int i=0; i<3; ++i) + pts[i] = m_vertices[m_triangles[facet_idx].verts_idxs[i]]; -}*/ + const Vec3f& p = m_cursor.center; + + for (int side = 0; side < 3; ++side) { + const Vec3f& a = pts[side]; + const Vec3f& b = pts[side<2 ? side+1 : 0]; + Vec3f s = (b-a).normalized(); + float t = (p-a).dot(s); + Vec3f vector = a+t*s - p; + float dist_sqr = vector.squaredNorm(); + if (dist_sqr < m_cursor.radius_sqr && t>=0.f && t<=(b-a).norm()) + return true; + } + return false; +} diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index 3e1b076c53..a50328d3b8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -113,19 +113,14 @@ private: // Private functions: bool select_triangle(int facet_idx, FacetSupportType type, bool cursor_inside = false); - bool is_point_inside_cursor(const Vec3f& point) const; - int vertices_inside(int facet_idx) const; - bool faces_camera(int facet) const; - void undivide_triangle(int facet_idx); - bool split_triangle(int facet_idx); - void remove_if_needless(int child_facet); bool is_pointer_in_triangle(int facet_idx) const; + bool is_edge_inside_cursor(int facet_idx) const; }; From fb73bb1c6637fc6deb350e987b3308bb32a70e36 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 23 Jun 2020 16:07:33 +0200 Subject: [PATCH 194/503] TriangleSelector: remerging triangles, bugfixes --- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 202 ++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp | 18 +- 2 files changed, 140 insertions(+), 80 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 95f46b3fe6..8972e92467 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -95,7 +95,10 @@ void GLGizmoFdmSupports::on_render() const glsafe(::glEnable(GL_DEPTH_TEST)); //render_triangles(selection); - m_triangle_selector->render(); + + if (m_triangle_selector && ! m_setting_angle) + m_triangle_selector->render(m_imgui); + m_c->object_clipper()->render_cut(); render_cursor_circle(); @@ -569,12 +572,6 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l if (! m_c->selection_info()->model_object()) return; - m_imgui->begin(std::string("TriangleSelector DEBUG"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); - static float edge_limit = 1.f; - m_imgui->slider_float("Edge limit (mm): ", &edge_limit, 0.1f, 8.f); - m_triangle_selector->set_edge_limit(edge_limit); - m_imgui->end(); - const float approx_height = m_imgui->scaled(18.0f); y = std::min(y, bottom_limit - approx_height); m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); @@ -740,9 +737,7 @@ CommonGizmosDataID GLGizmoFdmSupports::on_get_requirements() const int(CommonGizmosDataID::SelectionInfo) | int(CommonGizmosDataID::InstancesHider) | int(CommonGizmosDataID::Raycaster) - | int(CommonGizmosDataID::HollowedMesh) - | int(CommonGizmosDataID::ObjectClipper) - | int(CommonGizmosDataID::SupportsClipper)); + | int(CommonGizmosDataID::ObjectClipper)); } @@ -872,7 +867,7 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, while (facet_idx < int(facets_to_check.size())) { int facet = facets_to_check[facet_idx]; if (! visited[facet]) { - if (select_triangle(facet, new_state, facet == facet_start)) { + if (select_triangle(facet, new_state)) { // add neighboring facets to list to be proccessed later for (int n=0; n<3; ++n) { if (faces_camera(m_mesh->stl.neighbors_start[facet].neighbor[n])) @@ -891,49 +886,54 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, // the triangle recursively, selecting just subtriangles truly inside the circle. // This is done by an actual recursive call. Returns false if the triangle is // outside the cursor. -bool TriangleSelector::select_triangle(int facet_idx, FacetSupportType type, bool cursor_inside) +bool TriangleSelector::select_triangle(int facet_idx, FacetSupportType type, bool recursive_call) { assert(facet_idx < int(m_triangles.size())); - Triangle& tr = m_triangles[facet_idx]; - if (! tr.valid) + Triangle* tr = &m_triangles[facet_idx]; + if (! tr->valid) return false; - cursor_inside = is_pointer_in_triangle(facet_idx); - int num_of_inside_vertices = vertices_inside(facet_idx); if (num_of_inside_vertices == 0 - && ! cursor_inside + && ! is_pointer_in_triangle(facet_idx) && ! is_edge_inside_cursor(facet_idx)) return false; - if (vertices_inside(facet_idx) == 3) { + if (num_of_inside_vertices == 3) { // dump any subdivision and select whole triangle undivide_triangle(facet_idx); - tr.set_state(type); + tr->set_state(type); } else { // the triangle is partially inside, let's recursively divide it // (if not already) and try selecting its children. - if (! tr.is_split() && tr.get_state() == type) { + if (! tr->is_split() && tr->get_state() == type) { // This is leaf triangle that is already of correct type as a whole. // No need to split, all children would end up selected anyway. return true; } split_triangle(facet_idx); - assert(facet_idx < int(m_triangles.size())); - int num_of_children = tr.number_of_split_sides() + 1; + tr = &m_triangles[facet_idx]; // might have been invalidated + + + int num_of_children = tr->number_of_split_sides() + 1; if (num_of_children != 1) { - for (int i=0; ichildren.size())); + assert(tr->children[i] < int(m_triangles.size())); + + select_triangle(tr->children[i], type, true); + tr = &m_triangles[facet_idx]; // might have been invalidated + } } } - - // In case that all siblings are leafs and have the same state now, + // In case that all children are leafs and have the same state now, // they may be removed and substituted by the parent triangle. - //remove_if_needless(facet_idx); + if (! recursive_call) + remove_useless_children(facet_idx); return true; } @@ -945,13 +945,13 @@ bool TriangleSelector::split_triangle(int facet_idx) return false; } - Triangle& tr = m_triangles[facet_idx]; + Triangle* tr = &m_triangles[facet_idx]; - FacetSupportType old_type = tr.get_state(); + FacetSupportType old_type = tr->get_state(); const double limit_squared = m_edge_limit_sqr; - stl_triangle_vertex_indices& facet = tr.verts_idxs; + stl_triangle_vertex_indices& facet = tr->verts_idxs; const stl_vertex* pts[3] = { &m_vertices[facet[0]], &m_vertices[facet[1]], &m_vertices[facet[2]]}; double sides[3] = { (*pts[2]-*pts[1]).squaredNorm(), (*pts[0]-*pts[2]).squaredNorm(), (*pts[1]-*pts[0]).squaredNorm() }; @@ -964,7 +964,7 @@ bool TriangleSelector::split_triangle(int facet_idx) side_to_keep = pt_idx; } if (sides_to_split.empty()) { - tr.set_division(0); + tr->set_division(0); return false; } @@ -1012,17 +1012,19 @@ bool TriangleSelector::split_triangle(int facet_idx) m_triangles.emplace_back(verts_idxs[1], verts_idxs[3], verts_idxs[5]); } + tr = &m_triangles[facet_idx]; // may have been invalidated + // Save how the triangle was split. Second argument makes sense only for one // or two split sides, otherwise the value is ignored. - tr.set_division(sides_to_split.size(), + tr->set_division(sides_to_split.size(), sides_to_split.size() == 2 ? side_to_keep : sides_to_split[0]); // And save the children. All children should start in the same state as the triangle we just split. assert(! sides_to_split.empty() && int(sides_to_split.size()) <= 3); for (int i=0; i<=int(sides_to_split.size()); ++i) { - tr.children[i] = m_triangles.size()-1-i; - m_triangles[tr.children[i]].parent = facet_idx; - m_triangles[tr.children[i]].set_state(old_type); + tr->children[i] = m_triangles.size()-1-i; + m_triangles[tr->children[i]].parent = facet_idx; + m_triangles[tr->children[i]].set_state(old_type); } return true; @@ -1097,7 +1099,10 @@ bool TriangleSelector::is_edge_inside_cursor(int facet_idx) const Vec3f s = (b-a).normalized(); float t = (p-a).dot(s); Vec3f vector = a+t*s - p; - float dist_sqr = vector.squaredNorm(); + + // vector is 3D vector from center to the intersection. What we want to + // measure is length of its projection onto plane perpendicular to dir. + float dist_sqr = vector.squaredNorm() - std::pow(vector.dot(m_cursor.dir), 2.f); if (dist_sqr < m_cursor.radius_sqr && t>=0.f && t<=(b-a).norm()) return true; } @@ -1117,33 +1122,47 @@ void TriangleSelector::undivide_triangle(int facet_idx) undivide_triangle(tr.children[i]); m_triangles[tr.children[i]].valid = false; } + tr.set_division(0); // not split } - - tr.set_division(0); // not split } -void TriangleSelector::remove_if_needless(int child_facet) +void TriangleSelector::remove_useless_children(int facet_idx) { - if (m_triangles[child_facet].is_split() || ! m_triangles[child_facet].valid) + // Check that all children are leafs of the same type. If not, try to + // make them (recursive call). Remove them if sucessful. + + assert(facet_idx < int(m_triangles.size()) && m_triangles[facet_idx].valid); + Triangle& tr = m_triangles[facet_idx]; + + if (! tr.is_split()) { + // This is a leaf, there nothing to do. This can happen during the + // first (non-recursive call). Shouldn't otherwise. return; - int parent = m_triangles[child_facet].parent; - if (parent == -1) - return; // root - FacetSupportType child_type = m_triangles[child_facet].get_state(); + } - // Check type of all valid children, if they're same, they are needless. - for (int i=0; i<=m_triangles[parent].number_of_split_sides(); ++i) - if (m_triangles[m_triangles[parent].children[i]].is_split() - || m_triangles[m_triangles[parent].children[i]].get_state() != child_type) - return; // not all children are the same + // Call this for all non-leaf children. + for (int child_idx=0; child_idx<=tr.number_of_split_sides(); ++child_idx) { + assert(child_idx < int(m_triangles.size()) && m_triangles[child_idx].valid); + if (m_triangles[tr.children[child_idx]].is_split()) + remove_useless_children(tr.children[child_idx]); + } - // All children are the same, kill them. - undivide_triangle(parent); - m_triangles[parent].set_state(child_type); - // And now try the same for parent (which has just become leaf). - remove_if_needless(parent); + // Return if a child is not leaf or two children differ in type. + FacetSupportType first_child_type; + for (int child_idx=0; child_idx<=tr.number_of_split_sides(); ++child_idx) { + if (m_triangles[tr.children[child_idx]].is_split()) + return; + if (child_idx == 0) + first_child_type = m_triangles[tr.children[0]].get_state(); + else if (m_triangles[tr.children[child_idx]].get_state() != first_child_type) + return; + } + + // If we got here, the children can be removed. + undivide_triangle(facet_idx); + tr.set_state(first_child_type); } @@ -1158,33 +1177,12 @@ TriangleSelector::TriangleSelector(const TriangleMesh& mesh) m_mesh = &mesh; } - -void TriangleSelector::render() const +void TriangleSelector::render(ImGuiWrapper* imgui) { - ::glColor3f(0.f, 0.f, 1.f); - ::glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); - Vec3d offset = wxGetApp().model().objects.front()->instances.front()->get_transformation().get_offset(); ::glTranslatef(offset.x(), offset.y(), offset.z()); - ::glScalef(1.01f, 1.01f, 1.01f); + ::glScalef(1.005f, 1.005f, 1.005f); - ::glBegin( GL_TRIANGLES); - for (int tr_id=0; tr_idbegin(std::string("TriangleSelector dialog (DEV ONLY)"), + ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); + static float edge_limit = 1.f; + imgui->text("Edge limit (mm): "); + imgui->slider_float("", &edge_limit, 0.1f, 8.f); + set_edge_limit(edge_limit); + imgui->checkbox("Show triangles: ", m_show_triangles); + + int valid_triangles = std::count_if(m_triangles.begin(), m_triangles.end(), + [](const Triangle& tr) { return tr.valid; }); + imgui->text("Valid triangles: " + std::to_string(valid_triangles) + + "/" + std::to_string(m_triangles.size())); + imgui->text("Number of vertices: " + std::to_string(m_vertices.size())); + + imgui->end(); + + if (m_show_triangles) { + ::glColor3f(0.f, 0.f, 1.f); + ::glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); + + ::glBegin( GL_TRIANGLES); + for (int tr_id=0; tr_id +#define PRUSASLICER_TRIANGLE_SELECTOR_DEBUG + + namespace Slic3r { enum class FacetSupportType : int8_t; @@ -25,7 +28,7 @@ class ClippingPlane; class TriangleSelector { public: void set_edge_limit(float edge_limit) { m_edge_limit_sqr = std::pow(edge_limit, 2.f); } - void render() const; + // Create new object on a TriangleMesh. The referenced mesh must // stay valid, a ptr to it is saved and used. explicit TriangleSelector(const TriangleMesh& mesh); @@ -40,10 +43,19 @@ public: void unselect_all(); + // Render current selection. Transformation matrices are supposed + // to be already set. + void render(ImGuiWrapper* imgui = nullptr); + // Remove all unnecessary data (such as vertices that are not needed // because the selection has been made larger. void garbage_collect(); +#ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG + void render_debug(ImGuiWrapper* imgui); + bool m_show_triangles{true}; +#endif + private: // Triangle and info about how it's split. struct Triangle { @@ -112,13 +124,13 @@ private: // Private functions: bool select_triangle(int facet_idx, FacetSupportType type, - bool cursor_inside = false); + bool recursive_call = false); bool is_point_inside_cursor(const Vec3f& point) const; int vertices_inside(int facet_idx) const; bool faces_camera(int facet) const; void undivide_triangle(int facet_idx); bool split_triangle(int facet_idx); - void remove_if_needless(int child_facet); + void remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant. bool is_pointer_in_triangle(int facet_idx) const; bool is_edge_inside_cursor(int facet_idx) const; }; From b9321856f387d6ef07ee9d769fe9674d83b8e31b Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 24 Jun 2020 12:24:32 +0200 Subject: [PATCH 195/503] TriangleSelector: Reusing of previously calculated triangle divisions, partial garbage collection implementation --- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 144 ++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp | 38 ++--- 2 files changed, 117 insertions(+), 65 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 8972e92467..2296dd570e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -800,51 +800,29 @@ void GLGizmoFdmSupports::on_save(cereal::BinaryOutputArchive&) const } - +// sides_to_split==-1 : just restore previous split void TriangleSelector::Triangle::set_division(int sides_to_split, int special_side_idx) { - assert(sides_to_split >=0 && sides_to_split <= 3); + assert(sides_to_split >=-1 && sides_to_split <= 3); assert(special_side_idx >=-1 && special_side_idx < 3); // If splitting one or two sides, second argument must be provided. assert(sides_to_split != 1 || special_side_idx != -1); assert(sides_to_split != 2 || special_side_idx != -1); - division_type = sides_to_split | ((special_side_idx != -1 ? special_side_idx : 0 ) <<2); -} - - - -void TriangleSelector::Triangle::set_state(FacetSupportType type) -{ - // If this is not a leaf-node, this makes no sense and - // the bits are used for storing index of an edge. - assert(! is_split()); - division_type = (int8_t(type) << 2); -} - - - -int TriangleSelector::Triangle::side_to_keep() const -{ - assert(number_of_split_sides() == 2); - return division_type >> 2; -} - - - -int TriangleSelector::Triangle::side_to_split() const -{ - assert(number_of_split_sides() == 1); - return division_type >> 2; -} - - - -FacetSupportType TriangleSelector::Triangle::get_state() const -{ - assert(! is_split()); // this must be leaf - return FacetSupportType(division_type >> 2); + if (sides_to_split != -1) { + this->number_of_splits = sides_to_split; + if (sides_to_split != 0) { + assert(old_number_of_splits == 0); + this->special_side_idx = special_side_idx; + this->old_number_of_splits = sides_to_split; + } + } + else { + assert(old_number_of_splits != 0); + this->number_of_splits = old_number_of_splits; + // indices of children should still be there. + } } @@ -938,17 +916,29 @@ bool TriangleSelector::select_triangle(int facet_idx, FacetSupportType type, boo } -bool TriangleSelector::split_triangle(int facet_idx) +void TriangleSelector::split_triangle(int facet_idx) { if (m_triangles[facet_idx].is_split()) { - // The triangle was divided already. - return false; + // The triangle is divided already. + return; } Triangle* tr = &m_triangles[facet_idx]; FacetSupportType old_type = tr->get_state(); + if (tr->was_split_before() != 0) { + // This triangle is not split at the moment, but was at one point + // in history. We can just restore it and resurrect its children. + tr->set_division(-1); + for (int i=0; i<=tr->number_of_split_sides(); ++i) { + m_triangles[tr->children[i]].set_state(old_type); + m_triangles[tr->children[i]].valid = true; + } + return; + } + + // If we got here, we are about to actually split the triangle. const double limit_squared = m_edge_limit_sqr; stl_triangle_vertex_indices& facet = tr->verts_idxs; @@ -964,8 +954,9 @@ bool TriangleSelector::split_triangle(int facet_idx) side_to_keep = pt_idx; } if (sides_to_split.empty()) { + // This shall be unselected. tr->set_division(0); - return false; + return; } // indices of triangle vertices @@ -1023,11 +1014,8 @@ bool TriangleSelector::split_triangle(int facet_idx) assert(! sides_to_split.empty() && int(sides_to_split.size()) <= 3); for (int i=0; i<=int(sides_to_split.size()); ++i) { tr->children[i] = m_triangles.size()-1-i; - m_triangles[tr->children[i]].parent = facet_idx; m_triangles[tr->children[i]].set_state(old_type); } - - return true; } @@ -1166,6 +1154,45 @@ void TriangleSelector::remove_useless_children(int facet_idx) } + +void TriangleSelector::garbage_collect() +{ + // First make a map from old to new triangle indices. + int new_idx = m_orig_size_indices; + std::vector new_triangle_indices(m_triangles.size(), -1); + std::vector invalid_vertices(m_vertices.size(), false); + for (int i = m_orig_size_indices; itext("Edge limit (mm): "); imgui->slider_float("", &edge_limit, 0.1f, 8.f); set_edge_limit(edge_limit); - imgui->checkbox("Show triangles: ", m_show_triangles); + imgui->checkbox("Show split triangles: ", m_show_triangles); + imgui->checkbox("Show invalid triangles: ", m_show_invalid); int valid_triangles = std::count_if(m_triangles.begin(), m_triangles.end(), [](const Triangle& tr) { return tr.valid; }); imgui->text("Valid triangles: " + std::to_string(valid_triangles) + "/" + std::to_string(m_triangles.size())); imgui->text("Number of vertices: " + std::to_string(m_vertices.size())); + if (imgui->button("Force garbage collection")) + garbage_collect(); imgui->end(); if (m_show_triangles) { - ::glColor3f(0.f, 0.f, 1.f); ::glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); ::glBegin( GL_TRIANGLES); for (int tr_id=0; tr_id children; @@ -79,22 +80,25 @@ private: void set_division(int sides_to_split, int special_side_idx = -1); // Get/set current state. - void set_state(FacetSupportType state); - FacetSupportType get_state() const; + void set_state(FacetSupportType type) { assert(! is_split()); state = type; } + FacetSupportType get_state() const { assert(! is_split()); return state; } // Get info on how it's split. bool is_split() const { return number_of_split_sides() != 0; } - int number_of_split_sides() const { return division_type & 0b11; } - int side_to_keep() const; - int side_to_split() const; + int number_of_split_sides() const { return number_of_splits; } + int side_to_keep() const { assert(number_of_split_sides() == 2); return special_side_idx; } + int side_to_split() const { assert(number_of_split_sides() == 1); return special_side_idx; } + bool was_split_before() const { return old_number_of_splits != 0; } + void forget_history() { old_number_of_splits = 0; } private: - // Bitmask encoding which sides are split. - int8_t division_type; - // bits 0, 1 : decimal 0, 1, 2 or 3 (how many sides are split) - // bits 2, 3 (non-leaf): decimal 0, 1 or 2 identifying the special edge - // (one that splits in one-edge split or one that stays in two-edge split). - // bits 2, 3 (leaf): FacetSupportType value + int number_of_splits; + int special_side_idx; + FacetSupportType state; + + // How many children were spawned during last split? + // Is not reset on remerging the triangle. + int old_number_of_splits; }; // Lists of vertices and triangles, both original and new @@ -129,7 +133,7 @@ private: int vertices_inside(int facet_idx) const; bool faces_camera(int facet) const; void undivide_triangle(int facet_idx); - bool split_triangle(int facet_idx); + void split_triangle(int facet_idx); void remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant. bool is_pointer_in_triangle(int facet_idx) const; bool is_edge_inside_cursor(int facet_idx) const; From da6acd73e21407a8956d64b332b8fba47a94465f Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 24 Jun 2020 14:47:53 +0200 Subject: [PATCH 196/503] TriangleSelector: Vertices are reference-counted and garbage collected Garbage collection is triggered automatically when more than half of all triangles are invalid --- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 133 +++++++++++++------ src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp | 44 +++--- 2 files changed, 120 insertions(+), 57 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 2296dd570e..10d0459205 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -908,10 +908,20 @@ bool TriangleSelector::select_triangle(int facet_idx, FacetSupportType type, boo } } } - // In case that all children are leafs and have the same state now, - // they may be removed and substituted by the parent triangle. - if (! recursive_call) + + if (! recursive_call) { + // In case that all children are leafs and have the same state now, + // they may be removed and substituted by the parent triangle. remove_useless_children(facet_idx); + + // Make sure that we did not lose track of invalid triangles. + assert(m_invalid_triangles == std::count_if(m_triangles.begin(), m_triangles.end(), + [](const Triangle& tr) { return ! tr.valid; })); + + // Do garbage collection maybe? + if (2*m_invalid_triangles > int(m_triangles.size())) + garbage_collect(); + } return true; } @@ -934,6 +944,7 @@ void TriangleSelector::split_triangle(int facet_idx) for (int i=0; i<=tr->number_of_split_sides(); ++i) { m_triangles[tr->children[i]].set_state(old_type); m_triangles[tr->children[i]].valid = true; + --m_invalid_triangles; } return; } @@ -941,9 +952,11 @@ void TriangleSelector::split_triangle(int facet_idx) // If we got here, we are about to actually split the triangle. const double limit_squared = m_edge_limit_sqr; - stl_triangle_vertex_indices& facet = tr->verts_idxs; - const stl_vertex* pts[3] = { &m_vertices[facet[0]], &m_vertices[facet[1]], &m_vertices[facet[2]]}; - double sides[3] = { (*pts[2]-*pts[1]).squaredNorm(), (*pts[0]-*pts[2]).squaredNorm(), (*pts[1]-*pts[0]).squaredNorm() }; + std::array& facet = tr->verts_idxs; + const stl_vertex* pts[3] = { &m_vertices[facet[0]].v, &m_vertices[facet[1]].v, &m_vertices[facet[2]].v}; + double sides[3] = { (*pts[2]-*pts[1]).squaredNorm(), + (*pts[0]-*pts[2]).squaredNorm(), + (*pts[1]-*pts[0]).squaredNorm() }; std::vector sides_to_split; int side_to_keep = -1; @@ -970,37 +983,37 @@ void TriangleSelector::split_triangle(int facet_idx) if (sides_to_split.size() == 1) { - m_vertices.emplace_back((m_vertices[verts_idxs[1]] + m_vertices[verts_idxs[2]])/2.); + m_vertices.emplace_back((m_vertices[verts_idxs[1]].v + m_vertices[verts_idxs[2]].v)/2.); verts_idxs.insert(verts_idxs.begin()+2, m_vertices.size() - 1); - m_triangles.emplace_back(verts_idxs[0], verts_idxs[1], verts_idxs[2]); - m_triangles.emplace_back(verts_idxs[2], verts_idxs[3], verts_idxs[0]); + push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[2]); + push_triangle(verts_idxs[2], verts_idxs[3], verts_idxs[0]); } if (sides_to_split.size() == 2) { - m_vertices.emplace_back((m_vertices[verts_idxs[0]] + m_vertices[verts_idxs[1]])/2.); + m_vertices.emplace_back((m_vertices[verts_idxs[0]].v + m_vertices[verts_idxs[1]].v)/2.); verts_idxs.insert(verts_idxs.begin()+1, m_vertices.size() - 1); - m_vertices.emplace_back((m_vertices[verts_idxs[0]] + m_vertices[verts_idxs[3]])/2.); + m_vertices.emplace_back((m_vertices[verts_idxs[0]].v + m_vertices[verts_idxs[3]].v)/2.); verts_idxs.insert(verts_idxs.begin()+4, m_vertices.size() - 1); - m_triangles.emplace_back(verts_idxs[0], verts_idxs[1], verts_idxs[4]); - m_triangles.emplace_back(verts_idxs[1], verts_idxs[2], verts_idxs[4]); - m_triangles.emplace_back(verts_idxs[2], verts_idxs[3], verts_idxs[4]); + push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[4]); + push_triangle(verts_idxs[1], verts_idxs[2], verts_idxs[4]); + push_triangle(verts_idxs[2], verts_idxs[3], verts_idxs[4]); } if (sides_to_split.size() == 3) { - m_vertices.emplace_back((m_vertices[verts_idxs[0]] + m_vertices[verts_idxs[1]])/2.); + m_vertices.emplace_back((m_vertices[verts_idxs[0]].v + m_vertices[verts_idxs[1]].v)/2.); verts_idxs.insert(verts_idxs.begin()+1, m_vertices.size() - 1); - m_vertices.emplace_back((m_vertices[verts_idxs[2]] + m_vertices[verts_idxs[3]])/2.); + m_vertices.emplace_back((m_vertices[verts_idxs[2]].v + m_vertices[verts_idxs[3]].v)/2.); verts_idxs.insert(verts_idxs.begin()+3, m_vertices.size() - 1); - m_vertices.emplace_back((m_vertices[verts_idxs[4]] + m_vertices[verts_idxs[0]])/2.); + m_vertices.emplace_back((m_vertices[verts_idxs[4]].v + m_vertices[verts_idxs[0]].v)/2.); verts_idxs.insert(verts_idxs.begin()+5, m_vertices.size() - 1); - m_triangles.emplace_back(verts_idxs[0], verts_idxs[1], verts_idxs[5]); - m_triangles.emplace_back(verts_idxs[1], verts_idxs[2], verts_idxs[3]); - m_triangles.emplace_back(verts_idxs[3], verts_idxs[4], verts_idxs[5]); - m_triangles.emplace_back(verts_idxs[1], verts_idxs[3], verts_idxs[5]); + push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[5]); + push_triangle(verts_idxs[1], verts_idxs[2], verts_idxs[3]); + push_triangle(verts_idxs[3], verts_idxs[4], verts_idxs[5]); + push_triangle(verts_idxs[1], verts_idxs[3], verts_idxs[5]); } tr = &m_triangles[facet_idx]; // may have been invalidated @@ -1035,9 +1048,9 @@ bool TriangleSelector::is_pointer_in_triangle(int facet_idx) const return ((b-a).cross(c-a)).dot(d-a) > 0.; }; - const Vec3f& p1 = m_vertices[m_triangles[facet_idx].verts_idxs[0]]; - const Vec3f& p2 = m_vertices[m_triangles[facet_idx].verts_idxs[1]]; - const Vec3f& p3 = m_vertices[m_triangles[facet_idx].verts_idxs[2]]; + const Vec3f& p1 = m_vertices[m_triangles[facet_idx].verts_idxs[0]].v; + const Vec3f& p2 = m_vertices[m_triangles[facet_idx].verts_idxs[1]].v; + const Vec3f& p3 = m_vertices[m_triangles[facet_idx].verts_idxs[2]].v; const Vec3f& q1 = m_cursor.center + m_cursor.dir; const Vec3f q2 = m_cursor.center - m_cursor.dir; @@ -1065,7 +1078,7 @@ int TriangleSelector::vertices_inside(int facet_idx) const { int inside = 0; for (size_t i=0; i<3; ++i) { - if (is_point_inside_cursor(m_vertices[m_triangles[facet_idx].verts_idxs[i]])) + if (is_point_inside_cursor(m_vertices[m_triangles[facet_idx].verts_idxs[i]].v)) ++inside; } return inside; @@ -1077,7 +1090,7 @@ bool TriangleSelector::is_edge_inside_cursor(int facet_idx) const { Vec3f pts[3]; for (int i=0; i<3; ++i) - pts[i] = m_vertices[m_triangles[facet_idx].verts_idxs[i]]; + pts[i] = m_vertices[m_triangles[facet_idx].verts_idxs[i]].v; const Vec3f& p = m_cursor.center; @@ -1109,6 +1122,7 @@ void TriangleSelector::undivide_triangle(int facet_idx) for (int i=0; i<=tr.number_of_split_sides(); ++i) { undivide_triangle(tr.children[i]); m_triangles[tr.children[i]].valid = false; + ++m_invalid_triangles; } tr.set_division(0); // not split } @@ -1138,7 +1152,7 @@ void TriangleSelector::remove_useless_children(int facet_idx) // Return if a child is not leaf or two children differ in type. - FacetSupportType first_child_type; + FacetSupportType first_child_type = FacetSupportType::NONE; for (int child_idx=0; child_idx<=tr.number_of_split_sides(); ++child_idx) { if (m_triangles[tr.children[child_idx]].is_split()) return; @@ -1160,13 +1174,26 @@ void TriangleSelector::garbage_collect() // First make a map from old to new triangle indices. int new_idx = m_orig_size_indices; std::vector new_triangle_indices(m_triangles.size(), -1); - std::vector invalid_vertices(m_vertices.size(), false); for (int i = m_orig_size_indices; i new_vertices_indices(m_vertices.size(), -1); + for (int i=m_orig_size_vertices; i= 0); + if (m_vertices[i].ref_cnt != 0) { + new_vertices_indices[i] = new_idx; + ++new_idx; } } @@ -1174,6 +1201,9 @@ void TriangleSelector::garbage_collect() m_triangles.erase(std::remove_if(m_triangles.begin()+m_orig_size_indices, m_triangles.end(), [](const Triangle& tr) { return ! tr.valid; }), m_triangles.end()); + m_vertices.erase(std::remove_if(m_vertices.begin()+m_orig_size_vertices, m_vertices.end(), + [](const Vertex& vert) { return vert.ref_cnt == 0; }), + m_vertices.end()); // Now go through all remaining triangles and update changed indices. for (Triangle& tr : m_triangles) { @@ -1187,20 +1217,32 @@ void TriangleSelector::garbage_collect() } } + // Update indices into m_vertices. The original vertices are never + // touched and need not be reindexed. + for (int& idx : tr.verts_idxs) { + if (idx >= m_orig_size_vertices) { + assert(new_vertices_indices[idx] != -1); + idx = new_vertices_indices[idx]; + } + } + // If this triangle was split before, forget it. // Children referenced in the cache are dead by now. tr.forget_history(); } + + m_invalid_triangles = 0; } TriangleSelector::TriangleSelector(const TriangleMesh& mesh) { for (const stl_vertex& vert : mesh.its.vertices) - m_vertices.push_back(vert); + m_vertices.emplace_back(vert); for (const stl_triangle_vertex_indices& ind : mesh.its.indices) - m_triangles.emplace_back(Triangle(ind[0], ind[1], ind[2])); + push_triangle(ind[0], ind[1], ind[2]); m_orig_size_vertices = m_vertices.size(); m_orig_size_indices = m_triangles.size(); + m_invalid_triangles = 0; m_mesh = &mesh; } @@ -1222,9 +1264,9 @@ void TriangleSelector::render(ImGuiWrapper* imgui) ::glColor4f(1.f, 0.f, 0.f, 0.2f); for (int i=0; i<3; ++i) - ::glVertex3f(m_vertices[tr.verts_idxs[i]][0], - m_vertices[tr.verts_idxs[i]][1], - m_vertices[tr.verts_idxs[i]][2]); + ::glVertex3f(m_vertices[tr.verts_idxs[i]].v[0], + m_vertices[tr.verts_idxs[i]].v[1], + m_vertices[tr.verts_idxs[i]].v[2]); } ::glEnd(); @@ -1250,6 +1292,18 @@ void TriangleSelector::set_edge_limit(float edge_limit) } } + + +void TriangleSelector::push_triangle(int a, int b, int c) +{ + for (int i : {a, b, c}) { + assert(i >= 0 && i < int(m_vertices.size())); + ++m_vertices[i].ref_cnt; + } + m_triangles.emplace_back(a, b, c); +} + + #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG void TriangleSelector::render_debug(ImGuiWrapper* imgui) { @@ -1262,11 +1316,10 @@ void TriangleSelector::render_debug(ImGuiWrapper* imgui) imgui->checkbox("Show split triangles: ", m_show_triangles); imgui->checkbox("Show invalid triangles: ", m_show_invalid); - int valid_triangles = std::count_if(m_triangles.begin(), m_triangles.end(), - [](const Triangle& tr) { return tr.valid; }); + int valid_triangles = m_triangles.size() - m_invalid_triangles; imgui->text("Valid triangles: " + std::to_string(valid_triangles) + "/" + std::to_string(m_triangles.size())); - imgui->text("Number of vertices: " + std::to_string(m_vertices.size())); + imgui->text("Vertices: " + std::to_string(m_vertices.size())); if (imgui->button("Force garbage collection")) garbage_collect(); @@ -1290,9 +1343,9 @@ void TriangleSelector::render_debug(ImGuiWrapper* imgui) ::glColor3f(0.f, 0.f, 1.f); for (int i=0; i<3; ++i) - ::glVertex3f(m_vertices[tr.verts_idxs[i]][0], - m_vertices[tr.verts_idxs[i]][1], - m_vertices[tr.verts_idxs[i]][2]); + ::glVertex3f(m_vertices[tr.verts_idxs[i]].v[0], + m_vertices[tr.verts_idxs[i]].v[1], + m_vertices[tr.verts_idxs[i]].v[2]); } ::glEnd(); ::glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index fa03f7987b..fb14f3c5ee 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -33,7 +33,7 @@ public: // stay valid, a ptr to it is saved and used. explicit TriangleSelector(const TriangleMesh& mesh); - // Select all triangles inside the circle, subdivide where needed. + // Select all triangles fully inside the circle, subdivide where needed. void select_patch(const Vec3f& hit, // point where to start int facet_start, // facet that point belongs to const Vec3f& source, // camera position (mesh coords) @@ -41,14 +41,11 @@ public: float radius_sqr, // squared radius of the cursor FacetSupportType new_state); // enforcer or blocker? - void unselect_all(); - // Render current selection. Transformation matrices are supposed // to be already set. void render(ImGuiWrapper* imgui = nullptr); - // Remove all unnecessary data (such as vertices that are not needed - // because the selection has been made larger. + // Remove all unnecessary data. void garbage_collect(); #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG @@ -59,21 +56,24 @@ public: private: // Triangle and info about how it's split. - struct Triangle { + class Triangle { public: + // Use TriangleSelector::push_triangle to create a new triangle. + // It increments/decrements reference counter on vertices. Triangle(int a, int b, int c) - : verts_idxs{stl_triangle_vertex_indices(a, b, c)}, + : verts_idxs{a, b, c}, state{FacetSupportType(0)}, number_of_splits{0}, special_side_idx{0}, old_number_of_splits{0} {} - stl_triangle_vertex_indices verts_idxs; + // Indices into m_vertices. + std::array verts_idxs; - // Is this triangle valid or marked to remove? + // Is this triangle valid or marked to be removed? bool valid{true}; - // Children triangles (0 = no child) + // Children triangles. std::array children; // Set the division type. @@ -101,22 +101,31 @@ private: int old_number_of_splits; }; + struct Vertex { + explicit Vertex(const stl_vertex& vert) + : v{vert}, + ref_cnt{0} + {} + stl_vertex v; + int ref_cnt; + }; + // Lists of vertices and triangles, both original and new - std::vector m_vertices; + std::vector m_vertices; std::vector m_triangles; const TriangleMesh* m_mesh; + // Number of invalid triangles (to trigger garbage collection). + int m_invalid_triangles; + + // Limiting length of triangle side (squared). float m_edge_limit_sqr = 1.f; // Number of original vertices and triangles. int m_orig_size_vertices; int m_orig_size_indices; - // Limits for stopping the recursion. - float m_max_edge_length; - int m_max_recursion_depth; - - // Caches for cursor position, radius and direction. + // Cache for cursor position, radius and direction. struct Cursor { Vec3f center; Vec3f source; @@ -130,13 +139,14 @@ private: bool select_triangle(int facet_idx, FacetSupportType type, bool recursive_call = false); bool is_point_inside_cursor(const Vec3f& point) const; - int vertices_inside(int facet_idx) const; + int vertices_inside(int facet_idx) const; bool faces_camera(int facet) const; void undivide_triangle(int facet_idx); void split_triangle(int facet_idx); void remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant. bool is_pointer_in_triangle(int facet_idx) const; bool is_edge_inside_cursor(int facet_idx) const; + void push_triangle(int a, int b, int c); }; From 814f8be92f410e2cd5a7928a64a5a032072da342 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 1 Jul 2020 09:09:25 +0200 Subject: [PATCH 197/503] TriangleSelector: getting ready for frontend/backend separation --- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 279 +++++++++++++------ src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp | 11 +- 2 files changed, 207 insertions(+), 83 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 10d0459205..f2a6bb8adc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -972,63 +972,12 @@ void TriangleSelector::split_triangle(int facet_idx) return; } - // indices of triangle vertices - std::vector verts_idxs; - int idx = sides_to_split.size() == 2 ? side_to_keep : sides_to_split[0]; - for (int j=0; j<3; ++j) { - verts_idxs.push_back(facet[idx++]); - if (idx == 3) - idx = 0; - } - - - if (sides_to_split.size() == 1) { - m_vertices.emplace_back((m_vertices[verts_idxs[1]].v + m_vertices[verts_idxs[2]].v)/2.); - verts_idxs.insert(verts_idxs.begin()+2, m_vertices.size() - 1); - - push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[2]); - push_triangle(verts_idxs[2], verts_idxs[3], verts_idxs[0]); - } - - if (sides_to_split.size() == 2) { - m_vertices.emplace_back((m_vertices[verts_idxs[0]].v + m_vertices[verts_idxs[1]].v)/2.); - verts_idxs.insert(verts_idxs.begin()+1, m_vertices.size() - 1); - - m_vertices.emplace_back((m_vertices[verts_idxs[0]].v + m_vertices[verts_idxs[3]].v)/2.); - verts_idxs.insert(verts_idxs.begin()+4, m_vertices.size() - 1); - - push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[4]); - push_triangle(verts_idxs[1], verts_idxs[2], verts_idxs[4]); - push_triangle(verts_idxs[2], verts_idxs[3], verts_idxs[4]); - } - - if (sides_to_split.size() == 3) { - m_vertices.emplace_back((m_vertices[verts_idxs[0]].v + m_vertices[verts_idxs[1]].v)/2.); - verts_idxs.insert(verts_idxs.begin()+1, m_vertices.size() - 1); - m_vertices.emplace_back((m_vertices[verts_idxs[2]].v + m_vertices[verts_idxs[3]].v)/2.); - verts_idxs.insert(verts_idxs.begin()+3, m_vertices.size() - 1); - m_vertices.emplace_back((m_vertices[verts_idxs[4]].v + m_vertices[verts_idxs[0]].v)/2.); - verts_idxs.insert(verts_idxs.begin()+5, m_vertices.size() - 1); - - push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[5]); - push_triangle(verts_idxs[1], verts_idxs[2], verts_idxs[3]); - push_triangle(verts_idxs[3], verts_idxs[4], verts_idxs[5]); - push_triangle(verts_idxs[1], verts_idxs[3], verts_idxs[5]); - } - - tr = &m_triangles[facet_idx]; // may have been invalidated - - // Save how the triangle was split. Second argument makes sense only for one + // Save how the triangle will be split. Second argument makes sense only for one // or two split sides, otherwise the value is ignored. tr->set_division(sides_to_split.size(), sides_to_split.size() == 2 ? side_to_keep : sides_to_split[0]); - // And save the children. All children should start in the same state as the triangle we just split. - assert(! sides_to_split.empty() && int(sides_to_split.size()) <= 3); - for (int i=0; i<=int(sides_to_split.size()); ++i) { - tr->children[i] = m_triangles.size()-1-i; - m_triangles[tr->children[i]].set_state(old_type); - } + perform_split(facet_idx, old_type); } @@ -1253,22 +1202,45 @@ void TriangleSelector::render(ImGuiWrapper* imgui) ::glScalef(1.005f, 1.005f, 1.005f); - ::glBegin( GL_TRIANGLES); + int enf_cnt = 0; + int blc_cnt = 0; + for (const Triangle& tr : m_triangles) { if (! tr.valid || tr.is_split() || tr.get_state() == FacetSupportType::NONE) continue; - if (tr.get_state() == FacetSupportType::ENFORCER) - ::glColor4f(0.f, 0.f, 1.f, 0.2f); - else - ::glColor4f(1.f, 0.f, 0.f, 0.2f); + GLIndexedVertexArray& va = tr.get_state() == FacetSupportType::ENFORCER + ? m_iva_enforcers + : m_iva_blockers; + int& cnt = tr.get_state() == FacetSupportType::ENFORCER + ? enf_cnt + : blc_cnt; for (int i=0; i<3; ++i) - ::glVertex3f(m_vertices[tr.verts_idxs[i]].v[0], - m_vertices[tr.verts_idxs[i]].v[1], - m_vertices[tr.verts_idxs[i]].v[2]); + va.push_geometry(double(m_vertices[tr.verts_idxs[i]].v[0]), + double(m_vertices[tr.verts_idxs[i]].v[1]), + double(m_vertices[tr.verts_idxs[i]].v[2]), + 0., 0., 1.); + va.push_triangle(cnt, + cnt+1, + cnt+2); + cnt += 3; } - ::glEnd(); + + m_iva_enforcers.finalize_geometry(true); + m_iva_blockers.finalize_geometry(true); + + if (m_iva_enforcers.has_VBOs()) { + ::glColor4f(0.f, 0.f, 1.f, 0.2f); + m_iva_enforcers.render(); + } + m_iva_enforcers.release_geometry(); + + if (m_iva_blockers.has_VBOs()) { + ::glColor4f(1.f, 0.f, 0.f, 0.2f); + m_iva_blockers.render(); + } + m_iva_blockers.release_geometry(); #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG if (imgui) @@ -1304,6 +1276,110 @@ void TriangleSelector::push_triangle(int a, int b, int c) } +void TriangleSelector::perform_split(int facet_idx, FacetSupportType old_state) +{ + Triangle* tr = &m_triangles[facet_idx]; + + assert(tr->is_split()); + + // Read info about how to split this triangle. + int sides_to_split = tr->number_of_split_sides(); + + // indices of triangle vertices + std::vector verts_idxs; + int idx = tr->special_side(); + for (int j=0; j<3; ++j) { + verts_idxs.push_back(tr->verts_idxs[idx++]); + if (idx == 3) + idx = 0; + } + + if (sides_to_split == 1) { + m_vertices.emplace_back((m_vertices[verts_idxs[1]].v + m_vertices[verts_idxs[2]].v)/2.); + verts_idxs.insert(verts_idxs.begin()+2, m_vertices.size() - 1); + + push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[2]); + push_triangle(verts_idxs[2], verts_idxs[3], verts_idxs[0]); + } + + if (sides_to_split == 2) { + m_vertices.emplace_back((m_vertices[verts_idxs[0]].v + m_vertices[verts_idxs[1]].v)/2.); + verts_idxs.insert(verts_idxs.begin()+1, m_vertices.size() - 1); + + m_vertices.emplace_back((m_vertices[verts_idxs[0]].v + m_vertices[verts_idxs[3]].v)/2.); + verts_idxs.insert(verts_idxs.begin()+4, m_vertices.size() - 1); + + push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[4]); + push_triangle(verts_idxs[1], verts_idxs[2], verts_idxs[4]); + push_triangle(verts_idxs[2], verts_idxs[3], verts_idxs[4]); + } + + if (sides_to_split == 3) { + m_vertices.emplace_back((m_vertices[verts_idxs[0]].v + m_vertices[verts_idxs[1]].v)/2.); + verts_idxs.insert(verts_idxs.begin()+1, m_vertices.size() - 1); + m_vertices.emplace_back((m_vertices[verts_idxs[2]].v + m_vertices[verts_idxs[3]].v)/2.); + verts_idxs.insert(verts_idxs.begin()+3, m_vertices.size() - 1); + m_vertices.emplace_back((m_vertices[verts_idxs[4]].v + m_vertices[verts_idxs[0]].v)/2.); + verts_idxs.insert(verts_idxs.begin()+5, m_vertices.size() - 1); + + push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[5]); + push_triangle(verts_idxs[1], verts_idxs[2], verts_idxs[3]); + push_triangle(verts_idxs[3], verts_idxs[4], verts_idxs[5]); + push_triangle(verts_idxs[1], verts_idxs[3], verts_idxs[5]); + } + + tr = &m_triangles[facet_idx]; // may have been invalidated + + // And save the children. All children should start in the same state as the triangle we just split. + assert(sides_to_split <= 3); + for (int i=0; i<=sides_to_split; ++i) { + tr->children[i] = m_triangles.size()-1-i; + m_triangles[tr->children[i]].set_state(old_state); + } +} + + +std::map TriangleSelector::serialize() const +{ + std::map out; + for (int i=0; i serialize_recursive; + serialize_recursive = [this, &stored_triangles, &data, &serialize_recursive](int facet_idx) { + const Triangle& tr = m_triangles[facet_idx]; + int split_sides = tr.number_of_split_sides(); + assert( split_sides > 0 && split_sides <= 3); + data |= (split_sides << (stored_triangles * 4)); + + if (tr.is_split()) { + assert(split_sides > 0); + assert(tr.special_side() >= 0 && tr.special_side() <= 3); + data |= (tr.special_side() << (stored_triangles * 4 + 2)); + ++stored_triangles; + for (int child_idx=0; child_idx<=split_sides; ++child_idx) + serialize_recursive(tr.children[child_idx]); + } else { + assert(int8_t(tr.get_state()) <= 3); + data |= (int8_t(tr.get_state()) << (stored_triangles * 4 + 2)); + ++stored_triangles; + } + }; + + serialize_recursive(i); + out[i] = data; + } + + return out; +} + + #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG void TriangleSelector::render_debug(ImGuiWrapper* imgui) { @@ -1323,33 +1399,74 @@ void TriangleSelector::render_debug(ImGuiWrapper* imgui) if (imgui->button("Force garbage collection")) garbage_collect(); + if (imgui->button("Serialize")) { + auto map = serialize(); + for (auto& [idx, data] : map) + std::cout << idx << "\t" << data << std::endl; + } + imgui->end(); - if (m_show_triangles) { - ::glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); + if (! m_show_triangles) + return; - ::glBegin( GL_TRIANGLES); - for (int tr_id=0; tr_id cnts; - for (int i=0; i<3; ++i) - ::glVertex3f(m_vertices[tr.verts_idxs[i]].v[0], - m_vertices[tr.verts_idxs[i]].v[1], - m_vertices[tr.verts_idxs[i]].v[2]); + ::glScalef(1.01f, 1.01f, 1.01f); + + for (int tr_id=0; tr_idpush_geometry(double(m_vertices[tr.verts_idxs[i]].v[0]), + double(m_vertices[tr.verts_idxs[i]].v[1]), + double(m_vertices[tr.verts_idxs[i]].v[2]), + 0., 0., 1.); + va->push_triangle(*cnt, + *cnt+1, + *cnt+2); + *cnt += 3; } + + ::glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); + for (vtype i : {ORIGINAL, SPLIT, INVALID}) { + GLIndexedVertexArray& va = m_varrays[i]; + va.finalize_geometry(true); + if (va.has_VBOs()) { + switch (i) { + case ORIGINAL : ::glColor3f(0.f, 0.f, 1.f); break; + case SPLIT : ::glColor3f(1.f, 0.f, 0.f); break; + case INVALID : ::glColor3f(1.f, 1.f, 0.f); break; + } + va.render(); + } + } + ::glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); } #endif diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index fb14f3c5ee..f3a66eca93 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -48,6 +48,9 @@ public: // Remove all unnecessary data. void garbage_collect(); + // Store the division trees in compact form. + std::map serialize() const; + #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG void render_debug(ImGuiWrapper* imgui); bool m_show_triangles{true}; @@ -86,8 +89,7 @@ private: // Get info on how it's split. bool is_split() const { return number_of_split_sides() != 0; } int number_of_split_sides() const { return number_of_splits; } - int side_to_keep() const { assert(number_of_split_sides() == 2); return special_side_idx; } - int side_to_split() const { assert(number_of_split_sides() == 1); return special_side_idx; } + int special_side() const { assert(is_split()); return special_side_idx; } bool was_split_before() const { return old_number_of_splits != 0; } void forget_history() { old_number_of_splits = 0; } @@ -115,6 +117,10 @@ private: std::vector m_triangles; const TriangleMesh* m_mesh; + GLIndexedVertexArray m_iva_enforcers; + GLIndexedVertexArray m_iva_blockers; + std::array m_varrays; + // Number of invalid triangles (to trigger garbage collection). int m_invalid_triangles; @@ -147,6 +153,7 @@ private: bool is_pointer_in_triangle(int facet_idx) const; bool is_edge_inside_cursor(int facet_idx) const; void push_triangle(int a, int b, int c); + void perform_split(int facet_idx, FacetSupportType old_state); }; From b250c08ec9c76fe3e1895e3ed40536c4c884ecb4 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 2 Jul 2020 12:30:12 +0200 Subject: [PATCH 198/503] TriangleSelector: Serialization and deserialization --- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 153 ++++++++++++++++--- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp | 11 +- 2 files changed, 143 insertions(+), 21 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index f2a6bb8adc..01427ec098 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -1184,15 +1184,26 @@ void TriangleSelector::garbage_collect() } TriangleSelector::TriangleSelector(const TriangleMesh& mesh) + : m_mesh{&mesh} { - for (const stl_vertex& vert : mesh.its.vertices) + reset(); + +} + + +void TriangleSelector::reset() +{ + if (! m_orig_size_indices != 0) // unless this is run from constructor + garbage_collect(); + m_vertices.clear(); + m_triangles.clear(); + for (const stl_vertex& vert : m_mesh->its.vertices) m_vertices.emplace_back(vert); - for (const stl_triangle_vertex_indices& ind : mesh.its.indices) + for (const stl_triangle_vertex_indices& ind : m_mesh->its.indices) push_triangle(ind[0], ind[1], ind[2]); m_orig_size_vertices = m_vertices.size(); m_orig_size_indices = m_triangles.size(); m_invalid_triangles = 0; - m_mesh = &mesh; } void TriangleSelector::render(ImGuiWrapper* imgui) @@ -1339,35 +1350,56 @@ void TriangleSelector::perform_split(int facet_idx, FacetSupportType old_state) } -std::map TriangleSelector::serialize() const +std::map> TriangleSelector::serialize() const { - std::map out; + // Each original triangle of the mesh is assigned a number encoding its state + // or how it is split. Each triangle is encoded by 4 bits (xxyy): + // leaf triangle: xx = FacetSupportType, yy = 0 + // non-leaf: xx = special side, yy = number of split sides + // These are bitwise appended and formed into one 64-bit integer. + + // The function returns a map from original triangle indices to + // stream of bits encoding state and offsprings. + + std::map> out; for (int i=0; i data; // complete encoding of this mesh triangle + int stored_triangles = 0; // how many have been already encoded std::function serialize_recursive; - serialize_recursive = [this, &stored_triangles, &data, &serialize_recursive](int facet_idx) { + serialize_recursive = [this, &serialize_recursive, &stored_triangles, &data](int facet_idx) { const Triangle& tr = m_triangles[facet_idx]; + + // Always save number of split sides. It is zero for unsplit triangles. int split_sides = tr.number_of_split_sides(); - assert( split_sides > 0 && split_sides <= 3); - data |= (split_sides << (stored_triangles * 4)); + assert(split_sides >= 0 && split_sides <= 3); + + //data |= (split_sides << (stored_triangles * 4)); + data.push_back(split_sides & 0b01); + data.push_back(split_sides & 0b10); if (tr.is_split()) { + // If this triangle is split, save which side is split (in case + // of one split) or kept (in case of two splits). The value will + // be ignored for 3-side split. assert(split_sides > 0); assert(tr.special_side() >= 0 && tr.special_side() <= 3); - data |= (tr.special_side() << (stored_triangles * 4 + 2)); + data.push_back(tr.special_side() & 0b01); + data.push_back(tr.special_side() & 0b10); ++stored_triangles; + // Now save all children. for (int child_idx=0; child_idx<=split_sides; ++child_idx) serialize_recursive(tr.children[child_idx]); } else { - assert(int8_t(tr.get_state()) <= 3); - data |= (int8_t(tr.get_state()) << (stored_triangles * 4 + 2)); + // In case this is leaf, we better save information about its state. + assert(int(tr.get_state()) <= 3); + data.push_back(int(tr.get_state()) & 0b01); + data.push_back(int(tr.get_state()) & 0b10); ++stored_triangles; } }; @@ -1379,6 +1411,90 @@ std::map TriangleSelector::serialize() const return out; } +void TriangleSelector::deserialize(const std::map> data) +{ + reset(); // dump any current state + for (const auto& [triangle_id, code] : data) { + assert(triangle_id < int(m_triangles.size())); + int processed_triangles = 0; + struct ProcessingInfo { + int facet_id = 0; + int processed_children = 0; + int total_children = 0; + }; + + // Vector to store all parents that have offsprings. + std::vector parents; + + while (true) { + // Read next triangle info. + int next_code = 0; + for (int i=3; i>=0; --i) { + next_code = next_code << 1; + next_code |= int(code[4 * processed_triangles + i]); + } + ++processed_triangles; + + int num_of_split_sides = (next_code & 0b11); + int num_of_children = num_of_split_sides != 0 ? num_of_split_sides + 1 : 0; + bool is_split = num_of_children != 0; + FacetSupportType state = FacetSupportType(next_code >> 2); + int special_side = (next_code >> 2); + + // Take care of the first iteration separately, so handling of the others is simpler. + if (parents.empty()) { + if (! is_split) { + // root is not split. just set the state and that's it. + m_triangles[triangle_id].set_state(state); + break; + } else { + // root is split, add it into list of parents and split it. + // then go to the next. + parents.push_back({triangle_id, 0, num_of_children}); + m_triangles[triangle_id].set_division(num_of_children-1, special_side); + perform_split(triangle_id, FacetSupportType::NONE); + continue; + } + } + + // This is not the first iteration. This triangle is a child of last seen parent. + assert(! parents.empty()); + assert(parents.back().processed_children < parents.back().total_children); + + if (is_split) { + // split the triangle and save it as parent of the next ones. + const ProcessingInfo& last = parents.back(); + int this_idx = m_triangles[last.facet_id].children[last.processed_children]; + m_triangles[this_idx].set_division(num_of_children-1, special_side); + perform_split(this_idx, FacetSupportType::NONE); + parents.push_back({this_idx, 0, num_of_children}); + } else { + // this triangle belongs to last split one + m_triangles[m_triangles[parents.back().facet_id].children[parents.back().processed_children]].set_state(state); + ++parents.back().processed_children; + } + + + // If all children of the past parent triangle are claimed, move to grandparent. + while (parents.back().processed_children == parents.back().total_children) { + parents.pop_back(); + + if (parents.empty()) + break; + + // And increment the grandparent children counter, because + // we have just finished that branch and got back here. + ++parents.back().processed_children; + } + + // In case we popped back the root, we should be done. + if (parents.empty()) + break; + } + + } +} + #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG void TriangleSelector::render_debug(ImGuiWrapper* imgui) @@ -1399,10 +1515,9 @@ void TriangleSelector::render_debug(ImGuiWrapper* imgui) if (imgui->button("Force garbage collection")) garbage_collect(); - if (imgui->button("Serialize")) { + if (imgui->button("Serialize - deserialize")) { auto map = serialize(); - for (auto& [idx, data] : map) - std::cout << idx << "\t" << data << std::endl; + deserialize(map); } imgui->end(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index f3a66eca93..099c9a30cb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -45,11 +45,18 @@ public: // to be already set. void render(ImGuiWrapper* imgui = nullptr); + // Clear everything and make the tree empty. + void reset(); + // Remove all unnecessary data. void garbage_collect(); - // Store the division trees in compact form. - std::map serialize() const; + // Store the division trees in compact form (a long stream of + // bits for each triangle of the original mesh). + std::map> serialize() const; + + // Load serialized data. Assumes that correct mesh is loaded. + void deserialize(const std::map> data); #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG void render_debug(ImGuiWrapper* imgui); From 6baff45759bc9dd69e2a836bef59f62101d58f77 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 9 Jul 2020 16:25:34 +0200 Subject: [PATCH 199/503] TriangleSelector: Separated frontend/backend, support of multiple volumes, etc. --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/Model.cpp | 19 +- src/libslic3r/Model.hpp | 6 +- src/libslic3r/TriangleSelector.cpp | 654 ++++++++++++++++ src/libslic3r/TriangleSelector.hpp | 149 ++++ src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 736 +------------------ src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp | 145 +--- 7 files changed, 856 insertions(+), 855 deletions(-) create mode 100644 src/libslic3r/TriangleSelector.cpp create mode 100644 src/libslic3r/TriangleSelector.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 881466b399..9f566b4051 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -187,6 +187,8 @@ add_library(libslic3r STATIC Utils.hpp Time.cpp Time.hpp + TriangleSelector.cpp + TriangleSelector.hpp MTUtils.hpp VoronoiOffset.cpp VoronoiOffset.hpp diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 0719cac8cf..b6bae489b4 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -2,6 +2,7 @@ #include "ModelArrange.hpp" #include "Geometry.hpp" #include "MTUtils.hpp" +#include "TriangleSelector.hpp" #include "Format/AMF.hpp" #include "Format/OBJ.hpp" @@ -1833,25 +1834,21 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const std::vector FacetsAnnotation::get_facets(FacetSupportType type) const { std::vector out; - for (auto& [facet_idx, this_type] : m_data) + /*for (auto& [facet_idx, this_type] : m_data) if (this_type == type) out.push_back(facet_idx); - return out; + */return out; } -void FacetsAnnotation::set_facet(int idx, FacetSupportType type) +void FacetsAnnotation::set(const TriangleSelector& selector) { - bool changed = true; - - if (type == FacetSupportType::NONE) - changed = m_data.erase(idx) != 0; - else - m_data[idx] = type; - - if (changed) + std::map> sel_map = selector.serialize(); + if (sel_map != m_data) { + m_data = sel_map; update_timestamp(); + } } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index be298ae4bb..de20e0fdcc 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -39,6 +39,7 @@ class ModelVolume; class ModelWipeTower; class Print; class SLAPrint; +class TriangleSelector; namespace UndoRedo { class StackImpl; @@ -404,8 +405,9 @@ class FacetsAnnotation { public: using ClockType = std::chrono::steady_clock; + const std::map>& get_data() const { return m_data; } + void set(const TriangleSelector& selector); std::vector get_facets(FacetSupportType type) const; - void set_facet(int idx, FacetSupportType type); void clear(); ClockType::time_point get_timestamp() const { return timestamp; } @@ -419,7 +421,7 @@ public: } private: - std::map m_data; + std::map> m_data; ClockType::time_point timestamp; void update_timestamp() { diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp new file mode 100644 index 0000000000..340f5a29a4 --- /dev/null +++ b/src/libslic3r/TriangleSelector.cpp @@ -0,0 +1,654 @@ +#include "TriangleSelector.hpp" +#include "Model.hpp" + + +namespace Slic3r { + + + +// sides_to_split==-1 : just restore previous split +void TriangleSelector::Triangle::set_division(int sides_to_split, int special_side_idx) +{ + assert(sides_to_split >=-1 && sides_to_split <= 3); + assert(special_side_idx >=-1 && special_side_idx < 3); + + // If splitting one or two sides, second argument must be provided. + assert(sides_to_split != 1 || special_side_idx != -1); + assert(sides_to_split != 2 || special_side_idx != -1); + + if (sides_to_split != -1) { + this->number_of_splits = sides_to_split; + if (sides_to_split != 0) { + assert(old_number_of_splits == 0); + this->special_side_idx = special_side_idx; + this->old_number_of_splits = sides_to_split; + } + } + else { + assert(old_number_of_splits != 0); + this->number_of_splits = old_number_of_splits; + // indices of children should still be there. + } +} + + + +void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, + const Vec3f& source, const Vec3f& dir, + float radius_sqr, FacetSupportType new_state) +{ + assert(facet_start < m_orig_size_indices); + assert(is_approx(dir.norm(), 1.f)); + + // Save current cursor center, squared radius and camera direction, + // so we don't have to pass it around. + m_cursor = {hit, source, dir, radius_sqr}; + + // Now start with the facet the pointer points to and check all adjacent facets. + std::vector facets_to_check{facet_start}; + std::vector visited(m_orig_size_indices, false); // keep track of facets we already processed + int facet_idx = 0; // index into facets_to_check + while (facet_idx < int(facets_to_check.size())) { + int facet = facets_to_check[facet_idx]; + if (! visited[facet]) { + if (select_triangle(facet, new_state)) { + // add neighboring facets to list to be proccessed later + for (int n=0; n<3; ++n) { + if (faces_camera(m_mesh->stl.neighbors_start[facet].neighbor[n])) + facets_to_check.push_back(m_mesh->stl.neighbors_start[facet].neighbor[n]); + } + } + } + visited[facet] = true; + ++facet_idx; + } +} + + + +// Selects either the whole triangle (discarding any children it had), or divides +// the triangle recursively, selecting just subtriangles truly inside the circle. +// This is done by an actual recursive call. Returns false if the triangle is +// outside the cursor. +bool TriangleSelector::select_triangle(int facet_idx, FacetSupportType type, bool recursive_call) +{ + assert(facet_idx < int(m_triangles.size())); + + Triangle* tr = &m_triangles[facet_idx]; + if (! tr->valid) + return false; + + int num_of_inside_vertices = vertices_inside(facet_idx); + + if (num_of_inside_vertices == 0 + && ! is_pointer_in_triangle(facet_idx) + && ! is_edge_inside_cursor(facet_idx)) + return false; + + if (num_of_inside_vertices == 3) { + // dump any subdivision and select whole triangle + undivide_triangle(facet_idx); + tr->set_state(type); + } else { + // the triangle is partially inside, let's recursively divide it + // (if not already) and try selecting its children. + + if (! tr->is_split() && tr->get_state() == type) { + // This is leaf triangle that is already of correct type as a whole. + // No need to split, all children would end up selected anyway. + return true; + } + + split_triangle(facet_idx); + tr = &m_triangles[facet_idx]; // might have been invalidated + + + int num_of_children = tr->number_of_split_sides() + 1; + if (num_of_children != 1) { + for (int i=0; ichildren.size())); + assert(tr->children[i] < int(m_triangles.size())); + + select_triangle(tr->children[i], type, true); + tr = &m_triangles[facet_idx]; // might have been invalidated + } + } + } + + if (! recursive_call) { + // In case that all children are leafs and have the same state now, + // they may be removed and substituted by the parent triangle. + remove_useless_children(facet_idx); + + // Make sure that we did not lose track of invalid triangles. + assert(m_invalid_triangles == std::count_if(m_triangles.begin(), m_triangles.end(), + [](const Triangle& tr) { return ! tr.valid; })); + + // Do garbage collection maybe? + if (2*m_invalid_triangles > int(m_triangles.size())) + garbage_collect(); + } + return true; +} + + +void TriangleSelector::split_triangle(int facet_idx) +{ + if (m_triangles[facet_idx].is_split()) { + // The triangle is divided already. + return; + } + + Triangle* tr = &m_triangles[facet_idx]; + + FacetSupportType old_type = tr->get_state(); + + if (tr->was_split_before() != 0) { + // This triangle is not split at the moment, but was at one point + // in history. We can just restore it and resurrect its children. + tr->set_division(-1); + for (int i=0; i<=tr->number_of_split_sides(); ++i) { + m_triangles[tr->children[i]].set_state(old_type); + m_triangles[tr->children[i]].valid = true; + --m_invalid_triangles; + } + return; + } + + // If we got here, we are about to actually split the triangle. + const double limit_squared = m_edge_limit_sqr; + + std::array& facet = tr->verts_idxs; + const stl_vertex* pts[3] = { &m_vertices[facet[0]].v, &m_vertices[facet[1]].v, &m_vertices[facet[2]].v}; + double sides[3] = { (*pts[2]-*pts[1]).squaredNorm(), + (*pts[0]-*pts[2]).squaredNorm(), + (*pts[1]-*pts[0]).squaredNorm() }; + + std::vector sides_to_split; + int side_to_keep = -1; + for (int pt_idx = 0; pt_idx<3; ++pt_idx) { + if (sides[pt_idx] > limit_squared) + sides_to_split.push_back(pt_idx); + else + side_to_keep = pt_idx; + } + if (sides_to_split.empty()) { + // This shall be unselected. + tr->set_division(0); + return; + } + + // Save how the triangle will be split. Second argument makes sense only for one + // or two split sides, otherwise the value is ignored. + tr->set_division(sides_to_split.size(), + sides_to_split.size() == 2 ? side_to_keep : sides_to_split[0]); + + perform_split(facet_idx, old_type); +} + + +// Calculate distance of a point from a line. +bool TriangleSelector::is_point_inside_cursor(const Vec3f& point) const +{ + Vec3f diff = m_cursor.center - point; + return (diff - diff.dot(m_cursor.dir) * m_cursor.dir).squaredNorm() < m_cursor.radius_sqr; +} + + +// Is pointer in a triangle? +bool TriangleSelector::is_pointer_in_triangle(int facet_idx) const +{ + auto signed_volume_sign = [](const Vec3f& a, const Vec3f& b, + const Vec3f& c, const Vec3f& d) -> bool { + return ((b-a).cross(c-a)).dot(d-a) > 0.; + }; + + const Vec3f& p1 = m_vertices[m_triangles[facet_idx].verts_idxs[0]].v; + const Vec3f& p2 = m_vertices[m_triangles[facet_idx].verts_idxs[1]].v; + const Vec3f& p3 = m_vertices[m_triangles[facet_idx].verts_idxs[2]].v; + const Vec3f& q1 = m_cursor.center + m_cursor.dir; + const Vec3f q2 = m_cursor.center - m_cursor.dir; + + if (signed_volume_sign(q1,p1,p2,p3) != signed_volume_sign(q2,p1,p2,p3)) { + bool pos = signed_volume_sign(q1,q2,p1,p2); + if (signed_volume_sign(q1,q2,p2,p3) == pos && signed_volume_sign(q1,q2,p3,p1) == pos) + return true; + } + return false; +} + + + +// Determine whether this facet is potentially visible (still can be obscured). +bool TriangleSelector::faces_camera(int facet) const +{ + assert(facet < m_orig_size_indices); + // The normal is cached in mesh->stl, use it. + return (m_mesh->stl.facet_start[facet].normal.dot(m_cursor.dir) < 0.); +} + + +// How many vertices of a triangle are inside the circle? +int TriangleSelector::vertices_inside(int facet_idx) const +{ + int inside = 0; + for (size_t i=0; i<3; ++i) { + if (is_point_inside_cursor(m_vertices[m_triangles[facet_idx].verts_idxs[i]].v)) + ++inside; + } + return inside; +} + + +// Is edge inside cursor? +bool TriangleSelector::is_edge_inside_cursor(int facet_idx) const +{ + Vec3f pts[3]; + for (int i=0; i<3; ++i) + pts[i] = m_vertices[m_triangles[facet_idx].verts_idxs[i]].v; + + const Vec3f& p = m_cursor.center; + + for (int side = 0; side < 3; ++side) { + const Vec3f& a = pts[side]; + const Vec3f& b = pts[side<2 ? side+1 : 0]; + Vec3f s = (b-a).normalized(); + float t = (p-a).dot(s); + Vec3f vector = a+t*s - p; + + // vector is 3D vector from center to the intersection. What we want to + // measure is length of its projection onto plane perpendicular to dir. + float dist_sqr = vector.squaredNorm() - std::pow(vector.dot(m_cursor.dir), 2.f); + if (dist_sqr < m_cursor.radius_sqr && t>=0.f && t<=(b-a).norm()) + return true; + } + return false; +} + + + +// Recursively remove all subtriangles. +void TriangleSelector::undivide_triangle(int facet_idx) +{ + assert(facet_idx < int(m_triangles.size())); + Triangle& tr = m_triangles[facet_idx]; + + if (tr.is_split()) { + for (int i=0; i<=tr.number_of_split_sides(); ++i) { + undivide_triangle(tr.children[i]); + m_triangles[tr.children[i]].valid = false; + ++m_invalid_triangles; + } + tr.set_division(0); // not split + } +} + + +void TriangleSelector::remove_useless_children(int facet_idx) +{ + // Check that all children are leafs of the same type. If not, try to + // make them (recursive call). Remove them if sucessful. + + assert(facet_idx < int(m_triangles.size()) && m_triangles[facet_idx].valid); + Triangle& tr = m_triangles[facet_idx]; + + if (! tr.is_split()) { + // This is a leaf, there nothing to do. This can happen during the + // first (non-recursive call). Shouldn't otherwise. + return; + } + + // Call this for all non-leaf children. + for (int child_idx=0; child_idx<=tr.number_of_split_sides(); ++child_idx) { + assert(child_idx < int(m_triangles.size()) && m_triangles[child_idx].valid); + if (m_triangles[tr.children[child_idx]].is_split()) + remove_useless_children(tr.children[child_idx]); + } + + + // Return if a child is not leaf or two children differ in type. + FacetSupportType first_child_type = FacetSupportType::NONE; + for (int child_idx=0; child_idx<=tr.number_of_split_sides(); ++child_idx) { + if (m_triangles[tr.children[child_idx]].is_split()) + return; + if (child_idx == 0) + first_child_type = m_triangles[tr.children[0]].get_state(); + else if (m_triangles[tr.children[child_idx]].get_state() != first_child_type) + return; + } + + // If we got here, the children can be removed. + undivide_triangle(facet_idx); + tr.set_state(first_child_type); +} + + + +void TriangleSelector::garbage_collect() +{ + // First make a map from old to new triangle indices. + int new_idx = m_orig_size_indices; + std::vector new_triangle_indices(m_triangles.size(), -1); + for (int i = m_orig_size_indices; i new_vertices_indices(m_vertices.size(), -1); + for (int i=m_orig_size_vertices; i= 0); + if (m_vertices[i].ref_cnt != 0) { + new_vertices_indices[i] = new_idx; + ++new_idx; + } + } + + // We can remove all invalid triangles and vertices that are no longer referenced. + m_triangles.erase(std::remove_if(m_triangles.begin()+m_orig_size_indices, m_triangles.end(), + [](const Triangle& tr) { return ! tr.valid; }), + m_triangles.end()); + m_vertices.erase(std::remove_if(m_vertices.begin()+m_orig_size_vertices, m_vertices.end(), + [](const Vertex& vert) { return vert.ref_cnt == 0; }), + m_vertices.end()); + + // Now go through all remaining triangles and update changed indices. + for (Triangle& tr : m_triangles) { + assert(tr.valid); + + if (tr.is_split()) { + // There are children. Update their indices. + for (int j=0; j<=tr.number_of_split_sides(); ++j) { + assert(new_triangle_indices[tr.children[j]] != -1); + tr.children[j] = new_triangle_indices[tr.children[j]]; + } + } + + // Update indices into m_vertices. The original vertices are never + // touched and need not be reindexed. + for (int& idx : tr.verts_idxs) { + if (idx >= m_orig_size_vertices) { + assert(new_vertices_indices[idx] != -1); + idx = new_vertices_indices[idx]; + } + } + + // If this triangle was split before, forget it. + // Children referenced in the cache are dead by now. + tr.forget_history(); + } + + m_invalid_triangles = 0; +} + +TriangleSelector::TriangleSelector(const TriangleMesh& mesh) + : m_mesh{&mesh} +{ + reset(); +} + + +void TriangleSelector::reset() +{ + if (! m_orig_size_indices != 0) // unless this is run from constructor + garbage_collect(); + m_vertices.clear(); + m_triangles.clear(); + for (const stl_vertex& vert : m_mesh->its.vertices) + m_vertices.emplace_back(vert); + for (const stl_triangle_vertex_indices& ind : m_mesh->its.indices) + push_triangle(ind[0], ind[1], ind[2]); + m_orig_size_vertices = m_vertices.size(); + m_orig_size_indices = m_triangles.size(); + m_invalid_triangles = 0; +} + + + + + +void TriangleSelector::set_edge_limit(float edge_limit) +{ + float new_limit_sqr = std::pow(edge_limit, 2.f); + + if (new_limit_sqr != m_edge_limit_sqr) { + m_edge_limit_sqr = new_limit_sqr; + + // The way how triangles split may be different now, forget + // all cached splits. + garbage_collect(); + } +} + + + +void TriangleSelector::push_triangle(int a, int b, int c) +{ + for (int i : {a, b, c}) { + assert(i >= 0 && i < int(m_vertices.size())); + ++m_vertices[i].ref_cnt; + } + m_triangles.emplace_back(a, b, c); +} + + +void TriangleSelector::perform_split(int facet_idx, FacetSupportType old_state) +{ + Triangle* tr = &m_triangles[facet_idx]; + + assert(tr->is_split()); + + // Read info about how to split this triangle. + int sides_to_split = tr->number_of_split_sides(); + + // indices of triangle vertices + std::vector verts_idxs; + int idx = tr->special_side(); + for (int j=0; j<3; ++j) { + verts_idxs.push_back(tr->verts_idxs[idx++]); + if (idx == 3) + idx = 0; + } + + if (sides_to_split == 1) { + m_vertices.emplace_back((m_vertices[verts_idxs[1]].v + m_vertices[verts_idxs[2]].v)/2.); + verts_idxs.insert(verts_idxs.begin()+2, m_vertices.size() - 1); + + push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[2]); + push_triangle(verts_idxs[2], verts_idxs[3], verts_idxs[0]); + } + + if (sides_to_split == 2) { + m_vertices.emplace_back((m_vertices[verts_idxs[0]].v + m_vertices[verts_idxs[1]].v)/2.); + verts_idxs.insert(verts_idxs.begin()+1, m_vertices.size() - 1); + + m_vertices.emplace_back((m_vertices[verts_idxs[0]].v + m_vertices[verts_idxs[3]].v)/2.); + verts_idxs.insert(verts_idxs.begin()+4, m_vertices.size() - 1); + + push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[4]); + push_triangle(verts_idxs[1], verts_idxs[2], verts_idxs[4]); + push_triangle(verts_idxs[2], verts_idxs[3], verts_idxs[4]); + } + + if (sides_to_split == 3) { + m_vertices.emplace_back((m_vertices[verts_idxs[0]].v + m_vertices[verts_idxs[1]].v)/2.); + verts_idxs.insert(verts_idxs.begin()+1, m_vertices.size() - 1); + m_vertices.emplace_back((m_vertices[verts_idxs[2]].v + m_vertices[verts_idxs[3]].v)/2.); + verts_idxs.insert(verts_idxs.begin()+3, m_vertices.size() - 1); + m_vertices.emplace_back((m_vertices[verts_idxs[4]].v + m_vertices[verts_idxs[0]].v)/2.); + verts_idxs.insert(verts_idxs.begin()+5, m_vertices.size() - 1); + + push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[5]); + push_triangle(verts_idxs[1], verts_idxs[2], verts_idxs[3]); + push_triangle(verts_idxs[3], verts_idxs[4], verts_idxs[5]); + push_triangle(verts_idxs[1], verts_idxs[3], verts_idxs[5]); + } + + tr = &m_triangles[facet_idx]; // may have been invalidated + + // And save the children. All children should start in the same state as the triangle we just split. + assert(sides_to_split <= 3); + for (int i=0; i<=sides_to_split; ++i) { + tr->children[i] = m_triangles.size()-1-i; + m_triangles[tr->children[i]].set_state(old_state); + } +} + + +std::map> TriangleSelector::serialize() const +{ + // Each original triangle of the mesh is assigned a number encoding its state + // or how it is split. Each triangle is encoded by 4 bits (xxyy): + // leaf triangle: xx = FacetSupportType, yy = 0 + // non-leaf: xx = special side, yy = number of split sides + // These are bitwise appended and formed into one 64-bit integer. + + // The function returns a map from original triangle indices to + // stream of bits encoding state and offsprings. + + std::map> out; + for (int i=0; i data; // complete encoding of this mesh triangle + int stored_triangles = 0; // how many have been already encoded + + std::function serialize_recursive; + serialize_recursive = [this, &serialize_recursive, &stored_triangles, &data](int facet_idx) { + const Triangle& tr = m_triangles[facet_idx]; + + // Always save number of split sides. It is zero for unsplit triangles. + int split_sides = tr.number_of_split_sides(); + assert(split_sides >= 0 && split_sides <= 3); + + //data |= (split_sides << (stored_triangles * 4)); + data.push_back(split_sides & 0b01); + data.push_back(split_sides & 0b10); + + if (tr.is_split()) { + // If this triangle is split, save which side is split (in case + // of one split) or kept (in case of two splits). The value will + // be ignored for 3-side split. + assert(split_sides > 0); + assert(tr.special_side() >= 0 && tr.special_side() <= 3); + data.push_back(tr.special_side() & 0b01); + data.push_back(tr.special_side() & 0b10); + ++stored_triangles; + // Now save all children. + for (int child_idx=0; child_idx<=split_sides; ++child_idx) + serialize_recursive(tr.children[child_idx]); + } else { + // In case this is leaf, we better save information about its state. + assert(int(tr.get_state()) <= 3); + data.push_back(int(tr.get_state()) & 0b01); + data.push_back(int(tr.get_state()) & 0b10); + ++stored_triangles; + } + }; + + serialize_recursive(i); + out[i] = data; + } + + return out; +} + +void TriangleSelector::deserialize(const std::map> data) +{ + reset(); // dump any current state + for (const auto& [triangle_id, code] : data) { + assert(triangle_id < int(m_triangles.size())); + int processed_triangles = 0; + struct ProcessingInfo { + int facet_id = 0; + int processed_children = 0; + int total_children = 0; + }; + + // Vector to store all parents that have offsprings. + std::vector parents; + + while (true) { + // Read next triangle info. + int next_code = 0; + for (int i=3; i>=0; --i) { + next_code = next_code << 1; + next_code |= int(code[4 * processed_triangles + i]); + } + ++processed_triangles; + + int num_of_split_sides = (next_code & 0b11); + int num_of_children = num_of_split_sides != 0 ? num_of_split_sides + 1 : 0; + bool is_split = num_of_children != 0; + FacetSupportType state = FacetSupportType(next_code >> 2); + int special_side = (next_code >> 2); + + // Take care of the first iteration separately, so handling of the others is simpler. + if (parents.empty()) { + if (! is_split) { + // root is not split. just set the state and that's it. + m_triangles[triangle_id].set_state(state); + break; + } else { + // root is split, add it into list of parents and split it. + // then go to the next. + parents.push_back({triangle_id, 0, num_of_children}); + m_triangles[triangle_id].set_division(num_of_children-1, special_side); + perform_split(triangle_id, FacetSupportType::NONE); + continue; + } + } + + // This is not the first iteration. This triangle is a child of last seen parent. + assert(! parents.empty()); + assert(parents.back().processed_children < parents.back().total_children); + + if (is_split) { + // split the triangle and save it as parent of the next ones. + const ProcessingInfo& last = parents.back(); + int this_idx = m_triangles[last.facet_id].children[last.processed_children]; + m_triangles[this_idx].set_division(num_of_children-1, special_side); + perform_split(this_idx, FacetSupportType::NONE); + parents.push_back({this_idx, 0, num_of_children}); + } else { + // this triangle belongs to last split one + m_triangles[m_triangles[parents.back().facet_id].children[parents.back().processed_children]].set_state(state); + ++parents.back().processed_children; + } + + + // If all children of the past parent triangle are claimed, move to grandparent. + while (parents.back().processed_children == parents.back().total_children) { + parents.pop_back(); + + if (parents.empty()) + break; + + // And increment the grandparent children counter, because + // we have just finished that branch and got back here. + ++parents.back().processed_children; + } + + // In case we popped back the root, we should be done. + if (parents.empty()) + break; + } + + } +} + + + + +} // namespace Slic3r diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp new file mode 100644 index 0000000000..943029548c --- /dev/null +++ b/src/libslic3r/TriangleSelector.hpp @@ -0,0 +1,149 @@ +#ifndef libslic3r_TriangleSelector_hpp_ +#define libslic3r_TriangleSelector_hpp_ + +#define PRUSASLICER_TRIANGLE_SELECTOR_DEBUG + + +#include "Point.hpp" +#include "TriangleMesh.hpp" + +namespace Slic3r { + +enum class FacetSupportType : int8_t; + + + +// Following class holds information about selected triangles. It also has power +// to recursively subdivide the triangles and make the selection finer. +class TriangleSelector { +public: + void set_edge_limit(float edge_limit); + + // Create new object on a TriangleMesh. The referenced mesh must + // stay valid, a ptr to it is saved and used. + explicit TriangleSelector(const TriangleMesh& mesh); + + // Select all triangles fully inside the circle, subdivide where needed. + void select_patch(const Vec3f& hit, // point where to start + int facet_start, // facet that point belongs to + const Vec3f& source, // camera position (mesh coords) + const Vec3f& dir, // direction of the ray (mesh coords) + float radius_sqr, // squared radius of the cursor + FacetSupportType new_state); // enforcer or blocker? + + + // Clear everything and make the tree empty. + void reset(); + + // Remove all unnecessary data. + void garbage_collect(); + + // Store the division trees in compact form (a long stream of + // bits for each triangle of the original mesh). + std::map> serialize() const; + + // Load serialized data. Assumes that correct mesh is loaded. + void deserialize(const std::map> data); + + +protected: + // Triangle and info about how it's split. + class Triangle { + public: + // Use TriangleSelector::push_triangle to create a new triangle. + // It increments/decrements reference counter on vertices. + Triangle(int a, int b, int c) + : verts_idxs{a, b, c}, + state{FacetSupportType(0)}, + number_of_splits{0}, + special_side_idx{0}, + old_number_of_splits{0} + {} + // Indices into m_vertices. + std::array verts_idxs; + + // Is this triangle valid or marked to be removed? + bool valid{true}; + + // Children triangles. + std::array children; + + // Set the division type. + void set_division(int sides_to_split, int special_side_idx = -1); + + // Get/set current state. + void set_state(FacetSupportType type) { assert(! is_split()); state = type; } + FacetSupportType get_state() const { assert(! is_split()); return state; } + + // Get info on how it's split. + bool is_split() const { return number_of_split_sides() != 0; } + int number_of_split_sides() const { return number_of_splits; } + int special_side() const { assert(is_split()); return special_side_idx; } + bool was_split_before() const { return old_number_of_splits != 0; } + void forget_history() { old_number_of_splits = 0; } + + private: + int number_of_splits; + int special_side_idx; + FacetSupportType state; + + // How many children were spawned during last split? + // Is not reset on remerging the triangle. + int old_number_of_splits; + }; + + struct Vertex { + explicit Vertex(const stl_vertex& vert) + : v{vert}, + ref_cnt{0} + {} + stl_vertex v; + int ref_cnt; + }; + + // Lists of vertices and triangles, both original and new + std::vector m_vertices; + std::vector m_triangles; + const TriangleMesh* m_mesh; + + // Number of invalid triangles (to trigger garbage collection). + int m_invalid_triangles; + + // Limiting length of triangle side (squared). + float m_edge_limit_sqr = 1.f; + + // Number of original vertices and triangles. + int m_orig_size_vertices; + int m_orig_size_indices; + + // Cache for cursor position, radius and direction. + struct Cursor { + Vec3f center; + Vec3f source; + Vec3f dir; + float radius_sqr; + }; + + Cursor m_cursor; + + // Private functions: + bool select_triangle(int facet_idx, FacetSupportType type, + bool recursive_call = false); + bool is_point_inside_cursor(const Vec3f& point) const; + int vertices_inside(int facet_idx) const; + bool faces_camera(int facet) const; + void undivide_triangle(int facet_idx); + void split_triangle(int facet_idx); + void remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant. + bool is_pointer_in_triangle(int facet_idx) const; + bool is_edge_inside_cursor(int facet_idx) const; + void push_triangle(int a, int b, int c); + void perform_split(int facet_idx, FacetSupportType old_state); +}; + + + + +} // namespace Slic3r + +#endif // libslic3r_TriangleSelector_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 01427ec098..ca7695907e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -89,15 +89,12 @@ void GLGizmoFdmSupports::set_fdm_support_data(ModelObject* model_object, const S void GLGizmoFdmSupports::on_render() const { - //const Selection& selection = m_parent.get_selection(); + const Selection& selection = m_parent.get_selection(); glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_DEPTH_TEST)); - //render_triangles(selection); - - if (m_triangle_selector && ! m_setting_angle) - m_triangle_selector->render(m_imgui); + render_triangles(selection); m_c->object_clipper()->render_cut(); render_cursor_circle(); @@ -148,14 +145,9 @@ void GLGizmoFdmSupports::render_triangles(const Selection& selection) const glsafe(::glPushMatrix()); glsafe(::glMultMatrixd(trafo_matrix.data())); - // Now render both enforcers and blockers. - //for (int i=0; i<2; ++i) { - // glsafe(::glColor4f(i ? 1.f : 0.2f, 0.2f, i ? 0.2f : 1.0f, 0.5f)); - // for (const GLIndexedVertexArray& iva : m_ivas[mesh_id][i]) { - if (m_iva.has_VBOs()) - m_iva.render(); - // } - //} + if (! m_setting_angle) + m_triangle_selectors[mesh_id]->render(m_imgui); + glsafe(::glPopMatrix()); if (is_left_handed) glsafe(::glFrontFace(GL_CCW)); @@ -212,16 +204,14 @@ void GLGizmoFdmSupports::render_cursor_circle() const void GLGizmoFdmSupports::update_model_object() const { - return; - /*ModelObject* mo = m_c->selection_info()->model_object(); + ModelObject* mo = m_c->selection_info()->model_object(); int idx = -1; for (ModelVolume* mv : mo->volumes) { ++idx; if (! mv->is_model_part()) continue; - for (int i=0; im_supported_facets.set_facet(i, m_selected_facets[idx][i]); - }*/ + mv->m_supported_facets.set(*m_triangle_selectors[idx].get()); + } } @@ -230,21 +220,7 @@ void GLGizmoFdmSupports::update_from_model_object() wxBusyCursor wait; const ModelObject* mo = m_c->selection_info()->model_object(); - /*size_t num_of_volumes = 0; - for (const ModelVolume* mv : mo->volumes) - if (mv->is_model_part()) - ++num_of_volumes; - m_selected_facets.resize(num_of_volumes);*/ - - m_triangle_selector = std::make_unique(mo->volumes.front()->mesh()); - - /*m_ivas.clear(); - m_ivas.resize(num_of_volumes); - for (size_t i=0; ivolumes) { @@ -256,17 +232,9 @@ void GLGizmoFdmSupports::update_from_model_object() // This mesh does not account for the possible Z up SLA offset. const TriangleMesh* mesh = &mv->mesh(); - m_selected_facets[volume_id].assign(mesh->its.indices.size(), FacetSupportType::NONE); - - // Load current state from ModelVolume. - for (FacetSupportType type : {FacetSupportType::ENFORCER, FacetSupportType::BLOCKER}) { - const std::vector& list = mv->m_supported_facets.get_facets(type); - for (int i : list) - m_selected_facets[volume_id][i] = type; - } - update_vertex_buffers(mesh, volume_id, FacetSupportType::ENFORCER); - update_vertex_buffers(mesh, volume_id, FacetSupportType::BLOCKER); - }*/ + m_triangle_selectors.emplace_back(std::make_unique(*mesh)); + m_triangle_selectors.back()->deserialize(mv->m_supported_facets.get_data()); + } } @@ -321,7 +289,7 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous || action == SLAGizmoEventType::RightDown || (action == SLAGizmoEventType::Dragging && m_button_down != Button::None)) { - if (! m_triangle_selector) + if (m_triangle_selectors.empty()) return false; FacetSupportType new_state = FacetSupportType::NONE; @@ -426,23 +394,20 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous } } - // FIXME: just for now, only process first mesh - if (mesh_id != 0) - return false; - const Transform3d& trafo_matrix = trafo_matrices[mesh_id]; // Calculate how far can a point be from the line (in mesh coords). // FIXME: The scaling of the mesh can be non-uniform. const Vec3d sf = Geometry::Transformation(trafo_matrix).get_scaling_factor(); const float avg_scaling = (sf(0) + sf(1) + sf(2))/3.; - const float limit = pow(m_cursor_radius/avg_scaling , 2.f); + const float limit = std::pow(m_cursor_radius/avg_scaling , 2.f); // Calculate direction from camera to the hit (in mesh coords): Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast(); Vec3f dir = (closest_hit - camera_pos).normalized(); - m_triangle_selector->select_patch(closest_hit, closest_facet, camera_pos, + assert(mesh_id < int(m_triangle_selectors.size())); + m_triangle_selectors[mesh_id]->select_patch(closest_hit, closest_facet, camera_pos, dir, limit, new_state); return true; @@ -468,7 +433,7 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous } -void GLGizmoFdmSupports::update_vertex_buffers(const TriangleMesh* mesh, +/*void GLGizmoFdmSupports::update_vertex_buffers(const TriangleMesh* mesh, int mesh_id, FacetSupportType type, const std::vector* new_facets) @@ -506,7 +471,7 @@ void GLGizmoFdmSupports::update_vertex_buffers(const TriangleMesh* mesh, if (pushed) m_iva.finalize_geometry(true); - /*} else { + } else { // we are only appending - let's make new vertex array and let the old ones live ivas.push_back(GLIndexedVertexArray()); for (size_t facet_idx : *new_facets) @@ -516,9 +481,9 @@ void GLGizmoFdmSupports::update_vertex_buffers(const TriangleMesh* mesh, ivas.back().finalize_geometry(true); else ivas.pop_back(); - }*/ + } -} +}*/ void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool overwrite, bool block) @@ -761,8 +726,8 @@ void GLGizmoFdmSupports::on_set_state() } activate_internal_undo_redo_stack(false); m_old_mo_id = -1; - m_iva.release_geometry(); - m_selected_facets.clear(); + //m_iva.release_geometry(); + m_triangle_selectors.clear(); } m_old_state = m_state; } @@ -800,422 +765,14 @@ void GLGizmoFdmSupports::on_save(cereal::BinaryOutputArchive&) const } -// sides_to_split==-1 : just restore previous split -void TriangleSelector::Triangle::set_division(int sides_to_split, int special_side_idx) +void TriangleSelectorGUI::render(ImGuiWrapper* imgui) { - assert(sides_to_split >=-1 && sides_to_split <= 3); - assert(special_side_idx >=-1 && special_side_idx < 3); - - // If splitting one or two sides, second argument must be provided. - assert(sides_to_split != 1 || special_side_idx != -1); - assert(sides_to_split != 2 || special_side_idx != -1); - - if (sides_to_split != -1) { - this->number_of_splits = sides_to_split; - if (sides_to_split != 0) { - assert(old_number_of_splits == 0); - this->special_side_idx = special_side_idx; - this->old_number_of_splits = sides_to_split; - } - } - else { - assert(old_number_of_splits != 0); - this->number_of_splits = old_number_of_splits; - // indices of children should still be there. - } -} - - - -void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, - const Vec3f& source, const Vec3f& dir, - float radius_sqr, FacetSupportType new_state) -{ - assert(facet_start < m_orig_size_indices); - assert(is_approx(dir.norm(), 1.f)); - - // Save current cursor center, squared radius and camera direction, - // so we don't have to pass it around. - m_cursor = {hit, source, dir, radius_sqr}; - - // Now start with the facet the pointer points to and check all adjacent facets. - std::vector facets_to_check{facet_start}; - std::vector visited(m_orig_size_indices, false); // keep track of facets we already processed - int facet_idx = 0; // index into facets_to_check - while (facet_idx < int(facets_to_check.size())) { - int facet = facets_to_check[facet_idx]; - if (! visited[facet]) { - if (select_triangle(facet, new_state)) { - // add neighboring facets to list to be proccessed later - for (int n=0; n<3; ++n) { - if (faces_camera(m_mesh->stl.neighbors_start[facet].neighbor[n])) - facets_to_check.push_back(m_mesh->stl.neighbors_start[facet].neighbor[n]); - } - } - } - visited[facet] = true; - ++facet_idx; - } -} - - - -// Selects either the whole triangle (discarding any children it had), or divides -// the triangle recursively, selecting just subtriangles truly inside the circle. -// This is done by an actual recursive call. Returns false if the triangle is -// outside the cursor. -bool TriangleSelector::select_triangle(int facet_idx, FacetSupportType type, bool recursive_call) -{ - assert(facet_idx < int(m_triangles.size())); - - Triangle* tr = &m_triangles[facet_idx]; - if (! tr->valid) - return false; - - int num_of_inside_vertices = vertices_inside(facet_idx); - - if (num_of_inside_vertices == 0 - && ! is_pointer_in_triangle(facet_idx) - && ! is_edge_inside_cursor(facet_idx)) - return false; - - if (num_of_inside_vertices == 3) { - // dump any subdivision and select whole triangle - undivide_triangle(facet_idx); - tr->set_state(type); - } else { - // the triangle is partially inside, let's recursively divide it - // (if not already) and try selecting its children. - - if (! tr->is_split() && tr->get_state() == type) { - // This is leaf triangle that is already of correct type as a whole. - // No need to split, all children would end up selected anyway. - return true; - } - - split_triangle(facet_idx); - tr = &m_triangles[facet_idx]; // might have been invalidated - - - int num_of_children = tr->number_of_split_sides() + 1; - if (num_of_children != 1) { - for (int i=0; ichildren.size())); - assert(tr->children[i] < int(m_triangles.size())); - - select_triangle(tr->children[i], type, true); - tr = &m_triangles[facet_idx]; // might have been invalidated - } - } - } - - if (! recursive_call) { - // In case that all children are leafs and have the same state now, - // they may be removed and substituted by the parent triangle. - remove_useless_children(facet_idx); - - // Make sure that we did not lose track of invalid triangles. - assert(m_invalid_triangles == std::count_if(m_triangles.begin(), m_triangles.end(), - [](const Triangle& tr) { return ! tr.valid; })); - - // Do garbage collection maybe? - if (2*m_invalid_triangles > int(m_triangles.size())) - garbage_collect(); - } - return true; -} - - -void TriangleSelector::split_triangle(int facet_idx) -{ - if (m_triangles[facet_idx].is_split()) { - // The triangle is divided already. - return; - } - - Triangle* tr = &m_triangles[facet_idx]; - - FacetSupportType old_type = tr->get_state(); - - if (tr->was_split_before() != 0) { - // This triangle is not split at the moment, but was at one point - // in history. We can just restore it and resurrect its children. - tr->set_division(-1); - for (int i=0; i<=tr->number_of_split_sides(); ++i) { - m_triangles[tr->children[i]].set_state(old_type); - m_triangles[tr->children[i]].valid = true; - --m_invalid_triangles; - } - return; - } - - // If we got here, we are about to actually split the triangle. - const double limit_squared = m_edge_limit_sqr; - - std::array& facet = tr->verts_idxs; - const stl_vertex* pts[3] = { &m_vertices[facet[0]].v, &m_vertices[facet[1]].v, &m_vertices[facet[2]].v}; - double sides[3] = { (*pts[2]-*pts[1]).squaredNorm(), - (*pts[0]-*pts[2]).squaredNorm(), - (*pts[1]-*pts[0]).squaredNorm() }; - - std::vector sides_to_split; - int side_to_keep = -1; - for (int pt_idx = 0; pt_idx<3; ++pt_idx) { - if (sides[pt_idx] > limit_squared) - sides_to_split.push_back(pt_idx); - else - side_to_keep = pt_idx; - } - if (sides_to_split.empty()) { - // This shall be unselected. - tr->set_division(0); - return; - } - - // Save how the triangle will be split. Second argument makes sense only for one - // or two split sides, otherwise the value is ignored. - tr->set_division(sides_to_split.size(), - sides_to_split.size() == 2 ? side_to_keep : sides_to_split[0]); - - perform_split(facet_idx, old_type); -} - - -// Calculate distance of a point from a line. -bool TriangleSelector::is_point_inside_cursor(const Vec3f& point) const -{ - Vec3f diff = m_cursor.center - point; - return (diff - diff.dot(m_cursor.dir) * m_cursor.dir).squaredNorm() < m_cursor.radius_sqr; -} - - -// Is pointer in a triangle? -bool TriangleSelector::is_pointer_in_triangle(int facet_idx) const -{ - auto signed_volume_sign = [](const Vec3f& a, const Vec3f& b, - const Vec3f& c, const Vec3f& d) -> bool { - return ((b-a).cross(c-a)).dot(d-a) > 0.; - }; - - const Vec3f& p1 = m_vertices[m_triangles[facet_idx].verts_idxs[0]].v; - const Vec3f& p2 = m_vertices[m_triangles[facet_idx].verts_idxs[1]].v; - const Vec3f& p3 = m_vertices[m_triangles[facet_idx].verts_idxs[2]].v; - const Vec3f& q1 = m_cursor.center + m_cursor.dir; - const Vec3f q2 = m_cursor.center - m_cursor.dir; - - if (signed_volume_sign(q1,p1,p2,p3) != signed_volume_sign(q2,p1,p2,p3)) { - bool pos = signed_volume_sign(q1,q2,p1,p2); - if (signed_volume_sign(q1,q2,p2,p3) == pos && signed_volume_sign(q1,q2,p3,p1) == pos) - return true; - } - return false; -} - - - -// Determine whether this facet is potentially visible (still can be obscured). -bool TriangleSelector::faces_camera(int facet) const -{ - assert(facet < m_orig_size_indices); - // The normal is cached in mesh->stl, use it. - return (m_mesh->stl.facet_start[facet].normal.dot(m_cursor.dir) < 0.); -} - - -// How many vertices of a triangle are inside the circle? -int TriangleSelector::vertices_inside(int facet_idx) const -{ - int inside = 0; - for (size_t i=0; i<3; ++i) { - if (is_point_inside_cursor(m_vertices[m_triangles[facet_idx].verts_idxs[i]].v)) - ++inside; - } - return inside; -} - - -// Is edge inside cursor? -bool TriangleSelector::is_edge_inside_cursor(int facet_idx) const -{ - Vec3f pts[3]; - for (int i=0; i<3; ++i) - pts[i] = m_vertices[m_triangles[facet_idx].verts_idxs[i]].v; - - const Vec3f& p = m_cursor.center; - - for (int side = 0; side < 3; ++side) { - const Vec3f& a = pts[side]; - const Vec3f& b = pts[side<2 ? side+1 : 0]; - Vec3f s = (b-a).normalized(); - float t = (p-a).dot(s); - Vec3f vector = a+t*s - p; - - // vector is 3D vector from center to the intersection. What we want to - // measure is length of its projection onto plane perpendicular to dir. - float dist_sqr = vector.squaredNorm() - std::pow(vector.dot(m_cursor.dir), 2.f); - if (dist_sqr < m_cursor.radius_sqr && t>=0.f && t<=(b-a).norm()) - return true; - } - return false; -} - - - -// Recursively remove all subtriangles. -void TriangleSelector::undivide_triangle(int facet_idx) -{ - assert(facet_idx < int(m_triangles.size())); - Triangle& tr = m_triangles[facet_idx]; - - if (tr.is_split()) { - for (int i=0; i<=tr.number_of_split_sides(); ++i) { - undivide_triangle(tr.children[i]); - m_triangles[tr.children[i]].valid = false; - ++m_invalid_triangles; - } - tr.set_division(0); // not split - } -} - - -void TriangleSelector::remove_useless_children(int facet_idx) -{ - // Check that all children are leafs of the same type. If not, try to - // make them (recursive call). Remove them if sucessful. - - assert(facet_idx < int(m_triangles.size()) && m_triangles[facet_idx].valid); - Triangle& tr = m_triangles[facet_idx]; - - if (! tr.is_split()) { - // This is a leaf, there nothing to do. This can happen during the - // first (non-recursive call). Shouldn't otherwise. - return; - } - - // Call this for all non-leaf children. - for (int child_idx=0; child_idx<=tr.number_of_split_sides(); ++child_idx) { - assert(child_idx < int(m_triangles.size()) && m_triangles[child_idx].valid); - if (m_triangles[tr.children[child_idx]].is_split()) - remove_useless_children(tr.children[child_idx]); - } - - - // Return if a child is not leaf or two children differ in type. - FacetSupportType first_child_type = FacetSupportType::NONE; - for (int child_idx=0; child_idx<=tr.number_of_split_sides(); ++child_idx) { - if (m_triangles[tr.children[child_idx]].is_split()) - return; - if (child_idx == 0) - first_child_type = m_triangles[tr.children[0]].get_state(); - else if (m_triangles[tr.children[child_idx]].get_state() != first_child_type) - return; - } - - // If we got here, the children can be removed. - undivide_triangle(facet_idx); - tr.set_state(first_child_type); -} - - - -void TriangleSelector::garbage_collect() -{ - // First make a map from old to new triangle indices. - int new_idx = m_orig_size_indices; - std::vector new_triangle_indices(m_triangles.size(), -1); - for (int i = m_orig_size_indices; i new_vertices_indices(m_vertices.size(), -1); - for (int i=m_orig_size_vertices; i= 0); - if (m_vertices[i].ref_cnt != 0) { - new_vertices_indices[i] = new_idx; - ++new_idx; - } - } - - // We can remove all invalid triangles and vertices that are no longer referenced. - m_triangles.erase(std::remove_if(m_triangles.begin()+m_orig_size_indices, m_triangles.end(), - [](const Triangle& tr) { return ! tr.valid; }), - m_triangles.end()); - m_vertices.erase(std::remove_if(m_vertices.begin()+m_orig_size_vertices, m_vertices.end(), - [](const Vertex& vert) { return vert.ref_cnt == 0; }), - m_vertices.end()); - - // Now go through all remaining triangles and update changed indices. - for (Triangle& tr : m_triangles) { - assert(tr.valid); - - if (tr.is_split()) { - // There are children. Update their indices. - for (int j=0; j<=tr.number_of_split_sides(); ++j) { - assert(new_triangle_indices[tr.children[j]] != -1); - tr.children[j] = new_triangle_indices[tr.children[j]]; - } - } - - // Update indices into m_vertices. The original vertices are never - // touched and need not be reindexed. - for (int& idx : tr.verts_idxs) { - if (idx >= m_orig_size_vertices) { - assert(new_vertices_indices[idx] != -1); - idx = new_vertices_indices[idx]; - } - } - - // If this triangle was split before, forget it. - // Children referenced in the cache are dead by now. - tr.forget_history(); - } - - m_invalid_triangles = 0; -} - -TriangleSelector::TriangleSelector(const TriangleMesh& mesh) - : m_mesh{&mesh} -{ - reset(); - -} - - -void TriangleSelector::reset() -{ - if (! m_orig_size_indices != 0) // unless this is run from constructor - garbage_collect(); - m_vertices.clear(); - m_triangles.clear(); - for (const stl_vertex& vert : m_mesh->its.vertices) - m_vertices.emplace_back(vert); - for (const stl_triangle_vertex_indices& ind : m_mesh->its.indices) - push_triangle(ind[0], ind[1], ind[2]); - m_orig_size_vertices = m_vertices.size(); - m_orig_size_indices = m_triangles.size(); - m_invalid_triangles = 0; -} - -void TriangleSelector::render(ImGuiWrapper* imgui) -{ - Vec3d offset = wxGetApp().model().objects.front()->instances.front()->get_transformation().get_offset(); - ::glTranslatef(offset.x(), offset.y(), offset.z()); - ::glScalef(1.005f, 1.005f, 1.005f); - - int enf_cnt = 0; int blc_cnt = 0; + m_iva_enforcers.release_geometry(); + m_iva_blockers.release_geometry(); + for (const Triangle& tr : m_triangles) { if (! tr.valid || tr.is_split() || tr.get_state() == FacetSupportType::NONE) continue; @@ -1245,13 +802,13 @@ void TriangleSelector::render(ImGuiWrapper* imgui) ::glColor4f(0.f, 0.f, 1.f, 0.2f); m_iva_enforcers.render(); } - m_iva_enforcers.release_geometry(); + if (m_iva_blockers.has_VBOs()) { ::glColor4f(1.f, 0.f, 0.f, 0.2f); m_iva_blockers.render(); } - m_iva_blockers.release_geometry(); + #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG if (imgui) @@ -1262,242 +819,9 @@ void TriangleSelector::render(ImGuiWrapper* imgui) } -void TriangleSelector::set_edge_limit(float edge_limit) -{ - float new_limit_sqr = std::pow(edge_limit, 2.f); - - if (new_limit_sqr != m_edge_limit_sqr) { - m_edge_limit_sqr = new_limit_sqr; - - // The way how triangles split may be different now, forget - // all cached splits. - garbage_collect(); - } -} - - - -void TriangleSelector::push_triangle(int a, int b, int c) -{ - for (int i : {a, b, c}) { - assert(i >= 0 && i < int(m_vertices.size())); - ++m_vertices[i].ref_cnt; - } - m_triangles.emplace_back(a, b, c); -} - - -void TriangleSelector::perform_split(int facet_idx, FacetSupportType old_state) -{ - Triangle* tr = &m_triangles[facet_idx]; - - assert(tr->is_split()); - - // Read info about how to split this triangle. - int sides_to_split = tr->number_of_split_sides(); - - // indices of triangle vertices - std::vector verts_idxs; - int idx = tr->special_side(); - for (int j=0; j<3; ++j) { - verts_idxs.push_back(tr->verts_idxs[idx++]); - if (idx == 3) - idx = 0; - } - - if (sides_to_split == 1) { - m_vertices.emplace_back((m_vertices[verts_idxs[1]].v + m_vertices[verts_idxs[2]].v)/2.); - verts_idxs.insert(verts_idxs.begin()+2, m_vertices.size() - 1); - - push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[2]); - push_triangle(verts_idxs[2], verts_idxs[3], verts_idxs[0]); - } - - if (sides_to_split == 2) { - m_vertices.emplace_back((m_vertices[verts_idxs[0]].v + m_vertices[verts_idxs[1]].v)/2.); - verts_idxs.insert(verts_idxs.begin()+1, m_vertices.size() - 1); - - m_vertices.emplace_back((m_vertices[verts_idxs[0]].v + m_vertices[verts_idxs[3]].v)/2.); - verts_idxs.insert(verts_idxs.begin()+4, m_vertices.size() - 1); - - push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[4]); - push_triangle(verts_idxs[1], verts_idxs[2], verts_idxs[4]); - push_triangle(verts_idxs[2], verts_idxs[3], verts_idxs[4]); - } - - if (sides_to_split == 3) { - m_vertices.emplace_back((m_vertices[verts_idxs[0]].v + m_vertices[verts_idxs[1]].v)/2.); - verts_idxs.insert(verts_idxs.begin()+1, m_vertices.size() - 1); - m_vertices.emplace_back((m_vertices[verts_idxs[2]].v + m_vertices[verts_idxs[3]].v)/2.); - verts_idxs.insert(verts_idxs.begin()+3, m_vertices.size() - 1); - m_vertices.emplace_back((m_vertices[verts_idxs[4]].v + m_vertices[verts_idxs[0]].v)/2.); - verts_idxs.insert(verts_idxs.begin()+5, m_vertices.size() - 1); - - push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[5]); - push_triangle(verts_idxs[1], verts_idxs[2], verts_idxs[3]); - push_triangle(verts_idxs[3], verts_idxs[4], verts_idxs[5]); - push_triangle(verts_idxs[1], verts_idxs[3], verts_idxs[5]); - } - - tr = &m_triangles[facet_idx]; // may have been invalidated - - // And save the children. All children should start in the same state as the triangle we just split. - assert(sides_to_split <= 3); - for (int i=0; i<=sides_to_split; ++i) { - tr->children[i] = m_triangles.size()-1-i; - m_triangles[tr->children[i]].set_state(old_state); - } -} - - -std::map> TriangleSelector::serialize() const -{ - // Each original triangle of the mesh is assigned a number encoding its state - // or how it is split. Each triangle is encoded by 4 bits (xxyy): - // leaf triangle: xx = FacetSupportType, yy = 0 - // non-leaf: xx = special side, yy = number of split sides - // These are bitwise appended and formed into one 64-bit integer. - - // The function returns a map from original triangle indices to - // stream of bits encoding state and offsprings. - - std::map> out; - for (int i=0; i data; // complete encoding of this mesh triangle - int stored_triangles = 0; // how many have been already encoded - - std::function serialize_recursive; - serialize_recursive = [this, &serialize_recursive, &stored_triangles, &data](int facet_idx) { - const Triangle& tr = m_triangles[facet_idx]; - - // Always save number of split sides. It is zero for unsplit triangles. - int split_sides = tr.number_of_split_sides(); - assert(split_sides >= 0 && split_sides <= 3); - - //data |= (split_sides << (stored_triangles * 4)); - data.push_back(split_sides & 0b01); - data.push_back(split_sides & 0b10); - - if (tr.is_split()) { - // If this triangle is split, save which side is split (in case - // of one split) or kept (in case of two splits). The value will - // be ignored for 3-side split. - assert(split_sides > 0); - assert(tr.special_side() >= 0 && tr.special_side() <= 3); - data.push_back(tr.special_side() & 0b01); - data.push_back(tr.special_side() & 0b10); - ++stored_triangles; - // Now save all children. - for (int child_idx=0; child_idx<=split_sides; ++child_idx) - serialize_recursive(tr.children[child_idx]); - } else { - // In case this is leaf, we better save information about its state. - assert(int(tr.get_state()) <= 3); - data.push_back(int(tr.get_state()) & 0b01); - data.push_back(int(tr.get_state()) & 0b10); - ++stored_triangles; - } - }; - - serialize_recursive(i); - out[i] = data; - } - - return out; -} - -void TriangleSelector::deserialize(const std::map> data) -{ - reset(); // dump any current state - for (const auto& [triangle_id, code] : data) { - assert(triangle_id < int(m_triangles.size())); - int processed_triangles = 0; - struct ProcessingInfo { - int facet_id = 0; - int processed_children = 0; - int total_children = 0; - }; - - // Vector to store all parents that have offsprings. - std::vector parents; - - while (true) { - // Read next triangle info. - int next_code = 0; - for (int i=3; i>=0; --i) { - next_code = next_code << 1; - next_code |= int(code[4 * processed_triangles + i]); - } - ++processed_triangles; - - int num_of_split_sides = (next_code & 0b11); - int num_of_children = num_of_split_sides != 0 ? num_of_split_sides + 1 : 0; - bool is_split = num_of_children != 0; - FacetSupportType state = FacetSupportType(next_code >> 2); - int special_side = (next_code >> 2); - - // Take care of the first iteration separately, so handling of the others is simpler. - if (parents.empty()) { - if (! is_split) { - // root is not split. just set the state and that's it. - m_triangles[triangle_id].set_state(state); - break; - } else { - // root is split, add it into list of parents and split it. - // then go to the next. - parents.push_back({triangle_id, 0, num_of_children}); - m_triangles[triangle_id].set_division(num_of_children-1, special_side); - perform_split(triangle_id, FacetSupportType::NONE); - continue; - } - } - - // This is not the first iteration. This triangle is a child of last seen parent. - assert(! parents.empty()); - assert(parents.back().processed_children < parents.back().total_children); - - if (is_split) { - // split the triangle and save it as parent of the next ones. - const ProcessingInfo& last = parents.back(); - int this_idx = m_triangles[last.facet_id].children[last.processed_children]; - m_triangles[this_idx].set_division(num_of_children-1, special_side); - perform_split(this_idx, FacetSupportType::NONE); - parents.push_back({this_idx, 0, num_of_children}); - } else { - // this triangle belongs to last split one - m_triangles[m_triangles[parents.back().facet_id].children[parents.back().processed_children]].set_state(state); - ++parents.back().processed_children; - } - - - // If all children of the past parent triangle are claimed, move to grandparent. - while (parents.back().processed_children == parents.back().total_children) { - parents.pop_back(); - - if (parents.empty()) - break; - - // And increment the grandparent children counter, because - // we have just finished that branch and got back here. - ++parents.back().processed_children; - } - - // In case we popped back the root, we should be done. - if (parents.empty()) - break; - } - - } -} - #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG -void TriangleSelector::render_debug(ImGuiWrapper* imgui) +void TriangleSelectorGUI::render_debug(ImGuiWrapper* imgui) { imgui->begin(std::string("TriangleSelector dialog (DEV ONLY)"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); @@ -1585,5 +909,7 @@ void TriangleSelector::render_debug(ImGuiWrapper* imgui) } #endif + + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index 099c9a30cb..7020bb7d42 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -6,11 +6,11 @@ #include "slic3r/GUI/3DScene.hpp" #include "libslic3r/ObjectID.hpp" +#include "libslic3r/TriangleSelector.hpp" #include -#define PRUSASLICER_TRIANGLE_SELECTOR_DEBUG namespace Slic3r { @@ -23,144 +23,26 @@ enum class SLAGizmoEventType : unsigned char; class ClippingPlane; -// Following class holds information about selected triangles. It also has power -// to recursively subdivide the triangles and make the selection finer. -class TriangleSelector { + +class TriangleSelectorGUI : public TriangleSelector { public: - void set_edge_limit(float edge_limit); - - // Create new object on a TriangleMesh. The referenced mesh must - // stay valid, a ptr to it is saved and used. - explicit TriangleSelector(const TriangleMesh& mesh); - - // Select all triangles fully inside the circle, subdivide where needed. - void select_patch(const Vec3f& hit, // point where to start - int facet_start, // facet that point belongs to - const Vec3f& source, // camera position (mesh coords) - const Vec3f& dir, // direction of the ray (mesh coords) - float radius_sqr, // squared radius of the cursor - FacetSupportType new_state); // enforcer or blocker? + explicit TriangleSelectorGUI(const TriangleMesh& mesh) + : TriangleSelector(mesh) {} // Render current selection. Transformation matrices are supposed // to be already set. void render(ImGuiWrapper* imgui = nullptr); - // Clear everything and make the tree empty. - void reset(); - - // Remove all unnecessary data. - void garbage_collect(); - - // Store the division trees in compact form (a long stream of - // bits for each triangle of the original mesh). - std::map> serialize() const; - - // Load serialized data. Assumes that correct mesh is loaded. - void deserialize(const std::map> data); - #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG void render_debug(ImGuiWrapper* imgui); - bool m_show_triangles{true}; + bool m_show_triangles{false}; bool m_show_invalid{false}; #endif private: - // Triangle and info about how it's split. - class Triangle { - public: - // Use TriangleSelector::push_triangle to create a new triangle. - // It increments/decrements reference counter on vertices. - Triangle(int a, int b, int c) - : verts_idxs{a, b, c}, - state{FacetSupportType(0)}, - number_of_splits{0}, - special_side_idx{0}, - old_number_of_splits{0} - {} - // Indices into m_vertices. - std::array verts_idxs; - - // Is this triangle valid or marked to be removed? - bool valid{true}; - - // Children triangles. - std::array children; - - // Set the division type. - void set_division(int sides_to_split, int special_side_idx = -1); - - // Get/set current state. - void set_state(FacetSupportType type) { assert(! is_split()); state = type; } - FacetSupportType get_state() const { assert(! is_split()); return state; } - - // Get info on how it's split. - bool is_split() const { return number_of_split_sides() != 0; } - int number_of_split_sides() const { return number_of_splits; } - int special_side() const { assert(is_split()); return special_side_idx; } - bool was_split_before() const { return old_number_of_splits != 0; } - void forget_history() { old_number_of_splits = 0; } - - private: - int number_of_splits; - int special_side_idx; - FacetSupportType state; - - // How many children were spawned during last split? - // Is not reset on remerging the triangle. - int old_number_of_splits; - }; - - struct Vertex { - explicit Vertex(const stl_vertex& vert) - : v{vert}, - ref_cnt{0} - {} - stl_vertex v; - int ref_cnt; - }; - - // Lists of vertices and triangles, both original and new - std::vector m_vertices; - std::vector m_triangles; - const TriangleMesh* m_mesh; - GLIndexedVertexArray m_iva_enforcers; GLIndexedVertexArray m_iva_blockers; std::array m_varrays; - - // Number of invalid triangles (to trigger garbage collection). - int m_invalid_triangles; - - // Limiting length of triangle side (squared). - float m_edge_limit_sqr = 1.f; - - // Number of original vertices and triangles. - int m_orig_size_vertices; - int m_orig_size_indices; - - // Cache for cursor position, radius and direction. - struct Cursor { - Vec3f center; - Vec3f source; - Vec3f dir; - float radius_sqr; - }; - - Cursor m_cursor; - - // Private functions: - bool select_triangle(int facet_idx, FacetSupportType type, - bool recursive_call = false); - bool is_point_inside_cursor(const Vec3f& point) const; - int vertices_inside(int facet_idx) const; - bool faces_camera(int facet) const; - void undivide_triangle(int facet_idx); - void split_triangle(int facet_idx); - void remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant. - bool is_pointer_in_triangle(int facet_idx) const; - bool is_edge_inside_cursor(int facet_idx) const; - void push_triangle(int a, int b, int c); - void perform_split(int facet_idx, FacetSupportType old_state); }; @@ -178,17 +60,8 @@ private: static constexpr float CursorRadiusMax = 8.f; static constexpr float CursorRadiusStep = 0.2f; - // For each model-part volume, store a list of statuses of - // individual facets (one of the enum values above). - std::vector> m_selected_facets; - - GLIndexedVertexArray m_iva; - - void update_vertex_buffers(const TriangleMesh* mesh, - int mesh_id, - FacetSupportType type, // enforcers / blockers - const std::vector* new_facets = nullptr); // nullptr -> regenerate all - + // For each model-part volume, store status and division of the triangles. + std::vector> m_triangle_selectors; public: GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); @@ -220,8 +93,6 @@ private: bool m_setting_angle = false; bool m_internal_stack_active = false; bool m_schedule_update = false; - - std::unique_ptr m_triangle_selector; // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. From 0756a7e4b3b8776c5885c6589a376e0fd191335b Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 14 Jul 2020 14:02:39 +0200 Subject: [PATCH 200/503] TriangleSelector: 'Select by angle' and 'reset selection' functions fixed --- src/libslic3r/TriangleSelector.cpp | 9 ++ src/libslic3r/TriangleSelector.hpp | 3 + src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 94 ++++---------------- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp | 3 +- 4 files changed, 31 insertions(+), 78 deletions(-) diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index 340f5a29a4..9210bde08e 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -132,6 +132,15 @@ bool TriangleSelector::select_triangle(int facet_idx, FacetSupportType type, boo } + +void TriangleSelector::set_facet(int facet_idx, FacetSupportType state) +{ + assert(facet_idx < m_orig_size_indices); + undivide_triangle(facet_idx); + assert(! m_triangles[facet_idx].is_split()); + m_triangles[facet_idx].set_state(state); +} + void TriangleSelector::split_triangle(int facet_idx) { if (m_triangles[facet_idx].is_split()) { diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp index 943029548c..f3e23bea20 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -32,6 +32,9 @@ public: FacetSupportType new_state); // enforcer or blocker? + // Set facet of the mesh to a given state. Only works for original triangles. + void set_facet(int facet_idx, FacetSupportType state); + // Clear everything and make the tree empty. void reset(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index ca7695907e..417d101ea0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -48,7 +48,7 @@ bool GLGizmoFdmSupports::on_init() m_desc["block"] = _L("Block supports"); m_desc["remove_caption"] = _L("Shift + Left mouse button") + ": "; m_desc["remove"] = _L("Remove selection"); - m_desc["remove_all"] = _L("Remove all"); + m_desc["remove_all"] = _L("Remove all selection"); return true; } @@ -207,9 +207,9 @@ void GLGizmoFdmSupports::update_model_object() const ModelObject* mo = m_c->selection_info()->model_object(); int idx = -1; for (ModelVolume* mv : mo->volumes) { - ++idx; if (! mv->is_model_part()) continue; + ++idx; mv->m_supported_facets.set(*m_triangle_selectors[idx].get()); } } @@ -433,63 +433,9 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous } -/*void GLGizmoFdmSupports::update_vertex_buffers(const TriangleMesh* mesh, - int mesh_id, - FacetSupportType type, - const std::vector* new_facets) + +void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool block) { - //std::vector& ivas = m_ivas[mesh_id][type == FacetSupportType::ENFORCER ? 0 : 1]; - - // lambda to push facet into vertex buffer - auto push_facet = [this, &mesh, &mesh_id](size_t idx, GLIndexedVertexArray& iva) { - for (int i=0; i<3; ++i) - iva.push_geometry( - mesh->its.vertices[mesh->its.indices[idx](i)].cast(), - m_c->raycaster()->raycasters()[mesh_id]->get_triangle_normal(idx).cast() - ); - size_t num = iva.triangle_indices_size; - iva.push_triangle(num, num+1, num+2); - }; - - - //if (ivas.size() == MaxVertexBuffers || ! new_facets) { - // If there are too many or they should be regenerated, make one large - // GLVertexBufferArray. - //ivas.clear(); // destructors release geometry - //ivas.push_back(GLIndexedVertexArray()); - - m_iva.release_geometry(); - m_iva.clear(); - - bool pushed = false; - for (size_t facet_idx=0; facet_idxempty()) - ivas.back().finalize_geometry(true); - else - ivas.pop_back(); - } - -}*/ - - -void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool overwrite, bool block) -{ - return; -/* float threshold = (M_PI/180.)*threshold_deg; const Selection& selection = m_parent.get_selection(); const ModelObject* mo = m_c->selection_info()->model_object(); @@ -512,13 +458,12 @@ void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool overwr int idx = -1; for (const stl_facet& facet : mv->mesh().stl.facet_start) { ++idx; - if (facet.normal.dot(down) > dot_limit && (overwrite || m_selected_facets[mesh_id][idx] == FacetSupportType::NONE)) - m_selected_facets[mesh_id][idx] = block - ? FacetSupportType::BLOCKER - : FacetSupportType::ENFORCER; + if (facet.normal.dot(down) > dot_limit) + m_triangle_selectors[mesh_id]->set_facet(idx, + block + ? FacetSupportType::BLOCKER + : FacetSupportType::ENFORCER); } - update_vertex_buffers(&mv->mesh(), mesh_id, FacetSupportType::ENFORCER); - update_vertex_buffers(&mv->mesh(), mesh_id, FacetSupportType::BLOCKER); } activate_internal_undo_redo_stack(true); @@ -528,7 +473,6 @@ void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool overwr update_model_object(); m_parent.set_as_dirty(); m_setting_angle = false; -*/ } @@ -584,18 +528,17 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l ImGui::SameLine(); if (m_imgui->button(m_desc.at("remove_all"))) { - /*ModelObject* mo = m_c->selection_info()->model_object(); + Plater::TakeSnapshot(wxGetApp().plater(), wxString(_L("Reset selection"))); + ModelObject* mo = m_c->selection_info()->model_object(); int idx = -1; for (ModelVolume* mv : mo->volumes) { - ++idx; if (mv->is_model_part()) { - m_selected_facets[idx].assign(m_selected_facets[idx].size(), FacetSupportType::NONE); - mv->m_supported_facets.clear(); - update_vertex_buffers(&mv->mesh(), idx, FacetSupportType::ENFORCER); - update_vertex_buffers(&mv->mesh(), idx, FacetSupportType::BLOCKER); - m_parent.set_as_dirty(); + ++idx; + m_triangle_selectors[idx]->reset(); } - }*/ + } + update_model_object(); + m_parent.set_as_dirty(); } const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; @@ -651,12 +594,11 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l ImGui::SameLine(); if (m_imgui->slider_float("", &m_angle_threshold_deg, 0.f, 90.f, "%.f")) m_parent.set_slope_range({90.f - m_angle_threshold_deg, 90.f - m_angle_threshold_deg}); - m_imgui->checkbox(wxString("Overwrite already selected facets"), m_overwrite_selected); if (m_imgui->button("Enforce")) - select_facets_by_angle(m_angle_threshold_deg, m_overwrite_selected, false); + select_facets_by_angle(m_angle_threshold_deg, false); ImGui::SameLine(); if (m_imgui->button("Block")) - select_facets_by_angle(m_angle_threshold_deg, m_overwrite_selected, true); + select_facets_by_angle(m_angle_threshold_deg, true); ImGui::SameLine(); if (m_imgui->button("Cancel")) m_setting_angle = false; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index 7020bb7d42..2d1442164a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -82,8 +82,7 @@ private: void update_from_model_object(); void activate_internal_undo_redo_stack(bool activate); - void select_facets_by_angle(float threshold, bool overwrite, bool block); - bool m_overwrite_selected = false; + void select_facets_by_angle(float threshold, bool block); float m_angle_threshold_deg = 45.f; bool is_mesh_point_clipped(const Vec3d& point) const; From 3b91d11ddfcc362552a62e8fe03c11b1115db3fd Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 15 Jul 2020 10:28:20 +0200 Subject: [PATCH 201/503] TriangleSelector: backend is aware of divided triangles --- src/libslic3r/Model.cpp | 13 ++++++------- src/libslic3r/Model.hpp | 2 +- src/libslic3r/PrintObject.cpp | 10 +++++----- src/libslic3r/TriangleSelector.cpp | 19 +++++++++++++++++++ src/libslic3r/TriangleSelector.hpp | 2 ++ 5 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index b6bae489b4..d0410c2d4e 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1831,13 +1831,12 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const } -std::vector FacetsAnnotation::get_facets(FacetSupportType type) const +indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, FacetSupportType type) const { - std::vector out; - /*for (auto& [facet_idx, this_type] : m_data) - if (this_type == type) - out.push_back(facet_idx); - */return out; + TriangleSelector selector(mv.mesh()); + selector.deserialize(m_data); + indexed_triangle_set out = selector.get_facets(type); + return out; } @@ -1932,7 +1931,7 @@ bool model_custom_supports_data_changed(const ModelObject& mo, const ModelObject return true; } return false; -}; +} extern bool model_has_multi_part_objects(const Model &model) { diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index de20e0fdcc..3127af5ba7 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -407,7 +407,7 @@ public: const std::map>& get_data() const { return m_data; } void set(const TriangleSelector& selector); - std::vector get_facets(FacetSupportType type) const; + indexed_triangle_set get_facets(const ModelVolume& mv, FacetSupportType type) const; void clear(); ClockType::time_point get_timestamp() const { return timestamp; } diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index d2bdb6d531..1a2edcf6e8 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2673,8 +2673,8 @@ void PrintObject::project_and_append_custom_supports( FacetSupportType type, std::vector& expolys) const { for (const ModelVolume* mv : this->model_object()->volumes) { - const std::vector custom_facets = mv->m_supported_facets.get_facets(type); - if (custom_facets.empty()) + const indexed_triangle_set custom_facets = mv->m_supported_facets.get_facets(*mv, type); + if (custom_facets.indices.empty()) continue; const TriangleMesh& mesh = mv->mesh(); @@ -2705,11 +2705,11 @@ void PrintObject::project_and_append_custom_supports( }; // Vector to collect resulting projections from each triangle. - std::vector projections_of_triangles(custom_facets.size()); + std::vector projections_of_triangles(custom_facets.indices.size()); // Iterate over all triangles. tbb::parallel_for( - tbb::blocked_range(0, custom_facets.size()), + tbb::blocked_range(0, custom_facets.indices.size()), [&](const tbb::blocked_range& range) { for (size_t idx = range.begin(); idx < range.end(); ++ idx) { @@ -2717,7 +2717,7 @@ void PrintObject::project_and_append_custom_supports( // Transform the triangle into worlds coords. for (int i=0; i<3; ++i) - facet[i] = tr * mesh.its.vertices[mesh.its.indices[custom_facets[idx]](i)]; + facet[i] = tr * custom_facets.vertices[custom_facets.indices[idx](i)]; // Ignore triangles with upward-pointing normal. if ((facet[1]-facet[0]).cross(facet[2]-facet[0]).z() > 0.) diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index 9210bde08e..6e3f9f5185 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -512,6 +512,25 @@ void TriangleSelector::perform_split(int facet_idx, FacetSupportType old_state) } + +indexed_triangle_set TriangleSelector::get_facets(FacetSupportType state) const +{ + indexed_triangle_set out; + for (const Triangle& tr : m_triangles) { + if (tr.valid && ! tr.is_split() && tr.get_state() == state) { + stl_triangle_vertex_indices indices; + for (int i=0; i<3; ++i) { + out.vertices.emplace_back(m_vertices[tr.verts_idxs[i]].v); + indices[i] = out.vertices.size() - 1; + } + out.indices.emplace_back(indices); + } + } + return out; +} + + + std::map> TriangleSelector::serialize() const { // Each original triangle of the mesh is assigned a number encoding its state diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp index f3e23bea20..dc2ad9127d 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -31,6 +31,8 @@ public: float radius_sqr, // squared radius of the cursor FacetSupportType new_state); // enforcer or blocker? + // Get facets currently in the given state. + indexed_triangle_set get_facets(FacetSupportType state) const; // Set facet of the mesh to a given state. Only works for original triangles. void set_facet(int facet_idx, FacetSupportType state); From afb5d929c47b54567c8ef5b2d1d8376621b60048 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 15 Jul 2020 12:28:52 +0200 Subject: [PATCH 202/503] TriangleSelector: Schedule restarting background process after edit --- src/libslic3r/Model.cpp | 4 +++- src/libslic3r/Model.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 6 +++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index d0410c2d4e..4ff0a5c1bf 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1841,13 +1841,15 @@ indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, FacetSu -void FacetsAnnotation::set(const TriangleSelector& selector) +bool FacetsAnnotation::set(const TriangleSelector& selector) { std::map> sel_map = selector.serialize(); if (sel_map != m_data) { m_data = sel_map; update_timestamp(); + return true; } + return false; } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 3127af5ba7..16f3f00ad5 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -406,7 +406,7 @@ public: using ClockType = std::chrono::steady_clock; const std::map>& get_data() const { return m_data; } - void set(const TriangleSelector& selector); + bool set(const TriangleSelector& selector); indexed_triangle_set get_facets(const ModelVolume& mv, FacetSupportType type) const; void clear(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 417d101ea0..13c9cfef87 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -204,14 +204,18 @@ void GLGizmoFdmSupports::render_cursor_circle() const void GLGizmoFdmSupports::update_model_object() const { + bool updated = false; ModelObject* mo = m_c->selection_info()->model_object(); int idx = -1; for (ModelVolume* mv : mo->volumes) { if (! mv->is_model_part()) continue; ++idx; - mv->m_supported_facets.set(*m_triangle_selectors[idx].get()); + updated |= mv->m_supported_facets.set(*m_triangle_selectors[idx].get()); } + + if (updated) + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } From 5a1d9aee15f507be774f96c2a98ca0adda8cc1e4 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 21 Jul 2020 09:01:17 +0200 Subject: [PATCH 203/503] TriangleSelector: Fix of a macOS crash Calling reset() from constructor relied on uninitialized variable --- src/libslic3r/TriangleSelector.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp index dc2ad9127d..877bc122cd 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -118,8 +118,8 @@ protected: float m_edge_limit_sqr = 1.f; // Number of original vertices and triangles. - int m_orig_size_vertices; - int m_orig_size_indices; + int m_orig_size_vertices = 0; + int m_orig_size_indices = 0; // Cache for cursor position, radius and direction. struct Cursor { From 74a1aeff8e8421fd9523bd9ff788e33e5692c6e1 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 23 Jul 2020 08:17:04 +0200 Subject: [PATCH 204/503] TriangleSelector: bugfix - backend did not correctly account for mirrorring --- src/libslic3r/PrintObject.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 1a2edcf6e8..273fc9c108 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2677,10 +2677,10 @@ void PrintObject::project_and_append_custom_supports( if (custom_facets.indices.empty()) continue; - const TriangleMesh& mesh = mv->mesh(); const Transform3f& tr1 = mv->get_matrix().cast(); const Transform3f& tr2 = this->trafo().cast(); const Transform3f tr = tr2 * tr1; + const float tr_det_sign = (tr.matrix().determinant() > 0. ? 1.f : -1.f); // The projection will be at most a pentagon. Let's minimize heap @@ -2719,8 +2719,9 @@ void PrintObject::project_and_append_custom_supports( for (int i=0; i<3; ++i) facet[i] = tr * custom_facets.vertices[custom_facets.indices[idx](i)]; - // Ignore triangles with upward-pointing normal. - if ((facet[1]-facet[0]).cross(facet[2]-facet[0]).z() > 0.) + // Ignore triangles with upward-pointing normal. Don't forget about mirroring. + float z_comp = (facet[1]-facet[0]).cross(facet[2]-facet[0]).z(); + if (tr_det_sign * z_comp > 0.) continue; // Sort the three vertices according to z-coordinate. From 7ddb64783b6094e3b1c5a8006e977c5308a6b841 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 24 Jul 2020 17:45:49 +0200 Subject: [PATCH 205/503] TriangleSelector: edge limit is derived from cursor size --- src/libslic3r/TriangleSelector.cpp | 10 ++++++++-- src/libslic3r/TriangleSelector.hpp | 5 +++-- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp | 2 +- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index 6e3f9f5185..50d775ed86 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -35,14 +35,20 @@ void TriangleSelector::Triangle::set_division(int sides_to_split, int special_si void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, const Vec3f& source, const Vec3f& dir, - float radius_sqr, FacetSupportType new_state) + float radius, FacetSupportType new_state) { assert(facet_start < m_orig_size_indices); assert(is_approx(dir.norm(), 1.f)); // Save current cursor center, squared radius and camera direction, // so we don't have to pass it around. - m_cursor = {hit, source, dir, radius_sqr}; + m_cursor = {hit, source, dir, radius*radius}; + + // In case user changed cursor size since last time, update triangle edge limit. + if (m_old_cursor_radius != radius) { + set_edge_limit(radius / 5.f); + m_old_cursor_radius = radius; + } // Now start with the facet the pointer points to and check all adjacent facets. std::vector facets_to_check{facet_start}; diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp index 877bc122cd..fb90cff769 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -1,7 +1,7 @@ #ifndef libslic3r_TriangleSelector_hpp_ #define libslic3r_TriangleSelector_hpp_ -#define PRUSASLICER_TRIANGLE_SELECTOR_DEBUG +// #define PRUSASLICER_TRIANGLE_SELECTOR_DEBUG #include "Point.hpp" @@ -28,7 +28,7 @@ public: int facet_start, // facet that point belongs to const Vec3f& source, // camera position (mesh coords) const Vec3f& dir, // direction of the ray (mesh coords) - float radius_sqr, // squared radius of the cursor + float radius, // radius of the cursor FacetSupportType new_state); // enforcer or blocker? // Get facets currently in the given state. @@ -130,6 +130,7 @@ protected: }; Cursor m_cursor; + float m_old_cursor_radius; // Private functions: bool select_triangle(int facet_idx, FacetSupportType type, diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 13c9cfef87..3769e96605 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -404,7 +404,7 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous // FIXME: The scaling of the mesh can be non-uniform. const Vec3d sf = Geometry::Transformation(trafo_matrix).get_scaling_factor(); const float avg_scaling = (sf(0) + sf(1) + sf(2))/3.; - const float limit = std::pow(m_cursor_radius/avg_scaling , 2.f); + const float limit = m_cursor_radius/avg_scaling; // Calculate direction from camera to the hit (in mesh coords): Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index 2d1442164a..ce24ea8d28 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -56,7 +56,7 @@ private: GLUquadricObj* m_quadric; float m_cursor_radius = 2.f; - static constexpr float CursorRadiusMin = 0.f; + static constexpr float CursorRadiusMin = 0.4f; // cannot be zero static constexpr float CursorRadiusMax = 8.f; static constexpr float CursorRadiusStep = 0.2f; From 248fba82a4892e85ffcfecd24b82bd0f9ddd8135 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 24 Jul 2020 16:53:05 +0200 Subject: [PATCH 206/503] TriangleSelector: 3MF loading and saving --- src/libslic3r/Format/3mf.cpp | 17 +++++++++ src/libslic3r/Model.cpp | 58 ++++++++++++++++++++++++++++++ src/libslic3r/Model.hpp | 2 ++ src/libslic3r/TriangleSelector.cpp | 1 + 4 files changed, 78 insertions(+) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index edf55ba37e..3612e6898c 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -86,6 +86,7 @@ const char* OBJECTID_ATTR = "objectid"; const char* TRANSFORM_ATTR = "transform"; const char* PRINTABLE_ATTR = "printable"; const char* INSTANCESCOUNT_ATTR = "instances_count"; +const char* CUSTOM_SUPPORTS_ATTR = "slic3rpe:custom_supports"; const char* KEY_ATTR = "key"; const char* VALUE_ATTR = "value"; @@ -283,6 +284,7 @@ namespace Slic3r { { std::vector vertices; std::vector triangles; + std::vector custom_supports; bool empty() { @@ -293,6 +295,7 @@ namespace Slic3r { { vertices.clear(); triangles.clear(); + custom_supports.clear(); } }; @@ -1539,6 +1542,8 @@ namespace Slic3r { m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V1_ATTR)); m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V2_ATTR)); m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V3_ATTR)); + + m_curr_object.geometry.custom_supports.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SUPPORTS_ATTR)); return true; } @@ -1872,6 +1877,13 @@ namespace Slic3r { volume->source.transform = Slic3r::Geometry::Transformation(volume_matrix_to_object); volume->calculate_convex_hull(); + // recreate custom supports from previously loaded attribute + assert(geometry.custom_supports.size() == triangles_count); + for (unsigned i=0; im_supported_facets.set_triangle_from_string(i, geometry.custom_supports[i]); + } + // apply the remaining volume's metadata for (const Metadata& metadata : volume_data.metadata) { @@ -2383,6 +2395,11 @@ namespace Slic3r { { stream << "v" << j + 1 << "=\"" << its.indices[i][j] + volume_it->second.first_vertex_id << "\" "; } + + std::string custom_supports_data_string = volume->m_supported_facets.get_triangle_as_string(i); + if (! custom_supports_data_string.empty()) + stream << CUSTOM_SUPPORTS_ATTR << "=\"" << custom_supports_data_string << "\" "; + stream << "/>\n"; } } diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 4ff0a5c1bf..3beb74f235 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1862,6 +1862,64 @@ void FacetsAnnotation::clear() +// Following function takes data from a triangle and encodes it as string +// of hexadecimal numbers (one digit per triangle). Used for 3MF export, +// changing it may break backwards compatibility !!!!! +std::string FacetsAnnotation::get_triangle_as_string(int triangle_idx) const +{ + std::string out; + + auto triangle_it = m_data.find(triangle_idx); + if (triangle_it != m_data.end()) { + const std::vector& code = triangle_it->second; + int offset = 0; + while (offset < int(code.size())) { + int next_code = 0; + for (int i=3; i>=0; --i) { + next_code = next_code << 1; + next_code |= int(code[offset + i]); + } + offset += 4; + + assert(next_code >=0 && next_code <= 15); + char digit = next_code < 10 ? next_code + '0' : (next_code-10)+'A'; + out.insert(out.begin(), digit); + } + } + return out; +} + + + +// Recover triangle splitting & state from string of hexadecimal values previously +// generated by get_triangle_as_string. Used to load from 3MF. +void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::string& str) +{ + assert(! str.empty()); + m_data[triangle_id] = std::vector(); // zero current state or create new + std::vector& code = m_data[triangle_id]; + + for (auto it = str.crbegin(); it != str.crend(); ++it) { + const char ch = *it; + int dec = 0; + if (ch >= '0' && ch<='9') + dec = int(ch - '0'); + else if (ch >='A' && ch <= 'F') + dec = 10 + int(ch - 'A'); + else + assert(false); + + // Convert to binary and append into code. + for (int i=0; i<4; ++i) { + code.insert(code.end(), bool(dec & (1 << i))); + } + } + + +} + + + // Test whether the two models contain the same number of ModelObjects with the same set of IDs // ordered in the same order. In that case it is not necessary to kill the background processing. bool model_object_list_equal(const Model &model_old, const Model &model_new) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 16f3f00ad5..92dc84d17a 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -409,6 +409,8 @@ public: bool set(const TriangleSelector& selector); indexed_triangle_set get_facets(const ModelVolume& mv, FacetSupportType type) const; void clear(); + std::string get_triangle_as_string(int i) const; + void set_triangle_from_string(int triangle_id, const std::string& str); ClockType::time_point get_timestamp() const { return timestamp; } bool is_same_as(const FacetsAnnotation& other) const { diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index 50d775ed86..763bf58616 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -603,6 +603,7 @@ void TriangleSelector::deserialize(const std::map> data) reset(); // dump any current state for (const auto& [triangle_id, code] : data) { assert(triangle_id < int(m_triangles.size())); + assert(! code.empty()); int processed_triangles = 0; struct ProcessingInfo { int facet_id = 0; From 67d2f438458c2185d01d8db02fe74f5f56e209db Mon Sep 17 00:00:00 2001 From: David Kocik Date: Sun, 14 Jun 2020 23:14:44 +0200 Subject: [PATCH 207/503] Showing Eject button only after exporting is finished. Fix of #4212 --- src/slic3r/GUI/Plater.cpp | 5 ++++- src/slic3r/GUI/RemovableDriveManager.cpp | 2 ++ src/slic3r/GUI/RemovableDriveManager.hpp | 6 ++++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index ec13610b8c..e4e43bb7c7 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3450,7 +3450,7 @@ void Plater::priv::on_slicing_completed(wxCommandEvent &) break; default: break; } -} +} void Plater::priv::on_process_completed(wxCommandEvent &evt) { @@ -3510,7 +3510,10 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt) show_action_buttons(true); } else if (this->writing_to_removable_device || wxGetApp().get_mode() == comSimple) + { + wxGetApp().removable_drive_manager()->set_exporting_finished(true); show_action_buttons(false); + } this->writing_to_removable_device = false; } diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index d67ac4a22f..d865fe3476 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -393,6 +393,7 @@ bool RemovableDriveManager::set_and_verify_last_save_path(const std::string &pat #endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS m_last_save_path = this->get_removable_drive_from_path(path); + m_exporting_finished = false; return ! m_last_save_path.empty(); } @@ -407,6 +408,7 @@ RemovableDriveManager::RemovableDrivesStatus RemovableDriveManager::status() } if (! out.has_eject) m_last_save_path.clear(); + out.has_eject = out.has_eject && m_exporting_finished; return out; } diff --git a/src/slic3r/GUI/RemovableDriveManager.hpp b/src/slic3r/GUI/RemovableDriveManager.hpp index e1a8d6faf1..26ee12e40c 100644 --- a/src/slic3r/GUI/RemovableDriveManager.hpp +++ b/src/slic3r/GUI/RemovableDriveManager.hpp @@ -83,7 +83,7 @@ public: // Public to be accessible from RemovableDriveManagerMM::on_device_unmount OSX notification handler. // It would be better to make this method private and friend to RemovableDriveManagerMM, but RemovableDriveManagerMM is an ObjectiveC class. void update(); - + void set_exporting_finished(bool b) { m_exporting_finished = b; } #ifdef _WIN32 // Called by Win32 Volume arrived / detached callback. void volumes_changed(); @@ -121,7 +121,9 @@ private: std::vector::const_iterator find_last_save_path_drive_data() const; // Set with set_and_verify_last_save_path() to a removable drive path to be ejected. std::string m_last_save_path; - + // Verifies that exporting was finished so drive can be ejected. + // Set false by set_and_verify_last_save_path() that is called just before exporting. + bool m_exporting_finished; #if __APPLE__ void register_window_osx(); void unregister_window_osx(); From 1dc3561e2cc52fbe32aa13b2e74c09f6de621a1b Mon Sep 17 00:00:00 2001 From: David Kocik Date: Sun, 14 Jun 2020 23:53:17 +0200 Subject: [PATCH 208/503] added 's' in https when pointing users to our github --- src/slic3r/GUI/MainFrame.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 64a1319d44..687634d11f 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -106,7 +106,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S m_statusbar->embed(this); m_statusbar->set_status_text(_(L("Version")) + " " + SLIC3R_VERSION + - _(L(" - Remember to check for updates at http://github.com/prusa3d/PrusaSlicer/releases"))); + _(L(" - Remember to check for updates at https://github.com/prusa3d/PrusaSlicer/releases"))); /* Load default preset bitmaps before a tabpanel initialization, * but after filling of an em_unit value @@ -1141,7 +1141,7 @@ void MainFrame::init_menubar() append_menu_item(helpMenu, wxID_ANY, _(L("Prusa 3D &Drivers")), _(L("Open the Prusa3D drivers download page in your browser")), [this](wxCommandEvent&) { wxGetApp().open_web_page_localized("https://www.prusa3d.com/downloads"); }); append_menu_item(helpMenu, wxID_ANY, _(L("Software &Releases")), _(L("Open the software releases page in your browser")), - [this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://github.com/prusa3d/PrusaSlicer/releases"); }); + [this](wxCommandEvent&) { wxLaunchDefaultBrowser("https://github.com/prusa3d/PrusaSlicer/releases"); }); //# my $versioncheck = $self->_append_menu_item($helpMenu, "Check for &Updates...", "Check for new Slic3r versions", sub{ //# wxTheApp->check_version(1); //# }); @@ -1158,7 +1158,7 @@ void MainFrame::init_menubar() append_menu_item(helpMenu, wxID_ANY, _(L("Show &Configuration Folder")), _(L("Show user configuration folder (datadir)")), [this](wxCommandEvent&) { Slic3r::GUI::desktop_open_datadir_folder(); }); append_menu_item(helpMenu, wxID_ANY, _(L("Report an I&ssue")), wxString::Format(_(L("Report an issue on %s")), SLIC3R_APP_NAME), - [this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://github.com/prusa3d/slic3r/issues/new"); }); + [this](wxCommandEvent&) { wxLaunchDefaultBrowser("https://github.com/prusa3d/slic3r/issues/new"); }); append_menu_item(helpMenu, wxID_ANY, wxString::Format(_(L("&About %s")), SLIC3R_APP_NAME), _(L("Show about dialog")), [this](wxCommandEvent&) { Slic3r::GUI::about(); }); helpMenu->AppendSeparator(); From 864ecf750cd1dab68d50ac8ee5d4bdac77d4f4c8 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 15 Jun 2020 00:34:12 +0200 Subject: [PATCH 209/503] Deleted default value in Plater::export_gcode(bool prefer_removable). Only place where it is not sure what value should be is in btn_reslice - i chose true --- src/slic3r/GUI/MainFrame.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 2 +- src/slic3r/GUI/Plater.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 687634d11f..521dcab804 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -909,7 +909,7 @@ void MainFrame::init_menubar() wxMenu* export_menu = new wxMenu(); wxMenuItem* item_export_gcode = append_menu_item(export_menu, wxID_ANY, _(L("Export &G-code")) + dots +"\tCtrl+G", _(L("Export current plate as G-code")), - [this](wxCommandEvent&) { if (m_plater) m_plater->export_gcode(); }, "export_gcode", nullptr, + [this](wxCommandEvent&) { if (m_plater) m_plater->export_gcode(false); }, "export_gcode", nullptr, [this](){return can_export_gcode(); }, this); m_changeable_menu_items.push_back(item_export_gcode); wxMenuItem* item_send_gcode = append_menu_item(export_menu, wxID_ANY, _(L("S&end G-code")) + dots +"\tCtrl+Shift+G", _(L("Send to print current plate as G-code")), diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index e4e43bb7c7..761f574e15 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -929,7 +929,7 @@ Sidebar::Sidebar(Plater *parent) { const bool export_gcode_after_slicing = wxGetKeyState(WXK_SHIFT); if (export_gcode_after_slicing) - p->plater->export_gcode(); + p->plater->export_gcode(true); else p->plater->reslice(); p->plater->select_view_3D("Preview"); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 5d60e006b0..a08b19fa35 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -220,7 +220,7 @@ public: void cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false); - void export_gcode(bool prefer_removable = true); + void export_gcode(bool prefer_removable); void export_stl(bool extended = false, bool selection_only = false); void export_amf(); void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path()); From 18594261d293651bf5f8234cfefe692e7709a103 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 27 Jul 2020 12:18:21 +0200 Subject: [PATCH 210/503] Added handling of mouse wheel events to ImGuiWrapper --- src/slic3r/GUI/GLCanvas3D.cpp | 5 +++++ src/slic3r/GUI/ImGuiWrapper.cpp | 3 +++ 2 files changed, 8 insertions(+) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index b4e672c4fb..2d45277849 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3300,6 +3300,11 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) if (evt.MiddleIsDown()) return; + if (wxGetApp().imgui()->update_mouse_data(evt)) { + m_dirty = true; + return; + } + #if ENABLE_RETINA_GL const float scale = m_retina_helper->get_scale_factor(); evt.SetX(evt.GetX() * scale); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 51a9a6d4eb..88dd02ccb7 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -176,6 +176,9 @@ bool ImGuiWrapper::update_mouse_data(wxMouseEvent& evt) io.MouseDown[0] = evt.LeftIsDown(); io.MouseDown[1] = evt.RightIsDown(); io.MouseDown[2] = evt.MiddleIsDown(); + float wheel_delta = static_cast(evt.GetWheelDelta()); + if (wheel_delta != 0.0f) + io.MouseWheel = static_cast(evt.GetWheelRotation()) / wheel_delta; unsigned buttons = (evt.LeftIsDown() ? 1 : 0) | (evt.RightIsDown() ? 2 : 0) | (evt.MiddleIsDown() ? 4 : 0); m_mouse_buttons = buttons; From 14366800e2feac3ce075a317e0895e43411dfc4c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 27 Jul 2020 15:45:29 +0200 Subject: [PATCH 211/503] GCodeProcessor -> Added parsing of 3d part generated gcodes --- src/libslic3r/GCode/GCodeProcessor.cpp | 311 +++++++++++++++++++++++++ src/libslic3r/GCode/GCodeProcessor.hpp | 23 ++ src/slic3r/GUI/Plater.cpp | 1 + 3 files changed, 335 insertions(+) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 83cbb876ef..42d1c07575 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -296,6 +296,14 @@ void GCodeProcessor::TimeProcessor::reset() machines[static_cast(ETimeMode::Normal)].enabled = true; } +const std::vector> GCodeProcessor::Producers = { + { EProducer::PrusaSlicer, "PrusaSlicer" }, + { EProducer::Cura, "Cura" }, + { EProducer::Simplify3D, "Simplify3D" }, + { EProducer::CraftWare, "CraftWare" }, + { EProducer::ideaMaker, "ideaMaker" } +}; + unsigned int GCodeProcessor::s_result_id = 0; void GCodeProcessor::apply_config(const PrintConfig& config) @@ -364,6 +372,9 @@ void GCodeProcessor::reset() m_extruders_color = ExtrudersColor(); m_cp_color.reset(); + m_producer = EProducer::Unknown; + m_producers_enabled = false; + m_time_processor.reset(); m_result.reset(); @@ -539,6 +550,13 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_tags(const std::string& comment) { + if (m_producers_enabled && m_producer == EProducer::Unknown && detect_producer(comment)) + return; + else if (m_producers_enabled && m_producer != EProducer::Unknown) { + if (process_producers_tags(comment)) + return; + } + // extrusion role tag size_t pos = comment.find(Extrusion_Role_Tag); if (pos != comment.npos) { @@ -645,6 +663,299 @@ void GCodeProcessor::process_tags(const std::string& comment) } } +bool GCodeProcessor::process_producers_tags(const std::string& comment) +{ + switch (m_producer) + { + case EProducer::PrusaSlicer: { return process_prusaslicer_tags(comment); } + case EProducer::Cura: { return process_cura_tags(comment); } + case EProducer::Simplify3D: { return process_simplify3d_tags(comment); } + case EProducer::CraftWare: { return process_craftware_tags(comment); } + case EProducer::ideaMaker: { return process_ideamaker_tags(comment); } + default: { return false; } + } +} + +bool GCodeProcessor::process_prusaslicer_tags(const std::string& comment) +{ + std::cout << comment << "\n"; + return false; +} + +bool GCodeProcessor::process_cura_tags(const std::string& comment) +{ + // TYPE -> extrusion role + std::string tag = "TYPE:"; + size_t pos = comment.find(tag); + if (pos != comment.npos) { + std::string type = comment.substr(pos + tag.length()); + if (type == "SKIRT") + m_extrusion_role = erSkirt; + else if (type == "WALL-OUTER") + m_extrusion_role = erExternalPerimeter; + else if (type == "WALL-INNER") + m_extrusion_role = erPerimeter; + else if (type == "SKIN") + m_extrusion_role = erSolidInfill; + else if (type == "FILL") + m_extrusion_role = erInternalInfill; + else if (type == "SUPPORT") + m_extrusion_role = erSupportMaterial; + else if (type == "SUPPORT-INTERFACE") + m_extrusion_role = erSupportMaterialInterface; + else if (type == "PRIME-TOWER") + m_extrusion_role = erWipeTower; + else { + m_extrusion_role = erNone; + BOOST_LOG_TRIVIAL(warning) << "GCodeProcessor found unknown extrusion role: " << type; + } + + return true; + } + + return false; +} + +bool GCodeProcessor::process_simplify3d_tags(const std::string& comment) +{ + // extrusion roles + + // ; skirt + size_t pos = comment.find(" skirt"); + if (pos == 0) { + m_extrusion_role = erSkirt; + return true; + } + + // ; outer perimeter + pos = comment.find(" outer perimeter"); + if (pos == 0) { + m_extrusion_role = erExternalPerimeter; + return true; + } + + // ; inner perimeter + pos = comment.find(" inner perimeter"); + if (pos == 0) { + m_extrusion_role = erPerimeter; + return true; + } + + // ; gap fill + pos = comment.find(" gap fill"); + if (pos == 0) { + m_extrusion_role = erGapFill; + return true; + } + + // ; infill + pos = comment.find(" infill"); + if (pos == 0) { + m_extrusion_role = erInternalInfill; + return true; + } + + // ; solid layer + pos = comment.find(" solid layer"); + if (pos == 0) { + m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + return true; + } + + // ; bridge + pos = comment.find(" bridge"); + if (pos == 0) { + m_extrusion_role = erBridgeInfill; + return true; + } + + // ; support + pos = comment.find(" support"); + if (pos == 0) { + m_extrusion_role = erSupportMaterial; + return true; + } + + // ; prime pillar + pos = comment.find(" prime pillar"); + if (pos == 0) { + m_extrusion_role = erWipeTower; + return true; + } + + // ; ooze shield + pos = comment.find(" ooze shield"); + if (pos == 0) { + m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + return true; + } + + // ; raft + pos = comment.find(" raft"); + if (pos == 0) { + m_extrusion_role = erSkirt; + return true; + } + + // geometry + + // ; tool + std::string tag = " tool"; + pos = comment.find(tag); + if (pos == 0) { + std::string data = comment.substr(pos + tag.length()); + std::string h_tag = "H"; + size_t h_start = data.find(h_tag); + size_t h_end = data.find_first_of(' ', h_start); + std::string w_tag = "W"; + size_t w_start = data.find(w_tag); + size_t w_end = data.find_first_of(' ', w_start); + if (h_start != data.npos) { + try + { + std::string test = data.substr(h_start + 1, (h_end != data.npos) ? h_end - h_start - 1 : h_end); + m_height = std::stof(data.substr(h_start + 1, (h_end != data.npos) ? h_end - h_start - 1 : h_end)); + } + catch (...) + { + BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Height (" << comment << ")."; + } + } + if (w_start != data.npos) { + try + { + std::string test = data.substr(w_start + 1, (w_end != data.npos) ? w_end - w_start - 1 : w_end); + m_width = std::stof(data.substr(w_start + 1, (w_end != data.npos) ? w_end - w_start - 1 : w_end)); + } + catch (...) + { + BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Width (" << comment << ")."; + } + } + + return true; + } + + std::cout << comment << "\n"; + return false; +} + +bool GCodeProcessor::process_craftware_tags(const std::string& comment) +{ + // segType -> extrusion role + std::string tag = "segType:"; + size_t pos = comment.find(tag); + if (pos != comment.npos) { + std::string type = comment.substr(pos + tag.length()); + if (type == "Skirt") + m_extrusion_role = erSkirt; + else if (type == "Perimeter") + m_extrusion_role = erExternalPerimeter; + else if (type == "HShell") + m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + else if (type == "InnerHair") + m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + else if (type == "Loop") + m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + else if (type == "Infill") + m_extrusion_role = erInternalInfill; + else if (type == "Raft") + m_extrusion_role = erSkirt; + else if (type == "Support") + m_extrusion_role = erSupportMaterial; + else if (type == "SupportTouch") + m_extrusion_role = erSupportMaterial; + else if (type == "SoftSupport") + m_extrusion_role = erSupportMaterialInterface; + else if (type == "Pillar") + m_extrusion_role = erWipeTower; + else { + m_extrusion_role = erNone; + BOOST_LOG_TRIVIAL(warning) << "GCodeProcessor found unknown extrusion role: " << type; + } + + return true; + } + + return false; +} + +bool GCodeProcessor::process_ideamaker_tags(const std::string& comment) +{ + // TYPE -> extrusion role + std::string tag = "TYPE:"; + size_t pos = comment.find(tag); + if (pos != comment.npos) { + std::string type = comment.substr(pos + tag.length()); + if (type == "RAFT") + m_extrusion_role = erSkirt; + else if (type == "WALL-OUTER") + m_extrusion_role = erExternalPerimeter; + else if (type == "WALL-INNER") + m_extrusion_role = erPerimeter; + else if (type == "SOLID-FILL") + m_extrusion_role = erSolidInfill; + else if (type == "FILL") + m_extrusion_role = erInternalInfill; + else if (type == "BRIDGE") + m_extrusion_role = erBridgeInfill; + else if (type == "SUPPORT") + m_extrusion_role = erSupportMaterial; + else { + m_extrusion_role = erNone; + BOOST_LOG_TRIVIAL(warning) << "GCodeProcessor found unknown extrusion role: " << type; + } + return true; + } + + // geometry + + // width + tag = "WIDTH:"; + pos = comment.find(tag); + if (pos != comment.npos) { + try + { + m_width = std::stof(comment.substr(pos + tag.length())); + } + catch (...) + { + BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Width (" << comment << ")."; + } + return true; + } + + // height + tag = "HEIGHT:"; + pos = comment.find(tag); + if (pos != comment.npos) { + try + { + m_height = std::stof(comment.substr(pos + tag.length())); + } + catch (...) + { + BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Height (" << comment << ")."; + } + return true; + } + + return false; +} + +bool GCodeProcessor::detect_producer(const std::string& comment) +{ + for (const auto& [id, search_string] : Producers) { + size_t pos = comment.find(search_string); + if (pos != comment.npos) { + m_producer = id; + BOOST_LOG_TRIVIAL(info) << "Detected gcode producer: " << search_string; + return true; + } + } + return false; +} + void GCodeProcessor::process_G0(const GCodeReader::GCodeLine& line) { process_G1(line); diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index d19d363f95..9507adf4d5 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -243,6 +243,20 @@ namespace Slic3r { ExtrudersColor m_extruders_color; CpColor m_cp_color; + enum class EProducer + { + Unknown, + PrusaSlicer, + Cura, + Simplify3D, + CraftWare, + ideaMaker + }; + + static const std::vector> Producers; + EProducer m_producer; + bool m_producers_enabled; + TimeProcessor m_time_processor; Result m_result; @@ -253,6 +267,7 @@ namespace Slic3r { void apply_config(const PrintConfig& config); void enable_stealth_time_estimator(bool enabled); + void enable_producers(bool enabled) { m_producers_enabled = enabled; } void reset(); const Result& get_result() const { return m_result; } @@ -274,6 +289,14 @@ namespace Slic3r { // Process tags embedded into comments void process_tags(const std::string& comment); + bool process_producers_tags(const std::string& comment); + bool process_prusaslicer_tags(const std::string& comment); + bool process_cura_tags(const std::string& comment); + bool process_simplify3d_tags(const std::string& comment); + bool process_craftware_tags(const std::string& comment); + bool process_ideamaker_tags(const std::string& comment); + + bool detect_producer(const std::string& comment); // Move void process_G0(const GCodeReader::GCodeLine& line); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 8a5863616e..193ac8e0c1 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4662,6 +4662,7 @@ void Plater::load_gcode(const wxString& filename) // process gcode GCodeProcessor processor; // processor.apply_config(config); + processor.enable_producers(true); processor.process_file(filename.ToUTF8().data()); p->gcode_result = std::move(processor.extract_result()); From 924bda6ec08f8940347423b7f8b3e1e1d3115e88 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 27 Jul 2020 20:06:10 +0200 Subject: [PATCH 212/503] ChangePresetForPhysicalPrinterDialog and SavePresetWindow are merged to SavePresetDialog --- src/slic3r/GUI/PresetComboBoxes.cpp | 301 ++++++++++++++++++++-------- src/slic3r/GUI/PresetComboBoxes.hpp | 55 +++-- src/slic3r/GUI/Tab.cpp | 61 +----- 3 files changed, 253 insertions(+), 164 deletions(-) diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 3edc3947a1..01c83921ab 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -1005,106 +1005,135 @@ void TabPresetComboBox::update_dirty() #endif /* __APPLE __ */ } -void TabPresetComboBox::update_physical_printers( const std::string& preset_name) -{ - if (m_type != Preset::TYPE_PRINTER || !m_allow_to_update_physical_printers) - return; - - m_allow_to_update_physical_printers = false; - - PhysicalPrinterCollection& physical_printers = m_preset_bundle->physical_printers; - if (!physical_printers.has_selection()) - return; - - std::string printer_preset_name = physical_printers.get_selected_printer_preset_name(); - - if (Preset::remove_suffix_modified(preset_name) == printer_preset_name) { - if (!this->is_selected_physical_printer()) - physical_printers.unselect_printer(); - } - else - { - ChangePresetForPhysicalPrinterDialog dlg(Preset::remove_suffix_modified(preset_name)); - if(dlg.ShowModal() == wxID_OK) - { - if (dlg.m_selection == ChangePresetForPhysicalPrinterDialog::Switch) - // unselect physical printer, if it was selected - m_preset_bundle->physical_printers.unselect_printer(); - else - { - PhysicalPrinter printer = physical_printers.get_selected_printer(); - - if (dlg.m_selection == ChangePresetForPhysicalPrinterDialog::ChangePreset) - printer.delete_preset(printer_preset_name); - - if (printer.add_preset(preset_name)) - physical_printers.save_printer(printer); - else { - wxMessageDialog dialog(nullptr, _L("This preset is already exist for this physical printer. Please, select another one."), _L("Information"), wxICON_INFORMATION | wxOK); - dialog.ShowModal(); - } - - physical_printers.select_printer(printer.get_full_name(preset_name)); - } - } - else - wxGetApp().get_tab(Preset::TYPE_PRINTER)->select_preset(printer_preset_name); - } -} - //----------------------------------------------- -// ChangePresetForPhysicalPrinterDialog +// SavePresetDialog //----------------------------------------------- -ChangePresetForPhysicalPrinterDialog::ChangePresetForPhysicalPrinterDialog(const std::string& preset_name) - : DPIDialog(nullptr, wxID_ANY, _L("Warning"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), -1), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING/* | wxRESIZE_BORDER*/) +SavePresetDialog::SavePresetDialog(TabPresetComboBox* preset_cb, const std::string& suffix) + : DPIDialog(nullptr, wxID_ANY, _L("Save preset"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), -1), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER), + m_preset_cb(preset_cb) { SetFont(wxGetApp().normal_font()); - SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - - PhysicalPrinterCollection& printers = wxGetApp().preset_bundle->physical_printers; - std::string printer_name = printers.get_selected_printer_name(); - std::string old_preset_name = printers.get_selected_printer_preset_name(); - - wxString msg_text = from_u8((boost::format(_u8L("You have selected physical printer \"%1%\"\n" - "with related printer preset \"%2%\"")) % - printer_name % old_preset_name).str()); - wxStaticText* label_top = new wxStaticText(this, wxID_ANY, msg_text); - label_top->SetFont(wxGetApp().bold_font()); - - wxString choices[] = { from_u8((boost::format(_u8L("Change \"%1%\" to \"%2%\" for this physical printer")) % old_preset_name % preset_name).str()), - from_u8((boost::format(_u8L("Add \"%1%\" as a next preset for the the physical printer")) % preset_name).str()), - from_u8((boost::format(_u8L("Just switch to \"%1%\"")) % preset_name).str()) }; - - wxRadioBox* selection_type_box = new wxRadioBox(this, wxID_ANY, _L("What would you like to do?"), wxDefaultPosition, wxDefaultSize, WXSIZEOF(choices), choices, - 3, wxRA_SPECIFY_ROWS); - selection_type_box->SetFont(wxGetApp().normal_font()); - selection_type_box->SetSelection(0); - - selection_type_box->Bind(wxEVT_RADIOBOX, [this](wxCommandEvent& e) { - int selection = e.GetSelection(); - m_selection = (SelectionType)selection; - }); - - auto radio_sizer = new wxBoxSizer(wxHORIZONTAL); - radio_sizer->Add(selection_type_box, 1, wxALIGN_CENTER_VERTICAL); - - wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); - wxButton* btnOK = static_cast(this->FindWindowById(wxID_OK, this)); - btnOK->Bind(wxEVT_BUTTON, &ChangePresetForPhysicalPrinterDialog::OnOK, this); + SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - topSizer->Add(label_top, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W); - topSizer->Add(radio_sizer, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W); - topSizer->Add(btns, 0, wxEXPAND | wxALL, BORDER_W); + add_common_items(topSizer, suffix); + add_items_for_edit_ph_printer(topSizer); + + // add dialog's buttons + wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); + wxButton* btnOK = static_cast(this->FindWindowById(wxID_OK, this)); + btnOK->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { accept(); }); + btnOK->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { + evt.Enable(!m_combo->GetValue().IsEmpty()); }); + + topSizer->Add(btns, 0, wxEXPAND | wxALL, BORDER_W); SetSizer(topSizer); topSizer->SetSizeHints(this); } -void ChangePresetForPhysicalPrinterDialog::on_dpi_changed(const wxRect& suggested_rect) +void SavePresetDialog::add_common_items(wxBoxSizer* sizer, const std::string& suffix) +{ + const PresetCollection* presets = m_preset_cb->presets(); + const Preset& sel_preset = presets->get_selected_preset(); + std::string preset_name = sel_preset.is_default ? "Untitled" : + sel_preset.is_system ? (boost::format(("%1% - %2%")) % sel_preset.name % suffix).str() : + sel_preset.name; + + // if name contains extension + if (boost::iends_with(preset_name, ".ini")) { + size_t len = preset_name.length() - 4; + preset_name.resize(len); + } + + std::vector values; + for (const Preset& preset : *presets) { + if (preset.is_default || preset.is_system || preset.is_external) + continue; + values.push_back(preset.name); + } + + wxStaticText* label_top = new wxStaticText(this, wxID_ANY, from_u8((boost::format(_utf8(L("Save %s as:"))) % into_u8(wxGetApp().get_tab(m_preset_cb->type())->title())).str())); + m_combo = new wxComboBox(this, wxID_ANY, from_u8(preset_name), + wxDefaultPosition, wxDefaultSize, 0, 0, wxTE_PROCESS_ENTER); + for (auto value : values) + m_combo->Append(from_u8(value)); + + m_combo->Bind(wxEVT_TEXT_ENTER, [this](wxCommandEvent&) { accept(); }); + m_combo->Bind(wxEVT_TEXT, [this](wxCommandEvent&) { + update(normalize_utf8_nfc(m_combo->GetValue().ToUTF8())); + this->Layout(); + this->Fit(); + }); + + sizer->Add(label_top, 0, wxEXPAND | wxALL, BORDER_W); + sizer->Add(m_combo, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, BORDER_W); +} + +void SavePresetDialog::add_items_for_edit_ph_printer(wxBoxSizer* sizer) +{ + if (m_preset_cb->type() != Preset::TYPE_PRINTER || !m_preset_cb->is_selected_physical_printer()) + return; + + PhysicalPrinterCollection& printers = wxGetApp().preset_bundle->physical_printers; + m_ph_printer_name = printers.get_selected_printer_name(); + m_old_preset_name = printers.get_selected_printer_preset_name(); + + wxString msg_text = from_u8((boost::format(_u8L("You have selected physical printer \"%1%\" \n" + "with related printer preset \"%2%\"")) % + m_ph_printer_name % m_old_preset_name).str()); + m_label = new wxStaticText(this, wxID_ANY, msg_text); + m_label->SetFont(wxGetApp().bold_font()); + + wxString choices[] = {"","",""}; + + m_action_radio_box = new wxRadioBox(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, + WXSIZEOF(choices), choices, 3, wxRA_SPECIFY_ROWS); + m_action_radio_box->SetFont(wxGetApp().normal_font()); + m_action_radio_box->SetSelection(0); + m_action_radio_box->Bind(wxEVT_RADIOBOX, [this](wxCommandEvent& e) { + m_action = (ActionType)e.GetSelection(); }); + m_action = ChangePreset; + + m_radio_sizer = new wxBoxSizer(wxHORIZONTAL); + m_radio_sizer->Add(m_action_radio_box, 1, wxALIGN_CENTER_VERTICAL); + + sizer->Add(m_label, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W); + sizer->Add(m_radio_sizer, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W); + + update(m_preset_name); +} + +void SavePresetDialog::update(const std::string& preset_name) +{ + if (m_preset_cb->type() != Preset::TYPE_PRINTER || !m_preset_cb->is_selected_physical_printer()) + return; + + bool show = m_old_preset_name != preset_name; + + m_label->Show(show); + m_radio_sizer->ShowItems(show); + if (!show) { + this->SetMinSize(wxSize(100,50)); + return; + } + + wxString msg_text = from_u8((boost::format(_u8L("What would you like to do with \"%1%\" preset after saving?")) % preset_name).str()); + m_action_radio_box->SetLabel(msg_text); + + wxString choices[] = { from_u8((boost::format(_u8L("Change \"%1%\" to \"%2%\" for this physical printer \"%3%\"")) % m_old_preset_name % preset_name % m_ph_printer_name).str()), + from_u8((boost::format(_u8L("Add \"%1%\" as a next preset for the the physical printer \"%2%\"")) % preset_name % m_ph_printer_name).str()), + from_u8((boost::format(_u8L("Just switch to \"%1%\" preset")) % preset_name).str()) }; + + int n = 0; + for(const wxString& label: choices) + m_action_radio_box->SetString(n++, label); +} + +void SavePresetDialog::on_dpi_changed(const wxRect& suggested_rect) { const int& em = em_unit(); @@ -1117,10 +1146,106 @@ void ChangePresetForPhysicalPrinterDialog::on_dpi_changed(const wxRect& suggeste Refresh(); } -void ChangePresetForPhysicalPrinterDialog::OnOK(wxEvent& event) +bool SavePresetDialog::preset_name_is_accepted() { - event.Skip(); + const char* unusable_symbols = "<>[]:/\\|?*\""; + const std::string unusable_suffix = PresetCollection::get_suffix_modified();//"(modified)"; + for (size_t i = 0; i < std::strlen(unusable_symbols); i++) { + if (m_preset_name.find_first_of(unusable_symbols[i]) != std::string::npos) { + show_error(this, _L("The supplied name is not valid;") + "\n" + + _L("the following characters are not allowed:") + " " + unusable_symbols); + return false; + } + } + + if (m_preset_name.find(unusable_suffix) != std::string::npos) { + show_error(this, _L("The supplied name is not valid;") + "\n" + + _L("the following suffix is not allowed:") + "\n\t" + + from_u8(PresetCollection::get_suffix_modified())); + return false; + } + + if (m_preset_name == "- default -") { + show_error(this, _L("The supplied name is not available.")); + return false; + } + return true; +} + +bool SavePresetDialog::preset_is_possible_to_save() +{ + const Preset* existing = m_preset_cb->presets()->find_preset(m_preset_name, false); + if (existing && (existing->is_default || existing->is_system)) { + show_error(this, _L("Cannot overwrite a system profile.")); + return false; + } + if (existing && (existing->is_external)) { + show_error(this, _(L("Cannot overwrite an external profile."))); + return false; + } + if (existing && m_preset_name != m_preset_cb->presets()->get_selected_preset_name()) + { + wxString msg_text = GUI::from_u8((boost::format(_utf8(L("Preset with name \"%1%\" already exists."))) % m_preset_name).str()); + msg_text += "\n" + _L("Replace?"); + wxMessageDialog dialog(nullptr, msg_text, _L("Warning"), wxICON_WARNING | wxYES | wxNO); + + if (dialog.ShowModal() == wxID_NO) + return false; + + // Remove the preset from the list. + m_preset_cb->presets()->delete_preset(m_preset_name); + } + return true; +} + +void SavePresetDialog::update_physical_printers() +{ + if (m_action == UndefAction) + return; + + PhysicalPrinterCollection& physical_printers = wxGetApp().preset_bundle->physical_printers; + if (!physical_printers.has_selection()) + return; + + std::string printer_preset_name = physical_printers.get_selected_printer_preset_name(); + + if (m_action == Switch) + // unselect physical printer, if it was selected + physical_printers.unselect_printer(); + else + { + PhysicalPrinter printer = physical_printers.get_selected_printer(); + + if (m_action == ChangePreset) + printer.delete_preset(printer_preset_name); + + if (printer.add_preset(m_preset_name)) + physical_printers.save_printer(printer); + else { + wxMessageDialog dialog(nullptr, _L("This preset is already exist for this physical printer. Please, select another one."), _L("Information"), wxICON_INFORMATION | wxOK); + dialog.ShowModal(); + } + + physical_printers.select_printer(printer.get_full_name(m_preset_name)); + } +} + +void SavePresetDialog::accept() +{ + m_preset_name = normalize_utf8_nfc(m_combo->GetValue().ToUTF8()); + + if (m_preset_name.empty()) + return; + + if (!preset_name_is_accepted() || + !preset_is_possible_to_save()) + return; + + update_physical_printers(); + + EndModal(wxID_OK); } + }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index 6b8017f311..fa4554a5ea 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -13,6 +13,7 @@ class wxTextCtrl; class wxStaticText; class ScalableButton; class wxBoxSizer; +class wxComboBox; namespace Slic3r { @@ -167,50 +168,68 @@ class TabPresetComboBox : public PresetComboBox bool show_incompatible {false}; bool m_enable_all {false}; - bool m_allow_to_update_physical_printers {false}; - public: TabPresetComboBox(wxWindow *parent, Preset::Type preset_type); ~TabPresetComboBox() {} void set_show_incompatible_presets(bool show_incompatible_presets) { show_incompatible = show_incompatible_presets; } - void allow_to_update_physical_printers() { - m_allow_to_update_physical_printers = m_type == Preset::TYPE_PRINTER; - } void update() override; void update_dirty(); - void update_physical_printers(const std::string& preset_name); void msw_rescale() override; void set_enable_all(bool enable=true) { m_enable_all = enable; } + + PresetCollection* presets() const { return m_collection; } + Preset::Type type() const { return m_type; } }; //------------------------------------------------ -// ChangePresetForPhysicalPrinterDialog +// SavePresetDialog //------------------------------------------------ -class ChangePresetForPhysicalPrinterDialog : public DPIDialog +class SavePresetDialog : public DPIDialog { - void OnOK(wxEvent& event); + enum ActionType + { + ChangePreset, + AddPreset, + Switch, + UndefAction + }; + + TabPresetComboBox* m_preset_cb {nullptr}; + std::string m_preset_name; + wxComboBox* m_combo {nullptr}; + wxStaticText* m_label {nullptr}; + wxRadioBox* m_action_radio_box {nullptr}; + wxBoxSizer* m_radio_sizer {nullptr}; + ActionType m_action {UndefAction}; + + std::string m_ph_printer_name; + std::string m_old_preset_name; public: - enum SelectionType - { - Switch, - ChangePreset, - AddPreset - } m_selection {Switch}; + SavePresetDialog(TabPresetComboBox* preset_cb, const std::string& suffix); + ~SavePresetDialog() {} - ChangePresetForPhysicalPrinterDialog(const std::string& preset_name); - ~ChangePresetForPhysicalPrinterDialog() {} + std::string get_name() { return m_preset_name; } protected: void on_dpi_changed(const wxRect& suggested_rect) override; - void on_sys_color_changed() override {}; + void on_sys_color_changed() override {} + +private: + void add_common_items(wxBoxSizer *sizer, const std::string &suffix); + void add_items_for_edit_ph_printer(wxBoxSizer *sizer); + void update(const std::string &preset_name); + bool preset_name_is_accepted(); + bool preset_is_possible_to_save(); + void update_physical_printers(); + void accept(); }; } // namespace GUI diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 3f566eacb3..a97c10f7d5 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -164,11 +164,8 @@ void Tab::create_preset_tab() m_presets_choice->set_selection_changed_function([this](int selection) { if (!m_presets_choice->selection_is_changed_according_to_physical_printers()) { - // For the printer presets allow to update a physical printer if it is needed. - // After call of the update_physical_printers() this possibility will be disabled again to avoid a case, - // when select_preset is called from the others than this place - if (m_type == Preset::TYPE_PRINTER) - m_presets_choice->allow_to_update_physical_printers(); + if (m_type == Preset::TYPE_PRINTER && !m_presets_choice->is_selected_physical_printer()) + m_preset_bundle->physical_printers.unselect_printer(); // select preset select_preset(m_presets_choice->GetString(selection).ToUTF8().data()); @@ -3148,9 +3145,6 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, m_dependent_tabs = { Preset::Type::TYPE_SLA_PRINT, Preset::Type::TYPE_SLA_MATERIAL }; } - //update physical printer's related printer preset if it's needed - m_presets_choice->update_physical_printers(preset_name); - load_current_preset(); } } @@ -3290,56 +3284,10 @@ void Tab::save_preset(std::string name /*= ""*/, bool detach) std::string suffix = detach ? _utf8(L("Detached")) : _CTX_utf8(L_CONTEXT("Copy", "PresetName"), "PresetName"); if (name.empty()) { - const Preset &preset = m_presets->get_selected_preset(); - auto default_name = preset.is_default ? "Untitled" : -// preset.is_system ? (boost::format(_CTX_utf8(L_CONTEXT("%1% - Copy", "PresetName"), "PresetName")) % preset.name).str() : - preset.is_system ? (boost::format(("%1% - %2%")) % preset.name % suffix).str() : - preset.name; - - bool have_extention = boost::iends_with(default_name, ".ini"); - if (have_extention) { - size_t len = default_name.length()-4; - default_name.resize(len); - } - //[map $_->name, grep !$_->default && !$_->external, @{$self->{presets}}], - std::vector values; - for (size_t i = 0; i < m_presets->size(); ++i) { - const Preset &preset = m_presets->preset(i); - if (preset.is_default || preset.is_system || preset.is_external) - continue; - values.push_back(preset.name); - } - - SavePresetWindow dlg(parent()); - dlg.build(title(), default_name, values); + SavePresetDialog dlg(m_presets_choice, suffix); if (dlg.ShowModal() != wxID_OK) return; name = dlg.get_name(); - if (name == "") { - show_error(this, _(L("The supplied name is empty. It can't be saved."))); - return; - } - const Preset *existing = m_presets->find_preset(name, false); - if (existing && (existing->is_default || existing->is_system)) { - show_error(this, _(L("Cannot overwrite a system profile."))); - return; - } - if (existing && (existing->is_external)) { - show_error(this, _(L("Cannot overwrite an external profile."))); - return; - } - if (existing && name != preset.name) - { - wxString msg_text = GUI::from_u8((boost::format(_utf8(L("Preset with name \"%1%\" already exists."))) % name).str()); - msg_text += "\n" + _(L("Replace?")); - wxMessageDialog dialog(nullptr, msg_text, _(L("Warning")), wxICON_WARNING | wxYES | wxNO); - - if (dialog.ShowModal() == wxID_NO) - return; - - // Remove the preset from the list. - m_presets->delete_preset(name); - } } // Save the preset into Slic3r::data_dir / presets / section_name / preset_name.ini @@ -3347,9 +3295,6 @@ void Tab::save_preset(std::string name /*= ""*/, bool detach) // Mark the print & filament enabled if they are compatible with the currently selected preset. // If saving the preset changes compatibility with other presets, keep the now incompatible dependent presets selected, however with a "red flag" icon showing that they are no more compatible. m_preset_bundle->update_compatible(PresetSelectCompatibleType::Never); - //update physical printer's related printer preset if it's needed - m_presets_choice->allow_to_update_physical_printers(); - m_presets_choice->update_physical_printers(name); // Add the new item into the UI component, remove dirty flags and activate the saved item. update_tab_ui(); // Update the selection boxes at the plater. From d9228ee82cc541f997da7a731893c44a59023445 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 28 Jul 2020 09:48:55 +0200 Subject: [PATCH 213/503] GCodeProcessor -> Human readable extrusion roles in gcode --- src/libslic3r/ExtrusionEntity.cpp | 36 ++++++++++++++++++++++++++ src/libslic3r/ExtrusionEntity.hpp | 1 + src/libslic3r/GCode.cpp | 6 ++--- src/libslic3r/GCode/GCodeProcessor.cpp | 23 ++++------------ src/libslic3r/GCode/WipeTower.cpp | 2 +- 5 files changed, 46 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index d629a3c89b..b2c5e1350f 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -331,4 +331,40 @@ std::string ExtrusionEntity::role_to_string(ExtrusionRole role) return ""; } +ExtrusionRole ExtrusionEntity::string_to_role(const std::string& role) +{ + if (role == L("Perimeter")) + return erPerimeter; + else if (role == L("External perimeter")) + return erExternalPerimeter; + else if (role == L("Overhang perimeter")) + return erOverhangPerimeter; + else if (role == L("Internal infill")) + return erInternalInfill; + else if (role == L("Solid infill")) + return erSolidInfill; + else if (role == L("Top solid infill")) + return erTopSolidInfill; + else if (role == L("Ironing")) + return erIroning; + else if (role == L("Bridge infill")) + return erBridgeInfill; + else if (role == L("Gap fill")) + return erGapFill; + else if (role == L("Skirt")) + return erSkirt; + else if (role == L("Support material")) + return erSupportMaterial; + else if (role == L("Support material interface")) + return erSupportMaterialInterface; + else if (role == L("Wipe tower")) + return erWipeTower; + else if (role == L("Custom")) + return erCustom; + else if (role == L("Mixed")) + return erMixed; + else + return erNone; +} + } diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index 879f564b6c..3d7e581128 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -106,6 +106,7 @@ public: virtual double total_volume() const = 0; static std::string role_to_string(ExtrusionRole role); + static ExtrusionRole string_to_role(const std::string& role); }; typedef std::vector ExtrusionEntitiesPtr; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 8a835e07d5..742498baf8 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1388,7 +1388,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu #if ENABLE_GCODE_VIEWER // adds tag for processor - _write_format(file, ";%s%d\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), erCustom); + _write_format(file, ";%s%s\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), ExtrusionEntity::role_to_string(erCustom).c_str()); #else if (m_enable_analyzer) // adds tag for analyzer @@ -1546,7 +1546,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu #if ENABLE_GCODE_VIEWER // adds tag for processor - _write_format(file, ";%s%d\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), erCustom); + _write_format(file, ";%s%s\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), ExtrusionEntity::role_to_string(erCustom).c_str()); #else if (m_enable_analyzer) // adds tag for analyzer @@ -3226,7 +3226,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, #if ENABLE_GCODE_VIEWER if (path.role() != m_last_processor_extrusion_role) { m_last_processor_extrusion_role = path.role(); - sprintf(buf, ";%s%d\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), int(m_last_processor_extrusion_role)); + sprintf(buf, ";%s%s\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), ExtrusionEntity::role_to_string(m_last_processor_extrusion_role).c_str()); gcode += buf; } #else diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 42d1c07575..44598d2408 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -21,13 +21,13 @@ static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2 namespace Slic3r { -const std::string GCodeProcessor::Extrusion_Role_Tag = "PrusaSlicer__EXTRUSION_ROLE:"; +const std::string GCodeProcessor::Extrusion_Role_Tag = "ExtrType:"; const std::string GCodeProcessor::Width_Tag = "PrusaSlicer__WIDTH:"; const std::string GCodeProcessor::Height_Tag = "PrusaSlicer__HEIGHT:"; const std::string GCodeProcessor::Mm3_Per_Mm_Tag = "PrusaSlicer__MM3_PER_MM:"; -const std::string GCodeProcessor::Color_Change_Tag = "PrusaSlicer__COLOR_CHANGE"; -const std::string GCodeProcessor::Pause_Print_Tag = "PrusaSlicer__PAUSE_PRINT"; -const std::string GCodeProcessor::Custom_Code_Tag = "PrusaSlicer__CUSTOM_CODE"; +const std::string GCodeProcessor::Color_Change_Tag = "COLOR_CHANGE"; +const std::string GCodeProcessor::Pause_Print_Tag = "PAUSE_PRINT"; +const std::string GCodeProcessor::Custom_Code_Tag = "CUSTOM_CODE"; static bool is_valid_extrusion_role(int value) { @@ -560,20 +560,7 @@ void GCodeProcessor::process_tags(const std::string& comment) // extrusion role tag size_t pos = comment.find(Extrusion_Role_Tag); if (pos != comment.npos) { - try - { - int role = std::stoi(comment.substr(pos + Extrusion_Role_Tag.length())); - if (is_valid_extrusion_role(role)) - m_extrusion_role = static_cast(role); - else { - // todo: show some error ? - } - } - catch (...) - { - BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Extrusion Role (" << comment << ")."; - } - + m_extrusion_role = ExtrusionEntity::string_to_role(comment.substr(pos + Extrusion_Role_Tag.length())); return; } diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 73ac36dc41..e4f995aba1 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -64,7 +64,7 @@ public: #endif // ENABLE_GCODE_VIEWER m_gcode += buf; #if ENABLE_GCODE_VIEWER - sprintf(buf, ";%s%d\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), erWipeTower); + sprintf(buf, ";%s%s\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), ExtrusionEntity::role_to_string(erWipeTower).c_str()); #else sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erWipeTower); #endif // ENABLE_GCODE_VIEWER From 68ae95509fc4410baa9069cb407b8bb756e2be10 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 28 Jul 2020 11:31:16 +0200 Subject: [PATCH 214/503] Improved InfoMsg for a delete preset: * Now we show a list of printers name with selected preset + Added a edit_button for the editing of the physical printer fro the Settings Tab + Show whole list of the loaded presets with "Print host upload" --- src/libslic3r/Preset.cpp | 65 ++++++---- src/libslic3r/Preset.hpp | 11 +- src/slic3r/GUI/GUI_App.cpp | 38 ++---- src/slic3r/GUI/PhysicalPrinterDialog.cpp | 2 + src/slic3r/GUI/PresetComboBoxes.cpp | 15 ++- src/slic3r/GUI/PresetComboBoxes.hpp | 2 +- src/slic3r/GUI/Tab.cpp | 153 ++++++++++++++--------- src/slic3r/GUI/Tab.hpp | 3 +- 8 files changed, 173 insertions(+), 116 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 7e30831fe5..c7acf8f495 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1383,13 +1383,14 @@ const std::vector& PhysicalPrinter::print_host_options() return s_opts; } -bool PhysicalPrinter::has_print_host_information(const PrinterPresetCollection& printer_presets) +std::vector PhysicalPrinter::presets_with_print_host_information(const PrinterPresetCollection& printer_presets) { + std::vector presets; for (const Preset& preset : printer_presets) if (has_print_host_information(preset.config)) - return true; + presets.emplace_back(preset.name); - return false; + return presets; } bool PhysicalPrinter::has_print_host_information(const DynamicPrintConfig& config) @@ -1564,7 +1565,7 @@ void PhysicalPrinterCollection::load_printers(const std::string& dir_path, const // if there is saved user presets, contains information about "Print Host upload", // Create default printers with this presets // Note! "Print Host upload" options will be cleared after physical printer creations -void PhysicalPrinterCollection::load_printers_from_presets(PrinterPresetCollection& printer_presets, std::string def_printer_name) +void PhysicalPrinterCollection::load_printers_from_presets(PrinterPresetCollection& printer_presets) { int cnt=0; for (Preset& preset: printer_presets) { @@ -1579,8 +1580,12 @@ void PhysicalPrinterCollection::load_printers_from_presets(PrinterPresetCollecti // just add preset for this printer existed_printer->add_preset(preset.name); else { + std::string new_printer_name = (boost::format("Printer %1%") % ++cnt ).str(); + while (find_printer(new_printer_name)) + new_printer_name = (boost::format("Printer %1%") % ++cnt).str(); + // create new printer from this preset - PhysicalPrinter printer((boost::format("%1% %2%") % def_printer_name % ++cnt ).str(), preset); + PhysicalPrinter printer(new_printer_name, preset); printer.loaded = true; save_printer(printer); } @@ -1699,33 +1704,51 @@ bool PhysicalPrinterCollection::delete_selected_printer() return true; } -bool PhysicalPrinterCollection::delete_preset_from_printers( const std::string& preset_name, bool first_check /*=true*/) +bool PhysicalPrinterCollection::delete_preset_from_printers( const std::string& preset_name) { - if (first_check) { - for (auto printer: m_printers) - if (printer.preset_names.size()==1 && *printer.preset_names.begin() == preset_name) - return false; - } - std::vector printers_for_delete; - for (PhysicalPrinter& printer : m_printers) + for (PhysicalPrinter& printer : m_printers) { if (printer.preset_names.size() == 1 && *printer.preset_names.begin() == preset_name) printers_for_delete.emplace_back(printer.name); - else if (printer.delete_preset(preset_name)) { - if (printer.name == get_selected_printer_name() && - preset_name == get_selected_printer_preset_name()) - select_printer(printer); + else if (printer.delete_preset(preset_name)) save_printer(printer); - } + } - if (!printers_for_delete.empty()) { + if (!printers_for_delete.empty()) for (const std::string& printer_name : printers_for_delete) delete_printer(printer_name); - unselect_printer(); - } + + unselect_printer(); return true; } +// Get list of printers which have more than one preset and "preset_name" preset is one of them +std::vector PhysicalPrinterCollection::get_printers_with_preset(const std::string& preset_name) +{ + std::vector printers; + + for (auto printer : m_printers) { + if (printer.preset_names.size() == 1) + continue; + if (printer.preset_names.find(preset_name) != printer.preset_names.end()) + printers.emplace_back(printer.name); + } + + return printers; +} + +// Get list of printers which has only "preset_name" preset +std::vector PhysicalPrinterCollection::get_printers_with_only_preset(const std::string& preset_name) +{ + std::vector printers; + + for (auto printer : m_printers) + if (printer.preset_names.size() == 1 && *printer.preset_names.begin() == preset_name) + printers.emplace_back(printer.name); + + return printers; +} + std::string PhysicalPrinterCollection::get_selected_full_printer_name() const { return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_printer().get_full_name(m_selected_preset); diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index 6b5a2a5112..e34fca4dd7 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -555,7 +555,7 @@ public: static std::string separator(); static const std::vector& printer_options(); static const std::vector& print_host_options(); - static bool has_print_host_information(const PrinterPresetCollection& printer_presets); + static std::vector presets_with_print_host_information(const PrinterPresetCollection& printer_presets); static bool has_print_host_information(const DynamicPrintConfig& config); const std::set& get_preset_names() const; @@ -629,7 +629,7 @@ public: // Load ini files of the particular type from the provided directory path. void load_printers(const std::string& dir_path, const std::string& subdir); - void load_printers_from_presets(PrinterPresetCollection &printer_presets, std::string def_printer_name); + void load_printers_from_presets(PrinterPresetCollection &printer_presets); // Save the printer under a new name. If the name is different from the old one, // a new printer is stored into the list of printers. @@ -645,7 +645,12 @@ public: // Delete preset_name preset from all printers: // If there is last preset for the printer and first_check== false, then delete this printer // returns true if all presets were deleted successfully. - bool delete_preset_from_printers(const std::string& preset_name, bool first_check = true); + bool delete_preset_from_printers(const std::string& preset_name); + + // Get list of printers which have more than one preset and "preset_name" preset is one of them + std::vector get_printers_with_preset( const std::string &preset_name); + // Get list of printers which has only "preset_name" preset + std::vector get_printers_with_only_preset( const std::string &preset_name); // Return the selected preset, without the user modifications applied. PhysicalPrinter& get_selected_printer() { return m_printers[m_idx_selected]; } diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 1b7278bd7f..410614b948 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -635,38 +635,22 @@ void GUI_App::set_auto_toolbar_icon_scale(float scale) const // check user printer_presets for the containing information about "Print Host upload" void GUI_App::check_printer_presets() { - if (!PhysicalPrinter::has_print_host_information(preset_bundle->printers)) + std::vector preset_names = PhysicalPrinter::presets_with_print_host_information(preset_bundle->printers); + if (preset_names.empty()) return; - wxString msg_text = _L("You have presets with saved options for \"Print Host upload\".\n" - "But from this version of PrusaSlicer we don't show/use this information in Printer Settings.\n" + wxString msg_text = _L("You have next presets with saved options for \"Print Host upload\"") + ":"; + for (const std::string& preset_name : preset_names) + msg_text += "\n \"" + from_u8(preset_name) + "\","; + msg_text.RemoveLast(); + msg_text += "\n\n" + _L("But from this version of PrusaSlicer we don't show/use this information in Printer Settings.\n" "Now, this information will be exposed in physical printers settings.") + "\n\n" + - _L("Enter the name for the Printer device used by default during its creation.\n" - "Note: This name can be changed later from the physical printers settings") + ":"; - wxString msg_header = _L("Name for printer device"); + _L("By default new Printer devices will be named as \"Printer N\" during its creation.\n" + "Note: This name can be changed later from the physical printers settings"); - // get custom gcode - wxTextEntryDialog dlg(nullptr, msg_text, msg_header, _L("Printer"), wxTextEntryDialogStyle); + wxMessageDialog(nullptr, msg_text, _L("Information"), wxOK | wxICON_INFORMATION).ShowModal(); - // detect TextCtrl and OK button - wxTextCtrl* textctrl{ nullptr }; - wxWindowList& dlg_items = dlg.GetChildren(); - for (auto item : dlg_items) { - textctrl = dynamic_cast(item); - if (textctrl) - break; - } - - if (textctrl) { - textctrl->SetSelection(0, textctrl->GetLastPosition()); - - wxButton* btn_OK = static_cast(dlg.FindWindowById(wxID_OK)); - btn_OK->Bind(wxEVT_UPDATE_UI, [textctrl](wxUpdateUIEvent& evt) { - evt.Enable(!textctrl->IsEmpty()); - }, btn_OK->GetId()); - } - if (dlg.ShowModal() == wxID_OK) - preset_bundle->physical_printers.load_printers_from_presets(preset_bundle->printers, into_u8(dlg.GetValue())); + preset_bundle->physical_printers.load_printers_from_presets(preset_bundle->printers); } void GUI_App::recreate_GUI(const wxString& msg_name) diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.cpp b/src/slic3r/GUI/PhysicalPrinterDialog.cpp index 7d3c92c138..f14f498010 100644 --- a/src/slic3r/GUI/PhysicalPrinterDialog.cpp +++ b/src/slic3r/GUI/PhysicalPrinterDialog.cpp @@ -510,6 +510,8 @@ void PhysicalPrinterDialog::OnOK(wxEvent& event) // refresh preset list on Printer Settings Tab wxGetApp().get_tab(Preset::TYPE_PRINTER)->select_preset(printers.get_selected_printer_preset_name()); } + else + wxGetApp().get_tab(Preset::TYPE_PRINTER)->update_preset_choice(); event.Skip(); } diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 01c83921ab..666f10194c 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -171,7 +171,7 @@ void PresetComboBox::update_selection() SetToolTip(GetString(m_last_selected)); } -void PresetComboBox::update(const std::string& select_preset_name) +void PresetComboBox::update(std::string select_preset_name) { Freeze(); Clear(); @@ -192,6 +192,8 @@ void PresetComboBox::update(const std::string& select_preset_name) // marker used for disable incompatible printer models for the selected physical printer bool is_enabled = m_type == Preset::TYPE_PRINTER && printer_technology != ptAny ? preset.printer_technology() == printer_technology : true; + if (select_preset_name.empty() && is_enabled) + select_preset_name = preset.name; std::string bitmap_key = "cb"; if (m_type == Preset::TYPE_PRINTER) { @@ -208,7 +210,7 @@ void PresetComboBox::update(const std::string& select_preset_name) int item_id = Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), *bmp); if (!is_enabled) set_label_marker(item_id, LABEL_ITEM_DISABLED); - validate_selection(preset.name == select_preset_name || (select_preset_name.empty() && is_enabled)); + validate_selection(preset.name == select_preset_name); } else { @@ -659,6 +661,13 @@ void PlaterPresetComboBox::show_edit_menu() wxTheApp->CallAfter([]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, ConfigWizard::SP_PRINTERS); }); }, "edit_uni", menu, []() { return true; }, wxGetApp().plater()); + append_menu_item(menu, wxID_ANY, _L("Add physical printer"), "", + [this](wxCommandEvent&) { + PhysicalPrinterDialog dlg(wxEmptyString); + if (dlg.ShowModal() == wxID_OK) + update(); + }, "edit_uni", menu, []() { return true; }, wxGetApp().plater()); + wxGetApp().plater()->PopupMenu(menu); } @@ -784,7 +793,7 @@ void PlaterPresetComboBox::update() } } - if (/*m_type == Preset::TYPE_PRINTER || */m_type == Preset::TYPE_SLA_MATERIAL) { + if (m_type == Preset::TYPE_PRINTER || m_type == Preset::TYPE_SLA_MATERIAL) { wxBitmap* bmp = get_bmp("edit_preset_list", wide_icons, "edit_uni"); assert(bmp); diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index fa4554a5ea..a30d9f6e90 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -55,7 +55,7 @@ public: // and next internal selection was accomplished bool selection_is_changed_according_to_physical_printers(); - void update(const std::string& select_preset); + void update(std::string select_preset); virtual void update(); virtual void msw_rescale(); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index a97c10f7d5..4dbe7cc742 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -35,6 +35,7 @@ #include "Plater.hpp" #include "MainFrame.hpp" #include "format.hpp" +#include "PhysicalPrinterDialog.hpp" namespace Slic3r { namespace GUI { @@ -180,6 +181,8 @@ void Tab::create_preset_tab() add_scaled_button(panel, &m_btn_save_preset, "save"); add_scaled_button(panel, &m_btn_delete_preset, "cross"); + if (m_type == Preset::Type::TYPE_PRINTER) + add_scaled_button(panel, &m_btn_edit_ph_printer, "cog"); m_show_incompatible_presets = false; add_scaled_bitmap(this, m_bmp_show_incompatible_presets, "flag_red"); @@ -191,6 +194,8 @@ void Tab::create_preset_tab() m_btn_save_preset->SetToolTip(from_u8((boost::format(_utf8(L("Save current %s"))) % m_title).str())); m_btn_delete_preset->SetToolTip(_(L("Delete this preset"))); m_btn_delete_preset->Disable(); + if (m_btn_edit_ph_printer) + m_btn_edit_ph_printer->Disable(); add_scaled_button(panel, &m_question_btn, "question"); m_question_btn->SetToolTip(_(L("Hover the cursor over buttons to find more information \n" @@ -245,6 +250,10 @@ void Tab::create_preset_tab() m_hsizer->Add(m_btn_save_preset, 0, wxALIGN_CENTER_VERTICAL); m_hsizer->AddSpacer(int(4 * scale_factor)); m_hsizer->Add(m_btn_delete_preset, 0, wxALIGN_CENTER_VERTICAL); + if (m_btn_edit_ph_printer) { + m_hsizer->AddSpacer(int(4 * scale_factor)); + m_hsizer->Add(m_btn_edit_ph_printer, 0, wxALIGN_CENTER_VERTICAL); + } m_hsizer->AddSpacer(int(/*16*/8 * scale_factor)); m_hsizer->Add(m_btn_hide_incompatible_presets, 0, wxALIGN_CENTER_VERTICAL); m_hsizer->AddSpacer(int(8 * scale_factor)); @@ -291,6 +300,13 @@ void Tab::create_preset_tab() toggle_show_hide_incompatible(); })); + if (m_btn_edit_ph_printer) + m_btn_edit_ph_printer->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { + PhysicalPrinterDialog dlg(m_presets_choice->GetString(m_presets_choice->GetSelection())); + if (dlg.ShowModal() == wxID_OK) + update_tab_ui(); + })); + // Fill cache for mode bitmaps m_mode_bitmap_cache.reserve(3); m_mode_bitmap_cache.push_back(ScalableBitmap(this, "mode_simple" , mode_icon_px_size())); @@ -2786,8 +2802,7 @@ void Tab::load_current_preset() { const Preset& preset = m_presets->get_edited_preset(); -// (preset.is_default || preset.is_system) ? m_btn_delete_preset->Disable() : m_btn_delete_preset->Enable(true); - update_delete_preset_btn(); + update_btns_enabling(); update(); if (m_type == Slic3r::Preset::TYPE_PRINTER) { @@ -2924,23 +2939,25 @@ void Tab::update_page_tree_visibility() } -void Tab::update_delete_preset_btn() +void Tab::update_btns_enabling() { - if (m_type == Preset::TYPE_PRINTER && m_presets_choice->is_selected_physical_printer() && - m_preset_bundle->physical_printers.has_selection()) { - // we can't delete last preset from the physical printer + // we can't delete last preset from the physical printer + if (m_type == Preset::TYPE_PRINTER && m_preset_bundle->physical_printers.has_selection()) m_btn_delete_preset->Enable(m_preset_bundle->physical_printers.get_selected_printer().preset_names.size() > 1); - } else { const Preset& preset = m_presets->get_edited_preset(); m_btn_delete_preset->Enable(!preset.is_default && !preset.is_system); } + + // we can edit physical printer only if it's selected in the list + if (m_btn_edit_ph_printer) + m_btn_edit_ph_printer->Enable(m_preset_bundle->physical_printers.has_selection()); } void Tab::update_preset_choice() { m_presets_choice->update(); - update_delete_preset_btn(); + update_btns_enabling(); } // Called by the UI combo box when the user switches profiles, and also to delete the current profile. @@ -2950,55 +2967,16 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, { if (preset_name.empty()) { if (delete_current) { - PhysicalPrinterCollection& physical_printers = m_preset_bundle->physical_printers; - if (m_presets_choice->is_selected_physical_printer()) { - PhysicalPrinter& printer = physical_printers.get_selected_printer(); - - if (printer.preset_names.size()==1) { - wxMessageDialog dialog(nullptr, _L("It's a last for this physical printer. We can't delete it"), _L("Information"), wxICON_INFORMATION | wxOK); - dialog.ShowModal(); - } - else { - // just delete this preset from the current physical printer - printer.delete_preset(m_presets->get_edited_preset().name); - // select first from the possible presets for this printer - physical_printers.select_printer(printer); - - preset_name = physical_printers.get_selected_printer_preset_name(); - // revert delete_current value to avoid deleting of the new selected preset - delete_current = false; - } - } - else { - // Check preset for delete in physical printers - // Ask a customer about next action , if there is a printer with just one preset and this preset is equal to delete - if (m_type == Preset::TYPE_PRINTER && !physical_printers.empty() ) - { - // try to delete selected preset from the all printers it has - if (!physical_printers.delete_preset_from_printers(m_presets->get_edited_preset().name)) - { - wxMessageDialog dialog(nullptr, _L("There is/are a physical printer(s), which has/have one and only this printer preset.\n" - "This/Those printer(s) will be deleted after deleting of the selected preset.\n" - "Are you sure you want to delete the selected preset?"), _L("Warning"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION); - if (dialog.ShowModal() == wxID_NO) - return; - - // delete selected preset from printers and printer, if it's needed - physical_printers.delete_preset_from_printers(m_presets->get_edited_preset().name, false); - } - } - - // Find an alternate preset to be selected after the current preset is deleted. - const std::deque &presets = this->m_presets->get_presets(); - size_t idx_current = this->m_presets->get_idx_selected(); - // Find the next visible preset. - size_t idx_new = idx_current + 1; - if (idx_new < presets.size()) - for (; idx_new < presets.size() && ! presets[idx_new].is_visible; ++ idx_new) ; - if (idx_new == presets.size()) - for (idx_new = idx_current - 1; idx_new > 0 && ! presets[idx_new].is_visible; -- idx_new); - preset_name = presets[idx_new].name; - } + // Find an alternate preset to be selected after the current preset is deleted. + const std::deque &presets = this->m_presets->get_presets(); + size_t idx_current = this->m_presets->get_idx_selected(); + // Find the next visible preset. + size_t idx_new = idx_current + 1; + if (idx_new < presets.size()) + for (; idx_new < presets.size() && ! presets[idx_new].is_visible; ++ idx_new) ; + if (idx_new == presets.size()) + for (idx_new = idx_current - 1; idx_new > 0 && ! presets[idx_new].is_visible; -- idx_new); + preset_name = presets[idx_new].name; } else { // If no name is provided, select the "-- default --" preset. preset_name = m_presets->default_preset().name; @@ -3349,15 +3327,70 @@ void Tab::delete_preset() // Don't let the user delete the ' - default - ' configuration. std::string action = current_preset.is_external ? _utf8(L("remove")) : _utf8(L("delete")); // TRN remove/delete - const wxString msg = m_presets_choice->is_selected_physical_printer() ? - from_u8((boost::format(_utf8(L("Are you sure you want to delete \"%1%\" preset from the physical printer?"))) % current_preset.name).str()) : - from_u8((boost::format(_utf8(L("Are you sure you want to %1% the selected preset?"))) % action).str()); + + PhysicalPrinterCollection& physical_printers = m_preset_bundle->physical_printers; + wxString msg; + if (m_presets_choice->is_selected_physical_printer()) + msg = from_u8((boost::format(_u8L("Are you sure you want to delete \"%1%\" preset from the physical printer \"%2%\"?")) + % current_preset.name % physical_printers.get_selected_printer_name()).str()); + else + { + if (m_type == Preset::TYPE_PRINTER && !physical_printers.empty()) + { + // Check preset for delete in physical printers + // Ask a customer about next action, if there is a printer with just one preset and this preset is equal to delete + std::vector ph_printers = physical_printers.get_printers_with_preset(current_preset.name); + std::vector ph_printers_only = physical_printers.get_printers_with_only_preset(current_preset.name); + + if (!ph_printers.empty()) { + msg += _L("Next physical printer(s) has/have selected preset") + ":"; + for (const std::string& printer : ph_printers) + msg += "\n \"" + from_u8(printer) + "\","; + msg.RemoveLast(); + msg += "\n" + _L("Note, that selected preset will be deleted from this/those printer(s) too.")+ "\n\n"; + } + + if (!ph_printers_only.empty()) { + msg += _L("Next physical printer(s) has/have one and only selected preset") + ":"; + for (const std::string& printer : ph_printers_only) + msg += "\n \"" + from_u8(printer) + "\","; + msg.RemoveLast(); + msg += "\n" + _L("Note, that this/those printer(s) will be deleted after deleting of the selected preset.") + "\n\n"; + } + } + + msg += from_u8((boost::format(_u8L("Are you sure you want to %1% the selected preset?")) % action).str()); + } + action = current_preset.is_external ? _utf8(L("Remove")) : _utf8(L("Delete")); // TRN Remove/Delete wxString title = from_u8((boost::format(_utf8(L("%1% Preset"))) % action).str()); //action + _(L(" Preset")); if (current_preset.is_default || wxID_YES != wxMessageDialog(parent(), msg, title, wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION).ShowModal()) return; + + // if we just delete preset from the physical printer + if (m_presets_choice->is_selected_physical_printer()) { + PhysicalPrinter& printer = physical_printers.get_selected_printer(); + + if (printer.preset_names.size() == 1) { + wxMessageDialog dialog(nullptr, _L("It's a last for this physical printer. We can't delete it"), _L("Information"), wxICON_INFORMATION | wxOK); + dialog.ShowModal(); + return; + } + // just delete this preset from the current physical printer + printer.delete_preset(m_presets->get_edited_preset().name); + // select first from the possible presets for this printer + physical_printers.select_printer(printer); + + this->select_preset(physical_printers.get_selected_printer_preset_name()); + return; + } + + // delete selected preset from printers and printer, if it's needed + if (m_type == Preset::TYPE_PRINTER && !physical_printers.empty()) + physical_printers.delete_preset_from_printers(current_preset.name); + // Select will handle of the preset dependencies, of saving & closing the depending profiles, and // finally of deleting the preset. this->select_preset("", true); diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 57129955b0..24f25e2d74 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -119,6 +119,7 @@ protected: ScalableButton* m_search_btn; ScalableButton* m_btn_save_preset; ScalableButton* m_btn_delete_preset; + ScalableButton* m_btn_edit_ph_printer {nullptr}; ScalableButton* m_btn_hide_incompatible_presets; wxBoxSizer* m_hsizer; wxBoxSizer* m_left_sizer; @@ -275,7 +276,7 @@ public: void load_current_preset(); void rebuild_page_tree(); void update_page_tree_visibility(); - void update_delete_preset_btn(); + void update_btns_enabling(); void update_preset_choice(); // Select a new preset, possibly delete the current one. void select_preset(std::string preset_name = "", bool delete_current = false, const std::string& last_selected_ph_printer_name = ""); From 11cf9a87f143dcc18c82779ce63a7cdf768521e4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 29 Jul 2020 10:04:10 +0200 Subject: [PATCH 215/503] GCodeProcessor -> Calculate mm3 per mm on the fly --- src/libslic3r/GCode.cpp | 8 ++------ src/libslic3r/GCode.hpp | 7 ++++++- src/libslic3r/GCode/GCodeProcessor.cpp | 28 ++++++++++++-------------- src/libslic3r/GCode/GCodeProcessor.hpp | 2 +- src/libslic3r/GCode/WipeTower.cpp | 26 ++++++++++++------------ 5 files changed, 35 insertions(+), 36 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 742498baf8..ced52207d0 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1178,7 +1178,6 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu // resets analyzer's tracking data #if ENABLE_GCODE_VIEWER - m_last_mm3_per_mm = 0.0f; m_last_width = 0.0f; m_last_height = 0.0f; #else @@ -3237,16 +3236,13 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } #endif // ENABLE_GCODE_VIEWER +#if !ENABLE_GCODE_VIEWER if (last_was_wipe_tower || (m_last_mm3_per_mm != path.mm3_per_mm)) { m_last_mm3_per_mm = path.mm3_per_mm; -#if ENABLE_GCODE_VIEWER - sprintf(buf, ";%s%f\n", GCodeProcessor::Mm3_Per_Mm_Tag.c_str(), m_last_mm3_per_mm); - gcode += buf; -#else sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), m_last_mm3_per_mm); gcode += buf; -#endif // ENABLE_GCODE_VIEWER } +#endif // !ENABLE_GCODE_VIEWER if (last_was_wipe_tower || (m_last_width != path.width)) { m_last_width = path.width; diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 2732427620..443c25bb2f 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -173,7 +173,6 @@ public: m_last_pos_defined(false), m_last_extrusion_role(erNone), #if ENABLE_GCODE_VIEWER - m_last_mm3_per_mm(0.0f), m_last_width(0.0f), m_last_height(0.0f), #else @@ -378,10 +377,16 @@ private: double m_volumetric_speed; // Support for the extrusion role markers. Which marker is active? ExtrusionRole m_last_extrusion_role; +#if ENABLE_GCODE_VIEWER + // Support for G-Code Processor + float m_last_width; + float m_last_height; +#else // Support for G-Code Analyzer double m_last_mm3_per_mm; float m_last_width; float m_last_height; +#endif // ENABLE_GCODE_VIEWER Point m_last_pos; bool m_last_pos_defined; diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 44598d2408..ef9c64880d 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -24,7 +24,6 @@ namespace Slic3r { const std::string GCodeProcessor::Extrusion_Role_Tag = "ExtrType:"; const std::string GCodeProcessor::Width_Tag = "PrusaSlicer__WIDTH:"; const std::string GCodeProcessor::Height_Tag = "PrusaSlicer__HEIGHT:"; -const std::string GCodeProcessor::Mm3_Per_Mm_Tag = "PrusaSlicer__MM3_PER_MM:"; const std::string GCodeProcessor::Color_Change_Tag = "COLOR_CHANGE"; const std::string GCodeProcessor::Pause_Print_Tag = "PAUSE_PRINT"; const std::string GCodeProcessor::Custom_Code_Tag = "CUSTOM_CODE"; @@ -325,6 +324,10 @@ void GCodeProcessor::apply_config(const PrintConfig& config) m_extruders_color[id] = static_cast(id); } + for (double diam : config.filament_diameter.values) { + m_filament_diameters.push_back(static_cast(diam)); + } + m_time_processor.machine_limits = reinterpret_cast(config); // Filament load / unload times are not specific to a firmware flavor. Let anybody use it if they find it useful. // As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they @@ -370,6 +373,7 @@ void GCodeProcessor::reset() m_extrusion_role = erNone; m_extruder_id = 0; m_extruders_color = ExtrudersColor(); + m_filament_diameters = std::vector(); m_cp_color.reset(); m_producer = EProducer::Unknown; @@ -592,20 +596,6 @@ void GCodeProcessor::process_tags(const std::string& comment) return; } - // mm3 per mm tag - pos = comment.find(Mm3_Per_Mm_Tag); - if (pos != comment.npos) { - try - { - m_mm3_per_mm = std::stof(comment.substr(pos + Mm3_Per_Mm_Tag.length())); - } - catch (...) - { - BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Mm3_Per_Mm (" << comment << ")."; - } - return; - } - // color change tag pos = comment.find(Color_Change_Tag); if (pos != comment.npos) { @@ -1019,6 +1009,14 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) EMoveType type = move_type(delta_pos); + if (type == EMoveType::Extrude) { + if (delta_pos[E] > 0.0f) { + float ds = std::sqrt(sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z])); + if (ds > 0.0f && static_cast(m_extruder_id) < m_filament_diameters.size()) + m_mm3_per_mm = round_nearest(delta_pos[E] * static_cast(M_PI) * sqr(static_cast(m_filament_diameters[m_extruder_id])) / (4.0f * ds), 3); + } + } + // time estimate section auto move_length = [](const AxisCoords& delta_pos) { float sq_xyz_length = sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z]); diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 9507adf4d5..d59fc7bb9b 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -21,7 +21,6 @@ namespace Slic3r { static const std::string Extrusion_Role_Tag; static const std::string Width_Tag; static const std::string Height_Tag; - static const std::string Mm3_Per_Mm_Tag; static const std::string Color_Change_Tag; static const std::string Pause_Print_Tag; static const std::string Custom_Code_Tag; @@ -241,6 +240,7 @@ namespace Slic3r { ExtrusionRole m_extrusion_role; unsigned char m_extruder_id; ExtrudersColor m_extruders_color; + std::vector m_filament_diameters; CpColor m_cp_color; enum class EProducer diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index e4f995aba1..c20009a487 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -84,19 +84,17 @@ public: return *this; } - WipeTowerWriter& change_analyzer_mm3_per_mm(float len, float e) { - static const float area = float(M_PI) * 1.75f * 1.75f / 4.f; - float mm3_per_mm = (len == 0.f ? 0.f : area * e / len); - // adds tag for analyzer: - char buf[64]; -#if ENABLE_GCODE_VIEWER - sprintf(buf, ";%s%f\n", GCodeProcessor::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm); -#else - sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm); -#endif // ENABLE_GCODE_VIEWER - m_gcode += buf; - return *this; +#if !ENABLE_GCODE_VIEWER + WipeTowerWriter& change_analyzer_mm3_per_mm(float len, float e) { + static const float area = float(M_PI) * 1.75f * 1.75f / 4.f; + float mm3_per_mm = (len == 0.f ? 0.f : area * e / len); + // adds tag for analyzer: + char buf[64]; + sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm); + m_gcode += buf; + return *this; } +#endif // !ENABLE_GCODE_VIEWER WipeTowerWriter& set_initial_position(const Vec2f &pos, float width = 0.f, float depth = 0.f, float internal_angle = 0.f) { m_wipe_tower_width = width; @@ -169,8 +167,10 @@ public: Vec2f rot(this->rotate(Vec2f(x,y))); // this is where we want to go if (! m_preview_suppressed && e > 0.f && len > 0.f) { +#if !ENABLE_GCODE_VIEWER change_analyzer_mm3_per_mm(len, e); - // Width of a squished extrusion, corrected for the roundings of the squished extrusions. +#endif // !ENABLE_GCODE_VIEWER + // Width of a squished extrusion, corrected for the roundings of the squished extrusions. // This is left zero if it is a travel move. float width = e * m_filpar[0].filament_area / (len * m_layer_height); // Correct for the roundings of a squished extrusion. From 16e282110d2fc8ce503c0635397ad0e8300c78c3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 29 Jul 2020 11:13:48 +0200 Subject: [PATCH 216/503] GCodeProcessor -> Load config data from gcode files generated by PrusaSlicer --- src/libslic3r/GCode/GCodeProcessor.cpp | 45 +++++++++++++++++++++++--- src/libslic3r/GCode/GCodeProcessor.hpp | 1 + src/libslic3r/GCodeReader.cpp | 5 +++ src/libslic3r/GCodeReader.hpp | 6 ++++ 4 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index ef9c64880d..ae414ad441 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -346,6 +346,18 @@ void GCodeProcessor::apply_config(const PrintConfig& config) } } +void GCodeProcessor::apply_config(const DynamicPrintConfig& config) +{ + m_parser.apply_config(config); + + const ConfigOptionFloats* filament_diameters = config.option("filament_diameter"); + if (filament_diameters != nullptr) { + for (double diam : filament_diameters->values) { + m_filament_diameters.push_back(static_cast(diam)); + } + } +} + void GCodeProcessor::enable_stealth_time_estimator(bool enabled) { m_time_processor.machines[static_cast(ETimeMode::Stealth)].enabled = enabled; @@ -391,6 +403,28 @@ void GCodeProcessor::process_file(const std::string& filename) auto start_time = std::chrono::high_resolution_clock::now(); #endif // ENABLE_GCODE_VIEWER_STATISTICS + // pre-processing + // parse the gcode file to detect its producer + if (m_producers_enabled) { + m_parser.parse_file(filename, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { + std::string cmd = line.cmd(); + if (cmd.length() == 0) { + std::string comment = line.comment(); + if (comment.length() > 1 && detect_producer(comment)) + m_parser.quit_parsing_file(); + } + }); + + // if the gcode was produced by PrusaSlicer, + // extract the config from it + if (m_producer == EProducer::PrusaSlicer) { + DynamicPrintConfig config; + config.apply(FullPrintConfig::defaults()); + config.load_from_gcode_file(filename); + apply_config(config); + } + } + m_result.id = ++s_result_id; m_result.moves.emplace_back(MoveVertex()); m_parser.parse_file(filename, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { process_gcode_line(line); }); @@ -554,11 +588,12 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_tags(const std::string& comment) { - if (m_producers_enabled && m_producer == EProducer::Unknown && detect_producer(comment)) - return; - else if (m_producers_enabled && m_producer != EProducer::Unknown) { - if (process_producers_tags(comment)) - return; + // producers tags + if (m_producers_enabled) { + if (m_producer != EProducer::Unknown) { + if (process_producers_tags(comment)) + return; + } } // extrusion role tag diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index d59fc7bb9b..b2d702f7f3 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -266,6 +266,7 @@ namespace Slic3r { GCodeProcessor() { reset(); } void apply_config(const PrintConfig& config); + void apply_config(const DynamicPrintConfig& config); void enable_stealth_time_estimator(bool enabled); void enable_producers(bool enabled) { m_producers_enabled = enabled; } void reset(); diff --git a/src/libslic3r/GCodeReader.cpp b/src/libslic3r/GCodeReader.cpp index e68bc5ad29..ab77b01413 100644 --- a/src/libslic3r/GCodeReader.cpp +++ b/src/libslic3r/GCodeReader.cpp @@ -115,7 +115,12 @@ void GCodeReader::parse_file(const std::string &file, callback_t callback) { std::ifstream f(file); std::string line; +#if ENABLE_GCODE_VIEWER + m_parsing_file = true; + while (m_parsing_file && std::getline(f, line)) +#else while (std::getline(f, line)) +#endif // ENABLE_GCODE_VIEWER this->parse_line(line, callback); } diff --git a/src/libslic3r/GCodeReader.hpp b/src/libslic3r/GCodeReader.hpp index 9503ddcc16..7e0793cd9b 100644 --- a/src/libslic3r/GCodeReader.hpp +++ b/src/libslic3r/GCodeReader.hpp @@ -107,6 +107,9 @@ public: { GCodeLine gline; this->parse_line(line.c_str(), gline, callback); } void parse_file(const std::string &file, callback_t callback); +#if ENABLE_GCODE_VIEWER + void quit_parsing_file() { m_parsing_file = false; } +#endif // ENABLE_GCODE_VIEWER float& x() { return m_position[X]; } float x() const { return m_position[X]; } @@ -145,6 +148,9 @@ private: char m_extrusion_axis; float m_position[NUM_AXES]; bool m_verbose; +#if ENABLE_GCODE_VIEWER + bool m_parsing_file{ false }; +#endif // ENABLE_GCODE_VIEWER }; } /* namespace Slic3r */ From 9d4344a78ce56ecf722e946f2c1796a9e1df96e8 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 29 Jul 2020 12:47:42 +0200 Subject: [PATCH 217/503] GCodeProcessor/GCodeViewer -> Extract bed shape from gcode files generated by PrusaSlicer --- src/libslic3r/GCode/GCodeProcessor.cpp | 4 ++++ src/libslic3r/GCode/GCodeProcessor.hpp | 20 ++++++++++++++++++-- src/slic3r/GUI/GCodeViewer.cpp | 22 ++++++++++++++-------- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index ae414ad441..beb340a5da 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -356,6 +356,10 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) m_filament_diameters.push_back(static_cast(diam)); } } + + const ConfigOptionPoints* bed_shape = config.option("bed_shape"); + if (bed_shape != nullptr) + m_result.bed_shape = bed_shape->values; } void GCodeProcessor::enable_stealth_time_estimator(bool enabled) diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index b2d702f7f3..1def93e74b 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -210,11 +210,27 @@ namespace Slic3r { { unsigned int id; std::vector moves; +#if ENABLE_GCODE_VIEWER_AS_STATE + Pointfs bed_shape; +#endif // ENABLE_GCODE_VIEWER_AS_STATE #if ENABLE_GCODE_VIEWER_STATISTICS long long time{ 0 }; - void reset() { time = 0; moves = std::vector(); } + void reset() + { + time = 0; + moves = std::vector(); +#if ENABLE_GCODE_VIEWER_AS_STATE + bed_shape = Pointfs(); +#endif // ENABLE_GCODE_VIEWER_AS_STATE + } #else - void reset() { moves = std::vector(); } + void reset() + { + moves = std::vector(); +#if ENABLE_GCODE_VIEWER_AS_STATE + bed_shape = Pointfs(); +#endif // ENABLE_GCODE_VIEWER_AS_STATE + } #endif // ENABLE_GCODE_VIEWER_STATISTICS }; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 418223503b..817e7e650c 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -323,14 +323,20 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& #if ENABLE_GCODE_VIEWER_AS_STATE if (wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer) { - // adjust printbed size in dependence of toolpaths bbox - const double margin = 10.0; - Vec2d min(m_paths_bounding_box.min(0) - margin, m_paths_bounding_box.min(1) - margin); - Vec2d max(m_paths_bounding_box.max(0) + margin, m_paths_bounding_box.max(1) + margin); - Pointfs bed_shape = { { min(0), min(1) }, - { max(0), min(1) }, - { max(0), max(1) }, - { min(0), max(1) } }; + Pointfs bed_shape; + if (!gcode_result.bed_shape.empty()) + // bed shape detected in the gcode + bed_shape = gcode_result.bed_shape; + else { + // adjust printbed size in dependence of toolpaths bbox + const double margin = 10.0; + Vec2d min(m_paths_bounding_box.min(0) - margin, m_paths_bounding_box.min(1) - margin); + Vec2d max(m_paths_bounding_box.max(0) + margin, m_paths_bounding_box.max(1) + margin); + bed_shape = { { min(0), min(1) }, + { max(0), min(1) }, + { max(0), max(1) }, + { min(0), max(1) } }; + } wxGetApp().plater()->set_bed_shape(bed_shape, "", ""); } #endif // ENABLE_GCODE_VIEWER_AS_STATE From 54a434063167a87b9d57665b604b15914e190630 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 29 Jul 2020 13:05:16 +0200 Subject: [PATCH 218/503] GCodeViewer -> Hexagonal icons as default --- src/slic3r/GUI/GCodeViewer.cpp | 57 +--------------------------------- 1 file changed, 1 insertion(+), 56 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 817e7e650c..fed9a30d57 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1348,8 +1348,6 @@ void GCodeViewer::render_shells() const // glsafe(::glDepthMask(GL_TRUE)); } -#define USE_ICON_HEXAGON 1 - void GCodeViewer::render_legend() const { if (!m_legend_enabled) @@ -1444,11 +1442,7 @@ void GCodeViewer::render_legend() const auto append_range_item = [this, draw_list, &imgui, append_item](int i, float value, unsigned int decimals) { char buf[1024]; ::sprintf(buf, "%.*f", decimals, value); -#if USE_ICON_HEXAGON append_item(EItemType::Hexagon, Range_Colors[i], buf); -#else - append_item(EItemType::Rect, Range_Colors[i], buf); -#endif // USE_ICON_HEXAGON }; float step_size = range.step_size(); @@ -1533,13 +1527,8 @@ void GCodeViewer::render_legend() const if (!visible) ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f); -#if USE_ICON_HEXAGON append_item(EItemType::Hexagon, Extrusion_Role_Colors[static_cast(role)], _u8L(ExtrusionEntity::role_to_string(role)), [this, role]() { -#else - append_item(EItemType::Rect, Extrusion_Role_Colors[static_cast(role)], _u8L(ExtrusionEntity::role_to_string(role)), [this, role]() { -#endif // USE_ICON_HEXAGON - if (role < erCount) - { + if (role < erCount) { m_extrusions.role_visibility_flags = is_visible(role) ? m_extrusions.role_visibility_flags & ~(1 << role) : m_extrusions.role_visibility_flags | (1 << role); // update buffers' render paths refresh_render_paths(false, false); @@ -1562,11 +1551,7 @@ void GCodeViewer::render_legend() const { // shows only extruders actually used for (unsigned char i : m_extruder_ids) { -#if USE_ICON_HEXAGON append_item(EItemType::Hexagon, m_tool_colors[i], _u8L("Extruder") + " " + std::to_string(i + 1)); -#else - append_item(EItemType::Rect, m_tool_colors[i], _u8L("Extruder") + " " + std::to_string(i + 1)); -#endif // USE_ICON_HEXAGON } break; } @@ -1578,36 +1563,20 @@ void GCodeViewer::render_legend() const std::vector>> cp_values = color_print_ranges(0, custom_gcode_per_print_z); const int items_cnt = static_cast(cp_values.size()); if (items_cnt == 0) { // There are no color changes, but there are some pause print or custom Gcode -#if USE_ICON_HEXAGON append_item(EItemType::Hexagon, m_tool_colors.front(), _u8L("Default color")); -#else - append_item(EItemType::Rect, m_tool_colors.front(), _u8L("Default color")); -#endif // USE_ICON_HEXAGON } else { for (int i = items_cnt; i >= 0; --i) { // create label for color change item if (i == 0) { -#if USE_ICON_HEXAGON append_item(EItemType::Hexagon, m_tool_colors[0], upto_label(cp_values.front().second.first)); -#else - append_item(EItemType::Rect, m_tool_colors[0], upto_label(cp_values.front().second.first); -#endif // USE_ICON_HEXAGON break; } else if (i == items_cnt) { -#if USE_ICON_HEXAGON append_item(EItemType::Hexagon, cp_values[i - 1].first, above_label(cp_values[i - 1].second.second)); -#else - append_item(EItemType::Rect, cp_values[i - 1].first, above_label(cp_values[i - 1].second.second); -#endif // USE_ICON_HEXAGON continue; } -#if USE_ICON_HEXAGON append_item(EItemType::Hexagon, cp_values[i - 1].first, fromto_label(cp_values[i - 1].second.second, cp_values[i].second.first)); -#else - append_item(EItemType::Rect, cp_values[i - 1].first, fromto_label(cp_values[i - 1].second.second, cp_values[i].second.first)); -#endif // USE_ICON_HEXAGON } } } @@ -1618,11 +1587,7 @@ void GCodeViewer::render_legend() const std::vector>> cp_values = color_print_ranges(i, custom_gcode_per_print_z); const int items_cnt = static_cast(cp_values.size()); if (items_cnt == 0) { // There are no color changes, but there are some pause print or custom Gcode -#if USE_ICON_HEXAGON append_item(EItemType::Hexagon, m_tool_colors[i], _u8L("Extruder") + " " + std::to_string(i + 1) + " " + _u8L("default color")); -#else - append_item(EItemType::Rect, m_tool_colors[i], _u8L("Extruder") + " " + std::to_string(i + 1) + " " + _u8L("default color")); -#endif // USE_ICON_HEXAGON } else { for (int j = items_cnt; j >= 0; --j) { @@ -1630,29 +1595,17 @@ void GCodeViewer::render_legend() const std::string label = _u8L("Extruder") + " " + std::to_string(i + 1); if (j == 0) { label += " " + upto_label(cp_values.front().second.first); -#if USE_ICON_HEXAGON append_item(EItemType::Hexagon, m_tool_colors[i], label); -#else - append_item(EItemType::Rect, m_tool_colors[i], label); -#endif // USE_ICON_HEXAGON break; } else if (j == items_cnt) { label += " " + above_label(cp_values[j - 1].second.second); -#if USE_ICON_HEXAGON append_item(EItemType::Hexagon, cp_values[j - 1].first, label); -#else - append_item(EItemType::Rect, cp_values[j - 1].first, label); -#endif // USE_ICON_HEXAGON continue; } label += " " + fromto_label(cp_values[j - 1].second.second, cp_values[j].second.first); -#if USE_ICON_HEXAGON append_item(EItemType::Hexagon, cp_values[j - 1].first, label); -#else - append_item(EItemType::Rect, cp_values[j - 1].first, label); -#endif // USE_ICON_HEXAGON } } } @@ -1826,18 +1779,10 @@ void GCodeViewer::render_time_estimate() const ImDrawList* draw_list = ImGui::GetWindowDrawList(); ImVec2 pos = ImGui::GetCursorScreenPos(); pos.x -= 0.5f * ImGui::GetStyle().ItemSpacing.x; -#if USE_ICON_HEXAGON ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color1[0], color1[1], color1[2], 1.0f }), 6); center.x += icon_size; draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color2[0], color2[1], color2[2], 1.0f }), 6); -#else - draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, - ImGui::GetColorU32({ color1[0], color1[1], color1[2], 1.0f })); - pos.x += icon_size; - draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, - ImGui::GetColorU32({ color2[0], color2[1], color2[2], 1.0f })); -#endif // USE_ICON_HEXAGON ImGui::SameLine(offsets[0]); imgui.text(short_time(get_time_dhms(times.second - times.first))); }; From 0348986bda0e20c26a99a452be5402146303680c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 29 Jul 2020 14:20:01 +0200 Subject: [PATCH 219/503] Follow-up of 9d4344a78ce56ecf722e946f2c1796a9e1df96e8 -> ensure printbed always rendered as custom in gcode preview mode --- src/libslic3r/GCode/GCodeProcessor.cpp | 1 - src/slic3r/GUI/3DBed.cpp | 41 +++++++++++++++----------- src/slic3r/GUI/3DBed.hpp | 2 +- src/slic3r/GUI/GCodeViewer.cpp | 2 +- src/slic3r/GUI/MainFrame.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 13 ++++---- src/slic3r/GUI/Plater.hpp | 2 +- 7 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index beb340a5da..7a6b1ecb42 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -694,7 +694,6 @@ bool GCodeProcessor::process_producers_tags(const std::string& comment) bool GCodeProcessor::process_prusaslicer_tags(const std::string& comment) { - std::cout << comment << "\n"; return false; } diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index ca075fb372..9d16bead71 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -255,7 +255,7 @@ Bed3D::Bed3D() { } -bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model) +bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom) { auto check_texture = [](const std::string& texture) { return !texture.empty() && (boost::algorithm::iends_with(texture, ".png") || boost::algorithm::iends_with(texture, ".svg")) && boost::filesystem::exists(texture); @@ -265,30 +265,39 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c return !model.empty() && boost::algorithm::iends_with(model, ".stl") && boost::filesystem::exists(model); }; - auto [new_type, system_model, system_texture] = detect_type(shape); + EType type; + std::string model; + std::string texture; + if (force_as_custom) + type = Custom; + else { + auto [new_type, system_model, system_texture] = detect_type(shape); + type = new_type; + model = system_model; + texture = system_texture; + } - std::string texture_filename = custom_texture.empty() ? system_texture : custom_texture; + std::string texture_filename = custom_texture.empty() ? texture : custom_texture; if (!check_texture(texture_filename)) texture_filename.clear(); - std::string model_filename = custom_model.empty() ? system_model : custom_model; + std::string model_filename = custom_model.empty() ? model : custom_model; if (!check_model(model_filename)) model_filename.clear(); - if ((m_shape == shape) && (m_type == new_type) && (m_texture_filename == texture_filename) && (m_model_filename == model_filename)) + if (m_shape == shape && m_type == type && m_texture_filename == texture_filename && m_model_filename == model_filename) // No change, no need to update the UI. return false; m_shape = shape; m_texture_filename = texture_filename; m_model_filename = model_filename; - m_type = new_type; + m_type = type; calc_bounding_boxes(); ExPolygon poly; - for (const Vec2d& p : m_shape) - { + for (const Vec2d& p : m_shape) { poly.contour.append(Point(scale_(p(0)), scale_(p(1)))); } @@ -435,19 +444,15 @@ static std::string system_print_bed_texture(const Preset &preset) std::tuple Bed3D::detect_type(const Pointfs& shape) const { auto bundle = wxGetApp().preset_bundle; - if (bundle != nullptr) - { + if (bundle != nullptr) { const Preset* curr = &bundle->printers.get_selected_preset(); - while (curr != nullptr) - { - if (curr->config.has("bed_shape")) - { - if (shape == dynamic_cast(curr->config.option("bed_shape"))->values) - { + while (curr != nullptr) { + if (curr->config.has("bed_shape")) { + if (shape == dynamic_cast(curr->config.option("bed_shape"))->values) { std::string model_filename = system_print_bed_model(*curr); std::string texture_filename = system_print_bed_texture(*curr); if (!model_filename.empty() && !texture_filename.empty()) - return std::make_tuple(System, model_filename, texture_filename); + return { System, model_filename, texture_filename }; } } @@ -455,7 +460,7 @@ std::tuple Bed3D::detect_type(const Poin } } - return std::make_tuple(Custom, "", ""); + return { Custom, "", "" }; } void Bed3D::render_axes() const diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index b9e952c4a4..fbfc3078c1 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -144,7 +144,7 @@ public: const Pointfs& get_shape() const { return m_shape; } // Return true if the bed shape changed, so the calee will update the UI. - bool set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model); + bool set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false); const BoundingBoxf3& get_bounding_box(bool extended) const { return extended ? m_extended_bounding_box : m_bounding_box; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index fed9a30d57..af3d8d901c 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -337,7 +337,7 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& { max(0), max(1) }, { min(0), max(1) } }; } - wxGetApp().plater()->set_bed_shape(bed_shape, "", ""); + wxGetApp().plater()->set_bed_shape(bed_shape, "", "", true); } #endif // ENABLE_GCODE_VIEWER_AS_STATE } diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index c43563c8bf..0d527f48b3 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1490,7 +1490,7 @@ void MainFrame::set_mode(EMode mode) m_plater->select_view("iso"); // switch printbed - m_plater->set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, "", ""); + m_plater->set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, "", "", true); // switch menubar SetMenuBar(m_gcodeviewer_menubar); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 193ac8e0c1..43cf27e30f 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1867,7 +1867,7 @@ struct Plater::priv // triangulate the bed and store the triangles into m_bed.m_triangles, // fills the m_bed.m_grid_lines and sets m_bed.m_origin. // Sets m_bed.m_polygon to limit the object placement. - void set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model); + void set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false); bool can_delete() const; bool can_delete_all() const; @@ -4182,11 +4182,10 @@ bool Plater::priv::can_reload_from_disk() const return !paths.empty(); } -void Plater::priv::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model) +void Plater::priv::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom) { - bool new_shape = bed.set_shape(shape, custom_texture, custom_model); - if (new_shape) - { + bool new_shape = bed.set_shape(shape, custom_texture, custom_model, force_as_custom); + if (new_shape) { if (view3D) view3D->bed_shape_changed(); if (preview) preview->bed_shape_changed(); } @@ -5456,9 +5455,9 @@ void Plater::set_bed_shape() const } #if ENABLE_GCODE_VIEWER_AS_STATE -void Plater::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model) const +void Plater::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom) const { - p->set_bed_shape(shape, custom_texture, custom_model); + p->set_bed_shape(shape, custom_texture, custom_model, force_as_custom); } #endif // ENABLE_GCODE_VIEWER_AS_STATE diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 085ed0e69c..59d595bbe4 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -363,7 +363,7 @@ public: void set_bed_shape() const; #if ENABLE_GCODE_VIEWER_AS_STATE - void set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model) const; + void set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false) const; #endif // ENABLE_GCODE_VIEWER_AS_STATE // ROII wrapper for suppressing the Undo / Redo snapshot to be taken. From 39d08441cc4bf103bfc240e8609935aded72ffa7 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 29 Jul 2020 15:40:28 +0200 Subject: [PATCH 220/503] ENABLE_GCODE_VIEWER_AS_STATE -> Fixed collapse toolbar showing-up when presing [T] in gcode preview mode --- src/slic3r/GUI/GLCanvas3D.cpp | 5 +++++ src/slic3r/GUI/GLToolbar.cpp | 13 +------------ src/slic3r/GUI/GLToolbar.hpp | 4 ++-- src/slic3r/GUI/MainFrame.cpp | 6 +++++- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 2c13c752e6..175eecd8bd 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4295,8 +4295,13 @@ void GLCanvas3D::update_ui_from_settings() } #endif // ENABLE_RETINA_GL +#if ENABLE_GCODE_VIEWER_AS_STATE + if (wxGetApp().mainframe != nullptr && wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) + wxGetApp().plater()->get_collapse_toolbar().set_enabled(wxGetApp().app_config->get("show_collapse_button") == "1"); +#else bool enable_collapse = wxGetApp().app_config->get("show_collapse_button") == "1"; wxGetApp().plater()->get_collapse_toolbar().set_enabled(enable_collapse); +#endif // ENABLE_GCODE_VIEWER_AS_STATE } diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 4ab282b066..46371b037a 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -230,24 +230,13 @@ void GLToolbar::set_icons_size(float size) void GLToolbar::set_scale(float scale) { - if (m_layout.scale != scale) - { + if (m_layout.scale != scale) { m_layout.scale = scale; m_layout.dirty = true; m_icons_texture_dirty = true; } } -bool GLToolbar::is_enabled() const -{ - return m_enabled; -} - -void GLToolbar::set_enabled(bool enable) -{ - m_enabled = enable;//true; etFIXME -} - bool GLToolbar::add_item(const GLToolbarItem::Data& data) { GLToolbarItem* item = new GLToolbarItem(GLToolbarItem::Action, data); diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index 41c2735c9a..74e18de975 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -276,8 +276,8 @@ public: void set_icons_size(float size); void set_scale(float scale); - bool is_enabled() const; - void set_enabled(bool enable); + bool is_enabled() const { return m_enabled; } + void set_enabled(bool enable) { m_enabled = enable; } bool add_item(const GLToolbarItem::Data& data); bool add_separator(); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 0d527f48b3..7d6acad069 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -279,8 +279,12 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S update_ui_from_settings(); // FIXME (?) - if (m_plater != nullptr) + if (m_plater != nullptr) { +#if ENABLE_GCODE_VIEWER_AS_STATE + m_plater->get_collapse_toolbar().set_enabled(wxGetApp().app_config->get("show_collapse_button") == "1"); +#endif // ENABLE_GCODE_VIEWER_AS_STATE m_plater->show_action_buttons(true); + } } #if ENABLE_LAYOUT_NO_RESTART From 96a364c3e6356d7107d6c51b4c1763c81d0d3435 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 29 Jul 2020 16:05:30 +0200 Subject: [PATCH 221/503] SavePresetDialog: Improvements --- src/slic3r/GUI/PresetComboBoxes.cpp | 303 ++++++++++++++++------------ src/slic3r/GUI/PresetComboBoxes.hpp | 64 ++++-- src/slic3r/GUI/Tab.cpp | 2 +- 3 files changed, 229 insertions(+), 140 deletions(-) diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 666f10194c..35acbfd3ae 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -1016,41 +1016,21 @@ void TabPresetComboBox::update_dirty() //----------------------------------------------- -// SavePresetDialog +// SavePresetDialog::Item //----------------------------------------------- -SavePresetDialog::SavePresetDialog(TabPresetComboBox* preset_cb, const std::string& suffix) - : DPIDialog(nullptr, wxID_ANY, _L("Save preset"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), -1), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER), - m_preset_cb(preset_cb) +SavePresetDialog::Item::Item(Preset::Type type, const std::string& suffix, wxBoxSizer* sizer, SavePresetDialog* parent): + m_type(type), + m_parent(parent) { - SetFont(wxGetApp().normal_font()); - SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + Tab* tab = wxGetApp().get_tab(m_type); + assert(tab); + m_presets = tab->get_presets(); - wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - - add_common_items(topSizer, suffix); - add_items_for_edit_ph_printer(topSizer); - - // add dialog's buttons - wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); - wxButton* btnOK = static_cast(this->FindWindowById(wxID_OK, this)); - btnOK->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { accept(); }); - btnOK->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { - evt.Enable(!m_combo->GetValue().IsEmpty()); }); - - topSizer->Add(btns, 0, wxEXPAND | wxALL, BORDER_W); - - SetSizer(topSizer); - topSizer->SetSizeHints(this); -} - -void SavePresetDialog::add_common_items(wxBoxSizer* sizer, const std::string& suffix) -{ - const PresetCollection* presets = m_preset_cb->presets(); - const Preset& sel_preset = presets->get_selected_preset(); - std::string preset_name = sel_preset.is_default ? "Untitled" : - sel_preset.is_system ? (boost::format(("%1% - %2%")) % sel_preset.name % suffix).str() : - sel_preset.name; + const Preset& sel_preset = m_presets->get_selected_preset(); + std::string preset_name = sel_preset.is_default ? "Untitled" : + sel_preset.is_system ? (boost::format(("%1% - %2%")) % sel_preset.name % suffix).str() : + sel_preset.name; // if name contains extension if (boost::iends_with(preset_name, ".ini")) { @@ -1059,34 +1039,166 @@ void SavePresetDialog::add_common_items(wxBoxSizer* sizer, const std::string& su } std::vector values; - for (const Preset& preset : *presets) { + for (const Preset& preset : *m_presets) { if (preset.is_default || preset.is_system || preset.is_external) continue; values.push_back(preset.name); } - wxStaticText* label_top = new wxStaticText(this, wxID_ANY, from_u8((boost::format(_utf8(L("Save %s as:"))) % into_u8(wxGetApp().get_tab(m_preset_cb->type())->title())).str())); - m_combo = new wxComboBox(this, wxID_ANY, from_u8(preset_name), - wxDefaultPosition, wxDefaultSize, 0, 0, wxTE_PROCESS_ENTER); + wxStaticText* label_top = new wxStaticText(m_parent, wxID_ANY, from_u8((boost::format(_utf8(L("Save %s as:"))) % into_u8(tab->title())).str())); + + m_valid_bmp = new wxStaticBitmap(m_parent, wxID_ANY, create_scaled_bitmap("tick_mark", m_parent)); + + m_combo = new wxComboBox(m_parent, wxID_ANY, from_u8(preset_name)/*, + wxDefaultPosition, wxDefaultSize, 0, 0, wxTE_PROCESS_ENTER*/); for (auto value : values) m_combo->Append(from_u8(value)); - m_combo->Bind(wxEVT_TEXT_ENTER, [this](wxCommandEvent&) { accept(); }); - m_combo->Bind(wxEVT_TEXT, [this](wxCommandEvent&) { - update(normalize_utf8_nfc(m_combo->GetValue().ToUTF8())); - this->Layout(); - this->Fit(); - }); + m_combo->Bind(wxEVT_TEXT, [this](wxCommandEvent&) { update(); }); - sizer->Add(label_top, 0, wxEXPAND | wxALL, BORDER_W); - sizer->Add(m_combo, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, BORDER_W); + m_valid_label = new wxStaticText(m_parent, wxID_ANY, ""); + m_valid_label->SetFont(wxGetApp().bold_font()); + + wxBoxSizer* combo_sizer = new wxBoxSizer(wxHORIZONTAL); + combo_sizer->Add(m_valid_bmp, 0, wxEXPAND | wxRIGHT, BORDER_W); + combo_sizer->Add(m_combo, 1, wxEXPAND, BORDER_W); + + sizer->Add(label_top, 0, wxEXPAND | wxTOP| wxBOTTOM, BORDER_W); + sizer->Add(combo_sizer, 0, wxEXPAND | wxBOTTOM, BORDER_W); + sizer->Add(m_valid_label, 0, wxEXPAND | wxLEFT, 3*BORDER_W/* + m_valid_bmp->GetBitmap().GetWidth()*/); + + if (m_type == Preset::TYPE_PRINTER) + m_parent->add_info_for_edit_ph_printer(sizer); + + update(); } -void SavePresetDialog::add_items_for_edit_ph_printer(wxBoxSizer* sizer) +void SavePresetDialog::Item::update() { - if (m_preset_cb->type() != Preset::TYPE_PRINTER || !m_preset_cb->is_selected_physical_printer()) - return; + m_preset_name = into_u8(m_combo->GetValue()); + m_valid_type = Valid; + wxString info_line; + + const char* unusable_symbols = "<>[]:/\\|?*\""; + + const std::string unusable_suffix = PresetCollection::get_suffix_modified();//"(modified)"; + for (size_t i = 0; i < std::strlen(unusable_symbols); i++) { + if (m_preset_name.find_first_of(unusable_symbols[i]) != std::string::npos) { + info_line = _L("The supplied name is not valid;") + "\n" + + _L("the following characters are not allowed:") + " " + unusable_symbols; + m_valid_type = NoValid; + break; + } + } + + if (m_valid_type == Valid && m_preset_name.find(unusable_suffix) != std::string::npos) { + info_line = _L("The supplied name is not valid;") + "\n" + + _L("the following suffix is not allowed:") + "\n\t" + + from_u8(PresetCollection::get_suffix_modified()); + m_valid_type = NoValid; + } + + if (m_valid_type == Valid && m_preset_name == "- default -") { + info_line = _L("The supplied name is not available."); + m_valid_type = NoValid; + } + + const Preset* existing = m_presets->find_preset(m_preset_name, false); + if (m_valid_type == Valid && existing && (existing->is_default || existing->is_system)) { + info_line = _L("Cannot overwrite a system profile."); + m_valid_type = NoValid; + } + if (m_valid_type == Valid && existing && (existing->is_external)) { + info_line = _L("Cannot overwrite an external profile."); + m_valid_type = NoValid; + } + if (m_valid_type == Valid && existing && m_preset_name != m_presets->get_selected_preset_name()) + { + info_line = from_u8((boost::format(_u8L("Preset with name \"%1%\" already exists.")) % m_preset_name).str()) + "\n" + + _L("Note: This preset will be replaced after saving"); + m_valid_type = Warning; + } + + m_valid_label->SetLabel(info_line); + m_valid_label->Show(!info_line.IsEmpty()); + + std::string bmp_name = m_valid_type == Warning ? "exclamation" : + m_valid_type == NoValid ? "cross" : "tick_mark" ; + m_valid_bmp->SetBitmap(create_scaled_bitmap(bmp_name, m_parent)); + + if (m_type == Preset::TYPE_PRINTER) + m_parent->update_info_for_edit_ph_printer(m_preset_name); + + m_parent->layout(); +} + +void SavePresetDialog::Item::accept() +{ + if (m_valid_type == Warning) + m_presets->delete_preset(m_preset_name); +} + + +//----------------------------------------------- +// SavePresetDialog +//----------------------------------------------- + +SavePresetDialog::SavePresetDialog(Preset::Type type, const std::string& suffix) + : DPIDialog(nullptr, wxID_ANY, _L("Save preset"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER) +{ + SetFont(wxGetApp().normal_font()); + SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + + m_presets_sizer = new wxBoxSizer(wxVERTICAL); + + // Add first item + m_items.emplace_back(type, suffix, m_presets_sizer, this); + + // Add dialog's buttons + wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); + wxButton* btnOK = static_cast(this->FindWindowById(wxID_OK, this)); + btnOK->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { accept(); }); + btnOK->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(enable_ok_btn()); }); + + topSizer->Add(m_presets_sizer, 0, wxEXPAND | wxALL, BORDER_W); + topSizer->Add(btns, 0, wxEXPAND | wxALL, BORDER_W); + + SetSizer(topSizer); + topSizer->SetSizeHints(this); +} + +void SavePresetDialog::AddItem(Preset::Type type, const std::string& suffix) +{ + m_items.emplace_back(type, suffix, m_presets_sizer, this); +} + +std::string SavePresetDialog::get_name() +{ + return m_items.front().preset_name(); +} + +std::string SavePresetDialog::get_name(Preset::Type type) +{ + for (Item& item : m_items) + if (item.type() == type) + return item.preset_name(); + return ""; +} + +bool SavePresetDialog::enable_ok_btn() const +{ + for (Item item : m_items) + if (!item.is_valid()) + return false; + + return true; +} + +void SavePresetDialog::add_info_for_edit_ph_printer(wxBoxSizer* sizer) +{ PhysicalPrinterCollection& printers = wxGetApp().preset_bundle->physical_printers; m_ph_printer_name = printers.get_selected_printer_name(); m_old_preset_name = printers.get_selected_printer_preset_name(); @@ -1102,6 +1214,7 @@ void SavePresetDialog::add_items_for_edit_ph_printer(wxBoxSizer* sizer) m_action_radio_box = new wxRadioBox(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, WXSIZEOF(choices), choices, 3, wxRA_SPECIFY_ROWS); m_action_radio_box->SetFont(wxGetApp().normal_font()); + m_action_radio_box->SetLabelFont(wxGetApp().normal_font()); m_action_radio_box->SetSelection(0); m_action_radio_box->Bind(wxEVT_RADIOBOX, [this](wxCommandEvent& e) { m_action = (ActionType)e.GetSelection(); }); @@ -1110,18 +1223,13 @@ void SavePresetDialog::add_items_for_edit_ph_printer(wxBoxSizer* sizer) m_radio_sizer = new wxBoxSizer(wxHORIZONTAL); m_radio_sizer->Add(m_action_radio_box, 1, wxALIGN_CENTER_VERTICAL); - sizer->Add(m_label, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W); - sizer->Add(m_radio_sizer, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W); - - update(m_preset_name); + sizer->Add(m_label, 0, wxEXPAND | wxALL, 2*BORDER_W); + sizer->Add(m_radio_sizer, 1, wxEXPAND | wxLEFT, 2*BORDER_W); } -void SavePresetDialog::update(const std::string& preset_name) +void SavePresetDialog::update_info_for_edit_ph_printer(const std::string& preset_name) { - if (m_preset_cb->type() != Preset::TYPE_PRINTER || !m_preset_cb->is_selected_physical_printer()) - return; - - bool show = m_old_preset_name != preset_name; + bool show = wxGetApp().preset_bundle->physical_printers.has_selection() && m_old_preset_name != preset_name; m_label->Show(show); m_radio_sizer->ShowItems(show); @@ -1142,6 +1250,12 @@ void SavePresetDialog::update(const std::string& preset_name) m_action_radio_box->SetString(n++, label); } +void SavePresetDialog::layout() +{ + this->Layout(); + this->Fit(); +} + void SavePresetDialog::on_dpi_changed(const wxRect& suggested_rect) { const int& em = em_unit(); @@ -1149,65 +1263,13 @@ void SavePresetDialog::on_dpi_changed(const wxRect& suggested_rect) msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL }); const wxSize& size = wxSize(45 * em, 35 * em); - SetMinSize(size); + SetMinSize(/*size*/wxSize(100, 50)); Fit(); Refresh(); } -bool SavePresetDialog::preset_name_is_accepted() -{ - const char* unusable_symbols = "<>[]:/\\|?*\""; - const std::string unusable_suffix = PresetCollection::get_suffix_modified();//"(modified)"; - for (size_t i = 0; i < std::strlen(unusable_symbols); i++) { - if (m_preset_name.find_first_of(unusable_symbols[i]) != std::string::npos) { - show_error(this, _L("The supplied name is not valid;") + "\n" + - _L("the following characters are not allowed:") + " " + unusable_symbols); - return false; - } - } - - if (m_preset_name.find(unusable_suffix) != std::string::npos) { - show_error(this, _L("The supplied name is not valid;") + "\n" + - _L("the following suffix is not allowed:") + "\n\t" + - from_u8(PresetCollection::get_suffix_modified())); - return false; - } - - if (m_preset_name == "- default -") { - show_error(this, _L("The supplied name is not available.")); - return false; - } - return true; -} - -bool SavePresetDialog::preset_is_possible_to_save() -{ - const Preset* existing = m_preset_cb->presets()->find_preset(m_preset_name, false); - if (existing && (existing->is_default || existing->is_system)) { - show_error(this, _L("Cannot overwrite a system profile.")); - return false; - } - if (existing && (existing->is_external)) { - show_error(this, _(L("Cannot overwrite an external profile."))); - return false; - } - if (existing && m_preset_name != m_preset_cb->presets()->get_selected_preset_name()) - { - wxString msg_text = GUI::from_u8((boost::format(_utf8(L("Preset with name \"%1%\" already exists."))) % m_preset_name).str()); - msg_text += "\n" + _L("Replace?"); - wxMessageDialog dialog(nullptr, msg_text, _L("Warning"), wxICON_WARNING | wxYES | wxNO); - - if (dialog.ShowModal() == wxID_NO) - return false; - - // Remove the preset from the list. - m_preset_cb->presets()->delete_preset(m_preset_name); - } - return true; -} - -void SavePresetDialog::update_physical_printers() +void SavePresetDialog::update_physical_printers(const std::string& preset_name) { if (m_action == UndefAction) return; @@ -1228,29 +1290,20 @@ void SavePresetDialog::update_physical_printers() if (m_action == ChangePreset) printer.delete_preset(printer_preset_name); - if (printer.add_preset(m_preset_name)) + if (printer.add_preset(preset_name)) physical_printers.save_printer(printer); - else { - wxMessageDialog dialog(nullptr, _L("This preset is already exist for this physical printer. Please, select another one."), _L("Information"), wxICON_INFORMATION | wxOK); - dialog.ShowModal(); - } - physical_printers.select_printer(printer.get_full_name(m_preset_name)); + physical_printers.select_printer(printer.get_full_name(preset_name)); } } void SavePresetDialog::accept() { - m_preset_name = normalize_utf8_nfc(m_combo->GetValue().ToUTF8()); - - if (m_preset_name.empty()) - return; - - if (!preset_name_is_accepted() || - !preset_is_possible_to_save()) - return; - - update_physical_printers(); + for (Item& item : m_items) { + item.accept(); + if (item.type() == Preset::TYPE_PRINTER) + update_physical_printers(item.preset_name()); + } EndModal(wxID_OK); } diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index a30d9f6e90..c0de645dfe 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -14,6 +14,7 @@ class wxStaticText; class ScalableButton; class wxBoxSizer; class wxComboBox; +class wxStaticBitmap; namespace Slic3r { @@ -200,35 +201,70 @@ class SavePresetDialog : public DPIDialog UndefAction }; - TabPresetComboBox* m_preset_cb {nullptr}; - std::string m_preset_name; - wxComboBox* m_combo {nullptr}; - wxStaticText* m_label {nullptr}; + struct Item + { + enum ValidationType + { + Valid, + NoValid, + Warning + }; + + Item(Preset::Type type, const std::string& suffix, wxBoxSizer* sizer, SavePresetDialog* parent); + + void accept(); + + bool is_valid() const { return m_valid_type != NoValid; } + Preset::Type type() const { return m_type; } + std::string preset_name() const { return m_preset_name; } + + private: + Preset::Type m_type; + ValidationType m_valid_type; + std::string m_preset_name; + + SavePresetDialog* m_parent {nullptr}; + wxStaticBitmap* m_valid_bmp {nullptr}; + wxComboBox* m_combo {nullptr}; + wxStaticText* m_valid_label {nullptr}; + + PresetCollection* m_presets {nullptr}; + + void update(); + }; + + std::vector m_items; + + wxBoxSizer* m_presets_sizer {nullptr}; + wxStaticText* m_label {nullptr}; wxRadioBox* m_action_radio_box {nullptr}; wxBoxSizer* m_radio_sizer {nullptr}; ActionType m_action {UndefAction}; - std::string m_ph_printer_name; - std::string m_old_preset_name; + std::string m_ph_printer_name; + std::string m_old_preset_name; public: - SavePresetDialog(TabPresetComboBox* preset_cb, const std::string& suffix); + SavePresetDialog(Preset::Type type, const std::string& suffix); ~SavePresetDialog() {} - std::string get_name() { return m_preset_name; } + void AddItem(Preset::Type type, const std::string& suffix); + + std::string get_name(); + std::string get_name(Preset::Type type); + + bool enable_ok_btn() const; + void add_info_for_edit_ph_printer(wxBoxSizer *sizer); + void update_info_for_edit_ph_printer(const std::string &preset_name); + void layout(); protected: void on_dpi_changed(const wxRect& suggested_rect) override; void on_sys_color_changed() override {} private: - void add_common_items(wxBoxSizer *sizer, const std::string &suffix); - void add_items_for_edit_ph_printer(wxBoxSizer *sizer); - void update(const std::string &preset_name); - bool preset_name_is_accepted(); - bool preset_is_possible_to_save(); - void update_physical_printers(); + void update_physical_printers(const std::string& preset_name); void accept(); }; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 4dbe7cc742..9d37362ba4 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3262,7 +3262,7 @@ void Tab::save_preset(std::string name /*= ""*/, bool detach) std::string suffix = detach ? _utf8(L("Detached")) : _CTX_utf8(L_CONTEXT("Copy", "PresetName"), "PresetName"); if (name.empty()) { - SavePresetDialog dlg(m_presets_choice, suffix); + SavePresetDialog dlg(m_type, suffix); if (dlg.ShowModal() != wxID_OK) return; name = dlg.get_name(); From d84e70f59afaf7c052462f6a7cadf1fa28876eb9 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 30 Jul 2020 09:43:13 +0200 Subject: [PATCH 222/503] SavePresetDialog: Fixed OSX and Linux build + Added scaling of the validation icons --- src/slic3r/GUI/PresetComboBoxes.cpp | 34 ++++++++++++++++------------- src/slic3r/GUI/PresetComboBoxes.hpp | 1 + 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 35acbfd3ae..77bdb38122 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -1049,9 +1049,8 @@ SavePresetDialog::Item::Item(Preset::Type type, const std::string& suffix, wxBox m_valid_bmp = new wxStaticBitmap(m_parent, wxID_ANY, create_scaled_bitmap("tick_mark", m_parent)); - m_combo = new wxComboBox(m_parent, wxID_ANY, from_u8(preset_name)/*, - wxDefaultPosition, wxDefaultSize, 0, 0, wxTE_PROCESS_ENTER*/); - for (auto value : values) + m_combo = new wxComboBox(m_parent, wxID_ANY, from_u8(preset_name)); + for (const std::string& value : values) m_combo->Append(from_u8(value)); m_combo->Bind(wxEVT_TEXT, [this](wxCommandEvent&) { update(); }); @@ -1060,12 +1059,12 @@ SavePresetDialog::Item::Item(Preset::Type type, const std::string& suffix, wxBox m_valid_label->SetFont(wxGetApp().bold_font()); wxBoxSizer* combo_sizer = new wxBoxSizer(wxHORIZONTAL); - combo_sizer->Add(m_valid_bmp, 0, wxEXPAND | wxRIGHT, BORDER_W); + combo_sizer->Add(m_valid_bmp, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, BORDER_W); combo_sizer->Add(m_combo, 1, wxEXPAND, BORDER_W); sizer->Add(label_top, 0, wxEXPAND | wxTOP| wxBOTTOM, BORDER_W); sizer->Add(combo_sizer, 0, wxEXPAND | wxBOTTOM, BORDER_W); - sizer->Add(m_valid_label, 0, wxEXPAND | wxLEFT, 3*BORDER_W/* + m_valid_bmp->GetBitmap().GetWidth()*/); + sizer->Add(m_valid_label, 0, wxEXPAND | wxLEFT, 3*BORDER_W); if (m_type == Preset::TYPE_PRINTER) m_parent->add_info_for_edit_ph_printer(sizer); @@ -1123,9 +1122,7 @@ void SavePresetDialog::Item::update() m_valid_label->SetLabel(info_line); m_valid_label->Show(!info_line.IsEmpty()); - std::string bmp_name = m_valid_type == Warning ? "exclamation" : - m_valid_type == NoValid ? "cross" : "tick_mark" ; - m_valid_bmp->SetBitmap(create_scaled_bitmap(bmp_name, m_parent)); + update_valid_bmp(); if (m_type == Preset::TYPE_PRINTER) m_parent->update_info_for_edit_ph_printer(m_preset_name); @@ -1133,6 +1130,13 @@ void SavePresetDialog::Item::update() m_parent->layout(); } +void SavePresetDialog::Item::update_valid_bmp() +{ + std::string bmp_name = m_valid_type == Warning ? "exclamation" : + m_valid_type == NoValid ? "cross" : "tick_mark" ; + m_valid_bmp->SetBitmap(create_scaled_bitmap(bmp_name, m_parent)); +} + void SavePresetDialog::Item::accept() { if (m_valid_type == Warning) @@ -1147,7 +1151,6 @@ void SavePresetDialog::Item::accept() SavePresetDialog::SavePresetDialog(Preset::Type type, const std::string& suffix) : DPIDialog(nullptr, wxID_ANY, _L("Save preset"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER) { - SetFont(wxGetApp().normal_font()); SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); @@ -1213,18 +1216,16 @@ void SavePresetDialog::add_info_for_edit_ph_printer(wxBoxSizer* sizer) m_action_radio_box = new wxRadioBox(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, WXSIZEOF(choices), choices, 3, wxRA_SPECIFY_ROWS); - m_action_radio_box->SetFont(wxGetApp().normal_font()); - m_action_radio_box->SetLabelFont(wxGetApp().normal_font()); m_action_radio_box->SetSelection(0); m_action_radio_box->Bind(wxEVT_RADIOBOX, [this](wxCommandEvent& e) { m_action = (ActionType)e.GetSelection(); }); m_action = ChangePreset; m_radio_sizer = new wxBoxSizer(wxHORIZONTAL); - m_radio_sizer->Add(m_action_radio_box, 1, wxALIGN_CENTER_VERTICAL); + m_radio_sizer->Add(m_action_radio_box, 1, wxEXPAND | wxTOP, 2*BORDER_W); - sizer->Add(m_label, 0, wxEXPAND | wxALL, 2*BORDER_W); - sizer->Add(m_radio_sizer, 1, wxEXPAND | wxLEFT, 2*BORDER_W); + sizer->Add(m_label, 0, wxEXPAND | wxLEFT | wxTOP, 3*BORDER_W); + sizer->Add(m_radio_sizer, 1, wxEXPAND | wxLEFT, 3*BORDER_W); } void SavePresetDialog::update_info_for_edit_ph_printer(const std::string& preset_name) @@ -1262,7 +1263,10 @@ void SavePresetDialog::on_dpi_changed(const wxRect& suggested_rect) msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL }); - const wxSize& size = wxSize(45 * em, 35 * em); + for (Item& item : m_items) + item.update_valid_bmp(); + + //const wxSize& size = wxSize(45 * em, 35 * em); SetMinSize(/*size*/wxSize(100, 50)); Fit(); diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index c0de645dfe..f31b67fbe6 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -212,6 +212,7 @@ class SavePresetDialog : public DPIDialog Item(Preset::Type type, const std::string& suffix, wxBoxSizer* sizer, SavePresetDialog* parent); + void update_valid_bmp(); void accept(); bool is_valid() const { return m_valid_type != NoValid; } From 5eb3b21be791003fa575066c4d1096e9b879f509 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 30 Jul 2020 09:45:45 +0200 Subject: [PATCH 223/503] Added missed icons/tick_mark.svg --- resources/icons/tick_mark.svg | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 resources/icons/tick_mark.svg diff --git a/resources/icons/tick_mark.svg b/resources/icons/tick_mark.svg new file mode 100644 index 0000000000..4ccab2192d --- /dev/null +++ b/resources/icons/tick_mark.svg @@ -0,0 +1,6 @@ + + + + + + From 534e8bb909a7533020256104b220e93842e8ad02 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 30 Jul 2020 13:49:57 +0200 Subject: [PATCH 224/503] ENABLE_GCODE_VIEWER -> Export to gcode layer z and layer height at each layer change --- src/libslic3r/GCode.cpp | 42 +++++++++++++++++--------- src/libslic3r/GCode.hpp | 12 +++----- src/libslic3r/GCode/GCodeProcessor.cpp | 2 +- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index ced52207d0..2dea4fb22b 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1180,6 +1180,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu #if ENABLE_GCODE_VIEWER m_last_width = 0.0f; m_last_height = 0.0f; + m_last_layer_z = 0.0f; #else m_last_mm3_per_mm = GCodeAnalyzer::Default_mm3_per_mm; m_last_width = GCodeAnalyzer::Default_Width; @@ -2067,6 +2068,20 @@ void GCode::process_layer( std::string gcode; +#if ENABLE_GCODE_VIEWER + // export layer z + char buf[64]; + sprintf(buf, ";Z%g\n", print_z); + gcode += buf; + // export layer height + float height = first_layer ? static_cast(print_z) : static_cast(print_z) - m_last_layer_z; + sprintf(buf, ";%s%g\n", GCodeProcessor::Height_Tag.c_str(), height); + gcode += buf; + // update caches + m_last_layer_z = static_cast(print_z); + m_last_height = height; +#endif // ENABLE_GCODE_VIEWER + // Set new layer - this will change Z and force a retraction if retract_layer_change is enabled. if (! print.config().before_layer_gcode.value.empty()) { DynamicConfig config; @@ -3207,7 +3222,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } } - // adds analyzer tags and updates analyzer's tracking data + // adds processor tags and updates processor tracking data #if !ENABLE_GCODE_VIEWER if (m_enable_analyzer) { #endif // !ENABLE_GCODE_VIEWER @@ -3234,40 +3249,39 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), int(m_last_analyzer_extrusion_role)); gcode += buf; } -#endif // ENABLE_GCODE_VIEWER -#if !ENABLE_GCODE_VIEWER if (last_was_wipe_tower || (m_last_mm3_per_mm != path.mm3_per_mm)) { m_last_mm3_per_mm = path.mm3_per_mm; sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), m_last_mm3_per_mm); gcode += buf; } -#endif // !ENABLE_GCODE_VIEWER +#endif // ENABLE_GCODE_VIEWER - if (last_was_wipe_tower || (m_last_width != path.width)) { + if (last_was_wipe_tower || m_last_width != path.width) { m_last_width = path.width; #if ENABLE_GCODE_VIEWER - sprintf(buf, ";%s%f\n", GCodeProcessor::Width_Tag.c_str(), m_last_width); - gcode += buf; + sprintf(buf, ";%s%g\n", GCodeProcessor::Width_Tag.c_str(), m_last_width); #else sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), m_last_width); - gcode += buf; #endif // ENABLE_GCODE_VIEWER + gcode += buf; } - if (last_was_wipe_tower || (m_last_height != path.height)) { - m_last_height = path.height; + #if ENABLE_GCODE_VIEWER - sprintf(buf, ";%s%f\n", GCodeProcessor::Height_Tag.c_str(), m_last_height); + if (last_was_wipe_tower || std::abs(m_last_height - path.height) > EPSILON) { + m_last_height = path.height; + sprintf(buf, ";%s%g\n", GCodeProcessor::Height_Tag.c_str(), m_last_height); gcode += buf; + } #else + if (last_was_wipe_tower || m_last_height != path.height) { + m_last_height = path.height; sprintf(buf, ";%s%f\n", GCodeAnalyzer::Height_Tag.c_str(), m_last_height); gcode += buf; -#endif // ENABLE_GCODE_VIEWER } -#if !ENABLE_GCODE_VIEWER } -#endif // !ENABLE_GCODE_VIEWER +#endif // ENABLE_GCODE_VIEWER std::string comment; if (m_enable_cooling_markers) { diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 443c25bb2f..69f98bfa52 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -172,14 +172,11 @@ public: m_volumetric_speed(0), m_last_pos_defined(false), m_last_extrusion_role(erNone), -#if ENABLE_GCODE_VIEWER - m_last_width(0.0f), - m_last_height(0.0f), -#else +#if !ENABLE_GCODE_VIEWER m_last_mm3_per_mm(GCodeAnalyzer::Default_mm3_per_mm), m_last_width(GCodeAnalyzer::Default_Width), m_last_height(GCodeAnalyzer::Default_Height), -#endif // ENABLE_GCODE_VIEWER +#endif // !ENABLE_GCODE_VIEWER m_brim_done(false), m_second_layer_things_done(false), m_normal_time_estimator(GCodeTimeEstimator::Normal), @@ -379,8 +376,9 @@ private: ExtrusionRole m_last_extrusion_role; #if ENABLE_GCODE_VIEWER // Support for G-Code Processor - float m_last_width; - float m_last_height; + float m_last_width{ 0.0f }; + float m_last_height{ 0.0f }; + float m_last_layer_z{ 0.0f }; #else // Support for G-Code Analyzer double m_last_mm3_per_mm; diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 7a6b1ecb42..c10552482d 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -23,7 +23,7 @@ namespace Slic3r { const std::string GCodeProcessor::Extrusion_Role_Tag = "ExtrType:"; const std::string GCodeProcessor::Width_Tag = "PrusaSlicer__WIDTH:"; -const std::string GCodeProcessor::Height_Tag = "PrusaSlicer__HEIGHT:"; +const std::string GCodeProcessor::Height_Tag = "Height:"; const std::string GCodeProcessor::Color_Change_Tag = "COLOR_CHANGE"; const std::string GCodeProcessor::Pause_Print_Tag = "PAUSE_PRINT"; const std::string GCodeProcessor::Custom_Code_Tag = "CUSTOM_CODE"; From 2dee3abea033e57fe79fd986799726b38ac80483 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 30 Jul 2020 14:15:00 +0200 Subject: [PATCH 225/503] Revert titles in legend dialog to previous format --- src/slic3r/GUI/ImGuiWrapper.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 00d69b4ccb..6aef521667 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -762,16 +762,10 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co void ImGuiWrapper::title(const std::string& str) { - ImGuiWindow* window = ImGui::GetCurrentWindow(); - - ImRect frame_bb; - frame_bb.Min = { window->WorkRect.Min.x, window->DC.CursorPos.y }; - frame_bb.Max = { window->WorkRect.Max.x, window->DC.CursorPos.y + ImGui::CalcTextSize(str.c_str(), nullptr, false).y }; - frame_bb.Min.x -= IM_FLOOR(window->WindowPadding.x * 0.5f - 1.0f); - frame_bb.Max.x += IM_FLOOR(window->WindowPadding.x * 0.5f); - - window->DrawList->AddRectFilled(frame_bb.Min, frame_bb.Max, ImGui::GetColorU32(COL_ORANGE_DARK), 0.0f, 0); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); text(str); + ImGui::PopStyleColor(); + ImGui::Separator(); } void ImGuiWrapper::disabled_begin(bool disabled) From 3cf2914a9e574691ef9fe24f4ba1a735305a2bb2 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 30 Jul 2020 16:16:56 +0200 Subject: [PATCH 226/503] UnsavedChangesDialog: first implementation --- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/GUI_App.hpp | 6 + src/slic3r/GUI/Search.cpp | 10 +- src/slic3r/GUI/Tab.cpp | 5 + src/slic3r/GUI/UnsavedChangesDialog.cpp | 271 ++++++++++++++++++++++++ src/slic3r/GUI/UnsavedChangesDialog.hpp | 151 +++++++++++++ 6 files changed, 437 insertions(+), 8 deletions(-) create mode 100644 src/slic3r/GUI/UnsavedChangesDialog.cpp create mode 100644 src/slic3r/GUI/UnsavedChangesDialog.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 20ea4e33a9..cd28d6eb20 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -163,6 +163,8 @@ set(SLIC3R_GUI_SOURCES GUI/InstanceCheck.hpp GUI/Search.cpp GUI/Search.hpp + GUI/UnsavedChangesDialog.cpp + GUI/UnsavedChangesDialog.hpp Utils/Http.cpp Utils/Http.hpp Utils/FixModelByWin10.cpp diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index db551610b7..6fa9429157 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -86,6 +86,12 @@ class ConfigWizard; static wxString dots("…", wxConvUTF8); +// Does our wxWidgets version support markup? +// https://github.com/prusa3d/PrusaSlicer/issues/4282#issuecomment-634676371 +#if wxUSE_MARKUP && wxCHECK_VERSION(3, 1, 1) + #define SUPPORTS_MARKUP +#endif + class GUI_App : public wxApp { bool m_initialized { false }; diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index 2a2af5336b..6eab6b72ac 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -28,12 +28,6 @@ using GUI::into_u8; namespace Search { -// Does our wxWidgets version support markup? -// https://github.com/prusa3d/PrusaSlicer/issues/4282#issuecomment-634676371 -#if wxUSE_MARKUP && wxCHECK_VERSION(3, 1, 1) - #define SEARCH_SUPPORTS_MARKUP -#endif - static char marker_by_type(Preset::Type type, PrinterTechnology pt) { switch(type) { @@ -264,7 +258,7 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/) std::string label_u8 = into_u8(label); std::string label_plain = label_u8; -#ifdef SEARCH_SUPPORTS_MARKUP +#ifdef SUPPORTS_MARKUP boost::replace_all(label_plain, std::string(1, char(ImGui::ColorMarkerStart)), ""); boost::replace_all(label_plain, std::string(1, char(ImGui::ColorMarkerEnd)), ""); #else @@ -442,7 +436,7 @@ SearchDialog::SearchDialog(OptionsSearcher* searcher) wxDataViewTextRenderer* const markupRenderer = new wxDataViewTextRenderer(); -#ifdef SEARCH_SUPPORTS_MARKUP +#ifdef SUPPORTS_MARKUP markupRenderer->EnableMarkup(); #endif diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 9d37362ba4..49317f802f 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -36,6 +36,7 @@ #include "MainFrame.hpp" #include "format.hpp" #include "PhysicalPrinterDialog.hpp" +#include "UnsavedChangesDialog.hpp" namespace Slic3r { namespace GUI { @@ -3131,6 +3132,10 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, // if the current preset was not dirty, or the user agreed to discard the changes, 1 is returned. bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr*/, const std::string& new_printer_name /*= ""*/) { + UnsavedChangesDialog dlg(m_type); + dlg.ShowModal(); + + if (presets == nullptr) presets = m_presets; // Display a dialog showing the dirty options in a human readable form. const Preset& old_preset = presets->get_edited_preset(); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp new file mode 100644 index 0000000000..21da295d40 --- /dev/null +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -0,0 +1,271 @@ +#include "UnsavedChangesDialog.hpp" + +#include +#include +#include +#include +#include + +#include "wx/dataview.h" + +#include "libslic3r/PrintConfig.hpp" +#include "libslic3r/PresetBundle.hpp" +#include "GUI_App.hpp" +#include "Plater.hpp" +#include "Tab.hpp" + +#define FTS_FUZZY_MATCH_IMPLEMENTATION +#include "fts_fuzzy_match.h" + +#include "imgui/imconfig.h" + +using boost::optional; + +namespace Slic3r { + +namespace GUI { + +// ---------------------------------------------------------------------------- +// ModelNode: a node inside UnsavedChangesModel +// ---------------------------------------------------------------------------- + +// preset(root) node +ModelNode::ModelNode(const wxString& text, Preset::Type preset_type) : + m_parent(nullptr), + m_preset_type(preset_type), + m_text(text) +{ +} + +// group node +ModelNode::ModelNode(ModelNode* parent, const wxString& text, const std::string& icon_name) : + m_parent(parent), + m_text(text) +{ +} + +// group node +ModelNode::ModelNode(ModelNode* parent, const wxString& text, bool is_option) : + m_parent(parent), + m_text(text), + m_container(!is_option) +{ +} + + +// ---------------------------------------------------------------------------- +// UnsavedChangesModel +// ---------------------------------------------------------------------------- + +UnsavedChangesModel::UnsavedChangesModel(wxWindow* parent) +{ + int icon_id = 0; + for (const std::string& icon : { "cog", "printer", "sla_printer", "spool", "resin" }) + m_icon[icon_id++] = ScalableBitmap(parent, icon); + + m_root = new ModelNode("Preset", Preset::TYPE_INVALID); +} + +UnsavedChangesModel::~UnsavedChangesModel() +{ + delete m_root; +} + +void UnsavedChangesModel::GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const +{ + wxASSERT(item.IsOk()); + + ModelNode* node = (ModelNode*)item.GetID(); + switch (col) + { + case colToggle: + variant = node->m_toggle; + break; + case colTypeIcon: + variant << node->m_type_icon; + break; + case colGroupIcon: + variant << node->m_group_icon; + break; + case colMarkedText: + variant =node->m_text; + break; + case colOldValue: + variant =node->m_text; + break; + case colNewValue: + variant =node->m_text; + break; + + default: + wxLogError("UnsavedChangesModel::GetValue: wrong column %d", col); + } +} + +bool UnsavedChangesModel::SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col) +{ + assert(item.IsOk()); + + ModelNode* node = (ModelNode*)item.GetID(); + switch (col) + { + case colToggle: + node->m_toggle = variant.GetBool(); + return true; + case colTypeIcon: + node->m_type_icon << variant; + return true; + case colGroupIcon: + node->m_group_icon << variant; + return true; + case colMarkedText: + node->m_text = variant.GetString(); + return true; + case colOldValue: + node->m_text = variant.GetString(); + return true; + case colNewValue: + node->m_text = variant.GetString(); + return true; + default: + wxLogError("UnsavedChangesModel::SetValue: wrong column"); + } + return false; +} + +bool UnsavedChangesModel::IsEnabled(const wxDataViewItem& item, unsigned int col) const +{ + assert(item.IsOk()); + + ModelNode* node = (ModelNode*)item.GetID(); + + // disable unchecked nodes + return !node->IsToggle(); +} + +wxDataViewItem UnsavedChangesModel::GetParent(const wxDataViewItem& item) const +{ + // the invisible root node has no parent + if (!item.IsOk()) + return wxDataViewItem(nullptr); + + ModelNode* node = (ModelNode*)item.GetID(); + + // "MyMusic" also has no parent + if (node == m_root) + return wxDataViewItem(nullptr); + + return wxDataViewItem((void*)node->GetParent()); +} + +bool UnsavedChangesModel::IsContainer(const wxDataViewItem& item) const +{ + // the invisble root node can have children + if (!item.IsOk()) + return true; + + ModelNode* node = (ModelNode*)item.GetID(); + return node->IsContainer(); +} + +unsigned int UnsavedChangesModel::GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const +{ + ModelNode* node = (ModelNode*)parent.GetID(); + if (!node) { + array.Add(wxDataViewItem((void*)m_root)); + return 1; + } + + if (node->GetChildCount() == 0) + return 0; + + unsigned int count = node->GetChildren().GetCount(); + for (unsigned int pos = 0; pos < count; pos++) { + ModelNode* child = node->GetChildren().Item(pos); + array.Add(wxDataViewItem((void*)child)); + } + + return count; +} + + +wxString UnsavedChangesModel::GetColumnType(unsigned int col) const +{ + if (col == colToggle) + return "bool"; + + if (col < colMarkedText) + return "wxBitmap"; + + return "string"; +} + + +//------------------------------------------ +// UnsavedChangesDialog +//------------------------------------------ + +UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) + : DPIDialog(NULL, wxID_ANY, _L("Unsaved Changes"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +{ + wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); + SetBackgroundColour(bgr_clr); + + int border = 10; + int em = em_unit(); + + changes_tree = new wxDataViewCtrl(this, wxID_ANY, wxDefaultPosition, wxSize(em * 80, em * 60), wxBORDER_SIMPLE); + changes_tree_model = new UnsavedChangesModel(this); + changes_tree->AssociateModel(changes_tree_model); + + changes_tree->AppendToggleColumn(L"\u2610", UnsavedChangesModel::colToggle);//2610,11,12 //2714 + changes_tree->AppendBitmapColumn("", UnsavedChangesModel::colTypeIcon); + changes_tree->AppendBitmapColumn("", UnsavedChangesModel::colGroupIcon); + + wxDataViewTextRenderer* const markupRenderer = new wxDataViewTextRenderer(); + +#ifdef SUPPORTS_MARKUP + markupRenderer->EnableMarkup(); +#endif + + changes_tree->AppendColumn(new wxDataViewColumn("", markupRenderer, UnsavedChangesModel::colMarkedText, wxCOL_WIDTH_AUTOSIZE, wxALIGN_LEFT)); + changes_tree->AppendColumn(new wxDataViewColumn("Old value", markupRenderer, UnsavedChangesModel::colOldValue, wxCOL_WIDTH_AUTOSIZE, wxALIGN_LEFT)); + changes_tree->AppendColumn(new wxDataViewColumn("New value", markupRenderer, UnsavedChangesModel::colNewValue, wxCOL_WIDTH_AUTOSIZE, wxALIGN_LEFT)); + + wxStdDialogButtonSizer* cancel_btn = this->CreateStdDialogButtonSizer(wxCANCEL); + + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + + topSizer->Add(new wxStaticText(this, wxID_ANY, _L("There is unsaved changes for the current preset") + ":"), 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(changes_tree, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(cancel_btn, 0, wxEXPAND | wxALL, border); + + SetSizer(topSizer); + topSizer->SetSizeHints(this); +} + +void UnsavedChangesDialog::on_dpi_changed(const wxRect& suggested_rect) +{ + const int& em = em_unit(); + + msw_buttons_rescale(this, em, { wxID_CANCEL }); + + const wxSize& size = wxSize(80 * em, 60 * em); + SetMinSize(size); + + Fit(); + Refresh(); +} + +void UnsavedChangesDialog::on_sys_color_changed() +{ + // msw_rescale updates just icons, so use it +// changes_tree_model->msw_rescale(); + + Refresh(); +} + + +} + +} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp new file mode 100644 index 0000000000..a3ee7d984f --- /dev/null +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -0,0 +1,151 @@ +#ifndef slic3r_UnsavedChangesDialog_hpp_ +#define slic3r_UnsavedChangesDialog_hpp_ + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include "GUI_Utils.hpp" +#include "wxExtensions.hpp" +#include "libslic3r/Preset.hpp" + +namespace Slic3r { + +namespace GUI{ + +// ---------------------------------------------------------------------------- +// ModelNode: a node inside UnsavedChangesModel +// ---------------------------------------------------------------------------- + +class ModelNode; +WX_DEFINE_ARRAY_PTR(ModelNode*, ModelNodePtrArray); + +class ModelNode +{ + ModelNode* m_parent; + ModelNodePtrArray m_children; + wxBitmap m_empty_bmp; + Preset::Type m_preset_type {Preset::TYPE_INVALID}; + + // TODO/FIXME: + // the GTK version of wxDVC (in particular wxDataViewCtrlInternal::ItemAdded) + // needs to know in advance if a node is or _will be_ a container. + // Thus implementing: + // bool IsContainer() const + // { return m_children.GetCount()>0; } + // doesn't work with wxGTK when UnsavedChangesModel::AddToClassical is called + // AND the classical node was removed (a new node temporary without children + // would be added to the control) + bool m_container {true}; + +public: + + bool m_toggle {true}; + wxBitmap m_type_icon; + wxBitmap m_group_icon; + wxString m_text; + wxString m_old_value; + wxString m_new_value; + + // preset(root) node + ModelNode(const wxString& text, Preset::Type preset_type); + + // group node + ModelNode(ModelNode* parent, const wxString& text, const std::string& icon_name); + + // group node + ModelNode(ModelNode* parent, const wxString& text, bool is_option); + + ~ModelNode() { + // free all our children nodes + size_t count = m_children.GetCount(); + for (size_t i = 0; i < count; i++) { + ModelNode* child = m_children[i]; + delete child; + } + } + + bool IsContainer() const { return m_container; } + bool IsToggle() const { return m_toggle; } + + ModelNode* GetParent() { return m_parent; } + ModelNodePtrArray& GetChildren() { return m_children; } + ModelNode* GetNthChild(unsigned int n) { return m_children.Item(n); } + unsigned int GetChildCount() const { return m_children.GetCount(); } + + void Insert(ModelNode* child, unsigned int n) { m_children.Insert(child, n); } + void Append(ModelNode* child) { m_children.Add(child); } +}; + + +// ---------------------------------------------------------------------------- +// UnsavedChangesModel +// ---------------------------------------------------------------------------- + +class UnsavedChangesModel : public wxDataViewModel +{ + ModelNode* m_root; + ScalableBitmap m_icon[5]; + +public: + enum { + colToggle, + colTypeIcon, + colGroupIcon, + colMarkedText, + colOldValue, + colNewValue, + colMax + }; + + UnsavedChangesModel(wxWindow* parent); + ~UnsavedChangesModel(); + + virtual unsigned int GetColumnCount() const override { return colMax; } + virtual wxString GetColumnType(unsigned int col) const override; + + virtual wxDataViewItem GetParent(const wxDataViewItem& item) const override; + virtual unsigned int GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const override; + + virtual void GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const override; + virtual bool SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col) override; + + virtual bool IsEnabled(const wxDataViewItem& item, unsigned int col) const override; + virtual bool IsContainer(const wxDataViewItem& item) const override; + +}; + + +//------------------------------------------ +// UnsavedChangesDialog +//------------------------------------------ +class UnsavedChangesDialog : public DPIDialog +{ + wxDataViewCtrl* changes_tree{ nullptr }; + UnsavedChangesModel* changes_tree_model{ nullptr }; + +public: + UnsavedChangesDialog(Preset::Type type); + ~UnsavedChangesDialog() {} + + void ProcessSelection(wxDataViewItem selection); + +protected: + void on_dpi_changed(const wxRect& suggested_rect) override; + void on_sys_color_changed() override; +}; + + +} +} + +#endif //slic3r_UnsavedChangesDialog_hpp_ From a29b00a0b46b390a1ae195534e094faab49824d2 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Aug 2020 08:28:43 +0200 Subject: [PATCH 227/503] Use ImGui::TextColored() --- src/slic3r/GUI/GLCanvas3D.cpp | 73 ++++++++------------ src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 4 +- src/slic3r/GUI/ImGuiWrapper.cpp | 16 +++++ src/slic3r/GUI/ImGuiWrapper.hpp | 3 + src/slic3r/GUI/Mouse3DController.cpp | 63 ++++++----------- 5 files changed, 70 insertions(+), 89 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 2d45277849..5109d24264 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -225,56 +225,44 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); const Size& cnv_size = canvas.get_canvas_size(); - float canvas_w = (float)cnv_size.get_width(); - float canvas_h = (float)cnv_size.get_height(); ImGuiWrapper& imgui = *wxGetApp().imgui(); - imgui.set_next_window_pos(canvas_w - imgui.get_style_scaling() * THICKNESS_BAR_WIDTH, canvas_h, ImGuiCond_Always, 1.0f, 1.0f); + imgui.set_next_window_pos(static_cast(cnv_size.get_width()) - imgui.get_style_scaling() * THICKNESS_BAR_WIDTH, + static_cast(cnv_size.get_height()), ImGuiCond_Always, 1.0f, 1.0f); - imgui.begin(_(L("Variable layer height")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse); + imgui.begin(_L("Variable layer height"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(_(L("Left mouse button:"))); - ImGui::PopStyleColor(); + imgui.text_colored(ORANGE, _L("Left mouse button:")); ImGui::SameLine(); - imgui.text(_(L("Add detail"))); + imgui.text(_L("Add detail")); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(_(L("Right mouse button:"))); - ImGui::PopStyleColor(); + imgui.text_colored(ORANGE, _L("Right mouse button:")); ImGui::SameLine(); - imgui.text(_(L("Remove detail"))); + imgui.text(_L("Remove detail")); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(_(L("Shift + Left mouse button:"))); - ImGui::PopStyleColor(); + imgui.text_colored(ORANGE, _L("Shift + Left mouse button:")); ImGui::SameLine(); - imgui.text(_(L("Reset to base"))); + imgui.text(_L("Reset to base")); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(_(L("Shift + Right mouse button:"))); - ImGui::PopStyleColor(); + imgui.text_colored(ORANGE, _L("Shift + Right mouse button:")); ImGui::SameLine(); - imgui.text(_(L("Smoothing"))); + imgui.text(_L("Smoothing")); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(_(L("Mouse wheel:"))); - ImGui::PopStyleColor(); + imgui.text_colored(ORANGE, _L("Mouse wheel:")); ImGui::SameLine(); - imgui.text(_(L("Increase/decrease edit area"))); + imgui.text(_L("Increase/decrease edit area")); ImGui::Separator(); - if (imgui.button(_(L("Adaptive")))) + if (imgui.button(_L("Adaptive"))) wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, m_adaptive_quality)); ImGui::SameLine(); float text_align = ImGui::GetCursorPosX(); ImGui::AlignTextToFramePadding(); - imgui.text(_(L("Quality / Speed"))); - if (ImGui::IsItemHovered()) - { + imgui.text(_L("Quality / Speed")); + if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); - ImGui::TextUnformatted(_(L("Higher print quality versus higher print speed.")).ToUTF8()); + ImGui::TextUnformatted(_L("Higher print quality versus higher print speed.").ToUTF8()); ImGui::EndTooltip(); } @@ -285,13 +273,13 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const ImGui::SliderFloat("", &m_adaptive_quality, 0.0f, 1.f, "%.2f"); ImGui::Separator(); - if (imgui.button(_(L("Smooth")))) + if (imgui.button(_L("Smooth"))) wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), HeightProfileSmoothEvent(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, m_smooth_params)); ImGui::SameLine(); ImGui::SetCursorPosX(text_align); ImGui::AlignTextToFramePadding(); - imgui.text(_(L("Radius"))); + imgui.text(_L("Radius")); ImGui::SameLine(); ImGui::SetCursorPosX(widget_align); ImGui::PushItemWidth(imgui.get_style_scaling() * 120.0f); @@ -301,7 +289,7 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const ImGui::SetCursorPosX(text_align); ImGui::AlignTextToFramePadding(); - imgui.text(_(L("Keep min"))); + imgui.text(_L("Keep min")); ImGui::SameLine(); if (ImGui::GetCursorPosX() < widget_align) // because of line lenght after localization ImGui::SetCursorPosX(widget_align); @@ -310,7 +298,7 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const imgui.checkbox("##2", m_smooth_params.keep_min); ImGui::Separator(); - if (imgui.button(_(L("Reset")))) + if (imgui.button(_L("Reset"))) wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE)); imgui.end(); @@ -1430,8 +1418,7 @@ void GLCanvas3D::Tooltip::render(const Vec2d& mouse_position, GLCanvas3D& canvas #if ENABLE_SLOPE_RENDERING void GLCanvas3D::Slope::render() const { - if (m_dialog_shown) - { + if (m_dialog_shown) { const std::array& z_range = m_volumes.get_slope_z_range(); std::array angle_range = { Geometry::rad2deg(::acos(z_range[0])) - 90.0f, Geometry::rad2deg(::acos(z_range[1])) - 90.0f }; bool modified = false; @@ -1439,9 +1426,9 @@ void GLCanvas3D::Slope::render() const ImGuiWrapper& imgui = *wxGetApp().imgui(); const Size& cnv_size = m_canvas.get_canvas_size(); imgui.set_next_window_pos((float)cnv_size.get_width(), (float)cnv_size.get_height(), ImGuiCond_Always, 1.0f, 1.0f); - imgui.begin(_(L("Slope visualization")), nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + imgui.begin(_L("Slope visualization"), nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); - imgui.text(_(L("Facets' slope range (degrees)")) + ":"); + imgui.text(_L("Facets' slope range (degrees)") + ":"); ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.75f, 0.0f, 0.0f, 0.5f)); ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(1.0f, 0.0f, 0.0f, 0.5f)); @@ -1453,8 +1440,7 @@ void GLCanvas3D::Slope::render() const float slope_bound = 90.f - angle_range[1]; bool mod = ImGui::SliderFloat("##red", &slope_bound, 0.0f, 90.0f, "%.1f"); angle_range[1] = 90.f - slope_bound; - if (mod) - { + if (mod) { modified = true; if (angle_range[0] > angle_range[1]) angle_range[0] = angle_range[1]; @@ -1462,15 +1448,14 @@ void GLCanvas3D::Slope::render() const ImGui::PopStyleColor(4); ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.75f, 0.75f, 0.0f, 0.5f)); - ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(1.0f, 1.0f, 0.0f, 0.5f)); - ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0.85f, 0.85f, 0.0f, 0.5f)); - ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(0.25f, 0.25f, 0.0f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(1.0f, 1.0f, 0.0f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0.85f, 0.85f, 0.0f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(0.25f, 0.25f, 0.0f, 1.0f)); slope_bound = 90.f - angle_range[0]; mod = ImGui::SliderFloat("##yellow", &slope_bound, 0.0f, 90.0f, "%.1f"); angle_range[0] = 90.f - slope_bound; - if (mod) - { + if (mod) { modified = true; if (angle_range[1] < angle_range[0]) angle_range[1] = angle_range[0]; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 3769e96605..4bbd52c307 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -513,9 +513,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) { static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - m_imgui->text(caption); - ImGui::PopStyleColor(); + m_imgui->text_colored(ORANGE, caption); ImGui::SameLine(caption_max); m_imgui->text(text); }; diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 88dd02ccb7..a21194d94e 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -354,6 +354,22 @@ void ImGuiWrapper::text(const wxString &label) this->text(label_utf8.c_str()); } +void ImGuiWrapper::text_colored(const ImVec4& color, const char* label) +{ + ImGui::TextColored(color, label); +} + +void ImGuiWrapper::text_colored(const ImVec4& color, const std::string& label) +{ + this->text_colored(color, label.c_str()); +} + +void ImGuiWrapper::text_colored(const ImVec4& color, const wxString& label) +{ + auto label_utf8 = into_u8(label); + this->text_colored(color, label_utf8.c_str()); +} + bool ImGuiWrapper::slider_float(const char* label, float* v, float v_min, float v_max, const char* format/* = "%.3f"*/, float power/* = 1.0f*/) { return ImGui::SliderFloat(label, v, v_min, v_max, format, power); diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index bf542e1381..dc62e57a08 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -73,6 +73,9 @@ public: void text(const char *label); void text(const std::string &label); void text(const wxString &label); + void text_colored(const ImVec4& color, const char* label); + void text_colored(const ImVec4& color, const std::string& label); + void text_colored(const ImVec4& color, const wxString& label); bool slider_float(const char* label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); bool slider_float(const std::string& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index baa9356b69..ec7cd8d457 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -239,8 +239,7 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const // when the user clicks on [X] or [Close] button we need to trigger // an extra frame to let the dialog disappear - if (m_settings_dialog_closed_by_user) - { + if (m_settings_dialog_closed_by_user) { m_show_settings_dialog = false; m_settings_dialog_closed_by_user = false; canvas.request_extra_frame(); @@ -261,13 +260,10 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const static ImVec2 last_win_size(0.0f, 0.0f); bool shown = true; - if (imgui.begin(_(L("3Dconnexion settings")), &shown, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse)) - { - if (shown) - { + if (imgui.begin(_L("3Dconnexion settings"), &shown, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse)) { + if (shown) { ImVec2 win_size = ImGui::GetWindowSize(); - if ((last_win_size.x != win_size.x) || (last_win_size.y != win_size.y)) - { + if (last_win_size.x != win_size.x || last_win_size.y != win_size.y) { // when the user clicks on [X] button, the next time the dialog is shown // has a dummy size, so we trigger an extra frame to let it have the correct size last_win_size = win_size; @@ -275,59 +271,51 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const } const ImVec4& color = ImGui::GetStyleColorVec4(ImGuiCol_Separator); - ImGui::PushStyleColor(ImGuiCol_Text, color); - imgui.text(_(L("Device:"))); - ImGui::PopStyleColor(); + imgui.text_colored(color, _L("Device:")); ImGui::SameLine(); imgui.text(m_device_str); ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, color); - imgui.text(_(L("Speed:"))); - ImGui::PopStyleColor(); + imgui.text_colored(color, _L("Speed:")); float translation_scale = (float)params_copy.translation.scale / Params::DefaultTranslationScale; - if (imgui.slider_float(_(L("Translation")) + "##1", &translation_scale, 0.1f, 10.0f, "%.1f")) { + if (imgui.slider_float(_L("Translation") + "##1", &translation_scale, 0.1f, 10.0f, "%.1f")) { params_copy.translation.scale = Params::DefaultTranslationScale * (double)translation_scale; params_changed = true; } float rotation_scale = params_copy.rotation.scale / Params::DefaultRotationScale; - if (imgui.slider_float(_(L("Rotation")) + "##1", &rotation_scale, 0.1f, 10.0f, "%.1f")) { + if (imgui.slider_float(_L("Rotation") + "##1", &rotation_scale, 0.1f, 10.0f, "%.1f")) { params_copy.rotation.scale = Params::DefaultRotationScale * rotation_scale; params_changed = true; } float zoom_scale = params_copy.zoom.scale / Params::DefaultZoomScale; - if (imgui.slider_float(_(L("Zoom")), &zoom_scale, 0.1f, 10.0f, "%.1f")) { + if (imgui.slider_float(_L("Zoom"), &zoom_scale, 0.1f, 10.0f, "%.1f")) { params_copy.zoom.scale = Params::DefaultZoomScale * zoom_scale; params_changed = true; } ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, color); - imgui.text(_(L("Deadzone:"))); - ImGui::PopStyleColor(); + imgui.text_colored(color, _L("Deadzone:")); float translation_deadzone = (float)params_copy.translation.deadzone; - if (imgui.slider_float(_(L("Translation")) + "/" + _(L("Zoom")), &translation_deadzone, 0.0f, (float)Params::MaxTranslationDeadzone, "%.2f")) { + if (imgui.slider_float(_L("Translation") + "/" + _L("Zoom"), &translation_deadzone, 0.0f, (float)Params::MaxTranslationDeadzone, "%.2f")) { params_copy.translation.deadzone = (double)translation_deadzone; params_changed = true; } float rotation_deadzone = params_copy.rotation.deadzone; - if (imgui.slider_float(_(L("Rotation")) + "##2", &rotation_deadzone, 0.0f, Params::MaxRotationDeadzone, "%.2f")) { + if (imgui.slider_float(_L("Rotation") + "##2", &rotation_deadzone, 0.0f, Params::MaxRotationDeadzone, "%.2f")) { params_copy.rotation.deadzone = rotation_deadzone; params_changed = true; } ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, color); - imgui.text(_(L("Options:"))); - ImGui::PopStyleColor(); + imgui.text_colored(color, _L("Options:")); bool swap_yz = params_copy.swap_yz; - if (imgui.checkbox("Swap Y/Z axes", swap_yz)) { + if (imgui.checkbox(_L("Swap Y/Z axes"), swap_yz)) { params_copy.swap_yz = swap_yz; params_changed = true; } @@ -335,25 +323,20 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT ImGui::Separator(); ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, color); - imgui.text("DEBUG:"); - imgui.text("Vectors:"); - ImGui::PopStyleColor(); + imgui.text_colored(color, "DEBUG:"); + imgui.text_colored(color, "Vectors:"); Vec3f translation = m_state.get_first_vector_of_type(State::QueueItem::TranslationType).cast(); Vec3f rotation = m_state.get_first_vector_of_type(State::QueueItem::RotationType).cast(); ImGui::InputFloat3("Translation##3", translation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); ImGui::InputFloat3("Rotation##3", rotation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); - ImGui::PushStyleColor(ImGuiCol_Text, color); - imgui.text("Queue size:"); - ImGui::PopStyleColor(); + imgui.text_colored(color, "Queue size:"); int input_queue_size_current[2] = { int(m_state.input_queue_size_current()), int(m_state.input_queue_max_size_achieved) }; ImGui::InputInt2("Current##4", input_queue_size_current, ImGuiInputTextFlags_ReadOnly); int input_queue_size_param = int(params_copy.input_queue_max_size); - if (ImGui::InputInt("Max size", &input_queue_size_param, 1, 1, ImGuiInputTextFlags_ReadOnly)) - { + if (ImGui::InputInt("Max size", &input_queue_size_param, 1, 1, ImGuiInputTextFlags_ReadOnly)) { if (input_queue_size_param > 0) { params_copy.input_queue_max_size = input_queue_size_param; params_changed = true; @@ -361,23 +344,19 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const } ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, color); - imgui.text("Camera:"); - ImGui::PopStyleColor(); + imgui.text_colored(color, "Camera:"); Vec3f target = wxGetApp().plater()->get_camera().get_target().cast(); ImGui::InputFloat3("Target", target.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT ImGui::Separator(); - if (imgui.button(_(L("Close")))) - { + if (imgui.button(_L("Close"))) { // the user clicked on the [Close] button m_settings_dialog_closed_by_user = true; canvas.set_as_dirty(); } } - else - { + else { // the user clicked on the [X] button m_settings_dialog_closed_by_user = true; canvas.set_as_dirty(); From 1532920d81411f07ecb80edf6dbb6396fce73fab Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Aug 2020 08:46:32 +0200 Subject: [PATCH 228/503] GCodeProcessor -> Extended import of config data from gcode saved by PrusaSlicer --- src/libslic3r/GCode/GCodeProcessor.cpp | 207 ++++++++++++++++++++++--- src/libslic3r/GCode/GCodeProcessor.hpp | 8 +- src/slic3r/GUI/GCodeViewer.cpp | 17 +- src/slic3r/GUI/KBShortcutsDialog.cpp | 6 + 4 files changed, 205 insertions(+), 33 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index c10552482d..f3e07da881 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -24,9 +24,9 @@ namespace Slic3r { const std::string GCodeProcessor::Extrusion_Role_Tag = "ExtrType:"; const std::string GCodeProcessor::Width_Tag = "PrusaSlicer__WIDTH:"; const std::string GCodeProcessor::Height_Tag = "Height:"; -const std::string GCodeProcessor::Color_Change_Tag = "COLOR_CHANGE"; -const std::string GCodeProcessor::Pause_Print_Tag = "PAUSE_PRINT"; -const std::string GCodeProcessor::Custom_Code_Tag = "CUSTOM_CODE"; +const std::string GCodeProcessor::Color_Change_Tag = "Color change"; +const std::string GCodeProcessor::Pause_Print_Tag = "Pause print"; +const std::string GCodeProcessor::Custom_Code_Tag = "Custom gcode"; static bool is_valid_extrusion_role(int value) { @@ -297,7 +297,7 @@ void GCodeProcessor::TimeProcessor::reset() const std::vector> GCodeProcessor::Producers = { { EProducer::PrusaSlicer, "PrusaSlicer" }, - { EProducer::Cura, "Cura" }, + { EProducer::Cura, "Cura_SteamEngine" }, { EProducer::Simplify3D, "Simplify3D" }, { EProducer::CraftWare, "CraftWare" }, { EProducer::ideaMaker, "ideaMaker" } @@ -314,32 +314,34 @@ void GCodeProcessor::apply_config(const PrintConfig& config) size_t extruders_count = config.nozzle_diameter.values.size(); m_extruder_offsets.resize(extruders_count); - for (size_t id = 0; id < extruders_count; ++id) { - Vec2f offset = config.extruder_offset.get_at(id).cast(); - m_extruder_offsets[id] = Vec3f(offset(0), offset(1), 0.0f); + for (size_t i = 0; i < extruders_count; ++i) { + Vec2f offset = config.extruder_offset.get_at(i).cast(); + m_extruder_offsets[i] = { offset(0), offset(1), 0.0f }; } - m_extruders_color.resize(extruders_count); - for (size_t id = 0; id < extruders_count; ++id) { - m_extruders_color[id] = static_cast(id); + m_extruder_colors.resize(extruders_count); + for (size_t i = 0; i < extruders_count; ++i) { + m_extruder_colors[i] = static_cast(i); } - for (double diam : config.filament_diameter.values) { - m_filament_diameters.push_back(static_cast(diam)); + m_filament_diameters.resize(config.filament_diameter.values.size()); + for (size_t i = 0; i < config.filament_diameter.values.size(); ++i) { + m_filament_diameters[i] = static_cast(config.filament_diameter.values[i]); } m_time_processor.machine_limits = reinterpret_cast(config); // Filament load / unload times are not specific to a firmware flavor. Let anybody use it if they find it useful. // As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they // are considered to be active for the single extruder multi-material printers only. - m_time_processor.filament_load_times.clear(); - for (double d : config.filament_load_time.values) { - m_time_processor.filament_load_times.push_back(static_cast(d)); + m_time_processor.filament_load_times.resize(config.filament_load_time.values.size()); + for (size_t i = 0; i < config.filament_load_time.values.size(); ++i) { + m_time_processor.filament_load_times[i] = static_cast(config.filament_load_time.values[i]); } - m_time_processor.filament_unload_times.clear(); - for (double d : config.filament_unload_time.values) { - m_time_processor.filament_unload_times.push_back(static_cast(d)); + m_time_processor.filament_unload_times.resize(config.filament_unload_time.values.size()); + for (size_t i = 0; i < config.filament_unload_time.values.size(); ++i) { + m_time_processor.filament_unload_times[i] = static_cast(config.filament_unload_time.values[i]); } + for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i); m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION; @@ -350,6 +352,14 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) { m_parser.apply_config(config); + const ConfigOptionEnum* gcode_flavor = config.option>("gcode_flavor"); + if (gcode_flavor != nullptr) + m_flavor = gcode_flavor->value; + + const ConfigOptionPoints* bed_shape = config.option("bed_shape"); + if (bed_shape != nullptr) + m_result.bed_shape = bed_shape->values; + const ConfigOptionFloats* filament_diameters = config.option("filament_diameter"); if (filament_diameters != nullptr) { for (double diam : filament_diameters->values) { @@ -357,9 +367,127 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) } } - const ConfigOptionPoints* bed_shape = config.option("bed_shape"); - if (bed_shape != nullptr) - m_result.bed_shape = bed_shape->values; + const ConfigOptionPoints* extruder_offset = config.option("extruder_offset"); + if (extruder_offset != nullptr) { + m_extruder_offsets.resize(extruder_offset->values.size()); + for (size_t i = 0; i < extruder_offset->values.size(); ++i) { + Vec2f offset = extruder_offset->values[i].cast(); + m_extruder_offsets[i] = { offset(0), offset(1), 0.0f }; + } + } + + // ensure at least one (default) color is defined + std::string default_color = "#FF8000"; + m_result.extruder_colors = std::vector(1, default_color); + const ConfigOptionStrings* extruder_colour = config.option("extruder_colour"); + if (extruder_colour != nullptr) { + // takes colors from config + m_result.extruder_colors = extruder_colour->values; + // try to replace missing values with filament colors + const ConfigOptionStrings* filament_colour = config.option("filament_colour"); + if (filament_colour != nullptr && filament_colour->values.size() == m_result.extruder_colors.size()) { + for (size_t i = 0; i < m_result.extruder_colors.size(); ++i) { + if (m_result.extruder_colors[i].empty()) + m_result.extruder_colors[i] = filament_colour->values[i]; + } + } + } + + // replace missing values with default + for (size_t i = 0; i < m_result.extruder_colors.size(); ++i) { + if (m_result.extruder_colors[i].empty()) + m_result.extruder_colors[i] = default_color; + } + + m_extruder_colors.resize(m_result.extruder_colors.size()); + for (size_t i = 0; i < m_result.extruder_colors.size(); ++i) { + m_extruder_colors[i] = static_cast(i); + } + + const ConfigOptionFloats* filament_load_time = config.option("filament_load_time"); + if (filament_load_time != nullptr) { + m_time_processor.filament_load_times.resize(filament_load_time->values.size()); + for (size_t i = 0; i < filament_load_time->values.size(); ++i) { + m_time_processor.filament_load_times[i] = static_cast(filament_load_time->values[i]); + } + } + + const ConfigOptionFloats* filament_unload_time = config.option("filament_unload_time"); + if (filament_unload_time != nullptr) { + m_time_processor.filament_unload_times.resize(filament_unload_time->values.size()); + for (size_t i = 0; i < filament_unload_time->values.size(); ++i) { + m_time_processor.filament_unload_times[i] = static_cast(filament_unload_time->values[i]); + } + } + + const ConfigOptionFloats* machine_max_acceleration_x = config.option("machine_max_acceleration_x"); + if (machine_max_acceleration_x != nullptr) + m_time_processor.machine_limits.machine_max_acceleration_x.values = machine_max_acceleration_x->values; + + const ConfigOptionFloats* machine_max_acceleration_y = config.option("machine_max_acceleration_y"); + if (machine_max_acceleration_y != nullptr) + m_time_processor.machine_limits.machine_max_acceleration_y.values = machine_max_acceleration_y->values; + + const ConfigOptionFloats* machine_max_acceleration_z = config.option("machine_max_acceleration_z"); + if (machine_max_acceleration_z != nullptr) + m_time_processor.machine_limits.machine_max_acceleration_z.values = machine_max_acceleration_z->values; + + const ConfigOptionFloats* machine_max_acceleration_e = config.option("machine_max_acceleration_e"); + if (machine_max_acceleration_e != nullptr) + m_time_processor.machine_limits.machine_max_acceleration_e.values = machine_max_acceleration_e->values; + + const ConfigOptionFloats* machine_max_feedrate_x = config.option("machine_max_feedrate_x"); + if (machine_max_feedrate_x != nullptr) + m_time_processor.machine_limits.machine_max_feedrate_x.values = machine_max_feedrate_x->values; + + const ConfigOptionFloats* machine_max_feedrate_y = config.option("machine_max_feedrate_y"); + if (machine_max_feedrate_y != nullptr) + m_time_processor.machine_limits.machine_max_feedrate_y.values = machine_max_feedrate_y->values; + + const ConfigOptionFloats* machine_max_feedrate_z = config.option("machine_max_feedrate_z"); + if (machine_max_feedrate_z != nullptr) + m_time_processor.machine_limits.machine_max_feedrate_z.values = machine_max_feedrate_z->values; + + const ConfigOptionFloats* machine_max_feedrate_e = config.option("machine_max_feedrate_e"); + if (machine_max_feedrate_e != nullptr) + m_time_processor.machine_limits.machine_max_feedrate_e.values = machine_max_feedrate_e->values; + + const ConfigOptionFloats* machine_max_jerk_x = config.option("machine_max_jerk_x"); + if (machine_max_jerk_x != nullptr) + m_time_processor.machine_limits.machine_max_jerk_x.values = machine_max_jerk_x->values; + + const ConfigOptionFloats* machine_max_jerk_y = config.option("machine_max_jerk_y"); + if (machine_max_jerk_y != nullptr) + m_time_processor.machine_limits.machine_max_jerk_y.values = machine_max_jerk_y->values; + + const ConfigOptionFloats* machine_max_jerk_z = config.option("machine_max_jerkz"); + if (machine_max_jerk_z != nullptr) + m_time_processor.machine_limits.machine_max_jerk_z.values = machine_max_jerk_z->values; + + const ConfigOptionFloats* machine_max_jerk_e = config.option("machine_max_jerk_e"); + if (machine_max_jerk_e != nullptr) + m_time_processor.machine_limits.machine_max_jerk_e.values = machine_max_jerk_e->values; + + const ConfigOptionFloats* machine_max_acceleration_extruding = config.option("machine_max_acceleration_extruding"); + if (machine_max_acceleration_extruding != nullptr) + m_time_processor.machine_limits.machine_max_acceleration_extruding.values = machine_max_acceleration_extruding->values; + + const ConfigOptionFloats* machine_max_acceleration_retracting = config.option("machine_max_acceleration_retracting"); + if (machine_max_acceleration_retracting != nullptr) + m_time_processor.machine_limits.machine_max_acceleration_retracting.values = machine_max_acceleration_retracting->values; + + const ConfigOptionFloats* machine_min_extruding_rate = config.option("machine_min_extruding_rate"); + if (machine_min_extruding_rate != nullptr) + m_time_processor.machine_limits.machine_min_extruding_rate.values = machine_min_extruding_rate->values; + + const ConfigOptionFloats* machine_min_travel_rate = config.option("machine_min_travel_rate"); + if (machine_min_travel_rate != nullptr) + m_time_processor.machine_limits.machine_min_travel_rate.values = machine_min_travel_rate->values; + + for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i); + m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION; + } } void GCodeProcessor::enable_stealth_time_estimator(bool enabled) @@ -388,7 +516,7 @@ void GCodeProcessor::reset() m_extrusion_role = erNone; m_extruder_id = 0; - m_extruders_color = ExtrudersColor(); + m_extruder_colors = ExtruderColors(); m_filament_diameters = std::vector(); m_cp_color.reset(); @@ -643,13 +771,13 @@ void GCodeProcessor::process_tags(const std::string& comment) { unsigned char extruder_id = (pos == comment.npos) ? 0 : static_cast(std::stoi(comment.substr(pos + 1))); - m_extruders_color[extruder_id] = static_cast(m_extruder_offsets.size()) + m_cp_color.counter; // color_change position in list of color for preview + m_extruder_colors[extruder_id] = static_cast(m_extruder_offsets.size()) + m_cp_color.counter; // color_change position in list of color for preview ++m_cp_color.counter; if (m_cp_color.counter == UCHAR_MAX) m_cp_color.counter = 0; if (m_extruder_id == extruder_id) { - m_cp_color.current = m_extruders_color[extruder_id]; + m_cp_color.current = m_extruder_colors[extruder_id]; store_move_vertex(EMoveType::Color_change); } @@ -728,6 +856,35 @@ bool GCodeProcessor::process_cura_tags(const std::string& comment) return true; } + // flavor + tag = "FLAVOR:"; + pos = comment.find(tag); + if (pos != comment.npos) { + std::string flavor = comment.substr(pos + tag.length()); + if (flavor == "BFB") + m_flavor = gcfMarlin; // << ??????????????????????? + else if (flavor == "Mach3") + m_flavor = gcfMach3; + else if (flavor == "Makerbot") + m_flavor = gcfMakerWare; + else if (flavor == "UltiGCode") + m_flavor = gcfMarlin; // << ??????????????????????? + else if (flavor == "Marlin(Volumetric)") + m_flavor = gcfMarlin; // << ??????????????????????? + else if (flavor == "Griffin") + m_flavor = gcfMarlin; // << ??????????????????????? + else if (flavor == "Repetier") + m_flavor = gcfRepetier; + else if (flavor == "RepRap") + m_flavor = gcfRepRap; + else if (flavor == "Marlin") + m_flavor = gcfMarlin; + else + BOOST_LOG_TRIVIAL(warning) << "GCodeProcessor found unknown flavor: " << flavor; + + return true; + } + return false; } @@ -1582,7 +1739,7 @@ void GCodeProcessor::process_T(const std::string& command) else { unsigned char old_extruder_id = m_extruder_id; m_extruder_id = id; - m_cp_color.current = m_extruders_color[id]; + m_cp_color.current = m_extruder_colors[id]; // Specific to the MK3 MMU2: // The initial value of extruder_unloaded is set to true indicating // that the filament is parked in the MMU2 unit and there is nothing to be unloaded yet. diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 1def93e74b..bad04100ef 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -10,6 +10,7 @@ #include #include +#include namespace Slic3r { @@ -27,7 +28,7 @@ namespace Slic3r { private: using AxisCoords = std::array; - using ExtrudersColor = std::vector; + using ExtruderColors = std::vector; enum class EUnits : unsigned char { @@ -212,6 +213,7 @@ namespace Slic3r { std::vector moves; #if ENABLE_GCODE_VIEWER_AS_STATE Pointfs bed_shape; + std::vector extruder_colors; #endif // ENABLE_GCODE_VIEWER_AS_STATE #if ENABLE_GCODE_VIEWER_STATISTICS long long time{ 0 }; @@ -221,6 +223,7 @@ namespace Slic3r { moves = std::vector(); #if ENABLE_GCODE_VIEWER_AS_STATE bed_shape = Pointfs(); + extruder_colors = std::vector(); #endif // ENABLE_GCODE_VIEWER_AS_STATE } #else @@ -229,6 +232,7 @@ namespace Slic3r { moves = std::vector(); #if ENABLE_GCODE_VIEWER_AS_STATE bed_shape = Pointfs(); + extruder_colors = std::vector(); #endif // ENABLE_GCODE_VIEWER_AS_STATE } #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -255,7 +259,7 @@ namespace Slic3r { float m_fan_speed; // percentage ExtrusionRole m_extrusion_role; unsigned char m_extruder_id; - ExtrudersColor m_extruders_color; + ExtruderColors m_extruder_colors; std::vector m_filament_diameters; CpColor m_cp_color; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index af3d8d901c..8d4fb6f4cb 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -43,13 +43,13 @@ static GCodeProcessor::EMoveType buffer_type(unsigned char id) { std::array decode_color(const std::string& color) { static const float INV_255 = 1.0f / 255.0f; - std::array ret; + std::array ret = { 0.0f, 0.0f, 0.0f }; const char* c = color.data() + 1; - if ((color.size() == 7) && (color.front() == '#')) { + if (color.size() == 7 && color.front() == '#') { for (size_t j = 0; j < 3; ++j) { int digit1 = hex_digit_to_int(*c++); int digit2 = hex_digit_to_int(*c++); - if ((digit1 == -1) || (digit2 == -1)) + if (digit1 == -1 || digit2 == -1) break; ret[j] = float(digit1 * 16 + digit2) * INV_255; @@ -351,8 +351,14 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: if (m_vertices_count == 0) return; - // update tool colors - m_tool_colors = decode_colors(str_tool_colors); +#if ENABLE_GCODE_VIEWER_AS_STATE + if (m_view_type == EViewType::Tool && !gcode_result.extruder_colors.empty()) + // update tool colors from config stored in the gcode + m_tool_colors = decode_colors(gcode_result.extruder_colors); + else +#endif // ENABLE_GCODE_VIEWER_AS_STATE + // update tool colors + m_tool_colors = decode_colors(str_tool_colors); // update ranges for coloring / legend m_extrusions.reset_ranges(); @@ -1708,7 +1714,6 @@ void GCodeViewer::render_time_estimate() const } #endif // ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG - using Times = std::pair; using TimesList = std::vector>; using Headers = std::vector; diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 66e5ac4878..fc6bc98914 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -204,7 +204,13 @@ void KBShortcutsDialog::fill_shortcuts() { "U", L("Upper Layer") }, { "D", L("Lower Layer") }, { "L", L("Show/Hide Legend") }, +#if ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG + { "T", L("Show Estimated printing time") } +#else { "T", L("Show/Hide Estimated printing time") } +#endif // ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG +#endif // ENABLE_GCODE_VIEWER }; m_full_shortcuts.push_back(std::make_pair(_L("Preview"), preview_shortcuts)); From 757572b760d4062f780bc9b2568f5f0a8c410763 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Aug 2020 11:08:17 +0200 Subject: [PATCH 229/503] Tech ENABLE_LAYOUT_NO_RESTART set as default --- src/libslic3r/Technologies.hpp | 3 - src/slic3r/GUI/GUI_App.cpp | 13 --- src/slic3r/GUI/GUI_Utils.hpp | 22 +---- src/slic3r/GUI/MainFrame.cpp | 164 +-------------------------------- src/slic3r/GUI/MainFrame.hpp | 18 ---- src/slic3r/GUI/Preferences.cpp | 24 ----- 6 files changed, 5 insertions(+), 239 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index c6991c057b..e4b71697d8 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -54,8 +54,5 @@ // Enable built-in DPI changed event handler of wxWidgets 3.1.3 #define ENABLE_WX_3_1_3_DPI_CHANGED_EVENT (1 && ENABLE_2_3_0_ALPHA1) -// Enable changing application layout without the need to restart -#define ENABLE_LAYOUT_NO_RESTART (1 && ENABLE_2_3_0_ALPHA1) - #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index a7b562bd75..bfb1586195 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1061,34 +1061,21 @@ void GUI_App::add_config_menu(wxMenuBar *menu) break; case ConfigMenuPreferences: { -#if ENABLE_LAYOUT_NO_RESTART bool app_layout_changed = false; -#else - bool recreate_app = false; -#endif // ENABLE_LAYOUT_NO_RESTART { // the dialog needs to be destroyed before the call to recreate_GUI() // or sometimes the application crashes into wxDialogBase() destructor // so we put it into an inner scope PreferencesDialog dlg(mainframe); dlg.ShowModal(); -#if ENABLE_LAYOUT_NO_RESTART app_layout_changed = dlg.settings_layout_changed(); -#else - recreate_app = dlg.settings_layout_changed(); -#endif // ENABLE_LAYOUT_NO_RESTART } -#if ENABLE_LAYOUT_NO_RESTART if (app_layout_changed) { mainframe->GetSizer()->Hide((size_t)0); mainframe->update_layout(); mainframe->select_tab(0); mainframe->GetSizer()->Show((size_t)0); } -#else - if (recreate_app) - recreate_GUI(_L("Changing of the settings layout") + dots); -#endif // ENABLE_LAYOUT_NO_RESTART break; } case ConfigMenuLanguage: diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp index 2737b3edbf..6ce3f62a67 100644 --- a/src/slic3r/GUI/GUI_Utils.hpp +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -110,13 +110,8 @@ public: if (!m_can_rescale) return; -#if ENABLE_LAYOUT_NO_RESTART if (m_force_rescale || is_new_scale_factor()) rescale(wxRect()); -#else - if (is_new_scale_factor()) - rescale(wxRect()); -#endif // ENABLE_LAYOUT_NO_RESTART }); #else this->Bind(EVT_DPI_CHANGED_SLICER, [this](const DpiChangedEvent& evt) { @@ -127,13 +122,8 @@ public: if (!m_can_rescale) return; -#if ENABLE_LAYOUT_NO_RESTART if (m_force_rescale || is_new_scale_factor()) rescale(evt.rect); -#else - if (is_new_scale_factor()) - rescale(evt.rect); -#endif // ENABLE_LAYOUT_NO_RESTART }); #endif // wxVERSION_EQUAL_OR_GREATER_THAN @@ -175,9 +165,7 @@ public: int em_unit() const { return m_em_unit; } // int font_size() const { return m_font_size; } const wxFont& normal_font() const { return m_normal_font; } -#if ENABLE_LAYOUT_NO_RESTART void enable_force_rescale() { m_force_rescale = true; } -#endif // ENABLE_LAYOUT_NO_RESTART protected: virtual void on_dpi_changed(const wxRect &suggested_rect) = 0; @@ -191,9 +179,7 @@ private: wxFont m_normal_font; float m_prev_scale_factor; bool m_can_rescale{ true }; -#if ENABLE_LAYOUT_NO_RESTART bool m_force_rescale{ false }; -#endif // ENABLE_LAYOUT_NO_RESTART int m_new_font_point_size; @@ -233,17 +219,17 @@ private: { this->Freeze(); -#if ENABLE_LAYOUT_NO_RESTART && wxVERSION_EQUAL_OR_GREATER_THAN(3,1,3) +#if wxVERSION_EQUAL_OR_GREATER_THAN(3,1,3) if (m_force_rescale) { -#endif // ENABLE_LAYOUT_NO_RESTART +#endif // wxVERSION_EQUAL_OR_GREATER_THAN // rescale fonts of all controls scale_controls_fonts(this, m_new_font_point_size); // rescale current window font scale_win_font(this, m_new_font_point_size); -#if ENABLE_LAYOUT_NO_RESTART && wxVERSION_EQUAL_OR_GREATER_THAN(3,1,3) +#if wxVERSION_EQUAL_OR_GREATER_THAN(3,1,3) m_force_rescale = false; } -#endif // ENABLE_LAYOUT_NO_RESTART +#endif // wxVERSION_EQUAL_OR_GREATER_THAN // set normal application font as a current window font m_normal_font = this->GetFont(); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 521dcab804..628aee2aa6 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -42,7 +42,6 @@ namespace Slic3r { namespace GUI { -#if ENABLE_LAYOUT_NO_RESTART enum class ERescaleTarget { Mainframe, @@ -71,15 +70,12 @@ static void rescale_dialog_after_dpi_change(MainFrame& mainframe, SettingsDialog } } } -#endif // ENABLE_LAYOUT_NO_RESTART MainFrame::MainFrame() : DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "mainframe"), m_printhost_queue_dlg(new PrintHostQueueDialog(this)) , m_recent_projects(9) -#if ENABLE_LAYOUT_NO_RESTART , m_settings_dialog(this) -#endif // ENABLE_LAYOUT_NO_RESTART { // Fonts were created by the DPIFrame constructor for the monitor, on which the window opened. wxGetApp().update_fonts(this); @@ -124,43 +120,15 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S m_loaded = true; -#if !ENABLE_LAYOUT_NO_RESTART -#ifdef __APPLE__ - // Using SetMinSize() on Mac messes up the window position in some cases - // cf. https://groups.google.com/forum/#!topic/wx-users/yUKPBBfXWO0 - // So, if we haven't possibility to set MinSize() for the MainFrame, - // set the MinSize() as a half of regular for the m_plater and m_tabpanel, when settings layout is in slNew mode - // Otherwise, MainFrame will be maximized by height - if (slNew) { - wxSize size = wxGetApp().get_min_size(); - size.SetHeight(int(0.5*size.GetHeight())); - m_plater->SetMinSize(size); - m_tabpanel->SetMinSize(size); - } -#endif -#endif // !ENABLE_LAYOUT_NO_RESTART - // initialize layout m_main_sizer = new wxBoxSizer(wxVERTICAL); wxSizer* sizer = new wxBoxSizer(wxVERTICAL); sizer->Add(m_main_sizer, 1, wxEXPAND); -#if ENABLE_LAYOUT_NO_RESTART SetSizer(sizer); // initialize layout from config update_layout(); sizer->SetSizeHints(this); Fit(); -#else - if (m_plater && m_layout != slOld) - sizer->Add(m_plater, 1, wxEXPAND); - - if (m_tabpanel && m_layout != slDlg) - sizer->Add(m_tabpanel, 1, wxEXPAND); - - sizer->SetSizeHints(this); - SetSizer(sizer); - Fit(); -#endif // !ENABLE_LAYOUT_NO_RESTART const wxSize min_size = wxGetApp().get_min_size(); //wxSize(76*wxGetApp().em_unit(), 49*wxGetApp().em_unit()); #ifdef __APPLE__ @@ -252,12 +220,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S }); wxGetApp().persist_window_geometry(this, true); -#if ENABLE_LAYOUT_NO_RESTART wxGetApp().persist_window_geometry(&m_settings_dialog, true); -#else - if (m_settings_dialog != nullptr) - wxGetApp().persist_window_geometry(m_settings_dialog, true); -#endif // ENABLE_LAYOUT_NO_RESTART update_ui_from_settings(); // FIXME (?) @@ -265,7 +228,6 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S m_plater->show_action_buttons(true); } -#if ENABLE_LAYOUT_NO_RESTART void MainFrame::update_layout() { auto restore_to_creation = [this]() { @@ -380,7 +342,6 @@ void MainFrame::update_layout() Layout(); Thaw(); } -#endif // ENABLE_LAYOUT_NO_RESTART // Called when closing the application and when switching the application language. void MainFrame::shutdown() @@ -414,20 +375,9 @@ void MainFrame::shutdown() // In addition, there were some crashes due to the Paint events sent to already destructed windows. this->Show(false); -#if ENABLE_LAYOUT_NO_RESTART if (m_settings_dialog.IsShown()) // call Close() to trigger call to lambda defined into GUI_App::persist_window_geometry() m_settings_dialog.Close(); -#else - if (m_settings_dialog != nullptr) - { - if (m_settings_dialog->IsShown()) - // call Close() to trigger call to lambda defined into GUI_App::persist_window_geometry() - m_settings_dialog->Close(); - - m_settings_dialog->Destroy(); - } -#endif // ENABLE_LAYOUT_NO_RESTART // Stop the background thread (Windows and Linux). // Disconnect from a 3DConnextion driver (OSX). @@ -486,7 +436,6 @@ void MainFrame::update_title() void MainFrame::init_tabpanel() { -#if ENABLE_LAYOUT_NO_RESTART // wxNB_NOPAGETHEME: Disable Windows Vista theme for the Notebook background. The theme performance is terrible on Windows 10 // with multiple high resolution displays connected. m_tabpanel = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME); @@ -495,27 +444,6 @@ void MainFrame::init_tabpanel() #endif m_tabpanel->Hide(); m_settings_dialog.set_tabpanel(m_tabpanel); -#else - m_layout = wxGetApp().app_config->get("old_settings_layout_mode") == "1" ? slOld : - wxGetApp().app_config->get("new_settings_layout_mode") == "1" ? slNew : - wxGetApp().app_config->get("dlg_settings_layout_mode") == "1" ? slDlg : slOld; - - // From the very beginning the Print settings should be selected - m_last_selected_tab = m_layout == slDlg ? 0 : 1; - - if (m_layout == slDlg) { - m_settings_dialog = new SettingsDialog(this); - m_tabpanel = m_settings_dialog->get_tabpanel(); - } - else { - // wxNB_NOPAGETHEME: Disable Windows Vista theme for the Notebook background. The theme performance is terrible on Windows 10 - // with multiple high resolution displays connected. - m_tabpanel = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME); -#ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList - m_tabpanel->SetFont(Slic3r::GUI::wxGetApp().normal_font()); -#endif - } -#endif // ENABLE_LAYOUT_NO_RESTART m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, [this](wxEvent&) { wxWindow* panel = m_tabpanel->GetCurrentPage(); @@ -536,20 +464,9 @@ void MainFrame::init_tabpanel() select_tab(0); // select Plater }); -#if ENABLE_LAYOUT_NO_RESTART m_plater = new Plater(this, this); m_plater->Hide(); -#else - if (m_layout == slOld) { - m_plater = new Plater(m_tabpanel, this); - m_tabpanel->AddPage(m_plater, _L("Plater")); - } - else { - m_plater = new Plater(this, this); - if (m_layout == slNew) - m_tabpanel->AddPage(new wxPanel(m_tabpanel), _L("Plater")); // empty panel just for Plater tab - } -#endif // ENABLE_LAYOUT_NO_RESTART + wxGetApp().plater_ = m_plater; wxGetApp().obj_list()->create_popup_menus(); @@ -691,7 +608,6 @@ bool MainFrame::can_slice() const bool MainFrame::can_change_view() const { -#if ENABLE_LAYOUT_NO_RESTART switch (m_layout) { default: { return false; } @@ -702,15 +618,6 @@ bool MainFrame::can_change_view() const return page_id != wxNOT_FOUND && dynamic_cast(m_tabpanel->GetPage((size_t)page_id)) != nullptr; } } -#else - if (m_layout == slNew) - return m_plater->IsShown(); - if (m_layout == slDlg) - return true; - // slOld layout mode - int page_id = m_tabpanel->GetSelection(); - return page_id != wxNOT_FOUND && dynamic_cast(m_tabpanel->GetPage((size_t)page_id)) != nullptr; -#endif // ENABLE_LAYOUT_NO_RESTART } bool MainFrame::can_select() const @@ -756,11 +663,7 @@ void MainFrame::on_dpi_changed(const wxRect& suggested_rect) wxGetApp().plater()->msw_rescale(); // update Tabs -#if ENABLE_LAYOUT_NO_RESTART if (m_layout != ESettingsLayout::Dlg) // Do not update tabs if the Settings are in the separated dialog -#else - if (m_layout != slDlg) // Update tabs later, from the SettingsDialog, when the Settings are in the separated dialog -#endif // ENABLE_LAYOUT_NO_RESTART for (auto tab : wxGetApp().tabs_list) tab->msw_rescale(); @@ -789,10 +692,8 @@ void MainFrame::on_dpi_changed(const wxRect& suggested_rect) this->Maximize(is_maximized); -#if ENABLE_LAYOUT_NO_RESTART if (m_layout == ESettingsLayout::Dlg) rescale_dialog_after_dpi_change(*this, m_settings_dialog, ERescaleTarget::SettingsDialog); -#endif // ENABLE_LAYOUT_NO_RESTART } void MainFrame::on_sys_color_changed() @@ -1528,25 +1429,15 @@ void MainFrame::load_config(const DynamicPrintConfig& config) void MainFrame::select_tab(size_t tab/* = size_t(-1)*/) { bool tabpanel_was_hidden = false; -#if ENABLE_LAYOUT_NO_RESTART if (m_layout == ESettingsLayout::Dlg) { -#else - if (m_layout == slDlg) { -#endif // ENABLE_LAYOUT_NO_RESTART if (tab==0) { -#if ENABLE_LAYOUT_NO_RESTART if (m_settings_dialog.IsShown()) this->SetFocus(); -#else - if (m_settings_dialog->IsShown()) - this->SetFocus(); -#endif // ENABLE_LAYOUT_NO_RESTART // plater should be focused for correct navigation inside search window if (m_plater->canvas3D()->is_search_pressed()) m_plater->SetFocus(); return; } -#if ENABLE_LAYOUT_NO_RESTART // Show/Activate Settings Dialog #ifdef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList if (m_settings_dialog.IsShown()) @@ -1563,28 +1454,11 @@ void MainFrame::select_tab(size_t tab/* = size_t(-1)*/) m_settings_dialog.Show(); } #endif -#else - // Show/Activate Settings Dialog - if (m_settings_dialog->IsShown()) -#ifdef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList - m_settings_dialog->Hide(); -#else - m_settings_dialog->SetFocus(); - else -#endif - m_settings_dialog->Show(); -#endif // ENABLE_LAYOUT_NO_RESTART } -#if ENABLE_LAYOUT_NO_RESTART else if (m_layout == ESettingsLayout::New) { m_main_sizer->Show(m_plater, tab == 0); tabpanel_was_hidden = !m_main_sizer->IsShown(m_tabpanel); m_main_sizer->Show(m_tabpanel, tab != 0); -#else - else if (m_layout == slNew) { - m_plater->Show(tab == 0); - m_tabpanel->Show(tab != 0); -#endif // ENABLE_LAYOUT_NO_RESTART // plater should be focused for correct navigation inside search window if (tab == 0 && m_plater->canvas3D()->is_search_pressed()) @@ -1601,11 +1475,7 @@ void MainFrame::select_tab(size_t tab/* = size_t(-1)*/) tab->update_changed_tree_ui(); // when tab == -1, it means we should show the last selected tab -#if ENABLE_LAYOUT_NO_RESTART m_tabpanel->SetSelection(tab == (size_t)(-1) ? m_last_selected_tab : (m_layout == ESettingsLayout::Dlg && tab != 0) ? tab - 1 : tab); -#else - m_tabpanel->SetSelection(tab == (size_t)(-1) ? m_last_selected_tab : (m_layout == slDlg && tab != 0) ? tab-1 : tab); -#endif // ENABLE_LAYOUT_NO_RESTART } // Set a camera direction, zoom to all objects. @@ -1734,34 +1604,6 @@ SettingsDialog::SettingsDialog(MainFrame* mainframe) SetIcon(wxIcon(var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); #endif // _WIN32 -#if !ENABLE_LAYOUT_NO_RESTART - // wxNB_NOPAGETHEME: Disable Windows Vista theme for the Notebook background. The theme performance is terrible on Windows 10 - // with multiple high resolution displays connected. - m_tabpanel = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxGetApp().get_min_size(), wxNB_TOP | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME); -#ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList - m_tabpanel->SetFont(Slic3r::GUI::wxGetApp().normal_font()); -#endif - - m_tabpanel->Bind(wxEVT_KEY_UP, [this](wxKeyEvent& evt) { - if ((evt.GetModifiers() & wxMOD_CONTROL) != 0) { - switch (evt.GetKeyCode()) { - case '1': { m_main_frame->select_tab(0); break; } - case '2': { m_main_frame->select_tab(1); break; } - case '3': { m_main_frame->select_tab(2); break; } - case '4': { m_main_frame->select_tab(3); break; } -#ifdef __APPLE__ - case 'f': -#else /* __APPLE__ */ - case WXK_CONTROL_F: -#endif /* __APPLE__ */ - case 'F': { m_main_frame->plater()->search(false); break; } - default:break; - } - } - }); -#endif // !ENABLE_LAYOUT_NO_RESTART - -#if ENABLE_LAYOUT_NO_RESTART this->Bind(wxEVT_SHOW, [this](wxShowEvent& evt) { auto key_up_handker = [this](wxKeyEvent& evt) { @@ -1791,13 +1633,9 @@ SettingsDialog::SettingsDialog(MainFrame* mainframe) m_tabpanel->Unbind(wxEVT_KEY_UP, key_up_handker); } }); -#endif // ENABLE_LAYOUT_NO_RESTART // initialize layout auto sizer = new wxBoxSizer(wxVERTICAL); -#if !ENABLE_LAYOUT_NO_RESTART - sizer->Add(m_tabpanel, 1, wxEXPAND); -#endif // !ENABLE_LAYOUT_NO_RESTART sizer->SetSizeHints(this); SetSizer(sizer); Fit(); diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 4514b8f50f..3c93f6b58d 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -55,11 +55,7 @@ class SettingsDialog : public DPIDialog public: SettingsDialog(MainFrame* mainframe); ~SettingsDialog() {} -#if ENABLE_LAYOUT_NO_RESTART void set_tabpanel(wxNotebook* tabpanel) { m_tabpanel = tabpanel; } -#else - wxNotebook* get_tabpanel() { return m_tabpanel; } -#endif // ENABLE_LAYOUT_NO_RESTART protected: void on_dpi_changed(const wxRect& suggested_rect) override; @@ -119,7 +115,6 @@ class MainFrame : public DPIFrame wxFileHistory m_recent_projects; -#if ENABLE_LAYOUT_NO_RESTART enum class ESettingsLayout { Unknown, @@ -129,13 +124,6 @@ class MainFrame : public DPIFrame }; ESettingsLayout m_layout{ ESettingsLayout::Unknown }; -#else - enum SettingsLayout { - slOld = 0, - slNew, - slDlg, - } m_layout; -#endif // ENABLE_LAYOUT_NO_RESTART protected: virtual void on_dpi_changed(const wxRect &suggested_rect); @@ -145,9 +133,7 @@ public: MainFrame(); ~MainFrame() = default; -#if ENABLE_LAYOUT_NO_RESTART void update_layout(); -#endif // ENABLE_LAYOUT_NO_RESTART // Called when closing the application and when switching the application language. void shutdown(); @@ -190,12 +176,8 @@ public: Plater* m_plater { nullptr }; wxNotebook* m_tabpanel { nullptr }; -#if ENABLE_LAYOUT_NO_RESTART SettingsDialog m_settings_dialog; wxWindow* m_plater_page{ nullptr }; -#else - SettingsDialog* m_settings_dialog { nullptr }; -#endif // ENABLE_LAYOUT_NO_RESTART wxProgressDialog* m_progress_dialog { nullptr }; std::shared_ptr m_statusbar; diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 02e4a899d2..4b5808e16a 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -234,30 +234,6 @@ void PreferencesDialog::accept() } } -#if !ENABLE_LAYOUT_NO_RESTART - if (m_settings_layout_changed) { - // the dialog needs to be destroyed before the call to recreate_gui() - // or sometimes the application crashes into wxDialogBase() destructor - // so we put it into an inner scope - wxMessageDialog dialog(nullptr, - _L("Switching the settings layout mode will trigger application restart.\n" - "You will lose content of the plater.") + "\n\n" + - _L("Do you want to proceed?"), - wxString(SLIC3R_APP_NAME) + " - " + _L("Switching the settings layout mode"), - wxICON_QUESTION | wxOK | wxCANCEL); - - if (dialog.ShowModal() == wxID_CANCEL) - { - int selection = app_config->get("old_settings_layout_mode") == "1" ? 0 : - app_config->get("new_settings_layout_mode") == "1" ? 1 : - app_config->get("dlg_settings_layout_mode") == "1" ? 2 : 0; - - m_layout_mode_box->SetSelection(selection); - return; - } - } -#endif // !ENABLE_LAYOUT_NO_RESTART - for (std::map::iterator it = m_values.begin(); it != m_values.end(); ++it) app_config->set(it->first, it->second); From 4bfb69eb1482238e6ee975e905ae49b4b3acb106 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 3 Aug 2020 12:20:46 +0200 Subject: [PATCH 230/503] Added an icon for 'ironing' category --- resources/icons/ironing.svg | 27 +++++++++++++++++++++++++++ src/slic3r/GUI/GUI_ObjectList.cpp | 4 ++-- 2 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 resources/icons/ironing.svg diff --git a/resources/icons/ironing.svg b/resources/icons/ironing.svg new file mode 100644 index 0000000000..94917d6bfe --- /dev/null +++ b/resources/icons/ironing.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index f3ff264ced..c10853f690 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -94,7 +94,7 @@ ObjectList::ObjectList(wxWindow* parent) : // ptFFF CATEGORY_ICON[L("Layers and Perimeters")] = create_scaled_bitmap("layers"); CATEGORY_ICON[L("Infill")] = create_scaled_bitmap("infill"); - CATEGORY_ICON[L("Ironing")] = create_scaled_bitmap("infill"); // FIXME when the ironing icon is available + CATEGORY_ICON[L("Ironing")] = create_scaled_bitmap("ironing"); CATEGORY_ICON[L("Support material")] = create_scaled_bitmap("support"); CATEGORY_ICON[L("Speed")] = create_scaled_bitmap("time"); CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap("funnel"); @@ -645,7 +645,7 @@ void ObjectList::msw_rescale_icons() // ptFFF CATEGORY_ICON[L("Layers and Perimeters")] = create_scaled_bitmap("layers"); CATEGORY_ICON[L("Infill")] = create_scaled_bitmap("infill"); - CATEGORY_ICON[L("Ironing")] = create_scaled_bitmap("infill"); // FIXME when the ironing icon is available + CATEGORY_ICON[L("Ironing")] = create_scaled_bitmap("ironing"); CATEGORY_ICON[L("Support material")] = create_scaled_bitmap("support"); CATEGORY_ICON[L("Speed")] = create_scaled_bitmap("time"); CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap("funnel"); From 5249b3e018f84d232a578e4992844da3ce1ba6fc Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Aug 2020 13:57:10 +0200 Subject: [PATCH 231/503] ENABLE_GCODE_VIEWER -> Estimated print time statistics moved from PrintStatistics to GCodeProcessor --- src/libslic3r/GCode.cpp | 5 +- src/libslic3r/GCode/GCodeProcessor.cpp | 41 ++- src/libslic3r/GCode/GCodeProcessor.hpp | 68 +++-- src/libslic3r/Print.hpp | 24 -- src/slic3r/GUI/GCodeViewer.cpp | 334 +++++++++++-------------- src/slic3r/GUI/GCodeViewer.hpp | 9 +- src/slic3r/GUI/MainFrame.cpp | 4 - 7 files changed, 227 insertions(+), 258 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 2dea4fb22b..880572b2bb 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -775,12 +775,9 @@ void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_ } #if ENABLE_GCODE_VIEWER - print->m_print_statistics.clear_time_estimates(); m_processor.process_file(path_tmp); - if (result != nullptr) { + if (result != nullptr) *result = std::move(m_processor.extract_result()); - m_processor.update_print_stats_estimated_times(print->m_print_statistics); - } #endif // ENABLE_GCODE_VIEWER GCodeTimeEstimator::PostProcessData normal_data = m_normal_time_estimator.get_post_process_data(); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index f3e07da881..5d9644457d 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -570,31 +570,13 @@ void GCodeProcessor::process_file(const std::string& filename) gcode_time.times.push_back({ CustomGCode::ColorChange, gcode_time.cache }); } + update_estimated_times_stats(); + #if ENABLE_GCODE_VIEWER_STATISTICS m_result.time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start_time).count(); #endif // ENABLE_GCODE_VIEWER_STATISTICS } -void GCodeProcessor::update_print_stats_estimated_times(PrintStatistics& print_statistics) -{ - print_statistics.estimated_normal_print_time = get_time(GCodeProcessor::ETimeMode::Normal); - print_statistics.estimated_normal_custom_gcode_print_times = get_custom_gcode_times(GCodeProcessor::ETimeMode::Normal, true); - print_statistics.estimated_normal_moves_times = get_moves_time(GCodeProcessor::ETimeMode::Normal); - print_statistics.estimated_normal_roles_times = get_roles_time(GCodeProcessor::ETimeMode::Normal); - if (m_time_processor.machines[static_cast(GCodeProcessor::ETimeMode::Stealth)].enabled) { - print_statistics.estimated_silent_print_time = get_time(GCodeProcessor::ETimeMode::Stealth); - print_statistics.estimated_silent_custom_gcode_print_times = get_custom_gcode_times(GCodeProcessor::ETimeMode::Stealth, true); - print_statistics.estimated_silent_moves_times = get_moves_time(GCodeProcessor::ETimeMode::Stealth); - print_statistics.estimated_silent_roles_times = get_roles_time(GCodeProcessor::ETimeMode::Stealth); - } - else { - print_statistics.estimated_silent_print_time = 0.0f; - print_statistics.estimated_silent_custom_gcode_print_times.clear(); - print_statistics.estimated_silent_moves_times.clear(); - print_statistics.estimated_silent_roles_times.clear(); - } -} - float GCodeProcessor::get_time(ETimeMode mode) const { return (mode < ETimeMode::Count) ? m_time_processor.machines[static_cast(mode)].time : 0.0f; @@ -620,7 +602,7 @@ std::vector>> GCodeProcesso return ret; } -std::vector> GCodeProcessor::get_moves_time(ETimeMode mode) const +std::vector> GCodeProcessor::get_moves_time(ETimeMode mode) const { std::vector> ret; if (mode < ETimeMode::Count) { @@ -1892,6 +1874,23 @@ void GCodeProcessor::simulate_st_synchronize(float additional_time) } } +void GCodeProcessor::update_estimated_times_stats() +{ + auto update_mode = [this](GCodeProcessor::ETimeMode mode) { + PrintEstimatedTimeStatistics::Mode& data = m_result.time_statistics.modes[static_cast(mode)]; + data.time = get_time(mode); + data.custom_gcode_times = get_custom_gcode_times(mode, true); + data.moves_times = get_moves_time(mode); + data.roles_times = get_roles_time(mode); + }; + + update_mode(GCodeProcessor::ETimeMode::Normal); + if (m_time_processor.machines[static_cast(GCodeProcessor::ETimeMode::Stealth)].enabled) + update_mode(GCodeProcessor::ETimeMode::Stealth); + else + m_result.time_statistics.modes[static_cast(GCodeProcessor::ETimeMode::Stealth)].reset(); +} + } /* namespace Slic3r */ #endif // ENABLE_GCODE_VIEWER diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index bad04100ef..526300e55a 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -14,7 +14,54 @@ namespace Slic3r { - struct PrintStatistics; + enum class EMoveType : unsigned char + { + Noop, + Retract, + Unretract, + Tool_change, + Color_change, + Pause_Print, + Custom_GCode, + Travel, + Extrude, + Count + }; + + struct PrintEstimatedTimeStatistics + { + enum class ETimeMode : unsigned char + { + Normal, + Stealth, + Count + }; + + struct Mode + { + float time; + std::vector>> custom_gcode_times; + std::vector> moves_times; + std::vector> roles_times; + + void reset() { + time = 0.0f; + custom_gcode_times.clear(); + moves_times.clear(); + roles_times.clear(); + } + }; + + std::array(ETimeMode::Count)> modes; + + PrintEstimatedTimeStatistics() { reset(); } + + void reset() { + for (auto m : modes) { + m.reset(); + } + } + }; class GCodeProcessor { @@ -59,20 +106,6 @@ namespace Slic3r { }; public: - enum class EMoveType : unsigned char - { - Noop, - Retract, - Unretract, - Tool_change, - Color_change, - Pause_Print, - Custom_GCode, - Travel, - Extrude, - Count - }; - struct FeedrateProfile { float entry{ 0.0f }; // mm/s @@ -215,6 +248,8 @@ namespace Slic3r { Pointfs bed_shape; std::vector extruder_colors; #endif // ENABLE_GCODE_VIEWER_AS_STATE + PrintEstimatedTimeStatistics time_statistics; + #if ENABLE_GCODE_VIEWER_STATISTICS long long time{ 0 }; void reset() @@ -297,7 +332,6 @@ namespace Slic3r { // Process the gcode contained in the file with the given filename void process_file(const std::string& filename); - void update_print_stats_estimated_times(PrintStatistics& print_statistics); float get_time(ETimeMode mode) const; std::string get_time_dhm(ETimeMode mode) const; std::vector>> get_custom_gcode_times(ETimeMode mode, bool include_remaining) const; @@ -422,6 +456,8 @@ namespace Slic3r { // Simulates firmware st_synchronize() call void simulate_st_synchronize(float additional_time = 0.0f); + + void update_estimated_times_stats(); }; } /* namespace Slic3r */ diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 22af935d3e..62c00aa88d 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -304,22 +304,12 @@ struct PrintStatistics { PrintStatistics() { clear(); } #if ENABLE_GCODE_VIEWER - float estimated_normal_print_time; - float estimated_silent_print_time; #if ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR std::string estimated_normal_print_time_str; std::string estimated_silent_print_time_str; -#endif // ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR - std::vector>> estimated_normal_custom_gcode_print_times; - std::vector>> estimated_silent_custom_gcode_print_times; -#if ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR std::vector>> estimated_normal_custom_gcode_print_times_str; std::vector>> estimated_silent_custom_gcode_print_times_str; #endif // ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR - std::vector> estimated_normal_moves_times; - std::vector> estimated_silent_moves_times; - std::vector> estimated_normal_roles_times; - std::vector> estimated_silent_roles_times; #else std::string estimated_normal_print_time; std::string estimated_silent_print_time; @@ -344,7 +334,6 @@ struct PrintStatistics void clear() { #if ENABLE_GCODE_VIEWER - clear_time_estimates(); #if ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR estimated_normal_print_time_str.clear(); estimated_silent_print_time_str.clear(); @@ -366,19 +355,6 @@ struct PrintStatistics total_wipe_tower_filament = 0.; filament_stats.clear(); } - -#if ENABLE_GCODE_VIEWER - void clear_time_estimates() { - estimated_normal_print_time = 0.0f; - estimated_silent_print_time = 0.0f; - estimated_normal_custom_gcode_print_times.clear(); - estimated_silent_custom_gcode_print_times.clear(); - estimated_normal_moves_times.clear(); - estimated_silent_moves_times.clear(); - estimated_normal_roles_times.clear(); - estimated_silent_roles_times.clear(); - } -#endif //ENABLE_GCODE_VIEWER }; typedef std::vector PrintObjectPtrs; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 8d4fb6f4cb..4faa0486ff 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -32,12 +32,12 @@ namespace Slic3r { namespace GUI { -static unsigned char buffer_id(GCodeProcessor::EMoveType type) { - return static_cast(type) - static_cast(GCodeProcessor::EMoveType::Retract); +static unsigned char buffer_id(EMoveType type) { + return static_cast(type) - static_cast(EMoveType::Retract); } -static GCodeProcessor::EMoveType buffer_type(unsigned char id) { - return static_cast(static_cast(GCodeProcessor::EMoveType::Retract) + id); +static EMoveType buffer_type(unsigned char id) { + return static_cast(static_cast(EMoveType::Retract) + id); } std::array decode_color(const std::string& color) { @@ -92,19 +92,19 @@ bool GCodeViewer::Path::matches(const GCodeProcessor::MoveVertex& move) const { switch (move.type) { - case GCodeProcessor::EMoveType::Tool_change: - case GCodeProcessor::EMoveType::Color_change: - case GCodeProcessor::EMoveType::Pause_Print: - case GCodeProcessor::EMoveType::Custom_GCode: - case GCodeProcessor::EMoveType::Retract: - case GCodeProcessor::EMoveType::Unretract: - case GCodeProcessor::EMoveType::Extrude: + case EMoveType::Tool_change: + case EMoveType::Color_change: + case EMoveType::Pause_Print: + case EMoveType::Custom_GCode: + case EMoveType::Retract: + case EMoveType::Unretract: + case EMoveType::Extrude: { return type == move.type && role == move.extrusion_role && height == move.height && width == move.width && feedrate == move.feedrate && fan_speed == move.fan_speed && volumetric_rate == move.volumetric_rate() && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id; } - case GCodeProcessor::EMoveType::Travel: + case EMoveType::Travel: { return type == move.type && feedrate == move.feedrate && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id; } @@ -198,9 +198,7 @@ void GCodeViewer::SequentialView::Marker::render() const ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::SetNextWindowBgAlpha(0.25f); imgui.begin(std::string("ToolPosition"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(_u8L("Tool position") + ":"); - ImGui::PopStyleColor(); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Tool position") + ":"); ImGui::SameLine(); char buf[1024]; sprintf(buf, "X: %.2f, Y: %.2f, Z: %.2f", m_world_position(0), m_world_position(1), m_world_position(2)); @@ -274,18 +272,18 @@ bool GCodeViewer::init() switch (buffer_type(i)) { default: { break; } - case GCodeProcessor::EMoveType::Tool_change: - case GCodeProcessor::EMoveType::Color_change: - case GCodeProcessor::EMoveType::Pause_Print: - case GCodeProcessor::EMoveType::Custom_GCode: - case GCodeProcessor::EMoveType::Retract: - case GCodeProcessor::EMoveType::Unretract: + case EMoveType::Tool_change: + case EMoveType::Color_change: + case EMoveType::Pause_Print: + case EMoveType::Custom_GCode: + case EMoveType::Retract: + case EMoveType::Unretract: { m_buffers[i].vertices.format = VBuffer::EFormat::Position; break; } - case GCodeProcessor::EMoveType::Extrude: - case GCodeProcessor::EMoveType::Travel: + case EMoveType::Extrude: + case EMoveType::Travel: { m_buffers[i].vertices.format = VBuffer::EFormat::PositionNormal; break; @@ -293,7 +291,7 @@ bool GCodeViewer::init() } } - set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Extrude, true); + set_toolpath_move_type_visible(EMoveType::Extrude, true); m_sequential_view.marker.init(); init_shaders(); @@ -340,6 +338,8 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& wxGetApp().plater()->set_bed_shape(bed_shape, "", "", true); } #endif // ENABLE_GCODE_VIEWER_AS_STATE + + m_time_statistics = gcode_result.time_statistics; } void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std::vector& str_tool_colors) @@ -371,7 +371,7 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: switch (curr.type) { - case GCodeProcessor::EMoveType::Extrude: + case EMoveType::Extrude: { m_extrusions.ranges.height.update_from(curr.height); m_extrusions.ranges.width.update_from(curr.width); @@ -379,7 +379,7 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: m_extrusions.ranges.volumetric_rate.update_from(curr.volumetric_rate()); [[fallthrough]]; } - case GCodeProcessor::EMoveType::Travel: + case EMoveType::Travel: { if (m_buffers[buffer_id(curr.type)].visible) m_extrusions.ranges.feedrate.update_from(curr.feedrate); @@ -415,6 +415,7 @@ void GCodeViewer::reset() m_layers_zs = std::vector(); m_layers_z_range = { 0.0, 0.0 }; m_roles = std::vector(); + m_time_statistics.reset(); #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.reset_all(); @@ -452,13 +453,13 @@ void GCodeViewer::render() const #endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR } -bool GCodeViewer::is_toolpath_move_type_visible(GCodeProcessor::EMoveType type) const +bool GCodeViewer::is_toolpath_move_type_visible(EMoveType type) const { size_t id = static_cast(buffer_id(type)); return (id < m_buffers.size()) ? m_buffers[id].visible : false; } -void GCodeViewer::set_toolpath_move_type_visible(GCodeProcessor::EMoveType type, bool visible) +void GCodeViewer::set_toolpath_move_type_visible(EMoveType type, bool visible) { size_t id = static_cast(buffer_id(type)); if (id < m_buffers.size()) @@ -472,13 +473,13 @@ unsigned int GCodeViewer::get_options_visibility_flags() const }; unsigned int flags = 0; - flags = set_flag(flags, static_cast(Preview::OptionType::Travel), is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Travel)); - flags = set_flag(flags, static_cast(Preview::OptionType::Retractions), is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Retract)); - flags = set_flag(flags, static_cast(Preview::OptionType::Unretractions), is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Unretract)); - flags = set_flag(flags, static_cast(Preview::OptionType::ToolChanges), is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Tool_change)); - flags = set_flag(flags, static_cast(Preview::OptionType::ColorChanges), is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Color_change)); - flags = set_flag(flags, static_cast(Preview::OptionType::PausePrints), is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Pause_Print)); - flags = set_flag(flags, static_cast(Preview::OptionType::CustomGCodes), is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Custom_GCode)); + flags = set_flag(flags, static_cast(Preview::OptionType::Travel), is_toolpath_move_type_visible(EMoveType::Travel)); + flags = set_flag(flags, static_cast(Preview::OptionType::Retractions), is_toolpath_move_type_visible(EMoveType::Retract)); + flags = set_flag(flags, static_cast(Preview::OptionType::Unretractions), is_toolpath_move_type_visible(EMoveType::Unretract)); + flags = set_flag(flags, static_cast(Preview::OptionType::ToolChanges), is_toolpath_move_type_visible(EMoveType::Tool_change)); + flags = set_flag(flags, static_cast(Preview::OptionType::ColorChanges), is_toolpath_move_type_visible(EMoveType::Color_change)); + flags = set_flag(flags, static_cast(Preview::OptionType::PausePrints), is_toolpath_move_type_visible(EMoveType::Pause_Print)); + flags = set_flag(flags, static_cast(Preview::OptionType::CustomGCodes), is_toolpath_move_type_visible(EMoveType::Custom_GCode)); flags = set_flag(flags, static_cast(Preview::OptionType::Shells), m_shells.visible); flags = set_flag(flags, static_cast(Preview::OptionType::ToolMarker), m_sequential_view.marker.is_visible()); flags = set_flag(flags, static_cast(Preview::OptionType::Legend), is_legend_enabled()); @@ -494,13 +495,13 @@ void GCodeViewer::set_options_visibility_from_flags(unsigned int flags) return (flags & (1 << flag)) != 0; }; - set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Travel, is_flag_set(static_cast(Preview::OptionType::Travel))); - set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Retract, is_flag_set(static_cast(Preview::OptionType::Retractions))); - set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Unretract, is_flag_set(static_cast(Preview::OptionType::Unretractions))); - set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Tool_change, is_flag_set(static_cast(Preview::OptionType::ToolChanges))); - set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Color_change, is_flag_set(static_cast(Preview::OptionType::ColorChanges))); - set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Pause_Print, is_flag_set(static_cast(Preview::OptionType::PausePrints))); - set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Custom_GCode, is_flag_set(static_cast(Preview::OptionType::CustomGCodes))); + set_toolpath_move_type_visible(EMoveType::Travel, is_flag_set(static_cast(Preview::OptionType::Travel))); + set_toolpath_move_type_visible(EMoveType::Retract, is_flag_set(static_cast(Preview::OptionType::Retractions))); + set_toolpath_move_type_visible(EMoveType::Unretract, is_flag_set(static_cast(Preview::OptionType::Unretractions))); + set_toolpath_move_type_visible(EMoveType::Tool_change, is_flag_set(static_cast(Preview::OptionType::ToolChanges))); + set_toolpath_move_type_visible(EMoveType::Color_change, is_flag_set(static_cast(Preview::OptionType::ColorChanges))); + set_toolpath_move_type_visible(EMoveType::Pause_Print, is_flag_set(static_cast(Preview::OptionType::PausePrints))); + set_toolpath_move_type_visible(EMoveType::Custom_GCode, is_flag_set(static_cast(Preview::OptionType::CustomGCodes))); m_shells.visible = is_flag_set(static_cast(Preview::OptionType::Shells)); m_sequential_view.marker.set_visible(is_flag_set(static_cast(Preview::OptionType::ToolMarker))); enable_legend(is_flag_set(static_cast(Preview::OptionType::Legend))); @@ -537,7 +538,7 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const wxBusyCursor busy; // the data needed is contained into the Extrude TBuffer - const TBuffer& buffer = m_buffers[buffer_id(GCodeProcessor::EMoveType::Extrude)]; + const TBuffer& buffer = m_buffers[buffer_id(EMoveType::Extrude)]; if (buffer.vertices.id == 0 || buffer.indices.id == 0) return; @@ -805,21 +806,21 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const void GCodeViewer::init_shaders() { - unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); - unsigned char end_id = buffer_id(GCodeProcessor::EMoveType::Count); + unsigned char begin_id = buffer_id(EMoveType::Retract); + unsigned char end_id = buffer_id(EMoveType::Count); bool is_glsl_120 = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20); for (unsigned char i = begin_id; i < end_id; ++i) { switch (buffer_type(i)) { - case GCodeProcessor::EMoveType::Tool_change: { m_buffers[i].shader = is_glsl_120 ? "options_120_flat" : "options_110"; break; } - case GCodeProcessor::EMoveType::Color_change: { m_buffers[i].shader = is_glsl_120 ? "options_120_flat" : "options_110"; break; } - case GCodeProcessor::EMoveType::Pause_Print: { m_buffers[i].shader = is_glsl_120 ? "options_120_flat" : "options_110"; break; } - case GCodeProcessor::EMoveType::Custom_GCode: { m_buffers[i].shader = is_glsl_120 ? "options_120_flat" : "options_110"; break; } - case GCodeProcessor::EMoveType::Retract: { m_buffers[i].shader = is_glsl_120 ? "options_120_flat" : "options_110"; break; } - case GCodeProcessor::EMoveType::Unretract: { m_buffers[i].shader = is_glsl_120 ? "options_120_flat" : "options_110"; break; } - case GCodeProcessor::EMoveType::Extrude: { m_buffers[i].shader = "toolpaths"; break; } - case GCodeProcessor::EMoveType::Travel: { m_buffers[i].shader = "toolpaths"; break; } + case EMoveType::Tool_change: { m_buffers[i].shader = is_glsl_120 ? "options_120_flat" : "options_110"; break; } + case EMoveType::Color_change: { m_buffers[i].shader = is_glsl_120 ? "options_120_flat" : "options_110"; break; } + case EMoveType::Pause_Print: { m_buffers[i].shader = is_glsl_120 ? "options_120_flat" : "options_110"; break; } + case EMoveType::Custom_GCode: { m_buffers[i].shader = is_glsl_120 ? "options_120_flat" : "options_110"; break; } + case EMoveType::Retract: { m_buffers[i].shader = is_glsl_120 ? "options_120_flat" : "options_110"; break; } + case EMoveType::Unretract: { m_buffers[i].shader = is_glsl_120 ? "options_120_flat" : "options_110"; break; } + case EMoveType::Extrude: { m_buffers[i].shader = "toolpaths"; break; } + case EMoveType::Travel: { m_buffers[i].shader = "toolpaths"; break; } default: { break; } } } @@ -846,14 +847,17 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) m_paths_bounding_box.merge(move.position.cast()); else { #endif // ENABLE_GCODE_VIEWER_AS_STATE - if (move.type == GCodeProcessor::EMoveType::Extrude && move.width != 0.0f && move.height != 0.0f) + if (move.type == EMoveType::Extrude && move.width != 0.0f && move.height != 0.0f) m_paths_bounding_box.merge(move.position.cast()); #if ENABLE_GCODE_VIEWER_AS_STATE } #endif // ENABLE_GCODE_VIEWER_AS_STATE } - // max bounding box + // add origin + m_paths_bounding_box.merge(Vec3d::Zero()); + + // max bounding box (account for tool marker) m_max_bounding_box = m_paths_bounding_box; m_max_bounding_box.merge(m_paths_bounding_box.max + m_sequential_view.marker.get_bounding_box().size()[2] * Vec3d::UnitZ()); @@ -875,12 +879,12 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) switch (curr.type) { - case GCodeProcessor::EMoveType::Tool_change: - case GCodeProcessor::EMoveType::Color_change: - case GCodeProcessor::EMoveType::Pause_Print: - case GCodeProcessor::EMoveType::Custom_GCode: - case GCodeProcessor::EMoveType::Retract: - case GCodeProcessor::EMoveType::Unretract: + case EMoveType::Tool_change: + case EMoveType::Color_change: + case EMoveType::Pause_Print: + case EMoveType::Custom_GCode: + case EMoveType::Retract: + case EMoveType::Unretract: { for (int j = 0; j < 3; ++j) { buffer_vertices.push_back(curr.position[j]); @@ -889,8 +893,8 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) buffer_indices.push_back(static_cast(buffer_indices.size())); break; } - case GCodeProcessor::EMoveType::Extrude: - case GCodeProcessor::EMoveType::Travel: + case EMoveType::Extrude: + case EMoveType::Travel: { // x component of the normal to the current segment (the normal is parallel to the XY plane) float normal_x = (curr.position - prev.position).normalized()[1]; @@ -979,7 +983,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) // layers zs / roles / extruder ids / cp color ids -> extract from result for (size_t i = 0; i < m_vertices_count; ++i) { const GCodeProcessor::MoveVertex& move = gcode_result.moves[i]; - if (move.type == GCodeProcessor::EMoveType::Extrude) + if (move.type == EMoveType::Extrude) m_layers_zs.emplace_back(static_cast(move.position[2])); m_extruder_ids.emplace_back(move.extruder_id); @@ -1127,14 +1131,14 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool for (size_t i = 0; i < buffer.paths.size(); ++i) { const Path& path = buffer.paths[i]; - if (path.type == GCodeProcessor::EMoveType::Travel) { + if (path.type == EMoveType::Travel) { if (!is_travel_in_z_range(i)) continue; } else if (!is_in_z_range(path)) continue; - if (path.type == GCodeProcessor::EMoveType::Extrude && !is_visible(path)) + if (path.type == EMoveType::Extrude && !is_visible(path)) continue; // store valid path @@ -1156,7 +1160,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool for (const Path& path : buffer.paths) { if (path.first.s_id <= m_sequential_view.current.last && m_sequential_view.current.last <= path.last.s_id) { size_t offset = m_sequential_view.current.last - path.first.s_id; - if (offset > 0 && (path.type == GCodeProcessor::EMoveType::Travel || path.type == GCodeProcessor::EMoveType::Extrude)) + if (offset > 0 && (path.type == EMoveType::Travel || path.type == EMoveType::Extrude)) offset = 1 + 2 * (offset - 1); offset += path.first.i_id; @@ -1182,8 +1186,8 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool Color color; switch (path.type) { - case GCodeProcessor::EMoveType::Extrude: { color = extrusion_color(path); break; } - case GCodeProcessor::EMoveType::Travel: { color = (m_view_type == EViewType::Feedrate || m_view_type == EViewType::Tool || m_view_type == EViewType::ColorPrint) ? extrusion_color(path) : travel_color(path); break; } + case EMoveType::Extrude: { color = extrusion_color(path); break; } + case EMoveType::Travel: { color = (m_view_type == EViewType::Feedrate || m_view_type == EViewType::Tool || m_view_type == EViewType::ColorPrint) ? extrusion_color(path) : travel_color(path); break; } default: { color = { 0.0f, 0.0f, 0.0f }; break; } } @@ -1195,7 +1199,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool } unsigned int size = std::min(m_sequential_view.current.last, path.last.s_id) - std::max(m_sequential_view.current.first, path.first.s_id) + 1; - if (path.type == GCodeProcessor::EMoveType::Extrude || path.type == GCodeProcessor::EMoveType::Travel) + if (path.type == EMoveType::Extrude || path.type == EMoveType::Travel) size = 2 * (size - 1); it->sizes.push_back(size); @@ -1278,8 +1282,8 @@ void GCodeViewer::render_toolpaths() const glsafe(::glLineWidth(static_cast(line_width(zoom)))); - unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); - unsigned char end_id = buffer_id(GCodeProcessor::EMoveType::Count); + unsigned char begin_id = buffer_id(EMoveType::Retract); + unsigned char end_id = buffer_id(EMoveType::Count); for (unsigned char i = begin_id; i < end_id; ++i) { const TBuffer& buffer = m_buffers[i]; @@ -1302,14 +1306,14 @@ void GCodeViewer::render_toolpaths() const switch (buffer_type(i)) { default: { break; } - case GCodeProcessor::EMoveType::Tool_change: { render_as_points(buffer, EOptionsColors::ToolChanges, *shader); break; } - case GCodeProcessor::EMoveType::Color_change: { render_as_points(buffer, EOptionsColors::ColorChanges, *shader); break; } - case GCodeProcessor::EMoveType::Pause_Print: { render_as_points(buffer, EOptionsColors::PausePrints, *shader); break; } - case GCodeProcessor::EMoveType::Custom_GCode: { render_as_points(buffer, EOptionsColors::CustomGCodes, *shader); break; } - case GCodeProcessor::EMoveType::Retract: { render_as_points(buffer, EOptionsColors::Retractions, *shader); break; } - case GCodeProcessor::EMoveType::Unretract: { render_as_points(buffer, EOptionsColors::Unretractions, *shader); break; } - case GCodeProcessor::EMoveType::Extrude: - case GCodeProcessor::EMoveType::Travel: + case EMoveType::Tool_change: { render_as_points(buffer, EOptionsColors::ToolChanges, *shader); break; } + case EMoveType::Color_change: { render_as_points(buffer, EOptionsColors::ColorChanges, *shader); break; } + case EMoveType::Pause_Print: { render_as_points(buffer, EOptionsColors::PausePrints, *shader); break; } + case EMoveType::Custom_GCode: { render_as_points(buffer, EOptionsColors::CustomGCodes, *shader); break; } + case EMoveType::Retract: { render_as_points(buffer, EOptionsColors::Retractions, *shader); break; } + case EMoveType::Unretract: { render_as_points(buffer, EOptionsColors::Unretractions, *shader); break; } + case EMoveType::Extrude: + case EMoveType::Travel: { #if ENABLE_GCODE_VIEWER_SHADERS_EDITOR std::array light_intensity = { @@ -1406,7 +1410,7 @@ void GCodeViewer::render_legend() const draw_list->AddCircleFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); #else ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); - if (m_buffers[buffer_id(GCodeProcessor::EMoveType::Retract)].shader == "options_120_flat") { + if (m_buffers[buffer_id(EMoveType::Retract)].shader == "options_120_flat") { draw_list->AddCircleFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); float radius = 0.5f * icon_size; @@ -1623,7 +1627,7 @@ void GCodeViewer::render_legend() const } // travel paths - if (m_buffers[buffer_id(GCodeProcessor::EMoveType::Travel)].visible) { + if (m_buffers[buffer_id(EMoveType::Travel)].visible) { switch (m_view_type) { case EViewType::Feedrate: @@ -1649,20 +1653,20 @@ void GCodeViewer::render_legend() const } auto any_option_available = [this]() { - auto available = [this](GCodeProcessor::EMoveType type) { + auto available = [this](EMoveType type) { const TBuffer& buffer = m_buffers[buffer_id(type)]; return buffer.visible && buffer.indices.count > 0; }; - return available(GCodeProcessor::EMoveType::Color_change) || - available(GCodeProcessor::EMoveType::Custom_GCode) || - available(GCodeProcessor::EMoveType::Pause_Print) || - available(GCodeProcessor::EMoveType::Retract) || - available(GCodeProcessor::EMoveType::Tool_change) || - available(GCodeProcessor::EMoveType::Unretract); + return available(EMoveType::Color_change) || + available(EMoveType::Custom_GCode) || + available(EMoveType::Pause_Print) || + available(EMoveType::Retract) || + available(EMoveType::Tool_change) || + available(EMoveType::Unretract); }; - auto add_option = [this, append_item](GCodeProcessor::EMoveType move_type, EOptionsColors color, const std::string& text) { + auto add_option = [this, append_item](EMoveType move_type, EOptionsColors color, const std::string& text) { const TBuffer& buffer = m_buffers[buffer_id(move_type)]; if (buffer.visible && buffer.indices.count > 0) #if ENABLE_GCODE_VIEWER_SHADERS_EDITOR @@ -1679,12 +1683,12 @@ void GCodeViewer::render_legend() const imgui.title(_u8L("Options")); // items - add_option(GCodeProcessor::EMoveType::Retract, EOptionsColors::Retractions, _u8L("Retractions")); - add_option(GCodeProcessor::EMoveType::Unretract, EOptionsColors::Unretractions, _u8L("Unretractions")); - add_option(GCodeProcessor::EMoveType::Tool_change, EOptionsColors::ToolChanges, _u8L("Tool changes")); - add_option(GCodeProcessor::EMoveType::Color_change, EOptionsColors::ColorChanges, _u8L("Color changes")); - add_option(GCodeProcessor::EMoveType::Pause_Print, EOptionsColors::PausePrints, _u8L("Pause prints")); - add_option(GCodeProcessor::EMoveType::Custom_GCode, EOptionsColors::CustomGCodes, _u8L("Custom GCodes")); + add_option(EMoveType::Retract, EOptionsColors::Retractions, _u8L("Retractions")); + add_option(EMoveType::Unretract, EOptionsColors::Unretractions, _u8L("Unretractions")); + add_option(EMoveType::Tool_change, EOptionsColors::ToolChanges, _u8L("Tool changes")); + add_option(EMoveType::Color_change, EOptionsColors::ColorChanges, _u8L("Color changes")); + add_option(EMoveType::Pause_Print, EOptionsColors::PausePrints, _u8L("Pause prints")); + add_option(EMoveType::Custom_GCode, EOptionsColors::CustomGCodes, _u8L("Custom GCodes")); } imgui.end(); @@ -1700,10 +1704,6 @@ void GCodeViewer::render_time_estimate() const return; } - const PrintStatistics& ps = wxGetApp().plater()->fff_print().print_statistics(); - if (ps.estimated_normal_print_time <= 0.0f && ps.estimated_silent_print_time <= 0.0f) - return; - ImGuiWrapper& imgui = *wxGetApp().imgui(); #if ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG @@ -1737,19 +1737,17 @@ void GCodeViewer::render_time_estimate() const using PartialTimes = std::vector; auto append_headers = [&imgui](const Headers& headers, const ColumnOffsets& offsets) { - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(headers[0]); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, headers[0]); ImGui::SameLine(offsets[0]); - imgui.text(headers[1]); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, headers[1]); ImGui::SameLine(offsets[1]); - imgui.text(headers[2]); - ImGui::PopStyleColor(); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, headers[2]); ImGui::Separator(); }; auto append_mode = [this, &imgui, append_headers](float total_time, const PartialTimes& items, const Headers& partial_times_headers, - const std::vector>& moves_time, + const std::vector>& moves_time, const Headers& moves_headers, const std::vector>& roles_time, const Headers& roles_headers) { @@ -1775,9 +1773,7 @@ void GCodeViewer::render_time_estimate() const return ret; }; auto append_color = [this, &imgui](const Color& color1, const Color& color2, ColumnOffsets& offsets, const Times& times) { - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(_u8L("Color change")); - ImGui::PopStyleColor(); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Color change")); ImGui::SameLine(); float icon_size = ImGui::GetTextLineHeight(); @@ -1805,9 +1801,7 @@ void GCodeViewer::render_time_estimate() const { case PartialTime::EType::Print: { - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(_u8L("Print")); - ImGui::PopStyleColor(); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Print")); ImGui::SameLine(offsets[0]); imgui.text(short_time(get_time_dhms(item.times.second))); ImGui::SameLine(offsets[1]); @@ -1816,9 +1810,7 @@ void GCodeViewer::render_time_estimate() const } case PartialTime::EType::Pause: { - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(_u8L("Pause")); - ImGui::PopStyleColor(); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Pause")); ImGui::SameLine(offsets[0]); imgui.text(short_time(get_time_dhms(item.times.second - item.times.first))); break; @@ -1832,26 +1824,24 @@ void GCodeViewer::render_time_estimate() const } }; - auto move_type_label = [](GCodeProcessor::EMoveType type) { + auto move_type_label = [](EMoveType type) { switch (type) { - case GCodeProcessor::EMoveType::Noop: { return _u8L("Noop"); } - case GCodeProcessor::EMoveType::Retract: { return _u8L("Retraction"); } - case GCodeProcessor::EMoveType::Unretract: { return _u8L("Unretraction"); } - case GCodeProcessor::EMoveType::Tool_change: { return _u8L("Tool change"); } - case GCodeProcessor::EMoveType::Color_change: { return _u8L("Color change"); } - case GCodeProcessor::EMoveType::Pause_Print: { return _u8L("Pause print"); } - case GCodeProcessor::EMoveType::Custom_GCode: { return _u8L("Custom GCode"); } - case GCodeProcessor::EMoveType::Travel: { return _u8L("Travel"); } - case GCodeProcessor::EMoveType::Extrude: { return _u8L("Extrusion"); } - default: { return _u8L("Unknown"); } + case EMoveType::Noop: { return _u8L("Noop"); } + case EMoveType::Retract: { return _u8L("Retraction"); } + case EMoveType::Unretract: { return _u8L("Unretraction"); } + case EMoveType::Tool_change: { return _u8L("Tool change"); } + case EMoveType::Color_change: { return _u8L("Color change"); } + case EMoveType::Pause_Print: { return _u8L("Pause print"); } + case EMoveType::Custom_GCode: { return _u8L("Custom GCode"); } + case EMoveType::Travel: { return _u8L("Travel"); } + case EMoveType::Extrude: { return _u8L("Extrusion"); } + default: { return _u8L("Unknown"); } } }; auto append_time_item = [&imgui] (const std::string& label, float time, float percentage, const ImVec4& color, const ColumnOffsets& offsets) { - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(label); - ImGui::PopStyleColor(); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); ImGui::SameLine(offsets[0]); imgui.text(short_time(get_time_dhms(time))); ImGui::SameLine(offsets[1]); @@ -1868,7 +1858,7 @@ void GCodeViewer::render_time_estimate() const }; auto append_move_times = [this, &imgui, move_type_label, append_headers, append_time_item](float total_time, - const std::vector>& moves_time, + const std::vector>& moves_time, const Headers& headers, const ColumnOffsets& offsets) { if (moves_time.empty()) @@ -1879,7 +1869,7 @@ void GCodeViewer::render_time_estimate() const append_headers(headers, offsets); - std::vector> sorted_moves_time(moves_time); + std::vector> sorted_moves_time(moves_time); std::sort(sorted_moves_time.begin(), sorted_moves_time.end(), [](const auto& p1, const auto& p2) { return p2.second < p1.second; }); for (const auto& [type, time] : sorted_moves_time) { @@ -1909,7 +1899,7 @@ void GCodeViewer::render_time_estimate() const }; auto calc_common_offsets = [move_type_label]( - const std::vector>& moves_time, const Headers& moves_headers, + const std::vector>& moves_time, const Headers& moves_headers, const std::vector>& roles_time, const Headers& roles_headers) { ColumnOffsets ret = { std::max(ImGui::CalcTextSize(moves_headers[0].c_str()).x, ImGui::CalcTextSize(roles_headers[0].c_str()).x), std::max(ImGui::CalcTextSize(moves_headers[1].c_str()).x, ImGui::CalcTextSize(roles_headers[1].c_str()).x) }; @@ -1930,9 +1920,7 @@ void GCodeViewer::render_time_estimate() const return ret; }; - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(_u8L("Time") + ":"); - ImGui::PopStyleColor(); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Time") + ":"); ImGui::SameLine(); imgui.text(short_time(get_time_dhms(total_time))); append_partial_times(items, partial_times_headers); @@ -2032,21 +2020,23 @@ void GCodeViewer::render_time_estimate() const // mode tabs ImGui::BeginTabBar("mode_tabs"); - if (ps.estimated_normal_print_time > 0.0f) { + const PrintEstimatedTimeStatistics::Mode& normal_mode = m_time_statistics.modes[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Normal)]; + if (normal_mode.time > 0.0f) { if (ImGui::BeginTabItem(_u8L("Normal").c_str())) { - append_mode(ps.estimated_normal_print_time, - generate_partial_times(ps.estimated_normal_custom_gcode_print_times), partial_times_headers, - ps.estimated_normal_moves_times, moves_headers, - ps.estimated_normal_roles_times, roles_headers); + append_mode(normal_mode.time, + generate_partial_times(normal_mode.custom_gcode_times), partial_times_headers, + normal_mode.moves_times, moves_headers, + normal_mode.roles_times, roles_headers); ImGui::EndTabItem(); } } - if (ps.estimated_silent_print_time > 0.0f) { + const PrintEstimatedTimeStatistics::Mode& stealth_mode = m_time_statistics.modes[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Stealth)]; + if (stealth_mode.time > 0.0f) { if (ImGui::BeginTabItem(_u8L("Stealth").c_str())) { - append_mode(ps.estimated_silent_print_time, - generate_partial_times(ps.estimated_silent_custom_gcode_print_times), partial_times_headers, - ps.estimated_silent_moves_times, moves_headers, - ps.estimated_silent_roles_times, roles_headers); + append_mode(stealth_mode.time, + generate_partial_times(stealth_mode.custom_gcode_times), partial_times_headers, + stealth_mode.moves_times, moves_headers, + stealth_mode.roles_times, roles_headers); ImGui::EndTabItem(); } } @@ -2082,93 +2072,67 @@ void GCodeViewer::render_statistics() const imgui.begin(std::string("GCodeViewer Statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize); ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(std::string("GCodeProcessor time:")); - ImGui::PopStyleColor(); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("GCodeProcessor time:")); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.results_time) + " ms"); ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(std::string("Load time:")); - ImGui::PopStyleColor(); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Load time:")); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.load_time) + " ms"); - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(std::string("Resfresh time:")); - ImGui::PopStyleColor(); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Refresh time:")); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.refresh_time) + " ms"); - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(std::string("Resfresh paths time:")); - ImGui::PopStyleColor(); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Refresh paths time:")); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.refresh_paths_time) + " ms"); ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(std::string("Multi GL_POINTS calls:")); - ImGui::PopStyleColor(); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Multi GL_POINTS calls:")); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.gl_multi_points_calls_count)); - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(std::string("Multi GL_LINE_STRIP calls:")); - ImGui::PopStyleColor(); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Multi GL_LINE_STRIP calls:")); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.gl_multi_line_strip_calls_count)); ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(std::string("GCodeProcessor results:")); - ImGui::PopStyleColor(); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("GCodeProcessor results:")); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.results_size) + " bytes"); ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(std::string("Paths CPU:")); - ImGui::PopStyleColor(); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Paths CPU:")); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.paths_size) + " bytes"); - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(std::string("Render paths CPU:")); - ImGui::PopStyleColor(); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Render paths CPU:")); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.render_paths_size) + " bytes"); ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(std::string("Vertices GPU:")); - ImGui::PopStyleColor(); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Vertices GPU:")); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.vertices_gpu_size) + " bytes"); - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(std::string("Indices GPU:")); - ImGui::PopStyleColor(); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Vertices GPU:")); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.indices_gpu_size) + " bytes"); ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(std::string("Travel segments:")); - ImGui::PopStyleColor(); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Vertices GPU:")); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.travel_segments_count)); - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); - imgui.text(std::string("Extrude segments:")); - ImGui::PopStyleColor(); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Extrude segments:")); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.extrude_segments_count)); @@ -2242,7 +2206,7 @@ void GCodeViewer::render_shaders_editor() const bool GCodeViewer::is_travel_in_z_range(size_t id) const { - const TBuffer& buffer = m_buffers[buffer_id(GCodeProcessor::EMoveType::Travel)]; + const TBuffer& buffer = m_buffers[buffer_id(EMoveType::Travel)]; if (id >= buffer.paths.size()) return false; diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 3b50062fbc..0a32a97e67 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -88,7 +88,7 @@ class GCodeViewer Vec3f position{ Vec3f::Zero() }; }; - GCodeProcessor::EMoveType type{ GCodeProcessor::EMoveType::Noop }; + EMoveType type{ EMoveType::Noop }; ExtrusionRole role{ erNone }; Endpoint first; Endpoint last; @@ -326,7 +326,7 @@ public: private: unsigned int m_last_result_id{ 0 }; size_t m_vertices_count{ 0 }; - mutable std::vector m_buffers{ static_cast(GCodeProcessor::EMoveType::Extrude) }; + mutable std::vector m_buffers{ static_cast(EMoveType::Extrude) }; // bounding box of toolpaths BoundingBoxf3 m_paths_bounding_box; // bounding box of toolpaths + marker tools @@ -341,6 +341,7 @@ private: Shells m_shells; EViewType m_view_type{ EViewType::FeatureType }; bool m_legend_enabled{ true }; + PrintEstimatedTimeStatistics m_time_statistics; #if ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG mutable bool m_time_estimate_enabled{ false }; mutable unsigned int m_time_estimate_frames_count{ 0 }; @@ -391,8 +392,8 @@ public: m_view_type = type; } - bool is_toolpath_move_type_visible(GCodeProcessor::EMoveType type) const; - void set_toolpath_move_type_visible(GCodeProcessor::EMoveType type, bool visible); + bool is_toolpath_move_type_visible(EMoveType type) const; + void set_toolpath_move_type_visible(EMoveType type, bool visible); unsigned int get_toolpath_role_visibility_flags() const { return m_extrusions.role_visibility_flags; } void set_toolpath_role_visibility_flags(unsigned int flags) { m_extrusions.role_visibility_flags = flags; } unsigned int get_options_visibility_flags() const; diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 7d6acad069..f7f6285c65 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1432,8 +1432,6 @@ void MainFrame::set_mode(EMode mode) select_tab(0); #endif // ENABLE_LAYOUT_NO_RESTART - m_plater->fff_print().print_statistics().clear_time_estimates(); - m_plater->reset(); m_plater->reset_gcode_toolpaths(); @@ -1477,8 +1475,6 @@ void MainFrame::set_mode(EMode mode) update_layout(); #endif // ENABLE_LAYOUT_NO_RESTART - m_plater->fff_print().print_statistics().clear_time_estimates(); - m_plater->reset(); m_plater->reset_last_loaded_gcode(); m_plater->reset_gcode_toolpaths(); From 0840b2328a668f648de117b4743e5769f31990af Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Aug 2020 15:00:19 +0200 Subject: [PATCH 232/503] Tech ENABLE_GCODE_VIEWER_AS_STATE set as default --- src/libslic3r/GCode/GCodeProcessor.cpp | 5 -- src/libslic3r/GCode/GCodeProcessor.hpp | 6 --- src/libslic3r/Technologies.hpp | 1 - src/slic3r/GUI/GCodeViewer.cpp | 15 +----- src/slic3r/GUI/GLCanvas3D.cpp | 29 +++-------- src/slic3r/GUI/GLCanvas3D.hpp | 4 +- src/slic3r/GUI/GUI_App.cpp | 4 +- src/slic3r/GUI/GUI_App.hpp | 4 +- src/slic3r/GUI/GUI_Preview.cpp | 16 +++--- src/slic3r/GUI/MainFrame.cpp | 68 +++++++++++++------------- src/slic3r/GUI/MainFrame.hpp | 16 +++--- src/slic3r/GUI/Plater.cpp | 38 +++++++------- src/slic3r/GUI/Plater.hpp | 29 +++++------ 13 files changed, 91 insertions(+), 144 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 5d9644457d..7612af0e90 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1147,7 +1147,6 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) else if (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f || delta_pos[Z] != 0.0f) type = EMoveType::Travel; -#if ENABLE_GCODE_VIEWER_AS_STATE if (type == EMoveType::Extrude && (m_width == 0.0f || m_height == 0.0f)) { if (m_extrusion_role != erCustom) { m_width = 0.5f; @@ -1155,10 +1154,6 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) } type = EMoveType::Travel; } -#else - if (type == EMoveType::Extrude && (m_width == 0.0f || m_height == 0.0f || !is_valid_extrusion_role(m_extrusion_role))) - type = EMoveType::Travel; -#endif // ENABLE_GCODE_VIEWER_AS_STATE return type; }; diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 526300e55a..101c320db8 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -244,10 +244,8 @@ namespace Slic3r { { unsigned int id; std::vector moves; -#if ENABLE_GCODE_VIEWER_AS_STATE Pointfs bed_shape; std::vector extruder_colors; -#endif // ENABLE_GCODE_VIEWER_AS_STATE PrintEstimatedTimeStatistics time_statistics; #if ENABLE_GCODE_VIEWER_STATISTICS @@ -256,19 +254,15 @@ namespace Slic3r { { time = 0; moves = std::vector(); -#if ENABLE_GCODE_VIEWER_AS_STATE bed_shape = Pointfs(); extruder_colors = std::vector(); -#endif // ENABLE_GCODE_VIEWER_AS_STATE } #else void reset() { moves = std::vector(); -#if ENABLE_GCODE_VIEWER_AS_STATE bed_shape = Pointfs(); extruder_colors = std::vector(); -#endif // ENABLE_GCODE_VIEWER_AS_STATE } #endif // ENABLE_GCODE_VIEWER_STATISTICS }; diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 569a73bab1..c14e2df52f 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -58,7 +58,6 @@ #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) #define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_SHADERS_EDITOR (0 && ENABLE_GCODE_VIEWER) -#define ENABLE_GCODE_VIEWER_AS_STATE (1 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR (1 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG (1 && ENABLE_GCODE_VIEWER) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 4faa0486ff..e746635ee2 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -7,9 +7,7 @@ #include "libslic3r/Model.hpp" #include "libslic3r/Utils.hpp" #include "GUI_App.hpp" -#if ENABLE_GCODE_VIEWER_AS_STATE #include "MainFrame.hpp" -#endif // ENABLE_GCODE_VIEWER_AS_STATE #include "Plater.hpp" #include "PresetBundle.hpp" #include "Camera.hpp" @@ -314,13 +312,9 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& reset(); load_toolpaths(gcode_result); -#if ENABLE_GCODE_VIEWER_AS_STATE if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) -#endif // ENABLE_GCODE_VIEWER_AS_STATE load_shells(print, initialized); - -#if ENABLE_GCODE_VIEWER_AS_STATE - if (wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer) { + else { Pointfs bed_shape; if (!gcode_result.bed_shape.empty()) // bed shape detected in the gcode @@ -337,7 +331,6 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& } wxGetApp().plater()->set_bed_shape(bed_shape, "", "", true); } -#endif // ENABLE_GCODE_VIEWER_AS_STATE m_time_statistics = gcode_result.time_statistics; } @@ -351,12 +344,10 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: if (m_vertices_count == 0) return; -#if ENABLE_GCODE_VIEWER_AS_STATE if (m_view_type == EViewType::Tool && !gcode_result.extruder_colors.empty()) // update tool colors from config stored in the gcode m_tool_colors = decode_colors(gcode_result.extruder_colors); else -#endif // ENABLE_GCODE_VIEWER_AS_STATE // update tool colors m_tool_colors = decode_colors(str_tool_colors); @@ -841,17 +832,13 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) for (size_t i = 0; i < m_vertices_count; ++i) { const GCodeProcessor::MoveVertex& move = gcode_result.moves[i]; -#if ENABLE_GCODE_VIEWER_AS_STATE if (wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer) // for the gcode viewer we need all moves to correctly size the printbed m_paths_bounding_box.merge(move.position.cast()); else { -#endif // ENABLE_GCODE_VIEWER_AS_STATE if (move.type == EMoveType::Extrude && move.width != 0.0f && move.height != 0.0f) m_paths_bounding_box.merge(move.position.cast()); -#if ENABLE_GCODE_VIEWER_AS_STATE } -#endif // ENABLE_GCODE_VIEWER_AS_STATE } // add origin diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index dc8b21ce8d..2a744d72d7 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1913,12 +1913,12 @@ void GLCanvas3D::zoom_to_selection() _zoom_to_box(m_selection.get_bounding_box()); } -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER void GLCanvas3D::zoom_to_gcode() { _zoom_to_box(m_gcode_viewer.get_paths_bounding_box(), 1.05); } -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER void GLCanvas3D::select_view(const std::string& direction) { @@ -2696,9 +2696,7 @@ static void load_gcode_retractions(const GCodePreviewData::Retraction& retractio void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result) { m_gcode_viewer.load(gcode_result, *this->fff_print(), m_initialized); -#if ENABLE_GCODE_VIEWER_AS_STATE if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) -#endif // ENABLE_GCODE_VIEWER_AS_STATE _show_warning_texture_if_needed(WarningTexture::ToolpathOutside); } @@ -4280,17 +4278,15 @@ void GLCanvas3D::update_ui_from_settings() } #endif // ENABLE_RETINA_GL -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER if (wxGetApp().mainframe != nullptr && wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) wxGetApp().plater()->get_collapse_toolbar().set_enabled(wxGetApp().app_config->get("show_collapse_button") == "1"); #else bool enable_collapse = wxGetApp().app_config->get("show_collapse_button") == "1"; wxGetApp().plater()->get_collapse_toolbar().set_enabled(enable_collapse); -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER } - - GLCanvas3D::WipeTowerInfo GLCanvas3D::get_wipe_tower_info() const { WipeTowerInfo wti; @@ -5385,22 +5381,16 @@ static BoundingBoxf3 print_volume(const DynamicPrintConfig& config) void GLCanvas3D::_render_background() const { #if ENABLE_GCODE_VIEWER -#if ENABLE_GCODE_VIEWER_AS_STATE bool use_error_color = false; if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) { use_error_color = m_dynamic_background_enabled; -#else - bool use_error_color = m_dynamic_background_enabled; -#endif // ENABLE_GCODE_VIEWER_AS_STATE if (!m_volumes.empty()) use_error_color &= _is_any_volume_outside(); else { BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); use_error_color &= (test_volume.radius() > 0.0) ? !test_volume.contains(m_gcode_viewer.get_paths_bounding_box()) : false; } -#if ENABLE_GCODE_VIEWER_AS_STATE } -#endif // ENABLE_GCODE_VIEWER_AS_STATE #endif // ENABLE_GCODE_VIEWER glsafe(::glPushMatrix()); @@ -7119,20 +7109,13 @@ void GLCanvas3D::_show_warning_texture_if_needed(WarningTexture::Warning warning bool show = false; if (!m_volumes.empty()) show = _is_any_volume_outside(); - else - { -#if ENABLE_GCODE_VIEWER_AS_STATE - if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) - { + else { + if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) { BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box(); if (test_volume.radius() > 0.0 && paths_volume.radius() > 0.0) show = !test_volume.contains(paths_volume); } -#else - BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); - show = (test_volume.radius() > 0.0) ? !test_volume.contains(m_gcode_viewer.get_bounding_box()) : false; -#endif // ENABLE_GCODE_VIEWER_AS_STATE } _set_warning_texture(warning, show); #else diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 0b5a357fdb..15bb3e6f06 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -625,9 +625,9 @@ public: void zoom_to_bed(); void zoom_to_volumes(); void zoom_to_selection(); -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER void zoom_to_gcode(); -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER void select_view(const std::string& direction); void update_volumes_colors_by_extruder(); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 2fb60bf374..433e026f6c 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -754,7 +754,7 @@ void GUI_App::import_model(wxWindow *parent, wxArrayString& input_files) const dialog.GetPaths(input_files); } -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER void GUI_App::load_gcode(wxWindow* parent, wxString& input_file) const { input_file.Clear(); @@ -766,7 +766,7 @@ void GUI_App::load_gcode(wxWindow* parent, wxString& input_file) const if (dialog.ShowModal() == wxID_OK) input_file = dialog.GetPath(); } -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER bool GUI_App::switch_language() { diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index ad874bd1ef..aa94475319 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -156,9 +156,9 @@ public: void keyboard_shortcuts(); void load_project(wxWindow *parent, wxString& input_file) const; void import_model(wxWindow *parent, wxArrayString& input_files) const; -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER void load_gcode(wxWindow* parent, wxString& input_file) const; -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER static bool catch_error(std::function cb, const std::string& err); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 556f5de8cd..98c02322ee 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -13,9 +13,9 @@ #include "PresetBundle.hpp" #include "DoubleSlider.hpp" #include "Plater.hpp" -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER #include "MainFrame.hpp" -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER #include #include @@ -1196,11 +1196,11 @@ void Preview::update_double_slider_from_canvas(wxKeyEvent & event) void Preview::load_print_as_fff(bool keep_z_range) { -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER if (wxGetApp().mainframe == nullptr) // avoid proessing while mainframe is being constructed return; -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER if (m_loaded || m_process->current_printer_technology() != ptFFF) return; @@ -1225,11 +1225,11 @@ void Preview::load_print_as_fff(bool keep_z_range) } } -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer && !has_layers) #else if (! has_layers) -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER { #if ENABLE_GCODE_VIEWER hide_layers_slider(); @@ -1265,11 +1265,7 @@ void Preview::load_print_as_fff(bool keep_z_range) #if ENABLE_GCODE_VIEWER GCodeViewer::EViewType gcode_view_type = m_canvas->get_gcode_view_preview_type(); -#if ENABLE_GCODE_VIEWER_AS_STATE bool gcode_preview_data_valid = !m_gcode_result->moves.empty(); -#else - bool gcode_preview_data_valid = print->is_step_done(psGCodeExport); -#endif // ENABLE_GCODE_VIEWER_AS_STATE #else bool gcode_preview_data_valid = print->is_step_done(psGCodeExport) && ! m_gcode_preview_data->empty(); #endif // ENABLE_GCODE_VIEWER diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 778ce2134f..f017900f46 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -111,7 +111,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S // initialize tabpanel and menubar init_tabpanel(); -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER init_editor_menubar(); init_gcodeviewer_menubar(); @@ -129,7 +129,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S #endif // _WIN32 #else init_menubar(); -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER // set default tooltip timer in msec // SetAutoPop supposedly accepts long integers but some bug doesn't allow for larger values @@ -243,9 +243,9 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S update_ui_from_settings(); // FIXME (?) if (m_plater != nullptr) { -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER m_plater->get_collapse_toolbar().set_enabled(wxGetApp().app_config->get("show_collapse_button") == "1"); -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER m_plater->show_action_buttons(true); } } @@ -291,7 +291,7 @@ void MainFrame::update_layout() Layout(); }; -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER ESettingsLayout layout = (m_mode == EMode::GCodeViewer) ? ESettingsLayout::GCodeViewer : (wxGetApp().app_config->get("old_settings_layout_mode") == "1" ? ESettingsLayout::Old : wxGetApp().app_config->get("new_settings_layout_mode") == "1" ? ESettingsLayout::New : @@ -300,7 +300,7 @@ void MainFrame::update_layout() ESettingsLayout layout = wxGetApp().app_config->get("old_settings_layout_mode") == "1" ? ESettingsLayout::Old : wxGetApp().app_config->get("new_settings_layout_mode") == "1" ? ESettingsLayout::New : wxGetApp().app_config->get("dlg_settings_layout_mode") == "1" ? ESettingsLayout::Dlg : ESettingsLayout::Old; -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER if (m_layout == layout) return; @@ -356,14 +356,14 @@ void MainFrame::update_layout() m_plater->Show(); break; } -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER case ESettingsLayout::GCodeViewer: { m_main_sizer->Add(m_plater, 1, wxEXPAND); m_plater->Show(); break; } -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER } //#ifdef __APPLE__ @@ -398,7 +398,7 @@ void MainFrame::shutdown() } #endif // _WIN32 -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER if (m_plater != nullptr) { m_plater->stop_jobs(); @@ -423,7 +423,7 @@ void MainFrame::shutdown() // Cleanup of canvases' volumes needs to be done here or a crash may happen on some Linux Debian flavours // see: https://github.com/prusa3d/PrusaSlicer/issues/3964 if (m_plater) m_plater->reset_canvas_volumes(); -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER // Weird things happen as the Paint messages are floating around the windows being destructed. // Avoid the Paint messages by hiding the main window. @@ -436,11 +436,11 @@ void MainFrame::shutdown() m_settings_dialog.Close(); if (m_plater != nullptr) { -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER // restore sidebar if it was hidden when switching to gcode viewer mode if (m_restore_from_gcode_viewer.collapsed_sidebar) m_plater->collapse_sidebar(false); -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER // Stop the background thread (Windows and Linux). // Disconnect from a 3DConnextion driver (OSX). m_plater->get_mouse3d_controller().shutdown(); @@ -781,7 +781,7 @@ void MainFrame::on_sys_color_changed() msw_rescale_menu(menu_bar->GetMenu(id)); } -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER #ifdef _MSC_VER // \xA0 is a non-breaking space. It is entered here to spoil the automatic accelerators, // as the simple numeric accelerators spoil all numeric data entry. @@ -855,7 +855,7 @@ static void add_common_view_menu_items(wxMenu* view_menu, MainFrame* mainFrame, void MainFrame::init_editor_menubar() #else void MainFrame::init_menubar() -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER { #ifdef __APPLE__ wxMenuBar::SetAutoWindowMenu(false); @@ -1015,7 +1015,7 @@ void MainFrame::init_menubar() append_menu_item(fileMenu, wxID_ANY, _L("&Repair STL file") + dots, _L("Automatically repair an STL file"), [this](wxCommandEvent&) { repair_stl(); }, "wrench", nullptr, [this]() { return true; }, this); -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_ANY, _L("&G-code preview"), _L("Switch to G-code preview mode"), [this](wxCommandEvent&) { @@ -1023,13 +1023,13 @@ void MainFrame::init_menubar() set_mode(EMode::GCodeViewer); }, "", nullptr, [this]() { return m_plater != nullptr && m_plater->printer_technology() != ptSLA; }, this); -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_EXIT, _L("&Quit"), wxString::Format(_L("Quit %s"), SLIC3R_APP_NAME), [this](wxCommandEvent&) { Close(false); }); } -#if !ENABLE_GCODE_VIEWER_AS_STATE +#if !ENABLE_GCODE_VIEWER #ifdef _MSC_VER // \xA0 is a non-breaking space. It is entered here to spoil the automatic accelerators, // as the simple numeric accelerators spoil all numeric data entry. @@ -1039,7 +1039,7 @@ void MainFrame::init_menubar() wxString sep = " - "; wxString sep_space = ""; #endif -#endif // !ENABLE_GCODE_VIEWER_AS_STATE +#endif // !ENABLE_GCODE_VIEWER // Edit menu wxMenu* editMenu = nullptr; @@ -1123,7 +1123,7 @@ void MainFrame::init_menubar() [this](){return can_change_view(); }, this); } -#if !ENABLE_GCODE_VIEWER_AS_STATE +#if !ENABLE_GCODE_VIEWER #if _WIN32 // This is needed on Windows to fake the CTRL+# of the window menu when using the numpad wxAcceleratorEntry entries[6]; @@ -1136,7 +1136,7 @@ void MainFrame::init_menubar() wxAcceleratorTable accel(6, entries); SetAcceleratorTable(accel); #endif // _WIN32 -#endif // !ENABLE_GCODE_VIEWER_AS_STATE +#endif // !ENABLE_GCODE_VIEWER windowMenu->AppendSeparator(); append_menu_item(windowMenu, wxID_ANY, _L("Print &Host Upload Queue") + "\tCtrl+J", _L("Display the Print Host Upload Queue window"), @@ -1148,7 +1148,7 @@ void MainFrame::init_menubar() wxMenu* viewMenu = nullptr; if (m_plater) { viewMenu = new wxMenu(); -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER add_common_view_menu_items(viewMenu, this, std::bind(&MainFrame::can_change_view, this)); #else // The camera control accelerators are captured by GLCanvas3D::on_char(). @@ -1169,7 +1169,7 @@ void MainFrame::init_menubar() "", nullptr, [this](){return can_change_view(); }, this); append_menu_item(viewMenu, wxID_ANY, _L("Right") + sep + "&6", _L("Right View"), [this](wxCommandEvent&) { select_view("right"); }, "", nullptr, [this](){return can_change_view(); }, this); -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER viewMenu->AppendSeparator(); #if ENABLE_SLOPE_RENDERING wxMenu* options_menu = new wxMenu(); @@ -1191,7 +1191,7 @@ void MainFrame::init_menubar() } // Help menu -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER auto helpMenu = generate_help_menu(); #else auto helpMenu = new wxMenu(); @@ -1228,12 +1228,12 @@ void MainFrame::init_menubar() [this](wxCommandEvent&) { wxGetApp().gcode_thumbnails_debug(); }); #endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG } -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER // menubar // assign menubar to frame after appending items, otherwise special items // will not be handled correctly -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER m_editor_menubar = new wxMenuBar(); m_editor_menubar->Append(fileMenu, _L("&File")); if (editMenu) m_editor_menubar->Append(editMenu, _L("&Edit")); @@ -1253,16 +1253,16 @@ void MainFrame::init_menubar() wxGetApp().add_config_menu(menubar); menubar->Append(helpMenu, _(L("&Help"))); SetMenuBar(menubar); -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER #ifdef __APPLE__ // This fixes a bug on Mac OS where the quit command doesn't emit window close events // wx bug: https://trac.wxwidgets.org/ticket/18328 -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER wxMenu* apple_menu = m_editor_menubar->OSXGetAppleMenu(); #else wxMenu *apple_menu = menubar->OSXGetAppleMenu(); -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER if (apple_menu != nullptr) { apple_menu->Bind(wxEVT_MENU, [this](wxCommandEvent &) { Close(); @@ -1271,14 +1271,14 @@ void MainFrame::init_menubar() #endif if (plater()->printer_technology() == ptSLA) -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER update_editor_menubar(); #else update_menubar(); -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER } -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER void MainFrame::init_gcodeviewer_menubar() { wxMenu* fileMenu = new wxMenu; @@ -1412,13 +1412,13 @@ void MainFrame::set_mode(EMode mode) } } } -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER void MainFrame::update_editor_menubar() #else void MainFrame::update_menubar() -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER { const bool is_fff = plater()->printer_technology() == ptFFF; diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 8a3e4376b4..8961ad7172 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -68,7 +68,7 @@ class MainFrame : public DPIFrame wxString m_qs_last_input_file = wxEmptyString; wxString m_qs_last_output_file = wxEmptyString; wxString m_last_config = wxEmptyString; -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER wxMenuBar* m_editor_menubar{ nullptr }; wxMenuBar* m_gcodeviewer_menubar{ nullptr }; @@ -79,7 +79,7 @@ class MainFrame : public DPIFrame }; RestoreFromGCodeViewer m_restore_from_gcode_viewer; -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER #if 0 wxMenuItem* m_menu_item_repeat { nullptr }; // doesn't used now @@ -134,14 +134,14 @@ class MainFrame : public DPIFrame Old, New, Dlg, -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER GCodeViewer -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER }; ESettingsLayout m_layout{ ESettingsLayout::Unknown }; -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER public: enum class EMode : unsigned char { @@ -151,7 +151,7 @@ public: private: EMode m_mode{ EMode::Editor }; -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER protected: virtual void on_dpi_changed(const wxRect &suggested_rect); @@ -173,7 +173,7 @@ public: void init_tabpanel(); void create_preset_tabs(); void add_created_tab(Tab* panel); -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER void init_editor_menubar(); void update_editor_menubar(); void init_gcodeviewer_menubar(); @@ -183,7 +183,7 @@ public: #else void init_menubar(); void update_menubar(); -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER void update_ui_from_settings(); bool is_loaded() const { return m_loaded; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 43cf27e30f..b87b3cce36 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -33,13 +33,11 @@ #include "libslic3r/Format/STL.hpp" #include "libslic3r/Format/AMF.hpp" #include "libslic3r/Format/3mf.hpp" -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER #include "libslic3r/GCode/GCodeProcessor.hpp" #else -#if !ENABLE_GCODE_VIEWER #include "libslic3r/GCode/PreviewData.hpp" -#endif // !ENABLE_GCODE_VIEWER -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER #include "libslic3r/GCode/ThumbnailData.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/SLA/Hollowing.hpp" @@ -2767,10 +2765,8 @@ void Plater::priv::reset() #if ENABLE_GCODE_VIEWER reset_gcode_toolpaths(); -#endif // ENABLE_GCODE_VIEWER -#if ENABLE_GCODE_VIEWER_AS_STATE gcode_result.reset(); -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER // Stop and reset the Print content. this->background_process.reset(); @@ -4633,7 +4629,7 @@ void Plater::extract_config_from_project() load_files(input_paths, false, true); } -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER void Plater::load_gcode() { // Ask user for a gcode file name. @@ -4669,7 +4665,7 @@ void Plater::load_gcode(const wxString& filename) p->preview->reload_print(false); p->preview->get_canvas3d()->zoom_to_gcode(); } -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER std::vector Plater::load_files(const std::vector& input_files, bool load_model, bool load_config, bool imperial_units /*= false*/) { return p->load_files(input_files, load_model, load_config, imperial_units); } @@ -5443,7 +5439,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config) void Plater::set_bed_shape() const { -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER set_bed_shape(p->config->option("bed_shape")->values, p->config->option("bed_custom_texture")->value, p->config->option("bed_custom_model")->value); @@ -5451,15 +5447,15 @@ void Plater::set_bed_shape() const p->set_bed_shape(p->config->option("bed_shape")->values, p->config->option("bed_custom_texture")->value, p->config->option("bed_custom_model")->value); -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER } -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER void Plater::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom) const { p->set_bed_shape(shape, custom_texture, custom_model, force_as_custom); } -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER void Plater::force_filament_colors_update() { @@ -5634,11 +5630,11 @@ void Plater::set_printer_technology(PrinterTechnology printer_technology) p->label_btn_send = printer_technology == ptFFF ? L("Send G-code") : L("Send to printer"); if (wxGetApp().mainframe != nullptr) -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER wxGetApp().mainframe->update_editor_menubar(); #else wxGetApp().mainframe->update_menubar(); -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER p->update_main_toolbar_tooltips(); @@ -5790,24 +5786,24 @@ bool Plater::init_view_toolbar() return p->init_view_toolbar(); } -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER void Plater::enable_view_toolbar(bool enable) { p->view_toolbar.set_enabled(enable); } -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER bool Plater::init_collapse_toolbar() { return p->init_collapse_toolbar(); } -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER void Plater::enable_collapse_toolbar(bool enable) { p->collapse_toolbar.set_enabled(enable); } -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER const Camera& Plater::get_camera() const { @@ -5936,9 +5932,9 @@ bool Plater::can_undo() const { return p->undo_redo_stack().has_undo_snapshot(); bool Plater::can_redo() const { return p->undo_redo_stack().has_redo_snapshot(); } bool Plater::can_reload_from_disk() const { return p->can_reload_from_disk(); } const UndoRedo::Stack& Plater::undo_redo_stack_main() const { return p->undo_redo_stack_main(); } -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER void Plater::clear_undo_redo_stack_main() { p->undo_redo_stack_main().clear(); } -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER void Plater::enter_gizmos_stack() { p->enter_gizmos_stack(); } void Plater::leave_gizmos_stack() { p->leave_gizmos_stack(); } bool Plater::inside_snapshot_capture() { return p->inside_snapshot_capture(); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 59d595bbe4..8142a7c359 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -173,10 +173,10 @@ public: void add_model(bool imperial_units = false); void import_sl1_archive(); void extract_config_from_project(); -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER void load_gcode(); void load_gcode(const wxString& filename); -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER std::vector load_files(const std::vector& input_files, bool load_model = true, bool load_config = true, bool imperial_units = false); // To be called when providing a list of files to the GUI slic3r on command line. @@ -256,9 +256,9 @@ public: bool search_string_getter(int idx, const char** label, const char** tooltip); // For the memory statistics. const Slic3r::UndoRedo::Stack& undo_redo_stack_main() const; -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER void clear_undo_redo_stack_main(); -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER // Enter / leave the Gizmos specific Undo / Redo stack. To be used by the SLA support point editing gizmo. void enter_gizmos_stack(); void leave_gizmos_stack(); @@ -322,13 +322,13 @@ public: void sys_color_changed(); bool init_view_toolbar(); -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER void enable_view_toolbar(bool enable); -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER bool init_collapse_toolbar(); -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER void enable_collapse_toolbar(bool enable); -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER const Camera& get_camera() const; Camera& get_camera(); @@ -352,19 +352,16 @@ public: void update_preview_moves_slider(); void reset_gcode_toolpaths(); -#endif // ENABLE_GCODE_VIEWER - -#if ENABLE_GCODE_VIEWER_AS_STATE void reset_last_loaded_gcode() { m_last_loaded_gcode = ""; } -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER const Mouse3DController& get_mouse3d_controller() const; Mouse3DController& get_mouse3d_controller(); void set_bed_shape() const; -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER void set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false) const; -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER // ROII wrapper for suppressing the Undo / Redo snapshot to be taken. class SuppressSnapshots @@ -415,9 +412,9 @@ private: bool m_tracking_popup_menu = false; wxString m_tracking_popup_menu_error_message; -#if ENABLE_GCODE_VIEWER_AS_STATE +#if ENABLE_GCODE_VIEWER wxString m_last_loaded_gcode; -#endif // ENABLE_GCODE_VIEWER_AS_STATE +#endif // ENABLE_GCODE_VIEWER void suppress_snapshots(); void allow_snapshots(); From b3f8ae5ca75b52ef74e8da2be2392ea8339dcab2 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 3 Aug 2020 15:36:55 +0200 Subject: [PATCH 233/503] Notifications & warning dialog notifications dialog with warnings produced by slicing is shown before exporting --- resources/icons/cancel.svg | 10 + resources/icons/cross_focus_large.svg | 81 ++ resources/icons/timer_dot.svg | 72 ++ resources/icons/timer_dot_empty.svg | 73 ++ src/imgui/imconfig.h | 7 +- src/libslic3r/GCode.cpp | 1 + src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/BackgroundSlicingProcess.cpp | 10 +- src/slic3r/GUI/BackgroundSlicingProcess.hpp | 7 + src/slic3r/GUI/GLCanvas3D.cpp | 41 +- src/slic3r/GUI/GUI_App.cpp | 10 +- src/slic3r/GUI/GUI_App.hpp | 3 + src/slic3r/GUI/ImGuiWrapper.cpp | 32 +- src/slic3r/GUI/ImGuiWrapper.hpp | 3 + src/slic3r/GUI/Mouse3DController.cpp | 3 + src/slic3r/GUI/NotificationManager.cpp | 918 ++++++++++++++++++++ src/slic3r/GUI/NotificationManager.hpp | 268 ++++++ src/slic3r/GUI/Plater.cpp | 225 +++-- src/slic3r/GUI/Plater.hpp | 7 +- src/slic3r/Utils/PresetUpdater.cpp | 101 ++- src/slic3r/Utils/PresetUpdater.hpp | 6 +- 21 files changed, 1791 insertions(+), 89 deletions(-) create mode 100644 resources/icons/cancel.svg create mode 100644 resources/icons/cross_focus_large.svg create mode 100644 resources/icons/timer_dot.svg create mode 100644 resources/icons/timer_dot_empty.svg create mode 100644 src/slic3r/GUI/NotificationManager.cpp create mode 100644 src/slic3r/GUI/NotificationManager.hpp diff --git a/resources/icons/cancel.svg b/resources/icons/cancel.svg new file mode 100644 index 0000000000..da44606a08 --- /dev/null +++ b/resources/icons/cancel.svg @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/resources/icons/cross_focus_large.svg b/resources/icons/cross_focus_large.svg new file mode 100644 index 0000000000..c246f2bd9e --- /dev/null +++ b/resources/icons/cross_focus_large.svg @@ -0,0 +1,81 @@ + +image/svg+xml + + + + + + + + + + + diff --git a/resources/icons/timer_dot.svg b/resources/icons/timer_dot.svg new file mode 100644 index 0000000000..3a77962b60 --- /dev/null +++ b/resources/icons/timer_dot.svg @@ -0,0 +1,72 @@ + +image/svg+xml + + + + diff --git a/resources/icons/timer_dot_empty.svg b/resources/icons/timer_dot_empty.svg new file mode 100644 index 0000000000..a8e776b49e --- /dev/null +++ b/resources/icons/timer_dot_empty.svg @@ -0,0 +1,73 @@ + + + +image/svg+xml + + + + \ No newline at end of file diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index d32f64aa4b..feda857ae2 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -113,7 +113,12 @@ namespace ImGui const char PrinterSlaIconMarker = 0x6; const char FilamentIconMarker = 0x7; const char MaterialIconMarker = 0x8; - + const char CloseIconMarker = 0xB; + const char CloseIconHoverMarker = 0xC; + const char TimerDotMarker = 0xE; + const char TimerDotEmptyMarker = 0xF; + const char WarningMarker = 0x10; + const char ErrorMarker = 0x11; // void MyFunction(const char* name, const MyMatrix44& v); } diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 35dc5a53bd..7d80677184 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -686,6 +686,7 @@ std::vector>> GCode::collec std::sort(ordering.begin(), ordering.end(), [](const OrderingItem &oi1, const OrderingItem &oi2) { return oi1.print_z < oi2.print_z; }); std::vector>> layers_to_print; + // Merge numerically very close Z values. for (size_t i = 0; i < ordering.size();) { // Find the last layer with roughly the same print_z. diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 7e02c0fdd7..57e84f71ea 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -163,6 +163,8 @@ set(SLIC3R_GUI_SOURCES GUI/InstanceCheck.hpp GUI/Search.cpp GUI/Search.hpp + GUI/NotificationManager.cpp + GUI/NotificationManager.hpp Utils/Http.cpp Utils/Http.hpp Utils/FixModelByWin10.cpp diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 8d50998c48..7309654a85 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -89,11 +89,13 @@ void BackgroundSlicingProcess::process_fff() { assert(m_print == m_fff_print); m_print->process(); - wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_slicing_completed_id)); + wxCommandEvent evt(m_event_slicing_completed_id); + evt.SetInt((int)(m_fff_print->step_state_with_timestamp(PrintStep::psBrim).timestamp)); + wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone()); m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_thumbnail_cb); - if (this->set_step_started(bspsGCodeFinalize)) { if (! m_export_path.empty()) { + wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_export_began_id)); //FIXME localize the messages // Perform the final post-processing of the export path by applying the print statistics over the file name. std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path); @@ -124,6 +126,7 @@ void BackgroundSlicingProcess::process_fff() run_post_process_scripts(export_path, m_fff_print->config()); m_print->set_status(100, (boost::format(_utf8(L("G-code file exported to %1%"))) % export_path).str()); } else if (! m_upload_job.empty()) { + wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_export_began_id)); prepare_upload(); } else { m_print->set_status(100, _utf8(L("Slicing complete"))); @@ -149,6 +152,8 @@ void BackgroundSlicingProcess::process_sla() m_print->process(); if (this->set_step_started(bspsGCodeFinalize)) { if (! m_export_path.empty()) { + wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_export_began_id)); + const std::string export_path = m_sla_print->print_statistics().finalize_output_path(m_export_path); Zipper zipper(export_path); @@ -170,6 +175,7 @@ void BackgroundSlicingProcess::process_sla() m_print->set_status(100, (boost::format(_utf8(L("Masked SLA file exported to %1%"))) % export_path).str()); } else if (! m_upload_job.empty()) { + wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_export_began_id)); prepare_upload(); } else { m_print->set_status(100, _utf8(L("Slicing complete"))); diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index 38e9e10755..c4672f1b40 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -60,6 +60,10 @@ public: // The following wxCommandEvent will be sent to the UI thread / Plater window, when the G-code export is finished. // The wxCommandEvent is sent to the UI thread asynchronously without waiting for the event to be processed. void set_finished_event(int event_id) { m_event_finished_id = event_id; } + // The following wxCommandEvent will be sent to the UI thread / Plater window, when the G-code is being exported to + // specified path or uploaded. + // The wxCommandEvent is sent to the UI thread asynchronously without waiting for the event to be processed. + void set_export_began_event(int event_id) { m_event_export_began_id = event_id; } // Activate either m_fff_print or m_sla_print. // Return true if changed. @@ -190,6 +194,9 @@ private: int m_event_slicing_completed_id = 0; // wxWidgets command ID to be sent to the plater to inform that the task finished. int m_event_finished_id = 0; + // wxWidgets command ID to be sent to the plater to inform that the G-code is being exported. + int m_event_export_began_id = 0; + }; }; // namespace Slic3r diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 5109d24264..1edd7aa2b5 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -31,6 +31,7 @@ #include "GUI_ObjectManipulation.hpp" #include "Mouse3DController.hpp" #include "I18N.hpp" +#include "NotificationManager.hpp" #if ENABLE_RETINA_GL #include "slic3r/Utils/RetinaHelper.hpp" @@ -651,19 +652,45 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool m_warnings.emplace_back(warning); std::sort(m_warnings.begin(), m_warnings.end()); + + std::string text; + switch (warning) { + case ObjectOutside: text = L("An object outside the print area was detected."); break; + case ToolpathOutside: text = L("A toolpath outside the print area was detected."); break; + case SlaSupportsOutside: text = L("SLA supports outside the print area were detected."); break; + case SomethingNotShown: text = L("Some objects are not visible."); break; + case ObjectClashed: wxGetApp().plater()->get_notification_manager()->push_plater_error_notification(L("An object outside the print area was detected.\n" + "Resolve the current problem to continue slicing."), + *(wxGetApp().plater()->get_current_canvas3D())); + break; + } + if (!text.empty()) + wxGetApp().plater()->get_notification_manager()->push_plater_warning_notification(text, *(wxGetApp().plater()->get_current_canvas3D())); } else { if (it == m_warnings.end()) // deactivating something that is not active is an easy task return; m_warnings.erase(it); - if (m_warnings.empty()) { // nothing remains to be shown + + std::string text; + switch (warning) { + case ObjectOutside: text = L("An object outside the print area was detected."); break; + case ToolpathOutside: text = L("A toolpath outside the print area was detected."); break; + case SlaSupportsOutside: text = L("SLA supports outside the print area were detected."); break; + case SomethingNotShown: text = L("Some objects are not visibl.e"); break; + case ObjectClashed: wxGetApp().plater()->get_notification_manager()->close_plater_error_notification(); break; + } + if (!text.empty()) + wxGetApp().plater()->get_notification_manager()->close_plater_warning_notification(text); + + /*if (m_warnings.empty()) { // nothing remains to be shown reset(); m_msg_text = "";// save information for rescaling return; - } + }*/ } - + /* // Look at the end of our vector and generate proper texture. std::string text; bool red_colored = false; @@ -685,6 +712,7 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool // save information for rescaling m_msg_text = text; m_is_colored_red = red_colored; + */ } @@ -2074,6 +2102,8 @@ void GLCanvas3D::render() std::string tooltip; + + // Negative coordinate means out of the window, likely because the window was deactivated. // In that case the tooltip should be hidden. if (m_mouse.position.x() >= 0. && m_mouse.position.y() >= 0.) @@ -2103,6 +2133,8 @@ void GLCanvas3D::render() m_tooltip.render(m_mouse.position, *this); wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog(*this); + + wxGetApp().plater()->get_notification_manager()->render_notifications(*this); wxGetApp().imgui()->render(); @@ -3418,6 +3450,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) #ifdef SLIC3R_DEBUG_MOUSE_EVENTS printf((format_mouse_event_debug_message(evt) + " - Consumed by ImGUI\n").c_str()); #endif /* SLIC3R_DEBUG_MOUSE_EVENTS */ + m_dirty = true; // do not return if dragging or tooltip not empty to allow for tooltip update if (!m_mouse.dragging && m_tooltip.is_empty()) return; @@ -3811,7 +3844,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_gizmos.reset_all_states(); // Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor hovers over. - if (m_picking_enabled) + //if (m_picking_enabled) m_dirty = true; } else diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index bfb1586195..49d08565ac 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -54,6 +54,7 @@ #include "Mouse3DController.hpp" #include "RemovableDriveManager.hpp" #include "InstanceCheck.hpp" +#include "NotificationManager.hpp" #ifdef __WXMSW__ #include @@ -384,7 +385,7 @@ bool GUI_App::on_init_inner() // supplied as argument to --datadir; in that case we should still run the wizard preset_bundle->setup_directories(); -#ifdef __WXMSW__ +#ifdef __WXMSW__ associate_3mf_files(); #endif // __WXMSW__ @@ -392,6 +393,11 @@ bool GUI_App::on_init_inner() Bind(EVT_SLIC3R_VERSION_ONLINE, [this](const wxCommandEvent &evt) { app_config->set("version_online", into_u8(evt.GetString())); app_config->save(); + if(this->plater_ != nullptr) { + if (*Semver::parse(SLIC3R_VERSION) < * Semver::parse(into_u8(evt.GetString()))) { + this->plater_->get_notification_manager()->push_notification(NotificationType::NewAppAviable, *(this->plater_->get_current_canvas3D())); + } + } }); // initialize label colors and fonts @@ -1439,7 +1445,7 @@ void GUI_App::check_updates(const bool verbose) PresetUpdater::UpdateResult updater_result; try { - updater_result = preset_updater->config_update(app_config->orig_version()); + updater_result = preset_updater->config_update(app_config->orig_version(), verbose); if (updater_result == PresetUpdater::R_INCOMPAT_EXIT) { mainframe->Close(); } diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index c2b257f458..922d6173df 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -194,12 +194,15 @@ public: Plater* plater(); Model& model(); + AppConfig* app_config{ nullptr }; PresetBundle* preset_bundle{ nullptr }; PresetUpdater* preset_updater{ nullptr }; MainFrame* mainframe{ nullptr }; Plater* plater_{ nullptr }; + PresetUpdater* get_preset_updater() { return preset_updater; } + wxNotebook* tab_panel() const ; int extruders_cnt() const; int extruders_edited_cnt() const; diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index a21194d94e..266472ecaf 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -37,11 +37,17 @@ namespace GUI { static const std::map font_icons = { - {ImGui::PrintIconMarker , "cog" }, - {ImGui::PrinterIconMarker , "printer" }, - {ImGui::PrinterSlaIconMarker, "sla_printer"}, - {ImGui::FilamentIconMarker , "spool" }, - {ImGui::MaterialIconMarker , "resin" } + {ImGui::PrintIconMarker , "cog" }, + {ImGui::PrinterIconMarker , "printer" }, + {ImGui::PrinterSlaIconMarker, "sla_printer" }, + {ImGui::FilamentIconMarker , "spool" }, + {ImGui::MaterialIconMarker , "resin" }, + {ImGui::CloseIconMarker , "cross" }, + {ImGui::CloseIconHoverMarker, "cross_focus_large" }, + {ImGui::TimerDotMarker , "timer_dot" }, + {ImGui::TimerDotEmptyMarker , "timer_dot_empty" }, + {ImGui::WarningMarker , "flag_green" }, + {ImGui::ErrorMarker , "flag_red" } }; ImGuiWrapper::ImGuiWrapper() @@ -265,6 +271,11 @@ void ImGuiWrapper::set_next_window_bg_alpha(float alpha) ImGui::SetNextWindowBgAlpha(alpha); } +void ImGuiWrapper::set_next_window_size(float x, float y, ImGuiCond cond) +{ + ImGui::SetNextWindowSize(ImVec2(x, y), cond); +} + bool ImGuiWrapper::begin(const std::string &name, int flags) { return ImGui::Begin(name.c_str(), nullptr, (ImGuiWindowFlags)flags); @@ -296,12 +307,23 @@ bool ImGuiWrapper::button(const wxString &label) return ImGui::Button(label_utf8.c_str()); } +bool ImGuiWrapper::button(const wxString& label, float width, float height) +{ + auto label_utf8 = into_u8(label); + return ImGui::Button(label_utf8.c_str(), ImVec2(width, height)); +} + bool ImGuiWrapper::radio_button(const wxString &label, bool active) { auto label_utf8 = into_u8(label); return ImGui::RadioButton(label_utf8.c_str(), active); } +bool ImGuiWrapper::image_button() +{ + return false; +} + bool ImGuiWrapper::input_double(const std::string &label, const double &value, const std::string &format) { return ImGui::InputDouble(label.c_str(), const_cast(&value), 0.0f, 0.0f, format.c_str()); diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index dc62e57a08..ee553c4b6d 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -57,6 +57,7 @@ public: void set_next_window_pos(float x, float y, int flag, float pivot_x = 0.0f, float pivot_y = 0.0f); void set_next_window_bg_alpha(float alpha); + void set_next_window_size(float x, float y, ImGuiCond cond); bool begin(const std::string &name, int flags = 0); bool begin(const wxString &name, int flags = 0); @@ -65,7 +66,9 @@ public: void end(); bool button(const wxString &label); + bool button(const wxString& label, float width, float height); bool radio_button(const wxString &label, bool active); + bool image_button(); bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f"); bool input_double(const wxString &label, const double &value, const std::string &format = "%.3f"); bool input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format = "%.3f"); diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index ec7cd8d457..33f0d63793 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -7,6 +7,7 @@ #include "AppConfig.hpp" #include "GLCanvas3D.hpp" #include "Plater.hpp" +#include "NotificationManager.hpp" #include @@ -403,6 +404,8 @@ void Mouse3DController::disconnected() m_params_by_device[m_device_str] = m_params_ui; m_device_str.clear(); m_connected = false; + wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::Mouse3dDisconnected, *(wxGetApp().plater()->get_current_canvas3D())); + wxGetApp().plater()->CallAfter([]() { Plater *plater = wxGetApp().plater(); if (plater != nullptr) { diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp new file mode 100644 index 0000000000..b7301f3d82 --- /dev/null +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -0,0 +1,918 @@ +#include "NotificationManager.hpp" + +#include "GUI_App.hpp" +#include "Plater.hpp" +#include "GLCanvas3D.hpp" +#include "ImGuiWrapper.hpp" + +#include "wxExtensions.hpp" + +#include +#include +#include +#include + + + + +#define NOTIFICATION_MAX_MOVE 3.0f + +#define GAP_WIDTH 10.0f +#define SPACE_RIGHT_PANEL 10.0f + +namespace Slic3r { +namespace GUI { + +wxDEFINE_EVENT(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED, EjectDriveNotificationClickedEvent); +wxDEFINE_EVENT(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED, ExportGcodeNotificationClickedEvent); +wxDEFINE_EVENT(EVT_PRESET_UPDATE_AVIABLE_CLICKED, PresetUpdateAviableClickedEvent); + +namespace Notifications_Internal{ + void push_style_color(ImGuiCol idx, const ImVec4& col, bool fading_out, float current_fade_opacity) + { + if (fading_out) + ImGui::PushStyleColor(idx, ImVec4(col.x, col.y, col.z, col.w * current_fade_opacity)); + else + ImGui::PushStyleColor(idx, col); + } +} +//ScalableBitmap bmp_icon; +//------PopNotification-------- +NotificationManager::PopNotification::PopNotification(const NotificationData &n, const int id, wxEvtHandler* evt_handler) : + m_data (n) + , m_id (id) + , m_remaining_time (n.duration) + , m_last_remaining_time (n.duration) + , m_counting_down (n.duration != 0) + , m_text1 (n.text1) + , m_hypertext (n.hypertext) + , m_text2 (n.text2) + , m_evt_handler (evt_handler) +{ + init(); +} +NotificationManager::PopNotification::~PopNotification() +{ +} +NotificationManager::PopNotification::RenderResult NotificationManager::PopNotification::render(GLCanvas3D& canvas, const float& initial_y) +{ + if (m_finished) + return RenderResult::Finished; + if (m_close_pending) { + // request of extra frame will be done in caller function by ret val ClosePending + m_finished = true; + return RenderResult::ClosePending; + } + if (m_hidden) { + m_top_y = initial_y - GAP_WIDTH; + return RenderResult::Static; + } + RenderResult ret_val = m_counting_down ? RenderResult::Countdown : RenderResult::Static; + Size cnv_size = canvas.get_canvas_size(); + ImGuiWrapper& imgui = *wxGetApp().imgui(); + bool shown = true; + std::string name; + ImVec2 mouse_pos = ImGui::GetMousePos(); + + if (m_line_height != ImGui::CalcTextSize("A").y) + init(); + + set_next_window_size(imgui); + + //top y of window + m_top_y = initial_y + m_window_height; + //top right position + ImVec2 win_pos(1.0f * (float)cnv_size.get_width() - SPACE_RIGHT_PANEL, 1.0f * (float)cnv_size.get_height() - m_top_y); + imgui.set_next_window_pos(win_pos.x, win_pos.y, ImGuiCond_Always, 1.0f, 0.0f); + imgui.set_next_window_size(m_window_width, m_window_height, ImGuiCond_Always); + + //find if hovered + if (mouse_pos.x < win_pos.x && mouse_pos.x > win_pos.x - m_window_width && mouse_pos.y > win_pos.y&& mouse_pos.y < win_pos.y + m_window_height) + { + ImGui::SetNextWindowFocus(); + ret_val = RenderResult::Hovered; + //reset fading + m_fading_out = false; + m_current_fade_opacity = 1.f; + m_remaining_time = m_data.duration; + m_countdown_frame = 0; + } + + if (m_counting_down && m_remaining_time < 0) + m_close_pending = true; + + if (m_close_pending) { + // request of extra frame will be done in caller function by ret val ClosePending + m_finished = true; + return RenderResult::ClosePending; + } + + // color change based on fading out + bool fading_pop = false; + if (m_fading_out) { + if (!m_paused) + m_current_fade_opacity -= 1.f / ((m_fading_time + 1.f) * 60.f); + Notifications_Internal::push_style_color(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_fading_out, m_current_fade_opacity); + Notifications_Internal::push_style_color(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text), m_fading_out, m_current_fade_opacity); + fading_pop = true; + } + // background color + if (m_is_gray) { + ImVec4 backcolor(0.7f, 0.7f, 0.7f, 0.5f); + Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity); + } else if (m_data.level == NotificationLevel::ErrorNotification) { + ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg); + backcolor.x += 0.3f; + Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity); + } else if (m_data.level == NotificationLevel::WarningNotification) { + ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg); + backcolor.x += 0.3f; + backcolor.y += 0.15f; + Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity); + } + + //name of window - probably indentifies window and is shown so last_end add whitespaces according to id + for (size_t i = 0; i < m_id; i++) + name += " "; + if (imgui.begin(name, &shown, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar )) { + if (shown) { + + ImVec2 win_size = ImGui::GetWindowSize(); + + + //FIXME: dont forget to us this for texts + //GUI::format(_utf8(L())); + + /* + //countdown numbers + ImGui::SetCursorPosX(15); + ImGui::SetCursorPosY(15); + imgui.text(std::to_string(m_remaining_time).c_str()); + */ + if(m_counting_down) + render_countdown(imgui, win_size.x, win_size.y, win_pos.x, win_pos.y); + render_left_sign(imgui); + render_text(imgui, win_size.x, win_size.y, win_pos.x, win_pos.y); + render_close_button(imgui, win_size.x, win_size.y, win_pos.x, win_pos.y); + if (m_multiline && m_lines_count > 3) + render_minimize_button(imgui, win_pos.x, win_pos.y); + } else { + // the user clicked on the [X] button ( ImGuiWindowFlags_NoTitleBar means theres no [X] button) + m_close_pending = true; + canvas.set_as_dirty(); + } + } + imgui.end(); + + if (fading_pop) { + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + } + if (m_is_gray) + ImGui::PopStyleColor(); + else if (m_data.level == NotificationLevel::ErrorNotification) + ImGui::PopStyleColor(); + else if (m_data.level == NotificationLevel::WarningNotification) + ImGui::PopStyleColor(); + return ret_val; +} +void NotificationManager::PopNotification::init() +{ + std::string text = m_text1 + " " + m_hypertext; + int last_end = 0; + m_lines_count = 0; + + //determine line width + m_line_height = ImGui::CalcTextSize("A").y; + + m_left_indentation = m_line_height; + if (m_data.level == NotificationLevel::ErrorNotification || m_data.level == NotificationLevel::WarningNotification) { + std::string text; + text = (m_data.level == NotificationLevel::ErrorNotification ? ImGui::ErrorMarker : ImGui::WarningMarker); + float picture_width = ImGui::CalcTextSize(text.c_str()).x; + m_left_indentation = picture_width + m_line_height / 2; + } + m_window_width_offset = m_left_indentation + m_line_height * 2; + m_window_width = m_line_height * 25; + + // count lines + m_endlines.clear(); + while (last_end < text.length() - 1) + { + int next_hard_end = text.find_first_of('\n', last_end); + if (next_hard_end > 0 && ImGui::CalcTextSize(text.substr(last_end, next_hard_end - last_end).c_str()).x < m_window_width - m_window_width_offset) { + //next line is ended by '/n' + m_endlines.push_back(next_hard_end); + last_end = next_hard_end + 1; + } + else { + // find next suitable endline + if (ImGui::CalcTextSize(text.substr(last_end).c_str()).x >= m_window_width - 3.5f * m_line_height) {// m_window_width_offset) { + // more than one line till end + int next_space = text.find_first_of(' ', last_end); + if (next_space > 0) { + int next_space_candidate = text.find_first_of(' ', next_space + 1); + while (next_space_candidate > 0 && ImGui::CalcTextSize(text.substr(last_end, next_space_candidate - last_end).c_str()).x < m_window_width - m_window_width_offset) { + next_space = next_space_candidate; + next_space_candidate = text.find_first_of(' ', next_space + 1); + } + m_endlines.push_back(next_space); + last_end = next_space + 1; + } + } + else { + m_endlines.push_back(text.length()); + last_end = text.length(); + } + + } + m_lines_count++; + } +} +void NotificationManager::PopNotification::set_next_window_size(ImGuiWrapper& imgui) +{ + if (m_multiline) { + m_window_height = m_lines_count * m_line_height; + }else + { + m_window_height = 2 * m_line_height; + } + m_window_height += 1 * m_line_height; // top and bottom +} + +void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) +{ + ImVec2 win_size(win_size_x, win_size_y); + ImVec2 win_pos(win_pos_x, win_pos_y); + float x_offset = m_left_indentation; + std::string fulltext = m_text1 + m_hypertext; //+ m_text2; + ImVec2 text_size = ImGui::CalcTextSize(fulltext.c_str()); + // text posistions are calculated by lines count + // large texts has "more" button or are displayed whole + // smaller texts are divided as one liners and two liners + if (m_lines_count > 2) { + if (m_multiline) { + + int last_end = 0; + float starting_y = m_line_height/2;//10; + float shift_y = m_line_height;// -m_line_height / 20; + for (size_t i = 0; i < m_lines_count; i++) { + std::string line = m_text1.substr(last_end , m_endlines[i] - last_end); + last_end = m_endlines[i] + 1; + ImGui::SetCursorPosX(x_offset); + ImGui::SetCursorPosY(starting_y + i * shift_y); + imgui.text(line.c_str()); + } + //hyperlink text + if (!m_hypertext.empty()) + { + render_hypertext(imgui, x_offset + ImGui::CalcTextSize(m_text1.substr(m_endlines[m_lines_count - 2] + 1, m_endlines[m_lines_count - 1] - m_endlines[m_lines_count - 2] - 1).c_str()).x, starting_y + (m_lines_count - 1) * shift_y, m_hypertext); + } + + + } else { + // line1 + ImGui::SetCursorPosX(x_offset); + ImGui::SetCursorPosY(win_size.y / 2 - win_size.y / 6 - m_line_height / 2); + imgui.text(m_text1.substr(0, m_endlines[0]).c_str()); + // line2 + std::string line = m_text1.substr(m_endlines[0] + 1, m_endlines[1] - m_endlines[0] - 1); + if (ImGui::CalcTextSize(line.c_str()).x > m_window_width - m_window_width_offset - ImGui::CalcTextSize((".." + _u8L("More")).c_str()).x) + { + line = line.substr(0, line.length() - 6); + line += ".."; + }else + line += " "; + ImGui::SetCursorPosX(x_offset); + ImGui::SetCursorPosY(win_size.y / 2 + win_size.y / 6 - m_line_height / 2); + imgui.text(line.c_str()); + // "More" hypertext + render_hypertext(imgui, x_offset + ImGui::CalcTextSize(line.c_str()).x, win_size.y / 2 + win_size.y / 6 - m_line_height / 2, _u8L("More"), true); + } + } else { + //text 1 + float cursor_y = win_size.y / 2 - text_size.y / 2; + float cursor_x = x_offset; + if(m_lines_count > 1) { + // line1 + ImGui::SetCursorPosX(x_offset); + ImGui::SetCursorPosY(win_size.y / 2 - win_size.y / 6 - m_line_height / 2); + imgui.text(m_text1.substr(0, m_endlines[0]).c_str()); + // line2 + std::string line = m_text1.substr(m_endlines[0] + 1); + cursor_y = win_size.y / 2 + win_size.y / 6 - m_line_height / 2; + ImGui::SetCursorPosX(x_offset); + ImGui::SetCursorPosY(cursor_y); + imgui.text(line.c_str()); + cursor_x = x_offset + ImGui::CalcTextSize(line.c_str()).x; + } else { + ImGui::SetCursorPosX(x_offset); + ImGui::SetCursorPosY(cursor_y); + imgui.text(m_text1.c_str()); + cursor_x = x_offset + ImGui::CalcTextSize(m_text1.c_str()).x; + } + //hyperlink text + if (!m_hypertext.empty()) + { + render_hypertext(imgui, cursor_x + 4, cursor_y, m_hypertext); + } + + //notification text 2 + //text 2 is suposed to be after the hyperlink - currently it is not used + /* + if (!m_text2.empty()) + { + ImVec2 part_size = ImGui::CalcTextSize(m_hypertext.c_str()); + ImGui::SetCursorPosX(win_size.x / 2 + text_size.x / 2 - part_size.x + 8 - x_offset); + ImGui::SetCursorPosY(cursor_y); + imgui.text(m_text2.c_str()); + } + */ + } +} + +void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui, const float text_x, const float text_y, const std::string text, bool more) +{ + //invisible button + ImVec2 part_size = ImGui::CalcTextSize(text.c_str()); + ImGui::SetCursorPosX(text_x -4); + ImGui::SetCursorPosY(text_y -5); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f)); + if (imgui.button(" ", part_size.x + 6, part_size.y + 10)) + { + if (more) + { + m_multiline = true; + set_next_window_size(imgui); + } + else { + on_text_click(); + m_close_pending = true; + } + } + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + + //hover color + ImVec4 orange_color = ImGui::GetStyleColorVec4(ImGuiCol_Button); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly)) + orange_color.y += 0.2f; + + //text + Notifications_Internal::push_style_color(ImGuiCol_Text, orange_color, m_fading_out, m_current_fade_opacity); + ImGui::SetCursorPosX(text_x); + ImGui::SetCursorPosY(text_y); + imgui.text(text.c_str()); + ImGui::PopStyleColor(); + + //underline + ImVec2 lineEnd = ImGui::GetItemRectMax(); + lineEnd.y -= 2; + ImVec2 lineStart = lineEnd; + lineStart.x = ImGui::GetItemRectMin().x; + ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (int)(orange_color.w * 255.f * (m_fading_out ? m_current_fade_opacity : 1.f)))); + +} + +void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) +{ + ImVec2 win_size(win_size_x, win_size_y); + ImVec2 win_pos(win_pos_x, win_pos_y); + ImVec4 orange_color = ImGui::GetStyleColorVec4(ImGuiCol_Button); + orange_color.w = 0.8f; + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f)); + Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_fading_out, m_current_fade_opacity); + Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_fading_out, m_current_fade_opacity); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f)); + + + //button - if part if treggered + std::string button_text; + button_text = ImGui::CloseIconMarker; + + if (ImGui::IsMouseHoveringRect(ImVec2(win_pos.x - win_size.x / 10.f, win_pos.y), + ImVec2(win_pos.x, win_pos.y + win_size.y - (m_multiline? 2 * m_line_height : 0)), + true)) + { + button_text = ImGui::CloseIconHoverMarker; + } + ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str()); + ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f); + ImGui::SetCursorPosX(win_size.x - m_line_height * 2.25f); + ImGui::SetCursorPosY(win_size.y / 2 - button_size.y/2); + if (imgui.button(button_text.c_str(), button_size.x, button_size.y)) + { + m_close_pending = true; + } + + //invisible large button + ImGui::SetCursorPosX(win_size.x - win_size.x / 10.f); + ImGui::SetCursorPosY(0); + if (imgui.button(" ", win_size.x / 10.f, win_size.y - (m_multiline ? 2 * m_line_height : 0))) + { + m_close_pending = true; + } + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); +} +void NotificationManager::PopNotification::render_countdown(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) +{ + /* + ImVec2 win_size(win_size_x, win_size_y); + ImVec2 win_pos(win_pos_x, win_pos_y); + + //countdown dots + std::string dot_text; + dot_text = m_remaining_time <= (float)m_data.duration / 4 * 3 ? ImGui::TimerDotEmptyMarker : ImGui::TimerDotMarker; + ImGui::SetCursorPosX(win_size.x - m_line_height); + //ImGui::SetCursorPosY(win_size.y / 2 - 24); + ImGui::SetCursorPosY(0); + imgui.text(dot_text.c_str()); + + dot_text = m_remaining_time < m_data.duration / 2 ? ImGui::TimerDotEmptyMarker : ImGui::TimerDotMarker; + ImGui::SetCursorPosX(win_size.x - m_line_height); + //ImGui::SetCursorPosY(win_size.y / 2 - 9); + ImGui::SetCursorPosY(win_size.y / 2 - m_line_height / 2); + imgui.text(dot_text.c_str()); + + dot_text = m_remaining_time <= m_data.duration / 4 ? ImGui::TimerDotEmptyMarker : ImGui::TimerDotMarker; + ImGui::SetCursorPosX(win_size.x - m_line_height); + //ImGui::SetCursorPosY(win_size.y / 2 + 6); + ImGui::SetCursorPosY(win_size.y - m_line_height); + imgui.text(dot_text.c_str()); + */ + if (!m_fading_out && m_remaining_time <= m_data.duration / 4) { + m_fading_out = true; + m_fading_time = m_remaining_time; + } + + if (m_last_remaining_time != m_remaining_time) { + m_last_remaining_time = m_remaining_time; + m_countdown_frame = 0; + } + /* + //countdown line + ImVec4 orange_color = ImGui::GetStyleColorVec4(ImGuiCol_Button); + float invisible_length = ((float)(m_data.duration - m_remaining_time) / (float)m_data.duration * win_size_x); + invisible_length -= win_size_x / ((float)m_data.duration * 60.f) * (60 - m_countdown_frame); + ImVec2 lineEnd = ImVec2(win_pos_x - invisible_length, win_pos_y + win_size_y - 5); + ImVec2 lineStart = ImVec2(win_pos_x - win_size_x, win_pos_y + win_size_y - 5); + ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (int)(orange_color.picture_width * 255.f * (m_fading_out ? m_current_fade_opacity : 1.f))), 2.f); + if (!m_paused) + m_countdown_frame++; + */ +} +void NotificationManager::PopNotification::render_left_sign(ImGuiWrapper& imgui) +{ + if (m_data.level == NotificationLevel::ErrorNotification || m_data.level == NotificationLevel::WarningNotification) { + std::string text; + text = (m_data.level == NotificationLevel::ErrorNotification ? ImGui::ErrorMarker : ImGui::WarningMarker); + ImGui::SetCursorPosX(m_line_height / 3); + ImGui::SetCursorPosY(m_window_height / 2 - m_line_height / 2); + imgui.text(text.c_str()); + } +} +void NotificationManager::PopNotification::render_minimize_button(ImGuiWrapper& imgui, const float win_pos_x, const float win_pos_y) +{ + ImVec4 orange_color = ImGui::GetStyleColorVec4(ImGuiCol_Button); + orange_color.w = 0.8f; + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f)); + Notifications_Internal::push_style_color(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_fading_out, m_current_fade_opacity); + Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_fading_out, m_current_fade_opacity); + Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_fading_out, m_current_fade_opacity); + + + //button - if part if treggered + std::string button_text; + button_text = ImGui::CloseIconMarker; + if (ImGui::IsMouseHoveringRect(ImVec2(win_pos_x - m_window_width / 10.f, win_pos_y + m_window_height - 2 * m_line_height + 1), + ImVec2(win_pos_x, win_pos_y + m_window_height), + true)) + { + button_text = ImGui::CloseIconHoverMarker; + } + ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str()); + ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f); + ImGui::SetCursorPosX(m_window_width - m_line_height * 2.25f); + ImGui::SetCursorPosY(m_window_height - button_size.y - 5); + if (imgui.button(button_text.c_str(), button_size.x, button_size.y)) + { + m_multiline = false; + } + + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); +} +void NotificationManager::PopNotification::on_text_click() +{ + switch (m_data.type) { + case NotificationType::ExportToRemovableFinished : + assert(m_evt_handler != nullptr); + if (m_evt_handler != nullptr) + wxPostEvent(m_evt_handler, EjectDriveNotificationClickedEvent(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED)); + break; + case NotificationType::SlicingComplete : + //wxGetApp().plater()->export_gcode(false); + assert(m_evt_handler != nullptr); + if (m_evt_handler != nullptr) + wxPostEvent(m_evt_handler, ExportGcodeNotificationClickedEvent(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED)); + break; + case NotificationType::PresetUpdateAviable : + //wxGetApp().plater()->export_gcode(false); + assert(m_evt_handler != nullptr); + if (m_evt_handler != nullptr) + wxPostEvent(m_evt_handler, PresetUpdateAviableClickedEvent(EVT_PRESET_UPDATE_AVIABLE_CLICKED)); + break; + case NotificationType::NewAppAviable: + wxLaunchDefaultBrowser("https://github.com/prusa3d/PrusaSlicer/releases"); + break; + default: + break; + } +} +void NotificationManager::PopNotification::update(const NotificationData& n) +{ + m_text1 = n.text1; + m_hypertext = n.hypertext; + m_text2 = n.text2; + init(); +} +bool NotificationManager::PopNotification::compare_text(const std::string& text) +{ + std::string t1(m_text1); + std::string t2(text); + t1.erase(std::remove_if(t1.begin(), t1.end(), ::isspace), t1.end()); + t2.erase(std::remove_if(t2.begin(), t2.end(), ::isspace), t2.end()); + if (t1.compare(t2) == 0) + return true; + return false; +} + +NotificationManager::SlicingCompleteLargeNotification::SlicingCompleteLargeNotification(const NotificationData& n, const int id, wxEvtHandler* evt_handler, bool large) : + NotificationManager::PopNotification(n, id, evt_handler) +{ + set_large(large); +} +void NotificationManager::SlicingCompleteLargeNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) +{ + if (!m_is_large) + PopNotification::render_text(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); + else { + ImVec2 win_size(win_size_x, win_size_y); + ImVec2 win_pos(win_pos_x, win_pos_y); + + ImVec2 text1_size = ImGui::CalcTextSize(m_text1.c_str()); + float x_offset = m_left_indentation; + std::string fulltext = m_text1 + m_hypertext + m_text2; + ImVec2 text_size = ImGui::CalcTextSize(fulltext.c_str()); + float cursor_y = win_size.y / 2 - text_size.y / 2; + if (m_has_print_info) { + x_offset = 20; + cursor_y = win_size.y / 2 + win_size.y / 6 - text_size.y / 2; + ImGui::SetCursorPosX(x_offset); + ImGui::SetCursorPosY(cursor_y); + imgui.text(m_print_info.c_str()); + cursor_y = win_size.y / 2 - win_size.y / 6 - text_size.y / 2; + } + ImGui::SetCursorPosX(x_offset); + ImGui::SetCursorPosY(cursor_y); + imgui.text(m_text1.c_str()); + + render_hypertext(imgui, x_offset + text1_size.x + 4, cursor_y, m_hypertext); + + } +} +void NotificationManager::SlicingCompleteLargeNotification::set_print_info(std::string info) +{ + m_print_info = info; + m_has_print_info = true; + if(m_is_large) + m_lines_count = 2; +} +void NotificationManager::SlicingCompleteLargeNotification::set_large(bool l) +{ + m_is_large = l; + m_counting_down = !l; + m_hypertext = l ? _u8L("Export G-Code.") : std::string(); + m_hidden = !l; +} +//------NotificationManager-------- +NotificationManager::NotificationManager(wxEvtHandler* evt_handler) : + m_evt_handler(evt_handler) +{ +} +NotificationManager::~NotificationManager() +{ + for (PopNotification* notification : m_pop_notifications) + { + delete notification; + } +} +void NotificationManager::push_notification(const NotificationType type, GLCanvas3D& canvas, int timestamp) +{ + auto it = std::find_if(basic_notifications.begin(), basic_notifications.end(), + boost::bind(&NotificationData::type, _1) == type); + if (it != basic_notifications.end()) + push_notification_data( *it, canvas, timestamp); +} +void NotificationManager::push_notification(const std::string& text, GLCanvas3D& canvas, int timestamp) +{ + push_notification_data({ NotificationType::CustomNotification, NotificationLevel::RegularNotification, 10, text }, canvas, timestamp ); +} +void NotificationManager::push_notification(const std::string& text, NotificationManager::NotificationLevel level, GLCanvas3D& canvas, int timestamp) +{ + switch (level) + { + case Slic3r::GUI::NotificationManager::NotificationLevel::RegularNotification: + push_notification_data({ NotificationType::CustomNotification, level, 10, text }, canvas, timestamp); + break; + case Slic3r::GUI::NotificationManager::NotificationLevel::ErrorNotification: + push_notification_data({ NotificationType::CustomNotification, level, 0, text }, canvas, timestamp); + + break; + case Slic3r::GUI::NotificationManager::NotificationLevel::ImportantNotification: + push_notification_data({ NotificationType::CustomNotification, level, 0, text }, canvas, timestamp); + break; + default: + break; + } +} +void NotificationManager::push_slicing_error_notification(const std::string& text, GLCanvas3D& canvas) +{ + set_all_slicing_errors_gray(false); + push_notification_data({ NotificationType::SlicingError, NotificationLevel::ErrorNotification, 0, _u8L("ERROR:") + "\n" + text }, canvas, 0); + close_notification_of_type(NotificationType::SlicingComplete); +} +void NotificationManager::push_slicing_warning_notification(const std::string& text, bool gray, GLCanvas3D& canvas, size_t oid, int warning_step) +{ + NotificationData data { NotificationType::SlicingWarning, NotificationLevel::WarningNotification, 0, _u8L("WARNING:") + "\n" + text }; + + NotificationManager::SlicingWarningNotification* notification = new NotificationManager::SlicingWarningNotification(data, m_next_id++, m_evt_handler); + notification->set_object_id(oid); + notification->set_warning_step(warning_step); + if + (push_notification_data(notification, canvas, 0)) { + notification->set_gray(gray); + } + else { + delete notification; + } + +} +void NotificationManager::push_plater_error_notification(const std::string& text, GLCanvas3D& canvas) +{ + push_notification_data({ NotificationType::PlaterError, NotificationLevel::ErrorNotification, 0, _u8L("ERROR:") + "\n" + text }, canvas, 0); +} +void NotificationManager::push_plater_warning_notification(const std::string& text, GLCanvas3D& canvas) +{ + push_notification_data({ NotificationType::PlaterWarning, NotificationLevel::WarningNotification, 0, _u8L("WARNING:") + "\n" + text }, canvas, 0); +} +void NotificationManager::close_plater_error_notification() +{ + for (PopNotification* notification : m_pop_notifications) { + if (notification->get_type() == NotificationType::PlaterError) { + notification->close(); + } + } +} +void NotificationManager::close_plater_warning_notification(const std::string& text) +{ + for (PopNotification* notification : m_pop_notifications) { + if (notification->get_type() == NotificationType::PlaterWarning && notification->compare_text(_u8L("WARNING:") + "\n" + text)) { + notification->close(); + } + } +} +void NotificationManager::set_all_slicing_errors_gray(bool g) +{ + for (PopNotification* notification : m_pop_notifications) { + if (notification->get_type() == NotificationType::SlicingError) { + notification->set_gray(g); + } + } +} +void NotificationManager::set_all_slicing_warnings_gray(bool g) +{ + for (PopNotification* notification : m_pop_notifications) { + if (notification->get_type() == NotificationType::SlicingWarning) { + notification->set_gray(g); + } + } +} +void NotificationManager::set_slicing_warning_gray(const std::string& text, bool g) +{ + for (PopNotification* notification : m_pop_notifications) { + if (notification->get_type() == NotificationType::SlicingWarning && notification->compare_text(text)) { + notification->set_gray(g); + } + } +} +void NotificationManager::close_slicing_errors_and_warnings() +{ + for (PopNotification* notification : m_pop_notifications) { + if (notification->get_type() == NotificationType::SlicingError || notification->get_type() == NotificationType::SlicingWarning) { + notification->close(); + } + } +} +void NotificationManager::push_slicing_complete_notification(GLCanvas3D& canvas, int timestamp, bool large) +{ + std::string hypertext; + int time = 10; + if(large) + { + hypertext = _u8L("Export G-Code."); + time = 0; + } + NotificationData data{ NotificationType::SlicingComplete, NotificationLevel::RegularNotification, time, _u8L("Slicing finished."), hypertext }; + + NotificationManager::SlicingCompleteLargeNotification* notification = new NotificationManager::SlicingCompleteLargeNotification(data, m_next_id++, m_evt_handler, large); + if (push_notification_data(notification, canvas, timestamp)) { + } else { + delete notification; + } +} +void NotificationManager::set_slicing_complete_print_time(std::string info) +{ + for (PopNotification* notification : m_pop_notifications) { + if (notification->get_type() == NotificationType::SlicingComplete) { + dynamic_cast(notification)->set_print_info(info); + break; + } + } +} +void NotificationManager::set_slicing_complete_large(bool large) +{ + for (PopNotification* notification : m_pop_notifications) { + if (notification->get_type() == NotificationType::SlicingComplete) { + dynamic_cast(notification)->set_large(large); + break; + } + } +} +void NotificationManager::close_notification_of_type(const NotificationType type) +{ + for (PopNotification* notification : m_pop_notifications) { + if (notification->get_type() == type) { + notification->close(); + } + } +} +void NotificationManager::compare_warning_oids(const std::vector& living_oids) +{ + for (PopNotification* notification : m_pop_notifications) { + if (notification->get_type() == NotificationType::SlicingWarning) { + auto w = dynamic_cast(notification); + bool found = false; + for (size_t oid : living_oids) { + if (w->get_object_id() == oid) { + found = true; + break; + } + } + if (!found) + notification->close(); + } + } +} +bool NotificationManager::push_notification_data(const NotificationData ¬ification_data, GLCanvas3D& canvas, int timestamp) +{ + PopNotification* n = new PopNotification(notification_data, m_next_id++, m_evt_handler); + bool r = push_notification_data(n, canvas, timestamp); + if (!r) + delete n; + return r; +} +bool NotificationManager::push_notification_data(NotificationManager::PopNotification* notification, GLCanvas3D& canvas, int timestamp) +{ + // if timestamped notif, push only new one + if (timestamp != 0) { + if (m_used_timestamps.find(timestamp) == m_used_timestamps.end()) { + m_used_timestamps.insert(timestamp); + } else { + return false; + } + } + if (!this->find_older(notification)) { + m_pop_notifications.emplace_back(notification); + canvas.request_extra_frame(); + return true; + } else { + m_pop_notifications.back()->update(notification->get_data()); + canvas.request_extra_frame(); + return false; + } +} +void NotificationManager::render_notifications(GLCanvas3D& canvas) +{ + float last_x = 0.0f; + float current_height = 0.0f; + bool request_next_frame = false; + bool render_main = false; + bool hovered = false; + sort_notifications(); + // iterate thru notifications and render them / erease them + for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) { + if ((*it)->get_finished()) { + delete (*it); + it = m_pop_notifications.erase(it); + } else { + (*it)->set_paused(m_hovered); + PopNotification::RenderResult res = (*it)->render(canvas, last_x); + if (res != PopNotification::RenderResult::Finished) { + last_x = (*it)->get_top() + GAP_WIDTH; + current_height = std::max(current_height, (*it)->get_current_top()); + render_main = true; + } + if (res == PopNotification::RenderResult::Countdown || res == PopNotification::RenderResult::ClosePending || res == PopNotification::RenderResult::Finished) + request_next_frame = true; + if (res == PopNotification::RenderResult::Hovered) + hovered = true; + ++it; + } + } + m_hovered = hovered; + + //actualizate timers and request frame if needed + wxWindow* p = dynamic_cast (wxGetApp().plater()); + while (p->GetParent()) + p = p->GetParent(); + wxTopLevelWindow* top_level_wnd = dynamic_cast(p); + if (!top_level_wnd->IsActive()) + return; + + if (!m_hovered && m_last_time < wxGetLocalTime()) + { + if (wxGetLocalTime() - m_last_time == 1) + { + for(auto notification : m_pop_notifications) + { + notification->substract_remaining_time(); + } + } + m_last_time = wxGetLocalTime(); + } + + if (request_next_frame) + canvas.request_extra_frame(); +} + + +void NotificationManager::sort_notifications() +{ + std::sort(m_pop_notifications.begin(), m_pop_notifications.end(), [](PopNotification* n1, PopNotification* n2) { + int n1l = (int)n1->get_data().level; + int n2l = (int)n2->get_data().level; + if (n1l == n2l && n1->get_is_gray() && !n2->get_is_gray()) + return true; + return (n1l < n2l); + }); +} + +bool NotificationManager::find_older(NotificationManager::PopNotification* notification) +{ + NotificationType type = notification->get_type(); + std::string text = notification->get_data().text1; + for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end(); ++it) { + if((*it)->get_type() == type && !(*it)->get_finished()) { + if (type == NotificationType::CustomNotification || type == NotificationType::PlaterWarning) { + if (!(*it)->compare_text(text)) + continue; + }else if (type == NotificationType::SlicingWarning) { + auto w1 = dynamic_cast(notification); + auto w2 = dynamic_cast(*it); + if (w1 != nullptr && w2 != nullptr) { + if (!(*it)->compare_text(text) || w1->get_object_id() != w2->get_object_id()) { + continue; + } + } else { + continue; + } + } + + if (it != m_pop_notifications.end() - 1) + std::rotate(it, it + 1, m_pop_notifications.end()); + return true; + } + } + return false; +} + +void NotificationManager::dpi_changed() +{ + +} + +}//namespace GUI +}//namespace Slic3r diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp new file mode 100644 index 0000000000..d7037c53e4 --- /dev/null +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -0,0 +1,268 @@ +#ifndef slic3r_GUI_NotificationManager_hpp_ +#define slic3r_GUI_NotificationManager_hpp_ + +#include "Event.hpp" +#include "I18N.hpp" + +#include +#include +#include +#include + +namespace Slic3r { +namespace GUI { + +using EjectDriveNotificationClickedEvent = SimpleEvent; +wxDECLARE_EVENT(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED, EjectDriveNotificationClickedEvent); +using ExportGcodeNotificationClickedEvent = SimpleEvent; +wxDECLARE_EVENT(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED, ExportGcodeNotificationClickedEvent); +using PresetUpdateAviableClickedEvent = SimpleEvent; +wxDECLARE_EVENT(EVT_PRESET_UPDATE_AVIABLE_CLICKED, PresetUpdateAviableClickedEvent); + +class GLCanvas3D; +class ImGuiWrapper; + +enum class NotificationType +{ + CustomNotification, + SlicingComplete, + SlicingNotPossible, + ExportToRemovableFinished, + Mouse3dDisconnected, + Mouse3dConnected, + NewPresetsAviable, + NewAppAviable, + PresetUpdateAviable, + LoadingFailed, + ValidateError, // currently not used - instead Slicing error is used for both slicing and validate errors + SlicingError, + SlicingWarning, + PlaterError, + PlaterWarning, + ApplyError + +}; +class NotificationManager +{ +public: + enum class NotificationLevel : int + { + ErrorNotification = 4, + WarningNotification = 3, + ImportantNotification = 2, + RegularNotification = 1, + }; + // duration 0 means not disapearing + struct NotificationData { + NotificationType type; + NotificationLevel level; + const int duration; + const std::string text1; + const std::string hypertext = std::string(); + const std::string text2 = std::string(); + }; + + //Pop notification - shows only once to user. + class PopNotification + { + public: + enum class RenderResult + { + Finished, + ClosePending, + Static, + Countdown, + Hovered + }; + PopNotification(const NotificationData &n, const int id, wxEvtHandler* evt_handler); + virtual ~PopNotification(); + RenderResult render(GLCanvas3D& canvas, const float& initial_y); + // close will dissapear notification on next render + void close() { m_close_pending = true; } + // data from newer notification of same type + void update(const NotificationData& n); + bool get_finished() const { return m_finished; } + // returns top after movement + float get_top() const { return m_top_y; } + //returns top in actual frame + float get_current_top() const { return m_top_y; } + const NotificationType get_type() const { return m_data.type; } + const NotificationData get_data() const { return m_data; } + const bool get_is_gray() const { return m_is_gray; } + // Call equals one second down + void substract_remaining_time() { m_remaining_time--; } + void set_gray(bool g) { m_is_gray = g; } + void set_paused(bool p) { m_paused = p; } + bool compare_text(const std::string& text); + protected: + // Call after every size change + void init(); + // Calculetes correct size but not se it in imgui! + virtual void set_next_window_size(ImGuiWrapper& imgui); + virtual void render_text(ImGuiWrapper& imgui, + const float win_size_x, const float win_size_y, + const float win_pos_x , const float win_pos_y); + void render_close_button(ImGuiWrapper& imgui, + const float win_size_x, const float win_size_y, + const float win_pos_x , const float win_pos_y); + void render_countdown(ImGuiWrapper& imgui, + const float win_size_x, const float win_size_y, + const float win_pos_x , const float win_pos_y); + void render_hypertext(ImGuiWrapper& imgui, + const float text_x, const float text_y, + const std::string text, + bool more = false); + void render_left_sign(ImGuiWrapper& imgui); + void render_minimize_button(ImGuiWrapper& imgui, + const float win_pos_x, const float win_pos_y); + void on_text_click(); + + const NotificationData m_data; + + int m_id; + // Main text + std::string m_text1; + // Clickable text + std::string m_hypertext; + // Aditional text after hypertext - currently not used + std::string m_text2; + // Countdown variables + long m_remaining_time; + bool m_counting_down; + long m_last_remaining_time; + bool m_paused{ false }; + int m_countdown_frame{ 0 }; + bool m_fading_out{ false }; + // total time left when fading beggins + float m_fading_time{ 0.0f }; + float m_current_fade_opacity{ 1.f }; + // If hidden the notif is alive but not visible to user + bool m_hidden { false }; + // m_finished = true - does not render, marked to delete + bool m_finished { false }; + // Will go to m_finished next render + bool m_close_pending { false }; + // variables to count positions correctly + float m_window_width_offset; + float m_left_indentation; + // Total size of notification window - varies based on monitor + float m_window_height { 56.0f }; + float m_window_width { 450.0f }; + //Distance from bottom of notifications to top of this notification + float m_top_y { 0.0f }; + + // Height of text + // Used as basic scaling unit! + float m_line_height; + std::vector m_endlines; + // Gray are f.e. eorrors when its uknown if they are still valid + bool m_is_gray { false }; + //if multiline = true, notification is showing all lines(>2) + bool m_multiline { false }; + int m_lines_count{ 1 }; + wxEvtHandler* m_evt_handler; + }; + + class SlicingCompleteLargeNotification : public PopNotification + { + public: + SlicingCompleteLargeNotification(const NotificationData& n, const int id, wxEvtHandler* evt_handler, bool largeds); + void set_large(bool l); + bool get_large() { return m_is_large; } + + void set_print_info(std::string info); + protected: + virtual void render_text(ImGuiWrapper& imgui, + const float win_size_x, const float win_size_y, + const float win_pos_x, const float win_pos_y) + override; + + bool m_is_large; + bool m_has_print_info { false }; + std::string m_print_info { std::string() }; + }; + + class SlicingWarningNotification : public PopNotification + { + public: + SlicingWarningNotification(const NotificationData& n, const int id, wxEvtHandler* evt_handler) : PopNotification(n, id, evt_handler) {} + void set_object_id(size_t id) { object_id = id; } + const size_t get_object_id() { return object_id; } + void set_warning_step(int ws) { warning_step = ws; } + const int get_warning_step() { return warning_step; } + protected: + size_t object_id; + int warning_step; + }; + + NotificationManager(wxEvtHandler* evt_handler); + ~NotificationManager(); + + + // only type means one of basic_notification (see below) + void push_notification(const NotificationType type, GLCanvas3D& canvas, int timestamp = 0); + // only text means Undefined type + void push_notification(const std::string& text, GLCanvas3D& canvas, int timestamp = 0); + void push_notification(const std::string& text, NotificationLevel level, GLCanvas3D& canvas, int timestamp = 0); + // creates Slicing Error notification with custom text + void push_slicing_error_notification(const std::string& text, GLCanvas3D& canvas); + // creates Slicing Warning notification with custom text + void push_slicing_warning_notification(const std::string& text, bool gray, GLCanvas3D& canvas, size_t oid, int warning_step); + // marks slicing errors as gray + void set_all_slicing_errors_gray(bool g); + // marks slicing warings as gray + void set_all_slicing_warnings_gray(bool g); + void set_slicing_warning_gray(const std::string& text, bool g); + // imidietly stops showing slicing errors + void close_slicing_errors_and_warnings(); + void compare_warning_oids(const std::vector& living_oids); + void push_plater_error_notification(const std::string& text, GLCanvas3D& canvas); + void push_plater_warning_notification(const std::string& text, GLCanvas3D& canvas); + void close_plater_error_notification(); + void close_plater_warning_notification(const std::string& text); + // creates special notification slicing complete + // if large = true prints printing time and export button + void push_slicing_complete_notification(GLCanvas3D& canvas, int timestamp, bool large); + void set_slicing_complete_print_time(std::string info); + void set_slicing_complete_large(bool large); + // renders notifications in queue and deletes expired ones + void render_notifications(GLCanvas3D& canvas); + // finds and closes all notifications of given type + void close_notification_of_type(const NotificationType type); + void dpi_changed(); +private: + //pushes notification into the queue of notifications that are rendered + //can be used to create custom notification + bool push_notification_data(const NotificationData& notification_data, GLCanvas3D& canvas, int timestamp); + bool push_notification_data(NotificationManager::PopNotification* notification, GLCanvas3D& canvas, int timestamp); + //finds older notification of same type and moves it to the end of queue. returns true if found + bool find_older(NotificationManager::PopNotification* notification); + void sort_notifications(); + + wxEvtHandler* m_evt_handler; + std::deque m_pop_notifications; + int m_next_id { 1 }; + long m_last_time { 0 }; + bool m_hovered { false }; + //timestamps used for slining finished - notification could be gone so it needs to be stored here + std::unordered_set m_used_timestamps; + + //prepared (basic) notifications + const std::vector basic_notifications = { + {NotificationType::SlicingNotPossible, NotificationLevel::RegularNotification, 10, _u8L("Slicing is not possible.")}, + {NotificationType::ExportToRemovableFinished, NotificationLevel::ImportantNotification, 0, _u8L("Exporting finished."), _u8L("Eject drive.") }, + {NotificationType::Mouse3dDisconnected, NotificationLevel::RegularNotification, 10, _u8L("3D Mouse disconnected.") }, + {NotificationType::Mouse3dConnected, NotificationLevel::RegularNotification, 5, _u8L("3D Mouse connected.") }, + {NotificationType::NewPresetsAviable, NotificationLevel::ImportantNotification, 20, _u8L("New Presets are available."), _u8L("See here.") }, + {NotificationType::PresetUpdateAviable, NotificationLevel::ImportantNotification, 20, _u8L("Configuration update is available."), _u8L("See more.")}, + {NotificationType::NewAppAviable, NotificationLevel::ImportantNotification, 20, _u8L("New version is available."), _u8L("See Releases page.")}, + //{NotificationType::NewAppAviable, NotificationLevel::ImportantNotification, 20, _u8L("New vesion of PrusaSlicer is available.", _u8L("Download page.") }, + //{NotificationType::LoadingFailed, NotificationLevel::RegularNotification, 20, _u8L("Loading of model has Failed") }, + //{NotificationType::DeviceEjected, NotificationLevel::RegularNotification, 10, _u8L("Removable device has been safely ejected")} // if we want changeble text (like here name of device), we need to do it as CustomNotification + }; +}; + +}//namespace GUI +}//namespace Slic3r + +#endif //slic3r_GUI_NotificationManager_hpp_ \ No newline at end of file diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 761f574e15..9cfc717db9 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -75,8 +75,10 @@ #include "../Utils/PrintHost.hpp" #include "../Utils/FixModelByWin10.hpp" #include "../Utils/UndoRedo.hpp" +#include "../Utils/PresetUpdater.hpp" #include "RemovableDriveManager.hpp" #include "InstanceCheck.hpp" +#include "NotificationManager.hpp" #ifdef __APPLE__ #include "Gizmos/GLGizmosManager.hpp" @@ -102,6 +104,7 @@ wxDEFINE_EVENT(EVT_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); wxDEFINE_EVENT(EVT_SLICING_UPDATE, SlicingStatusEvent); wxDEFINE_EVENT(EVT_SLICING_COMPLETED, wxCommandEvent); wxDEFINE_EVENT(EVT_PROCESS_COMPLETED, wxCommandEvent); +wxDEFINE_EVENT(EVT_EXPORT_BEGAN, wxCommandEvent); // Sidebar widgets @@ -716,7 +719,7 @@ struct Sidebar::priv wxButton *btn_export_gcode; wxButton *btn_reslice; ScalableButton *btn_send_gcode; - ScalableButton *btn_remove_device; + ScalableButton *btn_eject_device; ScalableButton* btn_export_gcode_removable; //exports to removable drives (appears only if removable drive is connected) bool is_collapsed {false}; @@ -889,12 +892,12 @@ Sidebar::Sidebar(Plater *parent) }; init_scalable_btn(&p->btn_send_gcode , "export_gcode", _L("Send to printer") + "\tCtrl+Shift+G"); - init_scalable_btn(&p->btn_remove_device, "eject_sd" , _L("Remove device") + "\tCtrl+T"); + init_scalable_btn(&p->btn_eject_device, "eject_sd" , _L("Remove device") + "\tCtrl+T"); init_scalable_btn(&p->btn_export_gcode_removable, "export_to_sd", _L("Export to SD card / Flash drive") + "\tCtrl+U"); // regular buttons "Slice now" and "Export G-code" - const int scaled_height = p->btn_remove_device->GetBitmapHeight() + 4; + const int scaled_height = p->btn_eject_device->GetBitmapHeight() + 4; auto init_btn = [this](wxButton **btn, wxString label, const int button_height) { *btn = new wxButton(this, wxID_ANY, label, wxDefaultPosition, wxSize(-1, button_height), wxBU_EXACTFIT); @@ -912,7 +915,7 @@ Sidebar::Sidebar(Plater *parent) complect_btns_sizer->Add(p->btn_export_gcode, 1, wxEXPAND); complect_btns_sizer->Add(p->btn_send_gcode); complect_btns_sizer->Add(p->btn_export_gcode_removable); - complect_btns_sizer->Add(p->btn_remove_device); + complect_btns_sizer->Add(p->btn_eject_device); btns_sizer->Add(p->btn_reslice, 0, wxEXPAND | wxTOP, margin_5); @@ -935,7 +938,7 @@ Sidebar::Sidebar(Plater *parent) p->plater->select_view_3D("Preview"); }); p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->send_gcode(); }); - p->btn_remove_device->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->eject_drive(); }); + p->btn_eject_device->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->eject_drive(); }); p->btn_export_gcode_removable->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(true); }); } @@ -1083,9 +1086,9 @@ void Sidebar::msw_rescale() p->object_info->msw_rescale(); p->btn_send_gcode->msw_rescale(); - p->btn_remove_device->msw_rescale(); + p->btn_eject_device->msw_rescale(); p->btn_export_gcode_removable->msw_rescale(); - const int scaled_height = p->btn_remove_device->GetBitmap().GetHeight() + 4; + const int scaled_height = p->btn_eject_device->GetBitmap().GetHeight() + 4; p->btn_export_gcode->SetMinSize(wxSize(-1, scaled_height)); p->btn_reslice ->SetMinSize(wxSize(-1, scaled_height)); @@ -1114,7 +1117,7 @@ void Sidebar::sys_color_changed() // btn...->msw_rescale() updates icon on button, so use it p->btn_send_gcode->msw_rescale(); - p->btn_remove_device->msw_rescale(); + p->btn_eject_device->msw_rescale(); p->btn_export_gcode_removable->msw_rescale(); p->scrolled->Layout(); @@ -1350,6 +1353,12 @@ void Sidebar::update_sliced_info_sizer() new_label += format_wxstr("\n - %1%", _L("normal mode")); info_text += format_wxstr("\n%1%", ps.estimated_normal_print_time); fill_labels(ps.estimated_normal_custom_gcode_print_times, new_label, info_text); + + // uncomment next line to not disappear slicing finished notif when colapsing sidebar before time estimate + //if (p->plater->is_sidebar_collapsed()) + p->plater->get_notification_manager()->set_slicing_complete_large(p->plater->is_sidebar_collapsed()); + p->plater->get_notification_manager()->set_slicing_complete_print_time("Estimated printing time: " + ps.estimated_normal_print_time); + } if (ps.estimated_silent_print_time != "N/A") { new_label += format_wxstr("\n - %1%", _L("stealth mode")); @@ -1385,15 +1394,16 @@ void Sidebar::enable_buttons(bool enable) p->btn_reslice->Enable(enable); p->btn_export_gcode->Enable(enable); p->btn_send_gcode->Enable(enable); - p->btn_remove_device->Enable(enable); + p->btn_eject_device->Enable(enable); p->btn_export_gcode_removable->Enable(enable); } -bool Sidebar::show_reslice(bool show) const { return p->btn_reslice->Show(show); } -bool Sidebar::show_export(bool show) const { return p->btn_export_gcode->Show(show); } -bool Sidebar::show_send(bool show) const { return p->btn_send_gcode->Show(show); } -bool Sidebar::show_disconnect(bool show) const { return p->btn_remove_device->Show(show); } -bool Sidebar::show_export_removable(bool show)const { return p->btn_export_gcode_removable->Show(show); } +bool Sidebar::show_reslice(bool show) const { return p->btn_reslice->Show(show); } +bool Sidebar::show_export(bool show) const { return p->btn_export_gcode->Show(show); } +bool Sidebar::show_send(bool show) const { return p->btn_send_gcode->Show(show); } +bool Sidebar::show_export_removable(bool show) const { return p->btn_export_gcode_removable->Show(show); } +bool Sidebar::show_eject(bool show) const { return p->btn_eject_device->Show(show); } +bool Sidebar::get_eject_shown() const { return p->btn_eject_device->IsShown(); } bool Sidebar::is_multifilament() { @@ -1591,6 +1601,7 @@ struct Plater::priv GLToolbar view_toolbar; GLToolbar collapse_toolbar; Preview *preview; + NotificationManager* notification_manager; BackgroundSlicingProcess background_process; bool suppressed_backround_processing_update { false }; @@ -1775,7 +1786,17 @@ struct Plater::priv void on_slicing_update(SlicingStatusEvent&); void on_slicing_completed(wxCommandEvent&); void on_process_completed(wxCommandEvent&); + void on_export_began(wxCommandEvent&); void on_layer_editing_toggled(bool enable); + void on_slicing_began(); + + void clear_warnings(); + void add_warning(const Slic3r::PrintStateBase::Warning &warning, size_t oid); + void actualizate_warnings(const Model& model, size_t print_oid); + // Displays dialog window with list of warnings. + // Returns true if user clicks OK. + // Returns true if current_warnings vector is empty without showning the dialog + bool warnings_dialog(); void on_action_add(SimpleEvent&); void on_action_split_objects(SimpleEvent&); @@ -1826,7 +1847,7 @@ struct Plater::priv // Flag indicating that the G-code export targets a removable device, therefore the show_action_buttons() needs to be called at any case when the background processing finishes. bool writing_to_removable_device = { false }; bool inside_snapshot_capture() { return m_prevent_snapshots != 0; } - + bool process_completed_with_error { false }; private: bool init_object_menu(); bool init_common_menu(wxMenu* menu, const bool is_part = false); @@ -1854,6 +1875,11 @@ private: * */ std::string m_last_fff_printer_profile_name; std::string m_last_sla_printer_profile_name; + + // vector of all warnings generated by last slicing + std::vector> current_warnings; + bool show_warning_dialog { false }; + }; const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf|prusa)", std::regex::icase); @@ -1899,6 +1925,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) }); background_process.set_slicing_completed_event(EVT_SLICING_COMPLETED); background_process.set_finished_event(EVT_PROCESS_COMPLETED); + background_process.set_export_began_event(EVT_EXPORT_BEGAN); // Default printer technology for default config. background_process.select_technology(this->printer_technology); // Register progress callback from the Print class to the Plater. @@ -2010,8 +2037,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, [this](wxKeyEvent& evt) { preview->move_double_slider(evt); }); preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_EDIT_COLOR_CHANGE, [this](wxKeyEvent& evt) { preview->edit_double_slider(evt); }); - q->Bind(EVT_SLICING_COMPLETED, &priv::on_slicing_completed, this); + q->Bind(EVT_SLICING_COMPLETED, &priv::on_slicing_completed, this); q->Bind(EVT_PROCESS_COMPLETED, &priv::on_process_completed, this); + q->Bind(EVT_EXPORT_BEGAN, &priv::on_export_began, this); q->Bind(EVT_GLVIEWTOOLBAR_3D, [q](SimpleEvent&) { q->select_view_3D("3D"); }); q->Bind(EVT_GLVIEWTOOLBAR_PREVIEW, [q](SimpleEvent&) { q->select_view_3D("Preview"); }); @@ -2038,16 +2066,27 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) }); #endif /* _WIN32 */ - this->q->Bind(EVT_REMOVABLE_DRIVE_EJECTED, [this](RemovableDriveEjectEvent &evt) { + notification_manager = new NotificationManager(this->q); + this->q->Bind(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED, [this](EjectDriveNotificationClickedEvent&) { this->q->eject_drive(); }); + this->q->Bind(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED, [this](ExportGcodeNotificationClickedEvent&) { this->q->export_gcode(true); }); + this->q->Bind(EVT_PRESET_UPDATE_AVIABLE_CLICKED, [this](PresetUpdateAviableClickedEvent&) { wxGetApp().get_preset_updater()->on_update_notification_confirm(); }); + + this->q->Bind(EVT_REMOVABLE_DRIVE_EJECTED, [this, q](RemovableDriveEjectEvent &evt) { if (evt.data.second) { this->show_action_buttons(this->ready_to_slice); - Slic3r::GUI::show_info(this->q, format_wxstr(_L("Unmounting successful. The device %s(%s) can now be safely removed from the computer."), - evt.data.first.name, evt.data.first.path)); - } else - Slic3r::GUI::show_info(this->q, format_wxstr(_L("Ejecting of device %s(%s) has failed."), - evt.data.first.name, evt.data.first.path)); + notification_manager->push_notification(format(_L("Unmounting successful. The device %s(%s) can now be safely removed from the computer."),evt.data.first.name, evt.data.first.path), + NotificationManager::NotificationLevel::RegularNotification, *q->get_current_canvas3D()); + } else { + notification_manager->push_notification(format(_L("Ejecting of device %s(%s) has failed."), evt.data.first.name, evt.data.first.path), + NotificationManager::NotificationLevel::ErrorNotification, *q->get_current_canvas3D()); + } + }); + this->q->Bind(EVT_REMOVABLE_DRIVES_CHANGED, [this, q](RemovableDrivesChangedEvent &) { + this->show_action_buttons(this->ready_to_slice); + if (!this->sidebar->get_eject_shown()) { + notification_manager->close_notification_of_type(NotificationType::ExportToRemovableFinished); + } }); - this->q->Bind(EVT_REMOVABLE_DRIVES_CHANGED, [this](RemovableDrivesChangedEvent &) { this->show_action_buttons(this->ready_to_slice); }); // Start the background thread and register this window as a target for update events. wxGetApp().removable_drive_manager()->init(this->q); #ifdef _WIN32 @@ -2675,6 +2714,8 @@ void Plater::priv::reset() { Plater::TakeSnapshot snapshot(q, _L("Reset Project")); + clear_warnings(); + set_project_filename(wxEmptyString); // Prevent toolpaths preview from rendering while we modify the Print object @@ -2844,22 +2885,13 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool // The state of the Print changed, and it is non-zero. Let's validate it and give the user feedback on errors. std::string err = this->background_process.validate(); if (err.empty()) { + notification_manager->set_all_slicing_errors_gray(true); if (invalidated != Print::APPLY_STATUS_UNCHANGED && this->background_processing_enabled()) return_state |= UPDATE_BACKGROUND_PROCESS_RESTART; } else { - // The print is not valid. - // Only show the error message immediately, if the top level parent of this window is active. - auto p = dynamic_cast(this->q); - while (p->GetParent()) - p = p->GetParent(); - auto *top_level_wnd = dynamic_cast(p); - if (! postpone_error_messages && top_level_wnd && top_level_wnd->IsActive()) { - // The error returned from the Print needs to be translated into the local language. - GUI::show_error(this->q, err); - } else { - // Show the error message once the main window gets activated. - this->delayed_error_message = err; - } + // The print is not valid. + // Show error as notification. + notification_manager->push_slicing_error_notification(err, *q->get_current_canvas3D()); return_state |= UPDATE_BACKGROUND_PROCESS_INVALID; } } else if (! this->delayed_error_message.empty()) { @@ -2867,6 +2899,14 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool return_state |= UPDATE_BACKGROUND_PROCESS_INVALID; } + //actualizate warnings + if (invalidated != Print::APPLY_STATUS_UNCHANGED) { + actualizate_warnings(this->q->model(), this->background_process.current_print()->id().id); + notification_manager->set_all_slicing_warnings_gray(true); + show_warning_dialog = false; + process_completed_with_error = false; + } + if (invalidated != Print::APPLY_STATUS_UNCHANGED && was_running && ! this->background_process.running() && (return_state & UPDATE_BACKGROUND_PROCESS_RESTART) == 0) { // The background processing was killed and it will not be restarted. @@ -2929,6 +2969,8 @@ bool Plater::priv::restart_background_process(unsigned int state) this->statusbar()->set_status_text(_L("Cancelling")); this->background_process.stop(); }); + if (!show_warning_dialog) + on_slicing_began(); return true; } } @@ -2955,6 +2997,7 @@ void Plater::priv::export_gcode(fs::path output_path, bool output_path_on_remova if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) != 0) return; + show_warning_dialog = true; if (! output_path.empty()) { background_process.schedule_export(output_path.string(), output_path_on_removable_media); } else { @@ -3433,11 +3476,20 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) state = print_object->step_state_with_warnings(static_cast(warning_step)); } // Now process state.warnings. + for (auto const& warning : state.warnings) { + if (warning.current) { + notification_manager->push_slicing_warning_notification(warning.message, false, *q->get_current_canvas3D(), object_id.id, warning_step); + add_warning(warning, object_id.id); + } + } } } -void Plater::priv::on_slicing_completed(wxCommandEvent &) +void Plater::priv::on_slicing_completed(wxCommandEvent & evt) { + //notification_manager->push_notification(NotificationType::SlicingComplete, *q->get_current_canvas3D(), evt.GetInt()); + notification_manager->push_slicing_complete_notification(*q->get_current_canvas3D(), evt.GetInt(), is_sidebar_collapsed()); + switch (this->printer_technology) { case ptFFF: this->update_fff_scene(); @@ -3450,8 +3502,63 @@ void Plater::priv::on_slicing_completed(wxCommandEvent &) break; default: break; } -} +} +void Plater::priv::on_export_began(wxCommandEvent& evt) +{ + if (show_warning_dialog) + warnings_dialog(); +} +void Plater::priv::on_slicing_began() +{ + clear_warnings(); + notification_manager->close_notification_of_type(NotificationType::SlicingComplete); +} +void Plater::priv::add_warning(const Slic3r::PrintStateBase::Warning& warning, size_t oid) +{ + for (auto const& it : current_warnings) { + if (warning.message_id == it.first.message_id) { + if (warning.message_id != 0 || (warning.message_id == 0 && warning.message == it.first.message)) + return; + } + } + current_warnings.emplace_back(std::pair(warning, oid)); +} +void Plater::priv::actualizate_warnings(const Model& model, size_t print_oid) +{ + std::vector living_oids; + living_oids.push_back(model.id().id); + living_oids.push_back(print_oid); + for (auto it = model.objects.begin(); it != model.objects.end(); ++it) { + living_oids.push_back((*it)->id().id); + } + notification_manager->compare_warning_oids(living_oids); +} +void Plater::priv::clear_warnings() +{ + notification_manager->close_slicing_errors_and_warnings(); + this->current_warnings.clear(); +} +bool Plater::priv::warnings_dialog() +{ + if (current_warnings.empty()) + return true; + std::string text = _u8L("There are active warnings concerning sliced models:\n"); + bool empt = true; + for (auto const& it : current_warnings) { + int next_n = it.first.message.find_first_of('\n', 0); + text += "\n"; + if (next_n != std::string::npos) + text += it.first.message.substr(0, next_n); + else + text += it.first.message; + } + //text += "\n\nDo you still wish to export?"; + wxMessageDialog msg_wingow(this->q, text, wxString(SLIC3R_APP_NAME " ") + _L("generated warnings"), wxOK); + const auto res = msg_wingow.ShowModal(); + return res == wxID_OK; + +} void Plater::priv::on_process_completed(wxCommandEvent &evt) { // Stop the background task, wait until the thread goes into the "Idle" state. @@ -3470,14 +3577,13 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt) if (error) { wxString message = evt.GetString(); if (message.IsEmpty()) - message = _L("Export failed"); - if (q->m_tracking_popup_menu) - // We don't want to pop-up a message box when tracking a pop-up menu. - // We postpone the error message instead. - q->m_tracking_popup_menu_error_message = message; - else - show_error(q, message); + message = _L("Export failed."); + notification_manager->push_slicing_error_notification(boost::nowide::narrow(message), *q->get_current_canvas3D()); this->statusbar()->set_status_text(message); + const wxString invalid_str = _L("Invalid data"); + for (auto btn : { ActionButtonType::abReslice, ActionButtonType::abSendGCode, ActionButtonType::abExport }) + sidebar->set_btn_label(btn, invalid_str); + process_completed_with_error = true; } if (canceled) this->statusbar()->set_status_text(_L("Cancelled")); @@ -3503,18 +3609,21 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt) default: break; } - if (canceled) { if (wxGetApp().get_mode() == comSimple) sidebar->set_btn_label(ActionButtonType::abReslice, "Slice now"); show_action_buttons(true); } - else if (this->writing_to_removable_device || wxGetApp().get_mode() == comSimple) + else if (wxGetApp().get_mode() == comSimple) { - wxGetApp().removable_drive_manager()->set_exporting_finished(true); show_action_buttons(false); } - this->writing_to_removable_device = false; + else if (this->writing_to_removable_device) + { + show_action_buttons(false); + notification_manager->push_notification(NotificationType::ExportToRemovableFinished, *q->get_current_canvas3D()); + } + this->writing_to_removable_device = false; } void Plater::priv::on_layer_editing_toggled(bool enable) @@ -4156,7 +4265,7 @@ void Plater::priv::show_action_buttons(const bool ready_to_slice) const sidebar->show_export(true) | sidebar->show_send(send_gcode_shown) | sidebar->show_export_removable(removable_media_status.has_removable_drives) | - sidebar->show_disconnect(removable_media_status.has_eject)) + sidebar->show_eject(removable_media_status.has_eject)) sidebar->Layout(); } else @@ -4168,7 +4277,7 @@ void Plater::priv::show_action_buttons(const bool ready_to_slice) const sidebar->show_export(!ready_to_slice) | sidebar->show_send(send_gcode_shown && !ready_to_slice) | sidebar->show_export_removable(!ready_to_slice && removable_media_status.has_removable_drives) | - sidebar->show_disconnect(!ready_to_slice && removable_media_status.has_eject)) + sidebar->show_eject(!ready_to_slice && removable_media_status.has_eject)) sidebar->Layout(); } } @@ -4731,6 +4840,9 @@ void Plater::export_gcode(bool prefer_removable) if (p->model.objects.empty()) return; + if (p->process_completed_with_error)//here + return; + // If possible, remove accents from accented latin characters. // This function is useful for generating file names to be processed by legacy firmwares. fs::path default_output_file; @@ -4990,7 +5102,6 @@ void Plater::export_toolpaths_to_obj() const p->preview->get_canvas3d()->export_toolpaths_to_obj(into_u8(path).c_str()); } - void Plater::reslice() { // Stop arrange and (or) optimize rotation tasks. @@ -5676,6 +5787,16 @@ Mouse3DController& Plater::get_mouse3d_controller() return p->mouse3d_controller; } +const NotificationManager* Plater::get_notification_manager() const +{ + return p->notification_manager; +} + +NotificationManager* Plater::get_notification_manager() +{ + return p->notification_manager; +} + bool Plater::can_delete() const { return p->can_delete(); } bool Plater::can_delete_all() const { return p->can_delete_all(); } bool Plater::can_increase_instances() const { return p->can_increase_instances(); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index a08b19fa35..24e93c80e7 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -47,6 +47,7 @@ class ObjectLayers; class ObjectList; class GLCanvas3D; class Mouse3DController; +class NotificationManager; struct Camera; class Bed3D; class GLToolbar; @@ -130,8 +131,9 @@ public: bool show_reslice(bool show) const; bool show_export(bool show) const; bool show_send(bool show) const; - bool show_disconnect(bool show)const; + bool show_eject(bool show)const; bool show_export_removable(bool show) const; + bool get_eject_shown() const; bool is_multifilament(); void update_mode(); bool is_collapsed(); @@ -338,6 +340,9 @@ public: Mouse3DController& get_mouse3d_controller(); void set_bed_shape() const; + + const NotificationManager* get_notification_manager() const; + NotificationManager* get_notification_manager(); // ROII wrapper for suppressing the Undo / Redo snapshot to be taken. class SuppressSnapshots diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index c32613c468..7d316e77c2 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -27,6 +27,7 @@ #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/format.hpp" +#include "slic3r/GUI/NotificationManager.hpp" #include "slic3r/Utils/Http.hpp" #include "slic3r/Config/Version.hpp" #include "slic3r/Config/Snapshot.hpp" @@ -154,6 +155,9 @@ struct PresetUpdater::priv bool cancel; std::thread thread; + bool has_waiting_updates { false }; + Updates waiting_updates; + priv(); void set_download_prefs(AppConfig *app_config); @@ -165,6 +169,7 @@ struct PresetUpdater::priv void check_install_indices() const; Updates get_config_updates(const Semver& old_slic3r_version) const; void perform_updates(Updates &&updates, bool snapshot = true) const; + void set_waiting_updates(Updates u); }; PresetUpdater::priv::priv() @@ -326,7 +331,15 @@ void PresetUpdater::priv::sync_config(const VendorMap vendors) continue; } Slic3r::rename_file(idx_path_temp, idx_path); - index = std::move(new_index); + //if we rename path we need to change it in Index object too or create the object again + //index = std::move(new_index); + try { + index.load(idx_path); + } + catch (const std::exception& /* err */) { + BOOST_LOG_TRIVIAL(error) << format("Could not load downloaded index %1% for vendor %2%: invalid index?", idx_path, vendor.name); + continue; + } if (cancel) return; } @@ -632,6 +645,12 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons } } +void PresetUpdater::priv::set_waiting_updates(Updates u) +{ + waiting_updates = u; + has_waiting_updates = true; +} + PresetUpdater::PresetUpdater() : p(new priv()) {} @@ -690,9 +709,9 @@ void PresetUpdater::slic3r_update_notify() } } -PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver &old_slic3r_version) const +PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3r_version, bool no_notification) const { - if (! p->enabled_config_update) { return R_NOOP; } + if (! p->enabled_config_update) { return R_NOOP; } auto updates = p->get_config_updates(old_slic3r_version); if (updates.incompats.size() > 0) { @@ -779,30 +798,38 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver &old_slic3 } // regular update - BOOST_LOG_TRIVIAL(info) << format("Update of %1% bundles available. Asking for confirmation ...", updates.updates.size()); + if (no_notification) { + BOOST_LOG_TRIVIAL(info) << format("Update of %1% bundles available. Asking for confirmation ...", p->waiting_updates.updates.size()); - std::vector updates_msg; - for (const auto &update : updates.updates) { - std::string changelog_url = update.version.config_version.prerelease() == nullptr ? update.changelog_url : std::string(); - updates_msg.emplace_back(update.vendor, update.version.config_version, update.version.comment, std::move(changelog_url)); - } + std::vector updates_msg; + for (const auto& update : updates.updates) { + std::string changelog_url = update.version.config_version.prerelease() == nullptr ? update.changelog_url : std::string(); + updates_msg.emplace_back(update.vendor, update.version.config_version, update.version.comment, std::move(changelog_url)); + } - GUI::MsgUpdateConfig dlg(updates_msg); + GUI::MsgUpdateConfig dlg(updates_msg); - const auto res = dlg.ShowModal(); - if (res == wxID_OK) { - BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update"; - p->perform_updates(std::move(updates)); + const auto res = dlg.ShowModal(); + if (res == wxID_OK) { + BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update"; + p->perform_updates(std::move(updates)); - // Reload global configuration - auto *app_config = GUI::wxGetApp().app_config; - GUI::wxGetApp().preset_bundle->load_presets(*app_config); - GUI::wxGetApp().load_current_presets(); - return R_UPDATE_INSTALLED; + // Reload global configuration + auto* app_config = GUI::wxGetApp().app_config; + GUI::wxGetApp().preset_bundle->load_presets(*app_config); + GUI::wxGetApp().load_current_presets(); + return R_UPDATE_INSTALLED; + } + else { + BOOST_LOG_TRIVIAL(info) << "User refused the update"; + return R_UPDATE_REJECT; + } } else { - BOOST_LOG_TRIVIAL(info) << "User refused the update"; - return R_UPDATE_REJECT; + p->set_waiting_updates(updates); + GUI::wxGetApp().plater()->get_notification_manager()->push_notification(GUI::NotificationType::PresetUpdateAviable, *(GUI::wxGetApp().plater()->get_current_canvas3D())); } + + // MsgUpdateConfig will show after the notificaation is clicked } else { BOOST_LOG_TRIVIAL(info) << "No configuration updates available."; } @@ -825,5 +852,37 @@ void PresetUpdater::install_bundles_rsrc(std::vector bundles, bool p->perform_updates(std::move(updates), snapshot); } +void PresetUpdater::on_update_notification_confirm() +{ + if (!p->has_waiting_updates) + return; + BOOST_LOG_TRIVIAL(info) << format("Update of %1% bundles available. Asking for confirmation ...", p->waiting_updates.updates.size()); + + std::vector updates_msg; + for (const auto& update : p->waiting_updates.updates) { + std::string changelog_url = update.version.config_version.prerelease() == nullptr ? update.changelog_url : std::string(); + updates_msg.emplace_back(update.vendor, update.version.config_version, update.version.comment, std::move(changelog_url)); + } + + GUI::MsgUpdateConfig dlg(updates_msg); + + const auto res = dlg.ShowModal(); + if (res == wxID_OK) { + BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update"; + p->perform_updates(std::move(p->waiting_updates)); + + // Reload global configuration + auto* app_config = GUI::wxGetApp().app_config; + GUI::wxGetApp().preset_bundle->load_presets(*app_config); + GUI::wxGetApp().load_current_presets(); + p->has_waiting_updates = false; + //return R_UPDATE_INSTALLED; + } + else { + BOOST_LOG_TRIVIAL(info) << "User refused the update"; + //return R_UPDATE_REJECT; + } + +} } diff --git a/src/slic3r/Utils/PresetUpdater.hpp b/src/slic3r/Utils/PresetUpdater.hpp index e186958284..0ca363c613 100644 --- a/src/slic3r/Utils/PresetUpdater.hpp +++ b/src/slic3r/Utils/PresetUpdater.hpp @@ -35,16 +35,20 @@ public: R_INCOMPAT_CONFIGURED, R_UPDATE_INSTALLED, R_UPDATE_REJECT, + R_UPDATE_NOTIFICATION }; // If updating is enabled, check if updates are available in cache, if so, ask about installation. // A false return value implies Slic3r should exit due to incompatibility of configuration. // Providing old slic3r version upgrade profiles on upgrade of an application even in case // that the config index installed from the Internet is equal to the index contained in the installation package. - UpdateResult config_update(const Semver &old_slic3r_version) const; + // no_notification = force modal textbox, otherwise some cases only shows notification + UpdateResult config_update(const Semver &old_slic3r_version, bool no_notification) const; // "Update" a list of bundles from resources (behaves like an online update). void install_bundles_rsrc(std::vector bundles, bool snapshot = true) const; + + void on_update_notification_confirm(); private: struct priv; std::unique_ptr p; From 38239f09e3ea889aab14cc6c6bc2d6a27013981d Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 2 Jun 2020 17:28:46 +0200 Subject: [PATCH 234/503] Fix remove_bottom_points function --- src/libslic3r/SLA/SupportPoint.hpp | 4 ++-- src/libslic3r/SLA/SupportPointGenerator.cpp | 11 ++++------ src/libslic3r/SLA/SupportPointGenerator.hpp | 2 +- src/libslic3r/SLAPrintSteps.cpp | 23 ++++++++++----------- 4 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/SLA/SupportPoint.hpp b/src/libslic3r/SLA/SupportPoint.hpp index 202a614c32..455962cc40 100644 --- a/src/libslic3r/SLA/SupportPoint.hpp +++ b/src/libslic3r/SLA/SupportPoint.hpp @@ -29,13 +29,13 @@ struct SupportPoint float pos_y, float pos_z, float head_radius, - bool new_island) + bool new_island = false) : pos(pos_x, pos_y, pos_z) , head_front_radius(head_radius) , is_new_island(new_island) {} - SupportPoint(Vec3f position, float head_radius, bool new_island) + SupportPoint(Vec3f position, float head_radius, bool new_island = false) : pos(position) , head_front_radius(head_radius) , is_new_island(new_island) diff --git a/src/libslic3r/SLA/SupportPointGenerator.cpp b/src/libslic3r/SLA/SupportPointGenerator.cpp index 78c2ced356..b598439cae 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.cpp +++ b/src/libslic3r/SLA/SupportPointGenerator.cpp @@ -523,15 +523,12 @@ void SupportPointGenerator::uniformly_cover(const ExPolygons& islands, Structure } } -void remove_bottom_points(std::vector &pts, double gnd_lvl, double tolerance) +void remove_bottom_points(std::vector &pts, float lvl) { // get iterator to the reorganized vector end - auto endit = - std::remove_if(pts.begin(), pts.end(), - [tolerance, gnd_lvl](const sla::SupportPoint &sp) { - double diff = std::abs(gnd_lvl - - double(sp.pos(Z))); - return diff <= tolerance; + auto endit = std::remove_if(pts.begin(), pts.end(), [lvl] + (const sla::SupportPoint &sp) { + return sp.pos.z() <= lvl; }); // erase all elements after the new end diff --git a/src/libslic3r/SLA/SupportPointGenerator.hpp b/src/libslic3r/SLA/SupportPointGenerator.hpp index 2fe8e11fc7..1729230561 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.hpp +++ b/src/libslic3r/SLA/SupportPointGenerator.hpp @@ -214,7 +214,7 @@ private: std::mt19937 m_rng; }; -void remove_bottom_points(std::vector &pts, double gnd_lvl, double tolerance); +void remove_bottom_points(std::vector &pts, float lvl); }} // namespace Slic3r::sla diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index e421e9c1dc..ea016d5bb0 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -360,18 +360,6 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po) // removed them on purpose. No calculation will be done. po.m_supportdata->pts = 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 (is_zero_elevation(po.config())) { - double tolerance = po.config().pad_enable.getBool() ? - po.m_config.pad_wall_thickness.getFloat() : - po.m_config.support_base_height.getFloat(); - - remove_bottom_points(po.m_supportdata->pts, - po.m_supportdata->emesh.ground_level(), - tolerance); - } } void SLAPrint::Steps::support_tree(SLAPrintObject &po) @@ -382,6 +370,17 @@ void SLAPrint::Steps::support_tree(SLAPrintObject &po) if (pcfg.embed_object) po.m_supportdata->emesh.ground_level_offset(pcfg.wall_thickness_mm); + + // If the zero elevation mode is engaged, we have to filter out all the + // points that are on the bottom of the object + if (is_zero_elevation(po.config())) { + double discard = po.config().pad_enable.getBool() ? + po.m_config.pad_wall_height.getFloat() : + po.m_config.support_base_height.getFloat() ; + + remove_bottom_points(po.m_supportdata->pts, + float(po.m_supportdata->emesh.ground_level() + discard)); + } po.m_supportdata->cfg = make_support_cfg(po.m_config); // po.m_supportdata->emesh.load_holes(po.transformed_drainhole_points()); From 06223221466508358ee210161b5872dae2f883e0 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 2 Jun 2020 17:31:52 +0200 Subject: [PATCH 235/503] Create smaller supports in problematic areas with established strategies Completely remove the concept of CompactBridge. Replace it with Heads having the same back radius as front radius. Try to apply the same rules for mini supports as in the route_to_model step. Increased accuracy of bridge_mesh_intersect shot from support points Refining mini support integration --- src/libslic3r/SLA/SupportTree.cpp | 5 +- src/libslic3r/SLA/SupportTreeBuilder.cpp | 280 ++++++++----- src/libslic3r/SLA/SupportTreeBuilder.hpp | 102 +++-- src/libslic3r/SLA/SupportTreeBuildsteps.cpp | 426 ++++++++++---------- src/libslic3r/SLA/SupportTreeBuildsteps.hpp | 11 +- tests/sla_print/CMakeLists.txt | 2 +- tests/sla_print/sla_test_utils.cpp | 6 +- tests/sla_print/sla_treebuilder_tests.cpp | 96 +++++ 8 files changed, 571 insertions(+), 357 deletions(-) create mode 100644 tests/sla_print/sla_treebuilder_tests.cpp diff --git a/src/libslic3r/SLA/SupportTree.cpp b/src/libslic3r/SLA/SupportTree.cpp index 528778b68b..2edc4d21b1 100644 --- a/src/libslic3r/SLA/SupportTree.cpp +++ b/src/libslic3r/SLA/SupportTree.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -103,9 +104,11 @@ SupportTree::UPtr SupportTree::create(const SupportableMesh &sm, builder->m_ctl = ctl; if (sm.cfg.enabled) { - builder->build(sm); + // Execute takes care about the ground_level + SupportTreeBuildsteps::execute(*builder, sm); builder->merge_and_cleanup(); // clean metadata, leave only the meshes. } else { + // If a pad gets added later, it will be in the right Z level builder->ground_level = sm.emesh.ground_level(); } diff --git a/src/libslic3r/SLA/SupportTreeBuilder.cpp b/src/libslic3r/SLA/SupportTreeBuilder.cpp index cf6e7e0206..8c9b54bb72 100644 --- a/src/libslic3r/SLA/SupportTreeBuilder.cpp +++ b/src/libslic3r/SLA/SupportTreeBuilder.cpp @@ -155,6 +155,65 @@ Contour3D cylinder(double r, double h, size_t ssteps, const Vec3d &sp) return ret; } +Contour3D pinhead(double r_pin, double r_back, double length, size_t steps) +{ + assert(length > 0.); + assert(r_back > 0.); + assert(r_pin > 0.); + + Contour3D mesh; + + // We create two spheres which will be connected with a robe that fits + // both circles perfectly. + + // Set up the model detail level + const double detail = 2*PI/steps; + + // We don't generate whole circles. Instead, we generate only the + // portions which are visible (not covered by the robe) To know the + // exact portion of the bottom and top circles we need to use some + // rules of tangent circles from which we can derive (using simple + // triangles the following relations: + + // The height of the whole mesh + const double h = r_back + r_pin + length; + double phi = PI / 2. - std::acos((r_back - r_pin) / h); + + // To generate a whole circle we would pass a portion of (0, Pi) + // To generate only a half horizontal circle we can pass (0, Pi/2) + // The calculated phi is an offset to the half circles needed to smooth + // the transition from the circle to the robe geometry + + auto&& s1 = sphere(r_back, make_portion(0, PI/2 + phi), detail); + auto&& s2 = sphere(r_pin, make_portion(PI/2 + phi, PI), detail); + + for(auto& p : s2.points) p.z() += h; + + mesh.merge(s1); + mesh.merge(s2); + + for(size_t idx1 = s1.points.size() - steps, idx2 = s1.points.size(); + idx1 < s1.points.size() - 1; + idx1++, idx2++) + { + coord_t i1s1 = coord_t(idx1), i1s2 = coord_t(idx2); + coord_t i2s1 = i1s1 + 1, i2s2 = i1s2 + 1; + + mesh.faces3.emplace_back(i1s1, i2s1, i2s2); + mesh.faces3.emplace_back(i1s1, i2s2, i1s2); + } + + auto i1s1 = coord_t(s1.points.size()) - coord_t(steps); + auto i2s1 = coord_t(s1.points.size()) - 1; + auto i1s2 = coord_t(s1.points.size()); + auto i2s2 = coord_t(s1.points.size()) + coord_t(steps) - 1; + + mesh.faces3.emplace_back(i2s2, i2s1, i1s1); + mesh.faces3.emplace_back(i1s2, i2s2, i1s1); + + return mesh; +} + Head::Head(double r_big_mm, double r_small_mm, double length_mm, @@ -164,67 +223,17 @@ Head::Head(double r_big_mm, const size_t circlesteps) : steps(circlesteps) , dir(direction) - , tr(offset) + , pos(offset) , r_back_mm(r_big_mm) , r_pin_mm(r_small_mm) , width_mm(length_mm) , penetration_mm(penetration) { - assert(width_mm > 0.); - assert(r_back_mm > 0.); - assert(r_pin_mm > 0.); - - // We create two spheres which will be connected with a robe that fits - // both circles perfectly. - - // Set up the model detail level - const double detail = 2*PI/steps; - - // We don't generate whole circles. Instead, we generate only the - // portions which are visible (not covered by the robe) To know the - // exact portion of the bottom and top circles we need to use some - // rules of tangent circles from which we can derive (using simple - // triangles the following relations: - - // The height of the whole mesh - const double h = r_big_mm + r_small_mm + width_mm; - double phi = PI/2 - std::acos( (r_big_mm - r_small_mm) / h ); - - // To generate a whole circle we would pass a portion of (0, Pi) - // To generate only a half horizontal circle we can pass (0, Pi/2) - // The calculated phi is an offset to the half circles needed to smooth - // the transition from the circle to the robe geometry - - auto&& s1 = sphere(r_big_mm, make_portion(0, PI/2 + phi), detail); - auto&& s2 = sphere(r_small_mm, make_portion(PI/2 + phi, PI), detail); - - for(auto& p : s2.points) p.z() += h; - - mesh.merge(s1); - mesh.merge(s2); - - for(size_t idx1 = s1.points.size() - steps, idx2 = s1.points.size(); - idx1 < s1.points.size() - 1; - idx1++, idx2++) - { - coord_t i1s1 = coord_t(idx1), i1s2 = coord_t(idx2); - coord_t i2s1 = i1s1 + 1, i2s2 = i1s2 + 1; - - mesh.faces3.emplace_back(i1s1, i2s1, i2s2); - mesh.faces3.emplace_back(i1s1, i2s2, i1s2); - } - - auto i1s1 = coord_t(s1.points.size()) - coord_t(steps); - auto i2s1 = coord_t(s1.points.size()) - 1; - auto i1s2 = coord_t(s1.points.size()); - auto i2s2 = coord_t(s1.points.size()) + coord_t(steps) - 1; - - mesh.faces3.emplace_back(i2s2, i2s1, i1s1); - mesh.faces3.emplace_back(i1s2, i2s2, i1s1); + mesh = pinhead(r_pin_mm, r_back_mm, width_mm, steps); // To simplify further processing, we translate the mesh so that the // last vertex of the pointing sphere (the pinpoint) will be at (0,0,0) - for(auto& p : mesh.points) p.z() -= (h + r_small_mm - penetration_mm); + for(auto& p : mesh.points) p.z() -= (fullwidth() - r_back_mm); } Pillar::Pillar(const Vec3d &jp, const Vec3d &endp, double radius, size_t st): @@ -305,34 +314,6 @@ Bridge::Bridge(const Vec3d &j1, const Vec3d &j2, double r_mm, size_t steps): for(auto& p : mesh.points) p = quater * p + j1; } -CompactBridge::CompactBridge(const Vec3d &sp, - const Vec3d &ep, - const Vec3d &n, - double r, - bool endball, - size_t steps) -{ - Vec3d startp = sp + r * n; - Vec3d dir = (ep - startp).normalized(); - Vec3d endp = ep - r * dir; - - Bridge br(startp, endp, r, steps); - mesh.merge(br.mesh); - - // now add the pins - double fa = 2*PI/steps; - auto upperball = sphere(r, Portion{PI / 2 - fa, PI}, fa); - for(auto& p : upperball.points) p += startp; - - if(endball) { - auto lowerball = sphere(r, Portion{0, PI/2 + 2*fa}, fa); - for(auto& p : lowerball.points) p += endp; - mesh.merge(lowerball); - } - - mesh.merge(upperball); -} - Pad::Pad(const TriangleMesh &support_mesh, const ExPolygons & model_contours, double ground_level, @@ -368,7 +349,6 @@ SupportTreeBuilder::SupportTreeBuilder(SupportTreeBuilder &&o) , m_pillars{std::move(o.m_pillars)} , m_bridges{std::move(o.m_bridges)} , m_crossbridges{std::move(o.m_crossbridges)} - , m_compact_bridges{std::move(o.m_compact_bridges)} , m_pad{std::move(o.m_pad)} , m_meshcache{std::move(o.m_meshcache)} , m_meshcache_valid{o.m_meshcache_valid} @@ -382,7 +362,6 @@ SupportTreeBuilder::SupportTreeBuilder(const SupportTreeBuilder &o) , m_pillars{o.m_pillars} , m_bridges{o.m_bridges} , m_crossbridges{o.m_crossbridges} - , m_compact_bridges{o.m_compact_bridges} , m_pad{o.m_pad} , m_meshcache{o.m_meshcache} , m_meshcache_valid{o.m_meshcache_valid} @@ -397,7 +376,6 @@ SupportTreeBuilder &SupportTreeBuilder::operator=(SupportTreeBuilder &&o) m_pillars = std::move(o.m_pillars); m_bridges = std::move(o.m_bridges); m_crossbridges = std::move(o.m_crossbridges); - m_compact_bridges = std::move(o.m_compact_bridges); m_pad = std::move(o.m_pad); m_meshcache = std::move(o.m_meshcache); m_meshcache_valid = o.m_meshcache_valid; @@ -413,7 +391,6 @@ SupportTreeBuilder &SupportTreeBuilder::operator=(const SupportTreeBuilder &o) m_pillars = o.m_pillars; m_bridges = o.m_bridges; m_crossbridges = o.m_crossbridges; - m_compact_bridges = o.m_compact_bridges; m_pad = o.m_pad; m_meshcache = o.m_meshcache; m_meshcache_valid = o.m_meshcache_valid; @@ -443,12 +420,7 @@ const TriangleMesh &SupportTreeBuilder::merged_mesh() const if (ctl().stopcondition()) break; merged.merge(j.mesh); } - - for (auto &cb : m_compact_bridges) { - if (ctl().stopcondition()) break; - merged.merge(cb.mesh); - } - + for (auto &bs : m_bridges) { if (ctl().stopcondition()) break; merged.merge(bs.mesh); @@ -499,7 +471,6 @@ const TriangleMesh &SupportTreeBuilder::merge_and_cleanup() m_pillars = {}; m_junctions = {}; m_bridges = {}; - m_compact_bridges = {}; return ret; } @@ -514,11 +485,130 @@ const TriangleMesh &SupportTreeBuilder::retrieve_mesh(MeshType meshtype) const return m_meshcache; } -bool SupportTreeBuilder::build(const SupportableMesh &sm) +template +static Hit min_hit(const C &hits) { - ground_level = sm.emesh.ground_level() - sm.cfg.object_elevation_mm; - return SupportTreeBuildsteps::execute(*this, sm); + auto mit = std::min_element(hits.begin(), hits.end(), + [](const Hit &h1, const Hit &h2) { + return h1.distance() < h2.distance(); + }); + + return *mit; } +EigenMesh3D::hit_result query_hit(const SupportableMesh &msh, const Head &h) +{ + static const size_t SAMPLES = 8; + + // Move away slightly from the touching point to avoid raycasting on the + // inner surface of the mesh. + + const double& sd = msh.cfg.safety_distance_mm; + + auto& m = msh.emesh; + using HitResult = EigenMesh3D::hit_result; + + // Hit results + std::array hits; + + Vec3d s1 = h.pos, s2 = h.junction_point(); + + struct Rings { + double rpin; + double rback; + Vec3d spin; + Vec3d sback; + PointRing ring; + + Vec3d backring(size_t idx) { return ring.get(idx, sback, rback); } + Vec3d pinring(size_t idx) { return ring.get(idx, spin, rpin); } + } rings {h.r_pin_mm + sd, h.r_back_mm + sd, s1, s2, h.dir}; + + // We will shoot multiple rays from the head pinpoint in the direction + // of the pinhead robe (side) surface. The result will be the smallest + // hit distance. + + auto hitfn = [&m, &rings, sd](HitResult &hit, size_t i) { + // Point on the circle on the pin sphere + Vec3d ps = rings.pinring(i); + // This is the point on the circle on the back sphere + Vec3d p = rings.backring(i); + + // Point ps is not on mesh but can be inside or + // outside as well. This would cause many problems + // with ray-casting. To detect the position we will + // use the ray-casting result (which has an is_inside + // predicate). + + Vec3d n = (p - ps).normalized(); + auto q = m.query_ray_hit(ps + sd * n, n); + + if (q.is_inside()) { // the hit is inside the model + if (q.distance() > rings.rpin) { + // If we are inside the model and the hit + // distance is bigger than our pin circle + // diameter, it probably indicates that the + // support point was already inside the + // model, or there is really no space + // around the point. We will assign a zero + // hit distance to these cases which will + // enforce the function return value to be + // an invalid ray with zero hit distance. + // (see min_element at the end) + hit = HitResult(0.0); + } else { + // re-cast the ray from the outside of the + // object. The starting point has an offset + // of 2*safety_distance because the + // original ray has also had an offset + auto q2 = m.query_ray_hit(ps + (q.distance() + 2 * sd) * n, n); + hit = q2; + } + } else + hit = q; + }; + + ccr::enumerate(hits.begin(), hits.end(), hitfn); + + return min_hit(hits); } + +EigenMesh3D::hit_result query_hit(const SupportableMesh &msh, const Bridge &br, double safety_d) +{ + static const size_t SAMPLES = 8; + + Vec3d dir = (br.endp - br.startp).normalized(); + PointRing ring{dir}; + + using Hit = EigenMesh3D::hit_result; + + // Hit results + std::array hits; + + const double sd = std::isnan(safety_d) ? msh.cfg.safety_distance_mm : safety_d; + bool ins_check = sd < msh.cfg.safety_distance_mm; + + auto hitfn = [&br, &ring, &msh, dir, sd, ins_check](Hit & hit, size_t i) { + // Point on the circle on the pin sphere + Vec3d p = ring.get(i, br.startp, br.r + sd); + + auto hr = msh.emesh.query_ray_hit(p + sd * dir, dir); + + if (ins_check && hr.is_inside()) { + if (hr.distance() > 2 * br.r + sd) + hit = Hit(0.0); + else { + // re-cast the ray from the outside of the object + hit = msh.emesh.query_ray_hit(p + (hr.distance() + 2 * sd) * dir, + dir); + } + } else + hit = hr; + }; + + ccr::enumerate(hits.begin(), hits.end(), hitfn); + + return min_hit(hits); } + +}} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/SupportTreeBuilder.hpp b/src/libslic3r/SLA/SupportTreeBuilder.hpp index 90cf417c83..aec2a7a585 100644 --- a/src/libslic3r/SLA/SupportTreeBuilder.hpp +++ b/src/libslic3r/SLA/SupportTreeBuilder.hpp @@ -76,6 +76,8 @@ Contour3D sphere(double rho, Portion portion = make_portion(0.0, 2.0*PI), // sp: starting point Contour3D cylinder(double r, double h, size_t ssteps = 45, const Vec3d &sp = {0,0,0}); +Contour3D pinhead(double r_pin, double r_back, double length, size_t steps = 45); + const constexpr long ID_UNSET = -1; struct Head { @@ -83,7 +85,7 @@ struct Head { size_t steps = 45; Vec3d dir = {0, 0, -1}; - Vec3d tr = {0, 0, 0}; + Vec3d pos = {0, 0, 0}; double r_back_mm = 1; double r_pin_mm = 0.5; @@ -120,17 +122,22 @@ struct Head { // the -1 z coordinate auto quatern = Quaternion::FromTwoVectors(Vec3d{0, 0, -1}, dir); - for(auto& p : mesh.points) p = quatern * p + tr; + for(auto& p : mesh.points) p = quatern * p + pos; } + inline double real_width() const + { + return 2 * r_pin_mm + width_mm + 2 * r_back_mm ; + } + inline double fullwidth() const { - return 2 * r_pin_mm + width_mm + 2*r_back_mm - penetration_mm; + return real_width() - penetration_mm; } inline Vec3d junction_point() const { - return tr + ( 2 * r_pin_mm + width_mm + r_back_mm - penetration_mm)*dir; + return pos + (fullwidth() - r_back_mm) * dir; } inline double request_pillar_radius(double radius) const @@ -211,20 +218,6 @@ struct Bridge { size_t steps = 45); }; -// A bridge that spans from model surface to model surface with small connecting -// edges on the endpoints. Used for headless support points. -struct CompactBridge { - Contour3D mesh; - long id = ID_UNSET; - - CompactBridge(const Vec3d& sp, - const Vec3d& ep, - const Vec3d& n, - double r, - bool endball = true, - size_t steps = 45); -}; - // A wrapper struct around the pad struct Pad { TriangleMesh tmesh; @@ -242,6 +235,67 @@ struct Pad { bool empty() const { return tmesh.facets_count() == 0; } }; +// Give points on a 3D ring with given center, radius and orientation +// method based on: +// https://math.stackexchange.com/questions/73237/parametric-equation-of-a-circle-in-3d-space +template +class PointRing { + std::array m_phis; + + // Two vectors that will be perpendicular to each other and to the + // axis. Values for a(X) and a(Y) are now arbitrary, a(Z) is just a + // placeholder. + // a and b vectors are perpendicular to the ring direction and to each other. + // Together they define the plane where we have to iterate with the + // given angles in the 'm_phis' vector + Vec3d a = {0, 1, 0}, b; + double m_radius = 0.; + + static inline bool constexpr is_one(double val) + { + return std::abs(std::abs(val) - 1) < 1e-20; + } + +public: + + PointRing(const Vec3d &n) + { + m_phis = linspace_array(0., 2 * PI); + + // We have to address the case when the direction vector v (same as + // dir) is coincident with one of the world axes. In this case two of + // its components will be completely zero and one is 1.0. Our method + // becomes dangerous here due to division with zero. Instead, vector + // 'a' can be an element-wise rotated version of 'v' + if(is_one(n(X)) || is_one(n(Y)) || is_one(n(Z))) { + a = {n(Z), n(X), n(Y)}; + b = {n(Y), n(Z), n(X)}; + } + else { + a(Z) = -(n(Y)*a(Y)) / n(Z); a.normalize(); + b = a.cross(n); + } + } + + Vec3d get(size_t idx, const Vec3d src, double r) const + { + double phi = m_phis[idx]; + double sinphi = std::sin(phi); + double cosphi = std::cos(phi); + + double rpscos = r * cosphi; + double rpssin = r * sinphi; + + // Point on the sphere + return {src(X) + rpscos * a(X) + rpssin * b(X), + src(Y) + rpscos * a(Y) + rpssin * b(Y), + src(Z) + rpscos * a(Z) + rpssin * b(Z)}; + } +}; + +EigenMesh3D::hit_result query_hit(const SupportableMesh &msh, const Bridge &br, double safety_d = std::nan("")); +EigenMesh3D::hit_result query_hit(const SupportableMesh &msh, const Head &br, double safety_d = std::nan("")); + // This class will hold the support tree meshes with some additional // bookkeeping as well. Various parts of the support geometry are stored // separately and are merged when the caller queries the merged mesh. The @@ -264,7 +318,6 @@ class SupportTreeBuilder: public SupportTree { std::vector m_junctions; std::vector m_bridges; std::vector m_crossbridges; - std::vector m_compact_bridges; Pad m_pad; using Mutex = ccr::SpinningMutex; @@ -415,15 +468,6 @@ public: return _add_bridge(m_crossbridges, std::forward(args)...); } - template const CompactBridge& add_compact_bridge(Args&&...args) - { - std::lock_guard lk(m_mutex); - m_compact_bridges.emplace_back(std::forward(args)...); - m_compact_bridges.back().id = long(m_compact_bridges.size() - 1); - m_meshcache_valid = false; - return m_compact_bridges.back(); - } - Head &head(unsigned id) { std::lock_guard lk(m_mutex); @@ -488,8 +532,6 @@ public: virtual const TriangleMesh &retrieve_mesh( MeshType meshtype = MeshType::Support) const override; - - bool build(const SupportableMesh &supportable_mesh); }; }} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp index 29ad6057f1..df9de35555 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -42,6 +42,8 @@ bool SupportTreeBuildsteps::execute(SupportTreeBuilder & builder, { if(sm.pts.empty()) return false; + builder.ground_level = sm.emesh.ground_level() - sm.cfg.object_elevation_mm; + SupportTreeBuildsteps alg(builder, sm); // Let's define the individual steps of the processing. We can experiment @@ -166,64 +168,6 @@ bool SupportTreeBuildsteps::execute(SupportTreeBuilder & builder, return pc == ABORT; } -// Give points on a 3D ring with given center, radius and orientation -// method based on: -// https://math.stackexchange.com/questions/73237/parametric-equation-of-a-circle-in-3d-space -template -class PointRing { - std::array m_phis; - - // Two vectors that will be perpendicular to each other and to the - // axis. Values for a(X) and a(Y) are now arbitrary, a(Z) is just a - // placeholder. - // a and b vectors are perpendicular to the ring direction and to each other. - // Together they define the plane where we have to iterate with the - // given angles in the 'm_phis' vector - Vec3d a = {0, 1, 0}, b; - double m_radius = 0.; - - static inline bool constexpr is_one(double val) - { - return std::abs(std::abs(val) - 1) < 1e-20; - } - -public: - - PointRing(const Vec3d &n) - { - m_phis = linspace_array(0., 2 * PI); - - // We have to address the case when the direction vector v (same as - // dir) is coincident with one of the world axes. In this case two of - // its components will be completely zero and one is 1.0. Our method - // becomes dangerous here due to division with zero. Instead, vector - // 'a' can be an element-wise rotated version of 'v' - if(is_one(n(X)) || is_one(n(Y)) || is_one(n(Z))) { - a = {n(Z), n(X), n(Y)}; - b = {n(Y), n(Z), n(X)}; - } - else { - a(Z) = -(n(Y)*a(Y)) / n(Z); a.normalize(); - b = a.cross(n); - } - } - - Vec3d get(size_t idx, const Vec3d src, double r) const - { - double phi = m_phis[idx]; - double sinphi = std::sin(phi); - double cosphi = std::cos(phi); - - double rpscos = r * cosphi; - double rpssin = r * sinphi; - - // Point on the sphere - return {src(X) + rpscos * a(X) + rpssin * b(X), - src(Y) + rpscos * a(Y) + rpssin * b(Y), - src(Z) + rpscos * a(Z) + rpssin * b(Z)}; - } -}; - template static Hit min_hit(const C &hits) { @@ -312,7 +256,7 @@ EigenMesh3D::hit_result SupportTreeBuildsteps::pinhead_mesh_intersect( } EigenMesh3D::hit_result SupportTreeBuildsteps::bridge_mesh_intersect( - const Vec3d &src, const Vec3d &dir, double r, bool ins_check) + const Vec3d &src, const Vec3d &dir, double r, double safety_d) { static const size_t SAMPLES = 8; PointRing ring{dir}; @@ -321,16 +265,19 @@ EigenMesh3D::hit_result SupportTreeBuildsteps::bridge_mesh_intersect( // Hit results std::array hits; + + double sd = std::isnan(safety_d) ? m_cfg.safety_distance_mm : safety_d; + sd = sd * r / m_cfg.head_back_radius_mm; + + bool ins_check = sd < m_cfg.safety_distance_mm; ccr::enumerate(hits.begin(), hits.end(), - [this, r, src, ins_check, &ring, dir] (Hit &hit, size_t i) { - - const double sd = m_cfg.safety_distance_mm; - + [this, r, src, ins_check, &ring, dir, sd] (Hit &hit, size_t i) { + // Point on the circle on the pin sphere Vec3d p = ring.get(i, src, r + sd); - auto hr = m_mesh.query_ray_hit(p + sd * dir, dir); + auto hr = m_mesh.query_ray_hit(p + r * dir, dir); if(ins_check && hr.is_inside()) { if(hr.distance() > 2 * r + sd) hit = Hit(0.0); @@ -460,7 +407,7 @@ bool SupportTreeBuildsteps::connect_to_nearpillar(const Head &head, Vec3d bridgestart = headjp; Vec3d bridgeend = nearjp_u; - double max_len = m_cfg.max_bridge_length_mm; + double max_len = r * m_cfg.max_bridge_length_mm / m_cfg.head_back_radius_mm; double max_slope = m_cfg.bridge_slope; double zdiff = 0.0; @@ -494,7 +441,7 @@ bool SupportTreeBuildsteps::connect_to_nearpillar(const Head &head, // There will be a minimum distance from the ground where the // bridge is allowed to connect. This is an empiric value. - double minz = m_builder.ground_level + 2 * m_cfg.head_width_mm; + double minz = m_builder.ground_level + 4 * head.r_back_mm; if(bridgeend(Z) < minz) return false; double t = bridge_mesh_distance(bridgestart, dirv(bridgestart, bridgeend), r); @@ -509,7 +456,7 @@ bool SupportTreeBuildsteps::connect_to_nearpillar(const Head &head, if(zdiff > 0) { m_builder.add_pillar(head.id, bridgestart, r); m_builder.add_junction(bridgestart, r); - m_builder.add_bridge(bridgestart, bridgeend, head.r_back_mm); + m_builder.add_bridge(bridgestart, bridgeend, r); } else { m_builder.add_bridge(head.id, bridgeend); } @@ -520,40 +467,6 @@ bool SupportTreeBuildsteps::connect_to_nearpillar(const Head &head, return true; } -bool SupportTreeBuildsteps::search_pillar_and_connect(const Head &head) -{ - PointIndex spindex = m_pillar_index.guarded_clone(); - - long nearest_id = ID_UNSET; - - Vec3d querypoint = head.junction_point(); - - while(nearest_id < 0 && !spindex.empty()) { m_thr(); - // loop until a suitable head is not found - // if there is a pillar closer than the cluster center - // (this may happen as the clustering is not perfect) - // than we will bridge to this closer pillar - - Vec3d qp(querypoint(X), querypoint(Y), m_builder.ground_level); - auto qres = spindex.nearest(qp, 1); - if(qres.empty()) break; - - auto ne = qres.front(); - nearest_id = ne.second; - - if(nearest_id >= 0) { - if(size_t(nearest_id) < m_builder.pillarcount()) { - if(!connect_to_nearpillar(head, nearest_id)) { - nearest_id = ID_UNSET; // continue searching - spindex.remove(ne); // without the current pillar - } - } - } - } - - return nearest_id >= 0; -} - void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, const Vec3d &sourcedir, double radius, @@ -565,9 +478,10 @@ void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, Vec3d endp = {jp(X), jp(Y), gndlvl}; double sd = m_cfg.pillar_base_safety_distance_mm; long pillar_id = ID_UNSET; - double min_dist = sd + m_cfg.base_radius_mm + EPSILON; + bool can_add_base = radius >= m_cfg.head_back_radius_mm; + double base_r = can_add_base ? m_cfg.base_radius_mm : 0.; + double min_dist = sd + base_r + EPSILON; double dist = 0; - bool can_add_base = true; bool normal_mode = true; // If in zero elevation mode and the pillar is too close to the model body, @@ -612,7 +526,7 @@ void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, endp = jp + std::get<0>(result.optimum) * dir; Vec3d pgnd = {endp(X), endp(Y), gndlvl}; - can_add_base = result.score > min_dist; + can_add_base = can_add_base && result.score > min_dist; double gnd_offs = m_mesh.ground_level_offset(); auto abort_in_shame = @@ -712,84 +626,85 @@ void SupportTreeBuildsteps::filter() auto [polar, azimuth] = dir_to_spheric(n); // skip if the tilt is not sane - if(polar >= PI - m_cfg.normal_cutoff_angle) { + if(polar < PI - m_cfg.normal_cutoff_angle) return; - // We saturate the polar angle to 3pi/4 - polar = std::max(polar, 3*PI / 4); - - // save the head (pinpoint) position - Vec3d hp = m_points.row(fidx); - - double w = m_cfg.head_width_mm + - m_cfg.head_back_radius_mm + - 2*m_cfg.head_front_radius_mm; - - double pin_r = double(m_support_pts[fidx].head_front_radius); - - // Reassemble the now corrected normal - auto nn = spheric_to_dir(polar, azimuth).normalized(); - - // check available distance - EigenMesh3D::hit_result t - = pinhead_mesh_intersect(hp, // touching point - nn, // normal - pin_r, - m_cfg.head_back_radius_mm, - w); - - if(t.distance() <= w) { - - // Let's try to optimize this angle, there might be a - // viable normal that doesn't collide with the model - // geometry and its very close to the default. - - StopCriteria stc; - stc.max_iterations = m_cfg.optimizer_max_iterations; - stc.relative_score_difference = m_cfg.optimizer_rel_score_diff; - stc.stop_score = w; // space greater than w is enough - GeneticOptimizer solver(stc); - solver.seed(0); // we want deterministic behavior - - auto oresult = solver.optimize_max( - [this, pin_r, w, hp](double plr, double azm) - { - auto dir = spheric_to_dir(plr, azm).normalized(); - - double score = pinhead_mesh_distance( - hp, dir, pin_r, m_cfg.head_back_radius_mm, w); - - return score; - }, - initvals(polar, azimuth), // start with what we have - bound(3 * PI / 4, PI), // Must not exceed the tilt limit - bound(-PI, PI) // azimuth can be a full search - ); - - if(oresult.score > w) { - polar = std::get<0>(oresult.optimum); - azimuth = std::get<1>(oresult.optimum); - nn = spheric_to_dir(polar, azimuth).normalized(); - t = EigenMesh3D::hit_result(oresult.score); - } - } - - // save the verified and corrected normal - m_support_nmls.row(fidx) = nn; - - if (t.distance() > w) { - // Check distance from ground, we might have zero elevation. - if (hp(Z) + w * nn(Z) < m_builder.ground_level) { - addfn(m_iheadless, fidx); - } else { - // mark the point for needing a head. - addfn(m_iheads, fidx); - } - } else if (polar >= 3 * PI / 4) { - // Headless supports do not tilt like the headed ones - // so the normal should point almost to the ground. - addfn(m_iheadless, fidx); + // We saturate the polar angle to 3pi/4 + polar = std::max(polar, 3*PI / 4); + + // save the head (pinpoint) position + Vec3d hp = m_points.row(fidx); + + // The distance needed for a pinhead to not collide with model. + double w = m_cfg.head_width_mm + + m_cfg.head_back_radius_mm + + 2*m_cfg.head_front_radius_mm; + + double pin_r = double(m_support_pts[fidx].head_front_radius); + + // Reassemble the now corrected normal + auto nn = spheric_to_dir(polar, azimuth).normalized(); + + // check available distance + EigenMesh3D::hit_result t + = pinhead_mesh_intersect(hp, // touching point + nn, // normal + pin_r, + m_cfg.head_back_radius_mm, + w); + + if(t.distance() <= w) { + + // Let's try to optimize this angle, there might be a + // viable normal that doesn't collide with the model + // geometry and its very close to the default. + + StopCriteria stc; + stc.max_iterations = m_cfg.optimizer_max_iterations; + stc.relative_score_difference = m_cfg.optimizer_rel_score_diff; + stc.stop_score = w; // space greater than w is enough + GeneticOptimizer solver(stc); + solver.seed(0); // we want deterministic behavior + + auto oresult = solver.optimize_max( + [this, pin_r, w, hp](double plr, double azm) + { + auto dir = spheric_to_dir(plr, azm).normalized(); + + double score = pinhead_mesh_intersect( + hp, dir, pin_r, m_cfg.head_back_radius_mm, w).distance(); + + return score; + }, + initvals(polar, azimuth), // start with what we have + bound(3 * PI / 4, PI), // Must not exceed the tilt limit + bound(-PI, PI) // azimuth can be a full search + ); + + if(oresult.score > w) { + polar = std::get<0>(oresult.optimum); + azimuth = std::get<1>(oresult.optimum); + nn = spheric_to_dir(polar, azimuth).normalized(); + t = EigenMesh3D::hit_result(oresult.score); } } + + // save the verified and corrected normal + m_support_nmls.row(fidx) = nn; + + if (t.distance() > w) { + // Check distance from ground, we might have zero elevation. + if (hp(Z) + w * nn(Z) < m_builder.ground_level) { + addfn(m_iheadless, fidx); + } else { + // mark the point for needing a head. + addfn(m_iheads, fidx); + } + } else if (polar >= 3 * PI / 4) { + // Headless supports do not tilt like the headed ones + // so the normal should point almost to the ground. + addfn(m_iheadless, fidx); + } + }; ccr::enumerate(filtered_indices.begin(), filtered_indices.end(), filterfn); @@ -811,6 +726,27 @@ void SupportTreeBuildsteps::add_pinheads() m_support_pts[i].pos.cast() // displacement ); } + + for (unsigned i : m_iheadless) { + const auto R = double(m_support_pts[i].head_front_radius); + + // The support point position on the mesh + Vec3d sph = m_support_pts[i].pos.cast(); + + // Get an initial normal from the filtering step + Vec3d n = m_support_nmls.row(i); + + // First we need to determine the available space for a mini pinhead. + // The goal is the move away from the model a little bit to make the + // contact point small as possible and avoid pearcing the model body. + double pin_space = std::min(2 * R, bridge_mesh_distance(sph, n, R, 0.)); + + if (pin_space <= 0) continue; + + m_iheads.emplace_back(i); + m_builder.add_head(i, R, R, pin_space, + m_cfg.head_penetration_mm, n, sph); + } } void SupportTreeBuildsteps::classify() @@ -864,8 +800,6 @@ void SupportTreeBuildsteps::classify() void SupportTreeBuildsteps::routing_to_ground() { - const double pradius = m_cfg.head_back_radius_mm; - ClusterEl cl_centroids; cl_centroids.reserve(m_pillar_clusters.size()); @@ -931,7 +865,7 @@ void SupportTreeBuildsteps::routing_to_ground() Vec3d pstart = sidehead.junction_point(); // Vec3d pend = Vec3d{pstart(X), pstart(Y), gndlvl}; // Could not find a pillar, create one - create_ground_pillar(pstart, sidehead.dir, pradius, sidehead.id); + create_ground_pillar(pstart, sidehead.dir, sidehead.r_back_mm, sidehead.id); } } } @@ -943,7 +877,7 @@ bool SupportTreeBuildsteps::connect_to_ground(Head &head, const Vec3d &dir) double r = head.r_back_mm; double t = bridge_mesh_distance(hjp, dir, head.r_back_mm); double d = 0, tdown = 0; - t = std::min(t, m_cfg.max_bridge_length_mm); + t = std::min(t, m_cfg.max_bridge_length_mm * r / m_cfg.head_back_radius_mm); while (d < t && !std::isinf(tdown = bridge_mesh_distance(hjp + d * dir, DOWN, r))) d += r; @@ -1041,6 +975,42 @@ bool SupportTreeBuildsteps::connect_to_model_body(Head &head) return true; } +bool SupportTreeBuildsteps::search_pillar_and_connect(const Head &source) +{ + // Hope that a local copy takes less time than the whole search loop. + // We also need to remove elements progressively from the copied index. + PointIndex spindex = m_pillar_index.guarded_clone(); + + long nearest_id = ID_UNSET; + + Vec3d querypt = source.junction_point(); + + while(nearest_id < 0 && !spindex.empty()) { m_thr(); + // loop until a suitable head is not found + // if there is a pillar closer than the cluster center + // (this may happen as the clustering is not perfect) + // than we will bridge to this closer pillar + + Vec3d qp(querypt(X), querypt(Y), m_builder.ground_level); + auto qres = spindex.nearest(qp, 1); + if(qres.empty()) break; + + auto ne = qres.front(); + nearest_id = ne.second; + + if(nearest_id >= 0) { + if(size_t(nearest_id) < m_builder.pillarcount()) { + if(!connect_to_nearpillar(source, nearest_id)) { + nearest_id = ID_UNSET; // continue searching + spindex.remove(ne); // without the current pillar + } + } + } + } + + return nearest_id >= 0; +} + void SupportTreeBuildsteps::routing_to_model() { // We need to check if there is an easy way out to the bed surface. @@ -1054,18 +1024,18 @@ void SupportTreeBuildsteps::routing_to_model() auto& head = m_builder.head(idx); // Search nearby pillar - if(search_pillar_and_connect(head)) { head.transform(); return; } + if (search_pillar_and_connect(head)) { head.transform(); return; } // Cannot connect to nearby pillar. We will try to search for // a route to the ground. - if(connect_to_ground(head)) { head.transform(); return; } + if (connect_to_ground(head)) { head.transform(); return; } // No route to the ground, so connect to the model body as a last resort if (connect_to_model_body(head)) { return; } // We have failed to route this head. BOOST_LOG_TRIVIAL(warning) - << "Failed to route model facing support point. ID: " << idx; + << "Failed to route model facing support point. ID: " << idx; head.invalidate(); }); @@ -1107,9 +1077,10 @@ void SupportTreeBuildsteps::interconnect_pillars() // connections are already enough for the pillar if(pillar.links >= neighbors) return; + double max_d = d * pillar.r / m_cfg.head_back_radius_mm; // Query all remaining points within reach - auto qres = m_pillar_index.query([qp, d](const PointIndexEl& e){ - return distance(e.first, qp) < d; + auto qres = m_pillar_index.query([qp, max_d](const PointIndexEl& e){ + return distance(e.first, qp) < max_d; }); // sort the result by distance (have to check if this is needed) @@ -1288,37 +1259,54 @@ void SupportTreeBuildsteps::routing_headless() // We will sink the pins into the model surface for a distance of 1/3 of // the pin radius - for(unsigned i : m_iheadless) { - m_thr(); - - const auto R = double(m_support_pts[i].head_front_radius); - const double HWIDTH_MM = std::min(R, m_cfg.head_penetration_mm); - - // Exact support position - Vec3d sph = m_support_pts[i].pos.cast(); - Vec3d n = m_support_nmls.row(i); // mesh outward normal - Vec3d sp = sph - n * HWIDTH_MM; // stick head start point - - Vec3d sj = sp + R * n; // stick start point - - // This is only for checking - double idist = bridge_mesh_distance(sph, DOWN, R, true); - double realdist = ray_mesh_intersect(sj, DOWN).distance(); - double dist = realdist; - - if (std::isinf(dist)) dist = sph(Z) - m_builder.ground_level; - - if(std::isnan(idist) || idist < 2*R || std::isnan(dist) || dist < 2*R) { - BOOST_LOG_TRIVIAL(warning) << "Can not find route for headless" - << " support stick at: " - << sj.transpose(); - continue; - } - - bool use_endball = !std::isinf(realdist); - Vec3d ej = sj + (dist + HWIDTH_MM) * DOWN ; - m_builder.add_compact_bridge(sp, ej, n, R, use_endball); - } +// for(unsigned i : m_iheadless) { +// m_thr(); + +// const auto R = double(m_support_pts[i].head_front_radius); + +// // The support point position on the mesh +// Vec3d sph = m_support_pts[i].pos.cast(); + +// // Get an initial normal from the filtering step +// Vec3d n = m_support_nmls.row(i); + +// // First we need to determine the available space for a mini pinhead. +// // The goal is the move away from the model a little bit to make the +// // contact point small as possible and avoid pearcing the model body. +// double pin_space = std::min(2 * R, bridge_mesh_distance(sph, n, R, 0.)); + +// if (pin_space <= 0) continue; + +// auto &head = m_builder.add_head(i, R, R, pin_space, +// m_cfg.head_penetration_mm, n, sph); + +// // collision check + +// m_head_to_ground_scans[i] = +// bridge_mesh_intersect(head.junction_point(), DOWN, R); + +// // Here the steps will be similar as in route_to_model step: +// // 1. Search for a nearby pillar, include other mini pillars + +// // Search nearby pillar +// if (search_pillar_and_connect(head)) { head.transform(); continue; } + +// if (std::isinf(m_head_to_ground_scans[i].distance())) { +// create_ground_pillar(head.junction_point(), head.dir, m_cfg.head_back_radius_mm, head.id); +// } + +// // Cannot connect to nearby pillar. We will try to search for +// // a route to the ground. +// if (connect_to_ground(head)) { head.transform(); continue; } + +// // No route to the ground, so connect to the model body as a last resort +// if (connect_to_model_body(head)) { continue; } + +// BOOST_LOG_TRIVIAL(warning) << "Can not find route for headless" +// << " support stick at: " +// << sph.transpose(); +// head.invalidate(); +// } } } diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp index cfe78fe97a..1962f802b2 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp @@ -229,11 +229,6 @@ class SupportTreeBuildsteps { double r_pin, double r_back, double width); - - template - inline double pinhead_mesh_distance(Args&&...args) { - return pinhead_mesh_intersect(std::forward(args)...).distance(); - } // Checking bridge (pillar and stick as well) intersection with the model. // If the function is used for headless sticks, the ins_check parameter @@ -247,7 +242,7 @@ class SupportTreeBuildsteps { const Vec3d& s, const Vec3d& dir, double r, - bool ins_check = false); + double safety_d = std::nan("")); template inline double bridge_mesh_distance(Args&&...args) { @@ -268,8 +263,8 @@ class SupportTreeBuildsteps { inline bool connect_to_ground(Head& head); bool connect_to_model_body(Head &head); - - bool search_pillar_and_connect(const Head& head); + + bool search_pillar_and_connect(const Head& source); // This is a proxy function for pillar creation which will mind the gap // between the pad and the model bottom in zero elevation mode. diff --git a/tests/sla_print/CMakeLists.txt b/tests/sla_print/CMakeLists.txt index 9d47f3ae4d..f6b261fdaa 100644 --- a/tests/sla_print/CMakeLists.txt +++ b/tests/sla_print/CMakeLists.txt @@ -1,7 +1,7 @@ get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp sla_print_tests.cpp - sla_test_utils.hpp sla_test_utils.cpp + sla_test_utils.hpp sla_test_utils.cpp sla_treebuilder_tests.cpp sla_raycast_tests.cpp) target_link_libraries(${_TEST_NAME}_tests test_common libslic3r) set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests") diff --git a/tests/sla_print/sla_test_utils.cpp b/tests/sla_print/sla_test_utils.cpp index 1eaf796c00..5a3bd82a00 100644 --- a/tests/sla_print/sla_test_utils.cpp +++ b/tests/sla_print/sla_test_utils.cpp @@ -129,8 +129,7 @@ void test_supports(const std::string &obj_filename, // If there is no elevation, support points shall be removed from the // bottom of the object. if (std::abs(supportcfg.object_elevation_mm) < EPSILON) { - sla::remove_bottom_points(support_points, zmin, - supportcfg.base_height_mm); + sla::remove_bottom_points(support_points, zmin + supportcfg.base_height_mm); } else { // Should be support points at least on the bottom of the model REQUIRE_FALSE(support_points.empty()); @@ -141,7 +140,8 @@ void test_supports(const std::string &obj_filename, // Generate the actual support tree sla::SupportTreeBuilder treebuilder; - treebuilder.build(sla::SupportableMesh{emesh, support_points, supportcfg}); + sla::SupportableMesh sm{emesh, support_points, supportcfg}; + sla::SupportTreeBuildsteps::execute(treebuilder, sm); check_support_tree_integrity(treebuilder, supportcfg); diff --git a/tests/sla_print/sla_treebuilder_tests.cpp b/tests/sla_print/sla_treebuilder_tests.cpp new file mode 100644 index 0000000000..c785e4ba5e --- /dev/null +++ b/tests/sla_print/sla_treebuilder_tests.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include "libslic3r/TriangleMesh.hpp" +#include "libslic3r/SLA/SupportTreeBuilder.hpp" + +TEST_CASE("Test bridge_mesh_intersect on a cube's wall", "[SLABridgeMeshInters]") { + using namespace Slic3r; + + TriangleMesh cube = make_cube(10., 10., 10.); + + sla::SupportConfig cfg = {}; // use default config + sla::SupportPoints pts = {{10.f, 5.f, 5.f, float(cfg.head_front_radius_mm), false}}; + sla::SupportableMesh sm{cube, pts, cfg}; + + SECTION("Bridge is straight horizontal and pointing away from the cube") { + + sla::Bridge bridge(pts[0].pos.cast(), Vec3d{15., 5., 5.}, + pts[0].head_front_radius); + + auto hit = sla::query_hit(sm, bridge); + + REQUIRE(std::isinf(hit.distance())); + + cube.merge(sla::to_triangle_mesh(bridge.mesh)); + cube.require_shared_vertices(); + cube.WriteOBJFile("cube1.obj"); + } + + SECTION("Bridge is tilted down in 45 degrees, pointing away from the cube") { + sla::Bridge bridge(pts[0].pos.cast(), Vec3d{15., 5., 0.}, + pts[0].head_front_radius); + + auto hit = sla::query_hit(sm, bridge); + + REQUIRE(std::isinf(hit.distance())); + + cube.merge(sla::to_triangle_mesh(bridge.mesh)); + cube.require_shared_vertices(); + cube.WriteOBJFile("cube2.obj"); + } +} + + +TEST_CASE("Test bridge_mesh_intersect on a sphere", "[SLABridgeMeshInters]") { + using namespace Slic3r; + + TriangleMesh sphere = make_sphere(1.); + + sla::SupportConfig cfg = {}; // use default config + cfg.head_back_radius_mm = cfg.head_front_radius_mm; + sla::SupportPoints pts = {{1.f, 0.f, 0.f, float(cfg.head_front_radius_mm), false}}; + sla::SupportableMesh sm{sphere, pts, cfg}; + + SECTION("Bridge is straight horizontal and pointing away from the sphere") { + + sla::Bridge bridge(pts[0].pos.cast(), Vec3d{2., 0., 0.}, + pts[0].head_front_radius); + + auto hit = sla::query_hit(sm, bridge); + + sphere.merge(sla::to_triangle_mesh(bridge.mesh)); + sphere.require_shared_vertices(); + sphere.WriteOBJFile("sphere1.obj"); + + REQUIRE(std::isinf(hit.distance())); + } + + SECTION("Bridge is tilted down 45 deg and pointing away from the sphere") { + + sla::Bridge bridge(pts[0].pos.cast(), Vec3d{2., 0., -2.}, + pts[0].head_front_radius); + + auto hit = sla::query_hit(sm, bridge); + + sphere.merge(sla::to_triangle_mesh(bridge.mesh)); + sphere.require_shared_vertices(); + sphere.WriteOBJFile("sphere2.obj"); + + REQUIRE(std::isinf(hit.distance())); + } + + SECTION("Bridge is tilted down 90 deg and pointing away from the sphere") { + + sla::Bridge bridge(pts[0].pos.cast(), Vec3d{1., 0., -2.}, + pts[0].head_front_radius); + + auto hit = sla::query_hit(sm, bridge); + + sphere.merge(sla::to_triangle_mesh(bridge.mesh)); + sphere.require_shared_vertices(); + sphere.WriteOBJFile("sphere3.obj"); + + REQUIRE(std::isinf(hit.distance())); + } +} From 67b61c23f7cbc9061834b08d358ea9f53418ef46 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 3 Jun 2020 17:42:29 +0200 Subject: [PATCH 236/503] Remove the discard region for bottom points removal. This was a workaround for small supports not to end up in the middle of the gap between the pad and the object. The issue needs to be solved at the support generation. --- src/libslic3r/SLAPrintSteps.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index ea016d5bb0..defc5246cc 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -374,12 +374,11 @@ void SLAPrint::Steps::support_tree(SLAPrintObject &po) // If the zero elevation mode is engaged, we have to filter out all the // points that are on the bottom of the object if (is_zero_elevation(po.config())) { - double discard = po.config().pad_enable.getBool() ? - po.m_config.pad_wall_height.getFloat() : - po.m_config.support_base_height.getFloat() ; +// double discard = pcfg.embed_object.object_gap_mm / +// std::cos(po.m_supportdata->cfg.bridge_slope) ; remove_bottom_points(po.m_supportdata->pts, - float(po.m_supportdata->emesh.ground_level() + discard)); + float(po.m_supportdata->emesh.ground_level() + EPSILON)); } po.m_supportdata->cfg = make_support_cfg(po.m_config); From 7b6565abeb0f9456b6bc17e0d9ef98f2a706c06c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 5 Jun 2020 20:19:19 +0200 Subject: [PATCH 237/503] Improvements on mini pillars --- src/libslic3r/Point.hpp | 11 +- src/libslic3r/SLA/EigenMesh3D.hpp | 2 + src/libslic3r/SLA/SupportTreeBuilder.cpp | 16 ++ src/libslic3r/SLA/SupportTreeBuilder.hpp | 6 + src/libslic3r/SLA/SupportTreeBuildsteps.cpp | 178 ++++++++++---------- src/libslic3r/SLA/SupportTreeBuildsteps.hpp | 2 +- 6 files changed, 124 insertions(+), 91 deletions(-) diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index b818cd8bed..8c1c69fde3 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -60,10 +60,13 @@ inline int64_t cross2(const Vec2i64 &v1, const Vec2i64 &v2) { return v1(0) * v2( inline float cross2(const Vec2f &v1, const Vec2f &v2) { return v1(0) * v2(1) - v1(1) * v2(0); } inline double cross2(const Vec2d &v1, const Vec2d &v2) { return v1(0) * v2(1) - v1(1) * v2(0); } -inline Vec2i32 to_2d(const Vec2i32 &pt3) { return Vec2i32(pt3(0), pt3(1)); } -inline Vec2i64 to_2d(const Vec3i64 &pt3) { return Vec2i64(pt3(0), pt3(1)); } -inline Vec2f to_2d(const Vec3f &pt3) { return Vec2f (pt3(0), pt3(1)); } -inline Vec2d to_2d(const Vec3d &pt3) { return Vec2d (pt3(0), pt3(1)); } +template Eigen::Matrix +to_2d(const Eigen::Matrix &ptN) { return {ptN(0), ptN(1)}; } + +//inline Vec2i32 to_2d(const Vec3i32 &pt3) { return Vec2i32(pt3(0), pt3(1)); } +//inline Vec2i64 to_2d(const Vec3i64 &pt3) { return Vec2i64(pt3(0), pt3(1)); } +//inline Vec2f to_2d(const Vec3f &pt3) { return Vec2f (pt3(0), pt3(1)); } +//inline Vec2d to_2d(const Vec3d &pt3) { return Vec2d (pt3(0), pt3(1)); } inline Vec3d to_3d(const Vec2d &v, double z) { return Vec3d(v(0), v(1), z); } inline Vec3f to_3d(const Vec2f &v, float z) { return Vec3f(v(0), v(1), z); } diff --git a/src/libslic3r/SLA/EigenMesh3D.hpp b/src/libslic3r/SLA/EigenMesh3D.hpp index b932c0c18e..7b7562d477 100644 --- a/src/libslic3r/SLA/EigenMesh3D.hpp +++ b/src/libslic3r/SLA/EigenMesh3D.hpp @@ -125,6 +125,8 @@ public: } Vec3d normal_by_face_id(int face_id) const; + + const TriangleMesh * get_triangle_mesh() const { return m_tm; } }; // Calculate the normals for the selected points (from 'points' set) on the diff --git a/src/libslic3r/SLA/SupportTreeBuilder.cpp b/src/libslic3r/SLA/SupportTreeBuilder.cpp index 8c9b54bb72..121a001453 100644 --- a/src/libslic3r/SLA/SupportTreeBuilder.cpp +++ b/src/libslic3r/SLA/SupportTreeBuilder.cpp @@ -314,6 +314,22 @@ Bridge::Bridge(const Vec3d &j1, const Vec3d &j2, double r_mm, size_t steps): for(auto& p : mesh.points) p = quater * p + j1; } +Bridge::Bridge(const Vec3d &j1, + const Vec3d &j2, + double r1_mm, + double r2_mm, + size_t steps) +{ + Vec3d dir = (j2 - j1); + mesh = pinhead(r1_mm, r2_mm, dir.norm(), steps); + dir.normalize(); + + using Quaternion = Eigen::Quaternion; + auto quater = Quaternion::FromTwoVectors(Vec3d{0,0,1}, dir); + + for(auto& p : mesh.points) p = quater * p + j1; +} + Pad::Pad(const TriangleMesh &support_mesh, const ExPolygons & model_contours, double ground_level, diff --git a/src/libslic3r/SLA/SupportTreeBuilder.hpp b/src/libslic3r/SLA/SupportTreeBuilder.hpp index aec2a7a585..66462ebbdf 100644 --- a/src/libslic3r/SLA/SupportTreeBuilder.hpp +++ b/src/libslic3r/SLA/SupportTreeBuilder.hpp @@ -216,6 +216,12 @@ struct Bridge { const Vec3d &j2, double r_mm = 0.8, size_t steps = 45); + + Bridge(const Vec3d &j1, + const Vec3d &j2, + double r1_mm, + double r2_mm, + size_t steps = 45); }; // A wrapper struct around the pad diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp index df9de35555..e94e3c402e 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -467,107 +467,86 @@ bool SupportTreeBuildsteps::connect_to_nearpillar(const Head &head, return true; } -void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, +bool SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, const Vec3d &sourcedir, double radius, long head_id) { - const double SLOPE = 1. / std::cos(m_cfg.bridge_slope); - - double gndlvl = m_builder.ground_level; - Vec3d endp = {jp(X), jp(Y), gndlvl}; double sd = m_cfg.pillar_base_safety_distance_mm; long pillar_id = ID_UNSET; bool can_add_base = radius >= m_cfg.head_back_radius_mm; double base_r = can_add_base ? m_cfg.base_radius_mm : 0.; + double gndlvl = m_builder.ground_level; + if (!can_add_base) gndlvl -= m_mesh.ground_level_offset(); + Vec3d endp = {jp(X), jp(Y), gndlvl}; double min_dist = sd + base_r + EPSILON; - double dist = 0; bool normal_mode = true; - - // If in zero elevation mode and the pillar is too close to the model body, - // the support pillar can not be placed in the gap between the model and - // the pad, and the pillar bases must not touch the model body either. - // To solve this, a corrector bridge is inserted between the starting point - // (jp) and the new pillar. - 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. + Vec3d dir = sourcedir; - normal_mode = false; - - // The min distance needed to move away from the model in XY plane. - double current_d = min_dist - dist; - double current_bride_d = SLOPE * current_d; + auto to_floor = [gndlvl](const Vec3d &p) { return Vec3d{p.x(), p.y(), gndlvl}; }; + if (m_cfg.object_elevation_mm < EPSILON) + { // get a suitable direction for the corrector bridge. It is the // original sourcedir's azimuth but the polar angle is saturated to the // configured bridge slope. - auto [polar, azimuth] = dir_to_spheric(sourcedir); + auto [polar, azimuth] = dir_to_spheric(dir); polar = PI - m_cfg.bridge_slope; - auto dir = spheric_to_dir(polar, azimuth).normalized(); - - StopCriteria scr; - scr.stop_score = min_dist; - SubplexOptimizer solver(scr); - - // Search for a distance along the corrector bridge to move the endpoint - // sufficiently away form the model body. The first few optimization - // cycles should succeed here. - auto result = solver.optimize_max( - [this, dir, jp, gndlvl](double mv) { - Vec3d endpt = jp + mv * dir; - endpt(Z) = gndlvl; - return std::sqrt(m_mesh.squared_distance(endpt)); - }, - initvals(current_bride_d), - bound(0.0, m_cfg.max_bridge_length_mm - current_bride_d)); - - endp = jp + std::get<0>(result.optimum) * dir; - Vec3d pgnd = {endp(X), endp(Y), gndlvl}; - can_add_base = 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_distance(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 - SLOPE * (gndlvl - endp(Z)) * dir; // back off - else { - - auto hit = bridge_mesh_intersect(endp, DOWN, radius); - if (!std::isinf(hit.distance())) abort_in_shame(); - - pillar_id = m_builder.add_pillar(endp, pgnd, radius); - - if (can_add_base) - m_builder.add_pillar_base(pillar_id, m_cfg.base_height_mm, - m_cfg.base_radius_mm); - } - - m_builder.add_bridge(jp, endp, radius); - m_builder.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) + Vec3d dir = spheric_to_dir(polar, azimuth).normalized(); + + // Check the distance of the endpoint and the closest point on model + // body. It should be greater than the min_dist which is + // the safety distance from the model. It includes the pad gap if in + // zero elevation mode. + // + // Try to move along the established bridge direction to dodge the + // forbidden region for the endpoint. + double t = -radius; + while (std::sqrt(m_mesh.squared_distance(to_floor(endp))) < min_dist || + !std::isinf(bridge_mesh_distance(endp, DOWN, radius))) { + t += radius; + endp = jp + t * dir; + normal_mode = false; + + if (t > m_cfg.max_bridge_length_mm || endp(Z) < gndlvl) { m_builder.add_pillar(head_id, jp, radius); + return false; + } + } + } + + // Check if the deduced route is sane and exit with error if not. + if (bridge_mesh_distance(jp, dir, radius) < (endp - jp).norm()) { + m_builder.add_pillar(head_id, jp, radius); + return false; + } + + // If this is a mini pillar, do not let it grow too long, but change the + // radius to the normal pillar as soon as it is possible. + if (radius < m_cfg.head_back_radius_mm) { + double t = 0.; + double new_radius = m_cfg.head_back_radius_mm; + Vec3d new_endp = endp; + double d = 0.; + while (!std::isinf(d = bridge_mesh_distance(new_endp, DOWN, new_radius)) + && new_endp.z() > gndlvl) + { + t += m_cfg.head_fullwidth(); + new_endp = endp + t * DOWN; + } + + if (std::isinf(d) && new_endp.z() > gndlvl) { + if (t > 0.) { + m_builder.add_bridge(endp, new_endp, radius, new_radius); + endp = new_endp; + } else { + m_builder.add_junction(endp, new_radius); + } + radius = new_radius; } } + // Straigh path down, no area to dodge if (normal_mode) { pillar_id = head_id >= 0 ? m_builder.add_pillar(head_id, endp, radius) : m_builder.add_pillar(jp, endp, radius); @@ -575,10 +554,31 @@ void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, if (can_add_base) m_builder.add_pillar_base(pillar_id, m_cfg.base_height_mm, m_cfg.base_radius_mm); + } else { + + // Insert the bridge to get around the forbidden area + Vec3d pgnd{endp.x(), endp.y(), gndlvl}; + pillar_id = m_builder.add_pillar(endp, pgnd, radius); + + if (can_add_base) + m_builder.add_pillar_base(pillar_id, m_cfg.base_height_mm, + m_cfg.base_radius_mm); + + m_builder.add_bridge(jp, endp, radius); + m_builder.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_builder.add_pillar(head_id, jp, radius); } - + if(pillar_id >= 0) // Save the pillar endpoint in the spatial index m_pillar_index.guarded_insert(endp, unsigned(pillar_id)); + + return true; } void SupportTreeBuildsteps::filter() @@ -835,7 +835,11 @@ void SupportTreeBuildsteps::routing_to_ground() Head &h = m_builder.head(hid); h.transform(); - create_ground_pillar(h.junction_point(), h.dir, h.r_back_mm, h.id); + if (!create_ground_pillar(h.junction_point(), h.dir, h.r_back_mm, h.id)) { + BOOST_LOG_TRIVIAL(warning) + << "Pillar cannot be created for support point id: " << hid; + h.invalidate(); + } } // now we will go through the clusters ones again and connect the @@ -999,8 +1003,9 @@ bool SupportTreeBuildsteps::search_pillar_and_connect(const Head &source) nearest_id = ne.second; if(nearest_id >= 0) { - if(size_t(nearest_id) < m_builder.pillarcount()) { - if(!connect_to_nearpillar(source, nearest_id)) { + if (size_t(nearest_id) < m_builder.pillarcount()) { + if(!connect_to_nearpillar(source, nearest_id) || + m_builder.pillar(nearest_id).r < source.r_back_mm) { nearest_id = ID_UNSET; // continue searching spindex.remove(ne); // without the current pillar } @@ -1104,7 +1109,8 @@ void SupportTreeBuildsteps::interconnect_pillars() const Pillar& neighborpillar = m_builder.pillar(re.second); // this neighbor is occupied, skip - if(neighborpillar.links >= neighbors) continue; + if (neighborpillar.links >= neighbors) continue; + if (neighborpillar.r < pillar.r) continue; if(interconnect(pillar, neighborpillar)) { pairs.insert(hashval); diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp index 1962f802b2..bd6a9613c0 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp @@ -271,7 +271,7 @@ class SupportTreeBuildsteps { // jp is the starting junction point which needs to be routed down. // sourcedir is the allowed direction of an optional bridge between the // jp junction and the final pillar. - void create_ground_pillar(const Vec3d &jp, + bool create_ground_pillar(const Vec3d &jp, const Vec3d &sourcedir, double radius, long head_id = ID_UNSET); From ed460a3e7e14ccfbf8ee0345f03ce3fd40750dde Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 10 Jun 2020 15:34:06 +0200 Subject: [PATCH 238/503] Remove the `headless` step of support support tree gen --- src/libslic3r/SLA/SupportTreeBuildsteps.cpp | 97 +-------------------- src/libslic3r/SLA/SupportTreeBuildsteps.hpp | 5 -- 2 files changed, 2 insertions(+), 100 deletions(-) diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp index e94e3c402e..334c88fb90 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -56,7 +56,6 @@ bool SupportTreeBuildsteps::execute(SupportTreeBuilder & builder, ROUTING_GROUND, ROUTING_NONGROUND, CASCADE_PILLARS, - HEADLESS, MERGE_RESULT, DONE, ABORT, @@ -83,8 +82,6 @@ bool SupportTreeBuildsteps::execute(SupportTreeBuilder & builder, std::bind(&SupportTreeBuildsteps::interconnect_pillars, &alg), - std::bind(&SupportTreeBuildsteps::routing_headless, &alg), - std::bind(&SupportTreeBuildsteps::merge_result, &alg), [] () { @@ -103,10 +100,6 @@ bool SupportTreeBuildsteps::execute(SupportTreeBuilder & builder, BOOST_LOG_TRIVIAL(info) << "Skipping model-facing supports as requested."; }; - program[HEADLESS] = []() { - BOOST_LOG_TRIVIAL(info) << "Skipping headless stick generation as" - " requested."; - }; } // Let's define a simple automaton that will run our program. @@ -119,7 +112,6 @@ bool SupportTreeBuildsteps::execute(SupportTreeBuilder & builder, "Routing to ground", "Routing supports to model surface", "Interconnecting pillars", - "Processing small holes", "Merging support mesh", "Done", "Abort" @@ -133,7 +125,6 @@ bool SupportTreeBuildsteps::execute(SupportTreeBuilder & builder, 60, 70, 80, - 85, 99, 100, 0 @@ -148,8 +139,7 @@ bool SupportTreeBuildsteps::execute(SupportTreeBuilder & builder, case CLASSIFY: pc = ROUTING_GROUND; break; case ROUTING_GROUND: pc = ROUTING_NONGROUND; break; case ROUTING_NONGROUND: pc = CASCADE_PILLARS; break; - case CASCADE_PILLARS: pc = HEADLESS; break; - case HEADLESS: pc = MERGE_RESULT; break; + case CASCADE_PILLARS: pc = MERGE_RESULT; break; case MERGE_RESULT: pc = DONE; break; case DONE: case ABORT: break; @@ -521,31 +511,6 @@ bool SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, return false; } - // If this is a mini pillar, do not let it grow too long, but change the - // radius to the normal pillar as soon as it is possible. - if (radius < m_cfg.head_back_radius_mm) { - double t = 0.; - double new_radius = m_cfg.head_back_radius_mm; - Vec3d new_endp = endp; - double d = 0.; - while (!std::isinf(d = bridge_mesh_distance(new_endp, DOWN, new_radius)) - && new_endp.z() > gndlvl) - { - t += m_cfg.head_fullwidth(); - new_endp = endp + t * DOWN; - } - - if (std::isinf(d) && new_endp.z() > gndlvl) { - if (t > 0.) { - m_builder.add_bridge(endp, new_endp, radius, new_radius); - endp = new_endp; - } else { - m_builder.add_junction(endp, new_radius); - } - radius = new_radius; - } - } - // Straigh path down, no area to dodge if (normal_mode) { pillar_id = head_id >= 0 ? m_builder.add_pillar(head_id, endp, radius) : @@ -1258,62 +1223,4 @@ void SupportTreeBuildsteps::interconnect_pillars() } } -void SupportTreeBuildsteps::routing_headless() -{ - // For now we will just generate smaller headless sticks with a sharp - // ending point that connects to the mesh surface. - - // We will sink the pins into the model surface for a distance of 1/3 of - // the pin radius -// for(unsigned i : m_iheadless) { -// m_thr(); - -// const auto R = double(m_support_pts[i].head_front_radius); - -// // The support point position on the mesh -// Vec3d sph = m_support_pts[i].pos.cast(); - -// // Get an initial normal from the filtering step -// Vec3d n = m_support_nmls.row(i); - -// // First we need to determine the available space for a mini pinhead. -// // The goal is the move away from the model a little bit to make the -// // contact point small as possible and avoid pearcing the model body. -// double pin_space = std::min(2 * R, bridge_mesh_distance(sph, n, R, 0.)); - -// if (pin_space <= 0) continue; - -// auto &head = m_builder.add_head(i, R, R, pin_space, -// m_cfg.head_penetration_mm, n, sph); - -// // collision check - -// m_head_to_ground_scans[i] = -// bridge_mesh_intersect(head.junction_point(), DOWN, R); - -// // Here the steps will be similar as in route_to_model step: -// // 1. Search for a nearby pillar, include other mini pillars - -// // Search nearby pillar -// if (search_pillar_and_connect(head)) { head.transform(); continue; } - -// if (std::isinf(m_head_to_ground_scans[i].distance())) { -// create_ground_pillar(head.junction_point(), head.dir, m_cfg.head_back_radius_mm, head.id); -// } - -// // Cannot connect to nearby pillar. We will try to search for -// // a route to the ground. -// if (connect_to_ground(head)) { head.transform(); continue; } - -// // No route to the ground, so connect to the model body as a last resort -// if (connect_to_model_body(head)) { continue; } - -// BOOST_LOG_TRIVIAL(warning) << "Can not find route for headless" -// << " support stick at: " -// << sph.transpose(); -// head.invalidate(); -// } -} - -} -} +}} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp index bd6a9613c0..ae872f98b7 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp @@ -319,11 +319,6 @@ public: void interconnect_pillars(); - // Step: process the support points where there is not enough space for a - // full pinhead. In this case we will use a rounded sphere as a touching - // point and use a thinner bridge (let's call it a stick). - void routing_headless (); - inline void merge_result() { m_builder.merged_mesh(); } static bool execute(SupportTreeBuilder & builder, const SupportableMesh &sm); From 2ff04e6f682a3925d44e72a0179a139f58ecc9f3 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 15 Jun 2020 16:02:47 +0200 Subject: [PATCH 239/503] Bugfixes for support generator * Fix support heads floating in air * Fix failing tests for the bridge mesh intersection * Fix failing assertions WIP refactoring support tree gen, as its a mess. --- src/libslic3r/SLA/SupportTreeBuilder.cpp | 277 ++++++++------------ src/libslic3r/SLA/SupportTreeBuilder.hpp | 144 +++++----- src/libslic3r/SLA/SupportTreeBuildsteps.cpp | 149 +++++++++-- src/libslic3r/SLA/SupportTreeBuildsteps.hpp | 72 ++++- 4 files changed, 371 insertions(+), 271 deletions(-) diff --git a/src/libslic3r/SLA/SupportTreeBuilder.cpp b/src/libslic3r/SLA/SupportTreeBuilder.cpp index 121a001453..ebeca78a72 100644 --- a/src/libslic3r/SLA/SupportTreeBuilder.cpp +++ b/src/libslic3r/SLA/SupportTreeBuilder.cpp @@ -214,6 +214,56 @@ Contour3D pinhead(double r_pin, double r_back, double length, size_t steps) return mesh; } + +Contour3D pedestal(const Vec3d &endpt, double baseheight, double radius, size_t steps) +{ + if(baseheight <= 0) return {}; + + assert(steps >= 0); + auto last = int(steps - 1); + + Contour3D base; + + double a = 2*PI/steps; + double z = endpt(Z) + baseheight; + + for(size_t i = 0; i < steps; ++i) { + double phi = i*a; + double x = endpt(X) + radius * std::cos(phi); + double y = endpt(Y) + radius * std::sin(phi); + base.points.emplace_back(x, y, z); + } + + for(size_t i = 0; i < steps; ++i) { + double phi = i*a; + double x = endpt(X) + radius*std::cos(phi); + double y = endpt(Y) + radius*std::sin(phi); + base.points.emplace_back(x, y, z - baseheight); + } + + auto ep = endpt; ep(Z) += baseheight; + base.points.emplace_back(endpt); + base.points.emplace_back(ep); + + auto& indices = base.faces3; + auto hcenter = int(base.points.size() - 1); + auto lcenter = int(base.points.size() - 2); + auto offs = int(steps); + for(int i = 0; i < last; ++i) { + indices.emplace_back(i, i + offs, offs + i + 1); + indices.emplace_back(i, offs + i + 1, i + 1); + indices.emplace_back(i, i + 1, hcenter); + indices.emplace_back(lcenter, offs + i + 1, offs + i); + } + + indices.emplace_back(0, last, offs); + indices.emplace_back(last, offs + last, offs); + indices.emplace_back(hcenter, last, 0); + indices.emplace_back(offs, offs + last, lcenter); + + return base; +} + Head::Head(double r_big_mm, double r_small_mm, double length_mm, @@ -229,77 +279,76 @@ Head::Head(double r_big_mm, , width_mm(length_mm) , penetration_mm(penetration) { - mesh = pinhead(r_pin_mm, r_back_mm, width_mm, steps); +// mesh = pinhead(r_pin_mm, r_back_mm, width_mm, steps); // To simplify further processing, we translate the mesh so that the // last vertex of the pointing sphere (the pinpoint) will be at (0,0,0) - for(auto& p : mesh.points) p.z() -= (fullwidth() - r_back_mm); +// for(auto& p : mesh.points) p.z() -= (fullwidth() - r_back_mm); } -Pillar::Pillar(const Vec3d &jp, const Vec3d &endp, double radius, size_t st): - r(radius), steps(st), endpt(endp), starts_from_head(false) -{ - assert(steps > 0); - - height = jp(Z) - endp(Z); - if(height > EPSILON) { // Endpoint is below the starting point +//Pillar::Pillar(const Vec3d &endp, double h, double radius, size_t st): +// height{h}, r(radius), steps(st), endpt(endp), starts_from_head(false) +//{ +// assert(steps > 0); + +// if(height > EPSILON) { // Endpoint is below the starting point - // We just create a bridge geometry with the pillar parameters and - // move the data. - Contour3D body = cylinder(radius, height, st, endp); - mesh.points.swap(body.points); - mesh.faces3.swap(body.faces3); - } -} +// // We just create a bridge geometry with the pillar parameters and +// // move the data. +// Contour3D body = cylinder(radius, height, st, endp); +// mesh.points.swap(body.points); +// mesh.faces3.swap(body.faces3); +// } +//} -Pillar &Pillar::add_base(double baseheight, double radius) -{ - if(baseheight <= 0) return *this; - if(baseheight > height) baseheight = height; +//Pillar &Pillar::add_base(double baseheight, double radius) +//{ +// if(baseheight <= 0) return *this; +// if(baseheight > height) baseheight = height; - assert(steps >= 0); - auto last = int(steps - 1); +// assert(steps >= 0); +// auto last = int(steps - 1); - if(radius < r ) radius = r; +// if(radius < r ) radius = r; - double a = 2*PI/steps; - double z = endpt(Z) + baseheight; +// double a = 2*PI/steps; +// double z = endpt(Z) + baseheight; - for(size_t i = 0; i < steps; ++i) { - double phi = i*a; - double x = endpt(X) + r*std::cos(phi); - double y = endpt(Y) + r*std::sin(phi); - base.points.emplace_back(x, y, z); - } +// for(size_t i = 0; i < steps; ++i) { +// double phi = i*a; +// double x = endpt(X) + r*std::cos(phi); +// double y = endpt(Y) + r*std::sin(phi); +// base.points.emplace_back(x, y, z); +// } - for(size_t i = 0; i < steps; ++i) { - double phi = i*a; - double x = endpt(X) + radius*std::cos(phi); - double y = endpt(Y) + radius*std::sin(phi); - base.points.emplace_back(x, y, z - baseheight); - } +// for(size_t i = 0; i < steps; ++i) { +// double phi = i*a; +// double x = endpt(X) + radius*std::cos(phi); +// double y = endpt(Y) + radius*std::sin(phi); +// base.points.emplace_back(x, y, z - baseheight); +// } - auto ep = endpt; ep(Z) += baseheight; - base.points.emplace_back(endpt); - base.points.emplace_back(ep); +// auto ep = endpt; ep(Z) += baseheight; +// base.points.emplace_back(endpt); +// base.points.emplace_back(ep); - auto& indices = base.faces3; - auto hcenter = int(base.points.size() - 1); - auto lcenter = int(base.points.size() - 2); - auto offs = int(steps); - for(int i = 0; i < last; ++i) { - indices.emplace_back(i, i + offs, offs + i + 1); - indices.emplace_back(i, offs + i + 1, i + 1); - indices.emplace_back(i, i + 1, hcenter); - indices.emplace_back(lcenter, offs + i + 1, offs + i); - } +// auto& indices = base.faces3; +// auto hcenter = int(base.points.size() - 1); +// auto lcenter = int(base.points.size() - 2); +// auto offs = int(steps); +// for(int i = 0; i < last; ++i) { +// indices.emplace_back(i, i + offs, offs + i + 1); +// indices.emplace_back(i, offs + i + 1, i + 1); +// indices.emplace_back(i, i + 1, hcenter); +// indices.emplace_back(lcenter, offs + i + 1, offs + i); +// } - indices.emplace_back(0, last, offs); - indices.emplace_back(last, offs + last, offs); - indices.emplace_back(hcenter, last, 0); - indices.emplace_back(offs, offs + last, lcenter); - return *this; -} +// indices.emplace_back(0, last, offs); +// indices.emplace_back(last, offs + last, offs); +// indices.emplace_back(hcenter, last, 0); +// indices.emplace_back(offs, offs + last, lcenter); +// return *this; +//} Bridge::Bridge(const Vec3d &j1, const Vec3d &j2, double r_mm, size_t steps): r(r_mm), startp(j1), endp(j2) @@ -423,7 +472,7 @@ const TriangleMesh &SupportTreeBuilder::merged_mesh() const for (auto &head : m_heads) { if (ctl().stopcondition()) break; - if (head.is_valid()) merged.merge(head.mesh); + if (head.is_valid()) merged.merge(get_mesh(head)); } for (auto &stick : m_pillars) { @@ -512,119 +561,5 @@ static Hit min_hit(const C &hits) return *mit; } -EigenMesh3D::hit_result query_hit(const SupportableMesh &msh, const Head &h) -{ - static const size_t SAMPLES = 8; - - // Move away slightly from the touching point to avoid raycasting on the - // inner surface of the mesh. - - const double& sd = msh.cfg.safety_distance_mm; - - auto& m = msh.emesh; - using HitResult = EigenMesh3D::hit_result; - - // Hit results - std::array hits; - - Vec3d s1 = h.pos, s2 = h.junction_point(); - - struct Rings { - double rpin; - double rback; - Vec3d spin; - Vec3d sback; - PointRing ring; - - Vec3d backring(size_t idx) { return ring.get(idx, sback, rback); } - Vec3d pinring(size_t idx) { return ring.get(idx, spin, rpin); } - } rings {h.r_pin_mm + sd, h.r_back_mm + sd, s1, s2, h.dir}; - - // We will shoot multiple rays from the head pinpoint in the direction - // of the pinhead robe (side) surface. The result will be the smallest - // hit distance. - - auto hitfn = [&m, &rings, sd](HitResult &hit, size_t i) { - // Point on the circle on the pin sphere - Vec3d ps = rings.pinring(i); - // This is the point on the circle on the back sphere - Vec3d p = rings.backring(i); - - // Point ps is not on mesh but can be inside or - // outside as well. This would cause many problems - // with ray-casting. To detect the position we will - // use the ray-casting result (which has an is_inside - // predicate). - - Vec3d n = (p - ps).normalized(); - auto q = m.query_ray_hit(ps + sd * n, n); - - if (q.is_inside()) { // the hit is inside the model - if (q.distance() > rings.rpin) { - // If we are inside the model and the hit - // distance is bigger than our pin circle - // diameter, it probably indicates that the - // support point was already inside the - // model, or there is really no space - // around the point. We will assign a zero - // hit distance to these cases which will - // enforce the function return value to be - // an invalid ray with zero hit distance. - // (see min_element at the end) - hit = HitResult(0.0); - } else { - // re-cast the ray from the outside of the - // object. The starting point has an offset - // of 2*safety_distance because the - // original ray has also had an offset - auto q2 = m.query_ray_hit(ps + (q.distance() + 2 * sd) * n, n); - hit = q2; - } - } else - hit = q; - }; - - ccr::enumerate(hits.begin(), hits.end(), hitfn); - - return min_hit(hits); -} - -EigenMesh3D::hit_result query_hit(const SupportableMesh &msh, const Bridge &br, double safety_d) -{ - static const size_t SAMPLES = 8; - - Vec3d dir = (br.endp - br.startp).normalized(); - PointRing ring{dir}; - - using Hit = EigenMesh3D::hit_result; - - // Hit results - std::array hits; - - const double sd = std::isnan(safety_d) ? msh.cfg.safety_distance_mm : safety_d; - bool ins_check = sd < msh.cfg.safety_distance_mm; - - auto hitfn = [&br, &ring, &msh, dir, sd, ins_check](Hit & hit, size_t i) { - // Point on the circle on the pin sphere - Vec3d p = ring.get(i, br.startp, br.r + sd); - - auto hr = msh.emesh.query_ray_hit(p + sd * dir, dir); - - if (ins_check && hr.is_inside()) { - if (hr.distance() > 2 * br.r + sd) - hit = Hit(0.0); - else { - // re-cast the ray from the outside of the object - hit = msh.emesh.query_ray_hit(p + (hr.distance() + 2 * sd) * dir, - dir); - } - } else - hit = hr; - }; - - ccr::enumerate(hits.begin(), hits.end(), hitfn); - - return min_hit(hits); -} }} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/SupportTreeBuilder.hpp b/src/libslic3r/SLA/SupportTreeBuilder.hpp index 66462ebbdf..087173e556 100644 --- a/src/libslic3r/SLA/SupportTreeBuilder.hpp +++ b/src/libslic3r/SLA/SupportTreeBuilder.hpp @@ -74,10 +74,12 @@ Contour3D sphere(double rho, Portion portion = make_portion(0.0, 2.0*PI), // h: Height // ssteps: how many edges will create the base circle // sp: starting point -Contour3D cylinder(double r, double h, size_t ssteps = 45, const Vec3d &sp = {0,0,0}); +Contour3D cylinder(double r, double h, size_t steps = 45, const Vec3d &sp = Vec3d::Zero()); Contour3D pinhead(double r_pin, double r_back, double length, size_t steps = 45); +Contour3D pedestal(const Vec3d &pt, double baseheight, double radius, size_t steps = 45); + const constexpr long ID_UNSET = -1; struct Head { @@ -114,15 +116,7 @@ struct Head { void transform() { - using Quaternion = Eigen::Quaternion; - - // We rotate the head to the specified direction The head's pointing - // side is facing upwards so this means that it would hold a support - // point with a normal pointing straight down. This is the reason of - // the -1 z coordinate - auto quatern = Quaternion::FromTwoVectors(Vec3d{0, 0, -1}, dir); - - for(auto& p : mesh.points) p = quatern * p + pos; + // TODO: remove occurences } inline double real_width() const @@ -164,8 +158,8 @@ struct Junction { }; struct Pillar { - Contour3D mesh; - Contour3D base; +// Contour3D mesh; +// Contour3D base; double r = 1; size_t steps = 0; Vec3d endpt; @@ -182,27 +176,42 @@ struct Pillar { // How many pillars are cascaded with this one unsigned links = 0; - - Pillar(const Vec3d& jp, const Vec3d& endp, - double radius = 1, size_t st = 45); - - Pillar(const Junction &junc, const Vec3d &endp) - : Pillar(junc.pos, endp, junc.r, junc.steps) - {} - - Pillar(const Head &head, const Vec3d &endp, double radius = 1) - : Pillar(head.junction_point(), endp, - head.request_pillar_radius(radius), head.steps) - {} - + + Pillar(const Vec3d &endp, double h, double radius = 1, size_t st = 45): + height{h}, r(radius), steps(st), endpt(endp), starts_from_head(false) {} + + +// Pillar(const Junction &junc, const Vec3d &endp) +// : Pillar(junc.pos, endp, junc.r, junc.steps) +// {} + inline Vec3d startpoint() const { - return {endpt(X), endpt(Y), endpt(Z) + height}; + return {endpt.x(), endpt.y(), endpt.z() + height}; } inline const Vec3d& endpoint() const { return endpt; } - Pillar& add_base(double baseheight = 3, double radius = 2); +// Pillar& add_base(double baseheight = 3, double radius = 2); +}; + +struct Pedestal { + Vec3d pos; + double height, radius; + size_t steps = 45; + + Pedestal() = default; + Pedestal(const Vec3d &p, double h = 3., double r = 2., size_t stps = 45) + : pos{p}, height{h}, radius{r}, steps{stps} + {} + + Pedestal(const Pillar &p, double h = 3., double r = 2.) + : Pedestal{p.endpt, std::min(h, p.height), std::max(r, p.r), p.steps} + {} +}; + +struct PinJoin { + }; // A Bridge between two pillars (with junction endpoints) @@ -241,66 +250,39 @@ struct Pad { bool empty() const { return tmesh.facets_count() == 0; } }; -// Give points on a 3D ring with given center, radius and orientation -// method based on: -// https://math.stackexchange.com/questions/73237/parametric-equation-of-a-circle-in-3d-space -template -class PointRing { - std::array m_phis; +inline Contour3D get_mesh(const Head &h) +{ + Contour3D mesh = pinhead(h.r_pin_mm, h.r_back_mm, h.width_mm, h.steps); - // Two vectors that will be perpendicular to each other and to the - // axis. Values for a(X) and a(Y) are now arbitrary, a(Z) is just a - // placeholder. - // a and b vectors are perpendicular to the ring direction and to each other. - // Together they define the plane where we have to iterate with the - // given angles in the 'm_phis' vector - Vec3d a = {0, 1, 0}, b; - double m_radius = 0.; + using Quaternion = Eigen::Quaternion; - static inline bool constexpr is_one(double val) - { - return std::abs(std::abs(val) - 1) < 1e-20; + // We rotate the head to the specified direction The head's pointing + // side is facing upwards so this means that it would hold a support + // point with a normal pointing straight down. This is the reason of + // the -1 z coordinate + auto quatern = Quaternion::FromTwoVectors(Vec3d{0, 0, -1}, h.dir); + + for(auto& p : mesh.points) p = quatern * p + h.pos; +} + +inline Contour3D get_mesh(const Pillar &p) +{ + assert(p.steps > 0); + + if(p.height > EPSILON) { // Endpoint is below the starting point + // We just create a bridge geometry with the pillar parameters and + // move the data. + return cylinder(p.r, p.height, p.steps, p.endpoint()); } -public: + return {}; +} - PointRing(const Vec3d &n) - { - m_phis = linspace_array(0., 2 * PI); +inline Contour3D get_mesh(const Pedestal &p, double h, double r) +{ + return pedestal(p.pos, p.height, p.radius, p.steps); +} - // We have to address the case when the direction vector v (same as - // dir) is coincident with one of the world axes. In this case two of - // its components will be completely zero and one is 1.0. Our method - // becomes dangerous here due to division with zero. Instead, vector - // 'a' can be an element-wise rotated version of 'v' - if(is_one(n(X)) || is_one(n(Y)) || is_one(n(Z))) { - a = {n(Z), n(X), n(Y)}; - b = {n(Y), n(Z), n(X)}; - } - else { - a(Z) = -(n(Y)*a(Y)) / n(Z); a.normalize(); - b = a.cross(n); - } - } - - Vec3d get(size_t idx, const Vec3d src, double r) const - { - double phi = m_phis[idx]; - double sinphi = std::sin(phi); - double cosphi = std::cos(phi); - - double rpscos = r * cosphi; - double rpssin = r * sinphi; - - // Point on the sphere - return {src(X) + rpscos * a(X) + rpssin * b(X), - src(Y) + rpscos * a(Y) + rpssin * b(Y), - src(Z) + rpscos * a(Z) + rpssin * b(Z)}; - } -}; - -EigenMesh3D::hit_result query_hit(const SupportableMesh &msh, const Bridge &br, double safety_d = std::nan("")); -EigenMesh3D::hit_result query_hit(const SupportableMesh &msh, const Head &br, double safety_d = std::nan("")); // This class will hold the support tree meshes with some additional // bookkeeping as well. Various parts of the support geometry are stored diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp index 334c88fb90..a8e79dc179 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -15,6 +15,119 @@ using libnest2d::opt::StopCriteria; using libnest2d::opt::GeneticOptimizer; using libnest2d::opt::SubplexOptimizer; +EigenMesh3D::hit_result query_hit(const SupportableMesh &msh, const Head &h) +{ + static const size_t SAMPLES = 8; + + // Move away slightly from the touching point to avoid raycasting on the + // inner surface of the mesh. + + const double& sd = msh.cfg.safety_distance_mm; + + auto& m = msh.emesh; + using HitResult = EigenMesh3D::hit_result; + + // Hit results + std::array hits; + + Vec3d s1 = h.pos, s2 = h.junction_point(); + + struct Rings { + double rpin; + double rback; + Vec3d spin; + Vec3d sback; + PointRing ring; + + Vec3d backring(size_t idx) { return ring.get(idx, sback, rback); } + Vec3d pinring(size_t idx) { return ring.get(idx, spin, rpin); } + } rings {h.r_pin_mm + sd, h.r_back_mm + sd, s1, s2, h.dir}; + + // We will shoot multiple rays from the head pinpoint in the direction + // of the pinhead robe (side) surface. The result will be the smallest + // hit distance. + + auto hitfn = [&m, &rings, sd](HitResult &hit, size_t i) { + // Point on the circle on the pin sphere + Vec3d ps = rings.pinring(i); + // This is the point on the circle on the back sphere + Vec3d p = rings.backring(i); + + // Point ps is not on mesh but can be inside or + // outside as well. This would cause many problems + // with ray-casting. To detect the position we will + // use the ray-casting result (which has an is_inside + // predicate). + + Vec3d n = (p - ps).normalized(); + auto q = m.query_ray_hit(ps + sd * n, n); + + if (q.is_inside()) { // the hit is inside the model + if (q.distance() > rings.rpin) { + // If we are inside the model and the hit + // distance is bigger than our pin circle + // diameter, it probably indicates that the + // support point was already inside the + // model, or there is really no space + // around the point. We will assign a zero + // hit distance to these cases which will + // enforce the function return value to be + // an invalid ray with zero hit distance. + // (see min_element at the end) + hit = HitResult(0.0); + } else { + // re-cast the ray from the outside of the + // object. The starting point has an offset + // of 2*safety_distance because the + // original ray has also had an offset + auto q2 = m.query_ray_hit(ps + (q.distance() + 2 * sd) * n, n); + hit = q2; + } + } else + hit = q; + }; + + ccr::enumerate(hits.begin(), hits.end(), hitfn); + + return min_hit(hits); +} + +EigenMesh3D::hit_result query_hit(const SupportableMesh &msh, const Bridge &br, double safety_d) +{ + + static const size_t SAMPLES = 8; + + Vec3d dir = (br.endp - br.startp).normalized(); + PointRing ring{dir}; + + using Hit = EigenMesh3D::hit_result; + + // Hit results + std::array hits; + + double sd = std::isnan(safety_d) ? msh.cfg.safety_distance_mm : safety_d; + + auto hitfn = [&msh, &br, &ring, dir, sd] (Hit &hit, size_t i) { + + // Point on the circle on the pin sphere + Vec3d p = ring.get(i, br.startp, br.r + sd); + + auto hr = msh.emesh.query_ray_hit(p + br.r * dir, dir); + + if(hr.is_inside()) { + if(hr.distance() > 2 * br.r + sd) hit = Hit(0.0); + else { + // re-cast the ray from the outside of the object + hit = msh.emesh.query_ray_hit(p + (hr.distance() + 2 * sd) * dir, dir); + } + } else hit = hr; + }; + + ccr::enumerate(hits.begin(), hits.end(), hitfn); + + return min_hit(hits); +} + SupportTreeBuildsteps::SupportTreeBuildsteps(SupportTreeBuilder & builder, const SupportableMesh &sm) : m_cfg(sm.cfg) @@ -246,7 +359,7 @@ EigenMesh3D::hit_result SupportTreeBuildsteps::pinhead_mesh_intersect( } EigenMesh3D::hit_result SupportTreeBuildsteps::bridge_mesh_intersect( - const Vec3d &src, const Vec3d &dir, double r, double safety_d) + const Vec3d &src, const Vec3d &dir, double r, double sd) { static const size_t SAMPLES = 8; PointRing ring{dir}; @@ -255,25 +368,20 @@ EigenMesh3D::hit_result SupportTreeBuildsteps::bridge_mesh_intersect( // Hit results std::array hits; - - double sd = std::isnan(safety_d) ? m_cfg.safety_distance_mm : safety_d; - sd = sd * r / m_cfg.head_back_radius_mm; - - bool ins_check = sd < m_cfg.safety_distance_mm; ccr::enumerate(hits.begin(), hits.end(), - [this, r, src, ins_check, &ring, dir, sd] (Hit &hit, size_t i) { + [this, r, src, /*ins_check,*/ &ring, dir, sd] (Hit &hit, size_t i) { // Point on the circle on the pin sphere Vec3d p = ring.get(i, src, r + sd); auto hr = m_mesh.query_ray_hit(p + r * dir, dir); - if(ins_check && hr.is_inside()) { + if(/*ins_check && */hr.is_inside()) { if(hr.distance() > 2 * r + sd) hit = Hit(0.0); else { // re-cast the ray from the outside of the object - hit = m_mesh.query_ray_hit(p + (hr.distance() + 2 * sd) * dir, dir); + hit = m_mesh.query_ray_hit(p + (hr.distance() + EPSILON) * dir, dir); } } else hit = hr; }); @@ -499,7 +607,7 @@ bool SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, normal_mode = false; if (t > m_cfg.max_bridge_length_mm || endp(Z) < gndlvl) { - m_builder.add_pillar(head_id, jp, radius); + if (head_id >= 0) m_builder.add_pillar(head_id, jp, radius); return false; } } @@ -507,7 +615,7 @@ bool SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, // Check if the deduced route is sane and exit with error if not. if (bridge_mesh_distance(jp, dir, radius) < (endp - jp).norm()) { - m_builder.add_pillar(head_id, jp, radius); + if (head_id >= 0) m_builder.add_pillar(head_id, jp, radius); return false; } @@ -798,13 +906,16 @@ void SupportTreeBuildsteps::routing_to_ground() cl_centroids.emplace_back(hid); Head &h = m_builder.head(hid); - h.transform(); if (!create_ground_pillar(h.junction_point(), h.dir, h.r_back_mm, h.id)) { BOOST_LOG_TRIVIAL(warning) << "Pillar cannot be created for support point id: " << hid; - h.invalidate(); + m_iheads_onmodel.emplace_back(h.id); +// h.invalidate(); + continue; } + + h.transform(); } // now we will go through the clusters ones again and connect the @@ -854,12 +965,14 @@ bool SupportTreeBuildsteps::connect_to_ground(Head &head, const Vec3d &dir) if(!std::isinf(tdown)) return false; Vec3d endp = hjp + d * dir; - m_builder.add_bridge(head.id, endp); - m_builder.add_junction(endp, head.r_back_mm); + bool ret = false; + + if ((ret = create_ground_pillar(endp, dir, head.r_back_mm))) { + m_builder.add_bridge(head.id, endp); + m_builder.add_junction(endp, head.r_back_mm); + } - this->create_ground_pillar(endp, dir, head.r_back_mm); - - return true; + return ret; } bool SupportTreeBuildsteps::connect_to_ground(Head &head) diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp index ae872f98b7..bfa38505b0 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp @@ -46,6 +46,68 @@ inline Vec3d spheric_to_dir(const std::pair &v) return spheric_to_dir(v.first, v.second); } + +// Give points on a 3D ring with given center, radius and orientation +// method based on: +// https://math.stackexchange.com/questions/73237/parametric-equation-of-a-circle-in-3d-space +template +class PointRing { + std::array m_phis; + + // Two vectors that will be perpendicular to each other and to the + // axis. Values for a(X) and a(Y) are now arbitrary, a(Z) is just a + // placeholder. + // a and b vectors are perpendicular to the ring direction and to each other. + // Together they define the plane where we have to iterate with the + // given angles in the 'm_phis' vector + Vec3d a = {0, 1, 0}, b; + double m_radius = 0.; + + static inline bool constexpr is_one(double val) + { + return std::abs(std::abs(val) - 1) < 1e-20; + } + +public: + + PointRing(const Vec3d &n) + { + m_phis = linspace_array(0., 2 * PI); + + // We have to address the case when the direction vector v (same as + // dir) is coincident with one of the world axes. In this case two of + // its components will be completely zero and one is 1.0. Our method + // becomes dangerous here due to division with zero. Instead, vector + // 'a' can be an element-wise rotated version of 'v' + if(is_one(n(X)) || is_one(n(Y)) || is_one(n(Z))) { + a = {n(Z), n(X), n(Y)}; + b = {n(Y), n(Z), n(X)}; + } + else { + a(Z) = -(n(Y)*a(Y)) / n(Z); a.normalize(); + b = a.cross(n); + } + } + + Vec3d get(size_t idx, const Vec3d src, double r) const + { + double phi = m_phis[idx]; + double sinphi = std::sin(phi); + double cosphi = std::cos(phi); + + double rpscos = r * cosphi; + double rpssin = r * sinphi; + + // Point on the sphere + return {src(X) + rpscos * a(X) + rpssin * b(X), + src(Y) + rpscos * a(Y) + rpssin * b(Y), + src(Z) + rpscos * a(Z) + rpssin * b(Z)}; + } +}; + +EigenMesh3D::hit_result query_hit(const SupportableMesh &msh, const Bridge &br, double safety_d = std::nan("")); +EigenMesh3D::hit_result query_hit(const SupportableMesh &msh, const Head &br, double safety_d = std::nan("")); + // This function returns the position of the centroid in the input 'clust' // vector of point indices. template @@ -242,7 +304,15 @@ class SupportTreeBuildsteps { const Vec3d& s, const Vec3d& dir, double r, - double safety_d = std::nan("")); + double safety_d); + + EigenMesh3D::hit_result bridge_mesh_intersect( + const Vec3d& s, + const Vec3d& dir, + double r) + { + return bridge_mesh_intersect(s, dir, r, m_cfg.safety_distance_mm); + } template inline double bridge_mesh_distance(Args&&...args) { From 184f64f8281229c0ffb36d273eda24fb12e14ebb Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 16 Jun 2020 13:17:06 +0200 Subject: [PATCH 240/503] Separate support tree routing and meshing, remove Common.hpp/.cpp . * Remove Common.hpp and Common.cpp, move things into their respective modules in sla. --- src/libslic3r/CMakeLists.txt | 7 +- src/libslic3r/OpenVDBUtils.hpp | 1 - src/libslic3r/SLA/BoostAdapter.hpp | 4 +- src/libslic3r/SLA/Clustering.cpp | 152 +++++++ src/libslic3r/SLA/Clustering.hpp | 58 ++- src/libslic3r/SLA/Common.hpp | 27 -- src/libslic3r/SLA/Contour3D.hpp | 9 +- .../SLA/{Common.cpp => EigenMesh3D.cpp} | 372 ++---------------- src/libslic3r/SLA/EigenMesh3D.hpp | 6 +- src/libslic3r/SLA/Hollowing.cpp | 3 +- src/libslic3r/SLA/Hollowing.hpp | 1 - src/libslic3r/SLA/JobController.hpp | 1 + src/libslic3r/SLA/Pad.cpp | 1 - src/libslic3r/SLA/Rotfinder.cpp | 1 - src/libslic3r/SLA/SpatIndex.cpp | 161 ++++++++ src/libslic3r/SLA/SpatIndex.hpp | 2 +- src/libslic3r/SLA/SupportPoint.hpp | 1 - src/libslic3r/SLA/SupportPointGenerator.hpp | 1 - src/libslic3r/SLA/SupportTree.cpp | 1 - src/libslic3r/SLA/SupportTree.hpp | 1 - src/libslic3r/SLA/SupportTreeBuilder.cpp | 326 +-------------- src/libslic3r/SLA/SupportTreeBuilder.hpp | 184 +++------ src/libslic3r/SLA/SupportTreeBuildsteps.cpp | 62 +-- src/libslic3r/SLA/SupportTreeBuildsteps.hpp | 50 +-- src/libslic3r/SLA/SupportTreeMesher.cpp | 268 +++++++++++++ src/libslic3r/SLA/SupportTreeMesher.hpp | 94 +++++ tests/sla_print/sla_treebuilder_tests.cpp | 15 +- 27 files changed, 897 insertions(+), 912 deletions(-) create mode 100644 src/libslic3r/SLA/Clustering.cpp delete mode 100644 src/libslic3r/SLA/Common.hpp rename src/libslic3r/SLA/{Common.cpp => EigenMesh3D.cpp} (58%) create mode 100644 src/libslic3r/SLA/SpatIndex.cpp create mode 100644 src/libslic3r/SLA/SupportTreeMesher.cpp create mode 100644 src/libslic3r/SLA/SupportTreeMesher.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 9f566b4051..20f3c6b4ba 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -204,11 +204,11 @@ add_library(libslic3r STATIC SimplifyMesh.cpp MarchingSquares.hpp ${OpenVDBUtils_SOURCES} - SLA/Common.hpp - SLA/Common.cpp SLA/Pad.hpp SLA/Pad.cpp SLA/SupportTreeBuilder.hpp + SLA/SupportTreeMesher.hpp + SLA/SupportTreeMesher.cpp SLA/SupportTreeBuildsteps.hpp SLA/SupportTreeBuildsteps.cpp SLA/SupportTreeBuilder.cpp @@ -220,6 +220,7 @@ add_library(libslic3r STATIC SLA/Rotfinder.cpp SLA/BoostAdapter.hpp SLA/SpatIndex.hpp + SLA/SpatIndex.cpp SLA/RasterBase.hpp SLA/RasterBase.cpp SLA/AGGRaster.hpp @@ -236,7 +237,9 @@ add_library(libslic3r STATIC SLA/Contour3D.hpp SLA/Contour3D.cpp SLA/EigenMesh3D.hpp + SLA/EigenMesh3D.cpp SLA/Clustering.hpp + SLA/Clustering.cpp SLA/ReprojectPointsOnMesh.hpp ) diff --git a/src/libslic3r/OpenVDBUtils.hpp b/src/libslic3r/OpenVDBUtils.hpp index c493845a1c..e35231d35b 100644 --- a/src/libslic3r/OpenVDBUtils.hpp +++ b/src/libslic3r/OpenVDBUtils.hpp @@ -2,7 +2,6 @@ #define OPENVDBUTILS_HPP #include -#include #include #include diff --git a/src/libslic3r/SLA/BoostAdapter.hpp b/src/libslic3r/SLA/BoostAdapter.hpp index b7b3c63a6c..13e0465b14 100644 --- a/src/libslic3r/SLA/BoostAdapter.hpp +++ b/src/libslic3r/SLA/BoostAdapter.hpp @@ -1,7 +1,9 @@ #ifndef SLA_BOOSTADAPTER_HPP #define SLA_BOOSTADAPTER_HPP -#include +#include +#include + #include namespace boost { diff --git a/src/libslic3r/SLA/Clustering.cpp b/src/libslic3r/SLA/Clustering.cpp new file mode 100644 index 0000000000..41ff1d4f09 --- /dev/null +++ b/src/libslic3r/SLA/Clustering.cpp @@ -0,0 +1,152 @@ +#include "Clustering.hpp" +#include "boost/geometry/index/rtree.hpp" + +#include +#include + +namespace Slic3r { namespace sla { + +namespace bgi = boost::geometry::index; +using Index3D = bgi::rtree< PointIndexEl, bgi::rstar<16, 4> /* ? */ >; + +namespace { + +bool cmp_ptidx_elements(const PointIndexEl& e1, const PointIndexEl& e2) +{ + return e1.second < e2.second; +}; + +ClusteredPoints cluster(Index3D &sindex, + unsigned max_points, + std::function( + const Index3D &, const PointIndexEl &)> qfn) +{ + using Elems = std::vector; + + // Recursive function for visiting all the points in a given distance to + // each other + std::function group = + [&sindex, &group, max_points, qfn](Elems& pts, Elems& cluster) + { + for(auto& p : pts) { + std::vector tmp = qfn(sindex, p); + + std::sort(tmp.begin(), tmp.end(), cmp_ptidx_elements); + + Elems newpts; + std::set_difference(tmp.begin(), tmp.end(), + cluster.begin(), cluster.end(), + std::back_inserter(newpts), cmp_ptidx_elements); + + int c = max_points && newpts.size() + cluster.size() > max_points? + int(max_points - cluster.size()) : int(newpts.size()); + + cluster.insert(cluster.end(), newpts.begin(), newpts.begin() + c); + std::sort(cluster.begin(), cluster.end(), cmp_ptidx_elements); + + if(!newpts.empty() && (!max_points || cluster.size() < max_points)) + group(newpts, cluster); + } + }; + + std::vector clusters; + for(auto it = sindex.begin(); it != sindex.end();) { + Elems cluster = {}; + Elems pts = {*it}; + group(pts, cluster); + + for(auto& c : cluster) sindex.remove(c); + it = sindex.begin(); + + clusters.emplace_back(cluster); + } + + ClusteredPoints result; + for(auto& cluster : clusters) { + result.emplace_back(); + for(auto c : cluster) result.back().emplace_back(c.second); + } + + return result; +} + +std::vector distance_queryfn(const Index3D& sindex, + const PointIndexEl& p, + double dist, + unsigned max_points) +{ + std::vector tmp; tmp.reserve(max_points); + sindex.query( + bgi::nearest(p.first, max_points), + std::back_inserter(tmp) + ); + + for(auto it = tmp.begin(); it < tmp.end(); ++it) + if((p.first - it->first).norm() > dist) it = tmp.erase(it); + + return tmp; +} + +} // namespace + +// Clustering a set of points by the given criteria +ClusteredPoints cluster( + const std::vector& indices, + std::function pointfn, + double dist, + unsigned max_points) +{ + // A spatial index for querying the nearest points + Index3D sindex; + + // Build the index + for(auto idx : indices) sindex.insert( std::make_pair(pointfn(idx), idx)); + + return cluster(sindex, max_points, + [dist, max_points](const Index3D& sidx, const PointIndexEl& p) + { + return distance_queryfn(sidx, p, dist, max_points); + }); +} + +// Clustering a set of points by the given criteria +ClusteredPoints cluster( + const std::vector& indices, + std::function pointfn, + std::function predicate, + unsigned max_points) +{ + // A spatial index for querying the nearest points + Index3D sindex; + + // Build the index + for(auto idx : indices) sindex.insert( std::make_pair(pointfn(idx), idx)); + + return cluster(sindex, max_points, + [max_points, predicate](const Index3D& sidx, const PointIndexEl& p) + { + std::vector tmp; tmp.reserve(max_points); + sidx.query(bgi::satisfies([p, predicate](const PointIndexEl& e){ + return predicate(p, e); + }), std::back_inserter(tmp)); + return tmp; + }); +} + +ClusteredPoints cluster(const Eigen::MatrixXd& pts, double dist, unsigned max_points) +{ + // A spatial index for querying the nearest points + Index3D sindex; + + // Build the index + for(Eigen::Index i = 0; i < pts.rows(); i++) + sindex.insert(std::make_pair(Vec3d(pts.row(i)), unsigned(i))); + + return cluster(sindex, max_points, + [dist, max_points](const Index3D& sidx, const PointIndexEl& p) + { + return distance_queryfn(sidx, p, dist, max_points); + }); +} + +}} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/Clustering.hpp b/src/libslic3r/SLA/Clustering.hpp index 1b0d47d953..269ec28822 100644 --- a/src/libslic3r/SLA/Clustering.hpp +++ b/src/libslic3r/SLA/Clustering.hpp @@ -2,7 +2,8 @@ #define SLA_CLUSTERING_HPP #include -#include + +#include #include namespace Slic3r { namespace sla { @@ -16,7 +17,7 @@ ClusteredPoints cluster(const std::vector& indices, double dist, unsigned max_points); -ClusteredPoints cluster(const PointSet& points, +ClusteredPoints cluster(const Eigen::MatrixXd& points, double dist, unsigned max_points); @@ -26,5 +27,56 @@ ClusteredPoints cluster( std::function predicate, unsigned max_points); -}} +// This function returns the position of the centroid in the input 'clust' +// vector of point indices. +template +long cluster_centroid(const ClusterEl &clust, PointFn pointfn, DistFn df) +{ + switch(clust.size()) { + case 0: /* empty cluster */ return -1; + case 1: /* only one element */ return 0; + case 2: /* if two elements, there is no center */ return 0; + default: ; + } + + // The function works by calculating for each point the average distance + // from all the other points in the cluster. We create a selector bitmask of + // the same size as the cluster. The bitmask will have two true bits and + // false bits for the rest of items and we will loop through all the + // permutations of the bitmask (combinations of two points). Get the + // distance for the two points and add the distance to the averages. + // The point with the smallest average than wins. + + // The complexity should be O(n^2) but we will mostly apply this function + // for small clusters only (cca 3 elements) + + std::vector sel(clust.size(), false); // create full zero bitmask + std::fill(sel.end() - 2, sel.end(), true); // insert the two ones + std::vector avgs(clust.size(), 0.0); // store the average distances + + do { + std::array idx; + for(size_t i = 0, j = 0; i < clust.size(); i++) + if(sel[i]) idx[j++] = i; + + double d = df(pointfn(clust[idx[0]]), + pointfn(clust[idx[1]])); + + // add the distance to the sums for both associated points + for(auto i : idx) avgs[i] += d; + + // now continue with the next permutation of the bitmask with two 1s + } while(std::next_permutation(sel.begin(), sel.end())); + + // Divide by point size in the cluster to get the average (may be redundant) + for(auto& a : avgs) a /= clust.size(); + + // get the lowest average distance and return the index + auto minit = std::min_element(avgs.begin(), avgs.end()); + return long(minit - avgs.begin()); +} + + +}} // namespace Slic3r::sla + #endif // CLUSTERING_HPP diff --git a/src/libslic3r/SLA/Common.hpp b/src/libslic3r/SLA/Common.hpp deleted file mode 100644 index ca616cabce..0000000000 --- a/src/libslic3r/SLA/Common.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef SLA_COMMON_HPP -#define SLA_COMMON_HPP - -#include -#include -#include -#include -#include - - -namespace Slic3r { - -// Typedefs from Point.hpp -typedef Eigen::Matrix Vec3f; -typedef Eigen::Matrix Vec3d; -typedef Eigen::Matrix Vec3i; -typedef Eigen::Matrix Vec4i; - -namespace sla { - -using PointSet = Eigen::MatrixXd; - -} // namespace sla -} // namespace Slic3r - - -#endif // SLASUPPORTTREE_HPP diff --git a/src/libslic3r/SLA/Contour3D.hpp b/src/libslic3r/SLA/Contour3D.hpp index 295612f19b..1a4fa9a29c 100644 --- a/src/libslic3r/SLA/Contour3D.hpp +++ b/src/libslic3r/SLA/Contour3D.hpp @@ -1,11 +1,14 @@ #ifndef SLA_CONTOUR3D_HPP #define SLA_CONTOUR3D_HPP -#include - #include -namespace Slic3r { namespace sla { +namespace Slic3r { + +// Used for quads (TODO: remove this, and convert quads to triangles in OpenVDBUtils) +using Vec4i = Eigen::Matrix; + +namespace sla { class EigenMesh3D; diff --git a/src/libslic3r/SLA/Common.cpp b/src/libslic3r/SLA/EigenMesh3D.cpp similarity index 58% rename from src/libslic3r/SLA/Common.cpp rename to src/libslic3r/SLA/EigenMesh3D.cpp index a7420a7fb8..be44e324c6 100644 --- a/src/libslic3r/SLA/Common.cpp +++ b/src/libslic3r/SLA/EigenMesh3D.cpp @@ -1,185 +1,16 @@ -#include -#include -#include -#include -#include -#include -#include +#include "EigenMesh3D.hpp" +#include "Concurrency.hpp" + #include +#include -// for concave hull merging decisions -#include -#include "boost/geometry/index/rtree.hpp" - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4244) -#pragma warning(disable: 4267) -#endif - - -#include +#include #ifdef SLIC3R_HOLE_RAYCASTER - #include +#include #endif - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - - -namespace Slic3r { -namespace sla { - - -/* ************************************************************************** - * PointIndex implementation - * ************************************************************************** */ - -class PointIndex::Impl { -public: - using BoostIndex = boost::geometry::index::rtree< PointIndexEl, - boost::geometry::index::rstar<16, 4> /* ? */ >; - - BoostIndex m_store; -}; - -PointIndex::PointIndex(): m_impl(new Impl()) {} -PointIndex::~PointIndex() {} - -PointIndex::PointIndex(const PointIndex &cpy): m_impl(new Impl(*cpy.m_impl)) {} -PointIndex::PointIndex(PointIndex&& cpy): m_impl(std::move(cpy.m_impl)) {} - -PointIndex& PointIndex::operator=(const PointIndex &cpy) -{ - m_impl.reset(new Impl(*cpy.m_impl)); - return *this; -} - -PointIndex& PointIndex::operator=(PointIndex &&cpy) -{ - m_impl.swap(cpy.m_impl); - return *this; -} - -void PointIndex::insert(const PointIndexEl &el) -{ - m_impl->m_store.insert(el); -} - -bool PointIndex::remove(const PointIndexEl& el) -{ - return m_impl->m_store.remove(el) == 1; -} - -std::vector -PointIndex::query(std::function fn) const -{ - namespace bgi = boost::geometry::index; - - std::vector ret; - m_impl->m_store.query(bgi::satisfies(fn), std::back_inserter(ret)); - return ret; -} - -std::vector PointIndex::nearest(const Vec3d &el, unsigned k = 1) const -{ - namespace bgi = boost::geometry::index; - std::vector ret; ret.reserve(k); - m_impl->m_store.query(bgi::nearest(el, k), std::back_inserter(ret)); - return ret; -} - -size_t PointIndex::size() const -{ - return m_impl->m_store.size(); -} - -void PointIndex::foreach(std::function fn) -{ - for(auto& el : m_impl->m_store) fn(el); -} - -void PointIndex::foreach(std::function fn) const -{ - for(const auto &el : m_impl->m_store) fn(el); -} - -/* ************************************************************************** - * BoxIndex implementation - * ************************************************************************** */ - -class BoxIndex::Impl { -public: - using BoostIndex = boost::geometry::index:: - rtree /* ? */>; - - 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 BoxIndex::query(const BoundingBox &qrbb, - BoxIndex::QueryType qt) -{ - namespace bgi = boost::geometry::index; - - std::vector 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 fn) -{ - for(auto& el : m_impl->m_store) fn(el); -} - - -/* **************************************************************************** - * EigenMesh3D implementation - * ****************************************************************************/ - +namespace Slic3r { namespace sla { class EigenMesh3D::AABBImpl { private: @@ -189,7 +20,7 @@ public: void init(const TriangleMesh& tm) { m_tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set( - tm.its.vertices, tm.its.indices); + tm.its.vertices, tm.its.indices); } void intersect_ray(const TriangleMesh& tm, @@ -215,9 +46,9 @@ public: size_t idx_unsigned = 0; Vec3d closest_vec3d(closest); double dist = AABBTreeIndirect::squared_distance_to_indexed_triangle_set( - tm.its.vertices, - tm.its.indices, - m_tree, point, idx_unsigned, closest_vec3d); + tm.its.vertices, + tm.its.indices, + m_tree, point, idx_unsigned, closest_vec3d); i = int(idx_unsigned); closest = closest_vec3d; return dist; @@ -231,7 +62,7 @@ EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh) { auto&& bb = tmesh.bounding_box(); m_ground_level += bb.min(Z); - + // Build the AABB accelaration tree m_aabb->init(tmesh); } @@ -289,7 +120,6 @@ Vec3d EigenMesh3D::normal_by_face_id(int face_id) const { } - EigenMesh3D::hit_result EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const { @@ -325,7 +155,7 @@ EigenMesh3D::query_ray_hits(const Vec3d &s, const Vec3d &dir) const std::vector outs; std::vector hits; m_aabb->intersect_ray(*m_tm, s, dir, hits); - + // The sort is necessary, the hits are not always sorted. std::sort(hits.begin(), hits.end(), [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; }); @@ -334,7 +164,7 @@ EigenMesh3D::query_ray_hits(const Vec3d &s, const Vec3d &dir) const // along an axis of a cube due to floating-point approximations in igl (?) hits.erase(std::unique(hits.begin(), hits.end(), [](const igl::Hit& a, const igl::Hit& b) - { return a.t == b.t; }), + { return a.t == b.t; }), hits.end()); // Convert the igl::Hit into hit_result @@ -356,7 +186,7 @@ EigenMesh3D::query_ray_hits(const Vec3d &s, const Vec3d &dir) const #ifdef SLIC3R_HOLE_RAYCASTER EigenMesh3D::hit_result EigenMesh3D::filter_hits( - const std::vector& object_hits) const + const std::vector& object_hits) const { assert(! m_holes.empty()); hit_result out(*this); @@ -377,7 +207,7 @@ EigenMesh3D::hit_result EigenMesh3D::filter_hits( }; std::vector hole_isects; hole_isects.reserve(m_holes.size()); - + auto sf = s.cast(); auto dirf = dir.cast(); @@ -461,29 +291,17 @@ double EigenMesh3D::squared_distance(const Vec3d &p, int& i, Vec3d& c) const { return sqdst; } -/* **************************************************************************** - * Misc functions - * ****************************************************************************/ -namespace { - -bool point_on_edge(const Vec3d& p, const Vec3d& e1, const Vec3d& e2, - double eps = 0.05) +static bool point_on_edge(const Vec3d& p, const Vec3d& e1, const Vec3d& e2, + double eps = 0.05) { using Line3D = Eigen::ParametrizedLine; - + auto line = Line3D::Through(e1, e2); double d = line.distance(p); return std::abs(d) < eps; } -template double distance(const Vec& pp1, const Vec& pp2) { - auto p = pp2 - pp1; - return std::sqrt(p.transpose() * p); -} - -} - PointSet normals(const PointSet& points, const EigenMesh3D& mesh, double eps, @@ -531,11 +349,11 @@ PointSet normals(const PointSet& points, // ic will mark a single vertex. int ia = -1, ib = -1, ic = -1; - if (std::abs(distance(p, p1)) < eps) { + if (std::abs((p - p1).norm()) < eps) { ic = trindex(0); - } else if (std::abs(distance(p, p2)) < eps) { + } else if (std::abs((p - p2).norm()) < eps) { ic = trindex(1); - } else if (std::abs(distance(p, p3)) < eps) { + } else if (std::abs((p - p3).norm()) < eps) { ic = trindex(2); } else if (point_on_edge(p, p1, p2, eps)) { ia = trindex(0); @@ -612,148 +430,4 @@ PointSet normals(const PointSet& points, return ret; } -namespace bgi = boost::geometry::index; -using Index3D = bgi::rtree< PointIndexEl, bgi::rstar<16, 4> /* ? */ >; - -namespace { - -bool cmp_ptidx_elements(const PointIndexEl& e1, const PointIndexEl& e2) -{ - return e1.second < e2.second; -}; - -ClusteredPoints cluster(Index3D &sindex, - unsigned max_points, - std::function( - const Index3D &, const PointIndexEl &)> qfn) -{ - using Elems = std::vector; - - // Recursive function for visiting all the points in a given distance to - // each other - std::function group = - [&sindex, &group, max_points, qfn](Elems& pts, Elems& cluster) - { - for(auto& p : pts) { - std::vector tmp = qfn(sindex, p); - - std::sort(tmp.begin(), tmp.end(), cmp_ptidx_elements); - - Elems newpts; - std::set_difference(tmp.begin(), tmp.end(), - cluster.begin(), cluster.end(), - std::back_inserter(newpts), cmp_ptidx_elements); - - int c = max_points && newpts.size() + cluster.size() > max_points? - int(max_points - cluster.size()) : int(newpts.size()); - - cluster.insert(cluster.end(), newpts.begin(), newpts.begin() + c); - std::sort(cluster.begin(), cluster.end(), cmp_ptidx_elements); - - if(!newpts.empty() && (!max_points || cluster.size() < max_points)) - group(newpts, cluster); - } - }; - - std::vector clusters; - for(auto it = sindex.begin(); it != sindex.end();) { - Elems cluster = {}; - Elems pts = {*it}; - group(pts, cluster); - - for(auto& c : cluster) sindex.remove(c); - it = sindex.begin(); - - clusters.emplace_back(cluster); - } - - ClusteredPoints result; - for(auto& cluster : clusters) { - result.emplace_back(); - for(auto c : cluster) result.back().emplace_back(c.second); - } - - return result; -} - -std::vector distance_queryfn(const Index3D& sindex, - const PointIndexEl& p, - double dist, - unsigned max_points) -{ - std::vector tmp; tmp.reserve(max_points); - sindex.query( - bgi::nearest(p.first, max_points), - std::back_inserter(tmp) - ); - - for(auto it = tmp.begin(); it < tmp.end(); ++it) - if(distance(p.first, it->first) > dist) it = tmp.erase(it); - - return tmp; -} - -} // namespace - -// Clustering a set of points by the given criteria -ClusteredPoints cluster( - const std::vector& indices, - std::function pointfn, - double dist, - unsigned max_points) -{ - // A spatial index for querying the nearest points - Index3D sindex; - - // Build the index - for(auto idx : indices) sindex.insert( std::make_pair(pointfn(idx), idx)); - - return cluster(sindex, max_points, - [dist, max_points](const Index3D& sidx, const PointIndexEl& p) - { - return distance_queryfn(sidx, p, dist, max_points); - }); -} - -// Clustering a set of points by the given criteria -ClusteredPoints cluster( - const std::vector& indices, - std::function pointfn, - std::function predicate, - unsigned max_points) -{ - // A spatial index for querying the nearest points - Index3D sindex; - - // Build the index - for(auto idx : indices) sindex.insert( std::make_pair(pointfn(idx), idx)); - - return cluster(sindex, max_points, - [max_points, predicate](const Index3D& sidx, const PointIndexEl& p) - { - std::vector tmp; tmp.reserve(max_points); - sidx.query(bgi::satisfies([p, predicate](const PointIndexEl& e){ - return predicate(p, e); - }), std::back_inserter(tmp)); - return tmp; - }); -} - -ClusteredPoints cluster(const PointSet& pts, double dist, unsigned max_points) -{ - // A spatial index for querying the nearest points - Index3D sindex; - - // Build the index - for(Eigen::Index i = 0; i < pts.rows(); i++) - sindex.insert(std::make_pair(Vec3d(pts.row(i)), unsigned(i))); - - return cluster(sindex, max_points, - [dist, max_points](const Index3D& sidx, const PointIndexEl& p) - { - return distance_queryfn(sidx, p, dist, max_points); - }); -} - -} // namespace sla -} // namespace Slic3r +}} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/EigenMesh3D.hpp b/src/libslic3r/SLA/EigenMesh3D.hpp index 7b7562d477..c9196bb432 100644 --- a/src/libslic3r/SLA/EigenMesh3D.hpp +++ b/src/libslic3r/SLA/EigenMesh3D.hpp @@ -1,8 +1,10 @@ #ifndef SLA_EIGENMESH3D_H #define SLA_EIGENMESH3D_H -#include +#include +#include +#include // There is an implementation of a hole-aware raycaster that was eventually // not used in production version. It is now hidden under following define @@ -19,6 +21,8 @@ class TriangleMesh; namespace sla { +using PointSet = Eigen::MatrixXd; + /// An index-triangle structure for libIGL functions. Also serves as an /// alternative (raw) input format for the SLASupportTree. // Implemented in libslic3r/SLA/Common.cpp diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index 0dd9436a1d..44e4dd8390 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -3,11 +3,10 @@ #include #include #include -#include #include -#include #include #include +#include #include diff --git a/src/libslic3r/SLA/Hollowing.hpp b/src/libslic3r/SLA/Hollowing.hpp index cc7d310eae..1f65fa8b70 100644 --- a/src/libslic3r/SLA/Hollowing.hpp +++ b/src/libslic3r/SLA/Hollowing.hpp @@ -2,7 +2,6 @@ #define SLA_HOLLOWING_HPP #include -#include #include #include diff --git a/src/libslic3r/SLA/JobController.hpp b/src/libslic3r/SLA/JobController.hpp index 3baa3d12d1..b815e4d6fc 100644 --- a/src/libslic3r/SLA/JobController.hpp +++ b/src/libslic3r/SLA/JobController.hpp @@ -2,6 +2,7 @@ #define SLA_JOBCONTROLLER_HPP #include +#include namespace Slic3r { namespace sla { diff --git a/src/libslic3r/SLA/Pad.cpp b/src/libslic3r/SLA/Pad.cpp index d933ef5ed7..f2b189cd11 100644 --- a/src/libslic3r/SLA/Pad.cpp +++ b/src/libslic3r/SLA/Pad.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include diff --git a/src/libslic3r/SLA/Rotfinder.cpp b/src/libslic3r/SLA/Rotfinder.cpp index fda8383b11..81ef00e6b3 100644 --- a/src/libslic3r/SLA/Rotfinder.cpp +++ b/src/libslic3r/SLA/Rotfinder.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include #include "Model.hpp" diff --git a/src/libslic3r/SLA/SpatIndex.cpp b/src/libslic3r/SLA/SpatIndex.cpp new file mode 100644 index 0000000000..d95ba55bee --- /dev/null +++ b/src/libslic3r/SLA/SpatIndex.cpp @@ -0,0 +1,161 @@ +#include "SpatIndex.hpp" + +// for concave hull merging decisions +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4244) +#pragma warning(disable: 4267) +#endif + +#include "boost/geometry/index/rtree.hpp" + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace Slic3r { namespace sla { + +/* ************************************************************************** + * PointIndex implementation + * ************************************************************************** */ + +class PointIndex::Impl { +public: + using BoostIndex = boost::geometry::index::rtree< PointIndexEl, + boost::geometry::index::rstar<16, 4> /* ? */ >; + + BoostIndex m_store; +}; + +PointIndex::PointIndex(): m_impl(new Impl()) {} +PointIndex::~PointIndex() {} + +PointIndex::PointIndex(const PointIndex &cpy): m_impl(new Impl(*cpy.m_impl)) {} +PointIndex::PointIndex(PointIndex&& cpy): m_impl(std::move(cpy.m_impl)) {} + +PointIndex& PointIndex::operator=(const PointIndex &cpy) +{ + m_impl.reset(new Impl(*cpy.m_impl)); + return *this; +} + +PointIndex& PointIndex::operator=(PointIndex &&cpy) +{ + m_impl.swap(cpy.m_impl); + return *this; +} + +void PointIndex::insert(const PointIndexEl &el) +{ + m_impl->m_store.insert(el); +} + +bool PointIndex::remove(const PointIndexEl& el) +{ + return m_impl->m_store.remove(el) == 1; +} + +std::vector +PointIndex::query(std::function fn) const +{ + namespace bgi = boost::geometry::index; + + std::vector ret; + m_impl->m_store.query(bgi::satisfies(fn), std::back_inserter(ret)); + return ret; +} + +std::vector PointIndex::nearest(const Vec3d &el, unsigned k = 1) const +{ + namespace bgi = boost::geometry::index; + std::vector ret; ret.reserve(k); + m_impl->m_store.query(bgi::nearest(el, k), std::back_inserter(ret)); + return ret; +} + +size_t PointIndex::size() const +{ + return m_impl->m_store.size(); +} + +void PointIndex::foreach(std::function fn) +{ + for(auto& el : m_impl->m_store) fn(el); +} + +void PointIndex::foreach(std::function fn) const +{ + for(const auto &el : m_impl->m_store) fn(el); +} + +/* ************************************************************************** + * BoxIndex implementation + * ************************************************************************** */ + +class BoxIndex::Impl { +public: + using BoostIndex = boost::geometry::index:: + rtree /* ? */>; + + 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 BoxIndex::query(const BoundingBox &qrbb, + BoxIndex::QueryType qt) +{ + namespace bgi = boost::geometry::index; + + std::vector 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 fn) +{ + for(auto& el : m_impl->m_store) fn(el); +} + +}} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/SpatIndex.hpp b/src/libslic3r/SLA/SpatIndex.hpp index 2955cdcdf6..ef059d3ae6 100644 --- a/src/libslic3r/SLA/SpatIndex.hpp +++ b/src/libslic3r/SLA/SpatIndex.hpp @@ -73,7 +73,7 @@ public: BoxIndex& operator=(BoxIndex&&); void insert(const BoxIndexEl&); - inline void insert(const BoundingBox& bb, unsigned idx) + void insert(const BoundingBox& bb, unsigned idx) { insert(std::make_pair(bb, unsigned(idx))); } diff --git a/src/libslic3r/SLA/SupportPoint.hpp b/src/libslic3r/SLA/SupportPoint.hpp index 455962cc40..2b973697bb 100644 --- a/src/libslic3r/SLA/SupportPoint.hpp +++ b/src/libslic3r/SLA/SupportPoint.hpp @@ -2,7 +2,6 @@ #define SLA_SUPPORTPOINT_HPP #include -#include #include namespace Slic3r { namespace sla { diff --git a/src/libslic3r/SLA/SupportPointGenerator.hpp b/src/libslic3r/SLA/SupportPointGenerator.hpp index 1729230561..3f07e96746 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.hpp +++ b/src/libslic3r/SLA/SupportPointGenerator.hpp @@ -3,7 +3,6 @@ #include -#include #include #include diff --git a/src/libslic3r/SLA/SupportTree.cpp b/src/libslic3r/SLA/SupportTree.cpp index 2edc4d21b1..eec819e225 100644 --- a/src/libslic3r/SLA/SupportTree.cpp +++ b/src/libslic3r/SLA/SupportTree.cpp @@ -5,7 +5,6 @@ #include #include -#include #include #include #include diff --git a/src/libslic3r/SLA/SupportTree.hpp b/src/libslic3r/SLA/SupportTree.hpp index c6255aa2f2..3b9f603fd2 100644 --- a/src/libslic3r/SLA/SupportTree.hpp +++ b/src/libslic3r/SLA/SupportTree.hpp @@ -5,7 +5,6 @@ #include #include -#include #include #include #include diff --git a/src/libslic3r/SLA/SupportTreeBuilder.cpp b/src/libslic3r/SLA/SupportTreeBuilder.cpp index ebeca78a72..d4a9d00c99 100644 --- a/src/libslic3r/SLA/SupportTreeBuilder.cpp +++ b/src/libslic3r/SLA/SupportTreeBuilder.cpp @@ -1,278 +1,18 @@ #include #include +#include #include namespace Slic3r { namespace sla { -Contour3D sphere(double rho, Portion portion, double fa) { - - Contour3D ret; - - // prohibit close to zero radius - if(rho <= 1e-6 && rho >= -1e-6) return ret; - - auto& vertices = ret.points; - auto& facets = ret.faces3; - - // Algorithm: - // Add points one-by-one to the sphere grid and form facets using relative - // coordinates. Sphere is composed effectively of a mesh of stacked circles. - - // adjust via rounding to get an even multiple for any provided angle. - double angle = (2*PI / floor(2*PI / fa)); - - // Ring to be scaled to generate the steps of the sphere - std::vector ring; - - for (double i = 0; i < 2*PI; i+=angle) ring.emplace_back(i); - - const auto sbegin = size_t(2*std::get<0>(portion)/angle); - const auto send = size_t(2*std::get<1>(portion)/angle); - - const size_t steps = ring.size(); - const double increment = 1.0 / double(steps); - - // special case: first ring connects to 0,0,0 - // insert and form facets. - if(sbegin == 0) - vertices.emplace_back(Vec3d(0.0, 0.0, -rho + increment*sbegin*2.0*rho)); - - auto id = coord_t(vertices.size()); - for (size_t i = 0; i < ring.size(); i++) { - // Fixed scaling - const double z = -rho + increment*rho*2.0 * (sbegin + 1.0); - // radius of the circle for this step. - const double r = std::sqrt(std::abs(rho*rho - z*z)); - Vec2d b = Eigen::Rotation2Dd(ring[i]) * Eigen::Vector2d(0, r); - vertices.emplace_back(Vec3d(b(0), b(1), z)); - - if (sbegin == 0) - (i == 0) ? facets.emplace_back(coord_t(ring.size()), 0, 1) : - facets.emplace_back(id - 1, 0, id); - ++id; - } - - // General case: insert and form facets for each step, - // joining it to the ring below it. - for (size_t s = sbegin + 2; s < send - 1; s++) { - const double z = -rho + increment*double(s*2.0*rho); - const double r = std::sqrt(std::abs(rho*rho - z*z)); - - for (size_t i = 0; i < ring.size(); i++) { - Vec2d b = Eigen::Rotation2Dd(ring[i]) * Eigen::Vector2d(0, r); - vertices.emplace_back(Vec3d(b(0), b(1), z)); - auto id_ringsize = coord_t(id - int(ring.size())); - if (i == 0) { - // wrap around - facets.emplace_back(id - 1, id, id + coord_t(ring.size() - 1) ); - facets.emplace_back(id - 1, id_ringsize, id); - } else { - facets.emplace_back(id_ringsize - 1, id_ringsize, id); - facets.emplace_back(id - 1, id_ringsize - 1, id); - } - id++; - } - } - - // special case: last ring connects to 0,0,rho*2.0 - // only form facets. - if(send >= size_t(2*PI / angle)) { - vertices.emplace_back(Vec3d(0.0, 0.0, -rho + increment*send*2.0*rho)); - for (size_t i = 0; i < ring.size(); i++) { - auto id_ringsize = coord_t(id - int(ring.size())); - if (i == 0) { - // third vertex is on the other side of the ring. - facets.emplace_back(id - 1, id_ringsize, id); - } else { - auto ci = coord_t(id_ringsize + coord_t(i)); - facets.emplace_back(ci - 1, ci, id); - } - } - } - id++; - - return ret; -} - -Contour3D cylinder(double r, double h, size_t ssteps, const Vec3d &sp) -{ - Contour3D ret; - - auto steps = int(ssteps); - auto& points = ret.points; - auto& indices = ret.faces3; - points.reserve(2*ssteps); - double a = 2*PI/steps; - - Vec3d jp = sp; - Vec3d endp = {sp(X), sp(Y), sp(Z) + h}; - - // Upper circle points - for(int i = 0; i < steps; ++i) { - double phi = i*a; - double ex = endp(X) + r*std::cos(phi); - double ey = endp(Y) + r*std::sin(phi); - points.emplace_back(ex, ey, endp(Z)); - } - - // Lower circle points - for(int i = 0; i < steps; ++i) { - double phi = i*a; - double x = jp(X) + r*std::cos(phi); - double y = jp(Y) + r*std::sin(phi); - points.emplace_back(x, y, jp(Z)); - } - - // Now create long triangles connecting upper and lower circles - indices.reserve(2*ssteps); - auto offs = steps; - for(int i = 0; i < steps - 1; ++i) { - indices.emplace_back(i, i + offs, offs + i + 1); - indices.emplace_back(i, offs + i + 1, i + 1); - } - - // Last triangle connecting the first and last vertices - auto last = steps - 1; - indices.emplace_back(0, last, offs); - indices.emplace_back(last, offs + last, offs); - - // According to the slicing algorithms, we need to aid them with generating - // a watertight body. So we create a triangle fan for the upper and lower - // ending of the cylinder to close the geometry. - points.emplace_back(jp); int ci = int(points.size() - 1); - for(int i = 0; i < steps - 1; ++i) - indices.emplace_back(i + offs + 1, i + offs, ci); - - indices.emplace_back(offs, steps + offs - 1, ci); - - points.emplace_back(endp); ci = int(points.size() - 1); - for(int i = 0; i < steps - 1; ++i) - indices.emplace_back(ci, i, i + 1); - - indices.emplace_back(steps - 1, 0, ci); - - return ret; -} - -Contour3D pinhead(double r_pin, double r_back, double length, size_t steps) -{ - assert(length > 0.); - assert(r_back > 0.); - assert(r_pin > 0.); - - Contour3D mesh; - - // We create two spheres which will be connected with a robe that fits - // both circles perfectly. - - // Set up the model detail level - const double detail = 2*PI/steps; - - // We don't generate whole circles. Instead, we generate only the - // portions which are visible (not covered by the robe) To know the - // exact portion of the bottom and top circles we need to use some - // rules of tangent circles from which we can derive (using simple - // triangles the following relations: - - // The height of the whole mesh - const double h = r_back + r_pin + length; - double phi = PI / 2. - std::acos((r_back - r_pin) / h); - - // To generate a whole circle we would pass a portion of (0, Pi) - // To generate only a half horizontal circle we can pass (0, Pi/2) - // The calculated phi is an offset to the half circles needed to smooth - // the transition from the circle to the robe geometry - - auto&& s1 = sphere(r_back, make_portion(0, PI/2 + phi), detail); - auto&& s2 = sphere(r_pin, make_portion(PI/2 + phi, PI), detail); - - for(auto& p : s2.points) p.z() += h; - - mesh.merge(s1); - mesh.merge(s2); - - for(size_t idx1 = s1.points.size() - steps, idx2 = s1.points.size(); - idx1 < s1.points.size() - 1; - idx1++, idx2++) - { - coord_t i1s1 = coord_t(idx1), i1s2 = coord_t(idx2); - coord_t i2s1 = i1s1 + 1, i2s2 = i1s2 + 1; - - mesh.faces3.emplace_back(i1s1, i2s1, i2s2); - mesh.faces3.emplace_back(i1s1, i2s2, i1s2); - } - - auto i1s1 = coord_t(s1.points.size()) - coord_t(steps); - auto i2s1 = coord_t(s1.points.size()) - 1; - auto i1s2 = coord_t(s1.points.size()); - auto i2s2 = coord_t(s1.points.size()) + coord_t(steps) - 1; - - mesh.faces3.emplace_back(i2s2, i2s1, i1s1); - mesh.faces3.emplace_back(i1s2, i2s2, i1s1); - - return mesh; -} - - -Contour3D pedestal(const Vec3d &endpt, double baseheight, double radius, size_t steps) -{ - if(baseheight <= 0) return {}; - - assert(steps >= 0); - auto last = int(steps - 1); - - Contour3D base; - - double a = 2*PI/steps; - double z = endpt(Z) + baseheight; - - for(size_t i = 0; i < steps; ++i) { - double phi = i*a; - double x = endpt(X) + radius * std::cos(phi); - double y = endpt(Y) + radius * std::sin(phi); - base.points.emplace_back(x, y, z); - } - - for(size_t i = 0; i < steps; ++i) { - double phi = i*a; - double x = endpt(X) + radius*std::cos(phi); - double y = endpt(Y) + radius*std::sin(phi); - base.points.emplace_back(x, y, z - baseheight); - } - - auto ep = endpt; ep(Z) += baseheight; - base.points.emplace_back(endpt); - base.points.emplace_back(ep); - - auto& indices = base.faces3; - auto hcenter = int(base.points.size() - 1); - auto lcenter = int(base.points.size() - 2); - auto offs = int(steps); - for(int i = 0; i < last; ++i) { - indices.emplace_back(i, i + offs, offs + i + 1); - indices.emplace_back(i, offs + i + 1, i + 1); - indices.emplace_back(i, i + 1, hcenter); - indices.emplace_back(lcenter, offs + i + 1, offs + i); - } - - indices.emplace_back(0, last, offs); - indices.emplace_back(last, offs + last, offs); - indices.emplace_back(hcenter, last, 0); - indices.emplace_back(offs, offs + last, lcenter); - - return base; -} - Head::Head(double r_big_mm, double r_small_mm, double length_mm, double penetration, const Vec3d &direction, - const Vec3d &offset, - const size_t circlesteps) - : steps(circlesteps) - , dir(direction) + const Vec3d &offset) + : dir(direction) , pos(offset) , r_back_mm(r_big_mm) , r_pin_mm(r_small_mm) @@ -350,35 +90,6 @@ Head::Head(double r_big_mm, // return *this; //} -Bridge::Bridge(const Vec3d &j1, const Vec3d &j2, double r_mm, size_t steps): - r(r_mm), startp(j1), endp(j2) -{ - using Quaternion = Eigen::Quaternion; - Vec3d dir = (j2 - j1).normalized(); - double d = distance(j2, j1); - - mesh = cylinder(r, d, steps); - - auto quater = Quaternion::FromTwoVectors(Vec3d{0,0,1}, dir); - for(auto& p : mesh.points) p = quater * p + j1; -} - -Bridge::Bridge(const Vec3d &j1, - const Vec3d &j2, - double r1_mm, - double r2_mm, - size_t steps) -{ - Vec3d dir = (j2 - j1); - mesh = pinhead(r1_mm, r2_mm, dir.norm(), steps); - dir.normalize(); - - using Quaternion = Eigen::Quaternion; - auto quater = Quaternion::FromTwoVectors(Vec3d{0,0,1}, dir); - - for(auto& p : mesh.points) p = quater * p + j1; -} - Pad::Pad(const TriangleMesh &support_mesh, const ExPolygons & model_contours, double ground_level, @@ -464,7 +175,7 @@ SupportTreeBuilder &SupportTreeBuilder::operator=(const SupportTreeBuilder &o) return *this; } -const TriangleMesh &SupportTreeBuilder::merged_mesh() const +const TriangleMesh &SupportTreeBuilder::merged_mesh(size_t steps) const { if (m_meshcache_valid) return m_meshcache; @@ -472,28 +183,31 @@ const TriangleMesh &SupportTreeBuilder::merged_mesh() const for (auto &head : m_heads) { if (ctl().stopcondition()) break; - if (head.is_valid()) merged.merge(get_mesh(head)); + if (head.is_valid()) merged.merge(get_mesh(head, steps)); } - for (auto &stick : m_pillars) { + for (auto &pill : m_pillars) { if (ctl().stopcondition()) break; - merged.merge(stick.mesh); - merged.merge(stick.base); + merged.merge(get_mesh(pill, steps)); + } + + for (auto &pedest : m_pedestals) { + merged.merge(get_mesh(pedest, steps)); } for (auto &j : m_junctions) { if (ctl().stopcondition()) break; - merged.merge(j.mesh); + merged.merge(get_mesh(j, steps)); } for (auto &bs : m_bridges) { if (ctl().stopcondition()) break; - merged.merge(bs.mesh); + merged.merge(get_mesh(bs, steps)); } for (auto &bs : m_crossbridges) { if (ctl().stopcondition()) break; - merged.merge(bs.mesh); + merged.merge(get_mesh(bs, steps)); } if (ctl().stopcondition()) { @@ -550,16 +264,4 @@ const TriangleMesh &SupportTreeBuilder::retrieve_mesh(MeshType meshtype) const return m_meshcache; } -template -static Hit min_hit(const C &hits) -{ - auto mit = std::min_element(hits.begin(), hits.end(), - [](const Hit &h1, const Hit &h2) { - return h1.distance() < h2.distance(); - }); - - return *mit; -} - - }} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/SupportTreeBuilder.hpp b/src/libslic3r/SLA/SupportTreeBuilder.hpp index 087173e556..cc039de6fc 100644 --- a/src/libslic3r/SLA/SupportTreeBuilder.hpp +++ b/src/libslic3r/SLA/SupportTreeBuilder.hpp @@ -2,7 +2,6 @@ #define SLA_SUPPORTTREEBUILDER_HPP #include -#include #include #include #include @@ -50,13 +49,6 @@ namespace sla { * nearby pillar. */ -using Coordf = double; -using Portion = std::tuple; - -inline Portion make_portion(double a, double b) { - return std::make_tuple(a, b); -} - template double distance(const Vec& p) { return std::sqrt(p.transpose() * p); } @@ -66,27 +58,13 @@ template double distance(const Vec& pp1, const Vec& pp2) { return distance(p); } -Contour3D sphere(double rho, Portion portion = make_portion(0.0, 2.0*PI), - double fa=(2*PI/360)); - -// Down facing cylinder in Z direction with arguments: -// r: radius -// h: Height -// ssteps: how many edges will create the base circle -// sp: starting point -Contour3D cylinder(double r, double h, size_t steps = 45, const Vec3d &sp = Vec3d::Zero()); - -Contour3D pinhead(double r_pin, double r_back, double length, size_t steps = 45); - -Contour3D pedestal(const Vec3d &pt, double baseheight, double radius, size_t steps = 45); - const constexpr long ID_UNSET = -1; +const Vec3d DOWN = {0.0, 0.0, -1.0}; + +// A pinhead originating from a support point struct Head { - Contour3D mesh; - - size_t steps = 45; - Vec3d dir = {0, 0, -1}; + Vec3d dir = DOWN; Vec3d pos = {0, 0, 0}; double r_back_mm = 1; @@ -110,9 +88,9 @@ struct Head { double r_small_mm, double length_mm, double penetration, - const Vec3d &direction = {0, 0, -1}, // direction (normal to the dull end) - const Vec3d &offset = {0, 0, 0}, // displacement - const size_t circlesteps = 45); + const Vec3d &direction = DOWN, // direction (normal to the dull end) + const Vec3d &offset = {0, 0, 0} // displacement + ); void transform() { @@ -141,29 +119,27 @@ struct Head { } }; +struct Join { + enum Types { + jtPillarBrigde, jtHeadPillar, jtPillarPedestal, jtBridgePedestal, + jtPillarAnchor, jtBridgeAnchor + }; +}; + +// A junction connecting bridges and pillars struct Junction { - Contour3D mesh; double r = 1; - size_t steps = 45; Vec3d pos; long id = ID_UNSET; - - Junction(const Vec3d& tr, double r_mm, size_t stepnum = 45): - r(r_mm), steps(stepnum), pos(tr) - { - mesh = sphere(r_mm, make_portion(0, PI), 2*PI/steps); - for(auto& p : mesh.points) p += tr; - } + + Junction(const Vec3d &tr, double r_mm) : r(r_mm), pos(tr) {} }; + struct Pillar { -// Contour3D mesh; -// Contour3D base; - double r = 1; - size_t steps = 0; + double height, r; Vec3d endpt; - double height = 0; long id = ID_UNSET; @@ -177,60 +153,47 @@ struct Pillar { // How many pillars are cascaded with this one unsigned links = 0; - Pillar(const Vec3d &endp, double h, double radius = 1, size_t st = 45): - height{h}, r(radius), steps(st), endpt(endp), starts_from_head(false) {} + Pillar(const Vec3d &endp, double h, double radius = 1.): + height{h}, r(radius), endpt(endp), starts_from_head(false) {} - -// Pillar(const Junction &junc, const Vec3d &endp) -// : Pillar(junc.pos, endp, junc.r, junc.steps) -// {} - - inline Vec3d startpoint() const + Vec3d startpoint() const { return {endpt.x(), endpt.y(), endpt.z() + height}; } - inline const Vec3d& endpoint() const { return endpt; } + const Vec3d& endpoint() const { return endpt; } // Pillar& add_base(double baseheight = 3, double radius = 2); }; +// A base for pillars or bridges that end on the ground struct Pedestal { Vec3d pos; double height, radius; - size_t steps = 45; + long id = ID_UNSET; - Pedestal() = default; - Pedestal(const Vec3d &p, double h = 3., double r = 2., size_t stps = 45) - : pos{p}, height{h}, radius{r}, steps{stps} - {} - - Pedestal(const Pillar &p, double h = 3., double r = 2.) - : Pedestal{p.endpt, std::min(h, p.height), std::max(r, p.r), p.steps} + Pedestal(const Vec3d &p, double h = 3., double r = 2.) + : pos{p}, height{h}, radius{r} {} }; -struct PinJoin { - -}; +// This is the thing that anchors a pillar or bridge to the model body. +// It is actually a reverse pinhead. +struct Anchor: public Head { using Head::Head; }; // A Bridge between two pillars (with junction endpoints) struct Bridge { - Contour3D mesh; double r = 0.8; long id = ID_UNSET; Vec3d startp = Vec3d::Zero(), endp = Vec3d::Zero(); Bridge(const Vec3d &j1, const Vec3d &j2, - double r_mm = 0.8, - size_t steps = 45); + double r_mm = 0.8): r{r_mm}, startp{j1}, endp{j2} + {} - Bridge(const Vec3d &j1, - const Vec3d &j2, - double r1_mm, - double r2_mm, - size_t steps = 45); + double get_length() const { return (endp - startp).norm(); } + Vec3d get_dir() const { return (endp - startp).normalized(); } }; // A wrapper struct around the pad @@ -250,40 +213,6 @@ struct Pad { bool empty() const { return tmesh.facets_count() == 0; } }; -inline Contour3D get_mesh(const Head &h) -{ - Contour3D mesh = pinhead(h.r_pin_mm, h.r_back_mm, h.width_mm, h.steps); - - using Quaternion = Eigen::Quaternion; - - // We rotate the head to the specified direction The head's pointing - // side is facing upwards so this means that it would hold a support - // point with a normal pointing straight down. This is the reason of - // the -1 z coordinate - auto quatern = Quaternion::FromTwoVectors(Vec3d{0, 0, -1}, h.dir); - - for(auto& p : mesh.points) p = quatern * p + h.pos; -} - -inline Contour3D get_mesh(const Pillar &p) -{ - assert(p.steps > 0); - - if(p.height > EPSILON) { // Endpoint is below the starting point - // We just create a bridge geometry with the pillar parameters and - // move the data. - return cylinder(p.r, p.height, p.steps, p.endpoint()); - } - - return {}; -} - -inline Contour3D get_mesh(const Pedestal &p, double h, double r) -{ - return pedestal(p.pos, p.height, p.radius, p.steps); -} - - // This class will hold the support tree meshes with some additional // bookkeeping as well. Various parts of the support geometry are stored // separately and are merged when the caller queries the merged mesh. The @@ -300,12 +229,15 @@ inline Contour3D get_mesh(const Pedestal &p, double h, double r) // merged mesh. It can be retrieved using a dedicated method (pad()) class SupportTreeBuilder: public SupportTree { // For heads it is beneficial to use the same IDs as for the support points. - std::vector m_heads; - std::vector m_head_indices; - std::vector m_pillars; + std::vector m_heads; + std::vector m_head_indices; + std::vector m_pillars; std::vector m_junctions; - std::vector m_bridges; - std::vector m_crossbridges; + std::vector m_bridges; + std::vector m_crossbridges; + std::vector m_pedestals; + std::vector m_anchors; + Pad m_pad; using Mutex = ccr::SpinningMutex; @@ -347,7 +279,7 @@ public: return m_heads.back(); } - template long add_pillar(long headid, Args&&... args) + template long add_pillar(long headid, double length) { std::lock_guard lk(m_mutex); if (m_pillars.capacity() < m_heads.size()) @@ -356,7 +288,9 @@ public: assert(headid >= 0 && size_t(headid) < m_head_indices.size()); Head &head = m_heads[m_head_indices[size_t(headid)]]; - m_pillars.emplace_back(head, std::forward(args)...); + Vec3d hjp = head.junction_point() - Vec3d{0, 0, length}; + m_pillars.emplace_back(hjp, length, head.r_back_mm); + Pillar& pillar = m_pillars.back(); pillar.id = long(m_pillars.size() - 1); head.pillar_id = pillar.id; @@ -371,7 +305,19 @@ public: { std::lock_guard lk(m_mutex); assert(pid >= 0 && size_t(pid) < m_pillars.size()); - m_pillars[size_t(pid)].add_base(baseheight, radius); + m_pedestals.emplace_back(m_pillars[size_t(pid)].endpt, baseheight, radius); + m_pedestals.back().id = m_pedestals.size() - 1; + m_meshcache_valid = false; +// m_pillars[size_t(pid)].add_base(baseheight, radius); + } + + template const Anchor& add_anchor(Args&&...args) + { + std::lock_guard lk(m_mutex); + m_anchors.emplace_back(std::forward(args)...); + m_anchors.back().id = long(m_junctions.size() - 1); + m_meshcache_valid = false; + return m_anchors.back(); } void increment_bridges(const Pillar& pillar) @@ -432,18 +378,18 @@ public: return m_junctions.back(); } - const Bridge& add_bridge(const Vec3d &s, const Vec3d &e, double r, size_t n = 45) + const Bridge& add_bridge(const Vec3d &s, const Vec3d &e, double r) { - return _add_bridge(m_bridges, s, e, r, n); + return _add_bridge(m_bridges, s, e, r); } - const Bridge& add_bridge(long headid, const Vec3d &endp, size_t s = 45) + const Bridge& add_bridge(long headid, const Vec3d &endp) { std::lock_guard lk(m_mutex); assert(headid >= 0 && size_t(headid) < m_head_indices.size()); Head &h = m_heads[m_head_indices[size_t(headid)]]; - m_bridges.emplace_back(h.junction_point(), endp, h.r_back_mm, s); + m_bridges.emplace_back(h.junction_point(), endp, h.r_back_mm); m_bridges.back().id = long(m_bridges.size() - 1); h.bridge_id = m_bridges.back().id; @@ -471,7 +417,7 @@ public: } inline const std::vector &pillars() const { return m_pillars; } - inline const std::vector &heads() const { return m_heads; } + inline const std::vector &heads() const { return m_heads; } inline const std::vector &bridges() const { return m_bridges; } inline const std::vector &crossbridges() const { return m_crossbridges; } @@ -496,7 +442,7 @@ public: const Pad& pad() const { return m_pad; } // WITHOUT THE PAD!!! - const TriangleMesh &merged_mesh() const; + const TriangleMesh &merged_mesh(size_t steps = 45) const; // WITH THE PAD double full_height() const; diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp index a8e79dc179..4b8366ee44 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -7,14 +8,23 @@ namespace Slic3r { namespace sla { -static const Vec3d DOWN = {0.0, 0.0, -1.0}; - using libnest2d::opt::initvals; using libnest2d::opt::bound; using libnest2d::opt::StopCriteria; using libnest2d::opt::GeneticOptimizer; using libnest2d::opt::SubplexOptimizer; +template +static Hit min_hit(const C &hits) +{ + auto mit = std::min_element(hits.begin(), hits.end(), + [](const Hit &h1, const Hit &h2) { + return h1.distance() < h2.distance(); + }); + + return *mit; +} + EigenMesh3D::hit_result query_hit(const SupportableMesh &msh, const Head &h) { static const size_t SAMPLES = 8; @@ -158,7 +168,7 @@ bool SupportTreeBuildsteps::execute(SupportTreeBuilder & builder, builder.ground_level = sm.emesh.ground_level() - sm.cfg.object_elevation_mm; SupportTreeBuildsteps alg(builder, sm); - + // Let's define the individual steps of the processing. We can experiment // later with the ordering and the dependencies between them. enum Steps { @@ -271,17 +281,6 @@ bool SupportTreeBuildsteps::execute(SupportTreeBuilder & builder, return pc == ABORT; } -template -static Hit min_hit(const C &hits) -{ - auto mit = std::min_element(hits.begin(), hits.end(), - [](const Hit &h1, const Hit &h2) { - return h1.distance() < h2.distance(); - }); - - return *mit; -} - EigenMesh3D::hit_result SupportTreeBuildsteps::pinhead_mesh_intersect( const Vec3d &s, const Vec3d &dir, double r_pin, double r_back, double width) { @@ -552,7 +551,7 @@ bool SupportTreeBuildsteps::connect_to_nearpillar(const Head &head, if (m_builder.bridgecount(nearpillar()) < m_cfg.max_bridges_on_pillar) { // A partial pillar is needed under the starting head. if(zdiff > 0) { - m_builder.add_pillar(head.id, bridgestart, r); + m_builder.add_pillar(head.id, headjp.z() - bridgestart.z()); m_builder.add_junction(bridgestart, r); m_builder.add_bridge(bridgestart, bridgeend, r); } else { @@ -607,7 +606,7 @@ bool SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, normal_mode = false; if (t > m_cfg.max_bridge_length_mm || endp(Z) < gndlvl) { - if (head_id >= 0) m_builder.add_pillar(head_id, jp, radius); + if (head_id >= 0) m_builder.add_pillar(head_id, 0.); return false; } } @@ -615,14 +614,15 @@ bool SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, // Check if the deduced route is sane and exit with error if not. if (bridge_mesh_distance(jp, dir, radius) < (endp - jp).norm()) { - if (head_id >= 0) m_builder.add_pillar(head_id, jp, radius); + if (head_id >= 0) m_builder.add_pillar(head_id, 0.); return false; } // Straigh path down, no area to dodge if (normal_mode) { - pillar_id = head_id >= 0 ? m_builder.add_pillar(head_id, endp, radius) : - m_builder.add_pillar(jp, endp, radius); + double h = jp.z() - endp.z(); + pillar_id = head_id >= 0 ? m_builder.add_pillar(head_id, h) : + m_builder.add_pillar(jp, h, radius); if (can_add_base) m_builder.add_pillar_base(pillar_id, m_cfg.base_height_mm, @@ -630,8 +630,9 @@ bool SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, } else { // Insert the bridge to get around the forbidden area - Vec3d pgnd{endp.x(), endp.y(), gndlvl}; - pillar_id = m_builder.add_pillar(endp, pgnd, radius); +// Vec3d pgnd{endp.x(), endp.y(), gndlvl}; + double h = endp.z() - gndlvl; + pillar_id = m_builder.add_pillar(endp, h, radius); if (can_add_base) m_builder.add_pillar_base(pillar_id, m_cfg.base_height_mm, @@ -645,7 +646,7 @@ bool SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, // prevent from queries of head_pillar() to have non-existing // pillar when the head should have one. if (head_id >= 0) - m_builder.add_pillar(head_id, jp, radius); + m_builder.add_pillar(head_id, 0.); } if(pillar_id >= 0) // Save the pillar endpoint in the spatial index @@ -1034,7 +1035,7 @@ bool SupportTreeBuildsteps::connect_to_model_body(Head &head) head.transform(); - long pillar_id = m_builder.add_pillar(head.id, endp, head.r_back_mm); + long pillar_id = m_builder.add_pillar(head.id, hit.distance() + h); Pillar &pill = m_builder.pillar(pillar_id); Vec3d taildir = endp - hitp; @@ -1046,11 +1047,14 @@ bool SupportTreeBuildsteps::connect_to_model_body(Head &head) w = 0.; } - Head tailhead(head.r_back_mm, head.r_pin_mm, w, - m_cfg.head_penetration_mm, taildir, hitp); + m_builder.add_anchor(head.r_back_mm, head.r_pin_mm, w, + m_cfg.head_penetration_mm, taildir, hitp); - tailhead.transform(); - pill.base = tailhead.mesh; +// Head tailhead(head.r_back_mm, head.r_pin_mm, w, +// m_cfg.head_penetration_mm, taildir, hitp); + +// tailhead.transform(); +// pill.base = tailhead.mesh; m_pillar_index.guarded_insert(pill.endpoint(), pill.id); @@ -1297,8 +1301,8 @@ void SupportTreeBuildsteps::interconnect_pillars() if (found) for (unsigned n = 0; n < needpillars; n++) { Vec3d s = spts[n]; - Pillar p(s, Vec3d(s(X), s(Y), gnd), pillar().r); - p.add_base(m_cfg.base_height_mm, m_cfg.base_radius_mm); + Pillar p(s, s.z() - gnd, pillar().r); +// p.add_base(m_cfg.base_height_mm, m_cfg.base_radius_mm); if (interconnect(pillar(), p)) { Pillar &pp = m_builder.pillar(m_builder.add_pillar(p)); diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp index bfa38505b0..fc5670b163 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp @@ -5,6 +5,7 @@ #include #include +#include namespace Slic3r { namespace sla { @@ -108,55 +109,6 @@ public: EigenMesh3D::hit_result query_hit(const SupportableMesh &msh, const Bridge &br, double safety_d = std::nan("")); EigenMesh3D::hit_result query_hit(const SupportableMesh &msh, const Head &br, double safety_d = std::nan("")); -// This function returns the position of the centroid in the input 'clust' -// vector of point indices. -template -long cluster_centroid(const ClusterEl& clust, - const std::function &pointfn, - DistFn df) -{ - switch(clust.size()) { - case 0: /* empty cluster */ return ID_UNSET; - case 1: /* only one element */ return 0; - case 2: /* if two elements, there is no center */ return 0; - default: ; - } - - // The function works by calculating for each point the average distance - // from all the other points in the cluster. We create a selector bitmask of - // the same size as the cluster. The bitmask will have two true bits and - // false bits for the rest of items and we will loop through all the - // permutations of the bitmask (combinations of two points). Get the - // distance for the two points and add the distance to the averages. - // The point with the smallest average than wins. - - // The complexity should be O(n^2) but we will mostly apply this function - // for small clusters only (cca 3 elements) - - std::vector sel(clust.size(), false); // create full zero bitmask - std::fill(sel.end() - 2, sel.end(), true); // insert the two ones - std::vector avgs(clust.size(), 0.0); // store the average distances - - do { - std::array idx; - for(size_t i = 0, j = 0; i < clust.size(); i++) if(sel[i]) idx[j++] = i; - - double d = df(pointfn(clust[idx[0]]), - pointfn(clust[idx[1]])); - - // add the distance to the sums for both associated points - for(auto i : idx) avgs[i] += d; - - // now continue with the next permutation of the bitmask with two 1s - } while(std::next_permutation(sel.begin(), sel.end())); - - // Divide by point size in the cluster to get the average (may be redundant) - for(auto& a : avgs) a /= clust.size(); - - // get the lowest average distance and return the index - auto minit = std::min_element(avgs.begin(), avgs.end()); - return long(minit - avgs.begin()); -} inline Vec3d dirv(const Vec3d& startp, const Vec3d& endp) { return (endp - startp).normalized(); diff --git a/src/libslic3r/SLA/SupportTreeMesher.cpp b/src/libslic3r/SLA/SupportTreeMesher.cpp new file mode 100644 index 0000000000..1d9be6c348 --- /dev/null +++ b/src/libslic3r/SLA/SupportTreeMesher.cpp @@ -0,0 +1,268 @@ +#include "SupportTreeMesher.hpp" + +namespace Slic3r { namespace sla { + +Contour3D sphere(double rho, Portion portion, double fa) { + + Contour3D ret; + + // prohibit close to zero radius + if(rho <= 1e-6 && rho >= -1e-6) return ret; + + auto& vertices = ret.points; + auto& facets = ret.faces3; + + // Algorithm: + // Add points one-by-one to the sphere grid and form facets using relative + // coordinates. Sphere is composed effectively of a mesh of stacked circles. + + // adjust via rounding to get an even multiple for any provided angle. + double angle = (2*PI / floor(2*PI / fa)); + + // Ring to be scaled to generate the steps of the sphere + std::vector ring; + + for (double i = 0; i < 2*PI; i+=angle) ring.emplace_back(i); + + const auto sbegin = size_t(2*std::get<0>(portion)/angle); + const auto send = size_t(2*std::get<1>(portion)/angle); + + const size_t steps = ring.size(); + const double increment = 1.0 / double(steps); + + // special case: first ring connects to 0,0,0 + // insert and form facets. + if(sbegin == 0) + vertices.emplace_back(Vec3d(0.0, 0.0, -rho + increment*sbegin*2.0*rho)); + + auto id = coord_t(vertices.size()); + for (size_t i = 0; i < ring.size(); i++) { + // Fixed scaling + const double z = -rho + increment*rho*2.0 * (sbegin + 1.0); + // radius of the circle for this step. + const double r = std::sqrt(std::abs(rho*rho - z*z)); + Vec2d b = Eigen::Rotation2Dd(ring[i]) * Eigen::Vector2d(0, r); + vertices.emplace_back(Vec3d(b(0), b(1), z)); + + if (sbegin == 0) + (i == 0) ? facets.emplace_back(coord_t(ring.size()), 0, 1) : + facets.emplace_back(id - 1, 0, id); + ++id; + } + + // General case: insert and form facets for each step, + // joining it to the ring below it. + for (size_t s = sbegin + 2; s < send - 1; s++) { + const double z = -rho + increment*double(s*2.0*rho); + const double r = std::sqrt(std::abs(rho*rho - z*z)); + + for (size_t i = 0; i < ring.size(); i++) { + Vec2d b = Eigen::Rotation2Dd(ring[i]) * Eigen::Vector2d(0, r); + vertices.emplace_back(Vec3d(b(0), b(1), z)); + auto id_ringsize = coord_t(id - int(ring.size())); + if (i == 0) { + // wrap around + facets.emplace_back(id - 1, id, id + coord_t(ring.size() - 1) ); + facets.emplace_back(id - 1, id_ringsize, id); + } else { + facets.emplace_back(id_ringsize - 1, id_ringsize, id); + facets.emplace_back(id - 1, id_ringsize - 1, id); + } + id++; + } + } + + // special case: last ring connects to 0,0,rho*2.0 + // only form facets. + if(send >= size_t(2*PI / angle)) { + vertices.emplace_back(Vec3d(0.0, 0.0, -rho + increment*send*2.0*rho)); + for (size_t i = 0; i < ring.size(); i++) { + auto id_ringsize = coord_t(id - int(ring.size())); + if (i == 0) { + // third vertex is on the other side of the ring. + facets.emplace_back(id - 1, id_ringsize, id); + } else { + auto ci = coord_t(id_ringsize + coord_t(i)); + facets.emplace_back(ci - 1, ci, id); + } + } + } + id++; + + return ret; +} + +Contour3D cylinder(double r, double h, size_t ssteps, const Vec3d &sp) +{ + assert(steps > 0); + + Contour3D ret; + + auto steps = int(ssteps); + auto& points = ret.points; + auto& indices = ret.faces3; + points.reserve(2*ssteps); + double a = 2*PI/steps; + + Vec3d jp = sp; + Vec3d endp = {sp(X), sp(Y), sp(Z) + h}; + + // Upper circle points + for(int i = 0; i < steps; ++i) { + double phi = i*a; + double ex = endp(X) + r*std::cos(phi); + double ey = endp(Y) + r*std::sin(phi); + points.emplace_back(ex, ey, endp(Z)); + } + + // Lower circle points + for(int i = 0; i < steps; ++i) { + double phi = i*a; + double x = jp(X) + r*std::cos(phi); + double y = jp(Y) + r*std::sin(phi); + points.emplace_back(x, y, jp(Z)); + } + + // Now create long triangles connecting upper and lower circles + indices.reserve(2*ssteps); + auto offs = steps; + for(int i = 0; i < steps - 1; ++i) { + indices.emplace_back(i, i + offs, offs + i + 1); + indices.emplace_back(i, offs + i + 1, i + 1); + } + + // Last triangle connecting the first and last vertices + auto last = steps - 1; + indices.emplace_back(0, last, offs); + indices.emplace_back(last, offs + last, offs); + + // According to the slicing algorithms, we need to aid them with generating + // a watertight body. So we create a triangle fan for the upper and lower + // ending of the cylinder to close the geometry. + points.emplace_back(jp); int ci = int(points.size() - 1); + for(int i = 0; i < steps - 1; ++i) + indices.emplace_back(i + offs + 1, i + offs, ci); + + indices.emplace_back(offs, steps + offs - 1, ci); + + points.emplace_back(endp); ci = int(points.size() - 1); + for(int i = 0; i < steps - 1; ++i) + indices.emplace_back(ci, i, i + 1); + + indices.emplace_back(steps - 1, 0, ci); + + return ret; +} + +Contour3D pinhead(double r_pin, double r_back, double length, size_t steps) +{ + assert(steps > 0); + assert(length > 0.); + assert(r_back > 0.); + assert(r_pin > 0.); + + Contour3D mesh; + + // We create two spheres which will be connected with a robe that fits + // both circles perfectly. + + // Set up the model detail level + const double detail = 2*PI/steps; + + // We don't generate whole circles. Instead, we generate only the + // portions which are visible (not covered by the robe) To know the + // exact portion of the bottom and top circles we need to use some + // rules of tangent circles from which we can derive (using simple + // triangles the following relations: + + // The height of the whole mesh + const double h = r_back + r_pin + length; + double phi = PI / 2. - std::acos((r_back - r_pin) / h); + + // To generate a whole circle we would pass a portion of (0, Pi) + // To generate only a half horizontal circle we can pass (0, Pi/2) + // The calculated phi is an offset to the half circles needed to smooth + // the transition from the circle to the robe geometry + + auto&& s1 = sphere(r_back, make_portion(0, PI/2 + phi), detail); + auto&& s2 = sphere(r_pin, make_portion(PI/2 + phi, PI), detail); + + for(auto& p : s2.points) p.z() += h; + + mesh.merge(s1); + mesh.merge(s2); + + for(size_t idx1 = s1.points.size() - steps, idx2 = s1.points.size(); + idx1 < s1.points.size() - 1; + idx1++, idx2++) + { + coord_t i1s1 = coord_t(idx1), i1s2 = coord_t(idx2); + coord_t i2s1 = i1s1 + 1, i2s2 = i1s2 + 1; + + mesh.faces3.emplace_back(i1s1, i2s1, i2s2); + mesh.faces3.emplace_back(i1s1, i2s2, i1s2); + } + + auto i1s1 = coord_t(s1.points.size()) - coord_t(steps); + auto i2s1 = coord_t(s1.points.size()) - 1; + auto i1s2 = coord_t(s1.points.size()); + auto i2s2 = coord_t(s1.points.size()) + coord_t(steps) - 1; + + mesh.faces3.emplace_back(i2s2, i2s1, i1s1); + mesh.faces3.emplace_back(i1s2, i2s2, i1s1); + + return mesh; +} + +Contour3D pedestal(const Vec3d &endpt, double baseheight, double radius, size_t steps) +{ + assert(steps > 0); + + if(baseheight <= 0) return {}; + + assert(steps >= 0); + auto last = int(steps - 1); + + Contour3D base; + + double a = 2*PI/steps; + double z = endpt(Z) + baseheight; + + for(size_t i = 0; i < steps; ++i) { + double phi = i*a; + double x = endpt(X) + radius * std::cos(phi); + double y = endpt(Y) + radius * std::sin(phi); + base.points.emplace_back(x, y, z); + } + + for(size_t i = 0; i < steps; ++i) { + double phi = i*a; + double x = endpt(X) + radius*std::cos(phi); + double y = endpt(Y) + radius*std::sin(phi); + base.points.emplace_back(x, y, z - baseheight); + } + + auto ep = endpt; ep(Z) += baseheight; + base.points.emplace_back(endpt); + base.points.emplace_back(ep); + + auto& indices = base.faces3; + auto hcenter = int(base.points.size() - 1); + auto lcenter = int(base.points.size() - 2); + auto offs = int(steps); + for(int i = 0; i < last; ++i) { + indices.emplace_back(i, i + offs, offs + i + 1); + indices.emplace_back(i, offs + i + 1, i + 1); + indices.emplace_back(i, i + 1, hcenter); + indices.emplace_back(lcenter, offs + i + 1, offs + i); + } + + indices.emplace_back(0, last, offs); + indices.emplace_back(last, offs + last, offs); + indices.emplace_back(hcenter, last, 0); + indices.emplace_back(offs, offs + last, lcenter); + + return base; +} + +}} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/SupportTreeMesher.hpp b/src/libslic3r/SLA/SupportTreeMesher.hpp new file mode 100644 index 0000000000..677cab3b81 --- /dev/null +++ b/src/libslic3r/SLA/SupportTreeMesher.hpp @@ -0,0 +1,94 @@ +#ifndef SUPPORTTREEMESHER_HPP +#define SUPPORTTREEMESHER_HPP + +#include "libslic3r/Point.hpp" + +#include "libslic3r/SLA/SupportTreeBuilder.hpp" +#include "libslic3r/SLA/Contour3D.hpp" + +namespace Slic3r { namespace sla { + +using Portion = std::tuple; + +inline Portion make_portion(double a, double b) +{ + return std::make_tuple(a, b); +} + +Contour3D sphere(double rho, + Portion portion = make_portion(0., 2. * PI), + double fa = (2. * PI / 360.)); + +// Down facing cylinder in Z direction with arguments: +// r: radius +// h: Height +// ssteps: how many edges will create the base circle +// sp: starting point +Contour3D cylinder(double r, double h, size_t steps = 45, const Vec3d &sp = Vec3d::Zero()); + +Contour3D pinhead(double r_pin, double r_back, double length, size_t steps = 45); + +Contour3D pedestal(const Vec3d &pt, double baseheight, double radius, size_t steps = 45); + +inline Contour3D get_mesh(const Head &h, size_t steps) +{ + Contour3D mesh = pinhead(h.r_pin_mm, h.r_back_mm, h.width_mm, steps); + + // To simplify further processing, we translate the mesh so that the + // last vertex of the pointing sphere (the pinpoint) will be at (0,0,0) + for(auto& p : mesh.points) p.z() -= (h.fullwidth() - h.r_back_mm); + + using Quaternion = Eigen::Quaternion; + + // We rotate the head to the specified direction The head's pointing + // side is facing upwards so this means that it would hold a support + // point with a normal pointing straight down. This is the reason of + // the -1 z coordinate + auto quatern = Quaternion::FromTwoVectors(Vec3d{0, 0, -1}, h.dir); + + for(auto& p : mesh.points) p = quatern * p + h.pos; + + return mesh; +} + +inline Contour3D get_mesh(const Pillar &p, size_t steps) +{ + if(p.height > EPSILON) { // Endpoint is below the starting point + // We just create a bridge geometry with the pillar parameters and + // move the data. + return cylinder(p.r, p.height, steps, p.endpoint()); + } + + return {}; +} + +inline Contour3D get_mesh(const Pedestal &p, size_t steps) +{ + return pedestal(p.pos, p.height, p.radius, steps); +} + +inline Contour3D get_mesh(const Junction &j, size_t steps) +{ + Contour3D mesh = sphere(j.r, make_portion(0, PI), 2 *PI / steps); + for(auto& p : mesh.points) p += j.pos; + return mesh; +} + +inline Contour3D get_mesh(const Bridge &br, size_t steps) +{ + using Quaternion = Eigen::Quaternion; + Vec3d v = (br.endp - br.startp); + Vec3d dir = v.normalized(); + double d = v.norm(); + + Contour3D mesh = cylinder(br.r, d, steps); + + auto quater = Quaternion::FromTwoVectors(Vec3d{0,0,1}, dir); + for(auto& p : mesh.points) p = quater * p + br.startp; + + return mesh; +} + +}} + +#endif // SUPPORTTREEMESHER_HPP diff --git a/tests/sla_print/sla_treebuilder_tests.cpp b/tests/sla_print/sla_treebuilder_tests.cpp index c785e4ba5e..05aca963ea 100644 --- a/tests/sla_print/sla_treebuilder_tests.cpp +++ b/tests/sla_print/sla_treebuilder_tests.cpp @@ -2,7 +2,8 @@ #include #include "libslic3r/TriangleMesh.hpp" -#include "libslic3r/SLA/SupportTreeBuilder.hpp" +#include "libslic3r/SLA/SupportTreeBuildsteps.hpp" +#include "libslic3r/SLA/SupportTreeMesher.hpp" TEST_CASE("Test bridge_mesh_intersect on a cube's wall", "[SLABridgeMeshInters]") { using namespace Slic3r; @@ -13,6 +14,7 @@ TEST_CASE("Test bridge_mesh_intersect on a cube's wall", "[SLABridgeMeshInters]" sla::SupportPoints pts = {{10.f, 5.f, 5.f, float(cfg.head_front_radius_mm), false}}; sla::SupportableMesh sm{cube, pts, cfg}; + size_t steps = 45; SECTION("Bridge is straight horizontal and pointing away from the cube") { sla::Bridge bridge(pts[0].pos.cast(), Vec3d{15., 5., 5.}, @@ -22,7 +24,7 @@ TEST_CASE("Test bridge_mesh_intersect on a cube's wall", "[SLABridgeMeshInters]" REQUIRE(std::isinf(hit.distance())); - cube.merge(sla::to_triangle_mesh(bridge.mesh)); + cube.merge(sla::to_triangle_mesh(get_mesh(bridge, steps))); cube.require_shared_vertices(); cube.WriteOBJFile("cube1.obj"); } @@ -35,7 +37,7 @@ TEST_CASE("Test bridge_mesh_intersect on a cube's wall", "[SLABridgeMeshInters]" REQUIRE(std::isinf(hit.distance())); - cube.merge(sla::to_triangle_mesh(bridge.mesh)); + cube.merge(sla::to_triangle_mesh(get_mesh(bridge, steps))); cube.require_shared_vertices(); cube.WriteOBJFile("cube2.obj"); } @@ -52,6 +54,7 @@ TEST_CASE("Test bridge_mesh_intersect on a sphere", "[SLABridgeMeshInters]") { sla::SupportPoints pts = {{1.f, 0.f, 0.f, float(cfg.head_front_radius_mm), false}}; sla::SupportableMesh sm{sphere, pts, cfg}; + size_t steps = 45; SECTION("Bridge is straight horizontal and pointing away from the sphere") { sla::Bridge bridge(pts[0].pos.cast(), Vec3d{2., 0., 0.}, @@ -59,7 +62,7 @@ TEST_CASE("Test bridge_mesh_intersect on a sphere", "[SLABridgeMeshInters]") { auto hit = sla::query_hit(sm, bridge); - sphere.merge(sla::to_triangle_mesh(bridge.mesh)); + sphere.merge(sla::to_triangle_mesh(get_mesh(bridge, steps))); sphere.require_shared_vertices(); sphere.WriteOBJFile("sphere1.obj"); @@ -73,7 +76,7 @@ TEST_CASE("Test bridge_mesh_intersect on a sphere", "[SLABridgeMeshInters]") { auto hit = sla::query_hit(sm, bridge); - sphere.merge(sla::to_triangle_mesh(bridge.mesh)); + sphere.merge(sla::to_triangle_mesh(get_mesh(bridge, steps))); sphere.require_shared_vertices(); sphere.WriteOBJFile("sphere2.obj"); @@ -87,7 +90,7 @@ TEST_CASE("Test bridge_mesh_intersect on a sphere", "[SLABridgeMeshInters]") { auto hit = sla::query_hit(sm, bridge); - sphere.merge(sla::to_triangle_mesh(bridge.mesh)); + sphere.merge(sla::to_triangle_mesh(get_mesh(bridge, steps))); sphere.require_shared_vertices(); sphere.WriteOBJFile("sphere3.obj"); From 301a168b8998d6ffd8389af9729357cd771fc9e4 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 18 Jun 2020 13:49:35 +0200 Subject: [PATCH 241/503] Fix bugs and non working tests Fix failing tests Try to fix build on windows Try to fix failng tests on Mac --- src/libslic3r/SLA/SupportTreeBuilder.cpp | 91 +++++---------------- src/libslic3r/SLA/SupportTreeBuilder.hpp | 33 ++------ src/libslic3r/SLA/SupportTreeBuildsteps.cpp | 81 +++++++++++------- src/libslic3r/SLA/SupportTreeBuildsteps.hpp | 13 +-- src/libslic3r/SLA/SupportTreeMesher.cpp | 72 ++++++++-------- src/libslic3r/SLA/SupportTreeMesher.hpp | 19 +++-- tests/sla_print/sla_test_utils.cpp | 4 +- 7 files changed, 132 insertions(+), 181 deletions(-) diff --git a/src/libslic3r/SLA/SupportTreeBuilder.cpp b/src/libslic3r/SLA/SupportTreeBuilder.cpp index d4a9d00c99..9590936231 100644 --- a/src/libslic3r/SLA/SupportTreeBuilder.cpp +++ b/src/libslic3r/SLA/SupportTreeBuilder.cpp @@ -1,3 +1,5 @@ +#define NOMINMAX + #include #include #include @@ -19,77 +21,8 @@ Head::Head(double r_big_mm, , width_mm(length_mm) , penetration_mm(penetration) { -// mesh = pinhead(r_pin_mm, r_back_mm, width_mm, steps); - - // To simplify further processing, we translate the mesh so that the - // last vertex of the pointing sphere (the pinpoint) will be at (0,0,0) -// for(auto& p : mesh.points) p.z() -= (fullwidth() - r_back_mm); } -//Pillar::Pillar(const Vec3d &endp, double h, double radius, size_t st): -// height{h}, r(radius), steps(st), endpt(endp), starts_from_head(false) -//{ -// assert(steps > 0); - -// if(height > EPSILON) { // Endpoint is below the starting point - -// // We just create a bridge geometry with the pillar parameters and -// // move the data. -// Contour3D body = cylinder(radius, height, st, endp); -// mesh.points.swap(body.points); -// mesh.faces3.swap(body.faces3); -// } -//} - -//Pillar &Pillar::add_base(double baseheight, double radius) -//{ -// if(baseheight <= 0) return *this; -// if(baseheight > height) baseheight = height; - -// assert(steps >= 0); -// auto last = int(steps - 1); - -// if(radius < r ) radius = r; - -// double a = 2*PI/steps; -// double z = endpt(Z) + baseheight; - -// for(size_t i = 0; i < steps; ++i) { -// double phi = i*a; -// double x = endpt(X) + r*std::cos(phi); -// double y = endpt(Y) + r*std::sin(phi); -// base.points.emplace_back(x, y, z); -// } - -// for(size_t i = 0; i < steps; ++i) { -// double phi = i*a; -// double x = endpt(X) + radius*std::cos(phi); -// double y = endpt(Y) + radius*std::sin(phi); -// base.points.emplace_back(x, y, z - baseheight); -// } - -// auto ep = endpt; ep(Z) += baseheight; -// base.points.emplace_back(endpt); -// base.points.emplace_back(ep); - -// auto& indices = base.faces3; -// auto hcenter = int(base.points.size() - 1); -// auto lcenter = int(base.points.size() - 2); -// auto offs = int(steps); -// for(int i = 0; i < last; ++i) { -// indices.emplace_back(i, i + offs, offs + i + 1); -// indices.emplace_back(i, offs + i + 1, i + 1); -// indices.emplace_back(i, i + 1, hcenter); -// indices.emplace_back(lcenter, offs + i + 1, offs + i); -// } - -// indices.emplace_back(0, last, offs); -// indices.emplace_back(last, offs + last, offs); -// indices.emplace_back(hcenter, last, 0); -// indices.emplace_back(offs, offs + last, lcenter); -// return *this; -//} - Pad::Pad(const TriangleMesh &support_mesh, const ExPolygons & model_contours, double ground_level, @@ -175,6 +108,18 @@ SupportTreeBuilder &SupportTreeBuilder::operator=(const SupportTreeBuilder &o) return *this; } +void SupportTreeBuilder::add_pillar_base(long pid, double baseheight, double radius) +{ + std::lock_guard lk(m_mutex); + assert(pid >= 0 && size_t(pid) < m_pillars.size()); + Pillar& pll = m_pillars[size_t(pid)]; + m_pedestals.emplace_back(pll.endpt, std::min(baseheight, pll.height), + std::max(radius, pll.r), pll.r); + + m_pedestals.back().id = m_pedestals.size() - 1; + m_meshcache_valid = false; +} + const TriangleMesh &SupportTreeBuilder::merged_mesh(size_t steps) const { if (m_meshcache_valid) return m_meshcache; @@ -192,6 +137,7 @@ const TriangleMesh &SupportTreeBuilder::merged_mesh(size_t steps) const } for (auto &pedest : m_pedestals) { + if (ctl().stopcondition()) break; merged.merge(get_mesh(pedest, steps)); } @@ -209,7 +155,12 @@ const TriangleMesh &SupportTreeBuilder::merged_mesh(size_t steps) const if (ctl().stopcondition()) break; merged.merge(get_mesh(bs, steps)); } - + + for (auto &anch : m_anchors) { + if (ctl().stopcondition()) break; + merged.merge(get_mesh(anch, steps)); + } + if (ctl().stopcondition()) { // In case of failure we have to return an empty mesh m_meshcache = TriangleMesh(); diff --git a/src/libslic3r/SLA/SupportTreeBuilder.hpp b/src/libslic3r/SLA/SupportTreeBuilder.hpp index cc039de6fc..2b3ff91a06 100644 --- a/src/libslic3r/SLA/SupportTreeBuilder.hpp +++ b/src/libslic3r/SLA/SupportTreeBuilder.hpp @@ -91,12 +91,7 @@ struct Head { const Vec3d &direction = DOWN, // direction (normal to the dull end) const Vec3d &offset = {0, 0, 0} // displacement ); - - void transform() - { - // TODO: remove occurences - } - + inline double real_width() const { return 2 * r_pin_mm + width_mm + 2 * r_back_mm ; @@ -119,13 +114,6 @@ struct Head { } }; -struct Join { - enum Types { - jtPillarBrigde, jtHeadPillar, jtPillarPedestal, jtBridgePedestal, - jtPillarAnchor, jtBridgeAnchor - }; -}; - // A junction connecting bridges and pillars struct Junction { double r = 1; @@ -136,7 +124,6 @@ struct Junction { Junction(const Vec3d &tr, double r_mm) : r(r_mm), pos(tr) {} }; - struct Pillar { double height, r; Vec3d endpt; @@ -162,18 +149,16 @@ struct Pillar { } const Vec3d& endpoint() const { return endpt; } - -// Pillar& add_base(double baseheight = 3, double radius = 2); }; // A base for pillars or bridges that end on the ground struct Pedestal { Vec3d pos; - double height, radius; + double height, r_bottom, r_top; long id = ID_UNSET; - Pedestal(const Vec3d &p, double h = 3., double r = 2.) - : pos{p}, height{h}, radius{r} + Pedestal(const Vec3d &p, double h, double rbottom, double rtop) + : pos{p}, height{h}, r_bottom{rbottom}, r_top{rtop} {} }; @@ -301,15 +286,7 @@ public: return pillar.id; } - void add_pillar_base(long pid, double baseheight = 3, double radius = 2) - { - std::lock_guard lk(m_mutex); - assert(pid >= 0 && size_t(pid) < m_pillars.size()); - m_pedestals.emplace_back(m_pillars[size_t(pid)].endpt, baseheight, radius); - m_pedestals.back().id = m_pedestals.size() - 1; - m_meshcache_valid = false; -// m_pillars[size_t(pid)].add_base(baseheight, radius); - } + void add_pillar_base(long pid, double baseheight = 3, double radius = 2); template const Anchor& add_anchor(Args&&...args) { diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp index 4b8366ee44..c6b2884d26 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -580,7 +580,7 @@ bool SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, bool normal_mode = true; Vec3d dir = sourcedir; - auto to_floor = [gndlvl](const Vec3d &p) { return Vec3d{p.x(), p.y(), gndlvl}; }; + auto to_floor = [&gndlvl](const Vec3d &p) { return Vec3d{p.x(), p.y(), gndlvl}; }; if (m_cfg.object_elevation_mm < EPSILON) { @@ -599,6 +599,7 @@ bool SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, // Try to move along the established bridge direction to dodge the // forbidden region for the endpoint. double t = -radius; + bool succ = true; while (std::sqrt(m_mesh.squared_distance(to_floor(endp))) < min_dist || !std::isinf(bridge_mesh_distance(endp, DOWN, radius))) { t += radius; @@ -607,36 +608,58 @@ bool SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, if (t > m_cfg.max_bridge_length_mm || endp(Z) < gndlvl) { if (head_id >= 0) m_builder.add_pillar(head_id, 0.); - return false; + succ = false; + break; } } + + if (!succ) { + if (can_add_base) { + can_add_base = false; + base_r = 0.; + gndlvl -= m_mesh.ground_level_offset(); + min_dist = sd + base_r + EPSILON; + endp = {jp(X), jp(Y), gndlvl + radius}; + + t = -radius; + while (std::sqrt(m_mesh.squared_distance(to_floor(endp))) < min_dist || + !std::isinf(bridge_mesh_distance(endp, DOWN, radius))) { + t += radius; + endp = jp + t * dir; + normal_mode = false; + + if (t > m_cfg.max_bridge_length_mm || endp(Z) < (gndlvl + radius)) { + if (head_id >= 0) m_builder.add_pillar(head_id, 0.); + return false; + } + } + } else return false; + } } + double h = (jp - endp).norm(); + // Check if the deduced route is sane and exit with error if not. - if (bridge_mesh_distance(jp, dir, radius) < (endp - jp).norm()) { + if (bridge_mesh_distance(jp, dir, radius) < h) { if (head_id >= 0) m_builder.add_pillar(head_id, 0.); return false; } // Straigh path down, no area to dodge if (normal_mode) { - double h = jp.z() - endp.z(); pillar_id = head_id >= 0 ? m_builder.add_pillar(head_id, h) : - m_builder.add_pillar(jp, h, radius); + m_builder.add_pillar(endp, h, radius); if (can_add_base) - m_builder.add_pillar_base(pillar_id, m_cfg.base_height_mm, - m_cfg.base_radius_mm); + add_pillar_base(pillar_id); } else { // Insert the bridge to get around the forbidden area -// Vec3d pgnd{endp.x(), endp.y(), gndlvl}; - double h = endp.z() - gndlvl; - pillar_id = m_builder.add_pillar(endp, h, radius); + Vec3d pgnd{endp.x(), endp.y(), gndlvl}; + pillar_id = m_builder.add_pillar(pgnd, endp.z() - gndlvl, radius); if (can_add_base) - m_builder.add_pillar_base(pillar_id, m_cfg.base_height_mm, - m_cfg.base_radius_mm); + add_pillar_base(pillar_id); m_builder.add_bridge(jp, endp, radius); m_builder.add_junction(endp, radius); @@ -912,11 +935,8 @@ void SupportTreeBuildsteps::routing_to_ground() BOOST_LOG_TRIVIAL(warning) << "Pillar cannot be created for support point id: " << hid; m_iheads_onmodel.emplace_back(h.id); -// h.invalidate(); continue; } - - h.transform(); } // now we will go through the clusters ones again and connect the @@ -939,7 +959,6 @@ void SupportTreeBuildsteps::routing_to_ground() if (c == cidx) continue; auto &sidehead = m_builder.head(c); - sidehead.transform(); if (!connect_to_nearpillar(sidehead, centerpillarID) && !search_pillar_and_connect(sidehead)) { @@ -1016,6 +1035,12 @@ bool SupportTreeBuildsteps::connect_to_model_body(Head &head) if (it == m_head_to_ground_scans.end()) return false; auto &hit = it->second; + + if (!hit.is_hit()) { + // TODO scan for potential anchor points on model surface + return false; + } + Vec3d hjp = head.junction_point(); double zangle = std::asin(hit.direction()(Z)); zangle = std::max(zangle, PI/4); @@ -1033,13 +1058,11 @@ bool SupportTreeBuildsteps::connect_to_model_body(Head &head) Vec3d hitp = std::abs(hitdiff) < 2*head.r_back_mm? center_hit.position() : hit.position(); - head.transform(); - - long pillar_id = m_builder.add_pillar(head.id, hit.distance() + h); + long pillar_id = m_builder.add_pillar(head.id, hjp.z() - endp.z()); Pillar &pill = m_builder.pillar(pillar_id); Vec3d taildir = endp - hitp; - double dist = distance(endp, hitp) + m_cfg.head_penetration_mm; + double dist = (hitp - endp).norm() + m_cfg.head_penetration_mm; double w = dist - 2 * head.r_pin_mm - head.r_back_mm; if (w < 0.) { @@ -1050,12 +1073,6 @@ bool SupportTreeBuildsteps::connect_to_model_body(Head &head) m_builder.add_anchor(head.r_back_mm, head.r_pin_mm, w, m_cfg.head_penetration_mm, taildir, hitp); -// Head tailhead(head.r_back_mm, head.r_pin_mm, w, -// m_cfg.head_penetration_mm, taildir, hitp); - -// tailhead.transform(); -// pill.base = tailhead.mesh; - m_pillar_index.guarded_insert(pill.endpoint(), pill.id); return true; @@ -1111,11 +1128,11 @@ void SupportTreeBuildsteps::routing_to_model() auto& head = m_builder.head(idx); // Search nearby pillar - if (search_pillar_and_connect(head)) { head.transform(); return; } + if (search_pillar_and_connect(head)) { return; } // Cannot connect to nearby pillar. We will try to search for // a route to the ground. - if (connect_to_ground(head)) { head.transform(); return; } + if (connect_to_ground(head)) { return; } // No route to the ground, so connect to the model body as a last resort if (connect_to_model_body(head)) { return; } @@ -1300,12 +1317,14 @@ void SupportTreeBuildsteps::interconnect_pillars() if (found) for (unsigned n = 0; n < needpillars; n++) { - Vec3d s = spts[n]; - Pillar p(s, s.z() - gnd, pillar().r); -// p.add_base(m_cfg.base_height_mm, m_cfg.base_radius_mm); + Vec3d s = spts[n]; + Pillar p(Vec3d{s.x(), s.y(), gnd}, s.z() - gnd, pillar().r); if (interconnect(pillar(), p)) { Pillar &pp = m_builder.pillar(m_builder.add_pillar(p)); + + add_pillar_base(pp.id); + m_pillar_index.insert(pp.endpoint(), unsigned(pp.id)); m_builder.add_junction(s, pillar().r); diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp index fc5670b163..51d8344480 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp @@ -17,9 +17,7 @@ enum { // For indexing Eigen vectors as v(X), v(Y), v(Z) instead of numbers X, Y, Z }; -inline Vec2d to_vec2(const Vec3d& v3) { - return {v3(X), v3(Y)}; -} +inline Vec2d to_vec2(const Vec3d &v3) { return {v3(X), v3(Y)}; } inline std::pair dir_to_spheric(const Vec3d &n, double norm = 1.) { @@ -47,7 +45,6 @@ inline Vec3d spheric_to_dir(const std::pair &v) return spheric_to_dir(v.first, v.second); } - // Give points on a 3D ring with given center, radius and orientation // method based on: // https://math.stackexchange.com/questions/73237/parametric-equation-of-a-circle-in-3d-space @@ -297,8 +294,12 @@ class SupportTreeBuildsteps { const Vec3d &sourcedir, double radius, long head_id = ID_UNSET); - - + + void add_pillar_base(long pid) + { + m_builder.add_pillar_base(pid, m_cfg.base_height_mm, m_cfg.base_radius_mm); + } + public: SupportTreeBuildsteps(SupportTreeBuilder & builder, const SupportableMesh &sm); diff --git a/src/libslic3r/SLA/SupportTreeMesher.cpp b/src/libslic3r/SLA/SupportTreeMesher.cpp index 1d9be6c348..15491775b4 100644 --- a/src/libslic3r/SLA/SupportTreeMesher.cpp +++ b/src/libslic3r/SLA/SupportTreeMesher.cpp @@ -94,7 +94,7 @@ Contour3D sphere(double rho, Portion portion, double fa) { Contour3D cylinder(double r, double h, size_t ssteps, const Vec3d &sp) { - assert(steps > 0); + assert(ssteps > 0); Contour3D ret; @@ -157,7 +157,7 @@ Contour3D cylinder(double r, double h, size_t ssteps, const Vec3d &sp) Contour3D pinhead(double r_pin, double r_back, double length, size_t steps) { assert(steps > 0); - assert(length > 0.); + assert(length >= 0.); assert(r_back > 0.); assert(r_pin > 0.); @@ -167,7 +167,7 @@ Contour3D pinhead(double r_pin, double r_back, double length, size_t steps) // both circles perfectly. // Set up the model detail level - const double detail = 2*PI/steps; + const double detail = 2 * PI / steps; // We don't generate whole circles. Instead, we generate only the // portions which are visible (not covered by the robe) To know the @@ -176,26 +176,24 @@ Contour3D pinhead(double r_pin, double r_back, double length, size_t steps) // triangles the following relations: // The height of the whole mesh - const double h = r_back + r_pin + length; - double phi = PI / 2. - std::acos((r_back - r_pin) / h); + const double h = r_back + r_pin + length; + double phi = PI / 2. - std::acos((r_back - r_pin) / h); // To generate a whole circle we would pass a portion of (0, Pi) // To generate only a half horizontal circle we can pass (0, Pi/2) // The calculated phi is an offset to the half circles needed to smooth // the transition from the circle to the robe geometry - auto&& s1 = sphere(r_back, make_portion(0, PI/2 + phi), detail); - auto&& s2 = sphere(r_pin, make_portion(PI/2 + phi, PI), detail); + auto &&s1 = sphere(r_back, make_portion(0, PI / 2 + phi), detail); + auto &&s2 = sphere(r_pin, make_portion(PI / 2 + phi, PI), detail); - for(auto& p : s2.points) p.z() += h; + for (auto &p : s2.points) p.z() += h; mesh.merge(s1); mesh.merge(s2); - for(size_t idx1 = s1.points.size() - steps, idx2 = s1.points.size(); - idx1 < s1.points.size() - 1; - idx1++, idx2++) - { + for (size_t idx1 = s1.points.size() - steps, idx2 = s1.points.size(); + idx1 < s1.points.size() - 1; idx1++, idx2++) { coord_t i1s1 = coord_t(idx1), i1s2 = coord_t(idx2); coord_t i2s1 = i1s1 + 1, i2s2 = i1s2 + 1; @@ -214,43 +212,43 @@ Contour3D pinhead(double r_pin, double r_back, double length, size_t steps) return mesh; } -Contour3D pedestal(const Vec3d &endpt, double baseheight, double radius, size_t steps) +Contour3D halfcone(double baseheight, + double r_bottom, + double r_top, + const Vec3d &pos, + size_t steps) { assert(steps > 0); - if(baseheight <= 0) return {}; - - assert(steps >= 0); - auto last = int(steps - 1); + if (baseheight <= 0 || steps <= 0) return {}; Contour3D base; - double a = 2*PI/steps; - double z = endpt(Z) + baseheight; - - for(size_t i = 0; i < steps; ++i) { - double phi = i*a; - double x = endpt(X) + radius * std::cos(phi); - double y = endpt(Y) + radius * std::sin(phi); - base.points.emplace_back(x, y, z); + double a = 2 * PI / steps; + auto last = int(steps - 1); + Vec3d ep{pos.x(), pos.y(), pos.z() + baseheight}; + for (size_t i = 0; i < steps; ++i) { + double phi = i * a; + double x = pos.x() + r_top * std::cos(phi); + double y = pos.y() + r_top * std::sin(phi); + base.points.emplace_back(x, y, ep.z()); } - for(size_t i = 0; i < steps; ++i) { - double phi = i*a; - double x = endpt(X) + radius*std::cos(phi); - double y = endpt(Y) + radius*std::sin(phi); - base.points.emplace_back(x, y, z - baseheight); + for (size_t i = 0; i < steps; ++i) { + double phi = i * a; + double x = pos.x() + r_bottom * std::cos(phi); + double y = pos.y() + r_bottom * std::sin(phi); + base.points.emplace_back(x, y, pos.z()); } - auto ep = endpt; ep(Z) += baseheight; - base.points.emplace_back(endpt); + base.points.emplace_back(pos); base.points.emplace_back(ep); - auto& indices = base.faces3; - auto hcenter = int(base.points.size() - 1); - auto lcenter = int(base.points.size() - 2); - auto offs = int(steps); - for(int i = 0; i < last; ++i) { + auto &indices = base.faces3; + auto hcenter = int(base.points.size() - 1); + auto lcenter = int(base.points.size() - 2); + auto offs = int(steps); + for (int i = 0; i < last; ++i) { indices.emplace_back(i, i + offs, offs + i + 1); indices.emplace_back(i, offs + i + 1, i + 1); indices.emplace_back(i, i + 1, hcenter); diff --git a/src/libslic3r/SLA/SupportTreeMesher.hpp b/src/libslic3r/SLA/SupportTreeMesher.hpp index 677cab3b81..a086680c34 100644 --- a/src/libslic3r/SLA/SupportTreeMesher.hpp +++ b/src/libslic3r/SLA/SupportTreeMesher.hpp @@ -24,23 +24,28 @@ Contour3D sphere(double rho, // h: Height // ssteps: how many edges will create the base circle // sp: starting point -Contour3D cylinder(double r, double h, size_t steps = 45, const Vec3d &sp = Vec3d::Zero()); +Contour3D cylinder(double r, + double h, + size_t steps = 45, + const Vec3d &sp = Vec3d::Zero()); Contour3D pinhead(double r_pin, double r_back, double length, size_t steps = 45); -Contour3D pedestal(const Vec3d &pt, double baseheight, double radius, size_t steps = 45); +Contour3D halfcone(double baseheight, + double r_bottom, + double r_top, + const Vec3d &pt = Vec3d::Zero(), + size_t steps = 45); inline Contour3D get_mesh(const Head &h, size_t steps) { Contour3D mesh = pinhead(h.r_pin_mm, h.r_back_mm, h.width_mm, steps); - // To simplify further processing, we translate the mesh so that the - // last vertex of the pointing sphere (the pinpoint) will be at (0,0,0) for(auto& p : mesh.points) p.z() -= (h.fullwidth() - h.r_back_mm); using Quaternion = Eigen::Quaternion; - // We rotate the head to the specified direction The head's pointing + // We rotate the head to the specified direction. The head's pointing // side is facing upwards so this means that it would hold a support // point with a normal pointing straight down. This is the reason of // the -1 z coordinate @@ -64,7 +69,7 @@ inline Contour3D get_mesh(const Pillar &p, size_t steps) inline Contour3D get_mesh(const Pedestal &p, size_t steps) { - return pedestal(p.pos, p.height, p.radius, steps); + return halfcone(p.height, p.r_bottom, p.r_top, p.pos, steps); } inline Contour3D get_mesh(const Junction &j, size_t steps) @@ -89,6 +94,6 @@ inline Contour3D get_mesh(const Bridge &br, size_t steps) return mesh; } -}} +}} // namespace Slic3r::sla #endif // SUPPORTTREEMESHER_HPP diff --git a/tests/sla_print/sla_test_utils.cpp b/tests/sla_print/sla_test_utils.cpp index 5a3bd82a00..4cd94b7ed3 100644 --- a/tests/sla_print/sla_test_utils.cpp +++ b/tests/sla_print/sla_test_utils.cpp @@ -157,8 +157,8 @@ void test_supports(const std::string &obj_filename, if (std::abs(supportcfg.object_elevation_mm) < EPSILON) allowed_zmin = zmin - 2 * supportcfg.head_back_radius_mm; - REQUIRE(obb.min.z() >= allowed_zmin); - REQUIRE(obb.max.z() <= zmax); + REQUIRE(obb.min.z() >= Approx(allowed_zmin)); + REQUIRE(obb.max.z() <= Approx(zmax)); // Move out the support tree into the byproducts, we can examine it further // in various tests. From f19b3a2344cb499d962b9665a97028b053d98cbc Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 19 Jun 2020 09:25:17 +0200 Subject: [PATCH 242/503] Id-s put in a base class for support tree primitives --- src/libslic3r/SLA/SupportTreeBuilder.hpp | 30 +++++++++------------ src/libslic3r/SLA/SupportTreeBuildsteps.cpp | 8 +++--- src/libslic3r/SLA/SupportTreeBuildsteps.hpp | 2 +- tests/sla_print/sla_test_utils.cpp | 4 +-- 4 files changed, 20 insertions(+), 24 deletions(-) diff --git a/src/libslic3r/SLA/SupportTreeBuilder.hpp b/src/libslic3r/SLA/SupportTreeBuilder.hpp index 2b3ff91a06..aa8a4ea83b 100644 --- a/src/libslic3r/SLA/SupportTreeBuilder.hpp +++ b/src/libslic3r/SLA/SupportTreeBuilder.hpp @@ -58,12 +58,17 @@ template double distance(const Vec& pp1, const Vec& pp2) { return distance(p); } -const constexpr long ID_UNSET = -1; - const Vec3d DOWN = {0.0, 0.0, -1.0}; +struct SupportTreeNode +{ + static const constexpr long ID_UNSET = -1; + + long id = ID_UNSET; // For identification withing a tree. +}; + // A pinhead originating from a support point -struct Head { +struct Head: public SupportTreeNode { Vec3d dir = DOWN; Vec3d pos = {0, 0, 0}; @@ -71,10 +76,7 @@ struct Head { double r_pin_mm = 0.5; double width_mm = 2; double penetration_mm = 0.5; - - // For identification purposes. This will be used as the index into the - // container holding the head structures. See SLASupportTree::Impl - long id = ID_UNSET; + // If there is a pillar connecting to this head, then the id will be set. long pillar_id = ID_UNSET; @@ -115,21 +117,17 @@ struct Head { }; // A junction connecting bridges and pillars -struct Junction { +struct Junction: public SupportTreeNode { double r = 1; Vec3d pos; - - long id = ID_UNSET; Junction(const Vec3d &tr, double r_mm) : r(r_mm), pos(tr) {} }; -struct Pillar { +struct Pillar: public SupportTreeNode { double height, r; Vec3d endpt; - long id = ID_UNSET; - // If the pillar connects to a head, this is the id of that head bool starts_from_head = true; // Could start from a junction as well long start_junction_id = ID_UNSET; @@ -152,10 +150,9 @@ struct Pillar { }; // A base for pillars or bridges that end on the ground -struct Pedestal { +struct Pedestal: public SupportTreeNode { Vec3d pos; double height, r_bottom, r_top; - long id = ID_UNSET; Pedestal(const Vec3d &p, double h, double rbottom, double rtop) : pos{p}, height{h}, r_bottom{rbottom}, r_top{rtop} @@ -167,9 +164,8 @@ struct Pedestal { struct Anchor: public Head { using Head::Head; }; // A Bridge between two pillars (with junction endpoints) -struct Bridge { +struct Bridge: public SupportTreeNode { double r = 0.8; - long id = ID_UNSET; Vec3d startp = Vec3d::Zero(), endp = Vec3d::Zero(); Bridge(const Vec3d &j1, diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp index c6b2884d26..00f09b8121 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -570,7 +570,7 @@ bool SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, long head_id) { double sd = m_cfg.pillar_base_safety_distance_mm; - long pillar_id = ID_UNSET; + long pillar_id = SupportTreeNode::ID_UNSET; bool can_add_base = radius >= m_cfg.head_back_radius_mm; double base_r = can_add_base ? m_cfg.base_radius_mm : 0.; double gndlvl = m_builder.ground_level; @@ -1029,7 +1029,7 @@ bool SupportTreeBuildsteps::connect_to_ground(Head &head) bool SupportTreeBuildsteps::connect_to_model_body(Head &head) { - if (head.id <= ID_UNSET) return false; + if (head.id <= SupportTreeNode::ID_UNSET) return false; auto it = m_head_to_ground_scans.find(unsigned(head.id)); if (it == m_head_to_ground_scans.end()) return false; @@ -1084,7 +1084,7 @@ bool SupportTreeBuildsteps::search_pillar_and_connect(const Head &source) // We also need to remove elements progressively from the copied index. PointIndex spindex = m_pillar_index.guarded_clone(); - long nearest_id = ID_UNSET; + long nearest_id = SupportTreeNode::ID_UNSET; Vec3d querypt = source.junction_point(); @@ -1105,7 +1105,7 @@ bool SupportTreeBuildsteps::search_pillar_and_connect(const Head &source) if (size_t(nearest_id) < m_builder.pillarcount()) { if(!connect_to_nearpillar(source, nearest_id) || m_builder.pillar(nearest_id).r < source.r_back_mm) { - nearest_id = ID_UNSET; // continue searching + nearest_id = SupportTreeNode::ID_UNSET; // continue searching spindex.remove(ne); // without the current pillar } } diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp index 51d8344480..e8f73149e3 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp @@ -293,7 +293,7 @@ class SupportTreeBuildsteps { bool create_ground_pillar(const Vec3d &jp, const Vec3d &sourcedir, double radius, - long head_id = ID_UNSET); + long head_id = SupportTreeNode::ID_UNSET); void add_pillar_base(long pid) { diff --git a/tests/sla_print/sla_test_utils.cpp b/tests/sla_print/sla_test_utils.cpp index 4cd94b7ed3..bc0cfb0cd1 100644 --- a/tests/sla_print/sla_test_utils.cpp +++ b/tests/sla_print/sla_test_utils.cpp @@ -175,8 +175,8 @@ void check_support_tree_integrity(const sla::SupportTreeBuilder &stree, double H2 = cfg.max_dual_pillar_height_mm; for (const sla::Head &head : stree.heads()) { - REQUIRE((!head.is_valid() || head.pillar_id != sla::ID_UNSET || - head.bridge_id != sla::ID_UNSET)); + REQUIRE((!head.is_valid() || head.pillar_id != sla::SupportTreeNode::ID_UNSET || + head.bridge_id != sla::SupportTreeNode::ID_UNSET)); } for (const sla::Pillar &pillar : stree.pillars()) { From 645fbed88bb94d3addf32691e08f0e9453978120 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 19 Jun 2020 09:49:50 +0200 Subject: [PATCH 243/503] Make compile time support tree conf params constexpr --- src/libslic3r/SLA/SupportTree.cpp | 14 -------------- src/libslic3r/SLA/SupportTree.hpp | 14 +++++++------- 2 files changed, 7 insertions(+), 21 deletions(-) diff --git a/src/libslic3r/SLA/SupportTree.cpp b/src/libslic3r/SLA/SupportTree.cpp index eec819e225..1bb4cfab76 100644 --- a/src/libslic3r/SLA/SupportTree.cpp +++ b/src/libslic3r/SLA/SupportTree.cpp @@ -28,20 +28,6 @@ namespace Slic3r { namespace sla { -// Compile time configuration value definitions: - -// The max Z angle for a normal at which it will get completely ignored. -const double SupportConfig::normal_cutoff_angle = 150.0 * M_PI / 180.0; - -// The shortest distance of any support structure from the model surface -const double SupportConfig::safety_distance_mm = 0.5; - -const double SupportConfig::max_solo_pillar_height_mm = 15.0; -const double SupportConfig::max_dual_pillar_height_mm = 35.0; -const double SupportConfig::optimizer_rel_score_diff = 1e-6; -const unsigned SupportConfig::optimizer_max_iterations = 1000; -const unsigned SupportConfig::pillar_cascade_neighbors = 3; - void SupportTree::retrieve_full_mesh(TriangleMesh &outmesh) const { outmesh.merge(retrieve_mesh(MeshType::Support)); outmesh.merge(retrieve_mesh(MeshType::Pad)); diff --git a/src/libslic3r/SLA/SupportTree.hpp b/src/libslic3r/SLA/SupportTree.hpp index 3b9f603fd2..1415ab8fe9 100644 --- a/src/libslic3r/SLA/SupportTree.hpp +++ b/src/libslic3r/SLA/SupportTree.hpp @@ -94,16 +94,16 @@ struct SupportConfig // ///////////////////////////////////////////////////////////////////////// // The max Z angle for a normal at which it will get completely ignored. - static const double normal_cutoff_angle; + static const double constexpr normal_cutoff_angle = 150.0 * M_PI / 180.0; // The shortest distance of any support structure from the model surface - static const double safety_distance_mm; + static const double constexpr safety_distance_mm = 0.5; - static const double max_solo_pillar_height_mm; - static const double max_dual_pillar_height_mm; - static const double optimizer_rel_score_diff; - static const unsigned optimizer_max_iterations; - static const unsigned pillar_cascade_neighbors; + static const double constexpr max_solo_pillar_height_mm = 15.0; + static const double constexpr max_dual_pillar_height_mm = 35.0; + static const double constexpr optimizer_rel_score_diff = 1e-6; + static const unsigned constexpr optimizer_max_iterations = 1000; + static const unsigned constexpr pillar_cascade_neighbors = 3; }; From 1eec6c473c660196dbe7ca421d0abefbf4ea8739 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 25 Jun 2020 13:58:51 +0200 Subject: [PATCH 244/503] Rename EigenMesh3D to IndexedMesh and SupportConfig to SupportTreeConfig --- src/libslic3r/CMakeLists.txt | 4 +- src/libslic3r/SLA/Contour3D.cpp | 4 +- src/libslic3r/SLA/Contour3D.hpp | 4 +- src/libslic3r/SLA/Hollowing.cpp | 4 +- .../SLA/{EigenMesh3D.cpp => IndexedMesh.cpp} | 46 ++--- .../SLA/{EigenMesh3D.hpp => IndexedMesh.hpp} | 32 +-- src/libslic3r/SLA/ReprojectPointsOnMesh.hpp | 6 +- src/libslic3r/SLA/SupportPointGenerator.cpp | 10 +- src/libslic3r/SLA/SupportPointGenerator.hpp | 8 +- src/libslic3r/SLA/SupportTree.hpp | 21 +- src/libslic3r/SLA/SupportTreeBuildsteps.cpp | 186 +++++++++--------- src/libslic3r/SLA/SupportTreeBuildsteps.hpp | 19 +- src/libslic3r/SLAPrint.cpp | 6 +- src/libslic3r/SLAPrint.hpp | 2 +- src/slic3r/GUI/MeshUtils.cpp | 4 +- src/slic3r/GUI/MeshUtils.hpp | 4 +- tests/sla_print/sla_print_tests.cpp | 12 +- tests/sla_print/sla_raycast_tests.cpp | 4 +- tests/sla_print/sla_test_utils.cpp | 10 +- tests/sla_print/sla_test_utils.hpp | 12 +- tests/sla_print/sla_treebuilder_tests.cpp | 134 ++++++------- 21 files changed, 269 insertions(+), 263 deletions(-) rename src/libslic3r/SLA/{EigenMesh3D.cpp => IndexedMesh.cpp} (92%) rename src/libslic3r/SLA/{EigenMesh3D.hpp => IndexedMesh.hpp} (86%) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 20f3c6b4ba..91da5df5d6 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -236,8 +236,8 @@ add_library(libslic3r STATIC SLA/SupportPointGenerator.cpp SLA/Contour3D.hpp SLA/Contour3D.cpp - SLA/EigenMesh3D.hpp - SLA/EigenMesh3D.cpp + SLA/IndexedMesh.hpp + SLA/IndexedMesh.cpp SLA/Clustering.hpp SLA/Clustering.cpp SLA/ReprojectPointsOnMesh.hpp diff --git a/src/libslic3r/SLA/Contour3D.cpp b/src/libslic3r/SLA/Contour3D.cpp index 408465d43e..96d10af208 100644 --- a/src/libslic3r/SLA/Contour3D.cpp +++ b/src/libslic3r/SLA/Contour3D.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include @@ -27,7 +27,7 @@ Contour3D::Contour3D(TriangleMesh &&trmesh) faces3.swap(trmesh.its.indices); } -Contour3D::Contour3D(const EigenMesh3D &emesh) { +Contour3D::Contour3D(const IndexedMesh &emesh) { points.reserve(emesh.vertices().size()); faces3.reserve(emesh.indices().size()); diff --git a/src/libslic3r/SLA/Contour3D.hpp b/src/libslic3r/SLA/Contour3D.hpp index 1a4fa9a29c..3380cd6ab0 100644 --- a/src/libslic3r/SLA/Contour3D.hpp +++ b/src/libslic3r/SLA/Contour3D.hpp @@ -10,7 +10,7 @@ using Vec4i = Eigen::Matrix; namespace sla { -class EigenMesh3D; +class IndexedMesh; /// Dumb vertex mesh consisting of triangles (or) quads. Capable of merging with /// other meshes of this type and converting to and from other mesh formats. @@ -22,7 +22,7 @@ struct Contour3D { Contour3D() = default; Contour3D(const TriangleMesh &trmesh); Contour3D(TriangleMesh &&trmesh); - Contour3D(const EigenMesh3D &emesh); + Contour3D(const IndexedMesh &emesh); Contour3D& merge(const Contour3D& ctr); Contour3D& merge(const Pointf3s& triangles); diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index 44e4dd8390..5334054a05 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include #include @@ -159,7 +159,7 @@ bool DrainHole::get_intersections(const Vec3f& s, const Vec3f& dir, const Eigen::ParametrizedLine ray(s, dir.normalized()); for (size_t i=0; i<2; ++i) - out[i] = std::make_pair(sla::EigenMesh3D::hit_result::infty(), Vec3d::Zero()); + out[i] = std::make_pair(sla::IndexedMesh::hit_result::infty(), Vec3d::Zero()); const float sqr_radius = pow(radius, 2.f); diff --git a/src/libslic3r/SLA/EigenMesh3D.cpp b/src/libslic3r/SLA/IndexedMesh.cpp similarity index 92% rename from src/libslic3r/SLA/EigenMesh3D.cpp rename to src/libslic3r/SLA/IndexedMesh.cpp index be44e324c6..573b62b6db 100644 --- a/src/libslic3r/SLA/EigenMesh3D.cpp +++ b/src/libslic3r/SLA/IndexedMesh.cpp @@ -1,4 +1,4 @@ -#include "EigenMesh3D.hpp" +#include "IndexedMesh.hpp" #include "Concurrency.hpp" #include @@ -12,7 +12,7 @@ namespace Slic3r { namespace sla { -class EigenMesh3D::AABBImpl { +class IndexedMesh::AABBImpl { private: AABBTreeIndirect::Tree3f m_tree; @@ -57,7 +57,7 @@ public: static const constexpr double MESH_EPS = 1e-6; -EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh) +IndexedMesh::IndexedMesh(const TriangleMesh& tmesh) : m_aabb(new AABBImpl()), m_tm(&tmesh) { auto&& bb = tmesh.bounding_box(); @@ -67,61 +67,61 @@ EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh) m_aabb->init(tmesh); } -EigenMesh3D::~EigenMesh3D() {} +IndexedMesh::~IndexedMesh() {} -EigenMesh3D::EigenMesh3D(const EigenMesh3D &other): +IndexedMesh::IndexedMesh(const IndexedMesh &other): m_tm(other.m_tm), m_ground_level(other.m_ground_level), m_aabb( new AABBImpl(*other.m_aabb) ) {} -EigenMesh3D &EigenMesh3D::operator=(const EigenMesh3D &other) +IndexedMesh &IndexedMesh::operator=(const IndexedMesh &other) { m_tm = other.m_tm; m_ground_level = other.m_ground_level; m_aabb.reset(new AABBImpl(*other.m_aabb)); return *this; } -EigenMesh3D &EigenMesh3D::operator=(EigenMesh3D &&other) = default; +IndexedMesh &IndexedMesh::operator=(IndexedMesh &&other) = default; -EigenMesh3D::EigenMesh3D(EigenMesh3D &&other) = default; +IndexedMesh::IndexedMesh(IndexedMesh &&other) = default; -const std::vector& EigenMesh3D::vertices() const +const std::vector& IndexedMesh::vertices() const { return m_tm->its.vertices; } -const std::vector& EigenMesh3D::indices() const +const std::vector& IndexedMesh::indices() const { return m_tm->its.indices; } -const Vec3f& EigenMesh3D::vertices(size_t idx) const +const Vec3f& IndexedMesh::vertices(size_t idx) const { return m_tm->its.vertices[idx]; } -const Vec3i& EigenMesh3D::indices(size_t idx) const +const Vec3i& IndexedMesh::indices(size_t idx) const { return m_tm->its.indices[idx]; } -Vec3d EigenMesh3D::normal_by_face_id(int face_id) const { +Vec3d IndexedMesh::normal_by_face_id(int face_id) const { return m_tm->stl.facet_start[face_id].normal.cast(); } -EigenMesh3D::hit_result -EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const +IndexedMesh::hit_result +IndexedMesh::query_ray_hit(const Vec3d &s, const Vec3d &dir) const { assert(is_approx(dir.norm(), 1.)); igl::Hit hit; @@ -149,10 +149,10 @@ EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const return ret; } -std::vector -EigenMesh3D::query_ray_hits(const Vec3d &s, const Vec3d &dir) const +std::vector +IndexedMesh::query_ray_hits(const Vec3d &s, const Vec3d &dir) const { - std::vector outs; + std::vector outs; std::vector hits; m_aabb->intersect_ray(*m_tm, s, dir, hits); @@ -170,7 +170,7 @@ EigenMesh3D::query_ray_hits(const Vec3d &s, const Vec3d &dir) const // Convert the igl::Hit into hit_result outs.reserve(hits.size()); for (const igl::Hit& hit : hits) { - outs.emplace_back(EigenMesh3D::hit_result(*this)); + outs.emplace_back(IndexedMesh::hit_result(*this)); outs.back().m_t = double(hit.t); outs.back().m_dir = dir; outs.back().m_source = s; @@ -185,8 +185,8 @@ EigenMesh3D::query_ray_hits(const Vec3d &s, const Vec3d &dir) const #ifdef SLIC3R_HOLE_RAYCASTER -EigenMesh3D::hit_result EigenMesh3D::filter_hits( - const std::vector& object_hits) const +IndexedMesh::hit_result IndexedMesh::filter_hits( + const std::vector& object_hits) const { assert(! m_holes.empty()); hit_result out(*this); @@ -282,7 +282,7 @@ EigenMesh3D::hit_result EigenMesh3D::filter_hits( #endif -double EigenMesh3D::squared_distance(const Vec3d &p, int& i, Vec3d& c) const { +double IndexedMesh::squared_distance(const Vec3d &p, int& i, Vec3d& c) const { double sqdst = 0; Eigen::Matrix pp = p; Eigen::Matrix cc; @@ -303,7 +303,7 @@ static bool point_on_edge(const Vec3d& p, const Vec3d& e1, const Vec3d& e2, } PointSet normals(const PointSet& points, - const EigenMesh3D& mesh, + const IndexedMesh& mesh, double eps, std::function thr, // throw on cancel const std::vector& pt_indices) diff --git a/src/libslic3r/SLA/EigenMesh3D.hpp b/src/libslic3r/SLA/IndexedMesh.hpp similarity index 86% rename from src/libslic3r/SLA/EigenMesh3D.hpp rename to src/libslic3r/SLA/IndexedMesh.hpp index c9196bb432..b0970608e2 100644 --- a/src/libslic3r/SLA/EigenMesh3D.hpp +++ b/src/libslic3r/SLA/IndexedMesh.hpp @@ -1,5 +1,5 @@ -#ifndef SLA_EIGENMESH3D_H -#define SLA_EIGENMESH3D_H +#ifndef SLA_INDEXEDMESH_H +#define SLA_INDEXEDMESH_H #include #include @@ -26,7 +26,7 @@ using PointSet = Eigen::MatrixXd; /// An index-triangle structure for libIGL functions. Also serves as an /// alternative (raw) input format for the SLASupportTree. // Implemented in libslic3r/SLA/Common.cpp -class EigenMesh3D { +class IndexedMesh { class AABBImpl; const TriangleMesh* m_tm; @@ -42,15 +42,15 @@ class EigenMesh3D { public: - explicit EigenMesh3D(const TriangleMesh&); + explicit IndexedMesh(const TriangleMesh&); - EigenMesh3D(const EigenMesh3D& other); - EigenMesh3D& operator=(const EigenMesh3D&); + IndexedMesh(const IndexedMesh& other); + IndexedMesh& operator=(const IndexedMesh&); - EigenMesh3D(EigenMesh3D &&other); - EigenMesh3D& operator=(EigenMesh3D &&other); + IndexedMesh(IndexedMesh &&other); + IndexedMesh& operator=(IndexedMesh &&other); - ~EigenMesh3D(); + ~IndexedMesh(); inline double ground_level() const { return m_ground_level + m_gnd_offset; } inline void ground_level_offset(double o) { m_gnd_offset = o; } @@ -66,15 +66,15 @@ public: // m_t holds a distance from m_source to the intersection. double m_t = infty(); int m_face_id = -1; - const EigenMesh3D *m_mesh = nullptr; + const IndexedMesh *m_mesh = nullptr; Vec3d m_dir; Vec3d m_source; Vec3d m_normal; - friend class EigenMesh3D; + friend class IndexedMesh; // A valid object of this class can only be obtained from - // EigenMesh3D::query_ray_hit method. - explicit inline hit_result(const EigenMesh3D& em): m_mesh(&em) {} + // IndexedMesh::query_ray_hit method. + explicit inline hit_result(const IndexedMesh& em): m_mesh(&em) {} public: // This denotes no hit on the mesh. static inline constexpr double infty() { return std::numeric_limits::infinity(); } @@ -111,7 +111,7 @@ public: // This function is currently not used anywhere, it was written when the // holes were subtracted on slices, that is, before we started using CGAL // to actually cut the holes into the mesh. - hit_result filter_hits(const std::vector& obj_hits) const; + hit_result filter_hits(const std::vector& obj_hits) const; #endif // Casting a ray on the mesh, returns the distance where the hit occures. @@ -136,11 +136,11 @@ public: // Calculate the normals for the selected points (from 'points' set) on the // mesh. This will call squared distance for each point. PointSet normals(const PointSet& points, - const EigenMesh3D& convert_mesh, + const IndexedMesh& convert_mesh, double eps = 0.05, // min distance from edges std::function throw_on_cancel = [](){}, const std::vector& selected_points = {}); }} // namespace Slic3r::sla -#endif // EIGENMESH3D_H +#endif // INDEXEDMESH_H diff --git a/src/libslic3r/SLA/ReprojectPointsOnMesh.hpp b/src/libslic3r/SLA/ReprojectPointsOnMesh.hpp index 702d1bce18..4737a6c212 100644 --- a/src/libslic3r/SLA/ReprojectPointsOnMesh.hpp +++ b/src/libslic3r/SLA/ReprojectPointsOnMesh.hpp @@ -4,7 +4,7 @@ #include "libslic3r/Point.hpp" #include "SupportPoint.hpp" #include "Hollowing.hpp" -#include "EigenMesh3D.hpp" +#include "IndexedMesh.hpp" #include "libslic3r/Model.hpp" #include @@ -15,7 +15,7 @@ template Vec3d pos(const Pt &p) { return p.pos.template cast() template void pos(Pt &p, const Vec3d &pp) { p.pos = pp.cast(); } template -void reproject_support_points(const EigenMesh3D &mesh, std::vector &pts) +void reproject_support_points(const IndexedMesh &mesh, std::vector &pts) { tbb::parallel_for(size_t(0), pts.size(), [&mesh, &pts](size_t idx) { int junk; @@ -40,7 +40,7 @@ inline void reproject_points_and_holes(ModelObject *object) TriangleMesh rmsh = object->raw_mesh(); rmsh.require_shared_vertices(); - EigenMesh3D emesh{rmsh}; + IndexedMesh emesh{rmsh}; if (has_sppoints) reproject_support_points(emesh, object->sla_support_points); diff --git a/src/libslic3r/SLA/SupportPointGenerator.cpp b/src/libslic3r/SLA/SupportPointGenerator.cpp index b598439cae..3cd075ae69 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.cpp +++ b/src/libslic3r/SLA/SupportPointGenerator.cpp @@ -50,7 +50,7 @@ float SupportPointGenerator::distance_limit(float angle) const }*/ SupportPointGenerator::SupportPointGenerator( - const sla::EigenMesh3D &emesh, + const sla::IndexedMesh &emesh, const std::vector &slices, const std::vector & heights, const Config & config, @@ -64,7 +64,7 @@ SupportPointGenerator::SupportPointGenerator( } SupportPointGenerator::SupportPointGenerator( - const EigenMesh3D &emesh, + const IndexedMesh &emesh, const SupportPointGenerator::Config &config, std::function throw_on_cancel, std::function statusfn) @@ -95,8 +95,8 @@ void SupportPointGenerator::project_onto_mesh(std::vector& po m_throw_on_cancel(); Vec3f& p = points[point_id].pos; // Project the point upward and downward and choose the closer intersection with the mesh. - sla::EigenMesh3D::hit_result hit_up = m_emesh.query_ray_hit(p.cast(), Vec3d(0., 0., 1.)); - sla::EigenMesh3D::hit_result hit_down = m_emesh.query_ray_hit(p.cast(), Vec3d(0., 0., -1.)); + sla::IndexedMesh::hit_result hit_up = m_emesh.query_ray_hit(p.cast(), Vec3d(0., 0., 1.)); + sla::IndexedMesh::hit_result hit_down = m_emesh.query_ray_hit(p.cast(), Vec3d(0., 0., -1.)); bool up = hit_up.is_hit(); bool down = hit_down.is_hit(); @@ -104,7 +104,7 @@ void SupportPointGenerator::project_onto_mesh(std::vector& po if (!up && !down) continue; - sla::EigenMesh3D::hit_result& hit = (!down || (hit_up.distance() < hit_down.distance())) ? hit_up : hit_down; + sla::IndexedMesh::hit_result& hit = (!down || (hit_up.distance() < hit_down.distance())) ? hit_up : hit_down; p = p + (hit.distance() * hit.direction()).cast(); } }); diff --git a/src/libslic3r/SLA/SupportPointGenerator.hpp b/src/libslic3r/SLA/SupportPointGenerator.hpp index 3f07e96746..f1b3770254 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.hpp +++ b/src/libslic3r/SLA/SupportPointGenerator.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include @@ -27,10 +27,10 @@ public: inline float tear_pressure() const { return 1.f; } // pressure that the display exerts (the force unit per mm2) }; - SupportPointGenerator(const EigenMesh3D& emesh, const std::vector& slices, + SupportPointGenerator(const IndexedMesh& emesh, const std::vector& slices, const std::vector& heights, const Config& config, std::function throw_on_cancel, std::function statusfn); - SupportPointGenerator(const EigenMesh3D& emesh, const Config& config, std::function throw_on_cancel, std::function statusfn); + SupportPointGenerator(const IndexedMesh& emesh, const Config& config, std::function throw_on_cancel, std::function statusfn); const std::vector& output() const { return m_output; } std::vector& output() { return m_output; } @@ -206,7 +206,7 @@ private: static void output_structures(const std::vector &structures); #endif // SLA_SUPPORTPOINTGEN_DEBUG - const EigenMesh3D& m_emesh; + const IndexedMesh& m_emesh; std::function m_throw_on_cancel; std::function m_statusfn; diff --git a/src/libslic3r/SLA/SupportTree.hpp b/src/libslic3r/SLA/SupportTree.hpp index 1415ab8fe9..7d54b76a40 100644 --- a/src/libslic3r/SLA/SupportTree.hpp +++ b/src/libslic3r/SLA/SupportTree.hpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include @@ -31,7 +31,7 @@ enum class PillarConnectionMode dynamic }; -struct SupportConfig +struct SupportTreeConfig { bool enabled = true; @@ -107,23 +107,30 @@ struct SupportConfig }; +// TODO: Part of future refactor +//class SupportConfig { +// std::optional tree_cfg {std::in_place_t{}}; // fill up +// std::optional pad_cfg; +//}; + enum class MeshType { Support, Pad }; struct SupportableMesh { - EigenMesh3D emesh; + IndexedMesh emesh; SupportPoints pts; - SupportConfig cfg; + SupportTreeConfig cfg; + PadConfig pad_cfg; explicit SupportableMesh(const TriangleMesh & trmsh, const SupportPoints &sp, - const SupportConfig &c) + const SupportTreeConfig &c) : emesh{trmsh}, pts{sp}, cfg{c} {} - explicit SupportableMesh(const EigenMesh3D &em, + explicit SupportableMesh(const IndexedMesh &em, const SupportPoints &sp, - const SupportConfig &c) + const SupportTreeConfig &c) : emesh{em}, pts{sp}, cfg{c} {} }; diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp index 00f09b8121..b29ad0b9c1 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -14,7 +14,7 @@ using libnest2d::opt::StopCriteria; using libnest2d::opt::GeneticOptimizer; using libnest2d::opt::SubplexOptimizer; -template +template static Hit min_hit(const C &hits) { auto mit = std::min_element(hits.begin(), hits.end(), @@ -25,118 +25,118 @@ static Hit min_hit(const C &hits) return *mit; } -EigenMesh3D::hit_result query_hit(const SupportableMesh &msh, const Head &h) -{ - static const size_t SAMPLES = 8; +//IndexedMesh::hit_result query_hit(const SupportableMesh &msh, const Head &h) +//{ +// static const size_t SAMPLES = 8; - // Move away slightly from the touching point to avoid raycasting on the - // inner surface of the mesh. +// // Move away slightly from the touching point to avoid raycasting on the +// // inner surface of the mesh. - const double& sd = msh.cfg.safety_distance_mm; +// const double& sd = msh.cfg.safety_distance_mm; - auto& m = msh.emesh; - using HitResult = EigenMesh3D::hit_result; +// auto& m = msh.emesh; +// using HitResult = IndexedMesh::hit_result; - // Hit results - std::array hits; +// // Hit results +// std::array hits; - Vec3d s1 = h.pos, s2 = h.junction_point(); +// Vec3d s1 = h.pos, s2 = h.junction_point(); - struct Rings { - double rpin; - double rback; - Vec3d spin; - Vec3d sback; - PointRing ring; +// struct Rings { +// double rpin; +// double rback; +// Vec3d spin; +// Vec3d sback; +// PointRing ring; - Vec3d backring(size_t idx) { return ring.get(idx, sback, rback); } - Vec3d pinring(size_t idx) { return ring.get(idx, spin, rpin); } - } rings {h.r_pin_mm + sd, h.r_back_mm + sd, s1, s2, h.dir}; +// Vec3d backring(size_t idx) { return ring.get(idx, sback, rback); } +// Vec3d pinring(size_t idx) { return ring.get(idx, spin, rpin); } +// } rings {h.r_pin_mm + sd, h.r_back_mm + sd, s1, s2, h.dir}; - // We will shoot multiple rays from the head pinpoint in the direction - // of the pinhead robe (side) surface. The result will be the smallest - // hit distance. +// // We will shoot multiple rays from the head pinpoint in the direction +// // of the pinhead robe (side) surface. The result will be the smallest +// // hit distance. - auto hitfn = [&m, &rings, sd](HitResult &hit, size_t i) { - // Point on the circle on the pin sphere - Vec3d ps = rings.pinring(i); - // This is the point on the circle on the back sphere - Vec3d p = rings.backring(i); +// auto hitfn = [&m, &rings, sd](HitResult &hit, size_t i) { +// // Point on the circle on the pin sphere +// Vec3d ps = rings.pinring(i); +// // This is the point on the circle on the back sphere +// Vec3d p = rings.backring(i); - // Point ps is not on mesh but can be inside or - // outside as well. This would cause many problems - // with ray-casting. To detect the position we will - // use the ray-casting result (which has an is_inside - // predicate). +// // Point ps is not on mesh but can be inside or +// // outside as well. This would cause many problems +// // with ray-casting. To detect the position we will +// // use the ray-casting result (which has an is_inside +// // predicate). - Vec3d n = (p - ps).normalized(); - auto q = m.query_ray_hit(ps + sd * n, n); +// Vec3d n = (p - ps).normalized(); +// auto q = m.query_ray_hit(ps + sd * n, n); - if (q.is_inside()) { // the hit is inside the model - if (q.distance() > rings.rpin) { - // If we are inside the model and the hit - // distance is bigger than our pin circle - // diameter, it probably indicates that the - // support point was already inside the - // model, or there is really no space - // around the point. We will assign a zero - // hit distance to these cases which will - // enforce the function return value to be - // an invalid ray with zero hit distance. - // (see min_element at the end) - hit = HitResult(0.0); - } else { - // re-cast the ray from the outside of the - // object. The starting point has an offset - // of 2*safety_distance because the - // original ray has also had an offset - auto q2 = m.query_ray_hit(ps + (q.distance() + 2 * sd) * n, n); - hit = q2; - } - } else - hit = q; - }; +// if (q.is_inside()) { // the hit is inside the model +// if (q.distance() > rings.rpin) { +// // If we are inside the model and the hit +// // distance is bigger than our pin circle +// // diameter, it probably indicates that the +// // support point was already inside the +// // model, or there is really no space +// // around the point. We will assign a zero +// // hit distance to these cases which will +// // enforce the function return value to be +// // an invalid ray with zero hit distance. +// // (see min_element at the end) +// hit = HitResult(0.0); +// } else { +// // re-cast the ray from the outside of the +// // object. The starting point has an offset +// // of 2*safety_distance because the +// // original ray has also had an offset +// auto q2 = m.query_ray_hit(ps + (q.distance() + 2 * sd) * n, n); +// hit = q2; +// } +// } else +// hit = q; +// }; - ccr::enumerate(hits.begin(), hits.end(), hitfn); +// ccr::enumerate(hits.begin(), hits.end(), hitfn); - return min_hit(hits); -} +// return min_hit(hits); +//} -EigenMesh3D::hit_result query_hit(const SupportableMesh &msh, const Bridge &br, double safety_d) -{ +//IndexedMesh::hit_result query_hit(const SupportableMesh &msh, const Bridge &br, double safety_d) +//{ - static const size_t SAMPLES = 8; +// static const size_t SAMPLES = 8; - Vec3d dir = (br.endp - br.startp).normalized(); - PointRing ring{dir}; +// Vec3d dir = (br.endp - br.startp).normalized(); +// PointRing ring{dir}; - using Hit = EigenMesh3D::hit_result; +// using Hit = IndexedMesh::hit_result; - // Hit results - std::array hits; +// // Hit results +// std::array hits; - double sd = std::isnan(safety_d) ? msh.cfg.safety_distance_mm : safety_d; +// double sd = std::isnan(safety_d) ? msh.cfg.safety_distance_mm : safety_d; - auto hitfn = [&msh, &br, &ring, dir, sd] (Hit &hit, size_t i) { +// auto hitfn = [&msh, &br, &ring, dir, sd] (Hit &hit, size_t i) { - // Point on the circle on the pin sphere - Vec3d p = ring.get(i, br.startp, br.r + sd); +// // Point on the circle on the pin sphere +// Vec3d p = ring.get(i, br.startp, br.r + sd); - auto hr = msh.emesh.query_ray_hit(p + br.r * dir, dir); +// auto hr = msh.emesh.query_ray_hit(p + br.r * dir, dir); - if(hr.is_inside()) { - if(hr.distance() > 2 * br.r + sd) hit = Hit(0.0); - else { - // re-cast the ray from the outside of the object - hit = msh.emesh.query_ray_hit(p + (hr.distance() + 2 * sd) * dir, dir); - } - } else hit = hr; - }; +// if(hr.is_inside()) { +// if(hr.distance() > 2 * br.r + sd) hit = Hit(0.0); +// else { +// // re-cast the ray from the outside of the object +// hit = msh.emesh.query_ray_hit(p + (hr.distance() + 2 * sd) * dir, dir); +// } +// } else hit = hr; +// }; - ccr::enumerate(hits.begin(), hits.end(), hitfn); +// ccr::enumerate(hits.begin(), hits.end(), hitfn); - return min_hit(hits); -} +// return min_hit(hits); +//} SupportTreeBuildsteps::SupportTreeBuildsteps(SupportTreeBuilder & builder, const SupportableMesh &sm) @@ -281,7 +281,7 @@ bool SupportTreeBuildsteps::execute(SupportTreeBuilder & builder, return pc == ABORT; } -EigenMesh3D::hit_result SupportTreeBuildsteps::pinhead_mesh_intersect( +IndexedMesh::hit_result SupportTreeBuildsteps::pinhead_mesh_intersect( const Vec3d &s, const Vec3d &dir, double r_pin, double r_back, double width) { static const size_t SAMPLES = 8; @@ -292,7 +292,7 @@ EigenMesh3D::hit_result SupportTreeBuildsteps::pinhead_mesh_intersect( const double& sd = m_cfg.safety_distance_mm; auto& m = m_mesh; - using HitResult = EigenMesh3D::hit_result; + using HitResult = IndexedMesh::hit_result; // Hit results std::array hits; @@ -357,13 +357,13 @@ EigenMesh3D::hit_result SupportTreeBuildsteps::pinhead_mesh_intersect( return min_hit(hits); } -EigenMesh3D::hit_result SupportTreeBuildsteps::bridge_mesh_intersect( +IndexedMesh::hit_result SupportTreeBuildsteps::bridge_mesh_intersect( const Vec3d &src, const Vec3d &dir, double r, double sd) { static const size_t SAMPLES = 8; PointRing ring{dir}; - using Hit = EigenMesh3D::hit_result; + using Hit = IndexedMesh::hit_result; // Hit results std::array hits; @@ -742,7 +742,7 @@ void SupportTreeBuildsteps::filter() auto nn = spheric_to_dir(polar, azimuth).normalized(); // check available distance - EigenMesh3D::hit_result t + IndexedMesh::hit_result t = pinhead_mesh_intersect(hp, // touching point nn, // normal pin_r, @@ -781,7 +781,7 @@ void SupportTreeBuildsteps::filter() polar = std::get<0>(oresult.optimum); azimuth = std::get<1>(oresult.optimum); nn = spheric_to_dir(polar, azimuth).normalized(); - t = EigenMesh3D::hit_result(oresult.score); + t = IndexedMesh::hit_result(oresult.score); } } diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp index e8f73149e3..a985867890 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp @@ -103,9 +103,8 @@ public: } }; -EigenMesh3D::hit_result query_hit(const SupportableMesh &msh, const Bridge &br, double safety_d = std::nan("")); -EigenMesh3D::hit_result query_hit(const SupportableMesh &msh, const Head &br, double safety_d = std::nan("")); - +//IndexedMesh::hit_result query_hit(const SupportableMesh &msh, const Bridge &br, double safety_d = std::nan("")); +//IndexedMesh::hit_result query_hit(const SupportableMesh &msh, const Head &br, double safety_d = std::nan("")); inline Vec3d dirv(const Vec3d& startp, const Vec3d& endp) { return (endp - startp).normalized(); @@ -181,8 +180,8 @@ IntegerOnly pairhash(I a, I b) } class SupportTreeBuildsteps { - const SupportConfig& m_cfg; - const EigenMesh3D& m_mesh; + const SupportTreeConfig& m_cfg; + const IndexedMesh& m_mesh; const std::vector& m_support_pts; using PtIndices = std::vector; @@ -191,7 +190,7 @@ class SupportTreeBuildsteps { PtIndices m_iheads_onmodel; PtIndices m_iheadless; // headless support points - std::map m_head_to_ground_scans; + std::map m_head_to_ground_scans; // normals for support points from model faces. PointSet m_support_nmls; @@ -217,7 +216,7 @@ class SupportTreeBuildsteps { // When bridging heads to pillars... TODO: find a cleaner solution ccr::BlockingMutex m_bridge_mutex; - inline EigenMesh3D::hit_result ray_mesh_intersect(const Vec3d& s, + inline IndexedMesh::hit_result ray_mesh_intersect(const Vec3d& s, const Vec3d& dir) { return m_mesh.query_ray_hit(s, dir); @@ -234,7 +233,7 @@ class SupportTreeBuildsteps { // point was inside the model, an "invalid" hit_result will be returned // with a zero distance value instead of a NAN. This way the result can // be used safely for comparison with other distances. - EigenMesh3D::hit_result pinhead_mesh_intersect( + IndexedMesh::hit_result pinhead_mesh_intersect( const Vec3d& s, const Vec3d& dir, double r_pin, @@ -249,13 +248,13 @@ class SupportTreeBuildsteps { // point was inside the model, an "invalid" hit_result will be returned // with a zero distance value instead of a NAN. This way the result can // be used safely for comparison with other distances. - EigenMesh3D::hit_result bridge_mesh_intersect( + IndexedMesh::hit_result bridge_mesh_intersect( const Vec3d& s, const Vec3d& dir, double r, double safety_d); - EigenMesh3D::hit_result bridge_mesh_intersect( + IndexedMesh::hit_result bridge_mesh_intersect( const Vec3d& s, const Vec3d& dir, double r) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 2402207a8a..eee3bbc9fa 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -35,9 +35,9 @@ bool is_zero_elevation(const SLAPrintObjectConfig &c) } // Compile the argument for support creation from the static print config. -sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) +sla::SupportTreeConfig make_support_cfg(const SLAPrintObjectConfig& c) { - sla::SupportConfig scfg; + sla::SupportTreeConfig scfg; scfg.enabled = c.supports_enable.getBool(); scfg.head_front_radius_mm = 0.5*c.support_head_front_diameter.getFloat(); @@ -616,7 +616,7 @@ std::string SLAPrint::validate() const return L("Cannot proceed without support points! " "Add support points or disable support generation."); - sla::SupportConfig cfg = make_support_cfg(po->config()); + sla::SupportTreeConfig cfg = make_support_cfg(po->config()); double elv = cfg.object_elevation_mm; diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 9d41586ee6..f4b220c58c 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -544,7 +544,7 @@ private: bool is_zero_elevation(const SLAPrintObjectConfig &c); -sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c); +sla::SupportTreeConfig make_support_cfg(const SLAPrintObjectConfig& c); sla::PadConfig::EmbedObject builtin_pad_cfg(const SLAPrintObjectConfig& c); diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 581f50a882..ee0abe76f9 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -134,7 +134,7 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& Vec3d direction; line_from_mouse_pos(mouse_pos, trafo, camera, point, direction); - std::vector hits = m_emesh.query_ray_hits(point, direction); + std::vector hits = m_emesh.query_ray_hits(point, direction); if (hits.empty()) return false; // no intersection found @@ -184,7 +184,7 @@ std::vector MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo bool is_obscured = false; // Cast a ray in the direction of the camera and look for intersection with the mesh: - std::vector hits; + std::vector hits; // Offset the start of the ray by EPSILON to account for numerical inaccuracies. hits = m_emesh.query_ray_hits((inverse_trafo * pt + direction_to_camera_mesh * EPSILON).cast(), direction_to_camera.cast()); diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 2758577a25..60dcb30c81 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -3,7 +3,7 @@ #include "libslic3r/Point.hpp" #include "libslic3r/Geometry.hpp" -#include "libslic3r/SLA/EigenMesh3D.hpp" +#include "libslic3r/SLA/IndexedMesh.hpp" #include "admesh/stl.h" #include "slic3r/GUI/3DScene.hpp" @@ -147,7 +147,7 @@ public: Vec3f get_triangle_normal(size_t facet_idx) const; private: - sla::EigenMesh3D m_emesh; + sla::IndexedMesh m_emesh; std::vector m_normals; }; diff --git a/tests/sla_print/sla_print_tests.cpp b/tests/sla_print/sla_print_tests.cpp index 82df2c1a6f..9a9c762e3d 100644 --- a/tests/sla_print/sla_print_tests.cpp +++ b/tests/sla_print/sla_print_tests.cpp @@ -37,9 +37,9 @@ TEST_CASE("Support point generator should be deterministic if seeded", "[SLASupportGeneration], [SLAPointGen]") { TriangleMesh mesh = load_model("A_upsidedown.obj"); - sla::EigenMesh3D emesh{mesh}; + sla::IndexedMesh emesh{mesh}; - sla::SupportConfig supportcfg; + sla::SupportTreeConfig supportcfg; sla::SupportPointGenerator::Config autogencfg; autogencfg.head_diameter = float(2 * supportcfg.head_front_radius_mm); sla::SupportPointGenerator point_gen{emesh, autogencfg, [] {}, [](int) {}}; @@ -124,14 +124,14 @@ TEST_CASE("WingedPadAroundObjectIsValid", "[SLASupportGeneration]") { } TEST_CASE("ElevatedSupportGeometryIsValid", "[SLASupportGeneration]") { - sla::SupportConfig supportcfg; + sla::SupportTreeConfig supportcfg; supportcfg.object_elevation_mm = 5.; for (auto fname : SUPPORT_TEST_MODELS) test_supports(fname); } TEST_CASE("FloorSupportGeometryIsValid", "[SLASupportGeneration]") { - sla::SupportConfig supportcfg; + sla::SupportTreeConfig supportcfg; supportcfg.object_elevation_mm = 0; for (auto &fname: SUPPORT_TEST_MODELS) test_supports(fname, supportcfg); @@ -139,7 +139,7 @@ TEST_CASE("FloorSupportGeometryIsValid", "[SLASupportGeneration]") { TEST_CASE("ElevatedSupportsDoNotPierceModel", "[SLASupportGeneration]") { - sla::SupportConfig supportcfg; + sla::SupportTreeConfig supportcfg; for (auto fname : SUPPORT_TEST_MODELS) test_support_model_collision(fname, supportcfg); @@ -147,7 +147,7 @@ TEST_CASE("ElevatedSupportsDoNotPierceModel", "[SLASupportGeneration]") { TEST_CASE("FloorSupportsDoNotPierceModel", "[SLASupportGeneration]") { - sla::SupportConfig supportcfg; + sla::SupportTreeConfig supportcfg; supportcfg.object_elevation_mm = 0; for (auto fname : SUPPORT_TEST_MODELS) diff --git a/tests/sla_print/sla_raycast_tests.cpp b/tests/sla_print/sla_raycast_tests.cpp index c82e4569a8..b56909280b 100644 --- a/tests/sla_print/sla_raycast_tests.cpp +++ b/tests/sla_print/sla_raycast_tests.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include #include "sla_test_utils.hpp" @@ -65,7 +65,7 @@ TEST_CASE("Raycaster with loaded drillholes", "[sla_raycast]") cube.merge(*cube_inside); cube.require_shared_vertices(); - sla::EigenMesh3D emesh{cube}; + sla::IndexedMesh emesh{cube}; emesh.load_holes(holes); Vec3d s = center.cast(); diff --git a/tests/sla_print/sla_test_utils.cpp b/tests/sla_print/sla_test_utils.cpp index bc0cfb0cd1..c46cf675c6 100644 --- a/tests/sla_print/sla_test_utils.cpp +++ b/tests/sla_print/sla_test_utils.cpp @@ -2,13 +2,13 @@ #include "libslic3r/SLA/AGGRaster.hpp" void test_support_model_collision(const std::string &obj_filename, - const sla::SupportConfig &input_supportcfg, + const sla::SupportTreeConfig &input_supportcfg, const sla::HollowingConfig &hollowingcfg, const sla::DrainHoles &drainholes) { SupportByproducts byproducts; - sla::SupportConfig supportcfg = input_supportcfg; + sla::SupportTreeConfig supportcfg = input_supportcfg; // Set head penetration to a small negative value which should ensure that // the supports will not touch the model body. @@ -73,7 +73,7 @@ void export_failed_case(const std::vector &support_slices, const Sup } void test_supports(const std::string &obj_filename, - const sla::SupportConfig &supportcfg, + const sla::SupportTreeConfig &supportcfg, const sla::HollowingConfig &hollowingcfg, const sla::DrainHoles &drainholes, SupportByproducts &out) @@ -104,7 +104,7 @@ void test_supports(const std::string &obj_filename, // Create the special index-triangle mesh with spatial indexing which // is the input of the support point and support mesh generators - sla::EigenMesh3D emesh{mesh}; + sla::IndexedMesh emesh{mesh}; #ifdef SLIC3R_HOLE_RAYCASTER if (hollowingcfg.enabled) @@ -168,7 +168,7 @@ void test_supports(const std::string &obj_filename, } void check_support_tree_integrity(const sla::SupportTreeBuilder &stree, - const sla::SupportConfig &cfg) + const sla::SupportTreeConfig &cfg) { double gnd = stree.ground_level; double H1 = cfg.max_solo_pillar_height_mm; diff --git a/tests/sla_print/sla_test_utils.hpp b/tests/sla_print/sla_test_utils.hpp index 3652b1f81c..fdd883ed84 100644 --- a/tests/sla_print/sla_test_utils.hpp +++ b/tests/sla_print/sla_test_utils.hpp @@ -67,16 +67,16 @@ struct SupportByproducts const constexpr float CLOSING_RADIUS = 0.005f; void check_support_tree_integrity(const sla::SupportTreeBuilder &stree, - const sla::SupportConfig &cfg); + const sla::SupportTreeConfig &cfg); void test_supports(const std::string &obj_filename, - const sla::SupportConfig &supportcfg, + const sla::SupportTreeConfig &supportcfg, const sla::HollowingConfig &hollowingcfg, const sla::DrainHoles &drainholes, SupportByproducts &out); inline void test_supports(const std::string &obj_filename, - const sla::SupportConfig &supportcfg, + const sla::SupportTreeConfig &supportcfg, SupportByproducts &out) { sla::HollowingConfig hcfg; @@ -85,7 +85,7 @@ inline void test_supports(const std::string &obj_filename, } inline void test_supports(const std::string &obj_filename, - const sla::SupportConfig &supportcfg = {}) + const sla::SupportTreeConfig &supportcfg = {}) { SupportByproducts byproducts; test_supports(obj_filename, supportcfg, byproducts); @@ -97,13 +97,13 @@ void export_failed_case(const std::vector &support_slices, void test_support_model_collision( const std::string &obj_filename, - const sla::SupportConfig &input_supportcfg, + const sla::SupportTreeConfig &input_supportcfg, const sla::HollowingConfig &hollowingcfg, const sla::DrainHoles &drainholes); inline void test_support_model_collision( const std::string &obj_filename, - const sla::SupportConfig &input_supportcfg = {}) + const sla::SupportTreeConfig &input_supportcfg = {}) { sla::HollowingConfig hcfg; hcfg.enabled = false; diff --git a/tests/sla_print/sla_treebuilder_tests.cpp b/tests/sla_print/sla_treebuilder_tests.cpp index 05aca963ea..91c2ea6f8e 100644 --- a/tests/sla_print/sla_treebuilder_tests.cpp +++ b/tests/sla_print/sla_treebuilder_tests.cpp @@ -1,99 +1,99 @@ -#include -#include +//#include +//#include -#include "libslic3r/TriangleMesh.hpp" -#include "libslic3r/SLA/SupportTreeBuildsteps.hpp" -#include "libslic3r/SLA/SupportTreeMesher.hpp" +//#include "libslic3r/TriangleMesh.hpp" +//#include "libslic3r/SLA/SupportTreeBuildsteps.hpp" +//#include "libslic3r/SLA/SupportTreeMesher.hpp" -TEST_CASE("Test bridge_mesh_intersect on a cube's wall", "[SLABridgeMeshInters]") { - using namespace Slic3r; +//TEST_CASE("Test bridge_mesh_intersect on a cube's wall", "[SLABridgeMeshInters]") { +// using namespace Slic3r; - TriangleMesh cube = make_cube(10., 10., 10.); +// TriangleMesh cube = make_cube(10., 10., 10.); - sla::SupportConfig cfg = {}; // use default config - sla::SupportPoints pts = {{10.f, 5.f, 5.f, float(cfg.head_front_radius_mm), false}}; - sla::SupportableMesh sm{cube, pts, cfg}; +// sla::SupportConfig cfg = {}; // use default config +// sla::SupportPoints pts = {{10.f, 5.f, 5.f, float(cfg.head_front_radius_mm), false}}; +// sla::SupportableMesh sm{cube, pts, cfg}; - size_t steps = 45; - SECTION("Bridge is straight horizontal and pointing away from the cube") { +// size_t steps = 45; +// SECTION("Bridge is straight horizontal and pointing away from the cube") { - sla::Bridge bridge(pts[0].pos.cast(), Vec3d{15., 5., 5.}, - pts[0].head_front_radius); +// sla::Bridge bridge(pts[0].pos.cast(), Vec3d{15., 5., 5.}, +// pts[0].head_front_radius); - auto hit = sla::query_hit(sm, bridge); +// auto hit = sla::query_hit(sm, bridge); - REQUIRE(std::isinf(hit.distance())); +// REQUIRE(std::isinf(hit.distance())); - cube.merge(sla::to_triangle_mesh(get_mesh(bridge, steps))); - cube.require_shared_vertices(); - cube.WriteOBJFile("cube1.obj"); - } +// cube.merge(sla::to_triangle_mesh(get_mesh(bridge, steps))); +// cube.require_shared_vertices(); +// cube.WriteOBJFile("cube1.obj"); +// } - SECTION("Bridge is tilted down in 45 degrees, pointing away from the cube") { - sla::Bridge bridge(pts[0].pos.cast(), Vec3d{15., 5., 0.}, - pts[0].head_front_radius); +// SECTION("Bridge is tilted down in 45 degrees, pointing away from the cube") { +// sla::Bridge bridge(pts[0].pos.cast(), Vec3d{15., 5., 0.}, +// pts[0].head_front_radius); - auto hit = sla::query_hit(sm, bridge); +// auto hit = sla::query_hit(sm, bridge); - REQUIRE(std::isinf(hit.distance())); +// REQUIRE(std::isinf(hit.distance())); - cube.merge(sla::to_triangle_mesh(get_mesh(bridge, steps))); - cube.require_shared_vertices(); - cube.WriteOBJFile("cube2.obj"); - } -} +// cube.merge(sla::to_triangle_mesh(get_mesh(bridge, steps))); +// cube.require_shared_vertices(); +// cube.WriteOBJFile("cube2.obj"); +// } +//} -TEST_CASE("Test bridge_mesh_intersect on a sphere", "[SLABridgeMeshInters]") { - using namespace Slic3r; +//TEST_CASE("Test bridge_mesh_intersect on a sphere", "[SLABridgeMeshInters]") { +// using namespace Slic3r; - TriangleMesh sphere = make_sphere(1.); +// TriangleMesh sphere = make_sphere(1.); - sla::SupportConfig cfg = {}; // use default config - cfg.head_back_radius_mm = cfg.head_front_radius_mm; - sla::SupportPoints pts = {{1.f, 0.f, 0.f, float(cfg.head_front_radius_mm), false}}; - sla::SupportableMesh sm{sphere, pts, cfg}; +// sla::SupportConfig cfg = {}; // use default config +// cfg.head_back_radius_mm = cfg.head_front_radius_mm; +// sla::SupportPoints pts = {{1.f, 0.f, 0.f, float(cfg.head_front_radius_mm), false}}; +// sla::SupportableMesh sm{sphere, pts, cfg}; - size_t steps = 45; - SECTION("Bridge is straight horizontal and pointing away from the sphere") { +// size_t steps = 45; +// SECTION("Bridge is straight horizontal and pointing away from the sphere") { - sla::Bridge bridge(pts[0].pos.cast(), Vec3d{2., 0., 0.}, - pts[0].head_front_radius); +// sla::Bridge bridge(pts[0].pos.cast(), Vec3d{2., 0., 0.}, +// pts[0].head_front_radius); - auto hit = sla::query_hit(sm, bridge); +// auto hit = sla::query_hit(sm, bridge); - sphere.merge(sla::to_triangle_mesh(get_mesh(bridge, steps))); - sphere.require_shared_vertices(); - sphere.WriteOBJFile("sphere1.obj"); +// sphere.merge(sla::to_triangle_mesh(get_mesh(bridge, steps))); +// sphere.require_shared_vertices(); +// sphere.WriteOBJFile("sphere1.obj"); - REQUIRE(std::isinf(hit.distance())); - } +// REQUIRE(std::isinf(hit.distance())); +// } - SECTION("Bridge is tilted down 45 deg and pointing away from the sphere") { +// SECTION("Bridge is tilted down 45 deg and pointing away from the sphere") { - sla::Bridge bridge(pts[0].pos.cast(), Vec3d{2., 0., -2.}, - pts[0].head_front_radius); +// sla::Bridge bridge(pts[0].pos.cast(), Vec3d{2., 0., -2.}, +// pts[0].head_front_radius); - auto hit = sla::query_hit(sm, bridge); +// auto hit = sla::query_hit(sm, bridge); - sphere.merge(sla::to_triangle_mesh(get_mesh(bridge, steps))); - sphere.require_shared_vertices(); - sphere.WriteOBJFile("sphere2.obj"); +// sphere.merge(sla::to_triangle_mesh(get_mesh(bridge, steps))); +// sphere.require_shared_vertices(); +// sphere.WriteOBJFile("sphere2.obj"); - REQUIRE(std::isinf(hit.distance())); - } +// REQUIRE(std::isinf(hit.distance())); +// } - SECTION("Bridge is tilted down 90 deg and pointing away from the sphere") { +// SECTION("Bridge is tilted down 90 deg and pointing away from the sphere") { - sla::Bridge bridge(pts[0].pos.cast(), Vec3d{1., 0., -2.}, - pts[0].head_front_radius); +// sla::Bridge bridge(pts[0].pos.cast(), Vec3d{1., 0., -2.}, +// pts[0].head_front_radius); - auto hit = sla::query_hit(sm, bridge); +// auto hit = sla::query_hit(sm, bridge); - sphere.merge(sla::to_triangle_mesh(get_mesh(bridge, steps))); - sphere.require_shared_vertices(); - sphere.WriteOBJFile("sphere3.obj"); +// sphere.merge(sla::to_triangle_mesh(get_mesh(bridge, steps))); +// sphere.require_shared_vertices(); +// sphere.WriteOBJFile("sphere3.obj"); - REQUIRE(std::isinf(hit.distance())); - } -} +// REQUIRE(std::isinf(hit.distance())); +// } +//} From a68564e2d01994b13c1d675ec3d08ec0434d1b84 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 26 Jun 2020 15:12:37 +0200 Subject: [PATCH 245/503] Include test name with output obj files for sla_print_tests --- tests/sla_print/sla_test_utils.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/sla_print/sla_test_utils.cpp b/tests/sla_print/sla_test_utils.cpp index c46cf675c6..8978281d8c 100644 --- a/tests/sla_print/sla_test_utils.cpp +++ b/tests/sla_print/sla_test_utils.cpp @@ -69,7 +69,8 @@ void export_failed_case(const std::vector &support_slices, const Sup m.merge(byproducts.input_mesh); m.repair(); m.require_shared_vertices(); - m.WriteOBJFile(byproducts.obj_fname.c_str()); + m.WriteOBJFile((Catch::getResultCapture().getCurrentTestName() + "_" + + byproducts.obj_fname).c_str()); } void test_supports(const std::string &obj_filename, From 7c655b5d7e1732448b8a37fd335b8e9535372a38 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 29 Jun 2020 20:09:37 +0200 Subject: [PATCH 246/503] Fix junction made below ground level. --- src/libslic3r/SLA/SupportTreeBuildsteps.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp index b29ad0b9c1..2116568e8c 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -1333,9 +1333,8 @@ void SupportTreeBuildsteps::interconnect_pillars() if (distance(pillarsp, s) < t) m_builder.add_bridge(pillarsp, s, pillar().r); - if (pillar().endpoint()(Z) > m_builder.ground_level) - m_builder.add_junction(pillar().endpoint(), - pillar().r); + if (pillar().endpoint()(Z) > m_builder.ground_level + pillar().r) + m_builder.add_junction(pillar().endpoint(), pillar().r); newpills.emplace_back(pp.id); m_builder.increment_links(pillar()); From 8cb115a03543f0d09c9e113ab8e42cf9eb39a694 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 8 Jul 2020 11:31:01 +0200 Subject: [PATCH 247/503] Add possible manipulation of small support diameter. --- src/libslic3r/PrintConfig.cpp | 21 +++- src/libslic3r/PrintConfig.hpp | 5 + src/libslic3r/SLA/SupportTree.hpp | 2 + src/libslic3r/SLA/SupportTreeBuildsteps.cpp | 131 ++------------------ src/libslic3r/SLA/SupportTreeBuildsteps.hpp | 14 ++- src/libslic3r/SLAPrint.cpp | 6 +- src/slic3r/GUI/ConfigManipulation.cpp | 1 + src/slic3r/GUI/Preset.cpp | 1 + src/slic3r/GUI/Tab.cpp | 1 + 9 files changed, 57 insertions(+), 125 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index a25292298c..5c1ce4b7f8 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2715,7 +2715,7 @@ void PrintConfigDef::init_sla_params() def->set_default_value(new ConfigOptionBool(true)); def = this->add("support_head_front_diameter", coFloat); - def->label = L("Support head front diameter"); + def->label = L("Pinhead front diameter"); def->category = L("Supports"); def->tooltip = L("Diameter of the pointing side of the head"); def->sidetext = L("mm"); @@ -2724,7 +2724,7 @@ void PrintConfigDef::init_sla_params() def->set_default_value(new ConfigOptionFloat(0.4)); def = this->add("support_head_penetration", coFloat); - def->label = L("Support head penetration"); + def->label = L("Head penetration"); def->category = L("Supports"); def->tooltip = L("How much the pinhead has to penetrate the model surface"); def->sidetext = L("mm"); @@ -2733,7 +2733,7 @@ void PrintConfigDef::init_sla_params() def->set_default_value(new ConfigOptionFloat(0.2)); def = this->add("support_head_width", coFloat); - def->label = L("Support head width"); + def->label = L("Pinhead width"); def->category = L("Supports"); def->tooltip = L("Width from the back sphere center to the front sphere center"); def->sidetext = L("mm"); @@ -2743,7 +2743,7 @@ void PrintConfigDef::init_sla_params() def->set_default_value(new ConfigOptionFloat(1.0)); def = this->add("support_pillar_diameter", coFloat); - def->label = L("Support pillar diameter"); + def->label = L("Pillar diameter"); def->category = L("Supports"); def->tooltip = L("Diameter in mm of the support pillars"); def->sidetext = L("mm"); @@ -2751,6 +2751,17 @@ void PrintConfigDef::init_sla_params() def->max = 15; def->mode = comSimple; def->set_default_value(new ConfigOptionFloat(1.0)); + + def = this->add("support_small_pillar_diameter_percent", coPercent); + def->label = L("Small pillar diameter percent"); + def->category = L("Supports"); + def->tooltip = L("The percentage of smaller pillars compared to the normal pillar diameter " + "which are used in problematic areas where a normal pilla cannot fit."); + def->sidetext = L("%"); + def->min = 1; + def->max = 100; + def->mode = comExpert; + def->set_default_value(new ConfigOptionPercent(50)); def = this->add("support_max_bridges_on_pillar", coInt); def->label = L("Max bridges on a pillar"); @@ -2763,7 +2774,7 @@ void PrintConfigDef::init_sla_params() def->set_default_value(new ConfigOptionInt(3)); def = this->add("support_pillar_connection_mode", coEnum); - def->label = L("Support pillar connection mode"); + def->label = L("Pillar connection mode"); def->tooltip = L("Controls the bridge type between two neighboring pillars." " Can be zig-zag, cross (double zig-zag) or dynamic which" " will automatically switch between the first two depending" diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index f28ef2a228..0213a6d6bd 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1018,6 +1018,10 @@ public: // Radius in mm of the support pillars. ConfigOptionFloat support_pillar_diameter /*= 0.8*/; + + // The percentage of smaller pillars compared to the normal pillar diameter + // which are used in problematic areas where a normal pilla cannot fit. + ConfigOptionPercent support_small_pillar_diameter_percent; // How much bridge (supporting another pinhead) can be placed on a pillar. ConfigOptionInt support_max_bridges_on_pillar; @@ -1142,6 +1146,7 @@ protected: OPT_PTR(support_head_penetration); OPT_PTR(support_head_width); OPT_PTR(support_pillar_diameter); + OPT_PTR(support_small_pillar_diameter_percent); OPT_PTR(support_max_bridges_on_pillar); OPT_PTR(support_pillar_connection_mode); OPT_PTR(support_buildplate_only); diff --git a/src/libslic3r/SLA/SupportTree.hpp b/src/libslic3r/SLA/SupportTree.hpp index 7d54b76a40..4be90161d5 100644 --- a/src/libslic3r/SLA/SupportTree.hpp +++ b/src/libslic3r/SLA/SupportTree.hpp @@ -44,6 +44,8 @@ struct SupportTreeConfig // Radius of the back side of the 3d arrow. double head_back_radius_mm = 0.5; + double head_fallback_radius_mm = 0.25; + // Width in mm from the back sphere center to the front sphere center. double head_width_mm = 1.0; diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp index 2116568e8c..7f6c034ddd 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -25,119 +25,6 @@ static Hit min_hit(const C &hits) return *mit; } -//IndexedMesh::hit_result query_hit(const SupportableMesh &msh, const Head &h) -//{ -// static const size_t SAMPLES = 8; - -// // Move away slightly from the touching point to avoid raycasting on the -// // inner surface of the mesh. - -// const double& sd = msh.cfg.safety_distance_mm; - -// auto& m = msh.emesh; -// using HitResult = IndexedMesh::hit_result; - -// // Hit results -// std::array hits; - -// Vec3d s1 = h.pos, s2 = h.junction_point(); - -// struct Rings { -// double rpin; -// double rback; -// Vec3d spin; -// Vec3d sback; -// PointRing ring; - -// Vec3d backring(size_t idx) { return ring.get(idx, sback, rback); } -// Vec3d pinring(size_t idx) { return ring.get(idx, spin, rpin); } -// } rings {h.r_pin_mm + sd, h.r_back_mm + sd, s1, s2, h.dir}; - -// // We will shoot multiple rays from the head pinpoint in the direction -// // of the pinhead robe (side) surface. The result will be the smallest -// // hit distance. - -// auto hitfn = [&m, &rings, sd](HitResult &hit, size_t i) { -// // Point on the circle on the pin sphere -// Vec3d ps = rings.pinring(i); -// // This is the point on the circle on the back sphere -// Vec3d p = rings.backring(i); - -// // Point ps is not on mesh but can be inside or -// // outside as well. This would cause many problems -// // with ray-casting. To detect the position we will -// // use the ray-casting result (which has an is_inside -// // predicate). - -// Vec3d n = (p - ps).normalized(); -// auto q = m.query_ray_hit(ps + sd * n, n); - -// if (q.is_inside()) { // the hit is inside the model -// if (q.distance() > rings.rpin) { -// // If we are inside the model and the hit -// // distance is bigger than our pin circle -// // diameter, it probably indicates that the -// // support point was already inside the -// // model, or there is really no space -// // around the point. We will assign a zero -// // hit distance to these cases which will -// // enforce the function return value to be -// // an invalid ray with zero hit distance. -// // (see min_element at the end) -// hit = HitResult(0.0); -// } else { -// // re-cast the ray from the outside of the -// // object. The starting point has an offset -// // of 2*safety_distance because the -// // original ray has also had an offset -// auto q2 = m.query_ray_hit(ps + (q.distance() + 2 * sd) * n, n); -// hit = q2; -// } -// } else -// hit = q; -// }; - -// ccr::enumerate(hits.begin(), hits.end(), hitfn); - -// return min_hit(hits); -//} - -//IndexedMesh::hit_result query_hit(const SupportableMesh &msh, const Bridge &br, double safety_d) -//{ - -// static const size_t SAMPLES = 8; - -// Vec3d dir = (br.endp - br.startp).normalized(); -// PointRing ring{dir}; - -// using Hit = IndexedMesh::hit_result; - -// // Hit results -// std::array hits; - -// double sd = std::isnan(safety_d) ? msh.cfg.safety_distance_mm : safety_d; - -// auto hitfn = [&msh, &br, &ring, dir, sd] (Hit &hit, size_t i) { - -// // Point on the circle on the pin sphere -// Vec3d p = ring.get(i, br.startp, br.r + sd); - -// auto hr = msh.emesh.query_ray_hit(p + br.r * dir, dir); - -// if(hr.is_inside()) { -// if(hr.distance() > 2 * br.r + sd) hit = Hit(0.0); -// else { -// // re-cast the ray from the outside of the object -// hit = msh.emesh.query_ray_hit(p + (hr.distance() + 2 * sd) * dir, dir); -// } -// } else hit = hr; -// }; - -// ccr::enumerate(hits.begin(), hits.end(), hitfn); - -// return min_hit(hits); -//} - SupportTreeBuildsteps::SupportTreeBuildsteps(SupportTreeBuilder & builder, const SupportableMesh &sm) : m_cfg(sm.cfg) @@ -282,15 +169,18 @@ bool SupportTreeBuildsteps::execute(SupportTreeBuilder & builder, } IndexedMesh::hit_result SupportTreeBuildsteps::pinhead_mesh_intersect( - const Vec3d &s, const Vec3d &dir, double r_pin, double r_back, double width) + const Vec3d &s, + const Vec3d &dir, + double r_pin, + double r_back, + double width, + double sd) { static const size_t SAMPLES = 8; // Move away slightly from the touching point to avoid raycasting on the // inner surface of the mesh. - const double& sd = m_cfg.safety_distance_mm; - auto& m = m_mesh; using HitResult = IndexedMesh::hit_result; @@ -836,12 +726,17 @@ void SupportTreeBuildsteps::add_pinheads() // First we need to determine the available space for a mini pinhead. // The goal is the move away from the model a little bit to make the // contact point small as possible and avoid pearcing the model body. - double pin_space = std::min(2 * R, bridge_mesh_distance(sph, n, R, 0.)); + double back_r = m_cfg.head_fallback_radius_mm; + double max_w = 2 * R; + double pin_space = std::min(max_w, + pinhead_mesh_intersect(sph, n, R, back_r, + max_w, 0.) + .distance()); if (pin_space <= 0) continue; m_iheads.emplace_back(i); - m_builder.add_head(i, R, R, pin_space, + m_builder.add_head(i, back_r, R, pin_space, m_cfg.head_penetration_mm, n, sph); } } diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp index a985867890..d19194a87d 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp @@ -238,7 +238,19 @@ class SupportTreeBuildsteps { const Vec3d& dir, double r_pin, double r_back, - double width); + double width, + double safety_d); + + IndexedMesh::hit_result pinhead_mesh_intersect( + const Vec3d& s, + const Vec3d& dir, + double r_pin, + double r_back, + double width) + { + return pinhead_mesh_intersect(s, dir, r_pin, r_back, width, + m_cfg.safety_distance_mm); + } // Checking bridge (pillar and stick as well) intersection with the model. // If the function is used for headless sticks, the ins_check parameter diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index eee3bbc9fa..4395bea461 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -41,7 +41,10 @@ sla::SupportTreeConfig make_support_cfg(const SLAPrintObjectConfig& c) scfg.enabled = c.supports_enable.getBool(); scfg.head_front_radius_mm = 0.5*c.support_head_front_diameter.getFloat(); - scfg.head_back_radius_mm = 0.5*c.support_pillar_diameter.getFloat(); + double pillar_r = 0.5 * c.support_pillar_diameter.getFloat(); + scfg.head_back_radius_mm = pillar_r; + scfg.head_fallback_radius_mm = + 0.01 * c.support_small_pillar_diameter_percent.getFloat() * pillar_r; scfg.head_penetration_mm = c.support_head_penetration.getFloat(); scfg.head_width_mm = c.support_head_width.getFloat(); scfg.object_elevation_mm = is_zero_elevation(c) ? @@ -925,6 +928,7 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector& Preset::sla_print_options() "support_head_penetration", "support_head_width", "support_pillar_diameter", + "support_small_pillar_diameter_percent", "support_max_bridges_on_pillar", "support_pillar_connection_mode", "support_buildplate_only", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 84bc5a5726..86b483a8df 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3919,6 +3919,7 @@ void TabSLAPrint::build() optgroup = page->new_optgroup(L("Support pillar")); optgroup->append_single_option_line("support_pillar_diameter"); + optgroup->append_single_option_line("support_small_pillar_diameter_percent"); optgroup->append_single_option_line("support_max_bridges_on_pillar"); optgroup->append_single_option_line("support_pillar_connection_mode"); From 927b81ea9710266e2effb050ca27cb8762239870 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 16 Jul 2020 11:49:30 +0200 Subject: [PATCH 248/503] Working small-to-normal support merging Fixed fatal bug with anchors for mini supports Make the optimization cleaner in support generatior Much better widening behaviour Add an optimizer interface and the NLopt implementation into libslic3r New optimizer based only on nlopt C interfase Fix build and tests --- src/libslic3r/CMakeLists.txt | 1 + src/libslic3r/Optimizer.hpp | 369 ++++++++++ src/libslic3r/SLA/IndexedMesh.hpp | 2 +- src/libslic3r/SLA/SupportTreeBuilder.cpp | 5 + src/libslic3r/SLA/SupportTreeBuilder.hpp | 45 +- src/libslic3r/SLA/SupportTreeBuildsteps.cpp | 758 ++++++++++---------- src/libslic3r/SLA/SupportTreeBuildsteps.hpp | 17 +- src/libslic3r/SLA/SupportTreeMesher.hpp | 18 + tests/sla_print/sla_print_tests.cpp | 11 + 9 files changed, 831 insertions(+), 395 deletions(-) create mode 100644 src/libslic3r/Optimizer.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 91da5df5d6..58b74402e1 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -203,6 +203,7 @@ add_library(libslic3r STATIC SimplifyMeshImpl.hpp SimplifyMesh.cpp MarchingSquares.hpp + Optimizer.hpp ${OpenVDBUtils_SOURCES} SLA/Pad.hpp SLA/Pad.cpp diff --git a/src/libslic3r/Optimizer.hpp b/src/libslic3r/Optimizer.hpp new file mode 100644 index 0000000000..dc70abe332 --- /dev/null +++ b/src/libslic3r/Optimizer.hpp @@ -0,0 +1,369 @@ +#ifndef NLOPTOPTIMIZER_HPP +#define NLOPTOPTIMIZER_HPP + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4244) +#pragma warning(disable: 4267) +#endif +#include +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include +#include +#include +#include +#include +#include +#include + +namespace Slic3r { namespace opt { + +// A type to hold the complete result of the optimization. +template struct Result { + int resultcode; + std::array optimum; + double score; +}; + +// An interval of possible input values for optimization +class Bound { + double m_min, m_max; + +public: + Bound(double min = std::numeric_limits::min(), + double max = std::numeric_limits::max()) + : m_min(min), m_max(max) + {} + + double min() const noexcept { return m_min; } + double max() const noexcept { return m_max; } +}; + +// Helper types for optimization function input and bounds +template using Input = std::array; +template using Bounds = std::array; + +// A type for specifying the stop criteria. Setter methods can be concatenated +class StopCriteria { + + // If the absolute value difference between two scores. + double m_abs_score_diff = std::nan(""); + + // If the relative value difference between two scores. + double m_rel_score_diff = std::nan(""); + + // Stop if this value or better is found. + double m_stop_score = std::nan(""); + + // A predicate that if evaluates to true, the optimization should terminate + // and the best result found prior to termination should be returned. + std::function m_stop_condition = [] { return false; }; + + // The max allowed number of iterations. + unsigned m_max_iterations = 0; + +public: + + StopCriteria & abs_score_diff(double val) + { + m_abs_score_diff = val; return *this; + } + + double abs_score_diff() const { return m_abs_score_diff; } + + StopCriteria & rel_score_diff(double val) + { + m_rel_score_diff = val; return *this; + } + + double rel_score_diff() const { return m_rel_score_diff; } + + StopCriteria & stop_score(double val) + { + m_stop_score = val; return *this; + } + + double stop_score() const { return m_stop_score; } + + StopCriteria & max_iterations(double val) + { + m_max_iterations = val; return *this; + } + + double max_iterations() const { return m_max_iterations; } + + template StopCriteria & stop_condition(Fn &&cond) + { + m_stop_condition = cond; return *this; + } + + bool stop_condition() { return m_stop_condition(); } +}; + +// Helper to be used in static_assert. +template struct always_false { enum { value = false }; }; + +// Basic interface to optimizer object +template class Optimizer { +public: + + Optimizer(const StopCriteria &) + { + static_assert(always_false::value, + "Optimizer unimplemented for given method!"); + } + + Optimizer &to_min() { return *this; } + Optimizer &to_max() { return *this; } + Optimizer &set_criteria(const StopCriteria &) { return *this; } + StopCriteria get_criteria() const { return {}; }; + + template + Result optimize(Func&& func, + const Input &initvals, + const Bounds& bounds) { return {}; } + + // optional for randomized methods: + void seed(long /*s*/) {} +}; + +namespace detail { + +// Helper types for NLopt algorithm selection in template contexts +template struct NLoptAlg {}; + +// NLopt can combine multiple algorithms if one is global an other is a local +// method. This is how template specializations can be informed about this fact. +template +struct NLoptAlgComb {}; + +template struct IsNLoptAlg { + static const constexpr bool value = false; +}; + +template struct IsNLoptAlg> { + static const constexpr bool value = true; +}; + +template +struct IsNLoptAlg> { + static const constexpr bool value = true; +}; + +template +using NLoptOnly = std::enable_if_t::value, T>; + +// Convert any collection to tuple. This is useful for object functions taking +// an argument list of doubles. Make things cleaner on the call site of +// optimize(). +template struct to_tuple_ { + static auto call(const C &c) + { + return std::tuple_cat(std::tuple(c[N-I]), + to_tuple_::call(c)); + } +}; + +template struct to_tuple_<0, N, T, C> { + static auto call(const C &c) { return std::tuple<>(); } +}; + +// C array to tuple +template auto carray_tuple(const T *v) +{ + return to_tuple_::call(v); +} + +// Helper to convert C style array to std::array +template auto to_arr(const T (&a) [N]) +{ + std::array r; + std::copy(std::begin(a), std::end(a), std::begin(r)); + return r; +} + +enum class OptDir { MIN, MAX }; // Where to optimize + +struct NLopt { // Helper RAII class for nlopt_opt + nlopt_opt ptr = nullptr; + + template explicit NLopt(A&&...a) + { + ptr = nlopt_create(std::forward(a)...); + } + + NLopt(const NLopt&) = delete; + NLopt(NLopt&&) = delete; + NLopt& operator=(const NLopt&) = delete; + NLopt& operator=(NLopt&&) = delete; + + ~NLopt() { nlopt_destroy(ptr); } +}; + +template class NLoptOpt {}; + +// Optimizers based on NLopt. +template class NLoptOpt> { +protected: + StopCriteria m_stopcr; + OptDir m_dir; + + template using TOptData = + std::tuple*, NLoptOpt*, nlopt_opt>; + + template + static double optfunc(unsigned n, const double *params, + double *gradient, + void *data) + { + assert(n >= N); + + auto tdata = static_cast*>(data); + + if (std::get<1>(*tdata)->m_stopcr.stop_condition()) + nlopt_force_stop(std::get<2>(*tdata)); + + auto fnptr = std::get<0>(*tdata); + auto funval = carray_tuple(params); + + return std::apply(*fnptr, funval); + } + + template + void set_up(NLopt &nl, const Bounds& bounds) + { + std::array lb, ub; + + for (size_t i = 0; i < N; ++i) { + lb[i] = bounds[i].min(); + ub[i] = bounds[i].max(); + } + + nlopt_set_lower_bounds(nl.ptr, lb.data()); + nlopt_set_upper_bounds(nl.ptr, ub.data()); + + double abs_diff = m_stopcr.abs_score_diff(); + double rel_diff = m_stopcr.rel_score_diff(); + double stopval = m_stopcr.stop_score(); + if(!std::isnan(abs_diff)) nlopt_set_ftol_abs(nl.ptr, abs_diff); + if(!std::isnan(rel_diff)) nlopt_set_ftol_rel(nl.ptr, rel_diff); + if(!std::isnan(stopval)) nlopt_set_stopval(nl.ptr, stopval); + + if(this->m_stopcr.max_iterations() > 0) + nlopt_set_maxeval(nl.ptr, this->m_stopcr.max_iterations()); + } + + template + Result optimize(NLopt &nl, Fn &&fn, const Input &initvals) + { + Result r; + + TOptData data = std::make_tuple(&fn, this, nl.ptr); + + switch(m_dir) { + case OptDir::MIN: + nlopt_set_min_objective(nl.ptr, optfunc, &data); break; + case OptDir::MAX: + nlopt_set_max_objective(nl.ptr, optfunc, &data); break; + } + + r.optimum = initvals; + r.resultcode = nlopt_optimize(nl.ptr, r.optimum.data(), &r.score); + + return r; + } + +public: + + template + Result optimize(Func&& func, + const Input &initvals, + const Bounds& bounds) + { + NLopt nl{alg, N}; + set_up(nl, bounds); + + return optimize(nl, std::forward(func), initvals); + } + + explicit NLoptOpt(StopCriteria stopcr = {}) : m_stopcr(stopcr) {} + + void set_criteria(const StopCriteria &cr) { m_stopcr = cr; } + const StopCriteria &get_criteria() const noexcept { return m_stopcr; } + void set_dir(OptDir dir) noexcept { m_dir = dir; } + + void seed(long s) { nlopt_srand(s); } +}; + +template +class NLoptOpt>: public NLoptOpt> +{ + using Base = NLoptOpt>; +public: + + template + Result optimize(Fn&& f, + const Input &initvals, + const Bounds& bounds) + { + NLopt nl_glob{glob, N}, nl_loc{loc, N}; + + Base::set_up(nl_glob, bounds); + Base::set_up(nl_loc, bounds); + nlopt_set_local_optimizer(nl_glob.ptr, nl_loc.ptr); + + return Base::optimize(nl_glob, std::forward(f), initvals); + } + + explicit NLoptOpt(StopCriteria stopcr = {}) : Base{stopcr} {} +}; + +} // namespace detail; + +// Optimizers based on NLopt. +template class Optimizer> { + detail::NLoptOpt m_opt; + +public: + + Optimizer& to_max() { m_opt.set_dir(detail::OptDir::MAX); return *this; } + Optimizer& to_min() { m_opt.set_dir(detail::OptDir::MIN); return *this; } + + template + Result optimize(Func&& func, + const Input &initvals, + const Bounds& bounds) + { + return m_opt.optimize(std::forward(func), initvals, bounds); + } + + explicit Optimizer(StopCriteria stopcr = {}) : m_opt(stopcr) {} + + Optimizer &set_criteria(const StopCriteria &cr) + { + m_opt.set_criteria(cr); return *this; + } + + const StopCriteria &get_criteria() const { return m_opt.get_criteria(); } + + void seed(long s) { m_opt.seed(s); } +}; + +template Bounds bounds(const Bound (&b) [N]) { return detail::to_arr(b); } +template Input initvals(const double (&a) [N]) { return detail::to_arr(a); } + +// Predefinded NLopt algorithms that are used in the codebase +using AlgNLoptGenetic = detail::NLoptAlgComb; +using AlgNLoptSubplex = detail::NLoptAlg; +using AlgNLoptSimplex = detail::NLoptAlg; + +// Helper defs for pre-crafted global and local optimizers that work well. +using DefaultGlobalOptimizer = Optimizer; +using DefaultLocalOptimizer = Optimizer; + +}} // namespace Slic3r::opt + +#endif // NLOPTOPTIMIZER_HPP diff --git a/src/libslic3r/SLA/IndexedMesh.hpp b/src/libslic3r/SLA/IndexedMesh.hpp index b0970608e2..a72492b344 100644 --- a/src/libslic3r/SLA/IndexedMesh.hpp +++ b/src/libslic3r/SLA/IndexedMesh.hpp @@ -87,7 +87,7 @@ public: inline Vec3d position() const { return m_source + m_dir * m_t; } inline int face() const { return m_face_id; } inline bool is_valid() const { return m_mesh != nullptr; } - inline bool is_hit() const { return !std::isinf(m_t); } + inline bool is_hit() const { return m_face_id >= 0 && !std::isinf(m_t); } inline const Vec3d& normal() const { assert(is_valid()); diff --git a/src/libslic3r/SLA/SupportTreeBuilder.cpp b/src/libslic3r/SLA/SupportTreeBuilder.cpp index 9590936231..daa01ef24d 100644 --- a/src/libslic3r/SLA/SupportTreeBuilder.cpp +++ b/src/libslic3r/SLA/SupportTreeBuilder.cpp @@ -156,6 +156,11 @@ const TriangleMesh &SupportTreeBuilder::merged_mesh(size_t steps) const merged.merge(get_mesh(bs, steps)); } + for (auto &bs : m_diffbridges) { + if (ctl().stopcondition()) break; + merged.merge(get_mesh(bs, steps)); + } + for (auto &anch : m_anchors) { if (ctl().stopcondition()) break; merged.merge(get_mesh(anch, steps)); diff --git a/src/libslic3r/SLA/SupportTreeBuilder.hpp b/src/libslic3r/SLA/SupportTreeBuilder.hpp index aa8a4ea83b..f29263ca3f 100644 --- a/src/libslic3r/SLA/SupportTreeBuilder.hpp +++ b/src/libslic3r/SLA/SupportTreeBuilder.hpp @@ -177,6 +177,14 @@ struct Bridge: public SupportTreeNode { Vec3d get_dir() const { return (endp - startp).normalized(); } }; +struct DiffBridge: public Bridge { + double end_r; + + DiffBridge(const Vec3d &p_s, const Vec3d &p_e, double r_s, double r_e) + : Bridge{p_s, p_e, r_s}, end_r{r_e} + {} +}; + // A wrapper struct around the pad struct Pad { TriangleMesh tmesh; @@ -210,14 +218,15 @@ struct Pad { // merged mesh. It can be retrieved using a dedicated method (pad()) class SupportTreeBuilder: public SupportTree { // For heads it is beneficial to use the same IDs as for the support points. - std::vector m_heads; - std::vector m_head_indices; - std::vector m_pillars; - std::vector m_junctions; - std::vector m_bridges; - std::vector m_crossbridges; - std::vector m_pedestals; - std::vector m_anchors; + std::vector m_heads; + std::vector m_head_indices; + std::vector m_pillars; + std::vector m_junctions; + std::vector m_bridges; + std::vector m_crossbridges; + std::vector m_diffbridges; + std::vector m_pedestals; + std::vector m_anchors; Pad m_pad; @@ -228,8 +237,8 @@ class SupportTreeBuilder: public SupportTree { mutable bool m_meshcache_valid = false; mutable double m_model_height = 0; // the full height of the model - template - const Bridge& _add_bridge(std::vector &br, Args&&... args) + template + const BridgeT& _add_bridge(std::vector &br, Args&&... args) { std::lock_guard lk(m_mutex); br.emplace_back(std::forward(args)...); @@ -331,17 +340,6 @@ public: return pillar.id; } - const Pillar& head_pillar(unsigned headid) const - { - std::lock_guard lk(m_mutex); - assert(headid < m_head_indices.size()); - - const Head& h = m_heads[m_head_indices[headid]]; - assert(h.pillar_id >= 0 && h.pillar_id < long(m_pillars.size())); - - return m_pillars[size_t(h.pillar_id)]; - } - template const Junction& add_junction(Args&&... args) { std::lock_guard lk(m_mutex); @@ -374,6 +372,11 @@ public: { return _add_bridge(m_crossbridges, std::forward(args)...); } + + template const DiffBridge& add_diffbridge(Args&&... args) + { + return _add_bridge(m_diffbridges, std::forward(args)...); + } Head &head(unsigned id) { diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp index 7f6c034ddd..7ed4108023 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -1,18 +1,25 @@ #include #include -#include -#include +#include #include namespace Slic3r { namespace sla { -using libnest2d::opt::initvals; -using libnest2d::opt::bound; -using libnest2d::opt::StopCriteria; -using libnest2d::opt::GeneticOptimizer; -using libnest2d::opt::SubplexOptimizer; +using Slic3r::opt::initvals; +using Slic3r::opt::bounds; +using Slic3r::opt::StopCriteria; +using Slic3r::opt::Optimizer; +using Slic3r::opt::AlgNLoptSubplex; +using Slic3r::opt::AlgNLoptGenetic; + +StopCriteria get_criteria(const SupportTreeConfig &cfg) +{ + return StopCriteria{} + .rel_score_diff(cfg.optimizer_rel_score_diff) + .max_iterations(cfg.optimizer_max_iterations); +} template static Hit min_hit(const C &hits) @@ -37,7 +44,7 @@ SupportTreeBuildsteps::SupportTreeBuildsteps(SupportTreeBuilder & builder, { // Prepare the support points in Eigen/IGL format as well, we will use // it mostly in this form. - + long i = 0; for (const SupportPoint &sp : m_support_pts) { m_points.row(i)(X) = double(sp.pos(X)); @@ -51,7 +58,7 @@ bool SupportTreeBuildsteps::execute(SupportTreeBuilder & builder, const SupportableMesh &sm) { if(sm.pts.empty()) return false; - + builder.ground_level = sm.emesh.ground_level() - sm.cfg.object_elevation_mm; SupportTreeBuildsteps alg(builder, sm); @@ -72,46 +79,46 @@ bool SupportTreeBuildsteps::execute(SupportTreeBuilder & builder, NUM_STEPS //... }; - + // Collect the algorithm steps into a nice sequence std::array, NUM_STEPS> program = { [] () { // Begin... // Potentially clear up the shared data (not needed for now) }, - + std::bind(&SupportTreeBuildsteps::filter, &alg), - + std::bind(&SupportTreeBuildsteps::add_pinheads, &alg), - + std::bind(&SupportTreeBuildsteps::classify, &alg), - + std::bind(&SupportTreeBuildsteps::routing_to_ground, &alg), - + std::bind(&SupportTreeBuildsteps::routing_to_model, &alg), - + std::bind(&SupportTreeBuildsteps::interconnect_pillars, &alg), - + std::bind(&SupportTreeBuildsteps::merge_result, &alg), - + [] () { // Done }, - + [] () { // Abort } }; - + Steps pc = BEGIN; - + if(sm.cfg.ground_facing_only) { program[ROUTING_NONGROUND] = []() { BOOST_LOG_TRIVIAL(info) << "Skipping model-facing supports as requested."; }; } - + // Let's define a simple automaton that will run our program. auto progress = [&builder, &pc] () { static const std::array stepstr { @@ -126,7 +133,7 @@ bool SupportTreeBuildsteps::execute(SupportTreeBuilder & builder, "Done", "Abort" }; - + static const std::array stepstate { 0, 10, @@ -139,9 +146,9 @@ bool SupportTreeBuildsteps::execute(SupportTreeBuilder & builder, 100, 0 }; - + if(builder.ctl().stopcondition()) pc = ABORT; - + switch(pc) { case BEGIN: pc = FILTER; break; case FILTER: pc = PINHEADS; break; @@ -155,16 +162,16 @@ bool SupportTreeBuildsteps::execute(SupportTreeBuilder & builder, case ABORT: break; default: ; } - + builder.ctl().statuscb(stepstate[pc], stepstr[pc]); }; - + // Just here we run the computation... while(pc < DONE) { progress(); program[pc](); } - + return pc == ABORT; } @@ -177,48 +184,48 @@ IndexedMesh::hit_result SupportTreeBuildsteps::pinhead_mesh_intersect( double sd) { static const size_t SAMPLES = 8; - + // Move away slightly from the touching point to avoid raycasting on the // inner surface of the mesh. - + auto& m = m_mesh; using HitResult = IndexedMesh::hit_result; - + // Hit results std::array hits; - + struct Rings { double rpin; double rback; Vec3d spin; Vec3d sback; PointRing ring; - + Vec3d backring(size_t idx) { return ring.get(idx, sback, rback); } Vec3d pinring(size_t idx) { return ring.get(idx, spin, rpin); } } rings {r_pin + sd, r_back + sd, s, s + width * dir, dir}; - + // We will shoot multiple rays from the head pinpoint in the direction // of the pinhead robe (side) surface. The result will be the smallest // hit distance. - - ccr::enumerate(hits.begin(), hits.end(), + + ccr::enumerate(hits.begin(), hits.end(), [&m, &rings, sd](HitResult &hit, size_t i) { - + // Point on the circle on the pin sphere Vec3d ps = rings.pinring(i); // This is the point on the circle on the back sphere Vec3d p = rings.backring(i); - + // Point ps is not on mesh but can be inside or // outside as well. This would cause many problems // with ray-casting. To detect the position we will // use the ray-casting result (which has an is_inside - // predicate). - + // predicate). + Vec3d n = (p - ps).normalized(); auto q = m.query_ray_hit(ps + sd * n, n); - + if (q.is_inside()) { // the hit is inside the model if (q.distance() > rings.rpin) { // If we are inside the model and the hit @@ -243,7 +250,7 @@ IndexedMesh::hit_result SupportTreeBuildsteps::pinhead_mesh_intersect( } else hit = q; }); - + return min_hit(hits); } @@ -252,20 +259,20 @@ IndexedMesh::hit_result SupportTreeBuildsteps::bridge_mesh_intersect( { static const size_t SAMPLES = 8; PointRing ring{dir}; - + using Hit = IndexedMesh::hit_result; - + // Hit results std::array hits; - - ccr::enumerate(hits.begin(), hits.end(), + + ccr::enumerate(hits.begin(), hits.end(), [this, r, src, /*ins_check,*/ &ring, dir, sd] (Hit &hit, size_t i) { // Point on the circle on the pin sphere Vec3d p = ring.get(i, src, r + sd); - + auto hr = m_mesh.query_ray_hit(p + r * dir, dir); - + if(/*ins_check && */hr.is_inside()) { if(hr.distance() > 2 * r + sd) hit = Hit(0.0); else { @@ -274,7 +281,7 @@ IndexedMesh::hit_result SupportTreeBuildsteps::bridge_mesh_intersect( } } else hit = hr; }); - + return min_hit(hits); } @@ -288,61 +295,61 @@ bool SupportTreeBuildsteps::interconnect(const Pillar &pillar, // shorter pillar is too short to start a new bridge but the taller // pillar could still be bridged with the shorter one. bool was_connected = false; - + Vec3d supper = pillar.startpoint(); Vec3d slower = nextpillar.startpoint(); Vec3d eupper = pillar.endpoint(); Vec3d elower = nextpillar.endpoint(); - + double zmin = m_builder.ground_level + m_cfg.base_height_mm; eupper(Z) = std::max(eupper(Z), zmin); elower(Z) = std::max(elower(Z), zmin); - + // The usable length of both pillars should be positive if(slower(Z) - elower(Z) < 0) return false; if(supper(Z) - eupper(Z) < 0) return false; - + double pillar_dist = distance(Vec2d{slower(X), slower(Y)}, Vec2d{supper(X), supper(Y)}); double bridge_distance = pillar_dist / std::cos(-m_cfg.bridge_slope); double zstep = pillar_dist * std::tan(-m_cfg.bridge_slope); - + if(pillar_dist < 2 * m_cfg.head_back_radius_mm || pillar_dist > m_cfg.max_pillar_link_distance_mm) return false; - + if(supper(Z) < slower(Z)) supper.swap(slower); if(eupper(Z) < elower(Z)) eupper.swap(elower); - + double startz = 0, endz = 0; - + startz = slower(Z) - zstep < supper(Z) ? slower(Z) - zstep : slower(Z); endz = eupper(Z) + zstep > elower(Z) ? eupper(Z) + zstep : eupper(Z); - + if(slower(Z) - eupper(Z) < std::abs(zstep)) { // no space for even one cross - + // Get max available space startz = std::min(supper(Z), slower(Z) - zstep); endz = std::max(eupper(Z) + zstep, elower(Z)); - + // Align to center double available_dist = (startz - endz); double rounds = std::floor(available_dist / std::abs(zstep)); startz -= 0.5 * (available_dist - rounds * std::abs(zstep)); } - + auto pcm = m_cfg.pillar_connection_mode; bool docrosses = pcm == PillarConnectionMode::cross || (pcm == PillarConnectionMode::dynamic && pillar_dist > 2*m_cfg.base_radius_mm); - + // 'sj' means starting junction, 'ej' is the end junction of a bridge. // They will be swapped in every iteration thus the zig-zag pattern. // According to a config parameter, a second bridge may be added which // results in a cross connection between the pillars. Vec3d sj = supper, ej = slower; sj(Z) = startz; ej(Z) = sj(Z) + zstep; - + // TODO: This is a workaround to not have a faulty last bridge while(ej(Z) >= eupper(Z) /*endz*/) { if(bridge_mesh_distance(sj, dirv(sj, ej), pillar.r) >= bridge_distance) @@ -350,7 +357,7 @@ bool SupportTreeBuildsteps::interconnect(const Pillar &pillar, m_builder.add_crossbridge(sj, ej, pillar.r); was_connected = true; } - + // double bridging: (crosses) if(docrosses) { Vec3d sjback(ej(X), ej(Y), sj(Z)); @@ -363,11 +370,11 @@ bool SupportTreeBuildsteps::interconnect(const Pillar &pillar, was_connected = true; } } - + sj.swap(ej); ej(Z) = sj(Z) + zstep; } - + return was_connected; } @@ -377,67 +384,67 @@ bool SupportTreeBuildsteps::connect_to_nearpillar(const Head &head, auto nearpillar = [this, nearpillar_id]() -> const Pillar& { return m_builder.pillar(nearpillar_id); }; - - if (m_builder.bridgecount(nearpillar()) > m_cfg.max_bridges_on_pillar) + + if (m_builder.bridgecount(nearpillar()) > m_cfg.max_bridges_on_pillar) return false; - + Vec3d headjp = head.junction_point(); Vec3d nearjp_u = nearpillar().startpoint(); Vec3d nearjp_l = nearpillar().endpoint(); - + double r = head.r_back_mm; double d2d = distance(to_2d(headjp), to_2d(nearjp_u)); double d3d = distance(headjp, nearjp_u); - + double hdiff = nearjp_u(Z) - headjp(Z); double slope = std::atan2(hdiff, d2d); - + Vec3d bridgestart = headjp; Vec3d bridgeend = nearjp_u; double max_len = r * m_cfg.max_bridge_length_mm / m_cfg.head_back_radius_mm; double max_slope = m_cfg.bridge_slope; double zdiff = 0.0; - + // check the default situation if feasible for a bridge if(d3d > max_len || slope > -max_slope) { // not feasible to connect the two head junctions. We have to search // for a suitable touch point. - + double Zdown = headjp(Z) + d2d * std::tan(-max_slope); Vec3d touchjp = bridgeend; touchjp(Z) = Zdown; double D = distance(headjp, touchjp); zdiff = Zdown - nearjp_u(Z); - + if(zdiff > 0) { Zdown -= zdiff; bridgestart(Z) -= zdiff; touchjp(Z) = Zdown; - + double t = bridge_mesh_distance(headjp, DOWN, r); - + // We can't insert a pillar under the source head to connect // with the nearby pillar's starting junction if(t < zdiff) return false; } - + if(Zdown <= nearjp_u(Z) && Zdown >= nearjp_l(Z) && D < max_len) bridgeend(Z) = Zdown; else return false; } - + // There will be a minimum distance from the ground where the // bridge is allowed to connect. This is an empiric value. double minz = m_builder.ground_level + 4 * head.r_back_mm; if(bridgeend(Z) < minz) return false; - + double t = bridge_mesh_distance(bridgestart, dirv(bridgestart, bridgeend), r); - + // Cannot insert the bridge. (further search might not worth the hassle) if(t < distance(bridgestart, bridgeend)) return false; - + std::lock_guard lk(m_bridge_mutex); - + if (m_builder.bridgecount(nearpillar()) < m_cfg.max_bridges_on_pillar) { // A partial pillar is needed under the starting head. if(zdiff > 0) { @@ -447,31 +454,59 @@ bool SupportTreeBuildsteps::connect_to_nearpillar(const Head &head, } else { m_builder.add_bridge(head.id, bridgeend); } - + m_builder.increment_bridges(nearpillar()); } else return false; - + return true; } -bool SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, +bool SupportTreeBuildsteps::create_ground_pillar(const Vec3d &hjp, const Vec3d &sourcedir, double radius, long head_id) { - double sd = m_cfg.pillar_base_safety_distance_mm; + Vec3d jp = hjp, endp = jp, dir = sourcedir; long pillar_id = SupportTreeNode::ID_UNSET; - bool can_add_base = radius >= m_cfg.head_back_radius_mm; - double base_r = can_add_base ? m_cfg.base_radius_mm : 0.; - double gndlvl = m_builder.ground_level; - if (!can_add_base) gndlvl -= m_mesh.ground_level_offset(); - Vec3d endp = {jp(X), jp(Y), gndlvl}; - double min_dist = sd + base_r + EPSILON; - bool normal_mode = true; - Vec3d dir = sourcedir; + bool can_add_base = false, non_head = false; + + double gndlvl = 0.; // The Z level where pedestals should be + double jp_gnd = 0.; // The lowest Z where a junction center can be + double gap_dist = 0.; // The gap distance between the model and the pad auto to_floor = [&gndlvl](const Vec3d &p) { return Vec3d{p.x(), p.y(), gndlvl}; }; + auto eval_limits = [this, &radius, &can_add_base, &gndlvl, &gap_dist, &jp_gnd] + (bool base_en = true) + { + can_add_base = base_en && radius >= m_cfg.head_back_radius_mm; + double base_r = can_add_base ? m_cfg.base_radius_mm : 0.; + gndlvl = m_builder.ground_level; + if (!can_add_base) gndlvl -= m_mesh.ground_level_offset(); + jp_gnd = gndlvl + (can_add_base ? 0. : m_cfg.head_back_radius_mm); + gap_dist = m_cfg.pillar_base_safety_distance_mm + base_r + EPSILON; + }; + + eval_limits(); + + // We are dealing with a mini pillar that's potentially too long + if (radius < m_cfg.head_back_radius_mm && jp.z() - gndlvl > 20 * radius) + { + std::optional diffbr = + search_widening_path(jp, dir, radius, m_cfg.head_back_radius_mm); + + if (diffbr && diffbr->endp.z() > jp_gnd) { + auto &br = m_builder.add_diffbridge(diffbr.value()); + if (head_id >= 0) m_builder.head(head_id).bridge_id = br.id; + endp = diffbr->endp; + radius = diffbr->end_r; + m_builder.add_junction(endp, radius); + non_head = true; + dir = diffbr->get_dir(); + eval_limits(); + } else return false; + } + if (m_cfg.object_elevation_mm < EPSILON) { // get a suitable direction for the corrector bridge. It is the @@ -479,101 +514,118 @@ bool SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, // configured bridge slope. auto [polar, azimuth] = dir_to_spheric(dir); polar = PI - m_cfg.bridge_slope; - Vec3d dir = spheric_to_dir(polar, azimuth).normalized(); + Vec3d d = spheric_to_dir(polar, azimuth).normalized(); + double t = bridge_mesh_distance(endp, dir, radius); + double tmax = std::min(m_cfg.max_bridge_length_mm, t); + t = 0.; - // Check the distance of the endpoint and the closest point on model - // body. It should be greater than the min_dist which is - // the safety distance from the model. It includes the pad gap if in - // zero elevation mode. - // - // Try to move along the established bridge direction to dodge the - // forbidden region for the endpoint. - double t = -radius; - bool succ = true; - while (std::sqrt(m_mesh.squared_distance(to_floor(endp))) < min_dist || - !std::isinf(bridge_mesh_distance(endp, DOWN, radius))) { + double zd = endp.z() - jp_gnd; + double tmax2 = zd / std::sqrt(1 - m_cfg.bridge_slope * m_cfg.bridge_slope); + tmax = std::min(tmax, tmax2); + + Vec3d nexp = endp; + double dlast = 0.; + while (((dlast = std::sqrt(m_mesh.squared_distance(to_floor(nexp)))) < gap_dist || + !std::isinf(bridge_mesh_distance(nexp, DOWN, radius))) && t < tmax) { t += radius; - endp = jp + t * dir; - normal_mode = false; + nexp = endp + t * d; + } - if (t > m_cfg.max_bridge_length_mm || endp(Z) < gndlvl) { - if (head_id >= 0) m_builder.add_pillar(head_id, 0.); - succ = false; - break; + if (dlast < gap_dist && can_add_base) { + nexp = endp; + t = 0.; + can_add_base = false; + eval_limits(can_add_base); + + zd = endp.z() - jp_gnd; + tmax2 = zd / std::sqrt(1 - m_cfg.bridge_slope * m_cfg.bridge_slope); + tmax = std::min(tmax, tmax2); + + while (((dlast = std::sqrt(m_mesh.squared_distance(to_floor(nexp)))) < gap_dist || + !std::isinf(bridge_mesh_distance(nexp, DOWN, radius))) && t < tmax) { + t += radius; + nexp = endp + t * d; } } - if (!succ) { - if (can_add_base) { - can_add_base = false; - base_r = 0.; - gndlvl -= m_mesh.ground_level_offset(); - min_dist = sd + base_r + EPSILON; - endp = {jp(X), jp(Y), gndlvl + radius}; + // Could not find a path to avoid the pad gap + if (dlast < gap_dist) return false; - t = -radius; - while (std::sqrt(m_mesh.squared_distance(to_floor(endp))) < min_dist || - !std::isinf(bridge_mesh_distance(endp, DOWN, radius))) { - t += radius; - endp = jp + t * dir; - normal_mode = false; + if (t > 0.) { // Need to make additional bridge + const Bridge& br = m_builder.add_bridge(endp, nexp, radius); + if (head_id >= 0) m_builder.head(head_id).bridge_id = br.id; - if (t > m_cfg.max_bridge_length_mm || endp(Z) < (gndlvl + radius)) { - if (head_id >= 0) m_builder.add_pillar(head_id, 0.); - return false; - } - } - } else return false; + m_builder.add_junction(nexp, radius); + endp = nexp; + non_head = true; } } - double h = (jp - endp).norm(); + Vec3d gp = to_floor(endp); + double h = endp.z() - gp.z(); - // Check if the deduced route is sane and exit with error if not. - if (bridge_mesh_distance(jp, dir, radius) < h) { - if (head_id >= 0) m_builder.add_pillar(head_id, 0.); - return false; - } + pillar_id = head_id >= 0 && !non_head ? m_builder.add_pillar(head_id, h) : + m_builder.add_pillar(gp, h, radius); - // Straigh path down, no area to dodge - if (normal_mode) { - pillar_id = head_id >= 0 ? m_builder.add_pillar(head_id, h) : - m_builder.add_pillar(endp, h, radius); - - if (can_add_base) - add_pillar_base(pillar_id); - } else { - - // Insert the bridge to get around the forbidden area - Vec3d pgnd{endp.x(), endp.y(), gndlvl}; - pillar_id = m_builder.add_pillar(pgnd, endp.z() - gndlvl, radius); - - if (can_add_base) - add_pillar_base(pillar_id); - - m_builder.add_bridge(jp, endp, radius); - m_builder.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_builder.add_pillar(head_id, 0.); - } + if (can_add_base) + add_pillar_base(pillar_id); if(pillar_id >= 0) // Save the pillar endpoint in the spatial index - m_pillar_index.guarded_insert(endp, unsigned(pillar_id)); + m_pillar_index.guarded_insert(m_builder.pillar(pillar_id).endpt, + unsigned(pillar_id)); return true; } +std::optional SupportTreeBuildsteps::search_widening_path( + const Vec3d &jp, const Vec3d &dir, double radius, double new_radius) +{ + double w = radius + 2 * m_cfg.head_back_radius_mm; + double stopval = w + jp.z() - m_builder.ground_level; + Optimizer solver(get_criteria(m_cfg).stop_score(stopval)); + + auto [polar, azimuth] = dir_to_spheric(dir); + + double fallback_ratio = radius / m_cfg.head_back_radius_mm; + + auto oresult = solver.to_max().optimize( + [this, jp, radius, new_radius](double plr, double azm, double t) { + auto d = spheric_to_dir(plr, azm).normalized(); + double ret = pinhead_mesh_intersect(jp, d, radius, new_radius, t) + .distance(); + double down = bridge_mesh_distance(jp + t * d, d, new_radius); + + if (ret > t && std::isinf(down)) + ret += jp.z() - m_builder.ground_level; + + return ret; + }, + initvals({polar, azimuth, w}), // start with what we have + bounds({ + {PI - m_cfg.bridge_slope, PI}, // Must not exceed the slope limit + {-PI, PI}, // azimuth can be a full search + {radius + m_cfg.head_back_radius_mm, + fallback_ratio * m_cfg.max_bridge_length_mm} + })); + + if (oresult.score >= stopval) { + polar = std::get<0>(oresult.optimum); + azimuth = std::get<1>(oresult.optimum); + double t = std::get<2>(oresult.optimum); + Vec3d endp = jp + t * spheric_to_dir(polar, azimuth); + + return DiffBridge(jp, endp, radius, m_cfg.head_back_radius_mm); + } + + return {}; +} + void SupportTreeBuildsteps::filter() { // Get the points that are too close to each other and keep only the // first one auto aliases = cluster(m_points, D_SP, 2); - + PtIndices filtered_indices; filtered_indices.reserve(aliases.size()); m_iheads.reserve(aliases.size()); @@ -582,49 +634,62 @@ void SupportTreeBuildsteps::filter() // Here we keep only the front point of the cluster. filtered_indices.emplace_back(a.front()); } - + // calculate the normals to the triangles for filtered points auto nmls = sla::normals(m_points, m_mesh, m_cfg.head_front_radius_mm, m_thr, filtered_indices); - + // Not all of the support points have to be a valid position for // support creation. The angle may be inappropriate or there may // not be enough space for the pinhead. Filtering is applied for // these reasons. - - ccr::SpinningMutex mutex; - auto addfn = [&mutex](PtIndices &container, unsigned val) { - std::lock_guard lk(mutex); - container.emplace_back(val); - }; - - auto filterfn = [this, &nmls, addfn](unsigned fidx, size_t i) { + + std::vector heads; heads.reserve(m_support_pts.size()); + for (const SupportPoint &sp : m_support_pts) { m_thr(); - + heads.emplace_back( + std::nan(""), + sp.head_front_radius, + 0., + m_cfg.head_penetration_mm, + Vec3d::Zero(), // dir + sp.pos.cast() // displacement + ); + } + + std::function filterfn; + filterfn = [this, &nmls, &heads, &filterfn](unsigned fidx, size_t i, double back_r) { + m_thr(); + auto n = nmls.row(Eigen::Index(i)); - + // for all normals we generate the spherical coordinates and // saturate the polar angle to 45 degrees from the bottom then // convert back to standard coordinates to get the new normal. // Then we just create a quaternion from the two normals // (Quaternion::FromTwoVectors) and apply the rotation to the // arrow head. - + auto [polar, azimuth] = dir_to_spheric(n); - + // skip if the tilt is not sane - if(polar < PI - m_cfg.normal_cutoff_angle) return; - + if (polar < PI - m_cfg.normal_cutoff_angle) return; + // We saturate the polar angle to 3pi/4 - polar = std::max(polar, 3*PI / 4); + polar = std::max(polar, PI - m_cfg.bridge_slope); // save the head (pinpoint) position Vec3d hp = m_points.row(fidx); + double lmin = m_cfg.head_width_mm, lmax = lmin; + + if (back_r < m_cfg.head_back_radius_mm) { + lmin = 0., lmax = m_cfg.head_penetration_mm; + } + // The distance needed for a pinhead to not collide with model. - double w = m_cfg.head_width_mm + - m_cfg.head_back_radius_mm + - 2*m_cfg.head_front_radius_mm; + double w = lmin + 2 * back_r + 2 * m_cfg.head_front_radius_mm - + m_cfg.head_penetration_mm; double pin_r = double(m_support_pts[fidx].head_front_radius); @@ -632,113 +697,69 @@ void SupportTreeBuildsteps::filter() auto nn = spheric_to_dir(polar, azimuth).normalized(); // check available distance - IndexedMesh::hit_result t - = pinhead_mesh_intersect(hp, // touching point - nn, // normal - pin_r, - m_cfg.head_back_radius_mm, - w); - - if(t.distance() <= w) { + IndexedMesh::hit_result t = pinhead_mesh_intersect(hp, nn, pin_r, + back_r, w); + if (t.distance() < w) { // Let's try to optimize this angle, there might be a // viable normal that doesn't collide with the model // geometry and its very close to the default. - StopCriteria stc; - stc.max_iterations = m_cfg.optimizer_max_iterations; - stc.relative_score_difference = m_cfg.optimizer_rel_score_diff; - stc.stop_score = w; // space greater than w is enough - GeneticOptimizer solver(stc); - solver.seed(0); // we want deterministic behavior + // stc.stop_score = w; // space greater than w is enough + Optimizer solver(get_criteria(m_cfg)); + solver.seed(0); + //solver.seed(0); // we want deterministic behavior - auto oresult = solver.optimize_max( - [this, pin_r, w, hp](double plr, double azm) + auto oresult = solver.to_max().optimize( + [this, pin_r, back_r, hp](double plr, double azm, double l) { auto dir = spheric_to_dir(plr, azm).normalized(); double score = pinhead_mesh_intersect( - hp, dir, pin_r, m_cfg.head_back_radius_mm, w).distance(); + hp, dir, pin_r, back_r, l).distance(); return score; }, - initvals(polar, azimuth), // start with what we have - bound(3 * PI / 4, PI), // Must not exceed the tilt limit - bound(-PI, PI) // azimuth can be a full search - ); + initvals({polar, azimuth, (lmin + lmax) / 2.}), // start with what we have + bounds({ + {PI - m_cfg.bridge_slope, PI}, // Must not exceed the tilt limit + {-PI, PI}, // azimuth can be a full search + {lmin, lmax} + })); if(oresult.score > w) { polar = std::get<0>(oresult.optimum); azimuth = std::get<1>(oresult.optimum); nn = spheric_to_dir(polar, azimuth).normalized(); + lmin = std::get<2>(oresult.optimum); t = IndexedMesh::hit_result(oresult.score); } } - // save the verified and corrected normal - m_support_nmls.row(fidx) = nn; + if (t.distance() > w && hp(Z) + w * nn(Z) >= m_builder.ground_level) { + Head &h = heads[fidx]; + h.id = fidx; h.dir = nn; h.width_mm = lmin; h.r_back_mm = back_r; + } else if (back_r > m_cfg.head_fallback_radius_mm) { + filterfn(fidx, i, m_cfg.head_fallback_radius_mm); + } + }; - if (t.distance() > w) { - // Check distance from ground, we might have zero elevation. - if (hp(Z) + w * nn(Z) < m_builder.ground_level) { - addfn(m_iheadless, fidx); - } else { - // mark the point for needing a head. - addfn(m_iheads, fidx); - } - } else if (polar >= 3 * PI / 4) { - // Headless supports do not tilt like the headed ones - // so the normal should point almost to the ground. - addfn(m_iheadless, fidx); + ccr::enumerate(filtered_indices.begin(), filtered_indices.end(), + [this, &filterfn](unsigned fidx, size_t i) { + filterfn(fidx, i, m_cfg.head_back_radius_mm); + }); + + for (size_t i = 0; i < heads.size(); ++i) + if (heads[i].is_valid()) { + m_builder.add_head(i, heads[i]); + m_iheads.emplace_back(i); } - }; - - ccr::enumerate(filtered_indices.begin(), filtered_indices.end(), filterfn); - m_thr(); } void SupportTreeBuildsteps::add_pinheads() { - for (unsigned i : m_iheads) { - m_thr(); - m_builder.add_head( - i, - m_cfg.head_back_radius_mm, - m_support_pts[i].head_front_radius, - m_cfg.head_width_mm, - m_cfg.head_penetration_mm, - m_support_nmls.row(i), // dir - m_support_pts[i].pos.cast() // displacement - ); - } - - for (unsigned i : m_iheadless) { - const auto R = double(m_support_pts[i].head_front_radius); - - // The support point position on the mesh - Vec3d sph = m_support_pts[i].pos.cast(); - - // Get an initial normal from the filtering step - Vec3d n = m_support_nmls.row(i); - - // First we need to determine the available space for a mini pinhead. - // The goal is the move away from the model a little bit to make the - // contact point small as possible and avoid pearcing the model body. - double back_r = m_cfg.head_fallback_radius_mm; - double max_w = 2 * R; - double pin_space = std::min(max_w, - pinhead_mesh_intersect(sph, n, R, back_r, - max_w, 0.) - .distance()); - - if (pin_space <= 0) continue; - - m_iheads.emplace_back(i); - m_builder.add_head(i, back_r, R, pin_space, - m_cfg.head_penetration_mm, n, sph); - } } void SupportTreeBuildsteps::classify() @@ -747,37 +768,37 @@ void SupportTreeBuildsteps::classify() PtIndices ground_head_indices; ground_head_indices.reserve(m_iheads.size()); m_iheads_onmodel.reserve(m_iheads.size()); - + // First we decide which heads reach the ground and can be full // pillars and which shall be connected to the model surface (or // search a suitable path around the surface that leads to the // ground -- TODO) for(unsigned i : m_iheads) { m_thr(); - - auto& head = m_builder.head(i); + + Head &head = m_builder.head(i); double r = head.r_back_mm; Vec3d headjp = head.junction_point(); - + // collision check auto hit = bridge_mesh_intersect(headjp, DOWN, r); - + if(std::isinf(hit.distance())) ground_head_indices.emplace_back(i); else if(m_cfg.ground_facing_only) head.invalidate(); else m_iheads_onmodel.emplace_back(i); - + m_head_to_ground_scans[i] = hit; } - + // We want to search for clusters of points that are far enough // from each other in the XY plane to not cross their pillar bases // These clusters of support points will join in one pillar, // possibly in their centroid support point. - + auto pointfn = [this](unsigned i) { return m_builder.head(i).junction_point(); }; - + auto predicate = [this](const PointIndexEl &e1, const PointIndexEl &e2) { double d2d = distance(to_2d(e1.first), to_2d(e2.first)); @@ -794,10 +815,10 @@ void SupportTreeBuildsteps::routing_to_ground() { ClusterEl cl_centroids; cl_centroids.reserve(m_pillar_clusters.size()); - + for (auto &cl : m_pillar_clusters) { m_thr(); - + // place all the centroid head positions into the index. We // will query for alternative pillar positions. If a sidehead // cannot connect to the cluster centroid, we have to search @@ -805,9 +826,9 @@ void SupportTreeBuildsteps::routing_to_ground() // elements in the cluster, the centroid is arbitrary and the // sidehead is allowed to connect to a nearby pillar to // increase structural stability. - + if (cl.empty()) continue; - + // get the current cluster centroid auto & thr = m_thr; const auto &points = m_points; @@ -821,11 +842,11 @@ void SupportTreeBuildsteps::routing_to_ground() assert(lcid >= 0); unsigned hid = cl[size_t(lcid)]; // Head ID - + cl_centroids.emplace_back(hid); - + Head &h = m_builder.head(hid); - + if (!create_ground_pillar(h.junction_point(), h.dir, h.r_back_mm, h.id)) { BOOST_LOG_TRIVIAL(warning) << "Pillar cannot be created for support point id: " << hid; @@ -833,34 +854,32 @@ void SupportTreeBuildsteps::routing_to_ground() continue; } } - + // now we will go through the clusters ones again and connect the // sidepoints with the cluster centroid (which is a ground pillar) // or a nearby pillar if the centroid is unreachable. size_t ci = 0; for (auto cl : m_pillar_clusters) { m_thr(); - + auto cidx = cl_centroids[ci++]; - - // TODO: don't consider the cluster centroid but calculate a - // central position where the pillar can be placed. this way - // the weight is distributed more effectively on the pillar. - - auto centerpillarID = m_builder.head_pillar(cidx).id; - - for (auto c : cl) { - m_thr(); - if (c == cidx) continue; - - auto &sidehead = m_builder.head(c); - - if (!connect_to_nearpillar(sidehead, centerpillarID) && - !search_pillar_and_connect(sidehead)) { - Vec3d pstart = sidehead.junction_point(); - // Vec3d pend = Vec3d{pstart(X), pstart(Y), gndlvl}; - // Could not find a pillar, create one - create_ground_pillar(pstart, sidehead.dir, sidehead.r_back_mm, sidehead.id); + + auto q = m_pillar_index.query(m_builder.head(cidx).junction_point(), 1); + if (!q.empty()) { + long centerpillarID = q.front().second; + for (auto c : cl) { + m_thr(); + if (c == cidx) continue; + + auto &sidehead = m_builder.head(c); + + if (!connect_to_nearpillar(sidehead, centerpillarID) && + !search_pillar_and_connect(sidehead)) { + Vec3d pstart = sidehead.junction_point(); + // Vec3d pend = Vec3d{pstart(X), pstart(Y), gndlvl}; + // Could not find a pillar, create one + create_ground_pillar(pstart, sidehead.dir, sidehead.r_back_mm, sidehead.id); + } } } } @@ -876,9 +895,9 @@ bool SupportTreeBuildsteps::connect_to_ground(Head &head, const Vec3d &dir) while (d < t && !std::isinf(tdown = bridge_mesh_distance(hjp + d * dir, DOWN, r))) d += r; - + if(!std::isinf(tdown)) return false; - + Vec3d endp = hjp + d * dir; bool ret = false; @@ -886,38 +905,33 @@ bool SupportTreeBuildsteps::connect_to_ground(Head &head, const Vec3d &dir) m_builder.add_bridge(head.id, endp); m_builder.add_junction(endp, head.r_back_mm); } - + return ret; } bool SupportTreeBuildsteps::connect_to_ground(Head &head) { if (connect_to_ground(head, head.dir)) return true; - + // Optimize bridge direction: // Straight path failed so we will try to search for a suitable // direction out of the cavity. auto [polar, azimuth] = dir_to_spheric(head.dir); - - StopCriteria stc; - stc.max_iterations = m_cfg.optimizer_max_iterations; - stc.relative_score_difference = m_cfg.optimizer_rel_score_diff; - stc.stop_score = 1e6; - GeneticOptimizer solver(stc); + + Optimizer solver(get_criteria(m_cfg).stop_score(1e6)); solver.seed(0); // we want deterministic behavior - + double r_back = head.r_back_mm; - Vec3d hjp = head.junction_point(); - auto oresult = solver.optimize_max( + Vec3d hjp = head.junction_point(); + auto oresult = solver.to_max().optimize( [this, hjp, r_back](double plr, double azm) { Vec3d n = spheric_to_dir(plr, azm).normalized(); return bridge_mesh_distance(hjp, n, r_back); }, - initvals(polar, azimuth), // let's start with what we have - bound(3*PI/4, PI), // Must not exceed the slope limit - bound(-PI, PI) // azimuth can be a full range search - ); - + initvals({polar, azimuth}), // let's start with what we have + bounds({ {PI - m_cfg.bridge_slope, PI}, {-PI, PI} }) + ); + Vec3d bridgedir = spheric_to_dir(oresult.optimum).normalized(); return connect_to_ground(head, bridgedir); } @@ -925,10 +939,10 @@ bool SupportTreeBuildsteps::connect_to_ground(Head &head) bool SupportTreeBuildsteps::connect_to_model_body(Head &head) { if (head.id <= SupportTreeNode::ID_UNSET) return false; - + auto it = m_head_to_ground_scans.find(unsigned(head.id)); if (it == m_head_to_ground_scans.end()) return false; - + auto &hit = it->second; if (!hit.is_hit()) { @@ -943,9 +957,11 @@ bool SupportTreeBuildsteps::connect_to_model_body(Head &head) // The width of the tail head that we would like to have... h = std::min(hit.distance() - head.r_back_mm, h); - - if(h <= 0.) return false; - + + // If this is a mini pillar dont bother with the tail width, can be 0. + if (head.r_back_mm < m_cfg.head_back_radius_mm) h = std::max(h, 0.); + else if (h <= 0.) return false; + Vec3d endp{hjp(X), hjp(Y), hjp(Z) - hit.distance() + h}; auto center_hit = m_mesh.query_ray_hit(hjp, DOWN); @@ -969,7 +985,7 @@ bool SupportTreeBuildsteps::connect_to_model_body(Head &head) m_cfg.head_penetration_mm, taildir, hitp); m_pillar_index.guarded_insert(pill.endpoint(), pill.id); - + return true; } @@ -1011,7 +1027,7 @@ bool SupportTreeBuildsteps::search_pillar_and_connect(const Head &source) } void SupportTreeBuildsteps::routing_to_model() -{ +{ // We need to check if there is an easy way out to the bed surface. // If it can be routed there with a bridge shorter than // min_bridge_distance. @@ -1019,23 +1035,23 @@ void SupportTreeBuildsteps::routing_to_model() ccr::enumerate(m_iheads_onmodel.begin(), m_iheads_onmodel.end(), [this] (const unsigned idx, size_t) { m_thr(); - + auto& head = m_builder.head(idx); - + // Search nearby pillar if (search_pillar_and_connect(head)) { return; } - + // Cannot connect to nearby pillar. We will try to search for // a route to the ground. if (connect_to_ground(head)) { return; } - + // No route to the ground, so connect to the model body as a last resort if (connect_to_model_body(head)) { return; } - + // We have failed to route this head. BOOST_LOG_TRIVIAL(warning) << "Failed to route model facing support point. ID: " << idx; - + head.invalidate(); }); } @@ -1045,19 +1061,19 @@ void SupportTreeBuildsteps::interconnect_pillars() // Now comes the algorithm that connects pillars with each other. // Ideally every pillar should be connected with at least one of its // neighbors if that neighbor is within max_pillar_link_distance - + // Pillars with height exceeding H1 will require at least one neighbor // to connect with. Height exceeding H2 require two neighbors. double H1 = m_cfg.max_solo_pillar_height_mm; double H2 = m_cfg.max_dual_pillar_height_mm; double d = m_cfg.max_pillar_link_distance_mm; - + //A connection between two pillars only counts if the height ratio is // bigger than 50% double min_height_ratio = 0.5; - + std::set 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 @@ -1067,68 +1083,68 @@ void SupportTreeBuildsteps::interconnect_pillars() [this, d, &pairs, min_height_ratio, H1] (const PointIndexEl& el) { Vec3d qp = el.first; // endpoint of the pillar - + const Pillar& pillar = m_builder.pillar(el.second); // actual pillar - + // Get the max number of neighbors a pillar should connect to unsigned neighbors = m_cfg.pillar_cascade_neighbors; - + // connections are already enough for the pillar if(pillar.links >= neighbors) return; - + double max_d = d * pillar.r / m_cfg.head_back_radius_mm; // Query all remaining points within reach auto qres = m_pillar_index.query([qp, max_d](const PointIndexEl& e){ return distance(e.first, qp) < max_d; }); - + // sort the result by distance (have to check if this is needed) std::sort(qres.begin(), qres.end(), [qp](const PointIndexEl& e1, const PointIndexEl& e2){ return distance(e1.first, qp) < distance(e2.first, qp); }); - + for(auto& re : qres) { // process the queried neighbors - + if(re.second == el.second) continue; // Skip self - + auto a = el.second, b = re.second; - + // Get unique hash for the given pair (order doesn't matter) auto hashval = pairhash(a, b); - + // Search for the pair amongst the remembered pairs if(pairs.find(hashval) != pairs.end()) continue; - + const Pillar& neighborpillar = m_builder.pillar(re.second); - + // this neighbor is occupied, skip if (neighborpillar.links >= neighbors) continue; if (neighborpillar.r < pillar.r) continue; - + if(interconnect(pillar, neighborpillar)) { pairs.insert(hashval); - + // If the interconnection length between the two pillars is // less than 50% of the longer pillar's height, don't count if(pillar.height < H1 || neighborpillar.height / pillar.height > min_height_ratio) m_builder.increment_links(pillar); - + if(neighborpillar.height < H1 || pillar.height / neighborpillar.height > min_height_ratio) m_builder.increment_links(neighborpillar); - + } - + // connections are enough for one pillar if(pillar.links >= neighbors) break; } }; - + // Run the cascade for the pillars in the index 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. @@ -1136,16 +1152,16 @@ void SupportTreeBuildsteps::interconnect_pillars() // 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_builder.pillarcount(); - + // Again, go through all pillars, this time in the whole support tree // not just the index. for(size_t pid = 0; pid < pillarcount; pid++) { auto pillar = [this, pid]() { return m_builder.pillar(pid); }; - + // Decide how many additional pillars will be needed: - + unsigned needpillars = 0; if (pillar().bridges > m_cfg.max_bridges_on_pillar) needpillars = 3; @@ -1156,28 +1172,28 @@ void SupportTreeBuildsteps::interconnect_pillars() // No neighbors could be found and the pillar is too long. needpillars = 1; } - + needpillars = std::max(pillar().links, needpillars) - pillar().links; if (needpillars == 0) continue; - + // Search for new pillar locations: - + bool found = false; double alpha = 0; // goes to 2Pi double r = 2 * m_cfg.base_radius_mm; Vec3d pillarsp = pillar().startpoint(); - + // temp value for starting point detection Vec3d sp(pillarsp(X), pillarsp(Y), pillarsp(Z) - r); - + // A vector of bool for placement feasbility std::vector canplace(needpillars, false); std::vector spts(needpillars); // vector of starting points - + double gnd = m_builder.ground_level; double min_dist = m_cfg.pillar_base_safety_distance_mm + m_cfg.base_radius_mm + EPSILON; - + while(!found && alpha < 2*PI) { for (unsigned n = 0; n < needpillars && (!n || canplace[n - 1]); @@ -1188,25 +1204,25 @@ void SupportTreeBuildsteps::interconnect_pillars() s(X) += std::cos(a) * r; s(Y) += std::sin(a) * r; spts[n] = s; - + // Check the path vertically down Vec3d check_from = s + Vec3d{0., 0., pillar().r}; auto hr = bridge_mesh_intersect(check_from, DOWN, pillar().r); 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(canplace.begin(), canplace.end(), [](bool v) { return v; }); - + // 20 angles will be tried... alpha += 0.1 * PI; } - + std::vector newpills; newpills.reserve(needpillars); @@ -1247,7 +1263,7 @@ void SupportTreeBuildsteps::interconnect_pillars() m_builder.increment_links(nxpll); } } - + m_pillar_index.foreach(cascadefn); } } diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp index d19194a87d..013666f074 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp @@ -45,6 +45,11 @@ inline Vec3d spheric_to_dir(const std::pair &v) return spheric_to_dir(v.first, v.second); } +inline Vec3d spheric_to_dir(const std::array &v) +{ + return spheric_to_dir(v[0], v[1]); +} + // Give points on a 3D ring with given center, radius and orientation // method based on: // https://math.stackexchange.com/questions/73237/parametric-equation-of-a-circle-in-3d-space @@ -249,7 +254,8 @@ class SupportTreeBuildsteps { double width) { return pinhead_mesh_intersect(s, dir, r_pin, r_back, width, - m_cfg.safety_distance_mm); + r_back * m_cfg.safety_distance_mm / + m_cfg.head_back_radius_mm); } // Checking bridge (pillar and stick as well) intersection with the model. @@ -271,7 +277,9 @@ class SupportTreeBuildsteps { const Vec3d& dir, double r) { - return bridge_mesh_intersect(s, dir, r, m_cfg.safety_distance_mm); + return bridge_mesh_intersect(s, dir, r, + r * m_cfg.safety_distance_mm / + m_cfg.head_back_radius_mm); } template @@ -311,6 +319,11 @@ class SupportTreeBuildsteps { m_builder.add_pillar_base(pid, m_cfg.base_height_mm, m_cfg.base_radius_mm); } + std::optional search_widening_path(const Vec3d &jp, + const Vec3d &dir, + double radius, + double new_radius); + public: SupportTreeBuildsteps(SupportTreeBuilder & builder, const SupportableMesh &sm); diff --git a/src/libslic3r/SLA/SupportTreeMesher.hpp b/src/libslic3r/SLA/SupportTreeMesher.hpp index a086680c34..63182745da 100644 --- a/src/libslic3r/SLA/SupportTreeMesher.hpp +++ b/src/libslic3r/SLA/SupportTreeMesher.hpp @@ -94,6 +94,24 @@ inline Contour3D get_mesh(const Bridge &br, size_t steps) return mesh; } +inline Contour3D get_mesh(const DiffBridge &br, size_t steps) +{ + double h = br.get_length(); + Contour3D mesh = halfcone(h, br.r, br.end_r, Vec3d::Zero(), steps); + + using Quaternion = Eigen::Quaternion; + + // We rotate the head to the specified direction. The head's pointing + // side is facing upwards so this means that it would hold a support + // point with a normal pointing straight down. This is the reason of + // the -1 z coordinate + auto quatern = Quaternion::FromTwoVectors(Vec3d{0, 0, 1}, br.get_dir()); + + for(auto& p : mesh.points) p = quatern * p + br.startp; + + return mesh; +} + }} // namespace Slic3r::sla #endif // SUPPORTTREEMESHER_HPP diff --git a/tests/sla_print/sla_print_tests.cpp b/tests/sla_print/sla_print_tests.cpp index 9a9c762e3d..dad2b90971 100644 --- a/tests/sla_print/sla_print_tests.cpp +++ b/tests/sla_print/sla_print_tests.cpp @@ -4,6 +4,8 @@ #include "sla_test_utils.hpp" +#include + namespace { const char *const BELOW_PAD_TEST_OBJECTS[] = { @@ -228,3 +230,12 @@ TEST_CASE("Triangle mesh conversions should be correct", "[SLAConversions]") cntr.from_obj(infile); } } + +TEST_CASE("halfcone test", "[halfcone]") { + sla::DiffBridge br{Vec3d{1., 1., 1.}, Vec3d{10., 10., 10.}, 0.25, 0.5}; + + TriangleMesh m = sla::to_triangle_mesh(sla::get_mesh(br, 45)); + + m.require_shared_vertices(); + m.WriteOBJFile("Halfcone.obj"); +} From f3c0bf46d4573bc8592a8dab70695eb1fe8aaef1 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 17 Jul 2020 14:09:28 +0200 Subject: [PATCH 249/503] finish optimizer interface and remove commented code --- src/libslic3r/Optimizer.hpp | 73 ++++++++++++--------- src/libslic3r/SLA/SupportTreeBuildsteps.cpp | 23 +++---- 2 files changed, 54 insertions(+), 42 deletions(-) diff --git a/src/libslic3r/Optimizer.hpp b/src/libslic3r/Optimizer.hpp index dc70abe332..6495ae7ff4 100644 --- a/src/libslic3r/Optimizer.hpp +++ b/src/libslic3r/Optimizer.hpp @@ -103,6 +103,16 @@ public: bool stop_condition() { return m_stop_condition(); } }; +// Helper class to use optimization methods involving gradient. +template struct ScoreGradient { + double score; + std::optional> gradient; + + ScoreGradient(double s, const std::array &grad) + : score{s}, gradient{grad} + {} +}; + // Helper to be used in static_assert. template struct always_false { enum { value = false }; }; @@ -112,13 +122,13 @@ public: Optimizer(const StopCriteria &) { - static_assert(always_false::value, - "Optimizer unimplemented for given method!"); + static_assert (always_false::value, + "Optimizer unimplemented for given method!"); } - Optimizer &to_min() { return *this; } - Optimizer &to_max() { return *this; } - Optimizer &set_criteria(const StopCriteria &) { return *this; } + Optimizer &to_min() { return *this; } + Optimizer &to_max() { return *this; } + Optimizer &set_criteria(const StopCriteria &) { return *this; } StopCriteria get_criteria() const { return {}; }; template @@ -156,35 +166,20 @@ struct IsNLoptAlg> { template using NLoptOnly = std::enable_if_t::value, T>; -// Convert any collection to tuple. This is useful for object functions taking -// an argument list of doubles. Make things cleaner on the call site of -// optimize(). -template struct to_tuple_ { - static auto call(const C &c) - { - return std::tuple_cat(std::tuple(c[N-I]), - to_tuple_::call(c)); - } -}; - -template struct to_tuple_<0, N, T, C> { - static auto call(const C &c) { return std::tuple<>(); } -}; - -// C array to tuple -template auto carray_tuple(const T *v) -{ - return to_tuple_::call(v); -} - -// Helper to convert C style array to std::array -template auto to_arr(const T (&a) [N]) +// Helper to convert C style array to std::array. The copy should be optimized +// away with modern compilers. +template auto to_arr(const T *a) { std::array r; - std::copy(std::begin(a), std::end(a), std::begin(r)); + std::copy(a, a + N, std::begin(r)); return r; } +template auto to_arr(const T (&a) [N]) +{ + return to_arr(static_cast(a)); +} + enum class OptDir { MIN, MAX }; // Where to optimize struct NLopt { // Helper RAII class for nlopt_opt @@ -227,9 +222,19 @@ protected: nlopt_force_stop(std::get<2>(*tdata)); auto fnptr = std::get<0>(*tdata); - auto funval = carray_tuple(params); + auto funval = to_arr(params); - return std::apply(*fnptr, funval); + double scoreval = 0.; + using RetT = decltype((*fnptr)(funval)); + if constexpr (std::is_convertible_v>) { + ScoreGradient score = (*fnptr)(funval); + for (size_t i = 0; i < n; ++i) gradient[i] = (*score.gradient)[i]; + scoreval = score.score; + } else { + scoreval = (*fnptr)(funval); + } + + return scoreval; } template @@ -354,12 +359,18 @@ public: template Bounds bounds(const Bound (&b) [N]) { return detail::to_arr(b); } template Input initvals(const double (&a) [N]) { return detail::to_arr(a); } +template auto score_gradient(double s, const double (&grad)[N]) +{ + return ScoreGradient(s, detail::to_arr(grad)); +} // Predefinded NLopt algorithms that are used in the codebase using AlgNLoptGenetic = detail::NLoptAlgComb; using AlgNLoptSubplex = detail::NLoptAlg; using AlgNLoptSimplex = detail::NLoptAlg; +// TODO: define others if needed... + // Helper defs for pre-crafted global and local optimizers that work well. using DefaultGlobalOptimizer = Optimizer; using DefaultLocalOptimizer = Optimizer; diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp index 7ed4108023..2b40f00828 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -496,7 +496,7 @@ bool SupportTreeBuildsteps::create_ground_pillar(const Vec3d &hjp, search_widening_path(jp, dir, radius, m_cfg.head_back_radius_mm); if (diffbr && diffbr->endp.z() > jp_gnd) { - auto &br = m_builder.add_diffbridge(diffbr.value()); + auto &br = m_builder.add_diffbridge(*diffbr); if (head_id >= 0) m_builder.head(head_id).bridge_id = br.id; endp = diffbr->endp; radius = diffbr->end_r; @@ -589,7 +589,9 @@ std::optional SupportTreeBuildsteps::search_widening_path( double fallback_ratio = radius / m_cfg.head_back_radius_mm; auto oresult = solver.to_max().optimize( - [this, jp, radius, new_radius](double plr, double azm, double t) { + [this, jp, radius, new_radius](const opt::Input<3> &input) { + auto &[plr, azm, t] = input; + auto d = spheric_to_dir(plr, azm).normalized(); double ret = pinhead_mesh_intersect(jp, d, radius, new_radius, t) .distance(); @@ -705,24 +707,22 @@ void SupportTreeBuildsteps::filter() // viable normal that doesn't collide with the model // geometry and its very close to the default. - // stc.stop_score = w; // space greater than w is enough Optimizer solver(get_criteria(m_cfg)); - solver.seed(0); - //solver.seed(0); // we want deterministic behavior + solver.seed(0); // we want deterministic behavior auto oresult = solver.to_max().optimize( - [this, pin_r, back_r, hp](double plr, double azm, double l) + [this, pin_r, back_r, hp](const opt::Input<3> &input) { + auto &[plr, azm, l] = input; + auto dir = spheric_to_dir(plr, azm).normalized(); - double score = pinhead_mesh_intersect( + return pinhead_mesh_intersect( hp, dir, pin_r, back_r, l).distance(); - - return score; }, initvals({polar, azimuth, (lmin + lmax) / 2.}), // start with what we have bounds({ - {PI - m_cfg.bridge_slope, PI}, // Must not exceed the tilt limit + {PI - m_cfg.bridge_slope, PI}, // Must not exceed the slope limit {-PI, PI}, // azimuth can be a full search {lmin, lmax} })); @@ -924,7 +924,8 @@ bool SupportTreeBuildsteps::connect_to_ground(Head &head) double r_back = head.r_back_mm; Vec3d hjp = head.junction_point(); auto oresult = solver.to_max().optimize( - [this, hjp, r_back](double plr, double azm) { + [this, hjp, r_back](const opt::Input<2> &input) { + auto &[plr, azm] = input; Vec3d n = spheric_to_dir(plr, azm).normalized(); return bridge_mesh_distance(hjp, n, r_back); }, From 0614d6d4a8ce959085488b8f6690c48f13442c99 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 3 Aug 2020 19:07:30 +0200 Subject: [PATCH 250/503] Remove leftover junk comments --- src/libslic3r/SLAPrintSteps.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index defc5246cc..76bbf498d0 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -374,9 +374,6 @@ void SLAPrint::Steps::support_tree(SLAPrintObject &po) // If the zero elevation mode is engaged, we have to filter out all the // points that are on the bottom of the object if (is_zero_elevation(po.config())) { -// double discard = pcfg.embed_object.object_gap_mm / -// std::cos(po.m_supportdata->cfg.bridge_slope) ; - remove_bottom_points(po.m_supportdata->pts, float(po.m_supportdata->emesh.ground_level() + EPSILON)); } From 510e787bc7d6c5ceb27340e18457b7a4428fb095 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 4 Aug 2020 10:26:08 +0200 Subject: [PATCH 251/503] Fixed Print.xsp --- xs/xsp/Print.xsp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 0952513ca3..e4957c042f 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -76,10 +76,10 @@ _constant() %code%{ RETVAL = const_cast(&THIS->skirt()); %}; Ref brim() %code%{ RETVAL = const_cast(&THIS->brim()); %}; - std::string estimated_normal_print_time() - %code%{ RETVAL = THIS->print_statistics().estimated_normal_print_time; %}; - std::string estimated_silent_print_time() - %code%{ RETVAL = THIS->print_statistics().estimated_silent_print_time; %}; +// std::string estimated_normal_print_time() +// %code%{ RETVAL = THIS->print_statistics().estimated_normal_print_time; %}; +// std::string estimated_silent_print_time() +// %code%{ RETVAL = THIS->print_statistics().estimated_silent_print_time; %}; double total_used_filament() %code%{ RETVAL = THIS->print_statistics().total_used_filament; %}; double total_extruded_volume() From 8fc5be7e4f10bc397931269beecf1e5ab1fc8fef Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 5 Aug 2020 15:43:46 +0200 Subject: [PATCH 252/503] Refactoring to allow to quickly build the various options to show the estimated printing time in gcode viewer scene --- src/libslic3r/GCode.cpp | 76 +++++++++++++++------------- src/libslic3r/GCode.hpp | 6 ++- src/libslic3r/GCodeTimeEstimator.cpp | 23 ++------- src/libslic3r/GCodeTimeEstimator.hpp | 10 ++-- src/libslic3r/Print.cpp | 14 +---- src/libslic3r/Print.hpp | 22 ++------ src/libslic3r/Technologies.hpp | 8 ++- src/slic3r/GUI/GCodeViewer.cpp | 42 +++++++++------ src/slic3r/GUI/GCodeViewer.hpp | 10 +++- src/slic3r/GUI/GLCanvas3D.cpp | 11 ++-- src/slic3r/GUI/GLCanvas3D.hpp | 2 + src/slic3r/GUI/GUI_Preview.cpp | 10 ++-- src/slic3r/GUI/GUI_Preview.hpp | 2 + src/slic3r/GUI/KBShortcutsDialog.cpp | 10 ++-- src/slic3r/GUI/Plater.cpp | 53 ++----------------- 15 files changed, 125 insertions(+), 174 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index d7db315a7d..915694d12f 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -779,8 +779,7 @@ void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_ m_processor.process_file(path_tmp); if (result != nullptr) *result = std::move(m_processor.extract_result()); -#endif // ENABLE_GCODE_VIEWER - +#else GCodeTimeEstimator::PostProcessData normal_data = m_normal_time_estimator.get_post_process_data(); GCodeTimeEstimator::PostProcessData silent_data = m_silent_time_estimator.get_post_process_data(); @@ -789,21 +788,19 @@ void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_ BOOST_LOG_TRIVIAL(debug) << "Time estimator post processing" << log_memory_info(); GCodeTimeEstimator::post_process(path_tmp, 60.0f, remaining_times_enabled ? &normal_data : nullptr, (remaining_times_enabled && m_silent_time_estimator_enabled) ? &silent_data : nullptr); - if (remaining_times_enabled) - { + if (remaining_times_enabled) { m_normal_time_estimator.reset(); if (m_silent_time_estimator_enabled) m_silent_time_estimator.reset(); } -#if !ENABLE_GCODE_VIEWER // starts analyzer calculations if (m_enable_analyzer) { BOOST_LOG_TRIVIAL(debug) << "Preparing G-code preview data" << log_memory_info(); m_analyzer.calc_gcode_preview_data(*preview_data, [print]() { print->throw_if_canceled(); }); m_analyzer.reset(); } -#endif // !ENABLE_GCODE_VIEWER +#endif // ENABLE_GCODE_VIEWER if (rename_file(path_tmp, path)) throw std::runtime_error( @@ -820,7 +817,8 @@ void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_ // free functions called by GCode::_do_export() namespace DoExport { - static void init_time_estimators(const PrintConfig &config, GCodeTimeEstimator &normal_time_estimator, GCodeTimeEstimator &silent_time_estimator, bool &silent_time_estimator_enabled) +#if !ENABLE_GCODE_VIEWER + static void init_time_estimators(const PrintConfig &config, GCodeTimeEstimator &normal_time_estimator, GCodeTimeEstimator &silent_time_estimator, bool &silent_time_estimator_enabled) { // resets time estimators normal_time_estimator.reset(); @@ -892,10 +890,12 @@ namespace DoExport { normal_time_estimator.set_filament_unload_times(config.filament_unload_time.values); } } +#endif // !ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER - static void init_gcode_processor(const PrintConfig& config, GCodeProcessor& processor, bool silent_time_estimator_enabled) + static void init_gcode_processor(const PrintConfig& config, GCodeProcessor& processor, bool& silent_time_estimator_enabled) { + silent_time_estimator_enabled = (config.gcode_flavor == gcfMarlin) && config.silent_mode; processor.reset(); processor.apply_config(config); processor.enable_stealth_time_estimator(silent_time_estimator_enabled); @@ -1049,10 +1049,12 @@ namespace DoExport { // Fill in print_statistics and return formatted string containing filament statistics to be inserted into G-code comment section. static std::string update_print_stats_and_format_filament_stats( - const GCodeTimeEstimator &normal_time_estimator, +#if !ENABLE_GCODE_VIEWER + const GCodeTimeEstimator &normal_time_estimator, const GCodeTimeEstimator &silent_time_estimator, const bool silent_time_estimator_enabled, - const bool has_wipe_tower, +#endif // !ENABLE_GCODE_VIEWER + const bool has_wipe_tower, const WipeTowerData &wipe_tower_data, const std::vector &extruders, PrintStatistics &print_statistics) @@ -1060,21 +1062,13 @@ namespace DoExport { std::string filament_stats_string_out; print_statistics.clear(); -#if ENABLE_GCODE_VIEWER -#if ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR - print_statistics.estimated_normal_print_time_str = normal_time_estimator.get_time_dhm/*s*/(); - print_statistics.estimated_silent_print_time_str = silent_time_estimator_enabled ? silent_time_estimator.get_time_dhm/*s*/() : "N/A"; - print_statistics.estimated_normal_custom_gcode_print_times_str = normal_time_estimator.get_custom_gcode_times_dhm(true); - if (silent_time_estimator_enabled) - print_statistics.estimated_silent_custom_gcode_print_times_str = silent_time_estimator.get_custom_gcode_times_dhm(true); -#endif // ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR -#else +#if !ENABLE_GCODE_VIEWER print_statistics.estimated_normal_print_time = normal_time_estimator.get_time_dhm/*s*/(); print_statistics.estimated_silent_print_time = silent_time_estimator_enabled ? silent_time_estimator.get_time_dhm/*s*/() : "N/A"; print_statistics.estimated_normal_custom_gcode_print_times = normal_time_estimator.get_custom_gcode_times_dhm(true); if (silent_time_estimator_enabled) print_statistics.estimated_silent_custom_gcode_print_times = silent_time_estimator.get_custom_gcode_times_dhm(true); -#endif // ENABLE_GCODE_VIEWER +#endif // !ENABLE_GCODE_VIEWER print_statistics.total_toolchanges = std::max(0, wipe_tower_data.number_of_toolchanges); if (! extruders.empty()) { std::pair out_filament_used_mm ("; filament used [mm] = ", 0); @@ -1165,12 +1159,13 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu { PROFILE_FUNC(); - DoExport::init_time_estimators(print.config(), - // modifies the following: - m_normal_time_estimator, m_silent_time_estimator, m_silent_time_estimator_enabled); #if ENABLE_GCODE_VIEWER + // modifies m_silent_time_estimator_enabled DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled); #else + DoExport::init_time_estimators(print.config(), + // modifies the following: + m_normal_time_estimator, m_silent_time_estimator, m_silent_time_estimator_enabled); DoExport::init_gcode_analyzer(print.config(), m_analyzer); #endif // ENABLE_GCODE_VIEWER @@ -1274,12 +1269,13 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu print.throw_if_canceled(); // adds tags for time estimators - if (print.config().remaining_times.value) - { +#if !ENABLE_GCODE_VIEWER + if (print.config().remaining_times.value) { _writeln(file, GCodeTimeEstimator::Normal_First_M73_Output_Placeholder_Tag); if (m_silent_time_estimator_enabled) _writeln(file, GCodeTimeEstimator::Silent_First_M73_Output_Placeholder_Tag); } +#endif // !ENABLE_GCODE_VIEWER // Prepare the helper object for replacing placeholders in custom G-code and output filename. m_placeholder_parser = print.placeholder_parser(); @@ -1574,25 +1570,31 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu _write(file, m_writer.postamble()); // adds tags for time estimators +#if !ENABLE_GCODE_VIEWER if (print.config().remaining_times.value) { _writeln(file, GCodeTimeEstimator::Normal_Last_M73_Output_Placeholder_Tag); if (m_silent_time_estimator_enabled) _writeln(file, GCodeTimeEstimator::Silent_Last_M73_Output_Placeholder_Tag); } +#endif // !ENABLE_GCODE_VIEWER print.throw_if_canceled(); // calculates estimated printing time +#if !ENABLE_GCODE_VIEWER m_normal_time_estimator.calculate_time(false); if (m_silent_time_estimator_enabled) m_silent_time_estimator.calculate_time(false); +#endif // !ENABLE_GCODE_VIEWER // Get filament stats. _write(file, DoExport::update_print_stats_and_format_filament_stats( // Const inputs - m_normal_time_estimator, m_silent_time_estimator, m_silent_time_estimator_enabled, - has_wipe_tower, print.wipe_tower_data(), +#if !ENABLE_GCODE_VIEWER + m_normal_time_estimator, m_silent_time_estimator, m_silent_time_estimator_enabled, +#endif // !ENABLE_GCODE_VIEWER + has_wipe_tower, print.wipe_tower_data(), m_writer.extruders(), // Modifies print.m_print_statistics)); @@ -1601,9 +1603,11 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu _write_format(file, "; total filament cost = %.1lf\n", print.m_print_statistics.total_cost); if (print.m_print_statistics.total_toolchanges > 0) _write_format(file, "; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges); +#if !ENABLE_GCODE_VIEWER _write_format(file, "; estimated printing time (normal mode) = %s\n", m_normal_time_estimator.get_time_dhms().c_str()); if (m_silent_time_estimator_enabled) _write_format(file, "; estimated printing time (silent mode) = %s\n", m_silent_time_estimator.get_time_dhms().c_str()); +#endif // !ENABLE_GCODE_VIEWER // Append full config. _write(file, "\n"); @@ -1879,9 +1883,9 @@ namespace ProcessLayer #else // add tag for analyzer gcode += "; " + GCodeAnalyzer::Color_Change_Tag + ",T" + std::to_string(m600_extruder_before_layer) + "\n"; -#endif // ENABLE_GCODE_VIEWER // add tag for time estimator - gcode += "; " + GCodeTimeEstimator::Color_Change_Tag + "\n"; + gcode += "; " + GCodeTimeEstimator::Color_Change_Tag + "\n"; +#endif // ENABLE_GCODE_VIEWER if (!single_extruder_printer && m600_extruder_before_layer >= 0 && first_extruder_id != (unsigned)m600_extruder_before_layer // && !MMU1 @@ -1910,10 +1914,12 @@ namespace ProcessLayer //! FIXME_in_fw show message during print pause if (!pause_print_msg.empty()) gcode += "M117 " + pause_print_msg + "\n"; - // add tag for time estimator +#if !ENABLE_GCODE_VIEWER + // add tag for time estimator gcode += "; " + GCodeTimeEstimator::Pause_Print_Tag + "\n"; gcode += config.pause_print_gcode; - } +#endif // !ENABLE_GCODE_VIEWER + } else { #if ENABLE_GCODE_VIEWER @@ -2422,14 +2428,14 @@ void GCode::process_layer( #endif /* HAS_PRESSURE_EQUALIZER */ _write(file, gcode); - BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z << +#if !ENABLE_GCODE_VIEWER + BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z << ", time estimator memory: " << format_memsize_MB(m_normal_time_estimator.memory_used() + (m_silent_time_estimator_enabled ? m_silent_time_estimator.memory_used() : 0)) << -#if !ENABLE_GCODE_VIEWER ", analyzer memory: " << format_memsize_MB(m_analyzer.memory_used()) << -#endif // !ENABLE_GCODE_VIEWER log_memory_info(); +#endif // !ENABLE_GCODE_VIEWER } void GCode::apply_print_config(const PrintConfig &print_config) @@ -3078,10 +3084,12 @@ void GCode::_write(FILE* file, const char *what) // writes string to file fwrite(gcode, 1, ::strlen(gcode), file); +#if !ENABLE_GCODE_VIEWER // updates time estimator and gcode lines vector m_normal_time_estimator.add_gcode_block(gcode); if (m_silent_time_estimator_enabled) m_silent_time_estimator.add_gcode_block(gcode); +#endif // !ENABLE_GCODE_VIEWER } } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 69f98bfa52..6f97d5dbd3 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -17,8 +17,8 @@ #include "GCode/GCodeProcessor.hpp" #else #include "GCode/Analyzer.hpp" -#endif // ENABLE_GCODE_VIEWER #include "GCodeTimeEstimator.hpp" +#endif // ENABLE_GCODE_VIEWER #include "EdgeGrid.hpp" #include "GCode/ThumbnailData.hpp" @@ -179,8 +179,10 @@ public: #endif // !ENABLE_GCODE_VIEWER m_brim_done(false), m_second_layer_things_done(false), +#if !ENABLE_GCODE_VIEWER m_normal_time_estimator(GCodeTimeEstimator::Normal), m_silent_time_estimator(GCodeTimeEstimator::Silent), +#endif // !ENABLE_GCODE_VIEWER m_silent_time_estimator_enabled(false), m_last_obj_copy(nullptr, Point(std::numeric_limits::max(), std::numeric_limits::max())) {} @@ -405,9 +407,11 @@ private: // Index of a last object copy extruded. std::pair m_last_obj_copy; +#if !ENABLE_GCODE_VIEWER // Time estimators GCodeTimeEstimator m_normal_time_estimator; GCodeTimeEstimator m_silent_time_estimator; +#endif // !ENABLE_GCODE_VIEWER bool m_silent_time_estimator_enabled; #if ENABLE_GCODE_VIEWER diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp index 4bfe322ce2..aa9ee2f643 100644 --- a/src/libslic3r/GCodeTimeEstimator.cpp +++ b/src/libslic3r/GCodeTimeEstimator.cpp @@ -9,6 +9,8 @@ #include #include +#if !ENABLE_GCODE_VIEWER + static const float MMMIN_TO_MMSEC = 1.0f / 60.0f; static const float MILLISEC_TO_SEC = 0.001f; static const float INCHES_TO_MM = 25.4f; @@ -722,24 +724,6 @@ namespace Slic3r { return ret; } -#if ENABLE_GCODE_VIEWER -#if ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR - std::vector>> GCodeTimeEstimator::get_custom_gcode_times_dhm(bool include_remaining) const - { - std::vector>> ret; - - float total_time = 0.0f; - for (const auto& [type, time] : m_custom_gcode_times) { - std::string duration = _get_time_dhm(time); - std::string remaining = include_remaining ? _get_time_dhm(m_time - total_time) : ""; - ret.push_back({ type, { duration, remaining } }); - total_time += time; - } - - return ret; - } -#endif // ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR -#else std::vector> GCodeTimeEstimator::get_custom_gcode_times_dhm(bool include_remaining) const { std::vector> ret; @@ -760,7 +744,6 @@ namespace Slic3r { return ret; } -#endif // ENABLE_GCODE_VIEWER // Return an estimate of the memory consumed by the time estimator. size_t GCodeTimeEstimator::memory_used() const @@ -1690,3 +1673,5 @@ namespace Slic3r { } #endif // ENABLE_MOVE_STATS } + +#endif // !ENABLE_GCODE_VIEWER diff --git a/src/libslic3r/GCodeTimeEstimator.hpp b/src/libslic3r/GCodeTimeEstimator.hpp index 3b92f02692..0dd3407cb0 100644 --- a/src/libslic3r/GCodeTimeEstimator.hpp +++ b/src/libslic3r/GCodeTimeEstimator.hpp @@ -6,6 +6,8 @@ #include "GCodeReader.hpp" #include "CustomGCode.hpp" +#if !ENABLE_GCODE_VIEWER + #define ENABLE_MOVE_STATS 0 namespace Slic3r { @@ -370,13 +372,7 @@ namespace Slic3r { // Returns the estimated time, in format DDd HHh MMm, for each custom_gcode // If include_remaining==true the strings will be formatted as: "time for custom_gcode (remaining time at color start)" -#if ENABLE_GCODE_VIEWER -#if ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR - std::vector>> get_custom_gcode_times_dhm(bool include_remaining) const; -#endif // ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR -#else std::vector> get_custom_gcode_times_dhm(bool include_remaining) const; -#endif // ENABLE_GCODE_VIEWER // Return an estimate of the memory consumed by the time estimator. size_t memory_used() const; @@ -487,4 +483,6 @@ namespace Slic3r { } /* namespace Slic3r */ +#endif // !ENABLE_GCODE_VIEWER + #endif /* slic3r_GCodeTimeEstimator_hpp_ */ diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index a48d6f602f..1b1aad2578 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -2190,23 +2190,13 @@ std::string Print::output_filename(const std::string &filename_base) const DynamicConfig PrintStatistics::config() const { DynamicConfig config; -#if ENABLE_GCODE_VIEWER -#if ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR - config.set_key_value("print_time", new ConfigOptionString(this->estimated_normal_print_time_str)); - config.set_key_value("normal_print_time", new ConfigOptionString(this->estimated_normal_print_time_str)); - config.set_key_value("silent_print_time", new ConfigOptionString(this->estimated_silent_print_time_str)); -#else - config.set_key_value("print_time", new ConfigOptionString(short_time(get_time_dhms(this->estimated_normal_print_time)))); - config.set_key_value("normal_print_time", new ConfigOptionString(short_time(get_time_dhms(this->estimated_normal_print_time)))); - config.set_key_value("silent_print_time", new ConfigOptionString(short_time(get_time_dhms(this->estimated_silent_print_time)))); -#endif // ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR -#else +#if !ENABLE_GCODE_VIEWER std::string normal_print_time = short_time(this->estimated_normal_print_time); std::string silent_print_time = short_time(this->estimated_silent_print_time); config.set_key_value("print_time", new ConfigOptionString(normal_print_time)); config.set_key_value("normal_print_time", new ConfigOptionString(normal_print_time)); config.set_key_value("silent_print_time", new ConfigOptionString(silent_print_time)); -#endif // ENABLE_GCODE_VIEWER +#endif // !ENABLE_GCODE_VIEWER config.set_key_value("used_filament", new ConfigOptionFloat(this->total_used_filament / 1000.)); config.set_key_value("extruded_volume", new ConfigOptionFloat(this->total_extruded_volume)); config.set_key_value("total_cost", new ConfigOptionFloat(this->total_cost)); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 62c00aa88d..73efbf49a1 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -303,19 +303,12 @@ private: struct PrintStatistics { PrintStatistics() { clear(); } -#if ENABLE_GCODE_VIEWER -#if ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR - std::string estimated_normal_print_time_str; - std::string estimated_silent_print_time_str; - std::vector>> estimated_normal_custom_gcode_print_times_str; - std::vector>> estimated_silent_custom_gcode_print_times_str; -#endif // ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR -#else +#if !ENABLE_GCODE_VIEWER std::string estimated_normal_print_time; std::string estimated_silent_print_time; std::vector> estimated_normal_custom_gcode_print_times; std::vector> estimated_silent_custom_gcode_print_times; -#endif // ENABLE_GCODE_VIEWER +#endif // !ENABLE_GCODE_VIEWER double total_used_filament; double total_extruded_volume; double total_cost; @@ -333,19 +326,12 @@ struct PrintStatistics std::string finalize_output_path(const std::string &path_in) const; void clear() { -#if ENABLE_GCODE_VIEWER -#if ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR - estimated_normal_print_time_str.clear(); - estimated_silent_print_time_str.clear(); - estimated_normal_custom_gcode_print_times_str.clear(); - estimated_silent_custom_gcode_print_times_str.clear(); -#endif // ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR -#else +#if !ENABLE_GCODE_VIEWER estimated_normal_print_time.clear(); estimated_silent_print_time.clear(); estimated_normal_custom_gcode_print_times.clear(); estimated_silent_custom_gcode_print_times.clear(); -#endif //ENABLE_GCODE_VIEWER +#endif // !ENABLE_GCODE_VIEWER total_used_filament = 0.; total_extruded_volume = 0.; total_cost = 0.; diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index c14e2df52f..75849b689a 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -58,7 +58,11 @@ #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) #define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_SHADERS_EDITOR (0 && ENABLE_GCODE_VIEWER) -#define ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR (1 && ENABLE_GCODE_VIEWER) -#define ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG (1 && ENABLE_GCODE_VIEWER) + +#define TIME_ESTIMATE_NONE 0 +#define TIME_ESTIMATE_DEFAULT 1 +#define TIME_ESTIMATE_MODAL 2 +#define TIME_ESTIMATE_LEGEND 3 +#define GCODE_VIEWER_TIME_ESTIMATE TIME_ESTIMATE_MODAL #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index e746635ee2..7c5252070d 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -332,7 +332,9 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& wxGetApp().plater()->set_bed_shape(bed_shape, "", "", true); } +#if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE m_time_statistics = gcode_result.time_statistics; +#endif // GCODE_VIEWER_TIME_ESTIMATE } void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std::vector& str_tool_colors) @@ -406,7 +408,9 @@ void GCodeViewer::reset() m_layers_zs = std::vector(); m_layers_z_range = { 0.0, 0.0 }; m_roles = std::vector(); +#if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE m_time_statistics.reset(); +#endif // GCODE_VIEWER_TIME_ESTIMATE #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.reset_all(); @@ -419,7 +423,7 @@ void GCodeViewer::render() const m_statistics.reset_opengl(); #endif // ENABLE_GCODE_VIEWER_STATISTICS -#if ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG +#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_MODAL if (m_roles.empty()) { m_time_estimate_frames_count = 0; return; @@ -427,7 +431,7 @@ void GCodeViewer::render() const #else if (m_roles.empty()) return; -#endif // ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG +#endif // GCODE_VIEWER_TIME_ESTIMATE glsafe(::glEnable(GL_DEPTH_TEST)); render_toolpaths(); @@ -435,7 +439,9 @@ void GCodeViewer::render() const m_sequential_view.marker.render(); render_shells(); render_legend(); +#if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE render_time_estimate(); +#endif // GCODE_VIEWER_TIME_ESTIMATE #if ENABLE_GCODE_VIEWER_STATISTICS render_statistics(); #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -474,9 +480,9 @@ unsigned int GCodeViewer::get_options_visibility_flags() const flags = set_flag(flags, static_cast(Preview::OptionType::Shells), m_shells.visible); flags = set_flag(flags, static_cast(Preview::OptionType::ToolMarker), m_sequential_view.marker.is_visible()); flags = set_flag(flags, static_cast(Preview::OptionType::Legend), is_legend_enabled()); -#if !ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG +#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_DEFAULT flags = set_flag(flags, static_cast(Preview::OptionType::TimeEstimate), is_time_estimate_enabled()); -#endif // !ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG +#endif // GCODE_VIEWER_TIME_ESTIMATE return flags; } @@ -496,9 +502,9 @@ void GCodeViewer::set_options_visibility_from_flags(unsigned int flags) m_shells.visible = is_flag_set(static_cast(Preview::OptionType::Shells)); m_sequential_view.marker.set_visible(is_flag_set(static_cast(Preview::OptionType::ToolMarker))); enable_legend(is_flag_set(static_cast(Preview::OptionType::Legend))); -#if !ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG +#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_DEFAULT enable_time_estimate(is_flag_set(static_cast(Preview::OptionType::TimeEstimate))); -#endif // !ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG +#endif // GCODE_VIEWER_TIME_ESTIMATE } void GCodeViewer::set_layers_z_range(const std::array& layers_z_range) @@ -510,6 +516,7 @@ void GCodeViewer::set_layers_z_range(const std::array& layers_z_range wxGetApp().plater()->update_preview_moves_slider(); } +#if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE void GCodeViewer::enable_time_estimate(bool enable) { m_time_estimate_enabled = enable; @@ -517,6 +524,7 @@ void GCodeViewer::enable_time_estimate(bool enable) wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); } +#endif // GCODE_VIEWER_TIME_ESTIMATE void GCodeViewer::export_toolpaths_to_obj(const char* filename) const { @@ -1682,24 +1690,25 @@ void GCodeViewer::render_legend() const ImGui::PopStyleVar(); } +#if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE void GCodeViewer::render_time_estimate() const { if (!m_time_estimate_enabled) { -#if ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG +#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_MODAL m_time_estimate_frames_count = 0; -#endif // ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG +#endif // GCODE_VIEWER_TIME_ESTIMATE return; } ImGuiWrapper& imgui = *wxGetApp().imgui(); -#if ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG +#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_MODAL // esc if (ImGui::GetIO().KeysDown[27]) { m_time_estimate_enabled = false; return; } -#endif // ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG +#endif // GCODE_VIEWER_TIME_ESTIMATE using Times = std::pair; using TimesList = std::vector>; @@ -1833,7 +1842,7 @@ void GCodeViewer::render_time_estimate() const imgui.text(short_time(get_time_dhms(time))); ImGui::SameLine(offsets[1]); char buf[64]; - ::sprintf(buf, "%.2f%%", 100.0f * percentage); + ::sprintf(buf, "%.1f%%", 100.0f * percentage); ImGuiWindow* window = ImGui::GetCurrentWindow(); ImRect frame_bb; frame_bb.Min = { ImGui::GetCursorScreenPos().x, window->DC.CursorPos.y }; @@ -1978,7 +1987,7 @@ void GCodeViewer::render_time_estimate() const }; Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size(); -#if ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG +#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_MODAL std::string title = _u8L("Estimated printing time"); ImGui::OpenPopup(title.c_str()); @@ -1996,14 +2005,14 @@ void GCodeViewer::render_time_estimate() const } #else imgui.set_next_window_pos(static_cast(cnv_size.get_width()), static_cast(cnv_size.get_height()), ImGuiCond_Always, 1.0f, 1.0f); - ImGui::SetNextWindowSizeConstraints({ 0.0f, 0.0f }, { -1.0f, 0.5f * static_cast(cnv_size.get_height() })); + ImGui::SetNextWindowSizeConstraints({ 0.0f, 0.0f }, { -1.0f, 0.5f * static_cast(cnv_size.get_height()) }); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::SetNextWindowBgAlpha(0.6f); imgui.begin(std::string("Time_estimate"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove); // title imgui.title(_u8L("Estimated printing time")); -#endif // ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG +#endif // GCODE_VIEWER_TIME_ESTIMATE // mode tabs ImGui::BeginTabBar("mode_tabs"); @@ -2029,7 +2038,7 @@ void GCodeViewer::render_time_estimate() const } ImGui::EndTabBar(); -#if ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG +#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_MODAL // this is ugly, but it is the only way to ensure that the dialog is large // enough to show enterely the title // see: https://github.com/ocornut/imgui/issues/3239 @@ -2044,9 +2053,10 @@ void GCodeViewer::render_time_estimate() const } #else imgui.end(); -#endif // ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG +#endif // GCODE_VIEWER_TIME_ESTIMATE ImGui::PopStyleVar(); } +#endif // GCODE_VIEWER_TIME_ESTIMATE #if ENABLE_GCODE_VIEWER_STATISTICS void GCodeViewer::render_statistics() const diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 0a32a97e67..06cd1326fb 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -341,13 +341,15 @@ private: Shells m_shells; EViewType m_view_type{ EViewType::FeatureType }; bool m_legend_enabled{ true }; +#if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE PrintEstimatedTimeStatistics m_time_statistics; -#if ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG +#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_MODAL mutable bool m_time_estimate_enabled{ false }; mutable unsigned int m_time_estimate_frames_count{ 0 }; #else bool m_time_estimate_enabled{ false }; -#endif // ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG +#endif // GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_MODAL +#endif // GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE #if ENABLE_GCODE_VIEWER_STATISTICS mutable Statistics m_statistics; #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -403,8 +405,10 @@ public: bool is_legend_enabled() const { return m_legend_enabled; } void enable_legend(bool enable) { m_legend_enabled = enable; } +#if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE bool is_time_estimate_enabled() const { return m_time_estimate_enabled; } void enable_time_estimate(bool enable); +#endif // GCODE_VIEWER_TIME_ESTIMATE void export_toolpaths_to_obj(const char* filename) const; @@ -416,7 +420,9 @@ private: void render_toolpaths() const; void render_shells() const; void render_legend() const; +#if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE void render_time_estimate() const; +#endif // GCODE_VIEWER_TIME_ESTIMATE #if ENABLE_GCODE_VIEWER_STATISTICS void render_statistics() const; #endif // ENABLE_GCODE_VIEWER_STATISTICS diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 5a1381a779..35be455591 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3109,17 +3109,18 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) break; } #endif // ENABLE_RENDER_PICKING_PASS -#if ENABLE_GCODE_VIEWER +#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_DEFAULT || GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_MODAL case 'T': - case 't': { + case 't': + { if (!m_main_toolbar.is_enabled()) { m_gcode_viewer.enable_time_estimate(!m_gcode_viewer.is_time_estimate_enabled()); m_dirty = true; wxGetApp().plater()->update_preview_bottom_toolbar(); - } + } break; - } -#endif // ENABLE_GCODE_VIEWER + } +#endif // GCODE_VIEWER_TIME_ESTIMATE case 'Z': #if ENABLE_GCODE_VIEWER case 'z': diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 15bb3e6f06..ee1d32abb8 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -558,7 +558,9 @@ public: void reset_gcode_toolpaths() { m_gcode_viewer.reset(); } const GCodeViewer::SequentialView& get_gcode_sequential_view() const { return m_gcode_viewer.get_sequential_view(); } void update_gcode_sequential_view_current(unsigned int first, unsigned int last) { m_gcode_viewer.update_sequential_view_current(first, last); } +#if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE bool is_time_estimate_enabled() const { return m_gcode_viewer.is_time_estimate_enabled(); } +#endif // GCODE_VIEWER_TIME_ESTIMATE #endif // ENABLE_GCODE_VIEWER void toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 98c02322ee..38657a460a 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -323,12 +323,12 @@ bool Preview::init(wxWindow* parent, Model* model) get_option_type_string(OptionType::CustomGCodes) + "|0|" + get_option_type_string(OptionType::Shells) + "|0|" + get_option_type_string(OptionType::ToolMarker) + "|0|" + -#if ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG - get_option_type_string(OptionType::Legend) + "|1" -#else +#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_DEFAULT get_option_type_string(OptionType::Legend) + "|1|" + get_option_type_string(OptionType::TimeEstimate) + "|1" -#endif // ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG +#else + get_option_type_string(OptionType::Legend) + "|1" +#endif // GCODE_VIEWER_TIME_ESTIMATE ); Slic3r::GUI::create_combochecklist(m_combochecklist_options, GUI::into_u8(_L("Options")), options_items); #else @@ -1459,7 +1459,9 @@ wxString Preview::get_option_type_string(OptionType type) const case OptionType::Shells: { return _L("Shells"); } case OptionType::ToolMarker: { return _L("Tool marker"); } case OptionType::Legend: { return _L("Legend"); } +#if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE case OptionType::TimeEstimate: { return _L("Estimated printing time"); } +#endif // GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE default: { return ""; } } } diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index ff3bf41371..c74dccc5c1 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -150,7 +150,9 @@ public: Shells, ToolMarker, Legend, +#if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE TimeEstimate +#endif // GCODE_VIEWER_TIME_ESTIMATE }; Preview(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process, diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index fc6bc98914..2c65673cd0 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -204,13 +204,11 @@ void KBShortcutsDialog::fill_shortcuts() { "U", L("Upper Layer") }, { "D", L("Lower Layer") }, { "L", L("Show/Hide Legend") }, -#if ENABLE_GCODE_VIEWER -#if ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG - { "T", L("Show Estimated printing time") } -#else +#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_DEFAULT { "T", L("Show/Hide Estimated printing time") } -#endif // ENABLE_GCODE_VIEWER_MODAL_TIME_ESTIMATE_DIALOG -#endif // ENABLE_GCODE_VIEWER +#elif GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_MODAL + { "T", L("Show Estimated printing time") } +#endif // GCODE_VIEWER_TIME_ESTIMATE }; m_full_shortcuts.push_back(std::make_pair(_L("Preview"), preview_shortcuts)); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index e2c141e4be..a03d31bffb 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1324,15 +1324,10 @@ void Sidebar::update_sliced_info_sizer() p->sliced_info->SetTextAndShow(siCost, info_text, new_label); #if ENABLE_GCODE_VIEWER -#if ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR - if (p->plater->get_current_canvas3D()->is_time_estimate_enabled() || (ps.estimated_normal_print_time_str == "N/A" && ps.estimated_silent_print_time_str == "N/A")) + // hide the estimate time + p->sliced_info->SetTextAndShow(siEstimatedTime, "N/A"); #else - if (p->plater->get_current_canvas3D()->is_time_estimate_enabled() || (ps.estimated_normal_print_time <= 0.0f && ps.estimated_silent_print_time <= 0.0f)) -#endif // ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR -#else - if (ps.estimated_normal_print_time == "N/A" && ps.estimated_silent_print_time == "N/A") -#endif // ENABLE_GCODE_VIEWER p->sliced_info->SetTextAndShow(siEstimatedTime, "N/A"); else { new_label = _L("Estimated printing time") + ":"; @@ -1340,15 +1335,7 @@ void Sidebar::update_sliced_info_sizer() wxString str_color = _L("Color"); wxString str_pause = _L("Pause"); -#if ENABLE_GCODE_VIEWER -#if ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR - auto fill_labels = [str_color, str_pause](const std::vector>>& times, -#else - auto fill_labels = [str_color, str_pause](const std::vector>>& times, -#endif // ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR -#else auto fill_labels = [str_color, str_pause](const std::vector>& times, -#endif // ENABLE_GCODE_VIEWER wxString& new_label, wxString& info_text) { int color_change_count = 0; @@ -1366,41 +1353,10 @@ void Sidebar::update_sliced_info_sizer() if (i != (int)times.size() - 1 && times[i].first == CustomGCode::PausePrint) new_label += format_wxstr(" -> %1%", str_pause); -#if ENABLE_GCODE_VIEWER -#if ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR - info_text += format_wxstr("\n%1% (%2%)", times[i].second.first, times[i].second.second); -#else - info_text += format_wxstr("\n%1% (%2%)", short_time(get_time_dhms(times[i].second.first)), short_time(get_time_dhms(times[i].second.second))); -#endif // ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR -#else info_text += format_wxstr("\n%1%", times[i].second); -#endif // ENABLE_GCODE_VIEWER } }; -#if ENABLE_GCODE_VIEWER -#if ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR - if (ps.estimated_normal_print_time_str != "N/A") { - new_label += format_wxstr("\n - %1%", _L("normal mode")); - info_text += format_wxstr("\n%1%", ps.estimated_normal_print_time_str); - fill_labels(ps.estimated_normal_custom_gcode_print_times_str, new_label, info_text); - } - if (ps.estimated_silent_print_time_str != "N/A") { - new_label += format_wxstr("\n - %1%", _L("stealth mode")); - info_text += format_wxstr("\n%1%", ps.estimated_silent_print_time_str); - fill_labels(ps.estimated_silent_custom_gcode_print_times_str, new_label, info_text); -#else - if (ps.estimated_normal_print_time > 0.0f) { - new_label += format_wxstr("\n - %1%", _L("normal mode")); - info_text += format_wxstr("\n%1%", short_time(get_time_dhms(ps.estimated_normal_print_time))); - fill_labels(ps.estimated_normal_custom_gcode_print_times, new_label, info_text); - } - if (ps.estimated_silent_print_time > 0.0f) { - new_label += format_wxstr("\n - %1%", _L("stealth mode")); - info_text += format_wxstr("\n%1%", short_time(get_time_dhms(ps.estimated_silent_print_time))); - fill_labels(ps.estimated_silent_custom_gcode_print_times, new_label, info_text); -#endif // ENABLE_GCODE_VIEWER_USE_OLD_TIME_ESTIMATOR -#else if (ps.estimated_normal_print_time != "N/A") { new_label += format_wxstr("\n - %1%", _L("normal mode")); info_text += format_wxstr("\n%1%", ps.estimated_normal_print_time); @@ -1416,10 +1372,10 @@ void Sidebar::update_sliced_info_sizer() new_label += format_wxstr("\n - %1%", _L("stealth mode")); info_text += format_wxstr("\n%1%", ps.estimated_silent_print_time); fill_labels(ps.estimated_silent_custom_gcode_print_times, new_label, info_text); -#endif // ENABLE_GCODE_VIEWER } p->sliced_info->SetTextAndShow(siEstimatedTime, info_text, new_label); } +#endif // !ENABLE_GCODE_VIEWER // if there is a wipe tower, insert number of toolchanges info into the array: p->sliced_info->SetTextAndShow(siWTNumbetOfToolchanges, is_wipe_tower ? wxString::Format("%.d", ps.total_toolchanges) : "N/A"); @@ -1428,9 +1384,8 @@ void Sidebar::update_sliced_info_sizer() p->sliced_info->SetTextAndShow(siMateril_unit, "N/A"); } } -#if ENABLE_GCODE_VIEWER + Layout(); -#endif // ENABLE_GCODE_VIEWER } void Sidebar::show_sliced_info_sizer(const bool show) From 171acf094c3b3a7320ab665a041a4bf2ba5b62fb Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 5 Aug 2020 16:34:01 +0200 Subject: [PATCH 253/503] Change license of libnest2d to LGPLv3 --- src/libnest2d/LICENSE.txt | 816 ++++++++------------------------------ 1 file changed, 160 insertions(+), 656 deletions(-) diff --git a/src/libnest2d/LICENSE.txt b/src/libnest2d/LICENSE.txt index dba13ed2dd..07b1d92c0e 100644 --- a/src/libnest2d/LICENSE.txt +++ b/src/libnest2d/LICENSE.txt @@ -1,661 +1,165 @@ - GNU AFFERO GENERAL PUBLIC LICENSE - Version 3, 19 November 2007 + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - Preamble - - The GNU Affero General Public License is a free, copyleft license for -software and other kinds of works, specifically designed to ensure -cooperation with the community in the case of network server software. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -our General Public Licenses are intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - Developers that use our General Public Licenses protect your rights -with two steps: (1) assert copyright on the software, and (2) offer -you this License which gives you legal permission to copy, distribute -and/or modify the software. - - A secondary benefit of defending all users' freedom is that -improvements made in alternate versions of the program, if they -receive widespread use, become available for other developers to -incorporate. Many developers of free software are heartened and -encouraged by the resulting cooperation. However, in the case of -software used on network servers, this result may fail to come about. -The GNU General Public License permits making a modified version and -letting the public access it on a server without ever releasing its -source code to the public. - - The GNU Affero General Public License is designed specifically to -ensure that, in such cases, the modified source code becomes available -to the community. It requires the operator of a network server to -provide the source code of the modified version running there to the -users of that server. Therefore, public use of a modified version, on -a publicly accessible server, gives the public access to the source -code of the modified version. - - An older license, called the Affero General Public License and -published by Affero, was designed to accomplish similar goals. This is -a different license, not a version of the Affero GPL, but Affero has -released a new version of the Affero GPL which permits relicensing under -this license. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU Affero General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Remote Network Interaction; Use with the GNU General Public License. - - Notwithstanding any other provision of this License, if you modify the -Program, your modified version must prominently offer all users -interacting with it remotely through a computer network (if your version -supports such interaction) an opportunity to receive the Corresponding -Source of your version by providing access to the Corresponding Source -from a network server at no charge, through some standard or customary -means of facilitating copying of software. This Corresponding Source -shall include the Corresponding Source for any work covered by version 3 -of the GNU General Public License that is incorporated pursuant to the -following paragraph. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the work with which it is combined will remain governed by version -3 of the GNU General Public License. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU Affero General Public License from time to time. Such new versions -will be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU Affero General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU Affero General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU Affero General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If your software can interact with users remotely through a computer -network, you should also make sure that it provides a way for users to -get its source. For example, if your program is a web application, its -interface could display a "Source" link that leads users to an archive -of the code. There are many ways you could offer source, and different -solutions will be better for different programs; see section 13 for the -specific requirements. - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU AGPL, see -. + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. \ No newline at end of file From 1674d2af291b1933fd28258e5415501079f23ce9 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 5 Aug 2020 19:25:04 +0200 Subject: [PATCH 254/503] UnsavedChangesDialog improvements: * support markup text and colored icons for cells + Extended BitmapTextRenderer for using of markup text --- src/slic3r/GUI/ObjectDataViewModel.cpp | 76 +++- src/slic3r/GUI/ObjectDataViewModel.hpp | 19 +- src/slic3r/GUI/Search.cpp | 8 + src/slic3r/GUI/Search.hpp | 10 +- src/slic3r/GUI/Tab.cpp | 12 +- src/slic3r/GUI/UnsavedChangesDialog.cpp | 533 ++++++++++++++++++++---- src/slic3r/GUI/UnsavedChangesDialog.hpp | 108 +++-- 7 files changed, 664 insertions(+), 102 deletions(-) diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 336475d2e0..badaa7a041 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -9,6 +9,12 @@ #include #include +#include "wx/generic/private/markuptext.h" +#include "wx/generic/private/rowheightcache.h" +#include "wx/generic/private/widthcalc.h" +#if wxUSE_ACCESSIBILITY +#include "wx/private/markupparser.h" +#endif // wxUSE_ACCESSIBILITY namespace Slic3r { @@ -1575,9 +1581,44 @@ wxDataViewRenderer(wxT("PrusaDataViewBitmapText"), mode, align) } #endif // ENABLE_NONCUSTOM_DATA_VIEW_RENDERING +BitmapTextRenderer::~BitmapTextRenderer() +{ +#ifdef SUPPORTS_MARKUP + if (m_markupText) + delete m_markupText; +#endif // SUPPORTS_MARKUP +} + +#ifdef SUPPORTS_MARKUP +void BitmapTextRenderer::EnableMarkup(bool enable) +{ + if (enable) + { + if (!m_markupText) + { + m_markupText = new wxItemMarkupText(wxString()); + } + } + else + { + if (m_markupText) + { + delete m_markupText; + m_markupText = nullptr; + } + } +} +#endif // SUPPORTS_MARKUP + bool BitmapTextRenderer::SetValue(const wxVariant &value) { m_value << value; + +#ifdef SUPPORTS_MARKUP + if (m_markupText) + m_markupText->SetMarkup(m_value.GetText()); +#endif // SUPPORTS_MARKUP + return true; } @@ -1589,6 +1630,11 @@ bool BitmapTextRenderer::GetValue(wxVariant& WXUNUSED(value)) const #if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY wxString BitmapTextRenderer::GetAccessibleDescription() const { +#ifdef SUPPORTS_MARKUP + if (m_markupText) + return wxMarkupParser::Strip(m_text); +#endif // SUPPORTS_MARKUP + return m_value.GetText(); } #endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING @@ -1609,7 +1655,17 @@ bool BitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state) xoffset = icon_sz.x + 4; } - RenderText(m_value.GetText(), xoffset, rect, dc, state); +#ifdef SUPPORTS_MARKUP + if (m_markupText) + { + int flags = 0; + + rect.x += xoffset; + m_markupText->Render(GetView(), *dc, rect, flags, GetEllipsizeMode()); + } + else +#endif // SUPPORTS_MARKUP + RenderText(m_value.GetText(), xoffset, rect, dc, state); return true; } @@ -1618,7 +1674,23 @@ wxSize BitmapTextRenderer::GetSize() const { if (!m_value.GetText().empty()) { - wxSize size = GetTextExtent(m_value.GetText()); + wxSize size; +#ifdef SUPPORTS_MARKUP + if (m_markupText) + { + wxDataViewCtrl* const view = GetView(); + wxClientDC dc(view); + if (GetAttr().HasFont()) + dc.SetFont(GetAttr().GetEffectiveFont(view->GetFont())); + + size = m_markupText->Measure(dc); + } + else +#endif // SUPPORTS_MARKUP + size = GetTextExtent(m_value.GetText()); + + int lines = m_value.GetText().Freq('\n') + 1; + size.SetHeight(size.GetHeight() * lines); if (m_value.GetBitmap().IsOk()) size.x += m_value.GetBitmap().GetWidth() + 4; diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index c184842664..c8545e4a4f 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -2,9 +2,10 @@ #define slic3r_GUI_ObjectDataViewModel_hpp_ #include - #include +#include "GUI_App.hpp" + namespace Slic3r { enum class ModelVolumeType : int; @@ -83,9 +84,19 @@ public: ) : wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align), m_parent(parent) - {} + { +#ifdef SUPPORTS_MARKUP + m_markupText = nullptr; +#endif // SUPPORTS_MARKUP + } #endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING + ~BitmapTextRenderer(); + +#ifdef SUPPORTS_MARKUP + void EnableMarkup(bool enable = true); +#endif // SUPPORTS_MARKUP + bool SetValue(const wxVariant& value); bool GetValue(wxVariant& value) const; #if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY @@ -114,6 +125,10 @@ private: DataViewBitmapText m_value; bool m_was_unusable_symbol{ false }; wxWindow* m_parent{ nullptr }; + +#ifdef SUPPORTS_MARKUP + class wxItemMarkupText* m_markupText; +#endif // SUPPORTS_MARKUP }; diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index 6eab6b72ac..d13ef469f1 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -321,6 +321,14 @@ const Option& OptionsSearcher::get_option(size_t pos_in_filter) const return options[found[pos_in_filter].option_idx]; } +const Option& OptionsSearcher::get_option(const std::string& opt_key) const +{ + auto it = std::upper_bound(options.begin(), options.end(), Option({ boost::nowide::widen(opt_key) })); + assert(it != options.end()); + + return options[it - options.begin()]; +} + void OptionsSearcher::add_key(const std::string& opt_key, const wxString& group, const wxString& category) { groups_and_categories[opt_key] = GroupAndCategory{group, category}; diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp index 8202222e9d..a57e0d015d 100644 --- a/src/slic3r/GUI/Search.hpp +++ b/src/slic3r/GUI/Search.hpp @@ -37,8 +37,8 @@ struct GroupAndCategory { }; struct Option { - bool operator<(const Option& other) const { return other.label > this->label; } - bool operator>(const Option& other) const { return other.label < this->label; } +// bool operator<(const Option& other) const { return other.label > this->label; } + bool operator<(const Option& other) const { return other.opt_key > this->opt_key; } // Fuzzy matching works at a character level. Thus matching with wide characters is a safer bet than with short characters, // though for some languages (Chinese?) it may not work correctly. @@ -116,12 +116,18 @@ public: const FoundOption& operator[](const size_t pos) const noexcept { return found[pos]; } const Option& get_option(size_t pos_in_filter) const; + const Option& get_option(const std::string& opt_key) const; const std::vector& found_options() { return found; } const GroupAndCategory& get_group_and_category (const std::string& opt_key) { return groups_and_categories[opt_key]; } std::string& search_string() { return search_line; } void set_printer_technology(PrinterTechnology pt) { printer_technology = pt; } + + void sort_options_by_opt_key() { + std::sort(options.begin(), options.end(), [](const Option& o1, const Option& o2) { + return o1.opt_key < o2.opt_key; }); + } }; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 49317f802f..dbb8761d3a 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3133,8 +3133,16 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr*/, const std::string& new_printer_name /*= ""*/) { UnsavedChangesDialog dlg(m_type); - dlg.ShowModal(); - + if (dlg.ShowModal() == wxID_CANCEL) + return false; + if (dlg.just_continue()) + return true; + if (dlg.save_preset()) + // save selected changes + return false; + if (dlg.move_preset()) + // move selected changes + return false; if (presets == nullptr) presets = m_presets; // Display a dialog showing the dirty options in a human readable form. diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 21da295d40..46f086765c 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -13,11 +13,12 @@ #include "GUI_App.hpp" #include "Plater.hpp" #include "Tab.hpp" +#include "ObjectDataViewModel.hpp" #define FTS_FUZZY_MATCH_IMPLEMENTATION #include "fts_fuzzy_match.h" -#include "imgui/imconfig.h" +#include "BitmapCache.hpp" using boost::optional; @@ -29,12 +30,39 @@ namespace GUI { // ModelNode: a node inside UnsavedChangesModel // ---------------------------------------------------------------------------- +static const std::map type_icon_names = { + {Preset::TYPE_PRINT, "cog" }, + {Preset::TYPE_SLA_PRINT, "cog" }, + {Preset::TYPE_FILAMENT, "spool" }, + {Preset::TYPE_SLA_MATERIAL, "resin" }, + {Preset::TYPE_PRINTER, "sla_printer" }, +}; + +static std::string black = "#000000"; +static std::string grey = "#808080"; +static std::string orange = "#ed6b21"; + +static void color_string(wxString& str, const std::string& color) +{ +#ifdef SUPPORTS_MARKUP + str = from_u8((boost::format("%2%") % color % into_u8(str)).str()); +#endif +} + +static void make_string_bold(wxString& str) +{ +#ifdef SUPPORTS_MARKUP + str = from_u8((boost::format("%1%") % into_u8(str)).str()); +#endif +} + // preset(root) node -ModelNode::ModelNode(const wxString& text, Preset::Type preset_type) : +ModelNode::ModelNode(Preset::Type preset_type, const wxString& text) : m_parent(nullptr), m_preset_type(preset_type), m_text(text) { + m_icon = create_scaled_bitmap(type_icon_names.at(preset_type)); } // group node @@ -42,38 +70,230 @@ ModelNode::ModelNode(ModelNode* parent, const wxString& text, const std::string& m_parent(parent), m_text(text) { + m_icon = create_scaled_bitmap(icon_name); } -// group node -ModelNode::ModelNode(ModelNode* parent, const wxString& text, bool is_option) : +// category node +ModelNode::ModelNode(ModelNode* parent, const wxString& text) : m_parent(parent), - m_text(text), - m_container(!is_option) + m_text(text) { } +wxBitmap ModelNode::get_bitmap(const wxString& color) +{ + /* It's supposed that standard size of an icon is 48px*16px for 100% scaled display. + * So set sizes for solid_colored icons used for filament preset + * and scale them in respect to em_unit value + */ + const double em = em_unit(m_parent_win); + const int icon_width = lround(6.4 * em); + const int icon_height = lround(1.6 * em); + + BitmapCache bmp_cache; + unsigned char rgb[3]; + BitmapCache::parse_color(into_u8(color), rgb); + // there is no need to scale created solid bitmap + return bmp_cache.mksolid(icon_width, icon_height, rgb, true); +} + +// option node +ModelNode::ModelNode(ModelNode* parent, const wxString& text, const wxString& old_value, const wxString& new_value) : + m_parent(parent), + m_old_color(old_value.StartsWith("#") ? old_value : ""), + m_new_color(new_value.StartsWith("#") ? new_value : ""), + m_container(false), + m_text(text), + m_old_value(old_value), + m_new_value(new_value) +{ + // check if old/new_value is color + if (m_old_color.IsEmpty()) { + if (!m_new_color.IsEmpty()) + m_old_value = _L("Undef"); + } + else { + m_old_color_bmp = get_bitmap(m_old_color); + m_old_value.Clear(); + } + + if (m_new_color.IsEmpty()) { + if (!m_old_color.IsEmpty()) + m_new_value = _L("Undef"); + } + else { + m_new_color_bmp = get_bitmap(m_new_color); + m_new_value.Clear(); + } + + // "color" strings + color_string(m_old_value, black); + color_string(m_new_value, orange); +} + +void ModelNode::UpdateEnabling() +{ +#ifdef SUPPORTS_MARKUP + auto change_text_color = [](wxString& str, const std::string& clr_from, const std::string& clr_to) + { + std::string old_val = into_u8(str); + boost::replace_all(old_val, clr_from, clr_to); + str = from_u8(old_val); + }; + + if (!m_toggle) { + change_text_color(m_text, black, grey); + change_text_color(m_old_value, black, grey); + change_text_color(m_new_value, orange,grey); + } + else { + change_text_color(m_text, grey, black); + change_text_color(m_old_value, grey, black); + change_text_color(m_new_value, grey, orange); + } +#endif + // update icons for the colors + if (!m_old_color.IsEmpty()) + m_old_color_bmp = get_bitmap(m_toggle? m_old_color : grey); + if (!m_new_color.IsEmpty()) + m_new_color_bmp = get_bitmap(m_toggle? m_new_color : grey); +} + // ---------------------------------------------------------------------------- // UnsavedChangesModel // ---------------------------------------------------------------------------- -UnsavedChangesModel::UnsavedChangesModel(wxWindow* parent) +UnsavedChangesModel::UnsavedChangesModel(wxWindow* parent) : + m_parent_win(parent) { - int icon_id = 0; - for (const std::string& icon : { "cog", "printer", "sla_printer", "spool", "resin" }) - m_icon[icon_id++] = ScalableBitmap(parent, icon); - - m_root = new ModelNode("Preset", Preset::TYPE_INVALID); } UnsavedChangesModel::~UnsavedChangesModel() { - delete m_root; + for (ModelNode* preset_node : m_preset_nodes) + delete preset_node; +} + +wxDataViewItem UnsavedChangesModel::AddPreset(Preset::Type type, wxString preset_name) +{ + // "color" strings + color_string(preset_name, black); + make_string_bold(preset_name); + + auto preset = new ModelNode(type, preset_name); + m_preset_nodes.emplace_back(preset); + + wxDataViewItem child((void*)preset); + wxDataViewItem parent(nullptr); + + ItemAdded(parent, child); + return child; +} + +ModelNode* UnsavedChangesModel::AddOption(ModelNode* group_node, wxString option_name, wxString old_value, wxString new_value) +{ + ModelNode* option = new ModelNode(group_node, option_name, old_value, new_value); + group_node->Append(option); + ItemAdded(wxDataViewItem((void*)group_node), wxDataViewItem((void*)option)); + + return option; +} + +ModelNode* UnsavedChangesModel::AddOptionWithGroup(ModelNode* category_node, wxString group_name, wxString option_name, wxString old_value, wxString new_value) +{ + ModelNode* group_node = new ModelNode(category_node, group_name); + category_node->Append(group_node); + wxDataViewItem group_item = wxDataViewItem((void*)group_node); + ItemAdded(wxDataViewItem((void*)category_node), group_item); + m_ctrl->Expand(group_item); + + return AddOption(group_node, option_name, old_value, new_value); +} + +ModelNode* UnsavedChangesModel::AddOptionWithGroupAndCategory(ModelNode* preset_node, wxString category_name, wxString group_name, wxString option_name, wxString old_value, wxString new_value) +{ + ModelNode* category_node = new ModelNode(preset_node, category_name, "cog"); + preset_node->Append(category_node); + ItemAdded(wxDataViewItem((void*)preset_node), wxDataViewItem((void*)category_node)); + + return AddOptionWithGroup(category_node, group_name, option_name, old_value, new_value); +} + +wxDataViewItem UnsavedChangesModel::AddOption(Preset::Type type, wxString category_name, wxString group_name, wxString option_name, + wxString old_value, wxString new_value) +{ + // "color" strings + color_string(category_name, black); + color_string(group_name, black); + color_string(option_name, black); + + // "make" strings bold + make_string_bold(category_name); + make_string_bold(group_name); + + // add items + for (ModelNode* preset : m_preset_nodes) + if (preset->type() == type) + { + for (ModelNode* category : preset->GetChildren()) + if (category->text() == category_name) + { + for (ModelNode* group : category->GetChildren()) + if (group->text() == group_name) + return wxDataViewItem((void*)AddOption(group, option_name, old_value, new_value)); + + return wxDataViewItem((void*)AddOptionWithGroup(category, group_name, option_name, old_value, new_value)); + } + + return wxDataViewItem((void*)AddOptionWithGroupAndCategory(preset, category_name, group_name, option_name, old_value, new_value)); + } + + return wxDataViewItem(nullptr); +} + +static void update_children(ModelNode* parent) +{ + if (parent->IsContainer()) { + bool toggle = parent->IsToggled(); + for (ModelNode* child : parent->GetChildren()) { + child->Toggle(toggle); + child->UpdateEnabling(); + update_children(child); + } + } +} + +static void update_parents(ModelNode* node) +{ + ModelNode* parent = node->GetParent(); + if (parent) { + bool toggle = false; + for (ModelNode* child : parent->GetChildren()) { + if (child->IsToggled()) { + toggle = true; + break; + } + } + parent->Toggle(toggle); + parent->UpdateEnabling(); + update_parents(parent); + } +} + +void UnsavedChangesModel::UpdateItemEnabling(wxDataViewItem item) +{ + assert(item.IsOk()); + ModelNode* node = (ModelNode*)item.GetID(); + node->UpdateEnabling(); + + update_children(node); + update_parents(node); } void UnsavedChangesModel::GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const { - wxASSERT(item.IsOk()); + assert(item.IsOk()); ModelNode* node = (ModelNode*)item.GetID(); switch (col) @@ -81,20 +301,14 @@ void UnsavedChangesModel::GetValue(wxVariant& variant, const wxDataViewItem& ite case colToggle: variant = node->m_toggle; break; - case colTypeIcon: - variant << node->m_type_icon; - break; - case colGroupIcon: - variant << node->m_group_icon; - break; - case colMarkedText: - variant =node->m_text; + case colIconText: + variant << DataViewBitmapText(node->m_text, node->m_icon); break; case colOldValue: - variant =node->m_text; + variant << DataViewBitmapText(node->m_old_value, node->m_old_color_bmp); break; case colNewValue: - variant =node->m_text; + variant << DataViewBitmapText(node->m_new_value, node->m_new_color_bmp); break; default: @@ -109,24 +323,27 @@ bool UnsavedChangesModel::SetValue(const wxVariant& variant, const wxDataViewIte ModelNode* node = (ModelNode*)item.GetID(); switch (col) { + case colIconText: { + DataViewBitmapText data; + data << variant; + node->m_icon = data.GetBitmap(); + node->m_text = data.GetText(); + return true; } case colToggle: node->m_toggle = variant.GetBool(); return true; - case colTypeIcon: - node->m_type_icon << variant; - return true; - case colGroupIcon: - node->m_group_icon << variant; - return true; - case colMarkedText: - node->m_text = variant.GetString(); - return true; - case colOldValue: - node->m_text = variant.GetString(); - return true; - case colNewValue: - node->m_text = variant.GetString(); - return true; + case colOldValue: { + DataViewBitmapText data; + data << variant; + node->m_old_color_bmp = data.GetBitmap(); + node->m_old_value = data.GetText(); + return true; } + case colNewValue: { + DataViewBitmapText data; + data << variant; + node->m_new_color_bmp = data.GetBitmap(); + node->m_new_value = data.GetText(); + return true; } default: wxLogError("UnsavedChangesModel::SetValue: wrong column"); } @@ -136,11 +353,11 @@ bool UnsavedChangesModel::SetValue(const wxVariant& variant, const wxDataViewIte bool UnsavedChangesModel::IsEnabled(const wxDataViewItem& item, unsigned int col) const { assert(item.IsOk()); - - ModelNode* node = (ModelNode*)item.GetID(); + if (col == colToggle) + return true; // disable unchecked nodes - return !node->IsToggle(); + return ((ModelNode*)item.GetID())->IsToggled(); } wxDataViewItem UnsavedChangesModel::GetParent(const wxDataViewItem& item) const @@ -152,7 +369,7 @@ wxDataViewItem UnsavedChangesModel::GetParent(const wxDataViewItem& item) const ModelNode* node = (ModelNode*)item.GetID(); // "MyMusic" also has no parent - if (node == m_root) + if (node->IsRoot()) return wxDataViewItem(nullptr); return wxDataViewItem((void*)node->GetParent()); @@ -172,8 +389,9 @@ unsigned int UnsavedChangesModel::GetChildren(const wxDataViewItem& parent, wxDa { ModelNode* node = (ModelNode*)parent.GetID(); if (!node) { - array.Add(wxDataViewItem((void*)m_root)); - return 1; + for (auto preset_node : m_preset_nodes) + array.Add(wxDataViewItem((void*)preset_node)); + return m_preset_nodes.size(); } if (node->GetChildCount() == 0) @@ -191,13 +409,16 @@ unsigned int UnsavedChangesModel::GetChildren(const wxDataViewItem& parent, wxDa wxString UnsavedChangesModel::GetColumnType(unsigned int col) const { - if (col == colToggle) + switch (col) + { + case colToggle: return "bool"; - - if (col < colMarkedText) - return "wxBitmap"; - - return "string"; + case colIconText: + case colOldValue: + case colNewValue: + default: + return "DataViewBitmapText";//"string"; + } } @@ -214,43 +435,215 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) int border = 10; int em = em_unit(); - changes_tree = new wxDataViewCtrl(this, wxID_ANY, wxDefaultPosition, wxSize(em * 80, em * 60), wxBORDER_SIMPLE); - changes_tree_model = new UnsavedChangesModel(this); - changes_tree->AssociateModel(changes_tree_model); + m_tree = new wxDataViewCtrl(this, wxID_ANY, wxDefaultPosition, wxSize(em * 80, em * 30), wxBORDER_SIMPLE | wxDV_VARIABLE_LINE_HEIGHT); + m_tree_model = new UnsavedChangesModel(this); + m_tree->AssociateModel(m_tree_model); + m_tree_model->SetAssociatedControl(m_tree); - changes_tree->AppendToggleColumn(L"\u2610", UnsavedChangesModel::colToggle);//2610,11,12 //2714 - changes_tree->AppendBitmapColumn("", UnsavedChangesModel::colTypeIcon); - changes_tree->AppendBitmapColumn("", UnsavedChangesModel::colGroupIcon); - - wxDataViewTextRenderer* const markupRenderer = new wxDataViewTextRenderer(); + m_tree->AppendToggleColumn(/*L"\u2714"*/"", UnsavedChangesModel::colToggle, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em, wxALIGN_NOT);//2610,11,12 //2714 + BitmapTextRenderer* renderer = new BitmapTextRenderer(m_tree); #ifdef SUPPORTS_MARKUP - markupRenderer->EnableMarkup(); + renderer->EnableMarkup(); #endif + m_tree->AppendColumn(new wxDataViewColumn("", renderer, UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE)); + m_tree->AppendColumn(new wxDataViewColumn("Old value", renderer, UnsavedChangesModel::colOldValue, 20 * em, wxALIGN_TOP)); + m_tree->AppendColumn(new wxDataViewColumn("New value", renderer, UnsavedChangesModel::colNewValue, 20 * em, wxALIGN_TOP)); - changes_tree->AppendColumn(new wxDataViewColumn("", markupRenderer, UnsavedChangesModel::colMarkedText, wxCOL_WIDTH_AUTOSIZE, wxALIGN_LEFT)); - changes_tree->AppendColumn(new wxDataViewColumn("Old value", markupRenderer, UnsavedChangesModel::colOldValue, wxCOL_WIDTH_AUTOSIZE, wxALIGN_LEFT)); - changes_tree->AppendColumn(new wxDataViewColumn("New value", markupRenderer, UnsavedChangesModel::colNewValue, wxCOL_WIDTH_AUTOSIZE, wxALIGN_LEFT)); + m_tree->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &UnsavedChangesDialog::item_value_changed, this); - wxStdDialogButtonSizer* cancel_btn = this->CreateStdDialogButtonSizer(wxCANCEL); + wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCANCEL); + + Tab* tab = wxGetApp().get_tab(type); + assert(tab); + + PresetCollection* presets = tab->get_presets(); + + wxString label= from_u8((boost::format(_u8L("Save selected to preset:%1%"))% ("\"" + presets->get_selected_preset().name + "\"")).str()); + auto save_btn = new wxButton(this, m_save_btn_id = NewControlId(), label); + save_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Save); }); + buttons->Insert(0, save_btn, 0, wxLEFT, 5); + + label = from_u8((boost::format(_u8L("Move selected to preset:%1%"))% ("\"NewSelectedPreset\"")).str()); + auto move_btn = new wxButton(this, m_move_btn_id = NewControlId(), label); + move_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Move); }); + buttons->Insert(1, move_btn, 0, wxLEFT, 5); + + auto continue_btn = new wxButton(this, m_continue_btn_id = NewControlId(), _L("Continue without changes")); + continue_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Continue); }); + buttons->Insert(2, continue_btn, 0, wxLEFT, 5); wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - topSizer->Add(new wxStaticText(this, wxID_ANY, _L("There is unsaved changes for the current preset") + ":"), 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); - topSizer->Add(changes_tree, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); - topSizer->Add(cancel_btn, 0, wxEXPAND | wxALL, border); + topSizer->Add(new wxStaticText(this, wxID_ANY, _L("There is unsaved changes for") + (": \"" + tab->title() + "\"")), 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(m_tree, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(buttons, 0, wxEXPAND | wxALL, border); + + update(type); SetSizer(topSizer); topSizer->SetSizeHints(this); } +void UnsavedChangesDialog::item_value_changed(wxDataViewEvent& event) +{ + if (event.GetColumn() != UnsavedChangesModel::colToggle) + return; + + wxDataViewItem item = event.GetItem(); + + m_tree_model->UpdateItemEnabling(item); + m_tree->Refresh(); +} + +void UnsavedChangesDialog::close(Action action) +{ + m_action = action; + this->EndModal(wxID_CLOSE); +} + +template +wxString get_string_from_enum(const std::string& opt_key, const DynamicPrintConfig& config) +{ + const std::vector& names = config.def()->options.at(opt_key).enum_labels;//ConfigOptionEnum::get_enum_names(); + T val = config.option>(opt_key)->value; + return from_u8(_u8L(names[static_cast(val)])); +} + +static wxString get_string_value(const std::string& opt_key, const DynamicPrintConfig& config) +{ + wxString out; + + // FIXME controll, if opt_key has index + int opt_idx = 0; + + ConfigOptionType type = config.def()->options.at(opt_key).type; + + switch (type) { + case coInt: + return from_u8((boost::format("%1%") % config.opt_int(opt_key)).str()); + case coInts: { + const ConfigOptionInts* opt = config.opt(opt_key); + if (opt) + return from_u8((boost::format("%1%") % opt->get_at(opt_idx)).str()); + break; + } + case coBool: + return config.opt_bool(opt_key) ? "true" : "false"; + case coBools: { + const ConfigOptionBools* opt = config.opt(opt_key); + if (opt) + return opt->get_at(opt_idx) ? "true" : "false"; + break; + } + case coPercent: + return from_u8((boost::format("%1%%%") % int(config.optptr(opt_key)->getFloat())).str()); + case coPercents: { + const ConfigOptionPercents* opt = config.opt(opt_key); + if (opt) + return from_u8((boost::format("%1%%%") % int(opt->get_at(opt_idx))).str()); + break; + } + case coFloat: + return double_to_string(config.opt_float(opt_key)); + case coFloats: { + const ConfigOptionFloats* opt = config.opt(opt_key); + if (opt) + return double_to_string(opt->get_at(opt_idx)); + break; + } + case coString: + return from_u8(config.opt_string(opt_key)); + case coStrings: { + const ConfigOptionStrings* strings = config.opt(opt_key); + if (strings) { + if (opt_key == "compatible_printers" || opt_key == "compatible_prints") { + if (strings->empty()) + return _L("All"); + for (size_t id = 0; id < strings->size(); id++) + out += from_u8(strings->get_at(id)) + "\n"; + out.RemoveLast(1); + return out; + } + if (!strings->empty()) + return from_u8(strings->get_at(opt_idx)); + } + break; + } + case coFloatOrPercent: { + const ConfigOptionFloatOrPercent* opt = config.opt(opt_key); + if (opt) + out = double_to_string(opt->value) + (opt->percent ? "%" : ""); + return out; + } + case coEnum: { + if (opt_key == "top_fill_pattern" || + opt_key == "bottom_fill_pattern" || + opt_key == "fill_pattern") + return get_string_from_enum(opt_key, config); + if (opt_key == "gcode_flavor") + return get_string_from_enum(opt_key, config); + if (opt_key == "ironing_type") + return get_string_from_enum(opt_key, config); + if (opt_key == "support_material_pattern") + return get_string_from_enum(opt_key, config); + if (opt_key == "seam_position") + return get_string_from_enum(opt_key, config); + if (opt_key == "display_orientation") + return get_string_from_enum(opt_key, config); + if (opt_key == "support_pillar_connection_mode") + return get_string_from_enum(opt_key, config); + break; + } + case coPoints: { + /* + if (opt_key == "bed_shape") { + config.option(opt_key)->values = boost::any_cast>(value); + break; + } + ConfigOptionPoints* vec_new = new ConfigOptionPoints{ boost::any_cast(value) }; + config.option(opt_key)->set_at(vec_new, opt_index, 0); + */ + return "Points"; + } + default: + break; + } + return out; +} + +void UnsavedChangesDialog::update(Preset::Type type) +{ + Tab* tab = wxGetApp().get_tab(type); + assert(tab); + + PresetCollection* presets = tab->get_presets(); + // Display a dialog showing the dirty options in a human readable form. + const DynamicPrintConfig& old_config = presets->get_selected_preset().config; + const DynamicPrintConfig& new_config = presets->get_edited_preset().config; + + m_tree_model->AddPreset(type, from_u8(presets->get_edited_preset().name)); + + Search::OptionsSearcher& searcher = wxGetApp().sidebar().get_searcher(); + searcher.sort_options_by_opt_key(); + + // Collect dirty options. + for (const std::string& opt_key : presets->current_dirty_options()) { + const Search::Option& option = searcher.get_option(opt_key); + + m_tree_model->AddOption(type, option.category_local, option.group_local, option.label_local, + get_string_value(opt_key, old_config), get_string_value(opt_key, new_config)); + } +} + + void UnsavedChangesDialog::on_dpi_changed(const wxRect& suggested_rect) { const int& em = em_unit(); - msw_buttons_rescale(this, em, { wxID_CANCEL }); + msw_buttons_rescale(this, em, { wxID_CANCEL, m_save_btn_id, m_move_btn_id, m_continue_btn_id }); - const wxSize& size = wxSize(80 * em, 60 * em); + const wxSize& size = wxSize(80 * em, 30 * em); SetMinSize(size); Fit(); @@ -260,7 +653,7 @@ void UnsavedChangesDialog::on_dpi_changed(const wxRect& suggested_rect) void UnsavedChangesDialog::on_sys_color_changed() { // msw_rescale updates just icons, so use it -// changes_tree_model->msw_rescale(); +// m_tree_model->msw_rescale(); Refresh(); } diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index a3ee7d984f..c4a02d7bcc 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -31,11 +31,17 @@ WX_DEFINE_ARRAY_PTR(ModelNode*, ModelNodePtrArray); class ModelNode { + wxWindow* m_parent_win{ nullptr }; + ModelNode* m_parent; ModelNodePtrArray m_children; wxBitmap m_empty_bmp; Preset::Type m_preset_type {Preset::TYPE_INVALID}; + // saved values for colors if they exist + wxString m_old_color; + wxString m_new_color; + // TODO/FIXME: // the GTK version of wxDVC (in particular wxDataViewCtrlInternal::ItemAdded) // needs to know in advance if a node is or _will be_ a container. @@ -47,23 +53,29 @@ class ModelNode // would be added to the control) bool m_container {true}; + wxBitmap get_bitmap(const wxString& color); + public: bool m_toggle {true}; - wxBitmap m_type_icon; - wxBitmap m_group_icon; + wxBitmap m_icon; + wxBitmap m_old_color_bmp; + wxBitmap m_new_color_bmp; wxString m_text; wxString m_old_value; wxString m_new_value; // preset(root) node - ModelNode(const wxString& text, Preset::Type preset_type); + ModelNode(Preset::Type preset_type, const wxString& text); - // group node + // category node ModelNode(ModelNode* parent, const wxString& text, const std::string& icon_name); // group node - ModelNode(ModelNode* parent, const wxString& text, bool is_option); + ModelNode(ModelNode* parent, const wxString& text); + + // option node + ModelNode(ModelNode* parent, const wxString& text, const wxString& old_value, const wxString& new_value); ~ModelNode() { // free all our children nodes @@ -75,15 +87,21 @@ public: } bool IsContainer() const { return m_container; } - bool IsToggle() const { return m_toggle; } + bool IsToggled() const { return m_toggle; } + void Toggle(bool toggle = true) { m_toggle = toggle; } + bool IsRoot() const { return m_parent == nullptr; } + Preset::Type type() const { return m_preset_type; } + const wxString& text() const { return m_text; } ModelNode* GetParent() { return m_parent; } ModelNodePtrArray& GetChildren() { return m_children; } ModelNode* GetNthChild(unsigned int n) { return m_children.Item(n); } unsigned int GetChildCount() const { return m_children.GetCount(); } - void Insert(ModelNode* child, unsigned int n) { m_children.Insert(child, n); } - void Append(ModelNode* child) { m_children.Add(child); } + void Insert(ModelNode* child, unsigned int n) { m_children.Insert(child, n); } + void Append(ModelNode* child) { m_children.Add(child); } + + void UpdateEnabling(); }; @@ -93,15 +111,31 @@ public: class UnsavedChangesModel : public wxDataViewModel { - ModelNode* m_root; - ScalableBitmap m_icon[5]; + wxWindow* m_parent_win {nullptr}; + std::vector m_preset_nodes; + + wxDataViewCtrl* m_ctrl{ nullptr }; + + ModelNode *AddOption(ModelNode *group_node, + wxString option_name, + wxString old_value, + wxString new_value); + ModelNode *AddOptionWithGroup(ModelNode *category_node, + wxString group_name, + wxString option_name, + wxString old_value, + wxString new_value); + ModelNode *AddOptionWithGroupAndCategory(ModelNode *preset_node, + wxString category_name, + wxString group_name, + wxString option_name, + wxString old_value, + wxString new_value); public: enum { colToggle, - colTypeIcon, - colGroupIcon, - colMarkedText, + colIconText, colOldValue, colNewValue, colMax @@ -110,18 +144,28 @@ public: UnsavedChangesModel(wxWindow* parent); ~UnsavedChangesModel(); - virtual unsigned int GetColumnCount() const override { return colMax; } - virtual wxString GetColumnType(unsigned int col) const override; + void SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; } - virtual wxDataViewItem GetParent(const wxDataViewItem& item) const override; - virtual unsigned int GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const override; + wxDataViewItem AddPreset(Preset::Type type, wxString preset_name); + wxDataViewItem AddOption(Preset::Type type, wxString category_name, wxString group_name, wxString option_name, + wxString old_value, wxString new_value); - virtual void GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const override; - virtual bool SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col) override; + void UpdateItemEnabling(wxDataViewItem item); - virtual bool IsEnabled(const wxDataViewItem& item, unsigned int col) const override; - virtual bool IsContainer(const wxDataViewItem& item) const override; + unsigned int GetColumnCount() const override { return colMax; } + wxString GetColumnType(unsigned int col) const override; + wxDataViewItem GetParent(const wxDataViewItem& item) const override; + unsigned int GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const override; + + void GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const override; + bool SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col) override; + + bool IsEnabled(const wxDataViewItem& item, unsigned int col) const override; + bool IsContainer(const wxDataViewItem& item) const override; + // Is the container just a header or an item with all columns + // In our case it is an item with all columns + bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; } }; @@ -130,14 +174,30 @@ public: //------------------------------------------ class UnsavedChangesDialog : public DPIDialog { - wxDataViewCtrl* changes_tree{ nullptr }; - UnsavedChangesModel* changes_tree_model{ nullptr }; + wxDataViewCtrl* m_tree { nullptr }; + UnsavedChangesModel* m_tree_model { nullptr }; + + int m_save_btn_id { wxID_ANY }; + int m_move_btn_id { wxID_ANY }; + int m_continue_btn_id { wxID_ANY }; + + enum class Action { + Save, + Move, + Continue + } m_action; public: UnsavedChangesDialog(Preset::Type type); ~UnsavedChangesDialog() {} - void ProcessSelection(wxDataViewItem selection); + void update(Preset::Type type); + void item_value_changed(wxDataViewEvent &event); + void close(Action action); + + bool save_preset() const { return m_action == Action::Save; } + bool move_preset() const { return m_action == Action::Move; } + bool just_continue() const { return m_action == Action::Continue; } protected: void on_dpi_changed(const wxRect& suggested_rect) override; From 93c1671e09b65905ac7da7bba60dcea15d9f5e4e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 5 Aug 2020 20:26:40 +0200 Subject: [PATCH 255/503] Custom renderers extracted from the ObjectDataViewModel --- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/ExtraRenderers.cpp | 314 +++++++++++++++++++++++++ src/slic3r/GUI/ExtraRenderers.hpp | 162 +++++++++++++ src/slic3r/GUI/GUI_ObjectList.cpp | 14 +- src/slic3r/GUI/ObjectDataViewModel.cpp | 305 ------------------------ src/slic3r/GUI/ObjectDataViewModel.hpp | 153 +----------- 6 files changed, 490 insertions(+), 460 deletions(-) create mode 100644 src/slic3r/GUI/ExtraRenderers.cpp create mode 100644 src/slic3r/GUI/ExtraRenderers.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index cd28d6eb20..b22a3cb1fb 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -165,6 +165,8 @@ set(SLIC3R_GUI_SOURCES GUI/Search.hpp GUI/UnsavedChangesDialog.cpp GUI/UnsavedChangesDialog.hpp + GUI/ExtraRenderers.cpp + GUI/ExtraRenderers.hpp Utils/Http.cpp Utils/Http.hpp Utils/FixModelByWin10.cpp diff --git a/src/slic3r/GUI/ExtraRenderers.cpp b/src/slic3r/GUI/ExtraRenderers.cpp new file mode 100644 index 0000000000..494bfee6a2 --- /dev/null +++ b/src/slic3r/GUI/ExtraRenderers.cpp @@ -0,0 +1,314 @@ +#include "ExtraRenderers.hpp" +#include "wxExtensions.hpp" +#include "GUI.hpp" +#include "I18N.hpp" + +#include +#include "wx/generic/private/markuptext.h" +#include "wx/generic/private/rowheightcache.h" +#include "wx/generic/private/widthcalc.h" +#if wxUSE_ACCESSIBILITY +#include "wx/private/markupparser.h" +#endif // wxUSE_ACCESSIBILITY + +using Slic3r::GUI::from_u8; +using Slic3r::GUI::into_u8; + + +//----------------------------------------------------------------------------- +// DataViewBitmapText +//----------------------------------------------------------------------------- + +wxIMPLEMENT_DYNAMIC_CLASS(DataViewBitmapText, wxObject) + +IMPLEMENT_VARIANT_OBJECT(DataViewBitmapText) + +// --------------------------------------------------------- +// BitmapTextRenderer +// --------------------------------------------------------- + +#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING +BitmapTextRenderer::BitmapTextRenderer(wxDataViewCellMode mode /*= wxDATAVIEW_CELL_EDITABLE*/, + int align /*= wxDVR_DEFAULT_ALIGNMENT*/): +wxDataViewRenderer(wxT("PrusaDataViewBitmapText"), mode, align) +{ + SetMode(mode); + SetAlignment(align); +} +#endif // ENABLE_NONCUSTOM_DATA_VIEW_RENDERING + +BitmapTextRenderer::~BitmapTextRenderer() +{ +#ifdef SUPPORTS_MARKUP + if (m_markupText) + delete m_markupText; +#endif // SUPPORTS_MARKUP +} + +#ifdef SUPPORTS_MARKUP +void BitmapTextRenderer::EnableMarkup(bool enable) +{ + if (enable) + { + if (!m_markupText) + { + m_markupText = new wxItemMarkupText(wxString()); + } + } + else + { + if (m_markupText) + { + delete m_markupText; + m_markupText = nullptr; + } + } +} +#endif // SUPPORTS_MARKUP + +bool BitmapTextRenderer::SetValue(const wxVariant &value) +{ + m_value << value; + +#ifdef SUPPORTS_MARKUP + if (m_markupText) + m_markupText->SetMarkup(m_value.GetText()); +#endif // SUPPORTS_MARKUP + + return true; +} + +bool BitmapTextRenderer::GetValue(wxVariant& WXUNUSED(value)) const +{ + return false; +} + +#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY +wxString BitmapTextRenderer::GetAccessibleDescription() const +{ +#ifdef SUPPORTS_MARKUP + if (m_markupText) + return wxMarkupParser::Strip(m_text); +#endif // SUPPORTS_MARKUP + + return m_value.GetText(); +} +#endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING + +bool BitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state) +{ + int xoffset = 0; + + const wxBitmap& icon = m_value.GetBitmap(); + if (icon.IsOk()) + { +#ifdef __APPLE__ + wxSize icon_sz = icon.GetScaledSize(); +#else + wxSize icon_sz = icon.GetSize(); +#endif + dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon_sz.y) / 2); + xoffset = icon_sz.x + 4; + } + +#ifdef SUPPORTS_MARKUP + if (m_markupText) + { + int flags = 0; + + rect.x += xoffset; + m_markupText->Render(GetView(), *dc, rect, flags, GetEllipsizeMode()); + } + else +#endif // SUPPORTS_MARKUP + RenderText(m_value.GetText(), xoffset, rect, dc, state); + + return true; +} + +wxSize BitmapTextRenderer::GetSize() const +{ + if (!m_value.GetText().empty()) + { + wxSize size; +#ifdef SUPPORTS_MARKUP + if (m_markupText) + { + wxDataViewCtrl* const view = GetView(); + wxClientDC dc(view); + if (GetAttr().HasFont()) + dc.SetFont(GetAttr().GetEffectiveFont(view->GetFont())); + + size = m_markupText->Measure(dc); + } + else +#endif // SUPPORTS_MARKUP + size = GetTextExtent(m_value.GetText()); + + int lines = m_value.GetText().Freq('\n') + 1; + size.SetHeight(size.GetHeight() * lines); + + if (m_value.GetBitmap().IsOk()) + size.x += m_value.GetBitmap().GetWidth() + 4; + return size; + } + return wxSize(80, 20); +} + + +wxWindow* BitmapTextRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) +{ + if (!can_create_editor_ctrl()) + return nullptr; + + DataViewBitmapText data; + data << value; + + m_was_unusable_symbol = false; + + wxPoint position = labelRect.GetPosition(); + if (data.GetBitmap().IsOk()) { + const int bmp_width = data.GetBitmap().GetWidth(); + position.x += bmp_width; + labelRect.SetWidth(labelRect.GetWidth() - bmp_width); + } + + wxTextCtrl* text_editor = new wxTextCtrl(parent, wxID_ANY, data.GetText(), + position, labelRect.GetSize(), wxTE_PROCESS_ENTER); + text_editor->SetInsertionPointEnd(); + text_editor->SelectAll(); + + return text_editor; +} + +bool BitmapTextRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) +{ + wxTextCtrl* text_editor = wxDynamicCast(ctrl, wxTextCtrl); + if (!text_editor || text_editor->GetValue().IsEmpty()) + return false; + + std::string chosen_name = into_u8(text_editor->GetValue()); + const char* unusable_symbols = "<>:/\\|?*\""; + for (size_t i = 0; i < std::strlen(unusable_symbols); i++) { + if (chosen_name.find_first_of(unusable_symbols[i]) != std::string::npos) { + m_was_unusable_symbol = true; + return false; + } + } + + // The icon can't be edited so get its old value and reuse it. + wxVariant valueOld; + GetView()->GetModel()->GetValue(valueOld, m_item, /*colName*/0); + + DataViewBitmapText bmpText; + bmpText << valueOld; + + // But replace the text with the value entered by user. + bmpText.SetText(text_editor->GetValue()); + + value << bmpText; + return true; +} + +// ---------------------------------------------------------------------------- +// BitmapChoiceRenderer +// ---------------------------------------------------------------------------- + +bool BitmapChoiceRenderer::SetValue(const wxVariant& value) +{ + m_value << value; + return true; +} + +bool BitmapChoiceRenderer::GetValue(wxVariant& value) const +{ + value << m_value; + return true; +} + +bool BitmapChoiceRenderer::Render(wxRect rect, wxDC* dc, int state) +{ + int xoffset = 0; + + const wxBitmap& icon = m_value.GetBitmap(); + if (icon.IsOk()) + { + dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon.GetHeight()) / 2); + xoffset = icon.GetWidth() + 4; + + if (rect.height==0) + rect.height= icon.GetHeight(); + } + + RenderText(m_value.GetText(), xoffset, rect, dc, state); + + return true; +} + +wxSize BitmapChoiceRenderer::GetSize() const +{ + wxSize sz = GetTextExtent(m_value.GetText()); + + if (m_value.GetBitmap().IsOk()) + sz.x += m_value.GetBitmap().GetWidth() + 4; + + return sz; +} + + +wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) +{ + if (!can_create_editor_ctrl()) + return nullptr; + + std::vector icons = get_extruder_color_icons(); + if (icons.empty()) + return nullptr; + + DataViewBitmapText data; + data << value; + + auto c_editor = new wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, + labelRect.GetTopLeft(), wxSize(labelRect.GetWidth(), -1), + 0, nullptr , wxCB_READONLY); + + int i=0; + for (wxBitmap* bmp : icons) { + if (i==0) { + c_editor->Append(_L("default"), *bmp); + ++i; + } + + c_editor->Append(wxString::Format("%d", i), *bmp); + ++i; + } + c_editor->SetSelection(atoi(data.GetText().c_str())); + + // to avoid event propagation to other sidebar items + c_editor->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) { + evt.StopPropagation(); + // FinishEditing grabs new selection and triggers config update. We better call + // it explicitly, automatic update on KILL_FOCUS didn't work on Linux. + this->FinishEditing(); + }); + + return c_editor; +} + +bool BitmapChoiceRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) +{ + wxBitmapComboBox* c = (wxBitmapComboBox*)ctrl; + int selection = c->GetSelection(); + if (selection < 0) + return false; + + DataViewBitmapText bmpText; + + bmpText.SetText(c->GetString(selection)); + bmpText.SetBitmap(c->GetItemBitmap(selection)); + + value << bmpText; + return true; +} + + diff --git a/src/slic3r/GUI/ExtraRenderers.hpp b/src/slic3r/GUI/ExtraRenderers.hpp new file mode 100644 index 0000000000..96cf349453 --- /dev/null +++ b/src/slic3r/GUI/ExtraRenderers.hpp @@ -0,0 +1,162 @@ +#ifndef slic3r_GUI_ExtraRenderers_hpp_ +#define slic3r_GUI_ExtraRenderers_hpp_ + +#include + +#if wxUSE_MARKUP && wxCHECK_VERSION(3, 1, 1) + #define SUPPORTS_MARKUP +#endif + +// ---------------------------------------------------------------------------- +// DataViewBitmapText: helper class used by BitmapTextRenderer +// ---------------------------------------------------------------------------- + +class DataViewBitmapText : public wxObject +{ +public: + DataViewBitmapText( const wxString &text = wxEmptyString, + const wxBitmap& bmp = wxNullBitmap) : + m_text(text), + m_bmp(bmp) + { } + + DataViewBitmapText(const DataViewBitmapText &other) + : wxObject(), + m_text(other.m_text), + m_bmp(other.m_bmp) + { } + + void SetText(const wxString &text) { m_text = text; } + wxString GetText() const { return m_text; } + void SetBitmap(const wxBitmap &bmp) { m_bmp = bmp; } + const wxBitmap &GetBitmap() const { return m_bmp; } + + bool IsSameAs(const DataViewBitmapText& other) const { + return m_text == other.m_text && m_bmp.IsSameAs(other.m_bmp); + } + + bool operator==(const DataViewBitmapText& other) const { + return IsSameAs(other); + } + + bool operator!=(const DataViewBitmapText& other) const { + return !IsSameAs(other); + } + +private: + wxString m_text; + wxBitmap m_bmp; + + wxDECLARE_DYNAMIC_CLASS(DataViewBitmapText); +}; +DECLARE_VARIANT_OBJECT(DataViewBitmapText) + +// ---------------------------------------------------------------------------- +// BitmapTextRenderer +// ---------------------------------------------------------------------------- +#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING +class BitmapTextRenderer : public wxDataViewRenderer +#else +class BitmapTextRenderer : public wxDataViewCustomRenderer +#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING +{ +public: + BitmapTextRenderer(wxWindow* parent, + wxDataViewCellMode mode = +#ifdef __WXOSX__ + wxDATAVIEW_CELL_INERT +#else + wxDATAVIEW_CELL_EDITABLE +#endif + + , int align = wxDVR_DEFAULT_ALIGNMENT +#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING + ); +#else + ) : + wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align), + m_parent(parent) + { +#ifdef SUPPORTS_MARKUP + m_markupText = nullptr; +#endif // SUPPORTS_MARKUP + } +#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING + + ~BitmapTextRenderer(); + +#ifdef SUPPORTS_MARKUP + void EnableMarkup(bool enable = true); +#endif // SUPPORTS_MARKUP + + bool SetValue(const wxVariant& value); + bool GetValue(wxVariant& value) const; +#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY + virtual wxString GetAccessibleDescription() const override; +#endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING + + virtual bool Render(wxRect cell, wxDC* dc, int state) override; + virtual wxSize GetSize() const override; + + bool HasEditorCtrl() const override + { +#ifdef __WXOSX__ + return false; +#else + return true; +#endif + } + wxWindow* CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) override; + bool GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) override; + bool WasCanceled() const { return m_was_unusable_symbol; } + + void set_can_create_editor_ctrl_function(std::function can_create_fn) { can_create_editor_ctrl = can_create_fn; } + +private: + DataViewBitmapText m_value; + bool m_was_unusable_symbol{ false }; + wxWindow* m_parent{ nullptr }; + + std::function can_create_editor_ctrl { nullptr }; + +#ifdef SUPPORTS_MARKUP + class wxItemMarkupText* m_markupText; +#endif // SUPPORTS_MARKUP +}; + + +// ---------------------------------------------------------------------------- +// BitmapChoiceRenderer +// ---------------------------------------------------------------------------- + +class BitmapChoiceRenderer : public wxDataViewCustomRenderer +{ +public: + BitmapChoiceRenderer(wxDataViewCellMode mode = +#ifdef __WXOSX__ + wxDATAVIEW_CELL_INERT +#else + wxDATAVIEW_CELL_EDITABLE +#endif + , int align = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL + ) : wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align) {} + + bool SetValue(const wxVariant& value); + bool GetValue(wxVariant& value) const; + + virtual bool Render(wxRect cell, wxDC* dc, int state) override; + virtual wxSize GetSize() const override; + + bool HasEditorCtrl() const override { return true; } + wxWindow* CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) override; + bool GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) override; + + void set_can_create_editor_ctrl_function(std::function can_create_fn) { can_create_editor_ctrl = can_create_fn; } + +private: + DataViewBitmapText m_value; + std::function can_create_editor_ctrl { nullptr }; +}; + + +#endif // slic3r_GUI_ExtraRenderers_hpp_ diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 9d6b2b9cb8..a434e39fda 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -277,7 +277,11 @@ void ObjectList::create_objects_ctrl() // column ItemName(Icon+Text) of the view control: // And Icon can be consisting of several bitmaps - AppendColumn(new wxDataViewColumn(_(L("Name")), new BitmapTextRenderer(this), + BitmapTextRenderer* bmp_text_renderer = new BitmapTextRenderer(this); + bmp_text_renderer->set_can_create_editor_ctrl_function([this]() { + return m_objects_model->GetItemType(GetSelection()) & (itVolume | itObject); + }); + AppendColumn(new wxDataViewColumn(_L("Name"), bmp_text_renderer, colName, 20*em, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE)); // column PrintableProperty (Icon) of the view control: @@ -285,11 +289,15 @@ void ObjectList::create_objects_ctrl() wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE); // column Extruder of the view control: - AppendColumn(new wxDataViewColumn(_(L("Extruder")), new BitmapChoiceRenderer(), + BitmapChoiceRenderer* bmp_choice_renderer = new BitmapChoiceRenderer(); + bmp_choice_renderer->set_can_create_editor_ctrl_function([this]() { + return m_objects_model->GetItemType(GetSelection()) & (itVolume | itLayer | itObject); + }); + AppendColumn(new wxDataViewColumn(_L("Extruder"), bmp_choice_renderer, colExtruder, 8*em, wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE)); // column ItemEditing of the view control: - AppendBitmapColumn(_(L("Editing")), colEditing, wxDATAVIEW_CELL_INERT, 3*em, + AppendBitmapColumn(_L("Editing"), colEditing, wxDATAVIEW_CELL_INERT, 3*em, wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE); // For some reason under OSX on 4K(5K) monitors in wxDataViewColumn constructor doesn't set width of column. diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index badaa7a041..a42073dd0a 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -1555,311 +1555,6 @@ void ObjectDataViewModel::DeleteWarningIcon(const wxDataViewItem& item, const bo DeleteWarningIcon(child); } } -/* -} -} -*/ -//----------------------------------------------------------------------------- -// DataViewBitmapText -//----------------------------------------------------------------------------- - -wxIMPLEMENT_DYNAMIC_CLASS(DataViewBitmapText, wxObject) - -IMPLEMENT_VARIANT_OBJECT(DataViewBitmapText) - -// --------------------------------------------------------- -// BitmapTextRenderer -// --------------------------------------------------------- - -#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING -BitmapTextRenderer::BitmapTextRenderer(wxDataViewCellMode mode /*= wxDATAVIEW_CELL_EDITABLE*/, - int align /*= wxDVR_DEFAULT_ALIGNMENT*/): -wxDataViewRenderer(wxT("PrusaDataViewBitmapText"), mode, align) -{ - SetMode(mode); - SetAlignment(align); -} -#endif // ENABLE_NONCUSTOM_DATA_VIEW_RENDERING - -BitmapTextRenderer::~BitmapTextRenderer() -{ -#ifdef SUPPORTS_MARKUP - if (m_markupText) - delete m_markupText; -#endif // SUPPORTS_MARKUP -} - -#ifdef SUPPORTS_MARKUP -void BitmapTextRenderer::EnableMarkup(bool enable) -{ - if (enable) - { - if (!m_markupText) - { - m_markupText = new wxItemMarkupText(wxString()); - } - } - else - { - if (m_markupText) - { - delete m_markupText; - m_markupText = nullptr; - } - } -} -#endif // SUPPORTS_MARKUP - -bool BitmapTextRenderer::SetValue(const wxVariant &value) -{ - m_value << value; - -#ifdef SUPPORTS_MARKUP - if (m_markupText) - m_markupText->SetMarkup(m_value.GetText()); -#endif // SUPPORTS_MARKUP - - return true; -} - -bool BitmapTextRenderer::GetValue(wxVariant& WXUNUSED(value)) const -{ - return false; -} - -#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY -wxString BitmapTextRenderer::GetAccessibleDescription() const -{ -#ifdef SUPPORTS_MARKUP - if (m_markupText) - return wxMarkupParser::Strip(m_text); -#endif // SUPPORTS_MARKUP - - return m_value.GetText(); -} -#endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING - -bool BitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state) -{ - int xoffset = 0; - - const wxBitmap& icon = m_value.GetBitmap(); - if (icon.IsOk()) - { -#ifdef __APPLE__ - wxSize icon_sz = icon.GetScaledSize(); -#else - wxSize icon_sz = icon.GetSize(); -#endif - dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon_sz.y) / 2); - xoffset = icon_sz.x + 4; - } - -#ifdef SUPPORTS_MARKUP - if (m_markupText) - { - int flags = 0; - - rect.x += xoffset; - m_markupText->Render(GetView(), *dc, rect, flags, GetEllipsizeMode()); - } - else -#endif // SUPPORTS_MARKUP - RenderText(m_value.GetText(), xoffset, rect, dc, state); - - return true; -} - -wxSize BitmapTextRenderer::GetSize() const -{ - if (!m_value.GetText().empty()) - { - wxSize size; -#ifdef SUPPORTS_MARKUP - if (m_markupText) - { - wxDataViewCtrl* const view = GetView(); - wxClientDC dc(view); - if (GetAttr().HasFont()) - dc.SetFont(GetAttr().GetEffectiveFont(view->GetFont())); - - size = m_markupText->Measure(dc); - } - else -#endif // SUPPORTS_MARKUP - size = GetTextExtent(m_value.GetText()); - - int lines = m_value.GetText().Freq('\n') + 1; - size.SetHeight(size.GetHeight() * lines); - - if (m_value.GetBitmap().IsOk()) - size.x += m_value.GetBitmap().GetWidth() + 4; - return size; - } - return wxSize(80, 20); -} - - -wxWindow* BitmapTextRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) -{ - wxDataViewCtrl* const dv_ctrl = GetOwner()->GetOwner(); - ObjectDataViewModel* const model = dynamic_cast(dv_ctrl->GetModel()); - - if ( !(model->GetItemType(dv_ctrl->GetSelection()) & (itVolume | itObject)) ) - return nullptr; - - DataViewBitmapText data; - data << value; - - m_was_unusable_symbol = false; - - wxPoint position = labelRect.GetPosition(); - if (data.GetBitmap().IsOk()) { - const int bmp_width = data.GetBitmap().GetWidth(); - position.x += bmp_width; - labelRect.SetWidth(labelRect.GetWidth() - bmp_width); - } - - wxTextCtrl* text_editor = new wxTextCtrl(parent, wxID_ANY, data.GetText(), - position, labelRect.GetSize(), wxTE_PROCESS_ENTER); - text_editor->SetInsertionPointEnd(); - text_editor->SelectAll(); - - return text_editor; -} - -bool BitmapTextRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) -{ - wxTextCtrl* text_editor = wxDynamicCast(ctrl, wxTextCtrl); - if (!text_editor || text_editor->GetValue().IsEmpty()) - return false; - - std::string chosen_name = Slic3r::normalize_utf8_nfc(text_editor->GetValue().ToUTF8()); - const char* unusable_symbols = "<>:/\\|?*\""; - for (size_t i = 0; i < std::strlen(unusable_symbols); i++) { - if (chosen_name.find_first_of(unusable_symbols[i]) != std::string::npos) { - m_was_unusable_symbol = true; - return false; - } - } - - // The icon can't be edited so get its old value and reuse it. - wxVariant valueOld; - GetView()->GetModel()->GetValue(valueOld, m_item, colName); - - DataViewBitmapText bmpText; - bmpText << valueOld; - - // But replace the text with the value entered by user. - bmpText.SetText(text_editor->GetValue()); - - value << bmpText; - return true; -} - -// ---------------------------------------------------------------------------- -// BitmapChoiceRenderer -// ---------------------------------------------------------------------------- - -bool BitmapChoiceRenderer::SetValue(const wxVariant& value) -{ - m_value << value; - return true; -} - -bool BitmapChoiceRenderer::GetValue(wxVariant& value) const -{ - value << m_value; - return true; -} - -bool BitmapChoiceRenderer::Render(wxRect rect, wxDC* dc, int state) -{ - int xoffset = 0; - - const wxBitmap& icon = m_value.GetBitmap(); - if (icon.IsOk()) - { - dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon.GetHeight()) / 2); - xoffset = icon.GetWidth() + 4; - - if (rect.height==0) - rect.height= icon.GetHeight(); - } - - RenderText(m_value.GetText(), xoffset, rect, dc, state); - - return true; -} - -wxSize BitmapChoiceRenderer::GetSize() const -{ - wxSize sz = GetTextExtent(m_value.GetText()); - - if (m_value.GetBitmap().IsOk()) - sz.x += m_value.GetBitmap().GetWidth() + 4; - - return sz; -} - - -wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) -{ - wxDataViewCtrl* const dv_ctrl = GetOwner()->GetOwner(); - ObjectDataViewModel* const model = dynamic_cast(dv_ctrl->GetModel()); - - if (!(model->GetItemType(dv_ctrl->GetSelection()) & (itVolume | itLayer | itObject))) - return nullptr; - - std::vector icons = get_extruder_color_icons(); - if (icons.empty()) - return nullptr; - - DataViewBitmapText data; - data << value; - - auto c_editor = new wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, - labelRect.GetTopLeft(), wxSize(labelRect.GetWidth(), -1), - 0, nullptr , wxCB_READONLY); - - int i=0; - for (wxBitmap* bmp : icons) { - if (i==0) { - c_editor->Append(_(L("default")), *bmp); - ++i; - } - - c_editor->Append(wxString::Format("%d", i), *bmp); - ++i; - } - c_editor->SetSelection(atoi(data.GetText().c_str())); - - // to avoid event propagation to other sidebar items - c_editor->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) { - evt.StopPropagation(); - // FinishEditing grabs new selection and triggers config update. We better call - // it explicitly, automatic update on KILL_FOCUS didn't work on Linux. - this->FinishEditing(); - }); - - return c_editor; -} - -bool BitmapChoiceRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) -{ - wxBitmapComboBox* c = (wxBitmapComboBox*)ctrl; - int selection = c->GetSelection(); - if (selection < 0) - return false; - - DataViewBitmapText bmpText; - - bmpText.SetText(c->GetString(selection)); - bmpText.SetBitmap(c->GetItemBitmap(selection)); - - value << bmpText; - return true; -} } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index c8545e4a4f..12480139d2 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -4,7 +4,7 @@ #include #include -#include "GUI_App.hpp" +#include "ExtraRenderers.hpp" namespace Slic3r { @@ -15,157 +15,6 @@ namespace GUI { typedef double coordf_t; typedef std::pair t_layer_height_range; -// ---------------------------------------------------------------------------- -// DataViewBitmapText: helper class used by BitmapTextRenderer -// ---------------------------------------------------------------------------- - -class DataViewBitmapText : public wxObject -{ -public: - DataViewBitmapText( const wxString &text = wxEmptyString, - const wxBitmap& bmp = wxNullBitmap) : - m_text(text), - m_bmp(bmp) - { } - - DataViewBitmapText(const DataViewBitmapText &other) - : wxObject(), - m_text(other.m_text), - m_bmp(other.m_bmp) - { } - - void SetText(const wxString &text) { m_text = text; } - wxString GetText() const { return m_text; } - void SetBitmap(const wxBitmap &bmp) { m_bmp = bmp; } - const wxBitmap &GetBitmap() const { return m_bmp; } - - bool IsSameAs(const DataViewBitmapText& other) const { - return m_text == other.m_text && m_bmp.IsSameAs(other.m_bmp); - } - - bool operator==(const DataViewBitmapText& other) const { - return IsSameAs(other); - } - - bool operator!=(const DataViewBitmapText& other) const { - return !IsSameAs(other); - } - -private: - wxString m_text; - wxBitmap m_bmp; - - wxDECLARE_DYNAMIC_CLASS(DataViewBitmapText); -}; -DECLARE_VARIANT_OBJECT(DataViewBitmapText) - -// ---------------------------------------------------------------------------- -// BitmapTextRenderer -// ---------------------------------------------------------------------------- -#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING -class BitmapTextRenderer : public wxDataViewRenderer -#else -class BitmapTextRenderer : public wxDataViewCustomRenderer -#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING -{ -public: - BitmapTextRenderer(wxWindow* parent, - wxDataViewCellMode mode = -#ifdef __WXOSX__ - wxDATAVIEW_CELL_INERT -#else - wxDATAVIEW_CELL_EDITABLE -#endif - - , int align = wxDVR_DEFAULT_ALIGNMENT -#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING - ); -#else - ) : - wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align), - m_parent(parent) - { -#ifdef SUPPORTS_MARKUP - m_markupText = nullptr; -#endif // SUPPORTS_MARKUP - } -#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING - - ~BitmapTextRenderer(); - -#ifdef SUPPORTS_MARKUP - void EnableMarkup(bool enable = true); -#endif // SUPPORTS_MARKUP - - bool SetValue(const wxVariant& value); - bool GetValue(wxVariant& value) const; -#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY - virtual wxString GetAccessibleDescription() const override; -#endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING - - virtual bool Render(wxRect cell, wxDC* dc, int state) override; - virtual wxSize GetSize() const override; - - bool HasEditorCtrl() const override - { -#ifdef __WXOSX__ - return false; -#else - return true; -#endif - } - wxWindow* CreateEditorCtrl(wxWindow* parent, - wxRect labelRect, - const wxVariant& value) override; - bool GetValueFromEditorCtrl(wxWindow* ctrl, - wxVariant& value) override; - bool WasCanceled() const { return m_was_unusable_symbol; } - -private: - DataViewBitmapText m_value; - bool m_was_unusable_symbol{ false }; - wxWindow* m_parent{ nullptr }; - -#ifdef SUPPORTS_MARKUP - class wxItemMarkupText* m_markupText; -#endif // SUPPORTS_MARKUP -}; - - -// ---------------------------------------------------------------------------- -// BitmapChoiceRenderer -// ---------------------------------------------------------------------------- - -class BitmapChoiceRenderer : public wxDataViewCustomRenderer -{ -public: - BitmapChoiceRenderer(wxDataViewCellMode mode = -#ifdef __WXOSX__ - wxDATAVIEW_CELL_INERT -#else - wxDATAVIEW_CELL_EDITABLE -#endif - , int align = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL - ) : wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align) {} - - bool SetValue(const wxVariant& value); - bool GetValue(wxVariant& value) const; - - virtual bool Render(wxRect cell, wxDC* dc, int state) override; - virtual wxSize GetSize() const override; - - bool HasEditorCtrl() const override { return true; } - wxWindow* CreateEditorCtrl(wxWindow* parent, - wxRect labelRect, - const wxVariant& value) override; - bool GetValueFromEditorCtrl(wxWindow* ctrl, - wxVariant& value) override; - -private: - DataViewBitmapText m_value; -}; - - // ---------------------------------------------------------------------------- // ObjectDataViewModelNode: a node inside ObjectDataViewModel // ---------------------------------------------------------------------------- From 2aa1c2776c5c6b96b0e60ace24fef8720c73a502 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 6 Aug 2020 10:15:34 +0200 Subject: [PATCH 256/503] GCodeViewer -> Estimated printing times shown in the legend --- src/libslic3r/Technologies.hpp | 2 +- src/slic3r/GUI/GCodeViewer.cpp | 317 ++++++++++++++++++++++++++- src/slic3r/GUI/GCodeViewer.hpp | 3 + src/slic3r/GUI/GUI_Preview.cpp | 4 + src/slic3r/GUI/KBShortcutsDialog.cpp | 4 + 5 files changed, 320 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 75849b689a..609aecf635 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -63,6 +63,6 @@ #define TIME_ESTIMATE_DEFAULT 1 #define TIME_ESTIMATE_MODAL 2 #define TIME_ESTIMATE_LEGEND 3 -#define GCODE_VIEWER_TIME_ESTIMATE TIME_ESTIMATE_MODAL +#define GCODE_VIEWER_TIME_ESTIMATE TIME_ESTIMATE_LEGEND #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 7c5252070d..2b25a11bb5 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -411,6 +411,9 @@ void GCodeViewer::reset() #if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE m_time_statistics.reset(); #endif // GCODE_VIEWER_TIME_ESTIMATE +#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND + m_time_estimate_mode = PrintEstimatedTimeStatistics::ETimeMode::Normal; +#endif // GCODE_VIEWER_TIME_ESTIMATE #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.reset_all(); @@ -1375,8 +1378,21 @@ void GCodeViewer::render_legend() const Line }; +#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND + const PrintEstimatedTimeStatistics::Mode& time_mode = m_time_statistics.modes[static_cast(m_time_estimate_mode)]; + + float icon_size = ImGui::GetTextLineHeight(); + float percent_bar_size = 2.0f * ImGui::GetTextLineHeight(); + + auto append_item = [this, draw_list, icon_size, percent_bar_size, &imgui](EItemType type, const Color& color, const std::string& label, + bool visible = true, const std::string& time = "", float percent = 0.0f, float max_percent = 0.0f, const std::array& offsets = { 0.0f, 0.0f }, + std::function callback = nullptr) { + if (!visible) + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f); +#else auto append_item = [this, draw_list, &imgui](EItemType type, const Color& color, const std::string& label, std::function callback = nullptr) { float icon_size = ImGui::GetTextLineHeight(); +#endif // GCODE_VIEWER_TIME_ESTIMATE ImVec2 pos = ImGui::GetCursorScreenPos(); switch (type) { @@ -1438,9 +1454,49 @@ void GCodeViewer::render_legend() const if (callback != nullptr) { if (ImGui::MenuItem(label.c_str())) callback(); +#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND + else { + // show tooltip + if (ImGui::IsItemHovered()) { + if (!visible) + ImGui::PopStyleVar(); + ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROND); + ImGui::BeginTooltip(); + imgui.text(visible ? _u8L("Click to hide") : _u8L("Click to show")); + ImGui::EndTooltip(); + ImGui::PopStyleColor(); + if (!visible) + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f); + + // to avoid the tooltip to change size when moving the mouse + wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); + wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); + } + } + + if (!time.empty()) { + ImGui::SameLine(offsets[0]); + imgui.text(time); + ImGui::SameLine(offsets[1]); + pos = ImGui::GetCursorScreenPos(); + float width = percent_bar_size * percent / max_percent; + draw_list->AddRectFilled({ pos.x, pos.y + 2.0f }, { pos.x + width, pos.y + icon_size - 2.0f }, + ImGui::GetColorU32(ImGuiWrapper::COL_ORANGE_LIGHT)); + ImGui::Dummy({ percent_bar_size, icon_size }); + ImGui::SameLine(); + char buf[64]; + ::sprintf(buf, "%.1f%%", 100.0f * percent); + ImGui::TextUnformatted((percent > 0.0f) ? buf : ""); + } +#endif // GCODE_VIEWER_TIME_ESTIMATE } else imgui.text(label); + +#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND + if (!visible) + ImGui::PopStyleVar(); +#endif // GCODE_VIEWER_TIME_ESTIMATE }; auto append_range = [this, draw_list, &imgui, append_item](const Extrusions::Range& range, unsigned int decimals) { @@ -1461,6 +1517,34 @@ void GCodeViewer::render_legend() const } }; +#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND + auto append_headers = [&imgui](const std::array& texts, const std::array& offsets) { + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, texts[0]); + ImGui::SameLine(offsets[0]); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, texts[1]); + ImGui::SameLine(offsets[1]); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, texts[2]); + ImGui::Separator(); + }; + + auto max_width = [](const std::vector& items, const std::string& title, float extra_size = 0.0f) { + float ret = ImGui::CalcTextSize(title.c_str()).x; + for (const std::string& item : items) { + ret = std::max(ret, extra_size + ImGui::CalcTextSize(item.c_str()).x); + } + return ret; + }; + + auto calculate_offsets = [max_width](const std::vector& labels, const std::vector& times, + const std::array& titles, float extra_size = 0.0f) { + const ImGuiStyle& style = ImGui::GetStyle(); + std::array ret = { 0.0f, 0.0f }; + ret[0] = max_width(labels, titles[0], extra_size) + 3.0f * style.ItemSpacing.x; + ret[1] = ret[0] + max_width(times, titles[1]) + style.ItemSpacing.x; + return ret; + }; +#endif // GCODE_VIEWER_TIME_ESTIMATE + auto color_print_ranges = [this](unsigned char extruder_id, const std::vector& custom_gcode_per_print_z) { std::vector>> ret; ret.reserve(custom_gcode_per_print_z.size()); @@ -1508,10 +1592,83 @@ void GCodeViewer::render_legend() const return _u8L("from") + " " + std::string(buf1) + " " + _u8L("to") + " " + std::string(buf2) + " " + _u8L("mm"); }; - // extrusion paths -> title +#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND + auto role_time_and_percent = [this, time_mode](ExtrusionRole role) { + auto it = std::find_if(time_mode.roles_times.begin(), time_mode.roles_times.end(), [role](const std::pair& item) { return role == item.first; }); + return (it != time_mode.roles_times.end()) ? std::make_pair(it->second, it->second / time_mode.time) : std::make_pair(0.0f, 0.0f); + }; + + // data used to properly align items in columns when showing time + std::array offsets = { 0.0f, 0.0f }; + std::vector labels; + std::vector times; + std::vector percents; + float max_percent = 0.0f; + + if (m_view_type == EViewType::FeatureType) { + // calculate offsets to align time/percentage data + for (size_t i = 0; i < m_roles.size(); ++i) { + ExtrusionRole role = m_roles[i]; + if (role < erCount) { + labels.push_back(_u8L(ExtrusionEntity::role_to_string(role))); + auto [time, percent] = role_time_and_percent(role); + times.push_back((time > 0.0f) ? short_time(get_time_dhms(time)) : ""); + percents.push_back(percent); + max_percent = std::max(max_percent, percent); + } + } + + offsets = calculate_offsets(labels, times, { _u8L("Feature type"), _u8L("Time") }, icon_size); + } + + // total estimated printing time section + if (time_mode.time > 0.0f && (m_view_type == EViewType::FeatureType || + (m_view_type == EViewType::ColorPrint && !time_mode.custom_gcode_times.empty()))) { + ImGui::AlignTextToFramePadding(); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Estimated printing time") + ":"); + ImGui::SameLine(); + imgui.text(short_time(get_time_dhms(time_mode.time))); + + auto show_mode_button = [this, &imgui](const std::string& label, PrintEstimatedTimeStatistics::ETimeMode mode) { + if (m_time_statistics.modes[static_cast(mode)].roles_times.size() > 0) { + ImGui::SameLine(0.0f, 10.0f); + if (imgui.button(label)) { + m_time_estimate_mode = mode; + wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); + wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); + } + } + }; + + switch (m_time_estimate_mode) + { + case PrintEstimatedTimeStatistics::ETimeMode::Normal: + { + show_mode_button(_u8L("Show stealth mode"), PrintEstimatedTimeStatistics::ETimeMode::Stealth); + break; + } + case PrintEstimatedTimeStatistics::ETimeMode::Stealth: + { + show_mode_button(_u8L("Show normal mode"), PrintEstimatedTimeStatistics::ETimeMode::Normal); + break; + } + } + ImGui::Spacing(); + } +#endif // GCODE_VIEWER_TIME_ESTIMATE + + // extrusion paths section -> title switch (m_view_type) { +#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND + case EViewType::FeatureType: + { + append_headers({ _u8L("Feature type"), _u8L("Time"), _u8L("Percentage") }, offsets); + break; + } +#else case EViewType::FeatureType: { imgui.title(_u8L("Feature type")); break; } +#endif // GCODE_VIEWER_TIME_ESTIMATE case EViewType::Height: { imgui.title(_u8L("Height (mm)")); break; } case EViewType::Width: { imgui.title(_u8L("Width (mm)")); break; } case EViewType::Feedrate: { imgui.title(_u8L("Speed (mm/s)")); break; } @@ -1522,28 +1679,38 @@ void GCodeViewer::render_legend() const default: { break; } } - // extrusion paths -> items + // extrusion paths section -> items switch (m_view_type) { case EViewType::FeatureType: { - for (ExtrusionRole role : m_roles) { + for (size_t i = 0; i < m_roles.size(); ++i) { + ExtrusionRole role = m_roles[i]; + if (role >= erCount) + continue; bool visible = is_visible(role); +#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND + append_item(EItemType::Hexagon, Extrusion_Role_Colors[static_cast(role)], labels[i], + visible, times[i], percents[i], max_percent, offsets, [this, role, visible]() { +#else if (!visible) ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f); - append_item(EItemType::Hexagon, Extrusion_Role_Colors[static_cast(role)], _u8L(ExtrusionEntity::role_to_string(role)), [this, role]() { - if (role < erCount) { - m_extrusions.role_visibility_flags = is_visible(role) ? m_extrusions.role_visibility_flags & ~(1 << role) : m_extrusions.role_visibility_flags | (1 << role); + append_item(EItemType::Hexagon, Extrusion_Role_Colors[static_cast(role)], _u8L(ExtrusionEntity::role_to_string(role)), + [this, role, visible]() { +#endif // GCODE_VIEWER_TIME_ESTIMATE + m_extrusions.role_visibility_flags = visible ? m_extrusions.role_visibility_flags & ~(1 << role) : m_extrusions.role_visibility_flags | (1 << role); // update buffers' render paths refresh_render_paths(false, false); wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); wxGetApp().plater()->update_preview_bottom_toolbar(); } - }); + ); +#if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_LEGEND if (!visible) ImGui::PopStyleVar(); +#endif // GCODE_VIEWER_TIME_ESTIMATE } break; } @@ -1621,7 +1788,139 @@ void GCodeViewer::render_legend() const default: { break; } } - // travel paths +#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND + // partial estimated printing time section + if (m_view_type == EViewType::ColorPrint) { + using Times = std::pair; + using TimesList = std::vector>; + + // helper structure containig the data needed to render the time items + struct PartialTime + { + enum class EType : unsigned char + { + Print, + ColorChange, + Pause + }; + EType type; + int extruder_id; + Color color1; + Color color2; + Times times; + }; + using PartialTimes = std::vector; + + auto generate_partial_times = [this](const TimesList& times) { + PartialTimes items; + + std::vector custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes; + int extruders_count = wxGetApp().extruders_edited_cnt(); + std::vector last_color(extruders_count); + for (int i = 0; i < extruders_count; ++i) { + last_color[i] = m_tool_colors[i]; + } + int last_extruder_id = 1; + for (const auto& time_rec : times) { + switch (time_rec.first) + { + case CustomGCode::PausePrint: + { + auto it = std::find_if(custom_gcode_per_print_z.begin(), custom_gcode_per_print_z.end(), [time_rec](const CustomGCode::Item& item) { return item.type == time_rec.first; }); + if (it != custom_gcode_per_print_z.end()) { + items.push_back({ PartialTime::EType::Print, it->extruder, Color(), Color(), time_rec.second }); + items.push_back({ PartialTime::EType::Pause, it->extruder, Color(), Color(), time_rec.second }); + custom_gcode_per_print_z.erase(it); + } + break; + } + case CustomGCode::ColorChange: + { + auto it = std::find_if(custom_gcode_per_print_z.begin(), custom_gcode_per_print_z.end(), [time_rec](const CustomGCode::Item& item) { return item.type == time_rec.first; }); + if (it != custom_gcode_per_print_z.end()) { + items.push_back({ PartialTime::EType::Print, it->extruder, Color(), Color(), time_rec.second }); + items.push_back({ PartialTime::EType::ColorChange, it->extruder, last_color[it->extruder - 1], decode_color(it->color), time_rec.second }); + last_color[it->extruder - 1] = decode_color(it->color); + last_extruder_id = it->extruder; + custom_gcode_per_print_z.erase(it); + } + else + items.push_back({ PartialTime::EType::Print, last_extruder_id, Color(), Color(), time_rec.second }); + + break; + } + default: { break; } + } + } + + return items; + }; + + auto append_color = [this, &imgui](const Color& color1, const Color& color2, std::array& offsets, const Times& times) { + imgui.text(_u8L("Color change")); + ImGui::SameLine(); + + float icon_size = ImGui::GetTextLineHeight(); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + ImVec2 pos = ImGui::GetCursorScreenPos(); + pos.x -= 0.5f * ImGui::GetStyle().ItemSpacing.x; + ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); + draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color1[0], color1[1], color1[2], 1.0f }), 6); + center.x += icon_size; + draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color2[0], color2[1], color2[2], 1.0f }), 6); + ImGui::SameLine(offsets[0]); + imgui.text(short_time(get_time_dhms(times.second - times.first))); + }; + + PartialTimes partial_times = generate_partial_times(time_mode.custom_gcode_times); + if (!partial_times.empty()) { + labels.clear(); + times.clear(); + + for (const PartialTime& item : partial_times) { + switch (item.type) + { + case PartialTime::EType::Print: { labels.push_back(_u8L("Print")); break; } + case PartialTime::EType::Pause: { labels.push_back(_u8L("Pause")); break; } + case PartialTime::EType::ColorChange: { labels.push_back(_u8L("Color change")); break; } + } + times.push_back(short_time(get_time_dhms(item.times.second))); + } + offsets = calculate_offsets(labels, times, { _u8L("Event"), _u8L("Remaining time") }, 2.0f * icon_size); + + ImGui::Spacing(); + append_headers({ _u8L("Event"), _u8L("Remaining time"), _u8L("Duration") }, offsets); + for (const PartialTime& item : partial_times) { + switch (item.type) + { + case PartialTime::EType::Print: + { + imgui.text(_u8L("Print")); + ImGui::SameLine(offsets[0]); + imgui.text(short_time(get_time_dhms(item.times.second))); + ImGui::SameLine(offsets[1]); + imgui.text(short_time(get_time_dhms(item.times.first))); + break; + } + case PartialTime::EType::Pause: + { + imgui.text(_u8L("Pause")); + ImGui::SameLine(offsets[0]); + imgui.text(short_time(get_time_dhms(item.times.second - item.times.first))); + break; + } + case PartialTime::EType::ColorChange: + { + append_color(item.color1, item.color2, offsets, item.times); + break; + } + } + } + } + } +#endif // GCODE_VIEWER_TIME_ESTIMATE + + // travel paths section if (m_buffers[buffer_id(EMoveType::Travel)].visible) { switch (m_view_type) { @@ -1671,7 +1970,7 @@ void GCodeViewer::render_legend() const #endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR }; - // options + // options section if (any_option_available()) { // title ImGui::Spacing(); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 06cd1326fb..038b837b2b 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -350,6 +350,9 @@ private: bool m_time_estimate_enabled{ false }; #endif // GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_MODAL #endif // GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE +#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND + mutable PrintEstimatedTimeStatistics::ETimeMode m_time_estimate_mode{ PrintEstimatedTimeStatistics::ETimeMode::Normal }; +#endif // GCODE_VIEWER_TIME_ESTIMATE #if ENABLE_GCODE_VIEWER_STATISTICS mutable Statistics m_statistics; #endif // ENABLE_GCODE_VIEWER_STATISTICS diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 38657a460a..03e062d65d 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -1458,7 +1458,11 @@ wxString Preview::get_option_type_string(OptionType type) const case OptionType::CustomGCodes: { return _L("Custom GCodes"); } case OptionType::Shells: { return _L("Shells"); } case OptionType::ToolMarker: { return _L("Tool marker"); } +#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND + case OptionType::Legend: { return _L("Legend/Estimated printing time"); } +#else case OptionType::Legend: { return _L("Legend"); } +#endif // GCODE_VIEWER_TIME_ESTIMATE #if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE case OptionType::TimeEstimate: { return _L("Estimated printing time"); } #endif // GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 2c65673cd0..2d4b65a987 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -203,7 +203,11 @@ void KBShortcutsDialog::fill_shortcuts() { L("Arrow Down"), L("Lower Layer") }, { "U", L("Upper Layer") }, { "D", L("Lower Layer") }, +#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND + { "L", L("Show/Hide Legend/Estimated printing time") }, +#else { "L", L("Show/Hide Legend") }, +#endif // GCODE_VIEWER_TIME_ESTIMATE #if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_DEFAULT { "T", L("Show/Hide Estimated printing time") } #elif GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_MODAL From 1079d4644c04d075e783d3fb3ad59f13077fae32 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 6 Aug 2020 10:40:04 +0200 Subject: [PATCH 257/503] PhysicalPrinterDialog improvements : Printer device default name is changed to force the user to change it SavePresetDialog : Fixed OSX bug, when wxEVT_TEXT wasn't invoked after change selection in ComboBox --- src/slic3r/GUI/PhysicalPrinterDialog.cpp | 12 +++++++++--- src/slic3r/GUI/PresetComboBoxes.cpp | 5 +++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.cpp b/src/slic3r/GUI/PhysicalPrinterDialog.cpp index f14f498010..12d1cd2871 100644 --- a/src/slic3r/GUI/PhysicalPrinterDialog.cpp +++ b/src/slic3r/GUI/PhysicalPrinterDialog.cpp @@ -161,13 +161,15 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) SetFont(wxGetApp().normal_font()); SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - m_default_name = _L("My Printer Device"); + m_default_name = _L("Type here the name of your printer device"); + bool new_printer = true; if (printer_name.IsEmpty()) printer_name = m_default_name; else { std::string full_name = into_u8(printer_name); printer_name = from_u8(PhysicalPrinter::get_short_name(full_name)); + new_printer = false; } wxStaticText* label_top = new wxStaticText(this, wxID_ANY, _L("Descriptive name for the printer device") + ":"); @@ -206,7 +208,6 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) m_optgroup = new ConfigOptionsGroup(this, _L("Print Host upload"), m_config); build_printhost_settings(m_optgroup); - //m_optgroup->reload_config(); wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); wxButton* btnOK = static_cast(this->FindWindowById(wxID_OK, this)); @@ -230,6 +231,11 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) SetSizer(topSizer); topSizer->SetSizeHints(this); + + if (new_printer) { + m_printer_name->SetFocus(); + m_printer_name->SelectAll(); + } } PhysicalPrinterDialog::~PhysicalPrinterDialog() @@ -494,7 +500,7 @@ void PhysicalPrinterDialog::OnOK(wxEvent& event) std::string renamed_from; // temporary save previous printer name if it was edited - if (m_printer.name != _u8L("My Printer Device") && + if (m_printer.name != into_u8(m_default_name) && m_printer.name != into_u8(printer_name)) renamed_from = m_printer.name; diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 77bdb38122..da33ee51af 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -1054,6 +1054,11 @@ SavePresetDialog::Item::Item(Preset::Type type, const std::string& suffix, wxBox m_combo->Append(from_u8(value)); m_combo->Bind(wxEVT_TEXT, [this](wxCommandEvent&) { update(); }); +#ifdef __WXOSX__ + // Under OSX wxEVT_TEXT wasn't invoked after change selection in combobox, + // So process wxEVT_COMBOBOX too + m_combo->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { update(); }); +#endif //__WXOSX__ m_valid_label = new wxStaticText(m_parent, wxID_ANY, ""); m_valid_label->SetFont(wxGetApp().bold_font()); From 42f3bfb0f6a8df7f84a26c247fd1cd6620017200 Mon Sep 17 00:00:00 2001 From: Slic3rPE Date: Thu, 6 Aug 2020 10:56:14 +0200 Subject: [PATCH 258/503] Fixed a build under OSX --- src/slic3r/GUI/ExtraRenderers.cpp | 31 ++++++++++++++----------- src/slic3r/GUI/ExtraRenderers.hpp | 8 +++++-- src/slic3r/GUI/ObjectDataViewModel.cpp | 6 ----- src/slic3r/GUI/PresetComboBoxes.hpp | 1 + src/slic3r/GUI/UnsavedChangesDialog.cpp | 6 ++--- 5 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/slic3r/GUI/ExtraRenderers.cpp b/src/slic3r/GUI/ExtraRenderers.cpp index 494bfee6a2..b49a3eb60e 100644 --- a/src/slic3r/GUI/ExtraRenderers.cpp +++ b/src/slic3r/GUI/ExtraRenderers.cpp @@ -4,9 +4,11 @@ #include "I18N.hpp" #include +#ifdef wxHAS_GENERIC_DATAVIEWCTRL #include "wx/generic/private/markuptext.h" #include "wx/generic/private/rowheightcache.h" #include "wx/generic/private/widthcalc.h" +#endif #if wxUSE_ACCESSIBILITY #include "wx/private/markupparser.h" #endif // wxUSE_ACCESSIBILITY @@ -40,29 +42,30 @@ wxDataViewRenderer(wxT("PrusaDataViewBitmapText"), mode, align) BitmapTextRenderer::~BitmapTextRenderer() { #ifdef SUPPORTS_MARKUP + #ifdef wxHAS_GENERIC_DATAVIEWCTRL if (m_markupText) delete m_markupText; + #endif //wxHAS_GENERIC_DATAVIEWCTRL #endif // SUPPORTS_MARKUP } #ifdef SUPPORTS_MARKUP void BitmapTextRenderer::EnableMarkup(bool enable) { - if (enable) - { +#ifdef wxHAS_GENERIC_DATAVIEWCTRL + if (enable) { if (!m_markupText) - { m_markupText = new wxItemMarkupText(wxString()); - } } - else - { - if (m_markupText) - { + else { + if (m_markupText) { delete m_markupText; m_markupText = nullptr; } } +#elseif + is_markupText = enable +#endif //wxHAS_GENERIC_DATAVIEWCTRL } #endif // SUPPORTS_MARKUP @@ -70,10 +73,10 @@ bool BitmapTextRenderer::SetValue(const wxVariant &value) { m_value << value; -#ifdef SUPPORTS_MARKUP +#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) if (m_markupText) m_markupText->SetMarkup(m_value.GetText()); -#endif // SUPPORTS_MARKUP +#endif // SUPPORTS_MARKUP && wxHAS_GENERIC_DATAVIEWCTRL return true; } @@ -111,7 +114,7 @@ bool BitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state) xoffset = icon_sz.x + 4; } -#ifdef SUPPORTS_MARKUP +#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) if (m_markupText) { int flags = 0; @@ -120,7 +123,7 @@ bool BitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state) m_markupText->Render(GetView(), *dc, rect, flags, GetEllipsizeMode()); } else -#endif // SUPPORTS_MARKUP +#endif // SUPPORTS_MARKUP && wxHAS_GENERIC_DATAVIEWCTRL RenderText(m_value.GetText(), xoffset, rect, dc, state); return true; @@ -131,7 +134,7 @@ wxSize BitmapTextRenderer::GetSize() const if (!m_value.GetText().empty()) { wxSize size; -#ifdef SUPPORTS_MARKUP +#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) if (m_markupText) { wxDataViewCtrl* const view = GetView(); @@ -142,7 +145,7 @@ wxSize BitmapTextRenderer::GetSize() const size = m_markupText->Measure(dc); } else -#endif // SUPPORTS_MARKUP +#endif // SUPPORTS_MARKUP && wxHAS_GENERIC_DATAVIEWCTRL size = GetTextExtent(m_value.GetText()); int lines = m_value.GetText().Freq('\n') + 1; diff --git a/src/slic3r/GUI/ExtraRenderers.hpp b/src/slic3r/GUI/ExtraRenderers.hpp index 96cf349453..41f0d7d32e 100644 --- a/src/slic3r/GUI/ExtraRenderers.hpp +++ b/src/slic3r/GUI/ExtraRenderers.hpp @@ -77,9 +77,9 @@ public: wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align), m_parent(parent) { -#ifdef SUPPORTS_MARKUP +#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) m_markupText = nullptr; -#endif // SUPPORTS_MARKUP +#endif // SUPPORTS_MARKUP && wxHAS_GENERIC_DATAVIEWCTRL } #endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING @@ -120,7 +120,11 @@ private: std::function can_create_editor_ctrl { nullptr }; #ifdef SUPPORTS_MARKUP + #ifdef wxHAS_GENERIC_DATAVIEWCTRL class wxItemMarkupText* m_markupText; + #elseif + bool is_markupText; + #endif #endif // SUPPORTS_MARKUP }; diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index a42073dd0a..79fedfa527 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -9,12 +9,6 @@ #include #include -#include "wx/generic/private/markuptext.h" -#include "wx/generic/private/rowheightcache.h" -#include "wx/generic/private/widthcalc.h" -#if wxUSE_ACCESSIBILITY -#include "wx/private/markupparser.h" -#endif // wxUSE_ACCESSIBILITY namespace Slic3r { diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index f31b67fbe6..7f51f775ee 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -15,6 +15,7 @@ class ScalableButton; class wxBoxSizer; class wxComboBox; class wxStaticBitmap; +class wxRadioBox; namespace Slic3r { diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 46f086765c..4db41ffaa3 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -44,14 +44,14 @@ static std::string orange = "#ed6b21"; static void color_string(wxString& str, const std::string& color) { -#ifdef SUPPORTS_MARKUP +#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) str = from_u8((boost::format("%2%") % color % into_u8(str)).str()); #endif } static void make_string_bold(wxString& str) { -#ifdef SUPPORTS_MARKUP +#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) str = from_u8((boost::format("%1%") % into_u8(str)).str()); #endif } @@ -133,7 +133,7 @@ ModelNode::ModelNode(ModelNode* parent, const wxString& text, const wxString& ol void ModelNode::UpdateEnabling() { -#ifdef SUPPORTS_MARKUP +#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) auto change_text_color = [](wxString& str, const std::string& clr_from, const std::string& clr_to) { std::string old_val = into_u8(str); From 0c6f66eca668a2fea9aaa0f4794c08dca2e1a17b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 6 Aug 2020 13:36:21 +0200 Subject: [PATCH 259/503] GCodeViewer -> Tweaks in legend rendering --- src/slic3r/GUI/GCodeViewer.cpp | 46 +++++++++++++++++++-------------- src/slic3r/GUI/ImGuiWrapper.cpp | 21 ++++++++------- src/slic3r/GUI/ImGuiWrapper.hpp | 5 +++- 3 files changed, 43 insertions(+), 29 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 2b25a11bb5..db40e96158 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1460,7 +1460,7 @@ void GCodeViewer::render_legend() const if (ImGui::IsItemHovered()) { if (!visible) ImGui::PopStyleVar(); - ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROND); + ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND); ImGui::BeginTooltip(); imgui.text(visible ? _u8L("Click to hide") : _u8L("Click to show")); ImGui::EndTooltip(); @@ -1503,7 +1503,7 @@ void GCodeViewer::render_legend() const auto append_range_item = [this, draw_list, &imgui, append_item](int i, float value, unsigned int decimals) { char buf[1024]; ::sprintf(buf, "%.*f", decimals, value); - append_item(EItemType::Hexagon, Range_Colors[i], buf); + append_item(EItemType::Rect, Range_Colors[i], buf); }; float step_size = range.step_size(); @@ -1644,12 +1644,12 @@ void GCodeViewer::render_legend() const { case PrintEstimatedTimeStatistics::ETimeMode::Normal: { - show_mode_button(_u8L("Show stealth mode"), PrintEstimatedTimeStatistics::ETimeMode::Stealth); + show_mode_button(_u8L("Stealth mode"), PrintEstimatedTimeStatistics::ETimeMode::Stealth); break; } case PrintEstimatedTimeStatistics::ETimeMode::Stealth: { - show_mode_button(_u8L("Show normal mode"), PrintEstimatedTimeStatistics::ETimeMode::Normal); + show_mode_button(_u8L("Normal mode"), PrintEstimatedTimeStatistics::ETimeMode::Normal); break; } } @@ -1690,13 +1690,13 @@ void GCodeViewer::render_legend() const continue; bool visible = is_visible(role); #if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND - append_item(EItemType::Hexagon, Extrusion_Role_Colors[static_cast(role)], labels[i], + append_item(EItemType::Rect, Extrusion_Role_Colors[static_cast(role)], labels[i], visible, times[i], percents[i], max_percent, offsets, [this, role, visible]() { #else if (!visible) ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f); - append_item(EItemType::Hexagon, Extrusion_Role_Colors[static_cast(role)], _u8L(ExtrusionEntity::role_to_string(role)), + append_item(EItemType::Rect, Extrusion_Role_Colors[static_cast(role)], _u8L(ExtrusionEntity::role_to_string(role)), [this, role, visible]() { #endif // GCODE_VIEWER_TIME_ESTIMATE m_extrusions.role_visibility_flags = visible ? m_extrusions.role_visibility_flags & ~(1 << role) : m_extrusions.role_visibility_flags | (1 << role); @@ -1723,7 +1723,7 @@ void GCodeViewer::render_legend() const { // shows only extruders actually used for (unsigned char i : m_extruder_ids) { - append_item(EItemType::Hexagon, m_tool_colors[i], _u8L("Extruder") + " " + std::to_string(i + 1)); + append_item(EItemType::Rect, m_tool_colors[i], _u8L("Extruder") + " " + std::to_string(i + 1)); } break; } @@ -1735,20 +1735,20 @@ void GCodeViewer::render_legend() const std::vector>> cp_values = color_print_ranges(0, custom_gcode_per_print_z); const int items_cnt = static_cast(cp_values.size()); if (items_cnt == 0) { // There are no color changes, but there are some pause print or custom Gcode - append_item(EItemType::Hexagon, m_tool_colors.front(), _u8L("Default color")); + append_item(EItemType::Rect, m_tool_colors.front(), _u8L("Default color")); } else { for (int i = items_cnt; i >= 0; --i) { // create label for color change item if (i == 0) { - append_item(EItemType::Hexagon, m_tool_colors[0], upto_label(cp_values.front().second.first)); + append_item(EItemType::Rect, m_tool_colors[0], upto_label(cp_values.front().second.first)); break; } else if (i == items_cnt) { - append_item(EItemType::Hexagon, cp_values[i - 1].first, above_label(cp_values[i - 1].second.second)); + append_item(EItemType::Rect, cp_values[i - 1].first, above_label(cp_values[i - 1].second.second)); continue; } - append_item(EItemType::Hexagon, cp_values[i - 1].first, fromto_label(cp_values[i - 1].second.second, cp_values[i].second.first)); + append_item(EItemType::Rect, cp_values[i - 1].first, fromto_label(cp_values[i - 1].second.second, cp_values[i].second.first)); } } } @@ -1759,7 +1759,7 @@ void GCodeViewer::render_legend() const std::vector>> cp_values = color_print_ranges(i, custom_gcode_per_print_z); const int items_cnt = static_cast(cp_values.size()); if (items_cnt == 0) { // There are no color changes, but there are some pause print or custom Gcode - append_item(EItemType::Hexagon, m_tool_colors[i], _u8L("Extruder") + " " + std::to_string(i + 1) + " " + _u8L("default color")); + append_item(EItemType::Rect, m_tool_colors[i], _u8L("Extruder") + " " + std::to_string(i + 1) + " " + _u8L("default color")); } else { for (int j = items_cnt; j >= 0; --j) { @@ -1767,17 +1767,17 @@ void GCodeViewer::render_legend() const std::string label = _u8L("Extruder") + " " + std::to_string(i + 1); if (j == 0) { label += " " + upto_label(cp_values.front().second.first); - append_item(EItemType::Hexagon, m_tool_colors[i], label); + append_item(EItemType::Rect, m_tool_colors[i], label); break; } else if (j == items_cnt) { label += " " + above_label(cp_values[j - 1].second.second); - append_item(EItemType::Hexagon, cp_values[j - 1].first, label); + append_item(EItemType::Rect, cp_values[j - 1].first, label); continue; } label += " " + fromto_label(cp_values[j - 1].second.second, cp_values[j].second.first); - append_item(EItemType::Hexagon, cp_values[j - 1].first, label); + append_item(EItemType::Rect, cp_values[j - 1].first, label); } } } @@ -1864,10 +1864,18 @@ void GCodeViewer::render_legend() const ImDrawList* draw_list = ImGui::GetWindowDrawList(); ImVec2 pos = ImGui::GetCursorScreenPos(); pos.x -= 0.5f * ImGui::GetStyle().ItemSpacing.x; - ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); - draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color1[0], color1[1], color1[2], 1.0f }), 6); - center.x += icon_size; - draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color2[0], color2[1], color2[2], 1.0f }), 6); + + draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, + ImGui::GetColorU32({ color1[0], color1[1], color1[2], 1.0f })); + pos.x += icon_size; + draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, + ImGui::GetColorU32({ color2[0], color2[1], color2[2], 1.0f })); + +// ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); +// draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color1[0], color1[1], color1[2], 1.0f }), 6); +// center.x += icon_size; +// draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color2[0], color2[1], color2[2], 1.0f }), 6); + ImGui::SameLine(offsets[0]); imgui.text(short_time(get_time_dhms(times.second - times.first))); }; diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 0ecfdaf389..00353e4937 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -50,11 +50,14 @@ static const std::map font_icons = { {ImGui::ErrorMarker , "flag_red" } }; -const ImVec4 ImGuiWrapper::COL_WINDOW_BACKGROND = { 0.133f, 0.133f, 0.133f, 0.8f }; -const ImVec4 ImGuiWrapper::COL_GREY_DARK = { 0.333f, 0.333f, 0.333f, 1.0f }; -const ImVec4 ImGuiWrapper::COL_GREY_LIGHT = { 0.4f, 0.4f, 0.4f, 1.0f }; -const ImVec4 ImGuiWrapper::COL_ORANGE_DARK = { 0.757f, 0.404f, 0.216f, 1.0f }; -const ImVec4 ImGuiWrapper::COL_ORANGE_LIGHT = { 1.0f, 0.49f, 0.216f, 1.0f }; +const ImVec4 ImGuiWrapper::COL_GREY_DARK = { 0.333f, 0.333f, 0.333f, 1.0f }; +const ImVec4 ImGuiWrapper::COL_GREY_LIGHT = { 0.4f, 0.4f, 0.4f, 1.0f }; +const ImVec4 ImGuiWrapper::COL_ORANGE_DARK = { 0.757f, 0.404f, 0.216f, 1.0f }; +const ImVec4 ImGuiWrapper::COL_ORANGE_LIGHT = { 1.0f, 0.49f, 0.216f, 1.0f }; +const ImVec4 ImGuiWrapper::COL_WINDOW_BACKGROUND = { 0.133f, 0.133f, 0.133f, 0.8f }; +const ImVec4 ImGuiWrapper::COL_BUTTON_BACKGROUND = { 0.233f, 0.233f, 0.233f, 1.0f }; +const ImVec4 ImGuiWrapper::COL_BUTTON_HOVERED = { 0.433f, 0.433f, 0.433f, 1.8f }; +const ImVec4 ImGuiWrapper::COL_BUTTON_ACTIVE = ImGuiWrapper::COL_BUTTON_HOVERED; ImGuiWrapper::ImGuiWrapper() : m_glyph_ranges(nullptr) @@ -1031,7 +1034,7 @@ void ImGuiWrapper::init_style() // Window style.WindowRounding = 4.0f; - set_color(ImGuiCol_WindowBg, COL_WINDOW_BACKGROND); + set_color(ImGuiCol_WindowBg, COL_WINDOW_BACKGROUND); set_color(ImGuiCol_TitleBgActive, COL_ORANGE_DARK); // Generics @@ -1043,9 +1046,9 @@ void ImGuiWrapper::init_style() set_color(ImGuiCol_TextSelectedBg, COL_ORANGE_DARK); // Buttons - set_color(ImGuiCol_Button, COL_ORANGE_DARK); - set_color(ImGuiCol_ButtonHovered, COL_ORANGE_LIGHT); - set_color(ImGuiCol_ButtonActive, COL_ORANGE_LIGHT); + set_color(ImGuiCol_Button, COL_BUTTON_BACKGROUND); + set_color(ImGuiCol_ButtonHovered, COL_BUTTON_HOVERED); + set_color(ImGuiCol_ButtonActive, COL_BUTTON_ACTIVE); // Checkbox set_color(ImGuiCol_CheckMark, COL_ORANGE_LIGHT); diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index f1387ec194..5484e46c6f 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -96,11 +96,14 @@ public: bool want_text_input() const; bool want_any_input() const; - static const ImVec4 COL_WINDOW_BACKGROND; static const ImVec4 COL_GREY_DARK; static const ImVec4 COL_GREY_LIGHT; static const ImVec4 COL_ORANGE_DARK; static const ImVec4 COL_ORANGE_LIGHT; + static const ImVec4 COL_WINDOW_BACKGROUND; + static const ImVec4 COL_BUTTON_BACKGROUND; + static const ImVec4 COL_BUTTON_HOVERED; + static const ImVec4 COL_BUTTON_ACTIVE; private: void init_font(bool compress); From 41b1dc3d80d43f4a2df141edd3d4b5bd1ee24ce1 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 6 Aug 2020 14:05:42 +0200 Subject: [PATCH 260/503] Fix of custom supports 3MF loading Multiple-part objects were not handled correctly --- src/libslic3r/Format/3mf.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 3612e6898c..59dc85a0ae 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -1878,10 +1878,11 @@ namespace Slic3r { volume->calculate_convex_hull(); // recreate custom supports from previously loaded attribute - assert(geometry.custom_supports.size() == triangles_count); for (unsigned i=0; im_supported_facets.set_triangle_from_string(i, geometry.custom_supports[i]); + size_t index = src_start_id/3 + i; + assert(index < geometry.custom_supports.size()); + if (! geometry.custom_supports[index].empty()) + volume->m_supported_facets.set_triangle_from_string(i, geometry.custom_supports[index]); } // apply the remaining volume's metadata From bcd998f9f1169f8e41e29f7615a0f4fe5d887014 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 6 Aug 2020 14:25:00 +0200 Subject: [PATCH 261/503] GCodeViewer -> New set of colors for toolpaths --- src/slic3r/GUI/GCodeViewer.cpp | 37 +++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index db40e96158..ad84184015 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -218,23 +218,42 @@ void GCodeViewer::SequentialView::Marker::render() const const std::vector GCodeViewer::Extrusion_Role_Colors {{ { 0.75f, 0.75f, 0.75f }, // erNone - { 1.00f, 1.00f, 0.40f }, // erPerimeter - { 1.00f, 0.65f, 0.00f }, // erExternalPerimeter - { 0.00f, 0.00f, 1.00f }, // erOverhangPerimeter + { 1.00f, 0.90f, 0.43f }, // erPerimeter + { 1.00f, 0.49f, 0.22f }, // erExternalPerimeter + { 0.12f, 0.12f, 1.00f }, // erOverhangPerimeter { 0.69f, 0.19f, 0.16f }, // erInternalInfill - { 0.84f, 0.20f, 0.84f }, // erSolidInfill - { 1.00f, 0.10f, 0.10f }, // erTopSolidInfill - { 1.00f, 0.55f, 0.41f }, // erIroning - { 0.60f, 0.60f, 1.00f }, // erBridgeInfill + { 0.59f, 0.33f, 0.80f }, // erSolidInfill + { 0.94f, 0.33f, 0.33f }, // erTopSolidInfill + { 1.00f, 0.55f, 0.41f }, // erIroning + { 0.30f, 0.50f, 0.73f }, // erBridgeInfill { 1.00f, 1.00f, 1.00f }, // erGapFill - { 0.52f, 0.48f, 0.13f }, // erSkirt + { 0.00f, 0.53f, 0.43f }, // erSkirt { 0.00f, 1.00f, 0.00f }, // erSupportMaterial { 0.00f, 0.50f, 0.00f }, // erSupportMaterialInterface { 0.70f, 0.89f, 0.67f }, // erWipeTower - { 0.16f, 0.80f, 0.58f }, // erCustom + { 0.37f, 0.82f, 0.58f }, // erCustom { 0.00f, 0.00f, 0.00f } // erMixed }}; +//const std::vector GCodeViewer::Extrusion_Role_Colors {{ +// { 0.75f, 0.75f, 0.75f }, // erNone +// { 1.00f, 1.00f, 0.40f }, // erPerimeter +// { 1.00f, 0.65f, 0.00f }, // erExternalPerimeter +// { 0.00f, 0.00f, 1.00f }, // erOverhangPerimeter +// { 0.69f, 0.19f, 0.16f }, // erInternalInfill +// { 0.84f, 0.20f, 0.84f }, // erSolidInfill +// { 1.00f, 0.10f, 0.10f }, // erTopSolidInfill +// { 1.00f, 0.55f, 0.41f }, // erIroning +// { 0.60f, 0.60f, 1.00f }, // erBridgeInfill +// { 1.00f, 1.00f, 1.00f }, // erGapFill +// { 0.52f, 0.48f, 0.13f }, // erSkirt +// { 0.00f, 1.00f, 0.00f }, // erSupportMaterial +// { 0.00f, 0.50f, 0.00f }, // erSupportMaterialInterface +// { 0.70f, 0.89f, 0.67f }, // erWipeTower +// { 0.16f, 0.80f, 0.58f }, // erCustom +// { 0.00f, 0.00f, 0.00f } // erMixed +//}}; + const std::vector GCodeViewer::Options_Colors {{ { 1.00f, 0.00f, 1.00f }, // Retractions { 0.00f, 1.00f, 1.00f }, // Unretractions From 4913378dbe5d5e3377a56690d836813a87102d66 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 6 Aug 2020 15:54:12 +0200 Subject: [PATCH 262/503] Changed signature of the BitmapTextRenderer + Added experimental code for the rendering of the "markuped" text --- src/slic3r/GUI/ExtraRenderers.cpp | 30 ++++++++++++++++--------- src/slic3r/GUI/ExtraRenderers.hpp | 24 ++++++++------------ src/slic3r/GUI/GUI_ObjectList.cpp | 2 +- src/slic3r/GUI/Search.cpp | 2 +- src/slic3r/GUI/UnsavedChangesDialog.cpp | 17 ++++++-------- src/slic3r/GUI/UnsavedChangesDialog.hpp | 2 +- 6 files changed, 38 insertions(+), 39 deletions(-) diff --git a/src/slic3r/GUI/ExtraRenderers.cpp b/src/slic3r/GUI/ExtraRenderers.cpp index b49a3eb60e..046b8fa165 100644 --- a/src/slic3r/GUI/ExtraRenderers.cpp +++ b/src/slic3r/GUI/ExtraRenderers.cpp @@ -49,9 +49,9 @@ BitmapTextRenderer::~BitmapTextRenderer() #endif // SUPPORTS_MARKUP } -#ifdef SUPPORTS_MARKUP void BitmapTextRenderer::EnableMarkup(bool enable) { +#ifdef SUPPORTS_MARKUP #ifdef wxHAS_GENERIC_DATAVIEWCTRL if (enable) { if (!m_markupText) @@ -63,20 +63,30 @@ void BitmapTextRenderer::EnableMarkup(bool enable) m_markupText = nullptr; } } -#elseif - is_markupText = enable +#else + is_markupText = enable; #endif //wxHAS_GENERIC_DATAVIEWCTRL -} #endif // SUPPORTS_MARKUP +} bool BitmapTextRenderer::SetValue(const wxVariant &value) { m_value << value; -#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) +#ifdef SUPPORTS_MARKUP +#ifdef wxHAS_GENERIC_DATAVIEWCTRL if (m_markupText) m_markupText->SetMarkup(m_value.GetText()); -#endif // SUPPORTS_MARKUP && wxHAS_GENERIC_DATAVIEWCTRL +#else +#if defined(__WXGTK__) + GValue gvalue = G_VALUE_INIT; + g_value_init(&gvalue, G_TYPE_STRING); + g_value_set_string(&gvalue, wxGTK_CONV_FONT(str.GetText(), GetOwner()->GetOwner()->GetFont())); + g_object_set_property(G_OBJECT(m_renderer/*.GetText()*/), is_markupText ? "markup" : "text", &gvalue); + g_value_unset(&gvalue); +#endif // __WXGTK__ +#endif // wxHAS_GENERIC_DATAVIEWCTRL +#endif // SUPPORTS_MARKUP return true; } @@ -117,10 +127,8 @@ bool BitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state) #if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) if (m_markupText) { - int flags = 0; - rect.x += xoffset; - m_markupText->Render(GetView(), *dc, rect, flags, GetEllipsizeMode()); + m_markupText->Render(GetView(), *dc, rect, 0, GetEllipsizeMode()); } else #endif // SUPPORTS_MARKUP && wxHAS_GENERIC_DATAVIEWCTRL @@ -161,7 +169,7 @@ wxSize BitmapTextRenderer::GetSize() const wxWindow* BitmapTextRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) { - if (!can_create_editor_ctrl()) + if (can_create_editor_ctrl && !can_create_editor_ctrl()) return nullptr; DataViewBitmapText data; @@ -261,7 +269,7 @@ wxSize BitmapChoiceRenderer::GetSize() const wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) { - if (!can_create_editor_ctrl()) + if (can_create_editor_ctrl && !can_create_editor_ctrl()) return nullptr; std::vector icons = get_extruder_color_icons(); diff --git a/src/slic3r/GUI/ExtraRenderers.hpp b/src/slic3r/GUI/ExtraRenderers.hpp index 41f0d7d32e..4c1fb09dec 100644 --- a/src/slic3r/GUI/ExtraRenderers.hpp +++ b/src/slic3r/GUI/ExtraRenderers.hpp @@ -61,7 +61,7 @@ class BitmapTextRenderer : public wxDataViewCustomRenderer #endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING { public: - BitmapTextRenderer(wxWindow* parent, + BitmapTextRenderer(bool use_markup = false, wxDataViewCellMode mode = #ifdef __WXOSX__ wxDATAVIEW_CELL_INERT @@ -73,24 +73,19 @@ public: #if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING ); #else - ) : - wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align), - m_parent(parent) + ) : + wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align) { -#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) - m_markupText = nullptr; -#endif // SUPPORTS_MARKUP && wxHAS_GENERIC_DATAVIEWCTRL + EnableMarkup(use_markup); } #endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING ~BitmapTextRenderer(); -#ifdef SUPPORTS_MARKUP void EnableMarkup(bool enable = true); -#endif // SUPPORTS_MARKUP - bool SetValue(const wxVariant& value); - bool GetValue(wxVariant& value) const; + bool SetValue(const wxVariant& value) override; + bool GetValue(wxVariant& value) const override; #if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY virtual wxString GetAccessibleDescription() const override; #endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING @@ -115,15 +110,14 @@ public: private: DataViewBitmapText m_value; bool m_was_unusable_symbol{ false }; - wxWindow* m_parent{ nullptr }; std::function can_create_editor_ctrl { nullptr }; #ifdef SUPPORTS_MARKUP #ifdef wxHAS_GENERIC_DATAVIEWCTRL - class wxItemMarkupText* m_markupText; - #elseif - bool is_markupText; + class wxItemMarkupText* m_markupText { nullptr };; + #else + bool is_markupText {false}; #endif #endif // SUPPORTS_MARKUP }; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index bbc0f5760b..7648f7d233 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -277,7 +277,7 @@ void ObjectList::create_objects_ctrl() // column ItemName(Icon+Text) of the view control: // And Icon can be consisting of several bitmaps - BitmapTextRenderer* bmp_text_renderer = new BitmapTextRenderer(this); + BitmapTextRenderer* bmp_text_renderer = new BitmapTextRenderer(); bmp_text_renderer->set_can_create_editor_ctrl_function([this]() { return m_objects_model->GetItemType(GetSelection()) & (itVolume | itObject); }); diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index d13ef469f1..da9c8fe25d 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -323,7 +323,7 @@ const Option& OptionsSearcher::get_option(size_t pos_in_filter) const const Option& OptionsSearcher::get_option(const std::string& opt_key) const { - auto it = std::upper_bound(options.begin(), options.end(), Option({ boost::nowide::widen(opt_key) })); + auto it = std::lower_bound(options.begin(), options.end(), Option({ boost::nowide::widen(opt_key) })); assert(it != options.end()); return options[it - options.begin()]; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 4db41ffaa3..1bdc2959b7 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -35,7 +35,7 @@ static const std::map type_icon_names = { {Preset::TYPE_SLA_PRINT, "cog" }, {Preset::TYPE_FILAMENT, "spool" }, {Preset::TYPE_SLA_MATERIAL, "resin" }, - {Preset::TYPE_PRINTER, "sla_printer" }, + {Preset::TYPE_PRINTER, "printer" }, }; static std::string black = "#000000"; @@ -427,7 +427,7 @@ wxString UnsavedChangesModel::GetColumnType(unsigned int col) const //------------------------------------------ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) - : DPIDialog(NULL, wxID_ANY, _L("Unsaved Changes"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) + : DPIDialog(nullptr, wxID_ANY, _L("Unsaved Changes"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); SetBackgroundColour(bgr_clr); @@ -440,15 +440,12 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) m_tree->AssociateModel(m_tree_model); m_tree_model->SetAssociatedControl(m_tree); - m_tree->AppendToggleColumn(/*L"\u2714"*/"", UnsavedChangesModel::colToggle, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em, wxALIGN_NOT);//2610,11,12 //2714 + m_tree->AppendColumn(new wxDataViewColumn("", new BitmapTextRenderer(true), UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE)); - BitmapTextRenderer* renderer = new BitmapTextRenderer(m_tree); -#ifdef SUPPORTS_MARKUP - renderer->EnableMarkup(); -#endif - m_tree->AppendColumn(new wxDataViewColumn("", renderer, UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE)); - m_tree->AppendColumn(new wxDataViewColumn("Old value", renderer, UnsavedChangesModel::colOldValue, 20 * em, wxALIGN_TOP)); - m_tree->AppendColumn(new wxDataViewColumn("New value", renderer, UnsavedChangesModel::colNewValue, 20 * em, wxALIGN_TOP)); + m_tree->AppendToggleColumn(L"\u2714", UnsavedChangesModel::colToggle, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em);//2610,11,12 //2714 + + m_tree->AppendColumn(new wxDataViewColumn("Old value", new BitmapTextRenderer(true), UnsavedChangesModel::colOldValue, 20 * em, wxALIGN_TOP)); + m_tree->AppendColumn(new wxDataViewColumn("New value", new BitmapTextRenderer(true), UnsavedChangesModel::colNewValue, 20 * em, wxALIGN_TOP)); m_tree->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &UnsavedChangesDialog::item_value_changed, this); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index c4a02d7bcc..3d5867ea44 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -134,8 +134,8 @@ class UnsavedChangesModel : public wxDataViewModel public: enum { - colToggle, colIconText, + colToggle, colOldValue, colNewValue, colMax From 3688ae350b3b5728629fa5f8de3e37f95548ea68 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 6 Aug 2020 16:28:12 +0200 Subject: [PATCH 263/503] Added missed includes for GTK --- src/slic3r/GUI/ExtraRenderers.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/slic3r/GUI/ExtraRenderers.cpp b/src/slic3r/GUI/ExtraRenderers.cpp index 046b8fa165..d6a1d7a99d 100644 --- a/src/slic3r/GUI/ExtraRenderers.cpp +++ b/src/slic3r/GUI/ExtraRenderers.cpp @@ -9,6 +9,12 @@ #include "wx/generic/private/rowheightcache.h" #include "wx/generic/private/widthcalc.h" #endif + +#ifdef __WXGTK__ +#include "wx/gtk/private.h" +#include "wx/gtk/private/value.h" +#endif + #if wxUSE_ACCESSIBILITY #include "wx/private/markupparser.h" #endif // wxUSE_ACCESSIBILITY From 94efb5185bc1444a6adea9ad6f5ec9ca369f5e6e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 6 Aug 2020 16:54:14 +0200 Subject: [PATCH 264/503] One more experiment --- src/slic3r/GUI/ExtraRenderers.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/ExtraRenderers.cpp b/src/slic3r/GUI/ExtraRenderers.cpp index d6a1d7a99d..e4c09dc268 100644 --- a/src/slic3r/GUI/ExtraRenderers.cpp +++ b/src/slic3r/GUI/ExtraRenderers.cpp @@ -9,12 +9,12 @@ #include "wx/generic/private/rowheightcache.h" #include "wx/generic/private/widthcalc.h" #endif - +/* #ifdef __WXGTK__ #include "wx/gtk/private.h" #include "wx/gtk/private/value.h" #endif - +*/ #if wxUSE_ACCESSIBILITY #include "wx/private/markupparser.h" #endif // wxUSE_ACCESSIBILITY @@ -83,14 +83,16 @@ bool BitmapTextRenderer::SetValue(const wxVariant &value) #ifdef wxHAS_GENERIC_DATAVIEWCTRL if (m_markupText) m_markupText->SetMarkup(m_value.GetText()); + /* #else #if defined(__WXGTK__) - GValue gvalue = G_VALUE_INIT; + GValue gvalue = G_VALUE_INIT; g_value_init(&gvalue, G_TYPE_STRING); g_value_set_string(&gvalue, wxGTK_CONV_FONT(str.GetText(), GetOwner()->GetOwner()->GetFont())); - g_object_set_property(G_OBJECT(m_renderer/*.GetText()*/), is_markupText ? "markup" : "text", &gvalue); + g_object_set_property(G_OBJECT(m_renderer/ *.GetText()* /), is_markupText ? "markup" : "text", &gvalue); g_value_unset(&gvalue); #endif // __WXGTK__ + */ #endif // wxHAS_GENERIC_DATAVIEWCTRL #endif // SUPPORTS_MARKUP From b6746a3937f89a27092e3119c38c1c2cbd9e373b Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 7 Aug 2020 10:00:54 +0200 Subject: [PATCH 265/503] PhysicalPrinterDialog : Incompatible presets extracted to the separate group --- src/slic3r/GUI/PresetComboBoxes.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index da33ee51af..33ae4f54e9 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -179,7 +179,9 @@ void PresetComboBox::update(std::string select_preset_name) const std::deque& presets = m_collection->get_presets(); - std::map> nonsys_presets; + std::map> nonsys_presets; + std::map incomp_presets; + wxString selected = ""; if (!presets.front().is_visible) set_label_marker(Append(separator(L("System presets")), wxNullBitmap)); @@ -206,15 +208,15 @@ void PresetComboBox::update(std::string select_preset_name) wxBitmap* bmp = get_bmp(bitmap_key, main_icon_name, "lock_closed", is_enabled, preset.is_compatible, preset.is_system || preset.is_default); assert(bmp); - if (preset.is_default || preset.is_system) { - int item_id = Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), *bmp); - if (!is_enabled) - set_label_marker(item_id, LABEL_ITEM_DISABLED); + if (!is_enabled) + incomp_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), bmp); + else if (preset.is_default || preset.is_system) + { + Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), *bmp); validate_selection(preset.name == select_preset_name); } else { - std::pair pair(bmp, is_enabled); nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), std::pair(bmp, is_enabled)); if (preset.name == select_preset_name || (select_preset_name.empty() && is_enabled)) selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()); @@ -233,6 +235,13 @@ void PresetComboBox::update(std::string select_preset_name) validate_selection(it->first == selected); } } + if (!incomp_presets.empty()) + { + set_label_marker(Append(separator(L("Incompatible presets")), wxNullBitmap)); + for (std::map::iterator it = incomp_presets.begin(); it != incomp_presets.end(); ++it) { + set_label_marker(Append(it->first, *it->second), LABEL_ITEM_DISABLED); + } + } update_selection(); Thaw(); From 08576ad7462f4724267fae4fcf5062e83b8b8586 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 7 Aug 2020 10:21:20 +0200 Subject: [PATCH 266/503] UnsavedChangesDialog :: Toggle column is on first place now --- src/slic3r/GUI/ExtraRenderers.cpp | 6 +++--- src/slic3r/GUI/UnsavedChangesDialog.cpp | 4 +--- src/slic3r/GUI/UnsavedChangesDialog.hpp | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/ExtraRenderers.cpp b/src/slic3r/GUI/ExtraRenderers.cpp index e4c09dc268..2915d498c0 100644 --- a/src/slic3r/GUI/ExtraRenderers.cpp +++ b/src/slic3r/GUI/ExtraRenderers.cpp @@ -159,14 +159,14 @@ wxSize BitmapTextRenderer::GetSize() const dc.SetFont(GetAttr().GetEffectiveFont(view->GetFont())); size = m_markupText->Measure(dc); + + int lines = m_value.GetText().Freq('\n') + 1; + size.SetHeight(size.GetHeight() * lines); } else #endif // SUPPORTS_MARKUP && wxHAS_GENERIC_DATAVIEWCTRL size = GetTextExtent(m_value.GetText()); - int lines = m_value.GetText().Freq('\n') + 1; - size.SetHeight(size.GetHeight() * lines); - if (m_value.GetBitmap().IsOk()) size.x += m_value.GetBitmap().GetWidth() + 4; return size; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 1bdc2959b7..ab2c36ebdf 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -440,10 +440,8 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) m_tree->AssociateModel(m_tree_model); m_tree_model->SetAssociatedControl(m_tree); - m_tree->AppendColumn(new wxDataViewColumn("", new BitmapTextRenderer(true), UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE)); - m_tree->AppendToggleColumn(L"\u2714", UnsavedChangesModel::colToggle, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em);//2610,11,12 //2714 - + m_tree->AppendColumn(new wxDataViewColumn("", new BitmapTextRenderer(true), UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE)); m_tree->AppendColumn(new wxDataViewColumn("Old value", new BitmapTextRenderer(true), UnsavedChangesModel::colOldValue, 20 * em, wxALIGN_TOP)); m_tree->AppendColumn(new wxDataViewColumn("New value", new BitmapTextRenderer(true), UnsavedChangesModel::colNewValue, 20 * em, wxALIGN_TOP)); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index 3d5867ea44..c4a02d7bcc 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -134,8 +134,8 @@ class UnsavedChangesModel : public wxDataViewModel public: enum { - colIconText, colToggle, + colIconText, colOldValue, colNewValue, colMax From afcc14861f2c2a06f8d802c41af110e2e5476b1e Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 7 Aug 2020 10:42:25 +0200 Subject: [PATCH 267/503] Fix build on GCC (missing forward declaration) --- src/slic3r/GUI/PresetComboBoxes.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index f31b67fbe6..7f51f775ee 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -15,6 +15,7 @@ class ScalableButton; class wxBoxSizer; class wxComboBox; class wxStaticBitmap; +class wxRadioBox; namespace Slic3r { From c4569c93f2afc988de3c8b11fd1c9ce51af41b55 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 7 Aug 2020 15:09:58 +0200 Subject: [PATCH 268/503] UnsavedChangesDialog: Fixed get_string_from_enum() in respect to the InfillPattern --- src/slic3r/GUI/UnsavedChangesDialog.cpp | 47 ++++++++++++++++--------- src/slic3r/GUI/UnsavedChangesDialog.hpp | 11 ------ 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index ab2c36ebdf..3f85ac742f 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -2,11 +2,9 @@ #include #include +#include #include #include -#include - -#include "wx/dataview.h" #include "libslic3r/PrintConfig.hpp" #include "libslic3r/PresetBundle.hpp" @@ -15,8 +13,8 @@ #include "Tab.hpp" #include "ObjectDataViewModel.hpp" -#define FTS_FUZZY_MATCH_IMPLEMENTATION -#include "fts_fuzzy_match.h" +//#define FTS_FUZZY_MATCH_IMPLEMENTATION +//#include "fts_fuzzy_match.h" #include "BitmapCache.hpp" @@ -195,8 +193,10 @@ ModelNode* UnsavedChangesModel::AddOption(ModelNode* group_node, wxString option { ModelNode* option = new ModelNode(group_node, option_name, old_value, new_value); group_node->Append(option); - ItemAdded(wxDataViewItem((void*)group_node), wxDataViewItem((void*)option)); + wxDataViewItem group_item = wxDataViewItem((void*)group_node); + ItemAdded(group_item, wxDataViewItem((void*)option)); + m_ctrl->Expand(group_item); return option; } @@ -204,9 +204,7 @@ ModelNode* UnsavedChangesModel::AddOptionWithGroup(ModelNode* category_node, wxS { ModelNode* group_node = new ModelNode(category_node, group_name); category_node->Append(group_node); - wxDataViewItem group_item = wxDataViewItem((void*)group_node); - ItemAdded(wxDataViewItem((void*)category_node), group_item); - m_ctrl->Expand(group_item); + ItemAdded(wxDataViewItem((void*)category_node), wxDataViewItem((void*)group_node)); return AddOption(group_node, option_name, old_value, new_value); } @@ -435,16 +433,19 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) int border = 10; int em = em_unit(); - m_tree = new wxDataViewCtrl(this, wxID_ANY, wxDefaultPosition, wxSize(em * 80, em * 30), wxBORDER_SIMPLE | wxDV_VARIABLE_LINE_HEIGHT); + m_tree = new wxDataViewCtrl(this, wxID_ANY, wxDefaultPosition, wxSize(em * 80, em * 30), wxBORDER_SIMPLE | wxDV_VARIABLE_LINE_HEIGHT | wxDV_ROW_LINES); m_tree_model = new UnsavedChangesModel(this); m_tree->AssociateModel(m_tree_model); m_tree_model->SetAssociatedControl(m_tree); - m_tree->AppendToggleColumn(L"\u2714", UnsavedChangesModel::colToggle, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em);//2610,11,12 //2714 - m_tree->AppendColumn(new wxDataViewColumn("", new BitmapTextRenderer(true), UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE)); + m_tree->AppendToggleColumn(L"\u2714", UnsavedChangesModel::colToggle, wxDATAVIEW_CELL_ACTIVATABLE/*, 6 * em*/);//2610,11,12 //2714 + wxDataViewColumn* icon_text_clmn = new wxDataViewColumn("", new BitmapTextRenderer(true), UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE); + m_tree->AppendColumn(icon_text_clmn); m_tree->AppendColumn(new wxDataViewColumn("Old value", new BitmapTextRenderer(true), UnsavedChangesModel::colOldValue, 20 * em, wxALIGN_TOP)); m_tree->AppendColumn(new wxDataViewColumn("New value", new BitmapTextRenderer(true), UnsavedChangesModel::colNewValue, 20 * em, wxALIGN_TOP)); + m_tree->SetExpanderColumn(icon_text_clmn); + m_tree->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &UnsavedChangesDialog::item_value_changed, this); wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCANCEL); @@ -498,11 +499,25 @@ void UnsavedChangesDialog::close(Action action) } template -wxString get_string_from_enum(const std::string& opt_key, const DynamicPrintConfig& config) +wxString get_string_from_enum(const std::string& opt_key, const DynamicPrintConfig& config, bool is_infill = false) { - const std::vector& names = config.def()->options.at(opt_key).enum_labels;//ConfigOptionEnum::get_enum_names(); + const ConfigOptionDef& def = config.def()->options.at(opt_key); + const std::vector& names = def.enum_labels;//ConfigOptionEnum::get_enum_names(); T val = config.option>(opt_key)->value; - return from_u8(_u8L(names[static_cast(val)])); + + // Each infill doesn't use all list of infill declared in PrintConfig.hpp. + // So we should "convert" val to the correct one + if (is_infill) { + for (auto key_val : *def.enum_keys_map) + if ((int)key_val.second == (int)val) { + auto it = std::find(def.enum_values.begin(), def.enum_values.end(), key_val.first); + if (it == def.enum_values.end()) + return ""; + return from_u8(_utf8(names[it - def.enum_values.begin()])); + } + return _L("Undef"); + } + return from_u8(_utf8(names[static_cast(val)])); } static wxString get_string_value(const std::string& opt_key, const DynamicPrintConfig& config) @@ -575,7 +590,7 @@ static wxString get_string_value(const std::string& opt_key, const DynamicPrintC if (opt_key == "top_fill_pattern" || opt_key == "bottom_fill_pattern" || opt_key == "fill_pattern") - return get_string_from_enum(opt_key, config); + return get_string_from_enum(opt_key, config, true); if (opt_key == "gcode_flavor") return get_string_from_enum(opt_key, config); if (opt_key == "ironing_type") diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index c4a02d7bcc..8afd978961 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -1,19 +1,8 @@ #ifndef slic3r_UnsavedChangesDialog_hpp_ #define slic3r_UnsavedChangesDialog_hpp_ -#include -#include - -#include -#include -#include #include -#include - -#include -#include - #include "GUI_Utils.hpp" #include "wxExtensions.hpp" #include "libslic3r/Preset.hpp" From 64001c0fe5794dd747cb388b302cb8397e741ecc Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 7 Aug 2020 15:30:08 +0200 Subject: [PATCH 269/503] GCodeProcessor -> Fixed export of estimated time to gcode filename --- src/libslic3r/GCode.cpp | 12 ++++++++++++ src/libslic3r/GCode/GCodeProcessor.hpp | 3 +++ src/libslic3r/Print.cpp | 8 +++----- src/libslic3r/Print.hpp | 2 +- src/libslic3r/Utils.hpp | 19 +++++++++++++++++++ src/slic3r/GUI/3DBed.cpp | 2 ++ 6 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 915694d12f..dec0a7a197 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -715,6 +715,17 @@ std::vector>> GCode::collec } #if ENABLE_GCODE_VIEWER +// free functions called by GCode::do_export() +namespace DoExport { + static void update_print_estimated_times_stats(const GCodeProcessor& processor, PrintStatistics& print_statistics) + { + const GCodeProcessor::Result& result = processor.get_result(); + print_statistics.estimated_normal_print_time = get_time_dhm(result.time_statistics.modes[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Normal)].time); + print_statistics.estimated_silent_print_time = processor.is_stealth_time_estimator_enabled() ? + get_time_dhm(result.time_statistics.modes[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].time) : "N/A"; + } +} // namespace DoExport + void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb) #else void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb) @@ -777,6 +788,7 @@ void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_ #if ENABLE_GCODE_VIEWER m_processor.process_file(path_tmp); + DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics); if (result != nullptr) *result = std::move(m_processor.extract_result()); #else diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 101c320db8..e9c71038fe 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -317,6 +317,9 @@ namespace Slic3r { void apply_config(const PrintConfig& config); void apply_config(const DynamicPrintConfig& config); void enable_stealth_time_estimator(bool enabled); + bool is_stealth_time_estimator_enabled() const { + return m_time_processor.machines[static_cast(ETimeMode::Stealth)].enabled; + } void enable_producers(bool enabled) { m_producers_enabled = enabled; } void reset(); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 1b1aad2578..7924f18906 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -2190,13 +2190,11 @@ std::string Print::output_filename(const std::string &filename_base) const DynamicConfig PrintStatistics::config() const { DynamicConfig config; -#if !ENABLE_GCODE_VIEWER std::string normal_print_time = short_time(this->estimated_normal_print_time); std::string silent_print_time = short_time(this->estimated_silent_print_time); - config.set_key_value("print_time", new ConfigOptionString(normal_print_time)); - config.set_key_value("normal_print_time", new ConfigOptionString(normal_print_time)); - config.set_key_value("silent_print_time", new ConfigOptionString(silent_print_time)); -#endif // !ENABLE_GCODE_VIEWER + config.set_key_value("print_time", new ConfigOptionString(normal_print_time)); + config.set_key_value("normal_print_time", new ConfigOptionString(normal_print_time)); + config.set_key_value("silent_print_time", new ConfigOptionString(silent_print_time)); config.set_key_value("used_filament", new ConfigOptionFloat(this->total_used_filament / 1000.)); config.set_key_value("extruded_volume", new ConfigOptionFloat(this->total_extruded_volume)); config.set_key_value("total_cost", new ConfigOptionFloat(this->total_cost)); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 73efbf49a1..da98bba5f2 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -303,9 +303,9 @@ private: struct PrintStatistics { PrintStatistics() { clear(); } -#if !ENABLE_GCODE_VIEWER std::string estimated_normal_print_time; std::string estimated_silent_print_time; +#if !ENABLE_GCODE_VIEWER std::vector> estimated_normal_custom_gcode_print_times; std::vector> estimated_silent_custom_gcode_print_times; #endif // !ENABLE_GCODE_VIEWER diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 5cdf75037c..f7ff29b7e0 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -337,6 +337,25 @@ inline std::string get_time_dhms(float time_in_secs) return buffer; } +inline std::string get_time_dhm(float time_in_secs) +{ + int days = (int)(time_in_secs / 86400.0f); + time_in_secs -= (float)days * 86400.0f; + int hours = (int)(time_in_secs / 3600.0f); + time_in_secs -= (float)hours * 3600.0f; + int minutes = (int)(time_in_secs / 60.0f); + + char buffer[64]; + if (days > 0) + ::sprintf(buffer, "%dd %dh %dm %ds", days, hours, minutes, (int)time_in_secs); + else if (hours > 0) + ::sprintf(buffer, "%dh %dm %ds", hours, minutes, (int)time_in_secs); + else if (minutes > 0) + ::sprintf(buffer, "%dm %ds", minutes, (int)time_in_secs); + + return buffer; +} + } // namespace Slic3r #if WIN32 diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index b13f774dab..cbb74411e0 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -147,6 +147,8 @@ void Bed3D::Axes::set_stem_length(float length) } #else Bed3D::Axes::Axes() +: origin(Vec3d::Zero()) +, length(25.0 * Vec3d::Ones()) { m_quadric = ::gluNewQuadric(); if (m_quadric != nullptr) From f9e47b27028b7f513cea29843249894ae105ebc7 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Sat, 8 Aug 2020 17:03:20 +0200 Subject: [PATCH 270/503] Code refactoring: AppConfig.cpp(hpp) are removed from the GUI to libslic3r --- src/PrusaSlicer.cpp | 2 +- src/{slic3r/GUI => libslic3r}/AppConfig.cpp | 12 +++++++++--- src/{slic3r/GUI => libslic3r}/AppConfig.hpp | 3 ++- src/libslic3r/CMakeLists.txt | 2 ++ src/libslic3r/Preset.cpp | 2 +- src/libslic3r/PresetBundle.hpp | 3 +-- src/slic3r/CMakeLists.txt | 2 -- src/slic3r/Config/Snapshot.cpp | 1 - src/slic3r/GUI/3DScene.cpp | 2 +- src/slic3r/GUI/Camera.cpp | 2 +- src/slic3r/GUI/ConfigWizard_private.hpp | 1 - src/slic3r/GUI/GUI_App.cpp | 9 +++++++-- src/slic3r/GUI/Jobs/SLAImportJob.cpp | 1 - src/slic3r/GUI/MainFrame.cpp | 1 - src/slic3r/GUI/Mouse3DController.cpp | 1 - src/slic3r/GUI/Preferences.cpp | 2 +- src/slic3r/GUI/PrintHostDialogs.cpp | 2 +- 17 files changed, 27 insertions(+), 21 deletions(-) rename src/{slic3r/GUI => libslic3r}/AppConfig.cpp (97%) rename src/{slic3r/GUI => libslic3r}/AppConfig.hpp (99%) diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index a0422f5fa0..c37afb805d 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -44,6 +44,7 @@ #include "libslic3r/Format/OBJ.hpp" #include "libslic3r/Format/SL1.hpp" #include "libslic3r/Utils.hpp" +#include "libslic3r/AppConfig.hpp" #include "PrusaSlicer.hpp" @@ -52,7 +53,6 @@ #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/3DScene.hpp" #include "slic3r/GUI/InstanceCheck.hpp" - #include "slic3r/GUI/AppConfig.hpp" #include "slic3r/GUI/MainFrame.hpp" #include "slic3r/GUI/Plater.hpp" #endif /* SLIC3R_GUI */ diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/libslic3r/AppConfig.cpp similarity index 97% rename from src/slic3r/GUI/AppConfig.cpp rename to src/libslic3r/AppConfig.cpp index 93589e536e..8b41bd2716 100644 --- a/src/slic3r/GUI/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -15,8 +15,8 @@ #include #include -#include -#include "I18N.hpp" +//#include +//#include "I18N.hpp" namespace Slic3r { @@ -110,7 +110,7 @@ void AppConfig::set_defaults() erase("", "object_settings_size"); } -void AppConfig::load() +std::string AppConfig::load() { // 1) Read the complete config file into a boost::property_tree. namespace pt = boost::property_tree; @@ -120,10 +120,15 @@ void AppConfig::load() pt::read_ini(ifs, tree); } catch (pt::ptree_error& ex) { // Error while parsing config file. We'll customize the error message and rethrow to be displayed. + // ! But to avoid the use of _utf8 (related to use of wxWidgets) + // we will rethrow this exception from the place of load() call, if returned value wouldn't be empty + /* throw std::runtime_error( _utf8(L("Error parsing PrusaSlicer config file, it is probably corrupted. " "Try to manually delete the file to recover from the error. Your user profiles will not be affected.")) + "\n\n" + AppConfig::config_path() + "\n\n" + ex.what()); + */ + return ex.what(); } // 2) Parse the property_tree, extract the sections and key / value pairs. @@ -169,6 +174,7 @@ void AppConfig::load() // Override missing or keys with their defaults. this->set_defaults(); m_dirty = false; + return ""; } void AppConfig::save() diff --git a/src/slic3r/GUI/AppConfig.hpp b/src/libslic3r/AppConfig.hpp similarity index 99% rename from src/slic3r/GUI/AppConfig.hpp rename to src/libslic3r/AppConfig.hpp index 1e90d32e00..ffd1b9fdf5 100644 --- a/src/slic3r/GUI/AppConfig.hpp +++ b/src/libslic3r/AppConfig.hpp @@ -29,7 +29,8 @@ public: void set_defaults(); // Load the slic3r.ini from a user profile directory (or a datadir, if configured). - void load(); + // return error string or empty strinf + std::string load(); // Store the slic3r.ini into a user profile directory (or a datadir, if configured). void save(); diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index f11a6e7c2a..290b8953cc 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -151,6 +151,8 @@ add_library(libslic3r STATIC Preset.hpp PresetBundle.cpp PresetBundle.hpp + AppConfig.cpp + AppConfig.hpp Print.cpp Print.hpp PrintBase.cpp diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 44e6675d18..7176ca81a4 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1,7 +1,7 @@ #include #include "Preset.hpp" -#include "slic3r/GUI/AppConfig.hpp" +#include "AppConfig.hpp" #ifdef _MSC_VER #define WIN32_LEAN_AND_MEAN diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp index 2906584d33..567a12331f 100644 --- a/src/libslic3r/PresetBundle.hpp +++ b/src/libslic3r/PresetBundle.hpp @@ -2,13 +2,12 @@ #define slic3r_PresetBundle_hpp_ #include "Preset.hpp" +#include "AppConfig.hpp" #include #include #include -#include "slic3r/GUI/AppConfig.hpp" - namespace Slic3r { // Bundle of Print + Filament + Printer presets. diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 849460792b..f8598cea08 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -12,8 +12,6 @@ set(SLIC3R_GUI_SOURCES GUI/SysInfoDialog.hpp GUI/KBShortcutsDialog.cpp GUI/KBShortcutsDialog.hpp - GUI/AppConfig.cpp - GUI/AppConfig.hpp GUI/BackgroundSlicingProcess.cpp GUI/BackgroundSlicingProcess.hpp GUI/BitmapCache.cpp diff --git a/src/slic3r/Config/Snapshot.cpp b/src/slic3r/Config/Snapshot.cpp index f7d313418d..30596b614d 100644 --- a/src/slic3r/Config/Snapshot.cpp +++ b/src/slic3r/Config/Snapshot.cpp @@ -1,5 +1,4 @@ #include "Snapshot.hpp" -#include "../GUI/AppConfig.hpp" #include diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 5dc0d430f1..d683fd90c7 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -10,7 +10,6 @@ #if ENABLE_ENVIRONMENT_MAP #include "GUI_App.hpp" #include "Plater.hpp" -#include "AppConfig.hpp" #endif // ENABLE_ENVIRONMENT_MAP #include "libslic3r/ExtrusionEntity.hpp" @@ -24,6 +23,7 @@ #include "slic3r/GUI/BitmapCache.hpp" #include "libslic3r/Format/STL.hpp" #include "libslic3r/Utils.hpp" +#include "libslic3r/AppConfig.hpp" #include #include diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index ac32767c4b..c1bf8e825d 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -1,8 +1,8 @@ #include "libslic3r/libslic3r.h" +#include "libslic3r/AppConfig.hpp" #include "Camera.hpp" #include "GUI_App.hpp" -#include "AppConfig.hpp" #if ENABLE_CAMERA_STATISTICS #include "Mouse3DController.hpp" #endif // ENABLE_CAMERA_STATISTICS diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index be2919861f..9921552a73 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -22,7 +22,6 @@ #include "libslic3r/PrintConfig.hpp" #include "libslic3r/PresetBundle.hpp" #include "slic3r/Utils/PresetUpdater.hpp" -#include "AppConfig.hpp" #include "BedShapeDialog.hpp" #include "GUI.hpp" #include "wxExtensions.hpp" diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 38bda5f5e5..82c2861bc2 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -38,7 +38,6 @@ #include "GUI.hpp" #include "GUI_Utils.hpp" -#include "AppConfig.hpp" #include "3DScene.hpp" #include "MainFrame.hpp" #include "Plater.hpp" @@ -326,7 +325,13 @@ void GUI_App::init_app_config() // load settings app_conf_exists = app_config->exists(); if (app_conf_exists) { - app_config->load(); + std::string error = app_config->load(); + if (!error.empty()) + // Error while parsing config file. We'll customize the error message and rethrow to be displayed. + throw std::runtime_error( + _u8L("Error parsing PrusaSlicer config file, it is probably corrupted. " + "Try to manually delete the file to recover from the error. Your user profiles will not be affected.") + + "\n\n" + AppConfig::config_path() + "\n\n" + error); } } diff --git a/src/slic3r/GUI/Jobs/SLAImportJob.cpp b/src/slic3r/GUI/Jobs/SLAImportJob.cpp index cc779df2ad..2d5d5b0729 100644 --- a/src/slic3r/GUI/Jobs/SLAImportJob.cpp +++ b/src/slic3r/GUI/Jobs/SLAImportJob.cpp @@ -2,7 +2,6 @@ #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/GUI_App.hpp" -#include "slic3r/GUI/AppConfig.hpp" #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp" #include "slic3r/Utils/SLAImport.hpp" diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 0842803009..bbc1da534e 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -20,7 +20,6 @@ #include "Tab.hpp" #include "ProgressStatusBar.hpp" #include "3DScene.hpp" -#include "AppConfig.hpp" #include "PrintHostDialogs.hpp" #include "wxExtensions.hpp" #include "GUI_ObjectList.hpp" diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 9bbbf92a06..4c5ee20762 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -4,7 +4,6 @@ #include "Camera.hpp" #include "GUI_App.hpp" -#include "AppConfig.hpp" #include "GLCanvas3D.hpp" #include "Plater.hpp" #include "NotificationManager.hpp" diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 4b5808e16a..242c3d851d 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -1,8 +1,8 @@ #include "Preferences.hpp" -#include "AppConfig.hpp" #include "OptionsGroup.hpp" #include "GUI_App.hpp" #include "I18N.hpp" +#include "libslic3r/AppConfig.hpp" namespace Slic3r { namespace GUI { diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index bae60e47f9..216af5df49 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -15,11 +15,11 @@ #include "GUI.hpp" #include "GUI_App.hpp" -#include "AppConfig.hpp" #include "MsgDialog.hpp" #include "I18N.hpp" #include "../Utils/PrintHost.hpp" #include "wxExtensions.hpp" +#include "libslic3r/AppConfig.hpp" namespace fs = boost::filesystem; From 8b74ae4568db08dd1039204291b9b83e2842b7b4 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 10 Aug 2020 09:45:32 +0200 Subject: [PATCH 271/503] Use the wxDataViewIconTextRenderer instead of the DataViewBitmapTextRenderer under GTK --- src/slic3r/GUI/UnsavedChangesDialog.cpp | 37 ++++++++++++++++++++++--- src/slic3r/GUI/UnsavedChangesDialog.hpp | 10 +++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 3f85ac742f..33f6f19de0 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -42,14 +42,14 @@ static std::string orange = "#ed6b21"; static void color_string(wxString& str, const std::string& color) { -#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) +#if defined(SUPPORTS_MARKUP) && /*!defined(__APPLE__)*/defined(wxHAS_GENERIC_DATAVIEWCTRL) str = from_u8((boost::format("%2%") % color % into_u8(str)).str()); #endif } static void make_string_bold(wxString& str) { -#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) +#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__)//defined(wxHAS_GENERIC_DATAVIEWCTRL) str = from_u8((boost::format("%1%") % into_u8(str)).str()); #endif } @@ -60,7 +60,11 @@ ModelNode::ModelNode(Preset::Type preset_type, const wxString& text) : m_preset_type(preset_type), m_text(text) { +#ifdef __linux__ + m_icon.CopyFromBitmap(create_scaled_bitmap(type_icon_names.at(preset_type))); +#else m_icon = create_scaled_bitmap(type_icon_names.at(preset_type)); +#endif //__linux__ } // group node @@ -68,7 +72,11 @@ ModelNode::ModelNode(ModelNode* parent, const wxString& text, const std::string& m_parent(parent), m_text(text) { +#ifdef __linux__ + m_icon.CopyFromBitmap(create_scaled_bitmap(icon_name)); +#else m_icon = create_scaled_bitmap(icon_name); +#endif //__linux__ } // category node @@ -300,7 +308,11 @@ void UnsavedChangesModel::GetValue(wxVariant& variant, const wxDataViewItem& ite variant = node->m_toggle; break; case colIconText: +#ifdef __linux__ + variant << wxDataViewIconText(node->m_text, node->m_icon); +#else variant << DataViewBitmapText(node->m_text, node->m_icon); +#endif //__linux__ break; case colOldValue: variant << DataViewBitmapText(node->m_old_value, node->m_old_color_bmp); @@ -322,10 +334,18 @@ bool UnsavedChangesModel::SetValue(const wxVariant& variant, const wxDataViewIte switch (col) { case colIconText: { +#ifdef __linux__ + wxDataViewIconText data; +#else DataViewBitmapText data; +#endif //__linux__ data << variant; - node->m_icon = data.GetBitmap(); node->m_text = data.GetText(); +#ifdef __linux__ + node->m_icon = data.GetIcon(); +#else + node->m_icon = data.GetBitmap(); +#endif //__linux__ return true; } case colToggle: node->m_toggle = variant.GetBool(); @@ -439,7 +459,16 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) m_tree_model->SetAssociatedControl(m_tree); m_tree->AppendToggleColumn(L"\u2714", UnsavedChangesModel::colToggle, wxDATAVIEW_CELL_ACTIVATABLE/*, 6 * em*/);//2610,11,12 //2714 - wxDataViewColumn* icon_text_clmn = new wxDataViewColumn("", new BitmapTextRenderer(true), UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE); + +#ifdef __linux__ + wxDataViewIconTextRenderer* rd = new wxDataViewIconTextRenderer(); +#ifdef SUPPORTS_MARKUP + rd->EnableMarkup(true); +#endif + wxDataViewColumn* icon_text_clmn = new wxDataViewColumn("", rd, UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_CELL_INERT); +#else + wxDataViewColumn* icon_text_clmn = new wxDataViewColumn("", new BitmapTextRenderer(true), UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE); +#endif //__linux__ m_tree->AppendColumn(icon_text_clmn); m_tree->AppendColumn(new wxDataViewColumn("Old value", new BitmapTextRenderer(true), UnsavedChangesModel::colOldValue, 20 * em, wxALIGN_TOP)); m_tree->AppendColumn(new wxDataViewColumn("New value", new BitmapTextRenderer(true), UnsavedChangesModel::colNewValue, 20 * em, wxALIGN_TOP)); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index 8afd978961..29926cb241 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -18,6 +18,12 @@ namespace GUI{ class ModelNode; WX_DEFINE_ARRAY_PTR(ModelNode*, ModelNodePtrArray); +// On all of 3 different platforms Bitmap+Text icon column looks different +// because of Markup text is missed or not implemented. +// As a temporary workaround, we will use: +// MSW - DataViewBitmapText (our custom renderer wxBitmap + wxString, supported Markup text) +// OSX - -//-, but Markup text is not implemented right now +// GTK - wxDataViewIconText (wxWidgets for GTK renderer wxIcon + wxString, supported Markup text) class ModelNode { wxWindow* m_parent_win{ nullptr }; @@ -47,7 +53,11 @@ class ModelNode public: bool m_toggle {true}; +#ifdef __linux__ + wxIcon m_icon; +#else wxBitmap m_icon; +#endif //__linux__ wxBitmap m_old_color_bmp; wxBitmap m_new_color_bmp; wxString m_text; From f87ca111e15dce1d63c5d78045236698659148ff Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 10 Aug 2020 11:24:31 +0200 Subject: [PATCH 272/503] GTK specific: Use the wxDataViewIconTextRenderer instead of the DataViewBitmapRenderer for "Old/NewValue" columns too. + update ofthe enabling for the "Save/Move" buttons --- src/slic3r/GUI/UnsavedChangesDialog.cpp | 110 ++++++++++++++++++------ src/slic3r/GUI/UnsavedChangesDialog.hpp | 26 ++++-- 2 files changed, 106 insertions(+), 30 deletions(-) diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 33f6f19de0..cf1bc13e5f 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -42,14 +42,14 @@ static std::string orange = "#ed6b21"; static void color_string(wxString& str, const std::string& color) { -#if defined(SUPPORTS_MARKUP) && /*!defined(__APPLE__)*/defined(wxHAS_GENERIC_DATAVIEWCTRL) +#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__) str = from_u8((boost::format("%2%") % color % into_u8(str)).str()); #endif } static void make_string_bold(wxString& str) { -#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__)//defined(wxHAS_GENERIC_DATAVIEWCTRL) +#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__) str = from_u8((boost::format("%1%") % into_u8(str)).str()); #endif } @@ -86,7 +86,11 @@ ModelNode::ModelNode(ModelNode* parent, const wxString& text) : { } +#ifdef __linux__ +wxIcon ModelNode::get_bitmap(const wxString& color); +#else wxBitmap ModelNode::get_bitmap(const wxString& color) +#endif // __linux__ { /* It's supposed that standard size of an icon is 48px*16px for 100% scaled display. * So set sizes for solid_colored icons used for filament preset @@ -100,7 +104,16 @@ wxBitmap ModelNode::get_bitmap(const wxString& color) unsigned char rgb[3]; BitmapCache::parse_color(into_u8(color), rgb); // there is no need to scale created solid bitmap - return bmp_cache.mksolid(icon_width, icon_height, rgb, true); + wxBitmap bmp = bmp_cache.mksolid(icon_width, icon_height, rgb, true); + +#ifdef __linux__ + wxIcon icon; + icon.CopyFromBitmap(create_scaled_bitmap(icon_name)); + return icon; +#else + return bmp; +#endif // __linux__ + } // option node @@ -297,6 +310,13 @@ void UnsavedChangesModel::UpdateItemEnabling(wxDataViewItem item) update_parents(node); } +bool UnsavedChangesModel::IsEnabledItem(const wxDataViewItem& item) +{ + assert(item.IsOk()); + ModelNode* node = (ModelNode*)item.GetID(); + return node->IsToggled(); +} + void UnsavedChangesModel::GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const { assert(item.IsOk()); @@ -307,12 +327,19 @@ void UnsavedChangesModel::GetValue(wxVariant& variant, const wxDataViewItem& ite case colToggle: variant = node->m_toggle; break; - case colIconText: #ifdef __linux__ + case colIconText: variant << wxDataViewIconText(node->m_text, node->m_icon); + break; + case colOldValue: + variant << wxDataViewIconText(node->m_old_value, node->m_old_color_bmp); + break; + case colNewValue: + variant << wxDataViewIconText(node->m_new_value, node->m_new_color_bmp); + break; #else + case colIconText: variant << DataViewBitmapText(node->m_text, node->m_icon); -#endif //__linux__ break; case colOldValue: variant << DataViewBitmapText(node->m_old_value, node->m_old_color_bmp); @@ -320,6 +347,7 @@ void UnsavedChangesModel::GetValue(wxVariant& variant, const wxDataViewItem& ite case colNewValue: variant << DataViewBitmapText(node->m_new_value, node->m_new_color_bmp); break; +#endif //__linux__ default: wxLogError("UnsavedChangesModel::GetValue: wrong column %d", col); @@ -333,23 +361,35 @@ bool UnsavedChangesModel::SetValue(const wxVariant& variant, const wxDataViewIte ModelNode* node = (ModelNode*)item.GetID(); switch (col) { - case colIconText: { -#ifdef __linux__ - wxDataViewIconText data; -#else - DataViewBitmapText data; -#endif //__linux__ - data << variant; - node->m_text = data.GetText(); -#ifdef __linux__ - node->m_icon = data.GetIcon(); -#else - node->m_icon = data.GetBitmap(); -#endif //__linux__ - return true; } case colToggle: node->m_toggle = variant.GetBool(); return true; +#ifdef __linux__ + case colIconText: { + wxDataViewIconText data; + data << variant; + node->m_icon = data.GetIcon(); + node->m_text = data.GetText(); + return true; } + case colOldValue: { + wxDataViewIconText data; + data << variant; + node->m_old_color_bmp = data.GetIcon(); + node->m_old_value = data.GetText(); + return true; } + case colNewValue: { + wxDataViewIconText data; + data << variant; + node->m_new_color_bmp = data.GetIcon(); + node->m_new_value = data.GetText(); + return true; } +#else + case colIconText: { + DataViewBitmapText data; + data << variant; + node->m_icon = data.GetBitmap(); + node->m_text = data.GetText(); + return true; } case colOldValue: { DataViewBitmapText data; data << variant; @@ -362,6 +402,7 @@ bool UnsavedChangesModel::SetValue(const wxVariant& variant, const wxDataViewIte node->m_new_color_bmp = data.GetBitmap(); node->m_new_value = data.GetText(); return true; } +#endif //__linux__ default: wxLogError("UnsavedChangesModel::SetValue: wrong column"); } @@ -458,7 +499,7 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) m_tree->AssociateModel(m_tree_model); m_tree_model->SetAssociatedControl(m_tree); - m_tree->AppendToggleColumn(L"\u2714", UnsavedChangesModel::colToggle, wxDATAVIEW_CELL_ACTIVATABLE/*, 6 * em*/);//2610,11,12 //2714 + m_tree->AppendToggleColumn(L"\u2714", UnsavedChangesModel::colToggle, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em);//2610,11,12 //2714 #ifdef __linux__ wxDataViewIconTextRenderer* rd = new wxDataViewIconTextRenderer(); @@ -473,7 +514,7 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) m_tree->AppendColumn(new wxDataViewColumn("Old value", new BitmapTextRenderer(true), UnsavedChangesModel::colOldValue, 20 * em, wxALIGN_TOP)); m_tree->AppendColumn(new wxDataViewColumn("New value", new BitmapTextRenderer(true), UnsavedChangesModel::colNewValue, 20 * em, wxALIGN_TOP)); - m_tree->SetExpanderColumn(icon_text_clmn); +// m_tree->SetExpanderColumn(icon_text_clmn); m_tree->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &UnsavedChangesDialog::item_value_changed, this); @@ -486,14 +527,18 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) wxString label= from_u8((boost::format(_u8L("Save selected to preset:%1%"))% ("\"" + presets->get_selected_preset().name + "\"")).str()); auto save_btn = new wxButton(this, m_save_btn_id = NewControlId(), label); - save_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Save); }); buttons->Insert(0, save_btn, 0, wxLEFT, 5); + save_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Save); }); + save_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); + label = from_u8((boost::format(_u8L("Move selected to preset:%1%"))% ("\"NewSelectedPreset\"")).str()); auto move_btn = new wxButton(this, m_move_btn_id = NewControlId(), label); - move_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Move); }); buttons->Insert(1, move_btn, 0, wxLEFT, 5); + move_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Move); }); + move_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); + auto continue_btn = new wxButton(this, m_continue_btn_id = NewControlId(), _L("Continue without changes")); continue_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Continue); }); buttons->Insert(2, continue_btn, 0, wxLEFT, 5); @@ -519,6 +564,9 @@ void UnsavedChangesDialog::item_value_changed(wxDataViewEvent& event) m_tree_model->UpdateItemEnabling(item); m_tree->Refresh(); + + // update an enabling of the "save/move" buttons + m_empty_selection = get_selected_options().empty(); } void UnsavedChangesDialog::close(Action action) @@ -670,12 +718,24 @@ void UnsavedChangesDialog::update(Preset::Type type) for (const std::string& opt_key : presets->current_dirty_options()) { const Search::Option& option = searcher.get_option(opt_key); - m_tree_model->AddOption(type, option.category_local, option.group_local, option.label_local, - get_string_value(opt_key, old_config), get_string_value(opt_key, new_config)); + m_items_map.emplace(m_tree_model->AddOption(type, option.category_local, option.group_local, option.label_local, + get_string_value(opt_key, old_config), get_string_value(opt_key, new_config)), opt_key); } } +std::vector UnsavedChangesDialog::get_selected_options() +{ + std::vector ret; + + for (auto item : m_items_map) { + if (m_tree_model->IsEnabledItem(item.first)) + ret.emplace_back(item.second); + } + + return ret; +} + void UnsavedChangesDialog::on_dpi_changed(const wxRect& suggested_rect) { const int& em = em_unit(); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index 29926cb241..5c23da9978 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -2,6 +2,8 @@ #define slic3r_UnsavedChangesDialog_hpp_ #include +#include +#include #include "GUI_Utils.hpp" #include "wxExtensions.hpp" @@ -48,18 +50,24 @@ class ModelNode // would be added to the control) bool m_container {true}; +#ifdef __linux__ + wxIcon get_bitmap(const wxString& color); +#else wxBitmap get_bitmap(const wxString& color); +#endif //__linux__ public: bool m_toggle {true}; #ifdef __linux__ wxIcon m_icon; + wxIcon m_old_color_bmp; + wxIcon m_new_color_bmp; #else wxBitmap m_icon; -#endif //__linux__ wxBitmap m_old_color_bmp; wxBitmap m_new_color_bmp; +#endif //__linux__ wxString m_text; wxString m_old_value; wxString m_new_value; @@ -150,6 +158,7 @@ public: wxString old_value, wxString new_value); void UpdateItemEnabling(wxDataViewItem item); + bool IsEnabledItem(const wxDataViewItem& item); unsigned int GetColumnCount() const override { return colMax; } wxString GetColumnType(unsigned int col) const override; @@ -176,15 +185,20 @@ class UnsavedChangesDialog : public DPIDialog wxDataViewCtrl* m_tree { nullptr }; UnsavedChangesModel* m_tree_model { nullptr }; - int m_save_btn_id { wxID_ANY }; - int m_move_btn_id { wxID_ANY }; - int m_continue_btn_id { wxID_ANY }; + bool m_empty_selection { false }; + int m_save_btn_id { wxID_ANY }; + int m_move_btn_id { wxID_ANY }; + int m_continue_btn_id { wxID_ANY }; enum class Action { + Undef, Save, Move, Continue - } m_action; + } m_action {Action::Undef}; + + + std::map m_items_map; public: UnsavedChangesDialog(Preset::Type type); @@ -198,6 +212,8 @@ public: bool move_preset() const { return m_action == Action::Move; } bool just_continue() const { return m_action == Action::Continue; } + std::vector get_selected_options(); + protected: void on_dpi_changed(const wxRect& suggested_rect) override; void on_sys_color_changed() override; From 11c22e7fb2f38adc17d3007960f108c97700adb6 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 10 Aug 2020 11:41:19 +0200 Subject: [PATCH 273/503] Fixed Linux build --- src/slic3r/GUI/UnsavedChangesDialog.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index cf1bc13e5f..122e34e028 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -87,7 +87,7 @@ ModelNode::ModelNode(ModelNode* parent, const wxString& text) : } #ifdef __linux__ -wxIcon ModelNode::get_bitmap(const wxString& color); +wxIcon ModelNode::get_bitmap(const wxString& color) #else wxBitmap ModelNode::get_bitmap(const wxString& color) #endif // __linux__ @@ -113,7 +113,6 @@ wxBitmap ModelNode::get_bitmap(const wxString& color) #else return bmp; #endif // __linux__ - } // option node From 6ed2cb661de4d5175ebb8ee75b53c33009c9d1a8 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 10 Aug 2020 14:22:05 +0200 Subject: [PATCH 274/503] GCodeProcessor -> Export remaining time (lines M73) to gcode --- src/libslic3r/GCode.cpp | 17 ++- src/libslic3r/GCode/GCodeProcessor.cpp | 160 +++++++++++++++++++++++-- src/libslic3r/GCode/GCodeProcessor.hpp | 10 +- 3 files changed, 173 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index dec0a7a197..c02d24a4ee 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1281,13 +1281,16 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu print.throw_if_canceled(); // adds tags for time estimators -#if !ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER + if (print.config().remaining_times.value) + _writeln(file, GCodeProcessor::First_M73_Output_Placeholder_Tag); +#else if (print.config().remaining_times.value) { _writeln(file, GCodeTimeEstimator::Normal_First_M73_Output_Placeholder_Tag); if (m_silent_time_estimator_enabled) _writeln(file, GCodeTimeEstimator::Silent_First_M73_Output_Placeholder_Tag); } -#endif // !ENABLE_GCODE_VIEWER +#endif // ENABLE_GCODE_VIEWER // Prepare the helper object for replacing placeholders in custom G-code and output filename. m_placeholder_parser = print.placeholder_parser(); @@ -1582,14 +1585,16 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu _write(file, m_writer.postamble()); // adds tags for time estimators -#if !ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER if (print.config().remaining_times.value) - { + _writeln(file, GCodeProcessor::Last_M73_Output_Placeholder_Tag); +#else + if (print.config().remaining_times.value) { _writeln(file, GCodeTimeEstimator::Normal_Last_M73_Output_Placeholder_Tag); if (m_silent_time_estimator_enabled) _writeln(file, GCodeTimeEstimator::Silent_Last_M73_Output_Placeholder_Tag); } -#endif // !ENABLE_GCODE_VIEWER +#endif // ENABLE_GCODE_VIEWER print.throw_if_canceled(); @@ -2087,7 +2092,7 @@ void GCode::process_layer( #if ENABLE_GCODE_VIEWER // export layer z char buf[64]; - sprintf(buf, ";Z%g\n", print_z); + sprintf(buf, ";Z:%g\n", print_z); gcode += buf; // export layer height float height = first_layer ? static_cast(print_z) : static_cast(print_z) - m_last_layer_z; diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 7612af0e90..87bcde9d07 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -4,6 +4,7 @@ #include "GCodeProcessor.hpp" #include +#include #include #include @@ -28,6 +29,9 @@ const std::string GCodeProcessor::Color_Change_Tag = "Color change"; const std::string GCodeProcessor::Pause_Print_Tag = "Pause print"; const std::string GCodeProcessor::Custom_Code_Tag = "Custom gcode"; +const std::string GCodeProcessor::First_M73_Output_Placeholder_Tag = "; _GP_FIRST_M73_OUTPUT_PLACEHOLDER"; +const std::string GCodeProcessor::Last_M73_Output_Placeholder_Tag = "; _GP_LAST_M73_OUTPUT_PLACEHOLDER"; + static bool is_valid_extrusion_role(int value) { return (static_cast(erNone) <= value) && (value <= static_cast(erMixed)); @@ -161,6 +165,7 @@ void GCodeProcessor::TimeMachine::reset() prev.reset(); gcode_time.reset(); blocks = std::vector(); + g1_times_cache = std::vector(); std::fill(moves_time.begin(), moves_time.end(), 0.0f); std::fill(roles_time.begin(), roles_time.end(), 0.0f); } @@ -264,7 +269,6 @@ void GCodeProcessor::TimeMachine::calculate_time(size_t keep_last_n_blocks) recalculate_trapezoids(blocks); size_t n_blocks_process = blocks.size() - keep_last_n_blocks; -// m_g1_times.reserve(m_g1_times.size() + n_blocks_process); for (size_t i = 0; i < n_blocks_process; ++i) { const TimeBlock& block = blocks[i]; float block_time = block.time(); @@ -272,9 +276,7 @@ void GCodeProcessor::TimeMachine::calculate_time(size_t keep_last_n_blocks) gcode_time.cache += block_time; moves_time[static_cast(block.move_type)] += block_time; roles_time[static_cast(block.role)] += block_time; - -// if (block.g1_line_id >= 0) -// m_g1_times.emplace_back(block.g1_line_id, time); + g1_times_cache.push_back(time); } if (keep_last_n_blocks) @@ -286,6 +288,7 @@ void GCodeProcessor::TimeMachine::calculate_time(size_t keep_last_n_blocks) void GCodeProcessor::TimeProcessor::reset() { extruder_unloaded = true; + export_remaining_time_enabled = false; machine_limits = MachineEnvelopeConfig(); filament_load_times = std::vector(); filament_unload_times = std::vector(); @@ -295,6 +298,136 @@ void GCodeProcessor::TimeProcessor::reset() machines[static_cast(ETimeMode::Normal)].enabled = true; } +void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) +{ + boost::nowide::ifstream in(filename); + if (!in.good()) + throw std::runtime_error(std::string("Time estimator post process export failed.\nCannot open file for reading.\n")); + + // temporary file to contain modified gcode + std::string out_path = filename + ".postprocess"; + FILE* out = boost::nowide::fopen(out_path.c_str(), "wb"); + if (out == nullptr) + throw std::runtime_error(std::string("Time estimator post process export failed.\nCannot open file for writing.\n")); + + auto time_in_minutes = [](float time_in_seconds) { + return int(::roundf(time_in_seconds / 60.0f)); + }; + + auto format_line_M73 = [](const std::string& mask, int percent, int time) { + char line_M73[64]; + sprintf(line_M73, mask.c_str(), + std::to_string(percent).c_str(), + std::to_string(time).c_str()); + return std::string(line_M73); + }; + + GCodeReader parser; + std::string gcode_line; + size_t g1_lines_counter = 0; + // keeps track of last exported pair + std::array, static_cast(ETimeMode::Count)> last_exported; + for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + last_exported[i] = { 0, time_in_minutes(machines[i].time) }; + } + + // buffer line to export only when greater than 64K to reduce writing calls + std::string export_line; + + // replace placeholder lines with the proper final value + auto process_placeholders = [&](const std::string& gcode_line) { + std::string ret; + // remove trailing '\n' + std::string line = gcode_line.substr(0, gcode_line.length() - 1); + if (line == First_M73_Output_Placeholder_Tag || line == Last_M73_Output_Placeholder_Tag) { + for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + const TimeMachine& machine = machines[i]; + if (machine.enabled) { + ret += format_line_M73(machine.line_m73_mask.c_str(), + (line == First_M73_Output_Placeholder_Tag) ? 0 : 100, + (line == First_M73_Output_Placeholder_Tag) ? time_in_minutes(machines[i].time) : 0); + } + } + } + return std::make_pair(!ret.empty(), ret.empty() ? gcode_line : ret); + }; + + // add lines M73 to exported gcode + auto process_line_G1 = [&]() { + for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + const TimeMachine& machine = machines[i]; + if (machine.enabled && g1_lines_counter < machine.g1_times_cache.size()) { + float elapsed_time = machine.g1_times_cache[g1_lines_counter]; + std::pair to_export = { int(::roundf(100.0f * elapsed_time / machine.time)), + time_in_minutes(machine.time - elapsed_time) }; + if (last_exported[i] != to_export) { + export_line += format_line_M73(machine.line_m73_mask.c_str(), + to_export.first, to_export.second); + last_exported[i] = to_export; + } + } + } + }; + + // helper function to write to disk + auto write_string = [&](const std::string& str) { + fwrite((const void*)export_line.c_str(), 1, export_line.length(), out); + if (ferror(out)) { + in.close(); + fclose(out); + boost::nowide::remove(out_path.c_str()); + throw std::runtime_error(std::string("Time estimator post process export failed.\nIs the disk full?\n")); + } + export_line.clear(); + }; + + while (std::getline(in, gcode_line)) { + if (!in.good()) { + fclose(out); + throw std::runtime_error(std::string("Time estimator post process export failed.\nError while reading from file.\n")); + } + + gcode_line += "\n"; + auto [processed, result] = process_placeholders(gcode_line); + gcode_line = result; + if (!processed) { + parser.parse_line(gcode_line, + [&](GCodeReader& reader, const GCodeReader::GCodeLine& line) { + if (line.cmd_is("G1")) { + process_line_G1(); + ++g1_lines_counter; + } + }); + } + + export_line += gcode_line; + if (export_line.length() > 65535) + write_string(export_line); + } + + for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + const TimeMachine& machine = machines[i]; + ETimeMode mode = static_cast(i); + if (machine.enabled) { + char line[128]; + sprintf(line, "; estimated printing time (%s mode) = %s\n", + (mode == ETimeMode::Normal) ? "normal" : "silent", + get_time_dhms(machine.time).c_str()); + export_line += line; + } + } + + if (!export_line.empty()) + write_string(export_line); + + fclose(out); + in.close(); + + if (rename_file(out_path, filename)) + throw std::runtime_error(std::string("Failed to rename the output G-code file from ") + out_path + " to " + filename + '\n' + + "Is " + out_path + " locked?" + '\n'); +} + const std::vector> GCodeProcessor::Producers = { { EProducer::PrusaSlicer, "PrusaSlicer" }, { EProducer::Cura, "Cura_SteamEngine" }, @@ -305,6 +438,13 @@ const std::vector> GCodeProces unsigned int GCodeProcessor::s_result_id = 0; +GCodeProcessor::GCodeProcessor() +{ + reset(); + m_time_processor.machines[static_cast(ETimeMode::Normal)].line_m73_mask = "M73 P%s R%s\n"; + m_time_processor.machines[static_cast(ETimeMode::Stealth)].line_m73_mask = "M73 Q%s S%s\n"; +} + void GCodeProcessor::apply_config(const PrintConfig& config) { m_parser.apply_config(config); @@ -346,6 +486,8 @@ void GCodeProcessor::apply_config(const PrintConfig& config) float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i); m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION; } + + m_time_processor.export_remaining_time_enabled = config.remaining_times.value; } void GCodeProcessor::apply_config(const DynamicPrintConfig& config) @@ -572,6 +714,10 @@ void GCodeProcessor::process_file(const std::string& filename) update_estimated_times_stats(); + // post-process to add M73 lines into the gcode + if (m_time_processor.export_remaining_time_enabled) + m_time_processor.post_process(filename); + #if ENABLE_GCODE_VIEWER_STATISTICS m_result.time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start_time).count(); #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -1525,13 +1671,13 @@ void GCodeProcessor::process_M201(const GCodeReader::GCodeLine& line) if (line.has_x()) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_x, i, line.x() * factor); - if (line.has_y() && i < m_time_processor.machine_limits.machine_max_acceleration_y.values.size()) + if (line.has_y()) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_y, i, line.y() * factor); - if (line.has_z() && i < m_time_processor.machine_limits.machine_max_acceleration_z.values.size()) + if (line.has_z()) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_z, i, line.z() * factor); - if (line.has_e() && i < m_time_processor.machine_limits.machine_max_acceleration_e.values.size()) + if (line.has_e()) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_e, i, line.e() * factor); } } diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index e9c71038fe..840b5373bc 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -72,6 +72,8 @@ namespace Slic3r { static const std::string Color_Change_Tag; static const std::string Pause_Print_Tag; static const std::string Custom_Code_Tag; + static const std::string First_M73_Output_Placeholder_Tag; + static const std::string Last_M73_Output_Placeholder_Tag; private: using AxisCoords = std::array; @@ -182,10 +184,12 @@ namespace Slic3r { float acceleration; // mm/s^2 float extrude_factor_override_percentage; float time; // s + std::string line_m73_mask; State curr; State prev; CustomGCodeTime gcode_time; std::vector blocks; + std::vector g1_times_cache; std::array(EMoveType::Count)> moves_time; std::array(ExtrusionRole::erCount)> roles_time; @@ -212,6 +216,7 @@ namespace Slic3r { // This is currently only really used by the MK3 MMU2: // extruder_unloaded = true means no filament is loaded yet, all the filaments are parked in the MK3 MMU2 unit. bool extruder_unloaded; + bool export_remaining_time_enabled; MachineEnvelopeConfig machine_limits; // Additional load / unload times for a filament exchange sequence. std::vector filament_load_times; @@ -219,6 +224,9 @@ namespace Slic3r { std::array(ETimeMode::Count)> machines; void reset(); + + // post process the file with the given filename to add remaining time lines M73 + void post_process(const std::string& filename); }; public: @@ -312,7 +320,7 @@ namespace Slic3r { static unsigned int s_result_id; public: - GCodeProcessor() { reset(); } + GCodeProcessor(); void apply_config(const PrintConfig& config); void apply_config(const DynamicPrintConfig& config); From 058e024d2dbe8d2b2d0d127e54af171cb7871c48 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 10 Aug 2020 19:07:45 +0200 Subject: [PATCH 275/503] Implemented FullCompareDialog for show long string values + fixed build under GTK --- src/slic3r/GUI/Tab.cpp | 4 +- src/slic3r/GUI/UnsavedChangesDialog.cpp | 194 ++++++++++++++++++++---- src/slic3r/GUI/UnsavedChangesDialog.hpp | 51 ++++++- src/slic3r/GUI/wxExtensions.cpp | 16 +- src/slic3r/GUI/wxExtensions.hpp | 6 +- 5 files changed, 231 insertions(+), 40 deletions(-) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 4a250dc526..6501fba27b 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2990,7 +2990,7 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, bool canceled = false; bool technology_changed = false; m_dependent_tabs.clear(); - if (current_dirty && ! may_discard_current_dirty_preset()) { + if (current_dirty && ! may_discard_current_dirty_preset(nullptr, preset_name)) { canceled = true; } else if (print_tab) { // Before switching the print profile to a new one, verify, whether the currently active filament or SLA material @@ -3132,7 +3132,7 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, // if the current preset was not dirty, or the user agreed to discard the changes, 1 is returned. bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr*/, const std::string& new_printer_name /*= ""*/) { - UnsavedChangesDialog dlg(m_type); + UnsavedChangesDialog dlg(m_type, new_printer_name); if (dlg.ShowModal() == wxID_CANCEL) return false; if (dlg.just_continue()) diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 122e34e028..78d599f7aa 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -11,7 +11,8 @@ #include "GUI_App.hpp" #include "Plater.hpp" #include "Tab.hpp" -#include "ObjectDataViewModel.hpp" +#include "ExtraRenderers.hpp" +#include "wxExtensions.hpp" //#define FTS_FUZZY_MATCH_IMPLEMENTATION //#include "fts_fuzzy_match.h" @@ -104,14 +105,12 @@ wxBitmap ModelNode::get_bitmap(const wxString& color) unsigned char rgb[3]; BitmapCache::parse_color(into_u8(color), rgb); // there is no need to scale created solid bitmap - wxBitmap bmp = bmp_cache.mksolid(icon_width, icon_height, rgb, true); - -#ifdef __linux__ - wxIcon icon; - icon.CopyFromBitmap(create_scaled_bitmap(icon_name)); - return icon; +#ifndef __linux__ + return bmp_cache.mksolid(icon_width, icon_height, rgb, true); #else - return bmp; + wxIcon icon; + icon.CopyFromBitmap(bmp_cache.mksolid(icon_width, icon_height, rgb, true)); + return icon; #endif // __linux__ } @@ -484,7 +483,7 @@ wxString UnsavedChangesModel::GetColumnType(unsigned int col) const // UnsavedChangesDialog //------------------------------------------ -UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) +UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& new_selected_preset) : DPIDialog(nullptr, wxID_ANY, _L("Unsaved Changes"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); @@ -516,6 +515,8 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) // m_tree->SetExpanderColumn(icon_text_clmn); m_tree->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &UnsavedChangesDialog::item_value_changed, this); + m_tree->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &UnsavedChangesDialog::context_menu, this); + m_tree->Bind(wxEVT_MOTION, [this](wxMouseEvent& e) { show_info_line(Action::Undef); e.Skip(); }); wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCANCEL); @@ -525,31 +526,42 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) PresetCollection* presets = tab->get_presets(); wxString label= from_u8((boost::format(_u8L("Save selected to preset:%1%"))% ("\"" + presets->get_selected_preset().name + "\"")).str()); - auto save_btn = new wxButton(this, m_save_btn_id = NewControlId(), label); - buttons->Insert(0, save_btn, 0, wxLEFT, 5); + m_save_btn = new ScalableButton(this, m_save_btn_id = NewControlId(), "save", label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true); + buttons->Insert(0, m_save_btn, 0, wxLEFT, 5); - save_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Save); }); - save_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); + m_save_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Save); }); + m_save_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); + m_save_btn->Bind(wxEVT_MOTION, [this, presets](wxMouseEvent& e){ show_info_line(Action::Save, presets->get_selected_preset().name); e.Skip(); }); - label = from_u8((boost::format(_u8L("Move selected to preset:%1%"))% ("\"NewSelectedPreset\"")).str()); - auto move_btn = new wxButton(this, m_move_btn_id = NewControlId(), label); - buttons->Insert(1, move_btn, 0, wxLEFT, 5); + label = from_u8((boost::format(_u8L("Move selected to preset:%1%"))% ("\"" + from_u8(new_selected_preset) + "\"")).str()); + m_move_btn = new ScalableButton(this, m_move_btn_id = NewControlId(), "paste_menu", label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true); + buttons->Insert(1, m_move_btn, 0, wxLEFT, 5); - move_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Move); }); - move_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); + m_move_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Move); }); + m_move_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); + m_move_btn->Bind(wxEVT_MOTION, [this, new_selected_preset](wxMouseEvent& e){ show_info_line(Action::Move, new_selected_preset); e.Skip(); }); - auto continue_btn = new wxButton(this, m_continue_btn_id = NewControlId(), _L("Continue without changes")); - continue_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Continue); }); - buttons->Insert(2, continue_btn, 0, wxLEFT, 5); + label = _L("Continue without changes"); + m_continue_btn = new ScalableButton(this, m_continue_btn_id = NewControlId(), "cross", label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true); + buttons->Insert(2, m_continue_btn, 0, wxLEFT, 5); + m_continue_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Continue); }); + m_continue_btn->Bind(wxEVT_MOTION, [this](wxMouseEvent& e){ show_info_line(Action::Continue); e.Skip(); }); + + m_info_line = new wxStaticText(this, wxID_ANY, ""); + m_info_line->SetFont(wxGetApp().bold_font()); + m_info_line->Hide(); wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - topSizer->Add(new wxStaticText(this, wxID_ANY, _L("There is unsaved changes for") + (": \"" + tab->title() + "\"")), 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(new wxStaticText(this, wxID_ANY, _L("There are unsaved changes for") + (": \"" + tab->title() + "\"")), 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); topSizer->Add(m_tree, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(m_info_line, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, 2*border); topSizer->Add(buttons, 0, wxEXPAND | wxALL, border); update(type); + this->Bind(wxEVT_MOTION, [this](wxMouseEvent& e) { show_info_line(Action::Undef); e.Skip(); }); + SetSizer(topSizer); topSizer->SetSizeHints(this); } @@ -568,9 +580,61 @@ void UnsavedChangesDialog::item_value_changed(wxDataViewEvent& event) m_empty_selection = get_selected_options().empty(); } +void UnsavedChangesDialog::context_menu(wxDataViewEvent& event) +{ + if (!m_has_long_strings) + return; + + wxDataViewItem item = event.GetItem(); + if (!item) + { + wxPoint mouse_pos = wxGetMousePosition() - m_tree->GetScreenPosition(); + wxDataViewColumn* col = nullptr; + m_tree->HitTest(mouse_pos, item, col); + + if (!item) + item = m_tree->GetSelection(); + + if (!item) + return; + } + + auto it = m_items_map.find(item); + if (it == m_items_map.end() || !it->second.is_long) + return; + FullCompareDialog(it->second.opt_name, it->second.old_val, it->second.new_val).ShowModal(); +} + +void UnsavedChangesDialog::show_info_line(Action action, std::string preset_name) +{ + if (m_motion_action == action) + return; + if (action == Action::Undef && !m_has_long_strings) + m_info_line->Hide(); + else { + wxString text; + if (action == Action::Undef) + text = _L("Some fields are too long to fit. Right click on it to show full text."); + else if (action == Action::Continue) + text = _L("All changed options will be reverted."); + else { + std::string act_string = action == Action::Save ? _u8L("saved") : _u8L("moved"); + text = from_u8((boost::format("After press this button selected options will be %1% to the preset \"%2%\".") % act_string % preset_name).str()); + text += "\n" + _L("Unselected options will be reverted."); + } + m_info_line->SetLabel(text); + m_info_line->Show(); + } + + m_motion_action = action; + + Layout(); + Refresh(); +} + void UnsavedChangesDialog::close(Action action) { - m_action = action; + m_exit_action = action; this->EndModal(wxID_CLOSE); } @@ -698,6 +762,23 @@ static wxString get_string_value(const std::string& opt_key, const DynamicPrintC return out; } +wxString UnsavedChangesDialog::get_short_string(wxString full_string) +{ + int max_len = 30; + if (full_string.IsEmpty() || full_string.StartsWith("#") || + (full_string.Find("\n") == wxNOT_FOUND && full_string.Length() < max_len)) + return full_string; + + m_has_long_strings = true; + + int n_pos = full_string.Find("\n"); + if (n_pos != wxNOT_FOUND && n_pos < max_len) + max_len = n_pos; + + full_string.Truncate(max_len); + return full_string + dots; +} + void UnsavedChangesDialog::update(Preset::Type type) { Tab* tab = wxGetApp().get_tab(type); @@ -717,8 +798,14 @@ void UnsavedChangesDialog::update(Preset::Type type) for (const std::string& opt_key : presets->current_dirty_options()) { const Search::Option& option = searcher.get_option(opt_key); - m_items_map.emplace(m_tree_model->AddOption(type, option.category_local, option.group_local, option.label_local, - get_string_value(opt_key, old_config), get_string_value(opt_key, new_config)), opt_key); + ItemData item_data = { opt_key, option.label_local, get_string_value(opt_key, old_config), get_string_value(opt_key, new_config) }; + + wxString old_val = get_short_string(item_data.old_val); + wxString new_val = get_short_string(item_data.new_val); + if (old_val != item_data.old_val || new_val != item_data.new_val) + item_data.is_long = true; + + m_items_map.emplace(m_tree_model->AddOption(type, option.category_local, option.group_local, option.label_local, old_val, new_val), item_data); } } @@ -727,10 +814,9 @@ std::vector UnsavedChangesDialog::get_selected_options() { std::vector ret; - for (auto item : m_items_map) { + for (auto item : m_items_map) if (m_tree_model->IsEnabledItem(item.first)) - ret.emplace_back(item.second); - } + ret.emplace_back(item.second.opt_key); return ret; } @@ -757,6 +843,58 @@ void UnsavedChangesDialog::on_sys_color_changed() } +//------------------------------------------ +// FullCompareDialog +//------------------------------------------ + +FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString& old_value, const wxString& new_value) + : wxDialog(nullptr, wxID_ANY, option_name, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +{ + wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); + SetBackgroundColour(bgr_clr); + + int border = 10; + + wxStaticBoxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, this); + + wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(2, 2, 1, 0); + grid_sizer->SetFlexibleDirection(wxBOTH); + grid_sizer->AddGrowableCol(0,1); + grid_sizer->AddGrowableCol(1,1); + grid_sizer->AddGrowableRow(1,1); + + auto add_header = [grid_sizer, border, this](wxString label) { + wxStaticText* text = new wxStaticText(this, wxID_ANY, label); + text->SetFont(wxGetApp().bold_font()); + grid_sizer->Add(text, 0, wxALL, border); + }; + + auto add_value = [grid_sizer, border, this](wxString label, bool is_colored = false) { + wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, label, wxDefaultPosition, wxSize(300, -1), wxTE_MULTILINE | wxTE_READONLY | wxBORDER_NONE); + if (is_colored) + text->SetForegroundColour(wxColour(orange)); + grid_sizer->Add(text, 1, wxALL | wxEXPAND, border); + }; + + add_header(_L("Old value")); + add_header(_L("New value")); + add_value(old_value); + add_value(new_value, true); + + sizer->Add(grid_sizer, 1, wxEXPAND); + + wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxOK); + + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + + topSizer->Add(sizer, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(buttons, 0, wxEXPAND | wxALL, border); + + SetSizer(topSizer); + topSizer->SetSizeHints(this); +} + + } } // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index 5c23da9978..991c894422 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -9,8 +9,10 @@ #include "wxExtensions.hpp" #include "libslic3r/Preset.hpp" -namespace Slic3r { +class ScalableButton; +class wxStaticText; +namespace Slic3r { namespace GUI{ // ---------------------------------------------------------------------------- @@ -185,7 +187,13 @@ class UnsavedChangesDialog : public DPIDialog wxDataViewCtrl* m_tree { nullptr }; UnsavedChangesModel* m_tree_model { nullptr }; + ScalableButton* m_save_btn { nullptr }; + ScalableButton* m_move_btn { nullptr }; + ScalableButton* m_continue_btn { nullptr }; + wxStaticText* m_info_line { nullptr }; + bool m_empty_selection { false }; + bool m_has_long_strings { false }; int m_save_btn_id { wxID_ANY }; int m_move_btn_id { wxID_ANY }; int m_continue_btn_id { wxID_ANY }; @@ -195,22 +203,40 @@ class UnsavedChangesDialog : public DPIDialog Save, Move, Continue - } m_action {Action::Undef}; + }; + // selected action after Dialog closing + Action m_exit_action {Action::Undef}; - std::map m_items_map; + // Action during mouse motion + Action m_motion_action {Action::Undef}; + + struct ItemData + { + std::string opt_key; + wxString opt_name; + wxString old_val; + wxString new_val; + bool is_long {false}; + }; + // tree items related to the options + std::map m_items_map; public: - UnsavedChangesDialog(Preset::Type type); + UnsavedChangesDialog(Preset::Type type, const std::string& new_selected_preset); ~UnsavedChangesDialog() {} + wxString get_short_string(wxString full_string); + void update(Preset::Type type); void item_value_changed(wxDataViewEvent &event); + void context_menu(wxDataViewEvent &event); + void show_info_line(Action action, std::string preset_name = ""); void close(Action action); - bool save_preset() const { return m_action == Action::Save; } - bool move_preset() const { return m_action == Action::Move; } - bool just_continue() const { return m_action == Action::Continue; } + bool save_preset() const { return m_exit_action == Action::Save; } + bool move_preset() const { return m_exit_action == Action::Move; } + bool just_continue() const { return m_exit_action == Action::Continue; } std::vector get_selected_options(); @@ -220,6 +246,17 @@ protected: }; +//------------------------------------------ +// FullCompareDialog +//------------------------------------------ +class FullCompareDialog : public wxDialog +{ +public: + FullCompareDialog(const wxString& option_name, const wxString& old_value, const wxString& new_value); + ~FullCompareDialog() {} +}; + + } } diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 67b5a18f79..0cf09b4aea 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -782,9 +782,11 @@ ScalableButton::ScalableButton( wxWindow * parent, const wxString& label /* = wxEmptyString*/, const wxSize& size /* = wxDefaultSize*/, const wxPoint& pos /* = wxDefaultPosition*/, - long style /*= wxBU_EXACTFIT | wxNO_BORDER*/) : + long style /*= wxBU_EXACTFIT | wxNO_BORDER*/, + bool use_default_disabled_bitmap/* = false*/) : + m_parent(parent), m_current_icon_name(icon_name), - m_parent(parent) + m_use_default_disabled_bitmap (use_default_disabled_bitmap) { Create(parent, id, label, pos, size, style); #ifdef __WXMSW__ @@ -793,6 +795,8 @@ ScalableButton::ScalableButton( wxWindow * parent, #endif // __WXMSW__ SetBitmap(create_scaled_bitmap(icon_name, parent)); + if (m_use_default_disabled_bitmap) + SetBitmapDisabled(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt, true)); if (size != wxDefaultSize) { @@ -842,11 +846,19 @@ int ScalableButton::GetBitmapHeight() #endif } +void ScalableButton::UseDefaultBitmapDisabled() +{ + m_use_default_disabled_bitmap = true; + SetBitmapDisabled(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt, true)); +} + void ScalableButton::msw_rescale() { SetBitmap(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt)); if (!m_disabled_icon_name.empty()) SetBitmapDisabled(create_scaled_bitmap(m_disabled_icon_name, m_parent, m_px_cnt)); + else if (m_use_default_disabled_bitmap) + SetBitmapDisabled(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt, true)); if (m_width > 0 || m_height>0) { diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 9be3361bda..8fe28b2e57 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -210,7 +210,8 @@ public: const wxString& label = wxEmptyString, const wxSize& size = wxDefaultSize, const wxPoint& pos = wxDefaultPosition, - long style = wxBU_EXACTFIT | wxNO_BORDER); + long style = wxBU_EXACTFIT | wxNO_BORDER, + bool use_default_disabled_bitmap = false); ScalableButton( wxWindow * parent, @@ -224,6 +225,7 @@ public: void SetBitmap_(const ScalableBitmap& bmp); void SetBitmapDisabled_(const ScalableBitmap &bmp); int GetBitmapHeight(); + void UseDefaultBitmapDisabled(); void msw_rescale(); @@ -234,6 +236,8 @@ private: int m_width {-1}; // should be multiplied to em_unit int m_height{-1}; // should be multiplied to em_unit + bool m_use_default_disabled_bitmap {false}; + // bitmap dimensions int m_px_cnt{ 16 }; }; From 6a33c967cfa62290b932be44b3f174f6ff27e067 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 11 Aug 2020 09:17:52 +0200 Subject: [PATCH 276/503] Fixed GTK build --- src/slic3r/GUI/UnsavedChangesDialog.cpp | 38 ++++++++++++++----------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 78d599f7aa..dcd1d760ae 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -499,33 +499,38 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& m_tree->AppendToggleColumn(L"\u2714", UnsavedChangesModel::colToggle, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em);//2610,11,12 //2714 + auto append_bmp_text_column = [this](const wxString& label, unsigned model_column, int width, bool set_expander = false) + { #ifdef __linux__ - wxDataViewIconTextRenderer* rd = new wxDataViewIconTextRenderer(); + wxDataViewIconTextRenderer* rd = new wxDataViewIconTextRenderer(); #ifdef SUPPORTS_MARKUP - rd->EnableMarkup(true); + rd->EnableMarkup(true); #endif - wxDataViewColumn* icon_text_clmn = new wxDataViewColumn("", rd, UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_CELL_INERT); + wxDataViewColumn* column = new wxDataViewColumn(label, rd, model_column, width, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_CELL_INERT); #else - wxDataViewColumn* icon_text_clmn = new wxDataViewColumn("", new BitmapTextRenderer(true), UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE); + wxDataViewColumn* column = new wxDataViewColumn(label, new BitmapTextRenderer(true), model_column, width, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE); #endif //__linux__ - m_tree->AppendColumn(icon_text_clmn); - m_tree->AppendColumn(new wxDataViewColumn("Old value", new BitmapTextRenderer(true), UnsavedChangesModel::colOldValue, 20 * em, wxALIGN_TOP)); - m_tree->AppendColumn(new wxDataViewColumn("New value", new BitmapTextRenderer(true), UnsavedChangesModel::colNewValue, 20 * em, wxALIGN_TOP)); + m_tree->AppendColumn(column); + if (set_expander) + m_tree->SetExpanderColumn(column); + }; -// m_tree->SetExpanderColumn(icon_text_clmn); + append_bmp_text_column("", UnsavedChangesModel::colIconText, 30 * em); + append_bmp_text_column(_L("Old Value"), UnsavedChangesModel::colOldValue, 20 * em); + append_bmp_text_column(_L("New Value"), UnsavedChangesModel::colNewValue, 20 * em); m_tree->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &UnsavedChangesDialog::item_value_changed, this); m_tree->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &UnsavedChangesDialog::context_menu, this); m_tree->Bind(wxEVT_MOTION, [this](wxMouseEvent& e) { show_info_line(Action::Undef); e.Skip(); }); - wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCANCEL); + // Add Buttons + wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCANCEL); Tab* tab = wxGetApp().get_tab(type); assert(tab); - PresetCollection* presets = tab->get_presets(); - wxString label= from_u8((boost::format(_u8L("Save selected to preset:%1%"))% ("\"" + presets->get_selected_preset().name + "\"")).str()); + wxString label= from_u8((boost::format(_u8L("Save selected to preset: %1%"))% ("\"" + presets->get_selected_preset().name + "\"")).str()); m_save_btn = new ScalableButton(this, m_save_btn_id = NewControlId(), "save", label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true); buttons->Insert(0, m_save_btn, 0, wxLEFT, 5); @@ -533,7 +538,7 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& m_save_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); m_save_btn->Bind(wxEVT_MOTION, [this, presets](wxMouseEvent& e){ show_info_line(Action::Save, presets->get_selected_preset().name); e.Skip(); }); - label = from_u8((boost::format(_u8L("Move selected to preset:%1%"))% ("\"" + from_u8(new_selected_preset) + "\"")).str()); + label = from_u8((boost::format(_u8L("Move selected to preset: %1%"))% ("\"" + from_u8(new_selected_preset) + "\"")).str()); m_move_btn = new ScalableButton(this, m_move_btn_id = NewControlId(), "paste_menu", label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true); buttons->Insert(1, m_move_btn, 0, wxLEFT, 5); @@ -549,14 +554,13 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& m_info_line = new wxStaticText(this, wxID_ANY, ""); m_info_line->SetFont(wxGetApp().bold_font()); - m_info_line->Hide(); wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); topSizer->Add(new wxStaticText(this, wxID_ANY, _L("There are unsaved changes for") + (": \"" + tab->title() + "\"")), 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); - topSizer->Add(m_tree, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); - topSizer->Add(m_info_line, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, 2*border); - topSizer->Add(buttons, 0, wxEXPAND | wxALL, border); + topSizer->Add(m_tree, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(m_info_line, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, 2*border); + topSizer->Add(buttons, 0, wxEXPAND | wxALL, border); update(type); @@ -564,6 +568,8 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& SetSizer(topSizer); topSizer->SetSizeHints(this); + + show_info_line(Action::Undef); } void UnsavedChangesDialog::item_value_changed(wxDataViewEvent& event) From cb407e46e5d833d5e286d4e7e2f092fb3f19e6ec Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 11 Aug 2020 10:37:08 +0200 Subject: [PATCH 277/503] Fixed color update under GTK + FullCompareDialog : Use SetStile instead of SetForegroundColour for the wxTextCtrls --- src/slic3r/GUI/UnsavedChangesDialog.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index dcd1d760ae..9f9ec3d9f3 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -150,7 +150,7 @@ ModelNode::ModelNode(ModelNode* parent, const wxString& text, const wxString& ol void ModelNode::UpdateEnabling() { -#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) +#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__) auto change_text_color = [](wxString& str, const std::string& clr_from, const std::string& clr_to) { std::string old_val = into_u8(str); @@ -876,9 +876,10 @@ FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString }; auto add_value = [grid_sizer, border, this](wxString label, bool is_colored = false) { - wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, label, wxDefaultPosition, wxSize(300, -1), wxTE_MULTILINE | wxTE_READONLY | wxBORDER_NONE); + wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, label, wxDefaultPosition, wxSize(300, -1), wxTE_MULTILINE | wxTE_READONLY | wxBORDER_NONE | wxTE_RICH); if (is_colored) - text->SetForegroundColour(wxColour(orange)); +// text->SetForegroundColour(wxColour(orange)); + text->SetStyle(0, label.Len(), wxTextAttr(wxColour(orange))); grid_sizer->Add(text, 1, wxALL | wxEXPAND, border); }; From 5882c121cc2aca8fc85f1106a624d85330b3b968 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 11 Aug 2020 11:12:30 +0200 Subject: [PATCH 278/503] GCodeProcessor -> Fixed time estimate for stealth mode --- src/libslic3r/GCode/GCodeProcessor.cpp | 24 ++++++++++++++++++++++-- src/libslic3r/GCode/GCodeProcessor.hpp | 6 ++++++ src/slic3r/GUI/GCodeViewer.cpp | 14 +++++++++++--- src/slic3r/GUI/Plater.cpp | 2 +- 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 87bcde9d07..e4e52abdbe 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -159,6 +159,7 @@ void GCodeProcessor::TimeMachine::reset() { enabled = false; acceleration = 0.0f; + max_acceleration = 0.0f; extrude_factor_override_percentage = 1.0f; time = 0.0f; curr.reset(); @@ -289,6 +290,7 @@ void GCodeProcessor::TimeProcessor::reset() { extruder_unloaded = true; export_remaining_time_enabled = false; + machine_envelope_processing_enabled = false; machine_limits = MachineEnvelopeConfig(); filament_load_times = std::vector(); filament_unload_times = std::vector(); @@ -484,6 +486,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config) for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i); + m_time_processor.machines[i].max_acceleration = max_acceleration; m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION; } @@ -628,6 +631,7 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i); + m_time_processor.machines[i].max_acceleration = max_acceleration; m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION; } } @@ -1304,6 +1308,9 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) return type; }; + // enable processing of lines M201/M203/M204/M205 + m_time_processor.machine_envelope_processing_enabled = true; + // updates axes positions from line for (unsigned char a = X; a <= E; ++a) { m_end_position[a] = absolute_position((Axis)a, line); @@ -1664,6 +1671,9 @@ void GCodeProcessor::process_M135(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_M201(const GCodeReader::GCodeLine& line) { + if (!m_time_processor.machine_envelope_processing_enabled) + return; + // see http://reprap.org/wiki/G-code#M201:_Set_max_printing_acceleration float factor = (m_flavor != gcfRepRap && m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f; @@ -1684,6 +1694,9 @@ void GCodeProcessor::process_M201(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_M203(const GCodeReader::GCodeLine& line) { + if (!m_time_processor.machine_envelope_processing_enabled) + return; + // see http://reprap.org/wiki/G-code#M203:_Set_maximum_feedrate if (m_flavor == gcfRepetier) return; @@ -1709,6 +1722,9 @@ void GCodeProcessor::process_M203(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line) { + if (!m_time_processor.machine_envelope_processing_enabled) + return; + float value; for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { if (line.has_value('S', value)) { @@ -1736,6 +1752,9 @@ void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_M205(const GCodeReader::GCodeLine& line) { + if (!m_time_processor.machine_envelope_processing_enabled) + return; + for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { if (line.has_x()) { float max_jerk = line.x(); @@ -1968,8 +1987,9 @@ void GCodeProcessor::set_acceleration(ETimeMode mode, float value) { size_t id = static_cast(mode); if (id < m_time_processor.machines.size()) { - float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, id); - m_time_processor.machines[id].acceleration = (max_acceleration == 0.0f) ? value : std::min(value, max_acceleration); + m_time_processor.machines[id].acceleration = (m_time_processor.machines[id].max_acceleration == 0.0f) ? value : + // Clamp the acceleration with the maximum. + std::min(value, m_time_processor.machines[id].max_acceleration); } } diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 840b5373bc..ba8f921686 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -182,6 +182,8 @@ namespace Slic3r { bool enabled; float acceleration; // mm/s^2 + // hard limit for the acceleration, to which the firmware will clamp. + float max_acceleration; // mm/s^2 float extrude_factor_override_percentage; float time; // s std::string line_m73_mask; @@ -216,7 +218,10 @@ namespace Slic3r { // This is currently only really used by the MK3 MMU2: // extruder_unloaded = true means no filament is loaded yet, all the filaments are parked in the MK3 MMU2 unit. bool extruder_unloaded; + // whether or not to export post-process the gcode to export lines M73 in it bool export_remaining_time_enabled; + // allow to skip the lines M201/M203/M204/M205 generated by GCode::print_machine_envelope() + bool machine_envelope_processing_enabled; MachineEnvelopeConfig machine_limits; // Additional load / unload times for a filament exchange sequence. std::vector filament_load_times; @@ -328,6 +333,7 @@ namespace Slic3r { bool is_stealth_time_estimator_enabled() const { return m_time_processor.machines[static_cast(ETimeMode::Stealth)].enabled; } + void enable_machine_envelope_processing(bool enabled) { m_time_processor.machine_envelope_processing_enabled = enabled; } void enable_producers(bool enabled) { m_producers_enabled = enabled; } void reset(); diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index ad84184015..bbd357e980 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -9,7 +9,7 @@ #include "GUI_App.hpp" #include "MainFrame.hpp" #include "Plater.hpp" -#include "PresetBundle.hpp" +#include "libslic3r/PresetBundle.hpp" #include "Camera.hpp" #include "I18N.hpp" #include "GUI_Utils.hpp" @@ -1498,7 +1498,7 @@ void GCodeViewer::render_legend() const imgui.text(time); ImGui::SameLine(offsets[1]); pos = ImGui::GetCursorScreenPos(); - float width = percent_bar_size * percent / max_percent; + float width = std::max(1.0f, percent_bar_size * percent / max_percent); draw_list->AddRectFilled({ pos.x, pos.y + 2.0f }, { pos.x + width, pos.y + icon_size - 2.0f }, ImGui::GetColorU32(ImGuiWrapper::COL_ORANGE_LIGHT)); ImGui::Dummy({ percent_bar_size, icon_size }); @@ -1649,7 +1649,15 @@ void GCodeViewer::render_legend() const imgui.text(short_time(get_time_dhms(time_mode.time))); auto show_mode_button = [this, &imgui](const std::string& label, PrintEstimatedTimeStatistics::ETimeMode mode) { - if (m_time_statistics.modes[static_cast(mode)].roles_times.size() > 0) { + bool show = false; + for (size_t i = 0; i < m_time_statistics.modes.size(); ++i) { + if (i != static_cast(mode) && + short_time(get_time_dhms(m_time_statistics.modes[static_cast(mode)].time)) != short_time(get_time_dhms(m_time_statistics.modes[i].time))) { + show = true; + break; + } + } + if (show && m_time_statistics.modes[static_cast(mode)].roles_times.size() > 0) { ImGui::SameLine(0.0f, 10.0f); if (imgui.button(label)) { m_time_estimate_mode = mode; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 0b9f884516..dac3f6fae9 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4577,8 +4577,8 @@ void Plater::load_gcode(const wxString& filename) // process gcode GCodeProcessor processor; -// processor.apply_config(config); processor.enable_producers(true); + processor.enable_machine_envelope_processing(true); processor.process_file(filename.ToUTF8().data()); p->gcode_result = std::move(processor.extract_result()); From 5a0e04807965d8443da7793e2c6cacc461718a65 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 11 Aug 2020 14:23:47 +0200 Subject: [PATCH 279/503] ENABLE_GCODE_VIEWER -> Drag and drop .gcode files into gcode viewer --- src/libslic3r/GCode.cpp | 8 ++- src/libslic3r/GCode/GCodeProcessor.cpp | 86 +++++++++++--------------- src/libslic3r/GCode/GCodeProcessor.hpp | 5 +- src/slic3r/GUI/Plater.cpp | 43 +++++++++---- 4 files changed, 76 insertions(+), 66 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index c02d24a4ee..eb5ede0a5b 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1283,7 +1283,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu // adds tags for time estimators #if ENABLE_GCODE_VIEWER if (print.config().remaining_times.value) - _writeln(file, GCodeProcessor::First_M73_Output_Placeholder_Tag); + _writeln(file, GCodeProcessor::First_Line_M73_Placeholder_Tag); #else if (print.config().remaining_times.value) { _writeln(file, GCodeTimeEstimator::Normal_First_M73_Output_Placeholder_Tag); @@ -1587,7 +1587,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu // adds tags for time estimators #if ENABLE_GCODE_VIEWER if (print.config().remaining_times.value) - _writeln(file, GCodeProcessor::Last_M73_Output_Placeholder_Tag); + _writeln(file, GCodeProcessor::Last_Line_M73_Placeholder_Tag); #else if (print.config().remaining_times.value) { _writeln(file, GCodeTimeEstimator::Normal_Last_M73_Output_Placeholder_Tag); @@ -1620,7 +1620,9 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu _write_format(file, "; total filament cost = %.1lf\n", print.m_print_statistics.total_cost); if (print.m_print_statistics.total_toolchanges > 0) _write_format(file, "; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges); -#if !ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER + _writeln(file, GCodeProcessor::Estimated_Printing_Time_Placeholder_Tag); +#else _write_format(file, "; estimated printing time (normal mode) = %s\n", m_normal_time_estimator.get_time_dhms().c_str()); if (m_silent_time_estimator_enabled) _write_format(file, "; estimated printing time (silent mode) = %s\n", m_silent_time_estimator.get_time_dhms().c_str()); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index e4e52abdbe..530dfb1059 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -29,8 +29,9 @@ const std::string GCodeProcessor::Color_Change_Tag = "Color change"; const std::string GCodeProcessor::Pause_Print_Tag = "Pause print"; const std::string GCodeProcessor::Custom_Code_Tag = "Custom gcode"; -const std::string GCodeProcessor::First_M73_Output_Placeholder_Tag = "; _GP_FIRST_M73_OUTPUT_PLACEHOLDER"; -const std::string GCodeProcessor::Last_M73_Output_Placeholder_Tag = "; _GP_LAST_M73_OUTPUT_PLACEHOLDER"; +const std::string GCodeProcessor::First_Line_M73_Placeholder_Tag = "; _GP_FIRST_LINE_M73_PLACEHOLDER"; +const std::string GCodeProcessor::Last_Line_M73_Placeholder_Tag = "; _GP_LAST_LINE_M73_PLACEHOLDER"; +const std::string GCodeProcessor::Estimated_Printing_Time_Placeholder_Tag = "; _GP_ESTIMATED_PRINTING_TIME_PLACEHOLDER"; static bool is_valid_extrusion_role(int value) { @@ -338,16 +339,29 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) // replace placeholder lines with the proper final value auto process_placeholders = [&](const std::string& gcode_line) { - std::string ret; // remove trailing '\n' std::string line = gcode_line.substr(0, gcode_line.length() - 1); - if (line == First_M73_Output_Placeholder_Tag || line == Last_M73_Output_Placeholder_Tag) { + + std::string ret; + if (line == First_Line_M73_Placeholder_Tag || line == Last_Line_M73_Placeholder_Tag) { for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { const TimeMachine& machine = machines[i]; if (machine.enabled) { ret += format_line_M73(machine.line_m73_mask.c_str(), - (line == First_M73_Output_Placeholder_Tag) ? 0 : 100, - (line == First_M73_Output_Placeholder_Tag) ? time_in_minutes(machines[i].time) : 0); + (line == First_Line_M73_Placeholder_Tag) ? 0 : 100, + (line == First_Line_M73_Placeholder_Tag) ? time_in_minutes(machines[i].time) : 0); + } + } + } + else if (line == Estimated_Printing_Time_Placeholder_Tag) { + for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + const TimeMachine& machine = machines[i]; + if (machine.enabled) { + char buf[128]; + sprintf(buf, "; estimated printing time (%s mode) = %s\n", + (static_cast(i) == ETimeMode::Normal) ? "normal" : "silent", + get_time_dhms(machine.time).c_str()); + ret += buf; } } } @@ -407,18 +421,6 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) write_string(export_line); } - for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { - const TimeMachine& machine = machines[i]; - ETimeMode mode = static_cast(i); - if (machine.enabled) { - char line[128]; - sprintf(line, "; estimated printing time (%s mode) = %s\n", - (mode == ETimeMode::Normal) ? "normal" : "silent", - get_time_dhms(machine.time).c_str()); - export_line += line; - } - } - if (!export_line.empty()) write_string(export_line); @@ -870,12 +872,10 @@ void GCodeProcessor::process_tags(const std::string& comment) // width tag pos = comment.find(Width_Tag); if (pos != comment.npos) { - try - { + try { m_width = std::stof(comment.substr(pos + Width_Tag.length())); } - catch (...) - { + catch (...) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Width (" << comment << ")."; } return; @@ -884,12 +884,10 @@ void GCodeProcessor::process_tags(const std::string& comment) // height tag pos = comment.find(Height_Tag); if (pos != comment.npos) { - try - { + try { m_height = std::stof(comment.substr(pos + Height_Tag.length())); } - catch (...) - { + catch (...) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Height (" << comment << ")."; } return; @@ -899,8 +897,7 @@ void GCodeProcessor::process_tags(const std::string& comment) pos = comment.find(Color_Change_Tag); if (pos != comment.npos) { pos = comment.find_last_of(",T"); - try - { + try { unsigned char extruder_id = (pos == comment.npos) ? 0 : static_cast(std::stoi(comment.substr(pos + 1))); m_extruder_colors[extruder_id] = static_cast(m_extruder_offsets.size()) + m_cp_color.counter; // color_change position in list of color for preview @@ -915,8 +912,7 @@ void GCodeProcessor::process_tags(const std::string& comment) process_custom_gcode_time(CustomGCode::ColorChange); } - catch (...) - { + catch (...) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Color_Change (" << comment << ")."; } @@ -1115,24 +1111,20 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string& comment) size_t w_start = data.find(w_tag); size_t w_end = data.find_first_of(' ', w_start); if (h_start != data.npos) { - try - { + try { std::string test = data.substr(h_start + 1, (h_end != data.npos) ? h_end - h_start - 1 : h_end); m_height = std::stof(data.substr(h_start + 1, (h_end != data.npos) ? h_end - h_start - 1 : h_end)); } - catch (...) - { + catch (...) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Height (" << comment << ")."; } } if (w_start != data.npos) { - try - { + try { std::string test = data.substr(w_start + 1, (w_end != data.npos) ? w_end - w_start - 1 : w_end); m_width = std::stof(data.substr(w_start + 1, (w_end != data.npos) ? w_end - w_start - 1 : w_end)); } - catch (...) - { + catch (...) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Width (" << comment << ")."; } } @@ -1218,12 +1210,10 @@ bool GCodeProcessor::process_ideamaker_tags(const std::string& comment) tag = "WIDTH:"; pos = comment.find(tag); if (pos != comment.npos) { - try - { + try { m_width = std::stof(comment.substr(pos + tag.length())); } - catch (...) - { + catch (...) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Width (" << comment << ")."; } return true; @@ -1233,12 +1223,10 @@ bool GCodeProcessor::process_ideamaker_tags(const std::string& comment) tag = "HEIGHT:"; pos = comment.find(tag); if (pos != comment.npos) { - try - { + try { m_height = std::stof(comment.substr(pos + tag.length())); } - catch (...) - { + catch (...) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Height (" << comment << ")."; } return true; @@ -1871,8 +1859,7 @@ void GCodeProcessor::process_T(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_T(const std::string& command) { if (command.length() > 1) { - try - { + try { unsigned char id = static_cast(std::stoi(command.substr(1))); if (m_extruder_id != id) { unsigned char extruders_count = static_cast(m_extruder_offsets.size()); @@ -1895,8 +1882,7 @@ void GCodeProcessor::process_T(const std::string& command) store_move_vertex(EMoveType::Tool_change); } } - catch (...) - { + catch (...) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid toolchange (" << command << ")."; } } diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index ba8f921686..e35d3a9735 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -72,8 +72,9 @@ namespace Slic3r { static const std::string Color_Change_Tag; static const std::string Pause_Print_Tag; static const std::string Custom_Code_Tag; - static const std::string First_M73_Output_Placeholder_Tag; - static const std::string Last_M73_Output_Placeholder_Tag; + static const std::string First_Line_M73_Placeholder_Tag; + static const std::string Last_Line_M73_Placeholder_Tag; + static const std::string Estimated_Printing_Time_Placeholder_Tag; private: using AxisCoords = std::array; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index dac3f6fae9..79f8b5ad5d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1349,20 +1349,44 @@ private: Plater *plater; static const std::regex pattern_drop; +#if ENABLE_GCODE_VIEWER + static const std::regex pattern_gcode_drop; +#endif // ENABLE_GCODE_VIEWER }; const std::regex PlaterDropTarget::pattern_drop(".*[.](stl|obj|amf|3mf|prusa)", std::regex::icase); +#if ENABLE_GCODE_VIEWER +const std::regex PlaterDropTarget::pattern_gcode_drop(".*[.](gcode)", std::regex::icase); +#endif // ENABLE_GCODE_VIEWER bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &filenames) { std::vector paths; - for (const auto &filename : filenames) { - fs::path path(into_path(filename)); - if (std::regex_match(path.string(), pattern_drop)) { - paths.push_back(std::move(path)); - } else { +#if ENABLE_GCODE_VIEWER + if (wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer) { + for (const auto& filename : filenames) { + fs::path path(into_path(filename)); + if (std::regex_match(path.string(), pattern_gcode_drop)) + paths.push_back(std::move(path)); + } + + if (paths.size() > 1) { + wxMessageDialog((wxWindow*)plater, _L("Only one gcode file at a time can be opened."), wxString(SLIC3R_APP_NAME) + " - " + _L("Open G-code file"), wxCLOSE | wxICON_WARNING | wxCENTRE).ShowModal(); return false; } + else if (paths.size() == 1) { + plater->load_gcode(from_path(paths.front())); + return true; + } + } +#endif // ENABLE_GCODE_VIEWER + + for (const auto &filename : filenames) { + fs::path path(into_path(filename)); + if (std::regex_match(path.string(), pattern_drop)) + paths.push_back(std::move(path)); + else + return false; } wxString snapshot_label; @@ -1390,13 +1414,10 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi // because right now the plater is not cleared, we set the project file (from the latest imported .3mf or .amf file) // only if not set yet // if res is empty no data has been loaded - if (!res.empty() && plater->get_project_filename().empty()) - { - for (std::vector::const_reverse_iterator it = paths.rbegin(); it != paths.rend(); ++it) - { + if (!res.empty() && plater->get_project_filename().empty()) { + for (std::vector::const_reverse_iterator it = paths.rbegin(); it != paths.rend(); ++it) { std::string filename = (*it).filename().string(); - if (boost::algorithm::iends_with(filename, ".3mf") || boost::algorithm::iends_with(filename, ".amf")) - { + if (boost::algorithm::iends_with(filename, ".3mf") || boost::algorithm::iends_with(filename, ".amf")) { plater->set_project_filename(from_path(*it)); break; } From 4ca026d4b6ecfbb11bd8107a427bdbe86cfdc28e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 11 Aug 2020 15:44:32 +0200 Subject: [PATCH 280/503] ENABLE_GCODE_VIEWER -> More general drag and drop for .gcode files --- src/libslic3r/GCode.cpp | 2 +- src/slic3r/GUI/MainFrame.cpp | 4 +++- src/slic3r/GUI/Plater.cpp | 42 +++++++++++++++++++++++++----------- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index eb5ede0a5b..795aac898f 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1626,7 +1626,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu _write_format(file, "; estimated printing time (normal mode) = %s\n", m_normal_time_estimator.get_time_dhms().c_str()); if (m_silent_time_estimator_enabled) _write_format(file, "; estimated printing time (silent mode) = %s\n", m_silent_time_estimator.get_time_dhms().c_str()); -#endif // !ENABLE_GCODE_VIEWER +#endif // ENABLE_GCODE_VIEWER // Append full config. _write(file, "\n"); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 794da80b2a..6c3f2a4ea6 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1006,7 +1006,9 @@ void MainFrame::init_menubar() fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_ANY, _L("&G-code preview"), _L("Switch to G-code preview mode"), [this](wxCommandEvent&) { - if (m_plater->model().objects.empty() || wxMessageDialog((wxWindow*)this, _L("Switching to G-code preview mode will remove all objects, continue?"), wxString(SLIC3R_APP_NAME) + " - " + _L("Switch to G-code preview mode"), wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES) + if (m_plater->model().objects.empty() || + wxMessageDialog((wxWindow*)this, _L("Switching to G-code preview mode will remove all objects, continue?"), + wxString(SLIC3R_APP_NAME) + " - " + _L("Switch to G-code preview mode"), wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_QUESTION | wxCENTRE).ShowModal() == wxID_YES) set_mode(EMode::GCodeViewer); }, "", nullptr, [this]() { return m_plater != nullptr && m_plater->printer_technology() != ptSLA; }, this); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 79f8b5ad5d..67b531eae9 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1362,25 +1362,43 @@ const std::regex PlaterDropTarget::pattern_gcode_drop(".*[.](gcode)", std::regex bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &filenames) { std::vector paths; -#if ENABLE_GCODE_VIEWER - if (wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer) { - for (const auto& filename : filenames) { - fs::path path(into_path(filename)); - if (std::regex_match(path.string(), pattern_gcode_drop)) - paths.push_back(std::move(path)); - } - if (paths.size() > 1) { - wxMessageDialog((wxWindow*)plater, _L("Only one gcode file at a time can be opened."), wxString(SLIC3R_APP_NAME) + " - " + _L("Open G-code file"), wxCLOSE | wxICON_WARNING | wxCENTRE).ShowModal(); - return false; - } - else if (paths.size() == 1) { +#if ENABLE_GCODE_VIEWER + // gcode section + for (const auto& filename : filenames) { + fs::path path(into_path(filename)); + if (std::regex_match(path.string(), pattern_gcode_drop)) + paths.push_back(std::move(path)); + } + + if (paths.size() > 1) { + wxMessageDialog((wxWindow*)plater, _L("You can open only one .gcode file at a time."), + wxString(SLIC3R_APP_NAME) + " - " + _L("Open G-code file"), wxCLOSE | wxICON_WARNING | wxCENTRE).ShowModal(); + return false; + } + else if (paths.size() == 1) { + if (wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer) { plater->load_gcode(from_path(paths.front())); return true; } + else { + if (wxMessageDialog((wxWindow*)plater, _L("Do you want to switch to G-code preview ?"), + wxString(SLIC3R_APP_NAME) + " - " + _L("Open G-code file"), wxYES_NO | wxCANCEL | wxICON_QUESTION | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES) { + + if (plater->model().objects.empty() || + wxMessageDialog((wxWindow*)plater, _L("Switching to G-code preview mode will remove all objects, continue?"), + wxString(SLIC3R_APP_NAME) + " - " + _L("Switch to G-code preview mode"), wxYES_NO | wxCANCEL | wxICON_QUESTION | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES) { + wxGetApp().mainframe->set_mode(MainFrame::EMode::GCodeViewer); + plater->load_gcode(from_path(paths.front())); + return true; + } + } + return false; + } } #endif // ENABLE_GCODE_VIEWER + // model section for (const auto &filename : filenames) { fs::path path(into_path(filename)); if (std::regex_match(path.string(), pattern_drop)) From a6a5025a7600be41c305567ed16acfdf703194e4 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 11 Aug 2020 15:48:49 +0200 Subject: [PATCH 281/503] Fixed a crash appeared when we try to update PlaterPresetComboBox for empty selected preset. --- src/slic3r/GUI/PresetComboBoxes.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 33ae4f54e9..7539f36168 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -517,7 +517,7 @@ PlaterPresetComboBox::PlaterPresetComboBox(wxWindow *parent, Preset::Type preset const Preset* selected_preset = m_collection->find_preset(m_preset_bundle->filament_presets[m_extruder_idx]); // Wide icons are shown if the currently selected preset is not compatible with the current printer, // and red flag is drown in front of the selected preset. - bool wide_icons = selected_preset != nullptr && !selected_preset->is_compatible; + bool wide_icons = selected_preset && !selected_preset->is_compatible; float scale = m_em_unit*0.1f; int shifl_Left = wide_icons ? int(scale * 16 + 0.5) : 0; @@ -707,10 +707,11 @@ void PlaterPresetComboBox::update() assert(selected_filament_preset); } - const Preset& selected_preset = m_type == Preset::TYPE_FILAMENT ? *selected_filament_preset : m_collection->get_selected_preset(); + bool has_selection = m_collection->get_selected_idx() != size_t(-1); + const Preset* selected_preset = m_type == Preset::TYPE_FILAMENT ? selected_filament_preset : has_selection ? &m_collection->get_selected_preset() : nullptr; // Show wide icons if the currently selected preset is not compatible with the current printer, // and draw a red flag in front of the selected preset. - bool wide_icons = !selected_preset.is_compatible; + bool wide_icons = selected_preset && !selected_preset->is_compatible; std::map nonsys_presets; From 12f43736bd612de51c89dded1e40b9f0d8516a8c Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 12 Aug 2020 10:47:36 +0200 Subject: [PATCH 282/503] Fix of Support generator debugging functions after some refactoring --- src/libslic3r/SupportMaterial.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 43f582d5f9..95b4c334b1 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -409,7 +409,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) export_print_z_polygons_and_extrusions_to_svg( debug_out_path("support-w-fills-%d-%lf.svg", iRun, layers_sorted[i]->print_z).c_str(), layers_sorted.data() + i, j - i, - *object.support_layers[layer_id]); + *object.support_layers()[layer_id]); ++layer_id; } i = j; @@ -597,8 +597,8 @@ public: ::fwrite(&n_points, 4, 1, file); for (uint32_t j = 0; j < n_points; ++ j) { const Point &pt = poly.points[j]; - ::fwrite(&pt.x, sizeof(coord_t), 1, file); - ::fwrite(&pt.y, sizeof(coord_t), 1, file); + ::fwrite(&pt.x(), sizeof(coord_t), 1, file); + ::fwrite(&pt.y(), sizeof(coord_t), 1, file); } } n_polygons = m_trimming_polygons->size(); @@ -609,8 +609,8 @@ public: ::fwrite(&n_points, 4, 1, file); for (uint32_t j = 0; j < n_points; ++ j) { const Point &pt = poly.points[j]; - ::fwrite(&pt.x, sizeof(coord_t), 1, file); - ::fwrite(&pt.y, sizeof(coord_t), 1, file); + ::fwrite(&pt.x(), sizeof(coord_t), 1, file); + ::fwrite(&pt.y(), sizeof(coord_t), 1, file); } } ::fclose(file); @@ -1134,7 +1134,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ { ::Slic3r::SVG svg(debug_out_path("support-top-contacts-raw-run%d-layer%d-region%d.svg", iRun, layer_id, - std::find_if(layer.regions.begin(), layer.regions.end(), [layerm](const LayerRegion* other){return other == layerm;}) - layer.regions.begin()), + std::find_if(layer.regions().begin(), layer.regions().end(), [layerm](const LayerRegion* other){return other == layerm;}) - layer.regions().begin()), get_extents(diff_polygons)); Slic3r::ExPolygons expolys = union_ex(diff_polygons, false); svg.draw(expolys); @@ -1152,7 +1152,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ Slic3r::SVG::export_expolygons( debug_out_path("support-top-contacts-filtered-run%d-layer%d-region%d-z%f.svg", iRun, layer_id, - std::find_if(layer.regions.begin(), layer.regions.end(), [layerm](const LayerRegion* other){return other == layerm;}) - layer.regions.begin(), + std::find_if(layer.regions().begin(), layer.regions().end(), [layerm](const LayerRegion* other){return other == layerm;}) - layer.regions().begin(), layer.print_z), union_ex(diff_polygons, false)); #endif /* SLIC3R_DEBUG */ @@ -1482,7 +1482,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta svg.draw(union_ex(top, false), "blue", 0.5f); svg.draw(union_ex(projection_raw, true), "red", 0.5f); svg.draw_outline(union_ex(projection_raw, true), "red", "blue", scale_(0.1f)); - svg.draw(layer.slices, "green", 0.5f); + svg.draw(layer.lslices, "green", 0.5f); } #endif /* SLIC3R_DEBUG */ From fd93d9768d686ff14d03d07509ab5858de9e782a Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 11 Aug 2020 12:15:44 +0200 Subject: [PATCH 283/503] Fixes of two crashes in paint-on supports --- src/libslic3r/PrintObject.cpp | 3 ++- src/libslic3r/TriangleSelector.cpp | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 273fc9c108..0f1a91a920 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2823,8 +2823,9 @@ void PrintObject::project_and_append_custom_supports( // Now append the collected polygons to respective layers. for (auto& trg : projections_of_triangles) { int layer_id = trg.first_layer_id; - for (const LightPolygon& poly : trg.polygons) { + if (layer_id >= int(expolys.size())) + break; // part of triangle could be projected above top layer expolys[layer_id].emplace_back(std::move(poly.pts)); ++layer_id; } diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index 763bf58616..9555c42a69 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -60,8 +60,9 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, if (select_triangle(facet, new_state)) { // add neighboring facets to list to be proccessed later for (int n=0; n<3; ++n) { - if (faces_camera(m_mesh->stl.neighbors_start[facet].neighbor[n])) - facets_to_check.push_back(m_mesh->stl.neighbors_start[facet].neighbor[n]); + int neighbor_idx = m_mesh->stl.neighbors_start[facet].neighbor[n]; + if (neighbor_idx >=0 && faces_camera(neighbor_idx)) + facets_to_check.push_back(neighbor_idx); } } } From 6d4bb10ec59bfee42bc95ef8ea7b3892573404f0 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 12 Aug 2020 11:28:30 +0200 Subject: [PATCH 284/503] Fix of custom supports: object offset for Clipper was incorrectly accounted for --- src/libslic3r/PrintObject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 0f1a91a920..c90a05ef31 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2733,7 +2733,7 @@ void PrintObject::project_and_append_custom_supports( std::array trianglef; for (int i=0; i<3; ++i) { trianglef[i] = Vec2f(facet[i].x(), facet[i].y()); - trianglef[i] += Vec2f(unscale(this->center_offset().x()), + trianglef[i] -= Vec2f(unscale(this->center_offset().x()), unscale(this->center_offset().y())); } From 6dafdc5bab81b45a26a56d0e32fdfd035c06461b Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 12 Aug 2020 13:36:26 +0200 Subject: [PATCH 285/503] Fixed rescaling under MSW --- src/slic3r/GUI/UnsavedChangesDialog.cpp | 116 +++++++++++++++++------- src/slic3r/GUI/UnsavedChangesDialog.hpp | 9 +- 2 files changed, 90 insertions(+), 35 deletions(-) diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 9f9ec3d9f3..53dcf3f029 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -56,32 +56,29 @@ static void make_string_bold(wxString& str) } // preset(root) node -ModelNode::ModelNode(Preset::Type preset_type, const wxString& text) : +ModelNode::ModelNode(Preset::Type preset_type, const wxString& text, wxWindow* parent_win) : + m_parent_win(parent_win), m_parent(nullptr), m_preset_type(preset_type), + m_icon_name(type_icon_names.at(preset_type)), m_text(text) { -#ifdef __linux__ - m_icon.CopyFromBitmap(create_scaled_bitmap(type_icon_names.at(preset_type))); -#else - m_icon = create_scaled_bitmap(type_icon_names.at(preset_type)); -#endif //__linux__ + UpdateIcons(); } // group node ModelNode::ModelNode(ModelNode* parent, const wxString& text, const std::string& icon_name) : + m_parent_win(parent->m_parent_win), m_parent(parent), + m_icon_name(icon_name), m_text(text) { -#ifdef __linux__ - m_icon.CopyFromBitmap(create_scaled_bitmap(icon_name)); -#else - m_icon = create_scaled_bitmap(icon_name); -#endif //__linux__ + UpdateIcons(); } // category node ModelNode::ModelNode(ModelNode* parent, const wxString& text) : + m_parent_win(parent->m_parent_win), m_parent(parent), m_text(text) { @@ -150,12 +147,13 @@ ModelNode::ModelNode(ModelNode* parent, const wxString& text, const wxString& ol void ModelNode::UpdateEnabling() { -#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__) auto change_text_color = [](wxString& str, const std::string& clr_from, const std::string& clr_to) { +#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__) std::string old_val = into_u8(str); boost::replace_all(old_val, clr_from, clr_to); str = from_u8(old_val); +#endif }; if (!m_toggle) { @@ -168,12 +166,27 @@ void ModelNode::UpdateEnabling() change_text_color(m_old_value, grey, black); change_text_color(m_new_value, grey, orange); } -#endif // update icons for the colors + UpdateIcons(); +} + +void ModelNode::UpdateIcons() +{ + // update icons for the colors, if any exists if (!m_old_color.IsEmpty()) - m_old_color_bmp = get_bitmap(m_toggle? m_old_color : grey); + m_old_color_bmp = get_bitmap(m_toggle ? m_old_color : grey); if (!m_new_color.IsEmpty()) - m_new_color_bmp = get_bitmap(m_toggle? m_new_color : grey); + m_new_color_bmp = get_bitmap(m_toggle ? m_new_color : grey); + + // update main icon, if any exists + if (m_icon_name.empty()) + return; + +#ifdef __linux__ + m_icon.CopyFromBitmap(create_scaled_bitmap(m_icon_name, m_parent_win, 16, !m_toggle)); +#else + m_icon = create_scaled_bitmap(m_icon_name, m_parent_win, 16, !m_toggle); +#endif //__linux__ } @@ -198,7 +211,7 @@ wxDataViewItem UnsavedChangesModel::AddPreset(Preset::Type type, wxString preset color_string(preset_name, black); make_string_bold(preset_name); - auto preset = new ModelNode(type, preset_name); + auto preset = new ModelNode(type, preset_name, m_parent_win); m_preset_nodes.emplace_back(preset); wxDataViewItem child((void*)preset); @@ -478,6 +491,24 @@ wxString UnsavedChangesModel::GetColumnType(unsigned int col) const } } +static void rescale_children(ModelNode* parent) +{ + if (parent->IsContainer()) { + for (ModelNode* child : parent->GetChildren()) { + child->UpdateIcons(); + rescale_children(child); + } + } +} + +void UnsavedChangesModel::Rescale() +{ + for (ModelNode* node : m_preset_nodes) { + node->UpdateIcons(); + rescale_children(node); + } +} + //------------------------------------------ // UnsavedChangesDialog @@ -489,6 +520,13 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); SetBackgroundColour(bgr_clr); +#if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT + // ys_FIXME! temporary workaround for correct font scaling + // Because of from wxWidgets 3.1.3 auto rescaling is implemented for the Fonts, + // From the very beginning set dialog font to the wxSYS_DEFAULT_GUI_FONT + this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); +#endif // ENABLE_WX_3_1_3_DPI_CHANGED_EVENT + int border = 10; int em = em_unit(); @@ -521,7 +559,6 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& m_tree->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &UnsavedChangesDialog::item_value_changed, this); m_tree->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &UnsavedChangesDialog::context_menu, this); - m_tree->Bind(wxEVT_MOTION, [this](wxMouseEvent& e) { show_info_line(Action::Undef); e.Skip(); }); // Add Buttons wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCANCEL); @@ -534,26 +571,30 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& m_save_btn = new ScalableButton(this, m_save_btn_id = NewControlId(), "save", label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true); buttons->Insert(0, m_save_btn, 0, wxLEFT, 5); - m_save_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Save); }); - m_save_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); - m_save_btn->Bind(wxEVT_MOTION, [this, presets](wxMouseEvent& e){ show_info_line(Action::Save, presets->get_selected_preset().name); e.Skip(); }); + m_save_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Save); }); + m_save_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); + m_save_btn->Bind(wxEVT_ENTER_WINDOW,[this, presets](wxMouseEvent& e){ show_info_line(Action::Save, presets->get_selected_preset().name); e.Skip(); }); + m_save_btn->Bind(wxEVT_LEAVE_WINDOW,[this ](wxMouseEvent& e){ show_info_line(Action::Undef); e.Skip(); }); label = from_u8((boost::format(_u8L("Move selected to preset: %1%"))% ("\"" + from_u8(new_selected_preset) + "\"")).str()); m_move_btn = new ScalableButton(this, m_move_btn_id = NewControlId(), "paste_menu", label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true); buttons->Insert(1, m_move_btn, 0, wxLEFT, 5); - m_move_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Move); }); - m_move_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); - m_move_btn->Bind(wxEVT_MOTION, [this, new_selected_preset](wxMouseEvent& e){ show_info_line(Action::Move, new_selected_preset); e.Skip(); }); + m_move_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Move); }); + m_move_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); + m_move_btn->Bind(wxEVT_ENTER_WINDOW,[this, new_selected_preset](wxMouseEvent& e){ show_info_line(Action::Move, new_selected_preset); e.Skip(); }); + m_move_btn->Bind(wxEVT_LEAVE_WINDOW,[this ](wxMouseEvent& e){ show_info_line(Action::Undef); e.Skip(); }); label = _L("Continue without changes"); m_continue_btn = new ScalableButton(this, m_continue_btn_id = NewControlId(), "cross", label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true); buttons->Insert(2, m_continue_btn, 0, wxLEFT, 5); - m_continue_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Continue); }); - m_continue_btn->Bind(wxEVT_MOTION, [this](wxMouseEvent& e){ show_info_line(Action::Continue); e.Skip(); }); + m_continue_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Continue); }); + m_continue_btn->Bind(wxEVT_ENTER_WINDOW,[this](wxMouseEvent& e){ show_info_line(Action::Continue); e.Skip(); }); + m_continue_btn->Bind(wxEVT_LEAVE_WINDOW,[this](wxMouseEvent& e){ show_info_line(Action::Undef); e.Skip(); }); m_info_line = new wxStaticText(this, wxID_ANY, ""); - m_info_line->SetFont(wxGetApp().bold_font()); + m_info_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold()); + m_info_line->Hide(); wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); @@ -564,8 +605,6 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& update(type); - this->Bind(wxEVT_MOTION, [this](wxMouseEvent& e) { show_info_line(Action::Undef); e.Skip(); }); - SetSizer(topSizer); topSizer->SetSizeHints(this); @@ -829,21 +868,34 @@ std::vector UnsavedChangesDialog::get_selected_options() void UnsavedChangesDialog::on_dpi_changed(const wxRect& suggested_rect) { - const int& em = em_unit(); + int em = em_unit(); msw_buttons_rescale(this, em, { wxID_CANCEL, m_save_btn_id, m_move_btn_id, m_continue_btn_id }); + for (auto btn : { m_save_btn, m_move_btn, m_continue_btn } ) + btn->msw_rescale(); const wxSize& size = wxSize(80 * em, 30 * em); SetMinSize(size); + m_tree->GetColumn(UnsavedChangesModel::colToggle )->SetWidth(6 * em); + m_tree->GetColumn(UnsavedChangesModel::colIconText)->SetWidth(30 * em); + m_tree->GetColumn(UnsavedChangesModel::colOldValue)->SetWidth(20 * em); + m_tree->GetColumn(UnsavedChangesModel::colNewValue)->SetWidth(20 * em); + + m_tree_model->Rescale(); + m_tree->Refresh(); + Fit(); Refresh(); } void UnsavedChangesDialog::on_sys_color_changed() { + for (auto btn : { m_save_btn, m_move_btn, m_continue_btn } ) + btn->msw_rescale(); // msw_rescale updates just icons, so use it -// m_tree_model->msw_rescale(); + m_tree_model->Rescale(); + m_tree->Refresh(); Refresh(); } @@ -871,14 +923,14 @@ FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString auto add_header = [grid_sizer, border, this](wxString label) { wxStaticText* text = new wxStaticText(this, wxID_ANY, label); - text->SetFont(wxGetApp().bold_font()); + text->SetFont(this->GetFont().Bold()); grid_sizer->Add(text, 0, wxALL, border); }; auto add_value = [grid_sizer, border, this](wxString label, bool is_colored = false) { wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, label, wxDefaultPosition, wxSize(300, -1), wxTE_MULTILINE | wxTE_READONLY | wxBORDER_NONE | wxTE_RICH); + text->SetFont(this->GetFont()); if (is_colored) -// text->SetForegroundColour(wxColour(orange)); text->SetStyle(0, label.Len(), wxTextAttr(wxColour(orange))); grid_sizer->Add(text, 1, wxALL | wxEXPAND, border); }; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index 991c894422..49a9640e8c 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -30,13 +30,14 @@ WX_DEFINE_ARRAY_PTR(ModelNode*, ModelNodePtrArray); // GTK - wxDataViewIconText (wxWidgets for GTK renderer wxIcon + wxString, supported Markup text) class ModelNode { - wxWindow* m_parent_win{ nullptr }; + wxWindow* m_parent_win{ nullptr }; ModelNode* m_parent; ModelNodePtrArray m_children; wxBitmap m_empty_bmp; Preset::Type m_preset_type {Preset::TYPE_INVALID}; + std::string m_icon_name; // saved values for colors if they exist wxString m_old_color; wxString m_new_color; @@ -75,7 +76,7 @@ public: wxString m_new_value; // preset(root) node - ModelNode(Preset::Type preset_type, const wxString& text); + ModelNode(Preset::Type preset_type, const wxString& text, wxWindow* parent_win); // category node ModelNode(ModelNode* parent, const wxString& text, const std::string& icon_name); @@ -111,6 +112,7 @@ public: void Append(ModelNode* child) { m_children.Add(child); } void UpdateEnabling(); + void UpdateIcons(); }; @@ -120,7 +122,7 @@ public: class UnsavedChangesModel : public wxDataViewModel { - wxWindow* m_parent_win {nullptr}; + wxWindow* m_parent_win { nullptr }; std::vector m_preset_nodes; wxDataViewCtrl* m_ctrl{ nullptr }; @@ -164,6 +166,7 @@ public: unsigned int GetColumnCount() const override { return colMax; } wxString GetColumnType(unsigned int col) const override; + void Rescale(); wxDataViewItem GetParent(const wxDataViewItem& item) const override; unsigned int GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const override; From 493d52850ebdc374c8ae530a664fdc75817402a8 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 12 Aug 2020 15:07:02 +0200 Subject: [PATCH 286/503] ENABLE_GCODE_VIEWER -> Drag and drop for non .gcode files while gcode viewer mode is active --- src/slic3r/GUI/MainFrame.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 6c3f2a4ea6..3f000e3328 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1008,7 +1008,7 @@ void MainFrame::init_menubar() [this](wxCommandEvent&) { if (m_plater->model().objects.empty() || wxMessageDialog((wxWindow*)this, _L("Switching to G-code preview mode will remove all objects, continue?"), - wxString(SLIC3R_APP_NAME) + " - " + _L("Switch to G-code preview mode"), wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_QUESTION | wxCENTRE).ShowModal() == wxID_YES) + wxString(SLIC3R_APP_NAME) + " - " + _L("Switch to G-code preview mode"), wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION | wxCENTRE).ShowModal() == wxID_YES) set_mode(EMode::GCodeViewer); }, "", nullptr, [this]() { return m_plater != nullptr && m_plater->printer_technology() != ptSLA; }, this); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 67b531eae9..759be43f32 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1364,6 +1364,11 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi std::vector paths; #if ENABLE_GCODE_VIEWER +#ifdef WIN32 + // hides the system icon + this->MSWUpdateDragImageOnLeave(); +#endif // WIN32 + // gcode section for (const auto& filename : filenames) { fs::path path(into_path(filename)); @@ -1373,7 +1378,7 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi if (paths.size() > 1) { wxMessageDialog((wxWindow*)plater, _L("You can open only one .gcode file at a time."), - wxString(SLIC3R_APP_NAME) + " - " + _L("Open G-code file"), wxCLOSE | wxICON_WARNING | wxCENTRE).ShowModal(); + wxString(SLIC3R_APP_NAME) + " - " + _L("Drag and drop G-code file"), wxCLOSE | wxICON_WARNING | wxCENTRE).ShowModal(); return false; } else if (paths.size() == 1) { @@ -1383,11 +1388,11 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi } else { if (wxMessageDialog((wxWindow*)plater, _L("Do you want to switch to G-code preview ?"), - wxString(SLIC3R_APP_NAME) + " - " + _L("Open G-code file"), wxYES_NO | wxCANCEL | wxICON_QUESTION | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES) { + wxString(SLIC3R_APP_NAME) + " - " + _L("Drag and drop G-code file"), wxYES_NO | wxICON_QUESTION | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES) { if (plater->model().objects.empty() || wxMessageDialog((wxWindow*)plater, _L("Switching to G-code preview mode will remove all objects, continue?"), - wxString(SLIC3R_APP_NAME) + " - " + _L("Switch to G-code preview mode"), wxYES_NO | wxCANCEL | wxICON_QUESTION | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES) { + wxString(SLIC3R_APP_NAME) + " - " + _L("Switch to G-code preview mode"), wxYES_NO | wxICON_QUESTION | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES) { wxGetApp().mainframe->set_mode(MainFrame::EMode::GCodeViewer); plater->load_gcode(from_path(paths.front())); return true; @@ -1407,6 +1412,16 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi return false; } +#if ENABLE_GCODE_VIEWER + if (wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer) { + if (wxMessageDialog((wxWindow*)plater, _L("Do you want to exit G-code preview ?"), + wxString(SLIC3R_APP_NAME) + " - " + _L("Drag and drop model file"), wxYES_NO | wxICON_QUESTION | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES) + wxGetApp().mainframe->set_mode(MainFrame::EMode::Editor); + else + return false; + } +#endif // ENABLE_GCODE_VIEWER + wxString snapshot_label; assert(! paths.empty()); if (paths.size() == 1) { From 32595f7659ea42a3e87ab11a237520696b727ead Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 12 Aug 2020 16:12:51 +0200 Subject: [PATCH 287/503] Fixed scaling of the SavePresetDialog under MSW + fixed misunderstanding typo in PlaterPresetComboBox --- src/slic3r/GUI/PresetComboBoxes.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 7539f36168..c62c999f6b 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -715,7 +715,7 @@ void PlaterPresetComboBox::update() std::map nonsys_presets; - wxString selected = ""; + wxString selected_user_preset = ""; wxString tooltip = ""; const std::deque& presets = m_collection->get_presets(); @@ -742,7 +742,7 @@ void PlaterPresetComboBox::update() { // Assign an extruder color to the selected item if the extruder color is defined. filament_rgb = preset.config.opt_string("filament_colour", 0); - extruder_rgb = (selected && !extruder_color.empty()) ? extruder_color : filament_rgb; + extruder_rgb = (is_selected && !extruder_color.empty()) ? extruder_color : filament_rgb; single_bar = filament_rgb == extruder_rgb; bitmap_key += single_bar ? filament_rgb : filament_rgb + extruder_rgb; @@ -764,7 +764,7 @@ void PlaterPresetComboBox::update() { nonsys_presets.emplace(wxString::FromUTF8((name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), bmp); if (is_selected) { - selected = wxString::FromUTF8((name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()); + selected_user_preset = wxString::FromUTF8((name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()); tooltip = wxString::FromUTF8(preset.name.c_str()); } } @@ -776,7 +776,7 @@ void PlaterPresetComboBox::update() set_label_marker(Append(separator(L("User presets")), wxNullBitmap)); for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { Append(it->first, *it->second); - validate_selection(it->first == selected); + validate_selection(it->first == selected_user_preset); } } @@ -1166,7 +1166,13 @@ void SavePresetDialog::Item::accept() SavePresetDialog::SavePresetDialog(Preset::Type type, const std::string& suffix) : DPIDialog(nullptr, wxID_ANY, _L("Save preset"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER) { - SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); +#if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT + // ys_FIXME! temporary workaround for correct font scaling + // Because of from wxWidgets 3.1.3 auto rescaling is implemented for the Fonts, + // From the very beginning set dialog font to the wxSYS_DEFAULT_GUI_FONT + this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); +#endif // ENABLE_WX_3_1_3_DPI_CHANGED_EVENT wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); From a81e3ee2245a7c5a0c145a5e5cb3e7e72bfe690f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 12 Aug 2020 17:33:22 +0200 Subject: [PATCH 288/503] UnsavedChangesDialog : implemented "Save" function --- src/slic3r/GUI/Tab.cpp | 41 ++++++++++++++++++++----- src/slic3r/GUI/UnsavedChangesDialog.cpp | 14 +++++++-- src/slic3r/GUI/UnsavedChangesDialog.hpp | 1 + 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 6501fba27b..19f3974f77 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -326,7 +326,7 @@ void Tab::add_scaled_button(wxWindow* parent, const wxString& label/* = wxEmptyString*/, long style /*= wxBU_EXACTFIT | wxNO_BORDER*/) { - *btn = new ScalableButton(parent, wxID_ANY, icon_name, label, wxDefaultSize, wxDefaultPosition, style); + *btn = new ScalableButton(parent, wxID_ANY, icon_name, label, wxDefaultSize, wxDefaultPosition, style, true); m_scaled_buttons.push_back(*btn); } @@ -3132,19 +3132,43 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, // if the current preset was not dirty, or the user agreed to discard the changes, 1 is returned. bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr*/, const std::string& new_printer_name /*= ""*/) { + if (presets == nullptr) presets = m_presets; + UnsavedChangesDialog dlg(m_type, new_printer_name); if (dlg.ShowModal() == wxID_CANCEL) return false; if (dlg.just_continue()) return true; - if (dlg.save_preset()) - // save selected changes - return false; + if (dlg.save_preset()) // save selected changes + { + std::vector unselected_options = dlg.get_unselected_options(); + const Preset& preset = presets->get_edited_preset(); + std::string name = preset.name; + + // for system/default/external presets we should take an edited name + if (preset.is_system || preset.is_default || preset.is_external) { + SavePresetDialog save_dlg(m_type, _CTX_utf8(L_CONTEXT("Copy", "PresetName"), "PresetName")); + if (save_dlg.ShowModal() != wxID_OK) + return false; + name = save_dlg.get_name(); + } + + // if we want to save just some from selected options + if (!unselected_options.empty()) + { + DynamicPrintConfig& old_config = presets->get_selected_preset().config; + + for (const std::string& opt_key : unselected_options) + m_config->set_key_value(opt_key, old_config.option(opt_key)->clone()); + } + + save_preset(name); + return true; + } if (dlg.move_preset()) // move selected changes return false; - - if (presets == nullptr) presets = m_presets; +/* // Display a dialog showing the dirty options in a human readable form. const Preset& old_preset = presets->get_edited_preset(); std::string type_name = presets->name(); @@ -3157,13 +3181,13 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr wxString changes; for (const std::string &opt_key : presets->current_dirty_options()) { const ConfigOptionDef &opt = m_config->def()->options.at(opt_key); - /*std::string*/wxString name = ""; + wxString name = ""; if (! opt.category.empty()) name += _(opt.category) + " > "; name += !opt.full_label.empty() ? _(opt.full_label) : _(opt.label); - changes += tab + /*from_u8*/(name) + "\n"; + changes += tab + (name) + "\n"; } // Show a confirmation dialog with the list of dirty options. wxString message = name + "\n\n"; @@ -3180,6 +3204,7 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr message + "\n" + changes + "\n\n" + _(L("Discard changes and continue anyway?")), _(L("Unsaved Changes")), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION); return confirm.ShowModal() == wxID_YES; + */ } // If we are switching from the FFF-preset to the SLA, we should to control the printed objects if they have a part(s). diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 53dcf3f029..f93aa35c2a 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -854,6 +854,16 @@ void UnsavedChangesDialog::update(Preset::Type type) } } +std::vector UnsavedChangesDialog::get_unselected_options() +{ + std::vector ret; + + for (auto item : m_items_map) + if (!m_tree_model->IsEnabledItem(item.first)) + ret.emplace_back(item.second.opt_key); + + return ret; +} std::vector UnsavedChangesDialog::get_selected_options() { @@ -929,9 +939,7 @@ FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString auto add_value = [grid_sizer, border, this](wxString label, bool is_colored = false) { wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, label, wxDefaultPosition, wxSize(300, -1), wxTE_MULTILINE | wxTE_READONLY | wxBORDER_NONE | wxTE_RICH); - text->SetFont(this->GetFont()); - if (is_colored) - text->SetStyle(0, label.Len(), wxTextAttr(wxColour(orange))); + text->SetStyle(0, label.Len(), wxTextAttr(is_colored ? wxColour(orange) : wxNullColour, wxNullColour, this->GetFont())); grid_sizer->Add(text, 1, wxALL | wxEXPAND, border); }; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index 49a9640e8c..271d7595b6 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -241,6 +241,7 @@ public: bool move_preset() const { return m_exit_action == Action::Move; } bool just_continue() const { return m_exit_action == Action::Continue; } + std::vector get_unselected_options(); std::vector get_selected_options(); protected: From 491e7b16f95a242510c31015c8dfb09257bfa98b Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 13 Aug 2020 08:20:22 +0200 Subject: [PATCH 289/503] Fixed build under GTK --- src/slic3r/GUI/Tab.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 19f3974f77..5c54e9e46e 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3168,6 +3168,8 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr if (dlg.move_preset()) // move selected changes return false; + + return false; /* // Display a dialog showing the dirty options in a human readable form. const Preset& old_preset = presets->get_edited_preset(); From b80bde11f3929137b2ede00848f99b4c4a1a9d8f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 13 Aug 2020 12:51:50 +0200 Subject: [PATCH 290/503] GCodeProcessor -> Extract toolpaths height from gcode moves --- src/libslic3r/GCode/GCodeProcessor.cpp | 46 ++++++++++++++++++------ src/libslic3r/GCode/GCodeProcessor.hpp | 1 + src/libslic3r/Utils.hpp | 5 +-- src/slic3r/GUI/GCodeViewer.cpp | 48 +++++++++----------------- src/slic3r/GUI/GCodeViewer.hpp | 10 ++++-- 5 files changed, 64 insertions(+), 46 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 530dfb1059..18878cd5ad 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -23,7 +23,7 @@ static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2 namespace Slic3r { const std::string GCodeProcessor::Extrusion_Role_Tag = "ExtrType:"; -const std::string GCodeProcessor::Width_Tag = "PrusaSlicer__WIDTH:"; +const std::string GCodeProcessor::Width_Tag = "Width:"; const std::string GCodeProcessor::Height_Tag = "Height:"; const std::string GCodeProcessor::Color_Change_Tag = "Color change"; const std::string GCodeProcessor::Pause_Print_Tag = "Pause print"; @@ -81,6 +81,19 @@ static float acceleration_time_from_distance(float initial_feedrate, float dista return (acceleration != 0.0f) ? (speed_from_distance(initial_feedrate, distance, acceleration) - initial_feedrate) / acceleration : 0.0f; } +float round_to_nearest(float value, unsigned int decimals) +{ + float res = 0.0f; + if (decimals == 0) + res = std::round(value); + else { + char buf[64]; + sprintf(buf, "%.*g", decimals, value); + res = std::stof(buf); + } + return res; +} + void GCodeProcessor::CachedPosition::reset() { std::fill(position.begin(), position.end(), FLT_MAX); @@ -666,6 +679,7 @@ void GCodeProcessor::reset() m_extruder_id = 0; m_extruder_colors = ExtruderColors(); m_filament_diameters = std::vector(); + m_extruded_last_z = 0.0f; m_cp_color.reset(); m_producer = EProducer::Unknown; @@ -1285,14 +1299,6 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) else if (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f || delta_pos[Z] != 0.0f) type = EMoveType::Travel; - if (type == EMoveType::Extrude && (m_width == 0.0f || m_height == 0.0f)) { - if (m_extrusion_role != erCustom) { - m_width = 0.5f; - m_height = 0.5f; - } - type = EMoveType::Travel; - } - return type; }; @@ -1325,8 +1331,26 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) if (type == EMoveType::Extrude) { if (delta_pos[E] > 0.0f) { float ds = std::sqrt(sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z])); - if (ds > 0.0f && static_cast(m_extruder_id) < m_filament_diameters.size()) - m_mm3_per_mm = round_nearest(delta_pos[E] * static_cast(M_PI) * sqr(static_cast(m_filament_diameters[m_extruder_id])) / (4.0f * ds), 3); + if (ds > 0.0f && static_cast(m_extruder_id) < m_filament_diameters.size()) { + // extruded filament volume / tool displacement + m_mm3_per_mm = round_to_nearest(static_cast(M_PI * sqr(m_filament_diameters[m_extruder_id]) * 0.25) * delta_pos[E] / ds, 4); + } + + if (m_end_position[Z] > m_extruded_last_z + EPSILON) { + m_height = round_to_nearest(m_end_position[Z] - m_extruded_last_z, 4); + m_extruded_last_z = m_end_position[Z]; + } + } + } + + if (type == EMoveType::Extrude && (m_width == 0.0f || m_height == 0.0f)) { + if ((m_width == 0.0f && m_height == 0.0f) || m_extrusion_role == erCustom) + type = EMoveType::Travel; + else { + if (m_width == 0.0f) + m_width = 0.5f; + if (m_height == 0.0f) + m_height = 0.5f; } } diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index e35d3a9735..f103bab967 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -304,6 +304,7 @@ namespace Slic3r { unsigned char m_extruder_id; ExtruderColors m_extruder_colors; std::vector m_filament_diameters; + float m_extruded_last_z; CpColor m_cp_color; enum class EProducer diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index f7ff29b7e0..ef531169d1 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -110,19 +110,20 @@ std::string header_slic3r_generated(); // getpid platform wrapper extern unsigned get_current_pid(); +#if !ENABLE_GCODE_VIEWER template Real round_nearest(Real value, unsigned int decimals) { Real res = (Real)0; if (decimals == 0) res = ::round(value); - else - { + else { Real power = ::pow((Real)10, (int)decimals); res = ::round(value * power + (Real)0.5) / power; } return res; } +#endif // !ENABLE_GCODE_VIEWER // Compute the next highest power of 2 of 32-bit v // http://graphics.stanford.edu/~seander/bithacks.html diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index bbd357e980..b7bd81a0fa 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -235,38 +235,19 @@ const std::vector GCodeViewer::Extrusion_Role_Colors {{ { 0.00f, 0.00f, 0.00f } // erMixed }}; -//const std::vector GCodeViewer::Extrusion_Role_Colors {{ -// { 0.75f, 0.75f, 0.75f }, // erNone -// { 1.00f, 1.00f, 0.40f }, // erPerimeter -// { 1.00f, 0.65f, 0.00f }, // erExternalPerimeter -// { 0.00f, 0.00f, 1.00f }, // erOverhangPerimeter -// { 0.69f, 0.19f, 0.16f }, // erInternalInfill -// { 0.84f, 0.20f, 0.84f }, // erSolidInfill -// { 1.00f, 0.10f, 0.10f }, // erTopSolidInfill -// { 1.00f, 0.55f, 0.41f }, // erIroning -// { 0.60f, 0.60f, 1.00f }, // erBridgeInfill -// { 1.00f, 1.00f, 1.00f }, // erGapFill -// { 0.52f, 0.48f, 0.13f }, // erSkirt -// { 0.00f, 1.00f, 0.00f }, // erSupportMaterial -// { 0.00f, 0.50f, 0.00f }, // erSupportMaterialInterface -// { 0.70f, 0.89f, 0.67f }, // erWipeTower -// { 0.16f, 0.80f, 0.58f }, // erCustom -// { 0.00f, 0.00f, 0.00f } // erMixed -//}}; - const std::vector GCodeViewer::Options_Colors {{ - { 1.00f, 0.00f, 1.00f }, // Retractions - { 0.00f, 1.00f, 1.00f }, // Unretractions - { 1.00f, 1.00f, 1.00f }, // ToolChanges - { 1.00f, 0.00f, 0.00f }, // ColorChanges - { 0.00f, 1.00f, 0.00f }, // PausePrints - { 0.00f, 0.00f, 1.00f } // CustomGCodes + { 0.803f, 0.135f, 0.839f }, // Retractions + { 0.287f, 0.679f, 0.810f }, // Unretractions + { 0.758f, 0.744f, 0.389f }, // ToolChanges + { 0.856f, 0.582f, 0.546f }, // ColorChanges + { 0.322f, 0.942f, 0.512f }, // PausePrints + { 0.886f, 0.825f, 0.262f } // CustomGCodes }}; const std::vector GCodeViewer::Travel_Colors {{ - { 0.0f, 0.0f, 0.5f }, // Move - { 0.0f, 0.5f, 0.0f }, // Extrude - { 0.5f, 0.0f, 0.0f } // Retract + { 0.219f, 0.282f, 0.609f }, // Move + { 0.112f, 0.422f, 0.103f }, // Extrude + { 0.505f, 0.064f, 0.028f } // Retract }}; const std::vector GCodeViewer::Range_Colors {{ @@ -279,7 +260,8 @@ const std::vector GCodeViewer::Range_Colors {{ { 0.961f, 0.808f, 0.039f }, { 0.890f, 0.533f, 0.125f }, { 0.820f, 0.408f, 0.188f }, - { 0.761f, 0.322f, 0.235f } // reddish + { 0.761f, 0.322f, 0.235f }, + { 0.581f, 0.149f, 0.087f } // reddish }}; bool GCodeViewer::init() @@ -1525,11 +1507,15 @@ void GCodeViewer::render_legend() const append_item(EItemType::Rect, Range_Colors[i], buf); }; - float step_size = range.step_size(); - if (step_size == 0.0f) + if (range.count == 1) // single item use case append_range_item(0, range.min, decimals); + else if (range.count == 2) { + append_range_item(static_cast(Range_Colors.size()) - 1, range.max, decimals); + append_range_item(0, range.min, decimals); + } else { + float step_size = range.step_size(); for (int i = static_cast(Range_Colors.size()) - 1; i >= 0; --i) { append_range_item(i, range.min + static_cast(i) * step_size, decimals); } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 038b837b2b..74506677a6 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -142,11 +142,17 @@ class GCodeViewer { float min; float max; + unsigned int count; Range() { reset(); } - void update_from(const float value) { min = std::min(min, value); max = std::max(max, value); } - void reset() { min = FLT_MAX; max = -FLT_MAX; } + void update_from(const float value) { + if (value != max && value != min) + ++count; + min = std::min(min, value); + max = std::max(max, value); + } + void reset() { min = FLT_MAX; max = -FLT_MAX; count = 0; } float step_size() const { return (max - min) / (static_cast(Range_Colors.size()) - 1.0f); } Color get_color_at(float value) const; From 830d89576c6d18e47c2b69a238cf2bd2142d2840 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 13 Aug 2020 11:14:44 +0200 Subject: [PATCH 291/503] Added description line for the disabling of "Object elevation" Deleted mirrored parameter "pad_around_object" from "Support" category --- src/slic3r/GUI/Tab.cpp | 16 +++++++++++++++- src/slic3r/GUI/Tab.hpp | 3 +++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 4f4beb202c..9970eb5b96 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3971,9 +3971,16 @@ void TabSLAPrint::build() optgroup->append_single_option_line("support_base_safety_distance"); // Mirrored parameter from Pad page for toggling elevation on the same page - optgroup->append_single_option_line("pad_around_object"); +// optgroup->append_single_option_line("pad_around_object"); optgroup->append_single_option_line("support_object_elevation"); + Line line{ "", "" }; + line.full_width = 1; + line.widget = [this](wxWindow* parent) { + return description_line_widget(parent, &m_support_object_elevation_description_line); + }; + optgroup->append_line(line); + optgroup = page->new_optgroup(L("Connection of the support sticks and junctions")); optgroup->append_single_option_line("support_critical_angle"); optgroup->append_single_option_line("support_max_bridge_length"); @@ -4047,6 +4054,13 @@ void TabSLAPrint::update() m_update_cnt++; m_config_manipulation.update_print_sla_config(m_config, true); + + m_support_object_elevation_description_line->SetText(!m_config->opt_bool("pad_around_object") ? "" : + from_u8((boost::format(_u8L("\"%1%\" is disabled because \"%2%\" is on in \"%3%\" category.\n" + "To enable \"%1%\", please switch off \"%2%\"")) + % _L("Object elevation") % _L("Pad around object") % _L("Pad")).str())); + Layout(); + m_update_cnt--; if (m_update_cnt == 0) { diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 24f25e2d74..de4c5ccb91 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -460,6 +460,9 @@ public: // Tab(parent, _(L("Print Settings")), L("sla_print")) {} Tab(parent, _(L("Print Settings")), Slic3r::Preset::TYPE_SLA_PRINT) {} ~TabSLAPrint() {} + + ogStaticText* m_support_object_elevation_description_line = nullptr; + void build() override; void reload_config() override; void update() override; From 9486901b93f4dfeaf84f2db05b4f25769bbe9b82 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 4 Aug 2020 12:54:21 +0200 Subject: [PATCH 292/503] Minor change to SLAPrinter interface --- src/libslic3r/Format/SL1.cpp | 4 ++-- src/libslic3r/Format/SL1.hpp | 2 +- src/libslic3r/SLAPrint.hpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/Format/SL1.cpp b/src/libslic3r/Format/SL1.cpp index ba5e89330b..5c402ef5bf 100644 --- a/src/libslic3r/Format/SL1.cpp +++ b/src/libslic3r/Format/SL1.cpp @@ -126,9 +126,9 @@ uqptr SL1Archive::create_raster() const return sla::create_raster_grayscale_aa(res, pxdim, gamma, tr); } -sla::EncodedRaster SL1Archive::encode_raster(const sla::RasterBase &rst) const +sla::RasterEncoder SL1Archive::get_encoder() const { - return rst.encode(sla::PNGRasterEncoder()); + return sla::PNGRasterEncoder{}; } void SL1Archive::export_print(Zipper& zipper, diff --git a/src/libslic3r/Format/SL1.hpp b/src/libslic3r/Format/SL1.hpp index 1b9e95392b..fbb6d61604 100644 --- a/src/libslic3r/Format/SL1.hpp +++ b/src/libslic3r/Format/SL1.hpp @@ -13,7 +13,7 @@ class SL1Archive: public SLAPrinter { protected: uqptr create_raster() const override; - sla::EncodedRaster encode_raster(const sla::RasterBase &rst) const override; + sla::RasterEncoder get_encoder() const override; public: diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index f4b220c58c..0ad544baa2 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -374,7 +374,7 @@ protected: std::vector m_layers; virtual uqptr create_raster() const = 0; - virtual sla::EncodedRaster encode_raster(const sla::RasterBase &rst) const = 0; + virtual sla::RasterEncoder get_encoder() const = 0; public: virtual ~SLAPrinter() = default; @@ -389,7 +389,7 @@ public: [this, &drawfn](sla::EncodedRaster& enc, size_t idx) { auto rst = create_raster(); drawfn(*rst, idx); - enc = encode_raster(*rst); + enc = rst->encode(get_encoder()); }); } }; From 929cea59f34142e044b5b3c22edf0d1798f93ef9 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 5 Aug 2020 15:49:36 +0200 Subject: [PATCH 293/503] replace ccr_::enumerate with flexible for_each enumerate is unusual and would only work effectively with random access iterators this for_each takes advantage of tbb blocked_range replace ccr_::enumerate with flexible for_each enumerate is unusual and would only work effectively with random access iterators this for_each takes advantage of tbb blocked_range --- src/libslic3r/SLA/Concurrency.hpp | 49 ++++-- src/libslic3r/SLA/IndexedMesh.cpp | 6 +- src/libslic3r/SLA/SupportPointGenerator.cpp | 178 ++++++++++---------- src/libslic3r/SLA/SupportTreeBuildsteps.cpp | 24 +-- src/libslic3r/SLAPrint.hpp | 13 +- src/libslic3r/SLAPrintSteps.cpp | 19 ++- src/libslic3r/libslic3r.h | 5 + 7 files changed, 169 insertions(+), 125 deletions(-) diff --git a/src/libslic3r/SLA/Concurrency.hpp b/src/libslic3r/SLA/Concurrency.hpp index 8620c67b19..12a6e41e37 100644 --- a/src/libslic3r/SLA/Concurrency.hpp +++ b/src/libslic3r/SLA/Concurrency.hpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include namespace Slic3r { namespace sla { @@ -17,16 +19,29 @@ template struct _ccr {}; template<> struct _ccr { using SpinningMutex = tbb::spin_mutex; - using BlockingMutex = tbb::mutex; - + using BlockingMutex = tbb::mutex; + template - static inline void enumerate(It from, It to, Fn fn) + static IteratorOnly for_each(It from, + It to, + Fn && fn, + size_t granularity = 1) { - auto iN = to - from; - size_t N = iN < 0 ? 0 : size_t(iN); - - tbb::parallel_for(size_t(0), N, [from, fn](size_t n) { - fn(*(from + decltype(iN)(n)), n); + tbb::parallel_for(tbb::blocked_range{from, to, granularity}, + [&fn, from](const auto &range) { + for (auto &el : range) fn(el); + }); + } + + template + static IntegerOnly for_each(I from, + I to, + Fn && fn, + size_t granularity = 1) + { + tbb::parallel_for(tbb::blocked_range{from, to, granularity}, + [&fn](const auto &range) { + for (I i = range.begin(); i < range.end(); ++i) fn(i); }); } }; @@ -39,11 +54,23 @@ private: public: using SpinningMutex = _Mtx; using BlockingMutex = _Mtx; - + template - static inline void enumerate(It from, It to, Fn fn) + static IteratorOnly for_each(It from, + It to, + Fn &&fn, + size_t /* ignore granularity */ = 1) { - for (auto it = from; it != to; ++it) fn(*it, size_t(it - from)); + for (auto it = from; it != to; ++it) fn(*it); + } + + template + static IntegerOnly for_each(I from, + I to, + Fn &&fn, + size_t /* ignore granularity */ = 1) + { + for (I i = from; i < to; ++i) fn(i); } }; diff --git a/src/libslic3r/SLA/IndexedMesh.cpp b/src/libslic3r/SLA/IndexedMesh.cpp index 573b62b6db..efcf09873e 100644 --- a/src/libslic3r/SLA/IndexedMesh.cpp +++ b/src/libslic3r/SLA/IndexedMesh.cpp @@ -320,10 +320,10 @@ PointSet normals(const PointSet& points, PointSet ret(range.size(), 3); // for (size_t ridx = 0; ridx < range.size(); ++ridx) - ccr::enumerate( - range.begin(), range.end(), - [&ret, &mesh, &points, thr, eps](unsigned el, size_t ridx) { + ccr::for_each(size_t(0), range.size(), + [&ret, &mesh, &points, thr, eps, &range](size_t ridx) { thr(); + unsigned el = range[ridx]; auto eidx = Eigen::Index(el); int faceid = 0; Vec3d p; diff --git a/src/libslic3r/SLA/SupportPointGenerator.cpp b/src/libslic3r/SLA/SupportPointGenerator.cpp index 3cd075ae69..78b3349efd 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.cpp +++ b/src/libslic3r/SLA/SupportPointGenerator.cpp @@ -4,6 +4,7 @@ #include #include "SupportPointGenerator.hpp" +#include "Concurrency.hpp" #include "Model.hpp" #include "ExPolygon.hpp" #include "SVG.hpp" @@ -87,27 +88,28 @@ void SupportPointGenerator::project_onto_mesh(std::vector& po // The function makes sure that all the points are really exactly placed on the mesh. // Use a reasonable granularity to account for the worker thread synchronization cost. - tbb::parallel_for(tbb::blocked_range(0, points.size(), 64), - [this, &points](const tbb::blocked_range& range) { - for (size_t point_id = range.begin(); point_id < range.end(); ++ point_id) { - if ((point_id % 16) == 0) - // Don't call the following function too often as it flushes CPU write caches due to synchronization primitves. - m_throw_on_cancel(); - Vec3f& p = points[point_id].pos; - // Project the point upward and downward and choose the closer intersection with the mesh. - sla::IndexedMesh::hit_result hit_up = m_emesh.query_ray_hit(p.cast(), Vec3d(0., 0., 1.)); - sla::IndexedMesh::hit_result hit_down = m_emesh.query_ray_hit(p.cast(), Vec3d(0., 0., -1.)); + static constexpr size_t gransize = 64; - bool up = hit_up.is_hit(); - bool down = hit_down.is_hit(); + ccr_par::for_each(size_t(0), points.size(), [this, &points](size_t idx) + { + if ((idx % 16) == 0) + // Don't call the following function too often as it flushes CPU write caches due to synchronization primitves. + m_throw_on_cancel(); - if (!up && !down) - continue; + Vec3f& p = points[idx].pos; + // Project the point upward and downward and choose the closer intersection with the mesh. + sla::IndexedMesh::hit_result hit_up = m_emesh.query_ray_hit(p.cast(), Vec3d(0., 0., 1.)); + sla::IndexedMesh::hit_result hit_down = m_emesh.query_ray_hit(p.cast(), Vec3d(0., 0., -1.)); - sla::IndexedMesh::hit_result& hit = (!down || (hit_up.distance() < hit_down.distance())) ? hit_up : hit_down; - p = p + (hit.distance() * hit.direction()).cast(); - } - }); + bool up = hit_up.is_hit(); + bool down = hit_down.is_hit(); + + if (!up && !down) + return; + + sla::IndexedMesh::hit_result& hit = (!down || (hit_up.distance() < hit_down.distance())) ? hit_up : hit_down; + p = p + (hit.distance() * hit.direction()).cast(); + }, gransize); } static std::vector make_layers( @@ -126,78 +128,80 @@ static std::vector make_layers( //const float pixel_area = pow(wxGetApp().preset_bundle->project_config.option("display_width") / wxGetApp().preset_bundle->project_config.option("display_pixels_x"), 2.f); // const float pixel_area = pow(0.047f, 2.f); - // Use a reasonable granularity to account for the worker thread synchronization cost. - tbb::parallel_for(tbb::blocked_range(0, layers.size(), 32), - [&layers, &slices, &heights, pixel_area, throw_on_cancel](const tbb::blocked_range& range) { - for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { - if ((layer_id % 8) == 0) - // Don't call the following function too often as it flushes CPU write caches due to synchronization primitves. - throw_on_cancel(); - SupportPointGenerator::MyLayer &layer = layers[layer_id]; - const ExPolygons &islands = slices[layer_id]; - //FIXME WTF? - const float height = (layer_id>2 ? heights[layer_id-3] : heights[0]-(heights[1]-heights[0])); - layer.islands.reserve(islands.size()); - for (const ExPolygon &island : islands) { - float area = float(island.area() * SCALING_FACTOR * SCALING_FACTOR); - if (area >= pixel_area) - //FIXME this is not a correct centroid of a polygon with holes. - layer.islands.emplace_back(layer, island, get_extents(island.contour), Slic3r::unscale(island.contour.centroid()).cast(), area, height); - } - } - }); + ccr_par::for_each(0ul, layers.size(), + [&layers, &slices, &heights, pixel_area, throw_on_cancel](size_t layer_id) + { + if ((layer_id % 8) == 0) + // Don't call the following function too often as it flushes + // CPU write caches due to synchronization primitves. + throw_on_cancel(); + + SupportPointGenerator::MyLayer &layer = layers[layer_id]; + const ExPolygons & islands = slices[layer_id]; + // FIXME WTF? + const float height = (layer_id > 2 ? + heights[layer_id - 3] : + heights[0] - (heights[1] - heights[0])); + layer.islands.reserve(islands.size()); + for (const ExPolygon &island : islands) { + float area = float(island.area() * SCALING_FACTOR * SCALING_FACTOR); + if (area >= pixel_area) + // FIXME this is not a correct centroid of a polygon with holes. + layer.islands.emplace_back(layer, island, get_extents(island.contour), + unscaled(island.contour.centroid()), area, height); + } + }, 32 /*gransize*/); // Calculate overlap of successive layers. Link overlapping islands. - tbb::parallel_for(tbb::blocked_range(1, layers.size(), 8), - [&layers, &heights, throw_on_cancel](const tbb::blocked_range& range) { - for (size_t layer_id = range.begin(); layer_id < range.end(); ++layer_id) { - if ((layer_id % 2) == 0) - // Don't call the following function too often as it flushes CPU write caches due to synchronization primitves. - throw_on_cancel(); - SupportPointGenerator::MyLayer &layer_above = layers[layer_id]; - SupportPointGenerator::MyLayer &layer_below = layers[layer_id - 1]; - //FIXME WTF? - const float layer_height = (layer_id!=0 ? heights[layer_id]-heights[layer_id-1] : heights[0]); - const float safe_angle = 5.f * (float(M_PI)/180.f); // smaller number - less supports - const float between_layers_offset = float(scale_(layer_height / std::tan(safe_angle))); - const float slope_angle = 75.f * (float(M_PI)/180.f); // smaller number - less supports - const float slope_offset = float(scale_(layer_height / std::tan(slope_angle))); - //FIXME This has a quadratic time complexity, it will be excessively slow for many tiny islands. - for (SupportPointGenerator::Structure &top : layer_above.islands) { - for (SupportPointGenerator::Structure &bottom : layer_below.islands) { - float overlap_area = top.overlap_area(bottom); - if (overlap_area > 0) { - top.islands_below.emplace_back(&bottom, overlap_area); - bottom.islands_above.emplace_back(&top, overlap_area); - } - } - if (! top.islands_below.empty()) { - Polygons top_polygons = to_polygons(*top.polygon); - Polygons bottom_polygons = top.polygons_below(); - top.overhangs = diff_ex(top_polygons, bottom_polygons); - if (! top.overhangs.empty()) { - top.overhangs_area = 0.f; - std::vector> expolys_with_areas; - for (ExPolygon &ex : top.overhangs) { - float area = float(ex.area()); - expolys_with_areas.emplace_back(&ex, area); - top.overhangs_area += area; - } - std::sort(expolys_with_areas.begin(), expolys_with_areas.end(), + ccr_par::for_each(1ul, layers.size(), + [&layers, &heights, throw_on_cancel] (size_t layer_id) + { + if ((layer_id % 2) == 0) + // Don't call the following function too often as it flushes CPU write caches due to synchronization primitves. + throw_on_cancel(); + SupportPointGenerator::MyLayer &layer_above = layers[layer_id]; + SupportPointGenerator::MyLayer &layer_below = layers[layer_id - 1]; + //FIXME WTF? + const float layer_height = (layer_id!=0 ? heights[layer_id]-heights[layer_id-1] : heights[0]); + const float safe_angle = 5.f * (float(M_PI)/180.f); // smaller number - less supports + const float between_layers_offset = float(scale_(layer_height / std::tan(safe_angle))); + const float slope_angle = 75.f * (float(M_PI)/180.f); // smaller number - less supports + const float slope_offset = float(scale_(layer_height / std::tan(slope_angle))); + //FIXME This has a quadratic time complexity, it will be excessively slow for many tiny islands. + for (SupportPointGenerator::Structure &top : layer_above.islands) { + for (SupportPointGenerator::Structure &bottom : layer_below.islands) { + float overlap_area = top.overlap_area(bottom); + if (overlap_area > 0) { + top.islands_below.emplace_back(&bottom, overlap_area); + bottom.islands_above.emplace_back(&top, overlap_area); + } + } + if (! top.islands_below.empty()) { + Polygons top_polygons = to_polygons(*top.polygon); + Polygons bottom_polygons = top.polygons_below(); + top.overhangs = diff_ex(top_polygons, bottom_polygons); + if (! top.overhangs.empty()) { + top.overhangs_area = 0.f; + std::vector> expolys_with_areas; + for (ExPolygon &ex : top.overhangs) { + float area = float(ex.area()); + expolys_with_areas.emplace_back(&ex, area); + top.overhangs_area += area; + } + std::sort(expolys_with_areas.begin(), expolys_with_areas.end(), [](const std::pair &p1, const std::pair &p2) - { return p1.second > p2.second; }); - ExPolygons overhangs_sorted; - for (auto &p : expolys_with_areas) - overhangs_sorted.emplace_back(std::move(*p.first)); - top.overhangs = std::move(overhangs_sorted); - top.overhangs_area *= float(SCALING_FACTOR * SCALING_FACTOR); - top.overhangs_slopes = diff_ex(top_polygons, offset(bottom_polygons, slope_offset)); - top.dangling_areas = diff_ex(top_polygons, offset(bottom_polygons, between_layers_offset)); - } - } - } - } - }); + { return p1.second > p2.second; }); + ExPolygons overhangs_sorted; + for (auto &p : expolys_with_areas) + overhangs_sorted.emplace_back(std::move(*p.first)); + top.overhangs = std::move(overhangs_sorted); + top.overhangs_area *= float(SCALING_FACTOR * SCALING_FACTOR); + top.overhangs_slopes = diff_ex(top_polygons, offset(bottom_polygons, slope_offset)); + top.dangling_areas = diff_ex(top_polygons, offset(bottom_polygons, between_layers_offset)); + } + } + } + }, 8 /* gransize */); return layers; } diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp index 2b40f00828..0adcb85283 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -209,14 +209,16 @@ IndexedMesh::hit_result SupportTreeBuildsteps::pinhead_mesh_intersect( // of the pinhead robe (side) surface. The result will be the smallest // hit distance. - ccr::enumerate(hits.begin(), hits.end(), - [&m, &rings, sd](HitResult &hit, size_t i) { + ccr::for_each(size_t(0), hits.size(), + [&m, &rings, sd, &hits](size_t i) { // Point on the circle on the pin sphere Vec3d ps = rings.pinring(i); // This is the point on the circle on the back sphere Vec3d p = rings.backring(i); + auto &hit = hits[i]; + // Point ps is not on mesh but can be inside or // outside as well. This would cause many problems // with ray-casting. To detect the position we will @@ -265,8 +267,10 @@ IndexedMesh::hit_result SupportTreeBuildsteps::bridge_mesh_intersect( // Hit results std::array hits; - ccr::enumerate(hits.begin(), hits.end(), - [this, r, src, /*ins_check,*/ &ring, dir, sd] (Hit &hit, size_t i) { + ccr::for_each(size_t(0), hits.size(), + [this, r, src, /*ins_check,*/ &ring, dir, sd, &hits] (size_t i) + { + Hit &hit = hits[i]; // Point on the circle on the pin sphere Vec3d p = ring.get(i, src, r + sd); @@ -744,10 +748,10 @@ void SupportTreeBuildsteps::filter() } }; - ccr::enumerate(filtered_indices.begin(), filtered_indices.end(), - [this, &filterfn](unsigned fidx, size_t i) { - filterfn(fidx, i, m_cfg.head_back_radius_mm); - }); + ccr::for_each(0ul, filtered_indices.size(), + [this, &filterfn, &filtered_indices] (size_t i) { + filterfn(filtered_indices[i], i, m_cfg.head_back_radius_mm); + }); for (size_t i = 0; i < heads.size(); ++i) if (heads[i].is_valid()) { @@ -1033,8 +1037,8 @@ void SupportTreeBuildsteps::routing_to_model() // If it can be routed there with a bridge shorter than // min_bridge_distance. - ccr::enumerate(m_iheads_onmodel.begin(), m_iheads_onmodel.end(), - [this] (const unsigned idx, size_t) { + ccr::for_each(m_iheads_onmodel.begin(), m_iheads_onmodel.end(), + [this] (const unsigned idx) { m_thr(); auto& head = m_builder.head(idx); diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 0ad544baa2..8948dc3312 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -385,12 +385,13 @@ public: template void draw_layers(size_t layer_num, Fn &&drawfn) { m_layers.resize(layer_num); - sla::ccr::enumerate(m_layers.begin(), m_layers.end(), - [this, &drawfn](sla::EncodedRaster& enc, size_t idx) { - auto rst = create_raster(); - drawfn(*rst, idx); - enc = rst->encode(get_encoder()); - }); + sla::ccr::for_each(0ul, m_layers.size(), + [this, &drawfn] (size_t idx) { + sla::EncodedRaster& enc = m_layers[idx]; + auto rst = create_raster(); + drawfn(*rst, idx); + enc = rst->encode(get_encoder()); + }); } }; diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 76bbf498d0..b4c994e8a2 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -264,11 +264,12 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) std::vector interior_slices; interior_slicer.slice(slice_grid, SlicingMode::Regular, closing_r, &interior_slices, thr); - sla::ccr::enumerate(interior_slices.begin(), interior_slices.end(), - [&po](const ExPolygons &slice, size_t i) { - po.m_model_slices[i] = - diff_ex(po.m_model_slices[i], slice); - }); + sla::ccr::for_each(0ul, interior_slices.size(), + [&po, &interior_slices] (size_t i) { + const ExPolygons &slice = interior_slices[i]; + po.m_model_slices[i] = + diff_ex(po.m_model_slices[i], slice); + }); } auto mit = slindex_it; @@ -679,14 +680,16 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() { using Lock = std::lock_guard; // Going to parallel: - auto printlayerfn = [ + auto printlayerfn = [this, // functions and read only vars areafn, area_fill, display_area, exp_time, init_exp_time, fast_tilt, slow_tilt, delta_fade_time, // write vars &mutex, &models_volume, &supports_volume, &estim_time, &slow_layers, - &fast_layers, &fade_layer_time](PrintLayer& layer, size_t sliced_layer_cnt) + &fast_layers, &fade_layer_time](size_t sliced_layer_cnt) { + PrintLayer &layer = m_print->m_printer_input[sliced_layer_cnt]; + // vector of slice record references auto& slicerecord_references = layer.slices(); @@ -789,7 +792,7 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() { // sequential version for debugging: // for(size_t i = 0; i < m_printer_input.size(); ++i) printlayerfn(i); - sla::ccr::enumerate(printer_input.begin(), printer_input.end(), printlayerfn); + sla::ccr::for_each(0ul, printer_input.size(), printlayerfn); auto SCALING2 = SCALING_FACTOR * SCALING_FACTOR; print_statistics.support_used_material = supports_volume * SCALING2; diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index 5ceb62a5cf..5e57c45913 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -261,6 +261,11 @@ using IntegerOnly = std::enable_if_t::value, O>; template using ArithmeticOnly = std::enable_if_t::value, O>; +template +using IteratorOnly = std::enable_if_t< + !std::is_same_v::value_type, void>, O +>; + } // namespace Slic3r #endif From 399c5a9c98b2f9b6dbf28bc90420d0de831b72a9 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 13 Aug 2020 14:54:13 +0200 Subject: [PATCH 294/503] Show description for disabled elevation when pad or pad around is off Follow-up fix for 830d89 --- src/slic3r/GUI/Tab.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 9970eb5b96..a56dadf4f5 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -4055,7 +4055,8 @@ void TabSLAPrint::update() m_config_manipulation.update_print_sla_config(m_config, true); - m_support_object_elevation_description_line->SetText(!m_config->opt_bool("pad_around_object") ? "" : + bool elev = !m_config->opt_bool("pad_enable") || !m_config->opt_bool("pad_around_object"); + m_support_object_elevation_description_line->SetText(elev ? "" : from_u8((boost::format(_u8L("\"%1%\" is disabled because \"%2%\" is on in \"%3%\" category.\n" "To enable \"%1%\", please switch off \"%2%\"")) % _L("Object elevation") % _L("Pad around object") % _L("Pad")).str())); From 7158690ddd539d04ddcf67838ee7c9d5392f7c36 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 13 Aug 2020 15:09:22 +0200 Subject: [PATCH 295/503] Fix build on win and rpi --- src/libslic3r/SLA/SupportPointGenerator.cpp | 4 ++-- src/libslic3r/SLA/SupportTreeBuildsteps.cpp | 2 +- src/libslic3r/SLAPrint.hpp | 2 +- src/libslic3r/SLAPrintSteps.cpp | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/SLA/SupportPointGenerator.cpp b/src/libslic3r/SLA/SupportPointGenerator.cpp index 78b3349efd..449269445d 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.cpp +++ b/src/libslic3r/SLA/SupportPointGenerator.cpp @@ -128,7 +128,7 @@ static std::vector make_layers( //const float pixel_area = pow(wxGetApp().preset_bundle->project_config.option("display_width") / wxGetApp().preset_bundle->project_config.option("display_pixels_x"), 2.f); // const float pixel_area = pow(0.047f, 2.f); - ccr_par::for_each(0ul, layers.size(), + ccr_par::for_each(size_t(0), layers.size(), [&layers, &slices, &heights, pixel_area, throw_on_cancel](size_t layer_id) { if ((layer_id % 8) == 0) @@ -153,7 +153,7 @@ static std::vector make_layers( }, 32 /*gransize*/); // Calculate overlap of successive layers. Link overlapping islands. - ccr_par::for_each(1ul, layers.size(), + ccr_par::for_each(size_t(1), layers.size(), [&layers, &heights, throw_on_cancel] (size_t layer_id) { if ((layer_id % 2) == 0) diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp index 0adcb85283..0e7af8d508 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -748,7 +748,7 @@ void SupportTreeBuildsteps::filter() } }; - ccr::for_each(0ul, filtered_indices.size(), + ccr::for_each(size_t(0), filtered_indices.size(), [this, &filterfn, &filtered_indices] (size_t i) { filterfn(filtered_indices[i], i, m_cfg.head_back_radius_mm); }); diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 8948dc3312..5fbf8346c7 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -385,7 +385,7 @@ public: template void draw_layers(size_t layer_num, Fn &&drawfn) { m_layers.resize(layer_num); - sla::ccr::for_each(0ul, m_layers.size(), + sla::ccr::for_each(size_t(0), m_layers.size(), [this, &drawfn] (size_t idx) { sla::EncodedRaster& enc = m_layers[idx]; auto rst = create_raster(); diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index b4c994e8a2..eaf9698198 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -264,7 +264,7 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) std::vector interior_slices; interior_slicer.slice(slice_grid, SlicingMode::Regular, closing_r, &interior_slices, thr); - sla::ccr::for_each(0ul, interior_slices.size(), + sla::ccr::for_each(size_t(0), interior_slices.size(), [&po, &interior_slices] (size_t i) { const ExPolygons &slice = interior_slices[i]; po.m_model_slices[i] = @@ -792,7 +792,7 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() { // sequential version for debugging: // for(size_t i = 0; i < m_printer_input.size(); ++i) printlayerfn(i); - sla::ccr::for_each(0ul, printer_input.size(), printlayerfn); + sla::ccr::for_each(size_t(0), printer_input.size(), printlayerfn); auto SCALING2 = SCALING_FACTOR * SCALING_FACTOR; print_statistics.support_used_material = supports_volume * SCALING2; From add3894e8c0ddc7533b6bedd97033a36e60695d8 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 13 Aug 2020 09:00:58 +0200 Subject: [PATCH 296/503] Add reserve_vector to libslic3r.h to be globally usable. --- src/libslic3r/MTUtils.hpp | 9 --------- src/libslic3r/libslic3r.h | 9 +++++++++ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/MTUtils.hpp b/src/libslic3r/MTUtils.hpp index a6c8d61622..555cfe5019 100644 --- a/src/libslic3r/MTUtils.hpp +++ b/src/libslic3r/MTUtils.hpp @@ -114,15 +114,6 @@ template struct remove_cvref template using remove_cvref_t = typename remove_cvref::type; -template // Arbitrary allocator can be used -inline IntegerOnly> reserve_vector(I capacity) -{ - std::vector ret; - if (capacity > I(0)) ret.reserve(size_t(capacity)); - - return ret; -} - /// Exactly like Matlab https://www.mathworks.com/help/matlab/ref/linspace.html template> inline std::vector linspace_vector(const ArithmeticOnly &start, diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index 5e57c45913..76ff271360 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -266,6 +266,15 @@ using IteratorOnly = std::enable_if_t< !std::is_same_v::value_type, void>, O >; +template // Arbitrary allocator can be used +IntegerOnly> reserve_vector(I capacity) +{ + std::vector ret; + if (capacity > I(0)) ret.reserve(size_t(capacity)); + + return ret; +} + } // namespace Slic3r #endif From d7176c64bdb22061e6b603dee7042af63c628967 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 13 Aug 2020 15:45:16 +0200 Subject: [PATCH 297/503] Unsaved Changes : implemented "move" and improved "save" UnsavedChangesDialog : some code refactoring SavePresetDialog : processed empty name for the preset --- src/slic3r/GUI/PresetComboBoxes.cpp | 7 +++ src/slic3r/GUI/Tab.cpp | 80 ++++++++++-------------- src/slic3r/GUI/Tab.hpp | 1 + src/slic3r/GUI/UnsavedChangesDialog.cpp | 81 ++++++++++++++----------- src/slic3r/GUI/UnsavedChangesDialog.hpp | 5 +- 5 files changed, 90 insertions(+), 84 deletions(-) diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 7539f36168..7a9ea582af 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -1123,10 +1123,12 @@ void SavePresetDialog::Item::update() info_line = _L("Cannot overwrite a system profile."); m_valid_type = NoValid; } + if (m_valid_type == Valid && existing && (existing->is_external)) { info_line = _L("Cannot overwrite an external profile."); m_valid_type = NoValid; } + if (m_valid_type == Valid && existing && m_preset_name != m_presets->get_selected_preset_name()) { info_line = from_u8((boost::format(_u8L("Preset with name \"%1%\" already exists.")) % m_preset_name).str()) + "\n" + @@ -1134,6 +1136,11 @@ void SavePresetDialog::Item::update() m_valid_type = Warning; } + if (m_valid_type == Valid && m_preset_name.empty()) { + info_line = _L("The empty name is not available."); + m_valid_type = NoValid; + } + m_valid_label->SetLabel(info_line); m_valid_label->Show(!info_line.IsEmpty()); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 5c54e9e46e..ef124e0e6e 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3124,6 +3124,12 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, m_dependent_tabs = { Preset::Type::TYPE_SLA_PRINT, Preset::Type::TYPE_SLA_MATERIAL }; } + // check if there is something in the cache to move to the new selected preset + if (!m_cache_config.empty()) { + m_presets->get_edited_preset().config.apply(m_cache_config); + m_cache_config.clear(); + } + load_current_preset(); } } @@ -3134,11 +3140,10 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr { if (presets == nullptr) presets = m_presets; - UnsavedChangesDialog dlg(m_type, new_printer_name); + UnsavedChangesDialog dlg(m_type, presets, new_printer_name); if (dlg.ShowModal() == wxID_CANCEL) return false; - if (dlg.just_continue()) - return true; + if (dlg.save_preset()) // save selected changes { std::vector unselected_options = dlg.get_unselected_options(); @@ -3147,7 +3152,7 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr // for system/default/external presets we should take an edited name if (preset.is_system || preset.is_default || preset.is_external) { - SavePresetDialog save_dlg(m_type, _CTX_utf8(L_CONTEXT("Copy", "PresetName"), "PresetName")); + SavePresetDialog save_dlg(preset.type, _CTX_utf8(L_CONTEXT("Copy", "PresetName"), "PresetName")); if (save_dlg.ShowModal() != wxID_OK) return false; name = save_dlg.get_name(); @@ -3157,56 +3162,35 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr if (!unselected_options.empty()) { DynamicPrintConfig& old_config = presets->get_selected_preset().config; - + // revert unselected options to the old values for (const std::string& opt_key : unselected_options) - m_config->set_key_value(opt_key, old_config.option(opt_key)->clone()); + presets->get_edited_preset().config.set_key_value(opt_key, old_config.option(opt_key)->clone()); } - - save_preset(name); - return true; - } - if (dlg.move_preset()) - // move selected changes - return false; - return false; -/* - // Display a dialog showing the dirty options in a human readable form. - const Preset& old_preset = presets->get_edited_preset(); - std::string type_name = presets->name(); - wxString tab = " "; - wxString name = old_preset.is_default ? - from_u8((boost::format(_utf8(L("Default preset (%s)"))) % _utf8(type_name)).str()) : - from_u8((boost::format(_utf8(L("Preset (%s)"))) % _utf8(type_name)).str()) + "\n" + tab + old_preset.name; + if (m_type == presets->type()) // save changes for the current preset + save_preset(name); + else // save changes for dependent preset + { + // Save the preset into Slic3r::data_dir / presets / section_name / preset_name.ini + presets->save_current_preset(name); + // Mark the print & filament enabled if they are compatible with the currently selected preset. + // If saving the preset changes compatibility with other presets, keep the now incompatible dependent presets selected, however with a "red flag" icon showing that they are no more compatible. + m_preset_bundle->update_compatible(PresetSelectCompatibleType::Never); - // Collect descriptions of the dirty options. - wxString changes; - for (const std::string &opt_key : presets->current_dirty_options()) { - const ConfigOptionDef &opt = m_config->def()->options.at(opt_key); - wxString name = ""; - if (! opt.category.empty()) - name += _(opt.category) + " > "; - name += !opt.full_label.empty() ? - _(opt.full_label) : - _(opt.label); - changes += tab + (name) + "\n"; + /* If filament preset is saved for multi-material printer preset, + * there are cases when filament comboboxs are updated for old (non-modified) colors, + * but in full_config a filament_colors option aren't.*/ + if (presets->type() == Preset::TYPE_FILAMENT && wxGetApp().extruders_edited_cnt() > 1) + wxGetApp().plater()->force_filament_colors_update(); + } } - // Show a confirmation dialog with the list of dirty options. - wxString message = name + "\n\n"; - if (new_printer_name.empty()) - message += _(L("has the following unsaved changes:")); - else { - message += (m_type == Slic3r::Preset::TYPE_PRINTER) ? - _(L("is not compatible with printer")) : - _(L("is not compatible with print profile")); - message += wxString("\n") + tab + from_u8(new_printer_name) + "\n\n"; - message += _(L("and it has the following unsaved changes:")); + else if (dlg.move_preset()) // move selected changes + { + // copy selected options to the cache from edited preset + m_cache_config.apply_only(*m_config, dlg.get_selected_options()); } - wxMessageDialog confirm(parent(), - message + "\n" + changes + "\n\n" + _(L("Discard changes and continue anyway?")), - _(L("Unsaved Changes")), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION); - return confirm.ShowModal() == wxID_YES; - */ + + return true; } // If we are switching from the FFF-preset to the SLA, we should to control the printed objects if they have a part(s). diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 24f25e2d74..c9639bd008 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -236,6 +236,7 @@ public: bool m_show_btn_incompatible_presets = false; PresetCollection* m_presets; DynamicPrintConfig* m_config; + DynamicPrintConfig m_cache_config; ogStaticText* m_parent_preset_description_line; ScalableButton* m_detach_preset_btn = nullptr; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index f93aa35c2a..67ccc6b6ab 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -514,7 +514,7 @@ void UnsavedChangesModel::Rescale() // UnsavedChangesDialog //------------------------------------------ -UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& new_selected_preset) +UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset) : DPIDialog(nullptr, wxID_ANY, _L("Unsaved Changes"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); @@ -530,6 +530,9 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& int border = 10; int em = em_unit(); + m_action_line = new wxStaticText(this, wxID_ANY, ""); + m_action_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold()); + m_tree = new wxDataViewCtrl(this, wxID_ANY, wxDefaultPosition, wxSize(em * 80, em * 30), wxBORDER_SIMPLE | wxDV_VARIABLE_LINE_HEIGHT | wxDV_ROW_LINES); m_tree_model = new UnsavedChangesModel(this); m_tree->AssociateModel(m_tree_model); @@ -561,36 +564,24 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& m_tree->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &UnsavedChangesDialog::context_menu, this); // Add Buttons - wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCANCEL); + wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCANCEL); - Tab* tab = wxGetApp().get_tab(type); - assert(tab); - PresetCollection* presets = tab->get_presets(); + auto add_btn = [this, buttons](ScalableButton** btn, int& btn_id, const std::string& icon_name, Action close_act, int idx, bool process_enable = true) + { + *btn = new ScalableButton(this, btn_id = NewControlId(), icon_name, "", wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true); + buttons->Insert(idx, *btn, 0, wxLEFT, 5); - wxString label= from_u8((boost::format(_u8L("Save selected to preset: %1%"))% ("\"" + presets->get_selected_preset().name + "\"")).str()); - m_save_btn = new ScalableButton(this, m_save_btn_id = NewControlId(), "save", label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true); - buttons->Insert(0, m_save_btn, 0, wxLEFT, 5); + (*btn)->Bind(wxEVT_BUTTON, [this, close_act](wxEvent&) { close(close_act); }); + if (process_enable) + (*btn)->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); + (*btn)->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& e) { show_info_line(Action::Undef); e.Skip(); }); + }; - m_save_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Save); }); - m_save_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); - m_save_btn->Bind(wxEVT_ENTER_WINDOW,[this, presets](wxMouseEvent& e){ show_info_line(Action::Save, presets->get_selected_preset().name); e.Skip(); }); - m_save_btn->Bind(wxEVT_LEAVE_WINDOW,[this ](wxMouseEvent& e){ show_info_line(Action::Undef); e.Skip(); }); - - label = from_u8((boost::format(_u8L("Move selected to preset: %1%"))% ("\"" + from_u8(new_selected_preset) + "\"")).str()); - m_move_btn = new ScalableButton(this, m_move_btn_id = NewControlId(), "paste_menu", label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true); - buttons->Insert(1, m_move_btn, 0, wxLEFT, 5); - - m_move_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Move); }); - m_move_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); - m_move_btn->Bind(wxEVT_ENTER_WINDOW,[this, new_selected_preset](wxMouseEvent& e){ show_info_line(Action::Move, new_selected_preset); e.Skip(); }); - m_move_btn->Bind(wxEVT_LEAVE_WINDOW,[this ](wxMouseEvent& e){ show_info_line(Action::Undef); e.Skip(); }); - - label = _L("Continue without changes"); - m_continue_btn = new ScalableButton(this, m_continue_btn_id = NewControlId(), "cross", label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true); - buttons->Insert(2, m_continue_btn, 0, wxLEFT, 5); - m_continue_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Continue); }); - m_continue_btn->Bind(wxEVT_ENTER_WINDOW,[this](wxMouseEvent& e){ show_info_line(Action::Continue); e.Skip(); }); - m_continue_btn->Bind(wxEVT_LEAVE_WINDOW,[this](wxMouseEvent& e){ show_info_line(Action::Undef); e.Skip(); }); + int btn_idx = 0; + add_btn(&m_save_btn, m_save_btn_id, "save", Action::Save, btn_idx++); + if (type == dependent_presets->type()) + add_btn(&m_move_btn, m_move_btn_id, "paste_menu", Action::Move, btn_idx++); + add_btn(&m_continue_btn, m_continue_btn_id, "cross", Action::Continue, btn_idx, false); m_info_line = new wxStaticText(this, wxID_ANY, ""); m_info_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold()); @@ -598,12 +589,12 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - topSizer->Add(new wxStaticText(this, wxID_ANY, _L("There are unsaved changes for") + (": \"" + tab->title() + "\"")), 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(m_action_line,0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); topSizer->Add(m_tree, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); topSizer->Add(m_info_line, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, 2*border); topSizer->Add(buttons, 0, wxEXPAND | wxALL, border); - update(type); + update(type, dependent_presets, new_selected_preset); SetSizer(topSizer); topSizer->SetSizeHints(this); @@ -824,12 +815,34 @@ wxString UnsavedChangesDialog::get_short_string(wxString full_string) return full_string + dots; } -void UnsavedChangesDialog::update(Preset::Type type) +void UnsavedChangesDialog::update(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset) { - Tab* tab = wxGetApp().get_tab(type); - assert(tab); + PresetCollection* presets = dependent_presets; + + // activate buttons and labels + m_save_btn ->Bind(wxEVT_ENTER_WINDOW, [this, presets] (wxMouseEvent& e) { show_info_line(Action::Save, presets->get_selected_preset().name); e.Skip(); }); + if (m_move_btn) + m_move_btn ->Bind(wxEVT_ENTER_WINDOW, [this, new_selected_preset] (wxMouseEvent& e) { show_info_line(Action::Move, new_selected_preset); e.Skip(); }); + m_continue_btn ->Bind(wxEVT_ENTER_WINDOW, [this] (wxMouseEvent& e) { show_info_line(Action::Continue); e.Skip(); }); + + m_save_btn->SetLabel(from_u8((boost::format(_u8L("Save selected to preset: %1%")) % ("\"" + presets->get_selected_preset().name + "\"")).str())); + m_continue_btn->SetLabel(_L("Continue without changes")); + + wxString action_msg; + if (type == dependent_presets->type()) { + action_msg = _L("has the following unsaved changes:"); + + m_move_btn->SetLabel(from_u8((boost::format(_u8L("Move selected to preset: %1%")) % ("\"" + new_selected_preset + "\"")).str())); + } + else { + action_msg = type == Preset::TYPE_PRINTER ? + _L("is not compatible with printer") : + _L("is not compatible with print profile"); + action_msg += " \"" + from_u8(new_selected_preset) + "\" "; + action_msg += _L("and it has the following unsaved changes:"); + } + m_action_line->SetLabel(from_u8((boost::format(_utf8(L("Preset \"%1%\" %2%"))) % _utf8(presets->get_edited_preset().name) % action_msg).str())); - PresetCollection* presets = tab->get_presets(); // Display a dialog showing the dirty options in a human readable form. const DynamicPrintConfig& old_config = presets->get_selected_preset().config; const DynamicPrintConfig& new_config = presets->get_edited_preset().config; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index 271d7595b6..afb73a4f92 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -193,6 +193,7 @@ class UnsavedChangesDialog : public DPIDialog ScalableButton* m_save_btn { nullptr }; ScalableButton* m_move_btn { nullptr }; ScalableButton* m_continue_btn { nullptr }; + wxStaticText* m_action_line { nullptr }; wxStaticText* m_info_line { nullptr }; bool m_empty_selection { false }; @@ -226,12 +227,12 @@ class UnsavedChangesDialog : public DPIDialog std::map m_items_map; public: - UnsavedChangesDialog(Preset::Type type, const std::string& new_selected_preset); + UnsavedChangesDialog(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset); ~UnsavedChangesDialog() {} wxString get_short_string(wxString full_string); - void update(Preset::Type type); + void update(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset); void item_value_changed(wxDataViewEvent &event); void context_menu(wxDataViewEvent &event); void show_info_line(Action action, std::string preset_name = ""); From f2f5632c3ebc3ac2bbe47af60da60d97dc090202 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=A4hnel?= <6225312+danhae@users.noreply.github.com> Date: Fri, 14 Aug 2020 14:44:00 +0200 Subject: [PATCH 298/503] Update How to build - Mac OS.md Additional section "TL; DR" --- doc/How to build - Mac OS.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/doc/How to build - Mac OS.md b/doc/How to build - Mac OS.md index 082c560b7a..bab40ea265 100644 --- a/doc/How to build - Mac OS.md +++ b/doc/How to build - Mac OS.md @@ -79,3 +79,29 @@ This is set in the property list file /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Info.plist To remove the limitation, simply delete the key `MinimumSDKVersion` from that file. + + +# TL; DR + +Works on a fresh installation of MacOS Catalina 10.15.6 + +- Install [brew](https://brew.sh/): +- Open Terminal + +- Enter: + +```brew install cmake git gettext +brew update +brew upgrade +git clone https://github.com/prusa3d/PrusaSlicer/ +cd PrusaSlicer/deps +mkdir build +cd build +cmake .. +make +cd ../.. +mkdir build +cd build +cmake .. -DCMAKE_PREFIX_PATH="$PWD/../deps/build/destdir/usr/local" +make +src/prusa-slicer From 618f04717fcd2a62978685f3058ee4bb23815c09 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 14 Aug 2020 18:17:16 +0200 Subject: [PATCH 299/503] Unsaved Changes : improvement for the GUI_App::check_unsaved_changes() Added use of UnsavedChangesDialog --- src/libslic3r/PresetBundle.cpp | 26 ++++++ src/libslic3r/PresetBundle.hpp | 4 + src/slic3r/GUI/GUI_App.cpp | 77 ++++++++++++---- src/slic3r/GUI/PresetComboBoxes.cpp | 54 ++++++++---- src/slic3r/GUI/PresetComboBoxes.hpp | 8 +- src/slic3r/GUI/Tab.cpp | 27 ++---- src/slic3r/GUI/UnsavedChangesDialog.cpp | 112 +++++++++++++++++------- src/slic3r/GUI/UnsavedChangesDialog.hpp | 18 ++-- 8 files changed, 228 insertions(+), 98 deletions(-) diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 108985704c..ac1b0a7176 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -327,6 +327,32 @@ const std::string& PresetBundle::get_preset_name_by_alias( const Preset::Type& p return presets.get_preset_name_by_alias(alias); } +void PresetBundle::save_changes_for_preset(const std::string& new_name, Preset::Type type, + const std::vector& unselected_options) +{ + PresetCollection& presets = type == Preset::TYPE_PRINT ? prints : + type == Preset::TYPE_SLA_PRINT ? sla_prints : + type == Preset::TYPE_FILAMENT ? filaments : + type == Preset::TYPE_SLA_MATERIAL ? sla_materials : printers; + + // if we want to save just some from selected options + if (!unselected_options.empty()) { + // revert unselected options to the old values + presets.get_edited_preset().config.apply_only(presets.get_selected_preset().config, unselected_options); + } + + // Save the preset into Slic3r::data_dir / presets / section_name / preset_name.ini + presets.save_current_preset(new_name); + // Mark the print & filament enabled if they are compatible with the currently selected preset. + // If saving the preset changes compatibility with other presets, keep the now incompatible dependent presets selected, however with a "red flag" icon showing that they are no more compatible. + update_compatible(PresetSelectCompatibleType::Never); + + if (type == Preset::TYPE_FILAMENT) { + // synchronize the first filament presets. + set_filament_preset(0, filaments.get_selected_preset_name()); + } +} + void PresetBundle::load_installed_filaments(AppConfig &config) { if (! config.has_section(AppConfig::SECTION_FILAMENTS)) { diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp index 567a12331f..ff02bbeae3 100644 --- a/src/libslic3r/PresetBundle.hpp +++ b/src/libslic3r/PresetBundle.hpp @@ -130,6 +130,10 @@ public: const std::string& get_preset_name_by_alias(const Preset::Type& preset_type, const std::string& alias) const; + // Save current preset of a required type under a new name. If the name is different from the old one, + // Unselected option would be reverted to the beginning values + void save_changes_for_preset(const std::string& new_name, Preset::Type type, const std::vector& unselected_options); + static const char *PRUSA_BUNDLE; private: std::string load_system_presets(); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 82c2861bc2..266fd8fd63 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -57,6 +57,8 @@ #include "RemovableDriveManager.hpp" #include "InstanceCheck.hpp" #include "NotificationManager.hpp" +#include "UnsavedChangesDialog.hpp" +#include "PresetComboBoxes.hpp" #ifdef __WXMSW__ #include @@ -1157,29 +1159,66 @@ void GUI_App::add_config_menu(wxMenuBar *menu) // to notify the user whether he is aware that some preset changes will be lost. bool GUI_App::check_unsaved_changes(const wxString &header) { - wxString dirty; PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); - for (Tab *tab : tabs_list) + + bool has_unsaved_changes = false; + for (Tab* tab : tabs_list) if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty()) { - if (dirty.empty()) - dirty = tab->title(); - else - dirty += wxString(", ") + tab->title(); + has_unsaved_changes = true; + break; } - if (dirty.empty()) - // No changes, the application may close or reload presets. - return true; - // Ask the user. - wxString message; - if (! header.empty()) - message = header + "\n\n"; - message += _(L("The presets on the following tabs were modified")) + ": " + dirty + "\n\n" + _(L("Discard changes and continue anyway?")); - wxMessageDialog dialog(mainframe, - message, - wxString(SLIC3R_APP_NAME) + " - " + _(L("Unsaved Presets")), - wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT); - return dialog.ShowModal() == wxID_YES; + if (has_unsaved_changes) + { + UnsavedChangesDialog dlg(header); + if (dlg.ShowModal() == wxID_CANCEL) + return false; + + if (dlg.save_preset()) // save selected changes + { + struct NameType + { + std::string name; + Preset::Type type {Preset::TYPE_INVALID}; + }; + + std::vector names_and_types; + + // for system/default/external presets we should take an edited name + std::vector types; + for (Tab* tab : tabs_list) + if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty()) + { + const Preset& preset = tab->get_presets()->get_edited_preset(); + if (preset.is_system || preset.is_default || preset.is_external) + types.emplace_back(preset.type); + + names_and_types.emplace_back(NameType{ preset.name, preset.type }); + } + + + if (!types.empty()) { + SavePresetDialog save_dlg(types); + if (save_dlg.ShowModal() != wxID_OK) + return false; + + for (NameType& nt : names_and_types) { + const std::string name = save_dlg.get_name(nt.type); + if (!name.empty()) + nt.name = name; + } + } + + for (const NameType& nt : names_and_types) + preset_bundle->save_changes_for_preset(nt.name, nt.type, dlg.get_unselected_options(nt.type)); + + // if we saved changes to the new presets, we should to + // synchronize config.ini with the current selections. + preset_bundle->export_selections(*app_config); + } + } + + return true; } bool GUI_App::checked_tab(Tab* tab) diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 87db32ac69..9b0c9d0c86 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -1170,8 +1170,26 @@ void SavePresetDialog::Item::accept() // SavePresetDialog //----------------------------------------------- -SavePresetDialog::SavePresetDialog(Preset::Type type, const std::string& suffix) +SavePresetDialog::SavePresetDialog(Preset::Type type, std::string suffix) : DPIDialog(nullptr, wxID_ANY, _L("Save preset"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER) +{ + build(std::vector{type}, suffix); +} + +SavePresetDialog::SavePresetDialog(std::vector types, std::string suffix) + : DPIDialog(nullptr, wxID_ANY, _L("Save preset"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER) +{ + build(types, suffix); +} + +SavePresetDialog::~SavePresetDialog() +{ + for (auto item : m_items) { + delete item; + } +} + +void SavePresetDialog::build(std::vector types, std::string suffix) { SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); #if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT @@ -1179,14 +1197,18 @@ SavePresetDialog::SavePresetDialog(Preset::Type type, const std::string& suffix) // Because of from wxWidgets 3.1.3 auto rescaling is implemented for the Fonts, // From the very beginning set dialog font to the wxSYS_DEFAULT_GUI_FONT this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); -#endif // ENABLE_WX_3_1_3_DPI_CHANGED_EVENT +#endif // ENABLE_WX_3_1_3_DPI_CHANGED_EVENT + + if (suffix.empty()) + suffix = _CTX_utf8(L_CONTEXT("Copy", "PresetName"), "PresetName"); wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); m_presets_sizer = new wxBoxSizer(wxVERTICAL); // Add first item - m_items.emplace_back(type, suffix, m_presets_sizer, this); + for (Preset::Type type : types) + AddItem(type, suffix); // Add dialog's buttons wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); @@ -1203,26 +1225,26 @@ SavePresetDialog::SavePresetDialog(Preset::Type type, const std::string& suffix) void SavePresetDialog::AddItem(Preset::Type type, const std::string& suffix) { - m_items.emplace_back(type, suffix, m_presets_sizer, this); + m_items.emplace_back(new Item{type, suffix, m_presets_sizer, this}); } std::string SavePresetDialog::get_name() { - return m_items.front().preset_name(); + return m_items.front()->preset_name(); } std::string SavePresetDialog::get_name(Preset::Type type) { - for (Item& item : m_items) - if (item.type() == type) - return item.preset_name(); + for (const Item* item : m_items) + if (item->type() == type) + return item->preset_name(); return ""; } bool SavePresetDialog::enable_ok_btn() const { - for (Item item : m_items) - if (!item.is_valid()) + for (const Item* item : m_items) + if (!item->is_valid()) return false; return true; @@ -1291,8 +1313,8 @@ void SavePresetDialog::on_dpi_changed(const wxRect& suggested_rect) msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL }); - for (Item& item : m_items) - item.update_valid_bmp(); + for (Item* item : m_items) + item->update_valid_bmp(); //const wxSize& size = wxSize(45 * em, 35 * em); SetMinSize(/*size*/wxSize(100, 50)); @@ -1331,10 +1353,10 @@ void SavePresetDialog::update_physical_printers(const std::string& preset_name) void SavePresetDialog::accept() { - for (Item& item : m_items) { - item.accept(); - if (item.type() == Preset::TYPE_PRINTER) - update_physical_printers(item.preset_name()); + for (Item* item : m_items) { + item->accept(); + if (item->type() == Preset::TYPE_PRINTER) + update_physical_printers(item->preset_name()); } EndModal(wxID_OK); diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index 7f51f775ee..e33a2d753d 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -235,7 +235,7 @@ class SavePresetDialog : public DPIDialog void update(); }; - std::vector m_items; + std::vector m_items; wxBoxSizer* m_presets_sizer {nullptr}; wxStaticText* m_label {nullptr}; @@ -248,8 +248,9 @@ class SavePresetDialog : public DPIDialog public: - SavePresetDialog(Preset::Type type, const std::string& suffix); - ~SavePresetDialog() {} + SavePresetDialog(Preset::Type type, std::string suffix = ""); + SavePresetDialog(std::vector types, std::string suffix = ""); + ~SavePresetDialog(); void AddItem(Preset::Type type, const std::string& suffix); @@ -266,6 +267,7 @@ protected: void on_sys_color_changed() override {} private: + void build(std::vector types, std::string suffix = ""); void update_physical_printers(const std::string& preset_name); void accept(); }; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index cf999a409d..1363ddcad7 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3146,36 +3146,27 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr if (dlg.save_preset()) // save selected changes { - std::vector unselected_options = dlg.get_unselected_options(); + std::vector unselected_options = dlg.get_unselected_options(presets->type()); const Preset& preset = presets->get_edited_preset(); std::string name = preset.name; // for system/default/external presets we should take an edited name if (preset.is_system || preset.is_default || preset.is_external) { - SavePresetDialog save_dlg(preset.type, _CTX_utf8(L_CONTEXT("Copy", "PresetName"), "PresetName")); + SavePresetDialog save_dlg(preset.type); if (save_dlg.ShowModal() != wxID_OK) return false; name = save_dlg.get_name(); } - // if we want to save just some from selected options - if (!unselected_options.empty()) + if (m_type == presets->type()) // save changes for the current preset from this tab { - DynamicPrintConfig& old_config = presets->get_selected_preset().config; // revert unselected options to the old values - for (const std::string& opt_key : unselected_options) - presets->get_edited_preset().config.set_key_value(opt_key, old_config.option(opt_key)->clone()); - } - - if (m_type == presets->type()) // save changes for the current preset + presets->get_edited_preset().config.apply_only(presets->get_selected_preset().config, unselected_options); save_preset(name); - else // save changes for dependent preset + } + else { - // Save the preset into Slic3r::data_dir / presets / section_name / preset_name.ini - presets->save_current_preset(name); - // Mark the print & filament enabled if they are compatible with the currently selected preset. - // If saving the preset changes compatibility with other presets, keep the now incompatible dependent presets selected, however with a "red flag" icon showing that they are no more compatible. - m_preset_bundle->update_compatible(PresetSelectCompatibleType::Never); + m_preset_bundle->save_changes_for_preset(name, presets->type(), unselected_options); /* If filament preset is saved for multi-material printer preset, * there are cases when filament comboboxs are updated for old (non-modified) colors, @@ -3283,10 +3274,8 @@ void Tab::save_preset(std::string name /*= ""*/, bool detach) // focus currently.is there anything better than this ? //! m_treectrl->OnSetFocus(); - std::string suffix = detach ? _utf8(L("Detached")) : _CTX_utf8(L_CONTEXT("Copy", "PresetName"), "PresetName"); - if (name.empty()) { - SavePresetDialog dlg(m_type, suffix); + SavePresetDialog dlg(m_type, detach ? _u8L("Detached") : ""); if (dlg.ShowModal() != wxID_OK) return; name = dlg.get_name(); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 67ccc6b6ab..2fa89266e2 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -514,8 +514,19 @@ void UnsavedChangesModel::Rescale() // UnsavedChangesDialog //------------------------------------------ +UnsavedChangesDialog::UnsavedChangesDialog(const wxString& header) + : DPIDialog(nullptr, wxID_ANY, _L("Unsaved Changes"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +{ + build(Preset::TYPE_INVALID, nullptr, "", header); +} + UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset) : DPIDialog(nullptr, wxID_ANY, _L("Unsaved Changes"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +{ + build(type, dependent_presets, new_selected_preset); +} + +void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, const wxString& header) { wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); SetBackgroundColour(bgr_clr); @@ -579,7 +590,8 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, PresetCollection* int btn_idx = 0; add_btn(&m_save_btn, m_save_btn_id, "save", Action::Save, btn_idx++); - if (type == dependent_presets->type()) + if (type != Preset::TYPE_INVALID && type == dependent_presets->type() && + dependent_presets->get_edited_preset().printer_technology() == dependent_presets->find_preset(new_selected_preset)->printer_technology()) add_btn(&m_move_btn, m_move_btn_id, "paste_menu", Action::Move, btn_idx++); add_btn(&m_continue_btn, m_continue_btn_id, "cross", Action::Continue, btn_idx, false); @@ -594,7 +606,7 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, PresetCollection* topSizer->Add(m_info_line, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, 2*border); topSizer->Add(buttons, 0, wxEXPAND | wxALL, border); - update(type, dependent_presets, new_selected_preset); + update(type, dependent_presets, new_selected_preset, header); SetSizer(topSizer); topSizer->SetSizeHints(this); @@ -654,8 +666,12 @@ void UnsavedChangesDialog::show_info_line(Action action, std::string preset_name else if (action == Action::Continue) text = _L("All changed options will be reverted."); else { - std::string act_string = action == Action::Save ? _u8L("saved") : _u8L("moved"); - text = from_u8((boost::format("After press this button selected options will be %1% to the preset \"%2%\".") % act_string % preset_name).str()); + if (action == Action::Save && preset_name.empty()) + text = _L("After press this button selected options will be saved"); + else { + std::string act_string = action == Action::Save ? _u8L("saved") : _u8L("moved"); + text = from_u8((boost::format("After press this button selected options will be %1% to the preset \"%2%\".") % act_string % preset_name).str()); + } text += "\n" + _L("Unselected options will be reverted."); } m_info_line->SetLabel(text); @@ -815,64 +831,92 @@ wxString UnsavedChangesDialog::get_short_string(wxString full_string) return full_string + dots; } -void UnsavedChangesDialog::update(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset) +void UnsavedChangesDialog::update(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, const wxString& header) { PresetCollection* presets = dependent_presets; // activate buttons and labels - m_save_btn ->Bind(wxEVT_ENTER_WINDOW, [this, presets] (wxMouseEvent& e) { show_info_line(Action::Save, presets->get_selected_preset().name); e.Skip(); }); + m_save_btn ->Bind(wxEVT_ENTER_WINDOW, [this, presets] (wxMouseEvent& e) { show_info_line(Action::Save, presets ? presets->get_selected_preset().name : ""); e.Skip(); }); if (m_move_btn) m_move_btn ->Bind(wxEVT_ENTER_WINDOW, [this, new_selected_preset] (wxMouseEvent& e) { show_info_line(Action::Move, new_selected_preset); e.Skip(); }); m_continue_btn ->Bind(wxEVT_ENTER_WINDOW, [this] (wxMouseEvent& e) { show_info_line(Action::Continue); e.Skip(); }); - m_save_btn->SetLabel(from_u8((boost::format(_u8L("Save selected to preset: %1%")) % ("\"" + presets->get_selected_preset().name + "\"")).str())); m_continue_btn->SetLabel(_L("Continue without changes")); - wxString action_msg; - if (type == dependent_presets->type()) { - action_msg = _L("has the following unsaved changes:"); - - m_move_btn->SetLabel(from_u8((boost::format(_u8L("Move selected to preset: %1%")) % ("\"" + new_selected_preset + "\"")).str())); + if (type == Preset::TYPE_INVALID) { + m_action_line ->SetLabel(header + "\n" + _L("Next presets have the following unsaved changes:")); + m_save_btn ->SetLabel(_L("Save selected")); } else { - action_msg = type == Preset::TYPE_PRINTER ? - _L("is not compatible with printer") : - _L("is not compatible with print profile"); - action_msg += " \"" + from_u8(new_selected_preset) + "\" "; - action_msg += _L("and it has the following unsaved changes:"); + wxString action_msg; + if (type == dependent_presets->type()) { + action_msg = _L("has the following unsaved changes:"); + if (m_move_btn) + m_move_btn->SetLabel(from_u8((boost::format(_u8L("Move selected to preset: %1%")) % ("\"" + new_selected_preset + "\"")).str())); + } + else { + action_msg = type == Preset::TYPE_PRINTER ? + _L("is not compatible with printer") : + _L("is not compatible with print profile"); + action_msg += " \"" + from_u8(new_selected_preset) + "\"\n"; + action_msg += _L("and it has the following unsaved changes:"); + } + m_action_line->SetLabel(from_u8((boost::format(_utf8(L("Preset \"%1%\" %2%"))) % _utf8(presets->get_edited_preset().name) % action_msg).str())); + m_save_btn->SetLabel(from_u8((boost::format(_u8L("Save selected to preset: %1%")) % ("\"" + presets->get_selected_preset().name + "\"")).str())); } - m_action_line->SetLabel(from_u8((boost::format(_utf8(L("Preset \"%1%\" %2%"))) % _utf8(presets->get_edited_preset().name) % action_msg).str())); - // Display a dialog showing the dirty options in a human readable form. - const DynamicPrintConfig& old_config = presets->get_selected_preset().config; - const DynamicPrintConfig& new_config = presets->get_edited_preset().config; - - m_tree_model->AddPreset(type, from_u8(presets->get_edited_preset().name)); + update_tree(type, presets); +} +void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* presets_) +{ Search::OptionsSearcher& searcher = wxGetApp().sidebar().get_searcher(); searcher.sort_options_by_opt_key(); - // Collect dirty options. - for (const std::string& opt_key : presets->current_dirty_options()) { - const Search::Option& option = searcher.get_option(opt_key); + // list of the presets with unsaved changes + std::vector presets_list; + if (type == Preset::TYPE_INVALID) + { + PrinterTechnology printer_technology = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology(); - ItemData item_data = { opt_key, option.label_local, get_string_value(opt_key, old_config), get_string_value(opt_key, new_config) }; + for (Tab* tab : wxGetApp().tabs_list) + if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty()) + presets_list.emplace_back(tab->get_presets()); + } + else + presets_list.emplace_back(presets_); - wxString old_val = get_short_string(item_data.old_val); - wxString new_val = get_short_string(item_data.new_val); - if (old_val != item_data.old_val || new_val != item_data.new_val) - item_data.is_long = true; + // Display a dialog showing the dirty options in a human readable form. + for (PresetCollection* presets : presets_list) + { + const DynamicPrintConfig& old_config = presets->get_selected_preset().config; + const DynamicPrintConfig& new_config = presets->get_edited_preset().config; + type = presets->type(); - m_items_map.emplace(m_tree_model->AddOption(type, option.category_local, option.group_local, option.label_local, old_val, new_val), item_data); + m_tree_model->AddPreset(type, from_u8(presets->get_edited_preset().name)); + + // Collect dirty options. + for (const std::string& opt_key : presets->current_dirty_options()) { + const Search::Option& option = searcher.get_option(opt_key); + + ItemData item_data = { opt_key, option.label_local, get_string_value(opt_key, old_config), get_string_value(opt_key, new_config), type }; + + wxString old_val = get_short_string(item_data.old_val); + wxString new_val = get_short_string(item_data.new_val); + if (old_val != item_data.old_val || new_val != item_data.new_val) + item_data.is_long = true; + + m_items_map.emplace(m_tree_model->AddOption(type, option.category_local, option.group_local, option.label_local, old_val, new_val), item_data); + } } } -std::vector UnsavedChangesDialog::get_unselected_options() +std::vector UnsavedChangesDialog::get_unselected_options(Preset::Type type) { std::vector ret; for (auto item : m_items_map) - if (!m_tree_model->IsEnabledItem(item.first)) + if (item.second.type == type && !m_tree_model->IsEnabledItem(item.first)) ret.emplace_back(item.second.opt_key); return ret; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index afb73a4f92..f78a1fec0e 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -217,22 +217,26 @@ class UnsavedChangesDialog : public DPIDialog struct ItemData { - std::string opt_key; - wxString opt_name; - wxString old_val; - wxString new_val; - bool is_long {false}; + std::string opt_key; + wxString opt_name; + wxString old_val; + wxString new_val; + Preset::Type type; + bool is_long {false}; }; // tree items related to the options std::map m_items_map; public: + UnsavedChangesDialog(const wxString& header); UnsavedChangesDialog(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset); ~UnsavedChangesDialog() {} wxString get_short_string(wxString full_string); - void update(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset); + void build(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, const wxString& header = ""); + void update(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, const wxString& header); + void update_tree(Preset::Type type, PresetCollection *presets); void item_value_changed(wxDataViewEvent &event); void context_menu(wxDataViewEvent &event); void show_info_line(Action action, std::string preset_name = ""); @@ -242,7 +246,7 @@ public: bool move_preset() const { return m_exit_action == Action::Move; } bool just_continue() const { return m_exit_action == Action::Continue; } - std::vector get_unselected_options(); + std::vector get_unselected_options(Preset::Type type); std::vector get_selected_options(); protected: From f2d02faef4f73c9e71c4b06a438920937be7255b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 17 Aug 2020 10:06:41 +0200 Subject: [PATCH 300/503] GCodeProcessor -> Added debug code to check toolpaths data extracted from gcode, as mm3 per mm, height and width --- src/libslic3r/GCode.cpp | 11 ++ src/libslic3r/GCode.hpp | 6 + src/libslic3r/GCode/GCodeProcessor.cpp | 210 ++++++++++++++++--------- src/libslic3r/GCode/GCodeProcessor.hpp | 113 ++++++++++--- src/libslic3r/GCode/WipeTower.cpp | 16 ++ src/libslic3r/Technologies.hpp | 1 + src/slic3r/GUI/GCodeViewer.cpp | 4 +- 7 files changed, 263 insertions(+), 98 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 795aac898f..24c758d502 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1186,6 +1186,9 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu m_last_width = 0.0f; m_last_height = 0.0f; m_last_layer_z = 0.0f; +#if ENABLE_GCODE_VIEWER_DATA_CHECKING + m_last_mm3_per_mm = 0.0; +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING #else m_last_mm3_per_mm = GCodeAnalyzer::Default_mm3_per_mm; m_last_width = GCodeAnalyzer::Default_Width; @@ -3268,6 +3271,14 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, sprintf(buf, ";%s%s\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), ExtrusionEntity::role_to_string(m_last_processor_extrusion_role).c_str()); gcode += buf; } + +#if ENABLE_GCODE_VIEWER_DATA_CHECKING + if (last_was_wipe_tower || (m_last_mm3_per_mm != path.mm3_per_mm)) { + m_last_mm3_per_mm = path.mm3_per_mm; + sprintf(buf, ";%s%f\n", GCodeProcessor::Mm3_Per_Mm_Tag.c_str(), m_last_mm3_per_mm); + gcode += buf; + } +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING #else if (path.role() != m_last_analyzer_extrusion_role) { m_last_analyzer_extrusion_role = path.role(); diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 6f97d5dbd3..76f897c63b 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -172,6 +172,9 @@ public: m_volumetric_speed(0), m_last_pos_defined(false), m_last_extrusion_role(erNone), +#if ENABLE_GCODE_VIEWER_DATA_CHECKING + m_last_mm3_per_mm(0.0), +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING #if !ENABLE_GCODE_VIEWER m_last_mm3_per_mm(GCodeAnalyzer::Default_mm3_per_mm), m_last_width(GCodeAnalyzer::Default_Width), @@ -381,6 +384,9 @@ private: float m_last_width{ 0.0f }; float m_last_height{ 0.0f }; float m_last_layer_z{ 0.0f }; +#if ENABLE_GCODE_VIEWER_DATA_CHECKING + double m_last_mm3_per_mm; +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING #else // Support for G-Code Analyzer double m_last_mm3_per_mm; diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 18878cd5ad..2e6736d1ec 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -33,6 +33,10 @@ const std::string GCodeProcessor::First_Line_M73_Placeholder_Tag = "; _ const std::string GCodeProcessor::Last_Line_M73_Placeholder_Tag = "; _GP_LAST_LINE_M73_PLACEHOLDER"; const std::string GCodeProcessor::Estimated_Printing_Time_Placeholder_Tag = "; _GP_ESTIMATED_PRINTING_TIME_PLACEHOLDER"; +#if ENABLE_GCODE_VIEWER_DATA_CHECKING +const std::string GCodeProcessor::Mm3_Per_Mm_Tag = "Mm3_Per_Mm:"; +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING + static bool is_valid_extrusion_role(int value) { return (static_cast(erNone) <= value) && (value <= static_cast(erMixed)); @@ -308,10 +312,10 @@ void GCodeProcessor::TimeProcessor::reset() machine_limits = MachineEnvelopeConfig(); filament_load_times = std::vector(); filament_unload_times = std::vector(); - for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { machines[i].reset(); } - machines[static_cast(ETimeMode::Normal)].enabled = true; + machines[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Normal)].enabled = true; } void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) @@ -342,8 +346,8 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) std::string gcode_line; size_t g1_lines_counter = 0; // keeps track of last exported pair - std::array, static_cast(ETimeMode::Count)> last_exported; - for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + std::array, static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count)> last_exported; + for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { last_exported[i] = { 0, time_in_minutes(machines[i].time) }; } @@ -357,7 +361,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) std::string ret; if (line == First_Line_M73_Placeholder_Tag || line == Last_Line_M73_Placeholder_Tag) { - for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { const TimeMachine& machine = machines[i]; if (machine.enabled) { ret += format_line_M73(machine.line_m73_mask.c_str(), @@ -367,12 +371,12 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) } } else if (line == Estimated_Printing_Time_Placeholder_Tag) { - for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { const TimeMachine& machine = machines[i]; if (machine.enabled) { char buf[128]; sprintf(buf, "; estimated printing time (%s mode) = %s\n", - (static_cast(i) == ETimeMode::Normal) ? "normal" : "silent", + (static_cast(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal) ? "normal" : "silent", get_time_dhms(machine.time).c_str()); ret += buf; } @@ -383,7 +387,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) // add lines M73 to exported gcode auto process_line_G1 = [&]() { - for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { const TimeMachine& machine = machines[i]; if (machine.enabled && g1_lines_counter < machine.g1_times_cache.size()) { float elapsed_time = machine.g1_times_cache[g1_lines_counter]; @@ -458,8 +462,8 @@ unsigned int GCodeProcessor::s_result_id = 0; GCodeProcessor::GCodeProcessor() { reset(); - m_time_processor.machines[static_cast(ETimeMode::Normal)].line_m73_mask = "M73 P%s R%s\n"; - m_time_processor.machines[static_cast(ETimeMode::Stealth)].line_m73_mask = "M73 Q%s S%s\n"; + m_time_processor.machines[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Normal)].line_m73_mask = "M73 P%s R%s\n"; + m_time_processor.machines[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].line_m73_mask = "M73 Q%s S%s\n"; } void GCodeProcessor::apply_config(const PrintConfig& config) @@ -478,7 +482,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config) m_extruder_colors.resize(extruders_count); for (size_t i = 0; i < extruders_count; ++i) { - m_extruder_colors[i] = static_cast(i); + m_extruder_colors[i] = static_cast(i); } m_filament_diameters.resize(config.filament_diameter.values.size()); @@ -499,7 +503,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config) m_time_processor.filament_unload_times[i] = static_cast(config.filament_unload_time.values[i]); } - for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i); m_time_processor.machines[i].max_acceleration = max_acceleration; m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION; @@ -561,7 +565,7 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) m_extruder_colors.resize(m_result.extruder_colors.size()); for (size_t i = 0; i < m_result.extruder_colors.size(); ++i) { - m_extruder_colors[i] = static_cast(i); + m_extruder_colors[i] = static_cast(i); } const ConfigOptionFloats* filament_load_time = config.option("filament_load_time"); @@ -644,7 +648,7 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) if (machine_min_travel_rate != nullptr) m_time_processor.machine_limits.machine_min_travel_rate.values = machine_min_travel_rate->values; - for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i); m_time_processor.machines[i].max_acceleration = max_acceleration; m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION; @@ -653,15 +657,17 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) void GCodeProcessor::enable_stealth_time_estimator(bool enabled) { - m_time_processor.machines[static_cast(ETimeMode::Stealth)].enabled = enabled; + m_time_processor.machines[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].enabled = enabled; } void GCodeProcessor::reset() { + static const size_t Min_Extruder_Count = 5; + m_units = EUnits::Millimeters; m_global_positioning_type = EPositioningType::Absolute; m_e_local_positioning_type = EPositioningType::Absolute; - m_extruder_offsets = std::vector(1, Vec3f::Zero()); + m_extruder_offsets = std::vector(Min_Extruder_Count, Vec3f::Zero()); m_flavor = gcfRepRap; m_start_position = { 0.0f, 0.0f, 0.0f, 0.0f }; @@ -677,8 +683,12 @@ void GCodeProcessor::reset() m_extrusion_role = erNone; m_extruder_id = 0; - m_extruder_colors = ExtruderColors(); - m_filament_diameters = std::vector(); + m_extruder_colors.resize(Min_Extruder_Count); + for (size_t i = 0; i < Min_Extruder_Count; ++i) { + m_extruder_colors[i] = static_cast(i); + } + + m_filament_diameters = std::vector(Min_Extruder_Count, 1.75f); m_extruded_last_z = 0.0f; m_cp_color.reset(); @@ -689,6 +699,12 @@ void GCodeProcessor::reset() m_result.reset(); m_result.id = ++s_result_id; + +#if ENABLE_GCODE_VIEWER_DATA_CHECKING + m_mm3_per_mm_compare.reset(); + m_height_compare.reset(); + m_width_compare.reset(); +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING } void GCodeProcessor::process_file(const std::string& filename) @@ -724,7 +740,7 @@ void GCodeProcessor::process_file(const std::string& filename) m_parser.parse_file(filename, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { process_gcode_line(line); }); // process the remaining time blocks - for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { TimeMachine& machine = m_time_processor.machines[i]; TimeMachine::CustomGCodeTime& gcode_time = machine.gcode_time; machine.calculate_time(); @@ -738,25 +754,31 @@ void GCodeProcessor::process_file(const std::string& filename) if (m_time_processor.export_remaining_time_enabled) m_time_processor.post_process(filename); +#if ENABLE_GCODE_VIEWER_DATA_CHECKING + m_mm3_per_mm_compare.output(); + m_height_compare.output(); + m_width_compare.output(); +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING + #if ENABLE_GCODE_VIEWER_STATISTICS m_result.time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start_time).count(); #endif // ENABLE_GCODE_VIEWER_STATISTICS } -float GCodeProcessor::get_time(ETimeMode mode) const +float GCodeProcessor::get_time(PrintEstimatedTimeStatistics::ETimeMode mode) const { - return (mode < ETimeMode::Count) ? m_time_processor.machines[static_cast(mode)].time : 0.0f; + return (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) ? m_time_processor.machines[static_cast(mode)].time : 0.0f; } -std::string GCodeProcessor::get_time_dhm(ETimeMode mode) const +std::string GCodeProcessor::get_time_dhm(PrintEstimatedTimeStatistics::ETimeMode mode) const { - return (mode < ETimeMode::Count) ? short_time(get_time_dhms(m_time_processor.machines[static_cast(mode)].time)) : std::string("N/A"); + return (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) ? short_time(get_time_dhms(m_time_processor.machines[static_cast(mode)].time)) : std::string("N/A"); } -std::vector>> GCodeProcessor::get_custom_gcode_times(ETimeMode mode, bool include_remaining) const +std::vector>> GCodeProcessor::get_custom_gcode_times(PrintEstimatedTimeStatistics::ETimeMode mode, bool include_remaining) const { std::vector>> ret; - if (mode < ETimeMode::Count) { + if (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) { const TimeMachine& machine = m_time_processor.machines[static_cast(mode)]; float total_time = 0.0f; for (const auto& [type, time] : machine.gcode_time.times) { @@ -768,10 +790,10 @@ std::vector>> GCodeProcesso return ret; } -std::vector> GCodeProcessor::get_moves_time(ETimeMode mode) const +std::vector> GCodeProcessor::get_moves_time(PrintEstimatedTimeStatistics::ETimeMode mode) const { std::vector> ret; - if (mode < ETimeMode::Count) { + if (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) { for (size_t i = 0; i < m_time_processor.machines[static_cast(mode)].moves_time.size(); ++i) { float time = m_time_processor.machines[static_cast(mode)].moves_time[i]; if (time > 0.0f) @@ -781,10 +803,10 @@ std::vector> GCodeProcessor::get_moves_time(ETimeMod return ret; } -std::vector> GCodeProcessor::get_roles_time(ETimeMode mode) const +std::vector> GCodeProcessor::get_roles_time(PrintEstimatedTimeStatistics::ETimeMode mode) const { std::vector> ret; - if (mode < ETimeMode::Count) { + if (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) { for (size_t i = 0; i < m_time_processor.machines[static_cast(mode)].roles_time.size(); ++i) { float time = m_time_processor.machines[static_cast(mode)].roles_time[i]; if (time > 0.0f) @@ -888,6 +910,9 @@ void GCodeProcessor::process_tags(const std::string& comment) if (pos != comment.npos) { try { m_width = std::stof(comment.substr(pos + Width_Tag.length())); +#if ENABLE_GCODE_VIEWER_DATA_CHECKING + m_width_compare.last_tag_value = m_width; +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING } catch (...) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Width (" << comment << ")."; @@ -900,6 +925,9 @@ void GCodeProcessor::process_tags(const std::string& comment) if (pos != comment.npos) { try { m_height = std::stof(comment.substr(pos + Height_Tag.length())); +#if ENABLE_GCODE_VIEWER_DATA_CHECKING + m_height_compare.last_tag_value = m_height; +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING } catch (...) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Height (" << comment << ")."; @@ -947,6 +975,20 @@ void GCodeProcessor::process_tags(const std::string& comment) store_move_vertex(EMoveType::Custom_GCode); return; } + +#if ENABLE_GCODE_VIEWER_DATA_CHECKING + // mm3_per_mm print tag + pos = comment.find(Mm3_Per_Mm_Tag); + if (pos != comment.npos) { + try { + m_mm3_per_mm_compare.last_tag_value = std::stof(comment.substr(pos + Mm3_Per_Mm_Tag.length())); + } + catch (...) { + BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Mm3_Per_Mm (" << comment << ")."; + } + return; + } +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING } bool GCodeProcessor::process_producers_tags(const std::string& comment) @@ -1126,8 +1168,10 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string& comment) size_t w_end = data.find_first_of(' ', w_start); if (h_start != data.npos) { try { - std::string test = data.substr(h_start + 1, (h_end != data.npos) ? h_end - h_start - 1 : h_end); m_height = std::stof(data.substr(h_start + 1, (h_end != data.npos) ? h_end - h_start - 1 : h_end)); +#if ENABLE_GCODE_VIEWER_DATA_CHECKING + m_height_compare.last_tag_value = m_height; +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING } catch (...) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Height (" << comment << ")."; @@ -1135,8 +1179,10 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string& comment) } if (w_start != data.npos) { try { - std::string test = data.substr(w_start + 1, (w_end != data.npos) ? w_end - w_start - 1 : w_end); m_width = std::stof(data.substr(w_start + 1, (w_end != data.npos) ? w_end - w_start - 1 : w_end)); +#if ENABLE_GCODE_VIEWER_DATA_CHECKING + m_width_compare.last_tag_value = m_width; +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING } catch (...) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Width (" << comment << ")."; @@ -1146,7 +1192,7 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string& comment) return true; } - std::cout << comment << "\n"; +// std::cout << comment << "\n"; return false; } @@ -1226,6 +1272,9 @@ bool GCodeProcessor::process_ideamaker_tags(const std::string& comment) if (pos != comment.npos) { try { m_width = std::stof(comment.substr(pos + tag.length())); +#if ENABLE_GCODE_VIEWER_DATA_CHECKING + m_width_compare.last_tag_value = m_width; +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING } catch (...) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Width (" << comment << ")."; @@ -1239,6 +1288,9 @@ bool GCodeProcessor::process_ideamaker_tags(const std::string& comment) if (pos != comment.npos) { try { m_height = std::stof(comment.substr(pos + tag.length())); +#if ENABLE_GCODE_VIEWER_DATA_CHECKING + m_height_compare.last_tag_value = m_height; +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING } catch (...) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Height (" << comment << ")."; @@ -1327,19 +1379,29 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) return; EMoveType type = move_type(delta_pos); + if (type == EMoveType::Extrude && m_end_position[Z] == 0.0f) + type = EMoveType::Travel; if (type == EMoveType::Extrude) { - if (delta_pos[E] > 0.0f) { - float ds = std::sqrt(sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z])); - if (ds > 0.0f && static_cast(m_extruder_id) < m_filament_diameters.size()) { - // extruded filament volume / tool displacement - m_mm3_per_mm = round_to_nearest(static_cast(M_PI * sqr(m_filament_diameters[m_extruder_id]) * 0.25) * delta_pos[E] / ds, 4); - } + float d_xyz = std::sqrt(sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z])); + float filament_diameter = (static_cast(m_extruder_id) < m_filament_diameters.size()) ? m_filament_diameters[m_extruder_id] : m_filament_diameters.back(); + float filament_radius = 0.5f * filament_diameter; + float area_filament_cross_section = static_cast(M_PI) * sqr(filament_radius); + float volume_extruded_filament = area_filament_cross_section * delta_pos[E]; + float area_toolpath_cross_section = volume_extruded_filament / d_xyz; - if (m_end_position[Z] > m_extruded_last_z + EPSILON) { - m_height = round_to_nearest(m_end_position[Z] - m_extruded_last_z, 4); - m_extruded_last_z = m_end_position[Z]; - } + // volume extruded filament / tool displacement = area toolpath cross section + m_mm3_per_mm = round_to_nearest(area_toolpath_cross_section, 3); +#if ENABLE_GCODE_VIEWER_DATA_CHECKING + m_mm3_per_mm_compare.update(area_toolpath_cross_section, m_extrusion_role); +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING + + if (m_end_position[Z] > m_extruded_last_z + EPSILON) { + m_height = round_to_nearest(m_end_position[Z] - m_extruded_last_z, 4); +#if ENABLE_GCODE_VIEWER_DATA_CHECKING + m_height_compare.update(m_height, m_extrusion_role); +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING + m_extruded_last_z = m_end_position[Z]; } } @@ -1368,7 +1430,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) assert(distance != 0.0f); float inv_distance = 1.0f / distance; - for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { TimeMachine& machine = m_time_processor.machines[i]; if (!machine.enabled) continue; @@ -1378,8 +1440,8 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) std::vector& blocks = machine.blocks; curr.feedrate = (delta_pos[E] == 0.0f) ? - minimum_travel_feedrate(static_cast(i), m_feedrate) : - minimum_feedrate(static_cast(i), m_feedrate); + minimum_travel_feedrate(static_cast(i), m_feedrate) : + minimum_feedrate(static_cast(i), m_feedrate); TimeBlock block; block.move_type = type; @@ -1395,7 +1457,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) curr.abs_axis_feedrate[a] = std::abs(curr.axis_feedrate[a]); if (curr.abs_axis_feedrate[a] != 0.0f) { - float axis_max_feedrate = get_axis_max_feedrate(static_cast(i), static_cast(a)); + float axis_max_feedrate = get_axis_max_feedrate(static_cast(i), static_cast(a)); if (axis_max_feedrate != 0.0f) min_feedrate_factor = std::min(min_feedrate_factor, axis_max_feedrate / curr.abs_axis_feedrate[a]); } @@ -1412,11 +1474,11 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) // calculates block acceleration float acceleration = is_extrusion_only_move(delta_pos) ? - get_retract_acceleration(static_cast(i)) : - get_acceleration(static_cast(i)); + get_retract_acceleration(static_cast(i)) : + get_acceleration(static_cast(i)); for (unsigned char a = X; a <= E; ++a) { - float axis_max_acceleration = get_axis_max_acceleration(static_cast(i), static_cast(a)); + float axis_max_acceleration = get_axis_max_acceleration(static_cast(i), static_cast(a)); if (acceleration * std::abs(delta_pos[a]) * inv_distance > axis_max_acceleration) acceleration = axis_max_acceleration; } @@ -1427,7 +1489,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) curr.safe_feedrate = block.feedrate_profile.cruise; for (unsigned char a = X; a <= E; ++a) { - float axis_max_jerk = get_axis_max_jerk(static_cast(i), static_cast(a)); + float axis_max_jerk = get_axis_max_jerk(static_cast(i), static_cast(a)); if (curr.abs_axis_feedrate[a] > axis_max_jerk) curr.safe_feedrate = std::min(curr.safe_feedrate, axis_max_jerk); } @@ -1475,7 +1537,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) // axis reversal std::max(-v_exit, v_entry)); - float axis_max_jerk = get_axis_max_jerk(static_cast(i), static_cast(a)); + float axis_max_jerk = get_axis_max_jerk(static_cast(i), static_cast(a)); if (jerk > axis_max_jerk) { v_factor *= axis_max_jerk / jerk; limited = true; @@ -1689,7 +1751,7 @@ void GCodeProcessor::process_M201(const GCodeReader::GCodeLine& line) // see http://reprap.org/wiki/G-code#M201:_Set_max_printing_acceleration float factor = (m_flavor != gcfRepRap && m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f; - for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { if (line.has_x()) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_x, i, line.x() * factor); @@ -1717,7 +1779,7 @@ void GCodeProcessor::process_M203(const GCodeReader::GCodeLine& line) // http://smoothieware.org/supported-g-codes float factor = (m_flavor == gcfMarlin || m_flavor == gcfSmoothie) ? 1.0f : MMMIN_TO_MMSEC; - for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { if (line.has_x()) set_option_value(m_time_processor.machine_limits.machine_max_feedrate_x, i, line.x() * factor); @@ -1738,19 +1800,19 @@ void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line) return; float value; - for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { if (line.has_value('S', value)) { // Legacy acceleration format. This format is used by the legacy Marlin, MK2 or MK3 firmware, // and it is also generated by Slic3r to control acceleration per extrusion type // (there is a separate acceleration settings in Slicer for perimeter, first layer etc). - set_acceleration(static_cast(i), value); + set_acceleration(static_cast(i), value); if (line.has_value('T', value)) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value); } else { // New acceleration format, compatible with the upstream Marlin. if (line.has_value('P', value)) - set_acceleration(static_cast(i), value); + set_acceleration(static_cast(i), value); if (line.has_value('R', value)) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value); if (line.has_value('T', value)) { @@ -1767,7 +1829,7 @@ void GCodeProcessor::process_M205(const GCodeReader::GCodeLine& line) if (!m_time_processor.machine_envelope_processing_enabled) return; - for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { if (line.has_x()) { float max_jerk = line.x(); set_option_value(m_time_processor.machine_limits.machine_max_jerk_x, i, max_jerk); @@ -1798,7 +1860,7 @@ void GCodeProcessor::process_M221(const GCodeReader::GCodeLine& line) float value_t; if (line.has_value('S', value_s) && !line.has_value('T', value_t)) { value_s *= 0.01f; - for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { m_time_processor.machines[i].extrude_factor_override_percentage = value_s; } } @@ -1849,7 +1911,7 @@ void GCodeProcessor::process_M402(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_M566(const GCodeReader::GCodeLine& line) { - for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { if (line.has_x()) set_option_value(m_time_processor.machine_limits.machine_max_jerk_x, i, line.x() * MMMIN_TO_MMSEC); @@ -1930,7 +1992,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type) m_result.moves.emplace_back(vertex); } -float GCodeProcessor::minimum_feedrate(ETimeMode mode, float feedrate) const +float GCodeProcessor::minimum_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const { if (m_time_processor.machine_limits.machine_min_extruding_rate.empty()) return feedrate; @@ -1938,7 +2000,7 @@ float GCodeProcessor::minimum_feedrate(ETimeMode mode, float feedrate) const return std::max(feedrate, get_option_value(m_time_processor.machine_limits.machine_min_extruding_rate, static_cast(mode))); } -float GCodeProcessor::minimum_travel_feedrate(ETimeMode mode, float feedrate) const +float GCodeProcessor::minimum_travel_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const { if (m_time_processor.machine_limits.machine_min_travel_rate.empty()) return feedrate; @@ -1946,7 +2008,7 @@ float GCodeProcessor::minimum_travel_feedrate(ETimeMode mode, float feedrate) co return std::max(feedrate, get_option_value(m_time_processor.machine_limits.machine_min_travel_rate, static_cast(mode))); } -float GCodeProcessor::get_axis_max_feedrate(ETimeMode mode, Axis axis) const +float GCodeProcessor::get_axis_max_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const { switch (axis) { @@ -1958,7 +2020,7 @@ float GCodeProcessor::get_axis_max_feedrate(ETimeMode mode, Axis axis) const } } -float GCodeProcessor::get_axis_max_acceleration(ETimeMode mode, Axis axis) const +float GCodeProcessor::get_axis_max_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const { switch (axis) { @@ -1970,7 +2032,7 @@ float GCodeProcessor::get_axis_max_acceleration(ETimeMode mode, Axis axis) const } } -float GCodeProcessor::get_axis_max_jerk(ETimeMode mode, Axis axis) const +float GCodeProcessor::get_axis_max_jerk(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const { switch (axis) { @@ -1982,18 +2044,18 @@ float GCodeProcessor::get_axis_max_jerk(ETimeMode mode, Axis axis) const } } -float GCodeProcessor::get_retract_acceleration(ETimeMode mode) const +float GCodeProcessor::get_retract_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, static_cast(mode)); } -float GCodeProcessor::get_acceleration(ETimeMode mode) const +float GCodeProcessor::get_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const { size_t id = static_cast(mode); return (id < m_time_processor.machines.size()) ? m_time_processor.machines[id].acceleration : DEFAULT_ACCELERATION; } -void GCodeProcessor::set_acceleration(ETimeMode mode, float value) +void GCodeProcessor::set_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value) { size_t id = static_cast(mode); if (id < m_time_processor.machines.size()) { @@ -2021,7 +2083,7 @@ float GCodeProcessor::get_filament_unload_time(size_t extruder_id) void GCodeProcessor::process_custom_gcode_time(CustomGCode::Type code) { - for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { TimeMachine& machine = m_time_processor.machines[i]; if (!machine.enabled) continue; @@ -2040,14 +2102,14 @@ void GCodeProcessor::process_custom_gcode_time(CustomGCode::Type code) void GCodeProcessor::simulate_st_synchronize(float additional_time) { - for (size_t i = 0; i < static_cast(ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { m_time_processor.machines[i].simulate_st_synchronize(additional_time); } } void GCodeProcessor::update_estimated_times_stats() { - auto update_mode = [this](GCodeProcessor::ETimeMode mode) { + auto update_mode = [this](PrintEstimatedTimeStatistics::ETimeMode mode) { PrintEstimatedTimeStatistics::Mode& data = m_result.time_statistics.modes[static_cast(mode)]; data.time = get_time(mode); data.custom_gcode_times = get_custom_gcode_times(mode, true); @@ -2055,11 +2117,11 @@ void GCodeProcessor::update_estimated_times_stats() data.roles_times = get_roles_time(mode); }; - update_mode(GCodeProcessor::ETimeMode::Normal); - if (m_time_processor.machines[static_cast(GCodeProcessor::ETimeMode::Stealth)].enabled) - update_mode(GCodeProcessor::ETimeMode::Stealth); + update_mode(PrintEstimatedTimeStatistics::ETimeMode::Normal); + if (m_time_processor.machines[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].enabled) + update_mode(PrintEstimatedTimeStatistics::ETimeMode::Stealth); else - m_result.time_statistics.modes[static_cast(GCodeProcessor::ETimeMode::Stealth)].reset(); + m_result.time_statistics.modes[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].reset(); } } /* namespace Slic3r */ diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index f103bab967..166e1b8cca 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -76,6 +76,10 @@ namespace Slic3r { static const std::string Last_Line_M73_Placeholder_Tag; static const std::string Estimated_Printing_Time_Placeholder_Tag; +#if ENABLE_GCODE_VIEWER_DATA_CHECKING + static const std::string Mm3_Per_Mm_Tag; +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING + private: using AxisCoords = std::array; using ExtruderColors = std::vector; @@ -152,13 +156,6 @@ namespace Slic3r { float time() const; }; - enum class ETimeMode : unsigned char - { - Normal, - Stealth, - Count - }; - private: struct TimeMachine { @@ -227,7 +224,7 @@ namespace Slic3r { // Additional load / unload times for a filament exchange sequence. std::vector filament_load_times; std::vector filament_unload_times; - std::array(ETimeMode::Count)> machines; + std::array(PrintEstimatedTimeStatistics::ETimeMode::Count)> machines; void reset(); @@ -281,6 +278,72 @@ namespace Slic3r { #endif // ENABLE_GCODE_VIEWER_STATISTICS }; +#if ENABLE_GCODE_VIEWER_DATA_CHECKING + struct DataChecker + { + struct Error + { + float value; + float tag_value; + ExtrusionRole role; + }; + + std::string type; + float threshold{ 0.01f }; + float last_tag_value{ 0.0f }; + unsigned int count{ 0 }; + std::vector errors; + + DataChecker(const std::string& type, float threshold) + : type(type), threshold(threshold) + {} + + void update(float value, ExtrusionRole role) { + ++count; + if (last_tag_value != 0.0f) { + if (std::abs(value - last_tag_value) / last_tag_value > threshold) + errors.push_back({ value, last_tag_value, role }); + } + } + + void reset() { last_tag_value = 0.0f; errors.clear(); count = 0; } + + std::pair get_min() const { + float delta_min = FLT_MAX; + float perc_min = 0.0f; + for (const Error& e : errors) { + if (delta_min > e.value - e.tag_value) { + delta_min = e.value - e.tag_value; + perc_min = 100.0f * delta_min / e.tag_value; + } + } + return { delta_min, perc_min }; + } + + std::pair get_max() const { + float delta_max = -FLT_MAX; + float perc_max = 0.0f; + for (const Error& e : errors) { + if (delta_max < e.value - e.tag_value) { + delta_max = e.value - e.tag_value; + perc_max = 100.0f * delta_max / e.tag_value; + } + } + return { delta_max, perc_max }; + } + + void output() const { + if (!errors.empty()) { + std::cout << type << ":\n"; + std::cout << "Errors: " << errors.size() << " (" << 100.0f * float(errors.size()) / float(count) << "%)\n"; + auto [min, perc_min] = get_min(); + auto [max, perc_max] = get_max(); + std::cout << "min: " << min << "(" << perc_min << "%) - max: " << max << "(" << perc_max << "%)\n"; + } + } + }; +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING + private: GCodeReader m_parser; @@ -326,6 +389,12 @@ namespace Slic3r { Result m_result; static unsigned int s_result_id; +#if ENABLE_GCODE_VIEWER_DATA_CHECKING + DataChecker m_mm3_per_mm_compare{ "mm3_per_mm", 0.01f }; + DataChecker m_height_compare{ "height", 0.01f }; + DataChecker m_width_compare{ "width", 0.01f }; +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING + public: GCodeProcessor(); @@ -333,7 +402,7 @@ namespace Slic3r { void apply_config(const DynamicPrintConfig& config); void enable_stealth_time_estimator(bool enabled); bool is_stealth_time_estimator_enabled() const { - return m_time_processor.machines[static_cast(ETimeMode::Stealth)].enabled; + return m_time_processor.machines[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].enabled; } void enable_machine_envelope_processing(bool enabled) { m_time_processor.machine_envelope_processing_enabled = enabled; } void enable_producers(bool enabled) { m_producers_enabled = enabled; } @@ -345,12 +414,12 @@ namespace Slic3r { // Process the gcode contained in the file with the given filename void process_file(const std::string& filename); - float get_time(ETimeMode mode) const; - std::string get_time_dhm(ETimeMode mode) const; - std::vector>> get_custom_gcode_times(ETimeMode mode, bool include_remaining) const; + float get_time(PrintEstimatedTimeStatistics::ETimeMode mode) const; + std::string get_time_dhm(PrintEstimatedTimeStatistics::ETimeMode mode) const; + std::vector>> get_custom_gcode_times(PrintEstimatedTimeStatistics::ETimeMode mode, bool include_remaining) const; - std::vector> get_moves_time(ETimeMode mode) const; - std::vector> get_roles_time(ETimeMode mode) const; + std::vector> get_moves_time(PrintEstimatedTimeStatistics::ETimeMode mode) const; + std::vector> get_roles_time(PrintEstimatedTimeStatistics::ETimeMode mode) const; private: void process_gcode_line(const GCodeReader::GCodeLine& line); @@ -454,14 +523,14 @@ namespace Slic3r { void store_move_vertex(EMoveType type); - float minimum_feedrate(ETimeMode mode, float feedrate) const; - float minimum_travel_feedrate(ETimeMode mode, float feedrate) const; - float get_axis_max_feedrate(ETimeMode mode, Axis axis) const; - float get_axis_max_acceleration(ETimeMode mode, Axis axis) const; - float get_axis_max_jerk(ETimeMode mode, Axis axis) const; - float get_retract_acceleration(ETimeMode mode) const; - float get_acceleration(ETimeMode mode) const; - void set_acceleration(ETimeMode mode, float value); + float minimum_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const; + float minimum_travel_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const; + float get_axis_max_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const; + float get_axis_max_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const; + float get_axis_max_jerk(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const; + float get_retract_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const; + float get_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const; + void set_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value); float get_filament_load_time(size_t extruder_id); float get_filament_unload_time(size_t extruder_id); diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index c20009a487..e6e60b6186 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -84,6 +84,17 @@ public: return *this; } +#if ENABLE_GCODE_VIEWER_DATA_CHECKING + WipeTowerWriter& change_analyzer_mm3_per_mm(float len, float e) { + static const float area = float(M_PI) * 1.75f * 1.75f / 4.f; + float mm3_per_mm = (len == 0.f ? 0.f : area * e / len); + // adds tag for processor: + char buf[64]; + sprintf(buf, ";%s%f\n", GCodeProcessor::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm); + m_gcode += buf; + return *this; + } +#else #if !ENABLE_GCODE_VIEWER WipeTowerWriter& change_analyzer_mm3_per_mm(float len, float e) { static const float area = float(M_PI) * 1.75f * 1.75f / 4.f; @@ -95,6 +106,7 @@ public: return *this; } #endif // !ENABLE_GCODE_VIEWER +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING WipeTowerWriter& set_initial_position(const Vec2f &pos, float width = 0.f, float depth = 0.f, float internal_angle = 0.f) { m_wipe_tower_width = width; @@ -167,9 +179,13 @@ public: Vec2f rot(this->rotate(Vec2f(x,y))); // this is where we want to go if (! m_preview_suppressed && e > 0.f && len > 0.f) { +#if ENABLE_GCODE_VIEWER_DATA_CHECKING + change_analyzer_mm3_per_mm(len, e); +#else #if !ENABLE_GCODE_VIEWER change_analyzer_mm3_per_mm(len, e); #endif // !ENABLE_GCODE_VIEWER +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING // Width of a squished extrusion, corrected for the roundings of the squished extrusions. // This is left zero if it is a travel move. float width = e * m_filpar[0].filament_area / (len * m_layer_height); diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 609aecf635..e10cabc6cf 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -58,6 +58,7 @@ #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) #define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_SHADERS_EDITOR (0 && ENABLE_GCODE_VIEWER) +#define ENABLE_GCODE_VIEWER_DATA_CHECKING (0 && ENABLE_GCODE_VIEWER) #define TIME_ESTIMATE_NONE 0 #define TIME_ESTIMATE_DEFAULT 1 diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index b7bd81a0fa..17651fef37 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -975,8 +975,8 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) for (const TBuffer& buffer : m_buffers) { m_statistics.paths_size += SLIC3R_STDVEC_MEMSIZE(buffer.paths, Path); } - m_statistics.travel_segments_count = indices[buffer_id(GCodeProcessor::EMoveType::Travel)].size() / 2; - m_statistics.extrude_segments_count = indices[buffer_id(GCodeProcessor::EMoveType::Extrude)].size() / 2; + m_statistics.travel_segments_count = indices[buffer_id(EMoveType::Travel)].size() / 2; + m_statistics.extrude_segments_count = indices[buffer_id(EMoveType::Extrude)].size() / 2; #endif // ENABLE_GCODE_VIEWER_STATISTICS // layers zs / roles / extruder ids / cp color ids -> extract from result From 5b579aee9a4c97ccb0c0cd469750ecac3dcbb176 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 17 Aug 2020 10:54:41 +0200 Subject: [PATCH 301/503] GCodeProcessor -> Extract toolpaths width from gcode moves --- src/libslic3r/GCode/GCodeProcessor.cpp | 27 ++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 2e6736d1ec..a0661191cb 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -755,6 +755,7 @@ void GCodeProcessor::process_file(const std::string& filename) m_time_processor.post_process(filename); #if ENABLE_GCODE_VIEWER_DATA_CHECKING + std::cout << "\n"; m_mm3_per_mm_compare.output(); m_height_compare.output(); m_width_compare.output(); @@ -1403,18 +1404,24 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING m_extruded_last_z = m_end_position[Z]; } + + if (m_extrusion_role == erExternalPerimeter) + // cross section: rectangle + m_width = round_to_nearest(delta_pos[E] * static_cast(M_PI * sqr(1.05 * filament_radius)) / (d_xyz * m_height), 3); + else if (m_extrusion_role == erBridgeInfill || m_extrusion_role == erNone) + // cross section: circle + m_width = round_to_nearest(static_cast(m_filament_diameters[m_extruder_id]) * std::sqrt(delta_pos[E] / d_xyz), 3); + else + // cross section: rectangle + 2 semicircles + m_width = round_to_nearest(delta_pos[E] * static_cast(M_PI * sqr(filament_radius)) / (d_xyz * m_height) + static_cast(1.0 - 0.25 * M_PI) * m_height, 3); + +#if ENABLE_GCODE_VIEWER_DATA_CHECKING + m_width_compare.update(m_width, m_extrusion_role); +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING } - if (type == EMoveType::Extrude && (m_width == 0.0f || m_height == 0.0f)) { - if ((m_width == 0.0f && m_height == 0.0f) || m_extrusion_role == erCustom) - type = EMoveType::Travel; - else { - if (m_width == 0.0f) - m_width = 0.5f; - if (m_height == 0.0f) - m_height = 0.5f; - } - } + if (type == EMoveType::Extrude && (m_extrusion_role == erCustom || m_width == 0.0f || m_height == 0.0f)) + type = EMoveType::Travel; // time estimate section auto move_length = [](const AxisCoords& delta_pos) { From b1561534050a23acb323f355b6153e04a7a7e2fa Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 17 Aug 2020 13:07:13 +0200 Subject: [PATCH 302/503] GCodeViewer -> Use rounded values for toolpaths height, width and volumetric rate to reduce the number of generated paths --- src/libslic3r/GCode/GCodeProcessor.cpp | 50 ++++++++++---------------- src/slic3r/GUI/GCodeViewer.cpp | 30 ++++++++++++---- 2 files changed, 43 insertions(+), 37 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index a0661191cb..ab9a1039cd 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -85,19 +85,6 @@ static float acceleration_time_from_distance(float initial_feedrate, float dista return (acceleration != 0.0f) ? (speed_from_distance(initial_feedrate, distance, acceleration) - initial_feedrate) / acceleration : 0.0f; } -float round_to_nearest(float value, unsigned int decimals) -{ - float res = 0.0f; - if (decimals == 0) - res = std::round(value); - else { - char buf[64]; - sprintf(buf, "%.*g", decimals, value); - res = std::stof(buf); - } - return res; -} - void GCodeProcessor::CachedPosition::reset() { std::fill(position.begin(), position.end(), FLT_MAX); @@ -1392,13 +1379,13 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) float area_toolpath_cross_section = volume_extruded_filament / d_xyz; // volume extruded filament / tool displacement = area toolpath cross section - m_mm3_per_mm = round_to_nearest(area_toolpath_cross_section, 3); + m_mm3_per_mm = area_toolpath_cross_section; #if ENABLE_GCODE_VIEWER_DATA_CHECKING m_mm3_per_mm_compare.update(area_toolpath_cross_section, m_extrusion_role); #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING if (m_end_position[Z] > m_extruded_last_z + EPSILON) { - m_height = round_to_nearest(m_end_position[Z] - m_extruded_last_z, 4); + m_height = m_end_position[Z] - m_extruded_last_z; #if ENABLE_GCODE_VIEWER_DATA_CHECKING m_height_compare.update(m_height, m_extrusion_role); #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING @@ -1407,13 +1394,13 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) if (m_extrusion_role == erExternalPerimeter) // cross section: rectangle - m_width = round_to_nearest(delta_pos[E] * static_cast(M_PI * sqr(1.05 * filament_radius)) / (d_xyz * m_height), 3); + m_width = delta_pos[E] * static_cast(M_PI * sqr(1.05 * filament_radius)) / (d_xyz * m_height); else if (m_extrusion_role == erBridgeInfill || m_extrusion_role == erNone) // cross section: circle - m_width = round_to_nearest(static_cast(m_filament_diameters[m_extruder_id]) * std::sqrt(delta_pos[E] / d_xyz), 3); + m_width = static_cast(m_filament_diameters[m_extruder_id]) * std::sqrt(delta_pos[E] / d_xyz); else // cross section: rectangle + 2 semicircles - m_width = round_to_nearest(delta_pos[E] * static_cast(M_PI * sqr(filament_radius)) / (d_xyz * m_height) + static_cast(1.0 - 0.25 * M_PI) * m_height, 3); + m_width = delta_pos[E] * static_cast(M_PI * sqr(filament_radius)) / (d_xyz * m_height) + static_cast(1.0 - 0.25 * M_PI) * m_height; #if ENABLE_GCODE_VIEWER_DATA_CHECKING m_width_compare.update(m_width, m_extrusion_role); @@ -1983,19 +1970,20 @@ void GCodeProcessor::process_T(const std::string& command) void GCodeProcessor::store_move_vertex(EMoveType type) { - MoveVertex vertex; - vertex.type = type; - vertex.extrusion_role = m_extrusion_role; - vertex.position = Vec3f(m_end_position[X], m_end_position[Y], m_end_position[Z]) + m_extruder_offsets[m_extruder_id]; - vertex.delta_extruder = m_end_position[E] - m_start_position[E]; - vertex.feedrate = m_feedrate; - vertex.width = m_width; - vertex.height = m_height; - vertex.mm3_per_mm = m_mm3_per_mm; - vertex.fan_speed = m_fan_speed; - vertex.extruder_id = m_extruder_id; - vertex.cp_color_id = m_cp_color.current; - vertex.time = static_cast(m_result.moves.size()); + MoveVertex vertex = { + type, + m_extrusion_role, + m_extruder_id, + m_cp_color.current, + Vec3f(m_end_position[X], m_end_position[Y], m_end_position[Z]) + m_extruder_offsets[m_extruder_id], + m_end_position[E] - m_start_position[E], + m_feedrate, + m_width, + m_height, + m_mm3_per_mm, + m_fan_speed, + static_cast(m_result.moves.size()) + }; m_result.moves.emplace_back(vertex); } diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 17651fef37..4cb3ae1c52 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -38,7 +38,7 @@ static EMoveType buffer_type(unsigned char id) { return static_cast(static_cast(EMoveType::Retract) + id); } -std::array decode_color(const std::string& color) { +static std::array decode_color(const std::string& color) { static const float INV_255 = 1.0f / 255.0f; std::array ret = { 0.0f, 0.0f, 0.0f }; @@ -56,7 +56,7 @@ std::array decode_color(const std::string& color) { return ret; } -std::vector> decode_colors(const std::vector& colors) { +static std::vector> decode_colors(const std::vector& colors) { std::vector> output(colors.size(), { 0.0f, 0.0f, 0.0f }); for (size_t i = 0; i < colors.size(); ++i) { output[i] = decode_color(colors[i]); @@ -64,6 +64,19 @@ std::vector> decode_colors(const std::vector& return output; } +static float round_to_nearest(float value, unsigned int decimals) +{ + float res = 0.0f; + if (decimals == 0) + res = std::round(value); + else { + char buf[64]; + sprintf(buf, "%.*g", decimals, value); + res = std::stof(buf); + } + return res; +} + void GCodeViewer::VBuffer::reset() { // release gpu memory @@ -98,9 +111,11 @@ bool GCodeViewer::Path::matches(const GCodeProcessor::MoveVertex& move) const case EMoveType::Unretract: case EMoveType::Extrude: { - return type == move.type && role == move.extrusion_role && height == move.height && width == move.width && - feedrate == move.feedrate && fan_speed == move.fan_speed && volumetric_rate == move.volumetric_rate() && - extruder_id == move.extruder_id && cp_color_id == move.cp_color_id; + // use rounding to reduce the number of generated paths + return type == move.type && role == move.extrusion_role && height == round_to_nearest(move.height, 2) && + width == round_to_nearest(move.width, 2) && feedrate == move.feedrate && fan_speed == move.fan_speed && + volumetric_rate == round_to_nearest(move.volumetric_rate(), 2) && extruder_id == move.extruder_id && + cp_color_id == move.cp_color_id; } case EMoveType::Travel: { @@ -124,7 +139,10 @@ void GCodeViewer::TBuffer::reset() void GCodeViewer::TBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsigned int i_id, unsigned int s_id) { Path::Endpoint endpoint = { i_id, s_id, move.position }; - paths.push_back({ move.type, move.extrusion_role, endpoint, endpoint, move.delta_extruder, move.height, move.width, move.feedrate, move.fan_speed, move.volumetric_rate(), move.extruder_id, move.cp_color_id }); + // use rounding to reduce the number of generated paths + paths.push_back({ move.type, move.extrusion_role, endpoint, endpoint, move.delta_extruder, + round_to_nearest(move.height, 2), round_to_nearest(move.width, 2), move.feedrate, move.fan_speed, + round_to_nearest(move.volumetric_rate(), 2), move.extruder_id, move.cp_color_id }); } GCodeViewer::Color GCodeViewer::Extrusions::Range::get_color_at(float value) const From 73603e49371f12e73a0a93b54ca88f11b1bdbbd5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 17 Aug 2020 14:37:26 +0200 Subject: [PATCH 303/503] GCodeProcessor -> Do not export width tags to gcode --- src/libslic3r/GCode.cpp | 43 +++++++-- src/libslic3r/GCode.hpp | 10 ++- src/libslic3r/GCode/GCodeProcessor.cpp | 115 +++++++++++++++++++------ src/libslic3r/GCode/GCodeProcessor.hpp | 7 +- src/libslic3r/GCode/WipeTower.cpp | 89 ++++++++++++++++--- 5 files changed, 216 insertions(+), 48 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 24c758d502..63f215b76d 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1183,11 +1183,16 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu // resets analyzer's tracking data #if ENABLE_GCODE_VIEWER - m_last_width = 0.0f; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// m_last_width = 0.0f; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_last_height = 0.0f; m_last_layer_z = 0.0f; #if ENABLE_GCODE_VIEWER_DATA_CHECKING m_last_mm3_per_mm = 0.0; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + m_last_width = 0.0f; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING #else m_last_mm3_per_mm = GCodeAnalyzer::Default_mm3_per_mm; @@ -3255,8 +3260,12 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, if (m_enable_analyzer) { #endif // !ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER - // PrusaMultiMaterial::Writer may generate GCodeProcessor::Height_Tag and GCodeProcessor::Width_Tag lines without updating m_last_height and m_last_width - // so, if the last role was erWipeTower we force export of GCodeProcessor::Height_Tag and GCodeProcessor::Width_Tag lines +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + // PrusaMultiMaterial::Writer may generate GCodeProcessor::Height_Tag lines without updating m_last_height + // so, if the last role was erWipeTower we force export of GCodeProcessor::Height_Tag lines +// // PrusaMultiMaterial::Writer may generate GCodeProcessor::Height_Tag and GCodeProcessor::Width_Tag lines without updating m_last_height and m_last_width +// // so, if the last role was erWipeTower we force export of GCodeProcessor::Height_Tag and GCodeProcessor::Width_Tag lines +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool last_was_wipe_tower = (m_last_processor_extrusion_role == erWipeTower); #else // PrusaMultiMaterial::Writer may generate GCodeAnalyzer::Height_Tag and GCodeAnalyzer::Width_Tag lines without updating m_last_height and m_last_width @@ -3278,6 +3287,14 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, sprintf(buf, ";%s%f\n", GCodeProcessor::Mm3_Per_Mm_Tag.c_str(), m_last_mm3_per_mm); gcode += buf; } + +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + if (last_was_wipe_tower || m_last_width != path.width) { + m_last_width = path.width; + sprintf(buf, ";%s%g\n", GCodeProcessor::Width_Tag.c_str(), m_last_width); + gcode += buf; + } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING #else if (path.role() != m_last_analyzer_extrusion_role) { @@ -3291,17 +3308,27 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), m_last_mm3_per_mm); gcode += buf; } -#endif // ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (last_was_wipe_tower || m_last_width != path.width) { m_last_width = path.width; -#if ENABLE_GCODE_VIEWER - sprintf(buf, ";%s%g\n", GCodeProcessor::Width_Tag.c_str(), m_last_width); -#else sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), m_last_width); -#endif // ENABLE_GCODE_VIEWER gcode += buf; } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_GCODE_VIEWER + +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// if (last_was_wipe_tower || m_last_width != path.width) { +// m_last_width = path.width; +//#if ENABLE_GCODE_VIEWER +// sprintf(buf, ";%s%g\n", GCodeProcessor::Width_Tag.c_str(), m_last_width); +//#else +// sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), m_last_width); +//#endif // ENABLE_GCODE_VIEWER +// gcode += buf; +// } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 76f897c63b..3c8cf64b74 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -174,6 +174,9 @@ public: m_last_extrusion_role(erNone), #if ENABLE_GCODE_VIEWER_DATA_CHECKING m_last_mm3_per_mm(0.0), +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + m_last_width(0.0f), +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING #if !ENABLE_GCODE_VIEWER m_last_mm3_per_mm(GCodeAnalyzer::Default_mm3_per_mm), @@ -381,11 +384,16 @@ private: ExtrusionRole m_last_extrusion_role; #if ENABLE_GCODE_VIEWER // Support for G-Code Processor - float m_last_width{ 0.0f }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// float m_last_width{ 0.0f }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ float m_last_height{ 0.0f }; float m_last_layer_z{ 0.0f }; #if ENABLE_GCODE_VIEWER_DATA_CHECKING double m_last_mm3_per_mm; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + float m_last_width{ 0.0f }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING #else // Support for G-Code Analyzer diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index ab9a1039cd..39b9539c0c 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -23,7 +23,9 @@ static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2 namespace Slic3r { const std::string GCodeProcessor::Extrusion_Role_Tag = "ExtrType:"; -const std::string GCodeProcessor::Width_Tag = "Width:"; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//const std::string GCodeProcessor::Width_Tag = "Width:"; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ const std::string GCodeProcessor::Height_Tag = "Height:"; const std::string GCodeProcessor::Color_Change_Tag = "Color change"; const std::string GCodeProcessor::Pause_Print_Tag = "Pause print"; @@ -34,6 +36,9 @@ const std::string GCodeProcessor::Last_Line_M73_Placeholder_Tag = "; _ const std::string GCodeProcessor::Estimated_Printing_Time_Placeholder_Tag = "; _GP_ESTIMATED_PRINTING_TIME_PLACEHOLDER"; #if ENABLE_GCODE_VIEWER_DATA_CHECKING +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +const std::string GCodeProcessor::Width_Tag = "Width:"; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ const std::string GCodeProcessor::Mm3_Per_Mm_Tag = "Mm3_Per_Mm:"; #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING @@ -893,35 +898,67 @@ void GCodeProcessor::process_tags(const std::string& comment) return; } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_GCODE_VIEWER_DATA_CHECKING // width tag pos = comment.find(Width_Tag); if (pos != comment.npos) { try { - m_width = std::stof(comment.substr(pos + Width_Tag.length())); -#if ENABLE_GCODE_VIEWER_DATA_CHECKING - m_width_compare.last_tag_value = m_width; -#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING + m_width_compare.last_tag_value = std::stof(comment.substr(pos + Width_Tag.length())); } catch (...) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Width (" << comment << ")."; } return; } +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING +// // width tag +// pos = comment.find(Width_Tag); +// if (pos != comment.npos) { +// try { +// m_width = std::stof(comment.substr(pos + Width_Tag.length())); +//#if ENABLE_GCODE_VIEWER_DATA_CHECKING +// m_width_compare.last_tag_value = m_width; +//#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING +// } +// catch (...) { +// BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Width (" << comment << ")."; +// } +// return; +// } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_GCODE_VIEWER_DATA_CHECKING // height tag pos = comment.find(Height_Tag); if (pos != comment.npos) { try { - m_height = std::stof(comment.substr(pos + Height_Tag.length())); -#if ENABLE_GCODE_VIEWER_DATA_CHECKING - m_height_compare.last_tag_value = m_height; -#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING + m_height_compare.last_tag_value = std::stof(comment.substr(pos + Height_Tag.length())); } catch (...) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Height (" << comment << ")."; } return; } +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING + +// // height tag +// pos = comment.find(Height_Tag); +// if (pos != comment.npos) { +// try { +// m_height = std::stof(comment.substr(pos + Height_Tag.length())); +//#if ENABLE_GCODE_VIEWER_DATA_CHECKING +// m_height_compare.last_tag_value = m_height; +//#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING +// } +// catch (...) { +// BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Height (" << comment << ")."; +// } +// return; +// } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // color change tag pos = comment.find(Color_Change_Tag); @@ -1144,6 +1181,9 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string& comment) // geometry // ; tool +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_GCODE_VIEWER_DATA_CHECKING +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ std::string tag = " tool"; pos = comment.find(tag); if (pos == 0) { @@ -1156,10 +1196,14 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string& comment) size_t w_end = data.find_first_of(' ', w_start); if (h_start != data.npos) { try { - m_height = std::stof(data.substr(h_start + 1, (h_end != data.npos) ? h_end - h_start - 1 : h_end)); -#if ENABLE_GCODE_VIEWER_DATA_CHECKING - m_height_compare.last_tag_value = m_height; -#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + m_height_compare.last_tag_value = std::stof(data.substr(h_start + 1, (h_end != data.npos) ? h_end - h_start - 1 : h_end)); + +// m_height = std::stof(data.substr(h_start + 1, (h_end != data.npos) ? h_end - h_start - 1 : h_end)); +//#if ENABLE_GCODE_VIEWER_DATA_CHECKING +// m_height_compare.last_tag_value = m_height; +//#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } catch (...) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Height (" << comment << ")."; @@ -1167,10 +1211,14 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string& comment) } if (w_start != data.npos) { try { - m_width = std::stof(data.substr(w_start + 1, (w_end != data.npos) ? w_end - w_start - 1 : w_end)); -#if ENABLE_GCODE_VIEWER_DATA_CHECKING - m_width_compare.last_tag_value = m_width; -#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + m_width_compare.last_tag_value = std::stof(data.substr(w_start + 1, (w_end != data.npos) ? w_end - w_start - 1 : w_end)); + +// m_width = std::stof(data.substr(w_start + 1, (w_end != data.npos) ? w_end - w_start - 1 : w_end)); +//#if ENABLE_GCODE_VIEWER_DATA_CHECKING +// m_width_compare.last_tag_value = m_width; +//#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } catch (...) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Width (" << comment << ")."; @@ -1179,6 +1227,9 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string& comment) return true; } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // std::cout << comment << "\n"; return false; @@ -1254,15 +1305,22 @@ bool GCodeProcessor::process_ideamaker_tags(const std::string& comment) // geometry +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_GCODE_VIEWER_DATA_CHECKING +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // width tag = "WIDTH:"; pos = comment.find(tag); if (pos != comment.npos) { try { - m_width = std::stof(comment.substr(pos + tag.length())); -#if ENABLE_GCODE_VIEWER_DATA_CHECKING - m_width_compare.last_tag_value = m_width; -#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + m_width_compare.last_tag_value = std::stof(comment.substr(pos + tag.length())); + +// m_width = std::stof(comment.substr(pos + tag.length())); +//#if ENABLE_GCODE_VIEWER_DATA_CHECKING +// m_width_compare.last_tag_value = m_width; +//#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } catch (...) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Width (" << comment << ")."; @@ -1275,16 +1333,23 @@ bool GCodeProcessor::process_ideamaker_tags(const std::string& comment) pos = comment.find(tag); if (pos != comment.npos) { try { - m_height = std::stof(comment.substr(pos + tag.length())); -#if ENABLE_GCODE_VIEWER_DATA_CHECKING - m_height_compare.last_tag_value = m_height; -#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + m_height_compare.last_tag_value = std::stof(comment.substr(pos + tag.length())); + +// m_height = std::stof(comment.substr(pos + tag.length())); +//#if ENABLE_GCODE_VIEWER_DATA_CHECKING +// m_height_compare.last_tag_value = m_height; +//#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } catch (...) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Height (" << comment << ")."; } return true; } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ return false; } diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 166e1b8cca..52ae76f7ee 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -67,7 +67,9 @@ namespace Slic3r { { public: static const std::string Extrusion_Role_Tag; - static const std::string Width_Tag; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// static const std::string Width_Tag; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ static const std::string Height_Tag; static const std::string Color_Change_Tag; static const std::string Pause_Print_Tag; @@ -77,6 +79,9 @@ namespace Slic3r { static const std::string Estimated_Printing_Time_Placeholder_Tag; #if ENABLE_GCODE_VIEWER_DATA_CHECKING +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + static const std::string Width_Tag; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ static const std::string Mm3_Per_Mm_Tag; #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index e6e60b6186..3eaf31e1cd 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -51,7 +51,13 @@ public: m_extrusion_flow(0.f), m_preview_suppressed(false), m_elapsed_time(0.f), +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_GCODE_VIEWER_DATA_CHECKING +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_default_analyzer_line_width(line_width), +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_GCODE_VIEWER_DATA_CHECKING +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_gcode_flavor(flavor), m_filpar(filament_parameters) { @@ -69,20 +75,48 @@ public: sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erWipeTower); #endif // ENABLE_GCODE_VIEWER m_gcode += buf; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_GCODE_VIEWER_DATA_CHECKING +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ change_analyzer_line_width(line_width); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } - WipeTowerWriter& change_analyzer_line_width(float line_width) { - // adds tag for analyzer: - char buf[64]; -#if ENABLE_GCODE_VIEWER - sprintf(buf, ";%s%f\n", GCodeProcessor::Width_Tag.c_str(), line_width); -#else - sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), line_width); -#endif // ENABLE_GCODE_VIEWER - m_gcode += buf; - return *this; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_GCODE_VIEWER_DATA_CHECKING + WipeTowerWriter& change_analyzer_line_width(float line_width) { + // adds tag for analyzer: + char buf[64]; + sprintf(buf, ";%s%f\n", GCodeProcessor::Width_Tag.c_str(), line_width); + m_gcode += buf; + return *this; } +#else +#if !ENABLE_GCODE_VIEWER + WipeTowerWriter& change_analyzer_line_width(float line_width) { + // adds tag for analyzer: + char buf[64]; + sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), line_width); + m_gcode += buf; + return *this; +} +#endif // !ENABLE_GCODE_VIEWER +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING + +// WipeTowerWriter& change_analyzer_line_width(float line_width) { +// // adds tag for analyzer: +// char buf[64]; +//#if ENABLE_GCODE_VIEWER +// sprintf(buf, ";%s%f\n", GCodeProcessor::Width_Tag.c_str(), line_width); +//#else +// sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), line_width); +//#endif // ENABLE_GCODE_VIEWER +// m_gcode += buf; +// return *this; +// } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_DATA_CHECKING WipeTowerWriter& change_analyzer_mm3_per_mm(float len, float e) { @@ -141,8 +175,17 @@ public: // Suppress / resume G-code preview in Slic3r. Slic3r will have difficulty to differentiate the various // filament loading and cooling moves from normal extrusion moves. Therefore the writer // is asked to suppres output of some lines, which look like extrusions. - WipeTowerWriter& suppress_preview() { change_analyzer_line_width(0.f); m_preview_suppressed = true; return *this; } - WipeTowerWriter& resume_preview() { change_analyzer_line_width(m_default_analyzer_line_width); m_preview_suppressed = false; return *this; } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_GCODE_VIEWER_DATA_CHECKING + WipeTowerWriter& suppress_preview() { change_analyzer_line_width(0.f); m_preview_suppressed = true; return *this; } + WipeTowerWriter& resume_preview() { change_analyzer_line_width(m_default_analyzer_line_width); m_preview_suppressed = false; return *this; } +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + WipeTowerWriter& suppress_preview() { m_preview_suppressed = true; return *this; } + WipeTowerWriter& resume_preview() { m_preview_suppressed = false; return *this; } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ WipeTowerWriter& feedrate(float f) { @@ -447,7 +490,13 @@ private: float m_wipe_tower_depth = 0.f; unsigned m_last_fan_speed = 0; int current_temp = -1; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if !ENABLE_GCODE_VIEWER_DATA_CHECKING +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ const float m_default_analyzer_line_width; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // !ENABLE_GCODE_VIEWER_DATA_CHECKING +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ float m_used_filament_length = 0.f; GCodeFlavor m_gcode_flavor; const std::vector& m_filpar; @@ -888,8 +937,16 @@ void WipeTower::toolchange_Unload( const float line_width = m_perimeter_width * m_filpar[m_current_tool].ramming_line_width_multiplicator; // desired ramming line thickness const float y_step = line_width * m_filpar[m_current_tool].ramming_step_multiplicator * m_extra_spacing; // spacing between lines in mm +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_GCODE_VIEWER_DATA_CHECKING writer.append("; CP TOOLCHANGE UNLOAD\n") - .change_analyzer_line_width(line_width); + .change_analyzer_line_width(line_width); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + writer.append("; CP TOOLCHANGE UNLOAD\n"); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ unsigned i = 0; // iterates through ramming_speed m_left_to_right = true; // current direction of ramming @@ -966,7 +1023,13 @@ void WipeTower::toolchange_Unload( } } Vec2f end_of_ramming(writer.x(),writer.y()); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_GCODE_VIEWER_DATA_CHECKING +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ writer.change_analyzer_line_width(m_perimeter_width); // so the next lines are not affected by ramming_line_width_multiplier +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // Retraction: float old_x = writer.x(); From e050fb68bf3e486f46a41d0e6816c912300cac08 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 17 Aug 2020 15:13:18 +0200 Subject: [PATCH 304/503] Fixed UI changing update for "Ramming" parameter --- src/slic3r/GUI/OptionsGroup.cpp | 7 ++++++- src/slic3r/GUI/Tab.cpp | 33 ++++++++++++++++++++------------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 1bebb8827a..cc10d815fc 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -466,7 +466,8 @@ void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config, } else if (m_opt_map.find(opt_key) == m_opt_map.end() || // This option don't have corresponded field - opt_key == "bed_shape" || opt_key == "compatible_printers" || opt_key == "compatible_prints" ) { + opt_key == "bed_shape" || opt_key == "filament_ramming_parameters" || + opt_key == "compatible_printers" || opt_key == "compatible_prints" ) { value = get_config_value(config, opt_key); change_opt_value(*m_config, opt_key, value); return; @@ -699,6 +700,10 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config ret = config.option(opt_key)->values; break; } + if (opt_key == "filament_ramming_parameters") { + ret = config.opt_string(opt_key, static_cast(idx)); + break; + } if (config.option(opt_key)->values.empty()) ret = text_value; else if (opt->gui_flags.compare("serialized") == 0) { diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 1363ddcad7..a16222c86b 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -413,8 +413,9 @@ void Tab::update_labels_colour() else color = &m_modified_label_clr; } - if (opt.first == "bed_shape" || opt.first == "compatible_prints" || opt.first == "compatible_printers") { - wxStaticText* label = (m_colored_Labels.find(opt.first) == m_colored_Labels.end()) ? nullptr : m_colored_Labels.at(opt.first); + if (opt.first == "bed_shape" || opt.first == "filament_ramming_parameters" || + opt.first == "compatible_prints" || opt.first == "compatible_printers" ) { + wxStaticText* label = m_colored_Labels.find(opt.first) == m_colored_Labels.end() ? nullptr : m_colored_Labels.at(opt.first); if (label) { label->SetForegroundColour(*color); label->Refresh(true); @@ -504,7 +505,8 @@ void Tab::update_changed_ui() icon = &m_bmp_white_bullet; tt = &m_tt_white_bullet; } - if (opt.first == "bed_shape" || opt.first == "compatible_prints" || opt.first == "compatible_printers") { + if (opt.first == "bed_shape" || opt.first == "filament_ramming_parameters" || + opt.first == "compatible_prints" || opt.first == "compatible_printers") { wxStaticText* label = (m_colored_Labels.find(opt.first) == m_colored_Labels.end()) ? nullptr : m_colored_Labels.at(opt.first); if (label) { label->SetForegroundColour(*color); @@ -656,6 +658,9 @@ void Tab::update_changed_tree_ui() get_sys_and_mod_flags(opt_key, sys_page, modified_page); } } + if (m_type == Preset::TYPE_FILAMENT && page->title() == "Advanced") { + get_sys_and_mod_flags("filament_ramming_parameters", sys_page, modified_page); + } if (page->title() == "Dependencies") { if (m_type == Slic3r::Preset::TYPE_PRINTER) { sys_page = m_presets->get_selected_preset_parent() != nullptr; @@ -734,7 +739,10 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/) to_sys ? group->back_to_sys_value("bed_shape") : group->back_to_initial_value("bed_shape"); load_key_value("bed_shape", true/*some value*/, true); } - + } + if (group->title == "Toolchange parameters with single extruder MM printers") { + if ((m_options_list["filament_ramming_parameters"] & os) == 0) + to_sys ? group->back_to_sys_value("filament_ramming_parameters") : group->back_to_initial_value("filament_ramming_parameters"); } if (group->title == "Profile dependencies") { // "compatible_printers" option doesn't exists in Printer Settimgs Tab @@ -1737,22 +1745,21 @@ void TabFilament::build() optgroup->append_single_option_line("filament_cooling_initial_speed"); optgroup->append_single_option_line("filament_cooling_final_speed"); - line = optgroup->create_single_option_line("filament_ramming_parameters");// { _(L("Ramming")), "" }; - line.widget = [this](wxWindow* parent) { + create_line_with_widget(optgroup.get(), "filament_ramming_parameters", [this](wxWindow* parent) { auto ramming_dialog_btn = new wxButton(parent, wxID_ANY, _(L("Ramming settings"))+dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); ramming_dialog_btn->SetFont(Slic3r::GUI::wxGetApp().normal_font()); auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(ramming_dialog_btn); - ramming_dialog_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) - { + ramming_dialog_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) { RammingDialog dlg(this,(m_config->option("filament_ramming_parameters"))->get_at(0)); - if (dlg.ShowModal() == wxID_OK) - (m_config->option("filament_ramming_parameters"))->get_at(0) = dlg.get_parameters(); - })); + if (dlg.ShowModal() == wxID_OK) { + load_key_value("filament_ramming_parameters", dlg.get_parameters()); + update_changed_ui(); + } + }); return sizer; - }; - optgroup->append_line(line); + }); add_filament_overrides_page(); From c81d87b470f5550938202a222d79c01f1aba500b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 17 Aug 2020 15:59:36 +0200 Subject: [PATCH 305/503] Code cleanup --- src/libslic3r/GCode.cpp | 50 +++----------- src/libslic3r/GCode.hpp | 7 -- src/libslic3r/GCode/GCodeProcessor.cpp | 96 +++----------------------- src/libslic3r/GCode/GCodeProcessor.hpp | 5 -- src/libslic3r/GCode/WipeTower.cpp | 67 ++++-------------- src/slic3r/GUI/GCodeViewer.cpp | 6 +- 6 files changed, 34 insertions(+), 197 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 63f215b76d..3de70d0611 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1183,16 +1183,11 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu // resets analyzer's tracking data #if ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -// m_last_width = 0.0f; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_last_height = 0.0f; m_last_layer_z = 0.0f; #if ENABLE_GCODE_VIEWER_DATA_CHECKING m_last_mm3_per_mm = 0.0; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_last_width = 0.0f; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING #else m_last_mm3_per_mm = GCodeAnalyzer::Default_mm3_per_mm; @@ -3256,18 +3251,12 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } // adds processor tags and updates processor tracking data -#if !ENABLE_GCODE_VIEWER - if (m_enable_analyzer) { -#endif // !ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - // PrusaMultiMaterial::Writer may generate GCodeProcessor::Height_Tag lines without updating m_last_height - // so, if the last role was erWipeTower we force export of GCodeProcessor::Height_Tag lines -// // PrusaMultiMaterial::Writer may generate GCodeProcessor::Height_Tag and GCodeProcessor::Width_Tag lines without updating m_last_height and m_last_width -// // so, if the last role was erWipeTower we force export of GCodeProcessor::Height_Tag and GCodeProcessor::Width_Tag lines -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - bool last_was_wipe_tower = (m_last_processor_extrusion_role == erWipeTower); + // PrusaMultiMaterial::Writer may generate GCodeProcessor::Height_Tag lines without updating m_last_height + // so, if the last role was erWipeTower we force export of GCodeProcessor::Height_Tag lines + bool last_was_wipe_tower = (m_last_processor_extrusion_role == erWipeTower); #else + if (m_enable_analyzer) { // PrusaMultiMaterial::Writer may generate GCodeAnalyzer::Height_Tag and GCodeAnalyzer::Width_Tag lines without updating m_last_height and m_last_width // so, if the last role was erWipeTower we force export of GCodeAnalyzer::Height_Tag and GCodeAnalyzer::Width_Tag lines bool last_was_wipe_tower = (m_last_analyzer_extrusion_role == erWipeTower); @@ -3288,14 +3277,18 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, gcode += buf; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (last_was_wipe_tower || m_last_width != path.width) { m_last_width = path.width; sprintf(buf, ";%s%g\n", GCodeProcessor::Width_Tag.c_str(), m_last_width); gcode += buf; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING + + if (last_was_wipe_tower || std::abs(m_last_height - path.height) > EPSILON) { + m_last_height = path.height; + sprintf(buf, ";%s%g\n", GCodeProcessor::Height_Tag.c_str(), m_last_height); + gcode += buf; + } #else if (path.role() != m_last_analyzer_extrusion_role) { m_last_analyzer_extrusion_role = path.role(); @@ -3309,35 +3302,12 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, gcode += buf; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (last_was_wipe_tower || m_last_width != path.width) { m_last_width = path.width; sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), m_last_width); gcode += buf; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -// if (last_was_wipe_tower || m_last_width != path.width) { -// m_last_width = path.width; -//#if ENABLE_GCODE_VIEWER -// sprintf(buf, ";%s%g\n", GCodeProcessor::Width_Tag.c_str(), m_last_width); -//#else -// sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), m_last_width); -//#endif // ENABLE_GCODE_VIEWER -// gcode += buf; -// } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - - -#if ENABLE_GCODE_VIEWER - if (last_was_wipe_tower || std::abs(m_last_height - path.height) > EPSILON) { - m_last_height = path.height; - sprintf(buf, ";%s%g\n", GCodeProcessor::Height_Tag.c_str(), m_last_height); - gcode += buf; - } -#else if (last_was_wipe_tower || m_last_height != path.height) { m_last_height = path.height; sprintf(buf, ";%s%f\n", GCodeAnalyzer::Height_Tag.c_str(), m_last_height); diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 3c8cf64b74..8bae2ef43d 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -174,9 +174,7 @@ public: m_last_extrusion_role(erNone), #if ENABLE_GCODE_VIEWER_DATA_CHECKING m_last_mm3_per_mm(0.0), -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_last_width(0.0f), -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING #if !ENABLE_GCODE_VIEWER m_last_mm3_per_mm(GCodeAnalyzer::Default_mm3_per_mm), @@ -384,16 +382,11 @@ private: ExtrusionRole m_last_extrusion_role; #if ENABLE_GCODE_VIEWER // Support for G-Code Processor -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -// float m_last_width{ 0.0f }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ float m_last_height{ 0.0f }; float m_last_layer_z{ 0.0f }; #if ENABLE_GCODE_VIEWER_DATA_CHECKING double m_last_mm3_per_mm; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ float m_last_width{ 0.0f }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING #else // Support for G-Code Analyzer diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 39b9539c0c..6684255918 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -22,24 +22,19 @@ static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2 namespace Slic3r { -const std::string GCodeProcessor::Extrusion_Role_Tag = "ExtrType:"; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -//const std::string GCodeProcessor::Width_Tag = "Width:"; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -const std::string GCodeProcessor::Height_Tag = "Height:"; -const std::string GCodeProcessor::Color_Change_Tag = "Color change"; -const std::string GCodeProcessor::Pause_Print_Tag = "Pause print"; -const std::string GCodeProcessor::Custom_Code_Tag = "Custom gcode"; +const std::string GCodeProcessor::Extrusion_Role_Tag = "TYPE:"; +const std::string GCodeProcessor::Height_Tag = "HEIGHT:"; +const std::string GCodeProcessor::Color_Change_Tag = "COLOR CHANGE"; +const std::string GCodeProcessor::Pause_Print_Tag = "PAUSE PRINT"; +const std::string GCodeProcessor::Custom_Code_Tag = "CUSTOM GCODE"; const std::string GCodeProcessor::First_Line_M73_Placeholder_Tag = "; _GP_FIRST_LINE_M73_PLACEHOLDER"; const std::string GCodeProcessor::Last_Line_M73_Placeholder_Tag = "; _GP_LAST_LINE_M73_PLACEHOLDER"; const std::string GCodeProcessor::Estimated_Printing_Time_Placeholder_Tag = "; _GP_ESTIMATED_PRINTING_TIME_PLACEHOLDER"; #if ENABLE_GCODE_VIEWER_DATA_CHECKING -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -const std::string GCodeProcessor::Width_Tag = "Width:"; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -const std::string GCodeProcessor::Mm3_Per_Mm_Tag = "Mm3_Per_Mm:"; +const std::string GCodeProcessor::Width_Tag = "WIDTH:"; +const std::string GCodeProcessor::Mm3_Per_Mm_Tag = "MM3_PER_MM:"; #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING static bool is_valid_extrusion_role(int value) @@ -898,7 +893,6 @@ void GCodeProcessor::process_tags(const std::string& comment) return; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_DATA_CHECKING // width tag pos = comment.find(Width_Tag); @@ -911,26 +905,7 @@ void GCodeProcessor::process_tags(const std::string& comment) } return; } -#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING -// // width tag -// pos = comment.find(Width_Tag); -// if (pos != comment.npos) { -// try { -// m_width = std::stof(comment.substr(pos + Width_Tag.length())); -//#if ENABLE_GCODE_VIEWER_DATA_CHECKING -// m_width_compare.last_tag_value = m_width; -//#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING -// } -// catch (...) { -// BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Width (" << comment << ")."; -// } -// return; -// } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_GCODE_VIEWER_DATA_CHECKING // height tag pos = comment.find(Height_Tag); if (pos != comment.npos) { @@ -944,22 +919,6 @@ void GCodeProcessor::process_tags(const std::string& comment) } #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING -// // height tag -// pos = comment.find(Height_Tag); -// if (pos != comment.npos) { -// try { -// m_height = std::stof(comment.substr(pos + Height_Tag.length())); -//#if ENABLE_GCODE_VIEWER_DATA_CHECKING -// m_height_compare.last_tag_value = m_height; -//#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING -// } -// catch (...) { -// BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Height (" << comment << ")."; -// } -// return; -// } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - // color change tag pos = comment.find(Color_Change_Tag); if (pos != comment.npos) { @@ -1178,12 +1137,10 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string& comment) return true; } +#if ENABLE_GCODE_VIEWER_DATA_CHECKING // geometry // ; tool -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_GCODE_VIEWER_DATA_CHECKING -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ std::string tag = " tool"; pos = comment.find(tag); if (pos == 0) { @@ -1196,14 +1153,7 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string& comment) size_t w_end = data.find_first_of(' ', w_start); if (h_start != data.npos) { try { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_height_compare.last_tag_value = std::stof(data.substr(h_start + 1, (h_end != data.npos) ? h_end - h_start - 1 : h_end)); - -// m_height = std::stof(data.substr(h_start + 1, (h_end != data.npos) ? h_end - h_start - 1 : h_end)); -//#if ENABLE_GCODE_VIEWER_DATA_CHECKING -// m_height_compare.last_tag_value = m_height; -//#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } catch (...) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Height (" << comment << ")."; @@ -1211,14 +1161,7 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string& comment) } if (w_start != data.npos) { try { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_width_compare.last_tag_value = std::stof(data.substr(w_start + 1, (w_end != data.npos) ? w_end - w_start - 1 : w_end)); - -// m_width = std::stof(data.substr(w_start + 1, (w_end != data.npos) ? w_end - w_start - 1 : w_end)); -//#if ENABLE_GCODE_VIEWER_DATA_CHECKING -// m_width_compare.last_tag_value = m_width; -//#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } catch (...) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Width (" << comment << ")."; @@ -1227,11 +1170,8 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string& comment) return true; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -// std::cout << comment << "\n"; return false; } @@ -1303,24 +1243,15 @@ bool GCodeProcessor::process_ideamaker_tags(const std::string& comment) return true; } +#if ENABLE_GCODE_VIEWER_DATA_CHECKING // geometry -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_GCODE_VIEWER_DATA_CHECKING -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // width tag = "WIDTH:"; pos = comment.find(tag); if (pos != comment.npos) { try { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_width_compare.last_tag_value = std::stof(comment.substr(pos + tag.length())); - -// m_width = std::stof(comment.substr(pos + tag.length())); -//#if ENABLE_GCODE_VIEWER_DATA_CHECKING -// m_width_compare.last_tag_value = m_width; -//#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } catch (...) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Width (" << comment << ")."; @@ -1333,23 +1264,14 @@ bool GCodeProcessor::process_ideamaker_tags(const std::string& comment) pos = comment.find(tag); if (pos != comment.npos) { try { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_height_compare.last_tag_value = std::stof(comment.substr(pos + tag.length())); - -// m_height = std::stof(comment.substr(pos + tag.length())); -//#if ENABLE_GCODE_VIEWER_DATA_CHECKING -// m_height_compare.last_tag_value = m_height; -//#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } catch (...) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Height (" << comment << ")."; } return true; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ return false; } diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 52ae76f7ee..b19610801c 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -67,9 +67,6 @@ namespace Slic3r { { public: static const std::string Extrusion_Role_Tag; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -// static const std::string Width_Tag; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ static const std::string Height_Tag; static const std::string Color_Change_Tag; static const std::string Pause_Print_Tag; @@ -79,9 +76,7 @@ namespace Slic3r { static const std::string Estimated_Printing_Time_Placeholder_Tag; #if ENABLE_GCODE_VIEWER_DATA_CHECKING -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ static const std::string Width_Tag; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ static const std::string Mm3_Per_Mm_Tag; #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 3eaf31e1cd..1e8323230b 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -51,13 +51,9 @@ public: m_extrusion_flow(0.f), m_preview_suppressed(false), m_elapsed_time(0.f), -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_GCODE_VIEWER_DATA_CHECKING -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_default_analyzer_line_width(line_width), -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_GCODE_VIEWER_DATA_CHECKING -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_gcode_flavor(flavor), m_filpar(filament_parameters) { @@ -65,26 +61,20 @@ public: char buf[64]; #if ENABLE_GCODE_VIEWER sprintf(buf, ";%s%f\n", GCodeProcessor::Height_Tag.c_str(), m_layer_height); // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming + m_gcode += buf; + sprintf(buf, ";%s%s\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), ExtrusionEntity::role_to_string(erWipeTower).c_str()); + m_gcode += buf; #else sprintf(buf, ";%s%f\n", GCodeAnalyzer::Height_Tag.c_str(), m_layer_height); // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming -#endif // ENABLE_GCODE_VIEWER m_gcode += buf; -#if ENABLE_GCODE_VIEWER - sprintf(buf, ";%s%s\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), ExtrusionEntity::role_to_string(erWipeTower).c_str()); -#else sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erWipeTower); -#endif // ENABLE_GCODE_VIEWER m_gcode += buf; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER_DATA_CHECKING -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ change_analyzer_line_width(line_width); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_DATA_CHECKING WipeTowerWriter& change_analyzer_line_width(float line_width) { // adds tag for analyzer: @@ -93,32 +83,7 @@ public: m_gcode += buf; return *this; } -#else -#if !ENABLE_GCODE_VIEWER - WipeTowerWriter& change_analyzer_line_width(float line_width) { - // adds tag for analyzer: - char buf[64]; - sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), line_width); - m_gcode += buf; - return *this; -} -#endif // !ENABLE_GCODE_VIEWER -#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING -// WipeTowerWriter& change_analyzer_line_width(float line_width) { -// // adds tag for analyzer: -// char buf[64]; -//#if ENABLE_GCODE_VIEWER -// sprintf(buf, ";%s%f\n", GCodeProcessor::Width_Tag.c_str(), line_width); -//#else -// sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), line_width); -//#endif // ENABLE_GCODE_VIEWER -// m_gcode += buf; -// return *this; -// } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - -#if ENABLE_GCODE_VIEWER_DATA_CHECKING WipeTowerWriter& change_analyzer_mm3_per_mm(float len, float e) { static const float area = float(M_PI) * 1.75f * 1.75f / 4.f; float mm3_per_mm = (len == 0.f ? 0.f : area * e / len); @@ -130,6 +95,14 @@ public: } #else #if !ENABLE_GCODE_VIEWER + WipeTowerWriter& change_analyzer_line_width(float line_width) { + // adds tag for analyzer: + char buf[64]; + sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), line_width); + m_gcode += buf; + return *this; + } + WipeTowerWriter& change_analyzer_mm3_per_mm(float len, float e) { static const float area = float(M_PI) * 1.75f * 1.75f / 4.f; float mm3_per_mm = (len == 0.f ? 0.f : area * e / len); @@ -175,17 +148,13 @@ public: // Suppress / resume G-code preview in Slic3r. Slic3r will have difficulty to differentiate the various // filament loading and cooling moves from normal extrusion moves. Therefore the writer // is asked to suppres output of some lines, which look like extrusions. -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_DATA_CHECKING WipeTowerWriter& suppress_preview() { change_analyzer_line_width(0.f); m_preview_suppressed = true; return *this; } WipeTowerWriter& resume_preview() { change_analyzer_line_width(m_default_analyzer_line_width); m_preview_suppressed = false; return *this; } #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ WipeTowerWriter& suppress_preview() { m_preview_suppressed = true; return *this; } WipeTowerWriter& resume_preview() { m_preview_suppressed = false; return *this; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ WipeTowerWriter& feedrate(float f) { @@ -490,13 +459,9 @@ private: float m_wipe_tower_depth = 0.f; unsigned m_last_fan_speed = 0; int current_temp = -1; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if !ENABLE_GCODE_VIEWER_DATA_CHECKING -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ const float m_default_analyzer_line_width; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // !ENABLE_GCODE_VIEWER_DATA_CHECKING -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ float m_used_filament_length = 0.f; GCodeFlavor m_gcode_flavor; const std::vector& m_filpar; @@ -937,16 +902,12 @@ void WipeTower::toolchange_Unload( const float line_width = m_perimeter_width * m_filpar[m_current_tool].ramming_line_width_multiplicator; // desired ramming line thickness const float y_step = line_width * m_filpar[m_current_tool].ramming_step_multiplicator * m_extra_spacing; // spacing between lines in mm -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_DATA_CHECKING writer.append("; CP TOOLCHANGE UNLOAD\n") .change_analyzer_line_width(line_width); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ writer.append("; CP TOOLCHANGE UNLOAD\n"); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ unsigned i = 0; // iterates through ramming_speed m_left_to_right = true; // current direction of ramming @@ -1023,13 +984,9 @@ void WipeTower::toolchange_Unload( } } Vec2f end_of_ramming(writer.x(),writer.y()); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER_DATA_CHECKING -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ writer.change_analyzer_line_width(m_perimeter_width); // so the next lines are not affected by ramming_line_width_multiplier -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // Retraction: float old_x = writer.x(); diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 4cb3ae1c52..1a4485402b 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -385,10 +385,10 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: { case EMoveType::Extrude: { - m_extrusions.ranges.height.update_from(curr.height); - m_extrusions.ranges.width.update_from(curr.width); + m_extrusions.ranges.height.update_from(round_to_nearest(curr.height, 2)); + m_extrusions.ranges.width.update_from(round_to_nearest(curr.width, 2)); m_extrusions.ranges.fan_speed.update_from(curr.fan_speed); - m_extrusions.ranges.volumetric_rate.update_from(curr.volumetric_rate()); + m_extrusions.ranges.volumetric_rate.update_from(round_to_nearest(curr.volumetric_rate(), 2)); [[fallthrough]]; } case EMoveType::Travel: From 1172dfcb402b6fdd5df31262009d123fbb2ca918 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 18 Aug 2020 07:55:53 +0200 Subject: [PATCH 306/503] #4639 - ImGuiWrapper: fixed calls to ImGui::Text() and ImGui::TextColored() --- src/slic3r/GUI/ImGuiWrapper.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 266472ecaf..104680f502 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -362,7 +362,7 @@ bool ImGuiWrapper::checkbox(const wxString &label, bool &value) void ImGuiWrapper::text(const char *label) { - ImGui::Text(label, NULL); + ImGui::Text("%s", label); } void ImGuiWrapper::text(const std::string &label) @@ -378,7 +378,7 @@ void ImGuiWrapper::text(const wxString &label) void ImGuiWrapper::text_colored(const ImVec4& color, const char* label) { - ImGui::TextColored(color, label); + ImGui::TextColored(color, "%s", label); } void ImGuiWrapper::text_colored(const ImVec4& color, const std::string& label) From ca27d7296f5b892417508bdbedbbb789f4db8bbc Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 18 Aug 2020 08:27:07 +0200 Subject: [PATCH 307/503] Fixed build when ENABLE_GCODE_VIEWER is disabled --- src/libslic3r/GCode/WipeTower.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 1e8323230b..0752b6dfcb 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -51,9 +51,9 @@ public: m_extrusion_flow(0.f), m_preview_suppressed(false), m_elapsed_time(0.f), -#if !ENABLE_GCODE_VIEWER_DATA_CHECKING +#if !ENABLE_GCODE_VIEWER || ENABLE_GCODE_VIEWER_DATA_CHECKING m_default_analyzer_line_width(line_width), -#endif // !ENABLE_GCODE_VIEWER_DATA_CHECKING +#endif // !ENABLE_GCODE_VIEWER || ENABLE_GCODE_VIEWER_DATA_CHECKING m_gcode_flavor(flavor), m_filpar(filament_parameters) { @@ -148,13 +148,13 @@ public: // Suppress / resume G-code preview in Slic3r. Slic3r will have difficulty to differentiate the various // filament loading and cooling moves from normal extrusion moves. Therefore the writer // is asked to suppres output of some lines, which look like extrusions. -#if ENABLE_GCODE_VIEWER_DATA_CHECKING +#if !ENABLE_GCODE_VIEWER || ENABLE_GCODE_VIEWER_DATA_CHECKING WipeTowerWriter& suppress_preview() { change_analyzer_line_width(0.f); m_preview_suppressed = true; return *this; } WipeTowerWriter& resume_preview() { change_analyzer_line_width(m_default_analyzer_line_width); m_preview_suppressed = false; return *this; } #else WipeTowerWriter& suppress_preview() { m_preview_suppressed = true; return *this; } WipeTowerWriter& resume_preview() { m_preview_suppressed = false; return *this; } -#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING +#endif // !ENABLE_GCODE_VIEWER || ENABLE_GCODE_VIEWER_DATA_CHECKING WipeTowerWriter& feedrate(float f) { @@ -459,9 +459,9 @@ private: float m_wipe_tower_depth = 0.f; unsigned m_last_fan_speed = 0; int current_temp = -1; -#if !ENABLE_GCODE_VIEWER_DATA_CHECKING +#if !ENABLE_GCODE_VIEWER || ENABLE_GCODE_VIEWER_DATA_CHECKING const float m_default_analyzer_line_width; -#endif // !ENABLE_GCODE_VIEWER_DATA_CHECKING +#endif // !ENABLE_GCODE_VIEWER || ENABLE_GCODE_VIEWER_DATA_CHECKING float m_used_filament_length = 0.f; GCodeFlavor m_gcode_flavor; const std::vector& m_filpar; From 4ef52af9066aff676818cd3b1d157083c72805b2 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 18 Aug 2020 11:41:14 +0200 Subject: [PATCH 308/503] Add dedicated tests for support point generation --- src/libslic3r/BoundingBox.hpp | 14 +++ src/libslic3r/Line.cpp | 18 --- src/libslic3r/Line.hpp | 31 +++++- src/libslic3r/Point.hpp | 2 + src/libslic3r/SLA/Hollowing.cpp | 9 ++ src/libslic3r/SLA/Hollowing.hpp | 2 + tests/sla_print/CMakeLists.txt | 5 +- tests/sla_print/sla_supptgen_tests.cpp | 148 +++++++++++++++++++++++++ tests/sla_print/sla_test_utils.cpp | 68 ++++++++++++ tests/sla_print/sla_test_utils.hpp | 9 ++ 10 files changed, 285 insertions(+), 21 deletions(-) create mode 100644 tests/sla_print/sla_supptgen_tests.cpp diff --git a/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp index f03733dd86..08f01d8d85 100644 --- a/src/libslic3r/BoundingBox.hpp +++ b/src/libslic3r/BoundingBox.hpp @@ -192,6 +192,20 @@ inline BoundingBox3 scaled(const BoundingBoxf3 &bb) { return {scaled(bb.min), sc inline BoundingBoxf unscaled(const BoundingBox &bb) { return {unscaled(bb.min), unscaled(bb.max)}; } inline BoundingBoxf3 unscaled(const BoundingBox3 &bb) { return {unscaled(bb.min), unscaled(bb.max)}; } +template +auto cast(const BoundingBoxBase &b) +{ + return BoundingBoxBase>{b.min.template cast(), + b.max.template cast()}; +} + +template +auto cast(const BoundingBox3Base &b) +{ + return BoundingBox3Base>{b.min.template cast(), + b.max.template cast()}; +} + } // namespace Slic3r // Serialization through the Cereal library diff --git a/src/libslic3r/Line.cpp b/src/libslic3r/Line.cpp index 974f585dc2..0c43154a9b 100644 --- a/src/libslic3r/Line.cpp +++ b/src/libslic3r/Line.cpp @@ -33,24 +33,6 @@ bool Line::intersection_infinite(const Line &other, Point* point) const return true; } -// Distance to the closest point of line. -double Line::distance_to_squared(const Point &point, const Point &a, const Point &b) -{ - const Vec2d v = (b - a).cast(); - const Vec2d va = (point - a).cast(); - const double l2 = v.squaredNorm(); // avoid a sqrt - if (l2 == 0.0) - // a == b case - return va.squaredNorm(); - // Consider the line extending the segment, parameterized as a + t (b - a). - // We find projection of this point onto the line. - // It falls where t = [(this-a) . (b-a)] / |b-a|^2 - const double t = va.dot(v) / l2; - if (t < 0.0) return va.squaredNorm(); // beyond the 'a' end of the segment - else if (t > 1.0) return (point - b).cast().squaredNorm(); // beyond the 'b' end of the segment - return (t * v - va).squaredNorm(); -} - double Line::perp_distance_to(const Point &point) const { const Line &line = *this; diff --git a/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp index caab809f5c..980303feda 100644 --- a/src/libslic3r/Line.hpp +++ b/src/libslic3r/Line.hpp @@ -18,6 +18,35 @@ typedef std::vector ThickLines; Linef3 transform(const Linef3& line, const Transform3d& t); +namespace line_alg { + +// Distance to the closest point of line. +template +double distance_to_squared(const L &line, const Vec &point) +{ + const Vec v = line.vector().template cast(); + const Vec va = (point - line.a).template cast(); + const double l2 = v.squaredNorm(); // avoid a sqrt + if (l2 == 0.0) + // a == b case + return va.squaredNorm(); + // Consider the line extending the segment, parameterized as a + t (b - a). + // We find projection of this point onto the line. + // It falls where t = [(this-a) . (b-a)] / |b-a|^2 + const double t = va.dot(v) / l2; + if (t < 0.0) return va.squaredNorm(); // beyond the 'a' end of the segment + else if (t > 1.0) return (point - line.b).template cast().squaredNorm(); // beyond the 'b' end of the segment + return (t * v - va).squaredNorm(); +} + +template +double distance_to(const L &line, const Vec &point) +{ + return std::sqrt(distance_to_squared(line, point)); +} + +} // namespace line_alg + class Line { public: @@ -47,7 +76,7 @@ public: // Clip a line with a bounding box. Returns false if the line is completely outside of the bounding box. bool clip_with_bbox(const BoundingBox &bbox); - static double distance_to_squared(const Point &point, const Point &a, const Point &b); + static inline double distance_to_squared(const Point &point, const Point &a, const Point &b) { return line_alg::distance_to_squared(Line{a, b}, Vec<2, coord_t>{point}); } static double distance_to(const Point &point, const Point &a, const Point &b) { return sqrt(distance_to_squared(point, a, b)); } Point a; diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index 8c1c69fde3..84010c7eb1 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -88,6 +88,8 @@ inline std::string to_string(const Vec3d &pt) { return std::string("[") + std: std::vector transform(const std::vector& points, const Transform3f& t); Pointf3s transform(const Pointf3s& points, const Transform3d& t); +template using Vec = Eigen::Matrix; + class Point : public Vec2crd { public: diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index 5334054a05..6df752fd36 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -273,4 +273,13 @@ void cut_drainholes(std::vector & obj_slices, obj_slices[i] = diff_ex(obj_slices[i], hole_slices[i]); } +void hollow_mesh(TriangleMesh &mesh, const HollowingConfig &cfg) +{ + std::unique_ptr inter_ptr = + Slic3r::sla::generate_interior(mesh); + + if (inter_ptr) mesh.merge(*inter_ptr); + mesh.require_shared_vertices(); +} + }} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/Hollowing.hpp b/src/libslic3r/SLA/Hollowing.hpp index 1f65fa8b70..c7d27d946c 100644 --- a/src/libslic3r/SLA/Hollowing.hpp +++ b/src/libslic3r/SLA/Hollowing.hpp @@ -62,6 +62,8 @@ std::unique_ptr generate_interior(const TriangleMesh &mesh, const HollowingConfig & = {}, const JobController &ctl = {}); +void hollow_mesh(TriangleMesh &mesh, const HollowingConfig &cfg); + void cut_drainholes(std::vector & obj_slices, const std::vector &slicegrid, float closing_radius, diff --git a/tests/sla_print/CMakeLists.txt b/tests/sla_print/CMakeLists.txt index f6b261fdaa..d071b49739 100644 --- a/tests/sla_print/CMakeLists.txt +++ b/tests/sla_print/CMakeLists.txt @@ -1,7 +1,8 @@ get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) -add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp - sla_print_tests.cpp +add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp + sla_print_tests.cpp sla_test_utils.hpp sla_test_utils.cpp sla_treebuilder_tests.cpp + sla_supptgen_tests.cpp sla_raycast_tests.cpp) target_link_libraries(${_TEST_NAME}_tests test_common libslic3r) set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests") diff --git a/tests/sla_print/sla_supptgen_tests.cpp b/tests/sla_print/sla_supptgen_tests.cpp new file mode 100644 index 0000000000..1d7a3ebee2 --- /dev/null +++ b/tests/sla_print/sla_supptgen_tests.cpp @@ -0,0 +1,148 @@ +#include +#include + +#include +#include + +#include "sla_test_utils.hpp" + +namespace Slic3r { namespace sla { + +TEST_CASE("Overhanging point should be supported", "[SupGen]") { + + // Pyramid with 45 deg slope + TriangleMesh mesh = make_pyramid(10.f, 10.f); + mesh.rotate_y(PI); + mesh.require_shared_vertices(); + mesh.WriteOBJFile("Pyramid.obj"); + + sla::SupportPoints pts = calc_support_pts(mesh); + + // The overhang, which is the upside-down pyramid's edge + Vec3f overh{0., 0., -10.}; + + REQUIRE(!pts.empty()); + + float dist = (overh - pts.front().pos).norm(); + + for (const auto &pt : pts) + dist = std::min(dist, (overh - pt.pos).norm()); + + // Should require exactly one support point at the overhang + REQUIRE(pts.size() > 0); + REQUIRE(dist < 1.f); +} + +double min_point_distance(const sla::SupportPoints &pts) +{ + sla::PointIndex index; + + for (size_t i = 0; i < pts.size(); ++i) + index.insert(pts[i].pos.cast(), i); + + auto d = std::numeric_limits::max(); + index.foreach([&d, &index](const sla::PointIndexEl &el) { + auto res = index.nearest(el.first, 2); + for (const sla::PointIndexEl &r : res) + if (r.second != el.second) + d = std::min(d, (el.first - r.first).norm()); + }); + + return d; +} + +TEST_CASE("Overhanging horizontal surface should be supported", "[SupGen]") { + double width = 10., depth = 10., height = 1.; + + TriangleMesh mesh = make_cube(width, depth, height); + mesh.translate(0., 0., 5.); // lift up + mesh.require_shared_vertices(); + mesh.WriteOBJFile("Cuboid.obj"); + + sla::SupportPointGenerator::Config cfg; + sla::SupportPoints pts = calc_support_pts(mesh, cfg); + + double mm2 = width * depth; + + REQUIRE(!pts.empty()); + REQUIRE(pts.size() * cfg.support_force() > mm2 * cfg.tear_pressure()); + REQUIRE(min_point_distance(pts) >= cfg.minimal_distance); +} + +template auto&& center_around_bb(M &&mesh) +{ + auto bb = mesh.bounding_box(); + mesh.translate(-bb.center().template cast()); + + return std::forward(mesh); +} + +TEST_CASE("Overhanging edge should be supported", "[SupGen]") { + float width = 10.f, depth = 10.f, height = 5.f; + + TriangleMesh mesh = make_prism(width, depth, height); + mesh.rotate_y(PI); // rotate on its back + mesh.translate(0., 0., height); + mesh.require_shared_vertices(); + mesh.WriteOBJFile("Prism.obj"); + + sla::SupportPointGenerator::Config cfg; + sla::SupportPoints pts = calc_support_pts(mesh, cfg); + + REQUIRE(min_point_distance(pts) >= cfg.minimal_distance); + + Linef3 overh{ {0.f, -depth / 2.f, 0.f}, {0.f, depth / 2.f, 0.f}}; + + // Get all the points closer that 1 mm to the overhanging edge: + sla::SupportPoints overh_pts; overh_pts.reserve(pts.size()); + + std::copy_if(pts.begin(), pts.end(), std::back_inserter(overh_pts), + [&overh](const sla::SupportPoint &pt){ + return line_alg::distance_to(overh, Vec3d{pt.pos.cast()}) < 1.; + }); + + REQUIRE(overh_pts.size() * cfg.support_force() > overh.length() * cfg.tear_pressure()); + REQUIRE(min_point_distance(pts) >= cfg.minimal_distance); +} + +// FIXME: Not working yet +//TEST_CASE("Hollowed cube should be supported from the inside", "[SupGen][Hollowed]") { +// TriangleMesh mesh = make_cube(20., 20., 20.); + +// hollow_mesh(mesh, HollowingConfig{}); + +// mesh.WriteOBJFile("cube_hollowed.obj"); + +// auto bb = mesh.bounding_box(); +// auto h = float(bb.max.z() - bb.min.z()); +// Vec3f mv = bb.center().cast() - Vec3f{0.f, 0.f, 0.5f * h}; +// mesh.translate(-mv); +// mesh.require_shared_vertices(); + +// sla::SupportPointGenerator::Config cfg; +// sla::SupportPoints pts = calc_support_pts(mesh, cfg); +// sla::remove_bottom_points(pts, mesh.bounding_box().min.z() + EPSILON); + +// REQUIRE(!pts.empty()); +//} + +TEST_CASE("Two parallel plates should be supported", "[SupGen][Hollowed]") +{ + double width = 20., depth = 20., height = 1.; + + TriangleMesh mesh = center_around_bb(make_cube(width + 5., depth + 5., height)); + TriangleMesh mesh_high = center_around_bb(make_cube(width, depth, height)); + mesh_high.translate(0., 0., 10.); // lift up + mesh.merge(mesh_high); + mesh.require_shared_vertices(); + + mesh.WriteOBJFile("parallel_plates.obj"); + + sla::SupportPointGenerator::Config cfg; + sla::SupportPoints pts = calc_support_pts(mesh, cfg); + sla::remove_bottom_points(pts, mesh.bounding_box().min.z() + EPSILON); + + REQUIRE(!pts.empty()); +} + +}} // namespace Slic3r::sla diff --git a/tests/sla_print/sla_test_utils.cpp b/tests/sla_print/sla_test_utils.cpp index 8978281d8c..f6b548fa05 100644 --- a/tests/sla_print/sla_test_utils.cpp +++ b/tests/sla_print/sla_test_utils.cpp @@ -411,3 +411,71 @@ double predict_error(const ExPolygon &p, const sla::RasterBase::PixelDim &pd) return error; } + + +// Make a 3D pyramid +TriangleMesh make_pyramid(float base, float height) +{ + float a = base / 2.f; + + TriangleMesh mesh( + { + {-a, -a, 0}, {a, -a, 0}, {a, a, 0}, + {-a, a, 0}, {0.f, 0.f, height} + }, + { + {0, 1, 2}, + {0, 2, 3}, + {0, 1, 4}, + {1, 2, 4}, + {2, 3, 4}, + {3, 0, 4} + }); + + mesh.repair(); + + return mesh; +} + + TriangleMesh make_prism(double width, double length, double height) +{ + // We need two upward facing triangles + + double x = width / 2., y = length / 2.; + + TriangleMesh mesh( + { + {-x, -y, 0.}, {x, -y, 0.}, {0., -y, height}, + {-x, y, 0.}, {x, y, 0.}, {0., y, height}, + }, + { + {0, 1, 2}, // side 1 + {4, 3, 5}, // side 2 + {1, 4, 2}, {2, 4, 5}, // roof 1 + {0, 2, 5}, {0, 5, 3}, // roof 2 + {3, 4, 1}, {3, 1, 0} // bottom + }); + + return mesh; +} + +sla::SupportPoints calc_support_pts( + const TriangleMesh & mesh, + const sla::SupportPointGenerator::Config &cfg) +{ + // Prepare the slice grid and the slices + std::vector slices; + auto bb = cast(mesh.bounding_box()); + std::vector heights = grid(bb.min.z(), bb.max.z(), 0.1f); + slice_mesh(mesh, heights, slices, CLOSING_RADIUS, [] {}); + + // Prepare the support point calculator + sla::IndexedMesh emesh{mesh}; + sla::SupportPointGenerator spgen{emesh, cfg, []{}, [](int){}}; + + // Calculate the support points + spgen.seed(0); + spgen.execute(slices, heights); + + return spgen.output(); +} diff --git a/tests/sla_print/sla_test_utils.hpp b/tests/sla_print/sla_test_utils.hpp index fdd883ed84..d10a85b259 100644 --- a/tests/sla_print/sla_test_utils.hpp +++ b/tests/sla_print/sla_test_utils.hpp @@ -185,4 +185,13 @@ long raster_pxsum(const sla::RasterGrayscaleAA &raster); double predict_error(const ExPolygon &p, const sla::RasterBase::PixelDim &pd); +// Make a 3D pyramid +TriangleMesh make_pyramid(float base, float height); + +TriangleMesh make_prism(double width, double length, double height); + +sla::SupportPoints calc_support_pts( + const TriangleMesh & mesh, + const sla::SupportPointGenerator::Config &cfg = {}); + #endif // SLA_TEST_UTILS_HPP From 7fd2209b48a6dfeb6c4b884200c617279e6de79a Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 5 Aug 2020 13:30:05 +0200 Subject: [PATCH 309/503] Gizmos can be shown depending on current mode --- src/slic3r/GUI/GUI_App.cpp | 2 ++ src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 3 ++- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 12 ++++++++---- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 82c2861bc2..8f33a6e996 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -41,6 +41,7 @@ #include "3DScene.hpp" #include "MainFrame.hpp" #include "Plater.hpp" +#include "GLCanvas3D.hpp" #include "../Utils/PresetUpdater.hpp" #include "../Utils/PrintHost.hpp" @@ -1012,6 +1013,7 @@ void GUI_App::update_mode() tab->update_mode(); plater()->update_object_menu(); + plater()->canvas3D()->update_gizmos_on_off_state(); } void GUI_App::add_config_menu(wxMenuBar *menu) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 309c7cf423..58b46f0b6a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -631,7 +631,8 @@ bool GLGizmoFdmSupports::on_is_activable() const bool GLGizmoFdmSupports::on_is_selectable() const { - return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF ); + return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF + && wxGetApp().get_mode() != comSimple ); } std::string GLGizmoFdmSupports::on_get_name() const diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index c33ba2850e..78998b92d9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -144,8 +144,11 @@ void GLGizmosManager::refresh_on_off_state() if (m_serializing || m_current == Undefined || m_gizmos.empty()) return; - if (m_current != Undefined && ! m_gizmos[m_current]->is_activable()) + if (m_current != Undefined + && (! m_gizmos[m_current]->is_activable() || ! m_gizmos[m_current]->is_selectable())) { activate_gizmo(Undefined); + update_data(); + } } void GLGizmosManager::reset_all_states() @@ -204,9 +207,10 @@ void GLGizmosManager::update_data() enable_grabber(Scale, i, enable_scale_xyz); } - m_common_gizmos_data->update(get_current() - ? get_current()->get_requirements() - : CommonGizmosDataID(0)); + if (m_common_gizmos_data) + m_common_gizmos_data->update(get_current() + ? get_current()->get_requirements() + : CommonGizmosDataID(0)); if (selection.is_single_full_instance()) { From 97bc092cce53b893aa796779f2735b87a266e888 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 5 Aug 2020 14:03:22 +0200 Subject: [PATCH 310/503] Renamed FacetSupportType to EnforcerBlockerType So it's not misleading if we use it for seam painting --- src/libslic3r/Model.cpp | 2 +- src/libslic3r/Model.hpp | 4 ++-- src/libslic3r/Print.hpp | 6 ++--- src/libslic3r/PrintObject.cpp | 2 +- src/libslic3r/TriangleSelector.cpp | 24 ++++++++++---------- src/libslic3r/TriangleSelector.hpp | 20 ++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 20 ++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp | 2 +- 8 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 3beb74f235..196e9c213b 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1831,7 +1831,7 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const } -indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, FacetSupportType type) const +indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, EnforcerBlockerType type) const { TriangleSelector selector(mv.mesh()); selector.deserialize(m_data); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 92dc84d17a..608ce670f6 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -394,7 +394,7 @@ enum class ModelVolumeType : int { SUPPORT_BLOCKER, }; -enum class FacetSupportType : int8_t { +enum class EnforcerBlockerType : int8_t { // Maximum is 3. The value is serialized in TriangleSelector into 2 bits! NONE = 0, ENFORCER = 1, @@ -407,7 +407,7 @@ public: const std::map>& get_data() const { return m_data; } bool set(const TriangleSelector& selector); - indexed_triangle_set get_facets(const ModelVolume& mv, FacetSupportType type) const; + indexed_triangle_set get_facets(const ModelVolume& mv, EnforcerBlockerType type) const; void clear(); std::string get_triangle_as_string(int i) const; void set_triangle_from_string(int triangle_id, const std::string& str); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 05929dd2ef..08acb7a105 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -187,9 +187,9 @@ public: std::vector slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); } // Helpers to project custom supports on slices - void project_and_append_custom_supports(FacetSupportType type, std::vector& expolys) const; - void project_and_append_custom_enforcers(std::vector& enforcers) const { project_and_append_custom_supports(FacetSupportType::ENFORCER, enforcers); } - void project_and_append_custom_blockers(std::vector& blockers) const { project_and_append_custom_supports(FacetSupportType::BLOCKER, blockers); } + void project_and_append_custom_supports(EnforcerBlockerType type, std::vector& expolys) const; + void project_and_append_custom_enforcers(std::vector& enforcers) const { project_and_append_custom_supports(EnforcerBlockerType::ENFORCER, enforcers); } + void project_and_append_custom_blockers(std::vector& blockers) const { project_and_append_custom_supports(EnforcerBlockerType::BLOCKER, blockers); } private: // to be called from Print only. diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index c90a05ef31..ddeee1e778 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2670,7 +2670,7 @@ void PrintObject::_generate_support_material() void PrintObject::project_and_append_custom_supports( - FacetSupportType type, std::vector& expolys) const + EnforcerBlockerType type, std::vector& expolys) const { for (const ModelVolume* mv : this->model_object()->volumes) { const indexed_triangle_set custom_facets = mv->m_supported_facets.get_facets(*mv, type); diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index 9555c42a69..9f04374fdc 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -35,7 +35,7 @@ void TriangleSelector::Triangle::set_division(int sides_to_split, int special_si void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, const Vec3f& source, const Vec3f& dir, - float radius, FacetSupportType new_state) + float radius, EnforcerBlockerType new_state) { assert(facet_start < m_orig_size_indices); assert(is_approx(dir.norm(), 1.f)); @@ -77,7 +77,7 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, // the triangle recursively, selecting just subtriangles truly inside the circle. // This is done by an actual recursive call. Returns false if the triangle is // outside the cursor. -bool TriangleSelector::select_triangle(int facet_idx, FacetSupportType type, bool recursive_call) +bool TriangleSelector::select_triangle(int facet_idx, EnforcerBlockerType type, bool recursive_call) { assert(facet_idx < int(m_triangles.size())); @@ -140,7 +140,7 @@ bool TriangleSelector::select_triangle(int facet_idx, FacetSupportType type, boo -void TriangleSelector::set_facet(int facet_idx, FacetSupportType state) +void TriangleSelector::set_facet(int facet_idx, EnforcerBlockerType state) { assert(facet_idx < m_orig_size_indices); undivide_triangle(facet_idx); @@ -157,7 +157,7 @@ void TriangleSelector::split_triangle(int facet_idx) Triangle* tr = &m_triangles[facet_idx]; - FacetSupportType old_type = tr->get_state(); + EnforcerBlockerType old_type = tr->get_state(); if (tr->was_split_before() != 0) { // This triangle is not split at the moment, but was at one point @@ -323,7 +323,7 @@ void TriangleSelector::remove_useless_children(int facet_idx) // Return if a child is not leaf or two children differ in type. - FacetSupportType first_child_type = FacetSupportType::NONE; + EnforcerBlockerType first_child_type = EnforcerBlockerType::NONE; for (int child_idx=0; child_idx<=tr.number_of_split_sides(); ++child_idx) { if (m_triangles[tr.children[child_idx]].is_split()) return; @@ -456,7 +456,7 @@ void TriangleSelector::push_triangle(int a, int b, int c) } -void TriangleSelector::perform_split(int facet_idx, FacetSupportType old_state) +void TriangleSelector::perform_split(int facet_idx, EnforcerBlockerType old_state) { Triangle* tr = &m_triangles[facet_idx]; @@ -520,7 +520,7 @@ void TriangleSelector::perform_split(int facet_idx, FacetSupportType old_state) -indexed_triangle_set TriangleSelector::get_facets(FacetSupportType state) const +indexed_triangle_set TriangleSelector::get_facets(EnforcerBlockerType state) const { indexed_triangle_set out; for (const Triangle& tr : m_triangles) { @@ -542,7 +542,7 @@ std::map> TriangleSelector::serialize() const { // Each original triangle of the mesh is assigned a number encoding its state // or how it is split. Each triangle is encoded by 4 bits (xxyy): - // leaf triangle: xx = FacetSupportType, yy = 0 + // leaf triangle: xx = EnforcerBlockerType, yy = 0 // non-leaf: xx = special side, yy = number of split sides // These are bitwise appended and formed into one 64-bit integer. @@ -553,7 +553,7 @@ std::map> TriangleSelector::serialize() const for (int i=0; i data; // complete encoding of this mesh triangle @@ -627,7 +627,7 @@ void TriangleSelector::deserialize(const std::map> data) int num_of_split_sides = (next_code & 0b11); int num_of_children = num_of_split_sides != 0 ? num_of_split_sides + 1 : 0; bool is_split = num_of_children != 0; - FacetSupportType state = FacetSupportType(next_code >> 2); + EnforcerBlockerType state = EnforcerBlockerType(next_code >> 2); int special_side = (next_code >> 2); // Take care of the first iteration separately, so handling of the others is simpler. @@ -641,7 +641,7 @@ void TriangleSelector::deserialize(const std::map> data) // then go to the next. parents.push_back({triangle_id, 0, num_of_children}); m_triangles[triangle_id].set_division(num_of_children-1, special_side); - perform_split(triangle_id, FacetSupportType::NONE); + perform_split(triangle_id, EnforcerBlockerType::NONE); continue; } } @@ -655,7 +655,7 @@ void TriangleSelector::deserialize(const std::map> data) const ProcessingInfo& last = parents.back(); int this_idx = m_triangles[last.facet_id].children[last.processed_children]; m_triangles[this_idx].set_division(num_of_children-1, special_side); - perform_split(this_idx, FacetSupportType::NONE); + perform_split(this_idx, EnforcerBlockerType::NONE); parents.push_back({this_idx, 0, num_of_children}); } else { // this triangle belongs to last split one diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp index fb90cff769..be1b20ed40 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -9,7 +9,7 @@ namespace Slic3r { -enum class FacetSupportType : int8_t; +enum class EnforcerBlockerType : int8_t; @@ -29,13 +29,13 @@ public: const Vec3f& source, // camera position (mesh coords) const Vec3f& dir, // direction of the ray (mesh coords) float radius, // radius of the cursor - FacetSupportType new_state); // enforcer or blocker? + EnforcerBlockerType new_state); // enforcer or blocker? // Get facets currently in the given state. - indexed_triangle_set get_facets(FacetSupportType state) const; + indexed_triangle_set get_facets(EnforcerBlockerType state) const; // Set facet of the mesh to a given state. Only works for original triangles. - void set_facet(int facet_idx, FacetSupportType state); + void set_facet(int facet_idx, EnforcerBlockerType state); // Clear everything and make the tree empty. void reset(); @@ -59,7 +59,7 @@ protected: // It increments/decrements reference counter on vertices. Triangle(int a, int b, int c) : verts_idxs{a, b, c}, - state{FacetSupportType(0)}, + state{EnforcerBlockerType(0)}, number_of_splits{0}, special_side_idx{0}, old_number_of_splits{0} @@ -77,8 +77,8 @@ protected: void set_division(int sides_to_split, int special_side_idx = -1); // Get/set current state. - void set_state(FacetSupportType type) { assert(! is_split()); state = type; } - FacetSupportType get_state() const { assert(! is_split()); return state; } + void set_state(EnforcerBlockerType type) { assert(! is_split()); state = type; } + EnforcerBlockerType get_state() const { assert(! is_split()); return state; } // Get info on how it's split. bool is_split() const { return number_of_split_sides() != 0; } @@ -90,7 +90,7 @@ protected: private: int number_of_splits; int special_side_idx; - FacetSupportType state; + EnforcerBlockerType state; // How many children were spawned during last split? // Is not reset on remerging the triangle. @@ -133,7 +133,7 @@ protected: float m_old_cursor_radius; // Private functions: - bool select_triangle(int facet_idx, FacetSupportType type, + bool select_triangle(int facet_idx, EnforcerBlockerType type, bool recursive_call = false); bool is_point_inside_cursor(const Vec3f& point) const; int vertices_inside(int facet_idx) const; @@ -144,7 +144,7 @@ protected: bool is_pointer_in_triangle(int facet_idx) const; bool is_edge_inside_cursor(int facet_idx) const; void push_triangle(int a, int b, int c); - void perform_split(int facet_idx, FacetSupportType old_state); + void perform_split(int facet_idx, EnforcerBlockerType old_state); }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 58b46f0b6a..f3b6db4f26 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -296,16 +296,16 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous if (m_triangle_selectors.empty()) return false; - FacetSupportType new_state = FacetSupportType::NONE; + EnforcerBlockerType new_state = EnforcerBlockerType::NONE; if (! shift_down) { if (action == SLAGizmoEventType::Dragging) new_state = m_button_down == Button::Left - ? FacetSupportType::ENFORCER - : FacetSupportType::BLOCKER; + ? EnforcerBlockerType::ENFORCER + : EnforcerBlockerType::BLOCKER; else new_state = action == SLAGizmoEventType::LeftDown - ? FacetSupportType::ENFORCER - : FacetSupportType::BLOCKER; + ? EnforcerBlockerType::ENFORCER + : EnforcerBlockerType::BLOCKER; } const Camera& camera = wxGetApp().plater()->get_camera(); @@ -465,8 +465,8 @@ void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool block) if (facet.normal.dot(down) > dot_limit) m_triangle_selectors[mesh_id]->set_facet(idx, block - ? FacetSupportType::BLOCKER - : FacetSupportType::ENFORCER); + ? EnforcerBlockerType::BLOCKER + : EnforcerBlockerType::ENFORCER); } } @@ -719,13 +719,13 @@ void TriangleSelectorGUI::render(ImGuiWrapper* imgui) m_iva_blockers.release_geometry(); for (const Triangle& tr : m_triangles) { - if (! tr.valid || tr.is_split() || tr.get_state() == FacetSupportType::NONE) + if (! tr.valid || tr.is_split() || tr.get_state() == EnforcerBlockerType::NONE) continue; - GLIndexedVertexArray& va = tr.get_state() == FacetSupportType::ENFORCER + GLIndexedVertexArray& va = tr.get_state() == EnforcerBlockerType::ENFORCER ? m_iva_enforcers : m_iva_blockers; - int& cnt = tr.get_state() == FacetSupportType::ENFORCER + int& cnt = tr.get_state() == EnforcerBlockerType::ENFORCER ? enf_cnt : blc_cnt; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index ce24ea8d28..e1dee373f2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -15,7 +15,7 @@ namespace Slic3r { -enum class FacetSupportType : int8_t; +enum class EnforcerBlockerType : int8_t; namespace GUI { From 6db1e5ab8fb6bacf0a1e47e7796beb8cc902dfda Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 12 Aug 2020 12:58:23 +0200 Subject: [PATCH 311/503] Slight code cleanup --- src/libslic3r/ExtrusionEntity.hpp | 4 ++-- src/libslic3r/GCode.cpp | 16 ++++------------ 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index 879f564b6c..4749b52622 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -121,8 +121,8 @@ public: // Height of the extrusion, used for visualization purposes. float height; - ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), m_role(role) {}; - ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), m_role(role) {}; + ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), m_role(role) {} + ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), m_role(role) {} ExtrusionPath(const ExtrusionPath& rhs) : polyline(rhs.polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {} ExtrusionPath(ExtrusionPath&& rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {} ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs) : polyline(polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {} diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 7d80677184..62eb4b920c 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2463,7 +2463,7 @@ plot(p2.subs(r,0.2).subs(z,1.), (x, -1, 3), adaptive=False, nb_of_points=400) } } -static Points::iterator project_point_to_polygon_and_insert(Polygon &polygon, const Point &pt, double eps) +static Points::const_iterator project_point_to_polygon_and_insert(Polygon &polygon, const Point &pt, double eps) { assert(polygon.points.size() >= 2); if (polygon.points.size() <= 1) @@ -2651,7 +2651,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou // Insert a projection of last_pos into the polygon. size_t last_pos_proj_idx; { - Points::iterator it = project_point_to_polygon_and_insert(polygon, last_pos, 0.1 * nozzle_r); + auto it = project_point_to_polygon_and_insert(polygon, last_pos, 0.1 * nozzle_r); last_pos_proj_idx = it - polygon.points.begin(); } @@ -2671,11 +2671,9 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou if (was_clockwise) ccwAngle = - ccwAngle; float penalty = 0; -// if (ccwAngle <- float(PI/3.)) if (ccwAngle <- float(0.6 * PI)) // Sharp reflex vertex. We love that, it hides the seam perfectly. penalty = 0.f; -// else if (ccwAngle > float(PI/3.)) else if (ccwAngle > float(0.6 * PI)) // Seams on sharp convex vertices are more visible than on reflex vertices. penalty = penaltyConvexVertex; @@ -2688,7 +2686,6 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou penalty = penaltyConvexVertex + (penaltyFlatSurface - penaltyConvexVertex) * bspline_kernel(ccwAngle * float(PI * 2. / 3.)); } // Give a negative penalty for points close to the last point or the prefered seam location. - //float dist_to_last_pos_proj = last_pos_proj.distance_to(polygon.points[i]); float dist_to_last_pos_proj = (i < last_pos_proj_idx) ? std::min(lengths[last_pos_proj_idx] - lengths[i], lengths.back() - lengths[last_pos_proj_idx] + lengths[i]) : std::min(lengths[i] - lengths[last_pos_proj_idx], lengths.back() - lengths[i] + lengths[last_pos_proj_idx]); @@ -2708,14 +2705,10 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou // Signed distance is positive outside the object, negative inside the object. // The point is considered at an overhang, if it is more than nozzle radius // outside of the lower layer contour. - #ifdef NDEBUG // to suppress unused variable warning in release mode - (*lower_layer_edge_grid)->signed_distance(p, search_r, dist); - #else - bool found = (*lower_layer_edge_grid)->signed_distance(p, search_r, dist); - #endif + [[maybe_unused]] bool found = (*lower_layer_edge_grid)->signed_distance(p, search_r, dist); // If the approximate Signed Distance Field was initialized over lower_layer_edge_grid, // then the signed distnace shall always be known. - assert(found); + assert(found); penalties[i] += extrudate_overlap_penalty(float(nozzle_r), penaltyOverhangHalf, float(dist)); } } @@ -2723,7 +2716,6 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou // Find a point with a minimum penalty. size_t idx_min = std::min_element(penalties.begin(), penalties.end()) - penalties.begin(); - // if (seam_position == spAligned) // For all (aligned, nearest, rear) seams: { // Very likely the weight of idx_min is very close to the weight of last_pos_proj_idx. From bd4e4535f902e1a935a5241b8df187709f7baced Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 18 Aug 2020 12:37:07 +0200 Subject: [PATCH 312/503] GCodeProcessor -> Calculate per layer time estimate --- src/libslic3r/GCode.cpp | 2 + src/libslic3r/GCode/GCodeProcessor.cpp | 55 ++++++++++++++++++++++++-- src/libslic3r/GCode/GCodeProcessor.hpp | 7 ++++ src/slic3r/GUI/GCodeViewer.cpp | 2 +- 4 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 3de70d0611..9e1600cb43 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2095,6 +2095,8 @@ void GCode::process_layer( std::string gcode; #if ENABLE_GCODE_VIEWER + // add tag for processor + gcode += "; " + GCodeProcessor::Layer_Change_Tag + "\n"; // export layer z char buf[64]; sprintf(buf, ";Z:%g\n", print_z); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 6684255918..5124e1a99d 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -24,9 +24,10 @@ namespace Slic3r { const std::string GCodeProcessor::Extrusion_Role_Tag = "TYPE:"; const std::string GCodeProcessor::Height_Tag = "HEIGHT:"; -const std::string GCodeProcessor::Color_Change_Tag = "COLOR CHANGE"; -const std::string GCodeProcessor::Pause_Print_Tag = "PAUSE PRINT"; -const std::string GCodeProcessor::Custom_Code_Tag = "CUSTOM GCODE"; +const std::string GCodeProcessor::Layer_Change_Tag = "LAYER_CHANGE"; +const std::string GCodeProcessor::Color_Change_Tag = "COLOR_CHANGE"; +const std::string GCodeProcessor::Pause_Print_Tag = "PAUSE_PRINT"; +const std::string GCodeProcessor::Custom_Code_Tag = "CUSTOM_GCODE"; const std::string GCodeProcessor::First_Line_M73_Placeholder_Tag = "; _GP_FIRST_LINE_M73_PLACEHOLDER"; const std::string GCodeProcessor::Last_Line_M73_Placeholder_Tag = "; _GP_LAST_LINE_M73_PLACEHOLDER"; @@ -174,6 +175,7 @@ void GCodeProcessor::TimeMachine::reset() g1_times_cache = std::vector(); std::fill(moves_time.begin(), moves_time.end(), 0.0f); std::fill(roles_time.begin(), roles_time.end(), 0.0f); + layers_time = std::vector(); } void GCodeProcessor::TimeMachine::simulate_st_synchronize(float additional_time) @@ -282,6 +284,16 @@ void GCodeProcessor::TimeMachine::calculate_time(size_t keep_last_n_blocks) gcode_time.cache += block_time; moves_time[static_cast(block.move_type)] += block_time; roles_time[static_cast(block.role)] += block_time; + if (block.layer_id > 0) { + if (block.layer_id >= layers_time.size()) { + size_t curr_size = layers_time.size(); + layers_time.resize(block.layer_id); + for (size_t i = curr_size; i < layers_time.size(); ++i) { + layers_time[i] = 0.0f; + } + } + layers_time[block.layer_id - 1] += block_time; + } g1_times_cache.push_back(time); } @@ -347,6 +359,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) std::string line = gcode_line.substr(0, gcode_line.length() - 1); std::string ret; + if (line == First_Line_M73_Placeholder_Tag || line == Last_Line_M73_Placeholder_Tag) { for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { const TimeMachine& machine = machines[i]; @@ -369,9 +382,20 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) } } } + return std::make_pair(!ret.empty(), ret.empty() ? gcode_line : ret); }; + // check for temporary lines + auto is_temporary_decoration = [](const std::string& gcode_line) { + // remove trailing '\n' + std::string line = gcode_line.substr(0, gcode_line.length() - 1); + if (line == "; " + Layer_Change_Tag) + return true; + else + return false; + }; + // add lines M73 to exported gcode auto process_line_G1 = [&]() { for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { @@ -408,9 +432,15 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) } gcode_line += "\n"; + // replace placeholder lines auto [processed, result] = process_placeholders(gcode_line); gcode_line = result; if (!processed) { + // remove temporary lines + if (is_temporary_decoration(gcode_line)) + continue; + + // add lines M73 where needed parser.parse_line(gcode_line, [&](GCodeReader& reader, const GCodeReader::GCodeLine& line) { if (line.cmd_is("G1")) { @@ -677,6 +707,7 @@ void GCodeProcessor::reset() m_filament_diameters = std::vector(Min_Extruder_Count, 1.75f); m_extruded_last_z = 0.0f; + m_layer_id = 0; m_cp_color.reset(); m_producer = EProducer::Unknown; @@ -726,7 +757,7 @@ void GCodeProcessor::process_file(const std::string& filename) m_result.moves.emplace_back(MoveVertex()); m_parser.parse_file(filename, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { process_gcode_line(line); }); - // process the remaining time blocks + // process the time blocks for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { TimeMachine& machine = m_time_processor.machines[i]; TimeMachine::CustomGCodeTime& gcode_time = machine.gcode_time; @@ -804,6 +835,13 @@ std::vector> GCodeProcessor::get_roles_time(Prin return ret; } +std::vector GCodeProcessor::get_layers_time(PrintEstimatedTimeStatistics::ETimeMode mode) const +{ + return (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) ? + m_time_processor.machines[static_cast(mode)].layers_time : + std::vector(); +} + void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) { /* std::cout << line.raw() << std::endl; */ @@ -973,6 +1011,13 @@ void GCodeProcessor::process_tags(const std::string& comment) return; } #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING + + // layer change tag + pos = comment.find(Layer_Change_Tag); + if (pos != comment.npos) { + ++m_layer_id; + return; + } } bool GCodeProcessor::process_producers_tags(const std::string& comment) @@ -1428,6 +1473,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) block.move_type = type; block.role = m_extrusion_role; block.distance = distance; + block.layer_id = m_layer_id; // calculates block cruise feedrate float min_feedrate_factor = 1.0f; @@ -2097,6 +2143,7 @@ void GCodeProcessor::update_estimated_times_stats() data.custom_gcode_times = get_custom_gcode_times(mode, true); data.moves_times = get_moves_time(mode); data.roles_times = get_roles_time(mode); + data.layers_times = get_layers_time(mode); }; update_mode(PrintEstimatedTimeStatistics::ETimeMode::Normal); diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index b19610801c..90552e6582 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -43,12 +43,14 @@ namespace Slic3r { std::vector>> custom_gcode_times; std::vector> moves_times; std::vector> roles_times; + std::vector layers_times; void reset() { time = 0.0f; custom_gcode_times.clear(); moves_times.clear(); roles_times.clear(); + layers_times.clear(); } }; @@ -68,6 +70,7 @@ namespace Slic3r { public: static const std::string Extrusion_Role_Tag; static const std::string Height_Tag; + static const std::string Layer_Change_Tag; static const std::string Color_Change_Tag; static const std::string Pause_Print_Tag; static const std::string Custom_Code_Tag; @@ -142,6 +145,7 @@ namespace Slic3r { EMoveType move_type{ EMoveType::Noop }; ExtrusionRole role{ erNone }; + unsigned int layer_id{ 0 }; float distance{ 0.0f }; // mm float acceleration{ 0.0f }; // mm/s^2 float max_entry_speed{ 0.0f }; // mm/s @@ -192,6 +196,7 @@ namespace Slic3r { std::vector g1_times_cache; std::array(EMoveType::Count)> moves_time; std::array(ExtrusionRole::erCount)> roles_time; + std::vector layers_time; void reset(); @@ -368,6 +373,7 @@ namespace Slic3r { ExtruderColors m_extruder_colors; std::vector m_filament_diameters; float m_extruded_last_z; + unsigned int m_layer_id; CpColor m_cp_color; enum class EProducer @@ -420,6 +426,7 @@ namespace Slic3r { std::vector> get_moves_time(PrintEstimatedTimeStatistics::ETimeMode mode) const; std::vector> get_roles_time(PrintEstimatedTimeStatistics::ETimeMode mode) const; + std::vector get_layers_time(PrintEstimatedTimeStatistics::ETimeMode mode) const; private: void process_gcode_line(const GCodeReader::GCodeLine& line); diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 1a4485402b..ed1d5bc5c9 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -2352,7 +2352,7 @@ void GCodeViewer::render_time_estimate() const imgui.title(_u8L("Estimated printing time")); #endif // GCODE_VIEWER_TIME_ESTIMATE - // mode tabs + // mode tabs ImGui::BeginTabBar("mode_tabs"); const PrintEstimatedTimeStatistics::Mode& normal_mode = m_time_statistics.modes[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Normal)]; if (normal_mode.time > 0.0f) { From 7b5f84b7dfdb9de22bfb87d3d69ab29e021a5a23 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 18 Aug 2020 12:39:46 +0200 Subject: [PATCH 313/503] Extended hover capability in DoubleSlider::Control --- src/slic3r/GUI/DoubleSlider.cpp | 26 ++++++++++++++++++-------- src/slic3r/GUI/DoubleSlider.hpp | 6 ++++++ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index a7cb7d54d1..8a9ac34ea1 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -600,10 +600,8 @@ void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_ const wxString label = get_label(tick); dc.GetMultiLineTextExtent(label, &text_width, &text_height); wxPoint text_pos; - if (right_side) - { - if (is_horizontal()) - { + if (right_side) { + if (is_horizontal()) { int width; int height; get_size(&width, &height); @@ -614,17 +612,21 @@ void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_ } else text_pos = wxPoint(pos.x + m_thumb_size.x + 1, pos.y - 0.5 * text_height - 1); + + // update text rectangle + m_rect_lower_thumb_text = wxRect(text_pos, wxSize(text_width, text_height)); } - else - { - if (is_horizontal()) - { + else { + if (is_horizontal()) { int x = pos.x - text_width - 1; int xx = (x > 0) ? x : pos.x + 1; text_pos = wxPoint(xx, pos.y - m_thumb_size.x / 2 - text_height - 1); } else text_pos = wxPoint(pos.x - text_width - 1 - m_thumb_size.x, pos.y - 0.5 * text_height + 1); + + // update text rectangle + m_rect_higher_thumb_text = wxRect(text_pos, wxSize(text_width, text_height)); } dc.DrawText(label, text_pos); @@ -1206,6 +1208,14 @@ void Control::OnMotion(wxMouseEvent& event) else if (m_mode == SingleExtruder && is_point_in_rect(pos, get_colored_band_rect()) && get_edited_tick_for_position(pos) >= 0 ) m_focus = fiColorBand; + else if (is_point_in_rect(pos, m_rect_lower_thumb)) + m_focus = fiLowerThumb; + else if (is_point_in_rect(pos, m_rect_higher_thumb)) + m_focus = fiHigherThumb; + else if (is_point_in_rect(pos, m_rect_lower_thumb_text)) + m_focus = fiLowerThumbText; + else if (is_point_in_rect(pos, m_rect_higher_thumb_text)) + m_focus = fiHigherThumbText; else { m_focus = fiTick; tick = get_tick_near_point(pos); diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index fc8ebced87..fb87ac4a97 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -44,6 +44,10 @@ enum FocusedItem { fiCogIcon, fiColorBand, fiActionIcon, + fiLowerThumb, + fiHigherThumb, + fiLowerThumbText, + fiHigherThumbText, fiTick }; @@ -360,6 +364,8 @@ private: wxRect m_rect_lower_thumb; wxRect m_rect_higher_thumb; + mutable wxRect m_rect_lower_thumb_text; + mutable wxRect m_rect_higher_thumb_text; wxRect m_rect_tick_action; wxRect m_rect_one_layer_icon; wxRect m_rect_revert_icon; From 5052149b819c438b107a28268e3da0f48f57e8cb Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 18 Aug 2020 13:45:18 +0200 Subject: [PATCH 314/503] Fix build on msvc --- src/libslic3r/Point.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index 84010c7eb1..30a1a4942c 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -88,7 +88,7 @@ inline std::string to_string(const Vec3d &pt) { return std::string("[") + std: std::vector transform(const std::vector& points, const Transform3f& t); Pointf3s transform(const Pointf3s& points, const Transform3d& t); -template using Vec = Eigen::Matrix; +template using Vec = Eigen::Matrix; class Point : public Vec2crd { From 4641d44544389324df278fbe5f062aa6db58382e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 18 Aug 2020 16:49:06 +0200 Subject: [PATCH 315/503] UnsavedChangesDialog : improvements * Processed changes in options with nullable values * Processed changes on the extruders count --- src/slic3r/GUI/Tab.cpp | 37 +++++++++- src/slic3r/GUI/Tab.hpp | 3 + src/slic3r/GUI/UnsavedChangesDialog.cpp | 93 ++++++++++++++++++------- 3 files changed, 106 insertions(+), 27 deletions(-) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index a16222c86b..898890f6e5 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2866,7 +2866,7 @@ void Tab::load_current_preset() } on_presets_changed(); if (printer_technology == ptFFF) { - static_cast(this)->m_initial_extruders_count = static_cast(this)->m_extruders_count; + static_cast(this)->m_initial_extruders_count = static_cast(m_presets->get_selected_preset().config.option("nozzle_diameter"))->values.size(); //static_cast(this)->m_extruders_count; const Preset* parent_preset = m_presets->get_selected_preset_parent(); static_cast(this)->m_sys_extruders_count = parent_preset == nullptr ? 0 : static_cast(parent_preset->config.option("nozzle_diameter"))->values.size(); @@ -3131,6 +3131,10 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, m_dependent_tabs = { Preset::Type::TYPE_SLA_PRINT, Preset::Type::TYPE_SLA_MATERIAL }; } + // check and apply extruders count for printer preset + if (m_type == Preset::TYPE_PRINTER) + static_cast(this)->apply_extruder_cnt_from_cache(); + // check if there is something in the cache to move to the new selected preset if (!m_cache_config.empty()) { m_presets->get_edited_preset().config.apply(m_cache_config); @@ -3184,8 +3188,18 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr } else if (dlg.move_preset()) // move selected changes { + std::vector selected_options = dlg.get_selected_options(); + auto it = std::find(selected_options.begin(), selected_options.end(), "extruders_count"); + if (it != selected_options.end()) { + // erase "extruders_count" option from the list + selected_options.erase(it); + // cache the extruders count + if (m_type == Preset::TYPE_PRINTER) + static_cast(this)->cache_extruder_cnt(); + } + // copy selected options to the cache from edited preset - m_cache_config.apply_only(*m_config, dlg.get_selected_options()); + m_cache_config.apply_only(*m_config, selected_options); } return true; @@ -3593,6 +3607,25 @@ wxSizer* TabPrinter::create_bed_shape_widget(wxWindow* parent) return sizer; } +void TabPrinter::cache_extruder_cnt() +{ + if (m_presets->get_edited_preset().printer_technology() == ptSLA) + return; + + m_cache_extruder_count = m_extruders_count; +} + +void TabPrinter::apply_extruder_cnt_from_cache() +{ + if (m_presets->get_edited_preset().printer_technology() == ptSLA) + return; + + if (m_cache_extruder_count > 0) { + m_presets->get_edited_preset().set_num_extruders(m_cache_extruder_count); + m_cache_extruder_count = 0; + } +} + void Tab::compatible_widget_reload(PresetDependencies &deps) { bool has_any = ! m_config->option(deps.key_list)->values.empty(); diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index a97153f470..9bddebeab1 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -411,6 +411,7 @@ public: size_t m_extruders_count_old = 0; size_t m_initial_extruders_count; size_t m_sys_extruders_count; + size_t m_cache_extruder_count = 0; PrinterTechnology m_printer_technology = ptFFF; @@ -437,6 +438,8 @@ public: bool supports_printer_technology(const PrinterTechnology /* tech */) override { return true; } wxSizer* create_bed_shape_widget(wxWindow* parent); + void cache_extruder_cnt(); + void apply_extruder_cnt_from_cache(); }; class TabSLAMaterial : public Tab diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 2fa89266e2..bebc01c782 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -712,47 +712,71 @@ wxString get_string_from_enum(const std::string& opt_key, const DynamicPrintConf return from_u8(_utf8(names[static_cast(val)])); } -static wxString get_string_value(const std::string& opt_key, const DynamicPrintConfig& config) +static int get_id_from_opt_key(std::string opt_key) { + int pos = opt_key.find("#"); + if (pos > 0) { + boost::erase_head(opt_key, pos + 1); + return atoi(opt_key.c_str()); + } + return 0; +} + +static std::string get_pure_opt_key(std::string opt_key) +{ + int pos = opt_key.find("#"); + if (pos > 0) + boost::erase_tail(opt_key, opt_key.size() - pos); + return opt_key; +} + +static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& config) +{ + int opt_idx = get_id_from_opt_key(opt_key); + opt_key = get_pure_opt_key(opt_key); + + if (config.option(opt_key)->is_nil()) + return _L("N/A"); + wxString out; // FIXME controll, if opt_key has index - int opt_idx = 0; - ConfigOptionType type = config.def()->options.at(opt_key).type; + const ConfigOptionDef* opt = config.def()->get(opt_key); + bool is_nullable = opt->nullable; - switch (type) { + switch (opt->type) { case coInt: return from_u8((boost::format("%1%") % config.opt_int(opt_key)).str()); case coInts: { - const ConfigOptionInts* opt = config.opt(opt_key); - if (opt) - return from_u8((boost::format("%1%") % opt->get_at(opt_idx)).str()); - break; + int val = is_nullable ? + config.opt(opt_key)->get_at(opt_idx) : + config.opt(opt_key)->get_at(opt_idx); + return from_u8((boost::format("%1%") % val).str()); } case coBool: return config.opt_bool(opt_key) ? "true" : "false"; case coBools: { - const ConfigOptionBools* opt = config.opt(opt_key); - if (opt) - return opt->get_at(opt_idx) ? "true" : "false"; - break; + bool val = is_nullable ? + config.opt(opt_key)->get_at(opt_idx) : + config.opt(opt_key)->get_at(opt_idx); + return val ? "true" : "false"; } case coPercent: return from_u8((boost::format("%1%%%") % int(config.optptr(opt_key)->getFloat())).str()); case coPercents: { - const ConfigOptionPercents* opt = config.opt(opt_key); - if (opt) - return from_u8((boost::format("%1%%%") % int(opt->get_at(opt_idx))).str()); - break; + double val = is_nullable ? + config.opt(opt_key)->get_at(opt_idx) : + config.opt(opt_key)->get_at(opt_idx); + return from_u8((boost::format("%1%%%") % int(val)).str()); } case coFloat: return double_to_string(config.opt_float(opt_key)); case coFloats: { - const ConfigOptionFloats* opt = config.opt(opt_key); - if (opt) - return double_to_string(opt->get_at(opt_idx)); - break; + double val = is_nullable ? + config.opt(opt_key)->get_at(opt_idx) : + config.opt(opt_key)->get_at(opt_idx); + return double_to_string(val); } case coString: return from_u8(config.opt_string(opt_key)); @@ -896,7 +920,23 @@ void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* pres m_tree_model->AddPreset(type, from_u8(presets->get_edited_preset().name)); // Collect dirty options. - for (const std::string& opt_key : presets->current_dirty_options()) { + const bool deep_compare = (type == Preset::TYPE_PRINTER || type == Preset::TYPE_SLA_MATERIAL); + auto dirty_options = presets->current_dirty_options(deep_compare); + auto dirty_options_ = presets->current_dirty_options(); + + // process changes of extruders count + if (type == Preset::TYPE_PRINTER && + old_config.opt("extruder_colour")->values.size() != new_config.opt("extruder_colour")->values.size()) { + wxString local_label = _L("Extruders count"); + wxString old_val = from_u8((boost::format("%1%") % old_config.opt("extruder_colour")->values.size()).str()); + wxString new_val = from_u8((boost::format("%1%") % new_config.opt("extruder_colour")->values.size()).str()); + + ItemData item_data = { "extruders_count", local_label, old_val, new_val, type }; + m_items_map.emplace(m_tree_model->AddOption(type, _L("General"), _L("Capabilities"), local_label, old_val, new_val), item_data); + + } + + for (const std::string& opt_key : /*presets->current_dirty_options()*/dirty_options) { const Search::Option& option = searcher.get_option(opt_key); ItemData item_data = { opt_key, option.label_local, get_string_value(opt_key, old_config), get_string_value(opt_key, new_config), type }; @@ -915,9 +955,12 @@ std::vector UnsavedChangesDialog::get_unselected_options(Preset::Ty { std::vector ret; - for (auto item : m_items_map) + for (auto item : m_items_map) { + if (item.second.opt_key == "extruders_count") + continue; if (item.second.type == type && !m_tree_model->IsEnabledItem(item.first)) - ret.emplace_back(item.second.opt_key); + ret.emplace_back(get_pure_opt_key(item.second.opt_key)); + } return ret; } @@ -926,9 +969,9 @@ std::vector UnsavedChangesDialog::get_selected_options() { std::vector ret; - for (auto item : m_items_map) + for (auto item : m_items_map) if (m_tree_model->IsEnabledItem(item.first)) - ret.emplace_back(item.second.opt_key); + ret.emplace_back(get_pure_opt_key(item.second.opt_key)); return ret; } From d91fc7b8ab0f5a3ce284483d2fa339fa04f643c3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 19 Aug 2020 11:25:12 +0200 Subject: [PATCH 316/503] ENABLE_GCODE_VIEWER -> Removed options_120_solid shader --- resources/shaders/options_120_solid.fs | 89 -------------------------- resources/shaders/options_120_solid.vs | 14 ---- src/slic3r/GUI/GCodeViewer.cpp | 6 +- src/slic3r/GUI/GCodeViewer.hpp | 4 +- src/slic3r/GUI/GLShadersManager.cpp | 4 +- 5 files changed, 5 insertions(+), 112 deletions(-) delete mode 100644 resources/shaders/options_120_solid.fs delete mode 100644 resources/shaders/options_120_solid.vs diff --git a/resources/shaders/options_120_solid.fs b/resources/shaders/options_120_solid.fs deleted file mode 100644 index 4480d7b147..0000000000 --- a/resources/shaders/options_120_solid.fs +++ /dev/null @@ -1,89 +0,0 @@ -// version 120 is needed for gl_PointCoord -#version 120 - -#define INTENSITY_CORRECTION 0.6 - -// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) -const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); -#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SHININESS 20.0 - -// normalized values for (1./1.43, 0.2/1.43, 1./1.43) -const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); -#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) - -#define INTENSITY_AMBIENT 0.3 - -uniform vec3 uniform_color; - -uniform ivec4 viewport; -uniform float point_size; -uniform mat4 inv_proj_matrix; - -varying vec3 eye_center; -// x = tainted, y = specular; -vec2 intensity; - -float radius = 0.5 * point_size; - -vec3 eye_position_from_fragment() -{ - // Convert screen coordinates to normalized device coordinates (NDC) - vec4 ndc = vec4((gl_FragCoord.x / viewport.z - 0.5) * 2.0, - (gl_FragCoord.y / viewport.w - 0.5) * 2.0, - (gl_FragCoord.z - 0.5) * 2.0, - gl_FragCoord.w); - // Convert NDC throuch inverse clip coordinates to view coordinates - vec4 clip = inv_proj_matrix * ndc; - return clip.xyz; -} - -vec3 eye_position_on_sphere(vec3 eye_fragment_position) -{ - vec3 eye_dir = normalize(eye_fragment_position); - float a = dot(eye_dir, eye_dir); - float b = 2.0 * dot(-eye_center, eye_dir); - float c = dot(eye_center, eye_center) - radius * radius; - float discriminant = b * b - 4 * a * c; - float t = -(b + sqrt(discriminant)) / (2.0 * a); - return t * eye_dir; -} - -vec4 on_sphere_color(vec3 eye_on_sphere_position) -{ - vec3 eye_normal = normalize(eye_on_sphere_position - eye_center); - - // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. - // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. - float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); - - intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; - intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_on_sphere_position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); - - // Perform the same lighting calculation for the 2nd light source (no specular applied). - NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); - intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; - - return vec4(intensity.y + uniform_color.rgb * intensity.x, 1.0); -} - -float fragment_depth(vec3 eye_pos) -{ - vec4 clip_pos = gl_ProjectionMatrix * vec4(eye_pos, 1.0); - float ndc_depth = clip_pos.z / clip_pos.w; - return ((gl_DepthRange.far - gl_DepthRange.near) * ndc_depth + gl_DepthRange.near + gl_DepthRange.far) / 2.0; -} - -void main() -{ - vec2 pos = (gl_PointCoord - 0.5) * 2.0; - float radius = length(pos); - if (radius > 1.0) - discard; - - vec3 eye_on_sphere_position = eye_position_on_sphere(eye_position_from_fragment()); - -// gl_FragDepth = fragment_depth(eye_on_sphere_position); - gl_FragColor = on_sphere_color(eye_on_sphere_position); -} diff --git a/resources/shaders/options_120_solid.vs b/resources/shaders/options_120_solid.vs deleted file mode 100644 index 745ec8ddd1..0000000000 --- a/resources/shaders/options_120_solid.vs +++ /dev/null @@ -1,14 +0,0 @@ -#version 120 - -uniform float zoom; -uniform float point_size; -uniform float near_plane_height; - -varying vec3 eye_center; - -void main() -{ - eye_center = (gl_ModelViewMatrix * gl_Vertex).xyz; - gl_Position = ftransform(); - gl_PointSize = (gl_Position.w == 1.0) ? zoom * near_plane_height * point_size : near_plane_height * point_size / gl_Position.w; -} diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index ed1d5bc5c9..40c31d257d 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -2479,8 +2479,8 @@ void GCodeViewer::render_statistics() const void GCodeViewer::render_shaders_editor() const { auto set_shader = [this](const std::string& shader) { - unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); - unsigned char end_id = buffer_id(GCodeProcessor::EMoveType::Custom_GCode); + unsigned char begin_id = buffer_id(EMoveType::Retract); + unsigned char end_id = buffer_id(EMoveType::Custom_GCode); for (unsigned char i = begin_id; i <= end_id; ++i) { m_buffers[i].shader = shader; } @@ -2497,7 +2497,6 @@ void GCodeViewer::render_shaders_editor() const if (ImGui::TreeNode("GLSL version")) { ImGui::RadioButton("1.10 (low end PCs)", &m_shaders_editor.points.shader_version, 0); ImGui::RadioButton("1.20 flat (billboards) [default]", &m_shaders_editor.points.shader_version, 1); - ImGui::RadioButton("1.20 solid (spheres)", &m_shaders_editor.points.shader_version, 2); ImGui::TreePop(); } @@ -2505,7 +2504,6 @@ void GCodeViewer::render_shaders_editor() const { case 0: { set_shader("options_110"); break; } case 1: { set_shader("options_120_flat"); break; } - case 2: { set_shader("options_120_solid"); break; } } if (ImGui::TreeNode("Options")) { diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 74506677a6..0be17f790a 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -33,7 +33,7 @@ class GCodeViewer CustomGCodes }; - // vbo buffer containing vertices data for a specific toolpath type + // vbo buffer containing vertices data used to rendder a specific toolpath type struct VBuffer { enum class EFormat : unsigned char @@ -65,7 +65,7 @@ class GCodeViewer void reset(); }; - // ibo buffer containing indices data for a specific toolpath type + // ibo buffer containing indices data (triangles) used to render a specific toolpath type struct IBuffer { // ibo id diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index e62a81d39b..5f726d4ef1 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -35,10 +35,8 @@ std::pair GLShadersManager::init() valid &= append_shader("printbed", { "printbed.vs", "printbed.fs" }); // used to render options in gcode preview valid &= append_shader("options_110", { "options_110.vs", "options_110.fs" }); - if (GUI::wxGetApp().is_glsl_version_greater_or_equal_to(1, 20)) { + if (GUI::wxGetApp().is_glsl_version_greater_or_equal_to(1, 20)) valid &= append_shader("options_120_flat", { "options_120_flat.vs", "options_120_flat.fs" }); - valid &= append_shader("options_120_solid", { "options_120_solid.vs", "options_120_solid.fs" }); - } // used to render extrusion and travel paths in gcode preview valid &= append_shader("toolpaths", { "toolpaths.vs", "toolpaths.fs" }); // used to render objects in 3d editor From 41579db708fa45544db685121a809fc8d81b9f7b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 19 Aug 2020 11:55:18 +0200 Subject: [PATCH 317/503] GCodeViewer -> Use only white texts in legend --- src/slic3r/GUI/GCodeViewer.cpp | 24 ++++++++++++------------ src/slic3r/GUI/ImGuiWrapper.cpp | 2 -- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 40c31d257d..22d8615ca0 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1542,11 +1542,11 @@ void GCodeViewer::render_legend() const #if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND auto append_headers = [&imgui](const std::array& texts, const std::array& offsets) { - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, texts[0]); + imgui.text(texts[0]); ImGui::SameLine(offsets[0]); - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, texts[1]); + imgui.text(texts[1]); ImGui::SameLine(offsets[1]); - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, texts[2]); + imgui.text(texts[2]); ImGui::Separator(); }; @@ -1648,7 +1648,7 @@ void GCodeViewer::render_legend() const if (time_mode.time > 0.0f && (m_view_type == EViewType::FeatureType || (m_view_type == EViewType::ColorPrint && !time_mode.custom_gcode_times.empty()))) { ImGui::AlignTextToFramePadding(); - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Estimated printing time") + ":"); + imgui.text(_u8L("Estimated printing time") + ":"); ImGui::SameLine(); imgui.text(short_time(get_time_dhms(time_mode.time))); @@ -1703,7 +1703,7 @@ void GCodeViewer::render_legend() const case EViewType::Height: { imgui.title(_u8L("Height (mm)")); break; } case EViewType::Width: { imgui.title(_u8L("Width (mm)")); break; } case EViewType::Feedrate: { imgui.title(_u8L("Speed (mm/s)")); break; } - case EViewType::FanSpeed: { imgui.title(_u8L("Fan Speed (%%)")); break; } + case EViewType::FanSpeed: { imgui.title(_u8L("Fan Speed (%)")); break; } case EViewType::VolumetricRate: { imgui.title(_u8L("Volumetric flow rate (mm³/s)")); break; } case EViewType::Tool: { imgui.title(_u8L("Tool")); break; } case EViewType::ColorPrint: { imgui.title(_u8L("Color Print")); break; } @@ -2071,11 +2071,11 @@ void GCodeViewer::render_time_estimate() const using PartialTimes = std::vector; auto append_headers = [&imgui](const Headers& headers, const ColumnOffsets& offsets) { - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, headers[0]); + imgui.text(headers[0]); ImGui::SameLine(offsets[0]); - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, headers[1]); + imgui.text(headers[1]); ImGui::SameLine(offsets[1]); - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, headers[2]); + imgui.text(headers[2]); ImGui::Separator(); }; @@ -2135,7 +2135,7 @@ void GCodeViewer::render_time_estimate() const { case PartialTime::EType::Print: { - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Print")); + imgui.text(_u8L("Print")); ImGui::SameLine(offsets[0]); imgui.text(short_time(get_time_dhms(item.times.second))); ImGui::SameLine(offsets[1]); @@ -2144,7 +2144,7 @@ void GCodeViewer::render_time_estimate() const } case PartialTime::EType::Pause: { - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Pause")); + imgui.text(_u8L("Pause")); ImGui::SameLine(offsets[0]); imgui.text(short_time(get_time_dhms(item.times.second - item.times.first))); break; @@ -2175,7 +2175,7 @@ void GCodeViewer::render_time_estimate() const }; auto append_time_item = [&imgui] (const std::string& label, float time, float percentage, const ImVec4& color, const ColumnOffsets& offsets) { - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); + imgui.text(label); ImGui::SameLine(offsets[0]); imgui.text(short_time(get_time_dhms(time))); ImGui::SameLine(offsets[1]); @@ -2254,7 +2254,7 @@ void GCodeViewer::render_time_estimate() const return ret; }; - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Time") + ":"); + imgui.text(_u8L("Time") + ":"); ImGui::SameLine(); imgui.text(short_time(get_time_dhms(total_time))); append_partial_times(items, partial_times_headers); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index b79f119d45..7c27545026 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -803,9 +803,7 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co void ImGuiWrapper::title(const std::string& str) { - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT); text(str); - ImGui::PopStyleColor(); ImGui::Separator(); } From eca4f0a4cd31279fa27f84490dfb15f72c5d10e3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 19 Aug 2020 12:59:50 +0200 Subject: [PATCH 318/503] GCodeViewer -> Changed layout of sliders in preview --- src/slic3r/GUI/GUI_Preview.cpp | 51 +++++++++++++++++++--------------- src/slic3r/GUI/GUI_Preview.hpp | 1 + 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 2c5a6fe88b..d62b4dd506 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -186,6 +186,7 @@ Preview::Preview( : m_canvas_widget(nullptr) , m_canvas(nullptr) #if ENABLE_GCODE_VIEWER + , m_left_sizer(nullptr) , m_layers_slider_sizer(nullptr) , m_bottom_toolbar_panel(nullptr) #else @@ -237,6 +238,8 @@ bool Preview::init(wxWindow* parent, Model* model) if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 /* disable wxTAB_TRAVERSAL */)) return false; + SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + m_canvas_widget = OpenGLManager::create_wxglcanvas(*this); if (m_canvas_widget == nullptr) return false; @@ -255,9 +258,7 @@ bool Preview::init(wxWindow* parent, Model* model) m_layers_slider_sizer = create_layers_slider_sizer(); m_bottom_toolbar_panel = new wxPanel(this); - m_label_view_type = new wxStaticText(m_bottom_toolbar_panel, wxID_ANY, _L("View")); - m_choice_view_type = new wxChoice(m_bottom_toolbar_panel, wxID_ANY); #else m_double_slider_sizer = new wxBoxSizer(wxHORIZONTAL); @@ -340,15 +341,13 @@ bool Preview::init(wxWindow* parent, Model* model) m_checkbox_legend->SetValue(true); #endif // ENABLE_GCODE_VIEWER - wxBoxSizer* top_sizer = new wxBoxSizer(wxHORIZONTAL); - top_sizer->Add(m_canvas_widget, 1, wxALL | wxEXPAND, 0); #if ENABLE_GCODE_VIEWER - top_sizer->Add(m_layers_slider_sizer, 0, wxEXPAND, 0); -#else - top_sizer->Add(m_double_slider_sizer, 0, wxEXPAND, 0); -#endif // ENABLE_GCODE_VIEWER + m_left_sizer = new wxBoxSizer(wxVERTICAL); + m_left_sizer->Add(m_canvas_widget, 1, wxALL | wxEXPAND, 0); + + wxBoxSizer* right_sizer = new wxBoxSizer(wxVERTICAL); + right_sizer->Add(m_layers_slider_sizer, 1, wxEXPAND, 0); -#if ENABLE_GCODE_VIEWER m_moves_slider = new DoubleSlider::Control(m_bottom_toolbar_panel, wxID_ANY, 0, 0, 0, 100, wxDefaultPosition, wxSize(-1, 3 * GetTextExtent("m").y), wxSL_HORIZONTAL); m_moves_slider->SetDrawMode(DoubleSlider::dmSequentialGCodeView); @@ -366,7 +365,18 @@ bool Preview::init(wxWindow* parent, Model* model) bottom_toolbar_sizer->AddSpacer(5); bottom_toolbar_sizer->Add(m_moves_slider, 1, wxALL | wxEXPAND, 0); m_bottom_toolbar_panel->SetSizer(bottom_toolbar_sizer); + + m_left_sizer->Add(m_bottom_toolbar_panel, 0, wxALL | wxEXPAND, 0); + m_left_sizer->Hide(m_bottom_toolbar_panel); + + wxBoxSizer* main_sizer = new wxBoxSizer(wxHORIZONTAL); + main_sizer->Add(m_left_sizer, 1, wxALL | wxEXPAND, 0); + main_sizer->Add(right_sizer, 0, wxALL | wxEXPAND, 0); #else + wxBoxSizer* top_sizer = new wxBoxSizer(wxHORIZONTAL); + top_sizer->Add(m_canvas_widget, 1, wxALL | wxEXPAND, 0); + top_sizer->Add(m_double_slider_sizer, 0, wxEXPAND, 0); + wxBoxSizer* bottom_sizer = new wxBoxSizer(wxHORIZONTAL); bottom_sizer->Add(m_label_view_type, 0, wxALIGN_CENTER_VERTICAL, 5); bottom_sizer->Add(m_choice_view_type, 0, wxEXPAND | wxALL, 5); @@ -383,16 +393,12 @@ bool Preview::init(wxWindow* parent, Model* model) bottom_sizer->Add(m_checkbox_shells, 0, wxEXPAND | wxALL, 5); bottom_sizer->AddSpacer(20); bottom_sizer->Add(m_checkbox_legend, 0, wxEXPAND | wxALL, 5); -#endif // ENABLE_GCODE_VIEWER wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); main_sizer->Add(top_sizer, 1, wxALL | wxEXPAND, 0); -#if ENABLE_GCODE_VIEWER - main_sizer->Add(m_bottom_toolbar_panel, 0, wxALL | wxEXPAND, 0); - main_sizer->Hide(m_bottom_toolbar_panel); -#else main_sizer->Add(bottom_sizer, 0, wxALL | wxEXPAND, 0); #endif // ENABLE_GCODE_VIEWER + SetSizer(main_sizer); SetMinSize(GetSize()); GetSizer()->SetSizeHints(this); @@ -1233,8 +1239,8 @@ void Preview::load_print_as_fff(bool keep_z_range) { #if ENABLE_GCODE_VIEWER hide_layers_slider(); - GetSizer()->Hide(m_bottom_toolbar_panel); - GetSizer()->Layout(); + m_left_sizer->Hide(m_bottom_toolbar_panel); + m_left_sizer->Layout(); Refresh(); #else reset_sliders(true); @@ -1309,8 +1315,8 @@ void Preview::load_print_as_fff(bool keep_z_range) #if ENABLE_GCODE_VIEWER m_canvas->load_gcode_preview(*m_gcode_result); m_canvas->refresh_gcode_preview(*m_gcode_result, colors); - GetSizer()->Show(m_bottom_toolbar_panel); - GetSizer()->Layout(); + m_left_sizer->Show(m_bottom_toolbar_panel); + m_left_sizer->Layout(); Refresh(); zs = m_canvas->get_gcode_layers_zs(); #else @@ -1321,8 +1327,8 @@ void Preview::load_print_as_fff(bool keep_z_range) // Load the initial preview based on slices, not the final G-code. m_canvas->load_preview(colors, color_print_values); #if ENABLE_GCODE_VIEWER - GetSizer()->Hide(m_bottom_toolbar_panel); - GetSizer()->Layout(); + m_left_sizer->Hide(m_bottom_toolbar_panel); + m_left_sizer->Layout(); Refresh(); zs = m_canvas->get_volumes_print_zs(true); #endif // ENABLE_GCODE_VIEWER @@ -1384,8 +1390,9 @@ void Preview::load_print_as_sla() { m_canvas->load_sla_preview(); #if ENABLE_GCODE_VIEWER - GetSizer()->Hide(m_bottom_toolbar_panel); - GetSizer()->Layout(); + m_left_sizer->Hide(m_bottom_toolbar_panel); + m_left_sizer->Hide(m_bottom_toolbar_panel); + m_left_sizer->Layout(); Refresh(); #else show_hide_ui_elements("none"); diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index c74dccc5c1..ddb7af86fb 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -84,6 +84,7 @@ class Preview : public wxPanel wxGLCanvas* m_canvas_widget; GLCanvas3D* m_canvas; #if ENABLE_GCODE_VIEWER + wxBoxSizer* m_left_sizer; wxBoxSizer* m_layers_slider_sizer; wxPanel* m_bottom_toolbar_panel; #else From 15285a68a0437c1d933d48964e655f6508c7ab5f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 19 Aug 2020 13:04:51 +0200 Subject: [PATCH 319/503] BedShape is extracted to the separate structure --- src/slic3r/GUI/BedShapeDialog.cpp | 102 +++++++++++++++++++++--- src/slic3r/GUI/BedShapeDialog.hpp | 96 ++++++++++++++++++++++ src/slic3r/GUI/UnsavedChangesDialog.cpp | 4 +- 3 files changed, 190 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/BedShapeDialog.cpp b/src/slic3r/GUI/BedShapeDialog.cpp index f08b1a3794..98bd9a267f 100644 --- a/src/slic3r/GUI/BedShapeDialog.cpp +++ b/src/slic3r/GUI/BedShapeDialog.cpp @@ -59,6 +59,81 @@ void BedShapeDialog::on_dpi_changed(const wxRect &suggested_rect) const std::string BedShapePanel::NONE = "None"; const std::string BedShapePanel::EMPTY_STRING = ""; +static std::string get_option_label(BedShape::Parameter param) +{ + switch (param) { + case BedShape::Parameter::RectSize : return L("Size"); + case BedShape::Parameter::RectOrigin: return L("Origin"); + case BedShape::Parameter::Diameter : return L("Diameter"); + default: return ""; + } +} + +void BedShape::append_option_line(ConfigOptionsGroupShp optgroup, Parameter param) +{ + ConfigOptionDef def; + + if (param == Parameter::RectSize) { + def.type = coPoints; + def.set_default_value(new ConfigOptionPoints{ Vec2d(200, 200) }); + def.min = 0; + def.max = 1200; + def.label = get_option_label(param); + def.tooltip = L("Size in X and Y of the rectangular plate."); + + Option option(def, "rect_size"); + optgroup->append_single_option_line(option); + } + else if (param == Parameter::RectOrigin) { + def.type = coPoints; + def.set_default_value(new ConfigOptionPoints{ Vec2d(0, 0) }); + def.min = -600; + def.max = 600; + def.label = get_option_label(param); + def.tooltip = L("Distance of the 0,0 G-code coordinate from the front left corner of the rectangle."); + + Option option(def, "rect_origin"); + optgroup->append_single_option_line(option); + } + else if (param == Parameter::Diameter) { + def.type = coFloat; + def.set_default_value(new ConfigOptionFloat(200)); + def.sidetext = L("mm"); + def.label = get_option_label(param); + def.tooltip = L("Diameter of the print bed. It is assumed that origin (0,0) is located in the center."); + + Option option(def, "diameter"); + optgroup->append_single_option_line(option); + } +} + +wxString BedShape::get_name(Type type) +{ + switch (type) { + case Type::Rectangular : return _L("Rectangular"); + case Type::Circular : return _L("Circular"); + case Type::Custom : return _L("Custom"); + case Type::Invalid : + default : return _L("Invalid"); + } +} + +wxString BedShape::get_full_name_with_params() +{ + wxString out = _L("Shape") + ": " + get_name(type); + + if (type == Type::Rectangular) { + out += "\n" + get_option_label(Parameter::RectSize) + +": [" + ConfigOptionPoint(rectSize).serialize() + "]"; + out += "\n" + get_option_label(Parameter::RectOrigin) + +": [" + ConfigOptionPoint(rectOrigin).serialize() + "]"; + } + else if (type == Type::Circular) + out += "\n" + get_option_label(Parameter::Diameter) + +": [" + double_to_string(diameter) + "]"; + else if (type == Type::Custom) + out += "\n" + double_to_string(diameter); + + return out; +} + void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model) { m_shape = default_pt.values; @@ -72,7 +147,7 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf m_shape_options_book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition, wxSize(25*wxGetApp().em_unit(), -1), wxCHB_TOP); sbsizer->Add(m_shape_options_book); - auto optgroup = init_shape_options_page(_(L("Rectangular"))); +/* auto optgroup = init_shape_options_page(_(L("Rectangular"))); ConfigOptionDef def; def.type = coPoints; def.set_default_value(new ConfigOptionPoints{ Vec2d(200, 200) }); @@ -100,22 +175,31 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf def.tooltip = L("Diameter of the print bed. It is assumed that origin (0,0) is located in the center."); option = Option(def, "diameter"); optgroup->append_single_option_line(option); +*/ + + auto optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Rectangular)); + BedShape::append_option_line(optgroup, BedShape::Parameter::RectSize); + BedShape::append_option_line(optgroup, BedShape::Parameter::RectOrigin); + + optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Circular)); + BedShape::append_option_line(optgroup, BedShape::Parameter::Diameter); + +// optgroup = init_shape_options_page(_(L("Custom"))); + optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Custom)); - optgroup = init_shape_options_page(_(L("Custom"))); Line line{ "", "" }; line.full_width = 1; line.widget = [this](wxWindow* parent) { - wxButton* shape_btn = new wxButton(parent, wxID_ANY, _(L("Load shape from STL..."))); + wxButton* shape_btn = new wxButton(parent, wxID_ANY, _L("Load shape from STL...")); wxSizer* shape_sizer = new wxBoxSizer(wxHORIZONTAL); shape_sizer->Add(shape_btn, 1, wxEXPAND); wxSizer* sizer = new wxBoxSizer(wxVERTICAL); sizer->Add(shape_sizer, 1, wxEXPAND); - shape_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) - { + shape_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) { load_stl(); - })); + }); return sizer; }; @@ -494,8 +578,8 @@ void BedShapePanel::load_stl() if (dialog.ShowModal() != wxID_OK) return; - std::string file_name = dialog.GetPath().ToUTF8().data(); - if (!boost::algorithm::iends_with(file_name, ".stl")) + m_custom_shape = dialog.GetPath().ToUTF8().data(); + if (!boost::algorithm::iends_with(m_custom_shape, ".stl")) { show_error(this, _(L("Invalid file format."))); return; @@ -505,7 +589,7 @@ void BedShapePanel::load_stl() Model model; try { - model = Model::read_from_file(file_name); + model = Model::read_from_file(m_custom_shape); } catch (std::exception &) { show_error(this, _(L("Error! Invalid model"))); diff --git a/src/slic3r/GUI/BedShapeDialog.hpp b/src/slic3r/GUI/BedShapeDialog.hpp index b583eca4a1..9064e7ddca 100644 --- a/src/slic3r/GUI/BedShapeDialog.hpp +++ b/src/slic3r/GUI/BedShapeDialog.hpp @@ -16,6 +16,101 @@ namespace GUI { class ConfigOptionsGroup; using ConfigOptionsGroupShp = std::shared_ptr; + +struct BedShape { + + enum class Type { + Rectangular = 0, + Circular, + Custom, + Invalid + }; + + enum class Parameter { + RectSize, + RectOrigin, + Diameter + }; + + BedShape(const ConfigOptionPoints& points) { + auto polygon = Polygon::new_scale(points.values); + + // is this a rectangle ? + if (points.size() == 4) { + auto lines = polygon.lines(); + if (lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3])) { + // okay, it's a rectangle + // find origin + coordf_t x_min, x_max, y_min, y_max; + x_max = x_min = points.values[0](0); + y_max = y_min = points.values[0](1); + for (auto pt : points.values) + { + x_min = std::min(x_min, pt(0)); + x_max = std::max(x_max, pt(0)); + y_min = std::min(y_min, pt(1)); + y_max = std::max(y_max, pt(1)); + } + + type = Type::Rectangular; + rectSize = Vec2d(x_max - x_min, y_max - y_min); + rectOrigin = Vec2d(-x_min, -y_min); + + return; + } + } + + // is this a circle ? + { + // Analyze the array of points.Do they reside on a circle ? + auto center = polygon.bounding_box().center(); + std::vector vertex_distances; + double avg_dist = 0; + for (auto pt : polygon.points) + { + double distance = (pt - center).cast().norm(); + vertex_distances.push_back(distance); + avg_dist += distance; + } + + avg_dist /= vertex_distances.size(); + bool defined_value = true; + for (auto el : vertex_distances) + { + if (abs(el - avg_dist) > 10 * SCALED_EPSILON) + defined_value = false; + break; + } + if (defined_value) { + // all vertices are equidistant to center + type = Type::Circular; + diameter = unscale(avg_dist * 2); + + return; + } + } + + if (points.size() < 3) { + type = Type::Invalid; + return; + } + + // This is a custom bed shape, use the polygon provided. + type = Type::Custom; + } + + static void append_option_line(ConfigOptionsGroupShp optgroup, Parameter param); + static wxString get_name(Type type); + + wxString get_full_name_with_params(); + + Type type = Type::Invalid; + Vec2d rectSize; + Vec2d rectOrigin; + + double diameter; +}; + class BedShapePanel : public wxPanel { static const std::string NONE; @@ -24,6 +119,7 @@ class BedShapePanel : public wxPanel Bed_2D* m_canvas; std::vector m_shape; std::vector m_loaded_shape; + std::string m_custom_shape; std::string m_custom_texture; std::string m_custom_model; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index bebc01c782..079bd9922a 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -732,16 +732,14 @@ static std::string get_pure_opt_key(std::string opt_key) static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& config) { - int opt_idx = get_id_from_opt_key(opt_key); opt_key = get_pure_opt_key(opt_key); if (config.option(opt_key)->is_nil()) return _L("N/A"); + int opt_idx = get_id_from_opt_key(opt_key); wxString out; - // FIXME controll, if opt_key has index - const ConfigOptionDef* opt = config.def()->get(opt_key); bool is_nullable = opt->nullable; From db77f806819bcb1c395fff338a15b25d67d862a5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 19 Aug 2020 13:14:47 +0200 Subject: [PATCH 320/503] Follow-up of eca4f0a4cd31279fa27f84490dfb15f72c5d10e3. Fixed preview background on all platforms --- src/slic3r/GUI/GUI_Preview.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index d62b4dd506..57b1158f65 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -238,7 +238,14 @@ bool Preview::init(wxWindow* parent, Model* model) if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 /* disable wxTAB_TRAVERSAL */)) return false; +#if ENABLE_GCODE_VIEWER + // to match the background of the sliders +#ifdef _WIN32 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); +#else + SetBackgroundColour(GetParent()->GetBackgroundColour()); +#endif // _WIN32 +#endif // ENABLE_GCODE_VIEWER m_canvas_widget = OpenGLManager::create_wxglcanvas(*this); if (m_canvas_widget == nullptr) From 992d7065b2c456f8aa964969c9e003de7450e60a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 19 Aug 2020 15:19:07 +0200 Subject: [PATCH 321/503] GCodeViewer -> Modified shape of printbed for the unknown size case --- src/slic3r/GUI/3DBed.cpp | 109 +++++++++++---------------------- src/slic3r/GUI/GCodeViewer.cpp | 19 ++++-- 2 files changed, 52 insertions(+), 76 deletions(-) diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 11fa745f49..aa9bf38035 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -45,10 +45,8 @@ bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool float max_y = min_y; unsigned int v_count = 0; - for (const Polygon& t : triangles) - { - for (unsigned int i = 0; i < 3; ++i) - { + for (const Polygon& t : triangles) { + for (unsigned int i = 0; i < 3; ++i) { Vertex& v = m_vertices[v_count]; const Point& p = t.points[i]; @@ -59,8 +57,7 @@ bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool v.position[1] = y; v.position[2] = z; - if (generate_tex_coords) - { + if (generate_tex_coords) { v.tex_coords[0] = x; v.tex_coords[1] = y; @@ -74,17 +71,14 @@ bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool } } - if (generate_tex_coords) - { + if (generate_tex_coords) { float size_x = max_x - min_x; float size_y = max_y - min_y; - if ((size_x != 0.0f) && (size_y != 0.0f)) - { + if ((size_x != 0.0f) && (size_y != 0.0f)) { float inv_size_x = 1.0f / size_x; float inv_size_y = -1.0f / size_y; - for (Vertex& v : m_vertices) - { + for (Vertex& v : m_vertices) { v.tex_coords[0] = (v.tex_coords[0] - min_x) * inv_size_x; v.tex_coords[1] = (v.tex_coords[1] - min_y) * inv_size_y; } @@ -105,8 +99,7 @@ bool GeometryBuffer::set_from_lines(const Lines& lines, float z) m_vertices = std::vector(v_size, Vertex()); unsigned int v_count = 0; - for (const Line& l : lines) - { + for (const Line& l : lines) { Vertex& v1 = m_vertices[v_count]; v1.position[0] = unscale(l.a(0)); v1.position[1] = unscale(l.a(1)); @@ -360,8 +353,7 @@ void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor, void Bed3D::calc_bounding_boxes() const { m_bounding_box = BoundingBoxf3(); - for (const Vec2d& p : m_shape) - { + for (const Vec2d& p : m_shape) { m_bounding_box.merge(Vec3d(p(0), p(1), 0.0)); } @@ -374,8 +366,7 @@ void Bed3D::calc_bounding_boxes() const // extend to contain model, if any BoundingBoxf3 model_bb = m_model.get_bounding_box(); - if (model_bb.defined) - { + if (model_bb.defined) { model_bb.translate(m_model_offset); m_extended_bounding_box.merge(model_bb); } @@ -390,7 +381,7 @@ void Bed3D::calc_bounding_boxes() const void Bed3D::calc_triangles(const ExPolygon& poly) { Polygons triangles; - poly.triangulate(&triangles); + poly.triangulate_p2t(&triangles); if (!m_triangles.set_from_triangles(triangles, GROUND_Z, true)) printf("Unable to create bed triangles\n"); @@ -399,15 +390,13 @@ void Bed3D::calc_triangles(const ExPolygon& poly) void Bed3D::calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox) { Polylines axes_lines; - for (coord_t x = bed_bbox.min(0); x <= bed_bbox.max(0); x += scale_(10.0)) - { + for (coord_t x = bed_bbox.min(0); x <= bed_bbox.max(0); x += scale_(10.0)) { Polyline line; line.append(Point(x, bed_bbox.min(1))); line.append(Point(x, bed_bbox.max(1))); axes_lines.push_back(line); } - for (coord_t y = bed_bbox.min(1); y <= bed_bbox.max(1); y += scale_(10.0)) - { + for (coord_t y = bed_bbox.min(1); y <= bed_bbox.max(1); y += scale_(10.0)) { Polyline line; line.append(Point(bed_bbox.min(0), y)); line.append(Point(bed_bbox.max(0), y)); @@ -482,26 +471,21 @@ void Bed3D::render_system(GLCanvas3D& canvas, bool bottom, bool show_texture) co void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const { - if (m_texture_filename.empty()) - { + if (m_texture_filename.empty()) { m_texture.reset(); render_default(bottom); return; } - if ((m_texture.get_id() == 0) || (m_texture.get_source() != m_texture_filename)) - { + if ((m_texture.get_id() == 0) || (m_texture.get_source() != m_texture_filename)) { m_texture.reset(); - if (boost::algorithm::iends_with(m_texture_filename, ".svg")) - { + if (boost::algorithm::iends_with(m_texture_filename, ".svg")) { // use higher resolution images if graphic card and opengl version allow GLint max_tex_size = OpenGLManager::get_gl_info().get_max_tex_size(); - if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != m_texture_filename)) - { + if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != m_texture_filename)) { // generate a temporary lower resolution texture to show while no main texture levels have been compressed - if (!m_temp_texture.load_from_svg_file(m_texture_filename, false, false, false, max_tex_size / 8)) - { + if (!m_temp_texture.load_from_svg_file(m_texture_filename, false, false, false, max_tex_size / 8)) { render_default(bottom); return; } @@ -509,19 +493,15 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const } // starts generating the main texture, compression will run asynchronously - if (!m_texture.load_from_svg_file(m_texture_filename, true, true, true, max_tex_size)) - { + if (!m_texture.load_from_svg_file(m_texture_filename, true, true, true, max_tex_size)) { render_default(bottom); return; } - } - else if (boost::algorithm::iends_with(m_texture_filename, ".png")) - { + } + else if (boost::algorithm::iends_with(m_texture_filename, ".png")) { // generate a temporary lower resolution texture to show while no main texture levels have been compressed - if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != m_texture_filename)) - { - if (!m_temp_texture.load_from_file(m_texture_filename, false, GLTexture::None, false)) - { + if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != m_texture_filename)) { + if (!m_temp_texture.load_from_file(m_texture_filename, false, GLTexture::None, false)) { render_default(bottom); return; } @@ -529,20 +509,17 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const } // starts generating the main texture, compression will run asynchronously - if (!m_texture.load_from_file(m_texture_filename, true, GLTexture::MultiThreaded, true)) - { + if (!m_texture.load_from_file(m_texture_filename, true, GLTexture::MultiThreaded, true)) { render_default(bottom); return; } } - else - { + else { render_default(bottom); return; } } - else if (m_texture.unsent_compressed_data_available()) - { + else if (m_texture.unsent_compressed_data_available()) { // sends to gpu the already available compressed levels of the main texture m_texture.send_compressed_data_to_gpu(); @@ -554,17 +531,14 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const } - if (m_triangles.get_vertices_count() > 0) - { + if (m_triangles.get_vertices_count() > 0) { GLShaderProgram* shader = wxGetApp().get_shader("printbed"); - if (shader != nullptr) - { + if (shader != nullptr) { shader->start_using(); shader->set_uniform("transparent_background", bottom); shader->set_uniform("svg_source", boost::algorithm::iends_with(m_texture.get_source(), ".svg")); - if (m_vbo_id == 0) - { + if (m_vbo_id == 0) { glsafe(::glGenBuffers(1, &m_vbo_id)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id)); glsafe(::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)m_triangles.get_vertices_data_size(), (const GLvoid*)m_triangles.get_vertices_data(), GL_STATIC_DRAW)); @@ -593,13 +567,11 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const glsafe(::glBindTexture(GL_TEXTURE_2D, tex_id)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id)); - if (position_id != -1) - { + if (position_id != -1) { glsafe(::glEnableVertexAttribArray(position_id)); glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_triangles.get_position_offset())); } - if (tex_coords_id != -1) - { + if (tex_coords_id != -1) { glsafe(::glEnableVertexAttribArray(tex_coords_id)); glsafe(::glVertexAttribPointer(tex_coords_id, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_triangles.get_tex_coords_offset())); } @@ -631,8 +603,7 @@ void Bed3D::render_model() const if (m_model_filename.empty()) return; - if ((m_model.get_filename() != m_model_filename) && m_model.init_from_file(m_model_filename)) - { + if ((m_model.get_filename() != m_model_filename) && m_model.init_from_file(m_model_filename)) { // move the model so that its origin (0.0, 0.0, 0.0) goes into the bed shape center and a bit down to avoid z-fighting with the texture quad Vec3d shift = m_bounding_box.center(); shift(2) = -0.03; @@ -646,11 +617,9 @@ void Bed3D::render_model() const calc_bounding_boxes(); } - if (!m_model.get_filename().empty()) - { + if (!m_model.get_filename().empty()) { GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); - if (shader != nullptr) - { + if (shader != nullptr) { shader->start_using(); #if ENABLE_GCODE_VIEWER shader->set_uniform("uniform_color", m_model_color); @@ -668,8 +637,7 @@ void Bed3D::render_model() const void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture) const { - if (m_texture_filename.empty() && m_model_filename.empty()) - { + if (m_texture_filename.empty() && m_model_filename.empty()) { render_default(bottom); return; } @@ -686,8 +654,7 @@ void Bed3D::render_default(bool bottom) const m_texture.reset(); unsigned int triangles_vcount = m_triangles.get_vertices_count(); - if (triangles_vcount > 0) - { + if (triangles_vcount > 0) { bool has_model = !m_model.get_filename().empty(); glsafe(::glEnable(GL_DEPTH_TEST)); @@ -696,8 +663,7 @@ void Bed3D::render_default(bool bottom) const glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); - if (!has_model && !bottom) - { + if (!has_model && !bottom) { // draw background glsafe(::glDepthMask(GL_FALSE)); #if ENABLE_GCODE_VIEWER @@ -728,8 +694,7 @@ void Bed3D::render_default(bool bottom) const void Bed3D::reset() { - if (m_vbo_id > 0) - { + if (m_vbo_id > 0) { glsafe(::glDeleteBuffers(1, &m_vbo_id)); m_vbo_id = 0; } diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 22d8615ca0..db126552f6 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -343,10 +343,21 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& const double margin = 10.0; Vec2d min(m_paths_bounding_box.min(0) - margin, m_paths_bounding_box.min(1) - margin); Vec2d max(m_paths_bounding_box.max(0) + margin, m_paths_bounding_box.max(1) + margin); - bed_shape = { { min(0), min(1) }, - { max(0), min(1) }, - { max(0), max(1) }, - { min(0), max(1) } }; + + Vec2d size = max - min; + bed_shape = { + { min(0), min(1) }, + { max(0), min(1) }, + { max(0), min(1) + 0.442265 * size[1]}, + { max(0) - 10.0, min(1) + 0.4711325 * size[1]}, + { max(0) + 10.0, min(1) + 0.5288675 * size[1]}, + { max(0), min(1) + 0.557735 * size[1]}, + { max(0), max(1) }, + { min(0) + 0.557735 * size[0], max(1)}, + { min(0) + 0.5288675 * size[0], max(1) - 10.0}, + { min(0) + 0.4711325 * size[0], max(1) + 10.0}, + { min(0) + 0.442265 * size[0], max(1)}, + { min(0), max(1) } }; } wxGetApp().plater()->set_bed_shape(bed_shape, "", "", true); } From 8af49d7d877d5bdf1dac3c0c034c90eb6e3086fa Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 19 Aug 2020 15:35:58 +0200 Subject: [PATCH 322/503] Code refactoring for last commit --- src/slic3r/GUI/BedShapeDialog.cpp | 319 +++++++++++------------- src/slic3r/GUI/BedShapeDialog.hpp | 86 +------ src/slic3r/GUI/UnsavedChangesDialog.cpp | 17 +- 3 files changed, 167 insertions(+), 255 deletions(-) diff --git a/src/slic3r/GUI/BedShapeDialog.cpp b/src/slic3r/GUI/BedShapeDialog.cpp index 98bd9a267f..2fc7d10366 100644 --- a/src/slic3r/GUI/BedShapeDialog.cpp +++ b/src/slic3r/GUI/BedShapeDialog.cpp @@ -21,44 +21,72 @@ namespace Slic3r { namespace GUI { -void BedShapeDialog::build_dialog(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model) +BedShape::BedShape(const ConfigOptionPoints& points) { - SetFont(wxGetApp().normal_font()); - m_panel = new BedShapePanel(this); - m_panel->build_panel(default_pt, custom_texture, custom_model); + auto polygon = Polygon::new_scale(points.values); - auto main_sizer = new wxBoxSizer(wxVERTICAL); - main_sizer->Add(m_panel, 1, wxEXPAND); - main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10); + // is this a rectangle ? + if (points.size() == 4) { + auto lines = polygon.lines(); + if (lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3])) { + // okay, it's a rectangle + // find origin + coordf_t x_min, x_max, y_min, y_max; + x_max = x_min = points.values[0](0); + y_max = y_min = points.values[0](1); + for (auto pt : points.values) + { + x_min = std::min(x_min, pt(0)); + x_max = std::max(x_max, pt(0)); + y_min = std::min(y_min, pt(1)); + y_max = std::max(y_max, pt(1)); + } - SetSizer(main_sizer); - SetMinSize(GetSize()); - main_sizer->SetSizeHints(this); + m_type = Type::Rectangular; + m_rectSize = Vec2d(x_max - x_min, y_max - y_min); + m_rectOrigin = Vec2d(-x_min, -y_min); - this->Bind(wxEVT_CLOSE_WINDOW, ([this](wxCloseEvent& evt) { - EndModal(wxID_CANCEL); - })); + return; + } + } + + // is this a circle ? + { + // Analyze the array of points.Do they reside on a circle ? + auto center = polygon.bounding_box().center(); + std::vector vertex_distances; + double avg_dist = 0; + for (auto pt : polygon.points) + { + double distance = (pt - center).cast().norm(); + vertex_distances.push_back(distance); + avg_dist += distance; + } + + avg_dist /= vertex_distances.size(); + bool defined_value = true; + for (auto el : vertex_distances) + { + if (abs(el - avg_dist) > 10 * SCALED_EPSILON) + defined_value = false; + break; + } + if (defined_value) { + // all vertices are equidistant to center + m_type = Type::Circular; + m_diameter = unscale(avg_dist * 2); + + return; + } + } + + if (points.size() < 3) + return; + + // This is a custom bed shape, use the polygon provided. + m_type = Type::Custom; } -void BedShapeDialog::on_dpi_changed(const wxRect &suggested_rect) -{ - const int& em = em_unit(); - m_panel->m_shape_options_book->SetMinSize(wxSize(25 * em, -1)); - - for (auto og : m_panel->m_optgroups) - og->msw_rescale(); - - const wxSize& size = wxSize(50 * em, -1); - - SetMinSize(size); - SetSize(size); - - Refresh(); -} - -const std::string BedShapePanel::NONE = "None"; -const std::string BedShapePanel::EMPTY_STRING = ""; - static std::string get_option_label(BedShape::Parameter param) { switch (param) { @@ -118,22 +146,73 @@ wxString BedShape::get_name(Type type) } } +size_t BedShape::get_type() +{ + return static_cast(m_type == Type::Invalid ? Type::Rectangular : m_type); +} + wxString BedShape::get_full_name_with_params() { - wxString out = _L("Shape") + ": " + get_name(type); + wxString out = _L("Shape") + ": " + get_name(m_type); - if (type == Type::Rectangular) { - out += "\n" + get_option_label(Parameter::RectSize) + +": [" + ConfigOptionPoint(rectSize).serialize() + "]"; - out += "\n" + get_option_label(Parameter::RectOrigin) + +": [" + ConfigOptionPoint(rectOrigin).serialize() + "]"; + if (m_type == Type::Rectangular) { + out += "\n" + _(get_option_label(Parameter::RectSize)) + ": [" + ConfigOptionPoint(m_rectSize).serialize() + "]"; + out += "\n" + _(get_option_label(Parameter::RectOrigin))+ ": [" + ConfigOptionPoint(m_rectOrigin).serialize() + "]"; } - else if (type == Type::Circular) - out += "\n" + get_option_label(Parameter::Diameter) + +": [" + double_to_string(diameter) + "]"; - else if (type == Type::Custom) - out += "\n" + double_to_string(diameter); + else if (m_type == Type::Circular) + out += "\n" + _L(get_option_label(Parameter::Diameter)) + ": [" + double_to_string(m_diameter) + "]"; return out; } +void BedShape::apply_optgroup_values(ConfigOptionsGroupShp optgroup) +{ + if (m_type == Type::Rectangular || m_type == Type::Invalid) { + optgroup->set_value("rect_size" , new ConfigOptionPoints{ m_rectSize }); + optgroup->set_value("rect_origin" , new ConfigOptionPoints{ m_rectOrigin }); + } + else if (m_type == Type::Circular) + optgroup->set_value("diameter", double_to_string(m_diameter)); +} + +void BedShapeDialog::build_dialog(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model) +{ + SetFont(wxGetApp().normal_font()); + m_panel = new BedShapePanel(this); + m_panel->build_panel(default_pt, custom_texture, custom_model); + + auto main_sizer = new wxBoxSizer(wxVERTICAL); + main_sizer->Add(m_panel, 1, wxEXPAND); + main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10); + + SetSizer(main_sizer); + SetMinSize(GetSize()); + main_sizer->SetSizeHints(this); + + this->Bind(wxEVT_CLOSE_WINDOW, ([this](wxCloseEvent& evt) { + EndModal(wxID_CANCEL); + })); +} + +void BedShapeDialog::on_dpi_changed(const wxRect &suggested_rect) +{ + const int& em = em_unit(); + m_panel->m_shape_options_book->SetMinSize(wxSize(25 * em, -1)); + + for (auto og : m_panel->m_optgroups) + og->msw_rescale(); + + const wxSize& size = wxSize(50 * em, -1); + + SetMinSize(size); + SetSize(size); + + Refresh(); +} + +const std::string BedShapePanel::NONE = "None"; +const std::string BedShapePanel::EMPTY_STRING = ""; + void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model) { m_shape = default_pt.values; @@ -147,36 +226,6 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf m_shape_options_book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition, wxSize(25*wxGetApp().em_unit(), -1), wxCHB_TOP); sbsizer->Add(m_shape_options_book); -/* auto optgroup = init_shape_options_page(_(L("Rectangular"))); - ConfigOptionDef def; - def.type = coPoints; - def.set_default_value(new ConfigOptionPoints{ Vec2d(200, 200) }); - def.min = 0; - def.max = 1200; - def.label = L("Size"); - def.tooltip = L("Size in X and Y of the rectangular plate."); - Option option(def, "rect_size"); - optgroup->append_single_option_line(option); - - def.type = coPoints; - def.set_default_value(new ConfigOptionPoints{ Vec2d(0, 0) }); - def.min = -600; - def.max = 600; - def.label = L("Origin"); - def.tooltip = L("Distance of the 0,0 G-code coordinate from the front left corner of the rectangle."); - option = Option(def, "rect_origin"); - optgroup->append_single_option_line(option); - - optgroup = init_shape_options_page(_(L("Circular"))); - def.type = coFloat; - def.set_default_value(new ConfigOptionFloat(200)); - def.sidetext = L("mm"); - def.label = L("Diameter"); - def.tooltip = L("Diameter of the print bed. It is assumed that origin (0,0) is located in the center."); - option = Option(def, "diameter"); - optgroup->append_single_option_line(option); -*/ - auto optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Rectangular)); BedShape::append_option_line(optgroup, BedShape::Parameter::RectSize); BedShape::append_option_line(optgroup, BedShape::Parameter::RectOrigin); @@ -184,7 +233,6 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Circular)); BedShape::append_option_line(optgroup, BedShape::Parameter::Diameter); -// optgroup = init_shape_options_page(_(L("Custom"))); optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Custom)); Line line{ "", "" }; @@ -233,10 +281,6 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf update_preview(); } -#define SHAPE_RECTANGULAR 0 -#define SHAPE_CIRCULAR 1 -#define SHAPE_CUSTOM 2 - // Called from the constructor. // Create a panel for a rectangular / circular / custom bed shape. ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(const wxString& title) @@ -421,83 +465,18 @@ wxPanel* BedShapePanel::init_model_panel() // with the list of points in the ini file directly. void BedShapePanel::set_shape(const ConfigOptionPoints& points) { - auto polygon = Polygon::new_scale(points.values); + BedShape shape(points); - // is this a rectangle ? - if (points.size() == 4) { - auto lines = polygon.lines(); - if (lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3])) { - // okay, it's a rectangle - // find origin - coordf_t x_min, x_max, y_min, y_max; - x_max = x_min = points.values[0](0); - y_max = y_min = points.values[0](1); - for (auto pt : points.values) - { - x_min = std::min(x_min, pt(0)); - x_max = std::max(x_max, pt(0)); - y_min = std::min(y_min, pt(1)); - y_max = std::max(y_max, pt(1)); - } + m_shape_options_book->SetSelection(shape.get_type()); + shape.apply_optgroup_values(m_optgroups[shape.get_type()]); - auto origin = new ConfigOptionPoints{ Vec2d(-x_min, -y_min) }; + // Copy the polygon to the canvas, make a copy of the array, if custom shape is selected + if (shape.is_custom()) + m_loaded_shape = points.values; - m_shape_options_book->SetSelection(SHAPE_RECTANGULAR); - auto optgroup = m_optgroups[SHAPE_RECTANGULAR]; - optgroup->set_value("rect_size", new ConfigOptionPoints{ Vec2d(x_max - x_min, y_max - y_min) });//[x_max - x_min, y_max - y_min]); - optgroup->set_value("rect_origin", origin); - update_shape(); - return; - } - } - - // is this a circle ? - { - // Analyze the array of points.Do they reside on a circle ? - auto center = polygon.bounding_box().center(); - std::vector vertex_distances; - double avg_dist = 0; - for (auto pt: polygon.points) - { - double distance = (pt - center).cast().norm(); - vertex_distances.push_back(distance); - avg_dist += distance; - } - - avg_dist /= vertex_distances.size(); - bool defined_value = true; - for (auto el: vertex_distances) - { - if (abs(el - avg_dist) > 10 * SCALED_EPSILON) - defined_value = false; - break; - } - if (defined_value) { - // all vertices are equidistant to center - m_shape_options_book->SetSelection(SHAPE_CIRCULAR); - auto optgroup = m_optgroups[SHAPE_CIRCULAR]; - boost::any ret = wxNumberFormatter::ToString(unscale(avg_dist * 2), 0); - optgroup->set_value("diameter", ret); - update_shape(); - return; - } - } - - if (points.size() < 3) { - // Invalid polygon.Revert to default bed dimensions. - m_shape_options_book->SetSelection(SHAPE_RECTANGULAR); - auto optgroup = m_optgroups[SHAPE_RECTANGULAR]; - optgroup->set_value("rect_size", new ConfigOptionPoints{ Vec2d(200, 200) }); - optgroup->set_value("rect_origin", new ConfigOptionPoints{ Vec2d(0, 0) }); - update_shape(); - return; - } - - // This is a custom bed shape, use the polygon provided. - m_shape_options_book->SetSelection(SHAPE_CUSTOM); - // Copy the polygon to the canvas, make a copy of the array. - m_loaded_shape = points.values; update_shape(); + + return; } void BedShapePanel::update_preview() @@ -510,21 +489,20 @@ void BedShapePanel::update_preview() void BedShapePanel::update_shape() { auto page_idx = m_shape_options_book->GetSelection(); - if (page_idx == SHAPE_RECTANGULAR) { + auto opt_group = m_optgroups[page_idx]; + + BedShape::Type page_type = static_cast(page_idx); + + if (page_type == BedShape::Type::Rectangular) { Vec2d rect_size(Vec2d::Zero()); Vec2d rect_origin(Vec2d::Zero()); - try{ - rect_size = boost::any_cast(m_optgroups[SHAPE_RECTANGULAR]->get_value("rect_size")); } - catch (const std::exception & /* e */) { - return; - } - try { - rect_origin = boost::any_cast(m_optgroups[SHAPE_RECTANGULAR]->get_value("rect_origin")); - } - catch (const std::exception & /* e */) { - return; - } - + + try { rect_size = boost::any_cast(opt_group->get_value("rect_size")); } + catch (const std::exception& /* e */) { return; } + + try { rect_origin = boost::any_cast(opt_group->get_value("rect_origin")); } + catch (const std::exception & /* e */) { return; } + auto x = rect_size(0); auto y = rect_size(1); // empty strings or '-' or other things @@ -546,14 +524,11 @@ void BedShapePanel::update_shape() Vec2d(x1, y1), Vec2d(x0, y1) }; } - else if(page_idx == SHAPE_CIRCULAR) { + else if (page_type == BedShape::Type::Circular) { double diameter; - try{ - diameter = boost::any_cast(m_optgroups[SHAPE_CIRCULAR]->get_value("diameter")); - } - catch (const std::exception & /* e */) { - return; - } + try { diameter = boost::any_cast(opt_group->get_value("diameter")); } + catch (const std::exception & /* e */) { return; } + if (diameter == 0.0) return ; auto r = diameter / 2; auto twopi = 2 * PI; @@ -565,7 +540,7 @@ void BedShapePanel::update_shape() } m_shape = points; } - else if (page_idx == SHAPE_CUSTOM) + else if (page_type == BedShape::Type::Custom) m_shape = m_loaded_shape; update_preview(); @@ -578,8 +553,8 @@ void BedShapePanel::load_stl() if (dialog.ShowModal() != wxID_OK) return; - m_custom_shape = dialog.GetPath().ToUTF8().data(); - if (!boost::algorithm::iends_with(m_custom_shape, ".stl")) + std::string file_name = dialog.GetPath().ToUTF8().data(); + if (!boost::algorithm::iends_with(file_name, ".stl")) { show_error(this, _(L("Invalid file format."))); return; @@ -589,7 +564,7 @@ void BedShapePanel::load_stl() Model model; try { - model = Model::read_from_file(m_custom_shape); + model = Model::read_from_file(file_name); } catch (std::exception &) { show_error(this, _(L("Error! Invalid model"))); diff --git a/src/slic3r/GUI/BedShapeDialog.hpp b/src/slic3r/GUI/BedShapeDialog.hpp index 9064e7ddca..2cfbc73aec 100644 --- a/src/slic3r/GUI/BedShapeDialog.hpp +++ b/src/slic3r/GUI/BedShapeDialog.hpp @@ -17,8 +17,8 @@ class ConfigOptionsGroup; using ConfigOptionsGroupShp = std::shared_ptr; -struct BedShape { - +struct BedShape +{ enum class Type { Rectangular = 0, Circular, @@ -32,83 +32,24 @@ struct BedShape { Diameter }; - BedShape(const ConfigOptionPoints& points) { - auto polygon = Polygon::new_scale(points.values); + BedShape(const ConfigOptionPoints& points); - // is this a rectangle ? - if (points.size() == 4) { - auto lines = polygon.lines(); - if (lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3])) { - // okay, it's a rectangle - // find origin - coordf_t x_min, x_max, y_min, y_max; - x_max = x_min = points.values[0](0); - y_max = y_min = points.values[0](1); - for (auto pt : points.values) - { - x_min = std::min(x_min, pt(0)); - x_max = std::max(x_max, pt(0)); - y_min = std::min(y_min, pt(1)); - y_max = std::max(y_max, pt(1)); - } - - type = Type::Rectangular; - rectSize = Vec2d(x_max - x_min, y_max - y_min); - rectOrigin = Vec2d(-x_min, -y_min); - - return; - } - } - - // is this a circle ? - { - // Analyze the array of points.Do they reside on a circle ? - auto center = polygon.bounding_box().center(); - std::vector vertex_distances; - double avg_dist = 0; - for (auto pt : polygon.points) - { - double distance = (pt - center).cast().norm(); - vertex_distances.push_back(distance); - avg_dist += distance; - } - - avg_dist /= vertex_distances.size(); - bool defined_value = true; - for (auto el : vertex_distances) - { - if (abs(el - avg_dist) > 10 * SCALED_EPSILON) - defined_value = false; - break; - } - if (defined_value) { - // all vertices are equidistant to center - type = Type::Circular; - diameter = unscale(avg_dist * 2); - - return; - } - } - - if (points.size() < 3) { - type = Type::Invalid; - return; - } - - // This is a custom bed shape, use the polygon provided. - type = Type::Custom; - } + bool is_custom() { return m_type == Type::Custom; } static void append_option_line(ConfigOptionsGroupShp optgroup, Parameter param); static wxString get_name(Type type); + // convert Type to size_t + size_t get_type(); + wxString get_full_name_with_params(); + void apply_optgroup_values(ConfigOptionsGroupShp optgroup); - Type type = Type::Invalid; - Vec2d rectSize; - Vec2d rectOrigin; - - double diameter; +private: + Type m_type {Type::Invalid}; + Vec2d m_rectSize {200, 200}; + Vec2d m_rectOrigin {0, 0}; + double m_diameter {0}; }; class BedShapePanel : public wxPanel @@ -119,7 +60,6 @@ class BedShapePanel : public wxPanel Bed_2D* m_canvas; std::vector m_shape; std::vector m_loaded_shape; - std::string m_custom_shape; std::string m_custom_texture; std::string m_custom_model; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 079bd9922a..c147d3e2c2 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -667,10 +667,10 @@ void UnsavedChangesDialog::show_info_line(Action action, std::string preset_name text = _L("All changed options will be reverted."); else { if (action == Action::Save && preset_name.empty()) - text = _L("After press this button selected options will be saved"); + text = _L("Press to save the selected options"); else { std::string act_string = action == Action::Save ? _u8L("saved") : _u8L("moved"); - text = from_u8((boost::format("After press this button selected options will be %1% to the preset \"%2%\".") % act_string % preset_name).str()); + text = from_u8((boost::format("Press to %1% selected options to the preset \"%2%\".") % act_string % preset_name).str()); } text += "\n" + _L("Unselected options will be reverted."); } @@ -732,12 +732,12 @@ static std::string get_pure_opt_key(std::string opt_key) static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& config) { + int opt_idx = get_id_from_opt_key(opt_key); opt_key = get_pure_opt_key(opt_key); if (config.option(opt_key)->is_nil()) return _L("N/A"); - int opt_idx = get_id_from_opt_key(opt_key); wxString out; const ConfigOptionDef* opt = config.def()->get(opt_key); @@ -820,15 +820,12 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& break; } case coPoints: { - /* if (opt_key == "bed_shape") { - config.option(opt_key)->values = boost::any_cast>(value); - break; + BedShape shape(*config.option(opt_key)); + return shape.get_full_name_with_params(); } - ConfigOptionPoints* vec_new = new ConfigOptionPoints{ boost::any_cast(value) }; - config.option(opt_key)->set_at(vec_new, opt_index, 0); - */ - return "Points"; + Vec2d val = config.opt(opt_key)->get_at(opt_idx); + return from_u8((boost::format("[%1%]") % ConfigOptionPoint(val).serialize()).str()); } default: break; From 739cd2a4a20f50dace8cd053b671f3451fb9160a Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 19 Aug 2020 17:15:01 +0200 Subject: [PATCH 323/503] Fixed several indentation-related warnings --- src/libslic3r/PrintBase.hpp | 12 ++++++------ src/slic3r/GUI/Plater.cpp | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index 5e94e011a7..647c24c1ce 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -507,9 +507,9 @@ protected: bool set_started(PrintStepEnum step) { return m_state.set_started(step, this->state_mutex(), [this](){ this->throw_if_canceled(); }); } PrintStateBase::TimeStamp set_done(PrintStepEnum step) { std::pair status = m_state.set_done(step, this->state_mutex(), [this](){ this->throw_if_canceled(); }); - if (status.second) - this->status_update_warnings(this->id(), static_cast(step), PrintStateBase::WarningLevel::NON_CRITICAL, std::string()); - return status.first; + if (status.second) + this->status_update_warnings(this->id(), static_cast(step), PrintStateBase::WarningLevel::NON_CRITICAL, std::string()); + return status.first; } bool invalidate_step(PrintStepEnum step) { return m_state.invalidate(step, this->cancel_callback()); } @@ -556,9 +556,9 @@ protected: { return m_state.set_started(step, PrintObjectBase::state_mutex(m_print), [this](){ this->throw_if_canceled(); }); } PrintStateBase::TimeStamp set_done(PrintObjectStepEnum step) { std::pair status = m_state.set_done(step, PrintObjectBase::state_mutex(m_print), [this](){ this->throw_if_canceled(); }); - if (status.second) - this->status_update_warnings(m_print, static_cast(step), PrintStateBase::WarningLevel::NON_CRITICAL, std::string()); - return status.first; + if (status.second) + this->status_update_warnings(m_print, static_cast(step), PrintStateBase::WarningLevel::NON_CRITICAL, std::string()); + return status.first; } bool invalidate_step(PrintObjectStepEnum step) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 2c330b60e6..027611750a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2841,7 +2841,7 @@ void Plater::priv::export_gcode(fs::path output_path, bool output_path_on_remova if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) != 0) return; - show_warning_dialog = true; + show_warning_dialog = true; if (! output_path.empty()) { background_process.schedule_export(output_path.string(), output_path_on_removable_media); } else { @@ -4697,8 +4697,8 @@ void Plater::export_gcode(bool prefer_removable) if (p->model.objects.empty()) return; - if (p->process_completed_with_error)//here - return; + if (p->process_completed_with_error)//here + return; // If possible, remove accents from accented latin characters. // This function is useful for generating file names to be processed by legacy firmwares. From c29171790930a1a9f9b0374b6a5ab8ccec1e88a9 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 18 Aug 2020 15:17:26 +0200 Subject: [PATCH 324/503] Forbid translation of objects when SLA/Hollow/FDM gizmos are active --- src/slic3r/GUI/GLCanvas3D.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 9bed5fde7c..94f6f6ef32 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3658,6 +3658,13 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { m_mouse.dragging = true; + // Translation of objects is forbidden when SLA supports/hollowing/fdm + // supports gizmo is active. + if (m_gizmos.get_current_type() == GLGizmosManager::SlaSupports + || m_gizmos.get_current_type() == GLGizmosManager::FdmSupports + || m_gizmos.get_current_type() == GLGizmosManager::Hollow) + return; + Vec3d cur_pos = m_mouse.drag.start_position_3D; // we do not want to translate objects if the user just clicked on an object while pressing shift to remove it from the selection and then drag if (m_selection.contains_volume(get_first_hover_volume_idx())) From 320964a68c0004a59972ba9ec841b0572ef783e4 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 18 Aug 2020 15:18:00 +0200 Subject: [PATCH 325/503] TriangleSelector paints continuously when dragging fast Previously there would be distinct circles with gaps in between --- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 195 +++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp | 1 + 2 files changed, 110 insertions(+), 86 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 309c7cf423..e5a648d11e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -314,106 +314,128 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous const ModelInstance* mi = mo->instances[selection.get_instance_idx()]; const Transform3d& instance_trafo = mi->get_transformation().get_matrix(); - std::vector>> hit_positions_and_facet_ids; - bool clipped_mesh_was_hit = false; + // List of mouse positions that will be used as seeds for painting. + std::vector mouse_positions{mouse_position}; - Vec3f normal = Vec3f::Zero(); - Vec3f hit = Vec3f::Zero(); - size_t facet = 0; - Vec3f closest_hit = Vec3f::Zero(); - double closest_hit_squared_distance = std::numeric_limits::max(); - size_t closest_facet = 0; - int closest_hit_mesh_id = -1; + // In case current mouse position is far from the last one, + // add several positions from between into the list, so there + // are no gaps in the painted region. + { + if (m_last_mouse_position == Vec2d::Zero()) + m_last_mouse_position = mouse_position; + // resolution describes minimal distance limit using circle radius + // as a unit (e.g., 2 would mean the patches will be touching). + double resolution = 0.7; + double diameter_px = resolution * m_cursor_radius * camera.get_zoom(); + int patches_in_between = int(((mouse_position - m_last_mouse_position).norm() - diameter_px) / diameter_px); + if (patches_in_between > 0) { + Vec2d diff = (mouse_position - m_last_mouse_position)/(patches_in_between+1); + for (int i=1; i<=patches_in_between; ++i) + mouse_positions.emplace_back(m_last_mouse_position + i*diff); + } + } + m_last_mouse_position = Vec2d::Zero(); // only actual hits should be saved - // Transformations of individual meshes - std::vector trafo_matrices; + // Now "click" into all the prepared points and spill paint around them. + for (const Vec2d& mp : mouse_positions) { + std::vector>> hit_positions_and_facet_ids; + bool clipped_mesh_was_hit = false; - int mesh_id = -1; - // Cast a ray on all meshes, pick the closest hit and save it for the respective mesh - for (const ModelVolume* mv : mo->volumes) { - if (! mv->is_model_part()) - continue; + Vec3f normal = Vec3f::Zero(); + Vec3f hit = Vec3f::Zero(); + size_t facet = 0; + Vec3f closest_hit = Vec3f::Zero(); + double closest_hit_squared_distance = std::numeric_limits::max(); + size_t closest_facet = 0; + int closest_hit_mesh_id = -1; - ++mesh_id; + // Transformations of individual meshes + std::vector trafo_matrices; - trafo_matrices.push_back(instance_trafo * mv->get_matrix()); - hit_positions_and_facet_ids.push_back(std::vector>()); - - if (m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh( - mouse_position, - trafo_matrices[mesh_id], - camera, - hit, - normal, - m_clipping_plane.get(), - &facet)) - { - // In case this hit is clipped, skip it. - if (is_mesh_point_clipped(hit.cast())) { - clipped_mesh_was_hit = true; + int mesh_id = -1; + // Cast a ray on all meshes, pick the closest hit and save it for the respective mesh + for (const ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) continue; - } - // Is this hit the closest to the camera so far? - double hit_squared_distance = (camera.get_position()-trafo_matrices[mesh_id]*hit.cast()).squaredNorm(); - if (hit_squared_distance < closest_hit_squared_distance) { - closest_hit_squared_distance = hit_squared_distance; - closest_facet = facet; - closest_hit_mesh_id = mesh_id; - closest_hit = hit; + ++mesh_id; + + trafo_matrices.push_back(instance_trafo * mv->get_matrix()); + hit_positions_and_facet_ids.push_back(std::vector>()); + + if (m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh( + mp, + trafo_matrices[mesh_id], + camera, + hit, + normal, + m_clipping_plane.get(), + &facet)) + { + // In case this hit is clipped, skip it. + if (is_mesh_point_clipped(hit.cast())) { + clipped_mesh_was_hit = true; + continue; + } + + // Is this hit the closest to the camera so far? + double hit_squared_distance = (camera.get_position()-trafo_matrices[mesh_id]*hit.cast()).squaredNorm(); + if (hit_squared_distance < closest_hit_squared_distance) { + closest_hit_squared_distance = hit_squared_distance; + closest_facet = facet; + closest_hit_mesh_id = mesh_id; + closest_hit = hit; + } } } - } - bool dragging_while_painting = (action == SLAGizmoEventType::Dragging && m_button_down != Button::None); + bool dragging_while_painting = (action == SLAGizmoEventType::Dragging && m_button_down != Button::None); - // The mouse button click detection is enabled when there is a valid hit - // or when the user clicks the clipping plane. Missing the object entirely - // shall not capture the mouse. - if (closest_hit_mesh_id != -1 || clipped_mesh_was_hit) { - if (m_button_down == Button::None) - m_button_down = ((action == SLAGizmoEventType::LeftDown) ? Button::Left : Button::Right); - } - - if (closest_hit_mesh_id == -1) { - // In case we have no valid hit, we can return. The event will - // be stopped in following two cases: - // 1. clicking the clipping plane - // 2. dragging while painting (to prevent scene rotations and moving the object) - return clipped_mesh_was_hit - || dragging_while_painting; - } - - // Find respective mesh id. - // FIXME We need a separate TriangleSelector for each volume mesh. - mesh_id = -1; - //const TriangleMesh* mesh = nullptr; - for (const ModelVolume* mv : mo->volumes) { - if (! mv->is_model_part()) - continue; - ++mesh_id; - if (mesh_id == closest_hit_mesh_id) { - //mesh = &mv->mesh(); - break; + // The mouse button click detection is enabled when there is a valid hit + // or when the user clicks the clipping plane. Missing the object entirely + // shall not capture the mouse. + if (closest_hit_mesh_id != -1 || clipped_mesh_was_hit) { + if (m_button_down == Button::None) + m_button_down = ((action == SLAGizmoEventType::LeftDown) ? Button::Left : Button::Right); } + + if (closest_hit_mesh_id == -1) { + // In case we have no valid hit, we can return. The event will + // be stopped in following two cases: + // 1. clicking the clipping plane + // 2. dragging while painting (to prevent scene rotations and moving the object) + return clipped_mesh_was_hit + || dragging_while_painting; + } + + // Find respective mesh id. + mesh_id = -1; + for (const ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) + continue; + ++mesh_id; + if (mesh_id == closest_hit_mesh_id) + break; + } + + const Transform3d& trafo_matrix = trafo_matrices[mesh_id]; + + // Calculate how far can a point be from the line (in mesh coords). + // FIXME: The scaling of the mesh can be non-uniform. + const Vec3d sf = Geometry::Transformation(trafo_matrix).get_scaling_factor(); + const float avg_scaling = (sf(0) + sf(1) + sf(2))/3.; + const float limit = m_cursor_radius/avg_scaling; + + // Calculate direction from camera to the hit (in mesh coords): + Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast(); + Vec3f dir = (closest_hit - camera_pos).normalized(); + + assert(mesh_id < int(m_triangle_selectors.size())); + m_triangle_selectors[mesh_id]->select_patch(closest_hit, closest_facet, camera_pos, + dir, limit, new_state); + m_last_mouse_position = mouse_position; } - const Transform3d& trafo_matrix = trafo_matrices[mesh_id]; - - // Calculate how far can a point be from the line (in mesh coords). - // FIXME: The scaling of the mesh can be non-uniform. - const Vec3d sf = Geometry::Transformation(trafo_matrix).get_scaling_factor(); - const float avg_scaling = (sf(0) + sf(1) + sf(2))/3.; - const float limit = m_cursor_radius/avg_scaling; - - // Calculate direction from camera to the hit (in mesh coords): - Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast(); - Vec3f dir = (closest_hit - camera_pos).normalized(); - - assert(mesh_id < int(m_triangle_selectors.size())); - m_triangle_selectors[mesh_id]->select_patch(closest_hit, closest_facet, camera_pos, - dir, limit, new_state); - return true; } @@ -430,6 +452,7 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous update_model_object(); m_button_down = Button::None; + m_last_mouse_position = Vec2d::Zero(); return true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index ce24ea8d28..350f7c8908 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -92,6 +92,7 @@ private: bool m_setting_angle = false; bool m_internal_stack_active = false; bool m_schedule_update = false; + Vec2d m_last_mouse_position = Vec2d::Zero(); // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. From 7a093b08fd12537a347cbafeabe8307c3a295d55 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 21 Aug 2020 10:59:07 +0200 Subject: [PATCH 326/503] GCodeViewer -> Show printbed model and texture for system printers detected when loading gcode files produced by PrusaSlicer --- src/libslic3r/GCode/GCodeProcessor.cpp | 4 ++++ src/libslic3r/GCode/GCodeProcessor.hpp | 1 + src/libslic3r/Preset.cpp | 20 ++++++++++++++++++++ src/libslic3r/Preset.hpp | 4 ++++ src/slic3r/GUI/3DBed.cpp | 7 +++++++ src/slic3r/GUI/GCodeViewer.cpp | 17 +++++++++++++++-- 6 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 5124e1a99d..54addbd979 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -541,6 +541,10 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) if (bed_shape != nullptr) m_result.bed_shape = bed_shape->values; + const ConfigOptionString* printer_settings_id = config.option("printer_settings_id"); + if (printer_settings_id != nullptr) + m_result.printer_settings_id = printer_settings_id->value; + const ConfigOptionFloats* filament_diameters = config.option("filament_diameter"); if (filament_diameters != nullptr) { for (double diam : filament_diameters->values) { diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 90552e6582..22aeed7620 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -261,6 +261,7 @@ namespace Slic3r { unsigned int id; std::vector moves; Pointfs bed_shape; + std::string printer_settings_id; std::vector extruder_colors; PrintEstimatedTimeStatistics time_statistics; diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 7176ca81a4..a5160d2db3 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1812,6 +1812,26 @@ namespace PresetUtils { } return out; } + +#if ENABLE_GCODE_VIEWER + std::string system_printer_bed_model(const Preset& preset) + { + std::string out; + const VendorProfile::PrinterModel* pm = PresetUtils::system_printer_model(preset); + if (pm != nullptr && !pm->bed_model.empty()) + out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_model; + return out; + } + + std::string system_printer_bed_texture(const Preset& preset) + { + std::string out; + const VendorProfile::PrinterModel* pm = PresetUtils::system_printer_model(preset); + if (pm != nullptr && !pm->bed_texture.empty()) + out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_texture; + return out; + } +#endif // ENABLE_GCODE_VIEWER } // namespace PresetUtils } // namespace Slic3r diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index e34fca4dd7..30edfc859a 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -527,6 +527,10 @@ public: namespace PresetUtils { // PrinterModel of a system profile, from which this preset is derived, or null if it is not derived from a system profile. const VendorProfile::PrinterModel* system_printer_model(const Preset &preset); +#if ENABLE_GCODE_VIEWER + std::string system_printer_bed_model(const Preset& preset); + std::string system_printer_bed_texture(const Preset& preset); +#endif // ENABLE_GCODE_VIEWER } // namespace PresetUtils diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index aa9bf38035..8a29d08bd8 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -414,6 +414,7 @@ void Bed3D::calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox) printf("Unable to create bed grid lines\n"); } +#if !ENABLE_GCODE_VIEWER static std::string system_print_bed_model(const Preset &preset) { std::string out; @@ -431,6 +432,7 @@ static std::string system_print_bed_texture(const Preset &preset) out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_texture; return out; } +#endif // !ENABLE_GCODE_VIEWER std::tuple Bed3D::detect_type(const Pointfs& shape) const { @@ -440,8 +442,13 @@ std::tuple Bed3D::detect_type(const Poin while (curr != nullptr) { if (curr->config.has("bed_shape")) { if (shape == dynamic_cast(curr->config.option("bed_shape"))->values) { +#if ENABLE_GCODE_VIEWER + std::string model_filename = PresetUtils::system_printer_bed_model(*curr); + std::string texture_filename = PresetUtils::system_printer_bed_texture(*curr); +#else std::string model_filename = system_print_bed_model(*curr); std::string texture_filename = system_print_bed_texture(*curr); +#endif // ENABLE_GCODE_VIEWER if (!model_filename.empty() && !texture_filename.empty()) return { System, model_filename, texture_filename }; } diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index db126552f6..80a5e15ed5 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -335,9 +335,21 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& load_shells(print, initialized); else { Pointfs bed_shape; - if (!gcode_result.bed_shape.empty()) + std::string texture; + std::string model; + + if (!gcode_result.bed_shape.empty()) { // bed shape detected in the gcode bed_shape = gcode_result.bed_shape; + auto bundle = wxGetApp().preset_bundle; + if (bundle != nullptr && !gcode_result.printer_settings_id.empty()) { + const Preset* preset = bundle->printers.find_preset(gcode_result.printer_settings_id); + if (preset != nullptr) { + model = PresetUtils::system_printer_bed_model(*preset); + texture = PresetUtils::system_printer_bed_texture(*preset); + } + } + } else { // adjust printbed size in dependence of toolpaths bbox const double margin = 10.0; @@ -359,7 +371,8 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& { min(0) + 0.442265 * size[0], max(1)}, { min(0), max(1) } }; } - wxGetApp().plater()->set_bed_shape(bed_shape, "", "", true); + + wxGetApp().plater()->set_bed_shape(bed_shape, texture, model, gcode_result.bed_shape.empty()); } #if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE From 99a15af03d75c976264364d2ec25cb99948dc3c9 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 21 Aug 2020 11:36:08 +0200 Subject: [PATCH 327/503] GCodeViewer -> Allow to switch to gcode viewer state when an sla printer is selected --- src/slic3r/GUI/MainFrame.cpp | 16 ++++++++++++++-- src/slic3r/GUI/MainFrame.hpp | 1 + src/slic3r/GUI/Plater.cpp | 15 +++++++++++++++ src/slic3r/GUI/Plater.hpp | 4 ++++ 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 3f000e3328..ce8772eedc 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -434,6 +434,10 @@ void MainFrame::shutdown() // restore sidebar if it was hidden when switching to gcode viewer mode if (m_restore_from_gcode_viewer.collapsed_sidebar) m_plater->collapse_sidebar(false); + + // restore sla printer if it was deselected when switching to gcode viewer mode + if (m_restore_from_gcode_viewer.sla_technology) + m_plater->set_printer_technology(ptSLA); #endif // ENABLE_GCODE_VIEWER // Stop the background thread (Windows and Linux). // Disconnect from a 3DConnextion driver (OSX). @@ -1010,8 +1014,7 @@ void MainFrame::init_menubar() wxMessageDialog((wxWindow*)this, _L("Switching to G-code preview mode will remove all objects, continue?"), wxString(SLIC3R_APP_NAME) + " - " + _L("Switch to G-code preview mode"), wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION | wxCENTRE).ShowModal() == wxID_YES) set_mode(EMode::GCodeViewer); - }, "", nullptr, - [this]() { return m_plater != nullptr && m_plater->printer_technology() != ptSLA; }, this); + }, "", nullptr); #endif // ENABLE_GCODE_VIEWER fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_EXIT, _L("&Quit"), wxString::Format(_L("Quit %s"), SLIC3R_APP_NAME), @@ -1329,6 +1332,12 @@ void MainFrame::set_mode(EMode mode) m_plater->clear_undo_redo_stack_main(); m_plater->take_snapshot(_L("New Project")); + // restore sla printer if it was deselected when switching to gcode viewer mode + if (m_restore_from_gcode_viewer.sla_technology) { + m_plater->set_printer_technology(ptSLA); + m_restore_from_gcode_viewer.sla_technology = false; + } + // switch view m_plater->select_view_3D("3D"); m_plater->select_view("iso"); @@ -1371,6 +1380,9 @@ void MainFrame::set_mode(EMode mode) m_plater->clear_undo_redo_stack_main(); m_plater->take_snapshot(_L("New Project")); + // switch to FFF printer mode + m_restore_from_gcode_viewer.sla_technology = m_plater->set_printer_technology(ptFFF); + // switch view m_plater->select_view_3D("Preview"); m_plater->select_view("iso"); diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 8961ad7172..53d8488768 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -76,6 +76,7 @@ class MainFrame : public DPIFrame { bool collapsed_sidebar{ false }; bool collapse_toolbar_enabled{ false }; + bool sla_technology{ false }; }; RestoreFromGCodeViewer m_restore_from_gcode_viewer; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 97372b2474..1fe32fd2dc 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5596,12 +5596,23 @@ PrinterTechnology Plater::printer_technology() const const DynamicPrintConfig * Plater::config() const { return p->config; } +#if ENABLE_GCODE_VIEWER +bool Plater::set_printer_technology(PrinterTechnology printer_technology) +#else void Plater::set_printer_technology(PrinterTechnology printer_technology) +#endif // ENABLE_GCODE_VIEWER { p->printer_technology = printer_technology; +#if ENABLE_GCODE_VIEWER + bool ret = p->background_process.select_technology(printer_technology); + if (ret) { + // Update the active presets. + } +#else if (p->background_process.select_technology(printer_technology)) { // Update the active presets. } +#endif // ENABLE_GCODE_VIEWER //FIXME for SLA synchronize //p->background_process.apply(Model)! @@ -5618,6 +5629,10 @@ void Plater::set_printer_technology(PrinterTechnology printer_technology) p->update_main_toolbar_tooltips(); p->sidebar->get_searcher().set_printer_technology(printer_technology); + +#if ENABLE_GCODE_VIEWER + return ret; +#endif // ENABLE_GCODE_VIEWER } void Plater::changed_object(int obj_idx) diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 9c1270bcec..cc80186202 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -263,7 +263,11 @@ public: PrinterTechnology printer_technology() const; const DynamicPrintConfig * config() const; +#if ENABLE_GCODE_VIEWER + bool set_printer_technology(PrinterTechnology printer_technology); +#else void set_printer_technology(PrinterTechnology printer_technology); +#endif // ENABLE_GCODE_VIEWER void copy_selection_to_clipboard(); void paste_from_clipboard(); From 5ff6f3045edf5e488312344c064c822afe868fcc Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 21 Aug 2020 11:50:05 +0200 Subject: [PATCH 328/503] ENABLE_GCODE_VIEWER -> KBShortcutsDialog shows only related tabs when gcode viewer state is active --- src/slic3r/GUI/KBShortcutsDialog.cpp | 197 +++++++++++++-------------- src/slic3r/GUI/KBShortcutsDialog.hpp | 4 - 2 files changed, 98 insertions(+), 103 deletions(-) diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 2d4b65a987..1a551216e7 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -7,6 +7,9 @@ #include #include "GUI_App.hpp" #include "wxExtensions.hpp" +#if ENABLE_GCODE_VIEWER +#include "MainFrame.hpp" +#endif // ENABLE_GCODE_VIEWER #define NOTEBOOK_TOP 1 #define NOTEBOOK_LEFT 2 @@ -31,11 +34,7 @@ namespace GUI { KBShortcutsDialog::KBShortcutsDialog() : DPIDialog(NULL, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _L("Keyboard Shortcuts"), -#if ENABLE_SCROLLABLE wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) -#else - wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE) -#endif // ENABLE_SCROLLABLE { SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); @@ -66,13 +65,9 @@ main_sizer->Add(book, 1, wxEXPAND | wxALL, 10); fill_shortcuts(); for (size_t i = 0; i < m_full_shortcuts.size(); ++i) { -#if ENABLE_SCROLLABLE wxPanel* page = create_page(book, m_full_shortcuts[i], font, bold_font); m_pages.push_back(page); book->AddPage(page, m_full_shortcuts[i].first, i == 0); -#else - book->AddPage(create_page(book, m_full_shortcuts[i], font, bold_font), m_full_shortcuts[i].first, i == 0); -#endif // ENABLE_SCROLLABLE } wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxOK); @@ -99,104 +94,112 @@ void KBShortcutsDialog::fill_shortcuts() const std::string& ctrl = GUI::shortkey_ctrl_prefix(); const std::string& alt = GUI::shortkey_alt_prefix(); - Shortcuts commands_shortcuts = { - // File - { ctrl + "N", L("New project, clear plater") }, - { ctrl + "O", L("Open project STL/OBJ/AMF/3MF with config, clear plater") }, - { ctrl + "S", L("Save project (3mf)") }, - { ctrl + alt + "S", L("Save project as (3mf)") }, - { ctrl + "R", L("(Re)slice") }, - // File>Import - { ctrl + "I", L("Import STL/OBJ/AMF/3MF without config, keep plater") }, - { ctrl + "L", L("Import Config from ini/amf/3mf/gcode") }, - { ctrl + alt + "L", L("Load Config from ini/amf/3mf/gcode and merge") }, - // File>Export - { ctrl + "G", L("Export G-code") }, - { ctrl + "Shift+" + "G", L("Send G-code") }, - { ctrl + "E", L("Export config") }, - { ctrl + "U", L("Export to SD card / Flash drive") }, - { ctrl + "T", L("Eject SD card / Flash drive") }, - // Edit - { ctrl + "A", L("Select all objects") }, - { "Esc", L("Deselect all") }, - { "Del", L("Delete selected") }, - { ctrl + "Del", L("Delete all") }, - { ctrl + "Z", L("Undo") }, - { ctrl + "Y", L("Redo") }, - { ctrl + "C", L("Copy to clipboard") }, - { ctrl + "V", L("Paste from clipboard") }, - { "F5", L("Reload plater from disk") }, - { ctrl + "F", L("Search") }, - // Window - { ctrl + "1", L("Select Plater Tab") }, - { ctrl + "2", L("Select Print Settings Tab") }, - { ctrl + "3", L("Select Filament Settings Tab") }, - { ctrl + "4", L("Select Printer Settings Tab") }, - { ctrl + "5", L("Switch to 3D") }, - { ctrl + "6", L("Switch to Preview") }, - { ctrl + "J", L("Print host upload queue") }, - // View - { "0-6", L("Camera view") }, - { "E", L("Show/Hide object/instance labels") }, +#if ENABLE_GCODE_VIEWER + bool is_gcode_viewer = wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer; + + if (!is_gcode_viewer) { +#endif // ENABLE_GCODE_VIEWER + Shortcuts commands_shortcuts = { + // File + { ctrl + "N", L("New project, clear plater") }, + { ctrl + "O", L("Open project STL/OBJ/AMF/3MF with config, clear plater") }, + { ctrl + "S", L("Save project (3mf)") }, + { ctrl + alt + "S", L("Save project as (3mf)") }, + { ctrl + "R", L("(Re)slice") }, + // File>Import + { ctrl + "I", L("Import STL/OBJ/AMF/3MF without config, keep plater") }, + { ctrl + "L", L("Import Config from ini/amf/3mf/gcode") }, + { ctrl + alt + "L", L("Load Config from ini/amf/3mf/gcode and merge") }, + // File>Export + { ctrl + "G", L("Export G-code") }, + { ctrl + "Shift+" + "G", L("Send G-code") }, + { ctrl + "E", L("Export config") }, + { ctrl + "U", L("Export to SD card / Flash drive") }, + { ctrl + "T", L("Eject SD card / Flash drive") }, + // Edit + { ctrl + "A", L("Select all objects") }, + { "Esc", L("Deselect all") }, + { "Del", L("Delete selected") }, + { ctrl + "Del", L("Delete all") }, + { ctrl + "Z", L("Undo") }, + { ctrl + "Y", L("Redo") }, + { ctrl + "C", L("Copy to clipboard") }, + { ctrl + "V", L("Paste from clipboard") }, + { "F5", L("Reload plater from disk") }, + { ctrl + "F", L("Search") }, + // Window + { ctrl + "1", L("Select Plater Tab") }, + { ctrl + "2", L("Select Print Settings Tab") }, + { ctrl + "3", L("Select Filament Settings Tab") }, + { ctrl + "4", L("Select Printer Settings Tab") }, + { ctrl + "5", L("Switch to 3D") }, + { ctrl + "6", L("Switch to Preview") }, + { ctrl + "J", L("Print host upload queue") }, + // View + { "0-6", L("Camera view") }, + { "E", L("Show/Hide object/instance labels") }, #if ENABLE_SLOPE_RENDERING - { "D", L("Turn On/Off facets' slope rendering") }, + { "D", L("Turn On/Off facets' slope rendering") }, #endif // ENABLE_SLOPE_RENDERING - // Configuration - { ctrl + "P", L("Preferences") }, - // Help - { "?", L("Show keyboard shortcuts list") } - }; + // Configuration + { ctrl + "P", L("Preferences") }, + // Help + { "?", L("Show keyboard shortcuts list") } + }; - m_full_shortcuts.push_back(std::make_pair(_L("Commands"), commands_shortcuts)); + m_full_shortcuts.push_back(std::make_pair(_L("Commands"), commands_shortcuts)); - Shortcuts plater_shortcuts = { - { "A", L("Arrange") }, - { "Shift+A", L("Arrange selection") }, - { "+", L("Add Instance of the selected object") }, - { "-", L("Remove Instance of the selected object") }, - { ctrl, L("Press to select multiple objects\nor move multiple objects with mouse") }, - { "Shift+", L("Press to activate selection rectangle") }, - { alt, L("Press to activate deselection rectangle") }, - { L("Arrow Up"), L("Move selection 10 mm in positive Y direction") }, - { L("Arrow Down"), L("Move selection 10 mm in negative Y direction") }, - { L("Arrow Left"), L("Move selection 10 mm in negative X direction") }, - { L("Arrow Right"), L("Move selection 10 mm in positive X direction") }, - { std::string("Shift+") + L("Any arrow"), L("Movement step set to 1 mm") }, - { ctrl + L("Any arrow"), L("Movement in camera space") }, - { L("Page Up"), L("Rotate selection 45 degrees CCW") }, - { L("Page Down"), L("Rotate selection 45 degrees CW") }, - { "M", L("Gizmo move") }, - { "S", L("Gizmo scale") }, - { "R", L("Gizmo rotate") }, - { "C", L("Gizmo cut") }, - { "F", L("Gizmo Place face on bed") }, - { "H", L("Gizmo SLA hollow") }, - { "L", L("Gizmo SLA support points") }, - { "Esc", L("Unselect gizmo or clear selection") }, - { "K", L("Change camera type (perspective, orthographic)") }, - { "B", L("Zoom to Bed") }, - { "Z", L("Zoom to selected object\nor all objects in scene, if none selected") }, - { "I", L("Zoom in") }, - { "O", L("Zoom out") }, + Shortcuts plater_shortcuts = { + { "A", L("Arrange") }, + { "Shift+A", L("Arrange selection") }, + { "+", L("Add Instance of the selected object") }, + { "-", L("Remove Instance of the selected object") }, + { ctrl, L("Press to select multiple objects\nor move multiple objects with mouse") }, + { "Shift+", L("Press to activate selection rectangle") }, + { alt, L("Press to activate deselection rectangle") }, + { L("Arrow Up"), L("Move selection 10 mm in positive Y direction") }, + { L("Arrow Down"), L("Move selection 10 mm in negative Y direction") }, + { L("Arrow Left"), L("Move selection 10 mm in negative X direction") }, + { L("Arrow Right"), L("Move selection 10 mm in positive X direction") }, + { std::string("Shift+") + L("Any arrow"), L("Movement step set to 1 mm") }, + { ctrl + L("Any arrow"), L("Movement in camera space") }, + { L("Page Up"), L("Rotate selection 45 degrees CCW") }, + { L("Page Down"), L("Rotate selection 45 degrees CW") }, + { "M", L("Gizmo move") }, + { "S", L("Gizmo scale") }, + { "R", L("Gizmo rotate") }, + { "C", L("Gizmo cut") }, + { "F", L("Gizmo Place face on bed") }, + { "H", L("Gizmo SLA hollow") }, + { "L", L("Gizmo SLA support points") }, + { "Esc", L("Unselect gizmo or clear selection") }, + { "K", L("Change camera type (perspective, orthographic)") }, + { "B", L("Zoom to Bed") }, + { "Z", L("Zoom to selected object\nor all objects in scene, if none selected") }, + { "I", L("Zoom in") }, + { "O", L("Zoom out") }, #ifdef __linux__ - { ctrl + "M", L("Show/Hide 3Dconnexion devices settings dialog") }, + { ctrl + "M", L("Show/Hide 3Dconnexion devices settings dialog") }, #endif // __linux__ #if ENABLE_RENDER_PICKING_PASS - // Don't localize debugging texts. - { "P", "Toggle picking pass texture rendering on/off" }, + // Don't localize debugging texts. + { "P", "Toggle picking pass texture rendering on/off" }, #endif // ENABLE_RENDER_PICKING_PASS - }; + }; - m_full_shortcuts.push_back(std::make_pair(_L("Plater"), plater_shortcuts)); + m_full_shortcuts.push_back(std::make_pair(_L("Plater"), plater_shortcuts)); - Shortcuts gizmos_shortcuts = { - { "Shift+", L("Press to snap by 5% in Gizmo scale\nor to snap by 1mm in Gizmo move") }, - { "F", L("Scale selection to fit print volume\nin Gizmo scale") }, - { ctrl, L("Press to activate one direction scaling in Gizmo scale") }, - { alt, L("Press to scale (in Gizmo scale) or rotate (in Gizmo rotate)\nselected objects around their own center") }, - }; + Shortcuts gizmos_shortcuts = { + { "Shift+", L("Press to snap by 5% in Gizmo scale\nor to snap by 1mm in Gizmo move") }, + { "F", L("Scale selection to fit print volume\nin Gizmo scale") }, + { ctrl, L("Press to activate one direction scaling in Gizmo scale") }, + { alt, L("Press to scale (in Gizmo scale) or rotate (in Gizmo rotate)\nselected objects around their own center") }, + }; - m_full_shortcuts.push_back(std::make_pair(_L("Gizmos"), gizmos_shortcuts)); + m_full_shortcuts.push_back(std::make_pair(_L("Gizmos"), gizmos_shortcuts)); +#if ENABLE_GCODE_VIEWER + } +#endif // ENABLE_GCODE_VIEWER Shortcuts preview_shortcuts = { { L("Arrow Up"), L("Upper Layer") }, @@ -277,13 +280,9 @@ wxPanel* KBShortcutsDialog::create_page(wxWindow* parent, const std::pairSetScrollbars(20, 20, 50, 50); page->SetInitialSize(wxSize(850, 450)); -#else - wxPanel* page = new wxPanel(parent); -#endif // ENABLE_SCROLLABLE #if (BOOK_TYPE == LISTBOOK_TOP) || (BOOK_TYPE == LISTBOOK_LEFT) wxStaticBoxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, page, " " + shortcuts.first + " "); diff --git a/src/slic3r/GUI/KBShortcutsDialog.hpp b/src/slic3r/GUI/KBShortcutsDialog.hpp index 70820ae774..a8ec4e4267 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.hpp +++ b/src/slic3r/GUI/KBShortcutsDialog.hpp @@ -8,8 +8,6 @@ #include "GUI_Utils.hpp" #include "wxExtensions.hpp" -#define ENABLE_SCROLLABLE 1 - namespace Slic3r { namespace GUI { @@ -22,9 +20,7 @@ class KBShortcutsDialog : public DPIDialog ShortcutsVec m_full_shortcuts; ScalableBitmap m_logo_bmp; wxStaticBitmap* m_header_bitmap; -#if ENABLE_SCROLLABLE std::vector m_pages; -#endif // ENABLE_SCROLLABLE public: KBShortcutsDialog(); From a95509ce36a125086b64ad56f2debc7cd3e82837 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 19 Aug 2020 15:24:55 +0200 Subject: [PATCH 329/503] Changed internal coordinates of drain holes Drain holes reference position was saved slightly above the mesh to avoid problem when the hole is placed on flat or nearly flat surface The depth of the hole was internally bigger than what the user has set to compensato for it However, this leads to problem with scaling and makes reprojection of the holes on the mesh complicated This commit changes the reference point to the point on the mesh and the extra elevation is handled when rendering and drilling the hole. The change is reflected in 3MF drain holes versioning so that old 3MFs are loaded correctly. Reprojection on the mesh after reload from disk/fix through netfabb has been enabled. --- src/libslic3r/Format/3mf.cpp | 12 ++++++++++- src/libslic3r/Format/3mf.hpp | 11 ++++++++-- src/libslic3r/SLA/Hollowing.hpp | 2 ++ src/libslic3r/SLA/ReprojectPointsOnMesh.hpp | 14 ++++--------- src/libslic3r/SLAPrint.cpp | 6 ++++++ src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 21 +++++++------------- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 8 ++++---- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 2 -- 8 files changed, 43 insertions(+), 33 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 59dc85a0ae..c25b7b96a5 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -1103,7 +1103,7 @@ namespace Slic3r { sla::DrainHoles sla_drain_holes; - if (version == 1) { + if (version == 1 || version == 2) { for (unsigned int i=0; i; +constexpr float HoleStickOutLength = 1.f; + std::unique_ptr generate_interior(const TriangleMesh &mesh, const HollowingConfig & = {}, const JobController &ctl = {}); diff --git a/src/libslic3r/SLA/ReprojectPointsOnMesh.hpp b/src/libslic3r/SLA/ReprojectPointsOnMesh.hpp index 4737a6c212..20804193e2 100644 --- a/src/libslic3r/SLA/ReprojectPointsOnMesh.hpp +++ b/src/libslic3r/SLA/ReprojectPointsOnMesh.hpp @@ -28,15 +28,9 @@ void reproject_support_points(const IndexedMesh &mesh, std::vector &p inline void reproject_points_and_holes(ModelObject *object) { bool has_sppoints = !object->sla_support_points.empty(); + bool has_holes = !object->sla_drain_holes.empty(); - // Disabling reprojection of holes as they have a significant offset away - // from the model body which tolerates minor geometrical changes. - // - // TODO: uncomment and ensure the right offset of the hole points if - // reprojection would still be necessary. - // bool has_holes = !object->sla_drain_holes.empty(); - - if (!object || (/*!has_holes &&*/ !has_sppoints)) return; + if (!object || (!has_holes && !has_sppoints)) return; TriangleMesh rmsh = object->raw_mesh(); rmsh.require_shared_vertices(); @@ -45,8 +39,8 @@ inline void reproject_points_and_holes(ModelObject *object) if (has_sppoints) reproject_support_points(emesh, object->sla_support_points); -// if (has_holes) -// reproject_support_points(emesh, object->sla_drain_holes); + if (has_holes) + reproject_support_points(emesh, object->sla_drain_holes); } }} diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 4395bea461..07ec380160 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1181,6 +1181,12 @@ sla::DrainHoles SLAPrintObject::transformed_drainhole_points() const hl.normal = Vec3f(hl.normal(0)/(sc(0)*sc(0)), hl.normal(1)/(sc(1)*sc(1)), hl.normal(2)/(sc(2)*sc(2))); + + // Now shift the hole a bit above the object and make it deeper to + // compensate for it. This is to avoid problems when the hole is placed + // on (nearly) flat surface. + hl.pos -= hl.normal.normalized() * sla::HoleStickOutLength; + hl.height += sla::HoleStickOutLength; } return pts; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 273384da2e..04ada52536 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -130,7 +130,7 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons const sla::DrainHole& drain_hole = drain_holes[i]; const bool& point_selected = m_selected[i]; - if (is_mesh_point_clipped((drain_hole.pos+HoleStickOutLength*drain_hole.normal).cast())) + if (is_mesh_point_clipped(drain_hole.pos.cast())) continue; // First decide about the color of the point. @@ -174,10 +174,10 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); glsafe(::glPushMatrix()); glsafe(::glTranslated(0., 0., -drain_hole.height)); - ::gluCylinder(m_quadric, drain_hole.radius, drain_hole.radius, drain_hole.height, 24, 1); - glsafe(::glTranslated(0., 0., drain_hole.height)); + ::gluCylinder(m_quadric, drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength, 24, 1); + glsafe(::glTranslated(0., 0., drain_hole.height + sla::HoleStickOutLength)); ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); - glsafe(::glTranslated(0., 0., -drain_hole.height)); + glsafe(::glTranslated(0., 0., -drain_hole.height - sla::HoleStickOutLength)); glsafe(::glRotatef(180.f, 1.f, 0.f, 0.f)); ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); glsafe(::glPopMatrix()); @@ -307,13 +307,8 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add drainage hole"))); - Vec3d scaling = mo->instances[active_inst]->get_scaling_factor(); - Vec3f normal_transformed(pos_and_normal.second(0)/scaling(0), - pos_and_normal.second(1)/scaling(1), - pos_and_normal.second(2)/scaling(2)); - - mo->sla_drain_holes.emplace_back(pos_and_normal.first + HoleStickOutLength * pos_and_normal.second/* normal_transformed.normalized()*/, - -pos_and_normal.second, m_new_hole_radius, m_new_hole_height); + mo->sla_drain_holes.emplace_back(pos_and_normal.first, + -pos_and_normal.second, m_new_hole_radius, m_new_hole_height); m_selected.push_back(false); assert(m_selected.size() == mo->sla_drain_holes.size()); m_parent.set_as_dirty(); @@ -447,7 +442,7 @@ void GLGizmoHollow::on_update(const UpdateData& data) std::pair pos_and_normal; if (! unproject_on_mesh(data.mouse_pos.cast(), pos_and_normal)) return; - drain_holes[m_hover_id].pos = pos_and_normal.first + HoleStickOutLength * pos_and_normal.second; + drain_holes[m_hover_id].pos = pos_and_normal.first; drain_holes[m_hover_id].normal = -pos_and_normal.second; } } @@ -661,9 +656,7 @@ RENDER_AGAIN: m_imgui->text(m_desc["hole_depth"]); ImGui::SameLine(diameter_slider_left); - m_new_hole_height -= HoleStickOutLength; ImGui::SliderFloat(" ", &m_new_hole_height, 0.f, 10.f, "%.1f mm"); - m_new_hole_height += HoleStickOutLength; clicked |= ImGui::IsItemClicked(); edited |= ImGui::IsItemEdited(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 2856bb35de..bc29da6d2b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -222,7 +222,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) render_color[3] = 0.7f; glsafe(::glColor4fv(render_color)); for (const sla::DrainHole& drain_hole : m_c->selection_info()->model_object()->sla_drain_holes) { - if (is_mesh_point_clipped((drain_hole.pos+HoleStickOutLength*drain_hole.normal).cast())) + if (is_mesh_point_clipped(drain_hole.pos.cast())) continue; // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. @@ -241,10 +241,10 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); glsafe(::glPushMatrix()); glsafe(::glTranslated(0., 0., -drain_hole.height)); - ::gluCylinder(m_quadric, drain_hole.radius, drain_hole.radius, drain_hole.height, 24, 1); - glsafe(::glTranslated(0., 0., drain_hole.height)); + ::gluCylinder(m_quadric, drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength, 24, 1); + glsafe(::glTranslated(0., 0., drain_hole.height + sla::HoleStickOutLength)); ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); - glsafe(::glTranslated(0., 0., -drain_hole.height)); + glsafe(::glTranslated(0., 0., -drain_hole.height - sla::HoleStickOutLength)); glsafe(::glRotatef(180.f, 1.f, 0.f, 0.f)); ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); glsafe(::glPopMatrix()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 31c473bac7..aedf782e89 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -15,8 +15,6 @@ namespace GUI { class GLCanvas3D; -static constexpr float HoleStickOutLength = 1.f; - enum class SLAGizmoEventType : unsigned char { LeftDown = 1, LeftUp, From c51a45ee0f8c08cbee0d874d81da3ba5d42d9cea Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 24 Aug 2020 08:04:16 +0200 Subject: [PATCH 330/503] Drainholes are saved elevated for 3MF compatibility This is a follow-up of previous commit --- src/libslic3r/Format/3mf.cpp | 30 ++++++++++++++++++++---------- src/libslic3r/Format/3mf.hpp | 9 +-------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index c25b7b96a5..52a3335ee4 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -1103,7 +1103,7 @@ namespace Slic3r { sla::DrainHoles sla_drain_holes; - if (version == 1 || version == 2) { + if (version == 1) { for (unsigned int i=0; isla_drain_holes; + sla::DrainHoles drain_holes = object->sla_drain_holes; + + // The holes were placed 1mm above the mesh in the first implementation. + // This was a bad idea and the reference point was changed in 2.3 so + // to be on the mesh exactly. The elevated position is still saved + // in 3MFs for compatibility reasons. + for (sla::DrainHole& hole : drain_holes) { + hole.pos -= hole.normal.normalized(); + hole.height += 1.f; + } + + if (!drain_holes.empty()) { out += string_printf(fmt, count); diff --git a/src/libslic3r/Format/3mf.hpp b/src/libslic3r/Format/3mf.hpp index 02e2bd1d3c..ccfd9356d8 100644 --- a/src/libslic3r/Format/3mf.hpp +++ b/src/libslic3r/Format/3mf.hpp @@ -20,15 +20,8 @@ namespace Slic3r { support_points_format_version = 1 }; - - /* The same for holes. - - * version 0: undefined - * version 1: holes saved a bit above the mesh and deeper - * version 2: holes are saved on the mesh exactly - */ enum { - drain_holes_format_version = 2 + drain_holes_format_version = 1 }; class Model; From ac8a6fccbe6e649bb55452d1e5859a2029257678 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 25 Aug 2020 08:12:28 +0200 Subject: [PATCH 331/503] Renamed shaders --- .../{options_120_flat.fs => options_120.fs} | 0 .../{options_120_flat.vs => options_120.vs} | 0 .../{toolpaths.fs => toolpaths_lines.fs} | 0 .../{toolpaths.vs => toolpaths_lines.vs} | 0 src/slic3r/GUI/GCodeViewer.cpp | 25 +++++++++---------- src/slic3r/GUI/GCodeViewer.hpp | 2 +- src/slic3r/GUI/GLShadersManager.cpp | 4 +-- 7 files changed, 15 insertions(+), 16 deletions(-) rename resources/shaders/{options_120_flat.fs => options_120.fs} (100%) rename resources/shaders/{options_120_flat.vs => options_120.vs} (100%) rename resources/shaders/{toolpaths.fs => toolpaths_lines.fs} (100%) rename resources/shaders/{toolpaths.vs => toolpaths_lines.vs} (100%) diff --git a/resources/shaders/options_120_flat.fs b/resources/shaders/options_120.fs similarity index 100% rename from resources/shaders/options_120_flat.fs rename to resources/shaders/options_120.fs diff --git a/resources/shaders/options_120_flat.vs b/resources/shaders/options_120.vs similarity index 100% rename from resources/shaders/options_120_flat.vs rename to resources/shaders/options_120.vs diff --git a/resources/shaders/toolpaths.fs b/resources/shaders/toolpaths_lines.fs similarity index 100% rename from resources/shaders/toolpaths.fs rename to resources/shaders/toolpaths_lines.fs diff --git a/resources/shaders/toolpaths.vs b/resources/shaders/toolpaths_lines.vs similarity index 100% rename from resources/shaders/toolpaths.vs rename to resources/shaders/toolpaths_lines.vs diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 80a5e15ed5..bcb4a0a268 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -606,8 +606,7 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const fprintf(fp, "# Generated by %s based on Slic3r\n", SLIC3R_BUILD_ID); unsigned int colors_count = 1; - for (const Color& color : colors) - { + for (const Color& color : colors) { fprintf(fp, "\nnewmtl material_%d\n", colors_count++); fprintf(fp, "Ka 1 1 1\n"); fprintf(fp, "Kd %f %f %f\n", color[0], color[1], color[2]); @@ -858,14 +857,14 @@ void GCodeViewer::init_shaders() for (unsigned char i = begin_id; i < end_id; ++i) { switch (buffer_type(i)) { - case EMoveType::Tool_change: { m_buffers[i].shader = is_glsl_120 ? "options_120_flat" : "options_110"; break; } - case EMoveType::Color_change: { m_buffers[i].shader = is_glsl_120 ? "options_120_flat" : "options_110"; break; } - case EMoveType::Pause_Print: { m_buffers[i].shader = is_glsl_120 ? "options_120_flat" : "options_110"; break; } - case EMoveType::Custom_GCode: { m_buffers[i].shader = is_glsl_120 ? "options_120_flat" : "options_110"; break; } - case EMoveType::Retract: { m_buffers[i].shader = is_glsl_120 ? "options_120_flat" : "options_110"; break; } - case EMoveType::Unretract: { m_buffers[i].shader = is_glsl_120 ? "options_120_flat" : "options_110"; break; } - case EMoveType::Extrude: { m_buffers[i].shader = "toolpaths"; break; } - case EMoveType::Travel: { m_buffers[i].shader = "toolpaths"; break; } + case EMoveType::Tool_change: { m_buffers[i].shader = is_glsl_120 ? "options_120" : "options_110"; break; } + case EMoveType::Color_change: { m_buffers[i].shader = is_glsl_120 ? "options_120" : "options_110"; break; } + case EMoveType::Pause_Print: { m_buffers[i].shader = is_glsl_120 ? "options_120" : "options_110"; break; } + case EMoveType::Custom_GCode: { m_buffers[i].shader = is_glsl_120 ? "options_120" : "options_110"; break; } + case EMoveType::Retract: { m_buffers[i].shader = is_glsl_120 ? "options_120" : "options_110"; break; } + case EMoveType::Unretract: { m_buffers[i].shader = is_glsl_120 ? "options_120" : "options_110"; break; } + case EMoveType::Extrude: { m_buffers[i].shader = "toolpaths_lines"; break; } + case EMoveType::Travel: { m_buffers[i].shader = "toolpaths_lines"; break; } default: { break; } } } @@ -1463,8 +1462,8 @@ void GCodeViewer::render_legend() const else draw_list->AddCircleFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); #else - ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); - if (m_buffers[buffer_id(EMoveType::Retract)].shader == "options_120_flat") { + ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); + if (m_buffers[buffer_id(EMoveType::Retract)].shader == "options_120") { draw_list->AddCircleFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); float radius = 0.5f * icon_size; @@ -2527,7 +2526,7 @@ void GCodeViewer::render_shaders_editor() const switch (m_shaders_editor.points.shader_version) { case 0: { set_shader("options_110"); break; } - case 1: { set_shader("options_120_flat"); break; } + case 1: { set_shader("options_120"); break; } } if (ImGui::TreeNode("Options")) { diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 0be17f790a..e6c3e26eaa 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -65,7 +65,7 @@ class GCodeViewer void reset(); }; - // ibo buffer containing indices data (triangles) used to render a specific toolpath type + // ibo buffer containing indices data (lines/triangles) used to render a specific toolpath type struct IBuffer { // ibo id diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index 5f726d4ef1..6baf46f6b4 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -36,9 +36,9 @@ std::pair GLShadersManager::init() // used to render options in gcode preview valid &= append_shader("options_110", { "options_110.vs", "options_110.fs" }); if (GUI::wxGetApp().is_glsl_version_greater_or_equal_to(1, 20)) - valid &= append_shader("options_120_flat", { "options_120_flat.vs", "options_120_flat.fs" }); + valid &= append_shader("options_120", { "options_120.vs", "options_120.fs" }); // used to render extrusion and travel paths in gcode preview - valid &= append_shader("toolpaths", { "toolpaths.vs", "toolpaths.fs" }); + valid &= append_shader("toolpaths_lines", { "toolpaths_lines.vs", "toolpaths_lines.fs" }); // used to render objects in 3d editor valid &= append_shader("gouraud", { "gouraud.vs", "gouraud.fs" }); // used to render variable layers heights in 3d editor From 2783653369fd080b2b585d02525a905eee524432 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 26 Aug 2020 13:01:54 +0200 Subject: [PATCH 332/503] Added icon for gcode viewer mode --- .../icons/PrusaSlicerGCodeViewer_128px.png | Bin 0 -> 15949 bytes src/slic3r/GUI/MainFrame.cpp | 48 +++++++++++------- 2 files changed, 29 insertions(+), 19 deletions(-) create mode 100644 resources/icons/PrusaSlicerGCodeViewer_128px.png diff --git a/resources/icons/PrusaSlicerGCodeViewer_128px.png b/resources/icons/PrusaSlicerGCodeViewer_128px.png new file mode 100644 index 0000000000000000000000000000000000000000..0bf85abbd6ea0585d127686a8200b4a647a030b8 GIT binary patch literal 15949 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Ʊ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 AMF0Q* literal 0 HcmV?d00001 diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index ce8772eedc..7e12be4fcd 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -84,16 +84,19 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S #endif // Font is already set in DPIFrame constructor */ - // Load the icon either from the exe, or from the ico file. -#if _WIN32 - { - TCHAR szExeFileName[MAX_PATH]; - GetModuleFileName(nullptr, szExeFileName, MAX_PATH); - SetIcon(wxIcon(szExeFileName, wxBITMAP_TYPE_ICO)); - } -#else + SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); -#endif // _WIN32 +// // Load the icon either from the exe, or from the ico file. +//#if _WIN32 +// { +// +// TCHAR szExeFileName[MAX_PATH]; +// GetModuleFileName(nullptr, szExeFileName, MAX_PATH); +// SetIcon(wxIcon(szExeFileName, wxBITMAP_TYPE_ICO)); +// } +//#else +// SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); +//#endif // _WIN32 // initialize status bar m_statusbar = std::make_shared(this); @@ -1364,6 +1367,8 @@ void MainFrame::set_mode(EMode mode) m_plater->Thaw(); + SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); + break; } case EMode::GCodeViewer: @@ -1409,6 +1414,8 @@ void MainFrame::set_mode(EMode mode) m_plater->Thaw(); + SetIcon(wxIcon(Slic3r::var("PrusaSlicerGCodeViewer_128px.png"), wxBITMAP_TYPE_PNG)); + break; } } @@ -1912,16 +1919,19 @@ SettingsDialog::SettingsDialog(MainFrame* mainframe) this->SetFont(wxGetApp().normal_font()); this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - // Load the icon either from the exe, or from the ico file. -#if _WIN32 - { - TCHAR szExeFileName[MAX_PATH]; - GetModuleFileName(nullptr, szExeFileName, MAX_PATH); - SetIcon(wxIcon(szExeFileName, wxBITMAP_TYPE_ICO)); - } -#else - SetIcon(wxIcon(var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); -#endif // _WIN32 + + SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); +// // Load the icon either from the exe, or from the ico file. +//#if _WIN32 +// { +// +// TCHAR szExeFileName[MAX_PATH]; +// GetModuleFileName(nullptr, szExeFileName, MAX_PATH); +// SetIcon(wxIcon(szExeFileName, wxBITMAP_TYPE_ICO)); +// } +//#else +// SetIcon(wxIcon(var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); +//#endif // _WIN32 this->Bind(wxEVT_SHOW, [this](wxShowEvent& evt) { From 56431d26e5b01d9f206ad52a7eff4de443ec8038 Mon Sep 17 00:00:00 2001 From: Slic3rPE Date: Wed, 26 Aug 2020 14:56:26 +0200 Subject: [PATCH 333/503] Starting a new Slicer instance from the menu --- src/slic3r/GUI/MainFrame.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index bbc1da534e..122d9c6108 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -8,9 +8,12 @@ #include #include //#include +#include +#include #include #include +#include #include "libslic3r/Print.hpp" #include "libslic3r/Polygon.hpp" @@ -979,6 +982,18 @@ void MainFrame::init_menubar() append_menu_item(windowMenu, wxID_ANY, _(L("Print &Host Upload Queue")) + "\tCtrl+J", _(L("Display the Print Host Upload Queue window")), [this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, "upload_queue", nullptr, [this]() {return true; }, this); + + windowMenu->AppendSeparator(); + append_menu_item(windowMenu, wxID_ANY, _(L("Open new instance")) + "\tCtrl+I", _(L("Open a new PrusaSlicer instance")), + [this](wxCommandEvent&) { + wxString path = wxStandardPaths::Get().GetExecutablePath(); +#ifdef __APPLE__ + boost::process::spawn((const char*)path.c_str()); +#else + wxExecute(wxStandardPaths::Get().GetExecutablePath(), wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE | wxEXEC_MAKE_GROUP_LEADER); +#endif + }, "upload_queue", nullptr, + [this]() {return true; }, this); } // View menu From ba9c3a74ed6f46725fd092a43ae3895c38a19bc7 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 26 Aug 2020 15:29:33 +0200 Subject: [PATCH 334/503] GCodeViewer -> 1st iteration of rendering of extrude toolpaths as solid --- resources/shaders/options_110.fs | 4 +- resources/shaders/options_120.fs | 10 +- resources/shaders/toolpaths_lines.fs | 4 +- src/libslic3r/Technologies.hpp | 1 + src/slic3r/GUI/GCodeViewer.cpp | 330 ++++++++++++++++++++------- src/slic3r/GUI/GCodeViewer.hpp | 64 +++++- src/slic3r/GUI/GLShadersManager.cpp | 2 +- 7 files changed, 303 insertions(+), 112 deletions(-) diff --git a/resources/shaders/options_110.fs b/resources/shaders/options_110.fs index 474e355e09..ab656998df 100644 --- a/resources/shaders/options_110.fs +++ b/resources/shaders/options_110.fs @@ -1,8 +1,8 @@ #version 110 -uniform vec3 uniform_color; +uniform vec4 uniform_color; void main() { - gl_FragColor = vec4(uniform_color, 1.0); + gl_FragColor = uniform_color; } diff --git a/resources/shaders/options_120.fs b/resources/shaders/options_120.fs index 5bcc718761..d897a8ca73 100644 --- a/resources/shaders/options_120.fs +++ b/resources/shaders/options_120.fs @@ -1,19 +1,15 @@ // version 120 is needed for gl_PointCoord #version 120 -uniform vec3 uniform_color; +uniform vec4 uniform_color; uniform float percent_outline_radius; uniform float percent_center_radius; -vec4 hardcoded_color(float radius, vec3 color) -{ - return ((radius < 0.15) || (radius > 0.85)) ? vec4(0.5 * color, 1.0) : vec4(color, 1.0); -} -vec4 customizable_color(float radius, vec3 color) +vec4 customizable_color(float radius, vec4 color) { return ((radius < percent_center_radius) || (radius > 1.0 - percent_outline_radius)) ? - vec4(0.5 * color, 1.0) : vec4(color, 1.0); + vec4(0.5 * color.rgb, color.a) : color; } void main() diff --git a/resources/shaders/toolpaths_lines.fs b/resources/shaders/toolpaths_lines.fs index 13f60c0a82..7202a2e3e4 100644 --- a/resources/shaders/toolpaths_lines.fs +++ b/resources/shaders/toolpaths_lines.fs @@ -6,7 +6,7 @@ const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); // x = ambient, y = top diffuse, z = front diffuse, w = global uniform vec4 light_intensity; -uniform vec3 uniform_color; +uniform vec4 uniform_color; varying vec3 eye_position; varying vec3 eye_normal; @@ -27,5 +27,5 @@ void main() NdotL = abs(dot(normal, LIGHT_FRONT_DIR)); intensity += NdotL * light_intensity.z; - gl_FragColor = vec4(uniform_color * light_intensity.w * intensity, 1.0); + gl_FragColor = vec4(uniform_color.rgb * light_intensity.w * intensity, uniform_color.a); } diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index e10cabc6cf..00c899201a 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -59,6 +59,7 @@ #define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_SHADERS_EDITOR (0 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_DATA_CHECKING (0 && ENABLE_GCODE_VIEWER) +#define ENABLE_GCODE_RENDER_EXTRUSION_AS_TRIANGLES (1 && ENABLE_GCODE_VIEWER) #define TIME_ESTIMATE_NONE 0 #define TIME_ESTIMATE_DEFAULT 1 diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index bcb4a0a268..70b571fe00 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -286,6 +286,7 @@ bool GCodeViewer::init() { for (size_t i = 0; i < m_buffers.size(); ++i) { + TBuffer& buffer = m_buffers[i]; switch (buffer_type(i)) { default: { break; } @@ -296,13 +297,25 @@ bool GCodeViewer::init() case EMoveType::Retract: case EMoveType::Unretract: { - m_buffers[i].vertices.format = VBuffer::EFormat::Position; + buffer.primitive_type = TBuffer::EPrimitiveType::Point; + buffer.vertices.format = VBuffer::EFormat::Position; break; } case EMoveType::Extrude: + { +#if ENABLE_GCODE_RENDER_EXTRUSION_AS_TRIANGLES + buffer.primitive_type = TBuffer::EPrimitiveType::Triangle; + buffer.vertices.format = VBuffer::EFormat::PositionNormal3; +#else + buffer.primitive_type = TBuffer::EPrimitiveType::Line; + buffer.vertices.format = VBuffer::EFormat::PositionNormal1; +#endif // ENABLE_GCODE_RENDER_EXTRUSION_AS_TRIANGLES + break; + } case EMoveType::Travel: { - m_buffers[i].vertices.format = VBuffer::EFormat::PositionNormal; + buffer.primitive_type = TBuffer::EPrimitiveType::Line; + buffer.vertices.format = VBuffer::EFormat::PositionNormal1; break; } } @@ -863,7 +876,11 @@ void GCodeViewer::init_shaders() case EMoveType::Custom_GCode: { m_buffers[i].shader = is_glsl_120 ? "options_120" : "options_110"; break; } case EMoveType::Retract: { m_buffers[i].shader = is_glsl_120 ? "options_120" : "options_110"; break; } case EMoveType::Unretract: { m_buffers[i].shader = is_glsl_120 ? "options_120" : "options_110"; break; } +#if ENABLE_GCODE_RENDER_EXTRUSION_AS_TRIANGLES + case EMoveType::Extrude: { m_buffers[i].shader = "gouraud_light"; break; } +#else case EMoveType::Extrude: { m_buffers[i].shader = "toolpaths_lines"; break; } +#endif // ENABLE_GCODE_RENDER_EXTRUSION_AS_TRIANGLES case EMoveType::Travel: { m_buffers[i].shader = "toolpaths_lines"; break; } default: { break; } } @@ -901,6 +918,127 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) m_max_bounding_box = m_paths_bounding_box; m_max_bounding_box.merge(m_paths_bounding_box.max + m_sequential_view.marker.get_bounding_box().size()[2] * Vec3d::UnitZ()); + // format data into the buffers to be rendered as points + auto add_as_point = [](const GCodeProcessor::MoveVertex& curr, TBuffer& buffer, + std::vector& buffer_vertices, std::vector& buffer_indices, size_t move_id) { + for (int j = 0; j < 3; ++j) { + buffer_vertices.push_back(curr.position[j]); + } + buffer.add_path(curr, static_cast(buffer_indices.size()), static_cast(move_id)); + buffer_indices.push_back(static_cast(buffer_indices.size())); + }; + + // format data into the buffers to be rendered as lines + auto add_as_line = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer, + std::vector& buffer_vertices, std::vector& buffer_indices, size_t move_id) { + // x component of the normal to the current segment (the normal is parallel to the XY plane) + float normal_x = (curr.position - prev.position).normalized()[1]; + + if (prev.type != curr.type || !buffer.paths.back().matches(curr)) { + // add starting vertex position + for (int j = 0; j < 3; ++j) { + buffer_vertices.push_back(prev.position[j]); + } + // add starting vertex normal x component + buffer_vertices.push_back(normal_x); + // add starting index + buffer_indices.push_back(static_cast(buffer_indices.size())); + buffer.add_path(curr, static_cast(buffer_indices.size() - 1), static_cast(move_id - 1)); + buffer.paths.back().first.position = prev.position; + } + + Path& last_path = buffer.paths.back(); + if (last_path.first.i_id != last_path.last.i_id) { + // add previous vertex position + for (int j = 0; j < 3; ++j) { + buffer_vertices.push_back(prev.position[j]); + } + // add previous vertex normal x component + buffer_vertices.push_back(normal_x); + // add previous index + buffer_indices.push_back(static_cast(buffer_indices.size())); + } + + // add current vertex position + for (int j = 0; j < 3; ++j) { + buffer_vertices.push_back(curr.position[j]); + } + // add current vertex normal x component + buffer_vertices.push_back(normal_x); + // add current index + buffer_indices.push_back(static_cast(buffer_indices.size())); + last_path.last = { static_cast(buffer_indices.size() - 1), static_cast(move_id), curr.position }; + }; + + // format data into the buffers to be rendered as solid + auto add_as_solid = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer, + std::vector& buffer_vertices, std::vector& buffer_indices, size_t move_id) { + auto store_vertex = [](std::vector& buffer_vertices, const Vec3f& position, const Vec3f& normal) { + // append position + for (int j = 0; j < 3; ++j) { + buffer_vertices.push_back(position[j]); + } + // append normal + for (int j = 0; j < 3; ++j) { + buffer_vertices.push_back(normal[j]); + } + }; + auto store_triangle = [](std::vector& buffer_indices, unsigned int i1, unsigned int i2, unsigned int i3) { + buffer_indices.push_back(i1); + buffer_indices.push_back(i2); + buffer_indices.push_back(i3); + }; + + Vec3f dir = (curr.position - prev.position).normalized(); + Vec3f right = (std::abs(std::abs(dir.dot(Vec3f::UnitZ())) - 1.0f) < EPSILON) ? -Vec3f::UnitY() : Vec3f(dir[1], -dir[0], 0.0f).normalized(); + Vec3f up = right.cross(dir); + float prev_half_width = 0.5f * prev.width; + float prev_half_height = 0.5f * prev.height; + float curr_half_width = 0.5f * curr.width; + float curr_half_height = 0.5f * curr.height; + Vec3f prev_pos = Vec3f(prev.position[0], prev.position[1], prev.position[2]) - prev_half_height * up; + Vec3f curr_pos = Vec3f(curr.position[0], curr.position[1], curr.position[2]) - curr_half_height * up; + + if (prev.type != curr.type || !buffer.paths.back().matches(curr)) { + buffer.add_path(curr, static_cast(buffer_indices.size()), static_cast(move_id - 1)); + buffer.paths.back().first.position = prev.position; + } + + unsigned int starting_vertices_size = static_cast(buffer_vertices.size() / buffer.vertices.vertex_size_floats()); + + // vertices 1st endpoint + store_vertex(buffer_vertices, prev_pos + prev_half_height * up, up); // top + store_vertex(buffer_vertices, prev_pos + prev_half_width * right, right); // right + store_vertex(buffer_vertices, prev_pos - prev_half_height * up, -up); // bottom + store_vertex(buffer_vertices, prev_pos - prev_half_width * right, -right); // left + + // vertices 2nd endpoint + store_vertex(buffer_vertices, curr_pos + curr_half_height * up, up); // top + store_vertex(buffer_vertices, curr_pos + curr_half_width * right, right); // right + store_vertex(buffer_vertices, curr_pos - curr_half_height * up, -up); // bottom + store_vertex(buffer_vertices, curr_pos - curr_half_width * right, -right); // left + + // triangles starting cap + store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size + 2, starting_vertices_size + 1); + store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size + 3, starting_vertices_size + 2); + + // triangles sides + store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size + 1, starting_vertices_size + 4); + store_triangle(buffer_indices, starting_vertices_size + 1, starting_vertices_size + 5, starting_vertices_size + 4); + store_triangle(buffer_indices, starting_vertices_size + 1, starting_vertices_size + 2, starting_vertices_size + 5); + store_triangle(buffer_indices, starting_vertices_size + 2, starting_vertices_size + 6, starting_vertices_size + 5); + store_triangle(buffer_indices, starting_vertices_size + 2, starting_vertices_size + 3, starting_vertices_size + 6); + store_triangle(buffer_indices, starting_vertices_size + 3, starting_vertices_size + 7, starting_vertices_size + 6); + store_triangle(buffer_indices, starting_vertices_size + 3, starting_vertices_size + 0, starting_vertices_size + 7); + store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size + 4, starting_vertices_size + 7); + + // triangles ending cap + store_triangle(buffer_indices, starting_vertices_size + 4, starting_vertices_size + 6, starting_vertices_size + 7); + store_triangle(buffer_indices, starting_vertices_size + 4, starting_vertices_size + 5, starting_vertices_size + 6); + + buffer.paths.back().last = { static_cast(buffer_indices.size() - 1), static_cast(move_id), curr.position }; + }; + // toolpaths data -> extract from result std::vector> vertices(m_buffers.size()); std::vector> indices(m_buffers.size()); @@ -926,55 +1064,21 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) case EMoveType::Retract: case EMoveType::Unretract: { - for (int j = 0; j < 3; ++j) { - buffer_vertices.push_back(curr.position[j]); - } - buffer.add_path(curr, static_cast(buffer_indices.size()), static_cast(i)); - buffer_indices.push_back(static_cast(buffer_indices.size())); + add_as_point(curr, buffer, buffer_vertices, buffer_indices, i); break; } +#if ENABLE_GCODE_RENDER_EXTRUSION_AS_TRIANGLES case EMoveType::Extrude: + { + add_as_solid(prev, curr, buffer, buffer_vertices, buffer_indices, i); + break; + } +#else + case EMoveType::Extrude: +#endif // ENABLE_GCODE_RENDER_EXTRUSION_AS_TRIANGLES case EMoveType::Travel: { - // x component of the normal to the current segment (the normal is parallel to the XY plane) - float normal_x = (curr.position - prev.position).normalized()[1]; - - if (prev.type != curr.type || !buffer.paths.back().matches(curr)) { - // add starting vertex position - for (int j = 0; j < 3; ++j) { - buffer_vertices.push_back(prev.position[j]); - } - // add starting vertex normal x component - buffer_vertices.push_back(normal_x); - // add starting index - buffer_indices.push_back(buffer_indices.size()); - buffer.add_path(curr, static_cast(buffer_indices.size() - 1), static_cast(i - 1)); - Path& last_path = buffer.paths.back(); - last_path.first.position = prev.position; - } - - Path& last_path = buffer.paths.back(); - if (last_path.first.i_id != last_path.last.i_id) - { - // add previous vertex position - for (int j = 0; j < 3; ++j) { - buffer_vertices.push_back(prev.position[j]); - } - // add previous vertex normal x component - buffer_vertices.push_back(normal_x); - // add previous index - buffer_indices.push_back(buffer_indices.size()); - } - - // add current vertex position - for (int j = 0; j < 3; ++j) { - buffer_vertices.push_back(curr.position[j]); - } - // add current vertex normal x component - buffer_vertices.push_back(normal_x); - // add current index - buffer_indices.push_back(buffer_indices.size()); - last_path.last = { static_cast(buffer_indices.size() - 1), static_cast(i), curr.position }; + add_as_line(prev, curr, buffer, buffer_vertices, buffer_indices, i); break; } default: { break; } @@ -989,7 +1093,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) const std::vector& buffer_vertices = vertices[i]; buffer.vertices.count = buffer_vertices.size() / buffer.vertices.vertex_size_floats(); #if ENABLE_GCODE_VIEWER_STATISTICS - m_statistics.vertices_gpu_size = buffer_vertices.size() * sizeof(float); + m_statistics.vertices_gpu_size += buffer_vertices.size() * sizeof(float); #endif // ENABLE_GCODE_VIEWER_STATISTICS glsafe(::glGenBuffers(1, &buffer.vertices.id)); @@ -1199,15 +1303,24 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool // searches the path containing the current position for (const Path& path : buffer.paths) { if (path.first.s_id <= m_sequential_view.current.last && m_sequential_view.current.last <= path.last.s_id) { - size_t offset = m_sequential_view.current.last - path.first.s_id; - if (offset > 0 && (path.type == EMoveType::Travel || path.type == EMoveType::Extrude)) - offset = 1 + 2 * (offset - 1); - + unsigned int offset = m_sequential_view.current.last - path.first.s_id; + if (offset > 0) { + if (buffer.primitive_type == TBuffer::EPrimitiveType::Line) + offset = 2 * offset - 1; + else if (buffer.primitive_type == TBuffer::EPrimitiveType::Triangle) + offset = 36 * (offset - 1) + 30; + } offset += path.first.i_id; + // gets the index from the index buffer on gpu + unsigned int index = 0; + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.indices.id)); + glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, static_cast(offset * sizeof(unsigned int)), static_cast(sizeof(unsigned int)), static_cast(&index))); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + // gets the position from the vertices buffer on gpu glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vertices.id)); - glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, static_cast(offset * buffer.vertices.vertex_size_bytes()), static_cast(3 * sizeof(float)), static_cast(m_sequential_view.current_position.data()))); + glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, static_cast(index * buffer.vertices.vertex_size_bytes()), static_cast(3 * sizeof(float)), static_cast(m_sequential_view.current_position.data()))); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); found = true; break; @@ -1238,15 +1351,23 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool it->path_id = id; } - unsigned int size = std::min(m_sequential_view.current.last, path.last.s_id) - std::max(m_sequential_view.current.first, path.first.s_id) + 1; - if (path.type == EMoveType::Extrude || path.type == EMoveType::Travel) - size = 2 * (size - 1); + unsigned int size_in_vertices = std::min(m_sequential_view.current.last, path.last.s_id) - std::max(m_sequential_view.current.first, path.first.s_id) + 1; + unsigned int size_in_indices = 0; + switch (buffer->primitive_type) + { + case TBuffer::EPrimitiveType::Point: { size_in_indices = size_in_vertices; break; } + case TBuffer::EPrimitiveType::Line: { size_in_indices = 2 * (size_in_vertices - 1); break; } + case TBuffer::EPrimitiveType::Triangle: { size_in_indices = 36 * (size_in_vertices - 1); break; } + } + it->sizes.push_back(size_in_indices); - it->sizes.push_back(size); unsigned int delta_1st = 0; - if ((path.first.s_id < m_sequential_view.current.first) && (m_sequential_view.current.first <= path.last.s_id)) + if (path.first.s_id < m_sequential_view.current.first && m_sequential_view.current.first <= path.last.s_id) delta_1st = m_sequential_view.current.first - path.first.s_id; + if (buffer->primitive_type == TBuffer::EPrimitiveType::Triangle) + delta_1st *= 36; + it->offsets.push_back(static_cast((path.first.i_id + delta_1st) * sizeof(unsigned int))); } @@ -1266,8 +1387,15 @@ void GCodeViewer::render_toolpaths() const { #if ENABLE_GCODE_VIEWER_SHADERS_EDITOR float point_size = m_shaders_editor.points.point_size; + std::array light_intensity = { + m_shaders_editor.lines.lights.ambient, + m_shaders_editor.lines.lights.top_diffuse, + m_shaders_editor.lines.lights.front_diffuse, + m_shaders_editor.lines.lights.global + }; #else float point_size = 0.8f; + std::array light_intensity = { 0.25f, 0.7f, 0.75f, 0.75f }; #endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR const Camera& camera = wxGetApp().plater()->get_camera(); double zoom = camera.get_zoom(); @@ -1275,10 +1403,13 @@ void GCodeViewer::render_toolpaths() const float near_plane_height = camera.get_type() == Camera::Perspective ? static_cast(viewport[3]) / (2.0f * static_cast(2.0 * std::tan(0.5 * Geometry::deg2rad(camera.get_fov())))) : static_cast(viewport[3]) * 0.0005; - Transform3d inv_proj = camera.get_projection_matrix().inverse(); + auto set_uniform_color = [](const std::array& color, GLShaderProgram& shader) { + std::array color4 = { color[0], color[1], color[2], 1.0f }; + shader.set_uniform("uniform_color", color4); + }; - auto render_as_points = [this, zoom, inv_proj, viewport, point_size, near_plane_height](const TBuffer& buffer, EOptionsColors color_id, GLShaderProgram& shader) { - shader.set_uniform("uniform_color", Options_Colors[static_cast(color_id)]); + auto render_as_points = [this, zoom, point_size, near_plane_height, set_uniform_color](const TBuffer& buffer, EOptionsColors color_id, GLShaderProgram& shader) { + set_uniform_color(Options_Colors[static_cast(color_id)], shader); shader.set_uniform("zoom", zoom); #if ENABLE_GCODE_VIEWER_SHADERS_EDITOR shader.set_uniform("percent_outline_radius", 0.01f * static_cast(m_shaders_editor.points.percent_outline)); @@ -1287,8 +1418,6 @@ void GCodeViewer::render_toolpaths() const shader.set_uniform("percent_outline_radius", 0.0f); shader.set_uniform("percent_center_radius", 0.33f); #endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR - shader.set_uniform("viewport", viewport); - shader.set_uniform("inv_proj_matrix", inv_proj); shader.set_uniform("point_size", point_size); shader.set_uniform("near_plane_height", near_plane_height); @@ -1306,12 +1435,23 @@ void GCodeViewer::render_toolpaths() const glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); }; - auto render_as_lines = [this](const TBuffer& buffer, GLShaderProgram& shader) { + auto render_as_lines = [this, light_intensity, set_uniform_color](const TBuffer& buffer, GLShaderProgram& shader) { + shader.set_uniform("light_intensity", light_intensity); for (const RenderPath& path : buffer.render_paths) { - shader.set_uniform("uniform_color", path.color); + set_uniform_color(path.color, shader); glsafe(::glMultiDrawElements(GL_LINES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.gl_multi_line_strip_calls_count; + ++m_statistics.gl_multi_lines_calls_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS + } + }; + + auto render_as_triangles = [this, set_uniform_color](const TBuffer& buffer, GLShaderProgram& shader) { + for (const RenderPath& path : buffer.render_paths) { + set_uniform_color(path.color, shader); + glsafe(::glMultiDrawElements(GL_TRIANGLES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_multi_triangles_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } }; @@ -1338,40 +1478,50 @@ void GCodeViewer::render_toolpaths() const shader->start_using(); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vertices.id)); - glsafe(::glVertexPointer(buffer.vertices.vertex_size_floats(), GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)0)); + glsafe(::glVertexPointer(buffer.vertices.position_size_floats(), GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.position_offset_size())); glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + bool has_normals = buffer.vertices.normal_size_floats() > 0; + if (has_normals) { + glsafe(::glNormalPointer(GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.normal_offset_size())); + glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); + } glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.indices.id)); - switch (buffer_type(i)) + switch (buffer.primitive_type) { - default: { break; } - case EMoveType::Tool_change: { render_as_points(buffer, EOptionsColors::ToolChanges, *shader); break; } - case EMoveType::Color_change: { render_as_points(buffer, EOptionsColors::ColorChanges, *shader); break; } - case EMoveType::Pause_Print: { render_as_points(buffer, EOptionsColors::PausePrints, *shader); break; } - case EMoveType::Custom_GCode: { render_as_points(buffer, EOptionsColors::CustomGCodes, *shader); break; } - case EMoveType::Retract: { render_as_points(buffer, EOptionsColors::Retractions, *shader); break; } - case EMoveType::Unretract: { render_as_points(buffer, EOptionsColors::Unretractions, *shader); break; } - case EMoveType::Extrude: - case EMoveType::Travel: + case TBuffer::EPrimitiveType::Point: + { + EOptionsColors color; + switch (buffer_type(i)) + { + case EMoveType::Tool_change: { color = EOptionsColors::ToolChanges; break; } + case EMoveType::Color_change: { color = EOptionsColors::ColorChanges; break; } + case EMoveType::Pause_Print: { color = EOptionsColors::PausePrints; break; } + case EMoveType::Custom_GCode: { color = EOptionsColors::CustomGCodes; break; } + case EMoveType::Retract: { color = EOptionsColors::Retractions; break; } + case EMoveType::Unretract: { color = EOptionsColors::Unretractions; break; } + } + render_as_points(buffer, color, *shader); + break; + } + case TBuffer::EPrimitiveType::Line: { -#if ENABLE_GCODE_VIEWER_SHADERS_EDITOR - std::array light_intensity = { - m_shaders_editor.lines.lights.ambient, - m_shaders_editor.lines.lights.top_diffuse, - m_shaders_editor.lines.lights.front_diffuse, - m_shaders_editor.lines.lights.global }; -#else - std::array light_intensity = { 0.25f, 0.7f, 0.75f, 0.75f }; -#endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR - shader->set_uniform("light_intensity", light_intensity); render_as_lines(buffer, *shader); break; } + case TBuffer::EPrimitiveType::Triangle: + { + render_as_triangles(buffer, *shader); + break; + } } glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + if (has_normals) + glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); + glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); @@ -2454,9 +2604,13 @@ void GCodeViewer::render_statistics() const ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.gl_multi_points_calls_count)); - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Multi GL_LINE_STRIP calls:")); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Multi GL_LINES calls:")); ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.gl_multi_line_strip_calls_count)); + imgui.text(std::to_string(m_statistics.gl_multi_lines_calls_count)); + + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Multi GL_TRIANGLES calls:")); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.gl_multi_triangles_calls_count)); ImGui::Separator(); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index e6c3e26eaa..919b9de6e1 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -38,8 +38,12 @@ class GCodeViewer { enum class EFormat : unsigned char { + // vertex format: 3 floats -> position.x|position.y|position.z Position, - PositionNormal + // vertex format: 4 floats -> position.x|position.y|position.z|normal.x + PositionNormal1, + // vertex format: 6 floats -> position.x|position.y|position.z|normal.x|normal.y|normal.z + PositionNormal3 }; EFormat format{ EFormat::Position }; @@ -49,18 +53,45 @@ class GCodeViewer size_t count{ 0 }; size_t data_size_bytes() const { return count * vertex_size_bytes(); } - size_t vertex_size_floats() const + + size_t vertex_size_floats() const { return position_size_floats() + normal_size_floats(); } + size_t vertex_size_bytes() const { return vertex_size_floats() * sizeof(float); } + + size_t position_offset_floats() const { return 0; } + size_t position_offset_size() const { return position_offset_floats() * sizeof(float); } + size_t position_size_floats() const { switch (format) { - // vertex format: 3 floats -> position.x|position.y|position.z - case EFormat::Position: { return 3; } - // vertex format: 4 floats -> position.x|position.y|position.z|normal.x - case EFormat::PositionNormal: { return 4; } - default: { return 0; } + case EFormat::Position: + case EFormat::PositionNormal3: { return 3; } + case EFormat::PositionNormal1: { return 4; } + default: { return 0; } } } - size_t vertex_size_bytes() const { return vertex_size_floats() * sizeof(float); } + size_t position_size_bytes() const { return position_size_floats() * sizeof(float); } + + size_t normal_offset_floats() const + { + switch (format) + { + case EFormat::Position: + case EFormat::PositionNormal1: { return 0; } + case EFormat::PositionNormal3: { return 3; } + default: { return 0; } + } + } + size_t normal_offset_size() const { return normal_offset_floats() * sizeof(float); } + size_t normal_size_floats() const { + switch (format) + { + default: + case EFormat::Position: + case EFormat::PositionNormal1: { return 0; } + case EFormat::PositionNormal3: { return 3; } + } + } + size_t normal_size_bytes() const { return normal_size_floats() * sizeof(float); } void reset(); }; @@ -116,6 +147,14 @@ class GCodeViewer // buffer containing data for rendering a specific toolpath type struct TBuffer { + enum class EPrimitiveType : unsigned char + { + Point, + Line, + Triangle + }; + + EPrimitiveType primitive_type; VBuffer vertices; IBuffer indices; @@ -185,8 +224,7 @@ class GCodeViewer void reset_role_visibility_flags() { role_visibility_flags = 0; - for (unsigned int i = 0; i < erCount; ++i) - { + for (unsigned int i = 0; i < erCount; ++i) { role_visibility_flags |= 1 << i; } } @@ -204,7 +242,8 @@ class GCodeViewer long long refresh_paths_time{ 0 }; // opengl calls long long gl_multi_points_calls_count{ 0 }; - long long gl_multi_line_strip_calls_count{ 0 }; + long long gl_multi_lines_calls_count{ 0 }; + long long gl_multi_triangles_calls_count{ 0 }; // memory long long results_size{ 0 }; long long vertices_gpu_size{ 0 }; @@ -231,7 +270,8 @@ class GCodeViewer void reset_opengl() { gl_multi_points_calls_count = 0; - gl_multi_line_strip_calls_count = 0; + gl_multi_lines_calls_count = 0; + gl_multi_triangles_calls_count = 0; } void reset_sizes() { diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index 6baf46f6b4..1041faa3dc 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -37,7 +37,7 @@ std::pair GLShadersManager::init() valid &= append_shader("options_110", { "options_110.vs", "options_110.fs" }); if (GUI::wxGetApp().is_glsl_version_greater_or_equal_to(1, 20)) valid &= append_shader("options_120", { "options_120.vs", "options_120.fs" }); - // used to render extrusion and travel paths in gcode preview + // used to render extrusion and travel paths as lines in gcode preview valid &= append_shader("toolpaths_lines", { "toolpaths_lines.vs", "toolpaths_lines.fs" }); // used to render objects in 3d editor valid &= append_shader("gouraud", { "gouraud.vs", "gouraud.fs" }); From e0e75f4a0e9a3942edd5f635ba309adb2f8208ef Mon Sep 17 00:00:00 2001 From: Slic3rPE Date: Wed, 26 Aug 2020 15:50:05 +0200 Subject: [PATCH 335/503] Starting a new Slicer instance from the menu - fix of Windows build --- src/slic3r/GUI/MainFrame.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 122d9c6108..8f2aeef8ab 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -13,7 +13,6 @@ #include #include -#include #include "libslic3r/Print.hpp" #include "libslic3r/Polygon.hpp" @@ -41,6 +40,12 @@ #include #endif // _WIN32 +// For starting another PrusaSlicer instance on OSX. +// Fails to compile on Windows on the build server. +#ifdef __APPLE__ + #include +#endif + namespace Slic3r { namespace GUI { From 42a7f2b1d873907b96502889d319c7056eee2f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Wed, 26 Aug 2020 16:51:34 +0200 Subject: [PATCH 336/503] Preparation for new infill --- src/libslic3r/CMakeLists.txt | 2 ++ src/libslic3r/Fill/Fill.cpp | 1 + src/libslic3r/Fill/FillAdaptive.cpp | 19 +++++++++++ src/libslic3r/Fill/FillAdaptive.hpp | 53 +++++++++++++++++++++++++++++ src/libslic3r/Fill/FillBase.cpp | 2 ++ src/libslic3r/Fill/FillBase.hpp | 6 ++++ src/libslic3r/Print.hpp | 8 +++++ src/libslic3r/PrintConfig.cpp | 2 ++ src/libslic3r/PrintConfig.hpp | 3 +- 9 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 src/libslic3r/Fill/FillAdaptive.cpp create mode 100644 src/libslic3r/Fill/FillAdaptive.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 290b8953cc..afec759746 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -46,6 +46,8 @@ add_library(libslic3r STATIC Fill/Fill.hpp Fill/Fill3DHoneycomb.cpp Fill/Fill3DHoneycomb.hpp + Fill/FillAdaptive.cpp + Fill/FillAdaptive.hpp Fill/FillBase.cpp Fill/FillBase.hpp Fill/FillConcentric.cpp diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 3c16527f07..c948df400e 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -345,6 +345,7 @@ void Layer::make_fills() f->layer_id = this->id(); f->z = this->print_z; f->angle = surface_fill.params.angle; + f->adapt_fill_octree = this->object()->adaptiveInfillOctree(); // calculate flow spacing for infill pattern generation bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.flow.bridge; diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp new file mode 100644 index 0000000000..cb11385983 --- /dev/null +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -0,0 +1,19 @@ +#include "../ClipperUtils.hpp" +#include "../ExPolygon.hpp" +#include "../Surface.hpp" + +#include "FillAdaptive.hpp" + +namespace Slic3r { + +void FillAdaptive::_fill_surface_single( + const FillParams ¶ms, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon &expolygon, + Polylines &polylines_out) +{ + +} + +} // namespace Slic3r diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp new file mode 100644 index 0000000000..e0a97a1b9b --- /dev/null +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -0,0 +1,53 @@ +#ifndef slic3r_FillAdaptive_hpp_ +#define slic3r_FillAdaptive_hpp_ + +#include "FillBase.hpp" + +namespace Slic3r { + +namespace FillAdaptive_Internal +{ + struct CubeProperties + { + double edge_length; // Lenght of edge of a cube + double height; // Height of rotated cube (standing on the corner) + double diagonal_length; // Length of diagonal of a cube a face + double line_z_distance; // Defines maximal distance from a center of a cube on Z axis on which lines will be created + double line_xy_distance;// Defines maximal distance from a center of a cube on X and Y axis on which lines will be created + }; + + struct Cube + { + Vec3d center; + size_t depth; + CubeProperties properties; + std::vector children; + }; + + struct Octree + { + Cube *root_cube; + Vec3d origin; + }; +}; // namespace FillAdaptive_Internal + +class FillAdaptive : public Fill +{ +public: + virtual ~FillAdaptive() {} + +protected: + virtual Fill* clone() const { return new FillAdaptive(*this); }; + virtual void _fill_surface_single( + const FillParams ¶ms, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon &expolygon, + Polylines &polylines_out); + + virtual bool no_sort() const { return true; } +}; + +} // namespace Slic3r + +#endif // slic3r_FillAdaptive_hpp_ diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index c760218c01..c1f38dad5c 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -16,6 +16,7 @@ #include "FillRectilinear.hpp" #include "FillRectilinear2.hpp" #include "FillRectilinear3.hpp" +#include "FillAdaptive.hpp" namespace Slic3r { @@ -37,6 +38,7 @@ Fill* Fill::new_from_type(const InfillPattern type) case ipArchimedeanChords: return new FillArchimedeanChords(); case ipHilbertCurve: return new FillHilbertCurve(); case ipOctagramSpiral: return new FillOctagramSpiral(); + case ipAdaptiveCubic: return new FillAdaptive(); default: throw std::invalid_argument("unknown type"); } } diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index 2e9b647354..9f70b69e08 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -19,6 +19,10 @@ class ExPolygon; class Surface; enum InfillPattern : int; +namespace FillAdaptive_Internal { + struct Octree; +}; + class InfillFailedException : public std::runtime_error { public: InfillFailedException() : std::runtime_error("Infill failed") {} @@ -69,6 +73,8 @@ public: // In scaled coordinates. Bounding box of the 2D projection of the object. BoundingBox bounding_box; + FillAdaptive_Internal::Octree* adapt_fill_octree = nullptr; + public: virtual ~Fill() {} diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 05929dd2ef..5f8613a2db 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -25,6 +25,10 @@ enum class SlicingMode : uint32_t; class Layer; class SupportLayer; +namespace FillAdaptive_Internal { + struct Octree; +}; + // Print step IDs for keeping track of the print state. enum PrintStep { psSkirt, @@ -191,6 +195,7 @@ public: void project_and_append_custom_enforcers(std::vector& enforcers) const { project_and_append_custom_supports(FacetSupportType::ENFORCER, enforcers); } void project_and_append_custom_blockers(std::vector& blockers) const { project_and_append_custom_supports(FacetSupportType::BLOCKER, blockers); } + FillAdaptive_Internal::Octree* adaptiveInfillOctree() { return m_adapt_fill_octree; } private: // to be called from Print only. friend class Print; @@ -232,6 +237,7 @@ private: void discover_horizontal_shells(); void combine_infill(); void _generate_support_material(); + void prepare_adaptive_infill_data(); // XYZ in scaled coordinates Vec3crd m_size; @@ -252,6 +258,8 @@ private: // so that next call to make_perimeters() performs a union() before computing loops bool m_typed_slices = false; + FillAdaptive_Internal::Octree* m_adapt_fill_octree = nullptr; + std::vector slice_region(size_t region_id, const std::vector &z, SlicingMode mode) const; std::vector slice_modifiers(size_t region_id, const std::vector &z) const; std::vector slice_volumes(const std::vector &z, SlicingMode mode, const std::vector &volumes) const; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 3401dcc020..a855e4b1a8 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -881,6 +881,7 @@ void PrintConfigDef::init_fff_params() def->enum_values.push_back("hilbertcurve"); def->enum_values.push_back("archimedeanchords"); def->enum_values.push_back("octagramspiral"); + def->enum_values.push_back("adaptivecubic"); def->enum_labels.push_back(L("Rectilinear")); def->enum_labels.push_back(L("Grid")); def->enum_labels.push_back(L("Triangles")); @@ -894,6 +895,7 @@ void PrintConfigDef::init_fff_params() def->enum_labels.push_back(L("Hilbert Curve")); def->enum_labels.push_back(L("Archimedean Chords")); def->enum_labels.push_back(L("Octagram Spiral")); + def->enum_labels.push_back(L("Adaptive Cubic")); def->set_default_value(new ConfigOptionEnum(ipStars)); def = this->add("first_layer_acceleration", coFloat); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index c4566c983e..abd19a7fea 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -39,7 +39,7 @@ enum AuthorizationType { enum InfillPattern : int { ipRectilinear, ipMonotonous, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, - ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipCount, + ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipCount, }; enum class IroningType { @@ -139,6 +139,7 @@ template<> inline const t_config_enum_values& ConfigOptionEnum::g keys_map["hilbertcurve"] = ipHilbertCurve; keys_map["archimedeanchords"] = ipArchimedeanChords; keys_map["octagramspiral"] = ipOctagramSpiral; + keys_map["adaptivecubic"] = ipAdaptiveCubic; } return keys_map; } From 3ac16d9c9cd2d796db45c266964c507c423a7992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Wed, 26 Aug 2020 18:15:59 +0200 Subject: [PATCH 337/503] Building octree based on distance from mesh --- src/libslic3r/Fill/FillAdaptive.cpp | 86 +++++++++++++++++++++++++++++ src/libslic3r/Fill/FillAdaptive.hpp | 16 ++++++ src/libslic3r/PrintObject.cpp | 23 ++++++++ 3 files changed, 125 insertions(+) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index cb11385983..ce779ad005 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -1,6 +1,8 @@ #include "../ClipperUtils.hpp" #include "../ExPolygon.hpp" #include "../Surface.hpp" +#include "../Geometry.hpp" +#include "../AABBTreeIndirect.hpp" #include "FillAdaptive.hpp" @@ -16,4 +18,88 @@ void FillAdaptive::_fill_surface_single( } +FillAdaptive_Internal::Octree* FillAdaptive::build_octree( + TriangleMesh &triangleMesh, + coordf_t line_spacing, + const BoundingBoxf3 &printer_volume, + const Vec3d &cube_center) +{ + using namespace FillAdaptive_Internal; + + if(line_spacing <= 0) + { + return nullptr; + } + + // The furthest point from center of bed. + double furthest_point = std::sqrt(((printer_volume.size()[0] * printer_volume.size()[0]) / 4.0) + + ((printer_volume.size()[1] * printer_volume.size()[1]) / 4.0) + + (printer_volume.size()[2] * printer_volume.size()[2])); + double max_cube_edge_length = furthest_point * 2; + + std::vector cubes_properties; + for (double edge_length = (line_spacing * 2); edge_length < (max_cube_edge_length * 2); edge_length *= 2) + { + CubeProperties props{}; + props.edge_length = edge_length; + props.height = edge_length * sqrt(3); + props.diagonal_length = edge_length * sqrt(2); + props.line_z_distance = edge_length / sqrt(3); + props.line_xy_distance = edge_length / sqrt(6); + cubes_properties.push_back(props); + } + + if (triangleMesh.its.vertices.empty()) + { + triangleMesh.require_shared_vertices(); + } + + Vec3d rotation = Vec3d(Geometry::deg2rad(225.0), Geometry::deg2rad(215.0), Geometry::deg2rad(30.0)); + Transform3d rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), rotation, Vec3d::Ones(), Vec3d::Ones()); + + AABBTreeIndirect::Tree3f aabbTree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(triangleMesh.its.vertices, triangleMesh.its.indices); + Octree *octree = new Octree{new Cube{cube_center, cubes_properties.size() - 1, cubes_properties.back()}, cube_center}; + + FillAdaptive::expand_cube(octree->root_cube, cubes_properties, rotation_matrix, aabbTree, triangleMesh); + + return octree; +} + +void FillAdaptive::expand_cube( + FillAdaptive_Internal::Cube *cube, + const std::vector &cubes_properties, + const Transform3d &rotation_matrix, + const AABBTreeIndirect::Tree3f &distanceTree, + const TriangleMesh &triangleMesh) +{ + using namespace FillAdaptive_Internal; + + if (cube == nullptr || cube->depth == 0) + { + return; + } + + std::vector child_centers = { + Vec3d(-1, -1, -1), Vec3d( 1, -1, -1), Vec3d(-1, 1, -1), Vec3d(-1, -1, 1), + Vec3d( 1, 1, 1), Vec3d(-1, 1, 1), Vec3d( 1, -1, 1), Vec3d( 1, 1, -1) + }; + + double cube_radius_squared = (cube->properties.height * cube->properties.height) / 16; + + for (const Vec3d &child_center : child_centers) { + Vec3d child_center_transformed = cube->center + rotation_matrix * (child_center * (cube->properties.edge_length / 4)); + Vec3d closest_point = Vec3d::Zero(); + size_t closest_triangle_idx = 0; + + double distance_squared = AABBTreeIndirect::squared_distance_to_indexed_triangle_set( + triangleMesh.its.vertices, triangleMesh.its.indices, distanceTree, child_center_transformed, + closest_triangle_idx,closest_point); + + if(distance_squared <= cube_radius_squared) { + cube->children.push_back(new Cube{child_center_transformed, cube->depth - 1, cubes_properties[cube->depth - 1]}); + FillAdaptive::expand_cube(cube->children.back(), cubes_properties, rotation_matrix, distanceTree, triangleMesh); + } + } +} + } // namespace Slic3r diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index e0a97a1b9b..49c5276a93 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -1,6 +1,8 @@ #ifndef slic3r_FillAdaptive_hpp_ #define slic3r_FillAdaptive_hpp_ +#include "../AABBTreeIndirect.hpp" + #include "FillBase.hpp" namespace Slic3r { @@ -46,6 +48,20 @@ protected: Polylines &polylines_out); virtual bool no_sort() const { return true; } + +public: + static FillAdaptive_Internal::Octree* build_octree( + TriangleMesh &triangleMesh, + coordf_t line_spacing, + const BoundingBoxf3 &printer_volume, + const Vec3d &cube_center); + + static void expand_cube( + FillAdaptive_Internal::Cube *cube, + const std::vector &cubes_properties, + const Transform3d &rotation_matrix, + const AABBTreeIndirect::Tree3f &distanceTree, + const TriangleMesh &triangleMesh); }; } // namespace Slic3r diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index c90a05ef31..5752452ad7 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -9,6 +9,8 @@ #include "Surface.hpp" #include "Slicing.hpp" #include "Utils.hpp" +#include "AABBTreeIndirect.hpp" +#include "Fill/FillAdaptive.hpp" #include #include @@ -360,6 +362,8 @@ void PrintObject::prepare_infill() } // for each layer #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + this->prepare_adaptive_infill_data(); + this->set_done(posPrepareInfill); } @@ -428,6 +432,25 @@ void PrintObject::generate_support_material() } } +void PrintObject::prepare_adaptive_infill_data() +{ + float fill_density = this->print()->full_print_config().opt_float("fill_density"); + float infill_extrusion_width = this->print()->full_print_config().opt_float("infill_extrusion_width"); + + coordf_t line_spacing = infill_extrusion_width / ((fill_density / 100.0f) * 0.333333333f); + + BoundingBoxf bed_shape(this->print()->config().bed_shape.values); + BoundingBoxf3 printer_volume(Vec3d(bed_shape.min(0), bed_shape.min(1), 0), + Vec3d(bed_shape.max(0), bed_shape.max(1), this->print()->config().max_print_height)); + + Vec3d model_center = this->model_object()->bounding_box().center(); + model_center(2) = 0.0f; // Set position in Z axis to 0 + // Center of the first cube in octree + + TriangleMesh mesh = this->model_object()->mesh(); + this->m_adapt_fill_octree = FillAdaptive::build_octree(mesh, line_spacing, printer_volume, model_center); +} + void PrintObject::clear_layers() { for (Layer *l : m_layers) From 41f474a884bdad01b973cd6bb734cea3a31384fc Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 26 Aug 2020 21:51:50 +0200 Subject: [PATCH 338/503] Fixed performance issues when adding / removing Presets into PresetCollection. This improves application startup time by 25-33%. --- src/libslic3r/PrintConfig.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index c4566c983e..b133a2e4eb 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -239,9 +239,13 @@ class DynamicPrintConfig : public DynamicConfig public: DynamicPrintConfig() {} DynamicPrintConfig(const DynamicPrintConfig &rhs) : DynamicConfig(rhs) {} + DynamicPrintConfig(DynamicPrintConfig &&rhs) noexcept : DynamicConfig(std::move(rhs)) {} explicit DynamicPrintConfig(const StaticPrintConfig &rhs); explicit DynamicPrintConfig(const ConfigBase &rhs) : DynamicConfig(rhs) {} + DynamicPrintConfig& operator=(const DynamicPrintConfig &rhs) { DynamicConfig::operator=(rhs); return *this; } + DynamicPrintConfig& operator=(DynamicPrintConfig &&rhs) noexcept { DynamicConfig::operator=(std::move(rhs)); return *this; } + static DynamicPrintConfig full_print_config(); static DynamicPrintConfig* new_from_defaults_keys(const std::vector &keys); From eaaff4e707e8321b7bc1b9e9151fb94a4befc3bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Wed, 26 Aug 2020 22:18:51 +0200 Subject: [PATCH 339/503] Generating polylines from octree --- src/libslic3r/Fill/FillAdaptive.cpp | 57 +++++++++++++++++++++++++++++ src/libslic3r/Fill/FillAdaptive.hpp | 7 ++++ 2 files changed, 64 insertions(+) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index ce779ad005..cac9c1c3b2 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -15,7 +15,64 @@ void FillAdaptive::_fill_surface_single( ExPolygon &expolygon, Polylines &polylines_out) { + Polylines infill_polylines; + this->generate_polylines(this->adapt_fill_octree->root_cube, this->z, this->adapt_fill_octree->origin, infill_polylines); + // Crop all polylines + polylines_out = intersection_pl(infill_polylines, to_polygons(expolygon)); +} + +void FillAdaptive::generate_polylines( + FillAdaptive_Internal::Cube *cube, + double z_position, + const Vec3d &origin, + Polylines &polylines_out) +{ + using namespace FillAdaptive_Internal; + + if(cube == nullptr) + { + return; + } + + double z_diff = std::abs(z_position - cube->center.z()); + + if (z_diff > cube->properties.height / 2) + { + return; + } + + if (z_diff < cube->properties.line_z_distance) + { + Point from( + scale_((cube->properties.diagonal_length / 2) * (cube->properties.line_z_distance - z_diff) / cube->properties.line_z_distance), + scale_(cube->properties.line_xy_distance - ((z_position - (cube->center.z() - cube->properties.line_z_distance)) / sqrt(2)))); + Point to(-from.x(), from.y()); + // Relative to cube center + + float rotation_angle = Geometry::deg2rad(120.0); + + for (int dir_idx = 0; dir_idx < 3; dir_idx++) + { + Vec3d offset = cube->center - origin; + Point from_abs(from), to_abs(to); + + from_abs.x() += scale_(offset.x()); + from_abs.y() += scale_(offset.y()); + to_abs.x() += scale_(offset.x()); + to_abs.y() += scale_(offset.y()); + + polylines_out.push_back(Polyline(from_abs, to_abs)); + + from.rotate(rotation_angle); + to.rotate(rotation_angle); + } + } + + for(Cube *child : cube->children) + { + generate_polylines(child, z_position, origin, polylines_out); + } } FillAdaptive_Internal::Octree* FillAdaptive::build_octree( diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index 49c5276a93..9e1a196af1 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -33,6 +33,11 @@ namespace FillAdaptive_Internal }; }; // namespace FillAdaptive_Internal +// +// Some of the algorithms used by class FillAdaptive were inspired by +// Cura Engine's class SubDivCube +// https://github.com/Ultimaker/CuraEngine/blob/master/src/infill/SubDivCube.h +// class FillAdaptive : public Fill { public: @@ -49,6 +54,8 @@ protected: virtual bool no_sort() const { return true; } + void generate_polylines(FillAdaptive_Internal::Cube *cube, double z_position, const Vec3d &origin, Polylines &polylines_out); + public: static FillAdaptive_Internal::Octree* build_octree( TriangleMesh &triangleMesh, From fb24d8167ad2ab0d3464d9928d227d402bcee931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Wed, 26 Aug 2020 23:28:52 +0200 Subject: [PATCH 340/503] Add function for check existence of triangle in define radius --- src/libslic3r/AABBTreeIndirect.hpp | 34 +++++++++++++++++++++++++++++ src/libslic3r/Fill/FillAdaptive.cpp | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/AABBTreeIndirect.hpp b/src/libslic3r/AABBTreeIndirect.hpp index ec9b14a7ae..17d918aeb3 100644 --- a/src/libslic3r/AABBTreeIndirect.hpp +++ b/src/libslic3r/AABBTreeIndirect.hpp @@ -692,6 +692,40 @@ inline typename VectorType::Scalar squared_distance_to_indexed_triangle_set( detail::squared_distance_to_indexed_triangle_set_recursive(distancer, size_t(0), Scalar(0), std::numeric_limits::infinity(), hit_idx_out, hit_point_out); } +// Decides if exists some triangle in defined radius on a 3D indexed triangle set using a pre-built AABBTreeIndirect::Tree. +// Closest point to triangle test will be performed with the accuracy of VectorType::Scalar +// even if the triangle mesh and the AABB Tree are built with floats. +// Returns true if exists some triangle in defined radius, false otherwise. +template +inline bool is_any_triangle_in_radius( + // Indexed triangle set - 3D vertices. + const std::vector &vertices, + // Indexed triangle set - triangular faces, references to vertices. + const std::vector &faces, + // AABBTreeIndirect::Tree over vertices & faces, bounding boxes built with the accuracy of vertices. + const TreeType &tree, + // Point to which the closest point on the indexed triangle set is searched for. + const VectorType &point, + // Maximum distance in which triangle is search for + typename VectorType::Scalar &max_distance) +{ + using Scalar = typename VectorType::Scalar; + auto distancer = detail::IndexedTriangleSetDistancer + { vertices, faces, tree, point }; + + size_t hit_idx; + VectorType hit_point = VectorType::Ones() * (std::nan("")); + + if(tree.empty()) + { + return false; + } + + detail::squared_distance_to_indexed_triangle_set_recursive(distancer, size_t(0), Scalar(0), max_distance, hit_idx, hit_point); + + return hit_point.allFinite(); +} + } // namespace AABBTreeIndirect } // namespace Slic3r diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index cac9c1c3b2..ae067e659e 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -152,7 +152,7 @@ void FillAdaptive::expand_cube( triangleMesh.its.vertices, triangleMesh.its.indices, distanceTree, child_center_transformed, closest_triangle_idx,closest_point); - if(distance_squared <= cube_radius_squared) { + if(AABBTreeIndirect::is_any_triangle_in_radius(triangleMesh.its.vertices, triangleMesh.its.indices, distanceTree, child_center_transformed, cube_radius_squared)) { cube->children.push_back(new Cube{child_center_transformed, cube->depth - 1, cubes_properties[cube->depth - 1]}); FillAdaptive::expand_cube(cube->children.back(), cubes_properties, rotation_matrix, distanceTree, triangleMesh); } From cb328c99aad32d559d2cc08c7c1084009e89c0ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 27 Aug 2020 01:59:35 +0200 Subject: [PATCH 341/503] Polylines merging --- src/libslic3r/Fill/FillAdaptive.cpp | 84 ++++++++++++++++++++++++----- src/libslic3r/Fill/FillAdaptive.hpp | 4 +- 2 files changed, 75 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index ae067e659e..adc6c0c6fb 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -15,18 +15,54 @@ void FillAdaptive::_fill_surface_single( ExPolygon &expolygon, Polylines &polylines_out) { - Polylines infill_polylines; + std::vector infill_polylines(3); this->generate_polylines(this->adapt_fill_octree->root_cube, this->z, this->adapt_fill_octree->origin, infill_polylines); - // Crop all polylines - polylines_out = intersection_pl(infill_polylines, to_polygons(expolygon)); + for (Polylines &infill_polyline : infill_polylines) { + // Crop all polylines + infill_polyline = intersection_pl(infill_polyline, to_polygons(expolygon)); + polylines_out.insert(polylines_out.end(), infill_polyline.begin(), infill_polyline.end()); + } + +#ifdef SLIC3R_DEBUG_SLICE_PROCESSING + { + static int iRuna = 0; + BoundingBox bbox_svg = this->bounding_box; + { + ::Slic3r::SVG svg(debug_out_path("FillAdaptive-%d.svg", iRuna), bbox_svg); + for (const Polyline &polyline : polylines_out) + { + for (const Line &line : polyline.lines()) + { + Point from = line.a; + Point to = line.b; + Point diff = to - from; + + float shrink_length = scale_(0.4); + float line_slope = (float)diff.y() / diff.x(); + float shrink_x = shrink_length / (float)std::sqrt(1.0 + (line_slope * line_slope)); + float shrink_y = line_slope * shrink_x; + + to.x() -= shrink_x; + to.y() -= shrink_y; + from.x() += shrink_x; + from.y() += shrink_y; + + svg.draw(Line(from, to)); + } + } + } + + iRuna++; + } +#endif /* SLIC3R_DEBUG */ } void FillAdaptive::generate_polylines( FillAdaptive_Internal::Cube *cube, double z_position, const Vec3d &origin, - Polylines &polylines_out) + std::vector &polylines_out) { using namespace FillAdaptive_Internal; @@ -52,7 +88,7 @@ void FillAdaptive::generate_polylines( float rotation_angle = Geometry::deg2rad(120.0); - for (int dir_idx = 0; dir_idx < 3; dir_idx++) + for (int i = 0; i < 3; i++) { Vec3d offset = cube->center - origin; Point from_abs(from), to_abs(to); @@ -62,7 +98,8 @@ void FillAdaptive::generate_polylines( to_abs.x() += scale_(offset.x()); to_abs.y() += scale_(offset.y()); - polylines_out.push_back(Polyline(from_abs, to_abs)); +// polylines_out[i].push_back(Polyline(from_abs, to_abs)); + this->merge_polylines(polylines_out[i], Line(from_abs, to_abs)); from.rotate(rotation_angle); to.rotate(rotation_angle); @@ -75,6 +112,35 @@ void FillAdaptive::generate_polylines( } } +void FillAdaptive::merge_polylines(Polylines &polylines, const Line &new_line) +{ + int eps = scale_(0.10); + bool modified = false; + + for (Polyline &polyline : polylines) + { + if (std::abs(new_line.a.x() - polyline.points[1].x()) < eps && std::abs(new_line.a.y() - polyline.points[1].y()) < eps) + { + polyline.points[1].x() = new_line.b.x(); + polyline.points[1].y() = new_line.b.y(); + modified = true; + } + + if (std::abs(new_line.b.x() - polyline.points[0].x()) < eps && std::abs(new_line.b.y() - polyline.points[0].y()) < eps) + { + polyline.points[0].x() = new_line.a.x(); + polyline.points[0].y() = new_line.a.y(); + modified = true; + } + } + + if(!modified) + { + polylines.emplace_back(Polyline(new_line.a, new_line.b)); + } +} + + FillAdaptive_Internal::Octree* FillAdaptive::build_octree( TriangleMesh &triangleMesh, coordf_t line_spacing, @@ -145,12 +211,6 @@ void FillAdaptive::expand_cube( for (const Vec3d &child_center : child_centers) { Vec3d child_center_transformed = cube->center + rotation_matrix * (child_center * (cube->properties.edge_length / 4)); - Vec3d closest_point = Vec3d::Zero(); - size_t closest_triangle_idx = 0; - - double distance_squared = AABBTreeIndirect::squared_distance_to_indexed_triangle_set( - triangleMesh.its.vertices, triangleMesh.its.indices, distanceTree, child_center_transformed, - closest_triangle_idx,closest_point); if(AABBTreeIndirect::is_any_triangle_in_radius(triangleMesh.its.vertices, triangleMesh.its.indices, distanceTree, child_center_transformed, cube_radius_squared)) { cube->children.push_back(new Cube{child_center_transformed, cube->depth - 1, cubes_properties[cube->depth - 1]}); diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index 9e1a196af1..b2f4e37b1e 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -54,7 +54,9 @@ protected: virtual bool no_sort() const { return true; } - void generate_polylines(FillAdaptive_Internal::Cube *cube, double z_position, const Vec3d &origin, Polylines &polylines_out); + void generate_polylines(FillAdaptive_Internal::Cube *cube, double z_position, const Vec3d &origin, std::vector &polylines_out); + + void merge_polylines(Polylines &polylines, const Line &new_line); public: static FillAdaptive_Internal::Octree* build_octree( From c5a73a7cd6a5126cbe8cb92bafd077d4bb56cb0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 27 Aug 2020 07:28:43 +0200 Subject: [PATCH 342/503] Switch to smart pointers --- src/libslic3r/Fill/FillAdaptive.cpp | 17 +++++++++-------- src/libslic3r/Fill/FillAdaptive.hpp | 6 +++--- src/libslic3r/Print.hpp | 8 +++----- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index adc6c0c6fb..96509923c8 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -16,7 +16,7 @@ void FillAdaptive::_fill_surface_single( Polylines &polylines_out) { std::vector infill_polylines(3); - this->generate_polylines(this->adapt_fill_octree->root_cube, this->z, this->adapt_fill_octree->origin, infill_polylines); + this->generate_polylines(this->adapt_fill_octree->root_cube.get(), this->z, this->adapt_fill_octree->origin, infill_polylines); for (Polylines &infill_polyline : infill_polylines) { // Crop all polylines @@ -106,9 +106,9 @@ void FillAdaptive::generate_polylines( } } - for(Cube *child : cube->children) + for(const std::unique_ptr &child : cube->children) { - generate_polylines(child, z_position, origin, polylines_out); + generate_polylines(child.get(), z_position, origin, polylines_out); } } @@ -141,7 +141,7 @@ void FillAdaptive::merge_polylines(Polylines &polylines, const Line &new_line) } -FillAdaptive_Internal::Octree* FillAdaptive::build_octree( +std::unique_ptr FillAdaptive::build_octree( TriangleMesh &triangleMesh, coordf_t line_spacing, const BoundingBoxf3 &printer_volume, @@ -181,9 +181,10 @@ FillAdaptive_Internal::Octree* FillAdaptive::build_octree( Transform3d rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), rotation, Vec3d::Ones(), Vec3d::Ones()); AABBTreeIndirect::Tree3f aabbTree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(triangleMesh.its.vertices, triangleMesh.its.indices); - Octree *octree = new Octree{new Cube{cube_center, cubes_properties.size() - 1, cubes_properties.back()}, cube_center}; + std::unique_ptr octree = std::unique_ptr( + new Octree{std::unique_ptr(new Cube{cube_center, cubes_properties.size() - 1, cubes_properties.back()}), cube_center}); - FillAdaptive::expand_cube(octree->root_cube, cubes_properties, rotation_matrix, aabbTree, triangleMesh); + FillAdaptive::expand_cube(octree->root_cube.get(), cubes_properties, rotation_matrix, aabbTree, triangleMesh); return octree; } @@ -213,8 +214,8 @@ void FillAdaptive::expand_cube( Vec3d child_center_transformed = cube->center + rotation_matrix * (child_center * (cube->properties.edge_length / 4)); if(AABBTreeIndirect::is_any_triangle_in_radius(triangleMesh.its.vertices, triangleMesh.its.indices, distanceTree, child_center_transformed, cube_radius_squared)) { - cube->children.push_back(new Cube{child_center_transformed, cube->depth - 1, cubes_properties[cube->depth - 1]}); - FillAdaptive::expand_cube(cube->children.back(), cubes_properties, rotation_matrix, distanceTree, triangleMesh); + cube->children.push_back(std::unique_ptr(new Cube{child_center_transformed, cube->depth - 1, cubes_properties[cube->depth - 1]})); + FillAdaptive::expand_cube(cube->children.back().get(), cubes_properties, rotation_matrix, distanceTree, triangleMesh); } } } diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index b2f4e37b1e..fb1f2da8e3 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -23,12 +23,12 @@ namespace FillAdaptive_Internal Vec3d center; size_t depth; CubeProperties properties; - std::vector children; + std::vector> children; }; struct Octree { - Cube *root_cube; + std::unique_ptr root_cube; Vec3d origin; }; }; // namespace FillAdaptive_Internal @@ -59,7 +59,7 @@ protected: void merge_polylines(Polylines &polylines, const Line &new_line); public: - static FillAdaptive_Internal::Octree* build_octree( + static std::unique_ptr build_octree( TriangleMesh &triangleMesh, coordf_t line_spacing, const BoundingBoxf3 &printer_volume, diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 5f8613a2db..2e2746a345 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -11,6 +11,7 @@ #include "GCode/ToolOrdering.hpp" #include "GCode/WipeTower.hpp" #include "GCode/ThumbnailData.hpp" +#include "Fill/FillAdaptive.hpp" #include "libslic3r.h" @@ -25,9 +26,6 @@ enum class SlicingMode : uint32_t; class Layer; class SupportLayer; -namespace FillAdaptive_Internal { - struct Octree; -}; // Print step IDs for keeping track of the print state. enum PrintStep { @@ -195,7 +193,7 @@ public: void project_and_append_custom_enforcers(std::vector& enforcers) const { project_and_append_custom_supports(FacetSupportType::ENFORCER, enforcers); } void project_and_append_custom_blockers(std::vector& blockers) const { project_and_append_custom_supports(FacetSupportType::BLOCKER, blockers); } - FillAdaptive_Internal::Octree* adaptiveInfillOctree() { return m_adapt_fill_octree; } + FillAdaptive_Internal::Octree* adaptiveInfillOctree() { return m_adapt_fill_octree.get(); } private: // to be called from Print only. friend class Print; @@ -258,7 +256,7 @@ private: // so that next call to make_perimeters() performs a union() before computing loops bool m_typed_slices = false; - FillAdaptive_Internal::Octree* m_adapt_fill_octree = nullptr; + std::unique_ptr m_adapt_fill_octree = nullptr; std::vector slice_region(size_t region_id, const std::vector &z, SlicingMode mode) const; std::vector slice_modifiers(size_t region_id, const std::vector &z) const; From af30a3ab7ef303852575c1aaa54210bc48388dd4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 27 Aug 2020 09:13:30 +0200 Subject: [PATCH 343/503] Code cleanup --- resources/shaders/options_120.fs | 5 +- resources/shaders/toolpaths_lines.fs | 5 +- resources/shaders/toolpaths_lines.vs | 2 - src/libslic3r/Technologies.hpp | 1 - src/slic3r/GUI/GCodeViewer.cpp | 102 +-------------------------- src/slic3r/GUI/GCodeViewer.hpp | 35 --------- 6 files changed, 4 insertions(+), 146 deletions(-) diff --git a/resources/shaders/options_120.fs b/resources/shaders/options_120.fs index d897a8ca73..e9b61304f2 100644 --- a/resources/shaders/options_120.fs +++ b/resources/shaders/options_120.fs @@ -5,8 +5,7 @@ uniform vec4 uniform_color; uniform float percent_outline_radius; uniform float percent_center_radius; - -vec4 customizable_color(float radius, vec4 color) +vec4 calc_color(float radius, vec4 color) { return ((radius < percent_center_radius) || (radius > 1.0 - percent_outline_radius)) ? vec4(0.5 * color.rgb, color.a) : color; @@ -19,5 +18,5 @@ void main() if (radius > 1.0) discard; - gl_FragColor = customizable_color(radius, uniform_color); + gl_FragColor = calc_color(radius, uniform_color); } diff --git a/resources/shaders/toolpaths_lines.fs b/resources/shaders/toolpaths_lines.fs index 7202a2e3e4..31151cdc17 100644 --- a/resources/shaders/toolpaths_lines.fs +++ b/resources/shaders/toolpaths_lines.fs @@ -8,11 +8,8 @@ const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); uniform vec4 light_intensity; uniform vec4 uniform_color; -varying vec3 eye_position; varying vec3 eye_normal; -float intensity; - void main() { vec3 normal = normalize(eye_normal); @@ -21,7 +18,7 @@ void main() // Since these two are normalized the cosine is the dot product. Take the abs value to light the lines no matter in which direction the normal points. float NdotL = abs(dot(normal, LIGHT_TOP_DIR)); - intensity = light_intensity.x + NdotL * light_intensity.y; + float intensity = light_intensity.x + NdotL * light_intensity.y; // Perform the same lighting calculation for the 2nd light source. NdotL = abs(dot(normal, LIGHT_FRONT_DIR)); diff --git a/resources/shaders/toolpaths_lines.vs b/resources/shaders/toolpaths_lines.vs index 34d141bfe1..85d5c641f3 100644 --- a/resources/shaders/toolpaths_lines.vs +++ b/resources/shaders/toolpaths_lines.vs @@ -1,6 +1,5 @@ #version 110 -varying vec3 eye_position; varying vec3 eye_normal; vec3 world_normal() @@ -16,6 +15,5 @@ void main() { vec4 world_position = vec4(gl_Vertex.xyz, 1.0); gl_Position = gl_ModelViewProjectionMatrix * world_position; - eye_position = (gl_ModelViewMatrix * world_position).xyz; eye_normal = gl_NormalMatrix * world_normal(); } diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 00c899201a..de5933c7cf 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -57,7 +57,6 @@ // Enable G-Code viewer #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) #define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) -#define ENABLE_GCODE_VIEWER_SHADERS_EDITOR (0 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_DATA_CHECKING (0 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_RENDER_EXTRUSION_AS_TRIANGLES (1 && ENABLE_GCODE_VIEWER) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 70b571fe00..5e15889c86 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -504,9 +504,6 @@ void GCodeViewer::render() const #if ENABLE_GCODE_VIEWER_STATISTICS render_statistics(); #endif // ENABLE_GCODE_VIEWER_STATISTICS -#if ENABLE_GCODE_VIEWER_SHADERS_EDITOR - render_shaders_editor(); -#endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR } bool GCodeViewer::is_toolpath_move_type_visible(EMoveType type) const @@ -1385,18 +1382,8 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool void GCodeViewer::render_toolpaths() const { -#if ENABLE_GCODE_VIEWER_SHADERS_EDITOR - float point_size = m_shaders_editor.points.point_size; - std::array light_intensity = { - m_shaders_editor.lines.lights.ambient, - m_shaders_editor.lines.lights.top_diffuse, - m_shaders_editor.lines.lights.front_diffuse, - m_shaders_editor.lines.lights.global - }; -#else float point_size = 0.8f; - std::array light_intensity = { 0.25f, 0.7f, 0.75f, 0.75f }; -#endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR + std::array light_intensity = { 0.25f, 0.70f, 0.75f, 0.75f }; const Camera& camera = wxGetApp().plater()->get_camera(); double zoom = camera.get_zoom(); const std::array& viewport = camera.get_viewport(); @@ -1411,13 +1398,8 @@ void GCodeViewer::render_toolpaths() const auto render_as_points = [this, zoom, point_size, near_plane_height, set_uniform_color](const TBuffer& buffer, EOptionsColors color_id, GLShaderProgram& shader) { set_uniform_color(Options_Colors[static_cast(color_id)], shader); shader.set_uniform("zoom", zoom); -#if ENABLE_GCODE_VIEWER_SHADERS_EDITOR - shader.set_uniform("percent_outline_radius", 0.01f * static_cast(m_shaders_editor.points.percent_outline)); - shader.set_uniform("percent_center_radius", 0.01f * static_cast(m_shaders_editor.points.percent_center)); -#else shader.set_uniform("percent_outline_radius", 0.0f); shader.set_uniform("percent_center_radius", 0.33f); -#endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR shader.set_uniform("point_size", point_size); shader.set_uniform("near_plane_height", near_plane_height); @@ -1597,21 +1579,6 @@ void GCodeViewer::render_legend() const } case EItemType::Circle: { -#if ENABLE_GCODE_VIEWER_SHADERS_EDITOR - ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); - if (m_shaders_editor.points.shader_version == 1) { - draw_list->AddCircleFilled(center, 0.5f * icon_size, - ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); - float radius = 0.5f * icon_size * (1.0f - 0.01f * static_cast(m_shaders_editor.points.percent_outline)); - draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); - if (m_shaders_editor.points.percent_center > 0) { - radius = 0.5f * icon_size * 0.01f * static_cast(m_shaders_editor.points.percent_center); - draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); - } - } - else - draw_list->AddCircleFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); -#else ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); if (m_buffers[buffer_id(EMoveType::Retract)].shader == "options_120") { draw_list->AddCircleFilled(center, 0.5f * icon_size, @@ -1623,7 +1590,6 @@ void GCodeViewer::render_legend() const } else draw_list->AddCircleFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); -#endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR break; } @@ -2175,11 +2141,7 @@ void GCodeViewer::render_legend() const auto add_option = [this, append_item](EMoveType move_type, EOptionsColors color, const std::string& text) { const TBuffer& buffer = m_buffers[buffer_id(move_type)]; if (buffer.visible && buffer.indices.count > 0) -#if ENABLE_GCODE_VIEWER_SHADERS_EDITOR - append_item((m_shaders_editor.points.shader_version == 0) ? EItemType::Rect : EItemType::Circle, Options_Colors[static_cast(color)], text); -#else append_item((buffer.shader == "options_110") ? EItemType::Rect : EItemType::Circle, Options_Colors[static_cast(color)], text); -#endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR }; // options section @@ -2652,68 +2614,6 @@ void GCodeViewer::render_statistics() const } #endif // ENABLE_GCODE_VIEWER_STATISTICS -#if ENABLE_GCODE_VIEWER_SHADERS_EDITOR -void GCodeViewer::render_shaders_editor() const -{ - auto set_shader = [this](const std::string& shader) { - unsigned char begin_id = buffer_id(EMoveType::Retract); - unsigned char end_id = buffer_id(EMoveType::Custom_GCode); - for (unsigned char i = begin_id; i <= end_id; ++i) { - m_buffers[i].shader = shader; - } - }; - - ImGuiWrapper& imgui = *wxGetApp().imgui(); - - Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size(); - imgui.set_next_window_pos(static_cast(cnv_size.get_width()), 0.5f * static_cast(cnv_size.get_height()), ImGuiCond_Once, 1.0f, 0.5f); - - imgui.begin(std::string("Shaders editor (DEV only)"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize); - - if (ImGui::CollapsingHeader("Points", ImGuiTreeNodeFlags_DefaultOpen)) { - if (ImGui::TreeNode("GLSL version")) { - ImGui::RadioButton("1.10 (low end PCs)", &m_shaders_editor.points.shader_version, 0); - ImGui::RadioButton("1.20 flat (billboards) [default]", &m_shaders_editor.points.shader_version, 1); - ImGui::TreePop(); - } - - switch (m_shaders_editor.points.shader_version) - { - case 0: { set_shader("options_110"); break; } - case 1: { set_shader("options_120"); break; } - } - - if (ImGui::TreeNode("Options")) { - ImGui::SliderFloat("point size", &m_shaders_editor.points.point_size, 0.5f, 3.0f, "%.2f"); - if (m_shaders_editor.points.shader_version == 1) { - ImGui::SliderInt("% outline", &m_shaders_editor.points.percent_outline, 0, 50); - ImGui::SliderInt("% center", &m_shaders_editor.points.percent_center, 0, 50); - } - ImGui::TreePop(); - } - } - - if (ImGui::CollapsingHeader("Lines", ImGuiTreeNodeFlags_DefaultOpen)) { - if (ImGui::TreeNode("Lights")) { - ImGui::SliderFloat("ambient", &m_shaders_editor.lines.lights.ambient, 0.0f, 1.0f, "%.2f"); - ImGui::SliderFloat("top diffuse", &m_shaders_editor.lines.lights.top_diffuse, 0.0f, 1.0f, "%.2f"); - ImGui::SliderFloat("front diffuse", &m_shaders_editor.lines.lights.front_diffuse, 0.0f, 1.0f, "%.2f"); - ImGui::SliderFloat("global", &m_shaders_editor.lines.lights.global, 0.0f, 1.0f, "%.2f"); - ImGui::TreePop(); - } - } - - ImGui::SetWindowSize(ImVec2(std::max(ImGui::GetWindowWidth(), 600.0f), -1.0f), ImGuiCond_Always); - if (ImGui::GetWindowPos().x + ImGui::GetWindowWidth() > static_cast(cnv_size.get_width())) { - ImGui::SetWindowPos(ImVec2(static_cast(cnv_size.get_width()) - ImGui::GetWindowWidth(), ImGui::GetWindowPos().y), ImGuiCond_Always); - wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); - wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); - } - - imgui.end(); -} -#endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR - bool GCodeViewer::is_travel_in_z_range(size_t id) const { const TBuffer& buffer = m_buffers[buffer_id(EMoveType::Travel)]; diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 919b9de6e1..55c8ea1993 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -289,35 +289,6 @@ class GCodeViewer }; #endif // ENABLE_GCODE_VIEWER_STATISTICS -#if ENABLE_GCODE_VIEWER_SHADERS_EDITOR - struct ShadersEditor - { - struct Points - { - int shader_version{ 1 }; - float point_size{ 0.8f }; - int percent_outline{ 0 }; - int percent_center{ 33 }; - }; - - struct Lines - { - struct Lights - { - float ambient{ 0.25f }; - float top_diffuse{ 0.7f }; - float front_diffuse{ 0.75f }; - float global{ 0.75f }; - }; - - Lights lights; - }; - - Points points; - Lines lines; - }; -#endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR - public: struct SequentialView { @@ -402,9 +373,6 @@ private: #if ENABLE_GCODE_VIEWER_STATISTICS mutable Statistics m_statistics; #endif // ENABLE_GCODE_VIEWER_STATISTICS -#if ENABLE_GCODE_VIEWER_SHADERS_EDITOR - mutable ShadersEditor m_shaders_editor; -#endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR std::array m_detected_point_sizes = { 0.0f, 0.0f }; public: @@ -475,9 +443,6 @@ private: #if ENABLE_GCODE_VIEWER_STATISTICS void render_statistics() const; #endif // ENABLE_GCODE_VIEWER_STATISTICS -#if ENABLE_GCODE_VIEWER_SHADERS_EDITOR - void render_shaders_editor() const; -#endif // ENABLE_GCODE_VIEWER_SHADERS_EDITOR bool is_visible(ExtrusionRole role) const { return role < erCount && (m_extrusions.role_visibility_flags & (1 << role)) != 0; } From 689c8691ee45947d1b7d1f626d8bd65b622f3cbd Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 27 Aug 2020 10:15:07 +0200 Subject: [PATCH 344/503] Another code cleanup --- src/libslic3r/Technologies.hpp | 1 - src/slic3r/GUI/GCodeViewer.cpp | 13 ------------- 2 files changed, 14 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index de5933c7cf..af08d16401 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -58,7 +58,6 @@ #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) #define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_DATA_CHECKING (0 && ENABLE_GCODE_VIEWER) -#define ENABLE_GCODE_RENDER_EXTRUSION_AS_TRIANGLES (1 && ENABLE_GCODE_VIEWER) #define TIME_ESTIMATE_NONE 0 #define TIME_ESTIMATE_DEFAULT 1 diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 5e15889c86..64916182ad 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -303,13 +303,8 @@ bool GCodeViewer::init() } case EMoveType::Extrude: { -#if ENABLE_GCODE_RENDER_EXTRUSION_AS_TRIANGLES buffer.primitive_type = TBuffer::EPrimitiveType::Triangle; buffer.vertices.format = VBuffer::EFormat::PositionNormal3; -#else - buffer.primitive_type = TBuffer::EPrimitiveType::Line; - buffer.vertices.format = VBuffer::EFormat::PositionNormal1; -#endif // ENABLE_GCODE_RENDER_EXTRUSION_AS_TRIANGLES break; } case EMoveType::Travel: @@ -873,11 +868,7 @@ void GCodeViewer::init_shaders() case EMoveType::Custom_GCode: { m_buffers[i].shader = is_glsl_120 ? "options_120" : "options_110"; break; } case EMoveType::Retract: { m_buffers[i].shader = is_glsl_120 ? "options_120" : "options_110"; break; } case EMoveType::Unretract: { m_buffers[i].shader = is_glsl_120 ? "options_120" : "options_110"; break; } -#if ENABLE_GCODE_RENDER_EXTRUSION_AS_TRIANGLES case EMoveType::Extrude: { m_buffers[i].shader = "gouraud_light"; break; } -#else - case EMoveType::Extrude: { m_buffers[i].shader = "toolpaths_lines"; break; } -#endif // ENABLE_GCODE_RENDER_EXTRUSION_AS_TRIANGLES case EMoveType::Travel: { m_buffers[i].shader = "toolpaths_lines"; break; } default: { break; } } @@ -1064,15 +1055,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) add_as_point(curr, buffer, buffer_vertices, buffer_indices, i); break; } -#if ENABLE_GCODE_RENDER_EXTRUSION_AS_TRIANGLES case EMoveType::Extrude: { add_as_solid(prev, curr, buffer, buffer_vertices, buffer_indices, i); break; } -#else - case EMoveType::Extrude: -#endif // ENABLE_GCODE_RENDER_EXTRUSION_AS_TRIANGLES case EMoveType::Travel: { add_as_line(prev, curr, buffer, buffer_vertices, buffer_indices, i); From 17170b81b51dfd7a99d1f3ef7f7d9038bf3502c0 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 27 Aug 2020 12:14:49 +0200 Subject: [PATCH 345/503] Clean-up of Shiny profiler integration, so that the intrusiver profiling can be controlled per module. --- src/clipper/clipper.cpp | 58 ++++++++++++++++++++-------------- src/libslic3r/ClipperUtils.cpp | 25 ++++++++++----- src/slic3r/CMakeLists.txt | 1 + src/slic3r/Utils/Profile.hpp | 19 +++++++++++ 4 files changed, 71 insertions(+), 32 deletions(-) create mode 100644 src/slic3r/Utils/Profile.hpp diff --git a/src/clipper/clipper.cpp b/src/clipper/clipper.cpp index b85cf9025e..be4cb4a6a8 100644 --- a/src/clipper/clipper.cpp +++ b/src/clipper/clipper.cpp @@ -48,9 +48,19 @@ #include #include #include -#include #include +// Profiling support using the Shiny intrusive profiler +//#define CLIPPERLIB_PROFILE +#if defined(SLIC3R_PROFILE) && defined(CLIPPERLIB_PROFILE) + #include + #define CLIPPERLIB_PROFILE_FUNC() PROFILE_FUNC() + #define CLIPPERLIB_PROFILE_BLOCK(name) PROFILE_BLOCK(name) +#else + #define CLIPPERLIB_PROFILE_FUNC() + #define CLIPPERLIB_PROFILE_BLOCK(name) +#endif + #ifdef use_xyz namespace ClipperLib_Z { #else /* use_xyz */ @@ -263,7 +273,7 @@ int PointInPolygon (const IntPoint &pt, OutPt *op) // This is potentially very expensive! O(n^2)! bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2) { - PROFILE_FUNC(); + CLIPPERLIB_PROFILE_FUNC(); OutPt* op = OutPt1; do { @@ -714,7 +724,7 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward) bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) { - PROFILE_FUNC(); + CLIPPERLIB_PROFILE_FUNC(); // Remove duplicate end point from a closed input path. // Remove duplicate points from the end of the input path. int highI = (int)pg.size() -1; @@ -738,7 +748,7 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed) { - PROFILE_FUNC(); + CLIPPERLIB_PROFILE_FUNC(); std::vector num_edges(ppg.size(), 0); int num_edges_total = 0; for (size_t i = 0; i < ppg.size(); ++ i) { @@ -780,7 +790,7 @@ bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed) bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, bool Closed, TEdge* edges) { - PROFILE_FUNC(); + CLIPPERLIB_PROFILE_FUNC(); #ifdef use_lines if (!Closed && PolyTyp == ptClip) throw clipperException("AddPath: Open paths must be subject."); @@ -954,7 +964,7 @@ bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, b void ClipperBase::Clear() { - PROFILE_FUNC(); + CLIPPERLIB_PROFILE_FUNC(); m_MinimaList.clear(); m_edges.clear(); m_UseFullRange = false; @@ -966,7 +976,7 @@ void ClipperBase::Clear() // Sort the LML entries, initialize the left / right bound edges of each Local Minima. void ClipperBase::Reset() { - PROFILE_FUNC(); + CLIPPERLIB_PROFILE_FUNC(); if (m_MinimaList.empty()) return; //ie nothing to process std::sort(m_MinimaList.begin(), m_MinimaList.end(), [](const LocalMinimum& lm1, const LocalMinimum& lm2){ return lm1.Y < lm2.Y; }); @@ -995,7 +1005,7 @@ void ClipperBase::Reset() // Returns (0,0,0,0) for an empty rectangle. IntRect ClipperBase::GetBounds() { - PROFILE_FUNC(); + CLIPPERLIB_PROFILE_FUNC(); IntRect result; auto lm = m_MinimaList.begin(); if (lm == m_MinimaList.end()) @@ -1056,7 +1066,7 @@ Clipper::Clipper(int initOptions) : void Clipper::Reset() { - PROFILE_FUNC(); + CLIPPERLIB_PROFILE_FUNC(); ClipperBase::Reset(); m_Scanbeam = std::priority_queue(); m_Maxima.clear(); @@ -1071,7 +1081,7 @@ void Clipper::Reset() bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType subjFillType, PolyFillType clipFillType) { - PROFILE_FUNC(); + CLIPPERLIB_PROFILE_FUNC(); if (m_HasOpenPaths) throw clipperException("Error: PolyTree struct is needed for open path clipping."); solution.resize(0); @@ -1089,7 +1099,7 @@ bool Clipper::Execute(ClipType clipType, Paths &solution, bool Clipper::Execute(ClipType clipType, PolyTree& polytree, PolyFillType subjFillType, PolyFillType clipFillType) { - PROFILE_FUNC(); + CLIPPERLIB_PROFILE_FUNC(); m_SubjFillType = subjFillType; m_ClipFillType = clipFillType; m_ClipType = clipType; @@ -1103,10 +1113,10 @@ bool Clipper::Execute(ClipType clipType, PolyTree& polytree, bool Clipper::ExecuteInternal() { - PROFILE_FUNC(); + CLIPPERLIB_PROFILE_FUNC(); bool succeeded = true; try { - PROFILE_BLOCK(Clipper_ExecuteInternal_Process); + CLIPPERLIB_PROFILE_BLOCK(Clipper_ExecuteInternal_Process); Reset(); if (m_MinimaList.empty()) return true; cInt botY = m_Scanbeam.top(); @@ -1131,13 +1141,13 @@ bool Clipper::ExecuteInternal() if (succeeded) { - PROFILE_BLOCK(Clipper_ExecuteInternal_Fix); + CLIPPERLIB_PROFILE_BLOCK(Clipper_ExecuteInternal_Fix); //fix orientations ... //FIXME Vojtech: Does it not invalidate the loop hierarchy maintained as OutRec::FirstLeft pointers? //FIXME Vojtech: The area is calculated with floats, it may not be numerically stable! { - PROFILE_BLOCK(Clipper_ExecuteInternal_Fix_orientations); + CLIPPERLIB_PROFILE_BLOCK(Clipper_ExecuteInternal_Fix_orientations); for (OutRec *outRec : m_PolyOuts) if (outRec->Pts && !outRec->IsOpen && (outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0)) ReversePolyPtLinks(outRec->Pts); @@ -1147,7 +1157,7 @@ bool Clipper::ExecuteInternal() //unfortunately FixupOutPolygon() must be done after JoinCommonEdges() { - PROFILE_BLOCK(Clipper_ExecuteInternal_Fix_fixup); + CLIPPERLIB_PROFILE_BLOCK(Clipper_ExecuteInternal_Fix_fixup); for (OutRec *outRec : m_PolyOuts) if (outRec->Pts) { if (outRec->IsOpen) @@ -1401,7 +1411,7 @@ bool Clipper::IsContributing(const TEdge& edge) const // Called from Clipper::InsertLocalMinimaIntoAEL() and Clipper::IntersectEdges(). OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) { - PROFILE_FUNC(); + CLIPPERLIB_PROFILE_FUNC(); OutPt* result; TEdge *e, *prevE; if (IsHorizontal(*e2) || ( e1->Dx > e2->Dx )) @@ -1493,7 +1503,7 @@ void Clipper::CopyAELToSEL() // Called from Clipper::ExecuteInternal() void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) { - PROFILE_FUNC(); + CLIPPERLIB_PROFILE_FUNC(); while (!m_MinimaList.empty() && m_MinimaList.back().Y == botY) { TEdge* lb = m_MinimaList.back().LeftBound; @@ -2043,7 +2053,7 @@ OutPt* Clipper::GetLastOutPt(TEdge *e) void Clipper::ProcessHorizontals() { - PROFILE_FUNC(); + CLIPPERLIB_PROFILE_FUNC(); TEdge* horzEdge = m_SortedEdges; while(horzEdge) { @@ -2414,7 +2424,7 @@ void Clipper::UpdateEdgeIntoAEL(TEdge *&e) bool Clipper::ProcessIntersections(const cInt topY) { - PROFILE_FUNC(); + CLIPPERLIB_PROFILE_FUNC(); if( !m_ActiveEdges ) return true; try { BuildIntersectList(topY); @@ -2569,7 +2579,7 @@ void Clipper::DoMaxima(TEdge *e) void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) { - PROFILE_FUNC(); + CLIPPERLIB_PROFILE_FUNC(); TEdge* e = m_ActiveEdges; while( e ) { @@ -3177,7 +3187,7 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) // This is potentially very expensive! O(n^3)! void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const { - PROFILE_FUNC(); + CLIPPERLIB_PROFILE_FUNC(); //tests if NewOutRec contains the polygon before reassigning FirstLeft for (OutRec *outRec : m_PolyOuts) { @@ -3201,7 +3211,7 @@ void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const void Clipper::JoinCommonEdges() { - PROFILE_FUNC(); + CLIPPERLIB_PROFILE_FUNC(); for (Join &join : m_Joins) { OutRec *outRec1 = GetOutRec(join.OutPt1->Idx); @@ -3771,7 +3781,7 @@ void ClipperOffset::DoRound(int j, int k) // http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/Clipper/Properties/StrictlySimple.htm void Clipper::DoSimplePolygons() { - PROFILE_FUNC(); + CLIPPERLIB_PROFILE_FUNC(); size_t i = 0; while (i < m_PolyOuts.size()) { diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index d40d79b3d8..16d985e9c9 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -8,7 +8,16 @@ #include "SVG.hpp" #endif /* CLIPPER_UTILS_DEBUG */ -#include +// Profiling support using the Shiny intrusive profiler +//#define CLIPPER_UTILS_PROFILE +#if defined(SLIC3R_PROFILE) && defined(CLIPPER_UTILS_PROFILE) + #include + #define CLIPPERUTILS_PROFILE_FUNC() PROFILE_FUNC() + #define CLIPPERUTILS_PROFILE_BLOCK(name) PROFILE_BLOCK(name) +#else + #define CLIPPERUTILS_PROFILE_FUNC() + #define CLIPPERUTILS_PROFILE_BLOCK(name) +#endif #define CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR (0.005f) @@ -50,7 +59,7 @@ err: void scaleClipperPolygon(ClipperLib::Path &polygon) { - PROFILE_FUNC(); + CLIPPERUTILS_PROFILE_FUNC(); for (ClipperLib::Path::iterator pit = polygon.begin(); pit != polygon.end(); ++pit) { pit->X <<= CLIPPER_OFFSET_POWER_OF_2; pit->Y <<= CLIPPER_OFFSET_POWER_OF_2; @@ -59,7 +68,7 @@ void scaleClipperPolygon(ClipperLib::Path &polygon) void scaleClipperPolygons(ClipperLib::Paths &polygons) { - PROFILE_FUNC(); + CLIPPERUTILS_PROFILE_FUNC(); for (ClipperLib::Paths::iterator it = polygons.begin(); it != polygons.end(); ++it) for (ClipperLib::Path::iterator pit = (*it).begin(); pit != (*it).end(); ++pit) { pit->X <<= CLIPPER_OFFSET_POWER_OF_2; @@ -69,7 +78,7 @@ void scaleClipperPolygons(ClipperLib::Paths &polygons) void unscaleClipperPolygon(ClipperLib::Path &polygon) { - PROFILE_FUNC(); + CLIPPERUTILS_PROFILE_FUNC(); for (ClipperLib::Path::iterator pit = polygon.begin(); pit != polygon.end(); ++pit) { pit->X += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA; pit->Y += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA; @@ -80,7 +89,7 @@ void unscaleClipperPolygon(ClipperLib::Path &polygon) void unscaleClipperPolygons(ClipperLib::Paths &polygons) { - PROFILE_FUNC(); + CLIPPERUTILS_PROFILE_FUNC(); for (ClipperLib::Paths::iterator it = polygons.begin(); it != polygons.end(); ++it) for (ClipperLib::Path::iterator pit = (*it).begin(); pit != (*it).end(); ++pit) { pit->X += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA; @@ -790,7 +799,7 @@ ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear void safety_offset(ClipperLib::Paths* paths) { - PROFILE_FUNC(); + CLIPPERUTILS_PROFILE_FUNC(); // scale input scaleClipperPolygons(*paths); @@ -812,11 +821,11 @@ void safety_offset(ClipperLib::Paths* paths) if (! ccw) std::reverse(path.begin(), path.end()); { - PROFILE_BLOCK(safety_offset_AddPaths); + CLIPPERUTILS_PROFILE_BLOCK(safety_offset_AddPaths); co.AddPath((*paths)[i], ClipperLib::jtMiter, ClipperLib::etClosedPolygon); } { - PROFILE_BLOCK(safety_offset_Execute); + CLIPPERUTILS_PROFILE_BLOCK(safety_offset_Execute); // offset outside by 10um ClipperLib::Paths out_this; co.Execute(out_this, ccw ? 10.f * float(CLIPPER_OFFSET_SCALE) : -10.f * float(CLIPPER_OFFSET_SCALE)); diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index f8598cea08..8a2672c774 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -181,6 +181,7 @@ set(SLIC3R_GUI_SOURCES Utils/Bonjour.hpp Utils/PresetUpdater.cpp Utils/PresetUpdater.hpp + Utils/Profile.hpp Utils/UndoRedo.cpp Utils/UndoRedo.hpp Utils/HexFile.cpp diff --git a/src/slic3r/Utils/Profile.hpp b/src/slic3r/Utils/Profile.hpp new file mode 100644 index 0000000000..5fb1e31167 --- /dev/null +++ b/src/slic3r/Utils/Profile.hpp @@ -0,0 +1,19 @@ +#ifndef slic3r_GUI_Profile_hpp_ +#define slic3r_GUI_Profile_hpp_ + +// Profiling support using the Shiny intrusive profiler +//#define SLIC3R_PROFILE_GUI +#if defined(SLIC3R_PROFILE) && defined(SLIC3R_PROFILE_GUI) + #include + #define SLIC3R_GUI_PROFILE_FUNC() PROFILE_FUNC() + #define SLIC3R_GUI_PROFILE_BLOCK(name) PROFILE_BLOCK(name) + #define SLIC3R_GUI_PROFILE_UPDATE() PROFILE_UPDATE() + #define SLIC3R_GUI_PROFILE_OUTPUT(x) PROFILE_OUTPUT(x) +#else + #define SLIC3R_GUI_PROFILE_FUNC() + #define SLIC3R_GUI_PROFILE_BLOCK(name) + #define SLIC3R_GUI_PROFILE_UPDATE() + #define SLIC3R_GUI_PROFILE_OUTPUT(x) +#endif + +#endif // slic3r_GUI_Profile_hpp_ From b28f9b89353aaa7b382ba56adfa0dc737281fc5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 27 Aug 2020 13:04:53 +0200 Subject: [PATCH 346/503] Fix discontinuous extrusion lines for adaptive infill --- src/libslic3r/Fill/FillAdaptive.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index 96509923c8..a3068989ef 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -88,7 +88,7 @@ void FillAdaptive::generate_polylines( float rotation_angle = Geometry::deg2rad(120.0); - for (int i = 0; i < 3; i++) + for (int i = 0; i < polylines_out.size(); i++) { Vec3d offset = cube->center - origin; Point from_abs(from), to_abs(to); @@ -177,7 +177,7 @@ std::unique_ptr FillAdaptive::build_octree( triangleMesh.require_shared_vertices(); } - Vec3d rotation = Vec3d(Geometry::deg2rad(225.0), Geometry::deg2rad(215.0), Geometry::deg2rad(30.0)); + Vec3d rotation = Vec3d(Geometry::deg2rad(225.0), Geometry::deg2rad(215.264), Geometry::deg2rad(30.0)); Transform3d rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), rotation, Vec3d::Ones(), Vec3d::Ones()); AABBTreeIndirect::Tree3f aabbTree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(triangleMesh.its.vertices, triangleMesh.its.indices); From 8af3659e9890fd2c5d146cf9226b7517652278c5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 27 Aug 2020 13:11:28 +0200 Subject: [PATCH 347/503] GCodeViewer -> Fixed generation of solid toolpaths --- src/slic3r/GUI/GCodeViewer.cpp | 59 +++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 64916182ad..8cb18f4e18 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -977,16 +977,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) buffer_indices.push_back(i3); }; - Vec3f dir = (curr.position - prev.position).normalized(); - Vec3f right = (std::abs(std::abs(dir.dot(Vec3f::UnitZ())) - 1.0f) < EPSILON) ? -Vec3f::UnitY() : Vec3f(dir[1], -dir[0], 0.0f).normalized(); - Vec3f up = right.cross(dir); - float prev_half_width = 0.5f * prev.width; - float prev_half_height = 0.5f * prev.height; - float curr_half_width = 0.5f * curr.width; - float curr_half_height = 0.5f * curr.height; - Vec3f prev_pos = Vec3f(prev.position[0], prev.position[1], prev.position[2]) - prev_half_height * up; - Vec3f curr_pos = Vec3f(curr.position[0], curr.position[1], curr.position[2]) - curr_half_height * up; - if (prev.type != curr.type || !buffer.paths.back().matches(curr)) { buffer.add_path(curr, static_cast(buffer_indices.size()), static_cast(move_id - 1)); buffer.paths.back().first.position = prev.position; @@ -994,17 +984,27 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) unsigned int starting_vertices_size = static_cast(buffer_vertices.size() / buffer.vertices.vertex_size_floats()); + Vec3f dir = (curr.position - prev.position).normalized(); + Vec3f right = (std::abs(std::abs(dir.dot(Vec3f::UnitZ())) - 1.0f) < EPSILON) ? -Vec3f::UnitY() : Vec3f(dir[1], -dir[0], 0.0f).normalized(); + Vec3f up = right.cross(dir); + + float half_width = 0.5f * round_to_nearest(curr.width, 2); + float half_height = 0.5f * round_to_nearest(curr.height, 2); + + Vec3f prev_pos = prev.position - half_height * up; + Vec3f curr_pos = curr.position - half_height * up; + // vertices 1st endpoint - store_vertex(buffer_vertices, prev_pos + prev_half_height * up, up); // top - store_vertex(buffer_vertices, prev_pos + prev_half_width * right, right); // right - store_vertex(buffer_vertices, prev_pos - prev_half_height * up, -up); // bottom - store_vertex(buffer_vertices, prev_pos - prev_half_width * right, -right); // left + store_vertex(buffer_vertices, prev_pos + half_height * up, up); // top + store_vertex(buffer_vertices, prev_pos + half_width * right, right); // right + store_vertex(buffer_vertices, prev_pos - half_height * up, -up); // bottom + store_vertex(buffer_vertices, prev_pos - half_width * right, -right); // left // vertices 2nd endpoint - store_vertex(buffer_vertices, curr_pos + curr_half_height * up, up); // top - store_vertex(buffer_vertices, curr_pos + curr_half_width * right, right); // right - store_vertex(buffer_vertices, curr_pos - curr_half_height * up, -up); // bottom - store_vertex(buffer_vertices, curr_pos - curr_half_width * right, -right); // left + store_vertex(buffer_vertices, curr_pos + half_height * up, up); // top + store_vertex(buffer_vertices, curr_pos + half_width * right, right); // right + store_vertex(buffer_vertices, curr_pos - half_height * up, -up); // bottom + store_vertex(buffer_vertices, curr_pos - half_width * right, -right); // left // triangles starting cap store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size + 2, starting_vertices_size + 1); @@ -1104,8 +1104,21 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) for (const TBuffer& buffer : m_buffers) { m_statistics.paths_size += SLIC3R_STDVEC_MEMSIZE(buffer.paths, Path); } - m_statistics.travel_segments_count = indices[buffer_id(EMoveType::Travel)].size() / 2; - m_statistics.extrude_segments_count = indices[buffer_id(EMoveType::Extrude)].size() / 2; + unsigned int travel_buffer_id = buffer_id(EMoveType::Travel); + switch (m_buffers[travel_buffer_id].primitive_type) + { + case TBuffer::EPrimitiveType::Line: { m_statistics.travel_segments_count = indices[travel_buffer_id].size() / 2; break; } + case TBuffer::EPrimitiveType::Triangle: { m_statistics.travel_segments_count = indices[travel_buffer_id].size() / 36; break; } + default: { break; } + } + + unsigned int extrude_buffer_id = buffer_id(EMoveType::Extrude); + switch (m_buffers[extrude_buffer_id].primitive_type) + { + case TBuffer::EPrimitiveType::Line: { m_statistics.extrude_segments_count = indices[extrude_buffer_id].size() / 2; break; } + case TBuffer::EPrimitiveType::Triangle: { m_statistics.extrude_segments_count = indices[extrude_buffer_id].size() / 36; break; } + default: { break; } + } #endif // ENABLE_GCODE_VIEWER_STATISTICS // layers zs / roles / extruder ids / cp color ids -> extract from result @@ -2583,17 +2596,17 @@ void GCodeViewer::render_statistics() const ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.vertices_gpu_size) + " bytes"); - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Vertices GPU:")); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Indices GPU:")); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.indices_gpu_size) + " bytes"); ImGui::Separator(); - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Vertices GPU:")); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Travel segments count:")); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.travel_segments_count)); - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Extrude segments:")); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Extrude segments count:")); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.extrude_segments_count)); From 19e1d877aa0c9d7517a0f5e0aca03838871e962c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 24 Apr 2020 12:46:35 +0200 Subject: [PATCH 348/503] Don't use sla::EncodedRaster in SLAImport, revive opencsg sandbox --- sandboxes/opencsg/CMakeLists.txt | 1 + sandboxes/opencsg/main.cpp | 2 +- src/libslic3r/SLA/AGGRaster.hpp | 9 +++++---- src/slic3r/Utils/SLAImport.cpp | 21 +++++++++++---------- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/sandboxes/opencsg/CMakeLists.txt b/sandboxes/opencsg/CMakeLists.txt index ec1f4cae91..ace8f4d539 100644 --- a/sandboxes/opencsg/CMakeLists.txt +++ b/sandboxes/opencsg/CMakeLists.txt @@ -6,6 +6,7 @@ add_executable(opencsg_example WIN32 main.cpp Engine.hpp Engine.cpp ShaderCSGDisplay.hpp ShaderCSGDisplay.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/Jobs/Job.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/ProgressStatusBar.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/I18N.hpp ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/I18N.cpp) diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index adf9cc457f..f5fb124935 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -26,7 +26,7 @@ #include "libslic3r/Format/3mf.hpp" #include "libslic3r/SLAPrint.hpp" -#include "slic3r/GUI/Job.hpp" +#include "slic3r/GUI/Jobs/Job.hpp" #include "slic3r/GUI/ProgressStatusBar.hpp" using namespace Slic3r::GL; diff --git a/src/libslic3r/SLA/AGGRaster.hpp b/src/libslic3r/SLA/AGGRaster.hpp index 37baed9e88..917f718e98 100644 --- a/src/libslic3r/SLA/AGGRaster.hpp +++ b/src/libslic3r/SLA/AGGRaster.hpp @@ -128,12 +128,13 @@ protected: } public: - template AGGRaster(const Resolution &res, + template + AGGRaster(const Resolution &res, const PixelDim & pd, const Trafo & trafo, - const TColor & foreground, - const TColor & background, - GammaFn && gammafn) + const TColor & foreground, + const TColor & background, + GammaFn && gammafn) : m_resolution(res) , m_pxdim_scaled(SCALING_FACTOR / pd.w_mm, SCALING_FACTOR / pd.h_mm) , m_buf(res.pixels()) diff --git a/src/slic3r/Utils/SLAImport.cpp b/src/slic3r/Utils/SLAImport.cpp index 442025a77d..65ec46343d 100644 --- a/src/slic3r/Utils/SLAImport.cpp +++ b/src/slic3r/Utils/SLAImport.cpp @@ -43,9 +43,11 @@ namespace Slic3r { namespace { +struct PNGBuffer { std::vector buf; std::string fname; }; + struct ArchiveData { boost::property_tree::ptree profile, config; - std::vector images; + std::vector images; }; static const constexpr char *CONFIG_FNAME = "config.ini"; @@ -66,7 +68,7 @@ boost::property_tree::ptree read_ini(const mz_zip_archive_file_stat &entry, return tree; } -sla::EncodedRaster read_png(const mz_zip_archive_file_stat &entry, +PNGBuffer read_png(const mz_zip_archive_file_stat &entry, MZ_Archive & zip, const std::string & name) { @@ -75,9 +77,8 @@ sla::EncodedRaster read_png(const mz_zip_archive_file_stat &entry, if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename, buf.data(), buf.size(), 0)) throw std::runtime_error(zip.get_errorstr()); - - return sla::EncodedRaster(std::move(buf), - name.empty() ? entry.m_filename : name); + + return {std::move(buf), (name.empty() ? entry.m_filename : name)}; } ArchiveData extract_sla_archive(const std::string &zipfname, @@ -113,9 +114,9 @@ ArchiveData extract_sla_archive(const std::string &zipfname, if (boost::filesystem::path(name).extension().string() == ".png") { auto it = std::lower_bound( - arch.images.begin(), arch.images.end(), sla::EncodedRaster({}, name), - [](const sla::EncodedRaster &r1, const sla::EncodedRaster &r2) { - return std::less()(r1.extension(), r2.extension()); + arch.images.begin(), arch.images.end(), PNGBuffer{{}, name}, + [](const PNGBuffer &r1, const PNGBuffer &r2) { + return std::less()(r1.fname, r2.fname); }); arch.images.insert(it, read_png(entry, zip, name)); @@ -258,8 +259,8 @@ std::vector extract_slices_from_sla_archive( } } - auto &buf = arch.images[i]; - wxMemoryInputStream stream{buf.data(), buf.size()}; + PNGBuffer &png = arch.images[i]; + wxMemoryInputStream stream{png.buf.data(), png.buf.size()}; wxImage img{stream}; auto rings = marchsq::execute(img, 128, rstp.win); From 2bcd36d155d0fce0041faac6a89a3fe211830041 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 27 Apr 2020 18:43:47 +0200 Subject: [PATCH 349/503] PNG image read with libpng --- src/libslic3r/CMakeLists.txt | 4 +++ src/libslic3r/PNGRead.cpp | 59 +++++++++++++++++++++++++++++++++ src/libslic3r/PNGRead.hpp | 30 +++++++++++++++++ src/slic3r/Utils/SLAImport.cpp | 32 +++++++++--------- tests/libslic3r/CMakeLists.txt | 2 ++ tests/libslic3r/test_png_io.cpp | 52 +++++++++++++++++++++++++++++ 6 files changed, 164 insertions(+), 15 deletions(-) create mode 100644 src/libslic3r/PNGRead.cpp create mode 100644 src/libslic3r/PNGRead.hpp create mode 100644 tests/libslic3r/test_png_io.cpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 290b8953cc..aea3247220 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -161,6 +161,8 @@ add_library(libslic3r STATIC PrintConfig.hpp PrintObject.cpp PrintRegion.cpp + PNGRead.hpp + PNGRead.cpp Semver.cpp ShortestPath.cpp ShortestPath.hpp @@ -308,6 +310,8 @@ target_link_libraries(libslic3r TBB::tbb libslic3r_cgal ${CMAKE_DL_LIBS} + PNG::PNG + ZLIB::ZLIB ) if (TARGET OpenVDB::openvdb) diff --git a/src/libslic3r/PNGRead.cpp b/src/libslic3r/PNGRead.cpp new file mode 100644 index 0000000000..8bfa3cb951 --- /dev/null +++ b/src/libslic3r/PNGRead.cpp @@ -0,0 +1,59 @@ +#include "PNGRead.hpp" + +#include + +#include +#include + +namespace Slic3r { namespace png { + +struct png_deleter { void operator()(png_struct *p) { + png_destroy_read_struct( &p, nullptr, nullptr); } +}; + +using png_ptr_t = std::unique_ptr; + +bool is_png(const ReadBuf &rb) +{ + static const constexpr int PNG_SIG_BYTES = 8; + + return rb.sz >= PNG_SIG_BYTES && + !png_sig_cmp(static_cast(rb.buf), 0, PNG_SIG_BYTES); +} + +bool decode_png(const ReadBuf &rb, ImageGreyscale &img) +{ + if (!is_png(rb)) return false; + + png_ptr_t png{png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr)}; + + if(!png) return false; + + png_infop info = png_create_info_struct(png.get()); + if(!info) return {}; + + FILE *io = ::fmemopen(const_cast(rb.buf), rb.sz, "rb"); + png_init_io(png.get(), io); + + png_read_info(png.get(), info); + + img.cols = png_get_image_width(png.get(), info); + img.rows = png_get_image_height(png.get(), info); + size_t color_type = png_get_color_type(png.get(), info); + size_t bit_depth = png_get_bit_depth(png.get(), info); + + if (color_type != PNG_COLOR_TYPE_GRAY || bit_depth != 8) + return false; + + img.buf.resize(img.rows * img.cols); + + auto readbuf = static_cast(img.buf.data()); + for (size_t r = 0; r < img.rows; ++r) + png_read_row(png.get(), readbuf + r * img.cols, nullptr); + + fclose(io); + + return true; +} + +}} diff --git a/src/libslic3r/PNGRead.hpp b/src/libslic3r/PNGRead.hpp new file mode 100644 index 0000000000..88a948395b --- /dev/null +++ b/src/libslic3r/PNGRead.hpp @@ -0,0 +1,30 @@ +#ifndef PNGREAD_HPP +#define PNGREAD_HPP + +#include +#include + +namespace Slic3r { namespace png { + +struct ReadBuf { const void *buf = nullptr; const size_t sz = 0; }; + +template struct Image { + std::vector buf; + size_t rows, cols; + PxT get(size_t row, size_t col) const { return buf[row * cols + col]; } +}; + +struct RGB { uint8_t r, g, b; }; + +using ImageRGB = Image; +using ImageGreyscale = Image; + +// TODO +// bool decode_png(Buffer &&pngbuf, ImageRGB &img); + +bool is_png(const ReadBuf &pngbuf); + +bool decode_png(const ReadBuf &pngbuf, ImageGreyscale &img); + +}} +#endif // PNGREAD_HPP diff --git a/src/slic3r/Utils/SLAImport.cpp b/src/slic3r/Utils/SLAImport.cpp index 65ec46343d..13ea603396 100644 --- a/src/slic3r/Utils/SLAImport.cpp +++ b/src/slic3r/Utils/SLAImport.cpp @@ -9,32 +9,31 @@ #include "libslic3r/PrintConfig.hpp" #include "libslic3r/SLA/RasterBase.hpp" #include "libslic3r/miniz_extension.hpp" +#include "libslic3r/PNGRead.hpp" #include #include #include -#include #include namespace marchsq { -// Specialize this struct to register a raster type for the Marching squares alg -template<> struct _RasterTraits { - using Rst = wxImage; - +template<> struct _RasterTraits { + using Rst = Slic3r::png::ImageGreyscale; + // The type of pixel cell in the raster using ValueType = uint8_t; - + // Value at a given position static uint8_t get(const Rst &rst, size_t row, size_t col) { - return rst.GetRed(col, row); + return rst.get(row, col); } // Number of rows and cols of the raster - static size_t rows(const Rst &rst) { return rst.GetHeight(); } - static size_t cols(const Rst &rst) { return rst.GetWidth(); } + static size_t rows(const Rst &rst) { return rst.rows; } + static size_t cols(const Rst &rst) { return rst.cols; } }; } // namespace marchsq @@ -44,7 +43,6 @@ namespace Slic3r { namespace { struct PNGBuffer { std::vector buf; std::string fname; }; - struct ArchiveData { boost::property_tree::ptree profile, config; std::vector images; @@ -69,8 +67,8 @@ boost::property_tree::ptree read_ini(const mz_zip_archive_file_stat &entry, } PNGBuffer read_png(const mz_zip_archive_file_stat &entry, - MZ_Archive & zip, - const std::string & name) + MZ_Archive & zip, + const std::string & name) { std::vector buf(entry.m_uncomp_size); @@ -259,9 +257,13 @@ std::vector extract_slices_from_sla_archive( } } - PNGBuffer &png = arch.images[i]; - wxMemoryInputStream stream{png.buf.data(), png.buf.size()}; - wxImage img{stream}; +// PNGBuffer &png = arch.images[i]; +// wxMemoryInputStream stream{png.buf.data(), png.buf.size()}; +// wxImage img{stream}; + + png::ImageGreyscale img; + png::ReadBuf rb{arch.images[i].buf.data(), arch.images[i].buf.size()}; + png::decode_png(rb, img); auto rings = marchsq::execute(img, 128, rstp.win); ExPolygons expolys = rings_to_expolygons(rings, rstp.px_w, rstp.px_h); diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index 5a1e8f18b7..30b93eafc9 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -17,6 +17,8 @@ add_executable(${_TEST_NAME}_tests test_marchingsquares.cpp test_timeutils.cpp test_voronoi.cpp + test_png_io.cpp + test_timeutils.cpp ) if (TARGET OpenVDB::openvdb) diff --git a/tests/libslic3r/test_png_io.cpp b/tests/libslic3r/test_png_io.cpp new file mode 100644 index 0000000000..3378d0062e --- /dev/null +++ b/tests/libslic3r/test_png_io.cpp @@ -0,0 +1,52 @@ +#define NOMINMAX +#include + +#include "libslic3r/PNGRead.hpp" +#include "libslic3r/SLA/AGGRaster.hpp" + +using namespace Slic3r; + +static sla::RasterGrayscaleAA create_raster(const sla::RasterBase::Resolution &res) +{ + sla::RasterBase::PixelDim pixdim{1., 1.}; + + auto bb = BoundingBox({0, 0}, {scaled(1.), scaled(1.)}); + sla::RasterBase::Trafo trafo; + trafo.center_x = bb.center().x(); + trafo.center_y = bb.center().y(); + + return sla::RasterGrayscaleAA{res, pixdim, trafo, agg::gamma_threshold(.5)}; +} + +TEST_CASE("PNG read", "[PNG]") { + auto rst = create_raster({100, 100}); + + size_t rstsum = 0; + for (size_t r = 0; r < rst.resolution().height_px; ++r) + for (size_t c = 0; c < rst.resolution().width_px; ++c) + rstsum += rst.read_pixel(c, r); + + SECTION("Correct png buffer should be recognized as such.") { + auto enc_rst = rst.encode(sla::PNGRasterEncoder{}); + REQUIRE(Slic3r::png::is_png({enc_rst.data(), enc_rst.size()})); + } + + SECTION("Fake png buffer should be recognized as such.") { + std::vector fake(10, '\0'); + REQUIRE(!Slic3r::png::is_png({fake.data(), fake.size()})); + } + + SECTION("Decoded PNG buffer resolution should match the original") { + auto enc_rst = rst.encode(sla::PNGRasterEncoder{}); + + png::ImageGreyscale img; + png::decode_png({enc_rst.data(), enc_rst.size()}, img); + + REQUIRE(img.rows == rst.resolution().height_px); + REQUIRE(img.cols == rst.resolution().width_px); + + size_t sum = std::accumulate(img.buf.begin(), img.buf.end(), size_t(0)); + + REQUIRE(sum == rstsum); + } +} From 769ee15475a3edc10c8dd29db8e853675091677e Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 28 Apr 2020 20:21:29 +0200 Subject: [PATCH 350/503] Move SLA import to libslic3r with png reading using libpng Also fix flipped object issue --- src/libslic3r/Format/SL1.cpp | 306 ++++++++++++++++++++++++++ src/libslic3r/Format/SL1.hpp | 18 ++ src/libslic3r/PNGRead.cpp | 41 ++-- src/libslic3r/PNGRead.hpp | 10 +- src/slic3r/CMakeLists.txt | 2 - src/slic3r/GUI/GUI_ObjectList.hpp | 2 +- src/slic3r/GUI/Jobs/SLAImportJob.cpp | 3 +- src/slic3r/Utils/SLAImport.cpp | 317 --------------------------- src/slic3r/Utils/SLAImport.hpp | 35 --- 9 files changed, 359 insertions(+), 375 deletions(-) delete mode 100644 src/slic3r/Utils/SLAImport.cpp delete mode 100644 src/slic3r/Utils/SLAImport.hpp diff --git a/src/libslic3r/Format/SL1.cpp b/src/libslic3r/Format/SL1.cpp index 5c402ef5bf..3d672eccb4 100644 --- a/src/libslic3r/Format/SL1.cpp +++ b/src/libslic3r/Format/SL1.cpp @@ -8,8 +8,314 @@ #include "libslic3r/Zipper.hpp" #include "libslic3r/SLAPrint.hpp" +#include + +#include "libslic3r/SlicesToTriangleMesh.hpp" +#include "libslic3r/MarchingSquares.hpp" +#include "libslic3r/ClipperUtils.hpp" +#include "libslic3r/MTUtils.hpp" +#include "libslic3r/PrintConfig.hpp" +#include "libslic3r/SLA/RasterBase.hpp" +#include "libslic3r/miniz_extension.hpp" +#include "libslic3r/PNGRead.hpp" + +#include +#include +#include + +namespace marchsq { + +template<> struct _RasterTraits { + using Rst = Slic3r::png::ImageGreyscale; + + // The type of pixel cell in the raster + using ValueType = uint8_t; + + // Value at a given position + static uint8_t get(const Rst &rst, size_t row, size_t col) + { + return rst.get(row, col); + } + + // Number of rows and cols of the raster + static size_t rows(const Rst &rst) { return rst.rows; } + static size_t cols(const Rst &rst) { return rst.cols; } +}; + +} // namespace marchsq + namespace Slic3r { +namespace { + +struct PNGBuffer { std::vector buf; std::string fname; }; +struct ArchiveData { + boost::property_tree::ptree profile, config; + std::vector images; +}; + +static const constexpr char *CONFIG_FNAME = "config.ini"; +static const constexpr char *PROFILE_FNAME = "prusaslicer.ini"; + +boost::property_tree::ptree read_ini(const mz_zip_archive_file_stat &entry, + MZ_Archive & zip) +{ + std::string buf(size_t(entry.m_uncomp_size), '\0'); + + if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename, + buf.data(), buf.size(), 0)) + throw std::runtime_error(zip.get_errorstr()); + + boost::property_tree::ptree tree; + std::stringstream ss(buf); + boost::property_tree::read_ini(ss, tree); + return tree; +} + +PNGBuffer read_png(const mz_zip_archive_file_stat &entry, + MZ_Archive & zip, + const std::string & name) +{ + std::vector buf(entry.m_uncomp_size); + + if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename, + buf.data(), buf.size(), 0)) + throw std::runtime_error(zip.get_errorstr()); + + return {std::move(buf), (name.empty() ? entry.m_filename : name)}; +} + +ArchiveData extract_sla_archive(const std::string &zipfname, + const std::string &exclude) +{ + ArchiveData arch; + + // Little RAII + struct Arch: public MZ_Archive { + Arch(const std::string &fname) { + if (!open_zip_reader(&arch, fname)) + throw std::runtime_error(get_errorstr()); + } + + ~Arch() { close_zip_reader(&arch); } + } zip (zipfname); + + mz_uint num_entries = mz_zip_reader_get_num_files(&zip.arch); + + for (mz_uint i = 0; i < num_entries; ++i) + { + mz_zip_archive_file_stat entry; + + if (mz_zip_reader_file_stat(&zip.arch, i, &entry)) + { + std::string name = entry.m_filename; + boost::algorithm::to_lower(name); + + if (boost::algorithm::contains(name, exclude)) continue; + + if (name == CONFIG_FNAME) arch.config = read_ini(entry, zip); + if (name == PROFILE_FNAME) arch.profile = read_ini(entry, zip); + + if (boost::filesystem::path(name).extension().string() == ".png") { + auto it = std::lower_bound( + arch.images.begin(), arch.images.end(), PNGBuffer{{}, name}, + [](const PNGBuffer &r1, const PNGBuffer &r2) { + return std::less()(r1.fname, r2.fname); + }); + + arch.images.insert(it, read_png(entry, zip, name)); + } + } + } + + return arch; +} + +ExPolygons rings_to_expolygons(const std::vector &rings, + double px_w, double px_h) +{ + ExPolygons polys; polys.reserve(rings.size()); + + for (const marchsq::Ring &ring : rings) { + Polygon poly; Points &pts = poly.points; + pts.reserve(ring.size()); + + for (const marchsq::Coord &crd : ring) + pts.emplace_back(scaled(crd.c * px_w), scaled(crd.r * px_h)); + + polys.emplace_back(poly); + } + + // reverse the raster transformations + return union_ex(polys); +} + +template void foreach_vertex(ExPolygon &poly, Fn &&fn) +{ + for (auto &p : poly.contour.points) fn(p); + for (auto &h : poly.holes) + for (auto &p : h.points) fn(p); +} + +void invert_raster_trafo(ExPolygons & expolys, + const sla::RasterBase::Trafo &trafo, + coord_t width, + coord_t height) +{ + for (auto &expoly : expolys) { + if (trafo.mirror_y) + foreach_vertex(expoly, [height](Point &p) {p.y() = height - p.y(); }); + + if (trafo.mirror_x) + foreach_vertex(expoly, [width](Point &p) {p.x() = width - p.x(); }); + + expoly.translate(-trafo.center_x, -trafo.center_y); + + if (trafo.flipXY) + foreach_vertex(expoly, [](Point &p) { std::swap(p.x(), p.y()); }); + + if ((trafo.mirror_x + trafo.mirror_y + trafo.flipXY) % 2) { + expoly.contour.reverse(); + for (auto &h : expoly.holes) h.reverse(); + } + } +} + +struct RasterParams { + sla::RasterBase::Trafo trafo; // Raster transformations + coord_t width, height; // scaled raster dimensions (not resolution) + double px_h, px_w; // pixel dimesions + marchsq::Coord win; // marching squares window size +}; + +RasterParams get_raster_params(const DynamicPrintConfig &cfg) +{ + auto *opt_disp_cols = cfg.option("display_pixels_x"); + auto *opt_disp_rows = cfg.option("display_pixels_y"); + auto *opt_disp_w = cfg.option("display_width"); + auto *opt_disp_h = cfg.option("display_height"); + auto *opt_mirror_x = cfg.option("display_mirror_x"); + auto *opt_mirror_y = cfg.option("display_mirror_y"); + auto *opt_orient = cfg.option>("display_orientation"); + + if (!opt_disp_cols || !opt_disp_rows || !opt_disp_w || !opt_disp_h || + !opt_mirror_x || !opt_mirror_y || !opt_orient) + throw std::runtime_error("Invalid SL1 file"); + + RasterParams rstp; + + rstp.px_w = opt_disp_w->value / (opt_disp_cols->value - 1); + rstp.px_h = opt_disp_h->value / (opt_disp_rows->value - 1); + + rstp.trafo = sla::RasterBase::Trafo{opt_orient->value == sladoLandscape ? + sla::RasterBase::roLandscape : + sla::RasterBase::roPortrait, + {opt_mirror_x->value, opt_mirror_y->value}}; + + rstp.height = scaled(opt_disp_h->value); + rstp.width = scaled(opt_disp_w->value); + + return rstp; +} + +struct SliceParams { double layerh = 0., initial_layerh = 0.; }; + +SliceParams get_slice_params(const DynamicPrintConfig &cfg) +{ + auto *opt_layerh = cfg.option("layer_height"); + auto *opt_init_layerh = cfg.option("initial_layer_height"); + + if (!opt_layerh || !opt_init_layerh) + throw std::runtime_error("Invalid SL1 file"); + + return SliceParams{opt_layerh->getFloat(), opt_init_layerh->getFloat()}; +} + +std::vector extract_slices_from_sla_archive( + ArchiveData & arch, + const RasterParams & rstp, + std::function progr) +{ + auto jobdir = arch.config.get("jobDir"); + for (auto &c : jobdir) c = std::tolower(c); + + std::vector slices(arch.images.size()); + + struct Status + { + double incr, val, prev; + bool stop = false; + tbb::spin_mutex mutex; + } st {100. / slices.size(), 0., 0.}; + + tbb::parallel_for(size_t(0), arch.images.size(), + [&arch, &slices, &st, &rstp, progr](size_t i) { + // Status indication guarded with the spinlock + { + std::lock_guard lck(st.mutex); + if (st.stop) return; + + st.val += st.incr; + double curr = std::round(st.val); + if (curr > st.prev) { + st.prev = curr; + st.stop = !progr(int(curr)); + } + } + + png::ImageGreyscale img; + png::ReadBuf rb{arch.images[i].buf.data(), arch.images[i].buf.size()}; + if (!png::decode_png(rb, img)) return; + + auto rings = marchsq::execute(img, 128, rstp.win); + ExPolygons expolys = rings_to_expolygons(rings, rstp.px_w, rstp.px_h); + + // Invert the raster transformations indicated in + // the profile metadata + invert_raster_trafo(expolys, rstp.trafo, rstp.width, rstp.height); + + slices[i] = std::move(expolys); + }); + + if (st.stop) slices = {}; + + return slices; +} + +} // namespace + +void import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out) +{ + ArchiveData arch = extract_sla_archive(zipfname, "png"); + out.load(arch.profile); +} + +void import_sla_archive( + const std::string & zipfname, + Vec2i windowsize, + TriangleMesh & out, + DynamicPrintConfig & profile, + std::function progr) +{ + // Ensure minimum window size for marching squares + windowsize.x() = std::max(2, windowsize.x()); + windowsize.y() = std::max(2, windowsize.y()); + + ArchiveData arch = extract_sla_archive(zipfname, "thumbnail"); + profile.load(arch.profile); + + RasterParams rstp = get_raster_params(profile); + rstp.win = {windowsize.y(), windowsize.x()}; + + SliceParams slicp = get_slice_params(profile); + + std::vector slices = + extract_slices_from_sla_archive(arch, rstp, progr); + + if (!slices.empty()) + out = slices_to_triangle_mesh(slices, 0, slicp.layerh, slicp.initial_layerh); +} + using ConfMap = std::map; namespace { diff --git a/src/libslic3r/Format/SL1.hpp b/src/libslic3r/Format/SL1.hpp index fbb6d61604..ab731ff841 100644 --- a/src/libslic3r/Format/SL1.hpp +++ b/src/libslic3r/Format/SL1.hpp @@ -38,6 +38,24 @@ public: } }; +void import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out); + +void import_sla_archive( + const std::string & zipfname, + Vec2i windowsize, + TriangleMesh & out, + DynamicPrintConfig & profile, + std::function progr = [](int) { return true; }); + +inline void import_sla_archive( + const std::string & zipfname, + Vec2i windowsize, + TriangleMesh & out, + std::function progr = [](int) { return true; }) +{ + DynamicPrintConfig profile; + import_sla_archive(zipfname, windowsize, out, profile, progr); +} } // namespace Slic3r::sla diff --git a/src/libslic3r/PNGRead.cpp b/src/libslic3r/PNGRead.cpp index 8bfa3cb951..6eb7d593cc 100644 --- a/src/libslic3r/PNGRead.cpp +++ b/src/libslic3r/PNGRead.cpp @@ -7,11 +7,21 @@ namespace Slic3r { namespace png { -struct png_deleter { void operator()(png_struct *p) { - png_destroy_read_struct( &p, nullptr, nullptr); } -}; +struct PNGDescr { + png_struct *png = nullptr; png_info *info = nullptr; -using png_ptr_t = std::unique_ptr; + PNGDescr() = default; + PNGDescr(const PNGDescr&) = delete; + PNGDescr(PNGDescr&&) = delete; + PNGDescr& operator=(const PNGDescr&) = delete; + PNGDescr& operator=(PNGDescr&&) = delete; + + ~PNGDescr() + { + if (png && info) png_destroy_info_struct(png, &info); + if (png) png_destroy_read_struct( &png, nullptr, nullptr); + } +}; bool is_png(const ReadBuf &rb) { @@ -25,22 +35,23 @@ bool decode_png(const ReadBuf &rb, ImageGreyscale &img) { if (!is_png(rb)) return false; - png_ptr_t png{png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr)}; + PNGDescr dsc; + dsc.png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); - if(!png) return false; + if(!dsc.png) return false; - png_infop info = png_create_info_struct(png.get()); - if(!info) return {}; + dsc.info = png_create_info_struct(dsc.png); + if(!dsc.info) return {}; FILE *io = ::fmemopen(const_cast(rb.buf), rb.sz, "rb"); - png_init_io(png.get(), io); + png_init_io(dsc.png, io); - png_read_info(png.get(), info); + png_read_info(dsc.png, dsc.info); - img.cols = png_get_image_width(png.get(), info); - img.rows = png_get_image_height(png.get(), info); - size_t color_type = png_get_color_type(png.get(), info); - size_t bit_depth = png_get_bit_depth(png.get(), info); + img.cols = png_get_image_width(dsc.png, dsc.info); + img.rows = png_get_image_height(dsc.png, dsc.info); + size_t color_type = png_get_color_type(dsc.png, dsc.info); + size_t bit_depth = png_get_bit_depth(dsc.png, dsc.info); if (color_type != PNG_COLOR_TYPE_GRAY || bit_depth != 8) return false; @@ -49,7 +60,7 @@ bool decode_png(const ReadBuf &rb, ImageGreyscale &img) auto readbuf = static_cast(img.buf.data()); for (size_t r = 0; r < img.rows; ++r) - png_read_row(png.get(), readbuf + r * img.cols, nullptr); + png_read_row(dsc.png, readbuf + r * img.cols, nullptr); fclose(io); diff --git a/src/libslic3r/PNGRead.hpp b/src/libslic3r/PNGRead.hpp index 88a948395b..0e7311f2e5 100644 --- a/src/libslic3r/PNGRead.hpp +++ b/src/libslic3r/PNGRead.hpp @@ -19,12 +19,14 @@ struct RGB { uint8_t r, g, b; }; using ImageRGB = Image; using ImageGreyscale = Image; +bool is_png(const ReadBuf &pngbuf); + +// Only decodes true 8 bit grayscale png images. Returns false for other formats +// TODO: implement transformation of rgb images into grayscale... +bool decode_png(const ReadBuf &pngbuf, ImageGreyscale &img); + // TODO // bool decode_png(Buffer &&pngbuf, ImageRGB &img); -bool is_png(const ReadBuf &pngbuf); - -bool decode_png(const ReadBuf &pngbuf, ImageGreyscale &img); - }} #endif // PNGREAD_HPP diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 8a2672c774..b64cfdf4fc 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -187,8 +187,6 @@ set(SLIC3R_GUI_SOURCES Utils/HexFile.cpp Utils/HexFile.hpp Utils/Thread.hpp - Utils/SLAImport.hpp - Utils/SLAImport.cpp ) if (APPLE) diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index aa5264b07d..b59631d674 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -294,7 +294,7 @@ public: void load_part(ModelObject* model_object, std::vector> &volumes_info, ModelVolumeType type); void load_generic_subobject(const std::string& type_name, const ModelVolumeType type); void load_shape_object(const std::string &type_name); - void load_mesh_object(const TriangleMesh &mesh, const wxString &name); + void load_mesh_object(const TriangleMesh &mesh, const wxString &name); void del_object(const int obj_idx); void del_subobject_item(wxDataViewItem& item); void del_settings_from_config(const wxDataViewItem& parent_item); diff --git a/src/slic3r/GUI/Jobs/SLAImportJob.cpp b/src/slic3r/GUI/Jobs/SLAImportJob.cpp index 2d5d5b0729..ae92bd6989 100644 --- a/src/slic3r/GUI/Jobs/SLAImportJob.cpp +++ b/src/slic3r/GUI/Jobs/SLAImportJob.cpp @@ -1,10 +1,11 @@ #include "SLAImportJob.hpp" +#include "libslic3r/Format/SL1.hpp" + #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp" -#include "slic3r/Utils/SLAImport.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/PresetBundle.hpp" diff --git a/src/slic3r/Utils/SLAImport.cpp b/src/slic3r/Utils/SLAImport.cpp deleted file mode 100644 index 13ea603396..0000000000 --- a/src/slic3r/Utils/SLAImport.cpp +++ /dev/null @@ -1,317 +0,0 @@ -#include "SLAImport.hpp" - -#include - -#include "libslic3r/SlicesToTriangleMesh.hpp" -#include "libslic3r/MarchingSquares.hpp" -#include "libslic3r/ClipperUtils.hpp" -#include "libslic3r/MTUtils.hpp" -#include "libslic3r/PrintConfig.hpp" -#include "libslic3r/SLA/RasterBase.hpp" -#include "libslic3r/miniz_extension.hpp" -#include "libslic3r/PNGRead.hpp" - -#include -#include -#include - -#include - -namespace marchsq { - -template<> struct _RasterTraits { - using Rst = Slic3r::png::ImageGreyscale; - - // The type of pixel cell in the raster - using ValueType = uint8_t; - - // Value at a given position - static uint8_t get(const Rst &rst, size_t row, size_t col) - { - return rst.get(row, col); - } - - // Number of rows and cols of the raster - static size_t rows(const Rst &rst) { return rst.rows; } - static size_t cols(const Rst &rst) { return rst.cols; } -}; - -} // namespace marchsq - -namespace Slic3r { - -namespace { - -struct PNGBuffer { std::vector buf; std::string fname; }; -struct ArchiveData { - boost::property_tree::ptree profile, config; - std::vector images; -}; - -static const constexpr char *CONFIG_FNAME = "config.ini"; -static const constexpr char *PROFILE_FNAME = "prusaslicer.ini"; - -boost::property_tree::ptree read_ini(const mz_zip_archive_file_stat &entry, - MZ_Archive & zip) -{ - std::string buf(size_t(entry.m_uncomp_size), '\0'); - - if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename, - buf.data(), buf.size(), 0)) - throw std::runtime_error(zip.get_errorstr()); - - boost::property_tree::ptree tree; - std::stringstream ss(buf); - boost::property_tree::read_ini(ss, tree); - return tree; -} - -PNGBuffer read_png(const mz_zip_archive_file_stat &entry, - MZ_Archive & zip, - const std::string & name) -{ - std::vector buf(entry.m_uncomp_size); - - if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename, - buf.data(), buf.size(), 0)) - throw std::runtime_error(zip.get_errorstr()); - - return {std::move(buf), (name.empty() ? entry.m_filename : name)}; -} - -ArchiveData extract_sla_archive(const std::string &zipfname, - const std::string &exclude) -{ - ArchiveData arch; - - // Little RAII - struct Arch: public MZ_Archive { - Arch(const std::string &fname) { - if (!open_zip_reader(&arch, fname)) - throw std::runtime_error(get_errorstr()); - } - - ~Arch() { close_zip_reader(&arch); } - } zip (zipfname); - - mz_uint num_entries = mz_zip_reader_get_num_files(&zip.arch); - - for (mz_uint i = 0; i < num_entries; ++i) - { - mz_zip_archive_file_stat entry; - - if (mz_zip_reader_file_stat(&zip.arch, i, &entry)) - { - std::string name = entry.m_filename; - boost::algorithm::to_lower(name); - - if (boost::algorithm::contains(name, exclude)) continue; - - if (name == CONFIG_FNAME) arch.config = read_ini(entry, zip); - if (name == PROFILE_FNAME) arch.profile = read_ini(entry, zip); - - if (boost::filesystem::path(name).extension().string() == ".png") { - auto it = std::lower_bound( - arch.images.begin(), arch.images.end(), PNGBuffer{{}, name}, - [](const PNGBuffer &r1, const PNGBuffer &r2) { - return std::less()(r1.fname, r2.fname); - }); - - arch.images.insert(it, read_png(entry, zip, name)); - } - } - } - - return arch; -} - -ExPolygons rings_to_expolygons(const std::vector &rings, - double px_w, double px_h) -{ - ExPolygons polys; polys.reserve(rings.size()); - - for (const marchsq::Ring &ring : rings) { - Polygon poly; Points &pts = poly.points; - pts.reserve(ring.size()); - - for (const marchsq::Coord &crd : ring) - pts.emplace_back(scaled(crd.c * px_w), scaled(crd.r * px_h)); - - polys.emplace_back(poly); - } - - // reverse the raster transformations - return union_ex(polys); -} - -template void foreach_vertex(ExPolygon &poly, Fn &&fn) -{ - for (auto &p : poly.contour.points) fn(p); - for (auto &h : poly.holes) - for (auto &p : h.points) fn(p); -} - -void invert_raster_trafo(ExPolygons & expolys, - const sla::RasterBase::Trafo &trafo, - coord_t width, - coord_t height) -{ - for (auto &expoly : expolys) { - if (trafo.mirror_y) - foreach_vertex(expoly, [height](Point &p) {p.y() = height - p.y(); }); - - if (trafo.mirror_x) - foreach_vertex(expoly, [width](Point &p) {p.x() = width - p.x(); }); - - expoly.translate(-trafo.center_x, -trafo.center_y); - - if (trafo.flipXY) - foreach_vertex(expoly, [](Point &p) { std::swap(p.x(), p.y()); }); - - if ((trafo.mirror_x + trafo.mirror_y + trafo.flipXY) % 2) { - expoly.contour.reverse(); - for (auto &h : expoly.holes) h.reverse(); - } - } -} - -struct RasterParams { - sla::RasterBase::Trafo trafo; // Raster transformations - coord_t width, height; // scaled raster dimensions (not resolution) - double px_h, px_w; // pixel dimesions - marchsq::Coord win; // marching squares window size -}; - -RasterParams get_raster_params(const DynamicPrintConfig &cfg) -{ - auto *opt_disp_cols = cfg.option("display_pixels_x"); - auto *opt_disp_rows = cfg.option("display_pixels_y"); - auto *opt_disp_w = cfg.option("display_width"); - auto *opt_disp_h = cfg.option("display_height"); - auto *opt_mirror_x = cfg.option("display_mirror_x"); - auto *opt_mirror_y = cfg.option("display_mirror_y"); - auto *opt_orient = cfg.option>("display_orientation"); - - if (!opt_disp_cols || !opt_disp_rows || !opt_disp_w || !opt_disp_h || - !opt_mirror_x || !opt_mirror_y || !opt_orient) - throw std::runtime_error("Invalid SL1 file"); - - RasterParams rstp; - - rstp.px_w = opt_disp_w->value / (opt_disp_cols->value - 1); - rstp.px_h = opt_disp_h->value / (opt_disp_rows->value - 1); - - sla::RasterBase::Trafo trafo{opt_orient->value == sladoLandscape ? - sla::RasterBase::roLandscape : - sla::RasterBase::roPortrait, - {opt_mirror_x->value, opt_mirror_y->value}}; - - rstp.height = scaled(opt_disp_h->value); - rstp.width = scaled(opt_disp_w->value); - - return rstp; -} - -struct SliceParams { double layerh = 0., initial_layerh = 0.; }; - -SliceParams get_slice_params(const DynamicPrintConfig &cfg) -{ - auto *opt_layerh = cfg.option("layer_height"); - auto *opt_init_layerh = cfg.option("initial_layer_height"); - - if (!opt_layerh || !opt_init_layerh) - throw std::runtime_error("Invalid SL1 file"); - - return SliceParams{opt_layerh->getFloat(), opt_init_layerh->getFloat()}; -} - -std::vector extract_slices_from_sla_archive( - ArchiveData & arch, - const RasterParams & rstp, - std::function progr) -{ - auto jobdir = arch.config.get("jobDir"); - for (auto &c : jobdir) c = std::tolower(c); - - std::vector slices(arch.images.size()); - - struct Status - { - double incr, val, prev; - bool stop = false; - tbb::spin_mutex mutex; - } st {100. / slices.size(), 0., 0.}; - - tbb::parallel_for(size_t(0), arch.images.size(), - [&arch, &slices, &st, &rstp, progr](size_t i) { - // Status indication guarded with the spinlock - { - std::lock_guard lck(st.mutex); - if (st.stop) return; - - st.val += st.incr; - double curr = std::round(st.val); - if (curr > st.prev) { - st.prev = curr; - st.stop = !progr(int(curr)); - } - } - -// PNGBuffer &png = arch.images[i]; -// wxMemoryInputStream stream{png.buf.data(), png.buf.size()}; -// wxImage img{stream}; - - png::ImageGreyscale img; - png::ReadBuf rb{arch.images[i].buf.data(), arch.images[i].buf.size()}; - png::decode_png(rb, img); - - auto rings = marchsq::execute(img, 128, rstp.win); - ExPolygons expolys = rings_to_expolygons(rings, rstp.px_w, rstp.px_h); - - // Invert the raster transformations indicated in - // the profile metadata - invert_raster_trafo(expolys, rstp.trafo, rstp.width, rstp.height); - - slices[i] = std::move(expolys); - }); - - if (st.stop) slices = {}; - - return slices; -} - -} // namespace - -void import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out) -{ - ArchiveData arch = extract_sla_archive(zipfname, "png"); - out.load(arch.profile); -} - -void import_sla_archive( - const std::string & zipfname, - Vec2i windowsize, - TriangleMesh & out, - DynamicPrintConfig & profile, - std::function progr) -{ - // Ensure minimum window size for marching squares - windowsize.x() = std::max(2, windowsize.x()); - windowsize.y() = std::max(2, windowsize.y()); - - ArchiveData arch = extract_sla_archive(zipfname, "thumbnail"); - profile.load(arch.profile); - - RasterParams rstp = get_raster_params(profile); - rstp.win = {windowsize.y(), windowsize.x()}; - - SliceParams slicp = get_slice_params(profile); - - std::vector slices = - extract_slices_from_sla_archive(arch, rstp, progr); - - if (!slices.empty()) - out = slices_to_triangle_mesh(slices, 0, slicp.layerh, slicp.initial_layerh); -} - -} // namespace Slic3r diff --git a/src/slic3r/Utils/SLAImport.hpp b/src/slic3r/Utils/SLAImport.hpp deleted file mode 100644 index 73995014f4..0000000000 --- a/src/slic3r/Utils/SLAImport.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef SLAIMPORT_HPP -#define SLAIMPORT_HPP - -#include - -#include -#include - -namespace Slic3r { - -class TriangleMesh; -class DynamicPrintConfig; - -void import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out); - -void import_sla_archive( - const std::string & zipfname, - Vec2i windowsize, - TriangleMesh & out, - DynamicPrintConfig & profile, - std::function progr = [](int) { return true; }); - -inline void import_sla_archive( - const std::string & zipfname, - Vec2i windowsize, - TriangleMesh & out, - std::function progr = [](int) { return true; }) -{ - DynamicPrintConfig profile; - import_sla_archive(zipfname, windowsize, out, profile, progr); -} - -} - -#endif // SLAIMPORT_HPP From 8541ce406069e2b710b22bec937d3405ed0e8316 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 29 Apr 2020 16:26:39 +0200 Subject: [PATCH 351/503] SLA archive import will now recover the model's original position. --- CMakeLists.txt | 2 +- src/libslic3r/Format/SL1.cpp | 2 ++ src/slic3r/GUI/GUI_ObjectList.cpp | 19 ++++++++++++------- src/slic3r/GUI/GUI_ObjectList.hpp | 2 +- src/slic3r/GUI/Jobs/SLAImportJob.cpp | 6 ++++-- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 48ad5033e0..a4be182e7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -386,7 +386,7 @@ if (NOT EXPAT_FOUND) set(EXPAT_LIBRARIES expat) endif () -find_package(PNG) +find_package(PNG REQUIRED) find_package(OpenGL REQUIRED) diff --git a/src/libslic3r/Format/SL1.cpp b/src/libslic3r/Format/SL1.cpp index 3d672eccb4..ff1af5d8b1 100644 --- a/src/libslic3r/Format/SL1.cpp +++ b/src/libslic3r/Format/SL1.cpp @@ -162,6 +162,8 @@ void invert_raster_trafo(ExPolygons & expolys, coord_t width, coord_t height) { + if (trafo.flipXY) std::swap(height, width); + for (auto &expoly : expolys) { if (trafo.mirror_y) foreach_vertex(expoly, [height](Point &p) {p.y() = height - p.y(); }); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index a326eea7b3..44b2b6186d 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2209,7 +2209,7 @@ void ObjectList::load_shape_object(const std::string& type_name) load_mesh_object(mesh, _(L("Shape")) + "-" + _(type_name)); } -void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name) +void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center) { // Add mesh to model as a new object Model& model = wxGetApp().plater()->model(); @@ -2219,6 +2219,7 @@ void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name #endif /* _DEBUG */ std::vector object_idxs; + auto bb = mesh.bounding_box(); ModelObject* new_object = model.add_object(); new_object->name = into_u8(name); new_object->add_instance(); // each object should have at list one instance @@ -2228,13 +2229,17 @@ void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name // set a default extruder value, since user can't add it manually new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); new_object->invalidate_bounding_box(); - - new_object->center_around_origin(); + new_object->translate(-bb.center()); + + if (center) { + const BoundingBoxf bed_shape = wxGetApp().plater()->bed_shape_bb(); + new_object->instances[0]->set_offset(Slic3r::to_3d(bed_shape.center().cast(), -new_object->origin_translation(2))); + } else { + new_object->instances[0]->set_offset(bb.center()); + } + new_object->ensure_on_bed(); - - const BoundingBoxf bed_shape = wxGetApp().plater()->bed_shape_bb(); - new_object->instances[0]->set_offset(Slic3r::to_3d(bed_shape.center().cast(), -new_object->origin_translation(2))); - + object_idxs.push_back(model.objects.size() - 1); #ifdef _DEBUG check_model_ids_validity(model); diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index b59631d674..9f7dcd2475 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -294,7 +294,7 @@ public: void load_part(ModelObject* model_object, std::vector> &volumes_info, ModelVolumeType type); void load_generic_subobject(const std::string& type_name, const ModelVolumeType type); void load_shape_object(const std::string &type_name); - void load_mesh_object(const TriangleMesh &mesh, const wxString &name); + void load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center = true); void del_object(const int obj_idx); void del_subobject_item(wxDataViewItem& item); void del_settings_from_config(const wxDataViewItem& parent_item); diff --git a/src/slic3r/GUI/Jobs/SLAImportJob.cpp b/src/slic3r/GUI/Jobs/SLAImportJob.cpp index ae92bd6989..adecae6ac0 100644 --- a/src/slic3r/GUI/Jobs/SLAImportJob.cpp +++ b/src/slic3r/GUI/Jobs/SLAImportJob.cpp @@ -219,8 +219,10 @@ void SLAImportJob::finalize() wxGetApp().load_current_presets(); } - if (!p->mesh.empty()) - p->plater->sidebar().obj_list()->load_mesh_object(p->mesh, name); + if (!p->mesh.empty()) { + bool is_centered = false; + p->plater->sidebar().obj_list()->load_mesh_object(p->mesh, name, is_centered); + } reset(); } From 1560e15ed90adfea2d28077f3b4b4a57ab293409 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 3 Aug 2020 15:27:58 +0200 Subject: [PATCH 352/503] Add missing includes for win --- tests/libslic3r/test_png_io.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/libslic3r/test_png_io.cpp b/tests/libslic3r/test_png_io.cpp index 3378d0062e..51f94be326 100644 --- a/tests/libslic3r/test_png_io.cpp +++ b/tests/libslic3r/test_png_io.cpp @@ -1,8 +1,11 @@ #define NOMINMAX #include +#include + #include "libslic3r/PNGRead.hpp" #include "libslic3r/SLA/AGGRaster.hpp" +#include "libslic3r/BoundingBox.hpp" using namespace Slic3r; From 3f41e4023d040777bab5b754c5423769efd52c57 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 3 Aug 2020 15:50:05 +0200 Subject: [PATCH 353/503] Try to override mac library search order to find static dep libs --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index a4be182e7e..1d4576c37d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,11 @@ option(SLIC3R_ASAN "Enable ASan on Clang and GCC" 0) set(SLIC3R_GTK "2" CACHE STRING "GTK version to use with wxWidgets on Linux") +if (APPLE) + set(CMAKE_FIND_FRAMEWORK LAST) + set(CMAKE_FIND_APPBUNDLE LAST) +endif () + # Proposal for C++ unit tests and sandboxes option(SLIC3R_BUILD_SANDBOXES "Build development sandboxes" OFF) option(SLIC3R_BUILD_TESTS "Build unit tests" ON) From b09552e56faced8580fde7c4648a5116ae578d61 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 3 Aug 2020 18:00:32 +0200 Subject: [PATCH 354/503] Don't use fmemopen, its not standard. --- src/libslic3r/PNGRead.cpp | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/PNGRead.cpp b/src/libslic3r/PNGRead.cpp index 6eb7d593cc..695ceba3dc 100644 --- a/src/libslic3r/PNGRead.cpp +++ b/src/libslic3r/PNGRead.cpp @@ -31,20 +31,46 @@ bool is_png(const ReadBuf &rb) !png_sig_cmp(static_cast(rb.buf), 0, PNG_SIG_BYTES); } +// A wrapper around ReadBuf to be read repeatedly like a stream. libpng needs +// this form for its buffer read callback. +struct ReadBufReader { + const ReadBuf &rdbuf; size_t pos; + ReadBufReader(const ReadBuf &rd): rdbuf{rd}, pos{0} {} +}; + +// Buffer read callback for libpng. It provides an allocated output buffer and +// the amount of data it desires to read from the input. +void png_read_callback(png_struct *png_ptr, + png_bytep outBytes, + png_size_t byteCountToRead) +{ + // Retrieve our input buffer through the png_ptr + auto reader = static_cast(png_get_io_ptr(png_ptr)); + + if (!reader || byteCountToRead > reader->rdbuf.sz - reader->pos) return; + + auto buf = static_cast(reader->rdbuf.buf); + size_t pos = reader->pos; + + std::copy(buf + pos, buf + (pos + byteCountToRead), outBytes); + reader->pos += byteCountToRead; +} + bool decode_png(const ReadBuf &rb, ImageGreyscale &img) { if (!is_png(rb)) return false; PNGDescr dsc; - dsc.png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + dsc.png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, + nullptr); if(!dsc.png) return false; dsc.info = png_create_info_struct(dsc.png); if(!dsc.info) return {}; - FILE *io = ::fmemopen(const_cast(rb.buf), rb.sz, "rb"); - png_init_io(dsc.png, io); + ReadBufReader reader {rb}; + png_set_read_fn(dsc.png, static_cast(&reader), png_read_callback); png_read_info(dsc.png, dsc.info); @@ -62,9 +88,7 @@ bool decode_png(const ReadBuf &rb, ImageGreyscale &img) for (size_t r = 0; r < img.rows; ++r) png_read_row(dsc.png, readbuf + r * img.cols, nullptr); - fclose(io); - return true; } -}} +}} // namespace Slic3r::png From ad0df8fd098cfead66f9ede7456cd783a88ba049 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 3 Aug 2020 18:50:43 +0200 Subject: [PATCH 355/503] Be compatible with earlier libpng versions. --- src/libslic3r/PNGRead.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/PNGRead.cpp b/src/libslic3r/PNGRead.cpp index 695ceba3dc..5b5b9ffecb 100644 --- a/src/libslic3r/PNGRead.cpp +++ b/src/libslic3r/PNGRead.cpp @@ -27,8 +27,18 @@ bool is_png(const ReadBuf &rb) { static const constexpr int PNG_SIG_BYTES = 8; - return rb.sz >= PNG_SIG_BYTES && - !png_sig_cmp(static_cast(rb.buf), 0, PNG_SIG_BYTES); +#if PNG_LIBPNG_VER_MINOR <= 2 + // Earlier libpng versions had png_sig_cmp(png_bytep, ...) which is not + // a const pointer. It is not possible to cast away the const qualifier from + // the input buffer so... yes... life is challenging... + png_byte buf[PNG_SIG_BYTES]; + auto inbuf = static_cast(rb.buf); + std::copy(inbuf, inbuf + PNG_SIG_BYTES, buf); +#else + auto buf = static_cast(rb.buf); +#endif + + return rb.sz >= PNG_SIG_BYTES && !png_sig_cmp(buf, 0, PNG_SIG_BYTES); } // A wrapper around ReadBuf to be read repeatedly like a stream. libpng needs From 79567a1958f334e9c43208aa336e668e7da1a311 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 4 Aug 2020 10:13:01 +0200 Subject: [PATCH 356/503] Add some comments for png read interface --- src/libslic3r/PNGRead.cpp | 46 ++++++++++++++----------------- src/libslic3r/PNGRead.hpp | 58 ++++++++++++++++++++++++++++++++------- 2 files changed, 69 insertions(+), 35 deletions(-) diff --git a/src/libslic3r/PNGRead.cpp b/src/libslic3r/PNGRead.cpp index 5b5b9ffecb..e66143b845 100644 --- a/src/libslic3r/PNGRead.cpp +++ b/src/libslic3r/PNGRead.cpp @@ -41,13 +41,6 @@ bool is_png(const ReadBuf &rb) return rb.sz >= PNG_SIG_BYTES && !png_sig_cmp(buf, 0, PNG_SIG_BYTES); } -// A wrapper around ReadBuf to be read repeatedly like a stream. libpng needs -// this form for its buffer read callback. -struct ReadBufReader { - const ReadBuf &rdbuf; size_t pos; - ReadBufReader(const ReadBuf &rd): rdbuf{rd}, pos{0} {} -}; - // Buffer read callback for libpng. It provides an allocated output buffer and // the amount of data it desires to read from the input. void png_read_callback(png_struct *png_ptr, @@ -55,20 +48,21 @@ void png_read_callback(png_struct *png_ptr, png_size_t byteCountToRead) { // Retrieve our input buffer through the png_ptr - auto reader = static_cast(png_get_io_ptr(png_ptr)); + auto reader = static_cast(png_get_io_ptr(png_ptr)); - if (!reader || byteCountToRead > reader->rdbuf.sz - reader->pos) return; + if (!reader || !reader->is_ok()) return; - auto buf = static_cast(reader->rdbuf.buf); - size_t pos = reader->pos; - - std::copy(buf + pos, buf + (pos + byteCountToRead), outBytes); - reader->pos += byteCountToRead; + reader->read(static_cast(outBytes), byteCountToRead); } -bool decode_png(const ReadBuf &rb, ImageGreyscale &img) +bool decode_png(IStream &in_buf, ImageGreyscale &out_img) { - if (!is_png(rb)) return false; + static const constexpr int PNG_SIG_BYTES = 8; + + std::vector sig(PNG_SIG_BYTES, 0); + in_buf.read(sig.data(), PNG_SIG_BYTES); + if (!png_check_sig(sig.data(), PNG_SIG_BYTES)) + return false; PNGDescr dsc; dsc.png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, @@ -77,26 +71,28 @@ bool decode_png(const ReadBuf &rb, ImageGreyscale &img) if(!dsc.png) return false; dsc.info = png_create_info_struct(dsc.png); - if(!dsc.info) return {}; + if(!dsc.info) return false; - ReadBufReader reader {rb}; - png_set_read_fn(dsc.png, static_cast(&reader), png_read_callback); + png_set_read_fn(dsc.png, static_cast(&in_buf), png_read_callback); + + // Tell that we have already read the first bytes to check the signature + png_set_sig_bytes(dsc.png, PNG_SIG_BYTES); png_read_info(dsc.png, dsc.info); - img.cols = png_get_image_width(dsc.png, dsc.info); - img.rows = png_get_image_height(dsc.png, dsc.info); + out_img.cols = png_get_image_width(dsc.png, dsc.info); + out_img.rows = png_get_image_height(dsc.png, dsc.info); size_t color_type = png_get_color_type(dsc.png, dsc.info); size_t bit_depth = png_get_bit_depth(dsc.png, dsc.info); if (color_type != PNG_COLOR_TYPE_GRAY || bit_depth != 8) return false; - img.buf.resize(img.rows * img.cols); + out_img.buf.resize(out_img.rows * out_img.cols); - auto readbuf = static_cast(img.buf.data()); - for (size_t r = 0; r < img.rows; ++r) - png_read_row(dsc.png, readbuf + r * img.cols, nullptr); + auto readbuf = static_cast(out_img.buf.data()); + for (size_t r = 0; r < out_img.rows; ++r) + png_read_row(dsc.png, readbuf + r * out_img.cols, nullptr); return true; } diff --git a/src/libslic3r/PNGRead.hpp b/src/libslic3r/PNGRead.hpp index 0e7311f2e5..082edd5691 100644 --- a/src/libslic3r/PNGRead.hpp +++ b/src/libslic3r/PNGRead.hpp @@ -3,30 +3,68 @@ #include #include +#include namespace Slic3r { namespace png { -struct ReadBuf { const void *buf = nullptr; const size_t sz = 0; }; +// Interface for an input stream of encoded png image data. +struct IStream { + virtual ~IStream() = default; + virtual size_t read(std::uint8_t *outp, size_t amount) = 0; + virtual bool is_ok() const = 0; +}; +// The output format of decode_png: a 2D pixel matrix stored continuously row +// after row (row major layout). template struct Image { std::vector buf; size_t rows, cols; PxT get(size_t row, size_t col) const { return buf[row * cols + col]; } }; -struct RGB { uint8_t r, g, b; }; - -using ImageRGB = Image; using ImageGreyscale = Image; +// Only decodes true 8 bit grayscale png images. Returns false for other formats +// TODO (if needed): implement transformation of rgb images into grayscale... +bool decode_png(IStream &stream, ImageGreyscale &out_img); + +// TODO (if needed) +// struct RGB { uint8_t r, g, b; }; +// using ImageRGB = Image; +// bool decode_png(IStream &stream, ImageRGB &img); + + +// Encoded png data buffer: a simple read-only buffer and its size. +struct ReadBuf { const void *buf = nullptr; const size_t sz = 0; }; + bool is_png(const ReadBuf &pngbuf); -// Only decodes true 8 bit grayscale png images. Returns false for other formats -// TODO: implement transformation of rgb images into grayscale... -bool decode_png(const ReadBuf &pngbuf, ImageGreyscale &img); +template bool decode_png(const ReadBuf &in_buf, Img &out_img) +{ + struct ReadBufStream: public IStream { + const ReadBuf &rbuf_ref; size_t pos = 0; -// TODO -// bool decode_png(Buffer &&pngbuf, ImageRGB &img); + explicit ReadBufStream(const ReadBuf &buf): rbuf_ref{buf} {} + + size_t read(std::uint8_t *outp, size_t amount) override + { + if (amount > rbuf_ref.sz - pos) return 0; + + auto buf = static_cast(rbuf_ref.buf); + std::copy(buf + pos, buf + (pos + amount), outp); + pos += amount; + + return amount; + } + + bool is_ok() const override { return pos < rbuf_ref.sz; } + } stream{in_buf}; + + return decode_png(stream, out_img); +} + +// TODO: std::istream of FILE* could be similarly adapted in case its needed... + +}} // namespace Slic3r::png -}} #endif // PNGREAD_HPP From 93921dc7c8d975f2155bfc9d60a7c78bf8bf2745 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 28 Aug 2020 08:54:58 +0200 Subject: [PATCH 357/503] ENABLE_GCODE_VIEWER -> Experimental taskbar icon --- src/libslic3r/Technologies.hpp | 1 + src/slic3r/GUI/MainFrame.cpp | 38 ++++++++++++++++++++++++++++++++++ src/slic3r/GUI/MainFrame.hpp | 11 ++++++++++ 3 files changed, 50 insertions(+) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index af08d16401..8f5ec121aa 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -58,6 +58,7 @@ #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) #define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_DATA_CHECKING (0 && ENABLE_GCODE_VIEWER) +#define ENABLE_GCODE_VIEWER_TASKBAR_ICON (1 && ENABLE_GCODE_VIEWER) #define TIME_ESTIMATE_NONE 0 #define TIME_ESTIMATE_DEFAULT 1 diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index e46bd03fc2..aadaeece66 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -93,6 +93,28 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S // Font is already set in DPIFrame constructor */ +#if ENABLE_GCODE_VIEWER_TASKBAR_ICON + if (wxTaskBarIcon::IsAvailable()) { +#if defined(__WXOSX__) && wxOSX_USE_COCOA + m_taskbar_icon = new wxTaskBarIcon(wxTBI_DOCK); +#else + m_taskbar_icon = new wxTaskBarIcon(); +#endif + m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG), "PrusaSlicer"); + + m_taskbar_icon->Bind(wxEVT_TASKBAR_CLICK, [this](wxTaskBarIconEvent& evt) { + wxString msg = _L("You pressed the icon in taskbar for ") + "\n"; + if (m_mode == EMode::Editor) + msg += wxString(SLIC3R_APP_NAME); + else + msg += wxString(SLIC3R_APP_NAME) + "-GCode viewer"; + + wxMessageDialog dialog(nullptr, msg, _("Taskbar icon clicked"), wxOK); + dialog.ShowModal(); + }); + } +#endif // ENABLE_GCODE_VIEWER_TASKBAR_ICON + SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); // // Load the icon either from the exe, or from the ico file. //#if _WIN32 @@ -255,6 +277,14 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S } } +#if ENABLE_GCODE_VIEWER_TASKBAR_ICON +MainFrame::~MainFrame() +{ + if (m_taskbar_icon != nullptr) + delete m_taskbar_icon; +} +#endif // ENABLE_GCODE_VIEWER_TASKBAR_ICON + void MainFrame::update_layout() { auto restore_to_creation = [this]() { @@ -1388,6 +1418,10 @@ void MainFrame::set_mode(EMode mode) m_plater->Thaw(); SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); +#if ENABLE_GCODE_VIEWER_TASKBAR_ICON + if (m_taskbar_icon != nullptr) + m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG), "PrusaSlicer"); +#endif // ENABLE_GCODE_VIEWER_TASKBAR_ICON break; } @@ -1435,6 +1469,10 @@ void MainFrame::set_mode(EMode mode) m_plater->Thaw(); SetIcon(wxIcon(Slic3r::var("PrusaSlicerGCodeViewer_128px.png"), wxBITMAP_TYPE_PNG)); +#if ENABLE_GCODE_VIEWER_TASKBAR_ICON + if (m_taskbar_icon != nullptr) + m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG), "PrusaSlicer-GCode viewer"); +#endif // ENABLE_GCODE_VIEWER_TASKBAR_ICON break; } diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 53d8488768..7777a053d2 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -7,6 +7,9 @@ #include #include #include +#if ENABLE_GCODE_VIEWER_TASKBAR_ICON +#include +#endif // ENABLE_GCODE_VIEWER_TASKBAR_ICON #include #include @@ -160,7 +163,11 @@ protected: public: MainFrame(); +#if ENABLE_GCODE_VIEWER_TASKBAR_ICON + ~MainFrame(); +#else ~MainFrame() = default; +#endif // ENABLE_GCODE_VIEWER_TASKBAR_ICON void update_layout(); @@ -219,6 +226,10 @@ public: wxProgressDialog* m_progress_dialog { nullptr }; std::shared_ptr m_statusbar; +#if ENABLE_GCODE_VIEWER_TASKBAR_ICON + wxTaskBarIcon* m_taskbar_icon{ nullptr }; +#endif // ENABLE_GCODE_VIEWER_TASKBAR_ICON + #ifdef _WIN32 void* m_hDeviceNotify { nullptr }; uint32_t m_ulSHChangeNotifyRegister { 0 }; From d07d5e36de2e496edfc11200a355bf793365beee Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 28 Aug 2020 11:20:18 +0200 Subject: [PATCH 358/503] Follow-up of 93921dc7c8d975f2155bfc9d60a7c78bf8bf2745 -> Remove taskbar icon before to change it --- src/slic3r/GUI/MainFrame.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index aadaeece66..886e96e1a0 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -280,8 +280,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S #if ENABLE_GCODE_VIEWER_TASKBAR_ICON MainFrame::~MainFrame() { - if (m_taskbar_icon != nullptr) - delete m_taskbar_icon; + delete m_taskbar_icon; } #endif // ENABLE_GCODE_VIEWER_TASKBAR_ICON @@ -1419,8 +1418,10 @@ void MainFrame::set_mode(EMode mode) SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); #if ENABLE_GCODE_VIEWER_TASKBAR_ICON - if (m_taskbar_icon != nullptr) + if (m_taskbar_icon != nullptr) { + m_taskbar_icon->RemoveIcon(); m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG), "PrusaSlicer"); + } #endif // ENABLE_GCODE_VIEWER_TASKBAR_ICON break; @@ -1470,8 +1471,10 @@ void MainFrame::set_mode(EMode mode) SetIcon(wxIcon(Slic3r::var("PrusaSlicerGCodeViewer_128px.png"), wxBITMAP_TYPE_PNG)); #if ENABLE_GCODE_VIEWER_TASKBAR_ICON - if (m_taskbar_icon != nullptr) + if (m_taskbar_icon != nullptr) { + m_taskbar_icon->RemoveIcon(); m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG), "PrusaSlicer-GCode viewer"); + } #endif // ENABLE_GCODE_VIEWER_TASKBAR_ICON break; From 1c2ef87cfa088adc37119118f9014d6174bc355f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 28 Aug 2020 12:28:21 +0200 Subject: [PATCH 359/503] GCodeViewer -> Reduced vertices count when generating solid toolpaths --- src/slic3r/GUI/GCodeViewer.cpp | 86 +++++++++++++++++++++++----------- src/slic3r/GUI/GCodeViewer.hpp | 2 + 2 files changed, 61 insertions(+), 27 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 8cb18f4e18..c624fb338e 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -994,37 +994,69 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) Vec3f prev_pos = prev.position - half_height * up; Vec3f curr_pos = curr.position - half_height * up; - // vertices 1st endpoint - store_vertex(buffer_vertices, prev_pos + half_height * up, up); // top - store_vertex(buffer_vertices, prev_pos + half_width * right, right); // right - store_vertex(buffer_vertices, prev_pos - half_height * up, -up); // bottom - store_vertex(buffer_vertices, prev_pos - half_width * right, -right); // left + Path& last_path = buffer.paths.back(); + if (last_path.vertices_count() == 1) { + // vertices 1st endpoint + store_vertex(buffer_vertices, prev_pos + half_height * up, up); // top + store_vertex(buffer_vertices, prev_pos + half_width * right, right); // right + store_vertex(buffer_vertices, prev_pos - half_height * up, -up); // bottom + store_vertex(buffer_vertices, prev_pos - half_width * right, -right); // left - // vertices 2nd endpoint - store_vertex(buffer_vertices, curr_pos + half_height * up, up); // top - store_vertex(buffer_vertices, curr_pos + half_width * right, right); // right - store_vertex(buffer_vertices, curr_pos - half_height * up, -up); // bottom - store_vertex(buffer_vertices, curr_pos - half_width * right, -right); // left + // vertices 2nd endpoint + store_vertex(buffer_vertices, curr_pos + half_height * up, up); // top + store_vertex(buffer_vertices, curr_pos + half_width * right, right); // right + store_vertex(buffer_vertices, curr_pos - half_height * up, -up); // bottom + store_vertex(buffer_vertices, curr_pos - half_width * right, -right); // left - // triangles starting cap - store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size + 2, starting_vertices_size + 1); - store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size + 3, starting_vertices_size + 2); + // triangles starting cap + store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size + 2, starting_vertices_size + 1); + store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size + 3, starting_vertices_size + 2); - // triangles sides - store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size + 1, starting_vertices_size + 4); - store_triangle(buffer_indices, starting_vertices_size + 1, starting_vertices_size + 5, starting_vertices_size + 4); - store_triangle(buffer_indices, starting_vertices_size + 1, starting_vertices_size + 2, starting_vertices_size + 5); - store_triangle(buffer_indices, starting_vertices_size + 2, starting_vertices_size + 6, starting_vertices_size + 5); - store_triangle(buffer_indices, starting_vertices_size + 2, starting_vertices_size + 3, starting_vertices_size + 6); - store_triangle(buffer_indices, starting_vertices_size + 3, starting_vertices_size + 7, starting_vertices_size + 6); - store_triangle(buffer_indices, starting_vertices_size + 3, starting_vertices_size + 0, starting_vertices_size + 7); - store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size + 4, starting_vertices_size + 7); + // triangles sides + store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size + 1, starting_vertices_size + 4); + store_triangle(buffer_indices, starting_vertices_size + 1, starting_vertices_size + 5, starting_vertices_size + 4); + store_triangle(buffer_indices, starting_vertices_size + 1, starting_vertices_size + 2, starting_vertices_size + 5); + store_triangle(buffer_indices, starting_vertices_size + 2, starting_vertices_size + 6, starting_vertices_size + 5); + store_triangle(buffer_indices, starting_vertices_size + 2, starting_vertices_size + 3, starting_vertices_size + 6); + store_triangle(buffer_indices, starting_vertices_size + 3, starting_vertices_size + 7, starting_vertices_size + 6); + store_triangle(buffer_indices, starting_vertices_size + 3, starting_vertices_size + 0, starting_vertices_size + 7); + store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size + 4, starting_vertices_size + 7); - // triangles ending cap - store_triangle(buffer_indices, starting_vertices_size + 4, starting_vertices_size + 6, starting_vertices_size + 7); - store_triangle(buffer_indices, starting_vertices_size + 4, starting_vertices_size + 5, starting_vertices_size + 6); + // triangles ending cap + store_triangle(buffer_indices, starting_vertices_size + 4, starting_vertices_size + 6, starting_vertices_size + 7); + store_triangle(buffer_indices, starting_vertices_size + 4, starting_vertices_size + 5, starting_vertices_size + 6); + } + else { + // vertices 1st endpoint + store_vertex(buffer_vertices, prev_pos + half_width * right, right); // right + store_vertex(buffer_vertices, prev_pos - half_width * right, -right); // left - buffer.paths.back().last = { static_cast(buffer_indices.size() - 1), static_cast(move_id), curr.position }; + // vertices 2nd endpoint + store_vertex(buffer_vertices, curr_pos + half_height * up, up); // top + store_vertex(buffer_vertices, curr_pos + half_width * right, right); // right + store_vertex(buffer_vertices, curr_pos - half_height * up, -up); // bottom + store_vertex(buffer_vertices, curr_pos - half_width * right, -right); // left + + // triangles starting cap + store_triangle(buffer_indices, starting_vertices_size - 4, starting_vertices_size - 2, starting_vertices_size + 0); + store_triangle(buffer_indices, starting_vertices_size - 4, starting_vertices_size + 1, starting_vertices_size - 2); + + // triangles sides + store_triangle(buffer_indices, starting_vertices_size - 4, starting_vertices_size + 0, starting_vertices_size + 2); + store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size + 3, starting_vertices_size + 2); + store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size - 2, starting_vertices_size + 3); + store_triangle(buffer_indices, starting_vertices_size - 2, starting_vertices_size + 4, starting_vertices_size + 3); + store_triangle(buffer_indices, starting_vertices_size - 2, starting_vertices_size + 1, starting_vertices_size + 4); + store_triangle(buffer_indices, starting_vertices_size + 1, starting_vertices_size + 5, starting_vertices_size + 4); + store_triangle(buffer_indices, starting_vertices_size + 1, starting_vertices_size - 4, starting_vertices_size + 5); + store_triangle(buffer_indices, starting_vertices_size - 4, starting_vertices_size + 2, starting_vertices_size + 5); + + // triangles ending cap + store_triangle(buffer_indices, starting_vertices_size + 2, starting_vertices_size + 4, starting_vertices_size + 5); + store_triangle(buffer_indices, starting_vertices_size + 2, starting_vertices_size + 3, starting_vertices_size + 4); + } + + last_path.last = { static_cast(buffer_indices.size() - 1), static_cast(move_id), curr.position }; }; // toolpaths data -> extract from result @@ -1299,7 +1331,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool for (const TBuffer& buffer : m_buffers) { // searches the path containing the current position for (const Path& path : buffer.paths) { - if (path.first.s_id <= m_sequential_view.current.last && m_sequential_view.current.last <= path.last.s_id) { + if (path.contains(m_sequential_view.current.last)) { unsigned int offset = m_sequential_view.current.last - path.first.s_id; if (offset > 0) { if (buffer.primitive_type == TBuffer::EPrimitiveType::Line) diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 55c8ea1993..808c1a8ee0 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -133,6 +133,8 @@ class GCodeViewer unsigned char cp_color_id{ 0 }; bool matches(const GCodeProcessor::MoveVertex& move) const; + size_t vertices_count() const { return last.s_id - first.s_id + 1; } + bool contains(unsigned int id) const { return first.s_id <= id && id <= last.s_id; } }; // Used to batch the indices needed to render paths From 8e6760e03332290b76abc5a0fa38f3f3d64ff016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Sun, 30 Aug 2020 20:38:07 +0200 Subject: [PATCH 360/503] Fix crash on inconsistent input --- src/libslic3r/Fill/FillAdaptive.cpp | 2 +- src/libslic3r/PrintObject.cpp | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index a3068989ef..0563b612ab 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -149,7 +149,7 @@ std::unique_ptr FillAdaptive::build_octree( { using namespace FillAdaptive_Internal; - if(line_spacing <= 0) + if(line_spacing <= 0 || std::isnan(line_spacing)) { return nullptr; } diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 5752452ad7..b1b5d3a7b7 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -434,8 +434,16 @@ void PrintObject::generate_support_material() void PrintObject::prepare_adaptive_infill_data() { - float fill_density = this->print()->full_print_config().opt_float("fill_density"); - float infill_extrusion_width = this->print()->full_print_config().opt_float("infill_extrusion_width"); + const ConfigOptionFloatOrPercent* opt_fill_density = this->print()->full_print_config().option("fill_density"); + const ConfigOptionFloatOrPercent* opt_infill_extrusion_width = this->print()->full_print_config().option("infill_extrusion_width"); + + if(opt_fill_density == nullptr || opt_infill_extrusion_width == nullptr || opt_fill_density->value <= 0 || opt_infill_extrusion_width->value <= 0) + { + return; + } + + float fill_density = opt_fill_density->value; + float infill_extrusion_width = opt_infill_extrusion_width->value; coordf_t line_spacing = infill_extrusion_width / ((fill_density / 100.0f) * 0.333333333f); From 423d1f2f4013e7f01cec40c23b1e182b678e2fdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 31 Aug 2020 08:49:17 +0200 Subject: [PATCH 361/503] Fix wrong data type --- src/libslic3r/PrintObject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index b1b5d3a7b7..1ab5664a0f 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -434,7 +434,7 @@ void PrintObject::generate_support_material() void PrintObject::prepare_adaptive_infill_data() { - const ConfigOptionFloatOrPercent* opt_fill_density = this->print()->full_print_config().option("fill_density"); + const ConfigOptionPercent* opt_fill_density = this->print()->full_print_config().option("fill_density"); const ConfigOptionFloatOrPercent* opt_infill_extrusion_width = this->print()->full_print_config().option("infill_extrusion_width"); if(opt_fill_density == nullptr || opt_infill_extrusion_width == nullptr || opt_fill_density->value <= 0 || opt_infill_extrusion_width->value <= 0) From bf7b952eff82a0fe3805cf3bb84d3b9bc5c636e3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 1 Sep 2020 08:29:06 +0200 Subject: [PATCH 362/503] GCodeViewer -> Smoothed solid toolpaths corners --- src/libslic3r/Technologies.hpp | 2 +- src/slic3r/GUI/GCodeViewer.cpp | 214 +++++++++++++++++++++++++-------- src/slic3r/GUI/GCodeViewer.hpp | 13 +- 3 files changed, 175 insertions(+), 54 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 8f5ec121aa..834cebbe45 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -58,7 +58,7 @@ #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) #define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_DATA_CHECKING (0 && ENABLE_GCODE_VIEWER) -#define ENABLE_GCODE_VIEWER_TASKBAR_ICON (1 && ENABLE_GCODE_VIEWER) +#define ENABLE_GCODE_VIEWER_TASKBAR_ICON (0 && ENABLE_GCODE_VIEWER) #define TIME_ESTIMATE_NONE 0 #define TIME_ESTIMATE_DEFAULT 1 diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index c624fb338e..8853d7a754 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -297,19 +297,19 @@ bool GCodeViewer::init() case EMoveType::Retract: case EMoveType::Unretract: { - buffer.primitive_type = TBuffer::EPrimitiveType::Point; + buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Point; buffer.vertices.format = VBuffer::EFormat::Position; break; } case EMoveType::Extrude: { - buffer.primitive_type = TBuffer::EPrimitiveType::Triangle; + buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Triangle; buffer.vertices.format = VBuffer::EFormat::PositionNormal3; break; } case EMoveType::Travel: { - buffer.primitive_type = TBuffer::EPrimitiveType::Line; + buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Line; buffer.vertices.format = VBuffer::EFormat::PositionNormal1; break; } @@ -397,6 +397,8 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: if (m_vertices_count == 0) return; + wxBusyCursor busy; + if (m_view_type == EViewType::Tool && !gcode_result.extruder_colors.empty()) // update tool colors from config stored in the gcode m_tool_colors = decode_colors(gcode_result.extruder_colors); @@ -961,6 +963,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) // format data into the buffers to be rendered as solid auto add_as_solid = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer, std::vector& buffer_vertices, std::vector& buffer_indices, size_t move_id) { + static Vec3f prev_dir; + static Vec3f prev_up; + static float prev_length; auto store_vertex = [](std::vector& buffer_vertices, const Vec3f& position, const Vec3f& normal) { // append position for (int j = 0; j < 3; ++j) { @@ -976,6 +981,18 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) buffer_indices.push_back(i2); buffer_indices.push_back(i3); }; + auto extract_position_at = [](const std::vector& vertices, size_t id) { + return Vec3f(vertices[id + 0], vertices[id + 1], vertices[id + 2]); + }; + auto update_position_at = [](std::vector& vertices, size_t id, const Vec3f& position) { + vertices[id + 0] = position[0]; + vertices[id + 1] = position[1]; + vertices[id + 2] = position[2]; + }; + auto append_dummy_cap = [store_triangle](std::vector& buffer_indices, unsigned int id) { + store_triangle(buffer_indices, id, id, id); + store_triangle(buffer_indices, id, id, id); + }; if (prev.type != curr.type || !buffer.paths.back().matches(curr)) { buffer.add_path(curr, static_cast(buffer_indices.size()), static_cast(move_id - 1)); @@ -986,32 +1003,41 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) Vec3f dir = (curr.position - prev.position).normalized(); Vec3f right = (std::abs(std::abs(dir.dot(Vec3f::UnitZ())) - 1.0f) < EPSILON) ? -Vec3f::UnitY() : Vec3f(dir[1], -dir[0], 0.0f).normalized(); + Vec3f left = -right; Vec3f up = right.cross(dir); + Vec3f bottom = -up; - float half_width = 0.5f * round_to_nearest(curr.width, 2); - float half_height = 0.5f * round_to_nearest(curr.height, 2); + Path& last_path = buffer.paths.back(); + + float half_width = 0.5f * last_path.width; + float half_height = 0.5f * last_path.height; Vec3f prev_pos = prev.position - half_height * up; Vec3f curr_pos = curr.position - half_height * up; - Path& last_path = buffer.paths.back(); + float length = (curr_pos - prev_pos).norm(); if (last_path.vertices_count() == 1) { + // 1st segment + // vertices 1st endpoint - store_vertex(buffer_vertices, prev_pos + half_height * up, up); // top - store_vertex(buffer_vertices, prev_pos + half_width * right, right); // right - store_vertex(buffer_vertices, prev_pos - half_height * up, -up); // bottom - store_vertex(buffer_vertices, prev_pos - half_width * right, -right); // left + store_vertex(buffer_vertices, prev_pos + half_height * up, up); + store_vertex(buffer_vertices, prev_pos + half_width * right, right); + store_vertex(buffer_vertices, prev_pos + half_height * bottom, bottom); + store_vertex(buffer_vertices, prev_pos + half_width * left, left); // vertices 2nd endpoint - store_vertex(buffer_vertices, curr_pos + half_height * up, up); // top - store_vertex(buffer_vertices, curr_pos + half_width * right, right); // right - store_vertex(buffer_vertices, curr_pos - half_height * up, -up); // bottom - store_vertex(buffer_vertices, curr_pos - half_width * right, -right); // left + store_vertex(buffer_vertices, curr_pos + half_height * up, up); + store_vertex(buffer_vertices, curr_pos + half_width * right, right); + store_vertex(buffer_vertices, curr_pos + half_height * bottom, bottom); + store_vertex(buffer_vertices, curr_pos + half_width * left, left); // triangles starting cap store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size + 2, starting_vertices_size + 1); store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size + 3, starting_vertices_size + 2); + // dummy triangles outer corner cap + append_dummy_cap(buffer_indices, starting_vertices_size); + // triangles sides store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size + 1, starting_vertices_size + 4); store_triangle(buffer_indices, starting_vertices_size + 1, starting_vertices_size + 5, starting_vertices_size + 4); @@ -1027,20 +1053,101 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) store_triangle(buffer_indices, starting_vertices_size + 4, starting_vertices_size + 5, starting_vertices_size + 6); } else { - // vertices 1st endpoint - store_vertex(buffer_vertices, prev_pos + half_width * right, right); // right - store_vertex(buffer_vertices, prev_pos - half_width * right, -right); // left + // any other segment + Vec3f med_dir = (prev_dir + dir).normalized(); + float displacement = half_width * ::tan(::acos(std::clamp(dir.dot(med_dir), -1.0f, 1.0f))); + Vec3f displacement_vec = displacement * prev_dir; + bool can_displace = displacement < prev_length && displacement < length; + + size_t prev_right_id = (starting_vertices_size - 3) * buffer.vertices.vertex_size_floats(); + size_t prev_left_id = (starting_vertices_size - 1) * buffer.vertices.vertex_size_floats(); + Vec3f prev_right_pos = extract_position_at(buffer_vertices, prev_right_id); + Vec3f prev_left_pos = extract_position_at(buffer_vertices, prev_left_id); + + bool is_right_turn = prev_up.dot(prev_dir.cross(dir)) <= 0.0f; + // whether the angle between adjacent segments is greater than 45 degrees + bool is_sharp = prev_dir.dot(dir) < 0.7071068f; + + bool right_displaced = false; + bool left_displaced = false; + + // displace the vertex (inner with respect to the corner) of the previous segment 2nd enpoint, if possible + if (can_displace) { + if (is_right_turn) { + prev_right_pos -= displacement_vec; + update_position_at(buffer_vertices, prev_right_id, prev_right_pos); + right_displaced = true; + } + else { + prev_left_pos -= displacement_vec; + update_position_at(buffer_vertices, prev_left_id, prev_left_pos); + left_displaced = true; + } + } + + if (!is_sharp) { + // displace the vertex (outer with respect to the corner) of the previous segment 2nd enpoint, if possible + if (can_displace) { + if (is_right_turn) { + prev_left_pos += displacement_vec; + update_position_at(buffer_vertices, prev_left_id, prev_left_pos); + left_displaced = true; + } + else { + prev_right_pos += displacement_vec; + update_position_at(buffer_vertices, prev_right_id, prev_right_pos); + right_displaced = true; + } + } + + // vertices 1st endpoint (top and bottom are from previous segment 2nd endpoint) + // vertices position matches that of the previous segment 2nd endpoint, if displaced + store_vertex(buffer_vertices, right_displaced ? prev_right_pos : prev_pos + half_width * right, right); + store_vertex(buffer_vertices, left_displaced ? prev_left_pos : prev_pos + half_width * left, left); + } + else { + // vertices 1st endpoint (top and bottom are from previous segment 2nd endpoint) + // the inner corner vertex position matches that of the previous segment 2nd endpoint, if displaced + if (is_right_turn) { + store_vertex(buffer_vertices, right_displaced ? prev_right_pos : prev_pos + half_width * right, right); + store_vertex(buffer_vertices, prev_pos + half_width * left, left); + } + else { + store_vertex(buffer_vertices, prev_pos + half_width * right, right); + store_vertex(buffer_vertices, left_displaced ? prev_left_pos : prev_pos + half_width * left, left); + } + } // vertices 2nd endpoint - store_vertex(buffer_vertices, curr_pos + half_height * up, up); // top - store_vertex(buffer_vertices, curr_pos + half_width * right, right); // right - store_vertex(buffer_vertices, curr_pos - half_height * up, -up); // bottom - store_vertex(buffer_vertices, curr_pos - half_width * right, -right); // left + store_vertex(buffer_vertices, curr_pos + half_height * up, up); + store_vertex(buffer_vertices, curr_pos + half_width * right, right); + store_vertex(buffer_vertices, curr_pos + half_height * bottom, bottom); + store_vertex(buffer_vertices, curr_pos + half_width * left, left); // triangles starting cap store_triangle(buffer_indices, starting_vertices_size - 4, starting_vertices_size - 2, starting_vertices_size + 0); store_triangle(buffer_indices, starting_vertices_size - 4, starting_vertices_size + 1, starting_vertices_size - 2); + // triangles outer corner cap + if (is_right_turn) { + if (left_displaced) + // dummy triangles + append_dummy_cap(buffer_indices, starting_vertices_size); + else { + store_triangle(buffer_indices, starting_vertices_size - 4, starting_vertices_size + 1, starting_vertices_size - 1); + store_triangle(buffer_indices, starting_vertices_size + 1, starting_vertices_size - 2, starting_vertices_size - 1); + } + } + else { + if (right_displaced) + // dummy triangles + append_dummy_cap(buffer_indices, starting_vertices_size); + else { + store_triangle(buffer_indices, starting_vertices_size - 4, starting_vertices_size - 3, starting_vertices_size + 0); + store_triangle(buffer_indices, starting_vertices_size - 3, starting_vertices_size - 2, starting_vertices_size + 0); + } + } + // triangles sides store_triangle(buffer_indices, starting_vertices_size - 4, starting_vertices_size + 0, starting_vertices_size + 2); store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size + 3, starting_vertices_size + 2); @@ -1057,6 +1164,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) } last_path.last = { static_cast(buffer_indices.size() - 1), static_cast(move_id), curr.position }; + prev_dir = dir; + prev_up = up; + prev_length = length; }; // toolpaths data -> extract from result @@ -1137,20 +1247,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) m_statistics.paths_size += SLIC3R_STDVEC_MEMSIZE(buffer.paths, Path); } unsigned int travel_buffer_id = buffer_id(EMoveType::Travel); - switch (m_buffers[travel_buffer_id].primitive_type) - { - case TBuffer::EPrimitiveType::Line: { m_statistics.travel_segments_count = indices[travel_buffer_id].size() / 2; break; } - case TBuffer::EPrimitiveType::Triangle: { m_statistics.travel_segments_count = indices[travel_buffer_id].size() / 36; break; } - default: { break; } - } - + m_statistics.travel_segments_count = indices[travel_buffer_id].size() / m_buffers[travel_buffer_id].indices_per_segment(); unsigned int extrude_buffer_id = buffer_id(EMoveType::Extrude); - switch (m_buffers[extrude_buffer_id].primitive_type) - { - case TBuffer::EPrimitiveType::Line: { m_statistics.extrude_segments_count = indices[extrude_buffer_id].size() / 2; break; } - case TBuffer::EPrimitiveType::Triangle: { m_statistics.extrude_segments_count = indices[extrude_buffer_id].size() / 36; break; } - default: { break; } - } + m_statistics.extrude_segments_count = indices[extrude_buffer_id].size() / m_buffers[extrude_buffer_id].indices_per_segment(); #endif // ENABLE_GCODE_VIEWER_STATISTICS // layers zs / roles / extruder ids / cp color ids -> extract from result @@ -1334,10 +1433,12 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool if (path.contains(m_sequential_view.current.last)) { unsigned int offset = m_sequential_view.current.last - path.first.s_id; if (offset > 0) { - if (buffer.primitive_type == TBuffer::EPrimitiveType::Line) + if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Line) offset = 2 * offset - 1; - else if (buffer.primitive_type == TBuffer::EPrimitiveType::Triangle) - offset = 36 * (offset - 1) + 30; + else if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) { + unsigned int indices_count = buffer.indices_per_segment(); + offset = indices_count * (offset - 1) + (indices_count - 6); + } } offset += path.first.i_id; @@ -1382,11 +1483,11 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool unsigned int size_in_vertices = std::min(m_sequential_view.current.last, path.last.s_id) - std::max(m_sequential_view.current.first, path.first.s_id) + 1; unsigned int size_in_indices = 0; - switch (buffer->primitive_type) + switch (buffer->render_primitive_type) { - case TBuffer::EPrimitiveType::Point: { size_in_indices = size_in_vertices; break; } - case TBuffer::EPrimitiveType::Line: { size_in_indices = 2 * (size_in_vertices - 1); break; } - case TBuffer::EPrimitiveType::Triangle: { size_in_indices = 36 * (size_in_vertices - 1); break; } + case TBuffer::ERenderPrimitiveType::Point: { size_in_indices = size_in_vertices; break; } + case TBuffer::ERenderPrimitiveType::Line: + case TBuffer::ERenderPrimitiveType::Triangle: { size_in_indices = buffer->indices_per_segment() * (size_in_vertices - 1); break; } } it->sizes.push_back(size_in_indices); @@ -1394,8 +1495,8 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool if (path.first.s_id < m_sequential_view.current.first && m_sequential_view.current.first <= path.last.s_id) delta_1st = m_sequential_view.current.first - path.first.s_id; - if (buffer->primitive_type == TBuffer::EPrimitiveType::Triangle) - delta_1st *= 36; + if (buffer->render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) + delta_1st *= buffer->indices_per_segment(); it->offsets.push_back(static_cast((path.first.i_id + delta_1st) * sizeof(unsigned int))); } @@ -1502,9 +1603,9 @@ void GCodeViewer::render_toolpaths() const glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.indices.id)); - switch (buffer.primitive_type) + switch (buffer.render_primitive_type) { - case TBuffer::EPrimitiveType::Point: + case TBuffer::ERenderPrimitiveType::Point: { EOptionsColors color; switch (buffer_type(i)) @@ -1519,12 +1620,12 @@ void GCodeViewer::render_toolpaths() const render_as_points(buffer, color, *shader); break; } - case TBuffer::EPrimitiveType::Line: + case TBuffer::ERenderPrimitiveType::Line: { render_as_lines(buffer, *shader); break; } - case TBuffer::EPrimitiveType::Triangle: + case TBuffer::ERenderPrimitiveType::Triangle: { render_as_triangles(buffer, *shader); break; @@ -1819,7 +1920,19 @@ void GCodeViewer::render_legend() const if (time_mode.time > 0.0f && (m_view_type == EViewType::FeatureType || (m_view_type == EViewType::ColorPrint && !time_mode.custom_gcode_times.empty()))) { ImGui::AlignTextToFramePadding(); - imgui.text(_u8L("Estimated printing time") + ":"); + switch (m_time_estimate_mode) + { + case PrintEstimatedTimeStatistics::ETimeMode::Normal: + { + imgui.text(_u8L("Estimated printing time") + " [" + _u8L("Normal mode") + "]:"); + break; + } + case PrintEstimatedTimeStatistics::ETimeMode::Stealth: + { + imgui.text(_u8L("Estimated printing time") + " [" + _u8L("Stealth mode") + "]:"); + break; + } + } ImGui::SameLine(); imgui.text(short_time(get_time_dhms(time_mode.time))); @@ -1833,7 +1946,6 @@ void GCodeViewer::render_legend() const } } if (show && m_time_statistics.modes[static_cast(mode)].roles_times.size() > 0) { - ImGui::SameLine(0.0f, 10.0f); if (imgui.button(label)) { m_time_estimate_mode = mode; wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); @@ -1846,12 +1958,12 @@ void GCodeViewer::render_legend() const { case PrintEstimatedTimeStatistics::ETimeMode::Normal: { - show_mode_button(_u8L("Stealth mode"), PrintEstimatedTimeStatistics::ETimeMode::Stealth); + show_mode_button(_u8L("Show stealth mode"), PrintEstimatedTimeStatistics::ETimeMode::Stealth); break; } case PrintEstimatedTimeStatistics::ETimeMode::Stealth: { - show_mode_button(_u8L("Normal mode"), PrintEstimatedTimeStatistics::ETimeMode::Normal); + show_mode_button(_u8L("Show normal mode"), PrintEstimatedTimeStatistics::ETimeMode::Normal); break; } } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 808c1a8ee0..e49a1f08bf 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -149,14 +149,14 @@ class GCodeViewer // buffer containing data for rendering a specific toolpath type struct TBuffer { - enum class EPrimitiveType : unsigned char + enum class ERenderPrimitiveType : unsigned char { Point, Line, Triangle }; - EPrimitiveType primitive_type; + ERenderPrimitiveType render_primitive_type; VBuffer vertices; IBuffer indices; @@ -167,6 +167,15 @@ class GCodeViewer void reset(); void add_path(const GCodeProcessor::MoveVertex& move, unsigned int i_id, unsigned int s_id); + unsigned int indices_per_segment() const { + switch (render_primitive_type) + { + case ERenderPrimitiveType::Point: { return 1; } + case ERenderPrimitiveType::Line: { return 2; } + case ERenderPrimitiveType::Triangle: { return 42; } // 3 indices x 14 triangles + default: { return 0; } + } + } }; // helper to render shells From e32930aa6c5008a86323a6d2c9a7d6a4a67be930 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 1 Sep 2020 09:28:02 +0200 Subject: [PATCH 363/503] Code cleanup --- src/libslic3r/Technologies.hpp | 6 - src/slic3r/GUI/GCodeViewer.cpp | 439 +-------------------------- src/slic3r/GUI/GCodeViewer.hpp | 18 -- src/slic3r/GUI/GLCanvas3D.cpp | 12 - src/slic3r/GUI/GLCanvas3D.hpp | 3 - src/slic3r/GUI/GUI_Preview.cpp | 14 +- src/slic3r/GUI/GUI_Preview.hpp | 5 +- src/slic3r/GUI/KBShortcutsDialog.cpp | 9 - 8 files changed, 3 insertions(+), 503 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 834cebbe45..a0484b259c 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -60,10 +60,4 @@ #define ENABLE_GCODE_VIEWER_DATA_CHECKING (0 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_TASKBAR_ICON (0 && ENABLE_GCODE_VIEWER) -#define TIME_ESTIMATE_NONE 0 -#define TIME_ESTIMATE_DEFAULT 1 -#define TIME_ESTIMATE_MODAL 2 -#define TIME_ESTIMATE_LEGEND 3 -#define GCODE_VIEWER_TIME_ESTIMATE TIME_ESTIMATE_LEGEND - #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 8853d7a754..24ed5be9b4 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -383,9 +383,7 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& wxGetApp().plater()->set_bed_shape(bed_shape, texture, model, gcode_result.bed_shape.empty()); } -#if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE m_time_statistics = gcode_result.time_statistics; -#endif // GCODE_VIEWER_TIME_ESTIMATE } void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std::vector& str_tool_colors) @@ -461,12 +459,8 @@ void GCodeViewer::reset() m_layers_zs = std::vector(); m_layers_z_range = { 0.0, 0.0 }; m_roles = std::vector(); -#if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE m_time_statistics.reset(); -#endif // GCODE_VIEWER_TIME_ESTIMATE -#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND m_time_estimate_mode = PrintEstimatedTimeStatistics::ETimeMode::Normal; -#endif // GCODE_VIEWER_TIME_ESTIMATE #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.reset_all(); @@ -479,15 +473,8 @@ void GCodeViewer::render() const m_statistics.reset_opengl(); #endif // ENABLE_GCODE_VIEWER_STATISTICS -#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_MODAL - if (m_roles.empty()) { - m_time_estimate_frames_count = 0; - return; - } -#else if (m_roles.empty()) return; -#endif // GCODE_VIEWER_TIME_ESTIMATE glsafe(::glEnable(GL_DEPTH_TEST)); render_toolpaths(); @@ -495,9 +482,6 @@ void GCodeViewer::render() const m_sequential_view.marker.render(); render_shells(); render_legend(); -#if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE - render_time_estimate(); -#endif // GCODE_VIEWER_TIME_ESTIMATE #if ENABLE_GCODE_VIEWER_STATISTICS render_statistics(); #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -533,9 +517,6 @@ unsigned int GCodeViewer::get_options_visibility_flags() const flags = set_flag(flags, static_cast(Preview::OptionType::Shells), m_shells.visible); flags = set_flag(flags, static_cast(Preview::OptionType::ToolMarker), m_sequential_view.marker.is_visible()); flags = set_flag(flags, static_cast(Preview::OptionType::Legend), is_legend_enabled()); -#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_DEFAULT - flags = set_flag(flags, static_cast(Preview::OptionType::TimeEstimate), is_time_estimate_enabled()); -#endif // GCODE_VIEWER_TIME_ESTIMATE return flags; } @@ -555,9 +536,6 @@ void GCodeViewer::set_options_visibility_from_flags(unsigned int flags) m_shells.visible = is_flag_set(static_cast(Preview::OptionType::Shells)); m_sequential_view.marker.set_visible(is_flag_set(static_cast(Preview::OptionType::ToolMarker))); enable_legend(is_flag_set(static_cast(Preview::OptionType::Legend))); -#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_DEFAULT - enable_time_estimate(is_flag_set(static_cast(Preview::OptionType::TimeEstimate))); -#endif // GCODE_VIEWER_TIME_ESTIMATE } void GCodeViewer::set_layers_z_range(const std::array& layers_z_range) @@ -569,16 +547,6 @@ void GCodeViewer::set_layers_z_range(const std::array& layers_z_range wxGetApp().plater()->update_preview_moves_slider(); } -#if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE -void GCodeViewer::enable_time_estimate(bool enable) -{ - m_time_estimate_enabled = enable; - wxGetApp().update_ui_from_settings(); - wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); - wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); -} -#endif // GCODE_VIEWER_TIME_ESTIMATE - void GCodeViewer::export_toolpaths_to_obj(const char* filename) const { if (filename == nullptr) @@ -1685,7 +1653,6 @@ void GCodeViewer::render_legend() const Line }; -#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND const PrintEstimatedTimeStatistics::Mode& time_mode = m_time_statistics.modes[static_cast(m_time_estimate_mode)]; float icon_size = ImGui::GetTextLineHeight(); @@ -1696,10 +1663,6 @@ void GCodeViewer::render_legend() const std::function callback = nullptr) { if (!visible) ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f); -#else - auto append_item = [this, draw_list, &imgui](EItemType type, const Color& color, const std::string& label, std::function callback = nullptr) { - float icon_size = ImGui::GetTextLineHeight(); -#endif // GCODE_VIEWER_TIME_ESTIMATE ImVec2 pos = ImGui::GetCursorScreenPos(); switch (type) { @@ -1745,7 +1708,6 @@ void GCodeViewer::render_legend() const if (callback != nullptr) { if (ImGui::MenuItem(label.c_str())) callback(); -#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND else { // show tooltip if (ImGui::IsItemHovered()) { @@ -1779,15 +1741,12 @@ void GCodeViewer::render_legend() const ::sprintf(buf, "%.1f%%", 100.0f * percent); ImGui::TextUnformatted((percent > 0.0f) ? buf : ""); } -#endif // GCODE_VIEWER_TIME_ESTIMATE } else imgui.text(label); -#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND if (!visible) ImGui::PopStyleVar(); -#endif // GCODE_VIEWER_TIME_ESTIMATE }; auto append_range = [this, draw_list, &imgui, append_item](const Extrusions::Range& range, unsigned int decimals) { @@ -1812,7 +1771,6 @@ void GCodeViewer::render_legend() const } }; -#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND auto append_headers = [&imgui](const std::array& texts, const std::array& offsets) { imgui.text(texts[0]); ImGui::SameLine(offsets[0]); @@ -1838,7 +1796,6 @@ void GCodeViewer::render_legend() const ret[1] = ret[0] + max_width(times, titles[1]) + style.ItemSpacing.x; return ret; }; -#endif // GCODE_VIEWER_TIME_ESTIMATE auto color_print_ranges = [this](unsigned char extruder_id, const std::vector& custom_gcode_per_print_z) { std::vector>> ret; @@ -1887,7 +1844,6 @@ void GCodeViewer::render_legend() const return _u8L("from") + " " + std::string(buf1) + " " + _u8L("to") + " " + std::string(buf2) + " " + _u8L("mm"); }; -#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND auto role_time_and_percent = [this, time_mode](ExtrusionRole role) { auto it = std::find_if(time_mode.roles_times.begin(), time_mode.roles_times.end(), [role](const std::pair& item) { return role == item.first; }); return (it != time_mode.roles_times.end()) ? std::make_pair(it->second, it->second / time_mode.time) : std::make_pair(0.0f, 0.0f); @@ -1969,20 +1925,15 @@ void GCodeViewer::render_legend() const } ImGui::Spacing(); } -#endif // GCODE_VIEWER_TIME_ESTIMATE // extrusion paths section -> title switch (m_view_type) { -#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND case EViewType::FeatureType: { append_headers({ _u8L("Feature type"), _u8L("Time"), _u8L("Percentage") }, offsets); break; } -#else - case EViewType::FeatureType: { imgui.title(_u8L("Feature type")); break; } -#endif // GCODE_VIEWER_TIME_ESTIMATE case EViewType::Height: { imgui.title(_u8L("Height (mm)")); break; } case EViewType::Width: { imgui.title(_u8L("Width (mm)")); break; } case EViewType::Feedrate: { imgui.title(_u8L("Speed (mm/s)")); break; } @@ -2003,28 +1954,15 @@ void GCodeViewer::render_legend() const if (role >= erCount) continue; bool visible = is_visible(role); -#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND append_item(EItemType::Rect, Extrusion_Role_Colors[static_cast(role)], labels[i], visible, times[i], percents[i], max_percent, offsets, [this, role, visible]() { -#else - if (!visible) - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f); - - append_item(EItemType::Rect, Extrusion_Role_Colors[static_cast(role)], _u8L(ExtrusionEntity::role_to_string(role)), - [this, role, visible]() { -#endif // GCODE_VIEWER_TIME_ESTIMATE - m_extrusions.role_visibility_flags = visible ? m_extrusions.role_visibility_flags & ~(1 << role) : m_extrusions.role_visibility_flags | (1 << role); + m_extrusions.role_visibility_flags = visible ? m_extrusions.role_visibility_flags & ~(1 << role) : m_extrusions.role_visibility_flags | (1 << role); // update buffers' render paths refresh_render_paths(false, false); wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); wxGetApp().plater()->update_preview_bottom_toolbar(); } ); - -#if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_LEGEND - if (!visible) - ImGui::PopStyleVar(); -#endif // GCODE_VIEWER_TIME_ESTIMATE } break; } @@ -2102,7 +2040,6 @@ void GCodeViewer::render_legend() const default: { break; } } -#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND // partial estimated printing time section if (m_view_type == EViewType::ColorPrint) { using Times = std::pair; @@ -2185,11 +2122,6 @@ void GCodeViewer::render_legend() const draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, ImGui::GetColorU32({ color2[0], color2[1], color2[2], 1.0f })); -// ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); -// draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color1[0], color1[1], color1[2], 1.0f }), 6); -// center.x += icon_size; -// draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color2[0], color2[1], color2[2], 1.0f }), 6); - ImGui::SameLine(offsets[0]); imgui.text(short_time(get_time_dhms(times.second - times.first))); }; @@ -2240,7 +2172,6 @@ void GCodeViewer::render_legend() const } } } -#endif // GCODE_VIEWER_TIME_ESTIMATE // travel paths section if (m_buffers[buffer_id(EMoveType::Travel)].visible) { @@ -2307,374 +2238,6 @@ void GCodeViewer::render_legend() const ImGui::PopStyleVar(); } -#if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE -void GCodeViewer::render_time_estimate() const -{ - if (!m_time_estimate_enabled) { -#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_MODAL - m_time_estimate_frames_count = 0; -#endif // GCODE_VIEWER_TIME_ESTIMATE - return; - } - - ImGuiWrapper& imgui = *wxGetApp().imgui(); - -#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_MODAL - // esc - if (ImGui::GetIO().KeysDown[27]) { - m_time_estimate_enabled = false; - return; - } -#endif // GCODE_VIEWER_TIME_ESTIMATE - - using Times = std::pair; - using TimesList = std::vector>; - using Headers = std::vector; - using ColumnOffsets = std::array; - - // helper structure containig the data needed to render the time items - struct PartialTime - { - enum class EType : unsigned char - { - Print, - ColorChange, - Pause - }; - EType type; - int extruder_id; - Color color1; - Color color2; - Times times; - }; - using PartialTimes = std::vector; - - auto append_headers = [&imgui](const Headers& headers, const ColumnOffsets& offsets) { - imgui.text(headers[0]); - ImGui::SameLine(offsets[0]); - imgui.text(headers[1]); - ImGui::SameLine(offsets[1]); - imgui.text(headers[2]); - ImGui::Separator(); - }; - - auto append_mode = [this, &imgui, append_headers](float total_time, const PartialTimes& items, - const Headers& partial_times_headers, - const std::vector>& moves_time, - const Headers& moves_headers, - const std::vector>& roles_time, - const Headers& roles_headers) { - auto append_partial_times = [this, &imgui, append_headers](const PartialTimes& items, const Headers& headers) { - auto calc_offsets = [this, &headers](const PartialTimes& items) { - ColumnOffsets ret = { ImGui::CalcTextSize(headers[0].c_str()).x, ImGui::CalcTextSize(headers[1].c_str()).x }; - for (const PartialTime& item : items) { - std::string label; - switch (item.type) - { - case PartialTime::EType::Print: { label = _u8L("Print"); break; } - case PartialTime::EType::Pause: { label = _u8L("Pause"); break; } - case PartialTime::EType::ColorChange: { label = _u8L("Color change"); break; } - } - - ret[0] = std::max(ret[0], ImGui::CalcTextSize(label.c_str()).x); - ret[1] = std::max(ret[1], ImGui::CalcTextSize(short_time(get_time_dhms(item.times.second)).c_str()).x); - } - - const ImGuiStyle& style = ImGui::GetStyle(); - ret[0] += 2.0f * (ImGui::GetTextLineHeight() + style.ItemSpacing.x); - ret[1] += ret[0] + style.ItemSpacing.x; - return ret; - }; - auto append_color = [this, &imgui](const Color& color1, const Color& color2, ColumnOffsets& offsets, const Times& times) { - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Color change")); - ImGui::SameLine(); - - float icon_size = ImGui::GetTextLineHeight(); - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - ImVec2 pos = ImGui::GetCursorScreenPos(); - pos.x -= 0.5f * ImGui::GetStyle().ItemSpacing.x; - ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); - draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color1[0], color1[1], color1[2], 1.0f }), 6); - center.x += icon_size; - draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color2[0], color2[1], color2[2], 1.0f }), 6); - ImGui::SameLine(offsets[0]); - imgui.text(short_time(get_time_dhms(times.second - times.first))); - }; - - if (items.empty()) - return; - - ColumnOffsets offsets = calc_offsets(items); - - ImGui::Spacing(); - append_headers(headers, offsets); - - for (const PartialTime& item : items) { - switch (item.type) - { - case PartialTime::EType::Print: - { - imgui.text(_u8L("Print")); - ImGui::SameLine(offsets[0]); - imgui.text(short_time(get_time_dhms(item.times.second))); - ImGui::SameLine(offsets[1]); - imgui.text(short_time(get_time_dhms(item.times.first))); - break; - } - case PartialTime::EType::Pause: - { - imgui.text(_u8L("Pause")); - ImGui::SameLine(offsets[0]); - imgui.text(short_time(get_time_dhms(item.times.second - item.times.first))); - break; - } - case PartialTime::EType::ColorChange: - { - append_color(item.color1, item.color2, offsets, item.times); - break; - } - } - } - }; - - auto move_type_label = [](EMoveType type) { - switch (type) - { - case EMoveType::Noop: { return _u8L("Noop"); } - case EMoveType::Retract: { return _u8L("Retraction"); } - case EMoveType::Unretract: { return _u8L("Unretraction"); } - case EMoveType::Tool_change: { return _u8L("Tool change"); } - case EMoveType::Color_change: { return _u8L("Color change"); } - case EMoveType::Pause_Print: { return _u8L("Pause print"); } - case EMoveType::Custom_GCode: { return _u8L("Custom GCode"); } - case EMoveType::Travel: { return _u8L("Travel"); } - case EMoveType::Extrude: { return _u8L("Extrusion"); } - default: { return _u8L("Unknown"); } - } - }; - - auto append_time_item = [&imgui] (const std::string& label, float time, float percentage, const ImVec4& color, const ColumnOffsets& offsets) { - imgui.text(label); - ImGui::SameLine(offsets[0]); - imgui.text(short_time(get_time_dhms(time))); - ImGui::SameLine(offsets[1]); - char buf[64]; - ::sprintf(buf, "%.1f%%", 100.0f * percentage); - ImGuiWindow* window = ImGui::GetCurrentWindow(); - ImRect frame_bb; - frame_bb.Min = { ImGui::GetCursorScreenPos().x, window->DC.CursorPos.y }; - frame_bb.Max = { frame_bb.Min.x + percentage * (window->WorkRect.Max.x - frame_bb.Min.x), window->DC.CursorPos.y + ImGui::CalcTextSize(buf, nullptr, false).y }; - frame_bb.Min.x -= IM_FLOOR(window->WindowPadding.x * 0.5f - 1.0f); - frame_bb.Max.x += IM_FLOOR(window->WindowPadding.x * 0.5f); - window->DrawList->AddRectFilled(frame_bb.Min, frame_bb.Max, ImGui::GetColorU32({ color.x, color.y, color.z, 1.0f }), 0.0f, 0); - ImGui::TextUnformatted(buf); - }; - - auto append_move_times = [this, &imgui, move_type_label, append_headers, append_time_item](float total_time, - const std::vector>& moves_time, - const Headers& headers, const ColumnOffsets& offsets) { - - if (moves_time.empty()) - return; - - if (!ImGui::CollapsingHeader(_u8L("Moves Time").c_str(), ImGuiTreeNodeFlags_DefaultOpen)) - return; - - append_headers(headers, offsets); - - std::vector> sorted_moves_time(moves_time); - std::sort(sorted_moves_time.begin(), sorted_moves_time.end(), [](const auto& p1, const auto& p2) { return p2.second < p1.second; }); - - for (const auto& [type, time] : sorted_moves_time) { - append_time_item(move_type_label(type), time, time / total_time, ImGuiWrapper::COL_ORANGE_LIGHT, offsets); - } - }; - - auto append_role_times = [this, &imgui, append_headers, append_time_item](float total_time, - const std::vector>& roles_time, - const Headers& headers, const ColumnOffsets& offsets) { - - if (roles_time.empty()) - return; - - if (!ImGui::CollapsingHeader(_u8L("Features Time").c_str(), ImGuiTreeNodeFlags_DefaultOpen)) - return; - - append_headers(headers, offsets); - - std::vector> sorted_roles_time(roles_time); - std::sort(sorted_roles_time.begin(), sorted_roles_time.end(), [](const auto& p1, const auto& p2) { return p2.second < p1.second; }); - - for (const auto& [role, time] : sorted_roles_time) { - Color color = Extrusion_Role_Colors[static_cast(role)]; - append_time_item(_u8L(ExtrusionEntity::role_to_string(role)), time, time / total_time, { 0.666f * color[0], 0.666f * color[1], 0.666f * color[2], 1.0f}, offsets); - } - }; - - auto calc_common_offsets = [move_type_label]( - const std::vector>& moves_time, const Headers& moves_headers, - const std::vector>& roles_time, const Headers& roles_headers) { - ColumnOffsets ret = { std::max(ImGui::CalcTextSize(moves_headers[0].c_str()).x, ImGui::CalcTextSize(roles_headers[0].c_str()).x), - std::max(ImGui::CalcTextSize(moves_headers[1].c_str()).x, ImGui::CalcTextSize(roles_headers[1].c_str()).x) }; - - for (const auto& [type, time] : moves_time) { - ret[0] = std::max(ret[0], ImGui::CalcTextSize(move_type_label(type).c_str()).x); - ret[1] = std::max(ret[1], ImGui::CalcTextSize(short_time(get_time_dhms(time)).c_str()).x); - } - - for (const auto& [role, time] : roles_time) { - ret[0] = std::max(ret[0], ImGui::CalcTextSize(_u8L(ExtrusionEntity::role_to_string(role)).c_str()).x); - ret[1] = std::max(ret[1], ImGui::CalcTextSize(short_time(get_time_dhms(time)).c_str()).x); - } - - const ImGuiStyle& style = ImGui::GetStyle(); - ret[0] += 2.0f * style.ItemSpacing.x; - ret[1] += ret[0] + style.ItemSpacing.x; - return ret; - }; - - imgui.text(_u8L("Time") + ":"); - ImGui::SameLine(); - imgui.text(short_time(get_time_dhms(total_time))); - append_partial_times(items, partial_times_headers); - ColumnOffsets common_offsets = calc_common_offsets(moves_time, moves_headers, roles_time, roles_headers); - append_move_times(total_time, moves_time, moves_headers, common_offsets); - append_role_times(total_time, roles_time, roles_headers, common_offsets); - }; - - auto generate_partial_times = [this](const TimesList& times) { - PartialTimes items; - - std::vector custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes; - int extruders_count = wxGetApp().extruders_edited_cnt(); - std::vector last_color(extruders_count); - for (int i = 0; i < extruders_count; ++i) { - last_color[i] = m_tool_colors[i]; - } - int last_extruder_id = 1; - for (const auto& time_rec : times) { - switch (time_rec.first) - { - case CustomGCode::PausePrint: - { - auto it = std::find_if(custom_gcode_per_print_z.begin(), custom_gcode_per_print_z.end(), [time_rec](const CustomGCode::Item& item) { return item.type == time_rec.first; }); - if (it != custom_gcode_per_print_z.end()) { - items.push_back({ PartialTime::EType::Print, it->extruder, Color(), Color(), time_rec.second }); - items.push_back({ PartialTime::EType::Pause, it->extruder, Color(), Color(), time_rec.second }); - custom_gcode_per_print_z.erase(it); - } - break; - } - case CustomGCode::ColorChange: - { - auto it = std::find_if(custom_gcode_per_print_z.begin(), custom_gcode_per_print_z.end(), [time_rec](const CustomGCode::Item& item) { return item.type == time_rec.first; }); - if (it != custom_gcode_per_print_z.end()) { - items.push_back({ PartialTime::EType::Print, it->extruder, Color(), Color(), time_rec.second }); - items.push_back({ PartialTime::EType::ColorChange, it->extruder, last_color[it->extruder - 1], decode_color(it->color), time_rec.second }); - last_color[it->extruder - 1] = decode_color(it->color); - last_extruder_id = it->extruder; - custom_gcode_per_print_z.erase(it); - } - else - items.push_back({ PartialTime::EType::Print, last_extruder_id, Color(), Color(), time_rec.second }); - - break; - } - default: { break; } - } - } - - return items; - }; - - const Headers partial_times_headers = { - _u8L("Event"), - _u8L("Remaining"), - _u8L("Duration") - }; - const Headers moves_headers = { - _u8L("Type"), - _u8L("Time"), - _u8L("Percentage") - }; - const Headers roles_headers = { - _u8L("Feature"), - _u8L("Time"), - _u8L("Percentage") - }; - - Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size(); -#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_MODAL - std::string title = _u8L("Estimated printing time"); - ImGui::OpenPopup(title.c_str()); - - imgui.set_next_window_pos(0.5f * static_cast(cnv_size.get_width()), 0.5f * static_cast(cnv_size.get_height()), ImGuiCond_Always, 0.5f, 0.5f); - ImGui::SetNextWindowSize({ -1.0f, 0.666f * static_cast(cnv_size.get_height()) }); - ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - ImGui::SetNextWindowBgAlpha(0.6f); - if (ImGui::BeginPopupModal(title.c_str(), &m_time_estimate_enabled, ImGuiWindowFlags_AlwaysAutoResize)) { - if (m_time_estimate_enabled) { - // imgui takes several frames to grayout the content of the canvas - if (m_time_estimate_frames_count < 10) { - wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); - wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); - ++m_time_estimate_frames_count; - } -#else - imgui.set_next_window_pos(static_cast(cnv_size.get_width()), static_cast(cnv_size.get_height()), ImGuiCond_Always, 1.0f, 1.0f); - ImGui::SetNextWindowSizeConstraints({ 0.0f, 0.0f }, { -1.0f, 0.5f * static_cast(cnv_size.get_height()) }); - ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - ImGui::SetNextWindowBgAlpha(0.6f); - imgui.begin(std::string("Time_estimate"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove); - - // title - imgui.title(_u8L("Estimated printing time")); -#endif // GCODE_VIEWER_TIME_ESTIMATE - - // mode tabs - ImGui::BeginTabBar("mode_tabs"); - const PrintEstimatedTimeStatistics::Mode& normal_mode = m_time_statistics.modes[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Normal)]; - if (normal_mode.time > 0.0f) { - if (ImGui::BeginTabItem(_u8L("Normal").c_str())) { - append_mode(normal_mode.time, - generate_partial_times(normal_mode.custom_gcode_times), partial_times_headers, - normal_mode.moves_times, moves_headers, - normal_mode.roles_times, roles_headers); - ImGui::EndTabItem(); - } - } - const PrintEstimatedTimeStatistics::Mode& stealth_mode = m_time_statistics.modes[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Stealth)]; - if (stealth_mode.time > 0.0f) { - if (ImGui::BeginTabItem(_u8L("Stealth").c_str())) { - append_mode(stealth_mode.time, - generate_partial_times(stealth_mode.custom_gcode_times), partial_times_headers, - stealth_mode.moves_times, moves_headers, - stealth_mode.roles_times, roles_headers); - ImGui::EndTabItem(); - } - } - ImGui::EndTabBar(); - -#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_MODAL - // this is ugly, but it is the only way to ensure that the dialog is large - // enough to show enterely the title - // see: https://github.com/ocornut/imgui/issues/3239 - float width = std::max(ImGui::CalcTextSize(title.c_str()).x + 2.0f * ImGui::GetStyle().WindowPadding.x, 300.0f); - ImGui::SetCursorPosX(width); - ImGui::SetCursorPosX(0.0f); - } - else - m_time_estimate_enabled = false; - - ImGui::EndPopup(); - } -#else - imgui.end(); -#endif // GCODE_VIEWER_TIME_ESTIMATE - ImGui::PopStyleVar(); -} -#endif // GCODE_VIEWER_TIME_ESTIMATE - #if ENABLE_GCODE_VIEWER_STATISTICS void GCodeViewer::render_statistics() const { diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index e49a1f08bf..302296c412 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -369,18 +369,8 @@ private: Shells m_shells; EViewType m_view_type{ EViewType::FeatureType }; bool m_legend_enabled{ true }; -#if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE PrintEstimatedTimeStatistics m_time_statistics; -#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_MODAL - mutable bool m_time_estimate_enabled{ false }; - mutable unsigned int m_time_estimate_frames_count{ 0 }; -#else - bool m_time_estimate_enabled{ false }; -#endif // GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_MODAL -#endif // GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE -#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND mutable PrintEstimatedTimeStatistics::ETimeMode m_time_estimate_mode{ PrintEstimatedTimeStatistics::ETimeMode::Normal }; -#endif // GCODE_VIEWER_TIME_ESTIMATE #if ENABLE_GCODE_VIEWER_STATISTICS mutable Statistics m_statistics; #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -433,11 +423,6 @@ public: bool is_legend_enabled() const { return m_legend_enabled; } void enable_legend(bool enable) { m_legend_enabled = enable; } -#if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE - bool is_time_estimate_enabled() const { return m_time_estimate_enabled; } - void enable_time_estimate(bool enable); -#endif // GCODE_VIEWER_TIME_ESTIMATE - void export_toolpaths_to_obj(const char* filename) const; private: @@ -448,9 +433,6 @@ private: void render_toolpaths() const; void render_shells() const; void render_legend() const; -#if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE - void render_time_estimate() const; -#endif // GCODE_VIEWER_TIME_ESTIMATE #if ENABLE_GCODE_VIEWER_STATISTICS void render_statistics() const; #endif // ENABLE_GCODE_VIEWER_STATISTICS diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index c0da110d92..7ae6f42945 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3109,18 +3109,6 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) break; } #endif // ENABLE_RENDER_PICKING_PASS -#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_DEFAULT || GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_MODAL - case 'T': - case 't': - { - if (!m_main_toolbar.is_enabled()) { - m_gcode_viewer.enable_time_estimate(!m_gcode_viewer.is_time_estimate_enabled()); - m_dirty = true; - wxGetApp().plater()->update_preview_bottom_toolbar(); - } - break; - } -#endif // GCODE_VIEWER_TIME_ESTIMATE case 'Z': #if ENABLE_GCODE_VIEWER case 'z': diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index ee1d32abb8..03d42089b8 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -558,9 +558,6 @@ public: void reset_gcode_toolpaths() { m_gcode_viewer.reset(); } const GCodeViewer::SequentialView& get_gcode_sequential_view() const { return m_gcode_viewer.get_sequential_view(); } void update_gcode_sequential_view_current(unsigned int first, unsigned int last) { m_gcode_viewer.update_sequential_view_current(first, last); } -#if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE - bool is_time_estimate_enabled() const { return m_gcode_viewer.is_time_estimate_enabled(); } -#endif // GCODE_VIEWER_TIME_ESTIMATE #endif // ENABLE_GCODE_VIEWER void toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 57b1158f65..5dcd26a877 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -331,13 +331,8 @@ bool Preview::init(wxWindow* parent, Model* model) get_option_type_string(OptionType::CustomGCodes) + "|0|" + get_option_type_string(OptionType::Shells) + "|0|" + get_option_type_string(OptionType::ToolMarker) + "|0|" + -#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_DEFAULT - get_option_type_string(OptionType::Legend) + "|1|" + - get_option_type_string(OptionType::TimeEstimate) + "|1" -#else get_option_type_string(OptionType::Legend) + "|1" -#endif // GCODE_VIEWER_TIME_ESTIMATE - ); +); Slic3r::GUI::create_combochecklist(m_combochecklist_options, GUI::into_u8(_L("Options")), options_items); #else m_checkbox_travel = new wxCheckBox(this, wxID_ANY, _(L("Travel"))); @@ -1472,14 +1467,7 @@ wxString Preview::get_option_type_string(OptionType type) const case OptionType::CustomGCodes: { return _L("Custom GCodes"); } case OptionType::Shells: { return _L("Shells"); } case OptionType::ToolMarker: { return _L("Tool marker"); } -#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND case OptionType::Legend: { return _L("Legend/Estimated printing time"); } -#else - case OptionType::Legend: { return _L("Legend"); } -#endif // GCODE_VIEWER_TIME_ESTIMATE -#if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE - case OptionType::TimeEstimate: { return _L("Estimated printing time"); } -#endif // GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE default: { return ""; } } } diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index ddb7af86fb..d9ce44bd62 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -150,10 +150,7 @@ public: CustomGCodes, Shells, ToolMarker, - Legend, -#if GCODE_VIEWER_TIME_ESTIMATE != TIME_ESTIMATE_NONE - TimeEstimate -#endif // GCODE_VIEWER_TIME_ESTIMATE + Legend }; Preview(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process, diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 1a551216e7..1eceea22e4 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -206,16 +206,7 @@ void KBShortcutsDialog::fill_shortcuts() { L("Arrow Down"), L("Lower Layer") }, { "U", L("Upper Layer") }, { "D", L("Lower Layer") }, -#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_LEGEND { "L", L("Show/Hide Legend/Estimated printing time") }, -#else - { "L", L("Show/Hide Legend") }, -#endif // GCODE_VIEWER_TIME_ESTIMATE -#if GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_DEFAULT - { "T", L("Show/Hide Estimated printing time") } -#elif GCODE_VIEWER_TIME_ESTIMATE == TIME_ESTIMATE_MODAL - { "T", L("Show Estimated printing time") } -#endif // GCODE_VIEWER_TIME_ESTIMATE }; m_full_shortcuts.push_back(std::make_pair(_L("Preview"), preview_shortcuts)); From 683af51685ec21d09be4c0ef9ac8228b38bcb56d Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 1 Sep 2020 14:15:19 +0200 Subject: [PATCH 364/503] Replaced boost::filesystem::canonical() with boost::filesystem::absolute(), as canonical() is broken on Windows (reparse points aka symbolic links are not processed correctly). Fixes https://github.com/prusa3d/PrusaSlicer/issues/732 https://github.com/prusa3d/PrusaSlicer/issues/3956 https://github.com/prusa3d/PrusaSlicer/issues/4557 --- src/libslic3r/Preset.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index a5160d2db3..7aaa96c8cf 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -634,7 +634,9 @@ void PresetCollection::add_default_preset(const std::vector &keys, // Throws an exception on error. void PresetCollection::load_presets(const std::string &dir_path, const std::string &subdir) { - boost::filesystem::path dir = boost::filesystem::canonical(boost::filesystem::path(dir_path) / subdir).make_preferred(); + // Don't use boost::filesystem::canonical() on Windows, it is broken in regard to reparse points, + // see https://github.com/prusa3d/PrusaSlicer/issues/732 + boost::filesystem::path dir = boost::filesystem::absolute(boost::filesystem::path(dir_path) / subdir).make_preferred(); m_dir_path = dir.string(); std::string errors_cummulative; // Store the loaded presets into a new vector, otherwise the binary search for already existing presets would be broken. @@ -1518,7 +1520,9 @@ PhysicalPrinterCollection::PhysicalPrinterCollection( const std::vector Date: Tue, 1 Sep 2020 14:35:42 +0200 Subject: [PATCH 365/503] Fixed export of toolpaths to obj files --- src/slic3r/GUI/GCodeViewer.cpp | 25 +++++++++++++++++-------- src/slic3r/GUI/GCodeViewer.hpp | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 24ed5be9b4..772b290ea8 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -608,9 +608,15 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, 0, buffer.vertices.data_size_bytes(), vertices.data())); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - auto get_vertex = [&vertices, floats_per_vertex](size_t id) { + // get indices data from index buffer on gpu + std::vector indices = std::vector(buffer.indices.count); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.indices.id)); + glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, static_cast(indices.size() * sizeof(unsigned int)), indices.data())); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + auto get_vertex = [&vertices, floats_per_vertex](unsigned int id) { // extract vertex from vector of floats - size_t base_id = id * floats_per_vertex; + unsigned int base_id = id * floats_per_vertex; return Vec3f(vertices[base_id + 0], vertices[base_id + 1], vertices[base_id + 2]); }; @@ -626,7 +632,7 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const float length; }; - auto generate_segment = [get_vertex](size_t start_id, float half_width, float half_height) { + auto generate_segment = [get_vertex](unsigned int start_id, unsigned int end_id, float half_width, float half_height) { auto local_basis = [](const Vec3f& dir) { // calculate local basis (dir, right, up) on given segment std::array ret; @@ -650,13 +656,16 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const }; Vec3f v1 = get_vertex(start_id) - half_height * Vec3f::UnitZ(); - Vec3f v2 = get_vertex(start_id + 1) - half_height * Vec3f::UnitZ(); + Vec3f v2 = get_vertex(end_id) - half_height * Vec3f::UnitZ(); float length = (v2 - v1).norm(); const auto&& [dir, right, up] = local_basis(v2 - v1); return Segment({ v1, v2, dir, right, up, half_width * right, half_height * up, length }); }; size_t out_vertices_count = 0; + unsigned int indices_per_segment = buffer.indices_per_segment(); + unsigned int start_vertex_offset = buffer.start_segment_vertex_offset(); + unsigned int end_vertex_offset = buffer.end_segment_vertex_offset(); for (size_t i = 0; i < buffer.render_paths.size(); ++i) { // get paths segments from buffer paths @@ -675,9 +684,8 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const unsigned int start = static_cast(render_path.offsets[j] / sizeof(unsigned int)); unsigned int end = start + render_path.sizes[j]; - for (size_t k = start; k < end; k += 2) { - Segment curr = generate_segment(k, half_width, half_height); - + for (size_t k = start; k < end; k += static_cast(indices_per_segment)) { + Segment curr = generate_segment(indices[k + start_vertex_offset], indices[k + end_vertex_offset], half_width, half_height); if (k == start) { // starting endpoint vertices/normals out_vertices.push_back(curr.v1 + curr.rl_displacement); out_normals.push_back(curr.right); // right @@ -699,7 +707,8 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const out_vertices.push_back(curr.v1 - curr.rl_displacement); out_normals.push_back(-curr.right); // left out_vertices_count += 2; - Segment prev = generate_segment(k - 2, half_width, half_height); + size_t first_vertex_id = k - static_cast(indices_per_segment); + Segment prev = generate_segment(indices[first_vertex_id + start_vertex_offset], indices[first_vertex_id + end_vertex_offset], half_width, half_height); Vec3f med_dir = (prev.dir + curr.dir).normalized(); float disp = half_width * ::tan(::acos(std::clamp(curr.dir.dot(med_dir), -1.0f, 1.0f))); Vec3f disp_vec = disp * prev.dir; diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 302296c412..68fed6f334 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -176,6 +176,24 @@ class GCodeViewer default: { return 0; } } } + unsigned int start_segment_vertex_offset() const { + switch (render_primitive_type) + { + case ERenderPrimitiveType::Point: + case ERenderPrimitiveType::Line: + case ERenderPrimitiveType::Triangle: + default: { return 0; } + } + } + unsigned int end_segment_vertex_offset() const { + switch (render_primitive_type) + { + case ERenderPrimitiveType::Point: { return 0; } + case ERenderPrimitiveType::Line: { return 1; } + case ERenderPrimitiveType::Triangle: { return 36; } // 1 vertex of 13th triangle + default: { return 0; } + } + } }; // helper to render shells From 2455df4017e4000347dc77b543c8674bd052b145 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 24 Aug 2020 13:53:18 +0200 Subject: [PATCH 366/503] notifiactions: new icons + deleting old warnings&errors --- resources/icons/notification_close.svg | 18 +++++ resources/icons/notification_close_hover.svg | 66 +++++++++++++++++ resources/icons/notification_error.svg | 71 +++++++++++++++++++ resources/icons/notification_minimalize.svg | 14 ++++ .../icons/notification_minimalize_hover.svg | 58 +++++++++++++++ resources/icons/notification_warning.svg | 70 ++++++++++++++++++ src/imgui/imconfig.h | 24 ++++--- src/slic3r/GUI/ImGuiWrapper.cpp | 24 ++++--- src/slic3r/GUI/NotificationManager.cpp | 4 +- src/slic3r/GUI/Plater.cpp | 4 ++ 10 files changed, 329 insertions(+), 24 deletions(-) create mode 100644 resources/icons/notification_close.svg create mode 100644 resources/icons/notification_close_hover.svg create mode 100644 resources/icons/notification_error.svg create mode 100644 resources/icons/notification_minimalize.svg create mode 100644 resources/icons/notification_minimalize_hover.svg create mode 100644 resources/icons/notification_warning.svg diff --git a/resources/icons/notification_close.svg b/resources/icons/notification_close.svg new file mode 100644 index 0000000000..708d8bfef1 --- /dev/null +++ b/resources/icons/notification_close.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + diff --git a/resources/icons/notification_close_hover.svg b/resources/icons/notification_close_hover.svg new file mode 100644 index 0000000000..a04dce21ad --- /dev/null +++ b/resources/icons/notification_close_hover.svg @@ -0,0 +1,66 @@ + +image/svg+xml + + + + + + + + + + diff --git a/resources/icons/notification_error.svg b/resources/icons/notification_error.svg new file mode 100644 index 0000000000..5356e7af6e --- /dev/null +++ b/resources/icons/notification_error.svg @@ -0,0 +1,71 @@ + +image/svg+xml + + + + + + + + + + diff --git a/resources/icons/notification_minimalize.svg b/resources/icons/notification_minimalize.svg new file mode 100644 index 0000000000..bb3ae9b7a1 --- /dev/null +++ b/resources/icons/notification_minimalize.svg @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/resources/icons/notification_minimalize_hover.svg b/resources/icons/notification_minimalize_hover.svg new file mode 100644 index 0000000000..bc5bc6cca1 --- /dev/null +++ b/resources/icons/notification_minimalize_hover.svg @@ -0,0 +1,58 @@ + +image/svg+xml + + + + + + + diff --git a/resources/icons/notification_warning.svg b/resources/icons/notification_warning.svg new file mode 100644 index 0000000000..6ba7a046d8 --- /dev/null +++ b/resources/icons/notification_warning.svg @@ -0,0 +1,70 @@ + +image/svg+xml + + + + + + + + + + diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index feda857ae2..4a1d1faa0c 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -108,17 +108,19 @@ namespace ImGui const char ColorMarkerEnd = 0x3; // ETX // Special ASCII characters are used here as an ikons markers - const char PrintIconMarker = 0x4; - const char PrinterIconMarker = 0x5; - const char PrinterSlaIconMarker = 0x6; - const char FilamentIconMarker = 0x7; - const char MaterialIconMarker = 0x8; - const char CloseIconMarker = 0xB; - const char CloseIconHoverMarker = 0xC; - const char TimerDotMarker = 0xE; - const char TimerDotEmptyMarker = 0xF; - const char WarningMarker = 0x10; - const char ErrorMarker = 0x11; + const char PrintIconMarker = 0x4; + const char PrinterIconMarker = 0x5; + const char PrinterSlaIconMarker = 0x6; + const char FilamentIconMarker = 0x7; + const char MaterialIconMarker = 0x8; + const char CloseIconMarker = 0xB; + const char CloseIconHoverMarker = 0xC; +// const char TimerDotMarker = 0xE; +// const char TimerDotEmptyMarker = 0xF; + const char MinimalizeMarker = 0xE; + const char MinimalizeHoverMarker = 0xF; + const char WarningMarker = 0x10; + const char ErrorMarker = 0x11; // void MyFunction(const char* name, const MyMatrix44& v); } diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 7c27545026..e839fdf9b2 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -37,17 +37,19 @@ namespace GUI { static const std::map font_icons = { - {ImGui::PrintIconMarker , "cog" }, - {ImGui::PrinterIconMarker , "printer" }, - {ImGui::PrinterSlaIconMarker, "sla_printer" }, - {ImGui::FilamentIconMarker , "spool" }, - {ImGui::MaterialIconMarker , "resin" }, - {ImGui::CloseIconMarker , "cross" }, - {ImGui::CloseIconHoverMarker, "cross_focus_large" }, - {ImGui::TimerDotMarker , "timer_dot" }, - {ImGui::TimerDotEmptyMarker , "timer_dot_empty" }, - {ImGui::WarningMarker , "flag_green" }, - {ImGui::ErrorMarker , "flag_red" } + {ImGui::PrintIconMarker , "cog" }, + {ImGui::PrinterIconMarker , "printer" }, + {ImGui::PrinterSlaIconMarker , "sla_printer" }, + {ImGui::FilamentIconMarker , "spool" }, + {ImGui::MaterialIconMarker , "resin" }, + {ImGui::CloseIconMarker , "notification_close" }, + {ImGui::CloseIconHoverMarker , "notification_close_hover" }, + //{ImGui::TimerDotMarker , "timer_dot" }, + //{ImGui::TimerDotEmptyMarker , "timer_dot_empty" }, + {ImGui::MinimalizeMarker , "notification_minimalize" }, + {ImGui::MinimalizeHoverMarker , "notification_minimalize_hover" }, + {ImGui::WarningMarker , "notification_warning" }, + {ImGui::ErrorMarker , "notification_error" } }; const ImVec4 ImGuiWrapper::COL_GREY_DARK = { 0.333f, 0.333f, 0.333f, 1.0f }; diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index b7301f3d82..e90557a65e 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -492,12 +492,12 @@ void NotificationManager::PopNotification::render_minimize_button(ImGuiWrapper& //button - if part if treggered std::string button_text; - button_text = ImGui::CloseIconMarker; + button_text = ImGui::MinimalizeMarker; if (ImGui::IsMouseHoveringRect(ImVec2(win_pos_x - m_window_width / 10.f, win_pos_y + m_window_height - 2 * m_line_height + 1), ImVec2(win_pos_x, win_pos_y + m_window_height), true)) { - button_text = ImGui::CloseIconHoverMarker; + button_text = ImGui::MinimalizeHoverMarker; } ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str()); ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 1fe32fd2dc..1e4b344899 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3481,6 +3481,10 @@ void Plater::priv::add_warning(const Slic3r::PrintStateBase::Warning& warning, s } void Plater::priv::actualizate_warnings(const Model& model, size_t print_oid) { + if (model.objects.size() == 0) { + clear_warnings(); + return; + } std::vector living_oids; living_oids.push_back(model.id().id); living_oids.push_back(print_oid); From 25fb569017741eb89422dcb25316af96012409c0 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 25 Aug 2020 09:55:29 +0200 Subject: [PATCH 367/503] notifications: plater warning not visible in preview --- src/slic3r/GUI/NotificationManager.cpp | 8 ++++++++ src/slic3r/GUI/NotificationManager.hpp | 3 +++ src/slic3r/GUI/Plater.cpp | 6 +++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index e90557a65e..387a4d5deb 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -909,6 +909,14 @@ bool NotificationManager::find_older(NotificationManager::PopNotification* notif return false; } +void NotificationManager::set_in_preview(bool preview) +{ + m_in_preview = preview; + for (PopNotification* notification : m_pop_notifications) { + if (notification->get_type() == NotificationType::PlaterWarning) + notification->hide(preview); + } +} void NotificationManager::dpi_changed() { diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index d7037c53e4..2bd0ae86d0 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -94,6 +94,7 @@ public: void set_gray(bool g) { m_is_gray = g; } void set_paused(bool p) { m_paused = p; } bool compare_text(const std::string& text); + void hide(bool h) { m_hidden = h; } protected: // Call after every size change void init(); @@ -230,6 +231,7 @@ public: // finds and closes all notifications of given type void close_notification_of_type(const NotificationType type); void dpi_changed(); + void set_in_preview(bool preview); private: //pushes notification into the queue of notifications that are rendered //can be used to create custom notification @@ -246,6 +248,7 @@ private: bool m_hovered { false }; //timestamps used for slining finished - notification could be gone so it needs to be stored here std::unordered_set m_used_timestamps; + bool m_in_preview; //prepared (basic) notifications const std::vector basic_notifications = { diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 1e4b344899..45a1f6ea82 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1512,7 +1512,7 @@ struct Plater::priv GLToolbar view_toolbar; GLToolbar collapse_toolbar; Preview *preview; - NotificationManager* notification_manager; + NotificationManager* notification_manager { nullptr }; BackgroundSlicingProcess background_process; bool suppressed_backround_processing_update { false }; @@ -3304,6 +3304,8 @@ void Plater::priv::set_current_panel(wxPanel* panel) // sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) view3D->set_as_dirty(); view_toolbar.select_item("3D"); + if(notification_manager != nullptr) + notification_manager->set_in_preview(false); } else if (current_panel == preview) { @@ -3318,6 +3320,8 @@ void Plater::priv::set_current_panel(wxPanel* panel) preview->set_as_dirty(); view_toolbar.select_item("Preview"); + if (notification_manager != nullptr) + notification_manager->set_in_preview(true); } current_panel->SetFocusFromKbd(); From bca60821d8246a5bcf1e129c6599579347dc823e Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 25 Aug 2020 16:28:03 +0200 Subject: [PATCH 368/503] notifications: plater warning refactor --- src/slic3r/GUI/GLCanvas3D.cpp | 66 +++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 7ae6f42945..81f63cb4d4 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -620,53 +620,57 @@ GLCanvas3D::WarningTexture::WarningTexture() void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool state, const GLCanvas3D& canvas) { + // Since we have NotificationsManager.hpp the warning textures are no loger needed. + // However i have left the infrastructure here and only commented the rendering. + // The plater warning / error notifications are added and closed from here. + + std::string text; + bool error = false; + switch (warning) { + case ObjectOutside: text = L("An object outside the print area was detected."); break; + case ToolpathOutside: text = L("A toolpath outside the print area was detected."); break; + case SlaSupportsOutside: text = L("SLA supports outside the print area were detected."); break; + case SomethingNotShown: text = L("Some objects are not visible."); break; + case ObjectClashed: + text = L( "An object outside the print area was detected.\n" + "Resolve the current problem to continue slicing."); + error = true; + break; + } + if(state) { + if(error) + wxGetApp().plater()->get_notification_manager()->push_plater_error_notification(text,*(wxGetApp().plater()->get_current_canvas3D())); + else + wxGetApp().plater()->get_notification_manager()->push_plater_warning_notification(text, *(wxGetApp().plater()->get_current_canvas3D())); + } else { + if (error) + wxGetApp().plater()->get_notification_manager()->close_plater_error_notification(); + else + wxGetApp().plater()->get_notification_manager()->close_plater_warning_notification(text); + } + + /* auto it = std::find(m_warnings.begin(), m_warnings.end(), warning); if (state) { if (it != m_warnings.end()) // this warning is already set to be shown return; - m_warnings.emplace_back(warning); + m_warnings.push_back(warning); std::sort(m_warnings.begin(), m_warnings.end()); - - std::string text; - switch (warning) { - case ObjectOutside: text = L("An object outside the print area was detected."); break; - case ToolpathOutside: text = L("A toolpath outside the print area was detected."); break; - case SlaSupportsOutside: text = L("SLA supports outside the print area were detected."); break; - case SomethingNotShown: text = L("Some objects are not visible."); break; - case ObjectClashed: wxGetApp().plater()->get_notification_manager()->push_plater_error_notification(L("An object outside the print area was detected.\n" - "Resolve the current problem to continue slicing."), - *(wxGetApp().plater()->get_current_canvas3D())); - break; - } - if (!text.empty()) - wxGetApp().plater()->get_notification_manager()->push_plater_warning_notification(text, *(wxGetApp().plater()->get_current_canvas3D())); } else { if (it == m_warnings.end()) // deactivating something that is not active is an easy task return; m_warnings.erase(it); - - std::string text; - switch (warning) { - case ObjectOutside: text = L("An object outside the print area was detected."); break; - case ToolpathOutside: text = L("A toolpath outside the print area was detected."); break; - case SlaSupportsOutside: text = L("SLA supports outside the print area were detected."); break; - case SomethingNotShown: text = L("Some objects are not visibl.e"); break; - case ObjectClashed: wxGetApp().plater()->get_notification_manager()->close_plater_error_notification(); break; - } - if (!text.empty()) - wxGetApp().plater()->get_notification_manager()->close_plater_warning_notification(text); - - /*if (m_warnings.empty()) { // nothing remains to be shown + if (m_warnings.empty()) { // nothing remains to be shown reset(); m_msg_text = "";// save information for rescaling return; - }*/ + } } - /* + // Look at the end of our vector and generate proper texture. std::string text; bool red_colored = false; @@ -674,7 +678,7 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool case ObjectOutside : text = L("An object outside the print area was detected"); break; case ToolpathOutside : text = L("A toolpath outside the print area was detected"); break; case SlaSupportsOutside : text = L("SLA supports outside the print area were detected"); break; - case SomethingNotShown : text = L("Some objects are not visible"); break; + case SomethingNotShown : text = L("Some objects are not visible when editing supports"); break; case ObjectClashed: { text = L("An object outside the print area was detected\n" "Resolve the current problem to continue slicing"); From a81afce1b83681f14995f20f1ef9ef684139039f Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 25 Aug 2020 17:59:51 +0200 Subject: [PATCH 369/503] notifications not showing slicing finished when error --- src/slic3r/GUI/NotificationManager.cpp | 17 +++++++++++++---- src/slic3r/GUI/NotificationManager.hpp | 1 + 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 387a4d5deb..e27a4215cd 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -730,16 +730,16 @@ void NotificationManager::push_slicing_complete_notification(GLCanvas3D& canvas, { std::string hypertext; int time = 10; - if(large) - { + if (has_error_notification()) + return; + if (large) { hypertext = _u8L("Export G-Code."); time = 0; } NotificationData data{ NotificationType::SlicingComplete, NotificationLevel::RegularNotification, time, _u8L("Slicing finished."), hypertext }; NotificationManager::SlicingCompleteLargeNotification* notification = new NotificationManager::SlicingCompleteLargeNotification(data, m_next_id++, m_evt_handler, large); - if (push_notification_data(notification, canvas, timestamp)) { - } else { + if (!push_notification_data(notification, canvas, timestamp)) { delete notification; } } @@ -917,6 +917,15 @@ void NotificationManager::set_in_preview(bool preview) notification->hide(preview); } } +bool NotificationManager::has_error_notification() +{ + for (PopNotification* notification : m_pop_notifications) { + if (notification->get_data().level == NotificationLevel::ErrorNotification) + return true; + } + return false; +} + void NotificationManager::dpi_changed() { diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 2bd0ae86d0..0b066a3a04 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -240,6 +240,7 @@ private: //finds older notification of same type and moves it to the end of queue. returns true if found bool find_older(NotificationManager::PopNotification* notification); void sort_notifications(); + bool has_error_notification(); wxEvtHandler* m_evt_handler; std::deque m_pop_notifications; From 3984326ee335da301f6d1c2440a2f176ba57a65b Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 26 Aug 2020 10:49:42 +0200 Subject: [PATCH 370/503] notification init() at first render, not notification creation. Hopefully a fix of issue #4647. --- src/slic3r/GUI/NotificationManager.cpp | 7 ++++++- src/slic3r/GUI/NotificationManager.hpp | 11 ++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index e27a4215cd..47962f4b2a 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -49,13 +49,17 @@ NotificationManager::PopNotification::PopNotification(const NotificationData &n, , m_text2 (n.text2) , m_evt_handler (evt_handler) { - init(); + //init(); } NotificationManager::PopNotification::~PopNotification() { } NotificationManager::PopNotification::RenderResult NotificationManager::PopNotification::render(GLCanvas3D& canvas, const float& initial_y) { + if (!m_initialized) + { + init(); + } if (m_finished) return RenderResult::Finished; if (m_close_pending) { @@ -228,6 +232,7 @@ void NotificationManager::PopNotification::init() } m_lines_count++; } + m_initialized = true; } void NotificationManager::PopNotification::set_next_window_size(ImGuiWrapper& imgui) { diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 0b066a3a04..a11d08394c 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -121,6 +121,7 @@ public: const NotificationData m_data; int m_id; + bool m_initialized { false }; // Main text std::string m_text1; // Clickable text @@ -131,12 +132,12 @@ public: long m_remaining_time; bool m_counting_down; long m_last_remaining_time; - bool m_paused{ false }; - int m_countdown_frame{ 0 }; - bool m_fading_out{ false }; + bool m_paused { false }; + int m_countdown_frame { 0 }; + bool m_fading_out { false }; // total time left when fading beggins - float m_fading_time{ 0.0f }; - float m_current_fade_opacity{ 1.f }; + float m_fading_time { 0.0f }; + float m_current_fade_opacity { 1.f }; // If hidden the notif is alive but not visible to user bool m_hidden { false }; // m_finished = true - does not render, marked to delete From ac9e1e8e4aa8661ca79013650637cdcc42b79d13 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 1 Sep 2020 16:14:18 +0200 Subject: [PATCH 371/503] Extract app icon from exe on Windows --- src/slic3r/GUI/MainFrame.cpp | 59 +++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 886e96e1a0..bab5d7502e 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -115,18 +115,18 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S } #endif // ENABLE_GCODE_VIEWER_TASKBAR_ICON - SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); -// // Load the icon either from the exe, or from the ico file. -//#if _WIN32 -// { -// -// TCHAR szExeFileName[MAX_PATH]; -// GetModuleFileName(nullptr, szExeFileName, MAX_PATH); -// SetIcon(wxIcon(szExeFileName, wxBITMAP_TYPE_ICO)); -// } -//#else // SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); -//#endif // _WIN32 + // Load the icon either from the exe, or from the ico file. +#if _WIN32 + { + + TCHAR szExeFileName[MAX_PATH]; + GetModuleFileName(nullptr, szExeFileName, MAX_PATH); + SetIcon(wxIcon(szExeFileName, wxBITMAP_TYPE_ICO)); + } +#else + SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); +#endif // _WIN32 // initialize status bar m_statusbar = std::make_shared(this); @@ -1416,7 +1416,18 @@ void MainFrame::set_mode(EMode mode) m_plater->Thaw(); +// SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); + // Load the icon either from the exe, or from the ico file. +#if _WIN32 + { + + TCHAR szExeFileName[MAX_PATH]; + GetModuleFileName(nullptr, szExeFileName, MAX_PATH); + SetIcon(wxIcon(szExeFileName, wxBITMAP_TYPE_ICO)); + } +#else SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); +#endif // _WIN32 #if ENABLE_GCODE_VIEWER_TASKBAR_ICON if (m_taskbar_icon != nullptr) { m_taskbar_icon->RemoveIcon(); @@ -1473,7 +1484,7 @@ void MainFrame::set_mode(EMode mode) #if ENABLE_GCODE_VIEWER_TASKBAR_ICON if (m_taskbar_icon != nullptr) { m_taskbar_icon->RemoveIcon(); - m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG), "PrusaSlicer-GCode viewer"); + m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicerGCodeViewer_128px.png"), wxBITMAP_TYPE_PNG), "PrusaSlicer-GCode viewer"); } #endif // ENABLE_GCODE_VIEWER_TASKBAR_ICON @@ -1981,18 +1992,18 @@ SettingsDialog::SettingsDialog(MainFrame* mainframe) this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); -// // Load the icon either from the exe, or from the ico file. -//#if _WIN32 -// { -// -// TCHAR szExeFileName[MAX_PATH]; -// GetModuleFileName(nullptr, szExeFileName, MAX_PATH); -// SetIcon(wxIcon(szExeFileName, wxBITMAP_TYPE_ICO)); -// } -//#else -// SetIcon(wxIcon(var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); -//#endif // _WIN32 +// SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); + // Load the icon either from the exe, or from the ico file. +#if _WIN32 + { + + TCHAR szExeFileName[MAX_PATH]; + GetModuleFileName(nullptr, szExeFileName, MAX_PATH); + SetIcon(wxIcon(szExeFileName, wxBITMAP_TYPE_ICO)); + } +#else + SetIcon(wxIcon(var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); +#endif // _WIN32 this->Bind(wxEVT_SHOW, [this](wxShowEvent& evt) { From 08580a9b1800c30f0673bacc7c8049278dace7ff Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 1 Sep 2020 16:56:12 +0200 Subject: [PATCH 372/503] WIP: prusa-gcodeviewer command line wrapper to start the PrusaSlicer in standalone G-code viewer mode. Linux and OSX stuff will follow. --- resources/icons/PrusaSlicer-gcodeviewer.ico | Bin 0 -> 113976 bytes src/CMakeLists.txt | 13 ++++++++- src/PrusaSlicer.cpp | 4 +++ src/PrusaSlicer_app_msvc.cpp | 5 ++++ src/libslic3r/PrintConfig.cpp | 6 +++++ .../msw/PrusaSlicer-gcodeviewer.rc.in | 25 ++++++++++++++++++ 6 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 resources/icons/PrusaSlicer-gcodeviewer.ico create mode 100644 src/platform/msw/PrusaSlicer-gcodeviewer.rc.in diff --git a/resources/icons/PrusaSlicer-gcodeviewer.ico b/resources/icons/PrusaSlicer-gcodeviewer.ico new file mode 100644 index 0000000000000000000000000000000000000000..2c9272369fd5617beefe7847ab2264a40f033ab4 GIT binary patch literal 113976 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:/DEBUG>") + target_link_options(PrusaSlicer_app_console PUBLIC "$<$:/DEBUG>") endif () target_compile_definitions(PrusaSlicer_app_console PRIVATE -DSLIC3R_WRAPPER_CONSOLE) add_dependencies(PrusaSlicer_app_console PrusaSlicer) set_target_properties(PrusaSlicer_app_console PROPERTIES OUTPUT_NAME "prusa-slicer-console") target_link_libraries(PrusaSlicer_app_console PRIVATE boost_headeronly) + + add_executable(PrusaSlicer_app_gcodeviewer WIN32 PrusaSlicer_app_msvc.cpp ${CMAKE_CURRENT_BINARY_DIR}/PrusaSlicer-gcodeviewer.rc) + # Generate debug symbols even in release mode. + if (MSVC) + target_link_options(PrusaSlicer_app_gcodeviewer PUBLIC "$<$:/DEBUG>") + endif () + target_compile_definitions(PrusaSlicer_app_gcodeviewer PRIVATE -DSLIC3R_WRAPPER_NOCONSOLE -DSLIC3R_WRAPPER_GCODEVIEWER) + add_dependencies(PrusaSlicer_app_gcodeviewer PrusaSlicer) + set_target_properties(PrusaSlicer_app_gcodeviewer PROPERTIES OUTPUT_NAME "prusa-gcodeviewer") + target_link_libraries(PrusaSlicer_app_gcodeviewer PRIVATE boost_headeronly) endif () # Link the resources dir to where Slic3r GUI expects it diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index aaf3db9158..2962f0cdfe 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -101,6 +101,7 @@ int CLI::run(int argc, char **argv) std::find(m_transforms.begin(), m_transforms.end(), "cut") == m_transforms.end() && std::find(m_transforms.begin(), m_transforms.end(), "cut_x") == m_transforms.end() && std::find(m_transforms.begin(), m_transforms.end(), "cut_y") == m_transforms.end(); + bool start_as_gcodeviewer = false; const std::vector &load_configs = m_config.option("load", true)->values; @@ -521,6 +522,9 @@ int CLI::run(int argc, char **argv) << " (" << print.total_extruded_volume()/1000 << "cm3)" << std::endl; */ } + } else if (opt_key == "gcodeviewer") { + start_gui = true; + start_as_gcodeviewer = true; } else { boost::nowide::cerr << "error: option not supported yet: " << opt_key << std::endl; return 1; diff --git a/src/PrusaSlicer_app_msvc.cpp b/src/PrusaSlicer_app_msvc.cpp index 712cff687d..5f12c91479 100644 --- a/src/PrusaSlicer_app_msvc.cpp +++ b/src/PrusaSlicer_app_msvc.cpp @@ -221,6 +221,11 @@ int wmain(int argc, wchar_t **argv) std::vector argv_extended; argv_extended.emplace_back(argv[0]); +#ifdef SLIC3R_WRAPPER_GCODEVIEWER + wchar_t gcodeviewer_param[] = L"--gcodeviewer"; + argv_extended.emplace_back(gcodeviewer_param); +#endif /* SLIC3R_WRAPPER_GCODEVIEWER */ + #ifdef SLIC3R_GUI // Here one may push some additional parameters based on the wrapper type. bool force_mesa = false; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 3401dcc020..770983ad59 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3530,6 +3530,12 @@ CLIActionsConfigDef::CLIActionsConfigDef() def->cli = "export-gcode|gcode|g"; def->set_default_value(new ConfigOptionBool(false)); + def = this->add("gcodeviewer", coBool); + def->label = L("G-code viewer"); + def->tooltip = L("Visualize an already sliced and saved G-code"); + def->cli = "gcodeviewer"; + def->set_default_value(new ConfigOptionBool(false)); + def = this->add("slice", coBool); def->label = L("Slice"); def->tooltip = L("Slice the model as FFF or SLA based on the printer_technology configuration value."); diff --git a/src/platform/msw/PrusaSlicer-gcodeviewer.rc.in b/src/platform/msw/PrusaSlicer-gcodeviewer.rc.in new file mode 100644 index 0000000000..7f4e5a15c3 --- /dev/null +++ b/src/platform/msw/PrusaSlicer-gcodeviewer.rc.in @@ -0,0 +1,25 @@ +1 VERSIONINFO +FILEVERSION @SLIC3R_RC_VERSION@ +PRODUCTVERSION @SLIC3R_RC_VERSION@ +{ + BLOCK "StringFileInfo" + { + BLOCK "040904E4" + { + VALUE "CompanyName", "Prusa Research" + VALUE "FileDescription", "@SLIC3R_APP_NAME@ G-code Viewer" + VALUE "FileVersion", "@SLIC3R_BUILD_ID@" + VALUE "ProductName", "@SLIC3R_APP_NAME@ G-code Viewer" + VALUE "ProductVersion", "@SLIC3R_BUILD_ID@" + VALUE "InternalName", "@SLIC3R_APP_NAME@ G-code Viewer" + VALUE "LegalCopyright", "Copyright \251 2016-2020 Prusa Research, \251 2011-2018 Alessandro Ranelucci" + VALUE "OriginalFilename", "prusa-gcodeviewer.exe" + } + } + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x409, 1252 + } +} +2 ICON "@SLIC3R_RESOURCES_DIR@/icons/PrusaSlicer-gcodeviewer.ico" +1 24 "PrusaSlicer.manifest" From 6033e6990671d2ccc535058b5e0a8f8da862f832 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 1 Sep 2020 17:56:19 +0200 Subject: [PATCH 373/503] filaments selecting: sorting via printer, showing printers for filament --- src/slic3r/GUI/ConfigWizard.cpp | 484 ++++++++++++++++++------ src/slic3r/GUI/ConfigWizard_private.hpp | 150 ++++++-- 2 files changed, 476 insertions(+), 158 deletions(-) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 60323976c8..f8abfb1788 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -561,30 +561,37 @@ const std::string PageMaterials::EMPTY; PageMaterials::PageMaterials(ConfigWizard *parent, Materials *materials, wxString title, wxString shortname, wxString list1name) : ConfigWizardPage(parent, std::move(title), std::move(shortname)) , materials(materials) - , list_l1(new StringList(this)) - , list_l2(new StringList(this)) - , list_l3(new PresetList(this)) + , list_printer(new StringList(this, wxLB_MULTIPLE)) + , list_type(new StringList(this)) + , list_vendor(new StringList(this)) + , list_profile(new PresetList(this)) + , compatible_printers(new wxStaticText(this, wxID_ANY, _(L("")))) { append_spacer(VERTICAL_SPACING); const int em = parent->em_unit(); const int list_h = 30*em; - list_l1->SetMinSize(wxSize(8*em, list_h)); - list_l2->SetMinSize(wxSize(13*em, list_h)); - list_l3->SetMinSize(wxSize(25*em, list_h)); + list_printer->SetWindowStyle(wxLB_EXTENDED); - auto *grid = new wxFlexGridSizer(3, em/2, em); - grid->AddGrowableCol(2, 1); + list_printer->SetMinSize(wxSize(23*em, list_h)); + list_type->SetMinSize(wxSize(8*em, list_h)); + list_vendor->SetMinSize(wxSize(13*em, list_h)); + list_profile->SetMinSize(wxSize(23*em, list_h)); + + grid = new wxFlexGridSizer(4, em/2, em); + grid->AddGrowableCol(3, 1); grid->AddGrowableRow(1, 1); + grid->Add(new wxStaticText(this, wxID_ANY, _(L("Printer:")))); grid->Add(new wxStaticText(this, wxID_ANY, list1name)); grid->Add(new wxStaticText(this, wxID_ANY, _(L("Vendor:")))); grid->Add(new wxStaticText(this, wxID_ANY, _(L("Profile:")))); - grid->Add(list_l1, 0, wxEXPAND); - grid->Add(list_l2, 0, wxEXPAND); - grid->Add(list_l3, 1, wxEXPAND); + grid->Add(list_printer, 0, wxEXPAND); + grid->Add(list_type, 0, wxEXPAND); + grid->Add(list_vendor, 0, wxEXPAND); + grid->Add(list_profile, 1, wxEXPAND); auto *btn_sizer = new wxBoxSizer(wxHORIZONTAL); auto *sel_all = new wxButton(this, wxID_ANY, _(L("All"))); @@ -592,121 +599,342 @@ PageMaterials::PageMaterials(ConfigWizard *parent, Materials *materials, wxStrin btn_sizer->Add(sel_all, 0, wxRIGHT, em / 2); btn_sizer->Add(sel_none); + grid->Add(new wxBoxSizer(wxHORIZONTAL)); grid->Add(new wxBoxSizer(wxHORIZONTAL)); grid->Add(btn_sizer, 0, wxALIGN_RIGHT); + + auto* notes_sizer = new wxBoxSizer(wxHORIZONTAL); + notes_sizer->Add(compatible_printers); + grid->Add(notes_sizer); + append(grid, 1, wxEXPAND); - list_l1->Bind(wxEVT_LISTBOX, [this](wxCommandEvent &) { - update_lists(list_l1->GetSelection(), list_l2->GetSelection()); + list_printer->Bind(wxEVT_LISTBOX, [this](wxCommandEvent& evt) { + update_lists(evt.GetInt(), list_type->GetSelection(), list_vendor->GetSelection()); + }); + list_type->Bind(wxEVT_LISTBOX, [this](wxCommandEvent &) { + update_lists(list_printer->GetSelection(), list_type->GetSelection(), list_vendor->GetSelection()); }); - list_l2->Bind(wxEVT_LISTBOX, [this](wxCommandEvent &) { - update_lists(list_l1->GetSelection(), list_l2->GetSelection()); + list_vendor->Bind(wxEVT_LISTBOX, [this](wxCommandEvent &) { + update_lists(list_printer->GetSelection(), list_type->GetSelection(), list_vendor->GetSelection()); }); - list_l3->Bind(wxEVT_CHECKLISTBOX, [this](wxCommandEvent &evt) { select_material(evt.GetInt()); }); + list_profile->Bind(wxEVT_CHECKLISTBOX, [this](wxCommandEvent &evt) { select_material(evt.GetInt()); }); + list_profile->Bind(wxEVT_LISTBOX, [this](wxCommandEvent& evt) { on_material_highlighted(evt.GetInt()); }); sel_all->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { select_all(true); }); sel_none->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { select_all(false); }); + Bind(wxEVT_PAINT, [this](wxPaintEvent& evt) {on_paint();}); + + list_profile->Bind(wxEVT_MOTION, [this](wxMouseEvent& evt) { on_mouse_move_on_profiles(evt); }); + list_profile->Bind(wxEVT_ENTER_WINDOW, [this](wxMouseEvent& evt) { on_mouse_enter_profiles(evt); }); + list_profile->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& evt) { on_mouse_leave_profiles(evt); }); + reload_presets(); } - +void PageMaterials::on_paint() +{ + if (first_paint) { + first_paint = false; + prepare_compatible_printers_label(); + } +} +void PageMaterials::on_mouse_move_on_profiles(wxMouseEvent& evt) +{ + const wxClientDC dc(list_profile); + const wxPoint pos = evt.GetLogicalPosition(dc); + int item = list_profile->HitTest(pos); + BOOST_LOG_TRIVIAL(error) << "hit test: " << item; + on_material_hovered(item); +} +void PageMaterials::on_mouse_enter_profiles(wxMouseEvent& evt) +{} +void PageMaterials::on_mouse_leave_profiles(wxMouseEvent& evt) +{ + on_material_hovered(-1); +} void PageMaterials::reload_presets() { clear(); - list_l1->append(_(L("(All)")), &EMPTY); + list_printer->append(_(L("(All)")), &EMPTY); + list_printer->SetLabelMarkup("bald"); + for (const Preset* printer : materials->printers) { + list_printer->append(printer->name, &printer->name); + } - for (const std::string &type : materials->types) { - list_l1->append(type, &type); - } - - if (list_l1->GetCount() > 0) { - list_l1->SetSelection(0); - sel1_prev = wxNOT_FOUND; - sel2_prev = wxNOT_FOUND; - update_lists(0, 0); + if (list_printer->GetCount() > 0) { + list_printer->SetSelection(0); + sel_printer_prev = wxNOT_FOUND; + sel_type_prev = wxNOT_FOUND; + sel_vendor_prev = wxNOT_FOUND; + update_lists(0, 0, 0); } presets_loaded = true; } -void PageMaterials::update_lists(int sel1, int sel2) +void PageMaterials::prepare_compatible_printers_label() { - wxWindowUpdateLocker freeze_guard(this); - (void)freeze_guard; - - if (sel1 != sel1_prev) { - // Refresh the second list - - // XXX: The vendor list is created with quadratic complexity here, - // but the number of vendors is going to be very small this shouldn't be a problem. - - list_l2->Clear(); - list_l2->append(_(L("(All)")), &EMPTY); - if (sel1 != wxNOT_FOUND) { - const std::string &type = list_l1->get_data(sel1); - - materials->filter_presets(type, EMPTY, [this](const Preset *p) { - const std::string &vendor = this->materials->get_vendor(p); - - if (list_l2->find(vendor) == wxNOT_FOUND) { - list_l2->append(vendor, &vendor); - } - }); - } - - sel1_prev = sel1; - sel2 = 0; - sel2_prev = wxNOT_FOUND; - list_l2->SetSelection(sel2); - list_l3->Clear(); + assert(grid->GetColWidths().size() == 4); + compatible_printers_width = grid->GetColWidths()[3]; + empty_printers_label = "Compatible printers:"; + for (const Preset* printer : materials->printers) { + empty_printers_label += "\n"; } + clear_compatible_printers_label(); +} - if (sel2 != sel2_prev) { - // Refresh the third list +void PageMaterials::clear_compatible_printers_label() +{ + compatible_printers->SetLabel(boost::nowide::widen(empty_printers_label)); + compatible_printers->Wrap(compatible_printers_width); + Layout(); +} - list_l3->Clear(); - if (sel1 != wxNOT_FOUND && sel2 != wxNOT_FOUND) { - const std::string &type = list_l1->get_data(sel1); - const std::string &vendor = list_l2->get_data(sel2); - - materials->filter_presets(type, vendor, [this](const Preset *p) { - bool was_checked = false; - - int cur_i = list_l3->find(p->alias); - if (cur_i == wxNOT_FOUND) - cur_i = list_l3->append(p->alias, &p->alias); +void PageMaterials::on_material_hovered(int sel_material) +{ + if ( sel_material == last_hovered_item) + return; + if (sel_material == -1) { + clear_compatible_printers_label(); + return; + } + last_hovered_item = sel_material; + std::string compatible_printers_label = "compatible printers:\n"; + //selected material string + std::string material_name = list_profile->get_data(sel_material); + // get material preset + const std::vector matching_materials = materials->get_presets_by_alias(material_name); + if (matching_materials.empty()) + { + clear_compatible_printers_label(); + return; + } + //find matching printers + bool first = true; + for (const Preset* printer : materials->printers) { + bool compatible = false; + for (const Preset* material : matching_materials) { + if (is_compatible_with_printer(PresetWithVendorProfile(*material, material->vendor), PresetWithVendorProfile(*printer, printer->vendor))) { + if (first) + first = false; else - was_checked = list_l3->IsChecked(cur_i); - - const std::string& section = materials->appconfig_section(); - - const bool checked = wizard_p()->appconfig_new.has(section, p->name); - list_l3->Check(cur_i, checked | was_checked); - - /* Update preset selection in config. - * If one preset from aliases bundle is selected, - * than mark all presets with this aliases as selected - * */ - if (checked && !was_checked) - wizard_p()->update_presets_in_config(section, p->alias, true); - else if (!checked && was_checked) - wizard_p()->appconfig_new.set(section, p->name, "1"); - } ); + compatible_printers_label += "\n";//", "; + compatible_printers_label += printer->name; + compatible = true; + break; + } } - - sel2_prev = sel2; } + this->compatible_printers->SetLabel(boost::nowide::widen(compatible_printers_label)); + this->compatible_printers->Wrap(compatible_printers_width); +} + +void PageMaterials::on_material_highlighted(int sel_material) +{ + wxWindowUpdateLocker freeze_guard(this); + (void)freeze_guard; + + //std::string compatible_printers_label = "compatible printers:\n"; + //std::string empty_suplement = std::string(); + //unselect all printers + list_printer->SetSelection(wxNOT_FOUND); + //selected material string + std::string material_name = list_profile->get_data(sel_material); + // get material preset + const std::vector matching_materials = materials->get_presets_by_alias(material_name); + if (matching_materials.empty()) + return; + //find matching printers + //bool first = true; + for (const Preset* printer : materials->printers) { + bool compatible = false; + for (const Preset* material : matching_materials) { + if (is_compatible_with_printer(PresetWithVendorProfile(*material, material->vendor), PresetWithVendorProfile(*printer, printer->vendor))) { + //select printer + int index = list_printer->find(printer->name); + list_printer->SetSelection(index); + /*if (first) + first = false; + else + compatible_printers_label += "\n";//", "; + compatible_printers_label += printer->name; + compatible = true; + break;*/ + } + } + //if(!compatible) + // empty_suplement += std::string(printer->name.length() + 2, ' '); + } + // fill rest of label with blanks so it maintains legth + //compatible_printers_label += empty_suplement; + + update_lists(0,0,0); + list_profile->SetSelection(list_profile->find(material_name)); + + //this->compatible_printers->SetLabel(boost::nowide::widen(compatible_printers_label)); + //this->compatible_printers->Wrap(compatible_printers_width); + //Refresh(); +} + +void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor) +{ + wxWindowUpdateLocker freeze_guard(this); + (void)freeze_guard; + + wxArrayInt sel_printers; + int sel_printers_count = list_printer->GetSelections(sel_printers); + + if (sel_printers_count != sel_printer_prev) { + // Refresh type list + list_type->Clear(); + list_type->append(_(L("(All)")), &EMPTY); + if (sel_printers_count > 0) { + // If all is selected with other printers + // unselect "all" or all printers depending on last value + if (sel_printers[0] == 0 && sel_printers_count > 1) { + if (sel_printer == 0) { + list_printer->SetSelection(wxNOT_FOUND); + list_printer->SetSelection(0); + } else { + list_printer->SetSelection(0, false); + sel_printers_count = list_printer->GetSelections(sel_printers); + } + } + if (sel_printers[0] != 0) { + for (size_t i = 0; i < sel_printers_count; i++) { + const std::string& printer_name = list_printer->get_data(sel_printers[i]); + const Preset* printer = nullptr; + for (const Preset* it : materials->printers) { + if (it->name == printer_name) { + printer = it; + break; + } + } + materials->filter_presets(printer, EMPTY, EMPTY, [this](const Preset* p) { + const std::string& type = this->materials->get_type(p); + if (list_type->find(type) == wxNOT_FOUND) { + list_type->append(type, &type); + } + }); + } + } else { + //clear selection except "ALL" + list_printer->SetSelection(wxNOT_FOUND); + list_printer->SetSelection(0); + + materials->filter_presets(nullptr, EMPTY, EMPTY, [this](const Preset* p) { + const std::string& type = this->materials->get_type(p); + if (list_type->find(type) == wxNOT_FOUND) { + list_type->append(type, &type); + } + }); + } + + } + + sel_printer_prev = sel_printers_count; + sel_type = 0; + sel_type_prev = wxNOT_FOUND; + list_type->SetSelection(sel_type); + list_profile->Clear(); + } + + if (sel_type != sel_type_prev) { + // Refresh vendor list + + // XXX: The vendor list is created with quadratic complexity here, + // but the number of vendors is going to be very small this shouldn't be a problem. + + list_vendor->Clear(); + list_vendor->append(_(L("(All)")), &EMPTY); + if (sel_printers_count != 0 && sel_type != wxNOT_FOUND) { + const std::string& type = list_type->get_data(sel_type); + // find printer preset + for (size_t i = 0; i < sel_printers_count; i++) { + const std::string& printer_name = list_printer->get_data(sel_printers[i]); + const Preset* printer = nullptr; + for (const Preset* it : materials->printers) { + if (it->name == printer_name) { + printer = it; + break; + } + } + materials->filter_presets(printer, type, EMPTY, [this](const Preset* p) { + const std::string& vendor = this->materials->get_vendor(p); + if (list_vendor->find(vendor) == wxNOT_FOUND) { + list_vendor->append(vendor, &vendor); + } + }); + } + } + + sel_type_prev = sel_type; + sel_vendor = 0; + sel_vendor_prev = wxNOT_FOUND; + list_vendor->SetSelection(sel_vendor); + list_profile->Clear(); + } + + if (sel_vendor != sel_vendor_prev) { + // Refresh material list + + list_profile->Clear(); + clear_compatible_printers_label(); + if (sel_printers_count != 0 && sel_type != wxNOT_FOUND && sel_vendor != wxNOT_FOUND) { + const std::string& type = list_type->get_data(sel_type); + const std::string& vendor = list_vendor->get_data(sel_vendor); + // finst printer preset + for (size_t i = 0; i < sel_printers_count; i++) { + const std::string& printer_name = list_printer->get_data(sel_printers[i]); + const Preset* printer = nullptr; + for (const Preset* it : materials->printers) { + if (it->name == printer_name) { + printer = it; + break; + } + } + + materials->filter_presets(printer, type, vendor, [this](const Preset* p) { + bool was_checked = false; + //size_t printer_counter = materials->get_printer_counter(p); + int cur_i = list_profile->find(p->alias); + if (cur_i == wxNOT_FOUND) + //cur_i = list_profile->append(p->alias + " " + std::to_string(printer_counter)/*+ (omnipresent ? "" : " ONLY SOME PRINTERS")*/, &p->alias); + cur_i = list_profile->append(p->alias + (materials->get_omnipresent(p) ? "" : " *"), &p->alias); + else + was_checked = list_profile->IsChecked(cur_i); + + const std::string& section = materials->appconfig_section(); + + const bool checked = wizard_p()->appconfig_new.has(section, p->name); + list_profile->Check(cur_i, checked | was_checked); + + /* Update preset selection in config. + * If one preset from aliases bundle is selected, + * than mark all presets with this aliases as selected + * */ + if (checked && !was_checked) + wizard_p()->update_presets_in_config(section, p->alias, true); + else if (!checked && was_checked) + wizard_p()->appconfig_new.set(section, p->name, "1"); + }); + } + } + + sel_vendor_prev = sel_vendor; + } } void PageMaterials::select_material(int i) { - const bool checked = list_l3->IsChecked(i); + const bool checked = list_profile->IsChecked(i); - const std::string& alias_key = list_l3->get_data(i); + const std::string& alias_key = list_profile->get_data(i); wizard_p()->update_presets_in_config(materials->appconfig_section(), alias_key, checked); } @@ -715,10 +943,10 @@ void PageMaterials::select_all(bool select) wxWindowUpdateLocker freeze_guard(this); (void)freeze_guard; - for (unsigned i = 0; i < list_l3->GetCount(); i++) { - const bool current = list_l3->IsChecked(i); + for (unsigned i = 0; i < list_profile->GetCount(); i++) { + const bool current = list_profile->IsChecked(i); if (current != select) { - list_l3->Check(i, select); + list_profile->Check(i, select); select_material(i); } } @@ -726,11 +954,13 @@ void PageMaterials::select_all(bool select) void PageMaterials::clear() { - list_l1->Clear(); - list_l2->Clear(); - list_l3->Clear(); - sel1_prev = wxNOT_FOUND; - sel2_prev = wxNOT_FOUND; + list_printer->Clear(); + list_type->Clear(); + list_vendor->Clear(); + list_profile->Clear(); + sel_printer_prev = wxNOT_FOUND; + sel_type_prev = wxNOT_FOUND; + sel_vendor_prev = wxNOT_FOUND; presets_loaded = false; } @@ -740,6 +970,7 @@ void PageMaterials::on_activate() wizard_p()->update_materials(materials->technology); reload_presets(); } + first_paint = true; } @@ -1314,16 +1545,22 @@ const std::string Materials::UNKNOWN = "(Unknown)"; void Materials::push(const Preset *preset) { - presets.push_back(preset); + presets.emplace_back(preset, 0); types.insert(technology & T_FFF ? Materials::get_filament_type(preset) : Materials::get_material_type(preset)); } +void Materials::add_printer(const Preset* preset) +{ + printers.insert(preset); +} + void Materials::clear() { presets.clear(); types.clear(); + printers.clear(); } const std::string& Materials::appconfig_section() const @@ -1373,7 +1610,6 @@ const std::string& Materials::get_material_vendor(const Preset *preset) return opt != nullptr ? opt->value : UNKNOWN; } - // priv static const std::unordered_map> legacy_preset_map {{ @@ -1601,26 +1837,28 @@ void ConfigWizard::priv::update_materials(Technology technology) if (any_fff_selected && (technology & T_FFF)) { filaments.clear(); aliases_fff.clear(); - // Iterate filaments in all bundles for (const auto &pair : bundles) { for (const auto &filament : pair.second.preset_bundle->filaments) { // Check if filament is already added - if (filaments.containts(&filament)) - continue; + if (filaments.containts(&filament)) + continue; // Iterate printers in all bundles - // For now, we only allow the profiles to be compatible with another profiles inside the same bundle. -// for (const auto &pair : bundles) - for (const auto &printer : pair.second.preset_bundle->printers) - // Filter out inapplicable printers - if (printer.is_visible && printer.printer_technology() == ptFFF && - is_compatible_with_printer(PresetWithVendorProfile(filament, nullptr), PresetWithVendorProfile(printer, nullptr)) && - // Check if filament is already added - ! filaments.containts(&filament)) { - filaments.push(&filament); - if (!filament.alias.empty()) - aliases_fff[filament.alias].insert(filament.name); - } + for (const auto &printer : pair.second.preset_bundle->printers) { + if (!printer.is_visible || printer.printer_technology() != ptFFF) + continue; + // Filter out inapplicable printers + if (is_compatible_with_printer(PresetWithVendorProfile(filament, filament.vendor), PresetWithVendorProfile(printer, printer.vendor))) { + if (!filaments.containts(&filament)) { + filaments.push(&filament); + if (!filament.alias.empty()) + aliases_fff[filament.alias].insert(filament.name); + } + filaments.add_printer_counter(&filament); + filaments.add_printer(&printer); + } + } + } } } @@ -1637,17 +1875,21 @@ void ConfigWizard::priv::update_materials(Technology technology) continue; // Iterate printers in all bundles // For now, we only allow the profiles to be compatible with another profiles inside the same bundle. -// for (const auto &pair : bundles) - for (const auto &printer : pair.second.preset_bundle->printers) - // Filter out inapplicable printers - if (printer.is_visible && printer.printer_technology() == ptSLA && - is_compatible_with_printer(PresetWithVendorProfile(material, nullptr), PresetWithVendorProfile(printer, nullptr)) && - // Check if material is already added - ! sla_materials.containts(&material)) { + for (const auto& printer : pair.second.preset_bundle->printers) { + if(!printer.is_visible || printer.printer_technology() != ptSLA) + continue; + // Filter out inapplicable printers + if (is_compatible_with_printer(PresetWithVendorProfile(material, nullptr), PresetWithVendorProfile(printer, nullptr))) { + // Check if material is already added + if(!sla_materials.containts(&material)) { sla_materials.push(&material); if (!material.alias.empty()) aliases_sla[material.alias].insert(material.name); } + sla_materials.add_printer_counter(&material); + sla_materials.add_printer(&printer); + } + } } } } diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index 9921552a73..f7987a8908 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -57,32 +57,98 @@ enum Technology { T_ANY = ~0, }; +struct Bundle +{ + std::unique_ptr preset_bundle; + VendorProfile* vendor_profile{ nullptr }; + bool is_in_resources{ false }; + bool is_prusa_bundle{ false }; + + Bundle() = default; + Bundle(Bundle&& other); + + // Returns false if not loaded. Reason for that is logged as boost::log error. + bool load(fs::path source_path, bool is_in_resources, bool is_prusa_bundle = false); + + const std::string& vendor_id() const { return vendor_profile->id; } +}; + +struct BundleMap : std::unordered_map +{ + static BundleMap load(); + + Bundle& prusa_bundle(); + const Bundle& prusa_bundle() const; +}; + struct Materials { Technology technology; // use vector for the presets to purpose of save of presets sorting in the bundle - std::vector presets; + // bool is true if material is present in all printers (omnipresent) + // size_t is counter of printers compatible with material + std::vector> presets; std::set types; + std::set printers; Materials(Technology technology) : technology(technology) {} void push(const Preset *preset); + void add_printer(const Preset* preset); void clear(); bool containts(const Preset *preset) const { - return std::find(presets.begin(), presets.end(), preset) != presets.end(); + //return std::find(presets.begin(), presets.end(), preset) != presets.end(); + return std::find_if(presets.begin(), presets.end(), + [preset](const std::pair& element) { return element.first == preset; }) != presets.end(); + } + + bool get_omnipresent(const Preset* preset) { + return get_printer_counter(preset) == printers.size(); + } + + const std::vector get_presets_by_alias(const std::string name) { + std::vector ret_vec; + for (auto it = presets.begin(); it != presets.end(); ++it) { + if ((*it).first->alias == name) + ret_vec.push_back((*it).first); + } + return ret_vec; + } + + void add_printer_counter(const Preset* preset) { + for (auto it = presets.begin(); it != presets.end(); ++it) { + if ((*it).first->alias == preset->alias) + (*it).second += 1; + } + } + + size_t get_printer_counter(const Preset* preset) { + size_t highest = 0; + for (auto it : presets) { + if (it.first->alias == preset->alias && it.second > highest) + highest = it.second; + } + return highest; + } const std::string& appconfig_section() const; const std::string& get_type(const Preset *preset) const; const std::string& get_vendor(const Preset *preset) const; + - template void filter_presets(const std::string &type, const std::string &vendor, F cb) { - for (const Preset *preset : presets) { - if ((type.empty() || get_type(preset) == type) && (vendor.empty() || get_vendor(preset) == vendor)) { - cb(preset); - } - } - } + template void filter_presets(const Preset* printer, const std::string& type, const std::string& vendor, F cb) { + for (auto preset : presets) { + const Preset& prst = *(preset.first); + const Preset& prntr = *printer; + if ((printer == nullptr || is_compatible_with_printer(PresetWithVendorProfile(prst, prst.vendor), PresetWithVendorProfile(prntr, prntr.vendor))) && + (type.empty() || get_type(preset.first) == type) && + (vendor.empty() || get_vendor(preset.first) == vendor)) { + + cb(preset.first); + } + } + } static const std::string UNKNOWN; static const std::string& get_filament_type(const Preset *preset); @@ -91,33 +157,9 @@ struct Materials static const std::string& get_material_vendor(const Preset *preset); }; -struct Bundle -{ - std::unique_ptr preset_bundle; - VendorProfile *vendor_profile { nullptr }; - bool is_in_resources { false }; - bool is_prusa_bundle { false }; - - Bundle() = default; - Bundle(Bundle &&other); - - // Returns false if not loaded. Reason for that is logged as boost::log error. - bool load(fs::path source_path, bool is_in_resources, bool is_prusa_bundle = false); - - const std::string& vendor_id() const { return vendor_profile->id; } -}; - -struct BundleMap: std::unordered_map -{ - static BundleMap load(); - - Bundle& prusa_bundle(); - const Bundle& prusa_bundle() const; -}; struct PrinterPickerEvent; - // GUI elements typedef std::function ModelFilter; @@ -225,6 +267,7 @@ struct PagePrinters: ConfigWizardPage template struct DataList : public T { DataList(wxWindow *parent) : T(parent, wxID_ANY) {} + DataList(wxWindow* parent, int style) : T(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, style) {} // Note: We're _not_ using wxLB_SORT here because it doesn't do the right thing, // eg. "ABS" is sorted before "(All)" @@ -252,6 +295,25 @@ template struct DataList : public T } int size() { return this->GetCount(); } + + void on_mouse_move(const wxPoint& position) { + int item = T::HitTest(position); + + if(item == wxHitTest::wxHT_WINDOW_INSIDE) + BOOST_LOG_TRIVIAL(error) << "hit test wxHT_WINDOW_INSIDE"; + else if (item == wxHitTest::wxHT_WINDOW_OUTSIDE) + BOOST_LOG_TRIVIAL(error) << "hit test wxHT_WINDOW_OUTSIDE"; + else if(item == wxHitTest::wxHT_WINDOW_CORNER) + BOOST_LOG_TRIVIAL(error) << "hit test wxHT_WINDOW_CORNER"; + else if (item == wxHitTest::wxHT_WINDOW_VERT_SCROLLBAR) + BOOST_LOG_TRIVIAL(error) << "hit test wxHT_WINDOW_VERT_SCROLLBAR"; + else if (item == wxHitTest::wxHT_NOWHERE) + BOOST_LOG_TRIVIAL(error) << "hit test wxHT_NOWHERE"; + else if (item == wxHitTest::wxHT_MAX) + BOOST_LOG_TRIVIAL(error) << "hit test wxHT_MAX"; + else + BOOST_LOG_TRIVIAL(error) << "hit test: " << item; + } }; typedef DataList StringList; @@ -260,21 +322,35 @@ typedef DataList PresetList; struct PageMaterials: ConfigWizardPage { Materials *materials; - StringList *list_l1, *list_l2; - PresetList *list_l3; - int sel1_prev, sel2_prev; + StringList *list_printer, *list_type, *list_vendor; + PresetList *list_profile; + int sel_printer_prev, sel_type_prev, sel_vendor_prev; bool presets_loaded; + wxFlexGridSizer *grid; + wxStaticText *compatible_printers; + int compatible_printers_width = { 100 }; + std::string empty_printers_label; + bool first_paint = { false }; static const std::string EMPTY; + int last_hovered_item = { -1 } ; PageMaterials(ConfigWizard *parent, Materials *materials, wxString title, wxString shortname, wxString list1name); void reload_presets(); - void update_lists(int sel1, int sel2); + void update_lists(int sel1, int sel2, int sel3); + void on_material_highlighted(int sel_material); + void on_material_hovered(int sel_material); void select_material(int i); void select_all(bool select); void clear(); + void prepare_compatible_printers_label(); + void clear_compatible_printers_label(); + void on_paint(); + void on_mouse_move_on_profiles(wxMouseEvent& evt); + void on_mouse_enter_profiles(wxMouseEvent& evt); + void on_mouse_leave_profiles(wxMouseEvent& evt); virtual void on_activate() override; }; From 1eef1d32a08bf12c99d088d2783fcfe53d8a70bc Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 1 Sep 2020 18:04:56 +0200 Subject: [PATCH 374/503] Added two missing includes to fix build on gcc --- src/libslic3r/GCode/GCodeProcessor.cpp | 1 + src/slic3r/GUI/GLShader.hpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 54addbd979..13b1ed1a8d 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include diff --git a/src/slic3r/GUI/GLShader.hpp b/src/slic3r/GUI/GLShader.hpp index a1160f8e98..84fdf5ebad 100644 --- a/src/slic3r/GUI/GLShader.hpp +++ b/src/slic3r/GUI/GLShader.hpp @@ -4,6 +4,8 @@ #include #include +#include "libslic3r/Point.hpp" + namespace Slic3r { class GLShaderProgram From 761e71eb634e3af52da6b5ee3cb0f06e96d4892a Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 18 Aug 2020 13:45:18 +0200 Subject: [PATCH 375/503] Fix build on msvc --- src/libslic3r/Point.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index 84010c7eb1..30a1a4942c 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -88,7 +88,7 @@ inline std::string to_string(const Vec3d &pt) { return std::string("[") + std: std::vector transform(const std::vector& points, const Transform3f& t); Pointf3s transform(const Pointf3s& points, const Transform3d& t); -template using Vec = Eigen::Matrix; +template using Vec = Eigen::Matrix; class Point : public Vec2crd { From 255469347f92a8342974fe4a61ee6cd04c6af3bd Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 19 Aug 2020 17:15:01 +0200 Subject: [PATCH 376/503] Fixed several indentation-related warnings --- src/libslic3r/PrintBase.hpp | 12 ++++++------ src/slic3r/GUI/Plater.cpp | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index 5e94e011a7..647c24c1ce 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -507,9 +507,9 @@ protected: bool set_started(PrintStepEnum step) { return m_state.set_started(step, this->state_mutex(), [this](){ this->throw_if_canceled(); }); } PrintStateBase::TimeStamp set_done(PrintStepEnum step) { std::pair status = m_state.set_done(step, this->state_mutex(), [this](){ this->throw_if_canceled(); }); - if (status.second) - this->status_update_warnings(this->id(), static_cast(step), PrintStateBase::WarningLevel::NON_CRITICAL, std::string()); - return status.first; + if (status.second) + this->status_update_warnings(this->id(), static_cast(step), PrintStateBase::WarningLevel::NON_CRITICAL, std::string()); + return status.first; } bool invalidate_step(PrintStepEnum step) { return m_state.invalidate(step, this->cancel_callback()); } @@ -556,9 +556,9 @@ protected: { return m_state.set_started(step, PrintObjectBase::state_mutex(m_print), [this](){ this->throw_if_canceled(); }); } PrintStateBase::TimeStamp set_done(PrintObjectStepEnum step) { std::pair status = m_state.set_done(step, PrintObjectBase::state_mutex(m_print), [this](){ this->throw_if_canceled(); }); - if (status.second) - this->status_update_warnings(m_print, static_cast(step), PrintStateBase::WarningLevel::NON_CRITICAL, std::string()); - return status.first; + if (status.second) + this->status_update_warnings(m_print, static_cast(step), PrintStateBase::WarningLevel::NON_CRITICAL, std::string()); + return status.first; } bool invalidate_step(PrintObjectStepEnum step) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 2c330b60e6..027611750a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2841,7 +2841,7 @@ void Plater::priv::export_gcode(fs::path output_path, bool output_path_on_remova if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) != 0) return; - show_warning_dialog = true; + show_warning_dialog = true; if (! output_path.empty()) { background_process.schedule_export(output_path.string(), output_path_on_removable_media); } else { @@ -4697,8 +4697,8 @@ void Plater::export_gcode(bool prefer_removable) if (p->model.objects.empty()) return; - if (p->process_completed_with_error)//here - return; + if (p->process_completed_with_error)//here + return; // If possible, remove accents from accented latin characters. // This function is useful for generating file names to be processed by legacy firmwares. From d3e7684a5a47e34cb2cf94f194b75158bfe07068 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 18 Aug 2020 15:17:26 +0200 Subject: [PATCH 377/503] Forbid translation of objects when SLA/Hollow/FDM gizmos are active --- src/slic3r/GUI/GLCanvas3D.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 9bed5fde7c..94f6f6ef32 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3658,6 +3658,13 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { m_mouse.dragging = true; + // Translation of objects is forbidden when SLA supports/hollowing/fdm + // supports gizmo is active. + if (m_gizmos.get_current_type() == GLGizmosManager::SlaSupports + || m_gizmos.get_current_type() == GLGizmosManager::FdmSupports + || m_gizmos.get_current_type() == GLGizmosManager::Hollow) + return; + Vec3d cur_pos = m_mouse.drag.start_position_3D; // we do not want to translate objects if the user just clicked on an object while pressing shift to remove it from the selection and then drag if (m_selection.contains_volume(get_first_hover_volume_idx())) From 223eb6933c602a1c01fc2c14f3092504b05858f8 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 18 Aug 2020 15:18:00 +0200 Subject: [PATCH 378/503] TriangleSelector paints continuously when dragging fast Previously there would be distinct circles with gaps in between --- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 195 +++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp | 1 + 2 files changed, 110 insertions(+), 86 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index f3b6db4f26..62854ab465 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -314,106 +314,128 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous const ModelInstance* mi = mo->instances[selection.get_instance_idx()]; const Transform3d& instance_trafo = mi->get_transformation().get_matrix(); - std::vector>> hit_positions_and_facet_ids; - bool clipped_mesh_was_hit = false; + // List of mouse positions that will be used as seeds for painting. + std::vector mouse_positions{mouse_position}; - Vec3f normal = Vec3f::Zero(); - Vec3f hit = Vec3f::Zero(); - size_t facet = 0; - Vec3f closest_hit = Vec3f::Zero(); - double closest_hit_squared_distance = std::numeric_limits::max(); - size_t closest_facet = 0; - int closest_hit_mesh_id = -1; + // In case current mouse position is far from the last one, + // add several positions from between into the list, so there + // are no gaps in the painted region. + { + if (m_last_mouse_position == Vec2d::Zero()) + m_last_mouse_position = mouse_position; + // resolution describes minimal distance limit using circle radius + // as a unit (e.g., 2 would mean the patches will be touching). + double resolution = 0.7; + double diameter_px = resolution * m_cursor_radius * camera.get_zoom(); + int patches_in_between = int(((mouse_position - m_last_mouse_position).norm() - diameter_px) / diameter_px); + if (patches_in_between > 0) { + Vec2d diff = (mouse_position - m_last_mouse_position)/(patches_in_between+1); + for (int i=1; i<=patches_in_between; ++i) + mouse_positions.emplace_back(m_last_mouse_position + i*diff); + } + } + m_last_mouse_position = Vec2d::Zero(); // only actual hits should be saved - // Transformations of individual meshes - std::vector trafo_matrices; + // Now "click" into all the prepared points and spill paint around them. + for (const Vec2d& mp : mouse_positions) { + std::vector>> hit_positions_and_facet_ids; + bool clipped_mesh_was_hit = false; - int mesh_id = -1; - // Cast a ray on all meshes, pick the closest hit and save it for the respective mesh - for (const ModelVolume* mv : mo->volumes) { - if (! mv->is_model_part()) - continue; + Vec3f normal = Vec3f::Zero(); + Vec3f hit = Vec3f::Zero(); + size_t facet = 0; + Vec3f closest_hit = Vec3f::Zero(); + double closest_hit_squared_distance = std::numeric_limits::max(); + size_t closest_facet = 0; + int closest_hit_mesh_id = -1; - ++mesh_id; + // Transformations of individual meshes + std::vector trafo_matrices; - trafo_matrices.push_back(instance_trafo * mv->get_matrix()); - hit_positions_and_facet_ids.push_back(std::vector>()); - - if (m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh( - mouse_position, - trafo_matrices[mesh_id], - camera, - hit, - normal, - m_clipping_plane.get(), - &facet)) - { - // In case this hit is clipped, skip it. - if (is_mesh_point_clipped(hit.cast())) { - clipped_mesh_was_hit = true; + int mesh_id = -1; + // Cast a ray on all meshes, pick the closest hit and save it for the respective mesh + for (const ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) continue; - } - // Is this hit the closest to the camera so far? - double hit_squared_distance = (camera.get_position()-trafo_matrices[mesh_id]*hit.cast()).squaredNorm(); - if (hit_squared_distance < closest_hit_squared_distance) { - closest_hit_squared_distance = hit_squared_distance; - closest_facet = facet; - closest_hit_mesh_id = mesh_id; - closest_hit = hit; + ++mesh_id; + + trafo_matrices.push_back(instance_trafo * mv->get_matrix()); + hit_positions_and_facet_ids.push_back(std::vector>()); + + if (m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh( + mp, + trafo_matrices[mesh_id], + camera, + hit, + normal, + m_clipping_plane.get(), + &facet)) + { + // In case this hit is clipped, skip it. + if (is_mesh_point_clipped(hit.cast())) { + clipped_mesh_was_hit = true; + continue; + } + + // Is this hit the closest to the camera so far? + double hit_squared_distance = (camera.get_position()-trafo_matrices[mesh_id]*hit.cast()).squaredNorm(); + if (hit_squared_distance < closest_hit_squared_distance) { + closest_hit_squared_distance = hit_squared_distance; + closest_facet = facet; + closest_hit_mesh_id = mesh_id; + closest_hit = hit; + } } } - } - bool dragging_while_painting = (action == SLAGizmoEventType::Dragging && m_button_down != Button::None); + bool dragging_while_painting = (action == SLAGizmoEventType::Dragging && m_button_down != Button::None); - // The mouse button click detection is enabled when there is a valid hit - // or when the user clicks the clipping plane. Missing the object entirely - // shall not capture the mouse. - if (closest_hit_mesh_id != -1 || clipped_mesh_was_hit) { - if (m_button_down == Button::None) - m_button_down = ((action == SLAGizmoEventType::LeftDown) ? Button::Left : Button::Right); - } - - if (closest_hit_mesh_id == -1) { - // In case we have no valid hit, we can return. The event will - // be stopped in following two cases: - // 1. clicking the clipping plane - // 2. dragging while painting (to prevent scene rotations and moving the object) - return clipped_mesh_was_hit - || dragging_while_painting; - } - - // Find respective mesh id. - // FIXME We need a separate TriangleSelector for each volume mesh. - mesh_id = -1; - //const TriangleMesh* mesh = nullptr; - for (const ModelVolume* mv : mo->volumes) { - if (! mv->is_model_part()) - continue; - ++mesh_id; - if (mesh_id == closest_hit_mesh_id) { - //mesh = &mv->mesh(); - break; + // The mouse button click detection is enabled when there is a valid hit + // or when the user clicks the clipping plane. Missing the object entirely + // shall not capture the mouse. + if (closest_hit_mesh_id != -1 || clipped_mesh_was_hit) { + if (m_button_down == Button::None) + m_button_down = ((action == SLAGizmoEventType::LeftDown) ? Button::Left : Button::Right); } + + if (closest_hit_mesh_id == -1) { + // In case we have no valid hit, we can return. The event will + // be stopped in following two cases: + // 1. clicking the clipping plane + // 2. dragging while painting (to prevent scene rotations and moving the object) + return clipped_mesh_was_hit + || dragging_while_painting; + } + + // Find respective mesh id. + mesh_id = -1; + for (const ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) + continue; + ++mesh_id; + if (mesh_id == closest_hit_mesh_id) + break; + } + + const Transform3d& trafo_matrix = trafo_matrices[mesh_id]; + + // Calculate how far can a point be from the line (in mesh coords). + // FIXME: The scaling of the mesh can be non-uniform. + const Vec3d sf = Geometry::Transformation(trafo_matrix).get_scaling_factor(); + const float avg_scaling = (sf(0) + sf(1) + sf(2))/3.; + const float limit = m_cursor_radius/avg_scaling; + + // Calculate direction from camera to the hit (in mesh coords): + Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast(); + Vec3f dir = (closest_hit - camera_pos).normalized(); + + assert(mesh_id < int(m_triangle_selectors.size())); + m_triangle_selectors[mesh_id]->select_patch(closest_hit, closest_facet, camera_pos, + dir, limit, new_state); + m_last_mouse_position = mouse_position; } - const Transform3d& trafo_matrix = trafo_matrices[mesh_id]; - - // Calculate how far can a point be from the line (in mesh coords). - // FIXME: The scaling of the mesh can be non-uniform. - const Vec3d sf = Geometry::Transformation(trafo_matrix).get_scaling_factor(); - const float avg_scaling = (sf(0) + sf(1) + sf(2))/3.; - const float limit = m_cursor_radius/avg_scaling; - - // Calculate direction from camera to the hit (in mesh coords): - Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast(); - Vec3f dir = (closest_hit - camera_pos).normalized(); - - assert(mesh_id < int(m_triangle_selectors.size())); - m_triangle_selectors[mesh_id]->select_patch(closest_hit, closest_facet, camera_pos, - dir, limit, new_state); - return true; } @@ -430,6 +452,7 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous update_model_object(); m_button_down = Button::None; + m_last_mouse_position = Vec2d::Zero(); return true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index e1dee373f2..c3f920e2f0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -92,6 +92,7 @@ private: bool m_setting_angle = false; bool m_internal_stack_active = false; bool m_schedule_update = false; + Vec2d m_last_mouse_position = Vec2d::Zero(); // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. From 7a6531ede7bd7d2b60e777a76b93a529a3079d27 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 20 Aug 2020 16:35:56 +0200 Subject: [PATCH 379/503] Started work on separating FDM gizmo into base and child classes --- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 883 ------------------- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp | 115 +-- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 883 +++++++++++++++++++ src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp | 127 +++ 5 files changed, 1018 insertions(+), 992 deletions(-) create mode 100644 src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp create mode 100644 src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index f8598cea08..f1089ae935 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -51,6 +51,8 @@ set(SLIC3R_GUI_SOURCES GUI/Gizmos/GLGizmoCut.hpp GUI/Gizmos/GLGizmoHollow.cpp GUI/Gizmos/GLGizmoHollow.hpp + GUI/Gizmos/GLGizmoPainterBase.cpp + GUI/Gizmos/GLGizmoPainterBase.hpp GUI/GLSelectionRectangle.cpp GUI/GLSelectionRectangle.hpp GUI/GLTexture.hpp diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 62854ab465..e69de29bb2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -1,883 +0,0 @@ -// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. -#include "GLGizmoFdmSupports.hpp" -#include "slic3r/GUI/GLCanvas3D.hpp" -#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" - -#include - -#include "slic3r/GUI/GUI_App.hpp" -#include "slic3r/GUI/Camera.hpp" -#include "slic3r/GUI/Plater.hpp" -#include "libslic3r/PresetBundle.hpp" -#include "libslic3r/Model.hpp" - - - -namespace Slic3r { -namespace GUI { - - -GLGizmoFdmSupports::GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoBase(parent, icon_filename, sprite_id) - , m_quadric(nullptr) -{ - m_clipping_plane.reset(new ClippingPlane()); - m_quadric = ::gluNewQuadric(); - if (m_quadric != nullptr) - // using GLU_FILL does not work when the instance's transformation - // contains mirroring (normals are reverted) - ::gluQuadricDrawStyle(m_quadric, GLU_FILL); -} - -GLGizmoFdmSupports::~GLGizmoFdmSupports() -{ - if (m_quadric != nullptr) - ::gluDeleteQuadric(m_quadric); -} - -bool GLGizmoFdmSupports::on_init() -{ - m_shortcut_key = WXK_CONTROL_L; - - m_desc["clipping_of_view"] = _L("Clipping of view") + ": "; - m_desc["reset_direction"] = _L("Reset direction"); - m_desc["cursor_size"] = _L("Cursor size") + ": "; - m_desc["enforce_caption"] = _L("Left mouse button") + ": "; - m_desc["enforce"] = _L("Enforce supports"); - m_desc["block_caption"] = _L("Right mouse button") + " "; - m_desc["block"] = _L("Block supports"); - m_desc["remove_caption"] = _L("Shift + Left mouse button") + ": "; - m_desc["remove"] = _L("Remove selection"); - m_desc["remove_all"] = _L("Remove all selection"); - - return true; -} - - -void GLGizmoFdmSupports::activate_internal_undo_redo_stack(bool activate) -{ - if (activate && ! m_internal_stack_active) { - Plater::TakeSnapshot(wxGetApp().plater(), _L("FDM gizmo turned on")); - wxGetApp().plater()->enter_gizmos_stack(); - m_internal_stack_active = true; - } - if (! activate && m_internal_stack_active) { - wxGetApp().plater()->leave_gizmos_stack(); - Plater::TakeSnapshot(wxGetApp().plater(), _L("FDM gizmo turned off")); - m_internal_stack_active = false; - } -} - -void GLGizmoFdmSupports::set_fdm_support_data(ModelObject* model_object, const Selection& selection) -{ - if (m_state != On) - return; - - const ModelObject* mo = m_c->selection_info() ? m_c->selection_info()->model_object() : nullptr; - - if (mo && selection.is_from_single_instance() - && (m_schedule_update || mo->id() != m_old_mo_id || mo->volumes.size() != m_old_volumes_size)) - { - update_from_model_object(); - m_old_mo_id = mo->id(); - m_old_volumes_size = mo->volumes.size(); - m_schedule_update = false; - } -} - - - -void GLGizmoFdmSupports::on_render() const -{ - const Selection& selection = m_parent.get_selection(); - - glsafe(::glEnable(GL_BLEND)); - glsafe(::glEnable(GL_DEPTH_TEST)); - - render_triangles(selection); - - m_c->object_clipper()->render_cut(); - render_cursor_circle(); - - glsafe(::glDisable(GL_BLEND)); -} - -void GLGizmoFdmSupports::render_triangles(const Selection& selection) const -{ - if (m_setting_angle) - return; - - const ModelObject* mo = m_c->selection_info()->model_object(); - - glsafe(::glEnable(GL_POLYGON_OFFSET_FILL)); - ScopeGuard offset_fill_guard([]() { glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); } ); - glsafe(::glPolygonOffset(-1.0, 1.0)); - - // Take care of the clipping plane. The normal of the clipping plane is - // saved with opposite sign than we need to pass to OpenGL (FIXME) - bool clipping_plane_active = m_c->object_clipper()->get_position() != 0.; - if (clipping_plane_active) { - const ClippingPlane* clp = m_c->object_clipper()->get_clipping_plane(); - double clp_data[4]; - memcpy(clp_data, clp->get_data(), 4 * sizeof(double)); - for (int i=0; i<3; ++i) - clp_data[i] = -1. * clp_data[i]; - - glsafe(::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)clp_data)); - glsafe(::glEnable(GL_CLIP_PLANE0)); - } - - int mesh_id = -1; - for (const ModelVolume* mv : mo->volumes) { - if (! mv->is_model_part()) - continue; - - ++mesh_id; - - const Transform3d trafo_matrix = - mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * - mv->get_matrix(); - - bool is_left_handed = trafo_matrix.matrix().determinant() < 0.; - if (is_left_handed) - glsafe(::glFrontFace(GL_CW)); - - glsafe(::glPushMatrix()); - glsafe(::glMultMatrixd(trafo_matrix.data())); - - if (! m_setting_angle) - m_triangle_selectors[mesh_id]->render(m_imgui); - - glsafe(::glPopMatrix()); - if (is_left_handed) - glsafe(::glFrontFace(GL_CCW)); - } - if (clipping_plane_active) - glsafe(::glDisable(GL_CLIP_PLANE0)); -} - - -void GLGizmoFdmSupports::render_cursor_circle() const -{ - const Camera& camera = wxGetApp().plater()->get_camera(); - float zoom = (float)camera.get_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - - Size cnv_size = m_parent.get_canvas_size(); - float cnv_half_width = 0.5f * (float)cnv_size.get_width(); - float cnv_half_height = 0.5f * (float)cnv_size.get_height(); - if ((cnv_half_width == 0.0f) || (cnv_half_height == 0.0f)) - return; - Vec2d mouse_pos(m_parent.get_local_mouse_position()(0), m_parent.get_local_mouse_position()(1)); - Vec2d center(mouse_pos(0) - cnv_half_width, cnv_half_height - mouse_pos(1)); - center = center * inv_zoom; - - glsafe(::glLineWidth(1.5f)); - float color[3]; - color[0] = 0.f; - color[1] = 1.f; - color[2] = 0.3f; - glsafe(::glColor3fv(color)); - glsafe(::glDisable(GL_DEPTH_TEST)); - - glsafe(::glPushMatrix()); - glsafe(::glLoadIdentity()); - // ensure that the circle is renderered inside the frustrum - glsafe(::glTranslated(0.0, 0.0, -(camera.get_near_z() + 0.5))); - // ensure that the overlay fits the frustrum near z plane - double gui_scale = camera.get_gui_scale(); - glsafe(::glScaled(gui_scale, gui_scale, 1.0)); - - glsafe(::glPushAttrib(GL_ENABLE_BIT)); - glsafe(::glLineStipple(4, 0xAAAA)); - glsafe(::glEnable(GL_LINE_STIPPLE)); - - ::glBegin(GL_LINE_LOOP); - for (double angle=0; angle<2*M_PI; angle+=M_PI/20.) - ::glVertex2f(GLfloat(center.x()+m_cursor_radius*cos(angle)), GLfloat(center.y()+m_cursor_radius*sin(angle))); - glsafe(::glEnd()); - - glsafe(::glPopAttrib()); - glsafe(::glPopMatrix()); -} - - -void GLGizmoFdmSupports::update_model_object() const -{ - bool updated = false; - ModelObject* mo = m_c->selection_info()->model_object(); - int idx = -1; - for (ModelVolume* mv : mo->volumes) { - if (! mv->is_model_part()) - continue; - ++idx; - updated |= mv->m_supported_facets.set(*m_triangle_selectors[idx].get()); - } - - if (updated) - m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); -} - - -void GLGizmoFdmSupports::update_from_model_object() -{ - wxBusyCursor wait; - - const ModelObject* mo = m_c->selection_info()->model_object(); - m_triangle_selectors.clear(); - - int volume_id = -1; - for (const ModelVolume* mv : mo->volumes) { - if (! mv->is_model_part()) - continue; - - ++volume_id; - - // This mesh does not account for the possible Z up SLA offset. - const TriangleMesh* mesh = &mv->mesh(); - - m_triangle_selectors.emplace_back(std::make_unique(*mesh)); - m_triangle_selectors.back()->deserialize(mv->m_supported_facets.get_data()); - } -} - - - -bool GLGizmoFdmSupports::is_mesh_point_clipped(const Vec3d& point) const -{ - if (m_c->object_clipper()->get_position() == 0.) - return false; - - auto sel_info = m_c->selection_info(); - int active_inst = m_c->selection_info()->get_active_instance(); - const ModelInstance* mi = sel_info->model_object()->instances[active_inst]; - const Transform3d& trafo = mi->get_transformation().get_matrix(); - - Vec3d transformed_point = trafo * point; - transformed_point(2) += sel_info->get_sla_shift(); - return m_c->object_clipper()->get_clipping_plane()->is_point_clipped(transformed_point); -} - - -// Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event. -// The gizmo has an opportunity to react - if it does, it should return true so that the Canvas3D is -// aware that the event was reacted to and stops trying to make different sense of it. If the gizmo -// concludes that the event was not intended for it, it should return false. -bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) -{ - if (action == SLAGizmoEventType::MouseWheelUp - || action == SLAGizmoEventType::MouseWheelDown) { - if (control_down) { - double pos = m_c->object_clipper()->get_position(); - pos = action == SLAGizmoEventType::MouseWheelDown - ? std::max(0., pos - 0.01) - : std::min(1., pos + 0.01); - m_c->object_clipper()->set_position(pos, true); - return true; - } - else if (alt_down) { - m_cursor_radius = action == SLAGizmoEventType::MouseWheelDown - ? std::max(m_cursor_radius - CursorRadiusStep, CursorRadiusMin) - : std::min(m_cursor_radius + CursorRadiusStep, CursorRadiusMax); - m_parent.set_as_dirty(); - return true; - } - } - - if (action == SLAGizmoEventType::ResetClippingPlane) { - m_c->object_clipper()->set_position(-1., false); - return true; - } - - if (action == SLAGizmoEventType::LeftDown - || action == SLAGizmoEventType::RightDown - || (action == SLAGizmoEventType::Dragging && m_button_down != Button::None)) { - - if (m_triangle_selectors.empty()) - return false; - - EnforcerBlockerType new_state = EnforcerBlockerType::NONE; - if (! shift_down) { - if (action == SLAGizmoEventType::Dragging) - new_state = m_button_down == Button::Left - ? EnforcerBlockerType::ENFORCER - : EnforcerBlockerType::BLOCKER; - else - new_state = action == SLAGizmoEventType::LeftDown - ? EnforcerBlockerType::ENFORCER - : EnforcerBlockerType::BLOCKER; - } - - const Camera& camera = wxGetApp().plater()->get_camera(); - const Selection& selection = m_parent.get_selection(); - const ModelObject* mo = m_c->selection_info()->model_object(); - const ModelInstance* mi = mo->instances[selection.get_instance_idx()]; - const Transform3d& instance_trafo = mi->get_transformation().get_matrix(); - - // List of mouse positions that will be used as seeds for painting. - std::vector mouse_positions{mouse_position}; - - // In case current mouse position is far from the last one, - // add several positions from between into the list, so there - // are no gaps in the painted region. - { - if (m_last_mouse_position == Vec2d::Zero()) - m_last_mouse_position = mouse_position; - // resolution describes minimal distance limit using circle radius - // as a unit (e.g., 2 would mean the patches will be touching). - double resolution = 0.7; - double diameter_px = resolution * m_cursor_radius * camera.get_zoom(); - int patches_in_between = int(((mouse_position - m_last_mouse_position).norm() - diameter_px) / diameter_px); - if (patches_in_between > 0) { - Vec2d diff = (mouse_position - m_last_mouse_position)/(patches_in_between+1); - for (int i=1; i<=patches_in_between; ++i) - mouse_positions.emplace_back(m_last_mouse_position + i*diff); - } - } - m_last_mouse_position = Vec2d::Zero(); // only actual hits should be saved - - // Now "click" into all the prepared points and spill paint around them. - for (const Vec2d& mp : mouse_positions) { - std::vector>> hit_positions_and_facet_ids; - bool clipped_mesh_was_hit = false; - - Vec3f normal = Vec3f::Zero(); - Vec3f hit = Vec3f::Zero(); - size_t facet = 0; - Vec3f closest_hit = Vec3f::Zero(); - double closest_hit_squared_distance = std::numeric_limits::max(); - size_t closest_facet = 0; - int closest_hit_mesh_id = -1; - - // Transformations of individual meshes - std::vector trafo_matrices; - - int mesh_id = -1; - // Cast a ray on all meshes, pick the closest hit and save it for the respective mesh - for (const ModelVolume* mv : mo->volumes) { - if (! mv->is_model_part()) - continue; - - ++mesh_id; - - trafo_matrices.push_back(instance_trafo * mv->get_matrix()); - hit_positions_and_facet_ids.push_back(std::vector>()); - - if (m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh( - mp, - trafo_matrices[mesh_id], - camera, - hit, - normal, - m_clipping_plane.get(), - &facet)) - { - // In case this hit is clipped, skip it. - if (is_mesh_point_clipped(hit.cast())) { - clipped_mesh_was_hit = true; - continue; - } - - // Is this hit the closest to the camera so far? - double hit_squared_distance = (camera.get_position()-trafo_matrices[mesh_id]*hit.cast()).squaredNorm(); - if (hit_squared_distance < closest_hit_squared_distance) { - closest_hit_squared_distance = hit_squared_distance; - closest_facet = facet; - closest_hit_mesh_id = mesh_id; - closest_hit = hit; - } - } - } - - bool dragging_while_painting = (action == SLAGizmoEventType::Dragging && m_button_down != Button::None); - - // The mouse button click detection is enabled when there is a valid hit - // or when the user clicks the clipping plane. Missing the object entirely - // shall not capture the mouse. - if (closest_hit_mesh_id != -1 || clipped_mesh_was_hit) { - if (m_button_down == Button::None) - m_button_down = ((action == SLAGizmoEventType::LeftDown) ? Button::Left : Button::Right); - } - - if (closest_hit_mesh_id == -1) { - // In case we have no valid hit, we can return. The event will - // be stopped in following two cases: - // 1. clicking the clipping plane - // 2. dragging while painting (to prevent scene rotations and moving the object) - return clipped_mesh_was_hit - || dragging_while_painting; - } - - // Find respective mesh id. - mesh_id = -1; - for (const ModelVolume* mv : mo->volumes) { - if (! mv->is_model_part()) - continue; - ++mesh_id; - if (mesh_id == closest_hit_mesh_id) - break; - } - - const Transform3d& trafo_matrix = trafo_matrices[mesh_id]; - - // Calculate how far can a point be from the line (in mesh coords). - // FIXME: The scaling of the mesh can be non-uniform. - const Vec3d sf = Geometry::Transformation(trafo_matrix).get_scaling_factor(); - const float avg_scaling = (sf(0) + sf(1) + sf(2))/3.; - const float limit = m_cursor_radius/avg_scaling; - - // Calculate direction from camera to the hit (in mesh coords): - Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast(); - Vec3f dir = (closest_hit - camera_pos).normalized(); - - assert(mesh_id < int(m_triangle_selectors.size())); - m_triangle_selectors[mesh_id]->select_patch(closest_hit, closest_facet, camera_pos, - dir, limit, new_state); - m_last_mouse_position = mouse_position; - } - - return true; - } - - if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::RightUp) - && m_button_down != Button::None) { - // Take snapshot and update ModelVolume data. - wxString action_name = shift_down - ? _L("Remove selection") - : (m_button_down == Button::Left - ? _L("Add supports") - : _L("Block supports")); - activate_internal_undo_redo_stack(true); - Plater::TakeSnapshot(wxGetApp().plater(), action_name); - update_model_object(); - - m_button_down = Button::None; - m_last_mouse_position = Vec2d::Zero(); - return true; - } - - return false; -} - - - -void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool block) -{ - float threshold = (M_PI/180.)*threshold_deg; - const Selection& selection = m_parent.get_selection(); - const ModelObject* mo = m_c->selection_info()->model_object(); - const ModelInstance* mi = mo->instances[selection.get_instance_idx()]; - - int mesh_id = -1; - for (const ModelVolume* mv : mo->volumes) { - if (! mv->is_model_part()) - continue; - - ++mesh_id; - - const Transform3d trafo_matrix = mi->get_matrix(true) * mv->get_matrix(true); - Vec3f down = (trafo_matrix.inverse() * (-Vec3d::UnitZ())).cast().normalized(); - Vec3f limit = (trafo_matrix.inverse() * Vec3d(std::sin(threshold), 0, -std::cos(threshold))).cast().normalized(); - - float dot_limit = limit.dot(down); - - // Now calculate dot product of vert_direction and facets' normals. - int idx = -1; - for (const stl_facet& facet : mv->mesh().stl.facet_start) { - ++idx; - if (facet.normal.dot(down) > dot_limit) - m_triangle_selectors[mesh_id]->set_facet(idx, - block - ? EnforcerBlockerType::BLOCKER - : EnforcerBlockerType::ENFORCER); - } - } - - activate_internal_undo_redo_stack(true); - - Plater::TakeSnapshot(wxGetApp().plater(), block ? _L("Block supports by angle") - : _L("Add supports by angle")); - update_model_object(); - m_parent.set_as_dirty(); - m_setting_angle = false; -} - - -void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_limit) -{ - if (! m_c->selection_info()->model_object()) - return; - - const float approx_height = m_imgui->scaled(18.0f); - y = std::min(y, bottom_limit - approx_height); - m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); - - if (! m_setting_angle) { - m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); - - // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: - const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f); - const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f); - const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f); - const float minimal_slider_width = m_imgui->scaled(4.f); - - float caption_max = 0.f; - float total_text_max = 0.; - for (const std::string& t : {"enforce", "block", "remove"}) { - caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc.at(t+"_caption")).x); - total_text_max = std::max(total_text_max, caption_max + m_imgui->calc_text_size(m_desc.at(t)).x); - } - caption_max += m_imgui->scaled(1.f); - total_text_max += m_imgui->scaled(1.f); - - float window_width = minimal_slider_width + std::max(cursor_slider_left, clipping_slider_left); - window_width = std::max(window_width, total_text_max); - window_width = std::max(window_width, button_width); - - auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) { - static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); - m_imgui->text_colored(ORANGE, caption); - ImGui::SameLine(caption_max); - m_imgui->text(text); - }; - - for (const std::string& t : {"enforce", "block", "remove"}) - draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t)); - - m_imgui->text(""); - - if (m_imgui->button("Autoset by angle...")) { - m_setting_angle = true; - } - - ImGui::SameLine(); - - if (m_imgui->button(m_desc.at("remove_all"))) { - Plater::TakeSnapshot(wxGetApp().plater(), wxString(_L("Reset selection"))); - ModelObject* mo = m_c->selection_info()->model_object(); - int idx = -1; - for (ModelVolume* mv : mo->volumes) { - if (mv->is_model_part()) { - ++idx; - m_triangle_selectors[idx]->reset(); - } - } - update_model_object(); - m_parent.set_as_dirty(); - } - - const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; - - m_imgui->text(m_desc.at("cursor_size")); - ImGui::SameLine(clipping_slider_left); - ImGui::PushItemWidth(window_width - clipping_slider_left); - ImGui::SliderFloat(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f"); - if (ImGui::IsItemHovered()) { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(max_tooltip_width); - ImGui::TextUnformatted(_L("Alt + Mouse wheel").ToUTF8().data()); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); - } - - ImGui::Separator(); - if (m_c->object_clipper()->get_position() == 0.f) - m_imgui->text(m_desc.at("clipping_of_view")); - else { - if (m_imgui->button(m_desc.at("reset_direction"))) { - wxGetApp().CallAfter([this](){ - m_c->object_clipper()->set_position(-1., false); - }); - } - } - - ImGui::SameLine(clipping_slider_left); - ImGui::PushItemWidth(window_width - clipping_slider_left); - float clp_dist = m_c->object_clipper()->get_position(); - if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f")) - m_c->object_clipper()->set_position(clp_dist, true); - if (ImGui::IsItemHovered()) { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(max_tooltip_width); - ImGui::TextUnformatted(_L("Ctrl + Mouse wheel").ToUTF8().data()); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); - } - - m_imgui->end(); - if (m_setting_angle) { - m_parent.show_slope(false); - m_parent.set_slope_range({90.f - m_angle_threshold_deg, 90.f - m_angle_threshold_deg}); - m_parent.use_slope(true); - m_parent.set_as_dirty(); - } - } - else { - std::string name = "Autoset custom supports"; - m_imgui->begin(wxString(name), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); - m_imgui->text("Threshold:"); - ImGui::SameLine(); - if (m_imgui->slider_float("", &m_angle_threshold_deg, 0.f, 90.f, "%.f")) - m_parent.set_slope_range({90.f - m_angle_threshold_deg, 90.f - m_angle_threshold_deg}); - if (m_imgui->button("Enforce")) - select_facets_by_angle(m_angle_threshold_deg, false); - ImGui::SameLine(); - if (m_imgui->button("Block")) - select_facets_by_angle(m_angle_threshold_deg, true); - ImGui::SameLine(); - if (m_imgui->button("Cancel")) - m_setting_angle = false; - m_imgui->end(); - if (! m_setting_angle) { - m_parent.use_slope(false); - m_parent.set_as_dirty(); - } - } -} - -bool GLGizmoFdmSupports::on_is_activable() const -{ - const Selection& selection = m_parent.get_selection(); - - if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptFFF - || !selection.is_single_full_instance()) - return false; - - // Check that none of the selected volumes is outside. Only SLA auxiliaries (supports) are allowed outside. - const Selection::IndicesList& list = selection.get_volume_idxs(); - for (const auto& idx : list) - if (selection.get_volume(idx)->is_outside) - return false; - - return true; -} - -bool GLGizmoFdmSupports::on_is_selectable() const -{ - return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF - && wxGetApp().get_mode() != comSimple ); -} - -std::string GLGizmoFdmSupports::on_get_name() const -{ - return (_(L("FDM Support Editing")) + " [L]").ToUTF8().data(); -} - - -CommonGizmosDataID GLGizmoFdmSupports::on_get_requirements() const -{ - return CommonGizmosDataID( - int(CommonGizmosDataID::SelectionInfo) - | int(CommonGizmosDataID::InstancesHider) - | int(CommonGizmosDataID::Raycaster) - | int(CommonGizmosDataID::ObjectClipper)); -} - - -void GLGizmoFdmSupports::on_set_state() -{ - if (m_state == m_old_state) - return; - - if (m_state == On && m_old_state != On) { // the gizmo was just turned on - if (! m_parent.get_gizmos_manager().is_serializing()) { - wxGetApp().CallAfter([this]() { - activate_internal_undo_redo_stack(true); - }); - } - } - if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off - // we are actually shutting down - if (m_setting_angle) { - m_setting_angle = false; - m_parent.use_slope(false); - } - activate_internal_undo_redo_stack(false); - m_old_mo_id = -1; - //m_iva.release_geometry(); - m_triangle_selectors.clear(); - } - m_old_state = m_state; -} - - - -void GLGizmoFdmSupports::on_start_dragging() -{ - -} - - -void GLGizmoFdmSupports::on_stop_dragging() -{ - -} - - - -void GLGizmoFdmSupports::on_load(cereal::BinaryInputArchive&) -{ - // We should update the gizmo from current ModelObject, but it is not - // possible at this point. That would require having updated selection and - // common gizmos data, which is not done at this point. Instead, save - // a flag to do the update in set_fdm_support_data, which will be called - // soon after. - m_schedule_update = true; -} - - - -void GLGizmoFdmSupports::on_save(cereal::BinaryOutputArchive&) const -{ - -} - - -void TriangleSelectorGUI::render(ImGuiWrapper* imgui) -{ - int enf_cnt = 0; - int blc_cnt = 0; - - m_iva_enforcers.release_geometry(); - m_iva_blockers.release_geometry(); - - for (const Triangle& tr : m_triangles) { - if (! tr.valid || tr.is_split() || tr.get_state() == EnforcerBlockerType::NONE) - continue; - - GLIndexedVertexArray& va = tr.get_state() == EnforcerBlockerType::ENFORCER - ? m_iva_enforcers - : m_iva_blockers; - int& cnt = tr.get_state() == EnforcerBlockerType::ENFORCER - ? enf_cnt - : blc_cnt; - - for (int i=0; i<3; ++i) - va.push_geometry(double(m_vertices[tr.verts_idxs[i]].v[0]), - double(m_vertices[tr.verts_idxs[i]].v[1]), - double(m_vertices[tr.verts_idxs[i]].v[2]), - 0., 0., 1.); - va.push_triangle(cnt, - cnt+1, - cnt+2); - cnt += 3; - } - - m_iva_enforcers.finalize_geometry(true); - m_iva_blockers.finalize_geometry(true); - - if (m_iva_enforcers.has_VBOs()) { - ::glColor4f(0.f, 0.f, 1.f, 0.2f); - m_iva_enforcers.render(); - } - - - if (m_iva_blockers.has_VBOs()) { - ::glColor4f(1.f, 0.f, 0.f, 0.2f); - m_iva_blockers.render(); - } - - -#ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG - if (imgui) - render_debug(imgui); - else - assert(false); // If you want debug output, pass ptr to ImGuiWrapper. -#endif -} - - - -#ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG -void TriangleSelectorGUI::render_debug(ImGuiWrapper* imgui) -{ - imgui->begin(std::string("TriangleSelector dialog (DEV ONLY)"), - ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); - static float edge_limit = 1.f; - imgui->text("Edge limit (mm): "); - imgui->slider_float("", &edge_limit, 0.1f, 8.f); - set_edge_limit(edge_limit); - imgui->checkbox("Show split triangles: ", m_show_triangles); - imgui->checkbox("Show invalid triangles: ", m_show_invalid); - - int valid_triangles = m_triangles.size() - m_invalid_triangles; - imgui->text("Valid triangles: " + std::to_string(valid_triangles) + - "/" + std::to_string(m_triangles.size())); - imgui->text("Vertices: " + std::to_string(m_vertices.size())); - if (imgui->button("Force garbage collection")) - garbage_collect(); - - if (imgui->button("Serialize - deserialize")) { - auto map = serialize(); - deserialize(map); - } - - imgui->end(); - - if (! m_show_triangles) - return; - - enum vtype { - ORIGINAL = 0, - SPLIT, - INVALID - }; - - for (auto& va : m_varrays) - va.release_geometry(); - - std::array cnts; - - ::glScalef(1.01f, 1.01f, 1.01f); - - for (int tr_id=0; tr_idpush_geometry(double(m_vertices[tr.verts_idxs[i]].v[0]), - double(m_vertices[tr.verts_idxs[i]].v[1]), - double(m_vertices[tr.verts_idxs[i]].v[2]), - 0., 0., 1.); - va->push_triangle(*cnt, - *cnt+1, - *cnt+2); - *cnt += 3; - } - - ::glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); - for (vtype i : {ORIGINAL, SPLIT, INVALID}) { - GLIndexedVertexArray& va = m_varrays[i]; - va.finalize_geometry(true); - if (va.has_VBOs()) { - switch (i) { - case ORIGINAL : ::glColor3f(0.f, 0.f, 1.f); break; - case SPLIT : ::glColor3f(1.f, 0.f, 0.f); break; - case INVALID : ::glColor3f(1.f, 1.f, 0.f); break; - } - va.render(); - } - } - ::glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); -} -#endif - - - -} // namespace GUI -} // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index c3f920e2f0..196a21bc03 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -1,127 +1,24 @@ #ifndef slic3r_GLGizmoFdmSupports_hpp_ #define slic3r_GLGizmoFdmSupports_hpp_ -#include "GLGizmoBase.hpp" - -#include "slic3r/GUI/3DScene.hpp" - -#include "libslic3r/ObjectID.hpp" -#include "libslic3r/TriangleSelector.hpp" - -#include - - - +#include "GLGizmoPainterBase.hpp" namespace Slic3r { -enum class EnforcerBlockerType : int8_t; - namespace GUI { -enum class SLAGizmoEventType : unsigned char; -class ClippingPlane; - - - -class TriangleSelectorGUI : public TriangleSelector { -public: - explicit TriangleSelectorGUI(const TriangleMesh& mesh) - : TriangleSelector(mesh) {} - - // Render current selection. Transformation matrices are supposed - // to be already set. - void render(ImGuiWrapper* imgui = nullptr); - -#ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG - void render_debug(ImGuiWrapper* imgui); - bool m_show_triangles{false}; - bool m_show_invalid{false}; -#endif - -private: - GLIndexedVertexArray m_iva_enforcers; - GLIndexedVertexArray m_iva_blockers; - std::array m_varrays; -}; - - - -class GLGizmoFdmSupports : public GLGizmoBase +class GLGizmoFdmSupports : public GLGizmoPainterBase { -private: - ObjectID m_old_mo_id; - size_t m_old_volumes_size = 0; - - GLUquadricObj* m_quadric; - - float m_cursor_radius = 2.f; - static constexpr float CursorRadiusMin = 0.4f; // cannot be zero - static constexpr float CursorRadiusMax = 8.f; - static constexpr float CursorRadiusStep = 0.2f; - - // For each model-part volume, store status and division of the triangles. - std::vector> m_triangle_selectors; - public: - GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - ~GLGizmoFdmSupports() override; - void set_fdm_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); + GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) + : GLGizmoPainterBase(parent, icon_filename, sprite_id) {} - -private: - bool on_init() override; - void on_render() const override; - void on_render_for_picking() const override {} - - void render_triangles(const Selection& selection) const; - void render_cursor_circle() const; - - void update_model_object() const; - void update_from_model_object(); - void activate_internal_undo_redo_stack(bool activate); - - void select_facets_by_angle(float threshold, bool block); - float m_angle_threshold_deg = 45.f; - - bool is_mesh_point_clipped(const Vec3d& point) const; - - float m_clipping_plane_distance = 0.f; - std::unique_ptr m_clipping_plane; - bool m_setting_angle = false; - bool m_internal_stack_active = false; - bool m_schedule_update = false; - Vec2d m_last_mouse_position = Vec2d::Zero(); - - // This map holds all translated description texts, so they can be easily referenced during layout calculations - // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. - std::map m_desc; - - enum class Button { - None, - Left, - Right - }; - - Button m_button_down = Button::None; - EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state) - -protected: - void on_set_state() override; - void on_start_dragging() override; - void on_stop_dragging() override; - void on_render_input_window(float x, float y, float bottom_limit) override; - std::string on_get_name() const override; - bool on_is_activable() const override; - bool on_is_selectable() const override; - void on_load(cereal::BinaryInputArchive& ar) override; - void on_save(cereal::BinaryOutputArchive& ar) const override; - CommonGizmosDataID on_get_requirements() const override; }; + } // namespace GUI } // namespace Slic3r + #endif // slic3r_GLGizmoFdmSupports_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp new file mode 100644 index 0000000000..37792a48e0 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -0,0 +1,883 @@ +// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. +#include "GLGizmoPainterBase.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" + +#include + +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/Camera.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "libslic3r/PresetBundle.hpp" +#include "libslic3r/Model.hpp" + + + +namespace Slic3r { +namespace GUI { + + +GLGizmoPainterBase::GLGizmoPainterBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) + : GLGizmoBase(parent, icon_filename, sprite_id) + , m_quadric(nullptr) +{ + m_clipping_plane.reset(new ClippingPlane()); + m_quadric = ::gluNewQuadric(); + if (m_quadric != nullptr) + // using GLU_FILL does not work when the instance's transformation + // contains mirroring (normals are reverted) + ::gluQuadricDrawStyle(m_quadric, GLU_FILL); +} + +GLGizmoPainterBase::~GLGizmoPainterBase() +{ + if (m_quadric != nullptr) + ::gluDeleteQuadric(m_quadric); +} + +bool GLGizmoPainterBase::on_init() +{ + m_shortcut_key = WXK_CONTROL_L; + + m_desc["clipping_of_view"] = _L("Clipping of view") + ": "; + m_desc["reset_direction"] = _L("Reset direction"); + m_desc["cursor_size"] = _L("Cursor size") + ": "; + m_desc["enforce_caption"] = _L("Left mouse button") + ": "; + m_desc["enforce"] = _L("Enforce supports"); + m_desc["block_caption"] = _L("Right mouse button") + " "; + m_desc["block"] = _L("Block supports"); + m_desc["remove_caption"] = _L("Shift + Left mouse button") + ": "; + m_desc["remove"] = _L("Remove selection"); + m_desc["remove_all"] = _L("Remove all selection"); + + return true; +} + + +void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate) +{ + if (activate && ! m_internal_stack_active) { + Plater::TakeSnapshot(wxGetApp().plater(), _L("FDM gizmo turned on")); + wxGetApp().plater()->enter_gizmos_stack(); + m_internal_stack_active = true; + } + if (! activate && m_internal_stack_active) { + wxGetApp().plater()->leave_gizmos_stack(); + Plater::TakeSnapshot(wxGetApp().plater(), _L("FDM gizmo turned off")); + m_internal_stack_active = false; + } +} + +void GLGizmoPainterBase::set_fdm_support_data(ModelObject* model_object, const Selection& selection) +{ + if (m_state != On) + return; + + const ModelObject* mo = m_c->selection_info() ? m_c->selection_info()->model_object() : nullptr; + + if (mo && selection.is_from_single_instance() + && (m_schedule_update || mo->id() != m_old_mo_id || mo->volumes.size() != m_old_volumes_size)) + { + update_from_model_object(); + m_old_mo_id = mo->id(); + m_old_volumes_size = mo->volumes.size(); + m_schedule_update = false; + } +} + + + +void GLGizmoPainterBase::on_render() const +{ + const Selection& selection = m_parent.get_selection(); + + glsafe(::glEnable(GL_BLEND)); + glsafe(::glEnable(GL_DEPTH_TEST)); + + render_triangles(selection); + + m_c->object_clipper()->render_cut(); + render_cursor_circle(); + + glsafe(::glDisable(GL_BLEND)); +} + +void GLGizmoPainterBase::render_triangles(const Selection& selection) const +{ + if (m_setting_angle) + return; + + const ModelObject* mo = m_c->selection_info()->model_object(); + + glsafe(::glEnable(GL_POLYGON_OFFSET_FILL)); + ScopeGuard offset_fill_guard([]() { glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); } ); + glsafe(::glPolygonOffset(-1.0, 1.0)); + + // Take care of the clipping plane. The normal of the clipping plane is + // saved with opposite sign than we need to pass to OpenGL (FIXME) + bool clipping_plane_active = m_c->object_clipper()->get_position() != 0.; + if (clipping_plane_active) { + const ClippingPlane* clp = m_c->object_clipper()->get_clipping_plane(); + double clp_data[4]; + memcpy(clp_data, clp->get_data(), 4 * sizeof(double)); + for (int i=0; i<3; ++i) + clp_data[i] = -1. * clp_data[i]; + + glsafe(::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)clp_data)); + glsafe(::glEnable(GL_CLIP_PLANE0)); + } + + int mesh_id = -1; + for (const ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) + continue; + + ++mesh_id; + + const Transform3d trafo_matrix = + mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * + mv->get_matrix(); + + bool is_left_handed = trafo_matrix.matrix().determinant() < 0.; + if (is_left_handed) + glsafe(::glFrontFace(GL_CW)); + + glsafe(::glPushMatrix()); + glsafe(::glMultMatrixd(trafo_matrix.data())); + + if (! m_setting_angle) + m_triangle_selectors[mesh_id]->render(m_imgui); + + glsafe(::glPopMatrix()); + if (is_left_handed) + glsafe(::glFrontFace(GL_CCW)); + } + if (clipping_plane_active) + glsafe(::glDisable(GL_CLIP_PLANE0)); +} + + +void GLGizmoPainterBase::render_cursor_circle() const +{ + const Camera& camera = wxGetApp().plater()->get_camera(); + float zoom = (float)camera.get_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + + Size cnv_size = m_parent.get_canvas_size(); + float cnv_half_width = 0.5f * (float)cnv_size.get_width(); + float cnv_half_height = 0.5f * (float)cnv_size.get_height(); + if ((cnv_half_width == 0.0f) || (cnv_half_height == 0.0f)) + return; + Vec2d mouse_pos(m_parent.get_local_mouse_position()(0), m_parent.get_local_mouse_position()(1)); + Vec2d center(mouse_pos(0) - cnv_half_width, cnv_half_height - mouse_pos(1)); + center = center * inv_zoom; + + glsafe(::glLineWidth(1.5f)); + float color[3]; + color[0] = 0.f; + color[1] = 1.f; + color[2] = 0.3f; + glsafe(::glColor3fv(color)); + glsafe(::glDisable(GL_DEPTH_TEST)); + + glsafe(::glPushMatrix()); + glsafe(::glLoadIdentity()); + // ensure that the circle is renderered inside the frustrum + glsafe(::glTranslated(0.0, 0.0, -(camera.get_near_z() + 0.5))); + // ensure that the overlay fits the frustrum near z plane + double gui_scale = camera.get_gui_scale(); + glsafe(::glScaled(gui_scale, gui_scale, 1.0)); + + glsafe(::glPushAttrib(GL_ENABLE_BIT)); + glsafe(::glLineStipple(4, 0xAAAA)); + glsafe(::glEnable(GL_LINE_STIPPLE)); + + ::glBegin(GL_LINE_LOOP); + for (double angle=0; angle<2*M_PI; angle+=M_PI/20.) + ::glVertex2f(GLfloat(center.x()+m_cursor_radius*cos(angle)), GLfloat(center.y()+m_cursor_radius*sin(angle))); + glsafe(::glEnd()); + + glsafe(::glPopAttrib()); + glsafe(::glPopMatrix()); +} + + +void GLGizmoPainterBase::update_model_object() const +{ + bool updated = false; + ModelObject* mo = m_c->selection_info()->model_object(); + int idx = -1; + for (ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) + continue; + ++idx; + updated |= mv->m_supported_facets.set(*m_triangle_selectors[idx].get()); + } + + if (updated) + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); +} + + +void GLGizmoPainterBase::update_from_model_object() +{ + wxBusyCursor wait; + + const ModelObject* mo = m_c->selection_info()->model_object(); + m_triangle_selectors.clear(); + + int volume_id = -1; + for (const ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) + continue; + + ++volume_id; + + // This mesh does not account for the possible Z up SLA offset. + const TriangleMesh* mesh = &mv->mesh(); + + m_triangle_selectors.emplace_back(std::make_unique(*mesh)); + m_triangle_selectors.back()->deserialize(mv->m_supported_facets.get_data()); + } +} + + + +bool GLGizmoPainterBase::is_mesh_point_clipped(const Vec3d& point) const +{ + if (m_c->object_clipper()->get_position() == 0.) + return false; + + auto sel_info = m_c->selection_info(); + int active_inst = m_c->selection_info()->get_active_instance(); + const ModelInstance* mi = sel_info->model_object()->instances[active_inst]; + const Transform3d& trafo = mi->get_transformation().get_matrix(); + + Vec3d transformed_point = trafo * point; + transformed_point(2) += sel_info->get_sla_shift(); + return m_c->object_clipper()->get_clipping_plane()->is_point_clipped(transformed_point); +} + + +// Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event. +// The gizmo has an opportunity to react - if it does, it should return true so that the Canvas3D is +// aware that the event was reacted to and stops trying to make different sense of it. If the gizmo +// concludes that the event was not intended for it, it should return false. +bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) +{ + if (action == SLAGizmoEventType::MouseWheelUp + || action == SLAGizmoEventType::MouseWheelDown) { + if (control_down) { + double pos = m_c->object_clipper()->get_position(); + pos = action == SLAGizmoEventType::MouseWheelDown + ? std::max(0., pos - 0.01) + : std::min(1., pos + 0.01); + m_c->object_clipper()->set_position(pos, true); + return true; + } + else if (alt_down) { + m_cursor_radius = action == SLAGizmoEventType::MouseWheelDown + ? std::max(m_cursor_radius - CursorRadiusStep, CursorRadiusMin) + : std::min(m_cursor_radius + CursorRadiusStep, CursorRadiusMax); + m_parent.set_as_dirty(); + return true; + } + } + + if (action == SLAGizmoEventType::ResetClippingPlane) { + m_c->object_clipper()->set_position(-1., false); + return true; + } + + if (action == SLAGizmoEventType::LeftDown + || action == SLAGizmoEventType::RightDown + || (action == SLAGizmoEventType::Dragging && m_button_down != Button::None)) { + + if (m_triangle_selectors.empty()) + return false; + + EnforcerBlockerType new_state = EnforcerBlockerType::NONE; + if (! shift_down) { + if (action == SLAGizmoEventType::Dragging) + new_state = m_button_down == Button::Left + ? EnforcerBlockerType::ENFORCER + : EnforcerBlockerType::BLOCKER; + else + new_state = action == SLAGizmoEventType::LeftDown + ? EnforcerBlockerType::ENFORCER + : EnforcerBlockerType::BLOCKER; + } + + const Camera& camera = wxGetApp().plater()->get_camera(); + const Selection& selection = m_parent.get_selection(); + const ModelObject* mo = m_c->selection_info()->model_object(); + const ModelInstance* mi = mo->instances[selection.get_instance_idx()]; + const Transform3d& instance_trafo = mi->get_transformation().get_matrix(); + + // List of mouse positions that will be used as seeds for painting. + std::vector mouse_positions{mouse_position}; + + // In case current mouse position is far from the last one, + // add several positions from between into the list, so there + // are no gaps in the painted region. + { + if (m_last_mouse_position == Vec2d::Zero()) + m_last_mouse_position = mouse_position; + // resolution describes minimal distance limit using circle radius + // as a unit (e.g., 2 would mean the patches will be touching). + double resolution = 0.7; + double diameter_px = resolution * m_cursor_radius * camera.get_zoom(); + int patches_in_between = int(((mouse_position - m_last_mouse_position).norm() - diameter_px) / diameter_px); + if (patches_in_between > 0) { + Vec2d diff = (mouse_position - m_last_mouse_position)/(patches_in_between+1); + for (int i=1; i<=patches_in_between; ++i) + mouse_positions.emplace_back(m_last_mouse_position + i*diff); + } + } + m_last_mouse_position = Vec2d::Zero(); // only actual hits should be saved + + // Now "click" into all the prepared points and spill paint around them. + for (const Vec2d& mp : mouse_positions) { + std::vector>> hit_positions_and_facet_ids; + bool clipped_mesh_was_hit = false; + + Vec3f normal = Vec3f::Zero(); + Vec3f hit = Vec3f::Zero(); + size_t facet = 0; + Vec3f closest_hit = Vec3f::Zero(); + double closest_hit_squared_distance = std::numeric_limits::max(); + size_t closest_facet = 0; + int closest_hit_mesh_id = -1; + + // Transformations of individual meshes + std::vector trafo_matrices; + + int mesh_id = -1; + // Cast a ray on all meshes, pick the closest hit and save it for the respective mesh + for (const ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) + continue; + + ++mesh_id; + + trafo_matrices.push_back(instance_trafo * mv->get_matrix()); + hit_positions_and_facet_ids.push_back(std::vector>()); + + if (m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh( + mp, + trafo_matrices[mesh_id], + camera, + hit, + normal, + m_clipping_plane.get(), + &facet)) + { + // In case this hit is clipped, skip it. + if (is_mesh_point_clipped(hit.cast())) { + clipped_mesh_was_hit = true; + continue; + } + + // Is this hit the closest to the camera so far? + double hit_squared_distance = (camera.get_position()-trafo_matrices[mesh_id]*hit.cast()).squaredNorm(); + if (hit_squared_distance < closest_hit_squared_distance) { + closest_hit_squared_distance = hit_squared_distance; + closest_facet = facet; + closest_hit_mesh_id = mesh_id; + closest_hit = hit; + } + } + } + + bool dragging_while_painting = (action == SLAGizmoEventType::Dragging && m_button_down != Button::None); + + // The mouse button click detection is enabled when there is a valid hit + // or when the user clicks the clipping plane. Missing the object entirely + // shall not capture the mouse. + if (closest_hit_mesh_id != -1 || clipped_mesh_was_hit) { + if (m_button_down == Button::None) + m_button_down = ((action == SLAGizmoEventType::LeftDown) ? Button::Left : Button::Right); + } + + if (closest_hit_mesh_id == -1) { + // In case we have no valid hit, we can return. The event will + // be stopped in following two cases: + // 1. clicking the clipping plane + // 2. dragging while painting (to prevent scene rotations and moving the object) + return clipped_mesh_was_hit + || dragging_while_painting; + } + + // Find respective mesh id. + mesh_id = -1; + for (const ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) + continue; + ++mesh_id; + if (mesh_id == closest_hit_mesh_id) + break; + } + + const Transform3d& trafo_matrix = trafo_matrices[mesh_id]; + + // Calculate how far can a point be from the line (in mesh coords). + // FIXME: The scaling of the mesh can be non-uniform. + const Vec3d sf = Geometry::Transformation(trafo_matrix).get_scaling_factor(); + const float avg_scaling = (sf(0) + sf(1) + sf(2))/3.; + const float limit = m_cursor_radius/avg_scaling; + + // Calculate direction from camera to the hit (in mesh coords): + Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast(); + Vec3f dir = (closest_hit - camera_pos).normalized(); + + assert(mesh_id < int(m_triangle_selectors.size())); + m_triangle_selectors[mesh_id]->select_patch(closest_hit, closest_facet, camera_pos, + dir, limit, new_state); + m_last_mouse_position = mouse_position; + } + + return true; + } + + if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::RightUp) + && m_button_down != Button::None) { + // Take snapshot and update ModelVolume data. + wxString action_name = shift_down + ? _L("Remove selection") + : (m_button_down == Button::Left + ? _L("Add supports") + : _L("Block supports")); + activate_internal_undo_redo_stack(true); + Plater::TakeSnapshot(wxGetApp().plater(), action_name); + update_model_object(); + + m_button_down = Button::None; + m_last_mouse_position = Vec2d::Zero(); + return true; + } + + return false; +} + + + +void GLGizmoPainterBase::select_facets_by_angle(float threshold_deg, bool block) +{ + float threshold = (M_PI/180.)*threshold_deg; + const Selection& selection = m_parent.get_selection(); + const ModelObject* mo = m_c->selection_info()->model_object(); + const ModelInstance* mi = mo->instances[selection.get_instance_idx()]; + + int mesh_id = -1; + for (const ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) + continue; + + ++mesh_id; + + const Transform3d trafo_matrix = mi->get_matrix(true) * mv->get_matrix(true); + Vec3f down = (trafo_matrix.inverse() * (-Vec3d::UnitZ())).cast().normalized(); + Vec3f limit = (trafo_matrix.inverse() * Vec3d(std::sin(threshold), 0, -std::cos(threshold))).cast().normalized(); + + float dot_limit = limit.dot(down); + + // Now calculate dot product of vert_direction and facets' normals. + int idx = -1; + for (const stl_facet& facet : mv->mesh().stl.facet_start) { + ++idx; + if (facet.normal.dot(down) > dot_limit) + m_triangle_selectors[mesh_id]->set_facet(idx, + block + ? EnforcerBlockerType::BLOCKER + : EnforcerBlockerType::ENFORCER); + } + } + + activate_internal_undo_redo_stack(true); + + Plater::TakeSnapshot(wxGetApp().plater(), block ? _L("Block supports by angle") + : _L("Add supports by angle")); + update_model_object(); + m_parent.set_as_dirty(); + m_setting_angle = false; +} + + +void GLGizmoPainterBase::on_render_input_window(float x, float y, float bottom_limit) +{ + if (! m_c->selection_info()->model_object()) + return; + + const float approx_height = m_imgui->scaled(18.0f); + y = std::min(y, bottom_limit - approx_height); + m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); + + if (! m_setting_angle) { + m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); + + // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: + const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f); + const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f); + const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f); + const float minimal_slider_width = m_imgui->scaled(4.f); + + float caption_max = 0.f; + float total_text_max = 0.; + for (const std::string& t : {"enforce", "block", "remove"}) { + caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc.at(t+"_caption")).x); + total_text_max = std::max(total_text_max, caption_max + m_imgui->calc_text_size(m_desc.at(t)).x); + } + caption_max += m_imgui->scaled(1.f); + total_text_max += m_imgui->scaled(1.f); + + float window_width = minimal_slider_width + std::max(cursor_slider_left, clipping_slider_left); + window_width = std::max(window_width, total_text_max); + window_width = std::max(window_width, button_width); + + auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) { + static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); + m_imgui->text_colored(ORANGE, caption); + ImGui::SameLine(caption_max); + m_imgui->text(text); + }; + + for (const std::string& t : {"enforce", "block", "remove"}) + draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t)); + + m_imgui->text(""); + + if (m_imgui->button("Autoset by angle...")) { + m_setting_angle = true; + } + + ImGui::SameLine(); + + if (m_imgui->button(m_desc.at("remove_all"))) { + Plater::TakeSnapshot(wxGetApp().plater(), wxString(_L("Reset selection"))); + ModelObject* mo = m_c->selection_info()->model_object(); + int idx = -1; + for (ModelVolume* mv : mo->volumes) { + if (mv->is_model_part()) { + ++idx; + m_triangle_selectors[idx]->reset(); + } + } + update_model_object(); + m_parent.set_as_dirty(); + } + + const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; + + m_imgui->text(m_desc.at("cursor_size")); + ImGui::SameLine(clipping_slider_left); + ImGui::PushItemWidth(window_width - clipping_slider_left); + ImGui::SliderFloat(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f"); + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(max_tooltip_width); + ImGui::TextUnformatted(_L("Alt + Mouse wheel").ToUTF8().data()); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } + + ImGui::Separator(); + if (m_c->object_clipper()->get_position() == 0.f) + m_imgui->text(m_desc.at("clipping_of_view")); + else { + if (m_imgui->button(m_desc.at("reset_direction"))) { + wxGetApp().CallAfter([this](){ + m_c->object_clipper()->set_position(-1., false); + }); + } + } + + ImGui::SameLine(clipping_slider_left); + ImGui::PushItemWidth(window_width - clipping_slider_left); + float clp_dist = m_c->object_clipper()->get_position(); + if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f")) + m_c->object_clipper()->set_position(clp_dist, true); + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(max_tooltip_width); + ImGui::TextUnformatted(_L("Ctrl + Mouse wheel").ToUTF8().data()); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } + + m_imgui->end(); + if (m_setting_angle) { + m_parent.show_slope(false); + m_parent.set_slope_range({90.f - m_angle_threshold_deg, 90.f - m_angle_threshold_deg}); + m_parent.use_slope(true); + m_parent.set_as_dirty(); + } + } + else { + std::string name = "Autoset custom supports"; + m_imgui->begin(wxString(name), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); + m_imgui->text("Threshold:"); + ImGui::SameLine(); + if (m_imgui->slider_float("", &m_angle_threshold_deg, 0.f, 90.f, "%.f")) + m_parent.set_slope_range({90.f - m_angle_threshold_deg, 90.f - m_angle_threshold_deg}); + if (m_imgui->button("Enforce")) + select_facets_by_angle(m_angle_threshold_deg, false); + ImGui::SameLine(); + if (m_imgui->button("Block")) + select_facets_by_angle(m_angle_threshold_deg, true); + ImGui::SameLine(); + if (m_imgui->button("Cancel")) + m_setting_angle = false; + m_imgui->end(); + if (! m_setting_angle) { + m_parent.use_slope(false); + m_parent.set_as_dirty(); + } + } +} + +bool GLGizmoPainterBase::on_is_activable() const +{ + const Selection& selection = m_parent.get_selection(); + + if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptFFF + || !selection.is_single_full_instance()) + return false; + + // Check that none of the selected volumes is outside. Only SLA auxiliaries (supports) are allowed outside. + const Selection::IndicesList& list = selection.get_volume_idxs(); + for (const auto& idx : list) + if (selection.get_volume(idx)->is_outside) + return false; + + return true; +} + +bool GLGizmoPainterBase::on_is_selectable() const +{ + return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF + && wxGetApp().get_mode() != comSimple ); +} + +std::string GLGizmoPainterBase::on_get_name() const +{ + return (_(L("FDM Support Editing")) + " [L]").ToUTF8().data(); +} + + +CommonGizmosDataID GLGizmoPainterBase::on_get_requirements() const +{ + return CommonGizmosDataID( + int(CommonGizmosDataID::SelectionInfo) + | int(CommonGizmosDataID::InstancesHider) + | int(CommonGizmosDataID::Raycaster) + | int(CommonGizmosDataID::ObjectClipper)); +} + + +void GLGizmoPainterBase::on_set_state() +{ + if (m_state == m_old_state) + return; + + if (m_state == On && m_old_state != On) { // the gizmo was just turned on + if (! m_parent.get_gizmos_manager().is_serializing()) { + wxGetApp().CallAfter([this]() { + activate_internal_undo_redo_stack(true); + }); + } + } + if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off + // we are actually shutting down + if (m_setting_angle) { + m_setting_angle = false; + m_parent.use_slope(false); + } + activate_internal_undo_redo_stack(false); + m_old_mo_id = -1; + //m_iva.release_geometry(); + m_triangle_selectors.clear(); + } + m_old_state = m_state; +} + + + +void GLGizmoPainterBase::on_start_dragging() +{ + +} + + +void GLGizmoPainterBase::on_stop_dragging() +{ + +} + + + +void GLGizmoPainterBase::on_load(cereal::BinaryInputArchive&) +{ + // We should update the gizmo from current ModelObject, but it is not + // possible at this point. That would require having updated selection and + // common gizmos data, which is not done at this point. Instead, save + // a flag to do the update in set_fdm_support_data, which will be called + // soon after. + m_schedule_update = true; +} + + + +void GLGizmoPainterBase::on_save(cereal::BinaryOutputArchive&) const +{ + +} + + +void TriangleSelectorGUI::render(ImGuiWrapper* imgui) +{ + int enf_cnt = 0; + int blc_cnt = 0; + + m_iva_enforcers.release_geometry(); + m_iva_blockers.release_geometry(); + + for (const Triangle& tr : m_triangles) { + if (! tr.valid || tr.is_split() || tr.get_state() == EnforcerBlockerType::NONE) + continue; + + GLIndexedVertexArray& va = tr.get_state() == EnforcerBlockerType::ENFORCER + ? m_iva_enforcers + : m_iva_blockers; + int& cnt = tr.get_state() == EnforcerBlockerType::ENFORCER + ? enf_cnt + : blc_cnt; + + for (int i=0; i<3; ++i) + va.push_geometry(double(m_vertices[tr.verts_idxs[i]].v[0]), + double(m_vertices[tr.verts_idxs[i]].v[1]), + double(m_vertices[tr.verts_idxs[i]].v[2]), + 0., 0., 1.); + va.push_triangle(cnt, + cnt+1, + cnt+2); + cnt += 3; + } + + m_iva_enforcers.finalize_geometry(true); + m_iva_blockers.finalize_geometry(true); + + if (m_iva_enforcers.has_VBOs()) { + ::glColor4f(0.f, 0.f, 1.f, 0.2f); + m_iva_enforcers.render(); + } + + + if (m_iva_blockers.has_VBOs()) { + ::glColor4f(1.f, 0.f, 0.f, 0.2f); + m_iva_blockers.render(); + } + + +#ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG + if (imgui) + render_debug(imgui); + else + assert(false); // If you want debug output, pass ptr to ImGuiWrapper. +#endif +} + + + +#ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG +void TriangleSelectorGUI::render_debug(ImGuiWrapper* imgui) +{ + imgui->begin(std::string("TriangleSelector dialog (DEV ONLY)"), + ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); + static float edge_limit = 1.f; + imgui->text("Edge limit (mm): "); + imgui->slider_float("", &edge_limit, 0.1f, 8.f); + set_edge_limit(edge_limit); + imgui->checkbox("Show split triangles: ", m_show_triangles); + imgui->checkbox("Show invalid triangles: ", m_show_invalid); + + int valid_triangles = m_triangles.size() - m_invalid_triangles; + imgui->text("Valid triangles: " + std::to_string(valid_triangles) + + "/" + std::to_string(m_triangles.size())); + imgui->text("Vertices: " + std::to_string(m_vertices.size())); + if (imgui->button("Force garbage collection")) + garbage_collect(); + + if (imgui->button("Serialize - deserialize")) { + auto map = serialize(); + deserialize(map); + } + + imgui->end(); + + if (! m_show_triangles) + return; + + enum vtype { + ORIGINAL = 0, + SPLIT, + INVALID + }; + + for (auto& va : m_varrays) + va.release_geometry(); + + std::array cnts; + + ::glScalef(1.01f, 1.01f, 1.01f); + + for (int tr_id=0; tr_idpush_geometry(double(m_vertices[tr.verts_idxs[i]].v[0]), + double(m_vertices[tr.verts_idxs[i]].v[1]), + double(m_vertices[tr.verts_idxs[i]].v[2]), + 0., 0., 1.); + va->push_triangle(*cnt, + *cnt+1, + *cnt+2); + *cnt += 3; + } + + ::glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); + for (vtype i : {ORIGINAL, SPLIT, INVALID}) { + GLIndexedVertexArray& va = m_varrays[i]; + va.finalize_geometry(true); + if (va.has_VBOs()) { + switch (i) { + case ORIGINAL : ::glColor3f(0.f, 0.f, 1.f); break; + case SPLIT : ::glColor3f(1.f, 0.f, 0.f); break; + case INVALID : ::glColor3f(1.f, 1.f, 0.f); break; + } + va.render(); + } + } + ::glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); +} +#endif + + + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp new file mode 100644 index 0000000000..1770c96a7d --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -0,0 +1,127 @@ +#ifndef slic3r_GLGizmoPainterBase_hpp_ +#define slic3r_GLGizmoPainterBase_hpp_ + +#include "GLGizmoBase.hpp" + +#include "slic3r/GUI/3DScene.hpp" + +#include "libslic3r/ObjectID.hpp" +#include "libslic3r/TriangleSelector.hpp" + +#include + + + + +namespace Slic3r { + +enum class EnforcerBlockerType : int8_t; + +namespace GUI { + +enum class SLAGizmoEventType : unsigned char; +class ClippingPlane; + + + +class TriangleSelectorGUI : public TriangleSelector { +public: + explicit TriangleSelectorGUI(const TriangleMesh& mesh) + : TriangleSelector(mesh) {} + + // Render current selection. Transformation matrices are supposed + // to be already set. + void render(ImGuiWrapper* imgui = nullptr); + +#ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG + void render_debug(ImGuiWrapper* imgui); + bool m_show_triangles{false}; + bool m_show_invalid{false}; +#endif + +private: + GLIndexedVertexArray m_iva_enforcers; + GLIndexedVertexArray m_iva_blockers; + std::array m_varrays; +}; + + + +class GLGizmoPainterBase : public GLGizmoBase +{ +private: + ObjectID m_old_mo_id; + size_t m_old_volumes_size = 0; + + GLUquadricObj* m_quadric; + + float m_cursor_radius = 2.f; + static constexpr float CursorRadiusMin = 0.4f; // cannot be zero + static constexpr float CursorRadiusMax = 8.f; + static constexpr float CursorRadiusStep = 0.2f; + + // For each model-part volume, store status and division of the triangles. + std::vector> m_triangle_selectors; + +public: + GLGizmoPainterBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + ~GLGizmoPainterBase() override; + void set_fdm_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); + + +private: + bool on_init() override; + void on_render() const override; + void on_render_for_picking() const override {} + + void render_triangles(const Selection& selection) const; + void render_cursor_circle() const; + + void update_model_object() const; + void update_from_model_object(); + void activate_internal_undo_redo_stack(bool activate); + + void select_facets_by_angle(float threshold, bool block); + float m_angle_threshold_deg = 45.f; + + bool is_mesh_point_clipped(const Vec3d& point) const; + + float m_clipping_plane_distance = 0.f; + std::unique_ptr m_clipping_plane; + bool m_setting_angle = false; + bool m_internal_stack_active = false; + bool m_schedule_update = false; + Vec2d m_last_mouse_position = Vec2d::Zero(); + + // This map holds all translated description texts, so they can be easily referenced during layout calculations + // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. + std::map m_desc; + + enum class Button { + None, + Left, + Right + }; + + Button m_button_down = Button::None; + EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state) + +protected: + void on_set_state() override; + void on_start_dragging() override; + void on_stop_dragging() override; + void on_render_input_window(float x, float y, float bottom_limit) override; + std::string on_get_name() const override; + bool on_is_activable() const override; + bool on_is_selectable() const override; + void on_load(cereal::BinaryInputArchive& ar) override; + void on_save(cereal::BinaryOutputArchive& ar) const override; + CommonGizmosDataID on_get_requirements() const override; +}; + + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLGizmoPainterBase_hpp_ From a9435cccb8f9a93c7ab03b40a4ed75232c0af7d6 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 26 Aug 2020 11:33:41 +0200 Subject: [PATCH 380/503] Finished separation of FDM gizmo into base and child --- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 296 +++++++++++++++++++ src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp | 23 ++ src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 290 +----------------- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp | 54 ++-- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 12 +- src/slic3r/GUI/Gizmos/GLGizmosManager.hpp | 2 +- 6 files changed, 356 insertions(+), 321 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index e69de29bb2..cc08f86a73 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -0,0 +1,296 @@ +#include "GLGizmoFdmSupports.hpp" + +#include "libslic3r/Model.hpp" + +//#include "slic3r/GUI/3DScene.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/ImGuiWrapper.hpp" +#include "slic3r/GUI/Plater.hpp" + + +#include + + +namespace Slic3r { + +namespace GUI { + + + +void GLGizmoFdmSupports::on_opening() +{ + +} + + + +void GLGizmoFdmSupports::on_shutdown() +{ + if (m_setting_angle) { + m_setting_angle = false; + m_parent.use_slope(false); + } +} + + + +bool GLGizmoFdmSupports::on_init() +{ + m_shortcut_key = WXK_CONTROL_L; + + m_desc["clipping_of_view"] = _L("Clipping of view") + ": "; + m_desc["reset_direction"] = _L("Reset direction"); + m_desc["cursor_size"] = _L("Cursor size") + ": "; + m_desc["enforce_caption"] = _L("Left mouse button") + ": "; + m_desc["enforce"] = _L("Enforce supports"); + m_desc["block_caption"] = _L("Right mouse button") + " "; + m_desc["block"] = _L("Block supports"); + m_desc["remove_caption"] = _L("Shift + Left mouse button") + ": "; + m_desc["remove"] = _L("Remove selection"); + m_desc["remove_all"] = _L("Remove all selection"); + + return true; +} + + + +void GLGizmoFdmSupports::on_render() const +{ + const Selection& selection = m_parent.get_selection(); + + glsafe(::glEnable(GL_BLEND)); + glsafe(::glEnable(GL_DEPTH_TEST)); + + if (! m_setting_angle) + render_triangles(selection); + + m_c->object_clipper()->render_cut(); + render_cursor_circle(); + + glsafe(::glDisable(GL_BLEND)); +} + + + +void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_limit) +{ + if (! m_c->selection_info()->model_object()) + return; + + const float approx_height = m_imgui->scaled(18.0f); + y = std::min(y, bottom_limit - approx_height); + m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); + + if (! m_setting_angle) { + m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); + + // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: + const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f); + const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f); + const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f); + const float minimal_slider_width = m_imgui->scaled(4.f); + + float caption_max = 0.f; + float total_text_max = 0.; + for (const std::string& t : {"enforce", "block", "remove"}) { + caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc.at(t+"_caption")).x); + total_text_max = std::max(total_text_max, caption_max + m_imgui->calc_text_size(m_desc.at(t)).x); + } + caption_max += m_imgui->scaled(1.f); + total_text_max += m_imgui->scaled(1.f); + + float window_width = minimal_slider_width + std::max(cursor_slider_left, clipping_slider_left); + window_width = std::max(window_width, total_text_max); + window_width = std::max(window_width, button_width); + + auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) { + static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); + m_imgui->text_colored(ORANGE, caption); + ImGui::SameLine(caption_max); + m_imgui->text(text); + }; + + for (const std::string& t : {"enforce", "block", "remove"}) + draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t)); + + m_imgui->text(""); + + if (m_imgui->button("Autoset by angle...")) { + m_setting_angle = true; + } + + ImGui::SameLine(); + + if (m_imgui->button(m_desc.at("remove_all"))) { + Plater::TakeSnapshot(wxGetApp().plater(), wxString(_L("Reset selection"))); + ModelObject* mo = m_c->selection_info()->model_object(); + int idx = -1; + for (ModelVolume* mv : mo->volumes) { + if (mv->is_model_part()) { + ++idx; + m_triangle_selectors[idx]->reset(); + } + } + + update_model_object(); + m_parent.set_as_dirty(); + } + + const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; + + m_imgui->text(m_desc.at("cursor_size")); + ImGui::SameLine(clipping_slider_left); + ImGui::PushItemWidth(window_width - clipping_slider_left); + ImGui::SliderFloat(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f"); + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(max_tooltip_width); + ImGui::TextUnformatted(_L("Alt + Mouse wheel").ToUTF8().data()); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } + + ImGui::Separator(); + if (m_c->object_clipper()->get_position() == 0.f) + m_imgui->text(m_desc.at("clipping_of_view")); + else { + if (m_imgui->button(m_desc.at("reset_direction"))) { + wxGetApp().CallAfter([this](){ + m_c->object_clipper()->set_position(-1., false); + }); + } + } + + ImGui::SameLine(clipping_slider_left); + ImGui::PushItemWidth(window_width - clipping_slider_left); + float clp_dist = m_c->object_clipper()->get_position(); + if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f")) + m_c->object_clipper()->set_position(clp_dist, true); + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(max_tooltip_width); + ImGui::TextUnformatted(_L("Ctrl + Mouse wheel").ToUTF8().data()); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } + + m_imgui->end(); + if (m_setting_angle) { + m_parent.show_slope(false); + m_parent.set_slope_range({90.f - m_angle_threshold_deg, 90.f - m_angle_threshold_deg}); + m_parent.use_slope(true); + m_parent.set_as_dirty(); + } + } + else { + std::string name = "Autoset custom supports"; + m_imgui->begin(wxString(name), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); + m_imgui->text("Threshold:"); + ImGui::SameLine(); + if (m_imgui->slider_float("", &m_angle_threshold_deg, 0.f, 90.f, "%.f")) + m_parent.set_slope_range({90.f - m_angle_threshold_deg, 90.f - m_angle_threshold_deg}); + if (m_imgui->button("Enforce")) + select_facets_by_angle(m_angle_threshold_deg, false); + ImGui::SameLine(); + if (m_imgui->button("Block")) + select_facets_by_angle(m_angle_threshold_deg, true); + ImGui::SameLine(); + if (m_imgui->button("Cancel")) + m_setting_angle = false; + m_imgui->end(); + if (! m_setting_angle) { + m_parent.use_slope(false); + m_parent.set_as_dirty(); + } + } +} + + + +void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool block) +{ + float threshold = (M_PI/180.)*threshold_deg; + const Selection& selection = m_parent.get_selection(); + const ModelObject* mo = m_c->selection_info()->model_object(); + const ModelInstance* mi = mo->instances[selection.get_instance_idx()]; + + int mesh_id = -1; + for (const ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) + continue; + + ++mesh_id; + + const Transform3d trafo_matrix = mi->get_matrix(true) * mv->get_matrix(true); + Vec3f down = (trafo_matrix.inverse() * (-Vec3d::UnitZ())).cast().normalized(); + Vec3f limit = (trafo_matrix.inverse() * Vec3d(std::sin(threshold), 0, -std::cos(threshold))).cast().normalized(); + + float dot_limit = limit.dot(down); + + // Now calculate dot product of vert_direction and facets' normals. + int idx = -1; + for (const stl_facet& facet : mv->mesh().stl.facet_start) { + ++idx; + if (facet.normal.dot(down) > dot_limit) + m_triangle_selectors[mesh_id]->set_facet(idx, + block + ? EnforcerBlockerType::BLOCKER + : EnforcerBlockerType::ENFORCER); + } + } + + activate_internal_undo_redo_stack(true); + + Plater::TakeSnapshot(wxGetApp().plater(), block ? _L("Block supports by angle") + : _L("Add supports by angle")); + update_model_object(); + m_parent.set_as_dirty(); + m_setting_angle = false; +} + + + +void GLGizmoFdmSupports::update_model_object() const +{ + bool updated = false; + ModelObject* mo = m_c->selection_info()->model_object(); + int idx = -1; + for (ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) + continue; + ++idx; + updated |= mv->m_supported_facets.set(*m_triangle_selectors[idx].get()); + } + + if (updated) + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); +} + + + +void GLGizmoFdmSupports::update_from_model_object() +{ + wxBusyCursor wait; + + const ModelObject* mo = m_c->selection_info()->model_object(); + m_triangle_selectors.clear(); + + int volume_id = -1; + for (const ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) + continue; + + ++volume_id; + + // This mesh does not account for the possible Z up SLA offset. + const TriangleMesh* mesh = &mv->mesh(); + + m_triangle_selectors.emplace_back(std::make_unique(*mesh)); + m_triangle_selectors.back()->deserialize(mv->m_supported_facets.get_data()); + } +} + + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index 196a21bc03..dc0788c2c8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -13,6 +13,29 @@ public: GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoPainterBase(parent, icon_filename, sprite_id) {} +protected: + void on_render_input_window(float x, float y, float bottom_limit) override; + +private: + bool on_init() override; + void on_render() const override; + void on_render_for_picking() const override {} + + void update_model_object() const override; + void update_from_model_object() override; + + void on_opening() override; + void on_shutdown() override; + + void select_facets_by_angle(float threshold, bool block); + float m_angle_threshold_deg = 45.f; + bool m_setting_angle = false; + + + + // This map holds all translated description texts, so they can be easily referenced during layout calculations + // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. + std::map m_desc; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 37792a48e0..365d71316c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -19,39 +19,10 @@ namespace GUI { GLGizmoPainterBase::GLGizmoPainterBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) - , m_quadric(nullptr) { m_clipping_plane.reset(new ClippingPlane()); - m_quadric = ::gluNewQuadric(); - if (m_quadric != nullptr) - // using GLU_FILL does not work when the instance's transformation - // contains mirroring (normals are reverted) - ::gluQuadricDrawStyle(m_quadric, GLU_FILL); } -GLGizmoPainterBase::~GLGizmoPainterBase() -{ - if (m_quadric != nullptr) - ::gluDeleteQuadric(m_quadric); -} - -bool GLGizmoPainterBase::on_init() -{ - m_shortcut_key = WXK_CONTROL_L; - - m_desc["clipping_of_view"] = _L("Clipping of view") + ": "; - m_desc["reset_direction"] = _L("Reset direction"); - m_desc["cursor_size"] = _L("Cursor size") + ": "; - m_desc["enforce_caption"] = _L("Left mouse button") + ": "; - m_desc["enforce"] = _L("Enforce supports"); - m_desc["block_caption"] = _L("Right mouse button") + " "; - m_desc["block"] = _L("Block supports"); - m_desc["remove_caption"] = _L("Shift + Left mouse button") + ": "; - m_desc["remove"] = _L("Remove selection"); - m_desc["remove_all"] = _L("Remove all selection"); - - return true; -} void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate) @@ -68,7 +39,9 @@ void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate) } } -void GLGizmoPainterBase::set_fdm_support_data(ModelObject* model_object, const Selection& selection) + + +void GLGizmoPainterBase::set_painter_gizmo_data(const Selection& selection) { if (m_state != On) return; @@ -87,26 +60,8 @@ void GLGizmoPainterBase::set_fdm_support_data(ModelObject* model_object, const S -void GLGizmoPainterBase::on_render() const -{ - const Selection& selection = m_parent.get_selection(); - - glsafe(::glEnable(GL_BLEND)); - glsafe(::glEnable(GL_DEPTH_TEST)); - - render_triangles(selection); - - m_c->object_clipper()->render_cut(); - render_cursor_circle(); - - glsafe(::glDisable(GL_BLEND)); -} - void GLGizmoPainterBase::render_triangles(const Selection& selection) const { - if (m_setting_angle) - return; - const ModelObject* mo = m_c->selection_info()->model_object(); glsafe(::glEnable(GL_POLYGON_OFFSET_FILL)); @@ -145,8 +100,7 @@ void GLGizmoPainterBase::render_triangles(const Selection& selection) const glsafe(::glPushMatrix()); glsafe(::glMultMatrixd(trafo_matrix.data())); - if (! m_setting_angle) - m_triangle_selectors[mesh_id]->render(m_imgui); + m_triangle_selectors[mesh_id]->render(m_imgui); glsafe(::glPopMatrix()); if (is_left_handed) @@ -202,46 +156,6 @@ void GLGizmoPainterBase::render_cursor_circle() const } -void GLGizmoPainterBase::update_model_object() const -{ - bool updated = false; - ModelObject* mo = m_c->selection_info()->model_object(); - int idx = -1; - for (ModelVolume* mv : mo->volumes) { - if (! mv->is_model_part()) - continue; - ++idx; - updated |= mv->m_supported_facets.set(*m_triangle_selectors[idx].get()); - } - - if (updated) - m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); -} - - -void GLGizmoPainterBase::update_from_model_object() -{ - wxBusyCursor wait; - - const ModelObject* mo = m_c->selection_info()->model_object(); - m_triangle_selectors.clear(); - - int volume_id = -1; - for (const ModelVolume* mv : mo->volumes) { - if (! mv->is_model_part()) - continue; - - ++volume_id; - - // This mesh does not account for the possible Z up SLA offset. - const TriangleMesh* mesh = &mv->mesh(); - - m_triangle_selectors.emplace_back(std::make_unique(*mesh)); - m_triangle_selectors.back()->deserialize(mv->m_supported_facets.get_data()); - } -} - - bool GLGizmoPainterBase::is_mesh_point_clipped(const Vec3d& point) const { @@ -461,179 +375,10 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous -void GLGizmoPainterBase::select_facets_by_angle(float threshold_deg, bool block) -{ - float threshold = (M_PI/180.)*threshold_deg; - const Selection& selection = m_parent.get_selection(); - const ModelObject* mo = m_c->selection_info()->model_object(); - const ModelInstance* mi = mo->instances[selection.get_instance_idx()]; - - int mesh_id = -1; - for (const ModelVolume* mv : mo->volumes) { - if (! mv->is_model_part()) - continue; - - ++mesh_id; - - const Transform3d trafo_matrix = mi->get_matrix(true) * mv->get_matrix(true); - Vec3f down = (trafo_matrix.inverse() * (-Vec3d::UnitZ())).cast().normalized(); - Vec3f limit = (trafo_matrix.inverse() * Vec3d(std::sin(threshold), 0, -std::cos(threshold))).cast().normalized(); - - float dot_limit = limit.dot(down); - - // Now calculate dot product of vert_direction and facets' normals. - int idx = -1; - for (const stl_facet& facet : mv->mesh().stl.facet_start) { - ++idx; - if (facet.normal.dot(down) > dot_limit) - m_triangle_selectors[mesh_id]->set_facet(idx, - block - ? EnforcerBlockerType::BLOCKER - : EnforcerBlockerType::ENFORCER); - } - } - - activate_internal_undo_redo_stack(true); - - Plater::TakeSnapshot(wxGetApp().plater(), block ? _L("Block supports by angle") - : _L("Add supports by angle")); - update_model_object(); - m_parent.set_as_dirty(); - m_setting_angle = false; -} -void GLGizmoPainterBase::on_render_input_window(float x, float y, float bottom_limit) -{ - if (! m_c->selection_info()->model_object()) - return; - const float approx_height = m_imgui->scaled(18.0f); - y = std::min(y, bottom_limit - approx_height); - m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); - if (! m_setting_angle) { - m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); - - // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: - const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f); - const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f); - const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f); - const float minimal_slider_width = m_imgui->scaled(4.f); - - float caption_max = 0.f; - float total_text_max = 0.; - for (const std::string& t : {"enforce", "block", "remove"}) { - caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc.at(t+"_caption")).x); - total_text_max = std::max(total_text_max, caption_max + m_imgui->calc_text_size(m_desc.at(t)).x); - } - caption_max += m_imgui->scaled(1.f); - total_text_max += m_imgui->scaled(1.f); - - float window_width = minimal_slider_width + std::max(cursor_slider_left, clipping_slider_left); - window_width = std::max(window_width, total_text_max); - window_width = std::max(window_width, button_width); - - auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) { - static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); - m_imgui->text_colored(ORANGE, caption); - ImGui::SameLine(caption_max); - m_imgui->text(text); - }; - - for (const std::string& t : {"enforce", "block", "remove"}) - draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t)); - - m_imgui->text(""); - - if (m_imgui->button("Autoset by angle...")) { - m_setting_angle = true; - } - - ImGui::SameLine(); - - if (m_imgui->button(m_desc.at("remove_all"))) { - Plater::TakeSnapshot(wxGetApp().plater(), wxString(_L("Reset selection"))); - ModelObject* mo = m_c->selection_info()->model_object(); - int idx = -1; - for (ModelVolume* mv : mo->volumes) { - if (mv->is_model_part()) { - ++idx; - m_triangle_selectors[idx]->reset(); - } - } - update_model_object(); - m_parent.set_as_dirty(); - } - - const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; - - m_imgui->text(m_desc.at("cursor_size")); - ImGui::SameLine(clipping_slider_left); - ImGui::PushItemWidth(window_width - clipping_slider_left); - ImGui::SliderFloat(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f"); - if (ImGui::IsItemHovered()) { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(max_tooltip_width); - ImGui::TextUnformatted(_L("Alt + Mouse wheel").ToUTF8().data()); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); - } - - ImGui::Separator(); - if (m_c->object_clipper()->get_position() == 0.f) - m_imgui->text(m_desc.at("clipping_of_view")); - else { - if (m_imgui->button(m_desc.at("reset_direction"))) { - wxGetApp().CallAfter([this](){ - m_c->object_clipper()->set_position(-1., false); - }); - } - } - - ImGui::SameLine(clipping_slider_left); - ImGui::PushItemWidth(window_width - clipping_slider_left); - float clp_dist = m_c->object_clipper()->get_position(); - if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f")) - m_c->object_clipper()->set_position(clp_dist, true); - if (ImGui::IsItemHovered()) { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(max_tooltip_width); - ImGui::TextUnformatted(_L("Ctrl + Mouse wheel").ToUTF8().data()); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); - } - - m_imgui->end(); - if (m_setting_angle) { - m_parent.show_slope(false); - m_parent.set_slope_range({90.f - m_angle_threshold_deg, 90.f - m_angle_threshold_deg}); - m_parent.use_slope(true); - m_parent.set_as_dirty(); - } - } - else { - std::string name = "Autoset custom supports"; - m_imgui->begin(wxString(name), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); - m_imgui->text("Threshold:"); - ImGui::SameLine(); - if (m_imgui->slider_float("", &m_angle_threshold_deg, 0.f, 90.f, "%.f")) - m_parent.set_slope_range({90.f - m_angle_threshold_deg, 90.f - m_angle_threshold_deg}); - if (m_imgui->button("Enforce")) - select_facets_by_angle(m_angle_threshold_deg, false); - ImGui::SameLine(); - if (m_imgui->button("Block")) - select_facets_by_angle(m_angle_threshold_deg, true); - ImGui::SameLine(); - if (m_imgui->button("Cancel")) - m_setting_angle = false; - m_imgui->end(); - if (! m_setting_angle) { - m_parent.use_slope(false); - m_parent.set_as_dirty(); - } - } -} bool GLGizmoPainterBase::on_is_activable() const { @@ -680,6 +425,7 @@ void GLGizmoPainterBase::on_set_state() return; if (m_state == On && m_old_state != On) { // the gizmo was just turned on + on_opening(); if (! m_parent.get_gizmos_manager().is_serializing()) { wxGetApp().CallAfter([this]() { activate_internal_undo_redo_stack(true); @@ -688,10 +434,7 @@ void GLGizmoPainterBase::on_set_state() } if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off // we are actually shutting down - if (m_setting_angle) { - m_setting_angle = false; - m_parent.use_slope(false); - } + on_shutdown(); activate_internal_undo_redo_stack(false); m_old_mo_id = -1; //m_iva.release_geometry(); @@ -702,37 +445,18 @@ void GLGizmoPainterBase::on_set_state() -void GLGizmoPainterBase::on_start_dragging() -{ - -} - - -void GLGizmoPainterBase::on_stop_dragging() -{ - -} - - - void GLGizmoPainterBase::on_load(cereal::BinaryInputArchive&) { // We should update the gizmo from current ModelObject, but it is not // possible at this point. That would require having updated selection and // common gizmos data, which is not done at this point. Instead, save - // a flag to do the update in set_fdm_support_data, which will be called + // a flag to do the update in set_painter_gizmo_data, which will be called // soon after. m_schedule_update = true; } -void GLGizmoPainterBase::on_save(cereal::BinaryOutputArchive&) const -{ - -} - - void TriangleSelectorGUI::render(ImGuiWrapper* imgui) { int enf_cnt = 0; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index 1770c96a7d..886807b6a5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -46,14 +46,27 @@ private: }; - +// Following class is a base class for a gizmo with ability to paint on mesh +// using circular blush (such as FDM supports gizmo and seam painting gizmo). +// The purpose is not to duplicate code related to mesh painting. class GLGizmoPainterBase : public GLGizmoBase { private: ObjectID m_old_mo_id; size_t m_old_volumes_size = 0; - GLUquadricObj* m_quadric; +public: + GLGizmoPainterBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + ~GLGizmoPainterBase() override {} + void set_painter_gizmo_data(const Selection& selection); + bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); + +protected: + void render_triangles(const Selection& selection) const; + void render_cursor_circle() const; + virtual void update_model_object() const = 0; + virtual void update_from_model_object() = 0; + void activate_internal_undo_redo_stack(bool activate); float m_cursor_radius = 2.f; static constexpr float CursorRadiusMin = 0.4f; // cannot be zero @@ -63,41 +76,17 @@ private: // For each model-part volume, store status and division of the triangles. std::vector> m_triangle_selectors; -public: - GLGizmoPainterBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - ~GLGizmoPainterBase() override; - void set_fdm_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); - private: - bool on_init() override; - void on_render() const override; - void on_render_for_picking() const override {} - - void render_triangles(const Selection& selection) const; - void render_cursor_circle() const; - - void update_model_object() const; - void update_from_model_object(); - void activate_internal_undo_redo_stack(bool activate); - - void select_facets_by_angle(float threshold, bool block); - float m_angle_threshold_deg = 45.f; - bool is_mesh_point_clipped(const Vec3d& point) const; float m_clipping_plane_distance = 0.f; std::unique_ptr m_clipping_plane; - bool m_setting_angle = false; + bool m_internal_stack_active = false; bool m_schedule_update = false; Vec2d m_last_mouse_position = Vec2d::Zero(); - // This map holds all translated description texts, so they can be easily referenced during layout calculations - // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. - std::map m_desc; - enum class Button { None, Left, @@ -109,14 +98,17 @@ private: protected: void on_set_state() override; - void on_start_dragging() override; - void on_stop_dragging() override; - void on_render_input_window(float x, float y, float bottom_limit) override; + void on_start_dragging() override {} + void on_stop_dragging() override {} + + virtual void on_opening() = 0; + virtual void on_shutdown() = 0; + std::string on_get_name() const override; bool on_is_activable() const override; bool on_is_selectable() const override; void on_load(cereal::BinaryInputArchive& ar) override; - void on_save(cereal::BinaryOutputArchive& ar) const override; + void on_save(cereal::BinaryOutputArchive& ar) const override {} CommonGizmosDataID on_get_requirements() const override; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 78998b92d9..089e2c6ffc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -221,7 +221,7 @@ void GLGizmosManager::update_data() ModelObject* model_object = selection.get_model()->objects[selection.get_object_idx()]; set_flattening_data(model_object); set_sla_support_data(model_object); - set_fdm_support_data(model_object); + set_painter_gizmo_data(); } else if (selection.is_single_volume() || selection.is_single_modifier()) { @@ -230,7 +230,7 @@ void GLGizmosManager::update_data() set_rotation(Vec3d::Zero()); set_flattening_data(nullptr); set_sla_support_data(nullptr); - set_fdm_support_data(nullptr); + set_painter_gizmo_data(); } else if (is_wipe_tower) { @@ -239,7 +239,7 @@ void GLGizmosManager::update_data() set_rotation(Vec3d(0., 0., (M_PI/180.) * dynamic_cast(config.option("wipe_tower_rotation_angle"))->value)); set_flattening_data(nullptr); set_sla_support_data(nullptr); - set_fdm_support_data(nullptr); + set_painter_gizmo_data(); } else { @@ -247,7 +247,7 @@ void GLGizmosManager::update_data() set_rotation(Vec3d::Zero()); set_flattening_data(selection.is_from_single_object() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr); set_sla_support_data(selection.is_from_single_instance() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr); - set_fdm_support_data(selection.is_from_single_instance() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr); + set_painter_gizmo_data(); } } @@ -382,12 +382,12 @@ void GLGizmosManager::set_sla_support_data(ModelObject* model_object) gizmo_supports->set_sla_support_data(model_object, m_parent.get_selection()); } -void GLGizmosManager::set_fdm_support_data(ModelObject* model_object) +void GLGizmosManager::set_painter_gizmo_data() { if (!m_enabled || m_gizmos.empty()) return; - dynamic_cast(m_gizmos[FdmSupports].get())->set_fdm_support_data(model_object, m_parent.get_selection()); + dynamic_cast(m_gizmos[FdmSupports].get())->set_painter_gizmo_data(m_parent.get_selection()); } // Returns true if the gizmo used the event to do something, false otherwise. diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 4ad46a2a92..b8b78eceb0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -203,7 +203,7 @@ public: void set_sla_support_data(ModelObject* model_object); - void set_fdm_support_data(ModelObject* model_object); + void set_painter_gizmo_data(); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position = Vec2d::Zero(), bool shift_down = false, bool alt_down = false, bool control_down = false); ClippingPlane get_clipping_plane() const; From db7559157ca6b4e06481dfbf4eb3d8f823977da7 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 26 Aug 2020 13:15:15 +0200 Subject: [PATCH 381/503] Revert "Forbid translation of objects when SLA/Hollow/FDM gizmos are active" This reverts commit c29171790930a1a9f9b0374b6a5ab8ccec1e88a9. Translation of object when those gizmos are active should already be supressed by previous commit (ba97ebb0). The FDM gizmo was erroneously not blocking the translation, the commit that is reverted is therefore needless after this was fixed the way it should have been fixed in the first place. --- src/slic3r/GUI/GLCanvas3D.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 94f6f6ef32..04ce89a80e 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3657,14 +3657,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (!m_mouse.drag.move_requires_threshold) { m_mouse.dragging = true; - - // Translation of objects is forbidden when SLA supports/hollowing/fdm - // supports gizmo is active. - if (m_gizmos.get_current_type() == GLGizmosManager::SlaSupports - || m_gizmos.get_current_type() == GLGizmosManager::FdmSupports - || m_gizmos.get_current_type() == GLGizmosManager::Hollow) - return; - Vec3d cur_pos = m_mouse.drag.start_position_3D; // we do not want to translate objects if the user just clicked on an object while pressing shift to remove it from the selection and then drag if (m_selection.contains_volume(get_first_hover_volume_idx())) From 01b59ff57b7fdb85a25b623a0ebf6cb7dc02a1c7 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 31 Aug 2020 07:25:12 +0200 Subject: [PATCH 382/503] Seam gizmo created on frontend --- resources/icons/seam.svg | 42 ++++ src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/GLCanvas3D.cpp | 6 +- src/slic3r/GUI/Gizmos/GLGizmoBase.hpp | 1 - src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 20 +- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp | 3 +- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 10 - src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp | 1 - src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp | 208 +++++++++++++++++++ src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp | 42 ++++ src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 24 ++- src/slic3r/GUI/Gizmos/GLGizmosManager.hpp | 1 + 12 files changed, 323 insertions(+), 37 deletions(-) create mode 100644 resources/icons/seam.svg create mode 100644 src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp create mode 100644 src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp diff --git a/resources/icons/seam.svg b/resources/icons/seam.svg new file mode 100644 index 0000000000..119fb6afcc --- /dev/null +++ b/resources/icons/seam.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index f1089ae935..33994fe8ec 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -53,6 +53,8 @@ set(SLIC3R_GUI_SOURCES GUI/Gizmos/GLGizmoHollow.hpp GUI/Gizmos/GLGizmoPainterBase.cpp GUI/Gizmos/GLGizmoPainterBase.hpp + GUI/Gizmos/GLGizmoSeam.cpp + GUI/Gizmos/GLGizmoSeam.hpp GUI/GLSelectionRectangle.cpp GUI/GLSelectionRectangle.hpp GUI/GLTexture.hpp diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 04ce89a80e..6646a12579 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3582,7 +3582,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if (evt.LeftDown() && (evt.ShiftDown() || evt.AltDown()) && m_picking_enabled) { if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports - && m_gizmos.get_current_type() != GLGizmosManager::FdmSupports) + && m_gizmos.get_current_type() != GLGizmosManager::FdmSupports + && m_gizmos.get_current_type() != GLGizmosManager::Seam) { m_rectangle_selection.start_dragging(m_mouse.position, evt.ShiftDown() ? GLSelectionRectangle::Select : GLSelectionRectangle::Deselect); m_dirty = true; @@ -5317,7 +5318,8 @@ void GLCanvas3D::_render_bed(bool bottom, bool show_axes) const bool show_texture = ! bottom || (m_gizmos.get_current_type() != GLGizmosManager::FdmSupports - && m_gizmos.get_current_type() != GLGizmosManager::SlaSupports); + && m_gizmos.get_current_type() != GLGizmosManager::SlaSupports + && m_gizmos.get_current_type() != GLGizmosManager::Seam); wxGetApp().plater()->get_bed().render(const_cast(*this), bottom, scale_factor, show_axes, show_texture); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index 3ab58c2585..44f0a69729 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -124,7 +124,6 @@ public: void set_state(EState state) { m_state = state; on_set_state(); } int get_shortcut_key() const { return m_shortcut_key; } - void set_shortcut_key(int key) { m_shortcut_key = key; } const std::string& get_icon_filename() const { return m_icon_filename; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index cc08f86a73..a34eca1a66 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -18,13 +18,6 @@ namespace GUI { -void GLGizmoFdmSupports::on_opening() -{ - -} - - - void GLGizmoFdmSupports::on_shutdown() { if (m_setting_angle) { @@ -35,6 +28,13 @@ void GLGizmoFdmSupports::on_shutdown() +std::string GLGizmoFdmSupports::on_get_name() const +{ + return (_(L("FDM Support Editing")) + " [L]").ToUTF8().data(); +} + + + bool GLGizmoFdmSupports::on_init() { m_shortcut_key = WXK_CONTROL_L; @@ -176,12 +176,6 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l } m_imgui->end(); - if (m_setting_angle) { - m_parent.show_slope(false); - m_parent.set_slope_range({90.f - m_angle_threshold_deg, 90.f - m_angle_threshold_deg}); - m_parent.use_slope(true); - m_parent.set_as_dirty(); - } } else { std::string name = "Autoset custom supports"; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index dc0788c2c8..7100d611e6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -15,6 +15,7 @@ public: protected: void on_render_input_window(float x, float y, float bottom_limit) override; + std::string on_get_name() const override; private: bool on_init() override; @@ -24,7 +25,7 @@ private: void update_model_object() const override; void update_from_model_object() override; - void on_opening() override; + void on_opening() override {} void on_shutdown() override; void select_facets_by_angle(float threshold, bool block); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 365d71316c..1809b417cc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -375,11 +375,6 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous - - - - - bool GLGizmoPainterBase::on_is_activable() const { const Selection& selection = m_parent.get_selection(); @@ -403,11 +398,6 @@ bool GLGizmoPainterBase::on_is_selectable() const && wxGetApp().get_mode() != comSimple ); } -std::string GLGizmoPainterBase::on_get_name() const -{ - return (_(L("FDM Support Editing")) + " [L]").ToUTF8().data(); -} - CommonGizmosDataID GLGizmoPainterBase::on_get_requirements() const { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index 886807b6a5..da9b378957 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -104,7 +104,6 @@ protected: virtual void on_opening() = 0; virtual void on_shutdown() = 0; - std::string on_get_name() const override; bool on_is_activable() const override; bool on_is_selectable() const override; void on_load(cereal::BinaryInputArchive& ar) override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp new file mode 100644 index 0000000000..8a08f5ebe7 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp @@ -0,0 +1,208 @@ +#include "GLGizmoSeam.hpp" + +#include "libslic3r/Model.hpp" + +//#include "slic3r/GUI/3DScene.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/ImGuiWrapper.hpp" +#include "slic3r/GUI/Plater.hpp" + + +#include + + +namespace Slic3r { + +namespace GUI { + + + +bool GLGizmoSeam::on_init() +{ + m_shortcut_key = WXK_CONTROL_P; + + m_desc["clipping_of_view"] = _L("Clipping of view") + ": "; + m_desc["reset_direction"] = _L("Reset direction"); + m_desc["cursor_size"] = _L("Cursor size") + ": "; + m_desc["enforce_caption"] = _L("Left mouse button") + ": "; + m_desc["enforce"] = _L("Enforce seam"); + m_desc["block_caption"] = _L("Right mouse button") + " "; + m_desc["block"] = _L("Block seam"); + m_desc["remove_caption"] = _L("Shift + Left mouse button") + ": "; + m_desc["remove"] = _L("Remove selection"); + m_desc["remove_all"] = _L("Remove all selection"); + + return true; +} + + + +std::string GLGizmoSeam::on_get_name() const +{ + return (_(L("Seam Editing")) + " [P]").ToUTF8().data(); +} + + + +void GLGizmoSeam::on_render() const +{ + const Selection& selection = m_parent.get_selection(); + + glsafe(::glEnable(GL_BLEND)); + glsafe(::glEnable(GL_DEPTH_TEST)); + + render_triangles(selection); + + m_c->object_clipper()->render_cut(); + render_cursor_circle(); + + glsafe(::glDisable(GL_BLEND)); +} + + + +void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit) +{ + if (! m_c->selection_info()->model_object()) + return; + + const float approx_height = m_imgui->scaled(18.0f); + y = std::min(y, bottom_limit - approx_height); + m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); + + + m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); + + // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: + const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f); + const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f); + const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f); + const float minimal_slider_width = m_imgui->scaled(4.f); + + float caption_max = 0.f; + float total_text_max = 0.; + for (const std::string& t : {"enforce", "block", "remove"}) { + caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc.at(t+"_caption")).x); + total_text_max = std::max(total_text_max, caption_max + m_imgui->calc_text_size(m_desc.at(t)).x); + } + caption_max += m_imgui->scaled(1.f); + total_text_max += m_imgui->scaled(1.f); + + float window_width = minimal_slider_width + std::max(cursor_slider_left, clipping_slider_left); + window_width = std::max(window_width, total_text_max); + window_width = std::max(window_width, button_width); + + auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) { + static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); + m_imgui->text_colored(ORANGE, caption); + ImGui::SameLine(caption_max); + m_imgui->text(text); + }; + + for (const std::string& t : {"enforce", "block", "remove"}) + draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t)); + + m_imgui->text(""); + + if (m_imgui->button(m_desc.at("remove_all"))) { + Plater::TakeSnapshot(wxGetApp().plater(), wxString(_L("Reset selection"))); + ModelObject* mo = m_c->selection_info()->model_object(); + int idx = -1; + for (ModelVolume* mv : mo->volumes) { + if (mv->is_model_part()) { + ++idx; + m_triangle_selectors[idx]->reset(); + } + } + + update_model_object(); + m_parent.set_as_dirty(); + } + + const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; + + m_imgui->text(m_desc.at("cursor_size")); + ImGui::SameLine(clipping_slider_left); + ImGui::PushItemWidth(window_width - clipping_slider_left); + ImGui::SliderFloat(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f"); + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(max_tooltip_width); + ImGui::TextUnformatted(_L("Alt + Mouse wheel").ToUTF8().data()); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } + + ImGui::Separator(); + if (m_c->object_clipper()->get_position() == 0.f) + m_imgui->text(m_desc.at("clipping_of_view")); + else { + if (m_imgui->button(m_desc.at("reset_direction"))) { + wxGetApp().CallAfter([this](){ + m_c->object_clipper()->set_position(-1., false); + }); + } + } + + ImGui::SameLine(clipping_slider_left); + ImGui::PushItemWidth(window_width - clipping_slider_left); + float clp_dist = m_c->object_clipper()->get_position(); + if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f")) + m_c->object_clipper()->set_position(clp_dist, true); + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(max_tooltip_width); + ImGui::TextUnformatted(_L("Ctrl + Mouse wheel").ToUTF8().data()); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } + + m_imgui->end(); +} + + + +void GLGizmoSeam::update_model_object() const +{ + bool updated = false; + ModelObject* mo = m_c->selection_info()->model_object(); + int idx = -1; + for (ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) + continue; + ++idx; + updated |= mv->m_supported_facets.set(*m_triangle_selectors[idx].get()); + } + + if (updated) + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); +} + + + +void GLGizmoSeam::update_from_model_object() +{ + wxBusyCursor wait; + + const ModelObject* mo = m_c->selection_info()->model_object(); + m_triangle_selectors.clear(); + + int volume_id = -1; + for (const ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) + continue; + + ++volume_id; + + // This mesh does not account for the possible Z up SLA offset. + const TriangleMesh* mesh = &mv->mesh(); + + m_triangle_selectors.emplace_back(std::make_unique(*mesh)); + m_triangle_selectors.back()->deserialize(mv->m_supported_facets.get_data()); + } +} + + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp new file mode 100644 index 0000000000..469ec9180c --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp @@ -0,0 +1,42 @@ +#ifndef slic3r_GLGizmoSeam_hpp_ +#define slic3r_GLGizmoSeam_hpp_ + +#include "GLGizmoPainterBase.hpp" + +namespace Slic3r { + +namespace GUI { + +class GLGizmoSeam : public GLGizmoPainterBase +{ +public: + GLGizmoSeam(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) + : GLGizmoPainterBase(parent, icon_filename, sprite_id) {} + +protected: + void on_render_input_window(float x, float y, float bottom_limit) override; + std::string on_get_name() const override; + +private: + bool on_init() override; + void on_render() const override; + void on_render_for_picking() const override {} + + void update_model_object() const override; + void update_from_model_object() override; + + void on_opening() override {} + void on_shutdown() override {} + + // This map holds all translated description texts, so they can be easily referenced during layout calculations + // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. + std::map m_desc; +}; + + + +} // namespace GUI +} // namespace Slic3r + + +#endif // slic3r_GLGizmoSeam_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 089e2c6ffc..1087c64d5a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -16,6 +16,7 @@ #include "slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp" #include "slic3r/GUI/Gizmos/GLGizmoCut.hpp" #include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/PresetBundle.hpp" @@ -104,6 +105,7 @@ bool GLGizmosManager::init() m_gizmos.emplace_back(new GLGizmoHollow(m_parent, "hollow.svg", 5)); m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", 6)); m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, "sla_supports.svg", 7)); + m_gizmos.emplace_back(new GLGizmoSeam(m_parent, "seam.svg", 8)); m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent)); @@ -388,6 +390,7 @@ void GLGizmosManager::set_painter_gizmo_data() return; dynamic_cast(m_gizmos[FdmSupports].get())->set_painter_gizmo_data(m_parent.get_selection()); + dynamic_cast(m_gizmos[Seam].get())->set_painter_gizmo_data(m_parent.get_selection()); } // Returns true if the gizmo used the event to do something, false otherwise. @@ -402,6 +405,8 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p return dynamic_cast(m_gizmos[Hollow].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else if (m_current == FdmSupports) return dynamic_cast(m_gizmos[FdmSupports].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); + else if (m_current == Seam) + return dynamic_cast(m_gizmos[Seam].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else return false; } @@ -465,7 +470,7 @@ bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt) { bool processed = false; - if (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports) { + if (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam) { float rot = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta(); if (gizmo_event((rot > 0.f ? SLAGizmoEventType::MouseWheelUp : SLAGizmoEventType::MouseWheelDown), Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) processed = true; @@ -607,7 +612,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) if (evt.LeftDown()) { - if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports) + if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports ||m_current == Seam) && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) // the gizmo got the event and took some action, there is no need to do anything more processed = true; @@ -634,23 +639,24 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) // event was taken care of by the SlaSupports gizmo processed = true; } - else if (evt.RightDown() && (selected_object_idx != -1) && m_current == FdmSupports + else if (evt.RightDown() && (selected_object_idx != -1) && (m_current == FdmSupports || m_current == Seam) && gizmo_event(SLAGizmoEventType::RightDown, mouse_pos)) { - // event was taken care of by the FdmSupports gizmo + // event was taken care of by the FdmSupports / Seam gizmo processed = true; } - else if (evt.Dragging() && (m_parent.get_move_volume_id() != -1) && (m_current == SlaSupports || m_current == Hollow)) + else if (evt.Dragging() && (m_parent.get_move_volume_id() != -1) + && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam)) // don't allow dragging objects with the Sla gizmo on processed = true; - else if (evt.Dragging() && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports ) + else if (evt.Dragging() && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam ) && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) { // the gizmo got the event and took some action, no need to do anything more here m_parent.set_as_dirty(); processed = true; } - else if (evt.LeftUp() && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports) && !m_parent.is_mouse_dragging()) + else if (evt.LeftUp() && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam) && !m_parent.is_mouse_dragging()) { // in case SLA/FDM gizmo is selected, we just pass the LeftUp event and stop processing - neither // object moving or selecting is suppressed in that case @@ -662,7 +668,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) // to avoid to loose the selection when user clicks an the white faces of a different object while the Flatten gizmo is active processed = true; } - else if (evt.RightUp() && m_current == FdmSupports && !m_parent.is_mouse_dragging()) + else if (evt.RightUp() && (m_current == FdmSupports || m_current == Seam) && !m_parent.is_mouse_dragging()) { gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown()); processed = true; @@ -752,7 +758,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) case 'r' : case 'R' : { - if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports) && gizmo_event(SLAGizmoEventType::ResetClippingPlane)) + if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam) && gizmo_event(SLAGizmoEventType::ResetClippingPlane)) processed = true; break; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index b8b78eceb0..6b965525d5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -67,6 +67,7 @@ public: Hollow, SlaSupports, FdmSupports, + Seam, Undefined }; From d904862bc708ee3571480cc97b84c2def389c84e Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 1 Sep 2020 19:04:22 +0200 Subject: [PATCH 383/503] Build libpng as part of deps on Linux - We've found counter case where the system provided one is missing or is too old. --- deps/deps-linux.cmake | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/deps/deps-linux.cmake b/deps/deps-linux.cmake index 3ad3cca64f..ae972327f8 100644 --- a/deps/deps-linux.cmake +++ b/deps/deps-linux.cmake @@ -3,10 +3,11 @@ set(DEP_CMAKE_OPTS "-DCMAKE_POSITION_INDEPENDENT_CODE=ON") include("deps-unix-common.cmake") -find_package(PNG QUIET) -if (NOT PNG_FOUND) - message(WARNING "No PNG dev package found in system, building static library. You should install the system package.") -endif () +# Some Linuxes may have very old libpng, so it's best to bundle it instead of relying on the system version. +# find_package(PNG QUIET) +# if (NOT PNG_FOUND) +# message(WARNING "No PNG dev package found in system, building static library. You should install the system package.") +# endif () #TODO UDEV From 9c59b4f9305bab09af75eb1b2d61efff177efeab Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 31 Aug 2020 07:25:43 +0200 Subject: [PATCH 384/503] Custom seam: Model integration, backend invalidation, 3MF loading/saving --- src/libslic3r/Format/3mf.cpp | 14 +++++++++++++- src/libslic3r/Model.cpp | 12 ++++++++++++ src/libslic3r/Model.hpp | 14 +++++++++++--- src/libslic3r/Print.cpp | 4 ++++ src/libslic3r/Print.hpp | 6 ++---- src/libslic3r/PrintObject.cpp | 12 +++++++----- src/libslic3r/SupportMaterial.cpp | 4 ++-- src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp | 4 ++-- 8 files changed, 53 insertions(+), 17 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 59dc85a0ae..92119f91c0 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -87,6 +87,7 @@ const char* TRANSFORM_ATTR = "transform"; const char* PRINTABLE_ATTR = "printable"; const char* INSTANCESCOUNT_ATTR = "instances_count"; const char* CUSTOM_SUPPORTS_ATTR = "slic3rpe:custom_supports"; +const char* CUSTOM_SEAM_ATTR = "slic3rpe:custom_seam"; const char* KEY_ATTR = "key"; const char* VALUE_ATTR = "value"; @@ -285,6 +286,7 @@ namespace Slic3r { std::vector vertices; std::vector triangles; std::vector custom_supports; + std::vector custom_seam; bool empty() { @@ -296,6 +298,7 @@ namespace Slic3r { vertices.clear(); triangles.clear(); custom_supports.clear(); + custom_seam.clear(); } }; @@ -1544,6 +1547,7 @@ namespace Slic3r { m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V3_ATTR)); m_curr_object.geometry.custom_supports.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SUPPORTS_ATTR)); + m_curr_object.geometry.custom_seam.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SEAM_ATTR)); return true; } @@ -1877,14 +1881,18 @@ namespace Slic3r { volume->source.transform = Slic3r::Geometry::Transformation(volume_matrix_to_object); volume->calculate_convex_hull(); - // recreate custom supports from previously loaded attribute + // recreate custom supports and seam from previously loaded attribute for (unsigned i=0; im_supported_facets.set_triangle_from_string(i, geometry.custom_supports[index]); + if (! geometry.custom_seam[index].empty()) + volume->m_seam_facets.set_triangle_from_string(i, geometry.custom_seam[index]); } + // apply the remaining volume's metadata for (const Metadata& metadata : volume_data.metadata) { @@ -2401,6 +2409,10 @@ namespace Slic3r { if (! custom_supports_data_string.empty()) stream << CUSTOM_SUPPORTS_ATTR << "=\"" << custom_supports_data_string << "\" "; + std::string custom_seam_data_string = volume->m_seam_facets.get_triangle_as_string(i); + if (! custom_seam_data_string.empty()) + stream << CUSTOM_SEAM_ATTR << "=\"" << custom_seam_data_string << "\" "; + stream << "/>\n"; } } diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 196e9c213b..d12dc7a0f4 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1007,6 +1007,7 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, bool from_imperial for (ModelVolume* volume : volumes) { volume->m_supported_facets.clear(); + volume->m_seam_facets.clear(); if (!volume->mesh().empty()) { TriangleMesh mesh(volume->mesh()); mesh.require_shared_vertices(); @@ -1112,6 +1113,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b const auto volume_matrix = volume->get_matrix(); volume->m_supported_facets.clear(); + volume->m_seam_facets.clear(); if (! volume->is_model_part()) { // Modifiers are not cut, but we still need to add the instance transformation @@ -1993,6 +1995,16 @@ bool model_custom_supports_data_changed(const ModelObject& mo, const ModelObject return false; } +bool model_custom_seam_data_changed(const ModelObject& mo, const ModelObject& mo_new) { + assert(! model_volume_list_changed(mo, mo_new, ModelVolumeType::MODEL_PART)); + assert(mo.volumes.size() == mo_new.volumes.size()); + for (size_t i=0; im_seam_facets.is_same_as(mo.volumes[i]->m_seam_facets)) + return true; + } + return false; +} + extern bool model_has_multi_part_objects(const Model &model) { for (const ModelObject *model_object : model.objects) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 608ce670f6..a623f5cca0 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -464,6 +464,9 @@ public: // List of mesh facets to be supported/unsupported. FacetsAnnotation m_supported_facets; + // List of seam enforcers/blockers. + FacetsAnnotation m_seam_facets; + // A parent object owning this modifier volume. ModelObject* get_object() const { return this->object; } ModelVolumeType type() const { return m_type; } @@ -593,7 +596,7 @@ private: ObjectBase(other), name(other.name), source(other.source), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation), - m_supported_facets(other.m_supported_facets) + m_supported_facets(other.m_supported_facets), m_seam_facets(other.m_seam_facets) { assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); assert(this->id() == other.id() && this->config.id() == other.config.id()); @@ -612,6 +615,7 @@ private: assert(this->config.id().valid()); assert(this->config.id() != other.config.id()); assert(this->id() != this->config.id()); m_supported_facets.clear(); + m_seam_facets.clear(); } ModelVolume& operator=(ModelVolume &rhs) = delete; @@ -625,7 +629,7 @@ private: template void load(Archive &ar) { bool has_convex_hull; ar(name, source, m_mesh, m_type, m_material_id, m_transformation, - m_is_splittable, has_convex_hull, m_supported_facets); + m_is_splittable, has_convex_hull, m_supported_facets, m_seam_facets); cereal::load_by_value(ar, config); assert(m_mesh); if (has_convex_hull) { @@ -639,7 +643,7 @@ private: template void save(Archive &ar) const { bool has_convex_hull = m_convex_hull.get() != nullptr; ar(name, source, m_mesh, m_type, m_material_id, m_transformation, - m_is_splittable, has_convex_hull, m_supported_facets); + m_is_splittable, has_convex_hull, m_supported_facets, m_seam_facets); cereal::save_by_value(ar, config); if (has_convex_hull) cereal::save_optional(ar, m_convex_hull); @@ -904,6 +908,10 @@ extern bool model_volume_list_changed(const ModelObject &model_object_old, const // The function assumes that volumes list is synchronized. extern bool model_custom_supports_data_changed(const ModelObject& mo, const ModelObject& mo_new); +// Test whether the now ModelObject has newer custom seam data than the old one. +// The function assumes that volumes list is synchronized. +extern bool model_custom_seam_data_changed(const ModelObject& mo, const ModelObject& mo_new); + // If the model has multi-part objects, then it is currently not supported by the SLA mode. // Either the model cannot be loaded, or a SLA printer has to be activated. extern bool model_has_multi_part_objects(const Model &model); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 0c8a11fcf0..37c0a7d154 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -404,6 +404,7 @@ static inline void model_volume_list_copy_configs(ModelObject &model_object_dst, mv_dst.name = mv_src.name; static_cast(mv_dst.config) = static_cast(mv_src.config); mv_dst.m_supported_facets = mv_src.m_supported_facets; + mv_dst.m_seam_facets = mv_src.m_seam_facets; //FIXME what to do with the materials? // mv_dst.m_material_id = mv_src.m_material_id; ++ i_src; @@ -867,6 +868,9 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ model_volume_list_update_supports(model_object, model_object_new); } } + if (model_custom_seam_data_changed(model_object, model_object_new)) { + update_apply_status(this->invalidate_step(psGCodeExport)); + } if (! model_parts_differ && ! modifiers_differ) { // Synchronize Object's config. bool object_config_changed = model_object.config != model_object_new.config; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 08acb7a105..6cb80c1f44 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -186,10 +186,8 @@ public: std::vector slice_support_blockers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_BLOCKER); } std::vector slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); } - // Helpers to project custom supports on slices - void project_and_append_custom_supports(EnforcerBlockerType type, std::vector& expolys) const; - void project_and_append_custom_enforcers(std::vector& enforcers) const { project_and_append_custom_supports(EnforcerBlockerType::ENFORCER, enforcers); } - void project_and_append_custom_blockers(std::vector& blockers) const { project_and_append_custom_supports(EnforcerBlockerType::BLOCKER, blockers); } + // Helpers to project custom facets on slices + void project_and_append_custom_facets(bool seam, EnforcerBlockerType type, std::vector& expolys) const; private: // to be called from Print only. diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index ddeee1e778..aecf907710 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2669,12 +2669,14 @@ void PrintObject::_generate_support_material() } -void PrintObject::project_and_append_custom_supports( - EnforcerBlockerType type, std::vector& expolys) const +void PrintObject::project_and_append_custom_facets( + bool seam, EnforcerBlockerType type, std::vector& expolys) const { for (const ModelVolume* mv : this->model_object()->volumes) { - const indexed_triangle_set custom_facets = mv->m_supported_facets.get_facets(*mv, type); - if (custom_facets.indices.empty()) + const indexed_triangle_set custom_facets = seam + ? mv->m_seam_facets.get_facets(*mv, type) + : mv->m_supported_facets.get_facets(*mv, type); + if (! mv->is_model_part() || custom_facets.indices.empty()) continue; const Transform3f& tr1 = mv->get_matrix().cast(); @@ -2721,7 +2723,7 @@ void PrintObject::project_and_append_custom_supports( // Ignore triangles with upward-pointing normal. Don't forget about mirroring. float z_comp = (facet[1]-facet[0]).cross(facet[2]-facet[0]).z(); - if (tr_det_sign * z_comp > 0.) + if (! seam && tr_det_sign * z_comp > 0.) continue; // Sort the three vertices according to z-coordinate. diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 95b4c334b1..1669f60d21 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -972,8 +972,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ std::vector blockers = object.slice_support_blockers(); // Append custom supports. - object.project_and_append_custom_enforcers(enforcers); - object.project_and_append_custom_blockers(blockers); + object.project_and_append_custom_facets(false, EnforcerBlockerType::ENFORCER, enforcers); + object.project_and_append_custom_facets(false, EnforcerBlockerType::BLOCKER, blockers); // Output layers, sorted by top Z. MyLayersPtr contact_out; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp index 8a08f5ebe7..3c7d180a7b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp @@ -172,7 +172,7 @@ void GLGizmoSeam::update_model_object() const if (! mv->is_model_part()) continue; ++idx; - updated |= mv->m_supported_facets.set(*m_triangle_selectors[idx].get()); + updated |= mv->m_seam_facets.set(*m_triangle_selectors[idx].get()); } if (updated) @@ -199,7 +199,7 @@ void GLGizmoSeam::update_from_model_object() const TriangleMesh* mesh = &mv->mesh(); m_triangle_selectors.emplace_back(std::make_unique(*mesh)); - m_triangle_selectors.back()->deserialize(mv->m_supported_facets.get_data()); + m_triangle_selectors.back()->deserialize(mv->m_seam_facets.get_data()); } } From 46eb96e84fac3e38734c9b0aefaf829a3e028538 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 1 Sep 2020 23:26:08 +0200 Subject: [PATCH 385/503] Added two missing icons to fix build on Linux --- src/slic3r/GUI/ConfigWizard.cpp | 1 + src/slic3r/GUI/ConfigWizard_private.hpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index f8abfb1788..2cedbfdf78 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index f7987a8908..260eeb22cb 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include From d8487b1458eb768439a16bb255d82b71064a5f96 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 2 Sep 2020 09:06:42 +0200 Subject: [PATCH 386/503] Unsaved Changes: bug fix and improvements - changed width of the "Save dialog" - SavePresetDialog: added info for Print/Filament user presets incompatible with selected printer_technology - fixed missed "modified" suffix when options are moved to the another preset - "move selected options" button is added for dependent presets --- src/slic3r/GUI/PresetComboBoxes.cpp | 8 ++-- src/slic3r/GUI/Tab.cpp | 51 +++++++++++++++++-------- src/slic3r/GUI/Tab.hpp | 5 ++- src/slic3r/GUI/UnsavedChangesDialog.cpp | 22 ++++++----- 4 files changed, 58 insertions(+), 28 deletions(-) diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 9b0c9d0c86..7300a2887f 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -1059,7 +1059,7 @@ SavePresetDialog::Item::Item(Preset::Type type, const std::string& suffix, wxBox m_valid_bmp = new wxStaticBitmap(m_parent, wxID_ANY, create_scaled_bitmap("tick_mark", m_parent)); - m_combo = new wxComboBox(m_parent, wxID_ANY, from_u8(preset_name)); + m_combo = new wxComboBox(m_parent, wxID_ANY, from_u8(preset_name), wxDefaultPosition, wxSize(35 * wxGetApp().em_unit(), -1)); for (const std::string& value : values) m_combo->Append(from_u8(value)); @@ -1131,8 +1131,10 @@ void SavePresetDialog::Item::update() if (m_valid_type == Valid && existing && m_preset_name != m_presets->get_selected_preset_name()) { - info_line = from_u8((boost::format(_u8L("Preset with name \"%1%\" already exists.")) % m_preset_name).str()) + "\n" + - _L("Note: This preset will be replaced after saving"); + info_line = from_u8((boost::format(_u8L("Preset with name \"%1%\" already exists.")) % m_preset_name).str()); + if (!existing->is_compatible) + info_line += "\n" + _L("And selected preset is imcopatible with selected printer."); + info_line += "\n" + _L("Note: This preset will be replaced after saving"); m_valid_type = Warning; } diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 898890f6e5..b95227dad9 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1103,6 +1103,21 @@ void Tab::apply_searcher() wxGetApp().sidebar().get_searcher().apply(m_config, m_type, m_mode); } +void Tab::cache_config_diff(const std::vector& selected_options) +{ + m_cache_config.apply_only(m_presets->get_edited_preset().config, selected_options); +} + +void Tab::apply_config_from_cache() +{ + if (!m_cache_config.empty()) { + m_presets->get_edited_preset().config.apply(m_cache_config); + m_cache_config.clear(); + + update_dirty(); + } +} + // Call a callback to update the selection of presets on the plater: // To update the content of the selection boxes, @@ -1122,9 +1137,12 @@ void Tab::on_presets_changed() // Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors. for (auto t: m_dependent_tabs) { + Tab* tab = wxGetApp().get_tab(t); // If the printer tells us that the print or filament/sla_material preset has been switched or invalidated, // refresh the print or filament/sla_material tab page. - wxGetApp().get_tab(t)->load_current_preset(); + // But if there are options, moved from the previously selected preset, update them to edited preset + tab->apply_config_from_cache(); + tab->load_current_preset(); } // clear m_dependent_tabs after first update from select_preset() // to avoid needless preset loading from update() function @@ -3136,10 +3154,7 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, static_cast(this)->apply_extruder_cnt_from_cache(); // check if there is something in the cache to move to the new selected preset - if (!m_cache_config.empty()) { - m_presets->get_edited_preset().config.apply(m_cache_config); - m_cache_config.clear(); - } + apply_config_from_cache(); load_current_preset(); } @@ -3189,17 +3204,23 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr else if (dlg.move_preset()) // move selected changes { std::vector selected_options = dlg.get_selected_options(); - auto it = std::find(selected_options.begin(), selected_options.end(), "extruders_count"); - if (it != selected_options.end()) { - // erase "extruders_count" option from the list - selected_options.erase(it); - // cache the extruders count - if (m_type == Preset::TYPE_PRINTER) - static_cast(this)->cache_extruder_cnt(); - } + if (m_type == presets->type()) // move changes for the current preset from this tab + { + if (m_type == Preset::TYPE_PRINTER) { + auto it = std::find(selected_options.begin(), selected_options.end(), "extruders_count"); + if (it != selected_options.end()) { + // erase "extruders_count" option from the list + selected_options.erase(it); + // cache the extruders count + static_cast(this)->cache_extruder_cnt(); + } + } - // copy selected options to the cache from edited preset - m_cache_config.apply_only(*m_config, selected_options); + // copy selected options to the cache from edited preset + cache_config_diff(selected_options); + } + else + wxGetApp().get_tab(presets->type())->cache_config_diff(selected_options); } return true; diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 9bddebeab1..f0b2e97b3d 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -231,12 +231,13 @@ protected: } m_highlighter; + DynamicPrintConfig m_cache_config; + public: PresetBundle* m_preset_bundle; bool m_show_btn_incompatible_presets = false; PresetCollection* m_presets; DynamicPrintConfig* m_config; - DynamicPrintConfig m_cache_config; ogStaticText* m_parent_preset_description_line; ScalableButton* m_detach_preset_btn = nullptr; @@ -330,6 +331,8 @@ public: void update_wiping_button_visibility(); void activate_option(const std::string& opt_key, const wxString& category); void apply_searcher(); + void cache_config_diff(const std::vector& selected_options); + void apply_config_from_cache(); protected: void create_line_with_widget(ConfigOptionsGroup* optgroup, const std::string& opt_key, widget_t widget); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index c147d3e2c2..f30e719ce0 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -590,8 +590,8 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_ int btn_idx = 0; add_btn(&m_save_btn, m_save_btn_id, "save", Action::Save, btn_idx++); - if (type != Preset::TYPE_INVALID && type == dependent_presets->type() && - dependent_presets->get_edited_preset().printer_technology() == dependent_presets->find_preset(new_selected_preset)->printer_technology()) + if (dependent_presets && (type != dependent_presets->type() ? true : + dependent_presets->get_edited_preset().printer_technology() == dependent_presets->find_preset(new_selected_preset)->printer_technology())) add_btn(&m_move_btn, m_move_btn_id, "paste_menu", Action::Move, btn_idx++); add_btn(&m_continue_btn, m_continue_btn_id, "cross", Action::Continue, btn_idx, false); @@ -666,12 +666,11 @@ void UnsavedChangesDialog::show_info_line(Action action, std::string preset_name else if (action == Action::Continue) text = _L("All changed options will be reverted."); else { - if (action == Action::Save && preset_name.empty()) - text = _L("Press to save the selected options"); - else { - std::string act_string = action == Action::Save ? _u8L("saved") : _u8L("moved"); + std::string act_string = action == Action::Save ? _u8L("save") : _u8L("move"); + if (preset_name.empty()) + text = from_u8((boost::format("Press to %1% selected options.") % act_string).str()); + else text = from_u8((boost::format("Press to %1% selected options to the preset \"%2%\".") % act_string % preset_name).str()); - } text += "\n" + _L("Unselected options will be reverted."); } m_info_line->SetLabel(text); @@ -856,8 +855,10 @@ void UnsavedChangesDialog::update(Preset::Type type, PresetCollection* dependent // activate buttons and labels m_save_btn ->Bind(wxEVT_ENTER_WINDOW, [this, presets] (wxMouseEvent& e) { show_info_line(Action::Save, presets ? presets->get_selected_preset().name : ""); e.Skip(); }); - if (m_move_btn) - m_move_btn ->Bind(wxEVT_ENTER_WINDOW, [this, new_selected_preset] (wxMouseEvent& e) { show_info_line(Action::Move, new_selected_preset); e.Skip(); }); + if (m_move_btn) { + bool is_empty_name = type != dependent_presets->type(); + m_move_btn ->Bind(wxEVT_ENTER_WINDOW, [this, new_selected_preset, is_empty_name] (wxMouseEvent& e) { show_info_line(Action::Move, is_empty_name ? "" : new_selected_preset); e.Skip(); }); + } m_continue_btn ->Bind(wxEVT_ENTER_WINDOW, [this] (wxMouseEvent& e) { show_info_line(Action::Continue); e.Skip(); }); m_continue_btn->SetLabel(_L("Continue without changes")); @@ -879,6 +880,9 @@ void UnsavedChangesDialog::update(Preset::Type type, PresetCollection* dependent _L("is not compatible with print profile"); action_msg += " \"" + from_u8(new_selected_preset) + "\"\n"; action_msg += _L("and it has the following unsaved changes:"); + + if (m_move_btn) + m_move_btn->SetLabel(_L("Move selected to the first compatible preset")); } m_action_line->SetLabel(from_u8((boost::format(_utf8(L("Preset \"%1%\" %2%"))) % _utf8(presets->get_edited_preset().name) % action_msg).str())); m_save_btn->SetLabel(from_u8((boost::format(_u8L("Save selected to preset: %1%")) % ("\"" + presets->get_selected_preset().name + "\"")).str())); From 0cfa64e24568188516506f94d310d9c46688e053 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 2 Sep 2020 14:24:32 +0200 Subject: [PATCH 387/503] GCodeViewer -> Fixed bug in generating solid toolpaths and export of toolpaths to obj file --- src/slic3r/GUI/GCodeViewer.cpp | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 772b290ea8..bc424466bf 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -709,12 +709,18 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const size_t first_vertex_id = k - static_cast(indices_per_segment); Segment prev = generate_segment(indices[first_vertex_id + start_vertex_offset], indices[first_vertex_id + end_vertex_offset], half_width, half_height); - Vec3f med_dir = (prev.dir + curr.dir).normalized(); - float disp = half_width * ::tan(::acos(std::clamp(curr.dir.dot(med_dir), -1.0f, 1.0f))); + float disp = 0.0f; + float cos_dir = prev.dir.dot(curr.dir); + if (cos_dir > -0.9998477f) { + // if the angle between adjacent segments is smaller than 179 degrees + Vec3f med_dir = (prev.dir + curr.dir).normalized(); + disp = half_width * ::tan(::acos(std::clamp(curr.dir.dot(med_dir), -1.0f, 1.0f))); + } + Vec3f disp_vec = disp * prev.dir; bool is_right_turn = prev.up.dot(prev.dir.cross(curr.dir)) <= 0.0f; - if (prev.dir.dot(curr.dir) < 0.7071068f) { + if (cos_dir < 0.7071068f) { // if the angle between two consecutive segments is greater than 45 degrees // we add a cap in the outside corner // and displace the vertices in the inside corner to the same position, if possible @@ -725,7 +731,7 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const out_triangles.push_back({ base_id + 5, base_id + 3, base_id + 2 }); // update right vertices - if (disp < prev.length && disp < curr.length) { + if (disp > 0.0f && disp < prev.length && disp < curr.length) { base_id = out_vertices.size() - 6; out_vertices[base_id + 0] -= disp_vec; out_vertices[base_id + 4] = out_vertices[base_id + 0]; @@ -738,7 +744,7 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const out_triangles.push_back({ base_id + 0, base_id + 3, base_id + 4 }); // update left vertices - if (disp < prev.length && disp < curr.length) { + if (disp > 0.0f && disp < prev.length && disp < curr.length) { base_id = out_vertices.size() - 6; out_vertices[base_id + 2] -= disp_vec; out_vertices[base_id + 5] = out_vertices[base_id + 2]; @@ -1031,10 +1037,16 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) } else { // any other segment - Vec3f med_dir = (prev_dir + dir).normalized(); - float displacement = half_width * ::tan(::acos(std::clamp(dir.dot(med_dir), -1.0f, 1.0f))); + float displacement = 0.0f; + float cos_dir = prev_dir.dot(dir); + if (cos_dir > -0.9998477f) { + // if the angle between adjacent segments is smaller than 179 degrees + Vec3f med_dir = (prev_dir + dir).normalized(); + displacement = half_width * ::tan(::acos(std::clamp(dir.dot(med_dir), -1.0f, 1.0f))); + } + Vec3f displacement_vec = displacement * prev_dir; - bool can_displace = displacement < prev_length && displacement < length; + bool can_displace = displacement > 0.0f && displacement < prev_length && displacement < length; size_t prev_right_id = (starting_vertices_size - 3) * buffer.vertices.vertex_size_floats(); size_t prev_left_id = (starting_vertices_size - 1) * buffer.vertices.vertex_size_floats(); @@ -1043,7 +1055,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) bool is_right_turn = prev_up.dot(prev_dir.cross(dir)) <= 0.0f; // whether the angle between adjacent segments is greater than 45 degrees - bool is_sharp = prev_dir.dot(dir) < 0.7071068f; + bool is_sharp = cos_dir < 0.7071068f; bool right_displaced = false; bool left_displaced = false; From 5997f2759cfb1d041c47ee4e03d6cc7c03a02ac7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Wed, 2 Sep 2020 22:53:10 +0200 Subject: [PATCH 388/503] Change in passing octree struct --- src/libslic3r/Fill/Fill.cpp | 4 ++-- src/libslic3r/Layer.hpp | 6 +++++- src/libslic3r/Print.hpp | 9 ++++----- src/libslic3r/PrintObject.cpp | 20 ++++++++++---------- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index c948df400e..9d468a6aa9 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -318,7 +318,7 @@ void export_group_fills_to_svg(const char *path, const std::vector #endif // friend to Layer -void Layer::make_fills() +void Layer::make_fills(FillAdaptive_Internal::Octree* adaptive_fill_octree) { for (LayerRegion *layerm : m_regions) layerm->fills.clear(); @@ -345,7 +345,7 @@ void Layer::make_fills() f->layer_id = this->id(); f->z = this->print_z; f->angle = surface_fill.params.angle; - f->adapt_fill_octree = this->object()->adaptiveInfillOctree(); + f->adapt_fill_octree = adaptive_fill_octree; // calculate flow spacing for infill pattern generation bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.flow.bridge; diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index c104d46da1..4c824a1093 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -13,6 +13,10 @@ class Layer; class PrintRegion; class PrintObject; +namespace FillAdaptive_Internal { + struct Octree; +}; + class LayerRegion { public: @@ -134,7 +138,7 @@ public: return false; } void make_perimeters(); - void make_fills(); + void make_fills(FillAdaptive_Internal::Octree* adaptive_fill_octree); void make_ironing(); void export_region_slices_to_svg(const char *path) const; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 2e2746a345..9b5d9d4c1a 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -11,7 +11,6 @@ #include "GCode/ToolOrdering.hpp" #include "GCode/WipeTower.hpp" #include "GCode/ThumbnailData.hpp" -#include "Fill/FillAdaptive.hpp" #include "libslic3r.h" @@ -26,6 +25,9 @@ enum class SlicingMode : uint32_t; class Layer; class SupportLayer; +namespace FillAdaptive_Internal { + struct Octree; +}; // Print step IDs for keeping track of the print state. enum PrintStep { @@ -193,7 +195,6 @@ public: void project_and_append_custom_enforcers(std::vector& enforcers) const { project_and_append_custom_supports(FacetSupportType::ENFORCER, enforcers); } void project_and_append_custom_blockers(std::vector& blockers) const { project_and_append_custom_supports(FacetSupportType::BLOCKER, blockers); } - FillAdaptive_Internal::Octree* adaptiveInfillOctree() { return m_adapt_fill_octree.get(); } private: // to be called from Print only. friend class Print; @@ -235,7 +236,7 @@ private: void discover_horizontal_shells(); void combine_infill(); void _generate_support_material(); - void prepare_adaptive_infill_data(); + std::unique_ptr prepare_adaptive_infill_data(); // XYZ in scaled coordinates Vec3crd m_size; @@ -256,8 +257,6 @@ private: // so that next call to make_perimeters() performs a union() before computing loops bool m_typed_slices = false; - std::unique_ptr m_adapt_fill_octree = nullptr; - std::vector slice_region(size_t region_id, const std::vector &z, SlicingMode mode) const; std::vector slice_modifiers(size_t region_id, const std::vector &z) const; std::vector slice_volumes(const std::vector &z, SlicingMode mode, const std::vector &volumes) const; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 1ab5664a0f..25b20ea9af 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -362,8 +362,6 @@ void PrintObject::prepare_infill() } // for each layer #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - this->prepare_adaptive_infill_data(); - this->set_done(posPrepareInfill); } @@ -373,13 +371,15 @@ void PrintObject::infill() this->prepare_infill(); if (this->set_started(posInfill)) { + std::unique_ptr octree = this->prepare_adaptive_infill_data(); + BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start"; tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), - [this](const tbb::blocked_range& range) { + [this, &octree](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { m_print->throw_if_canceled(); - m_layers[layer_idx]->make_fills(); + m_layers[layer_idx]->make_fills(octree.get()); } } ); @@ -432,14 +432,14 @@ void PrintObject::generate_support_material() } } -void PrintObject::prepare_adaptive_infill_data() +std::unique_ptr PrintObject::prepare_adaptive_infill_data() { const ConfigOptionPercent* opt_fill_density = this->print()->full_print_config().option("fill_density"); const ConfigOptionFloatOrPercent* opt_infill_extrusion_width = this->print()->full_print_config().option("infill_extrusion_width"); if(opt_fill_density == nullptr || opt_infill_extrusion_width == nullptr || opt_fill_density->value <= 0 || opt_infill_extrusion_width->value <= 0) { - return; + return std::unique_ptr{}; } float fill_density = opt_fill_density->value; @@ -448,15 +448,15 @@ void PrintObject::prepare_adaptive_infill_data() coordf_t line_spacing = infill_extrusion_width / ((fill_density / 100.0f) * 0.333333333f); BoundingBoxf bed_shape(this->print()->config().bed_shape.values); - BoundingBoxf3 printer_volume(Vec3d(bed_shape.min(0), bed_shape.min(1), 0), - Vec3d(bed_shape.max(0), bed_shape.max(1), this->print()->config().max_print_height)); + BoundingBoxf3 printer_volume(Vec3d(bed_shape.min.x(), bed_shape.min.y(), 0), + Vec3d(bed_shape.max.x(), bed_shape.max.y(), this->print()->config().max_print_height)); Vec3d model_center = this->model_object()->bounding_box().center(); - model_center(2) = 0.0f; // Set position in Z axis to 0 + model_center.z() = 0.0f; // Set position in Z axis to 0 // Center of the first cube in octree TriangleMesh mesh = this->model_object()->mesh(); - this->m_adapt_fill_octree = FillAdaptive::build_octree(mesh, line_spacing, printer_volume, model_center); + return FillAdaptive::build_octree(mesh, line_spacing, printer_volume, model_center); } void PrintObject::clear_layers() From 71237cf11ff21abc649666043b6279e5dc945fb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 3 Sep 2020 07:52:53 +0200 Subject: [PATCH 389/503] Fix tests which expect make_fills without arguments --- src/libslic3r/Layer.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 4c824a1093..014d2623af 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -138,6 +138,7 @@ public: return false; } void make_perimeters(); + void make_fills() { this->make_fills(nullptr); }; void make_fills(FillAdaptive_Internal::Octree* adaptive_fill_octree); void make_ironing(); From fd3a31651c2e7c5855944813ea09e8cbfdf17cfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 3 Sep 2020 08:04:05 +0200 Subject: [PATCH 390/503] Octree's first cube depends on model size. --- src/libslic3r/Fill/FillAdaptive.cpp | 21 +++++++++++---------- src/libslic3r/Fill/FillAdaptive.hpp | 3 +-- src/libslic3r/PrintObject.cpp | 9 ++------- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index 0563b612ab..62c4a3af7b 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -142,9 +142,8 @@ void FillAdaptive::merge_polylines(Polylines &polylines, const Line &new_line) std::unique_ptr FillAdaptive::build_octree( - TriangleMesh &triangleMesh, + TriangleMesh &triangle_mesh, coordf_t line_spacing, - const BoundingBoxf3 &printer_volume, const Vec3d &cube_center) { using namespace FillAdaptive_Internal; @@ -154,10 +153,11 @@ std::unique_ptr FillAdaptive::build_octree( return nullptr; } - // The furthest point from center of bed. - double furthest_point = std::sqrt(((printer_volume.size()[0] * printer_volume.size()[0]) / 4.0) + - ((printer_volume.size()[1] * printer_volume.size()[1]) / 4.0) + - (printer_volume.size()[2] * printer_volume.size()[2])); + Vec3d bb_size = triangle_mesh.bounding_box().size(); + // The furthest point from the center of the bottom of the mesh bounding box. + double furthest_point = std::sqrt(((bb_size.x() * bb_size.x()) / 4.0) + + ((bb_size.y() * bb_size.y()) / 4.0) + + (bb_size.z() * bb_size.z())); double max_cube_edge_length = furthest_point * 2; std::vector cubes_properties; @@ -172,19 +172,20 @@ std::unique_ptr FillAdaptive::build_octree( cubes_properties.push_back(props); } - if (triangleMesh.its.vertices.empty()) + if (triangle_mesh.its.vertices.empty()) { - triangleMesh.require_shared_vertices(); + triangle_mesh.require_shared_vertices(); } Vec3d rotation = Vec3d(Geometry::deg2rad(225.0), Geometry::deg2rad(215.264), Geometry::deg2rad(30.0)); Transform3d rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), rotation, Vec3d::Ones(), Vec3d::Ones()); - AABBTreeIndirect::Tree3f aabbTree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(triangleMesh.its.vertices, triangleMesh.its.indices); + AABBTreeIndirect::Tree3f aabbTree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set( + triangle_mesh.its.vertices, triangle_mesh.its.indices); std::unique_ptr octree = std::unique_ptr( new Octree{std::unique_ptr(new Cube{cube_center, cubes_properties.size() - 1, cubes_properties.back()}), cube_center}); - FillAdaptive::expand_cube(octree->root_cube.get(), cubes_properties, rotation_matrix, aabbTree, triangleMesh); + FillAdaptive::expand_cube(octree->root_cube.get(), cubes_properties, rotation_matrix, aabbTree, triangle_mesh); return octree; } diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index fb1f2da8e3..c7539df5a7 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -60,9 +60,8 @@ protected: public: static std::unique_ptr build_octree( - TriangleMesh &triangleMesh, + TriangleMesh &triangle_mesh, coordf_t line_spacing, - const BoundingBoxf3 &printer_volume, const Vec3d &cube_center); static void expand_cube( diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 25b20ea9af..f6823baae7 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -447,16 +447,11 @@ std::unique_ptr PrintObject::prepare_adaptive_inf coordf_t line_spacing = infill_extrusion_width / ((fill_density / 100.0f) * 0.333333333f); - BoundingBoxf bed_shape(this->print()->config().bed_shape.values); - BoundingBoxf3 printer_volume(Vec3d(bed_shape.min.x(), bed_shape.min.y(), 0), - Vec3d(bed_shape.max.x(), bed_shape.max.y(), this->print()->config().max_print_height)); - - Vec3d model_center = this->model_object()->bounding_box().center(); - model_center.z() = 0.0f; // Set position in Z axis to 0 // Center of the first cube in octree + Vec3d model_center = this->model_object()->bounding_box().center(); TriangleMesh mesh = this->model_object()->mesh(); - return FillAdaptive::build_octree(mesh, line_spacing, printer_volume, model_center); + return FillAdaptive::build_octree(mesh, line_spacing, model_center); } void PrintObject::clear_layers() From 573194e059836916b6f216dc068c27a89ea7b843 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 3 Sep 2020 08:32:06 +0200 Subject: [PATCH 391/503] GCodeProcessor -> Added cancel callback --- src/libslic3r/GCode.cpp | 2 +- src/libslic3r/GCode/GCodeProcessor.cpp | 18 +++++++++++++----- src/libslic3r/GCode/GCodeProcessor.hpp | 3 ++- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 135389eb3e..0ee9ec0143 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -787,7 +787,7 @@ void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_ } #if ENABLE_GCODE_VIEWER - m_processor.process_file(path_tmp); + m_processor.process_file(path_tmp, [print]() { print->throw_if_canceled(); }); DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics); if (result != nullptr) *result = std::move(m_processor.extract_result()); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 13b1ed1a8d..cd42dc2e6c 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -11,10 +11,7 @@ #include #if ENABLE_GCODE_VIEWER - -#if ENABLE_GCODE_VIEWER_STATISTICS #include -#endif // ENABLE_GCODE_VIEWER_STATISTICS static const float INCHES_TO_MM = 25.4f; static const float MMMIN_TO_MMSEC = 1.0f / 60.0f; @@ -730,8 +727,10 @@ void GCodeProcessor::reset() #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING } -void GCodeProcessor::process_file(const std::string& filename) +void GCodeProcessor::process_file(const std::string& filename, std::function cancel_callback) { + auto last_cancel_callback_time = std::chrono::high_resolution_clock::now(); + #if ENABLE_GCODE_VIEWER_STATISTICS auto start_time = std::chrono::high_resolution_clock::now(); #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -758,9 +757,18 @@ void GCodeProcessor::process_file(const std::string& filename) } } + // process gcode m_result.id = ++s_result_id; m_result.moves.emplace_back(MoveVertex()); - m_parser.parse_file(filename, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { process_gcode_line(line); }); + m_parser.parse_file(filename, [this, cancel_callback, &last_cancel_callback_time](GCodeReader& reader, const GCodeReader::GCodeLine& line) { + auto curr_time = std::chrono::high_resolution_clock::now(); + // call the cancel callback every 100 ms + if (std::chrono::duration_cast(curr_time - last_cancel_callback_time).count() > 100) { + cancel_callback(); + last_cancel_callback_time = curr_time; + } + process_gcode_line(line); + }); // process the time blocks for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 22aeed7620..42772d12bc 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -419,7 +419,8 @@ namespace Slic3r { Result&& extract_result() { return std::move(m_result); } // Process the gcode contained in the file with the given filename - void process_file(const std::string& filename); + // throws CanceledException through print->throw_if_canceled() (sent by the caller as callback). + void process_file(const std::string& filename, std::function cancel_callback = std::function()); float get_time(PrintEstimatedTimeStatistics::ETimeMode mode) const; std::string get_time_dhm(PrintEstimatedTimeStatistics::ETimeMode mode) const; From cbe93815b2ce8c59217aae8b25fed06fab2c9019 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 3 Sep 2020 09:27:53 +0200 Subject: [PATCH 392/503] Fixed layout after switching mode of settings layout --- src/slic3r/GUI/GUI_App.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index e675a9292a..d444017f40 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1124,10 +1124,10 @@ void GUI_App::add_config_menu(wxMenuBar *menu) app_layout_changed = dlg.settings_layout_changed(); } if (app_layout_changed) { - mainframe->GetSizer()->Hide((size_t)0); + // hide full main_sizer for mainFrame + mainframe->GetSizer()->Show(false); mainframe->update_layout(); mainframe->select_tab(0); - mainframe->GetSizer()->Show((size_t)0); } break; } From 0f0c9a0726cc7a6cb1e180df5d1e730a73ea6489 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 3 Sep 2020 10:44:54 +0200 Subject: [PATCH 393/503] OSX specific: UnsavedChangesDialog: Fixed strange ellipsis for items in DataViewCtrl --- src/slic3r/GUI/UnsavedChangesDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index f30e719ce0..5a0d23a209 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -531,7 +531,7 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_ wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); SetBackgroundColour(bgr_clr); -#if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT +#if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT && defined(__WXMSW__) // ys_FIXME! temporary workaround for correct font scaling // Because of from wxWidgets 3.1.3 auto rescaling is implemented for the Fonts, // From the very beginning set dialog font to the wxSYS_DEFAULT_GUI_FONT From a3a1c2017224221ff2a61eb4901e7a0c4be458aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 3 Sep 2020 11:56:41 +0200 Subject: [PATCH 394/503] Code cleanup --- src/libslic3r/Fill/FillAdaptive.cpp | 56 +++++++++++++++-------------- src/libslic3r/Fill/FillAdaptive.hpp | 10 ++++-- 2 files changed, 38 insertions(+), 28 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index 62c4a3af7b..91da86b69e 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -15,15 +15,20 @@ void FillAdaptive::_fill_surface_single( ExPolygon &expolygon, Polylines &polylines_out) { - std::vector infill_polylines(3); - this->generate_polylines(this->adapt_fill_octree->root_cube.get(), this->z, this->adapt_fill_octree->origin, infill_polylines); + std::vector infill_lines_dir(3); + this->generate_infill_lines(this->adapt_fill_octree->root_cube.get(), this->z, this->adapt_fill_octree->origin, infill_lines_dir); - for (Polylines &infill_polyline : infill_polylines) { - // Crop all polylines - infill_polyline = intersection_pl(infill_polyline, to_polygons(expolygon)); - polylines_out.insert(polylines_out.end(), infill_polyline.begin(), infill_polyline.end()); + for (Lines &infill_lines : infill_lines_dir) + { + for (const Line &line : infill_lines) + { + polylines_out.emplace_back(line.a, line.b); + } } + // Crop all polylines + polylines_out = intersection_pl(polylines_out, to_polygons(expolygon)); + #ifdef SLIC3R_DEBUG_SLICE_PROCESSING { static int iRuna = 0; @@ -58,11 +63,11 @@ void FillAdaptive::_fill_surface_single( #endif /* SLIC3R_DEBUG */ } -void FillAdaptive::generate_polylines( +void FillAdaptive::generate_infill_lines( FillAdaptive_Internal::Cube *cube, double z_position, const Vec3d &origin, - std::vector &polylines_out) + std::vector &dir_lines_out) { using namespace FillAdaptive_Internal; @@ -86,9 +91,8 @@ void FillAdaptive::generate_polylines( Point to(-from.x(), from.y()); // Relative to cube center - float rotation_angle = Geometry::deg2rad(120.0); - - for (int i = 0; i < polylines_out.size(); i++) + float rotation_angle = (2.0 * M_PI) / 3.0; + for (Lines &lines : dir_lines_out) { Vec3d offset = cube->center - origin; Point from_abs(from), to_abs(to); @@ -98,8 +102,8 @@ void FillAdaptive::generate_polylines( to_abs.x() += scale_(offset.x()); to_abs.y() += scale_(offset.y()); -// polylines_out[i].push_back(Polyline(from_abs, to_abs)); - this->merge_polylines(polylines_out[i], Line(from_abs, to_abs)); +// lines.emplace_back(from_abs, to_abs); + this->connect_lines(lines, Line(from_abs, to_abs)); from.rotate(rotation_angle); to.rotate(rotation_angle); @@ -108,35 +112,35 @@ void FillAdaptive::generate_polylines( for(const std::unique_ptr &child : cube->children) { - generate_polylines(child.get(), z_position, origin, polylines_out); + generate_infill_lines(child.get(), z_position, origin, dir_lines_out); } } -void FillAdaptive::merge_polylines(Polylines &polylines, const Line &new_line) +void FillAdaptive::connect_lines(Lines &lines, const Line &new_line) { int eps = scale_(0.10); bool modified = false; - for (Polyline &polyline : polylines) + for (Line &line : lines) { - if (std::abs(new_line.a.x() - polyline.points[1].x()) < eps && std::abs(new_line.a.y() - polyline.points[1].y()) < eps) + if (std::abs(new_line.a.x() - line.b.x()) < eps && std::abs(new_line.a.y() - line.b.y()) < eps) { - polyline.points[1].x() = new_line.b.x(); - polyline.points[1].y() = new_line.b.y(); + line.b.x() = new_line.b.x(); + line.b.y() = new_line.b.y(); modified = true; } - if (std::abs(new_line.b.x() - polyline.points[0].x()) < eps && std::abs(new_line.b.y() - polyline.points[0].y()) < eps) + if (std::abs(new_line.b.x() - line.a.x()) < eps && std::abs(new_line.b.y() - line.a.y()) < eps) { - polyline.points[0].x() = new_line.a.x(); - polyline.points[0].y() = new_line.a.y(); + line.a.x() = new_line.a.x(); + line.a.y() = new_line.a.y(); modified = true; } } if(!modified) { - polylines.emplace_back(Polyline(new_line.a, new_line.b)); + lines.push_back(new_line); } } @@ -182,8 +186,8 @@ std::unique_ptr FillAdaptive::build_octree( AABBTreeIndirect::Tree3f aabbTree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set( triangle_mesh.its.vertices, triangle_mesh.its.indices); - std::unique_ptr octree = std::unique_ptr( - new Octree{std::unique_ptr(new Cube{cube_center, cubes_properties.size() - 1, cubes_properties.back()}), cube_center}); + auto octree = std::make_unique( + std::make_unique(cube_center, cubes_properties.size() - 1, cubes_properties.back()), cube_center); FillAdaptive::expand_cube(octree->root_cube.get(), cubes_properties, rotation_matrix, aabbTree, triangle_mesh); @@ -215,7 +219,7 @@ void FillAdaptive::expand_cube( Vec3d child_center_transformed = cube->center + rotation_matrix * (child_center * (cube->properties.edge_length / 4)); if(AABBTreeIndirect::is_any_triangle_in_radius(triangleMesh.its.vertices, triangleMesh.its.indices, distanceTree, child_center_transformed, cube_radius_squared)) { - cube->children.push_back(std::unique_ptr(new Cube{child_center_transformed, cube->depth - 1, cubes_properties[cube->depth - 1]})); + cube->children.emplace_back(std::make_unique(child_center_transformed, cube->depth - 1, cubes_properties[cube->depth - 1])); FillAdaptive::expand_cube(cube->children.back().get(), cubes_properties, rotation_matrix, distanceTree, triangleMesh); } } diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index c7539df5a7..570318aa40 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -24,12 +24,18 @@ namespace FillAdaptive_Internal size_t depth; CubeProperties properties; std::vector> children; + + Cube(const Vec3d ¢er, size_t depth, const CubeProperties &properties) + : center(center), depth(depth), properties(properties) {} }; struct Octree { std::unique_ptr root_cube; Vec3d origin; + + Octree(std::unique_ptr rootCube, const Vec3d &origin) + : root_cube(std::move(rootCube)), origin(origin) {} }; }; // namespace FillAdaptive_Internal @@ -54,9 +60,9 @@ protected: virtual bool no_sort() const { return true; } - void generate_polylines(FillAdaptive_Internal::Cube *cube, double z_position, const Vec3d &origin, std::vector &polylines_out); + void generate_infill_lines(FillAdaptive_Internal::Cube *cube, double z_position, const Vec3d &origin, std::vector &dir_lines_out); - void merge_polylines(Polylines &polylines, const Line &new_line); + void connect_lines(Lines &lines, const Line &new_line); public: static std::unique_ptr build_octree( From 353c65fa4cb5e25c4e74be49ed87882d1ed40e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 3 Sep 2020 13:05:28 +0200 Subject: [PATCH 395/503] Connect infill to perimeters --- src/libslic3r/Fill/FillAdaptive.cpp | 35 ++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index 91da86b69e..d3246dc18b 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -3,6 +3,7 @@ #include "../Surface.hpp" #include "../Geometry.hpp" #include "../AABBTreeIndirect.hpp" +#include "../ShortestPath.hpp" #include "FillAdaptive.hpp" @@ -15,19 +16,47 @@ void FillAdaptive::_fill_surface_single( ExPolygon &expolygon, Polylines &polylines_out) { + // Store grouped lines by its direction (multiple of 120°) std::vector infill_lines_dir(3); this->generate_infill_lines(this->adapt_fill_octree->root_cube.get(), this->z, this->adapt_fill_octree->origin, infill_lines_dir); + Polylines all_polylines; + all_polylines.reserve(infill_lines_dir[0].size() * 3); for (Lines &infill_lines : infill_lines_dir) { for (const Line &line : infill_lines) { - polylines_out.emplace_back(line.a, line.b); + all_polylines.emplace_back(line.a, line.b); } } - // Crop all polylines - polylines_out = intersection_pl(polylines_out, to_polygons(expolygon)); + if (params.dont_connect) + { + // Crop all polylines + polylines_out = intersection_pl(all_polylines, to_polygons(expolygon)); + } + else + { + // Crop all polylines + all_polylines = intersection_pl(all_polylines, to_polygons(expolygon)); + + Polylines boundary_polylines; + Polylines non_boundary_polylines; + for (const Polyline &polyline : all_polylines) + { + // connect_infill required all polylines to touch the boundary. + if(polyline.lines().size() == 1 && expolygon.has_boundary_point(polyline.lines().front().a) && expolygon.has_boundary_point(polyline.lines().front().b)) + { + boundary_polylines.push_back(polyline); + } else { + non_boundary_polylines.push_back(polyline); + } + } + + boundary_polylines = chain_polylines(boundary_polylines); + FillAdaptive::connect_infill(std::move(boundary_polylines), expolygon, polylines_out, this->spacing, params); + polylines_out.insert(polylines_out.end(), non_boundary_polylines.begin(), non_boundary_polylines.end()); + } #ifdef SLIC3R_DEBUG_SLICE_PROCESSING { From 184cb7afd9d2def98a08fde50534e61c70d2611d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 3 Sep 2020 14:28:25 +0200 Subject: [PATCH 396/503] Fix bug in lines merging --- src/libslic3r/Fill/FillAdaptive.cpp | 30 +++++++++++++---------------- src/libslic3r/Fill/FillAdaptive.hpp | 2 +- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index d3246dc18b..030debad62 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -145,35 +145,31 @@ void FillAdaptive::generate_infill_lines( } } -void FillAdaptive::connect_lines(Lines &lines, const Line &new_line) +void FillAdaptive::connect_lines(Lines &lines, Line new_line) { int eps = scale_(0.10); - bool modified = false; - - for (Line &line : lines) + for (size_t i = 0; i < lines.size(); ++i) { - if (std::abs(new_line.a.x() - line.b.x()) < eps && std::abs(new_line.a.y() - line.b.y()) < eps) + if (std::abs(new_line.a.x() - lines[i].b.x()) < eps && std::abs(new_line.a.y() - lines[i].b.y()) < eps) { - line.b.x() = new_line.b.x(); - line.b.y() = new_line.b.y(); - modified = true; + new_line.a = lines[i].a; + lines.erase(lines.begin() + i); + --i; + continue; } - if (std::abs(new_line.b.x() - line.a.x()) < eps && std::abs(new_line.b.y() - line.a.y()) < eps) + if (std::abs(new_line.b.x() - lines[i].a.x()) < eps && std::abs(new_line.b.y() - lines[i].a.y()) < eps) { - line.a.x() = new_line.a.x(); - line.a.y() = new_line.a.y(); - modified = true; + new_line.b = lines[i].b; + lines.erase(lines.begin() + i); + --i; + continue; } } - if(!modified) - { - lines.push_back(new_line); - } + lines.emplace_back(new_line.a, new_line.b); } - std::unique_ptr FillAdaptive::build_octree( TriangleMesh &triangle_mesh, coordf_t line_spacing, diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index 570318aa40..44a2536f00 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -62,7 +62,7 @@ protected: void generate_infill_lines(FillAdaptive_Internal::Cube *cube, double z_position, const Vec3d &origin, std::vector &dir_lines_out); - void connect_lines(Lines &lines, const Line &new_line); + static void connect_lines(Lines &lines, Line new_line); public: static std::unique_ptr build_octree( From c49221c6217b787785807284bb6a7164395abe62 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 3 Sep 2020 15:40:14 +0200 Subject: [PATCH 397/503] Fix of Settings scaling when they are placed in non-modal Dialog --- src/slic3r/GUI/GUI_Utils.hpp | 9 ++++++++- src/slic3r/GUI/MainFrame.cpp | 10 +++++++++- src/slic3r/GUI/PresetComboBoxes.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 2 +- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp index 96b24524c2..749a556b84 100644 --- a/src/slic3r/GUI/GUI_Utils.hpp +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -218,7 +218,7 @@ private: void rescale(const wxRect &suggested_rect) { this->Freeze(); - +/* #if wxVERSION_EQUAL_OR_GREATER_THAN(3,1,3) if (m_force_rescale) { #endif // wxVERSION_EQUAL_OR_GREATER_THAN @@ -230,6 +230,13 @@ private: m_force_rescale = false; } #endif // wxVERSION_EQUAL_OR_GREATER_THAN +*/ +#if !wxVERSION_EQUAL_OR_GREATER_THAN(3,1,3) + // rescale fonts of all controls + scale_controls_fonts(this, m_new_font_point_size); + // rescale current window font + scale_win_font(this, m_new_font_point_size); +#endif // wxVERSION_EQUAL_OR_GREATER_THAN // set normal application font as a current window font m_normal_font = this->GetFont(); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index bab5d7502e..191e6c4553 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -787,9 +787,10 @@ void MainFrame::on_dpi_changed(const wxRect& suggested_rect) this->SetSize(sz); this->Maximize(is_maximized); - +/* if (m_layout == ESettingsLayout::Dlg) rescale_dialog_after_dpi_change(*this, m_settings_dialog, ERescaleTarget::SettingsDialog); + */ } void MainFrame::on_sys_color_changed() @@ -1988,7 +1989,14 @@ SettingsDialog::SettingsDialog(MainFrame* mainframe) wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMINIMIZE_BOX | wxMAXIMIZE_BOX, "settings_dialog"), m_main_frame(mainframe) { +#if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT && defined(__WXMSW__) + // ys_FIXME! temporary workaround for correct font scaling + // Because of from wxWidgets 3.1.3 auto rescaling is implemented for the Fonts, + // From the very beginning set dialog font to the wxSYS_DEFAULT_GUI_FONT + this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); +#else this->SetFont(wxGetApp().normal_font()); +#endif // ENABLE_WX_3_1_3_DPI_CHANGED_EVENT this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 7300a2887f..8bc9393873 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -1194,7 +1194,7 @@ SavePresetDialog::~SavePresetDialog() void SavePresetDialog::build(std::vector types, std::string suffix) { SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); -#if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT +#if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT && defined(__WXMSW__) // ys_FIXME! temporary workaround for correct font scaling // Because of from wxWidgets 3.1.3 auto rescaling is implemented for the Fonts, // From the very beginning set dialog font to the wxSYS_DEFAULT_GUI_FONT diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index b95227dad9..29c9e33022 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -102,7 +102,7 @@ Tab::Tab(wxNotebook* parent, const wxString& title, Preset::Type type) : wxGetApp().tabs_list.push_back(this); - m_em_unit = wxGetApp().em_unit(); + m_em_unit = em_unit(m_parent); //wxGetApp().em_unit(); m_config_manipulation = get_config_manipulation(); From c2af265df81a600908689ae7732c69d4b256e4b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 3 Sep 2020 16:08:40 +0200 Subject: [PATCH 398/503] Change to using raw_mesh instead of mesh --- src/libslic3r/PrintObject.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index f6823baae7..5a486776cc 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -447,11 +447,14 @@ std::unique_ptr PrintObject::prepare_adaptive_inf coordf_t line_spacing = infill_extrusion_width / ((fill_density / 100.0f) * 0.333333333f); - // Center of the first cube in octree - Vec3d model_center = this->model_object()->bounding_box().center(); + TriangleMesh mesh = this->model_object()->raw_mesh(); + mesh.transform(m_trafo, true); + // Apply XY shift + mesh.translate(- unscale(m_center_offset.x()), - unscale(m_center_offset.y()), 0); - TriangleMesh mesh = this->model_object()->mesh(); - return FillAdaptive::build_octree(mesh, line_spacing, model_center); + // Center of the first cube in octree + Vec3d mesh_origin = mesh.bounding_box().center(); + return FillAdaptive::build_octree(mesh, line_spacing, mesh_origin); } void PrintObject::clear_layers() From ce18b824ada00c8eb67c13fe5b89a2b03e2a32f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 3 Sep 2020 19:21:55 +0200 Subject: [PATCH 399/503] Octree representation rework --- src/libslic3r/Fill/FillAdaptive.cpp | 51 ++++++++++++++++++----------- src/libslic3r/Fill/FillAdaptive.hpp | 44 ++++++++++++++----------- 2 files changed, 55 insertions(+), 40 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index 030debad62..577ba7e610 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -18,7 +18,10 @@ void FillAdaptive::_fill_surface_single( { // Store grouped lines by its direction (multiple of 120°) std::vector infill_lines_dir(3); - this->generate_infill_lines(this->adapt_fill_octree->root_cube.get(), this->z, this->adapt_fill_octree->origin, infill_lines_dir); + this->generate_infill_lines(this->adapt_fill_octree->root_cube.get(), + this->z, this->adapt_fill_octree->origin,infill_lines_dir, + this->adapt_fill_octree->cubes_properties, + this->adapt_fill_octree->cubes_properties.size() - 1); Polylines all_polylines; all_polylines.reserve(infill_lines_dir[0].size() * 3); @@ -96,7 +99,9 @@ void FillAdaptive::generate_infill_lines( FillAdaptive_Internal::Cube *cube, double z_position, const Vec3d &origin, - std::vector &dir_lines_out) + std::vector &dir_lines_out, + const std::vector &cubes_properties, + int depth) { using namespace FillAdaptive_Internal; @@ -107,16 +112,16 @@ void FillAdaptive::generate_infill_lines( double z_diff = std::abs(z_position - cube->center.z()); - if (z_diff > cube->properties.height / 2) + if (z_diff > cubes_properties[depth].height / 2) { return; } - if (z_diff < cube->properties.line_z_distance) + if (z_diff < cubes_properties[depth].line_z_distance) { Point from( - scale_((cube->properties.diagonal_length / 2) * (cube->properties.line_z_distance - z_diff) / cube->properties.line_z_distance), - scale_(cube->properties.line_xy_distance - ((z_position - (cube->center.z() - cube->properties.line_z_distance)) / sqrt(2)))); + scale_((cubes_properties[depth].diagonal_length / 2) * (cubes_properties[depth].line_z_distance - z_diff) / cubes_properties[depth].line_z_distance), + scale_(cubes_properties[depth].line_xy_distance - ((z_position - (cube->center.z() - cubes_properties[depth].line_z_distance)) / sqrt(2)))); Point to(-from.x(), from.y()); // Relative to cube center @@ -141,7 +146,10 @@ void FillAdaptive::generate_infill_lines( for(const std::unique_ptr &child : cube->children) { - generate_infill_lines(child.get(), z_position, origin, dir_lines_out); + if(child != nullptr) + { + generate_infill_lines(child.get(), z_position, origin, dir_lines_out, cubes_properties, depth - 1); + } } } @@ -206,15 +214,14 @@ std::unique_ptr FillAdaptive::build_octree( triangle_mesh.require_shared_vertices(); } - Vec3d rotation = Vec3d(Geometry::deg2rad(225.0), Geometry::deg2rad(215.264), Geometry::deg2rad(30.0)); + Vec3d rotation = Vec3d((5.0 * M_PI) / 4.0, Geometry::deg2rad(215.264), M_PI / 6.0); Transform3d rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), rotation, Vec3d::Ones(), Vec3d::Ones()); AABBTreeIndirect::Tree3f aabbTree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set( triangle_mesh.its.vertices, triangle_mesh.its.indices); - auto octree = std::make_unique( - std::make_unique(cube_center, cubes_properties.size() - 1, cubes_properties.back()), cube_center); + auto octree = std::make_unique(std::make_unique(cube_center), cube_center, cubes_properties); - FillAdaptive::expand_cube(octree->root_cube.get(), cubes_properties, rotation_matrix, aabbTree, triangle_mesh); + FillAdaptive::expand_cube(octree->root_cube.get(), cubes_properties, rotation_matrix, aabbTree, triangle_mesh, cubes_properties.size() - 1); return octree; } @@ -223,12 +230,12 @@ void FillAdaptive::expand_cube( FillAdaptive_Internal::Cube *cube, const std::vector &cubes_properties, const Transform3d &rotation_matrix, - const AABBTreeIndirect::Tree3f &distanceTree, - const TriangleMesh &triangleMesh) + const AABBTreeIndirect::Tree3f &distance_tree, + const TriangleMesh &triangle_mesh, int depth) { using namespace FillAdaptive_Internal; - if (cube == nullptr || cube->depth == 0) + if (cube == nullptr || depth == 0) { return; } @@ -238,14 +245,18 @@ void FillAdaptive::expand_cube( Vec3d( 1, 1, 1), Vec3d(-1, 1, 1), Vec3d( 1, -1, 1), Vec3d( 1, 1, -1) }; - double cube_radius_squared = (cube->properties.height * cube->properties.height) / 16; + double cube_radius_squared = (cubes_properties[depth].height * cubes_properties[depth].height) / 16; - for (const Vec3d &child_center : child_centers) { - Vec3d child_center_transformed = cube->center + rotation_matrix * (child_center * (cube->properties.edge_length / 4)); + for (size_t i = 0; i < 8; ++i) + { + const Vec3d &child_center = child_centers[i]; + Vec3d child_center_transformed = cube->center + rotation_matrix * (child_center * (cubes_properties[depth].edge_length / 4)); - if(AABBTreeIndirect::is_any_triangle_in_radius(triangleMesh.its.vertices, triangleMesh.its.indices, distanceTree, child_center_transformed, cube_radius_squared)) { - cube->children.emplace_back(std::make_unique(child_center_transformed, cube->depth - 1, cubes_properties[cube->depth - 1])); - FillAdaptive::expand_cube(cube->children.back().get(), cubes_properties, rotation_matrix, distanceTree, triangleMesh); + if(AABBTreeIndirect::is_any_triangle_in_radius(triangle_mesh.its.vertices, triangle_mesh.its.indices, + distance_tree, child_center_transformed, cube_radius_squared)) + { + cube->children[i] = std::make_unique(child_center_transformed); + FillAdaptive::expand_cube(cube->children[i].get(), cubes_properties, rotation_matrix, distance_tree, triangle_mesh, depth - 1); } } } diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index 44a2536f00..14694b766c 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -21,21 +21,18 @@ namespace FillAdaptive_Internal struct Cube { Vec3d center; - size_t depth; - CubeProperties properties; - std::vector> children; - - Cube(const Vec3d ¢er, size_t depth, const CubeProperties &properties) - : center(center), depth(depth), properties(properties) {} + std::unique_ptr children[8] = {}; + Cube(const Vec3d ¢er) : center(center) {} }; struct Octree { std::unique_ptr root_cube; Vec3d origin; + std::vector cubes_properties; - Octree(std::unique_ptr rootCube, const Vec3d &origin) - : root_cube(std::move(rootCube)), origin(origin) {} + Octree(std::unique_ptr rootCube, const Vec3d &origin, const std::vector &cubes_properties) + : root_cube(std::move(rootCube)), origin(origin), cubes_properties(cubes_properties) {} }; }; // namespace FillAdaptive_Internal @@ -52,30 +49,37 @@ public: protected: virtual Fill* clone() const { return new FillAdaptive(*this); }; virtual void _fill_surface_single( - const FillParams ¶ms, + const FillParams ¶ms, unsigned int thickness_layers, - const std::pair &direction, - ExPolygon &expolygon, + const std::pair &direction, + ExPolygon &expolygon, Polylines &polylines_out); virtual bool no_sort() const { return true; } - void generate_infill_lines(FillAdaptive_Internal::Cube *cube, double z_position, const Vec3d &origin, std::vector &dir_lines_out); + void generate_infill_lines( + FillAdaptive_Internal::Cube *cube, + double z_position, + const Vec3d & origin, + std::vector & dir_lines_out, + const std::vector &cubes_properties, + int depth); static void connect_lines(Lines &lines, Line new_line); public: static std::unique_ptr build_octree( - TriangleMesh &triangle_mesh, - coordf_t line_spacing, - const Vec3d &cube_center); + TriangleMesh &triangle_mesh, + coordf_t line_spacing, + const Vec3d & cube_center); static void expand_cube( - FillAdaptive_Internal::Cube *cube, - const std::vector &cubes_properties, - const Transform3d &rotation_matrix, - const AABBTreeIndirect::Tree3f &distanceTree, - const TriangleMesh &triangleMesh); + FillAdaptive_Internal::Cube *cube, + const std::vector &cubes_properties, + const Transform3d & rotation_matrix, + const AABBTreeIndirect::Tree3f &distance_tree, + const TriangleMesh & triangle_mesh, + int depth); }; } // namespace Slic3r From 6c01d537e4d7a31107eb07d82a5ddcde03cc9271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 3 Sep 2020 23:15:46 +0200 Subject: [PATCH 400/503] Enable changing adaptive infill density for different objects --- src/libslic3r/PrintObject.cpp | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 5a486776cc..087d3fe3c5 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -434,17 +434,34 @@ void PrintObject::generate_support_material() std::unique_ptr PrintObject::prepare_adaptive_infill_data() { - const ConfigOptionPercent* opt_fill_density = this->print()->full_print_config().option("fill_density"); - const ConfigOptionFloatOrPercent* opt_infill_extrusion_width = this->print()->full_print_config().option("infill_extrusion_width"); + float fill_density = 0; + float infill_extrusion_width = 0; - if(opt_fill_density == nullptr || opt_infill_extrusion_width == nullptr || opt_fill_density->value <= 0 || opt_infill_extrusion_width->value <= 0) + // Compute the average of above parameters over all layers + for (size_t layer_idx = 0; layer_idx < this->m_layers.size(); ++layer_idx) + { + for (size_t region_id = 0; region_id < this->m_layers[layer_idx]->m_regions.size(); ++region_id) + { + LayerRegion *layerm = this->m_layers[layer_idx]->m_regions[region_id]; + + // Check if region_id is used for this layer + if(!layerm->fill_surfaces.surfaces.empty()) { + const PrintRegionConfig ®ion_config = layerm->region()->config(); + + fill_density += region_config.fill_density; + infill_extrusion_width += region_config.infill_extrusion_width; + } + } + } + + fill_density /= this->m_layers.size(); + infill_extrusion_width /= this->m_layers.size(); + + if(fill_density <= 0 || infill_extrusion_width <= 0) { return std::unique_ptr{}; } - float fill_density = opt_fill_density->value; - float infill_extrusion_width = opt_infill_extrusion_width->value; - coordf_t line_spacing = infill_extrusion_width / ((fill_density / 100.0f) * 0.333333333f); TriangleMesh mesh = this->model_object()->raw_mesh(); From ba87a4fd9a607fd0ed7f60241a25884ff0d6d61b Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 4 Sep 2020 10:08:54 +0200 Subject: [PATCH 401/503] Fixed rescale of the MainFrame/SettingsDialog after switching between settings layouts on the 2 monitors with different DPI --- src/slic3r/GUI/GUI_Utils.hpp | 1 + src/slic3r/GUI/MainFrame.cpp | 40 +++++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp index 749a556b84..6a93d4156e 100644 --- a/src/slic3r/GUI/GUI_Utils.hpp +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -231,6 +231,7 @@ private: } #endif // wxVERSION_EQUAL_OR_GREATER_THAN */ + m_force_rescale = false; #if !wxVERSION_EQUAL_OR_GREATER_THAN(3,1,3) // rescale fonts of all controls scale_controls_fonts(this, m_new_font_point_size); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 191e6c4553..ac6f541e76 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -310,8 +310,10 @@ void MainFrame::update_layout() m_plater_page = nullptr; } + /* if (m_layout == ESettingsLayout::Dlg) rescale_dialog_after_dpi_change(*this, m_settings_dialog, ERescaleTarget::Mainframe); + */ clean_sizer(m_main_sizer); clean_sizer(m_settings_dialog.GetSizer()); @@ -347,6 +349,14 @@ void MainFrame::update_layout() if (m_layout != ESettingsLayout::Unknown) restore_to_creation(); + enum class State { + noUpdate, + fromDlg, + toDlg + }; + State update_scaling_state = m_layout == ESettingsLayout::Dlg ? State::fromDlg : + layout == ESettingsLayout::Dlg ? State::toDlg : State::noUpdate; + m_layout = layout; // From the very beginning the Print settings should be selected @@ -384,7 +394,7 @@ void MainFrame::update_layout() m_tabpanel->Reparent(&m_settings_dialog); m_settings_dialog.GetSizer()->Add(m_tabpanel, 1, wxEXPAND); - rescale_dialog_after_dpi_change(*this, m_settings_dialog, ERescaleTarget::SettingsDialog); +// rescale_dialog_after_dpi_change(*this, m_settings_dialog, ERescaleTarget::SettingsDialog); m_tabpanel->Show(); m_plater->Show(); @@ -400,6 +410,34 @@ void MainFrame::update_layout() #endif // ENABLE_GCODE_VIEWER } + if (update_scaling_state != State::noUpdate) + { + int mainframe_dpi = get_dpi_for_window(this); + int dialog_dpi = get_dpi_for_window(&m_settings_dialog); + if (mainframe_dpi != dialog_dpi) { + wxSize oldDPI = update_scaling_state == State::fromDlg ? wxSize(dialog_dpi, dialog_dpi) : wxSize(mainframe_dpi, mainframe_dpi); + wxSize newDPI = update_scaling_state == State::toDlg ? wxSize(dialog_dpi, dialog_dpi) : wxSize(mainframe_dpi, mainframe_dpi); + + if (update_scaling_state == State::fromDlg) + this->enable_force_rescale(); + else + (&m_settings_dialog)->enable_force_rescale(); + + wxWindow* win { nullptr }; + if (update_scaling_state == State::fromDlg) + win = this; + else + win = &m_settings_dialog; + +#if wxVERSION_EQUAL_OR_GREATER_THAN(3,1,3) + m_tabpanel->MSWUpdateOnDPIChange(oldDPI, newDPI); + win->GetEventHandler()->AddPendingEvent(wxDPIChangedEvent(oldDPI, newDPI)); +#else + win->GetEventHandler()->AddPendingEvent(DpiChangedEvent(EVT_DPI_CHANGED_SLICER, newDPI, win->GetRect())); +#endif // wxVERSION_EQUAL_OR_GREATER_THAN + } + } + //#ifdef __APPLE__ // // Using SetMinSize() on Mac messes up the window position in some cases // // cf. https://groups.google.com/forum/#!topic/wx-users/yUKPBBfXWO0 From 436e12e99f9d516896f156176d99b6a8a3ad246e Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 4 Sep 2020 12:46:34 +0200 Subject: [PATCH 402/503] Seam gizmo: fixed action names in undo/redo stack --- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 7 ++++ src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 37 ++++++++++++++++---- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp | 5 +++ src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp | 7 ++++ src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp | 1 + 6 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index af1517637f..6b3456b60e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -285,5 +285,12 @@ void GLGizmoFdmSupports::update_from_model_object() } + +PainterGizmoType GLGizmoFdmSupports::get_painter_type() const +{ + return PainterGizmoType::FDM_SUPPORTS; +} + + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index 913133617c..0c39992f0b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -27,6 +27,7 @@ private: void on_opening() override {} void on_shutdown() override; + PainterGizmoType get_painter_type() const override; void select_facets_by_angle(float threshold, bool block); float m_angle_threshold_deg = 45.f; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 1809b417cc..ed98bf71d5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -28,13 +28,19 @@ GLGizmoPainterBase::GLGizmoPainterBase(GLCanvas3D& parent, const std::string& ic void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate) { if (activate && ! m_internal_stack_active) { - Plater::TakeSnapshot(wxGetApp().plater(), _L("FDM gizmo turned on")); + wxString str = get_painter_type() == PainterGizmoType::FDM_SUPPORTS + ? _L("Supports gizmo turned on") + : _L("Seam gizmo turned on"); + Plater::TakeSnapshot(wxGetApp().plater(), str); wxGetApp().plater()->enter_gizmos_stack(); m_internal_stack_active = true; } if (! activate && m_internal_stack_active) { + wxString str = get_painter_type() == PainterGizmoType::SEAM + ? _L("Seam gizmo turned off") + : _L("Supports gizmo turned off"); wxGetApp().plater()->leave_gizmos_stack(); - Plater::TakeSnapshot(wxGetApp().plater(), _L("FDM gizmo turned off")); + Plater::TakeSnapshot(wxGetApp().plater(), str); m_internal_stack_active = false; } } @@ -356,11 +362,28 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::RightUp) && m_button_down != Button::None) { // Take snapshot and update ModelVolume data. - wxString action_name = shift_down - ? _L("Remove selection") - : (m_button_down == Button::Left - ? _L("Add supports") - : _L("Block supports")); + wxString action_name; + if (get_painter_type() == PainterGizmoType::FDM_SUPPORTS) { + if (shift_down) + action_name = _L("Remove selection"); + else { + if (m_button_down == Button::Left) + action_name = _L("Add supports"); + else + action_name = _L("Block supports"); + } + } + if (get_painter_type() == PainterGizmoType::SEAM) { + if (shift_down) + action_name = _L("Remove selection"); + else { + if (m_button_down == Button::Left) + action_name = _L("Enforce seam"); + else + action_name = _L("Block seam"); + } + } + activate_internal_undo_redo_stack(true); Plater::TakeSnapshot(wxGetApp().plater(), action_name); update_model_object(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index da9b378957..b3e2b65f1f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -22,6 +22,10 @@ namespace GUI { enum class SLAGizmoEventType : unsigned char; class ClippingPlane; +enum class PainterGizmoType { + FDM_SUPPORTS, + SEAM +}; class TriangleSelectorGUI : public TriangleSelector { @@ -103,6 +107,7 @@ protected: virtual void on_opening() = 0; virtual void on_shutdown() = 0; + virtual PainterGizmoType get_painter_type() const = 0; bool on_is_activable() const override; bool on_is_selectable() const override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp index 3c7d180a7b..d0edfba131 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp @@ -204,5 +204,12 @@ void GLGizmoSeam::update_from_model_object() } +PainterGizmoType GLGizmoSeam::get_painter_type() const +{ + return PainterGizmoType::SEAM; +} + + + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp index 469ec9180c..c3eb98c808 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp @@ -16,6 +16,7 @@ public: protected: void on_render_input_window(float x, float y, float bottom_limit) override; std::string on_get_name() const override; + PainterGizmoType get_painter_type() const override; private: bool on_init() override; From c8133b91b74774ce9ec015986d744991d93a3219 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 4 Sep 2020 13:00:33 +0200 Subject: [PATCH 403/503] Code cleaning. + Use default DPIfont for wxHtmlWindows --- src/slic3r/GUI/AboutDialog.cpp | 4 +-- src/slic3r/GUI/ConfigSnapshotDialog.cpp | 4 +-- src/slic3r/GUI/GUI_Utils.hpp | 14 +--------- src/slic3r/GUI/MainFrame.cpp | 35 ------------------------- src/slic3r/GUI/SysInfoDialog.cpp | 4 +-- 5 files changed, 7 insertions(+), 54 deletions(-) diff --git a/src/slic3r/GUI/AboutDialog.cpp b/src/slic3r/GUI/AboutDialog.cpp index 92f8d1fdbd..f95b8d93ba 100644 --- a/src/slic3r/GUI/AboutDialog.cpp +++ b/src/slic3r/GUI/AboutDialog.cpp @@ -52,7 +52,7 @@ CopyrightsDialog::CopyrightsDialog() m_html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxSize(40 * em_unit(), 20 * em_unit()), wxHW_SCROLLBAR_AUTO); - wxFont font = GetFont(); + wxFont font = get_default_font_for_dpi(get_dpi_for_window(this));// GetFont(); const int fs = font.GetPointSize(); const int fs2 = static_cast(1.2f*fs); int size[] = { fs, fs, fs, fs, fs2, fs2, fs2 }; @@ -249,7 +249,7 @@ AboutDialog::AboutDialog() m_html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO/*NEVER*/); { m_html->SetMinSize(wxSize(-1, 16 * wxGetApp().em_unit())); - wxFont font = GetFont(); + wxFont font = get_default_font_for_dpi(get_dpi_for_window(this));// GetFont(); const auto text_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue()); auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue()); diff --git a/src/slic3r/GUI/ConfigSnapshotDialog.cpp b/src/slic3r/GUI/ConfigSnapshotDialog.cpp index 6a44b96dc6..4855bea81e 100644 --- a/src/slic3r/GUI/ConfigSnapshotDialog.cpp +++ b/src/slic3r/GUI/ConfigSnapshotDialog.cpp @@ -114,7 +114,7 @@ ConfigSnapshotDialog::ConfigSnapshotDialog(const Config::SnapshotDB &snapshot_db // text html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO); { - wxFont font = wxGetApp().normal_font();//wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + wxFont font = get_default_font_for_dpi(get_dpi_for_window(this));// wxGetApp().normal_font();//wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); #ifdef __WXMSW__ const int fs = font.GetPointSize(); const int fs1 = static_cast(0.8f*fs); @@ -140,7 +140,7 @@ ConfigSnapshotDialog::ConfigSnapshotDialog(const Config::SnapshotDB &snapshot_db void ConfigSnapshotDialog::on_dpi_changed(const wxRect &suggested_rect) { - wxFont font = GetFont(); + wxFont font = get_default_font_for_dpi(get_dpi_for_window(this));// GetFont(); const int fs = font.GetPointSize(); const int fs1 = static_cast(0.8f*fs); const int fs2 = static_cast(1.1f*fs); diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp index 6a93d4156e..f29e0cd848 100644 --- a/src/slic3r/GUI/GUI_Utils.hpp +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -218,19 +218,7 @@ private: void rescale(const wxRect &suggested_rect) { this->Freeze(); -/* -#if wxVERSION_EQUAL_OR_GREATER_THAN(3,1,3) - if (m_force_rescale) { -#endif // wxVERSION_EQUAL_OR_GREATER_THAN - // rescale fonts of all controls - scale_controls_fonts(this, m_new_font_point_size); - // rescale current window font - scale_win_font(this, m_new_font_point_size); -#if wxVERSION_EQUAL_OR_GREATER_THAN(3,1,3) - m_force_rescale = false; - } -#endif // wxVERSION_EQUAL_OR_GREATER_THAN -*/ + m_force_rescale = false; #if !wxVERSION_EQUAL_OR_GREATER_THAN(3,1,3) // rescale fonts of all controls diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index ac6f541e76..b342ebc724 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -55,29 +55,6 @@ enum class ERescaleTarget SettingsDialog }; -static void rescale_dialog_after_dpi_change(MainFrame& mainframe, SettingsDialog& dialog, ERescaleTarget target) -{ - int mainframe_dpi = get_dpi_for_window(&mainframe); - int dialog_dpi = get_dpi_for_window(&dialog); - if (mainframe_dpi != dialog_dpi) { - if (target == ERescaleTarget::SettingsDialog) { - dialog.enable_force_rescale(); -#if wxVERSION_EQUAL_OR_GREATER_THAN(3,1,3) - dialog.GetEventHandler()->AddPendingEvent(wxDPIChangedEvent(wxSize(mainframe_dpi, mainframe_dpi), wxSize(dialog_dpi, dialog_dpi))); -#else - dialog.GetEventHandler()->AddPendingEvent(DpiChangedEvent(EVT_DPI_CHANGED_SLICER, dialog_dpi, dialog.GetRect())); -#endif // wxVERSION_EQUAL_OR_GREATER_THAN - } else { -#if wxVERSION_EQUAL_OR_GREATER_THAN(3,1,3) - mainframe.GetEventHandler()->AddPendingEvent(wxDPIChangedEvent(wxSize(dialog_dpi, dialog_dpi), wxSize(mainframe_dpi, mainframe_dpi))); -#else - mainframe.enable_force_rescale(); - mainframe.GetEventHandler()->AddPendingEvent(DpiChangedEvent(EVT_DPI_CHANGED_SLICER, mainframe_dpi, mainframe.GetRect())); -#endif // wxVERSION_EQUAL_OR_GREATER_THAN - } - } -} - MainFrame::MainFrame() : DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "mainframe"), m_printhost_queue_dlg(new PrintHostQueueDialog(this)) @@ -310,11 +287,6 @@ void MainFrame::update_layout() m_plater_page = nullptr; } - /* - if (m_layout == ESettingsLayout::Dlg) - rescale_dialog_after_dpi_change(*this, m_settings_dialog, ERescaleTarget::Mainframe); - */ - clean_sizer(m_main_sizer); clean_sizer(m_settings_dialog.GetSizer()); @@ -393,9 +365,6 @@ void MainFrame::update_layout() m_main_sizer->Add(m_plater, 1, wxEXPAND); m_tabpanel->Reparent(&m_settings_dialog); m_settings_dialog.GetSizer()->Add(m_tabpanel, 1, wxEXPAND); - -// rescale_dialog_after_dpi_change(*this, m_settings_dialog, ERescaleTarget::SettingsDialog); - m_tabpanel->Show(); m_plater->Show(); break; @@ -825,10 +794,6 @@ void MainFrame::on_dpi_changed(const wxRect& suggested_rect) this->SetSize(sz); this->Maximize(is_maximized); -/* - if (m_layout == ESettingsLayout::Dlg) - rescale_dialog_after_dpi_change(*this, m_settings_dialog, ERescaleTarget::SettingsDialog); - */ } void MainFrame::on_sys_color_changed() diff --git a/src/slic3r/GUI/SysInfoDialog.cpp b/src/slic3r/GUI/SysInfoDialog.cpp index 3bd0fcf9f7..7a41aca1c3 100644 --- a/src/slic3r/GUI/SysInfoDialog.cpp +++ b/src/slic3r/GUI/SysInfoDialog.cpp @@ -109,7 +109,7 @@ SysInfoDialog::SysInfoDialog() } // main_info_text - wxFont font = wxGetApp().normal_font(); + wxFont font = get_default_font_for_dpi(get_dpi_for_window(this));// wxGetApp().normal_font(); const auto text_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue()); auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue()); @@ -175,7 +175,7 @@ void SysInfoDialog::on_dpi_changed(const wxRect &suggested_rect) m_logo_bmp.msw_rescale(); m_logo->SetBitmap(m_logo_bmp.bmp()); - wxFont font = GetFont(); + wxFont font = get_default_font_for_dpi(get_dpi_for_window(this));// GetFont(); const int fs = font.GetPointSize() - 1; int font_size[] = { static_cast(fs*1.5), static_cast(fs*1.4), static_cast(fs*1.3), fs, fs, fs, fs }; From 486c07702c35d43bc49e2afaf53e7baa7b30845a Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 4 Sep 2020 13:42:44 +0200 Subject: [PATCH 404/503] Added SplashScreen --- src/slic3r/GUI/GUI_App.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index d444017f40..7b4c275c1d 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -30,6 +30,7 @@ #include #include +#include #include "libslic3r/Utils.hpp" #include "libslic3r/Model.hpp" @@ -436,6 +437,10 @@ bool GUI_App::on_init_inner() // Let the libslic3r know the callback, which will translate messages on demand. Slic3r::I18N::set_translate_callback(libslic3r_translate_callback); + wxBitmap bitmap = create_scaled_bitmap("wrench", nullptr, 400); + wxSplashScreen* scrn = new wxSplashScreen(bitmap, wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_TIMEOUT, 2500, NULL, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFRAME_NO_TASKBAR | wxSIMPLE_BORDER | wxSTAY_ON_TOP); + wxYield(); + // application frame if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr) wxImage::AddHandler(new wxPNGHandler()); From 9d786b5f889f4d126ce33d85c0d6a61b87aad21b Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 4 Sep 2020 16:21:36 +0200 Subject: [PATCH 405/503] Fixed non-MSW builds --- src/slic3r/GUI/MainFrame.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index b342ebc724..f6fd939e25 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -321,6 +321,7 @@ void MainFrame::update_layout() if (m_layout != ESettingsLayout::Unknown) restore_to_creation(); +#ifdef __WXMSW__ enum class State { noUpdate, fromDlg, @@ -328,6 +329,7 @@ void MainFrame::update_layout() }; State update_scaling_state = m_layout == ESettingsLayout::Dlg ? State::fromDlg : layout == ESettingsLayout::Dlg ? State::toDlg : State::noUpdate; +#endif //__WXMSW__ m_layout = layout; @@ -379,6 +381,7 @@ void MainFrame::update_layout() #endif // ENABLE_GCODE_VIEWER } +#ifdef __WXMSW__ if (update_scaling_state != State::noUpdate) { int mainframe_dpi = get_dpi_for_window(this); @@ -406,6 +409,7 @@ void MainFrame::update_layout() #endif // wxVERSION_EQUAL_OR_GREATER_THAN } } +#endif //__WXMSW__ //#ifdef __APPLE__ // // Using SetMinSize() on Mac messes up the window position in some cases From 902de849c0d3dcb7ff153268c0e0c028180000f9 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 4 Sep 2020 20:25:27 +0200 Subject: [PATCH 406/503] Implemented class SplashScreen for using of text --- resources/icons/prusa_slicer_logo.svg | 5 +++ src/slic3r/GUI/GUI_App.cpp | 50 ++++++++++++++++++++++++--- 2 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 resources/icons/prusa_slicer_logo.svg diff --git a/resources/icons/prusa_slicer_logo.svg b/resources/icons/prusa_slicer_logo.svg new file mode 100644 index 0000000000..927c3e70ba --- /dev/null +++ b/resources/icons/prusa_slicer_logo.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 7b4c275c1d..08219ed865 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -77,6 +77,46 @@ namespace GUI { class MainFrame; +class SplashScreen : public wxSplashScreen +{ +public: + SplashScreen(const wxBitmap& bitmap, long splashStyle, int milliseconds, wxWindow* parent) + : wxSplashScreen(bitmap, splashStyle, milliseconds, parent, wxID_ANY) + { + wxASSERT(bitmap.IsOk()); + m_main_bitmap = bitmap; + } + + void SetText(const wxString& text) + { + SetBmp(m_main_bitmap); + if (!text.empty()) { + wxBitmap bitmap(m_main_bitmap); + wxMemoryDC memDC; + + memDC.SelectObject(bitmap); + + memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold()); + memDC.SetTextForeground(wxColour(237, 107, 33)); + memDC.DrawText(text, 120, 120); + + memDC.SelectObject(wxNullBitmap); + SetBmp(bitmap); + } + wxYield(); + } + + void SetBmp(wxBitmap& bmp) + { + m_window->SetBitmap(bmp); + m_window->Refresh(); + m_window->Update(); + } + +private: + wxBitmap m_main_bitmap; +}; + wxString file_wildcards(FileType file_type, const std::string &custom_extension) { static const std::string defaults[FT_SIZE] = { @@ -390,6 +430,10 @@ bool GUI_App::on_init_inner() app_config->set("version", SLIC3R_VERSION); app_config->save(); + + wxBitmap bitmap = create_scaled_bitmap("prusa_slicer_logo", nullptr, 400); + SplashScreen* scrn = new SplashScreen(bitmap, wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_TIMEOUT, 4000, nullptr); + scrn->SetText(_L("Loading configuration...")); preset_bundle = new PresetBundle(); @@ -437,13 +481,11 @@ bool GUI_App::on_init_inner() // Let the libslic3r know the callback, which will translate messages on demand. Slic3r::I18N::set_translate_callback(libslic3r_translate_callback); - wxBitmap bitmap = create_scaled_bitmap("wrench", nullptr, 400); - wxSplashScreen* scrn = new wxSplashScreen(bitmap, wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_TIMEOUT, 2500, NULL, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFRAME_NO_TASKBAR | wxSIMPLE_BORDER | wxSTAY_ON_TOP); - wxYield(); - // application frame if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr) wxImage::AddHandler(new wxPNGHandler()); + scrn->SetText(_L("Creating settings tabs...")); + mainframe = new MainFrame(); // hide settings tabs after first Layout mainframe->select_tab(0); From e10d1eba54068659a9c23df419d83f0efa70f049 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 7 Sep 2020 08:35:34 +0200 Subject: [PATCH 407/503] GCodeProcessor -> Use decorations to detect toolpaths height for gcode files generated by PrusaSlicer --- src/libslic3r/GCode/GCodeProcessor.cpp | 49 +++++++++++++++----------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index cd42dc2e6c..13764f11e7 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -944,6 +944,20 @@ void GCodeProcessor::process_tags(const std::string& comment) return; } + if (!m_producers_enabled || m_producer == EProducer::PrusaSlicer) { + // height tag + pos = comment.find(Height_Tag); + if (pos != comment.npos) { + try { + m_height = std::stof(comment.substr(pos + Height_Tag.length())); + } + catch (...) { + BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Height (" << comment << ")."; + } + return; + } + } + #if ENABLE_GCODE_VIEWER_DATA_CHECKING // width tag pos = comment.find(Width_Tag); @@ -956,18 +970,6 @@ void GCodeProcessor::process_tags(const std::string& comment) } return; } - - // height tag - pos = comment.find(Height_Tag); - if (pos != comment.npos) { - try { - m_height_compare.last_tag_value = std::stof(comment.substr(pos + Height_Tag.length())); - } - catch (...) { - BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Height (" << comment << ")."; - } - return; - } #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING // color change tag @@ -1416,12 +1418,12 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) type = EMoveType::Travel; if (type == EMoveType::Extrude) { - float d_xyz = std::sqrt(sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z])); + float delta_xyz = std::sqrt(sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z])); float filament_diameter = (static_cast(m_extruder_id) < m_filament_diameters.size()) ? m_filament_diameters[m_extruder_id] : m_filament_diameters.back(); float filament_radius = 0.5f * filament_diameter; float area_filament_cross_section = static_cast(M_PI) * sqr(filament_radius); float volume_extruded_filament = area_filament_cross_section * delta_pos[E]; - float area_toolpath_cross_section = volume_extruded_filament / d_xyz; + float area_toolpath_cross_section = volume_extruded_filament / delta_xyz; // volume extruded filament / tool displacement = area toolpath cross section m_mm3_per_mm = area_toolpath_cross_section; @@ -1429,23 +1431,28 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) m_mm3_per_mm_compare.update(area_toolpath_cross_section, m_extrusion_role); #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING - if (m_end_position[Z] > m_extruded_last_z + EPSILON) { - m_height = m_end_position[Z] - m_extruded_last_z; + if (m_producers_enabled && m_producer != EProducer::PrusaSlicer) { + if (m_end_position[Z] > m_extruded_last_z + EPSILON) { + m_height = m_end_position[Z] - m_extruded_last_z; #if ENABLE_GCODE_VIEWER_DATA_CHECKING - m_height_compare.update(m_height, m_extrusion_role); + m_height_compare.update(m_height, m_extrusion_role); #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING - m_extruded_last_z = m_end_position[Z]; + m_extruded_last_z = m_end_position[Z]; + } } if (m_extrusion_role == erExternalPerimeter) // cross section: rectangle - m_width = delta_pos[E] * static_cast(M_PI * sqr(1.05 * filament_radius)) / (d_xyz * m_height); + m_width = delta_pos[E] * static_cast(M_PI * sqr(1.05 * filament_radius)) / (delta_xyz * m_height); else if (m_extrusion_role == erBridgeInfill || m_extrusion_role == erNone) // cross section: circle - m_width = static_cast(m_filament_diameters[m_extruder_id]) * std::sqrt(delta_pos[E] / d_xyz); + m_width = static_cast(m_filament_diameters[m_extruder_id]) * std::sqrt(delta_pos[E] / delta_xyz); else // cross section: rectangle + 2 semicircles - m_width = delta_pos[E] * static_cast(M_PI * sqr(filament_radius)) / (d_xyz * m_height) + static_cast(1.0 - 0.25 * M_PI) * m_height; + m_width = delta_pos[E] * static_cast(M_PI * sqr(filament_radius)) / (delta_xyz * m_height) + static_cast(1.0 - 0.25 * M_PI) * m_height; + + // clamp width to avoid artifacts which may arise from wrong values of m_height + m_width = std::min(m_width, 4.0f * m_height); #if ENABLE_GCODE_VIEWER_DATA_CHECKING m_width_compare.update(m_width, m_extrusion_role); From 97e62be9024b6a98d22bc920165b5ae3888aa22c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 7 Sep 2020 09:14:06 +0200 Subject: [PATCH 408/503] Check if exist any boundary polyline --- src/libslic3r/Fill/FillAdaptive.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index 577ba7e610..bf9cd7f9d2 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -51,13 +51,19 @@ void FillAdaptive::_fill_surface_single( if(polyline.lines().size() == 1 && expolygon.has_boundary_point(polyline.lines().front().a) && expolygon.has_boundary_point(polyline.lines().front().b)) { boundary_polylines.push_back(polyline); - } else { + } + else + { non_boundary_polylines.push_back(polyline); } } - boundary_polylines = chain_polylines(boundary_polylines); - FillAdaptive::connect_infill(std::move(boundary_polylines), expolygon, polylines_out, this->spacing, params); + if(!boundary_polylines.empty()) + { + boundary_polylines = chain_polylines(boundary_polylines); + FillAdaptive::connect_infill(std::move(boundary_polylines), expolygon, polylines_out, this->spacing, params); + } + polylines_out.insert(polylines_out.end(), non_boundary_polylines.begin(), non_boundary_polylines.end()); } From 8579184d70568d2850125c557a6d35b2551a49e6 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 7 Sep 2020 11:30:31 +0200 Subject: [PATCH 409/503] Follow-up of 573194e059836916b6f216dc068c27a89ea7b843 -> Fixed crash when opening a gcode file --- src/libslic3r/GCode/GCodeProcessor.cpp | 13 ++++++++----- src/libslic3r/GCode/GCodeProcessor.hpp | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 13764f11e7..e9264dbd4c 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -759,13 +759,16 @@ void GCodeProcessor::process_file(const std::string& filename, std::function(curr_time - last_cancel_callback_time).count() > 100) { - cancel_callback(); - last_cancel_callback_time = curr_time; + if (cancel_callback != nullptr) { + // call the cancel callback every 100 ms + auto curr_time = std::chrono::high_resolution_clock::now(); + if (std::chrono::duration_cast(curr_time - last_cancel_callback_time).count() > 100) { + cancel_callback(); + last_cancel_callback_time = curr_time; + } } process_gcode_line(line); }); diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 42772d12bc..b31591ca86 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -420,7 +420,7 @@ namespace Slic3r { // Process the gcode contained in the file with the given filename // throws CanceledException through print->throw_if_canceled() (sent by the caller as callback). - void process_file(const std::string& filename, std::function cancel_callback = std::function()); + void process_file(const std::string& filename, std::function cancel_callback = nullptr); float get_time(PrintEstimatedTimeStatistics::ETimeMode mode) const; std::string get_time_dhm(PrintEstimatedTimeStatistics::ETimeMode mode) const; From fd4c28ed91c3d095c6ee296b55904b22c3e51759 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 7 Sep 2020 15:55:03 +0200 Subject: [PATCH 410/503] WIP: G-code viewer menu, refactoring of starting a background process. --- src/slic3r/CMakeLists.txt | 2 ++ src/slic3r/GUI/MainFrame.cpp | 25 ++++++------------------- src/slic3r/Utils/Thread.hpp | 6 +++--- 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 5681ed66db..1c30078102 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -195,6 +195,8 @@ set(SLIC3R_GUI_SOURCES Utils/Bonjour.hpp Utils/PresetUpdater.cpp Utils/PresetUpdater.hpp + Utils/Process.cpp + Utils/Process.hpp Utils/Profile.hpp Utils/UndoRedo.cpp Utils/UndoRedo.hpp diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index f6fd939e25..f4d7f03eca 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -9,7 +9,6 @@ #include //#include #include -#include #include #include @@ -31,6 +30,7 @@ #include "I18N.hpp" #include "GLCanvas3D.hpp" #include "Plater.hpp" +#include "../Utils/Process.hpp" #include #include "GUI_App.hpp" @@ -40,12 +40,6 @@ #include #endif // _WIN32 -// For starting another PrusaSlicer instance on OSX. -// Fails to compile on Windows on the build server. -#ifdef __APPLE__ - #include -#endif - namespace Slic3r { namespace GUI { @@ -1054,8 +1048,8 @@ void MainFrame::init_menubar() append_menu_item(fileMenu, wxID_ANY, _L("&Repair STL file") + dots, _L("Automatically repair an STL file"), [this](wxCommandEvent&) { repair_stl(); }, "wrench", nullptr, [this]() { return true; }, this); -#if ENABLE_GCODE_VIEWER fileMenu->AppendSeparator(); +#if ENABLE_GCODE_VIEWER append_menu_item(fileMenu, wxID_ANY, _L("&G-code preview"), _L("Switch to G-code preview mode"), [this](wxCommandEvent&) { if (m_plater->model().objects.empty() || @@ -1064,6 +1058,8 @@ void MainFrame::init_menubar() set_mode(EMode::GCodeViewer); }, "", nullptr); #endif // ENABLE_GCODE_VIEWER + append_menu_item(fileMenu, wxID_ANY, _L("&G-code preview") + dots, _L("Open G-code viewer"), + [this](wxCommandEvent&) { start_new_gcodeviewer_open_file(this); }, "", nullptr); fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_EXIT, _L("&Quit"), wxString::Format(_L("Quit %s"), SLIC3R_APP_NAME), [this](wxCommandEvent&) { Close(false); }); @@ -1180,20 +1176,11 @@ void MainFrame::init_menubar() windowMenu->AppendSeparator(); append_menu_item(windowMenu, wxID_ANY, _L("Print &Host Upload Queue") + "\tCtrl+J", _L("Display the Print Host Upload Queue window"), - [this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, "upload_queue", nullptr, - [this]() {return true; }, this); + [this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, "upload_queue", nullptr, [this]() {return true; }, this); windowMenu->AppendSeparator(); append_menu_item(windowMenu, wxID_ANY, _(L("Open new instance")) + "\tCtrl+I", _(L("Open a new PrusaSlicer instance")), - [this](wxCommandEvent&) { - wxString path = wxStandardPaths::Get().GetExecutablePath(); -#ifdef __APPLE__ - boost::process::spawn((const char*)path.c_str()); -#else - wxExecute(wxStandardPaths::Get().GetExecutablePath(), wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE | wxEXEC_MAKE_GROUP_LEADER); -#endif - }, "upload_queue", nullptr, - [this]() {return true; }, this); + [this](wxCommandEvent&) { start_new_slicer(); }, "", nullptr); } // View menu diff --git a/src/slic3r/Utils/Thread.hpp b/src/slic3r/Utils/Thread.hpp index e9c76d2aba..194971c9eb 100644 --- a/src/slic3r/Utils/Thread.hpp +++ b/src/slic3r/Utils/Thread.hpp @@ -1,5 +1,5 @@ -#ifndef THREAD_HPP -#define THREAD_HPP +#ifndef GUI_THREAD_HPP +#define GUI_THREAD_HPP #include #include @@ -25,4 +25,4 @@ template inline boost::thread create_thread(Fn &&fn) } -#endif // THREAD_HPP +#endif // GUI_THREAD_HPP From b0bedf33c0d145f2f6494c6e540668a1d49e5e68 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 7 Sep 2020 15:55:03 +0200 Subject: [PATCH 411/503] WIP: G-code viewer menu, refactoring of starting a background process. --- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/MainFrame.cpp | 25 ++------- src/slic3r/Utils/Process.cpp | 105 +++++++++++++++++++++++++++++++++++ src/slic3r/Utils/Process.hpp | 19 +++++++ src/slic3r/Utils/Thread.hpp | 6 +- 5 files changed, 135 insertions(+), 22 deletions(-) create mode 100644 src/slic3r/Utils/Process.cpp create mode 100644 src/slic3r/Utils/Process.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 5681ed66db..1c30078102 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -195,6 +195,8 @@ set(SLIC3R_GUI_SOURCES Utils/Bonjour.hpp Utils/PresetUpdater.cpp Utils/PresetUpdater.hpp + Utils/Process.cpp + Utils/Process.hpp Utils/Profile.hpp Utils/UndoRedo.cpp Utils/UndoRedo.hpp diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index f6fd939e25..f4d7f03eca 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -9,7 +9,6 @@ #include //#include #include -#include #include #include @@ -31,6 +30,7 @@ #include "I18N.hpp" #include "GLCanvas3D.hpp" #include "Plater.hpp" +#include "../Utils/Process.hpp" #include #include "GUI_App.hpp" @@ -40,12 +40,6 @@ #include #endif // _WIN32 -// For starting another PrusaSlicer instance on OSX. -// Fails to compile on Windows on the build server. -#ifdef __APPLE__ - #include -#endif - namespace Slic3r { namespace GUI { @@ -1054,8 +1048,8 @@ void MainFrame::init_menubar() append_menu_item(fileMenu, wxID_ANY, _L("&Repair STL file") + dots, _L("Automatically repair an STL file"), [this](wxCommandEvent&) { repair_stl(); }, "wrench", nullptr, [this]() { return true; }, this); -#if ENABLE_GCODE_VIEWER fileMenu->AppendSeparator(); +#if ENABLE_GCODE_VIEWER append_menu_item(fileMenu, wxID_ANY, _L("&G-code preview"), _L("Switch to G-code preview mode"), [this](wxCommandEvent&) { if (m_plater->model().objects.empty() || @@ -1064,6 +1058,8 @@ void MainFrame::init_menubar() set_mode(EMode::GCodeViewer); }, "", nullptr); #endif // ENABLE_GCODE_VIEWER + append_menu_item(fileMenu, wxID_ANY, _L("&G-code preview") + dots, _L("Open G-code viewer"), + [this](wxCommandEvent&) { start_new_gcodeviewer_open_file(this); }, "", nullptr); fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_EXIT, _L("&Quit"), wxString::Format(_L("Quit %s"), SLIC3R_APP_NAME), [this](wxCommandEvent&) { Close(false); }); @@ -1180,20 +1176,11 @@ void MainFrame::init_menubar() windowMenu->AppendSeparator(); append_menu_item(windowMenu, wxID_ANY, _L("Print &Host Upload Queue") + "\tCtrl+J", _L("Display the Print Host Upload Queue window"), - [this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, "upload_queue", nullptr, - [this]() {return true; }, this); + [this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, "upload_queue", nullptr, [this]() {return true; }, this); windowMenu->AppendSeparator(); append_menu_item(windowMenu, wxID_ANY, _(L("Open new instance")) + "\tCtrl+I", _(L("Open a new PrusaSlicer instance")), - [this](wxCommandEvent&) { - wxString path = wxStandardPaths::Get().GetExecutablePath(); -#ifdef __APPLE__ - boost::process::spawn((const char*)path.c_str()); -#else - wxExecute(wxStandardPaths::Get().GetExecutablePath(), wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE | wxEXEC_MAKE_GROUP_LEADER); -#endif - }, "upload_queue", nullptr, - [this]() {return true; }, this); + [this](wxCommandEvent&) { start_new_slicer(); }, "", nullptr); } // View menu diff --git a/src/slic3r/Utils/Process.cpp b/src/slic3r/Utils/Process.cpp new file mode 100644 index 0000000000..17e3d6fedf --- /dev/null +++ b/src/slic3r/Utils/Process.cpp @@ -0,0 +1,105 @@ +#include "Process.hpp" + +#include + +#include "../GUI/GUI.hpp" +// for file_wildcards() +#include "../GUI/GUI_App.hpp" +// localization +#include "../GUI/I18N.hpp" + +// For starting another PrusaSlicer instance on OSX. +// Fails to compile on Windows on the build server. +#ifdef __APPLE__ + #include +#endif + +#include + +namespace Slic3r { +namespace GUI { + +enum class NewSlicerInstanceType { + Slicer, + GCodeViewer +}; + +// Start a new Slicer process instance either in a Slicer mode or in a G-code mode. +// Optionally load a 3MF, STL or a G-code on start. +static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance_type, const wxString *path_to_open) +{ +#ifdef _WIN32 + wxString path; + wxFileName::SplitPath(wxStandardPaths::Get().GetExecutablePath(), &path, nullptr, nullptr, wxPATH_NATIVE); + path += "\\"; + path += (instance_type == NewSlicerInstanceType::Slicer) ? "prusa-slicer.exe" : "prusa-gcodeviewer.exe"; + std::vector args; + args.reserve(3); + args.emplace_back(path.wc_str()); + if (path_to_open != nullptr) + args.emplace_back(path_to_open->wc_str()); + args.emplace_back(nullptr); + wxExecute(const_cast(args.data()), wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE | wxEXEC_MAKE_GROUP_LEADER); +#else + // Own executable path. + boost::filesystem::path own_path = into_path(wxStandardPaths::Get().GetExecutablePath()); + #if defined(__APPLE__) + { + own_path /= (instance_type == NewSlicerInstanceType::Slicer) ? "PrusaSlicer" : "PrusaGCodeViewer"; + // On Apple the wxExecute fails, thus we use boost::process instead. + path_to_open ? boost::process::spawn(path.string(), into_u8(*path_to_open)) : boost::process::spawn(path.string()); + } + #else // Linux or Unix + { + std::vector args; + args.reserve(3); + #ifdef(__linux) + static const char *gcodeviewer_param = "--gcodeviewer"; + { + // If executed by an AppImage, start the AppImage, not the main process. + // see https://docs.appimage.org/packaging-guide/environment-variables.html#id2 + const char *appimage_binary = std::getenv("APPIMAGE"); + if (appimage_binary) { + args.emplace_back(appimage_binary); + if (instance_type == NewSlicerInstanceType::GCodeViewer) + args.emplace_back(gcodeviewer_param); + if () + } + } + #endif // __linux + std::string to_open; + if (path_to_open) { + to_open = into_u8(*path_to_open); + args.emplace_back(to_open.c_str()); + } + args.emplace_back(nullptr); + wxExecute(const_cast(&args.data()), wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE | wxEXEC_MAKE_GROUP_LEADER); + } + #endif // Linux or Unix +#endif // Win32 +} + +void start_new_slicer(const wxString *path_to_open) +{ + start_new_slicer_or_gcodeviewer(NewSlicerInstanceType::Slicer, path_to_open); +} + +void start_new_gcodeviewer(const wxString *path_to_open) +{ + start_new_slicer_or_gcodeviewer(NewSlicerInstanceType::GCodeViewer, path_to_open); +} + +void start_new_gcodeviewer_open_file(wxWindow *parent) +{ + wxFileDialog dialog(parent ? parent : wxGetApp().GetTopWindow(), + _L("Open G-code file:"), + from_u8(wxGetApp().app_config->get_last_dir()), wxString(), + file_wildcards(FT_GCODE), wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if (dialog.ShowModal() == wxID_OK) { + wxString path = dialog.GetPath(); + start_new_gcodeviewer(&path); + } +} + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/Utils/Process.hpp b/src/slic3r/Utils/Process.hpp new file mode 100644 index 0000000000..c6acaa643e --- /dev/null +++ b/src/slic3r/Utils/Process.hpp @@ -0,0 +1,19 @@ +#ifndef GUI_PROCESS_HPP +#define GUI_PROCESS_HPP + +class wxWindow; + +namespace Slic3r { +namespace GUI { + +// Start a new slicer instance, optionally with a file to open. +void start_new_slicer(const wxString *path_to_open = nullptr); +// Start a new G-code viewer instance, optionally with a file to open. +void start_new_gcodeviewer(const wxString *path_to_open = nullptr); +// Open a file dialog, ask the user to select a new G-code to open, start a new G-code viewer. +void start_new_gcodeviewer_open_file(wxWindow *parent = nullptr); + +} // namespace GUI +} // namespace Slic3r + +#endif // GUI_PROCESS_HPP diff --git a/src/slic3r/Utils/Thread.hpp b/src/slic3r/Utils/Thread.hpp index e9c76d2aba..194971c9eb 100644 --- a/src/slic3r/Utils/Thread.hpp +++ b/src/slic3r/Utils/Thread.hpp @@ -1,5 +1,5 @@ -#ifndef THREAD_HPP -#define THREAD_HPP +#ifndef GUI_THREAD_HPP +#define GUI_THREAD_HPP #include #include @@ -25,4 +25,4 @@ template inline boost::thread create_thread(Fn &&fn) } -#endif // THREAD_HPP +#endif // GUI_THREAD_HPP From 9473ae8fe2fcac9394f85cb3267d8acf62540906 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 7 Sep 2020 16:56:22 +0200 Subject: [PATCH 412/503] Fix of previous commit, added symlinks to gcodeviewer on Linux & OSX --- src/CMakeLists.txt | 29 ++++++++++++++++++++--------- src/slic3r/Utils/Process.cpp | 11 ++++++++--- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0b0b3c0eef..8b9462cb2a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -209,20 +209,31 @@ if (WIN32) add_custom_target(PrusaSlicerDllsCopy ALL DEPENDS PrusaSlicer) prusaslicer_copy_dlls(PrusaSlicerDllsCopy) -elseif (XCODE) - # Because of Debug/Release/etc. configurations (similar to MSVC) the slic3r binary is located in an extra level - add_custom_command(TARGET PrusaSlicer POST_BUILD - COMMAND ln -sfn "${SLIC3R_RESOURCES_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/resources" - COMMENT "Symlinking the resources directory into the build tree" - VERBATIM - ) else () + if (XCODE) + add_custom_command(TARGET PrusaSlicer POST_BUILD + COMMAND ln -sf "${CMAKE_CURRENT_BINARY_DIR}/PrusaSlicer" "${CMAKE_CURRENT_BINARY_DIR}/prusa-slicer" + COMMAND ln -sf "${CMAKE_CURRENT_BINARY_DIR}/PrusaSlicer" "${CMAKE_CURRENT_BINARY_DIR}/prusa-gcodeviewer" + COMMAND ln -sf "${CMAKE_CURRENT_BINARY_DIR}/PrusaSlicer" "${CMAKE_CURRENT_BINARY_DIR}/PrusaGCodeViewer" + COMMENT "Symlinking the G-code viewer to PrusaSlicer, symlinking to prusa-slicer and prusa-gcodeviewer" + VERBATIM + ) + # Because of Debug/Release/etc. configurations (similar to MSVC) the slic3r binary is located in an extra level + set(BIN_RESOURCES_DIR "${CMAKE_CURRENT_BINARY_DIR}/resources") + else () + add_custom_command(TARGET PrusaSlicer POST_BUILD + COMMAND ln -sf "${CMAKE_CURRENT_BINARY_DIR}/prusa-slicer" "${CMAKE_CURRENT_BINARY_DIR}/prusa-gcodeviewer" + COMMENT "Symlinking the G-code viewer to PrusaSlicer" + VERBATIM + ) + set(BIN_RESOURCES_DIR "${CMAKE_CURRENT_BINARY_DIR}/../resources") + endif () add_custom_command(TARGET PrusaSlicer POST_BUILD - COMMAND ln -sfn "${SLIC3R_RESOURCES_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/../resources" + COMMAND ln -sfn "${SLIC3R_RESOURCES_DIR}" "${BIN_RESOURCES_DIR}" COMMENT "Symlinking the resources directory into the build tree" VERBATIM ) -endif() +endif () # Slic3r binary install target if (WIN32) diff --git a/src/slic3r/Utils/Process.cpp b/src/slic3r/Utils/Process.cpp index 17e3d6fedf..ad3730dd88 100644 --- a/src/slic3r/Utils/Process.cpp +++ b/src/slic3r/Utils/Process.cpp @@ -53,7 +53,7 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance { std::vector args; args.reserve(3); - #ifdef(__linux) + #ifdef __linux static const char *gcodeviewer_param = "--gcodeviewer"; { // If executed by an AppImage, start the AppImage, not the main process. @@ -63,17 +63,22 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance args.emplace_back(appimage_binary); if (instance_type == NewSlicerInstanceType::GCodeViewer) args.emplace_back(gcodeviewer_param); - if () } } #endif // __linux + std::string bin_path; + if (args.empty()) { + // Binary path was not set to the AppImage in the Linux specific block above, call the application directly. + bin_path = (own_path.parent_path() / ((instance_type == NewSlicerInstanceType::Slicer) ? "prusa-slicer" : "prusa-gcodeviewer")).string(); + args.emplace_back(bin_path.c_str()); + } std::string to_open; if (path_to_open) { to_open = into_u8(*path_to_open); args.emplace_back(to_open.c_str()); } args.emplace_back(nullptr); - wxExecute(const_cast(&args.data()), wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE | wxEXEC_MAKE_GROUP_LEADER); + wxExecute(const_cast(args.data()), wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE | wxEXEC_MAKE_GROUP_LEADER); } #endif // Linux or Unix #endif // Win32 From 1221c67d7f9bdb40de5cee3518036511b9f9e8c4 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 7 Sep 2020 17:09:27 +0200 Subject: [PATCH 413/503] Fix for OSX --- src/slic3r/Utils/Process.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/Utils/Process.cpp b/src/slic3r/Utils/Process.cpp index ad3730dd88..596b73ff81 100644 --- a/src/slic3r/Utils/Process.cpp +++ b/src/slic3r/Utils/Process.cpp @@ -42,12 +42,12 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance wxExecute(const_cast(args.data()), wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE | wxEXEC_MAKE_GROUP_LEADER); #else // Own executable path. - boost::filesystem::path own_path = into_path(wxStandardPaths::Get().GetExecutablePath()); + boost::filesystem::path bin_path = into_path(wxStandardPaths::Get().GetExecutablePath()); #if defined(__APPLE__) { - own_path /= (instance_type == NewSlicerInstanceType::Slicer) ? "PrusaSlicer" : "PrusaGCodeViewer"; + bin_path /= (instance_type == NewSlicerInstanceType::Slicer) ? "PrusaSlicer" : "PrusaGCodeViewer"; // On Apple the wxExecute fails, thus we use boost::process instead. - path_to_open ? boost::process::spawn(path.string(), into_u8(*path_to_open)) : boost::process::spawn(path.string()); + path_to_open ? boost::process::spawn(bin_path.string(), into_u8(*path_to_open)) : boost::process::spawn(bin_path.string()); } #else // Linux or Unix { From ae0e576c32b360314a962ddc0eef645c3cc3fe2e Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 7 Sep 2020 17:41:16 +0200 Subject: [PATCH 414/503] Fixing Linux build --- src/slic3r/Utils/Process.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/Utils/Process.cpp b/src/slic3r/Utils/Process.cpp index 596b73ff81..83438390c0 100644 --- a/src/slic3r/Utils/Process.cpp +++ b/src/slic3r/Utils/Process.cpp @@ -66,11 +66,11 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance } } #endif // __linux - std::string bin_path; + std::string my_path; if (args.empty()) { // Binary path was not set to the AppImage in the Linux specific block above, call the application directly. - bin_path = (own_path.parent_path() / ((instance_type == NewSlicerInstanceType::Slicer) ? "prusa-slicer" : "prusa-gcodeviewer")).string(); - args.emplace_back(bin_path.c_str()); + my_path = (bin_path.parent_path() / ((instance_type == NewSlicerInstanceType::Slicer) ? "prusa-slicer" : "prusa-gcodeviewer")).string(); + args.emplace_back(my_path.c_str()); } std::string to_open; if (path_to_open) { From 8622437c12d21ca402cc7cd1cf8fb54a603ab62a Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 7 Sep 2020 18:09:51 +0200 Subject: [PATCH 415/503] fixing symlinks --- src/CMakeLists.txt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8b9462cb2a..e80349f844 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -212,9 +212,10 @@ if (WIN32) else () if (XCODE) add_custom_command(TARGET PrusaSlicer POST_BUILD - COMMAND ln -sf "${CMAKE_CURRENT_BINARY_DIR}/PrusaSlicer" "${CMAKE_CURRENT_BINARY_DIR}/prusa-slicer" - COMMAND ln -sf "${CMAKE_CURRENT_BINARY_DIR}/PrusaSlicer" "${CMAKE_CURRENT_BINARY_DIR}/prusa-gcodeviewer" - COMMAND ln -sf "${CMAKE_CURRENT_BINARY_DIR}/PrusaSlicer" "${CMAKE_CURRENT_BINARY_DIR}/PrusaGCodeViewer" + COMMAND ln -sf PrusaSlicer prusa-slicer + COMMAND ln -sf PrusaSlicer prusa-gcodeviewer + COMMAND ln -sf PrusaSlicer PrusaGCodeViewer + WORKING_DIRECTORY "$" COMMENT "Symlinking the G-code viewer to PrusaSlicer, symlinking to prusa-slicer and prusa-gcodeviewer" VERBATIM ) @@ -222,7 +223,8 @@ else () set(BIN_RESOURCES_DIR "${CMAKE_CURRENT_BINARY_DIR}/resources") else () add_custom_command(TARGET PrusaSlicer POST_BUILD - COMMAND ln -sf "${CMAKE_CURRENT_BINARY_DIR}/prusa-slicer" "${CMAKE_CURRENT_BINARY_DIR}/prusa-gcodeviewer" + COMMAND ln -sf prusa-slicer prusa-gcodeviewer + WORKING_DIRECTORY "$" COMMENT "Symlinking the G-code viewer to PrusaSlicer" VERBATIM ) From 5618293a28f4ebce3a863338ef3d176ac094cc65 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 7 Sep 2020 21:20:49 +0200 Subject: [PATCH 416/503] Splash screen : implemented smart splash screen --- src/slic3r/GUI/GUI_App.cpp | 126 +++++++++++++++++++++++++++++++++++-- 1 file changed, 121 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 08219ed865..11bcda2c44 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -77,6 +77,109 @@ namespace GUI { class MainFrame; +// ysFIXME +static int get_dpi_for_main_display() +{ + wxFrame fr(nullptr, wxID_ANY, wxEmptyString); + return get_dpi_for_window(&fr); +} + +// scale input bitmap and return scale factor +static float scale_bitmap(wxBitmap& bmp) +{ + int dpi = get_dpi_for_main_display(); + float sf = 1.0; + // scale bitmap if needed + if (dpi != DPI_DEFAULT) { + wxImage image = bmp.ConvertToImage(); + if (image.IsOk() && image.GetWidth() != 0 && image.GetHeight() != 0) + { + sf = (float)dpi / DPI_DEFAULT; + int width = int(sf * image.GetWidth()); + int height = int(sf * image.GetHeight()); + image.Rescale(width, height, wxIMAGE_QUALITY_BILINEAR); + + bmp = wxBitmap(std::move(image)); + } + } + return sf; +} + +static void word_wrap_string(wxString& input, int line_len) +{ + int idx = -1; + int cur_len = 0; + for (size_t i = 0; i < input.Len(); i++) + { + cur_len++; + if (input[i] == ' ') + idx = i; + if (input[i] == '\n') + { + idx = -1; + cur_len = 0; + } + if (cur_len >= line_len && idx >= 0) + { + input[idx] = '\n'; + cur_len = static_cast(i) - idx; + } + } +} + +static void DecorateSplashScreen(wxBitmap& bmp) +{ + wxASSERT(bmp.IsOk()); + float scale_factor = scale_bitmap(bmp); + + // use a memory DC to draw directly onto the bitmap + wxMemoryDC memDc(bmp); + + // draw an dark grey box at the left of the splashscreen. + // this box will be 2/9 of the weight of the bitmap, and be at the left. + const wxRect bannerRect(wxPoint(0, (bmp.GetHeight() / 9) * 2 - 2), wxPoint((bmp.GetWidth() / 5) * 2, bmp.GetHeight())); + wxDCBrushChanger bc(memDc, wxBrush(wxColour(51, 51, 51))); + wxDCPenChanger pc(memDc, wxPen(wxColour(51, 51, 51))); + memDc.DrawRectangle(bannerRect); + + // title + wxString title_string = SLIC3R_APP_NAME; + wxFont title_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + title_font.SetPointSize(24); + + // dynamically get the version to display + wxString version_string = _L("Version") + " " + std::string(SLIC3R_VERSION); + wxFont version_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Larger().Larger(); + + // create a copyright notice that uses the year that this file was compiled + wxString year(__DATE__); + wxString cr_symbol = wxString::FromUTF8("\xc2\xa9"); + wxString copyright_string = wxString::Format("%s 2016-%s Prusa Research.\n" + "%s 2011-2018 Alessandro Ranellucci.", + cr_symbol, year.Mid(year.Length() - 4), cr_symbol); + wxFont copyright_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Larger(); + + copyright_string += "Slic3r" + _L("is licensed under the") + _L("GNU Affero General Public License, version 3") + "\n\n" + + _L("PrusaSlicer is based on Slic3r by Alessandro Ranellucci and the RepRap community.") + "\n\n" + + _L("Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and numerous others."); + + word_wrap_string(copyright_string, 50); + + wxCoord margin = int(scale_factor * 20); + + // draw the (orange) labels inside of our black box (at the left of the splashscreen) + memDc.SetTextForeground(wxColour(237, 107, 33)); + + memDc.SetFont(title_font); + memDc.DrawLabel(title_string, bannerRect.Deflate(margin, 0), wxALIGN_TOP | wxALIGN_LEFT); + + memDc.SetFont(version_font); + memDc.DrawLabel(version_string, bannerRect.Deflate(margin, 2 * margin), wxALIGN_TOP | wxALIGN_LEFT); + + memDc.SetFont(copyright_font); + memDc.DrawLabel(copyright_string, bannerRect.Deflate(margin, 2 * margin), wxALIGN_BOTTOM | wxALIGN_LEFT); +} + class SplashScreen : public wxSplashScreen { public: @@ -85,6 +188,10 @@ public: { wxASSERT(bitmap.IsOk()); m_main_bitmap = bitmap; + + int dpi = get_dpi_for_main_display(); + if (dpi != DPI_DEFAULT) + m_scale_factor = (float)dpi / DPI_DEFAULT; } void SetText(const wxString& text) @@ -92,13 +199,14 @@ public: SetBmp(m_main_bitmap); if (!text.empty()) { wxBitmap bitmap(m_main_bitmap); - wxMemoryDC memDC; + wxMemoryDC memDC; memDC.SelectObject(bitmap); - memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold()); + wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold().Larger(); + memDC.SetFont(font); memDC.SetTextForeground(wxColour(237, 107, 33)); - memDC.DrawText(text, 120, 120); + memDC.DrawText(text, int(m_scale_factor * 45), int(m_scale_factor * 215)); memDC.SelectObject(wxNullBitmap); SetBmp(bitmap); @@ -114,7 +222,8 @@ public: } private: - wxBitmap m_main_bitmap; + wxBitmap m_main_bitmap; + float m_scale_factor {1.0}; }; wxString file_wildcards(FileType file_type, const std::string &custom_extension) @@ -431,8 +540,15 @@ bool GUI_App::on_init_inner() app_config->set("version", SLIC3R_VERSION); app_config->save(); + if (wxImage::FindHandler(wxBITMAP_TYPE_JPEG) == nullptr) + wxImage::AddHandler(new wxJPEGHandler()); + wxBitmap bitmap = create_scaled_bitmap("prusa_slicer_logo", nullptr, 400); - SplashScreen* scrn = new SplashScreen(bitmap, wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_TIMEOUT, 4000, nullptr); + wxBitmap bmp(from_u8(var("splashscreen.jpg")), wxBITMAP_TYPE_JPEG); + + DecorateSplashScreen(bmp); + + SplashScreen* scrn = new SplashScreen(bmp.IsOk() ? bmp : bitmap, wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_TIMEOUT, 4000, nullptr); scrn->SetText(_L("Loading configuration...")); preset_bundle = new PresetBundle(); From 889f05167af523da4f0c8d8028049e970ea91358 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 7 Sep 2020 21:36:51 +0200 Subject: [PATCH 417/503] Changing the binary name on OSX to PrusaSlicer. --- src/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e80349f844..c0137502a4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -106,9 +106,9 @@ if (MINGW) set_target_properties(PrusaSlicer PROPERTIES PREFIX "") endif (MINGW) -if (NOT WIN32) - # Binary name on unix like systems (OSX, Linux) - set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer") +if (NOT WIN32 AND NOT APPLE) + # Binary name on unix like systems (Linux, Unix) + set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer") endif () target_link_libraries(PrusaSlicer libslic3r cereal) From 620c85f264f8175552aab041b4ced2d39c132cbb Mon Sep 17 00:00:00 2001 From: test Date: Mon, 7 Sep 2020 22:00:01 +0200 Subject: [PATCH 418/503] Fix on OSX --- src/slic3r/Utils/Process.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/Utils/Process.cpp b/src/slic3r/Utils/Process.cpp index 83438390c0..e29160870d 100644 --- a/src/slic3r/Utils/Process.cpp +++ b/src/slic3r/Utils/Process.cpp @@ -45,7 +45,7 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance boost::filesystem::path bin_path = into_path(wxStandardPaths::Get().GetExecutablePath()); #if defined(__APPLE__) { - bin_path /= (instance_type == NewSlicerInstanceType::Slicer) ? "PrusaSlicer" : "PrusaGCodeViewer"; + bin_path = bin_path.parent_path() / ((instance_type == NewSlicerInstanceType::Slicer) ? "PrusaSlicer" : "PrusaGCodeViewer"); // On Apple the wxExecute fails, thus we use boost::process instead. path_to_open ? boost::process::spawn(bin_path.string(), into_u8(*path_to_open)) : boost::process::spawn(bin_path.string()); } From f237b33515b25833bae40e96c11565564f4ee400 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 7 Sep 2020 22:26:58 +0200 Subject: [PATCH 419/503] Yet another fix on OSX. --- src/CMakeLists.txt | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c0137502a4..ca57ca5531 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -210,31 +210,32 @@ if (WIN32) prusaslicer_copy_dlls(PrusaSlicerDllsCopy) else () - if (XCODE) + if (APPLE) + # On OSX, the name of the binary matches the name of the Application. add_custom_command(TARGET PrusaSlicer POST_BUILD COMMAND ln -sf PrusaSlicer prusa-slicer COMMAND ln -sf PrusaSlicer prusa-gcodeviewer COMMAND ln -sf PrusaSlicer PrusaGCodeViewer WORKING_DIRECTORY "$" COMMENT "Symlinking the G-code viewer to PrusaSlicer, symlinking to prusa-slicer and prusa-gcodeviewer" - VERBATIM - ) - # Because of Debug/Release/etc. configurations (similar to MSVC) the slic3r binary is located in an extra level - set(BIN_RESOURCES_DIR "${CMAKE_CURRENT_BINARY_DIR}/resources") + VERBATIM) else () add_custom_command(TARGET PrusaSlicer POST_BUILD COMMAND ln -sf prusa-slicer prusa-gcodeviewer WORKING_DIRECTORY "$" COMMENT "Symlinking the G-code viewer to PrusaSlicer" - VERBATIM - ) + VERBATIM) + endif () + if (XCODE) + # Because of Debug/Release/etc. configurations (similar to MSVC) the slic3r binary is located in an extra level + set(BIN_RESOURCES_DIR "${CMAKE_CURRENT_BINARY_DIR}/resources") + else () set(BIN_RESOURCES_DIR "${CMAKE_CURRENT_BINARY_DIR}/../resources") endif () add_custom_command(TARGET PrusaSlicer POST_BUILD COMMAND ln -sfn "${SLIC3R_RESOURCES_DIR}" "${BIN_RESOURCES_DIR}" COMMENT "Symlinking the resources directory into the build tree" - VERBATIM - ) + VERBATIM) endif () # Slic3r binary install target From d830e1c970f8562ca862759cf3bcb6c4f80c9c54 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 7 Sep 2020 22:37:55 +0200 Subject: [PATCH 420/503] Run PrusaSlicer as G-code viewer based on argv[0] name on Unix systems. --- src/PrusaSlicer.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 2962f0cdfe..94996dc928 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -101,8 +102,14 @@ int CLI::run(int argc, char **argv) std::find(m_transforms.begin(), m_transforms.end(), "cut") == m_transforms.end() && std::find(m_transforms.begin(), m_transforms.end(), "cut_x") == m_transforms.end() && std::find(m_transforms.begin(), m_transforms.end(), "cut_y") == m_transforms.end(); - bool start_as_gcodeviewer = false; - + bool start_as_gcodeviewer = +#ifdef _WIN32 + false; +#else + // On Unix systems, the prusa-slicer binary may be symlinked to give the application a different meaning. + boost::algorithm::iends_with(boost::filesystem::path(argv[0]).filename().string(), "gcodeviewer"); +#endif // _WIN32 + const std::vector &load_configs = m_config.option("load", true)->values; // load config files supplied via --load From 6dbc1ccf21a5ee95892b2f8a774c0622f6863ac1 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 8 Sep 2020 08:19:06 +0200 Subject: [PATCH 421/503] Added missed image for splash screen --- resources/icons/splashscreen.jpg | Bin 0 -> 133522 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 resources/icons/splashscreen.jpg diff --git a/resources/icons/splashscreen.jpg b/resources/icons/splashscreen.jpg new file mode 100644 index 0000000000000000000000000000000000000000..86c55f30f6af0fb6de922766bccd388f987d2d20 GIT binary patch literal 133522 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 bcUFrAWU5*b{v{~IY9mqVuR~GP|4jZ5b80Hr literal 0 HcmV?d00001 From 1418b71541f66793709d206df2835569926eab95 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 8 Sep 2020 09:12:40 +0200 Subject: [PATCH 422/503] Linux: Try to fix warning "Na handler for image type 15" --- src/slic3r/GUI/GUI_App.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 11bcda2c44..c16aeaada5 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -539,9 +539,13 @@ bool GUI_App::on_init_inner() app_config->set("version", SLIC3R_VERSION); app_config->save(); - +/* if (wxImage::FindHandler(wxBITMAP_TYPE_JPEG) == nullptr) wxImage::AddHandler(new wxJPEGHandler()); + if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr) + wxImage::AddHandler(new wxPNGHandler()); +*/ + wxInitAllImageHandlers(); wxBitmap bitmap = create_scaled_bitmap("prusa_slicer_logo", nullptr, 400); wxBitmap bmp(from_u8(var("splashscreen.jpg")), wxBITMAP_TYPE_JPEG); @@ -598,8 +602,6 @@ bool GUI_App::on_init_inner() Slic3r::I18N::set_translate_callback(libslic3r_translate_callback); // application frame - if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr) - wxImage::AddHandler(new wxPNGHandler()); scrn->SetText(_L("Creating settings tabs...")); mainframe = new MainFrame(); From 663f17a0e3de953dde239ed65dec59eca198a7fc Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 8 Sep 2020 09:57:17 +0200 Subject: [PATCH 423/503] Improved logging of spawning a subprocess. --- src/slic3r/Utils/Process.cpp | 40 ++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/slic3r/Utils/Process.cpp b/src/slic3r/Utils/Process.cpp index e29160870d..4347f66828 100644 --- a/src/slic3r/Utils/Process.cpp +++ b/src/slic3r/Utils/Process.cpp @@ -8,6 +8,11 @@ // localization #include "../GUI/I18N.hpp" +#include +#include + +#include + // For starting another PrusaSlicer instance on OSX. // Fails to compile on Windows on the build server. #ifdef __APPLE__ @@ -29,17 +34,19 @@ enum class NewSlicerInstanceType { static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance_type, const wxString *path_to_open) { #ifdef _WIN32 - wxString path; - wxFileName::SplitPath(wxStandardPaths::Get().GetExecutablePath(), &path, nullptr, nullptr, wxPATH_NATIVE); - path += "\\"; - path += (instance_type == NewSlicerInstanceType::Slicer) ? "prusa-slicer.exe" : "prusa-gcodeviewer.exe"; - std::vector args; - args.reserve(3); - args.emplace_back(path.wc_str()); - if (path_to_open != nullptr) - args.emplace_back(path_to_open->wc_str()); - args.emplace_back(nullptr); - wxExecute(const_cast(args.data()), wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE | wxEXEC_MAKE_GROUP_LEADER); + wxString path; + wxFileName::SplitPath(wxStandardPaths::Get().GetExecutablePath(), &path, nullptr, nullptr, wxPATH_NATIVE); + path += "\\"; + path += (instance_type == NewSlicerInstanceType::Slicer) ? "prusa-slicer.exe" : "prusa-gcodeviewer.exe"; + std::vector args; + args.reserve(3); + args.emplace_back(path.wc_str()); + if (path_to_open != nullptr) + args.emplace_back(path_to_open->wc_str()); + args.emplace_back(nullptr); + BOOST_LOG_TRIVIAL(info) << "Trying to spawn a new slicer \"" << to_u8(path) << "\""; + if (wxExecute(const_cast(args.data()), wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE | wxEXEC_MAKE_GROUP_LEADER) <= 0) + BOOST_LOG_TRIVIAL(error) << "Failed to spawn a new slicer \"" << to_u8(path); #else // Own executable path. boost::filesystem::path bin_path = into_path(wxStandardPaths::Get().GetExecutablePath()); @@ -47,7 +54,12 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance { bin_path = bin_path.parent_path() / ((instance_type == NewSlicerInstanceType::Slicer) ? "PrusaSlicer" : "PrusaGCodeViewer"); // On Apple the wxExecute fails, thus we use boost::process instead. - path_to_open ? boost::process::spawn(bin_path.string(), into_u8(*path_to_open)) : boost::process::spawn(bin_path.string()); + BOOST_LOG_TRIVIAL(info) << "Trying to spawn a new slicer \"" << bin_path.string() << "\""; + try { + path_to_open ? boost::process::spawn(bin_path.string(), into_u8(*path_to_open)) : boost::process::spawn(bin_path.string()); + } catch (const std::exception &ex) { + BOOST_LOG_TRIVIAL(error) << "Failed to spawn a new slicer \"" << bin_path.string() << "\": " << ex.what(); + } } #else // Linux or Unix { @@ -78,7 +90,9 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance args.emplace_back(to_open.c_str()); } args.emplace_back(nullptr); - wxExecute(const_cast(args.data()), wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE | wxEXEC_MAKE_GROUP_LEADER); + BOOST_LOG_TRIVIAL(info) << "Trying to spawn a new slicer \"" << args[0] << "\""; + if (wxExecute(const_cast(args.data()), wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE | wxEXEC_MAKE_GROUP_LEADER) <= 0) + BOOST_LOG_TRIVIAL(error) << "Failed to spawn a new slicer \"" << args[0]; } #endif // Linux or Unix #endif // Win32 From 77ba284a59b782c9898cd6eeb1867ba69cbcf8c3 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 8 Sep 2020 11:22:27 +0200 Subject: [PATCH 424/503] Trying to fix spawn on OSX --- src/slic3r/Utils/Process.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/Utils/Process.cpp b/src/slic3r/Utils/Process.cpp index 4347f66828..3ee141e804 100644 --- a/src/slic3r/Utils/Process.cpp +++ b/src/slic3r/Utils/Process.cpp @@ -56,7 +56,7 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance // On Apple the wxExecute fails, thus we use boost::process instead. BOOST_LOG_TRIVIAL(info) << "Trying to spawn a new slicer \"" << bin_path.string() << "\""; try { - path_to_open ? boost::process::spawn(bin_path.string(), into_u8(*path_to_open)) : boost::process::spawn(bin_path.string()); + path_to_open ? boost::process::spawn(bin_path, into_u8(*path_to_open)) : boost::process::spawn(bin_path, boost::path::args()); } catch (const std::exception &ex) { BOOST_LOG_TRIVIAL(error) << "Failed to spawn a new slicer \"" << bin_path.string() << "\": " << ex.what(); } From 3c51581e92f7ec88ac82007d5e6ffd2eca8840e5 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 8 Sep 2020 11:36:00 +0200 Subject: [PATCH 425/503] Another fix --- src/slic3r/Utils/Process.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/slic3r/Utils/Process.cpp b/src/slic3r/Utils/Process.cpp index 3ee141e804..ab5a9b1e9b 100644 --- a/src/slic3r/Utils/Process.cpp +++ b/src/slic3r/Utils/Process.cpp @@ -11,6 +11,7 @@ #include #include +#include #include // For starting another PrusaSlicer instance on OSX. @@ -56,7 +57,7 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance // On Apple the wxExecute fails, thus we use boost::process instead. BOOST_LOG_TRIVIAL(info) << "Trying to spawn a new slicer \"" << bin_path.string() << "\""; try { - path_to_open ? boost::process::spawn(bin_path, into_u8(*path_to_open)) : boost::process::spawn(bin_path, boost::path::args()); + path_to_open ? boost::process::spawn(bin_path, into_u8(*path_to_open)) : boost::process::spawn(bin_path, boost::filesystem::path::args()); } catch (const std::exception &ex) { BOOST_LOG_TRIVIAL(error) << "Failed to spawn a new slicer \"" << bin_path.string() << "\": " << ex.what(); } From ab556a398b579b65802cfa7fdd39d543ab49fbec Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 8 Sep 2020 11:40:06 +0200 Subject: [PATCH 426/503] GCode viewer using the proper layout when started as a standalone application --- .../icons/PrusaSlicer-gcodeviewer_128px.png | Bin 0 -> 5069 bytes .../icons/PrusaSlicerGCodeViewer_128px.png | Bin 15949 -> 0 bytes src/PrusaSlicer.cpp | 4 + src/libslic3r/AppConfig.cpp | 5 + src/libslic3r/AppConfig.hpp | 11 ++ src/libslic3r/Technologies.hpp | 1 + src/slic3r/GUI/GCodeViewer.cpp | 8 ++ src/slic3r/GUI/GLCanvas3D.cpp | 16 +++ src/slic3r/GUI/GUI_App.cpp | 93 ++++++++---- src/slic3r/GUI/GUI_App.hpp | 25 ++++ src/slic3r/GUI/GUI_Preview.cpp | 4 + src/slic3r/GUI/GUI_Preview.hpp | 5 + src/slic3r/GUI/KBShortcutsDialog.cpp | 6 + src/slic3r/GUI/MainFrame.cpp | 125 ++++++++++++---- src/slic3r/GUI/MainFrame.hpp | 17 ++- src/slic3r/GUI/Plater.cpp | 136 ++++++++++-------- 16 files changed, 341 insertions(+), 115 deletions(-) create mode 100644 resources/icons/PrusaSlicer-gcodeviewer_128px.png delete mode 100644 resources/icons/PrusaSlicerGCodeViewer_128px.png diff --git a/resources/icons/PrusaSlicer-gcodeviewer_128px.png b/resources/icons/PrusaSlicer-gcodeviewer_128px.png new file mode 100644 index 0000000000000000000000000000000000000000..d8e3e438be6c5fc64f7ebc8744a842bdcbfd6aa5 GIT binary patch literal 5069 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 literal 0 HcmV?d00001 diff --git a/resources/icons/PrusaSlicerGCodeViewer_128px.png b/resources/icons/PrusaSlicerGCodeViewer_128px.png deleted file mode 100644 index 0bf85abbd6ea0585d127686a8200b4a647a030b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15949 zcmeHuby$?$_V&;%jWk1d4c#$Fmy{sQ%n;HrbR(cB0s=~Ri6|l6DI!Rhgi_K1LnuRk zgYSFZ^PclN@ty1Xe&1h*>l&En+4s8F-fORY@3o&9V)b>^i12Cg0RRAzhPsLY>TmeX z3l|&pyGP!35Oseg(8v^SVCx6;@N&0za)ANi0Uj_Q%-_i#0PvriN;mUjle(pH9q>d7 zT`6+jvoT|c=nQ#8^4!2lqpVr6@=Y{TAACaOt14emEa#Wt#DeFSVAS2%%@%PiO%#txkSWOduD; zPz~~0>RD|*qvnb^ZlnakXI1sXRE zu!UUb^1!|0Ywuo`@yP7ScC$Upi!YG7f?7@Tp3A_LgJ%9Co(nel)SW4RWQ6GO4rqfVe$lm)Rj0 zU>UIA-k!zSIWFrh2B&X#RT*55@j^<5MUVJuhQ-hEc?HJ9){GZkm%nX?fs5bD?6wY8 zwCsj1&BdzeNuQ<|>d9PE*5SO~zne_lACizEt1r@f++1;?{H&+Am5`Z(^P><(bsqZ4 zdy??cE}d30|Xa zk?4-*-gUund=daUHS?2{LMO!SDb2PtVC^N^WqY1uj#P9`pK0?+oQ$XMcisBbX5Tr4+H~kW+PgEVhW~9Pz&8(3^FfsR4U~LQN(<|Dhjm|aP1XQv9LfS zXcc`TTZv3k>w9MlsX_a|VO`GudXmAuT$^S~p!~C$eVsSmc7u6F>i3si*-e~DTP|F5 z6PtbNVcRw(L+&HgzRo`|yh;{I;O}g7OeOEf?HM(_1U7HmbP8!1l>|=H233=6@e&ZP z2~MR4P3P}w<25lp*+?0SCjElv^q2@Nw}@$9O`)S~G$W4}p7H(ht!Hu%r;}Eht=>3` znRtlKFyMvV7oaz2dp?j%VVN|bUM^}zSTYE*zW+oA?iZeUuEo=&u*^sz`Yy!$jw|1N z)+aMQ&q#Y=G8^e5RfbMyvbUVM`G>f*>)lx1ADRkJwkG0#3M(ysm6~gtuLbk>^I|(> zWe!Pw*u~!a_IQjyY@FG0m93t_l2dlY~k66NCwr9*xOOk9e5VrfM z>6@VsPDcWsx$PlF(a$O)Sjf6SIP`gGaPa>7YgnJ8eH23gwP zbcp&SLhwR%TWCWYsQ3+Nz=Ub$pY zN6lXNFKBNrh&)c%%cu0quc`Px=2G_oKF&a+6sRR#^2$--fY{qE(xQMRZ+m4dxkh{6 zXDy=YIHScg@Y559{0oL}mC%fio7ckMYg)d*r-|E!XcnPQ zfu^)45b2z!8tU8yju8rT5?{Ag!p}XM7M-+B1T)8b$-dA$Z4O^a{usLJU(sD!ToKu< z`*Jo$R9UAyj=9KfaM*tC(_=dknwPpVY-gd(ZEqGKK@}HICv%n0|-o$lTBr%$Ynlhf9Nv7`ecrvbrtJt zZFw1cz1)2V!p!_Z(g8vWI>Rqqa$?J!|3%LaYN>Ch42teTs`Pl_#bKzx z1R3AXZAZ5egeN;e^>Pi)laR4dbFpC>^XHG;jrt=^S98oH#F>L`n-$$j-i~xu6unfm z^94q$3HX9~!9!rZAB4Zul10%z7XqY?+N!qH>^@l_KwR2 z{JpPQNOP>xk$g#wx9XHN4;!L2a|g0Vl2%{VcGKT=?W`NAsLg zRET7x_hv}kt-`)=O{;R$IZD+BHJ5(ARy|!S8ExjO&7*2;QmLQZ)S$nAjenxX0{)a= zG^izq{*vo(A*E^>F*|oHAW7_nC*veeU1pj7(=!oAojXnyx|x=D7TN6z7JqLfv@C)L z=hP{onNL1B$TX_riC^^nye#sDrazl%m$O?%g^hj2X)OJjX5&IF2u7Q*gR#P z;D>s~YJLXHu)I4B3ZSE1w8vI>{^$q2BKKEVXuuMX0INOYGetXZl4<(=VvI>TBh%M zx?t?-vlChFFthW8MefiMXQq{y7eNQ&dj%vJ7k3T@O)lcFI?6-#T2?+JRj&Anke0q_ z#9r4if0Bt2+iLxGx1nCuX{BHH6?;8TLi>2=V^@=u-m_0Xi=FN4=rWS8f`2@mCzevl zk8LE(@zFlrByW}v@zc|-x|m~0V1NEHBPE0bVDfdbh)AD{wRwJ#%Yi6^kZ8tGFtfK@IqDU```us zCKuC%CKvD+qv4D^K$yVN{`E(5hek&udkuWFs)1Lp+dT@XIlrDVpXibf6yx%BT#0v= zjgeGnn$O2lMZHPxyLzoF$_8cbCfaYXQ6u=Fc7`dxZKQJl#aFqAmUI{Ak}9PXq>z*M z>m5c(mq$1O5Vj|P*~x=!_%qwfOOBZk>F0?*+CB)yaGEOeYFXcN^@~h2=Q;ppeVB~1 zpd)-n zn~@9t)!T}0`=@o+Me(DH?5fqmIduCRi#TPwZzVKN!m}}8)_8QaFKuY|9Q#&SRO_*p zogAt1-nwLZy%Z)=VhHt^^ENf>&dav|{|x|d0t z@GFbn)_mDzt5I(YsVMO~hr&aAaIK|kDT_x(7)n??6_27P%r#1Xis7P)&KUe+CF2g< zNnid+vwc8B6_#I(fEgmb@-x;i|kB^SAI}5u<-zyUGDU%?Dv=H?h zns@7mqyrNxUfkJoQ*O=FJ$xV2jg_yyX)ey5LW9qf{VG#4wjqTuO>3T#{=88o%*y4O z$}j32aynmrnyu1%*I{vyidwB)`kf}SNPJ5<0+R_krR{>k7yB$ox-_-+k8Uz4j75qJHAd-A{0P}YD@q)na_`=-atbcau;o{@{XP-Xau$x=I`t1U>7esaHm*+p*sA=fv|6y|z zMh7QXk6#uy=szQ&kUwx9K3*=rFi?mf%mwC(>WDXrS@>`89**vCcW*~`k3Y=*W>9}L z|Bo)A9RFjJzeMg=uYN^XTE!jWbK_n^eH{S{;a^YTV5f*V$0Apyx>4N!=*DvHt;m5WZU zwhk~s4>yNj4Sx+O*xkk5OUE4wlVcJ2yCw}VSpT;tL~<{mWq+3eX>E6i?TuXg)^X^s#^1C= z+15euH>}{l0{;&tLq~T%xBokyze4|DQSyTOxqCV5dFk0bfI;B@n&%&Z|6npeskk@X zD?sDl4C?Q2vcD}qb(F2USHPeA8^Sz)yZS9WE>6Eh6$tz#Z_>7qn<&b$Kz=0v^w%7s z;{02q(?3{x!Xly~API4Memfy?2*0QuDp#b0VWRwE5Md!(I|-S-jot;eNJW zFeL|+EXc7a-RxdK{l91t5DW(1WE_x7#}?w`b|X;Se{1jO2)kJj|1>Lqo9*9sl7BKh zZ(Cp3-wh7pXzS(xLv544YvjKgG)%%?R8&eB#4jZ!Da9{p4+Zf{qBM(N7$htqA_qXZ0u>jtli(K>MJcT)2sPc3QWBE<;=)1@C`3%eUc&zG zLi%@uRzq!Ezmx&U^}8DWlbHnx@uT)^khqaBNLmaeEh5hH`|xB1Z>oX6WU%bbeyO7) z{pTE)z1dHtHEwj#(8t5W#R=y1x4HipE%+z6KiU7168C>={%6>4)?jy!0MriS2-ov- z`a{xjsau>7fLLJj%1d#GXzRoe;vQQZB$@=aK<&9483>pzOXe+2$-cKtsK7ye%#55e3}#jYRf;SW(^ z!T{>Q5w@L{n#!+7NPtstOBm`Mo`<@bHvm9De)B>DWMomJZsNc-bnf7M!==R+W$Sg$ zz5)Ok(lk_*jQrEpGQ|xH?7yyk zdgSevlmsJ5Y?hdKAKUmGNWlAI|Hy5mg+4!}Ks5p20z@nNG=cjGZ5_QP+{C8-G;l6B z0mA5X#RR}dpv}l)*$`4=yP;!r@-NYPU^HTU8!{BVuIErtZ^4ev5sh>KtfT1x2E$L+ z&7e^^JQ$B*m;#)5?eqXZ#HqIpVnKtqryAhO|^)fsNPU>aVIO(>^WSxwc`myMZ#CKS@~)#k#V6G2!->WGsO zH~N-l6rpMM*b_q2j;4!l*;(4BW0Yxu6{#8m*xb^05Vh2&Q%i4TcEAiH zlSGGO+>Ri{i248!np<$eyymMS_a9e5q*D(C700_+@Ou%Z&&u?!0ayygOOm&aF+PMZ zrp;VQ8uY(Hv!R)2C-o=t|A^_QfN6Lwq}Y#kJ5CHqu>Y(ROvf4CKzMT}m%cr!eS7drf%}Gn>-j{BcjEV*YT&iADwNs|A%) zZ^q0EH7~q-5!W~ulo{%@5$2e`|%GocMxRG+Wkyo5Tno zFplXb6NdtvB`aDT)>%(l|FDW2$a>(`Z922jIKSjoMFy{;v?@(Qr%OJdJzT1&Jyj`1t$6Q zyAXh6=w_vFx3~&>{bYe!va+NrOMXnp22<0!t-GDiG51iuk_3>n=eu?)h;{3I$W-J` zxu-(eho_2-s0@APx9r_|HHe9E?=E;o|I~abUQu5$U-5yVV9_20IWC1}%fm4MC1xH4 zdW2x|k|^tVP2$i|BL7kKx=xaK^STI(ucjJAJ*OH+lC7S4hNN%ibNq8dpe2oxCG2aRPkvGw@iO#mX(ECUV3X* zD=$6t>P(Mzv5Kto!20>F?(^WZ?TA%W|D^M6VG%|BzKbxx1x_-{)}ClxL@lC8XW?;La&6c8XWQw{M`p6AEQpE!=2R6$tzmns@!^m)?;8WB6SxtTN83m+-{z{xQ90Z%yBN`yfnD z30Z_>yIMHzwDw|*_Yva*@b~~%gh+>&<+aYY1MfhX4RK&gi85BlNPrBxaC9lB|en@NXZVsXnvjCtWug*+k`xYZxlC8Ix>-+z7hYCbnITg{&Dt*!FLsb??q{X3W}>57tQJYly^R(btH ztdko9qbDZ>?S{-As~7Rm(3xƱ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 AMF0Q* diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 2962f0cdfe..ea56124e6a 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -534,7 +534,11 @@ int CLI::run(int argc, char **argv) if (start_gui) { #ifdef SLIC3R_GUI // #ifdef USE_WX +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + GUI::GUI_App* gui = new GUI::GUI_App(start_as_gcodeviewer ? GUI::GUI_App::EAppMode::GCodeViewer : GUI::GUI_App::EAppMode::Editor); +#else GUI::GUI_App *gui = new GUI::GUI_App(); +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION bool gui_single_instance_setting = gui->app_config->get("single_instance") == "1"; if (Slic3r::instance_check(argc, argv, gui_single_instance_setting)) { diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index 8b41bd2716..db3bd78ddf 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -179,6 +179,11 @@ std::string AppConfig::load() void AppConfig::save() { +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (!m_save_enabled) + return; +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + // The config is first written to a file with a PID suffix and then moved // to avoid race conditions with multiple instances of Slic3r const auto path = config_path(); diff --git a/src/libslic3r/AppConfig.hpp b/src/libslic3r/AppConfig.hpp index ffd1b9fdf5..3f4ce20089 100644 --- a/src/libslic3r/AppConfig.hpp +++ b/src/libslic3r/AppConfig.hpp @@ -18,6 +18,9 @@ public: AppConfig() : m_dirty(false), m_orig_version(Semver::invalid()), +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + m_save_enabled(true), +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION m_legacy_datadir(false) { this->reset(); @@ -157,6 +160,10 @@ public: bool get_mouse_device_swap_yz(const std::string& name, bool& swap) const { return get_3dmouse_device_numeric_value(name, "swap_yz", swap); } +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + void enable_save(bool enable) { m_save_enabled = enable; } +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + static const std::string SECTION_FILAMENTS; static const std::string SECTION_MATERIALS; @@ -183,6 +190,10 @@ private: bool m_dirty; // Original version found in the ini file before it was overwritten Semver m_orig_version; +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + // Whether or not calls to save() should take effect + bool m_save_enabled; +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION // Whether the existing version is before system profiles & configuration updating bool m_legacy_datadir; }; diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index a0484b259c..2dbad472fe 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -59,5 +59,6 @@ #define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_DATA_CHECKING (0 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_TASKBAR_ICON (0 && ENABLE_GCODE_VIEWER) +#define ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION (1 && ENABLE_GCODE_VIEWER) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index bc424466bf..2b9bf8ca46 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -339,7 +339,11 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& reset(); load_toolpaths(gcode_result); +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (wxGetApp().is_editor()) +#else if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION load_shells(print, initialized); else { Pointfs bed_shape; @@ -875,7 +879,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) for (size_t i = 0; i < m_vertices_count; ++i) { const GCodeProcessor::MoveVertex& move = gcode_result.moves[i]; +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (wxGetApp().is_gcode_viewer()) +#else if (wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer) +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION // for the gcode viewer we need all moves to correctly size the printbed m_paths_bounding_box.merge(move.position.cast()); else { diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e7f0f094db..2f9f9464cd 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2732,7 +2732,11 @@ static void load_gcode_retractions(const GCodePreviewData::Retraction& retractio void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result) { m_gcode_viewer.load(gcode_result, *this->fff_print(), m_initialized); +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (wxGetApp().is_editor()) +#else if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION _show_warning_texture_if_needed(WarningTexture::ToolpathOutside); } @@ -4302,7 +4306,11 @@ void GLCanvas3D::update_ui_from_settings() #endif // ENABLE_RETINA_GL #if ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (wxGetApp().is_editor()) +#else if (wxGetApp().mainframe != nullptr && wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION wxGetApp().plater()->get_collapse_toolbar().set_enabled(wxGetApp().app_config->get("show_collapse_button") == "1"); #else bool enable_collapse = wxGetApp().app_config->get("show_collapse_button") == "1"; @@ -5405,7 +5413,11 @@ void GLCanvas3D::_render_background() const { #if ENABLE_GCODE_VIEWER bool use_error_color = false; +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (wxGetApp().is_editor()) { +#else if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) { +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION use_error_color = m_dynamic_background_enabled; if (!m_volumes.empty()) use_error_color &= _is_any_volume_outside(); @@ -7134,7 +7146,11 @@ void GLCanvas3D::_show_warning_texture_if_needed(WarningTexture::Warning warning if (!m_volumes.empty()) show = _is_any_volume_outside(); else { +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (wxGetApp().is_editor()) { +#else if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) { +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box(); if (test_volume.radius() > 0.0 && paths_volume.radius() > 0.0) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 08219ed865..65aa026b54 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1,3 +1,4 @@ +#include "libslic3r/Technologies.hpp" #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" #include "GUI_ObjectManipulation.hpp" @@ -309,8 +310,15 @@ static void generic_exception_handle() IMPLEMENT_APP(GUI_App) +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +GUI_App::GUI_App(EAppMode mode) +#else GUI_App::GUI_App() +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION : wxApp() +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + , m_app_mode(mode) +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION , m_em_unit(10) , m_imgui(new ImGuiWrapper()) , m_wizard(nullptr) @@ -366,6 +374,12 @@ void GUI_App::init_app_config() if (!app_config) app_config = new AppConfig(); +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (is_gcode_viewer()) + // disable config save to avoid to mess it up for the editor + app_config->enable_save(false); +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + // load settings app_conf_exists = app_config->exists(); if (app_conf_exists) { @@ -402,18 +416,18 @@ bool GUI_App::on_init_inner() wxCHECK_MSG(wxDirExists(resources_dir), false, wxString::Format("Resources path does not exist or is not a directory: %s", resources_dir)); - // Enable this to get the default Win32 COMCTRL32 behavior of static boxes. + // Enable this to get the default Win32 COMCTRL32 behavior of static boxes. // wxSystemOptions::SetOption("msw.staticbox.optimized-paint", 0); // Enable this to disable Windows Vista themes for all wxNotebooks. The themes seem to lead to terrible // performance when working on high resolution multi-display setups. // wxSystemOptions::SetOption("msw.notebook.themed-background", 0); // Slic3r::debugf "wxWidgets version %s, Wx version %s\n", wxVERSION_STRING, wxVERSION; - + std::string msg = Http::tls_global_init(); std::string ssl_cert_store = app_config->get("tls_accepted_cert_store_location"); bool ssl_accept = app_config->get("tls_cert_store_accepted") == "yes" && ssl_cert_store == Http::tls_system_cert_store(); - + if (!msg.empty() && !ssl_accept) { wxRichMessageDialog dlg(nullptr, @@ -423,38 +437,44 @@ bool GUI_App::on_init_inner() if (dlg.ShowModal() != wxID_YES) return false; app_config->set("tls_cert_store_accepted", - dlg.IsCheckBoxChecked() ? "yes" : "no"); + dlg.IsCheckBoxChecked() ? "yes" : "no"); app_config->set("tls_accepted_cert_store_location", - dlg.IsCheckBoxChecked() ? Http::tls_system_cert_store() : ""); + dlg.IsCheckBoxChecked() ? Http::tls_system_cert_store() : ""); } - + app_config->set("version", SLIC3R_VERSION); app_config->save(); wxBitmap bitmap = create_scaled_bitmap("prusa_slicer_logo", nullptr, 400); SplashScreen* scrn = new SplashScreen(bitmap, wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_TIMEOUT, 4000, nullptr); scrn->SetText(_L("Loading configuration...")); - + preset_bundle = new PresetBundle(); - + // just checking for existence of Slic3r::data_dir is not enough : it may be an empty directory // supplied as argument to --datadir; in that case we should still run the wizard preset_bundle->setup_directories(); +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (is_editor()) { +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #ifdef __WXMSW__ - associate_3mf_files(); + associate_3mf_files(); #endif // __WXMSW__ - preset_updater = new PresetUpdater(); - Bind(EVT_SLIC3R_VERSION_ONLINE, [this](const wxCommandEvent &evt) { - app_config->set("version_online", into_u8(evt.GetString())); - app_config->save(); - if(this->plater_ != nullptr) { - if (*Semver::parse(SLIC3R_VERSION) < * Semver::parse(into_u8(evt.GetString()))) { - this->plater_->get_notification_manager()->push_notification(NotificationType::NewAppAviable, *(this->plater_->get_current_canvas3D())); - } - } - }); + preset_updater = new PresetUpdater(); + Bind(EVT_SLIC3R_VERSION_ONLINE, [this](const wxCommandEvent& evt) { + app_config->set("version_online", into_u8(evt.GetString())); + app_config->save(); + if (this->plater_ != nullptr) { + if (*Semver::parse(SLIC3R_VERSION) < *Semver::parse(into_u8(evt.GetString()))) { + this->plater_->get_notification_manager()->push_notification(NotificationType::NewAppAviable, *(this->plater_->get_current_canvas3D())); + } + } + }); +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + } +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION // initialize label colors and fonts init_label_colours(); @@ -484,7 +504,11 @@ bool GUI_App::on_init_inner() // application frame if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr) wxImage::AddHandler(new wxPNGHandler()); - scrn->SetText(_L("Creating settings tabs...")); + +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (is_editor()) +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + scrn->SetText(_L("Creating settings tabs...")); mainframe = new MainFrame(); // hide settings tabs after first Layout @@ -519,13 +543,20 @@ bool GUI_App::on_init_inner() static bool once = true; if (once) { once = false; - check_updates(false); +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (preset_updater != nullptr) { +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + check_updates(false); + + CallAfter([this] { + config_wizard_startup(); + preset_updater->slic3r_update_notify(); + preset_updater->sync(preset_bundle); + }); +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + } +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION - CallAfter([this] { - config_wizard_startup(); - preset_updater->slic3r_update_notify(); - preset_updater->sync(preset_bundle); - }); #ifdef _WIN32 //sets window property to mainframe so other instances can indentify it OtherInstanceMessageHandler::init_windows_properties(mainframe, m_instance_hash_int); @@ -533,8 +564,16 @@ bool GUI_App::on_init_inner() } }); +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (is_gcode_viewer()) { + mainframe->update_layout(); + if (plater_ != nullptr) + // ensure the selected technology is ptFFF + plater_->set_printer_technology(ptFFF); + } +#else load_current_presets(); - +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION mainframe->Show(true); /* Temporary workaround for the correct behavior of the Scrolled sidebar panel: diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 34114c03cb..d63825de3e 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -94,8 +94,22 @@ static wxString dots("…", wxConvUTF8); class GUI_App : public wxApp { +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +public: + enum class EAppMode : unsigned char + { + Editor, + GCodeViewer + }; + +private: +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + bool m_initialized { false }; bool app_conf_exists{ false }; +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + EAppMode m_app_mode{ EAppMode::Editor }; +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION wxColour m_color_label_modified; wxColour m_color_label_sys; @@ -125,13 +139,24 @@ class GUI_App : public wxApp std::unique_ptr m_single_instance_checker; std::string m_instance_hash_string; size_t m_instance_hash_int; + public: bool OnInit() override; bool initialized() const { return m_initialized; } +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + explicit GUI_App(EAppMode mode = EAppMode::Editor); +#else GUI_App(); +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION ~GUI_App() override; +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + EAppMode get_app_mode() const { return m_app_mode; } + bool is_editor() const { return m_app_mode == EAppMode::Editor; } + bool is_gcode_viewer() const { return m_app_mode == EAppMode::GCodeViewer; } +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + static std::string get_gl_info(bool format_as_html, bool extensions); wxGLContext* init_glcontext(wxGLCanvas& canvas); bool init_opengl(); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 5dcd26a877..530b3358e2 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -1234,7 +1234,11 @@ void Preview::load_print_as_fff(bool keep_z_range) } #if ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (wxGetApp().is_editor() && !has_layers) +#else if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer && !has_layers) +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #else if (! has_layers) #endif // ENABLE_GCODE_VIEWER diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index d9ce44bd62..6297663067 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -194,6 +194,9 @@ Preview(wxWindow* parent, Model* model, DynamicPrintConfig* config, #if ENABLE_GCODE_VIEWER void update_bottom_toolbar(); void update_moves_slider(); +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + void hide_layers_slider(); +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #endif // ENABLE_GCODE_VIEWER private: @@ -203,7 +206,9 @@ private: void unbind_event_handlers(); #if ENABLE_GCODE_VIEWER +#if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION void hide_layers_slider(); +#endif // !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #else void show_hide_ui_elements(const std::string& what); diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 1eceea22e4..632bc48ed0 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -95,9 +95,15 @@ void KBShortcutsDialog::fill_shortcuts() const std::string& alt = GUI::shortkey_alt_prefix(); #if ENABLE_GCODE_VIEWER +#if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION bool is_gcode_viewer = wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer; +#endif // !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (wxGetApp().is_editor()) { +#else if (!is_gcode_viewer) { +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #endif // ENABLE_GCODE_VIEWER Shortcuts commands_shortcuts = { // File diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index f6fd939e25..853d9a6d75 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -92,7 +92,6 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S } #endif // ENABLE_GCODE_VIEWER_TASKBAR_ICON -// SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); // Load the icon either from the exe, or from the ico file. #if _WIN32 { @@ -102,7 +101,24 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S SetIcon(wxIcon(szExeFileName, wxBITMAP_TYPE_ICO)); } #else - SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + switch (wxGetApp().get_mode()) + { + default: + case GUI_App::EMode::Editor: + { +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + break; + } + case GUI_App::EMode::GCodeViewer: + { + SetIcon(wxIcon(Slic3r::var("PrusaSlicer-gcodeviewer_128px.png"), wxBITMAP_TYPE_PNG)); + break; + } + } +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #endif // _WIN32 // initialize status bar @@ -116,8 +132,15 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S // initialize tabpanel and menubar init_tabpanel(); #if ENABLE_GCODE_VIEWER - init_editor_menubar(); - init_gcodeviewer_menubar(); +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (wxGetApp().is_gcode_viewer()) + init_menubar_as_gcodeviewer(); + else + init_menubar_as_editor(); +#else + init_menubar_as_editor(); + init_menubar_as_gcodeviewer(); +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #if _WIN32 // This is needed on Windows to fake the CTRL+# of the window menu when using the numpad @@ -148,7 +171,10 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S sizer->Add(m_main_sizer, 1, wxEXPAND); SetSizer(sizer); // initialize layout from config - update_layout(); +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (wxGetApp().is_editor()) +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + update_layout(); sizer->SetSizeHints(this); Fit(); @@ -300,10 +326,17 @@ void MainFrame::update_layout() }; #if ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + ESettingsLayout layout = wxGetApp().is_gcode_viewer() ? ESettingsLayout::GCodeViewer : + (wxGetApp().app_config->get("old_settings_layout_mode") == "1" ? ESettingsLayout::Old : + wxGetApp().app_config->get("new_settings_layout_mode") == "1" ? ESettingsLayout::New : + wxGetApp().app_config->get("dlg_settings_layout_mode") == "1" ? ESettingsLayout::Dlg : ESettingsLayout::Old); +#else ESettingsLayout layout = (m_mode == EMode::GCodeViewer) ? ESettingsLayout::GCodeViewer : (wxGetApp().app_config->get("old_settings_layout_mode") == "1" ? ESettingsLayout::Old : wxGetApp().app_config->get("new_settings_layout_mode") == "1" ? ESettingsLayout::New : wxGetApp().app_config->get("dlg_settings_layout_mode") == "1" ? ESettingsLayout::Dlg : ESettingsLayout::Old); +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #else ESettingsLayout layout = wxGetApp().app_config->get("old_settings_layout_mode") == "1" ? ESettingsLayout::Old : wxGetApp().app_config->get("new_settings_layout_mode") == "1" ? ESettingsLayout::New : @@ -375,6 +408,12 @@ void MainFrame::update_layout() case ESettingsLayout::GCodeViewer: { m_main_sizer->Add(m_plater, 1, wxEXPAND); +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + m_plater->set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, "", "", true); + m_plater->enable_view_toolbar(false); + m_plater->get_collapse_toolbar().set_enabled(false); + m_plater->collapse_sidebar(true); +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION m_plater->Show(); break; } @@ -482,6 +521,7 @@ void MainFrame::shutdown() if (m_plater != nullptr) { #if ENABLE_GCODE_VIEWER +#if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION // restore sidebar if it was hidden when switching to gcode viewer mode if (m_restore_from_gcode_viewer.collapsed_sidebar) m_plater->collapse_sidebar(false); @@ -489,6 +529,7 @@ void MainFrame::shutdown() // restore sla printer if it was deselected when switching to gcode viewer mode if (m_restore_from_gcode_viewer.sla_technology) m_plater->set_printer_technology(ptSLA); +#endif // !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #endif // ENABLE_GCODE_VIEWER // Stop the background thread (Windows and Linux). // Disconnect from a 3DConnextion driver (OSX). @@ -590,7 +631,10 @@ void MainFrame::init_tabpanel() // or when the preset's "modified" status changes. Bind(EVT_TAB_PRESETS_CHANGED, &MainFrame::on_presets_changed, this); // #ys_FIXME_to_delete - create_preset_tabs(); +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (wxGetApp().is_editor()) +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + create_preset_tabs(); if (m_plater) { // load initial config @@ -891,7 +935,7 @@ static void add_common_view_menu_items(wxMenu* view_menu, MainFrame* mainFrame, "", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame); } -void MainFrame::init_editor_menubar() +void MainFrame::init_menubar_as_editor() #else void MainFrame::init_menubar() #endif // ENABLE_GCODE_VIEWER @@ -1055,6 +1099,7 @@ void MainFrame::init_menubar() [this](wxCommandEvent&) { repair_stl(); }, "wrench", nullptr, [this]() { return true; }, this); #if ENABLE_GCODE_VIEWER +#if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_ANY, _L("&G-code preview"), _L("Switch to G-code preview mode"), [this](wxCommandEvent&) { @@ -1063,6 +1108,7 @@ void MainFrame::init_menubar() wxString(SLIC3R_APP_NAME) + " - " + _L("Switch to G-code preview mode"), wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION | wxCENTRE).ShowModal() == wxID_YES) set_mode(EMode::GCodeViewer); }, "", nullptr); +#endif // !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #endif // ENABLE_GCODE_VIEWER fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_EXIT, _L("&Quit"), wxString::Format(_L("Quit %s"), SLIC3R_APP_NAME), @@ -1286,6 +1332,17 @@ void MainFrame::init_menubar() // assign menubar to frame after appending items, otherwise special items // will not be handled correctly #if ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + m_menubar = new wxMenuBar(); + m_menubar->Append(fileMenu, _L("&File")); + if (editMenu) m_menubar->Append(editMenu, _L("&Edit")); + m_menubar->Append(windowMenu, _L("&Window")); + if (viewMenu) m_menubar->Append(viewMenu, _L("&View")); + // Add additional menus from C++ + wxGetApp().add_config_menu(m_menubar); + m_menubar->Append(helpMenu, _L("&Help")); + SetMenuBar(m_menubar); +#else m_editor_menubar = new wxMenuBar(); m_editor_menubar->Append(fileMenu, _L("&File")); if (editMenu) m_editor_menubar->Append(editMenu, _L("&Edit")); @@ -1295,6 +1352,7 @@ void MainFrame::init_menubar() wxGetApp().add_config_menu(m_editor_menubar); m_editor_menubar->Append(helpMenu, _L("&Help")); SetMenuBar(m_editor_menubar); +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #else auto menubar = new wxMenuBar(); menubar->Append(fileMenu, _(L("&File"))); @@ -1323,15 +1381,11 @@ void MainFrame::init_menubar() #endif if (plater()->printer_technology() == ptSLA) -#if ENABLE_GCODE_VIEWER - update_editor_menubar(); -#else update_menubar(); -#endif // ENABLE_GCODE_VIEWER } #if ENABLE_GCODE_VIEWER -void MainFrame::init_gcodeviewer_menubar() +void MainFrame::init_menubar_as_gcodeviewer() { wxMenu* fileMenu = new wxMenu; { @@ -1342,9 +1396,11 @@ void MainFrame::init_gcodeviewer_menubar() append_menu_item(fileMenu, wxID_ANY, _L("Export &toolpaths as OBJ") + dots, _L("Export toolpaths as OBJ"), [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->export_toolpaths_to_obj(); }, "export_plater", nullptr, [this]() {return can_export_toolpaths(); }, this); +#if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_ANY, _L("Exit &G-code preview"), _L("Switch to editor mode"), [this](wxCommandEvent&) { set_mode(EMode::Editor); }); +#endif // !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_EXIT, _L("&Quit"), wxString::Format(_L("Quit %s"), SLIC3R_APP_NAME), [this](wxCommandEvent&) { Close(false); }); @@ -1360,13 +1416,22 @@ void MainFrame::init_gcodeviewer_menubar() // helpmenu auto helpMenu = generate_help_menu(); +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + m_menubar = new wxMenuBar(); + m_menubar->Append(fileMenu, _L("&File")); + if (viewMenu != nullptr) m_menubar->Append(viewMenu, _L("&View")); + m_menubar->Append(helpMenu, _L("&Help")); + SetMenuBar(m_menubar); +#else m_gcodeviewer_menubar = new wxMenuBar(); m_gcodeviewer_menubar->Append(fileMenu, _L("&File")); - if ((viewMenu != nullptr)) + if (viewMenu != nullptr) m_gcodeviewer_menubar->Append(viewMenu, _L("&View")); m_gcodeviewer_menubar->Append(helpMenu, _L("&Help")); +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION } +#if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION void MainFrame::set_mode(EMode mode) { if (m_mode == mode) @@ -1432,7 +1497,7 @@ void MainFrame::set_mode(EMode mode) TCHAR szExeFileName[MAX_PATH]; GetModuleFileName(nullptr, szExeFileName, MAX_PATH); SetIcon(wxIcon(szExeFileName, wxBITMAP_TYPE_ICO)); - } + } #else SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); #endif // _WIN32 @@ -1488,11 +1553,11 @@ void MainFrame::set_mode(EMode mode) m_plater->Thaw(); - SetIcon(wxIcon(Slic3r::var("PrusaSlicerGCodeViewer_128px.png"), wxBITMAP_TYPE_PNG)); + SetIcon(wxIcon(Slic3r::var("PrusaSlicer-gcodeviewer_128px.png"), wxBITMAP_TYPE_PNG)); #if ENABLE_GCODE_VIEWER_TASKBAR_ICON if (m_taskbar_icon != nullptr) { m_taskbar_icon->RemoveIcon(); - m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicerGCodeViewer_128px.png"), wxBITMAP_TYPE_PNG), "PrusaSlicer-GCode viewer"); + m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer-gcodeviewer_128px.png"), wxBITMAP_TYPE_PNG), "PrusaSlicer-GCode viewer"); } #endif // ENABLE_GCODE_VIEWER_TASKBAR_ICON @@ -1500,20 +1565,22 @@ void MainFrame::set_mode(EMode mode) } } } +#endif // !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #endif // ENABLE_GCODE_VIEWER -#if ENABLE_GCODE_VIEWER -void MainFrame::update_editor_menubar() -#else void MainFrame::update_menubar() -#endif // ENABLE_GCODE_VIEWER { +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (wxGetApp().is_gcode_viewer()) + return; +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + const bool is_fff = plater()->printer_technology() == ptFFF; - m_changeable_menu_items[miExport] ->SetItemLabel((is_fff ? _(L("Export &G-code")) : _(L("E&xport")) ) + dots + "\tCtrl+G"); - m_changeable_menu_items[miSend] ->SetItemLabel((is_fff ? _(L("S&end G-code")) : _(L("S&end to print"))) + dots + "\tCtrl+Shift+G"); + m_changeable_menu_items[miExport] ->SetItemLabel((is_fff ? _L("Export &G-code") : _L("E&xport")) + dots + "\tCtrl+G"); + m_changeable_menu_items[miSend] ->SetItemLabel((is_fff ? _L("S&end G-code") : _L("S&end to print")) + dots + "\tCtrl+Shift+G"); - m_changeable_menu_items[miMaterialTab] ->SetItemLabel((is_fff ? _(L("&Filament Settings Tab")) : _(L("Mate&rial Settings Tab"))) + "\tCtrl+3"); + m_changeable_menu_items[miMaterialTab] ->SetItemLabel((is_fff ? _L("&Filament Settings Tab") : _L("Mate&rial Settings Tab")) + "\tCtrl+3"); m_changeable_menu_items[miMaterialTab] ->SetBitmap(create_scaled_bitmap(is_fff ? "spool" : "resin")); m_changeable_menu_items[miPrinterTab] ->SetBitmap(create_scaled_bitmap(is_fff ? "printer" : "sla_printer")); @@ -1996,6 +2063,11 @@ SettingsDialog::SettingsDialog(MainFrame* mainframe) wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMINIMIZE_BOX | wxMAXIMIZE_BOX, "settings_dialog"), m_main_frame(mainframe) { +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (wxGetApp().is_gcode_viewer()) + return; +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + #if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT && defined(__WXMSW__) // ys_FIXME! temporary workaround for correct font scaling // Because of from wxWidgets 3.1.3 auto rescaling is implemented for the Fonts, @@ -2006,8 +2078,6 @@ SettingsDialog::SettingsDialog(MainFrame* mainframe) #endif // ENABLE_WX_3_1_3_DPI_CHANGED_EVENT this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - -// SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); // Load the icon either from the exe, or from the ico file. #if _WIN32 { @@ -2070,6 +2140,11 @@ SettingsDialog::SettingsDialog(MainFrame* mainframe) void SettingsDialog::on_dpi_changed(const wxRect& suggested_rect) { +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (wxGetApp().is_gcode_viewer()) + return; +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + const int& em = em_unit(); const wxSize& size = wxSize(85 * em, 50 * em); diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 7777a053d2..867e11e86b 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -57,7 +57,7 @@ class SettingsDialog : public DPIDialog MainFrame* m_main_frame { nullptr }; public: SettingsDialog(MainFrame* mainframe); - ~SettingsDialog() {} + ~SettingsDialog() = default; void set_tabpanel(wxNotebook* tabpanel) { m_tabpanel = tabpanel; } protected: @@ -72,6 +72,9 @@ class MainFrame : public DPIFrame wxString m_qs_last_output_file = wxEmptyString; wxString m_last_config = wxEmptyString; #if ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + wxMenuBar* m_menubar{ nullptr }; +#else wxMenuBar* m_editor_menubar{ nullptr }; wxMenuBar* m_gcodeviewer_menubar{ nullptr }; @@ -83,6 +86,7 @@ class MainFrame : public DPIFrame }; RestoreFromGCodeViewer m_restore_from_gcode_viewer; +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #endif // ENABLE_GCODE_VIEWER #if 0 @@ -146,6 +150,7 @@ class MainFrame : public DPIFrame ESettingsLayout m_layout{ ESettingsLayout::Unknown }; #if ENABLE_GCODE_VIEWER +#if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION public: enum class EMode : unsigned char { @@ -155,6 +160,7 @@ public: private: EMode m_mode{ EMode::Editor }; +#endif // !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #endif // ENABLE_GCODE_VIEWER protected: @@ -182,16 +188,17 @@ public: void create_preset_tabs(); void add_created_tab(Tab* panel); #if ENABLE_GCODE_VIEWER - void init_editor_menubar(); - void update_editor_menubar(); - void init_gcodeviewer_menubar(); + void init_menubar_as_editor(); + void init_menubar_as_gcodeviewer(); +#if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION EMode get_mode() const { return m_mode; } void set_mode(EMode mode); +#endif // !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #else void init_menubar(); - void update_menubar(); #endif // ENABLE_GCODE_VIEWER + void update_menubar(); void update_ui_from_settings(); bool is_loaded() const { return m_loaded; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 45a1f6ea82..09640ebaf4 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1369,41 +1369,52 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi this->MSWUpdateDragImageOnLeave(); #endif // WIN32 - // gcode section - for (const auto& filename : filenames) { - fs::path path(into_path(filename)); - if (std::regex_match(path.string(), pattern_gcode_drop)) - paths.push_back(std::move(path)); - } - - if (paths.size() > 1) { - wxMessageDialog((wxWindow*)plater, _L("You can open only one .gcode file at a time."), - wxString(SLIC3R_APP_NAME) + " - " + _L("Drag and drop G-code file"), wxCLOSE | wxICON_WARNING | wxCENTRE).ShowModal(); - return false; - } - else if (paths.size() == 1) { - if (wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer) { - plater->load_gcode(from_path(paths.front())); - return true; +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (wxGetApp().is_gcode_viewer()) { +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + // gcode section + for (const auto& filename : filenames) { + fs::path path(into_path(filename)); + if (std::regex_match(path.string(), pattern_gcode_drop)) + paths.push_back(std::move(path)); } - else { - if (wxMessageDialog((wxWindow*)plater, _L("Do you want to switch to G-code preview ?"), - wxString(SLIC3R_APP_NAME) + " - " + _L("Drag and drop G-code file"), wxYES_NO | wxICON_QUESTION | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES) { - if (plater->model().objects.empty() || - wxMessageDialog((wxWindow*)plater, _L("Switching to G-code preview mode will remove all objects, continue?"), - wxString(SLIC3R_APP_NAME) + " - " + _L("Switch to G-code preview mode"), wxYES_NO | wxICON_QUESTION | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES) { - wxGetApp().mainframe->set_mode(MainFrame::EMode::GCodeViewer); - plater->load_gcode(from_path(paths.front())); - return true; - } - } + if (paths.size() > 1) { + wxMessageDialog((wxWindow*)plater, _L("You can open only one .gcode file at a time."), + wxString(SLIC3R_APP_NAME) + " - " + _L("Drag and drop G-code file"), wxCLOSE | wxICON_WARNING | wxCENTRE).ShowModal(); return false; } + else if (paths.size() == 1) { +#if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer) { +#endif // !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + plater->load_gcode(from_path(paths.front())); + return true; +#if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + } + else { + if (wxMessageDialog((wxWindow*)plater, _L("Do you want to switch to G-code preview ?"), + wxString(SLIC3R_APP_NAME) + " - " + _L("Drag and drop G-code file"), wxYES_NO | wxICON_QUESTION | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES) { + + if (plater->model().objects.empty() || + wxMessageDialog((wxWindow*)plater, _L("Switching to G-code preview mode will remove all objects, continue?"), + wxString(SLIC3R_APP_NAME) + " - " + _L("Switch to G-code preview mode"), wxYES_NO | wxICON_QUESTION | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES) { + wxGetApp().mainframe->set_mode(MainFrame::EMode::GCodeViewer); + plater->load_gcode(from_path(paths.front())); + return true; + } + } + return false; + } +#endif // !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + } +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + return false; } +#endif //ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #endif // ENABLE_GCODE_VIEWER - // model section + // editor section for (const auto &filename : filenames) { fs::path path(into_path(filename)); if (std::regex_match(path.string(), pattern_drop)) @@ -1413,6 +1424,7 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi } #if ENABLE_GCODE_VIEWER +#if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION if (wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer) { if (wxMessageDialog((wxWindow*)plater, _L("Do you want to exit G-code preview ?"), wxString(SLIC3R_APP_NAME) + " - " + _L("Drag and drop model file"), wxYES_NO | wxICON_QUESTION | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES) @@ -1420,6 +1432,7 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi else return false; } +#endif // !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #endif // ENABLE_GCODE_VIEWER wxString snapshot_label; @@ -1970,7 +1983,13 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) q->SetDropTarget(new PlaterDropTarget(q)); // if my understanding is right, wxWindow takes the owenership q->Layout(); +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + set_current_panel(wxGetApp().is_editor() ? (wxPanel*)view3D : (wxPanel*)preview); + if (wxGetApp().is_gcode_viewer()) + preview->hide_layers_slider(); +#else set_current_panel(view3D); +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION // updates camera type from .ini file camera.set_type(get_config("use_perspective_camera")); @@ -1990,33 +2009,38 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) #endif /* _WIN32 */ notification_manager = new NotificationManager(this->q); - this->q->Bind(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED, [this](EjectDriveNotificationClickedEvent&) { this->q->eject_drive(); }); - this->q->Bind(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED, [this](ExportGcodeNotificationClickedEvent&) { this->q->export_gcode(true); }); - this->q->Bind(EVT_PRESET_UPDATE_AVIABLE_CLICKED, [this](PresetUpdateAviableClickedEvent&) { wxGetApp().get_preset_updater()->on_update_notification_confirm(); }); - - this->q->Bind(EVT_REMOVABLE_DRIVE_EJECTED, [this, q](RemovableDriveEjectEvent &evt) { - if (evt.data.second) { - this->show_action_buttons(this->ready_to_slice); - notification_manager->push_notification(format(_L("Unmounting successful. The device %s(%s) can now be safely removed from the computer."),evt.data.first.name, evt.data.first.path), - NotificationManager::NotificationLevel::RegularNotification, *q->get_current_canvas3D()); - } else { - notification_manager->push_notification(format(_L("Ejecting of device %s(%s) has failed."), evt.data.first.name, evt.data.first.path), - NotificationManager::NotificationLevel::ErrorNotification, *q->get_current_canvas3D()); - } - }); - this->q->Bind(EVT_REMOVABLE_DRIVES_CHANGED, [this, q](RemovableDrivesChangedEvent &) { - this->show_action_buttons(this->ready_to_slice); - if (!this->sidebar->get_eject_shown()) { - notification_manager->close_notification_of_type(NotificationType::ExportToRemovableFinished); - } - }); - // Start the background thread and register this window as a target for update events. - wxGetApp().removable_drive_manager()->init(this->q); +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (wxGetApp().is_editor()) { +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + this->q->Bind(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED, [this](EjectDriveNotificationClickedEvent&) { this->q->eject_drive(); }); + this->q->Bind(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED, [this](ExportGcodeNotificationClickedEvent&) { this->q->export_gcode(true); }); + this->q->Bind(EVT_PRESET_UPDATE_AVIABLE_CLICKED, [this](PresetUpdateAviableClickedEvent&) { wxGetApp().get_preset_updater()->on_update_notification_confirm(); }); + this->q->Bind(EVT_REMOVABLE_DRIVE_EJECTED, [this, q](RemovableDriveEjectEvent &evt) { + if (evt.data.second) { + this->show_action_buttons(this->ready_to_slice); + notification_manager->push_notification(format(_L("Unmounting successful. The device %s(%s) can now be safely removed from the computer."),evt.data.first.name, evt.data.first.path), + NotificationManager::NotificationLevel::RegularNotification, *q->get_current_canvas3D()); + } else { + notification_manager->push_notification(format(_L("Ejecting of device %s(%s) has failed."), evt.data.first.name, evt.data.first.path), + NotificationManager::NotificationLevel::ErrorNotification, *q->get_current_canvas3D()); + } + }); + this->q->Bind(EVT_REMOVABLE_DRIVES_CHANGED, [this, q](RemovableDrivesChangedEvent &) { + this->show_action_buttons(this->ready_to_slice); + if (!this->sidebar->get_eject_shown()) { + notification_manager->close_notification_of_type(NotificationType::ExportToRemovableFinished); + } + }); + // Start the background thread and register this window as a target for update events. + wxGetApp().removable_drive_manager()->init(this->q); #ifdef _WIN32 - // Trigger enumeration of removable media on Win32 notification. - this->q->Bind(EVT_VOLUME_ATTACHED, [this](VolumeAttachedEvent &evt) { wxGetApp().removable_drive_manager()->volumes_changed(); }); - this->q->Bind(EVT_VOLUME_DETACHED, [this](VolumeDetachedEvent &evt) { wxGetApp().removable_drive_manager()->volumes_changed(); }); + // Trigger enumeration of removable media on Win32 notification. + this->q->Bind(EVT_VOLUME_ATTACHED, [this](VolumeAttachedEvent &evt) { wxGetApp().removable_drive_manager()->volumes_changed(); }); + this->q->Bind(EVT_VOLUME_DETACHED, [this](VolumeDetachedEvent &evt) { wxGetApp().removable_drive_manager()->volumes_changed(); }); #endif /* _WIN32 */ +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + } +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION // Initialize the Undo / Redo stack with a first snapshot. this->take_snapshot(_L("New Project")); @@ -5384,7 +5408,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config) this->set_printer_technology(config.opt_enum(opt_key)); // print technology is changed, so we should to update a search list p->sidebar->update_searcher(); - } + } else if ((opt_key == "bed_shape") || (opt_key == "bed_custom_texture") || (opt_key == "bed_custom_model")) { bed_shape_changed = true; update_scheduled = true; @@ -5628,11 +5652,7 @@ void Plater::set_printer_technology(PrinterTechnology printer_technology) p->label_btn_send = printer_technology == ptFFF ? L("Send G-code") : L("Send to printer"); if (wxGetApp().mainframe != nullptr) -#if ENABLE_GCODE_VIEWER - wxGetApp().mainframe->update_editor_menubar(); -#else wxGetApp().mainframe->update_menubar(); -#endif // ENABLE_GCODE_VIEWER p->update_main_toolbar_tooltips(); From f58d3116bfd31c78b00eef3e597232be1b1e8e2d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 8 Sep 2020 11:43:18 +0200 Subject: [PATCH 427/503] Fixed crash when loading gcode files saved with older version of PrusaSlicer 2.3.0.alpha --- src/libslic3r/GCode/GCodeProcessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index e9264dbd4c..db69f4f0ba 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1434,7 +1434,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) m_mm3_per_mm_compare.update(area_toolpath_cross_section, m_extrusion_role); #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING - if (m_producers_enabled && m_producer != EProducer::PrusaSlicer) { + if ((m_producers_enabled && m_producer != EProducer::PrusaSlicer) || m_height == 0.0f) { if (m_end_position[Z] > m_extruded_last_z + EPSILON) { m_height = m_end_position[Z] - m_extruded_last_z; #if ENABLE_GCODE_VIEWER_DATA_CHECKING From 0fde670fd654b794c49b462bd140c48f46af6d58 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 8 Sep 2020 11:49:02 +0200 Subject: [PATCH 428/503] osx fix --- src/slic3r/Utils/Process.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/Utils/Process.cpp b/src/slic3r/Utils/Process.cpp index ab5a9b1e9b..2301cd2504 100644 --- a/src/slic3r/Utils/Process.cpp +++ b/src/slic3r/Utils/Process.cpp @@ -57,7 +57,7 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance // On Apple the wxExecute fails, thus we use boost::process instead. BOOST_LOG_TRIVIAL(info) << "Trying to spawn a new slicer \"" << bin_path.string() << "\""; try { - path_to_open ? boost::process::spawn(bin_path, into_u8(*path_to_open)) : boost::process::spawn(bin_path, boost::filesystem::path::args()); + path_to_open ? boost::process::spawn(bin_path, into_u8(*path_to_open)) : boost::process::spawn(bin_path, boost::process::args()); } catch (const std::exception &ex) { BOOST_LOG_TRIVIAL(error) << "Failed to spawn a new slicer \"" << bin_path.string() << "\": " << ex.what(); } From 2443b7aaeaa7e1769fed84fe2e5d57572ca85337 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 8 Sep 2020 11:55:21 +0200 Subject: [PATCH 429/503] Splash screen for gcode viewer --- resources/icons/splashscreen-gcodeviewer.jpg | Bin 0 -> 135897 bytes src/slic3r/GUI/GUI_App.cpp | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 resources/icons/splashscreen-gcodeviewer.jpg diff --git a/resources/icons/splashscreen-gcodeviewer.jpg b/resources/icons/splashscreen-gcodeviewer.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f170f390c2b822410ef2853041ce58563410ed25 GIT binary patch literal 135897 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 literal 0 HcmV?d00001 diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 426dfc81c7..738c41210f 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -562,7 +562,7 @@ bool GUI_App::on_init_inner() wxInitAllImageHandlers(); wxBitmap bitmap = create_scaled_bitmap("prusa_slicer_logo", nullptr, 400); - wxBitmap bmp(from_u8(var("splashscreen.jpg")), wxBITMAP_TYPE_JPEG); + wxBitmap bmp(is_editor() ? from_u8(var("splashscreen.jpg")) : from_u8(var("splashscreen-gcodeviewer.jpg")), wxBITMAP_TYPE_JPEG); DecorateSplashScreen(bmp); From 7270d222dfe410eefda01bcfab487d9b28c2e9f0 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 8 Sep 2020 12:10:07 +0200 Subject: [PATCH 430/503] Fix build on OsX --- src/slic3r/GUI/GUI_App.cpp | 4 ++++ src/slic3r/GUI/MainFrame.cpp | 37 +++++++++++++++++++++++++++--------- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 738c41210f..65c0bd878d 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -562,7 +562,11 @@ bool GUI_App::on_init_inner() wxInitAllImageHandlers(); wxBitmap bitmap = create_scaled_bitmap("prusa_slicer_logo", nullptr, 400); +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION wxBitmap bmp(is_editor() ? from_u8(var("splashscreen.jpg")) : from_u8(var("splashscreen-gcodeviewer.jpg")), wxBITMAP_TYPE_JPEG); +#else + wxBitmap bmp(from_u8(var("splashscreen.jpg")), wxBITMAP_TYPE_JPEG); +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION DecorateSplashScreen(bmp); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 853d9a6d75..5a140f9a9c 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -102,17 +102,17 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S } #else #if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION - switch (wxGetApp().get_mode()) + switch (wxGetApp().get_app_mode()) { default: - case GUI_App::EMode::Editor: + case GUI_App::EAppMode::Editor: { #endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); #if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION break; } - case GUI_App::EMode::GCodeViewer: + case GUI_App::EAppMode::GCodeViewer: { SetIcon(wxIcon(Slic3r::var("PrusaSlicer-gcodeviewer_128px.png"), wxBITMAP_TYPE_PNG)); break; @@ -1355,13 +1355,13 @@ void MainFrame::init_menubar() #endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #else auto menubar = new wxMenuBar(); - menubar->Append(fileMenu, _(L("&File"))); - if (editMenu) menubar->Append(editMenu, _(L("&Edit"))); - menubar->Append(windowMenu, _(L("&Window"))); - if (viewMenu) menubar->Append(viewMenu, _(L("&View"))); + menubar->Append(fileMenu, _L("&File")); + if (editMenu) menubar->Append(editMenu, _L("&Edit")); + menubar->Append(windowMenu, _L("&Window")); + if (viewMenu) menubar->Append(viewMenu, _L("&View")); // Add additional menus from C++ wxGetApp().add_config_menu(menubar); - menubar->Append(helpMenu, _(L("&Help"))); + menubar->Append(helpMenu, _L("&Help")); SetMenuBar(menubar); #endif // ENABLE_GCODE_VIEWER @@ -1369,7 +1369,11 @@ void MainFrame::init_menubar() // This fixes a bug on Mac OS where the quit command doesn't emit window close events // wx bug: https://trac.wxwidgets.org/ticket/18328 #if ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + wxMenu* apple_menu = menubar->OSXGetAppleMenu(); +#else wxMenu* apple_menu = m_editor_menubar->OSXGetAppleMenu(); +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #else wxMenu *apple_menu = menubar->OSXGetAppleMenu(); #endif // ENABLE_GCODE_VIEWER @@ -1378,7 +1382,7 @@ void MainFrame::init_menubar() Close(); }, wxID_EXIT); } -#endif +#endif // __APPLE__ if (plater()->printer_technology() == ptSLA) update_menubar(); @@ -1429,6 +1433,21 @@ void MainFrame::init_menubar_as_gcodeviewer() m_gcodeviewer_menubar->Append(viewMenu, _L("&View")); m_gcodeviewer_menubar->Append(helpMenu, _L("&Help")); #endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + +#ifdef __APPLE__ + // This fixes a bug on Mac OS where the quit command doesn't emit window close events + // wx bug: https://trac.wxwidgets.org/ticket/18328 +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + wxMenu* apple_menu = menubar->OSXGetAppleMenu(); +#else + wxMenu* apple_menu = m_gcodeviewer_menubar->OSXGetAppleMenu(); +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (apple_menu != nullptr) { + apple_menu->Bind(wxEVT_MENU, [this](wxCommandEvent&) { + Close(); + }, wxID_EXIT); + } +#endif // __APPLE__ } #if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION From f6534f5f7a3cdc41d005212278d9d89604c116d4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 8 Sep 2020 12:36:57 +0200 Subject: [PATCH 431/503] Follow-up of 7270d222dfe410eefda01bcfab487d9b28c2e9f0 -> Fix of build on OsX and Linux --- src/slic3r/GUI/MainFrame.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 5a140f9a9c..151648740d 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1370,7 +1370,7 @@ void MainFrame::init_menubar() // wx bug: https://trac.wxwidgets.org/ticket/18328 #if ENABLE_GCODE_VIEWER #if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION - wxMenu* apple_menu = menubar->OSXGetAppleMenu(); + wxMenu* apple_menu = m_menubar->OSXGetAppleMenu(); #else wxMenu* apple_menu = m_editor_menubar->OSXGetAppleMenu(); #endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION @@ -1438,7 +1438,7 @@ void MainFrame::init_menubar_as_gcodeviewer() // This fixes a bug on Mac OS where the quit command doesn't emit window close events // wx bug: https://trac.wxwidgets.org/ticket/18328 #if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION - wxMenu* apple_menu = menubar->OSXGetAppleMenu(); + wxMenu* apple_menu = m_menubar->OSXGetAppleMenu(); #else wxMenu* apple_menu = m_gcodeviewer_menubar->OSXGetAppleMenu(); #endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION From 0f64b67ffa9f88055150b59c6314ec7d15377963 Mon Sep 17 00:00:00 2001 From: test Date: Tue, 8 Sep 2020 12:39:11 +0200 Subject: [PATCH 432/503] osx fix --- src/slic3r/Utils/Process.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/slic3r/Utils/Process.cpp b/src/slic3r/Utils/Process.cpp index 2301cd2504..fa5ecb1f07 100644 --- a/src/slic3r/Utils/Process.cpp +++ b/src/slic3r/Utils/Process.cpp @@ -18,6 +18,7 @@ // Fails to compile on Windows on the build server. #ifdef __APPLE__ #include + #include #endif #include @@ -57,7 +58,10 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance // On Apple the wxExecute fails, thus we use boost::process instead. BOOST_LOG_TRIVIAL(info) << "Trying to spawn a new slicer \"" << bin_path.string() << "\""; try { - path_to_open ? boost::process::spawn(bin_path, into_u8(*path_to_open)) : boost::process::spawn(bin_path, boost::process::args()); + std::vector args; + if (path_to_open) + args.emplace_back(into_u8(*path_to_open)); + boost::process::spawn(bin_path, args); } catch (const std::exception &ex) { BOOST_LOG_TRIVIAL(error) << "Failed to spawn a new slicer \"" << bin_path.string() << "\": " << ex.what(); } From 946f51467fec8c90f7b85ae1d943f672f2c3d9bc Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 8 Sep 2020 13:33:43 +0200 Subject: [PATCH 433/503] WIP Standalone G-code viewer --- src/CMakeLists.txt | 42 ++++++++---- src/PrusaSlicer.cpp | 11 ++- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/MainFrame.cpp | 25 ++----- src/slic3r/Utils/Process.cpp | 125 +++++++++++++++++++++++++++++++++++ src/slic3r/Utils/Process.hpp | 19 ++++++ src/slic3r/Utils/Thread.hpp | 6 +- 7 files changed, 192 insertions(+), 38 deletions(-) create mode 100644 src/slic3r/Utils/Process.cpp create mode 100644 src/slic3r/Utils/Process.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0b0b3c0eef..ca57ca5531 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -106,9 +106,9 @@ if (MINGW) set_target_properties(PrusaSlicer PROPERTIES PREFIX "") endif (MINGW) -if (NOT WIN32) - # Binary name on unix like systems (OSX, Linux) - set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer") +if (NOT WIN32 AND NOT APPLE) + # Binary name on unix like systems (Linux, Unix) + set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer") endif () target_link_libraries(PrusaSlicer libslic3r cereal) @@ -209,20 +209,34 @@ if (WIN32) add_custom_target(PrusaSlicerDllsCopy ALL DEPENDS PrusaSlicer) prusaslicer_copy_dlls(PrusaSlicerDllsCopy) -elseif (XCODE) - # Because of Debug/Release/etc. configurations (similar to MSVC) the slic3r binary is located in an extra level - add_custom_command(TARGET PrusaSlicer POST_BUILD - COMMAND ln -sfn "${SLIC3R_RESOURCES_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/resources" - COMMENT "Symlinking the resources directory into the build tree" - VERBATIM - ) else () + if (APPLE) + # On OSX, the name of the binary matches the name of the Application. + add_custom_command(TARGET PrusaSlicer POST_BUILD + COMMAND ln -sf PrusaSlicer prusa-slicer + COMMAND ln -sf PrusaSlicer prusa-gcodeviewer + COMMAND ln -sf PrusaSlicer PrusaGCodeViewer + WORKING_DIRECTORY "$" + COMMENT "Symlinking the G-code viewer to PrusaSlicer, symlinking to prusa-slicer and prusa-gcodeviewer" + VERBATIM) + else () + add_custom_command(TARGET PrusaSlicer POST_BUILD + COMMAND ln -sf prusa-slicer prusa-gcodeviewer + WORKING_DIRECTORY "$" + COMMENT "Symlinking the G-code viewer to PrusaSlicer" + VERBATIM) + endif () + if (XCODE) + # Because of Debug/Release/etc. configurations (similar to MSVC) the slic3r binary is located in an extra level + set(BIN_RESOURCES_DIR "${CMAKE_CURRENT_BINARY_DIR}/resources") + else () + set(BIN_RESOURCES_DIR "${CMAKE_CURRENT_BINARY_DIR}/../resources") + endif () add_custom_command(TARGET PrusaSlicer POST_BUILD - COMMAND ln -sfn "${SLIC3R_RESOURCES_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/../resources" + COMMAND ln -sfn "${SLIC3R_RESOURCES_DIR}" "${BIN_RESOURCES_DIR}" COMMENT "Symlinking the resources directory into the build tree" - VERBATIM - ) -endif() + VERBATIM) +endif () # Slic3r binary install target if (WIN32) diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 2962f0cdfe..94996dc928 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -101,8 +102,14 @@ int CLI::run(int argc, char **argv) std::find(m_transforms.begin(), m_transforms.end(), "cut") == m_transforms.end() && std::find(m_transforms.begin(), m_transforms.end(), "cut_x") == m_transforms.end() && std::find(m_transforms.begin(), m_transforms.end(), "cut_y") == m_transforms.end(); - bool start_as_gcodeviewer = false; - + bool start_as_gcodeviewer = +#ifdef _WIN32 + false; +#else + // On Unix systems, the prusa-slicer binary may be symlinked to give the application a different meaning. + boost::algorithm::iends_with(boost::filesystem::path(argv[0]).filename().string(), "gcodeviewer"); +#endif // _WIN32 + const std::vector &load_configs = m_config.option("load", true)->values; // load config files supplied via --load diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 5681ed66db..1c30078102 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -195,6 +195,8 @@ set(SLIC3R_GUI_SOURCES Utils/Bonjour.hpp Utils/PresetUpdater.cpp Utils/PresetUpdater.hpp + Utils/Process.cpp + Utils/Process.hpp Utils/Profile.hpp Utils/UndoRedo.cpp Utils/UndoRedo.hpp diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index f6fd939e25..f4d7f03eca 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -9,7 +9,6 @@ #include //#include #include -#include #include #include @@ -31,6 +30,7 @@ #include "I18N.hpp" #include "GLCanvas3D.hpp" #include "Plater.hpp" +#include "../Utils/Process.hpp" #include #include "GUI_App.hpp" @@ -40,12 +40,6 @@ #include #endif // _WIN32 -// For starting another PrusaSlicer instance on OSX. -// Fails to compile on Windows on the build server. -#ifdef __APPLE__ - #include -#endif - namespace Slic3r { namespace GUI { @@ -1054,8 +1048,8 @@ void MainFrame::init_menubar() append_menu_item(fileMenu, wxID_ANY, _L("&Repair STL file") + dots, _L("Automatically repair an STL file"), [this](wxCommandEvent&) { repair_stl(); }, "wrench", nullptr, [this]() { return true; }, this); -#if ENABLE_GCODE_VIEWER fileMenu->AppendSeparator(); +#if ENABLE_GCODE_VIEWER append_menu_item(fileMenu, wxID_ANY, _L("&G-code preview"), _L("Switch to G-code preview mode"), [this](wxCommandEvent&) { if (m_plater->model().objects.empty() || @@ -1064,6 +1058,8 @@ void MainFrame::init_menubar() set_mode(EMode::GCodeViewer); }, "", nullptr); #endif // ENABLE_GCODE_VIEWER + append_menu_item(fileMenu, wxID_ANY, _L("&G-code preview") + dots, _L("Open G-code viewer"), + [this](wxCommandEvent&) { start_new_gcodeviewer_open_file(this); }, "", nullptr); fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_EXIT, _L("&Quit"), wxString::Format(_L("Quit %s"), SLIC3R_APP_NAME), [this](wxCommandEvent&) { Close(false); }); @@ -1180,20 +1176,11 @@ void MainFrame::init_menubar() windowMenu->AppendSeparator(); append_menu_item(windowMenu, wxID_ANY, _L("Print &Host Upload Queue") + "\tCtrl+J", _L("Display the Print Host Upload Queue window"), - [this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, "upload_queue", nullptr, - [this]() {return true; }, this); + [this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, "upload_queue", nullptr, [this]() {return true; }, this); windowMenu->AppendSeparator(); append_menu_item(windowMenu, wxID_ANY, _(L("Open new instance")) + "\tCtrl+I", _(L("Open a new PrusaSlicer instance")), - [this](wxCommandEvent&) { - wxString path = wxStandardPaths::Get().GetExecutablePath(); -#ifdef __APPLE__ - boost::process::spawn((const char*)path.c_str()); -#else - wxExecute(wxStandardPaths::Get().GetExecutablePath(), wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE | wxEXEC_MAKE_GROUP_LEADER); -#endif - }, "upload_queue", nullptr, - [this]() {return true; }, this); + [this](wxCommandEvent&) { start_new_slicer(); }, "", nullptr); } // View menu diff --git a/src/slic3r/Utils/Process.cpp b/src/slic3r/Utils/Process.cpp new file mode 100644 index 0000000000..2301cd2504 --- /dev/null +++ b/src/slic3r/Utils/Process.cpp @@ -0,0 +1,125 @@ +#include "Process.hpp" + +#include + +#include "../GUI/GUI.hpp" +// for file_wildcards() +#include "../GUI/GUI_App.hpp" +// localization +#include "../GUI/I18N.hpp" + +#include +#include + +#include +#include + +// For starting another PrusaSlicer instance on OSX. +// Fails to compile on Windows on the build server. +#ifdef __APPLE__ + #include +#endif + +#include + +namespace Slic3r { +namespace GUI { + +enum class NewSlicerInstanceType { + Slicer, + GCodeViewer +}; + +// Start a new Slicer process instance either in a Slicer mode or in a G-code mode. +// Optionally load a 3MF, STL or a G-code on start. +static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance_type, const wxString *path_to_open) +{ +#ifdef _WIN32 + wxString path; + wxFileName::SplitPath(wxStandardPaths::Get().GetExecutablePath(), &path, nullptr, nullptr, wxPATH_NATIVE); + path += "\\"; + path += (instance_type == NewSlicerInstanceType::Slicer) ? "prusa-slicer.exe" : "prusa-gcodeviewer.exe"; + std::vector args; + args.reserve(3); + args.emplace_back(path.wc_str()); + if (path_to_open != nullptr) + args.emplace_back(path_to_open->wc_str()); + args.emplace_back(nullptr); + BOOST_LOG_TRIVIAL(info) << "Trying to spawn a new slicer \"" << to_u8(path) << "\""; + if (wxExecute(const_cast(args.data()), wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE | wxEXEC_MAKE_GROUP_LEADER) <= 0) + BOOST_LOG_TRIVIAL(error) << "Failed to spawn a new slicer \"" << to_u8(path); +#else + // Own executable path. + boost::filesystem::path bin_path = into_path(wxStandardPaths::Get().GetExecutablePath()); + #if defined(__APPLE__) + { + bin_path = bin_path.parent_path() / ((instance_type == NewSlicerInstanceType::Slicer) ? "PrusaSlicer" : "PrusaGCodeViewer"); + // On Apple the wxExecute fails, thus we use boost::process instead. + BOOST_LOG_TRIVIAL(info) << "Trying to spawn a new slicer \"" << bin_path.string() << "\""; + try { + path_to_open ? boost::process::spawn(bin_path, into_u8(*path_to_open)) : boost::process::spawn(bin_path, boost::process::args()); + } catch (const std::exception &ex) { + BOOST_LOG_TRIVIAL(error) << "Failed to spawn a new slicer \"" << bin_path.string() << "\": " << ex.what(); + } + } + #else // Linux or Unix + { + std::vector args; + args.reserve(3); + #ifdef __linux + static const char *gcodeviewer_param = "--gcodeviewer"; + { + // If executed by an AppImage, start the AppImage, not the main process. + // see https://docs.appimage.org/packaging-guide/environment-variables.html#id2 + const char *appimage_binary = std::getenv("APPIMAGE"); + if (appimage_binary) { + args.emplace_back(appimage_binary); + if (instance_type == NewSlicerInstanceType::GCodeViewer) + args.emplace_back(gcodeviewer_param); + } + } + #endif // __linux + std::string my_path; + if (args.empty()) { + // Binary path was not set to the AppImage in the Linux specific block above, call the application directly. + my_path = (bin_path.parent_path() / ((instance_type == NewSlicerInstanceType::Slicer) ? "prusa-slicer" : "prusa-gcodeviewer")).string(); + args.emplace_back(my_path.c_str()); + } + std::string to_open; + if (path_to_open) { + to_open = into_u8(*path_to_open); + args.emplace_back(to_open.c_str()); + } + args.emplace_back(nullptr); + BOOST_LOG_TRIVIAL(info) << "Trying to spawn a new slicer \"" << args[0] << "\""; + if (wxExecute(const_cast(args.data()), wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE | wxEXEC_MAKE_GROUP_LEADER) <= 0) + BOOST_LOG_TRIVIAL(error) << "Failed to spawn a new slicer \"" << args[0]; + } + #endif // Linux or Unix +#endif // Win32 +} + +void start_new_slicer(const wxString *path_to_open) +{ + start_new_slicer_or_gcodeviewer(NewSlicerInstanceType::Slicer, path_to_open); +} + +void start_new_gcodeviewer(const wxString *path_to_open) +{ + start_new_slicer_or_gcodeviewer(NewSlicerInstanceType::GCodeViewer, path_to_open); +} + +void start_new_gcodeviewer_open_file(wxWindow *parent) +{ + wxFileDialog dialog(parent ? parent : wxGetApp().GetTopWindow(), + _L("Open G-code file:"), + from_u8(wxGetApp().app_config->get_last_dir()), wxString(), + file_wildcards(FT_GCODE), wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if (dialog.ShowModal() == wxID_OK) { + wxString path = dialog.GetPath(); + start_new_gcodeviewer(&path); + } +} + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/Utils/Process.hpp b/src/slic3r/Utils/Process.hpp new file mode 100644 index 0000000000..c6acaa643e --- /dev/null +++ b/src/slic3r/Utils/Process.hpp @@ -0,0 +1,19 @@ +#ifndef GUI_PROCESS_HPP +#define GUI_PROCESS_HPP + +class wxWindow; + +namespace Slic3r { +namespace GUI { + +// Start a new slicer instance, optionally with a file to open. +void start_new_slicer(const wxString *path_to_open = nullptr); +// Start a new G-code viewer instance, optionally with a file to open. +void start_new_gcodeviewer(const wxString *path_to_open = nullptr); +// Open a file dialog, ask the user to select a new G-code to open, start a new G-code viewer. +void start_new_gcodeviewer_open_file(wxWindow *parent = nullptr); + +} // namespace GUI +} // namespace Slic3r + +#endif // GUI_PROCESS_HPP diff --git a/src/slic3r/Utils/Thread.hpp b/src/slic3r/Utils/Thread.hpp index e9c76d2aba..194971c9eb 100644 --- a/src/slic3r/Utils/Thread.hpp +++ b/src/slic3r/Utils/Thread.hpp @@ -1,5 +1,5 @@ -#ifndef THREAD_HPP -#define THREAD_HPP +#ifndef GUI_THREAD_HPP +#define GUI_THREAD_HPP #include #include @@ -25,4 +25,4 @@ template inline boost::thread create_thread(Fn &&fn) } -#endif // THREAD_HPP +#endif // GUI_THREAD_HPP From 9ce3086f0209fdea07285635b50aceb79b49602a Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 8 Sep 2020 13:40:14 +0200 Subject: [PATCH 434/503] Splash screen : Try to fix scaling on Linux --- src/slic3r/GUI/GUI_App.cpp | 51 ++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index c16aeaada5..1e0d4ef859 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -78,23 +78,33 @@ namespace GUI { class MainFrame; // ysFIXME -static int get_dpi_for_main_display() +static float get_scale_for_main_display() { wxFrame fr(nullptr, wxID_ANY, wxEmptyString); - return get_dpi_for_window(&fr); + +#if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT && !defined(__WXGTK__) + int dpi = get_dpi_for_window(&fr); + float sf = dpi != DPI_DEFAULT ? sf = (float)dpi / DPI_DEFAULT : 1.0; +#else + printf("dpi = %d\n", get_dpi_for_window(&fr)); + // initialize default width_unit according to the width of the one symbol ("m") of the currently active font of this window. + float sf = 0.1 * std::max(10, fr.GetTextExtent("m").x - 1); +#endif // ENABLE_WX_3_1_3_DPI_CHANGED_EVENT + + printf("scale factor = %f\n", sf); + return sf; } // scale input bitmap and return scale factor static float scale_bitmap(wxBitmap& bmp) { - int dpi = get_dpi_for_main_display(); - float sf = 1.0; + float sf = get_scale_for_main_display(); + // scale bitmap if needed - if (dpi != DPI_DEFAULT) { + if (sf > 1.0) { wxImage image = bmp.ConvertToImage(); if (image.IsOk() && image.GetWidth() != 0 && image.GetHeight() != 0) { - sf = (float)dpi / DPI_DEFAULT; int width = int(sf * image.GetWidth()); int height = int(sf * image.GetHeight()); image.Rescale(width, height, wxIMAGE_QUALITY_BILINEAR); @@ -102,11 +112,13 @@ static float scale_bitmap(wxBitmap& bmp) bmp = wxBitmap(std::move(image)); } } + return sf; } -static void word_wrap_string(wxString& input, int line_len) +static void word_wrap_string(wxString& input, int line_px_len, float scalef) { + int line_len = std::roundf( (float)line_px_len / (scalef * 10)) + 10; int idx = -1; int cur_len = 0; for (size_t i = 0; i < input.Len(); i++) @@ -136,11 +148,12 @@ static void DecorateSplashScreen(wxBitmap& bmp) wxMemoryDC memDc(bmp); // draw an dark grey box at the left of the splashscreen. - // this box will be 2/9 of the weight of the bitmap, and be at the left. - const wxRect bannerRect(wxPoint(0, (bmp.GetHeight() / 9) * 2 - 2), wxPoint((bmp.GetWidth() / 5) * 2, bmp.GetHeight())); + // this box will be 2/5 of the weight of the bitmap, and be at the left. + int banner_width = (bmp.GetWidth() / 5) * 2 - 2; + const wxRect banner_rect(wxPoint(0, (bmp.GetHeight() / 9) * 2), wxPoint(banner_width, bmp.GetHeight())); wxDCBrushChanger bc(memDc, wxBrush(wxColour(51, 51, 51))); wxDCPenChanger pc(memDc, wxPen(wxColour(51, 51, 51))); - memDc.DrawRectangle(bannerRect); + memDc.DrawRectangle(banner_rect); // title wxString title_string = SLIC3R_APP_NAME; @@ -156,14 +169,15 @@ static void DecorateSplashScreen(wxBitmap& bmp) wxString cr_symbol = wxString::FromUTF8("\xc2\xa9"); wxString copyright_string = wxString::Format("%s 2016-%s Prusa Research.\n" "%s 2011-2018 Alessandro Ranellucci.", - cr_symbol, year.Mid(year.Length() - 4), cr_symbol); + cr_symbol, year.Mid(year.Length() - 4), cr_symbol) + "\n\n"; wxFont copyright_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Larger(); - copyright_string += "Slic3r" + _L("is licensed under the") + _L("GNU Affero General Public License, version 3") + "\n\n" + + copyright_string += //"Slic3r" + _L("is licensed under the") + _L("GNU Affero General Public License, version 3") + "\n\n" + _L("PrusaSlicer is based on Slic3r by Alessandro Ranellucci and the RepRap community.") + "\n\n" + _L("Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and numerous others."); - word_wrap_string(copyright_string, 50); +// word_wrap_string(copyright_string, 50); + word_wrap_string(copyright_string, banner_width, scale_factor); wxCoord margin = int(scale_factor * 20); @@ -171,13 +185,13 @@ static void DecorateSplashScreen(wxBitmap& bmp) memDc.SetTextForeground(wxColour(237, 107, 33)); memDc.SetFont(title_font); - memDc.DrawLabel(title_string, bannerRect.Deflate(margin, 0), wxALIGN_TOP | wxALIGN_LEFT); + memDc.DrawLabel(title_string, banner_rect.Deflate(margin, 0), wxALIGN_TOP | wxALIGN_LEFT); memDc.SetFont(version_font); - memDc.DrawLabel(version_string, bannerRect.Deflate(margin, 2 * margin), wxALIGN_TOP | wxALIGN_LEFT); + memDc.DrawLabel(version_string, banner_rect.Deflate(margin, 2 * margin), wxALIGN_TOP | wxALIGN_LEFT); memDc.SetFont(copyright_font); - memDc.DrawLabel(copyright_string, bannerRect.Deflate(margin, 2 * margin), wxALIGN_BOTTOM | wxALIGN_LEFT); + memDc.DrawLabel(copyright_string, banner_rect.Deflate(margin, 2 * margin), wxALIGN_BOTTOM | wxALIGN_LEFT); } class SplashScreen : public wxSplashScreen @@ -189,9 +203,10 @@ public: wxASSERT(bitmap.IsOk()); m_main_bitmap = bitmap; - int dpi = get_dpi_for_main_display(); +/* int dpi = get_dpi_for_main_display(); if (dpi != DPI_DEFAULT) - m_scale_factor = (float)dpi / DPI_DEFAULT; + m_scale_factor = (float)dpi / DPI_DEFAULT; */ + m_scale_factor = get_scale_for_main_display(); } void SetText(const wxString& text) From 844f62af66dd93a342827879fda6cb036003025a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 8 Sep 2020 14:01:32 +0200 Subject: [PATCH 435/503] Cleanup toolpaths when changing printer to SLA --- src/slic3r/GUI/Plater.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 45a1f6ea82..3a4c31ebd7 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2811,7 +2811,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool if (this->preview != nullptr) { // If the preview is not visible, the following line just invalidates the preview, // but the G-code paths or SLA preview are calculated first once the preview is made visible. - this->preview->get_canvas3d()->reset_gcode_toolpaths(); + reset_gcode_toolpaths(); this->preview->reload_print(); } #else @@ -5384,6 +5384,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config) this->set_printer_technology(config.opt_enum(opt_key)); // print technology is changed, so we should to update a search list p->sidebar->update_searcher(); + p->reset_gcode_toolpaths(); } else if ((opt_key == "bed_shape") || (opt_key == "bed_custom_texture") || (opt_key == "bed_custom_model")) { bed_shape_changed = true; From ceaa61071a1b5315caa6b9b1677b7c13f50d6aea Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 8 Sep 2020 14:07:47 +0200 Subject: [PATCH 436/503] Fix of the previous merge, Windows specific. --- src/slic3r/Utils/Process.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/Utils/Process.cpp b/src/slic3r/Utils/Process.cpp index 96f5062070..b81a748277 100644 --- a/src/slic3r/Utils/Process.cpp +++ b/src/slic3r/Utils/Process.cpp @@ -49,9 +49,9 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance if (path_to_open != nullptr) args.emplace_back(path_to_open->wc_str()); args.emplace_back(nullptr); - BOOST_LOG_TRIVIAL(info) << "Trying to spawn a new slicer \"" << to_u8(path) << "\""; + BOOST_LOG_TRIVIAL(info) << "Trying to spawn a new slicer \"" << into_u8(path) << "\""; if (wxExecute(const_cast(args.data()), wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE | wxEXEC_MAKE_GROUP_LEADER) <= 0) - BOOST_LOG_TRIVIAL(error) << "Failed to spawn a new slicer \"" << to_u8(path); + BOOST_LOG_TRIVIAL(error) << "Failed to spawn a new slicer \"" << into_u8(path); #else // Own executable path. boost::filesystem::path bin_path = into_path(wxStandardPaths::Get().GetExecutablePath()); From 0a4debc98c0499a2aa8cfa1940bc042c482fa323 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 8 Sep 2020 14:25:10 +0200 Subject: [PATCH 437/503] Fix of a preceding merge --- src/slic3r/Utils/Process.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/slic3r/Utils/Process.cpp b/src/slic3r/Utils/Process.cpp index b81a748277..561971b139 100644 --- a/src/slic3r/Utils/Process.cpp +++ b/src/slic3r/Utils/Process.cpp @@ -18,10 +18,7 @@ // Fails to compile on Windows on the build server. #ifdef __APPLE__ #include -<<<<<<< HEAD -======= #include ->>>>>>> vb_gcodeviewer_menu #endif #include From 07499ff9d099a2f2a3b2da5e6dfc8e7dba6f8f05 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 8 Sep 2020 15:15:00 +0200 Subject: [PATCH 438/503] Fixed Scale on Linux --- src/slic3r/GUI/GUI_App.cpp | 13 +++++++------ src/slic3r/GUI/GUI_Utils.hpp | 7 +++++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 1e0d4ef859..7b6d8c5aa8 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -77,9 +77,11 @@ namespace GUI { class MainFrame; -// ysFIXME static float get_scale_for_main_display() { + // ysFIXME : Workaround : + // wxFrame is created on the main monitor, so we can take a scale factor from this one + // before The Application and the Mainframe are created wxFrame fr(nullptr, wxID_ANY, wxEmptyString); #if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT && !defined(__WXGTK__) @@ -118,7 +120,9 @@ static float scale_bitmap(wxBitmap& bmp) static void word_wrap_string(wxString& input, int line_px_len, float scalef) { + // calculate count od symbols in one line according to the scale int line_len = std::roundf( (float)line_px_len / (scalef * 10)) + 10; + int idx = -1; int cur_len = 0; for (size_t i = 0; i < input.Len(); i++) @@ -176,7 +180,6 @@ static void DecorateSplashScreen(wxBitmap& bmp) _L("PrusaSlicer is based on Slic3r by Alessandro Ranellucci and the RepRap community.") + "\n\n" + _L("Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and numerous others."); -// word_wrap_string(copyright_string, 50); word_wrap_string(copyright_string, banner_width, scale_factor); wxCoord margin = int(scale_factor * 20); @@ -198,14 +201,12 @@ class SplashScreen : public wxSplashScreen { public: SplashScreen(const wxBitmap& bitmap, long splashStyle, int milliseconds, wxWindow* parent) - : wxSplashScreen(bitmap, splashStyle, milliseconds, parent, wxID_ANY) + : wxSplashScreen(bitmap, splashStyle, milliseconds, parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, + wxSIMPLE_BORDER | wxFRAME_NO_TASKBAR) { wxASSERT(bitmap.IsOk()); m_main_bitmap = bitmap; -/* int dpi = get_dpi_for_main_display(); - if (dpi != DPI_DEFAULT) - m_scale_factor = (float)dpi / DPI_DEFAULT; */ m_scale_factor = get_scale_for_main_display(); } diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp index f29e0cd848..1c88de5707 100644 --- a/src/slic3r/GUI/GUI_Utils.hpp +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -92,10 +92,13 @@ public: #ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList this->SetFont(m_normal_font); #endif - // initialize default width_unit according to the width of the one symbol ("m") of the currently active font of this window. -#if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT + + // Linux specific issue : get_dpi_for_window(this) still doesn't responce to the Display's scale in new wxWidgets(3.1.3). + // So, calculate the m_em_unit value from the font size, as before +#if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT && !defined(__WXGTK__) m_em_unit = std::max(10, 10.0f * m_scale_factor); #else + // initialize default width_unit according to the width of the one symbol ("m") of the currently active font of this window. m_em_unit = std::max(10, this->GetTextExtent("m").x - 1); #endif // ENABLE_WX_3_1_3_DPI_CHANGED_EVENT From a13b732f278ad5a7afda879a5ad617648d1a85de Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 8 Sep 2020 15:30:01 +0200 Subject: [PATCH 439/503] Fixed loading current presets --- src/slic3r/GUI/GUI_App.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 65c0bd878d..612b44d5b0 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -692,9 +692,9 @@ bool GUI_App::on_init_inner() // ensure the selected technology is ptFFF plater_->set_printer_technology(ptFFF); } -#else - load_current_presets(); + else #endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + load_current_presets(); mainframe->Show(true); /* Temporary workaround for the correct behavior of the Scrolled sidebar panel: From ce06fc6cb7bc09df8fcc18b006b780f9f16da9d0 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 8 Sep 2020 15:30:59 +0200 Subject: [PATCH 440/503] Added networking support for SL1 Digest authorization. Renamed login/password/authorization_type to printhost_user/printhost_password/printhost_authorization_type. Added initialization of physical printer preset with default values. --- src/libslic3r/Preset.cpp | 34 +++++++++++++++--------- src/libslic3r/Preset.hpp | 17 ++++++------ src/libslic3r/PrintConfig.cpp | 8 +++--- src/slic3r/GUI/Field.cpp | 2 +- src/slic3r/GUI/GUI.cpp | 2 +- src/slic3r/GUI/OptionsGroup.cpp | 2 +- src/slic3r/GUI/PhysicalPrinterDialog.cpp | 24 +++++++++-------- src/slic3r/Utils/Http.cpp | 10 +++++++ src/slic3r/Utils/Http.hpp | 2 ++ src/slic3r/Utils/OctoPrint.cpp | 23 ++++++++++++++++ src/slic3r/Utils/OctoPrint.hpp | 15 +++++++++-- 11 files changed, 98 insertions(+), 41 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 7aaa96c8cf..284c37435a 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1365,9 +1365,10 @@ const std::vector& PhysicalPrinter::printer_options() "print_host", "printhost_apikey", "printhost_cafile", - "authorization_type", - "login", - "password" + "printhost_authorization_type", + // HTTP digest authentization (RFC 2617) + "printhost_user", + "printhost_password" }; } return s_opts; @@ -1412,11 +1413,11 @@ const std::set& PhysicalPrinter::get_preset_names() const bool PhysicalPrinter::has_empty_config() const { - return config.opt_string("print_host" ).empty() && - config.opt_string("printhost_apikey").empty() && - config.opt_string("printhost_cafile").empty() && - config.opt_string("login" ).empty() && - config.opt_string("password" ).empty(); + return config.opt_string("print_host" ).empty() && + config.opt_string("printhost_apikey" ).empty() && + config.opt_string("printhost_cafile" ).empty() && + config.opt_string("printhost_user" ).empty() && + config.opt_string("printhost_password").empty(); } void PhysicalPrinter::update_preset_names_in_config() @@ -1441,7 +1442,7 @@ void PhysicalPrinter::save(const std::string& file_name_from, const std::string& void PhysicalPrinter::update_from_preset(const Preset& preset) { - config.apply_only(preset.config, printer_options(), false); + config.apply_only(preset.config, printer_options(), true); // add preset names to the options list auto ret = preset_names.emplace(preset.name); update_preset_names_in_config(); @@ -1476,8 +1477,8 @@ bool PhysicalPrinter::delete_preset(const std::string& preset_name) return preset_names.erase(preset_name) > 0; } -PhysicalPrinter::PhysicalPrinter(const std::string& name, const Preset& preset) : - name(name) +PhysicalPrinter::PhysicalPrinter(const std::string& name, const DynamicPrintConfig &default_config, const Preset& preset) : + name(name), config(default_config) { update_from_preset(preset); } @@ -1514,6 +1515,13 @@ std::string PhysicalPrinter::get_preset_name(std::string name) PhysicalPrinterCollection::PhysicalPrinterCollection( const std::vector& keys) { + // Default config for a physical printer containing all key/value pairs of PhysicalPrinter::printer_options(). + for (const std::string &key : keys) { + const ConfigOptionDef *opt = print_config_def.get(key); + assert(opt); + assert(opt->default_value); + m_default_config.set_key_value(key, opt->default_value->clone()); + } } // Load all printers found in dir_path. @@ -1539,7 +1547,7 @@ void PhysicalPrinterCollection::load_printers(const std::string& dir_path, const continue; } try { - PhysicalPrinter printer(name); + PhysicalPrinter printer(name, this->default_config()); printer.file = dir_entry.path().string(); // Load the preset file, apply preset values on top of defaults. try { @@ -1590,7 +1598,7 @@ void PhysicalPrinterCollection::load_printers_from_presets(PrinterPresetCollecti new_printer_name = (boost::format("Printer %1%") % ++cnt).str(); // create new printer from this preset - PhysicalPrinter printer(new_printer_name, preset); + PhysicalPrinter printer(new_printer_name, this->default_config(), preset); printer.loaded = true; save_printer(printer); } diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index 30edfc859a..ec11d59fae 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -460,8 +460,7 @@ private: // If a preset does not exist, an iterator is returned indicating where to insert a preset with the same name. std::deque::iterator find_preset_internal(const std::string &name) { - Preset key(m_type, name); - auto it = std::lower_bound(m_presets.begin() + m_num_default_presets, m_presets.end(), key); + auto it = Slic3r::lower_bound_by_predicate(m_presets.begin() + m_num_default_presets, m_presets.end(), [&name](const auto& l) { return l.name < name; }); if (it == m_presets.end() || it->name != name) { // Preset has not been not found in the sorted list of non-default presets. Try the defaults. for (size_t i = 0; i < m_num_default_presets; ++ i) @@ -539,9 +538,8 @@ namespace PresetUtils { class PhysicalPrinter { public: - PhysicalPrinter() {} - PhysicalPrinter(const std::string& name) : name(name){} - PhysicalPrinter(const std::string& name, const Preset& preset); + PhysicalPrinter(const std::string& name, const DynamicPrintConfig &default_config) : name(name), config(default_config) {} + PhysicalPrinter(const std::string& name, const DynamicPrintConfig &default_config, const Preset& preset); void set_name(const std::string &name); // Name of the Physical Printer, usually derived form the file name. @@ -698,6 +696,8 @@ public: // Generate a file path from a profile name. Add the ".ini" suffix if it is missing. std::string path_from_name(const std::string& new_name) const; + const DynamicPrintConfig& default_config() const { return m_default_config; } + private: PhysicalPrinterCollection& operator=(const PhysicalPrinterCollection& other); @@ -707,9 +707,7 @@ private: // If a preset does not exist, an iterator is returned indicating where to insert a preset with the same name. std::deque::iterator find_printer_internal(const std::string& name) { - PhysicalPrinter printer(name); - auto it = std::lower_bound(m_printers.begin(), m_printers.end(), printer); - return it; + return Slic3r::lower_bound_by_predicate(m_printers.begin(), m_printers.end(), [&name](const auto& l) { return l.name < name; }); } std::deque::const_iterator find_printer_internal(const std::string& name) const { @@ -723,6 +721,9 @@ private: // so that the addresses of the presets don't change during resizing of the container. std::deque m_printers; + // Default config for a physical printer containing all key/value pairs of PhysicalPrinter::printer_options(). + DynamicPrintConfig m_default_config; + // Selected printer. size_t m_idx_selected = size_t(-1); // The name of the preset which is currently select for this printer diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 770983ad59..239969a1d2 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -133,13 +133,13 @@ void PrintConfigDef::init_common_params() // Options used by physical printers - def = this->add("login", coString); - def->label = L("Login"); + def = this->add("printhost_user", coString); + def->label = L("User"); // def->tooltip = L(""); def->mode = comAdvanced; def->set_default_value(new ConfigOptionString("")); - def = this->add("password", coString); + def = this->add("printhost_password", coString); def->label = L("Password"); // def->tooltip = L(""); def->mode = comAdvanced; @@ -151,7 +151,7 @@ void PrintConfigDef::init_common_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionString("")); - def = this->add("authorization_type", coEnum); + def = this->add("printhost_authorization_type", coEnum); def->label = L("Authorization Type"); // def->tooltip = L(""); def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 9cb3d726de..a21826205b 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -1080,7 +1080,7 @@ boost::any& Choice::get_value() m_value = static_cast(ret_enum); else if (m_opt_id.compare("support_pillar_connection_mode") == 0) m_value = static_cast(ret_enum); - else if (m_opt_id == "authorization_type") + else if (m_opt_id == "printhost_authorization_type") m_value = static_cast(ret_enum); } else if (m_opt.gui_type == "f_enum_open") { diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 913716dfd7..6c76b62272 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -194,7 +194,7 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); else if(opt_key.compare("support_pillar_connection_mode") == 0) config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); - else if(opt_key == "authorization_type") + else if(opt_key == "printhost_authorization_type") config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); } break; diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index cc10d815fc..dd00b3d688 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -755,7 +755,7 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config else if (opt_key == "support_pillar_connection_mode") { ret = static_cast(config.option>(opt_key)->value); } - else if (opt_key == "authorization_type") { + else if (opt_key == "printhost_authorization_type") { ret = static_cast(config.option>(opt_key)->value); } } diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.cpp b/src/slic3r/GUI/PhysicalPrinterDialog.cpp index 12d1cd2871..ca2d625561 100644 --- a/src/slic3r/GUI/PhysicalPrinterDialog.cpp +++ b/src/slic3r/GUI/PhysicalPrinterDialog.cpp @@ -155,8 +155,9 @@ void PresetForPrinter::msw_rescale() // PhysicalPrinterDialog //------------------------------------------ -PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) - : DPIDialog(NULL, wxID_ANY, _L("Physical Printer"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), -1), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) : + DPIDialog(NULL, wxID_ANY, _L("Physical Printer"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), -1), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), + m_printer("", wxGetApp().preset_bundle->physical_printers.default_config()) { SetFont(wxGetApp().normal_font()); SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); @@ -186,7 +187,8 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) PhysicalPrinter* printer = printers.find_printer(into_u8(printer_name)); if (!printer) { const Preset& preset = wxGetApp().preset_bundle->printers.get_edited_preset(); - printer = new PhysicalPrinter(into_u8(printer_name), preset); + //FIXME Vojtech: WTF??? Memory leak? + printer = new PhysicalPrinter(into_u8(printer_name), m_printer.config, preset); // if printer_name is empty it means that new printer is created, so enable all items in the preset list m_presets.emplace_back(new PresetForPrinter(this, preset.name)); } @@ -249,7 +251,7 @@ PhysicalPrinterDialog::~PhysicalPrinterDialog() void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgroup) { m_optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { - if (opt_key == "authorization_type") + if (opt_key == "printhost_authorization_type") this->update(); }; @@ -308,7 +310,7 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr host_line.append_widget(print_host_test); m_optgroup->append_line(host_line); - m_optgroup->append_single_option_line("authorization_type"); + m_optgroup->append_single_option_line("printhost_authorization_type"); option = m_optgroup->get_option("printhost_apikey"); option.opt.width = Field::def_width_wider(); @@ -368,7 +370,7 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr m_optgroup->append_line(line); } - for (const std::string& opt_key : std::vector{ "login", "password" }) { + for (const std::string& opt_key : std::vector{ "printhost_user", "printhost_password" }) { option = m_optgroup->get_option(opt_key); option.opt.width = Field::def_width_wider(); m_optgroup->append_single_option_line(option); @@ -385,20 +387,20 @@ void PhysicalPrinterDialog::update() // Only offer the host type selection for FFF, for SLA it's always the SL1 printer (at the moment) if (tech == ptFFF) { m_optgroup->show_field("host_type"); - m_optgroup->hide_field("authorization_type"); - for (const std::string& opt_key : std::vector{ "login", "password" }) + m_optgroup->hide_field("printhost_authorization_type"); + for (const std::string& opt_key : std::vector{ "printhost_user", "printhost_password" }) m_optgroup->hide_field(opt_key); } else { m_optgroup->set_value("host_type", int(PrintHostType::htOctoPrint), false); m_optgroup->hide_field("host_type"); - m_optgroup->show_field("authorization_type"); + m_optgroup->show_field("printhost_authorization_type"); - AuthorizationType auth_type = m_config->option>("authorization_type")->value; + AuthorizationType auth_type = m_config->option>("printhost_authorization_type")->value; m_optgroup->show_field("printhost_apikey", auth_type == AuthorizationType::atKeyPassword); - for (const std::string& opt_key : std::vector{ "login", "password" }) + for (const std::string& opt_key : std::vector{ "printhost_user", "printhost_password" }) m_optgroup->show_field(opt_key, auth_type == AuthorizationType::atUserPassword); } diff --git a/src/slic3r/Utils/Http.cpp b/src/slic3r/Utils/Http.cpp index a16aac5b5b..e55c21fe17 100644 --- a/src/slic3r/Utils/Http.cpp +++ b/src/slic3r/Utils/Http.cpp @@ -415,6 +415,16 @@ Http& Http::remove_header(std::string name) return *this; } +// Authorization by HTTP digest, based on RFC2617. +Http& Http::auth_digest(const std::string &user, const std::string &password) +{ + curl_easy_setopt(p->curl, CURLOPT_USERNAME, user.c_str()); + curl_easy_setopt(p->curl, CURLOPT_PASSWORD, password.c_str()); + curl_easy_setopt(p->curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); + + return *this; +} + Http& Http::ca_file(const std::string &name) { if (p && priv::ca_file_supported(p->curl)) { diff --git a/src/slic3r/Utils/Http.hpp b/src/slic3r/Utils/Http.hpp index f162362796..ae3660fbfe 100644 --- a/src/slic3r/Utils/Http.hpp +++ b/src/slic3r/Utils/Http.hpp @@ -64,6 +64,8 @@ public: Http& header(std::string name, const std::string &value); // Removes a header field. Http& remove_header(std::string name); + // Authorization by HTTP digest, based on RFC2617. + Http& auth_digest(const std::string &user, const std::string &password); // Sets a CA certificate file for usage with HTTPS. This is only supported on some backends, // specifically, this is supported with OpenSSL and NOT supported with Windows and OS X native certificate store. // See also ca_file_supported(). diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index ee87f822fa..82d8a6bb63 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -170,6 +170,13 @@ std::string OctoPrint::make_url(const std::string &path) const } } +SL1Host::SL1Host(DynamicPrintConfig *config) : + OctoPrint(config), + authorization_type(dynamic_cast*>(config->option("printhost_authorization_type"))->value), + username(config->opt_string("printhost_user")), + password(config->opt_string("printhost_password")) +{ +} // SL1Host const char* SL1Host::get_name() const { return "SL1Host"; } @@ -191,4 +198,20 @@ bool SL1Host::validate_version_text(const boost::optional &version_ return version_text ? boost::starts_with(*version_text, "Prusa SLA") : false; } +void SL1Host::set_auth(Http &http) const +{ + switch (authorization_type) { + case atKeyPassword: + http.header("X-Api-Key", get_apikey()); + break; + case atUserPassword: + http.auth_digest(username, password); + break; + } + + if (! get_cafile().empty()) { + http.ca_file(get_cafile()); + } +} + } diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index 965019d859..4f8e4819fc 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -29,6 +29,8 @@ public: bool can_test() const override { return true; } bool can_start_print() const override { return true; } std::string get_host() const override { return host; } + const std::string& get_apikey() const { return apikey; } + const std::string& get_cafile() const { return cafile; } protected: virtual bool validate_version_text(const boost::optional &version_text) const; @@ -38,14 +40,14 @@ private: std::string apikey; std::string cafile; - void set_auth(Http &http) const; + virtual void set_auth(Http &http) const; std::string make_url(const std::string &path) const; }; class SL1Host: public OctoPrint { public: - SL1Host(DynamicPrintConfig *config) : OctoPrint(config) {} + SL1Host(DynamicPrintConfig *config); ~SL1Host() override = default; const char* get_name() const override; @@ -56,6 +58,15 @@ public: protected: bool validate_version_text(const boost::optional &version_text) const override; + +private: + void set_auth(Http &http) const override; + + // Host authorization type. + AuthorizationType authorization_type; + // username and password for HTTP Digest Authentization (RFC RFC2617) + std::string username; + std::string password; }; } From 3c14d883a1b9a73ae04324a1dc062791e5721a2b Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 8 Sep 2020 16:11:01 +0200 Subject: [PATCH 441/503] PhysicalPrinterDialog: Fixed memory leak --- src/slic3r/GUI/PhysicalPrinterDialog.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.cpp b/src/slic3r/GUI/PhysicalPrinterDialog.cpp index ca2d625561..3d832ae569 100644 --- a/src/slic3r/GUI/PhysicalPrinterDialog.cpp +++ b/src/slic3r/GUI/PhysicalPrinterDialog.cpp @@ -187,8 +187,7 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) : PhysicalPrinter* printer = printers.find_printer(into_u8(printer_name)); if (!printer) { const Preset& preset = wxGetApp().preset_bundle->printers.get_edited_preset(); - //FIXME Vojtech: WTF??? Memory leak? - printer = new PhysicalPrinter(into_u8(printer_name), m_printer.config, preset); + m_printer = PhysicalPrinter(into_u8(printer_name), m_printer.config, preset); // if printer_name is empty it means that new printer is created, so enable all items in the preset list m_presets.emplace_back(new PresetForPrinter(this, preset.name)); } @@ -197,9 +196,8 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) : const std::set& preset_names = printer->get_preset_names(); for (const std::string& preset_name : preset_names) m_presets.emplace_back(new PresetForPrinter(this, preset_name)); + m_printer = *printer; } - assert(printer); - m_printer = *printer; if (m_presets.size() == 1) m_presets.front()->SuppressDelete(); From 6b10214bec25cbe8800bcd4f12f477a9eaf852e9 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 9 Sep 2020 09:06:50 +0200 Subject: [PATCH 442/503] Fixed export of pause print lines into gcode --- src/libslic3r/GCode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 0ee9ec0143..cfde3a03a7 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1939,8 +1939,8 @@ namespace ProcessLayer #if !ENABLE_GCODE_VIEWER // add tag for time estimator gcode += "; " + GCodeTimeEstimator::Pause_Print_Tag + "\n"; - gcode += config.pause_print_gcode; #endif // !ENABLE_GCODE_VIEWER + gcode += config.pause_print_gcode; } else { From e010d287c5c47bbb7faa018595f152b64e5d64c3 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 9 Sep 2020 11:41:23 +0200 Subject: [PATCH 443/503] Fixed launching of new slicer instances on Windows. --- src/slic3r/Utils/Process.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/slic3r/Utils/Process.cpp b/src/slic3r/Utils/Process.cpp index 561971b139..375c10a6a5 100644 --- a/src/slic3r/Utils/Process.cpp +++ b/src/slic3r/Utils/Process.cpp @@ -47,7 +47,9 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance args.emplace_back(path_to_open->wc_str()); args.emplace_back(nullptr); BOOST_LOG_TRIVIAL(info) << "Trying to spawn a new slicer \"" << into_u8(path) << "\""; - if (wxExecute(const_cast(args.data()), wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE | wxEXEC_MAKE_GROUP_LEADER) <= 0) + // Don't call with wxEXEC_HIDE_CONSOLE, PrusaSlicer in GUI mode would just show the splash screen. It would not open the main window though, it would + // just hang in the background. + if (wxExecute(const_cast(args.data()), wxEXEC_ASYNC) <= 0) BOOST_LOG_TRIVIAL(error) << "Failed to spawn a new slicer \"" << into_u8(path); #else // Own executable path. @@ -96,7 +98,7 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance } args.emplace_back(nullptr); BOOST_LOG_TRIVIAL(info) << "Trying to spawn a new slicer \"" << args[0] << "\""; - if (wxExecute(const_cast(args.data()), wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE | wxEXEC_MAKE_GROUP_LEADER) <= 0) + if (wxExecute(const_cast(args.data()), wxEXEC_ASYNC | wxEXEC_MAKE_GROUP_LEADER) <= 0) BOOST_LOG_TRIVIAL(error) << "Failed to spawn a new slicer \"" << args[0]; } #endif // Linux or Unix From 0d26df3cf6a2a75973a0c6df235089e12f1195ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Wed, 26 Aug 2020 16:51:34 +0200 Subject: [PATCH 444/503] Preparation for new infill --- src/libslic3r/CMakeLists.txt | 2 ++ src/libslic3r/Fill/Fill.cpp | 1 + src/libslic3r/Fill/FillAdaptive.cpp | 19 +++++++++++ src/libslic3r/Fill/FillAdaptive.hpp | 53 +++++++++++++++++++++++++++++ src/libslic3r/Fill/FillBase.cpp | 2 ++ src/libslic3r/Fill/FillBase.hpp | 6 ++++ src/libslic3r/Print.hpp | 8 +++++ src/libslic3r/PrintConfig.cpp | 2 ++ src/libslic3r/PrintConfig.hpp | 3 +- 9 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 src/libslic3r/Fill/FillAdaptive.cpp create mode 100644 src/libslic3r/Fill/FillAdaptive.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 3d241dd371..09f75c747c 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -46,6 +46,8 @@ add_library(libslic3r STATIC Fill/Fill.hpp Fill/Fill3DHoneycomb.cpp Fill/Fill3DHoneycomb.hpp + Fill/FillAdaptive.cpp + Fill/FillAdaptive.hpp Fill/FillBase.cpp Fill/FillBase.hpp Fill/FillConcentric.cpp diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 3c16527f07..c948df400e 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -345,6 +345,7 @@ void Layer::make_fills() f->layer_id = this->id(); f->z = this->print_z; f->angle = surface_fill.params.angle; + f->adapt_fill_octree = this->object()->adaptiveInfillOctree(); // calculate flow spacing for infill pattern generation bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.flow.bridge; diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp new file mode 100644 index 0000000000..cb11385983 --- /dev/null +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -0,0 +1,19 @@ +#include "../ClipperUtils.hpp" +#include "../ExPolygon.hpp" +#include "../Surface.hpp" + +#include "FillAdaptive.hpp" + +namespace Slic3r { + +void FillAdaptive::_fill_surface_single( + const FillParams ¶ms, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon &expolygon, + Polylines &polylines_out) +{ + +} + +} // namespace Slic3r diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp new file mode 100644 index 0000000000..e0a97a1b9b --- /dev/null +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -0,0 +1,53 @@ +#ifndef slic3r_FillAdaptive_hpp_ +#define slic3r_FillAdaptive_hpp_ + +#include "FillBase.hpp" + +namespace Slic3r { + +namespace FillAdaptive_Internal +{ + struct CubeProperties + { + double edge_length; // Lenght of edge of a cube + double height; // Height of rotated cube (standing on the corner) + double diagonal_length; // Length of diagonal of a cube a face + double line_z_distance; // Defines maximal distance from a center of a cube on Z axis on which lines will be created + double line_xy_distance;// Defines maximal distance from a center of a cube on X and Y axis on which lines will be created + }; + + struct Cube + { + Vec3d center; + size_t depth; + CubeProperties properties; + std::vector children; + }; + + struct Octree + { + Cube *root_cube; + Vec3d origin; + }; +}; // namespace FillAdaptive_Internal + +class FillAdaptive : public Fill +{ +public: + virtual ~FillAdaptive() {} + +protected: + virtual Fill* clone() const { return new FillAdaptive(*this); }; + virtual void _fill_surface_single( + const FillParams ¶ms, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon &expolygon, + Polylines &polylines_out); + + virtual bool no_sort() const { return true; } +}; + +} // namespace Slic3r + +#endif // slic3r_FillAdaptive_hpp_ diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index c760218c01..c1f38dad5c 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -16,6 +16,7 @@ #include "FillRectilinear.hpp" #include "FillRectilinear2.hpp" #include "FillRectilinear3.hpp" +#include "FillAdaptive.hpp" namespace Slic3r { @@ -37,6 +38,7 @@ Fill* Fill::new_from_type(const InfillPattern type) case ipArchimedeanChords: return new FillArchimedeanChords(); case ipHilbertCurve: return new FillHilbertCurve(); case ipOctagramSpiral: return new FillOctagramSpiral(); + case ipAdaptiveCubic: return new FillAdaptive(); default: throw std::invalid_argument("unknown type"); } } diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index 2e9b647354..9f70b69e08 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -19,6 +19,10 @@ class ExPolygon; class Surface; enum InfillPattern : int; +namespace FillAdaptive_Internal { + struct Octree; +}; + class InfillFailedException : public std::runtime_error { public: InfillFailedException() : std::runtime_error("Infill failed") {} @@ -69,6 +73,8 @@ public: // In scaled coordinates. Bounding box of the 2D projection of the object. BoundingBox bounding_box; + FillAdaptive_Internal::Octree* adapt_fill_octree = nullptr; + public: virtual ~Fill() {} diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index d436f90bf5..02dd6df356 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -30,6 +30,10 @@ enum class SlicingMode : uint32_t; class Layer; class SupportLayer; +namespace FillAdaptive_Internal { + struct Octree; +}; + // Print step IDs for keeping track of the print state. enum PrintStep { psSkirt, @@ -194,6 +198,7 @@ public: // Helpers to project custom facets on slices void project_and_append_custom_facets(bool seam, EnforcerBlockerType type, std::vector& expolys) const; + FillAdaptive_Internal::Octree* adaptiveInfillOctree() { return m_adapt_fill_octree; } private: // to be called from Print only. friend class Print; @@ -235,6 +240,7 @@ private: void discover_horizontal_shells(); void combine_infill(); void _generate_support_material(); + void prepare_adaptive_infill_data(); // XYZ in scaled coordinates Vec3crd m_size; @@ -255,6 +261,8 @@ private: // so that next call to make_perimeters() performs a union() before computing loops bool m_typed_slices = false; + FillAdaptive_Internal::Octree* m_adapt_fill_octree = nullptr; + std::vector slice_region(size_t region_id, const std::vector &z, SlicingMode mode) const; std::vector slice_modifiers(size_t region_id, const std::vector &z) const; std::vector slice_volumes(const std::vector &z, SlicingMode mode, const std::vector &volumes) const; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 239969a1d2..718fae365b 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -881,6 +881,7 @@ void PrintConfigDef::init_fff_params() def->enum_values.push_back("hilbertcurve"); def->enum_values.push_back("archimedeanchords"); def->enum_values.push_back("octagramspiral"); + def->enum_values.push_back("adaptivecubic"); def->enum_labels.push_back(L("Rectilinear")); def->enum_labels.push_back(L("Grid")); def->enum_labels.push_back(L("Triangles")); @@ -894,6 +895,7 @@ void PrintConfigDef::init_fff_params() def->enum_labels.push_back(L("Hilbert Curve")); def->enum_labels.push_back(L("Archimedean Chords")); def->enum_labels.push_back(L("Octagram Spiral")); + def->enum_labels.push_back(L("Adaptive Cubic")); def->set_default_value(new ConfigOptionEnum(ipStars)); def = this->add("first_layer_acceleration", coFloat); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index b133a2e4eb..3726444fab 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -39,7 +39,7 @@ enum AuthorizationType { enum InfillPattern : int { ipRectilinear, ipMonotonous, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, - ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipCount, + ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipCount, }; enum class IroningType { @@ -139,6 +139,7 @@ template<> inline const t_config_enum_values& ConfigOptionEnum::g keys_map["hilbertcurve"] = ipHilbertCurve; keys_map["archimedeanchords"] = ipArchimedeanChords; keys_map["octagramspiral"] = ipOctagramSpiral; + keys_map["adaptivecubic"] = ipAdaptiveCubic; } return keys_map; } From 34f38c4a79e426d4a479bdf33644b2cb77ed0eff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Wed, 26 Aug 2020 18:15:59 +0200 Subject: [PATCH 445/503] Building octree based on distance from mesh --- src/libslic3r/Fill/FillAdaptive.cpp | 86 +++++++++++++++++++++++++++++ src/libslic3r/Fill/FillAdaptive.hpp | 16 ++++++ src/libslic3r/PrintObject.cpp | 23 ++++++++ 3 files changed, 125 insertions(+) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index cb11385983..ce779ad005 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -1,6 +1,8 @@ #include "../ClipperUtils.hpp" #include "../ExPolygon.hpp" #include "../Surface.hpp" +#include "../Geometry.hpp" +#include "../AABBTreeIndirect.hpp" #include "FillAdaptive.hpp" @@ -16,4 +18,88 @@ void FillAdaptive::_fill_surface_single( } +FillAdaptive_Internal::Octree* FillAdaptive::build_octree( + TriangleMesh &triangleMesh, + coordf_t line_spacing, + const BoundingBoxf3 &printer_volume, + const Vec3d &cube_center) +{ + using namespace FillAdaptive_Internal; + + if(line_spacing <= 0) + { + return nullptr; + } + + // The furthest point from center of bed. + double furthest_point = std::sqrt(((printer_volume.size()[0] * printer_volume.size()[0]) / 4.0) + + ((printer_volume.size()[1] * printer_volume.size()[1]) / 4.0) + + (printer_volume.size()[2] * printer_volume.size()[2])); + double max_cube_edge_length = furthest_point * 2; + + std::vector cubes_properties; + for (double edge_length = (line_spacing * 2); edge_length < (max_cube_edge_length * 2); edge_length *= 2) + { + CubeProperties props{}; + props.edge_length = edge_length; + props.height = edge_length * sqrt(3); + props.diagonal_length = edge_length * sqrt(2); + props.line_z_distance = edge_length / sqrt(3); + props.line_xy_distance = edge_length / sqrt(6); + cubes_properties.push_back(props); + } + + if (triangleMesh.its.vertices.empty()) + { + triangleMesh.require_shared_vertices(); + } + + Vec3d rotation = Vec3d(Geometry::deg2rad(225.0), Geometry::deg2rad(215.0), Geometry::deg2rad(30.0)); + Transform3d rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), rotation, Vec3d::Ones(), Vec3d::Ones()); + + AABBTreeIndirect::Tree3f aabbTree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(triangleMesh.its.vertices, triangleMesh.its.indices); + Octree *octree = new Octree{new Cube{cube_center, cubes_properties.size() - 1, cubes_properties.back()}, cube_center}; + + FillAdaptive::expand_cube(octree->root_cube, cubes_properties, rotation_matrix, aabbTree, triangleMesh); + + return octree; +} + +void FillAdaptive::expand_cube( + FillAdaptive_Internal::Cube *cube, + const std::vector &cubes_properties, + const Transform3d &rotation_matrix, + const AABBTreeIndirect::Tree3f &distanceTree, + const TriangleMesh &triangleMesh) +{ + using namespace FillAdaptive_Internal; + + if (cube == nullptr || cube->depth == 0) + { + return; + } + + std::vector child_centers = { + Vec3d(-1, -1, -1), Vec3d( 1, -1, -1), Vec3d(-1, 1, -1), Vec3d(-1, -1, 1), + Vec3d( 1, 1, 1), Vec3d(-1, 1, 1), Vec3d( 1, -1, 1), Vec3d( 1, 1, -1) + }; + + double cube_radius_squared = (cube->properties.height * cube->properties.height) / 16; + + for (const Vec3d &child_center : child_centers) { + Vec3d child_center_transformed = cube->center + rotation_matrix * (child_center * (cube->properties.edge_length / 4)); + Vec3d closest_point = Vec3d::Zero(); + size_t closest_triangle_idx = 0; + + double distance_squared = AABBTreeIndirect::squared_distance_to_indexed_triangle_set( + triangleMesh.its.vertices, triangleMesh.its.indices, distanceTree, child_center_transformed, + closest_triangle_idx,closest_point); + + if(distance_squared <= cube_radius_squared) { + cube->children.push_back(new Cube{child_center_transformed, cube->depth - 1, cubes_properties[cube->depth - 1]}); + FillAdaptive::expand_cube(cube->children.back(), cubes_properties, rotation_matrix, distanceTree, triangleMesh); + } + } +} + } // namespace Slic3r diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index e0a97a1b9b..49c5276a93 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -1,6 +1,8 @@ #ifndef slic3r_FillAdaptive_hpp_ #define slic3r_FillAdaptive_hpp_ +#include "../AABBTreeIndirect.hpp" + #include "FillBase.hpp" namespace Slic3r { @@ -46,6 +48,20 @@ protected: Polylines &polylines_out); virtual bool no_sort() const { return true; } + +public: + static FillAdaptive_Internal::Octree* build_octree( + TriangleMesh &triangleMesh, + coordf_t line_spacing, + const BoundingBoxf3 &printer_volume, + const Vec3d &cube_center); + + static void expand_cube( + FillAdaptive_Internal::Cube *cube, + const std::vector &cubes_properties, + const Transform3d &rotation_matrix, + const AABBTreeIndirect::Tree3f &distanceTree, + const TriangleMesh &triangleMesh); }; } // namespace Slic3r diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index aecf907710..272ee6e819 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -9,6 +9,8 @@ #include "Surface.hpp" #include "Slicing.hpp" #include "Utils.hpp" +#include "AABBTreeIndirect.hpp" +#include "Fill/FillAdaptive.hpp" #include #include @@ -360,6 +362,8 @@ void PrintObject::prepare_infill() } // for each layer #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + this->prepare_adaptive_infill_data(); + this->set_done(posPrepareInfill); } @@ -428,6 +432,25 @@ void PrintObject::generate_support_material() } } +void PrintObject::prepare_adaptive_infill_data() +{ + float fill_density = this->print()->full_print_config().opt_float("fill_density"); + float infill_extrusion_width = this->print()->full_print_config().opt_float("infill_extrusion_width"); + + coordf_t line_spacing = infill_extrusion_width / ((fill_density / 100.0f) * 0.333333333f); + + BoundingBoxf bed_shape(this->print()->config().bed_shape.values); + BoundingBoxf3 printer_volume(Vec3d(bed_shape.min(0), bed_shape.min(1), 0), + Vec3d(bed_shape.max(0), bed_shape.max(1), this->print()->config().max_print_height)); + + Vec3d model_center = this->model_object()->bounding_box().center(); + model_center(2) = 0.0f; // Set position in Z axis to 0 + // Center of the first cube in octree + + TriangleMesh mesh = this->model_object()->mesh(); + this->m_adapt_fill_octree = FillAdaptive::build_octree(mesh, line_spacing, printer_volume, model_center); +} + void PrintObject::clear_layers() { for (Layer *l : m_layers) From 9f049b26198f49f34539b4ed694bdb417eec5e61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Wed, 26 Aug 2020 22:18:51 +0200 Subject: [PATCH 446/503] Generating polylines from octree --- src/libslic3r/Fill/FillAdaptive.cpp | 57 +++++++++++++++++++++++++++++ src/libslic3r/Fill/FillAdaptive.hpp | 7 ++++ 2 files changed, 64 insertions(+) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index ce779ad005..cac9c1c3b2 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -15,7 +15,64 @@ void FillAdaptive::_fill_surface_single( ExPolygon &expolygon, Polylines &polylines_out) { + Polylines infill_polylines; + this->generate_polylines(this->adapt_fill_octree->root_cube, this->z, this->adapt_fill_octree->origin, infill_polylines); + // Crop all polylines + polylines_out = intersection_pl(infill_polylines, to_polygons(expolygon)); +} + +void FillAdaptive::generate_polylines( + FillAdaptive_Internal::Cube *cube, + double z_position, + const Vec3d &origin, + Polylines &polylines_out) +{ + using namespace FillAdaptive_Internal; + + if(cube == nullptr) + { + return; + } + + double z_diff = std::abs(z_position - cube->center.z()); + + if (z_diff > cube->properties.height / 2) + { + return; + } + + if (z_diff < cube->properties.line_z_distance) + { + Point from( + scale_((cube->properties.diagonal_length / 2) * (cube->properties.line_z_distance - z_diff) / cube->properties.line_z_distance), + scale_(cube->properties.line_xy_distance - ((z_position - (cube->center.z() - cube->properties.line_z_distance)) / sqrt(2)))); + Point to(-from.x(), from.y()); + // Relative to cube center + + float rotation_angle = Geometry::deg2rad(120.0); + + for (int dir_idx = 0; dir_idx < 3; dir_idx++) + { + Vec3d offset = cube->center - origin; + Point from_abs(from), to_abs(to); + + from_abs.x() += scale_(offset.x()); + from_abs.y() += scale_(offset.y()); + to_abs.x() += scale_(offset.x()); + to_abs.y() += scale_(offset.y()); + + polylines_out.push_back(Polyline(from_abs, to_abs)); + + from.rotate(rotation_angle); + to.rotate(rotation_angle); + } + } + + for(Cube *child : cube->children) + { + generate_polylines(child, z_position, origin, polylines_out); + } } FillAdaptive_Internal::Octree* FillAdaptive::build_octree( diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index 49c5276a93..9e1a196af1 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -33,6 +33,11 @@ namespace FillAdaptive_Internal }; }; // namespace FillAdaptive_Internal +// +// Some of the algorithms used by class FillAdaptive were inspired by +// Cura Engine's class SubDivCube +// https://github.com/Ultimaker/CuraEngine/blob/master/src/infill/SubDivCube.h +// class FillAdaptive : public Fill { public: @@ -49,6 +54,8 @@ protected: virtual bool no_sort() const { return true; } + void generate_polylines(FillAdaptive_Internal::Cube *cube, double z_position, const Vec3d &origin, Polylines &polylines_out); + public: static FillAdaptive_Internal::Octree* build_octree( TriangleMesh &triangleMesh, From c311b84b212329f04d6ed48305b935db4cc6d498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Wed, 26 Aug 2020 23:28:52 +0200 Subject: [PATCH 447/503] Add function for check existence of triangle in define radius --- src/libslic3r/AABBTreeIndirect.hpp | 34 +++++++++++++++++++++++++++++ src/libslic3r/Fill/FillAdaptive.cpp | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/AABBTreeIndirect.hpp b/src/libslic3r/AABBTreeIndirect.hpp index ec9b14a7ae..17d918aeb3 100644 --- a/src/libslic3r/AABBTreeIndirect.hpp +++ b/src/libslic3r/AABBTreeIndirect.hpp @@ -692,6 +692,40 @@ inline typename VectorType::Scalar squared_distance_to_indexed_triangle_set( detail::squared_distance_to_indexed_triangle_set_recursive(distancer, size_t(0), Scalar(0), std::numeric_limits::infinity(), hit_idx_out, hit_point_out); } +// Decides if exists some triangle in defined radius on a 3D indexed triangle set using a pre-built AABBTreeIndirect::Tree. +// Closest point to triangle test will be performed with the accuracy of VectorType::Scalar +// even if the triangle mesh and the AABB Tree are built with floats. +// Returns true if exists some triangle in defined radius, false otherwise. +template +inline bool is_any_triangle_in_radius( + // Indexed triangle set - 3D vertices. + const std::vector &vertices, + // Indexed triangle set - triangular faces, references to vertices. + const std::vector &faces, + // AABBTreeIndirect::Tree over vertices & faces, bounding boxes built with the accuracy of vertices. + const TreeType &tree, + // Point to which the closest point on the indexed triangle set is searched for. + const VectorType &point, + // Maximum distance in which triangle is search for + typename VectorType::Scalar &max_distance) +{ + using Scalar = typename VectorType::Scalar; + auto distancer = detail::IndexedTriangleSetDistancer + { vertices, faces, tree, point }; + + size_t hit_idx; + VectorType hit_point = VectorType::Ones() * (std::nan("")); + + if(tree.empty()) + { + return false; + } + + detail::squared_distance_to_indexed_triangle_set_recursive(distancer, size_t(0), Scalar(0), max_distance, hit_idx, hit_point); + + return hit_point.allFinite(); +} + } // namespace AABBTreeIndirect } // namespace Slic3r diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index cac9c1c3b2..ae067e659e 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -152,7 +152,7 @@ void FillAdaptive::expand_cube( triangleMesh.its.vertices, triangleMesh.its.indices, distanceTree, child_center_transformed, closest_triangle_idx,closest_point); - if(distance_squared <= cube_radius_squared) { + if(AABBTreeIndirect::is_any_triangle_in_radius(triangleMesh.its.vertices, triangleMesh.its.indices, distanceTree, child_center_transformed, cube_radius_squared)) { cube->children.push_back(new Cube{child_center_transformed, cube->depth - 1, cubes_properties[cube->depth - 1]}); FillAdaptive::expand_cube(cube->children.back(), cubes_properties, rotation_matrix, distanceTree, triangleMesh); } From c0d21bd2b496bf8b6393cb395cfce169bd857c20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 27 Aug 2020 01:59:35 +0200 Subject: [PATCH 448/503] Polylines merging --- src/libslic3r/Fill/FillAdaptive.cpp | 84 ++++++++++++++++++++++++----- src/libslic3r/Fill/FillAdaptive.hpp | 4 +- 2 files changed, 75 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index ae067e659e..adc6c0c6fb 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -15,18 +15,54 @@ void FillAdaptive::_fill_surface_single( ExPolygon &expolygon, Polylines &polylines_out) { - Polylines infill_polylines; + std::vector infill_polylines(3); this->generate_polylines(this->adapt_fill_octree->root_cube, this->z, this->adapt_fill_octree->origin, infill_polylines); - // Crop all polylines - polylines_out = intersection_pl(infill_polylines, to_polygons(expolygon)); + for (Polylines &infill_polyline : infill_polylines) { + // Crop all polylines + infill_polyline = intersection_pl(infill_polyline, to_polygons(expolygon)); + polylines_out.insert(polylines_out.end(), infill_polyline.begin(), infill_polyline.end()); + } + +#ifdef SLIC3R_DEBUG_SLICE_PROCESSING + { + static int iRuna = 0; + BoundingBox bbox_svg = this->bounding_box; + { + ::Slic3r::SVG svg(debug_out_path("FillAdaptive-%d.svg", iRuna), bbox_svg); + for (const Polyline &polyline : polylines_out) + { + for (const Line &line : polyline.lines()) + { + Point from = line.a; + Point to = line.b; + Point diff = to - from; + + float shrink_length = scale_(0.4); + float line_slope = (float)diff.y() / diff.x(); + float shrink_x = shrink_length / (float)std::sqrt(1.0 + (line_slope * line_slope)); + float shrink_y = line_slope * shrink_x; + + to.x() -= shrink_x; + to.y() -= shrink_y; + from.x() += shrink_x; + from.y() += shrink_y; + + svg.draw(Line(from, to)); + } + } + } + + iRuna++; + } +#endif /* SLIC3R_DEBUG */ } void FillAdaptive::generate_polylines( FillAdaptive_Internal::Cube *cube, double z_position, const Vec3d &origin, - Polylines &polylines_out) + std::vector &polylines_out) { using namespace FillAdaptive_Internal; @@ -52,7 +88,7 @@ void FillAdaptive::generate_polylines( float rotation_angle = Geometry::deg2rad(120.0); - for (int dir_idx = 0; dir_idx < 3; dir_idx++) + for (int i = 0; i < 3; i++) { Vec3d offset = cube->center - origin; Point from_abs(from), to_abs(to); @@ -62,7 +98,8 @@ void FillAdaptive::generate_polylines( to_abs.x() += scale_(offset.x()); to_abs.y() += scale_(offset.y()); - polylines_out.push_back(Polyline(from_abs, to_abs)); +// polylines_out[i].push_back(Polyline(from_abs, to_abs)); + this->merge_polylines(polylines_out[i], Line(from_abs, to_abs)); from.rotate(rotation_angle); to.rotate(rotation_angle); @@ -75,6 +112,35 @@ void FillAdaptive::generate_polylines( } } +void FillAdaptive::merge_polylines(Polylines &polylines, const Line &new_line) +{ + int eps = scale_(0.10); + bool modified = false; + + for (Polyline &polyline : polylines) + { + if (std::abs(new_line.a.x() - polyline.points[1].x()) < eps && std::abs(new_line.a.y() - polyline.points[1].y()) < eps) + { + polyline.points[1].x() = new_line.b.x(); + polyline.points[1].y() = new_line.b.y(); + modified = true; + } + + if (std::abs(new_line.b.x() - polyline.points[0].x()) < eps && std::abs(new_line.b.y() - polyline.points[0].y()) < eps) + { + polyline.points[0].x() = new_line.a.x(); + polyline.points[0].y() = new_line.a.y(); + modified = true; + } + } + + if(!modified) + { + polylines.emplace_back(Polyline(new_line.a, new_line.b)); + } +} + + FillAdaptive_Internal::Octree* FillAdaptive::build_octree( TriangleMesh &triangleMesh, coordf_t line_spacing, @@ -145,12 +211,6 @@ void FillAdaptive::expand_cube( for (const Vec3d &child_center : child_centers) { Vec3d child_center_transformed = cube->center + rotation_matrix * (child_center * (cube->properties.edge_length / 4)); - Vec3d closest_point = Vec3d::Zero(); - size_t closest_triangle_idx = 0; - - double distance_squared = AABBTreeIndirect::squared_distance_to_indexed_triangle_set( - triangleMesh.its.vertices, triangleMesh.its.indices, distanceTree, child_center_transformed, - closest_triangle_idx,closest_point); if(AABBTreeIndirect::is_any_triangle_in_radius(triangleMesh.its.vertices, triangleMesh.its.indices, distanceTree, child_center_transformed, cube_radius_squared)) { cube->children.push_back(new Cube{child_center_transformed, cube->depth - 1, cubes_properties[cube->depth - 1]}); diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index 9e1a196af1..b2f4e37b1e 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -54,7 +54,9 @@ protected: virtual bool no_sort() const { return true; } - void generate_polylines(FillAdaptive_Internal::Cube *cube, double z_position, const Vec3d &origin, Polylines &polylines_out); + void generate_polylines(FillAdaptive_Internal::Cube *cube, double z_position, const Vec3d &origin, std::vector &polylines_out); + + void merge_polylines(Polylines &polylines, const Line &new_line); public: static FillAdaptive_Internal::Octree* build_octree( From 14a7fbc9f70755f058d15be76318425537d3ca9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 27 Aug 2020 07:28:43 +0200 Subject: [PATCH 449/503] Switch to smart pointers --- src/libslic3r/Fill/FillAdaptive.cpp | 17 +++++++++-------- src/libslic3r/Fill/FillAdaptive.hpp | 6 +++--- src/libslic3r/Print.hpp | 8 +++----- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index adc6c0c6fb..96509923c8 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -16,7 +16,7 @@ void FillAdaptive::_fill_surface_single( Polylines &polylines_out) { std::vector infill_polylines(3); - this->generate_polylines(this->adapt_fill_octree->root_cube, this->z, this->adapt_fill_octree->origin, infill_polylines); + this->generate_polylines(this->adapt_fill_octree->root_cube.get(), this->z, this->adapt_fill_octree->origin, infill_polylines); for (Polylines &infill_polyline : infill_polylines) { // Crop all polylines @@ -106,9 +106,9 @@ void FillAdaptive::generate_polylines( } } - for(Cube *child : cube->children) + for(const std::unique_ptr &child : cube->children) { - generate_polylines(child, z_position, origin, polylines_out); + generate_polylines(child.get(), z_position, origin, polylines_out); } } @@ -141,7 +141,7 @@ void FillAdaptive::merge_polylines(Polylines &polylines, const Line &new_line) } -FillAdaptive_Internal::Octree* FillAdaptive::build_octree( +std::unique_ptr FillAdaptive::build_octree( TriangleMesh &triangleMesh, coordf_t line_spacing, const BoundingBoxf3 &printer_volume, @@ -181,9 +181,10 @@ FillAdaptive_Internal::Octree* FillAdaptive::build_octree( Transform3d rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), rotation, Vec3d::Ones(), Vec3d::Ones()); AABBTreeIndirect::Tree3f aabbTree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(triangleMesh.its.vertices, triangleMesh.its.indices); - Octree *octree = new Octree{new Cube{cube_center, cubes_properties.size() - 1, cubes_properties.back()}, cube_center}; + std::unique_ptr octree = std::unique_ptr( + new Octree{std::unique_ptr(new Cube{cube_center, cubes_properties.size() - 1, cubes_properties.back()}), cube_center}); - FillAdaptive::expand_cube(octree->root_cube, cubes_properties, rotation_matrix, aabbTree, triangleMesh); + FillAdaptive::expand_cube(octree->root_cube.get(), cubes_properties, rotation_matrix, aabbTree, triangleMesh); return octree; } @@ -213,8 +214,8 @@ void FillAdaptive::expand_cube( Vec3d child_center_transformed = cube->center + rotation_matrix * (child_center * (cube->properties.edge_length / 4)); if(AABBTreeIndirect::is_any_triangle_in_radius(triangleMesh.its.vertices, triangleMesh.its.indices, distanceTree, child_center_transformed, cube_radius_squared)) { - cube->children.push_back(new Cube{child_center_transformed, cube->depth - 1, cubes_properties[cube->depth - 1]}); - FillAdaptive::expand_cube(cube->children.back(), cubes_properties, rotation_matrix, distanceTree, triangleMesh); + cube->children.push_back(std::unique_ptr(new Cube{child_center_transformed, cube->depth - 1, cubes_properties[cube->depth - 1]})); + FillAdaptive::expand_cube(cube->children.back().get(), cubes_properties, rotation_matrix, distanceTree, triangleMesh); } } } diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index b2f4e37b1e..fb1f2da8e3 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -23,12 +23,12 @@ namespace FillAdaptive_Internal Vec3d center; size_t depth; CubeProperties properties; - std::vector children; + std::vector> children; }; struct Octree { - Cube *root_cube; + std::unique_ptr root_cube; Vec3d origin; }; }; // namespace FillAdaptive_Internal @@ -59,7 +59,7 @@ protected: void merge_polylines(Polylines &polylines, const Line &new_line); public: - static FillAdaptive_Internal::Octree* build_octree( + static std::unique_ptr build_octree( TriangleMesh &triangleMesh, coordf_t line_spacing, const BoundingBoxf3 &printer_volume, diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 02dd6df356..1cc83dbe48 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -11,6 +11,7 @@ #include "GCode/ToolOrdering.hpp" #include "GCode/WipeTower.hpp" #include "GCode/ThumbnailData.hpp" +#include "Fill/FillAdaptive.hpp" #if ENABLE_GCODE_VIEWER #include "GCode/GCodeProcessor.hpp" #endif // ENABLE_GCODE_VIEWER @@ -30,9 +31,6 @@ enum class SlicingMode : uint32_t; class Layer; class SupportLayer; -namespace FillAdaptive_Internal { - struct Octree; -}; // Print step IDs for keeping track of the print state. enum PrintStep { @@ -198,7 +196,7 @@ public: // Helpers to project custom facets on slices void project_and_append_custom_facets(bool seam, EnforcerBlockerType type, std::vector& expolys) const; - FillAdaptive_Internal::Octree* adaptiveInfillOctree() { return m_adapt_fill_octree; } + FillAdaptive_Internal::Octree* adaptiveInfillOctree() { return m_adapt_fill_octree.get(); } private: // to be called from Print only. friend class Print; @@ -261,7 +259,7 @@ private: // so that next call to make_perimeters() performs a union() before computing loops bool m_typed_slices = false; - FillAdaptive_Internal::Octree* m_adapt_fill_octree = nullptr; + std::unique_ptr m_adapt_fill_octree = nullptr; std::vector slice_region(size_t region_id, const std::vector &z, SlicingMode mode) const; std::vector slice_modifiers(size_t region_id, const std::vector &z) const; From 867681ae56f85e828fb4fc9370d43605c05f1906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 27 Aug 2020 13:04:53 +0200 Subject: [PATCH 450/503] Fix discontinuous extrusion lines for adaptive infill --- src/libslic3r/Fill/FillAdaptive.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index 96509923c8..a3068989ef 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -88,7 +88,7 @@ void FillAdaptive::generate_polylines( float rotation_angle = Geometry::deg2rad(120.0); - for (int i = 0; i < 3; i++) + for (int i = 0; i < polylines_out.size(); i++) { Vec3d offset = cube->center - origin; Point from_abs(from), to_abs(to); @@ -177,7 +177,7 @@ std::unique_ptr FillAdaptive::build_octree( triangleMesh.require_shared_vertices(); } - Vec3d rotation = Vec3d(Geometry::deg2rad(225.0), Geometry::deg2rad(215.0), Geometry::deg2rad(30.0)); + Vec3d rotation = Vec3d(Geometry::deg2rad(225.0), Geometry::deg2rad(215.264), Geometry::deg2rad(30.0)); Transform3d rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), rotation, Vec3d::Ones(), Vec3d::Ones()); AABBTreeIndirect::Tree3f aabbTree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(triangleMesh.its.vertices, triangleMesh.its.indices); From 65ba40f0445b938045f9f5e5cfdcf9acba9e4cdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Sun, 30 Aug 2020 20:38:07 +0200 Subject: [PATCH 451/503] Fix crash on inconsistent input --- src/libslic3r/Fill/FillAdaptive.cpp | 2 +- src/libslic3r/PrintObject.cpp | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index a3068989ef..0563b612ab 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -149,7 +149,7 @@ std::unique_ptr FillAdaptive::build_octree( { using namespace FillAdaptive_Internal; - if(line_spacing <= 0) + if(line_spacing <= 0 || std::isnan(line_spacing)) { return nullptr; } diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 272ee6e819..43ebf58db7 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -434,8 +434,16 @@ void PrintObject::generate_support_material() void PrintObject::prepare_adaptive_infill_data() { - float fill_density = this->print()->full_print_config().opt_float("fill_density"); - float infill_extrusion_width = this->print()->full_print_config().opt_float("infill_extrusion_width"); + const ConfigOptionFloatOrPercent* opt_fill_density = this->print()->full_print_config().option("fill_density"); + const ConfigOptionFloatOrPercent* opt_infill_extrusion_width = this->print()->full_print_config().option("infill_extrusion_width"); + + if(opt_fill_density == nullptr || opt_infill_extrusion_width == nullptr || opt_fill_density->value <= 0 || opt_infill_extrusion_width->value <= 0) + { + return; + } + + float fill_density = opt_fill_density->value; + float infill_extrusion_width = opt_infill_extrusion_width->value; coordf_t line_spacing = infill_extrusion_width / ((fill_density / 100.0f) * 0.333333333f); From 9eeb5e4364f641c30eeb8bf78594455fbf1f50dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 31 Aug 2020 08:49:17 +0200 Subject: [PATCH 452/503] Fix wrong data type --- src/libslic3r/PrintObject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 43ebf58db7..9d023a095c 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -434,7 +434,7 @@ void PrintObject::generate_support_material() void PrintObject::prepare_adaptive_infill_data() { - const ConfigOptionFloatOrPercent* opt_fill_density = this->print()->full_print_config().option("fill_density"); + const ConfigOptionPercent* opt_fill_density = this->print()->full_print_config().option("fill_density"); const ConfigOptionFloatOrPercent* opt_infill_extrusion_width = this->print()->full_print_config().option("infill_extrusion_width"); if(opt_fill_density == nullptr || opt_infill_extrusion_width == nullptr || opt_fill_density->value <= 0 || opt_infill_extrusion_width->value <= 0) From 33121b705a58d00ca80398d99ad5e60e8387a342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Wed, 2 Sep 2020 22:53:10 +0200 Subject: [PATCH 453/503] Change in passing octree struct --- src/libslic3r/Fill/Fill.cpp | 4 ++-- src/libslic3r/Layer.hpp | 6 +++++- src/libslic3r/Print.hpp | 9 ++++----- src/libslic3r/PrintObject.cpp | 20 ++++++++++---------- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index c948df400e..9d468a6aa9 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -318,7 +318,7 @@ void export_group_fills_to_svg(const char *path, const std::vector #endif // friend to Layer -void Layer::make_fills() +void Layer::make_fills(FillAdaptive_Internal::Octree* adaptive_fill_octree) { for (LayerRegion *layerm : m_regions) layerm->fills.clear(); @@ -345,7 +345,7 @@ void Layer::make_fills() f->layer_id = this->id(); f->z = this->print_z; f->angle = surface_fill.params.angle; - f->adapt_fill_octree = this->object()->adaptiveInfillOctree(); + f->adapt_fill_octree = adaptive_fill_octree; // calculate flow spacing for infill pattern generation bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.flow.bridge; diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index c104d46da1..4c824a1093 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -13,6 +13,10 @@ class Layer; class PrintRegion; class PrintObject; +namespace FillAdaptive_Internal { + struct Octree; +}; + class LayerRegion { public: @@ -134,7 +138,7 @@ public: return false; } void make_perimeters(); - void make_fills(); + void make_fills(FillAdaptive_Internal::Octree* adaptive_fill_octree); void make_ironing(); void export_region_slices_to_svg(const char *path) const; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 1cc83dbe48..effb6bde90 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -11,7 +11,6 @@ #include "GCode/ToolOrdering.hpp" #include "GCode/WipeTower.hpp" #include "GCode/ThumbnailData.hpp" -#include "Fill/FillAdaptive.hpp" #if ENABLE_GCODE_VIEWER #include "GCode/GCodeProcessor.hpp" #endif // ENABLE_GCODE_VIEWER @@ -31,6 +30,9 @@ enum class SlicingMode : uint32_t; class Layer; class SupportLayer; +namespace FillAdaptive_Internal { + struct Octree; +}; // Print step IDs for keeping track of the print state. enum PrintStep { @@ -196,7 +198,6 @@ public: // Helpers to project custom facets on slices void project_and_append_custom_facets(bool seam, EnforcerBlockerType type, std::vector& expolys) const; - FillAdaptive_Internal::Octree* adaptiveInfillOctree() { return m_adapt_fill_octree.get(); } private: // to be called from Print only. friend class Print; @@ -238,7 +239,7 @@ private: void discover_horizontal_shells(); void combine_infill(); void _generate_support_material(); - void prepare_adaptive_infill_data(); + std::unique_ptr prepare_adaptive_infill_data(); // XYZ in scaled coordinates Vec3crd m_size; @@ -259,8 +260,6 @@ private: // so that next call to make_perimeters() performs a union() before computing loops bool m_typed_slices = false; - std::unique_ptr m_adapt_fill_octree = nullptr; - std::vector slice_region(size_t region_id, const std::vector &z, SlicingMode mode) const; std::vector slice_modifiers(size_t region_id, const std::vector &z) const; std::vector slice_volumes(const std::vector &z, SlicingMode mode, const std::vector &volumes) const; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 9d023a095c..1699cd5ad3 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -362,8 +362,6 @@ void PrintObject::prepare_infill() } // for each layer #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - this->prepare_adaptive_infill_data(); - this->set_done(posPrepareInfill); } @@ -373,13 +371,15 @@ void PrintObject::infill() this->prepare_infill(); if (this->set_started(posInfill)) { + std::unique_ptr octree = this->prepare_adaptive_infill_data(); + BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start"; tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), - [this](const tbb::blocked_range& range) { + [this, &octree](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { m_print->throw_if_canceled(); - m_layers[layer_idx]->make_fills(); + m_layers[layer_idx]->make_fills(octree.get()); } } ); @@ -432,14 +432,14 @@ void PrintObject::generate_support_material() } } -void PrintObject::prepare_adaptive_infill_data() +std::unique_ptr PrintObject::prepare_adaptive_infill_data() { const ConfigOptionPercent* opt_fill_density = this->print()->full_print_config().option("fill_density"); const ConfigOptionFloatOrPercent* opt_infill_extrusion_width = this->print()->full_print_config().option("infill_extrusion_width"); if(opt_fill_density == nullptr || opt_infill_extrusion_width == nullptr || opt_fill_density->value <= 0 || opt_infill_extrusion_width->value <= 0) { - return; + return std::unique_ptr{}; } float fill_density = opt_fill_density->value; @@ -448,15 +448,15 @@ void PrintObject::prepare_adaptive_infill_data() coordf_t line_spacing = infill_extrusion_width / ((fill_density / 100.0f) * 0.333333333f); BoundingBoxf bed_shape(this->print()->config().bed_shape.values); - BoundingBoxf3 printer_volume(Vec3d(bed_shape.min(0), bed_shape.min(1), 0), - Vec3d(bed_shape.max(0), bed_shape.max(1), this->print()->config().max_print_height)); + BoundingBoxf3 printer_volume(Vec3d(bed_shape.min.x(), bed_shape.min.y(), 0), + Vec3d(bed_shape.max.x(), bed_shape.max.y(), this->print()->config().max_print_height)); Vec3d model_center = this->model_object()->bounding_box().center(); - model_center(2) = 0.0f; // Set position in Z axis to 0 + model_center.z() = 0.0f; // Set position in Z axis to 0 // Center of the first cube in octree TriangleMesh mesh = this->model_object()->mesh(); - this->m_adapt_fill_octree = FillAdaptive::build_octree(mesh, line_spacing, printer_volume, model_center); + return FillAdaptive::build_octree(mesh, line_spacing, printer_volume, model_center); } void PrintObject::clear_layers() From 2debffc49629b57f8e5751bab4b363d11f7e7e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 3 Sep 2020 07:52:53 +0200 Subject: [PATCH 454/503] Fix tests which expect make_fills without arguments --- src/libslic3r/Layer.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 4c824a1093..014d2623af 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -138,6 +138,7 @@ public: return false; } void make_perimeters(); + void make_fills() { this->make_fills(nullptr); }; void make_fills(FillAdaptive_Internal::Octree* adaptive_fill_octree); void make_ironing(); From d09ac41d2c602f58a5bc6b549b53d67c3f1308bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 3 Sep 2020 08:04:05 +0200 Subject: [PATCH 455/503] Octree's first cube depends on model size. --- src/libslic3r/Fill/FillAdaptive.cpp | 21 +++++++++++---------- src/libslic3r/Fill/FillAdaptive.hpp | 3 +-- src/libslic3r/PrintObject.cpp | 9 ++------- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index 0563b612ab..62c4a3af7b 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -142,9 +142,8 @@ void FillAdaptive::merge_polylines(Polylines &polylines, const Line &new_line) std::unique_ptr FillAdaptive::build_octree( - TriangleMesh &triangleMesh, + TriangleMesh &triangle_mesh, coordf_t line_spacing, - const BoundingBoxf3 &printer_volume, const Vec3d &cube_center) { using namespace FillAdaptive_Internal; @@ -154,10 +153,11 @@ std::unique_ptr FillAdaptive::build_octree( return nullptr; } - // The furthest point from center of bed. - double furthest_point = std::sqrt(((printer_volume.size()[0] * printer_volume.size()[0]) / 4.0) + - ((printer_volume.size()[1] * printer_volume.size()[1]) / 4.0) + - (printer_volume.size()[2] * printer_volume.size()[2])); + Vec3d bb_size = triangle_mesh.bounding_box().size(); + // The furthest point from the center of the bottom of the mesh bounding box. + double furthest_point = std::sqrt(((bb_size.x() * bb_size.x()) / 4.0) + + ((bb_size.y() * bb_size.y()) / 4.0) + + (bb_size.z() * bb_size.z())); double max_cube_edge_length = furthest_point * 2; std::vector cubes_properties; @@ -172,19 +172,20 @@ std::unique_ptr FillAdaptive::build_octree( cubes_properties.push_back(props); } - if (triangleMesh.its.vertices.empty()) + if (triangle_mesh.its.vertices.empty()) { - triangleMesh.require_shared_vertices(); + triangle_mesh.require_shared_vertices(); } Vec3d rotation = Vec3d(Geometry::deg2rad(225.0), Geometry::deg2rad(215.264), Geometry::deg2rad(30.0)); Transform3d rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), rotation, Vec3d::Ones(), Vec3d::Ones()); - AABBTreeIndirect::Tree3f aabbTree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(triangleMesh.its.vertices, triangleMesh.its.indices); + AABBTreeIndirect::Tree3f aabbTree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set( + triangle_mesh.its.vertices, triangle_mesh.its.indices); std::unique_ptr octree = std::unique_ptr( new Octree{std::unique_ptr(new Cube{cube_center, cubes_properties.size() - 1, cubes_properties.back()}), cube_center}); - FillAdaptive::expand_cube(octree->root_cube.get(), cubes_properties, rotation_matrix, aabbTree, triangleMesh); + FillAdaptive::expand_cube(octree->root_cube.get(), cubes_properties, rotation_matrix, aabbTree, triangle_mesh); return octree; } diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index fb1f2da8e3..c7539df5a7 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -60,9 +60,8 @@ protected: public: static std::unique_ptr build_octree( - TriangleMesh &triangleMesh, + TriangleMesh &triangle_mesh, coordf_t line_spacing, - const BoundingBoxf3 &printer_volume, const Vec3d &cube_center); static void expand_cube( diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 1699cd5ad3..1236a297fc 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -447,16 +447,11 @@ std::unique_ptr PrintObject::prepare_adaptive_inf coordf_t line_spacing = infill_extrusion_width / ((fill_density / 100.0f) * 0.333333333f); - BoundingBoxf bed_shape(this->print()->config().bed_shape.values); - BoundingBoxf3 printer_volume(Vec3d(bed_shape.min.x(), bed_shape.min.y(), 0), - Vec3d(bed_shape.max.x(), bed_shape.max.y(), this->print()->config().max_print_height)); - - Vec3d model_center = this->model_object()->bounding_box().center(); - model_center.z() = 0.0f; // Set position in Z axis to 0 // Center of the first cube in octree + Vec3d model_center = this->model_object()->bounding_box().center(); TriangleMesh mesh = this->model_object()->mesh(); - return FillAdaptive::build_octree(mesh, line_spacing, printer_volume, model_center); + return FillAdaptive::build_octree(mesh, line_spacing, model_center); } void PrintObject::clear_layers() From 398d429ce1913bc7916b0a422b8f1d33f2a20971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 3 Sep 2020 11:56:41 +0200 Subject: [PATCH 456/503] Code cleanup --- src/libslic3r/Fill/FillAdaptive.cpp | 56 +++++++++++++++-------------- src/libslic3r/Fill/FillAdaptive.hpp | 10 ++++-- 2 files changed, 38 insertions(+), 28 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index 62c4a3af7b..91da86b69e 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -15,15 +15,20 @@ void FillAdaptive::_fill_surface_single( ExPolygon &expolygon, Polylines &polylines_out) { - std::vector infill_polylines(3); - this->generate_polylines(this->adapt_fill_octree->root_cube.get(), this->z, this->adapt_fill_octree->origin, infill_polylines); + std::vector infill_lines_dir(3); + this->generate_infill_lines(this->adapt_fill_octree->root_cube.get(), this->z, this->adapt_fill_octree->origin, infill_lines_dir); - for (Polylines &infill_polyline : infill_polylines) { - // Crop all polylines - infill_polyline = intersection_pl(infill_polyline, to_polygons(expolygon)); - polylines_out.insert(polylines_out.end(), infill_polyline.begin(), infill_polyline.end()); + for (Lines &infill_lines : infill_lines_dir) + { + for (const Line &line : infill_lines) + { + polylines_out.emplace_back(line.a, line.b); + } } + // Crop all polylines + polylines_out = intersection_pl(polylines_out, to_polygons(expolygon)); + #ifdef SLIC3R_DEBUG_SLICE_PROCESSING { static int iRuna = 0; @@ -58,11 +63,11 @@ void FillAdaptive::_fill_surface_single( #endif /* SLIC3R_DEBUG */ } -void FillAdaptive::generate_polylines( +void FillAdaptive::generate_infill_lines( FillAdaptive_Internal::Cube *cube, double z_position, const Vec3d &origin, - std::vector &polylines_out) + std::vector &dir_lines_out) { using namespace FillAdaptive_Internal; @@ -86,9 +91,8 @@ void FillAdaptive::generate_polylines( Point to(-from.x(), from.y()); // Relative to cube center - float rotation_angle = Geometry::deg2rad(120.0); - - for (int i = 0; i < polylines_out.size(); i++) + float rotation_angle = (2.0 * M_PI) / 3.0; + for (Lines &lines : dir_lines_out) { Vec3d offset = cube->center - origin; Point from_abs(from), to_abs(to); @@ -98,8 +102,8 @@ void FillAdaptive::generate_polylines( to_abs.x() += scale_(offset.x()); to_abs.y() += scale_(offset.y()); -// polylines_out[i].push_back(Polyline(from_abs, to_abs)); - this->merge_polylines(polylines_out[i], Line(from_abs, to_abs)); +// lines.emplace_back(from_abs, to_abs); + this->connect_lines(lines, Line(from_abs, to_abs)); from.rotate(rotation_angle); to.rotate(rotation_angle); @@ -108,35 +112,35 @@ void FillAdaptive::generate_polylines( for(const std::unique_ptr &child : cube->children) { - generate_polylines(child.get(), z_position, origin, polylines_out); + generate_infill_lines(child.get(), z_position, origin, dir_lines_out); } } -void FillAdaptive::merge_polylines(Polylines &polylines, const Line &new_line) +void FillAdaptive::connect_lines(Lines &lines, const Line &new_line) { int eps = scale_(0.10); bool modified = false; - for (Polyline &polyline : polylines) + for (Line &line : lines) { - if (std::abs(new_line.a.x() - polyline.points[1].x()) < eps && std::abs(new_line.a.y() - polyline.points[1].y()) < eps) + if (std::abs(new_line.a.x() - line.b.x()) < eps && std::abs(new_line.a.y() - line.b.y()) < eps) { - polyline.points[1].x() = new_line.b.x(); - polyline.points[1].y() = new_line.b.y(); + line.b.x() = new_line.b.x(); + line.b.y() = new_line.b.y(); modified = true; } - if (std::abs(new_line.b.x() - polyline.points[0].x()) < eps && std::abs(new_line.b.y() - polyline.points[0].y()) < eps) + if (std::abs(new_line.b.x() - line.a.x()) < eps && std::abs(new_line.b.y() - line.a.y()) < eps) { - polyline.points[0].x() = new_line.a.x(); - polyline.points[0].y() = new_line.a.y(); + line.a.x() = new_line.a.x(); + line.a.y() = new_line.a.y(); modified = true; } } if(!modified) { - polylines.emplace_back(Polyline(new_line.a, new_line.b)); + lines.push_back(new_line); } } @@ -182,8 +186,8 @@ std::unique_ptr FillAdaptive::build_octree( AABBTreeIndirect::Tree3f aabbTree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set( triangle_mesh.its.vertices, triangle_mesh.its.indices); - std::unique_ptr octree = std::unique_ptr( - new Octree{std::unique_ptr(new Cube{cube_center, cubes_properties.size() - 1, cubes_properties.back()}), cube_center}); + auto octree = std::make_unique( + std::make_unique(cube_center, cubes_properties.size() - 1, cubes_properties.back()), cube_center); FillAdaptive::expand_cube(octree->root_cube.get(), cubes_properties, rotation_matrix, aabbTree, triangle_mesh); @@ -215,7 +219,7 @@ void FillAdaptive::expand_cube( Vec3d child_center_transformed = cube->center + rotation_matrix * (child_center * (cube->properties.edge_length / 4)); if(AABBTreeIndirect::is_any_triangle_in_radius(triangleMesh.its.vertices, triangleMesh.its.indices, distanceTree, child_center_transformed, cube_radius_squared)) { - cube->children.push_back(std::unique_ptr(new Cube{child_center_transformed, cube->depth - 1, cubes_properties[cube->depth - 1]})); + cube->children.emplace_back(std::make_unique(child_center_transformed, cube->depth - 1, cubes_properties[cube->depth - 1])); FillAdaptive::expand_cube(cube->children.back().get(), cubes_properties, rotation_matrix, distanceTree, triangleMesh); } } diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index c7539df5a7..570318aa40 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -24,12 +24,18 @@ namespace FillAdaptive_Internal size_t depth; CubeProperties properties; std::vector> children; + + Cube(const Vec3d ¢er, size_t depth, const CubeProperties &properties) + : center(center), depth(depth), properties(properties) {} }; struct Octree { std::unique_ptr root_cube; Vec3d origin; + + Octree(std::unique_ptr rootCube, const Vec3d &origin) + : root_cube(std::move(rootCube)), origin(origin) {} }; }; // namespace FillAdaptive_Internal @@ -54,9 +60,9 @@ protected: virtual bool no_sort() const { return true; } - void generate_polylines(FillAdaptive_Internal::Cube *cube, double z_position, const Vec3d &origin, std::vector &polylines_out); + void generate_infill_lines(FillAdaptive_Internal::Cube *cube, double z_position, const Vec3d &origin, std::vector &dir_lines_out); - void merge_polylines(Polylines &polylines, const Line &new_line); + void connect_lines(Lines &lines, const Line &new_line); public: static std::unique_ptr build_octree( From 03e103fcc89383866d7275fb583579fefee7dc8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 3 Sep 2020 13:05:28 +0200 Subject: [PATCH 457/503] Connect infill to perimeters --- src/libslic3r/Fill/FillAdaptive.cpp | 35 ++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index 91da86b69e..d3246dc18b 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -3,6 +3,7 @@ #include "../Surface.hpp" #include "../Geometry.hpp" #include "../AABBTreeIndirect.hpp" +#include "../ShortestPath.hpp" #include "FillAdaptive.hpp" @@ -15,19 +16,47 @@ void FillAdaptive::_fill_surface_single( ExPolygon &expolygon, Polylines &polylines_out) { + // Store grouped lines by its direction (multiple of 120°) std::vector infill_lines_dir(3); this->generate_infill_lines(this->adapt_fill_octree->root_cube.get(), this->z, this->adapt_fill_octree->origin, infill_lines_dir); + Polylines all_polylines; + all_polylines.reserve(infill_lines_dir[0].size() * 3); for (Lines &infill_lines : infill_lines_dir) { for (const Line &line : infill_lines) { - polylines_out.emplace_back(line.a, line.b); + all_polylines.emplace_back(line.a, line.b); } } - // Crop all polylines - polylines_out = intersection_pl(polylines_out, to_polygons(expolygon)); + if (params.dont_connect) + { + // Crop all polylines + polylines_out = intersection_pl(all_polylines, to_polygons(expolygon)); + } + else + { + // Crop all polylines + all_polylines = intersection_pl(all_polylines, to_polygons(expolygon)); + + Polylines boundary_polylines; + Polylines non_boundary_polylines; + for (const Polyline &polyline : all_polylines) + { + // connect_infill required all polylines to touch the boundary. + if(polyline.lines().size() == 1 && expolygon.has_boundary_point(polyline.lines().front().a) && expolygon.has_boundary_point(polyline.lines().front().b)) + { + boundary_polylines.push_back(polyline); + } else { + non_boundary_polylines.push_back(polyline); + } + } + + boundary_polylines = chain_polylines(boundary_polylines); + FillAdaptive::connect_infill(std::move(boundary_polylines), expolygon, polylines_out, this->spacing, params); + polylines_out.insert(polylines_out.end(), non_boundary_polylines.begin(), non_boundary_polylines.end()); + } #ifdef SLIC3R_DEBUG_SLICE_PROCESSING { From 000987451a6a537d752e5c683dd7f69018273dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 3 Sep 2020 14:28:25 +0200 Subject: [PATCH 458/503] Fix bug in lines merging --- src/libslic3r/Fill/FillAdaptive.cpp | 30 +++++++++++++---------------- src/libslic3r/Fill/FillAdaptive.hpp | 2 +- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index d3246dc18b..030debad62 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -145,35 +145,31 @@ void FillAdaptive::generate_infill_lines( } } -void FillAdaptive::connect_lines(Lines &lines, const Line &new_line) +void FillAdaptive::connect_lines(Lines &lines, Line new_line) { int eps = scale_(0.10); - bool modified = false; - - for (Line &line : lines) + for (size_t i = 0; i < lines.size(); ++i) { - if (std::abs(new_line.a.x() - line.b.x()) < eps && std::abs(new_line.a.y() - line.b.y()) < eps) + if (std::abs(new_line.a.x() - lines[i].b.x()) < eps && std::abs(new_line.a.y() - lines[i].b.y()) < eps) { - line.b.x() = new_line.b.x(); - line.b.y() = new_line.b.y(); - modified = true; + new_line.a = lines[i].a; + lines.erase(lines.begin() + i); + --i; + continue; } - if (std::abs(new_line.b.x() - line.a.x()) < eps && std::abs(new_line.b.y() - line.a.y()) < eps) + if (std::abs(new_line.b.x() - lines[i].a.x()) < eps && std::abs(new_line.b.y() - lines[i].a.y()) < eps) { - line.a.x() = new_line.a.x(); - line.a.y() = new_line.a.y(); - modified = true; + new_line.b = lines[i].b; + lines.erase(lines.begin() + i); + --i; + continue; } } - if(!modified) - { - lines.push_back(new_line); - } + lines.emplace_back(new_line.a, new_line.b); } - std::unique_ptr FillAdaptive::build_octree( TriangleMesh &triangle_mesh, coordf_t line_spacing, diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index 570318aa40..44a2536f00 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -62,7 +62,7 @@ protected: void generate_infill_lines(FillAdaptive_Internal::Cube *cube, double z_position, const Vec3d &origin, std::vector &dir_lines_out); - void connect_lines(Lines &lines, const Line &new_line); + static void connect_lines(Lines &lines, Line new_line); public: static std::unique_ptr build_octree( From acedb66cdc5abb6d5f9c2d575eefb9a4f834a00a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 3 Sep 2020 16:08:40 +0200 Subject: [PATCH 459/503] Change to using raw_mesh instead of mesh --- src/libslic3r/PrintObject.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 1236a297fc..d9c533939c 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -447,11 +447,14 @@ std::unique_ptr PrintObject::prepare_adaptive_inf coordf_t line_spacing = infill_extrusion_width / ((fill_density / 100.0f) * 0.333333333f); - // Center of the first cube in octree - Vec3d model_center = this->model_object()->bounding_box().center(); + TriangleMesh mesh = this->model_object()->raw_mesh(); + mesh.transform(m_trafo, true); + // Apply XY shift + mesh.translate(- unscale(m_center_offset.x()), - unscale(m_center_offset.y()), 0); - TriangleMesh mesh = this->model_object()->mesh(); - return FillAdaptive::build_octree(mesh, line_spacing, model_center); + // Center of the first cube in octree + Vec3d mesh_origin = mesh.bounding_box().center(); + return FillAdaptive::build_octree(mesh, line_spacing, mesh_origin); } void PrintObject::clear_layers() From aca212c5bca2bd8373c212c23e038c618a02a47e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 3 Sep 2020 19:21:55 +0200 Subject: [PATCH 460/503] Octree representation rework --- src/libslic3r/Fill/FillAdaptive.cpp | 51 ++++++++++++++++++----------- src/libslic3r/Fill/FillAdaptive.hpp | 44 ++++++++++++++----------- 2 files changed, 55 insertions(+), 40 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index 030debad62..577ba7e610 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -18,7 +18,10 @@ void FillAdaptive::_fill_surface_single( { // Store grouped lines by its direction (multiple of 120°) std::vector infill_lines_dir(3); - this->generate_infill_lines(this->adapt_fill_octree->root_cube.get(), this->z, this->adapt_fill_octree->origin, infill_lines_dir); + this->generate_infill_lines(this->adapt_fill_octree->root_cube.get(), + this->z, this->adapt_fill_octree->origin,infill_lines_dir, + this->adapt_fill_octree->cubes_properties, + this->adapt_fill_octree->cubes_properties.size() - 1); Polylines all_polylines; all_polylines.reserve(infill_lines_dir[0].size() * 3); @@ -96,7 +99,9 @@ void FillAdaptive::generate_infill_lines( FillAdaptive_Internal::Cube *cube, double z_position, const Vec3d &origin, - std::vector &dir_lines_out) + std::vector &dir_lines_out, + const std::vector &cubes_properties, + int depth) { using namespace FillAdaptive_Internal; @@ -107,16 +112,16 @@ void FillAdaptive::generate_infill_lines( double z_diff = std::abs(z_position - cube->center.z()); - if (z_diff > cube->properties.height / 2) + if (z_diff > cubes_properties[depth].height / 2) { return; } - if (z_diff < cube->properties.line_z_distance) + if (z_diff < cubes_properties[depth].line_z_distance) { Point from( - scale_((cube->properties.diagonal_length / 2) * (cube->properties.line_z_distance - z_diff) / cube->properties.line_z_distance), - scale_(cube->properties.line_xy_distance - ((z_position - (cube->center.z() - cube->properties.line_z_distance)) / sqrt(2)))); + scale_((cubes_properties[depth].diagonal_length / 2) * (cubes_properties[depth].line_z_distance - z_diff) / cubes_properties[depth].line_z_distance), + scale_(cubes_properties[depth].line_xy_distance - ((z_position - (cube->center.z() - cubes_properties[depth].line_z_distance)) / sqrt(2)))); Point to(-from.x(), from.y()); // Relative to cube center @@ -141,7 +146,10 @@ void FillAdaptive::generate_infill_lines( for(const std::unique_ptr &child : cube->children) { - generate_infill_lines(child.get(), z_position, origin, dir_lines_out); + if(child != nullptr) + { + generate_infill_lines(child.get(), z_position, origin, dir_lines_out, cubes_properties, depth - 1); + } } } @@ -206,15 +214,14 @@ std::unique_ptr FillAdaptive::build_octree( triangle_mesh.require_shared_vertices(); } - Vec3d rotation = Vec3d(Geometry::deg2rad(225.0), Geometry::deg2rad(215.264), Geometry::deg2rad(30.0)); + Vec3d rotation = Vec3d((5.0 * M_PI) / 4.0, Geometry::deg2rad(215.264), M_PI / 6.0); Transform3d rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), rotation, Vec3d::Ones(), Vec3d::Ones()); AABBTreeIndirect::Tree3f aabbTree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set( triangle_mesh.its.vertices, triangle_mesh.its.indices); - auto octree = std::make_unique( - std::make_unique(cube_center, cubes_properties.size() - 1, cubes_properties.back()), cube_center); + auto octree = std::make_unique(std::make_unique(cube_center), cube_center, cubes_properties); - FillAdaptive::expand_cube(octree->root_cube.get(), cubes_properties, rotation_matrix, aabbTree, triangle_mesh); + FillAdaptive::expand_cube(octree->root_cube.get(), cubes_properties, rotation_matrix, aabbTree, triangle_mesh, cubes_properties.size() - 1); return octree; } @@ -223,12 +230,12 @@ void FillAdaptive::expand_cube( FillAdaptive_Internal::Cube *cube, const std::vector &cubes_properties, const Transform3d &rotation_matrix, - const AABBTreeIndirect::Tree3f &distanceTree, - const TriangleMesh &triangleMesh) + const AABBTreeIndirect::Tree3f &distance_tree, + const TriangleMesh &triangle_mesh, int depth) { using namespace FillAdaptive_Internal; - if (cube == nullptr || cube->depth == 0) + if (cube == nullptr || depth == 0) { return; } @@ -238,14 +245,18 @@ void FillAdaptive::expand_cube( Vec3d( 1, 1, 1), Vec3d(-1, 1, 1), Vec3d( 1, -1, 1), Vec3d( 1, 1, -1) }; - double cube_radius_squared = (cube->properties.height * cube->properties.height) / 16; + double cube_radius_squared = (cubes_properties[depth].height * cubes_properties[depth].height) / 16; - for (const Vec3d &child_center : child_centers) { - Vec3d child_center_transformed = cube->center + rotation_matrix * (child_center * (cube->properties.edge_length / 4)); + for (size_t i = 0; i < 8; ++i) + { + const Vec3d &child_center = child_centers[i]; + Vec3d child_center_transformed = cube->center + rotation_matrix * (child_center * (cubes_properties[depth].edge_length / 4)); - if(AABBTreeIndirect::is_any_triangle_in_radius(triangleMesh.its.vertices, triangleMesh.its.indices, distanceTree, child_center_transformed, cube_radius_squared)) { - cube->children.emplace_back(std::make_unique(child_center_transformed, cube->depth - 1, cubes_properties[cube->depth - 1])); - FillAdaptive::expand_cube(cube->children.back().get(), cubes_properties, rotation_matrix, distanceTree, triangleMesh); + if(AABBTreeIndirect::is_any_triangle_in_radius(triangle_mesh.its.vertices, triangle_mesh.its.indices, + distance_tree, child_center_transformed, cube_radius_squared)) + { + cube->children[i] = std::make_unique(child_center_transformed); + FillAdaptive::expand_cube(cube->children[i].get(), cubes_properties, rotation_matrix, distance_tree, triangle_mesh, depth - 1); } } } diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index 44a2536f00..14694b766c 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -21,21 +21,18 @@ namespace FillAdaptive_Internal struct Cube { Vec3d center; - size_t depth; - CubeProperties properties; - std::vector> children; - - Cube(const Vec3d ¢er, size_t depth, const CubeProperties &properties) - : center(center), depth(depth), properties(properties) {} + std::unique_ptr children[8] = {}; + Cube(const Vec3d ¢er) : center(center) {} }; struct Octree { std::unique_ptr root_cube; Vec3d origin; + std::vector cubes_properties; - Octree(std::unique_ptr rootCube, const Vec3d &origin) - : root_cube(std::move(rootCube)), origin(origin) {} + Octree(std::unique_ptr rootCube, const Vec3d &origin, const std::vector &cubes_properties) + : root_cube(std::move(rootCube)), origin(origin), cubes_properties(cubes_properties) {} }; }; // namespace FillAdaptive_Internal @@ -52,30 +49,37 @@ public: protected: virtual Fill* clone() const { return new FillAdaptive(*this); }; virtual void _fill_surface_single( - const FillParams ¶ms, + const FillParams ¶ms, unsigned int thickness_layers, - const std::pair &direction, - ExPolygon &expolygon, + const std::pair &direction, + ExPolygon &expolygon, Polylines &polylines_out); virtual bool no_sort() const { return true; } - void generate_infill_lines(FillAdaptive_Internal::Cube *cube, double z_position, const Vec3d &origin, std::vector &dir_lines_out); + void generate_infill_lines( + FillAdaptive_Internal::Cube *cube, + double z_position, + const Vec3d & origin, + std::vector & dir_lines_out, + const std::vector &cubes_properties, + int depth); static void connect_lines(Lines &lines, Line new_line); public: static std::unique_ptr build_octree( - TriangleMesh &triangle_mesh, - coordf_t line_spacing, - const Vec3d &cube_center); + TriangleMesh &triangle_mesh, + coordf_t line_spacing, + const Vec3d & cube_center); static void expand_cube( - FillAdaptive_Internal::Cube *cube, - const std::vector &cubes_properties, - const Transform3d &rotation_matrix, - const AABBTreeIndirect::Tree3f &distanceTree, - const TriangleMesh &triangleMesh); + FillAdaptive_Internal::Cube *cube, + const std::vector &cubes_properties, + const Transform3d & rotation_matrix, + const AABBTreeIndirect::Tree3f &distance_tree, + const TriangleMesh & triangle_mesh, + int depth); }; } // namespace Slic3r From 5633526ecfa9215180a6009c4c300cbedf24fa83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 3 Sep 2020 23:15:46 +0200 Subject: [PATCH 461/503] Enable changing adaptive infill density for different objects --- src/libslic3r/PrintObject.cpp | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index d9c533939c..167be8a36b 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -434,17 +434,34 @@ void PrintObject::generate_support_material() std::unique_ptr PrintObject::prepare_adaptive_infill_data() { - const ConfigOptionPercent* opt_fill_density = this->print()->full_print_config().option("fill_density"); - const ConfigOptionFloatOrPercent* opt_infill_extrusion_width = this->print()->full_print_config().option("infill_extrusion_width"); + float fill_density = 0; + float infill_extrusion_width = 0; - if(opt_fill_density == nullptr || opt_infill_extrusion_width == nullptr || opt_fill_density->value <= 0 || opt_infill_extrusion_width->value <= 0) + // Compute the average of above parameters over all layers + for (size_t layer_idx = 0; layer_idx < this->m_layers.size(); ++layer_idx) + { + for (size_t region_id = 0; region_id < this->m_layers[layer_idx]->m_regions.size(); ++region_id) + { + LayerRegion *layerm = this->m_layers[layer_idx]->m_regions[region_id]; + + // Check if region_id is used for this layer + if(!layerm->fill_surfaces.surfaces.empty()) { + const PrintRegionConfig ®ion_config = layerm->region()->config(); + + fill_density += region_config.fill_density; + infill_extrusion_width += region_config.infill_extrusion_width; + } + } + } + + fill_density /= this->m_layers.size(); + infill_extrusion_width /= this->m_layers.size(); + + if(fill_density <= 0 || infill_extrusion_width <= 0) { return std::unique_ptr{}; } - float fill_density = opt_fill_density->value; - float infill_extrusion_width = opt_infill_extrusion_width->value; - coordf_t line_spacing = infill_extrusion_width / ((fill_density / 100.0f) * 0.333333333f); TriangleMesh mesh = this->model_object()->raw_mesh(); From 5e9399247c414dc8b41db9b8ab8622754a0209eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 7 Sep 2020 09:14:06 +0200 Subject: [PATCH 462/503] Check if exist any boundary polyline --- src/libslic3r/Fill/FillAdaptive.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index 577ba7e610..bf9cd7f9d2 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -51,13 +51,19 @@ void FillAdaptive::_fill_surface_single( if(polyline.lines().size() == 1 && expolygon.has_boundary_point(polyline.lines().front().a) && expolygon.has_boundary_point(polyline.lines().front().b)) { boundary_polylines.push_back(polyline); - } else { + } + else + { non_boundary_polylines.push_back(polyline); } } - boundary_polylines = chain_polylines(boundary_polylines); - FillAdaptive::connect_infill(std::move(boundary_polylines), expolygon, polylines_out, this->spacing, params); + if(!boundary_polylines.empty()) + { + boundary_polylines = chain_polylines(boundary_polylines); + FillAdaptive::connect_infill(std::move(boundary_polylines), expolygon, polylines_out, this->spacing, params); + } + polylines_out.insert(polylines_out.end(), non_boundary_polylines.begin(), non_boundary_polylines.end()); } From 2f9dd9d9e80a628c792d209ae3a4e694f4abd333 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 9 Sep 2020 15:03:51 +0200 Subject: [PATCH 463/503] Completed implementation of 'File->GCode preview...' command --- src/PrusaSlicer.cpp | 123 ++++++++++++++++++++++------------ src/libslic3r/GCodeReader.cpp | 7 ++ src/slic3r/GUI/GUI_App.cpp | 1 - 3 files changed, 87 insertions(+), 44 deletions(-) diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 9874a9d4d7..7972d49b76 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -140,37 +140,57 @@ int CLI::run(int argc, char **argv) m_print_config.apply(config); } - // Read input file(s) if any. - for (const std::string &file : m_input_files) { - if (! boost::filesystem::exists(file)) { - boost::nowide::cerr << "No such file: " << file << std::endl; - exit(1); +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + // are we starting as gcodeviewer ? + for (auto it = m_actions.begin(); it != m_actions.end(); ++it) { + if (*it == "gcodeviewer") { + start_gui = true; + start_as_gcodeviewer = true; + m_actions.erase(it); + break; } - Model model; - try { - // When loading an AMF or 3MF, config is imported as well, including the printer technology. - DynamicPrintConfig config; - model = Model::read_from_file(file, &config, true); - PrinterTechnology other_printer_technology = Slic3r::printer_technology(config); - if (printer_technology == ptUnknown) { - printer_technology = other_printer_technology; - } else if (printer_technology != other_printer_technology && other_printer_technology != ptUnknown) { - boost::nowide::cerr << "Mixing configurations for FFF and SLA technologies" << std::endl; + } +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + + // Read input file(s) if any. +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (!start_as_gcodeviewer) { +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + for (const std::string& file : m_input_files) { + if (!boost::filesystem::exists(file)) { + boost::nowide::cerr << "No such file: " << file << std::endl; + exit(1); + } + Model model; + try { + // When loading an AMF or 3MF, config is imported as well, including the printer technology. + DynamicPrintConfig config; + model = Model::read_from_file(file, &config, true); + PrinterTechnology other_printer_technology = Slic3r::printer_technology(config); + if (printer_technology == ptUnknown) { + printer_technology = other_printer_technology; + } + else if (printer_technology != other_printer_technology && other_printer_technology != ptUnknown) { + boost::nowide::cerr << "Mixing configurations for FFF and SLA technologies" << std::endl; + return 1; + } + // config is applied to m_print_config before the current m_config values. + config += std::move(m_print_config); + m_print_config = std::move(config); + } + catch (std::exception& e) { + boost::nowide::cerr << file << ": " << e.what() << std::endl; return 1; } - // config is applied to m_print_config before the current m_config values. - config += std::move(m_print_config); - m_print_config = std::move(config); - } catch (std::exception &e) { - boost::nowide::cerr << file << ": " << e.what() << std::endl; - return 1; + if (model.objects.empty()) { + boost::nowide::cerr << "Error: file is empty: " << file << std::endl; + continue; + } + m_models.push_back(model); } - if (model.objects.empty()) { - boost::nowide::cerr << "Error: file is empty: " << file << std::endl; - continue; - } - m_models.push_back(model); +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION } +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION // Apply command line options to a more specific DynamicPrintConfig which provides normalize() // (command line options override --load files) @@ -529,9 +549,11 @@ int CLI::run(int argc, char **argv) << " (" << print.total_extruded_volume()/1000 << "cm3)" << std::endl; */ } +#if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION } else if (opt_key == "gcodeviewer") { - start_gui = true; + start_gui = true; start_as_gcodeviewer = true; +#endif // !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION } else { boost::nowide::cerr << "error: option not supported yet: " << opt_key << std::endl; return 1; @@ -555,28 +577,43 @@ int CLI::run(int argc, char **argv) // gui->autosave = m_config.opt_string("autosave"); GUI::GUI_App::SetInstance(gui); +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + gui->CallAfter([gui, this, &load_configs, start_as_gcodeviewer] { +#else gui->CallAfter([gui, this, &load_configs] { +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (!gui->initialized()) { return; } + +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + if (start_as_gcodeviewer) { + if (!m_input_files.empty()) + gui->plater()->load_gcode(wxString::FromUTF8(m_input_files[0])); + } else { +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #if 0 - // Load the cummulative config over the currently active profiles. - //FIXME if multiple configs are loaded, only the last one will have an effect. - // We need to decide what to do about loading of separate presets (just print preset, just filament preset etc). - // As of now only the full configs are supported here. - if (!m_print_config.empty()) - gui->mainframe->load_config(m_print_config); + // Load the cummulative config over the currently active profiles. + //FIXME if multiple configs are loaded, only the last one will have an effect. + // We need to decide what to do about loading of separate presets (just print preset, just filament preset etc). + // As of now only the full configs are supported here. + if (!m_print_config.empty()) + gui->mainframe->load_config(m_print_config); #endif - if (! load_configs.empty()) - // Load the last config to give it a name at the UI. The name of the preset may be later - // changed by loading an AMF or 3MF. - //FIXME this is not strictly correct, as one may pass a print/filament/printer profile here instead of a full config. - gui->mainframe->load_config_file(load_configs.back()); - // If loading a 3MF file, the config is loaded from the last one. - if (! m_input_files.empty()) - gui->plater()->load_files(m_input_files, true, true); - if (! m_extra_config.empty()) - gui->mainframe->load_config(m_extra_config); + if (!load_configs.empty()) + // Load the last config to give it a name at the UI. The name of the preset may be later + // changed by loading an AMF or 3MF. + //FIXME this is not strictly correct, as one may pass a print/filament/printer profile here instead of a full config. + gui->mainframe->load_config_file(load_configs.back()); + // If loading a 3MF file, the config is loaded from the last one. + if (!m_input_files.empty()) + gui->plater()->load_files(m_input_files, true, true); + if (!m_extra_config.empty()) + gui->mainframe->load_config(m_extra_config); +#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + } +#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION }); int result = wxEntry(argc, argv); return result; diff --git a/src/libslic3r/GCodeReader.cpp b/src/libslic3r/GCodeReader.cpp index ab77b01413..ee24d5bb76 100644 --- a/src/libslic3r/GCodeReader.cpp +++ b/src/libslic3r/GCodeReader.cpp @@ -1,6 +1,9 @@ #include "GCodeReader.hpp" #include #include +#if ENABLE_GCODE_VIEWER +#include +#endif // ENABLE_GCODE_VIEWER #include #include #include @@ -113,7 +116,11 @@ void GCodeReader::update_coordinates(GCodeLine &gline, std::pair Date: Wed, 9 Sep 2020 15:55:06 +0200 Subject: [PATCH 464/503] Refactoring of adaptive cubic infill: Don't create an octree for the infill if it is not needed. --- src/libslic3r/Fill/FillAdaptive.cpp | 97 ++++++++++++++++++++++++++--- src/libslic3r/Fill/FillAdaptive.hpp | 8 +++ src/libslic3r/PrintObject.cpp | 33 +--------- 3 files changed, 100 insertions(+), 38 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index bf9cd7f9d2..b1a40047d4 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -3,12 +3,93 @@ #include "../Surface.hpp" #include "../Geometry.hpp" #include "../AABBTreeIndirect.hpp" +#include "../Layer.hpp" +#include "../Print.hpp" #include "../ShortestPath.hpp" #include "FillAdaptive.hpp" namespace Slic3r { +std::pair adaptive_fill_line_spacing(const PrintObject &print_object) +{ + // Output, spacing for icAdaptiveCubic and icSupportCubic + double adaptive_line_spacing = 0.; + double support_line_spacing = 0.; + + enum class Tristate { + Yes, + No, + Maybe + }; + struct RegionFillData { + Tristate has_adaptive_infill; + Tristate has_support_infill; + double density; + double extrusion_width; + }; + std::vector region_fill_data; + region_fill_data.reserve(print_object.print()->regions().size()); + bool build_octree = false; + for (const PrintRegion *region : print_object.print()->regions()) { + const PrintRegionConfig &config = region->config(); + bool nonempty = config.fill_density > 0; + bool has_adaptive_infill = nonempty && config.fill_pattern == ipAdaptiveCubic; + bool has_support_infill = nonempty && false; // config.fill_pattern == icSupportCubic; + region_fill_data.push_back(RegionFillData({ + has_adaptive_infill ? Tristate::Maybe : Tristate::No, + has_support_infill ? Tristate::Maybe : Tristate::No, + config.fill_density, + config.infill_extrusion_width + })); + build_octree |= has_adaptive_infill || has_support_infill; + } + + if (build_octree) { + // Compute the average of above parameters over all layers + for (const Layer *layer : print_object.layers()) + for (size_t region_id = 0; region_id < layer->regions().size(); ++ region_id) { + RegionFillData &rd = region_fill_data[region_id]; + if (rd.has_adaptive_infill == Tristate::Maybe && ! layer->regions()[region_id]->fill_surfaces.empty()) + rd.has_adaptive_infill = Tristate::Yes; + if (rd.has_support_infill == Tristate::Maybe && ! layer->regions()[region_id]->fill_surfaces.empty()) + rd.has_support_infill = Tristate::Yes; + } + + double adaptive_fill_density = 0.; + double adaptive_infill_extrusion_width = 0.; + int adaptive_cnt = 0; + double support_fill_density = 0.; + double support_infill_extrusion_width = 0.; + int support_cnt = 0; + + for (const RegionFillData &rd : region_fill_data) { + if (rd.has_adaptive_infill == Tristate::Yes) { + adaptive_fill_density += rd.density; + adaptive_infill_extrusion_width += rd.extrusion_width; + ++ adaptive_cnt; + } else if (rd.has_support_infill == Tristate::Yes) { + support_fill_density += rd.density; + support_infill_extrusion_width += rd.extrusion_width; + ++ support_cnt; + } + } + + auto to_line_spacing = [](int cnt, double density, double extrusion_width) { + if (cnt) { + density /= double(cnt); + extrusion_width /= double(cnt); + return extrusion_width / ((density / 100.0f) * 0.333333333f); + } else + return 0.; + }; + adaptive_line_spacing = to_line_spacing(adaptive_cnt, adaptive_fill_density, adaptive_infill_extrusion_width); + support_line_spacing = to_line_spacing(support_cnt, support_fill_density, support_infill_extrusion_width); + } + + return std::make_pair(adaptive_line_spacing, support_line_spacing); +} + void FillAdaptive::_fill_surface_single( const FillParams ¶ms, unsigned int thickness_layers, @@ -21,7 +102,7 @@ void FillAdaptive::_fill_surface_single( this->generate_infill_lines(this->adapt_fill_octree->root_cube.get(), this->z, this->adapt_fill_octree->origin,infill_lines_dir, this->adapt_fill_octree->cubes_properties, - this->adapt_fill_octree->cubes_properties.size() - 1); + int(this->adapt_fill_octree->cubes_properties.size()) - 1); Polylines all_polylines; all_polylines.reserve(infill_lines_dir[0].size() * 3); @@ -131,16 +212,16 @@ void FillAdaptive::generate_infill_lines( Point to(-from.x(), from.y()); // Relative to cube center - float rotation_angle = (2.0 * M_PI) / 3.0; + double rotation_angle = (2.0 * M_PI) / 3.0; for (Lines &lines : dir_lines_out) { Vec3d offset = cube->center - origin; Point from_abs(from), to_abs(to); - from_abs.x() += scale_(offset.x()); - from_abs.y() += scale_(offset.y()); - to_abs.x() += scale_(offset.x()); - to_abs.y() += scale_(offset.y()); + from_abs.x() += int(scale_(offset.x())); + from_abs.y() += int(scale_(offset.y())); + to_abs.x() += int(scale_(offset.x())); + to_abs.y() += int(scale_(offset.y())); // lines.emplace_back(from_abs, to_abs); this->connect_lines(lines, Line(from_abs, to_abs)); @@ -161,7 +242,7 @@ void FillAdaptive::generate_infill_lines( void FillAdaptive::connect_lines(Lines &lines, Line new_line) { - int eps = scale_(0.10); + auto eps = int(scale_(0.10)); for (size_t i = 0; i < lines.size(); ++i) { if (std::abs(new_line.a.x() - lines[i].b.x()) < eps && std::abs(new_line.a.y() - lines[i].b.y()) < eps) @@ -227,7 +308,7 @@ std::unique_ptr FillAdaptive::build_octree( triangle_mesh.its.vertices, triangle_mesh.its.indices); auto octree = std::make_unique(std::make_unique(cube_center), cube_center, cubes_properties); - FillAdaptive::expand_cube(octree->root_cube.get(), cubes_properties, rotation_matrix, aabbTree, triangle_mesh, cubes_properties.size() - 1); + FillAdaptive::expand_cube(octree->root_cube.get(), cubes_properties, rotation_matrix, aabbTree, triangle_mesh, int(cubes_properties.size()) - 1); return octree; } diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index 14694b766c..dd7394384c 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -7,6 +7,8 @@ namespace Slic3r { +class PrintObject; + namespace FillAdaptive_Internal { struct CubeProperties @@ -82,6 +84,12 @@ public: int depth); }; +// Calculate line spacing for +// 1) adaptive cubic infill +// 2) adaptive internal support cubic infill +// Returns zero for a particular infill type if no such infill is to be generated. +std::pair adaptive_fill_line_spacing(const PrintObject &print_object); + } // namespace Slic3r #endif // slic3r_FillAdaptive_hpp_ diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 087d3fe3c5..41a01e653c 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -434,44 +434,17 @@ void PrintObject::generate_support_material() std::unique_ptr PrintObject::prepare_adaptive_infill_data() { - float fill_density = 0; - float infill_extrusion_width = 0; - - // Compute the average of above parameters over all layers - for (size_t layer_idx = 0; layer_idx < this->m_layers.size(); ++layer_idx) - { - for (size_t region_id = 0; region_id < this->m_layers[layer_idx]->m_regions.size(); ++region_id) - { - LayerRegion *layerm = this->m_layers[layer_idx]->m_regions[region_id]; - - // Check if region_id is used for this layer - if(!layerm->fill_surfaces.surfaces.empty()) { - const PrintRegionConfig ®ion_config = layerm->region()->config(); - - fill_density += region_config.fill_density; - infill_extrusion_width += region_config.infill_extrusion_width; - } - } - } - - fill_density /= this->m_layers.size(); - infill_extrusion_width /= this->m_layers.size(); - - if(fill_density <= 0 || infill_extrusion_width <= 0) - { + auto [adaptive_line_spacing, support_line_spacing] = adaptive_fill_line_spacing(*this); + if (adaptive_line_spacing == 0.) return std::unique_ptr{}; - } - - coordf_t line_spacing = infill_extrusion_width / ((fill_density / 100.0f) * 0.333333333f); TriangleMesh mesh = this->model_object()->raw_mesh(); mesh.transform(m_trafo, true); // Apply XY shift mesh.translate(- unscale(m_center_offset.x()), - unscale(m_center_offset.y()), 0); - // Center of the first cube in octree Vec3d mesh_origin = mesh.bounding_box().center(); - return FillAdaptive::build_octree(mesh, line_spacing, mesh_origin); + return FillAdaptive::build_octree(mesh, adaptive_line_spacing, mesh_origin); } void PrintObject::clear_layers() From 88457bf4124310bdaca8d37e5b16e122b415e348 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 10 Sep 2020 08:49:50 +0200 Subject: [PATCH 465/503] Tech ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION set as default --- src/PrusaSlicer.cpp | 33 ++-- src/libslic3r/AppConfig.cpp | 4 +- src/libslic3r/AppConfig.hpp | 12 +- src/libslic3r/Technologies.hpp | 1 - src/slic3r/GUI/GCodeViewer.cpp | 8 - src/slic3r/GUI/GLCanvas3D.cpp | 16 -- src/slic3r/GUI/GUI_App.cpp | 40 ++--- src/slic3r/GUI/GUI_App.hpp | 16 +- src/slic3r/GUI/GUI_Preview.cpp | 4 - src/slic3r/GUI/GUI_Preview.hpp | 10 +- src/slic3r/GUI/KBShortcutsDialog.cpp | 8 - src/slic3r/GUI/MainFrame.cpp | 233 ++------------------------- src/slic3r/GUI/MainFrame.hpp | 35 +--- src/slic3r/GUI/Plater.cpp | 50 +----- 14 files changed, 77 insertions(+), 393 deletions(-) diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 7972d49b76..05e84b9416 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -140,7 +140,7 @@ int CLI::run(int argc, char **argv) m_print_config.apply(config); } -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER // are we starting as gcodeviewer ? for (auto it = m_actions.begin(); it != m_actions.end(); ++it) { if (*it == "gcodeviewer") { @@ -150,12 +150,12 @@ int CLI::run(int argc, char **argv) break; } } -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER // Read input file(s) if any. -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER if (!start_as_gcodeviewer) { -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER for (const std::string& file : m_input_files) { if (!boost::filesystem::exists(file)) { boost::nowide::cerr << "No such file: " << file << std::endl; @@ -188,9 +188,9 @@ int CLI::run(int argc, char **argv) } m_models.push_back(model); } -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER } -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER // Apply command line options to a more specific DynamicPrintConfig which provides normalize() // (command line options override --load files) @@ -549,11 +549,11 @@ int CLI::run(int argc, char **argv) << " (" << print.total_extruded_volume()/1000 << "cm3)" << std::endl; */ } -#if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if !ENABLE_GCODE_VIEWER } else if (opt_key == "gcodeviewer") { start_gui = true; start_as_gcodeviewer = true; -#endif // !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // !ENABLE_GCODE_VIEWER } else { boost::nowide::cerr << "error: option not supported yet: " << opt_key << std::endl; return 1; @@ -563,11 +563,11 @@ int CLI::run(int argc, char **argv) if (start_gui) { #ifdef SLIC3R_GUI // #ifdef USE_WX -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER GUI::GUI_App* gui = new GUI::GUI_App(start_as_gcodeviewer ? GUI::GUI_App::EAppMode::GCodeViewer : GUI::GUI_App::EAppMode::Editor); #else GUI::GUI_App *gui = new GUI::GUI_App(); -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER bool gui_single_instance_setting = gui->app_config->get("single_instance") == "1"; if (Slic3r::instance_check(argc, argv, gui_single_instance_setting)) { @@ -577,22 +577,21 @@ int CLI::run(int argc, char **argv) // gui->autosave = m_config.opt_string("autosave"); GUI::GUI_App::SetInstance(gui); -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER gui->CallAfter([gui, this, &load_configs, start_as_gcodeviewer] { #else gui->CallAfter([gui, this, &load_configs] { -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION - +#endif // ENABLE_GCODE_VIEWER if (!gui->initialized()) { return; } -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER if (start_as_gcodeviewer) { if (!m_input_files.empty()) gui->plater()->load_gcode(wxString::FromUTF8(m_input_files[0])); } else { -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER_AS #if 0 // Load the cummulative config over the currently active profiles. //FIXME if multiple configs are loaded, only the last one will have an effect. @@ -611,9 +610,9 @@ int CLI::run(int argc, char **argv) gui->plater()->load_files(m_input_files, true, true); if (!m_extra_config.empty()) gui->mainframe->load_config(m_extra_config); -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER } -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER }); int result = wxEntry(argc, argv); return result; diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index db3bd78ddf..2d96e0b50d 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -179,10 +179,10 @@ std::string AppConfig::load() void AppConfig::save() { -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER if (!m_save_enabled) return; -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER // The config is first written to a file with a PID suffix and then moved // to avoid race conditions with multiple instances of Slic3r diff --git a/src/libslic3r/AppConfig.hpp b/src/libslic3r/AppConfig.hpp index 3f4ce20089..f22a6314ab 100644 --- a/src/libslic3r/AppConfig.hpp +++ b/src/libslic3r/AppConfig.hpp @@ -18,9 +18,9 @@ public: AppConfig() : m_dirty(false), m_orig_version(Semver::invalid()), -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER m_save_enabled(true), -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER m_legacy_datadir(false) { this->reset(); @@ -160,9 +160,9 @@ public: bool get_mouse_device_swap_yz(const std::string& name, bool& swap) const { return get_3dmouse_device_numeric_value(name, "swap_yz", swap); } -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER void enable_save(bool enable) { m_save_enabled = enable; } -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER static const std::string SECTION_FILAMENTS; static const std::string SECTION_MATERIALS; @@ -190,10 +190,10 @@ private: bool m_dirty; // Original version found in the ini file before it was overwritten Semver m_orig_version; -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER // Whether or not calls to save() should take effect bool m_save_enabled; -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER // Whether the existing version is before system profiles & configuration updating bool m_legacy_datadir; }; diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 2dbad472fe..a0484b259c 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -59,6 +59,5 @@ #define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_DATA_CHECKING (0 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_TASKBAR_ICON (0 && ENABLE_GCODE_VIEWER) -#define ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION (1 && ENABLE_GCODE_VIEWER) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 2b9bf8ca46..5984afaa45 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -339,11 +339,7 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& reset(); load_toolpaths(gcode_result); -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION if (wxGetApp().is_editor()) -#else - if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION load_shells(print, initialized); else { Pointfs bed_shape; @@ -879,11 +875,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) for (size_t i = 0; i < m_vertices_count; ++i) { const GCodeProcessor::MoveVertex& move = gcode_result.moves[i]; -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION if (wxGetApp().is_gcode_viewer()) -#else - if (wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer) -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION // for the gcode viewer we need all moves to correctly size the printbed m_paths_bounding_box.merge(move.position.cast()); else { diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 2f9f9464cd..00034087c7 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2732,11 +2732,7 @@ static void load_gcode_retractions(const GCodePreviewData::Retraction& retractio void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result) { m_gcode_viewer.load(gcode_result, *this->fff_print(), m_initialized); -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION if (wxGetApp().is_editor()) -#else - if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION _show_warning_texture_if_needed(WarningTexture::ToolpathOutside); } @@ -4306,11 +4302,7 @@ void GLCanvas3D::update_ui_from_settings() #endif // ENABLE_RETINA_GL #if ENABLE_GCODE_VIEWER -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION if (wxGetApp().is_editor()) -#else - if (wxGetApp().mainframe != nullptr && wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION wxGetApp().plater()->get_collapse_toolbar().set_enabled(wxGetApp().app_config->get("show_collapse_button") == "1"); #else bool enable_collapse = wxGetApp().app_config->get("show_collapse_button") == "1"; @@ -5413,11 +5405,7 @@ void GLCanvas3D::_render_background() const { #if ENABLE_GCODE_VIEWER bool use_error_color = false; -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION if (wxGetApp().is_editor()) { -#else - if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) { -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION use_error_color = m_dynamic_background_enabled; if (!m_volumes.empty()) use_error_color &= _is_any_volume_outside(); @@ -7146,11 +7134,7 @@ void GLCanvas3D::_show_warning_texture_if_needed(WarningTexture::Warning warning if (!m_volumes.empty()) show = _is_any_volume_outside(); else { -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION if (wxGetApp().is_editor()) { -#else - if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) { -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box(); if (test_volume.radius() > 0.0 && paths_volume.radius() > 0.0) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index aeac415f7b..f6b0a44146 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -434,15 +434,15 @@ static void generic_exception_handle() IMPLEMENT_APP(GUI_App) -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER GUI_App::GUI_App(EAppMode mode) #else GUI_App::GUI_App() -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER : wxApp() -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER , m_app_mode(mode) -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER , m_em_unit(10) , m_imgui(new ImGuiWrapper()) , m_wizard(nullptr) @@ -498,11 +498,11 @@ void GUI_App::init_app_config() if (!app_config) app_config = new AppConfig(); -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER if (is_gcode_viewer()) // disable config save to avoid to mess it up for the editor app_config->enable_save(false); -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER // load settings app_conf_exists = app_config->exists(); @@ -577,11 +577,11 @@ bool GUI_App::on_init_inner() wxInitAllImageHandlers(); wxBitmap bitmap = create_scaled_bitmap("prusa_slicer_logo", nullptr, 400); -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER wxBitmap bmp(is_editor() ? from_u8(var("splashscreen.jpg")) : from_u8(var("splashscreen-gcodeviewer.jpg")), wxBITMAP_TYPE_JPEG); #else wxBitmap bmp(from_u8(var("splashscreen.jpg")), wxBITMAP_TYPE_JPEG); -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER DecorateSplashScreen(bmp); @@ -594,9 +594,9 @@ bool GUI_App::on_init_inner() // supplied as argument to --datadir; in that case we should still run the wizard preset_bundle->setup_directories(); -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER if (is_editor()) { -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER #ifdef __WXMSW__ associate_3mf_files(); #endif // __WXMSW__ @@ -611,9 +611,9 @@ bool GUI_App::on_init_inner() } } }); -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER } -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER // initialize label colors and fonts init_label_colours(); @@ -641,9 +641,9 @@ bool GUI_App::on_init_inner() Slic3r::I18N::set_translate_callback(libslic3r_translate_callback); // application frame -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER if (is_editor()) -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER scrn->SetText(_L("Creating settings tabs...")); mainframe = new MainFrame(); @@ -679,9 +679,9 @@ bool GUI_App::on_init_inner() static bool once = true; if (once) { once = false; -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER if (preset_updater != nullptr) { -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER check_updates(false); CallAfter([this] { @@ -689,9 +689,9 @@ bool GUI_App::on_init_inner() preset_updater->slic3r_update_notify(); preset_updater->sync(preset_bundle); }); -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER } -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER #ifdef _WIN32 //sets window property to mainframe so other instances can indentify it @@ -700,7 +700,7 @@ bool GUI_App::on_init_inner() } }); -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER if (is_gcode_viewer()) { mainframe->update_layout(); if (plater_ != nullptr) @@ -708,7 +708,7 @@ bool GUI_App::on_init_inner() plater_->set_printer_technology(ptFFF); } else -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER load_current_presets(); mainframe->Show(true); diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index d63825de3e..9bf470a42d 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -94,7 +94,7 @@ static wxString dots("…", wxConvUTF8); class GUI_App : public wxApp { -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER public: enum class EAppMode : unsigned char { @@ -103,13 +103,13 @@ public: }; private: -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER bool m_initialized { false }; bool app_conf_exists{ false }; -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER EAppMode m_app_mode{ EAppMode::Editor }; -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER wxColour m_color_label_modified; wxColour m_color_label_sys; @@ -144,18 +144,18 @@ public: bool OnInit() override; bool initialized() const { return m_initialized; } -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER explicit GUI_App(EAppMode mode = EAppMode::Editor); #else GUI_App(); -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER ~GUI_App() override; -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER EAppMode get_app_mode() const { return m_app_mode; } bool is_editor() const { return m_app_mode == EAppMode::Editor; } bool is_gcode_viewer() const { return m_app_mode == EAppMode::GCodeViewer; } -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER static std::string get_gl_info(bool format_as_html, bool extensions); wxGLContext* init_glcontext(wxGLCanvas& canvas); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 530b3358e2..8ea54c6f18 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -1234,11 +1234,7 @@ void Preview::load_print_as_fff(bool keep_z_range) } #if ENABLE_GCODE_VIEWER -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION if (wxGetApp().is_editor() && !has_layers) -#else - if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer && !has_layers) -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #else if (! has_layers) #endif // ENABLE_GCODE_VIEWER diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 6297663067..c0a457d9cd 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -194,9 +194,7 @@ Preview(wxWindow* parent, Model* model, DynamicPrintConfig* config, #if ENABLE_GCODE_VIEWER void update_bottom_toolbar(); void update_moves_slider(); -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION void hide_layers_slider(); -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #endif // ENABLE_GCODE_VIEWER private: @@ -205,16 +203,12 @@ private: void bind_event_handlers(); void unbind_event_handlers(); -#if ENABLE_GCODE_VIEWER -#if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION - void hide_layers_slider(); -#endif // !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION -#else +#if !ENABLE_GCODE_VIEWER void show_hide_ui_elements(const std::string& what); void reset_sliders(bool reset_all); void update_sliders(const std::vector& layers_z, bool keep_z_range = false); -#endif // ENABLE_GCODE_VIEWER +#endif // !ENABLE_GCODE_VIEWER void on_size(wxSizeEvent& evt); void on_choice_view_type(wxCommandEvent& evt); diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 632bc48ed0..0875b76a48 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -95,15 +95,7 @@ void KBShortcutsDialog::fill_shortcuts() const std::string& alt = GUI::shortkey_alt_prefix(); #if ENABLE_GCODE_VIEWER -#if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION - bool is_gcode_viewer = wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer; -#endif // !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION - -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION if (wxGetApp().is_editor()) { -#else - if (!is_gcode_viewer) { -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #endif // ENABLE_GCODE_VIEWER Shortcuts commands_shortcuts = { // File diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 5e3fe4cde1..2589691a3b 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -95,15 +95,15 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S SetIcon(wxIcon(szExeFileName, wxBITMAP_TYPE_ICO)); } #else -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER switch (wxGetApp().get_app_mode()) { default: case GUI_App::EAppMode::Editor: { -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER break; } case GUI_App::EAppMode::GCodeViewer: @@ -112,7 +112,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S break; } } -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER #endif // _WIN32 // initialize status bar @@ -126,15 +126,10 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S // initialize tabpanel and menubar init_tabpanel(); #if ENABLE_GCODE_VIEWER -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION if (wxGetApp().is_gcode_viewer()) init_menubar_as_gcodeviewer(); else init_menubar_as_editor(); -#else - init_menubar_as_editor(); - init_menubar_as_gcodeviewer(); -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #if _WIN32 // This is needed on Windows to fake the CTRL+# of the window menu when using the numpad @@ -165,9 +160,9 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S sizer->Add(m_main_sizer, 1, wxEXPAND); SetSizer(sizer); // initialize layout from config -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER if (wxGetApp().is_editor()) -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER update_layout(); sizer->SetSizeHints(this); Fit(); @@ -320,17 +315,10 @@ void MainFrame::update_layout() }; #if ENABLE_GCODE_VIEWER -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION ESettingsLayout layout = wxGetApp().is_gcode_viewer() ? ESettingsLayout::GCodeViewer : (wxGetApp().app_config->get("old_settings_layout_mode") == "1" ? ESettingsLayout::Old : wxGetApp().app_config->get("new_settings_layout_mode") == "1" ? ESettingsLayout::New : wxGetApp().app_config->get("dlg_settings_layout_mode") == "1" ? ESettingsLayout::Dlg : ESettingsLayout::Old); -#else - ESettingsLayout layout = (m_mode == EMode::GCodeViewer) ? ESettingsLayout::GCodeViewer : - (wxGetApp().app_config->get("old_settings_layout_mode") == "1" ? ESettingsLayout::Old : - wxGetApp().app_config->get("new_settings_layout_mode") == "1" ? ESettingsLayout::New : - wxGetApp().app_config->get("dlg_settings_layout_mode") == "1" ? ESettingsLayout::Dlg : ESettingsLayout::Old); -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #else ESettingsLayout layout = wxGetApp().app_config->get("old_settings_layout_mode") == "1" ? ESettingsLayout::Old : wxGetApp().app_config->get("new_settings_layout_mode") == "1" ? ESettingsLayout::New : @@ -402,12 +390,10 @@ void MainFrame::update_layout() case ESettingsLayout::GCodeViewer: { m_main_sizer->Add(m_plater, 1, wxEXPAND); -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION m_plater->set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, "", "", true); m_plater->enable_view_toolbar(false); m_plater->get_collapse_toolbar().set_enabled(false); m_plater->collapse_sidebar(true); -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION m_plater->Show(); break; } @@ -514,17 +500,6 @@ void MainFrame::shutdown() m_settings_dialog.Close(); if (m_plater != nullptr) { -#if ENABLE_GCODE_VIEWER -#if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION - // restore sidebar if it was hidden when switching to gcode viewer mode - if (m_restore_from_gcode_viewer.collapsed_sidebar) - m_plater->collapse_sidebar(false); - - // restore sla printer if it was deselected when switching to gcode viewer mode - if (m_restore_from_gcode_viewer.sla_technology) - m_plater->set_printer_technology(ptSLA); -#endif // !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION -#endif // ENABLE_GCODE_VIEWER // Stop the background thread (Windows and Linux). // Disconnect from a 3DConnextion driver (OSX). m_plater->get_mouse3d_controller().shutdown(); @@ -625,9 +600,9 @@ void MainFrame::init_tabpanel() // or when the preset's "modified" status changes. Bind(EVT_TAB_PRESETS_CHANGED, &MainFrame::on_presets_changed, this); // #ys_FIXME_to_delete -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER if (wxGetApp().is_editor()) -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER create_preset_tabs(); if (m_plater) { @@ -1093,17 +1068,6 @@ void MainFrame::init_menubar() [this](wxCommandEvent&) { repair_stl(); }, "wrench", nullptr, [this]() { return true; }, this); fileMenu->AppendSeparator(); -#if ENABLE_GCODE_VIEWER -#if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION - append_menu_item(fileMenu, wxID_ANY, _L("&G-code preview"), _L("Switch to G-code preview mode"), - [this](wxCommandEvent&) { - if (m_plater->model().objects.empty() || - wxMessageDialog((wxWindow*)this, _L("Switching to G-code preview mode will remove all objects, continue?"), - wxString(SLIC3R_APP_NAME) + " - " + _L("Switch to G-code preview mode"), wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION | wxCENTRE).ShowModal() == wxID_YES) - set_mode(EMode::GCodeViewer); - }, "", nullptr); -#endif // !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION -#endif // ENABLE_GCODE_VIEWER append_menu_item(fileMenu, wxID_ANY, _L("&G-code preview") + dots, _L("Open G-code viewer"), [this](wxCommandEvent&) { start_new_gcodeviewer_open_file(this); }, "", nullptr); fileMenu->AppendSeparator(); @@ -1319,7 +1283,6 @@ void MainFrame::init_menubar() // assign menubar to frame after appending items, otherwise special items // will not be handled correctly #if ENABLE_GCODE_VIEWER -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION m_menubar = new wxMenuBar(); m_menubar->Append(fileMenu, _L("&File")); if (editMenu) m_menubar->Append(editMenu, _L("&Edit")); @@ -1329,17 +1292,6 @@ void MainFrame::init_menubar() wxGetApp().add_config_menu(m_menubar); m_menubar->Append(helpMenu, _L("&Help")); SetMenuBar(m_menubar); -#else - m_editor_menubar = new wxMenuBar(); - m_editor_menubar->Append(fileMenu, _L("&File")); - if (editMenu) m_editor_menubar->Append(editMenu, _L("&Edit")); - m_editor_menubar->Append(windowMenu, _L("&Window")); - if (viewMenu) m_editor_menubar->Append(viewMenu, _L("&View")); - // Add additional menus from C++ - wxGetApp().add_config_menu(m_editor_menubar); - m_editor_menubar->Append(helpMenu, _L("&Help")); - SetMenuBar(m_editor_menubar); -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #else auto menubar = new wxMenuBar(); menubar->Append(fileMenu, _L("&File")); @@ -1356,11 +1308,7 @@ void MainFrame::init_menubar() // This fixes a bug on Mac OS where the quit command doesn't emit window close events // wx bug: https://trac.wxwidgets.org/ticket/18328 #if ENABLE_GCODE_VIEWER -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION wxMenu* apple_menu = m_menubar->OSXGetAppleMenu(); -#else - wxMenu* apple_menu = m_editor_menubar->OSXGetAppleMenu(); -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #else wxMenu *apple_menu = menubar->OSXGetAppleMenu(); #endif // ENABLE_GCODE_VIEWER @@ -1387,11 +1335,6 @@ void MainFrame::init_menubar_as_gcodeviewer() append_menu_item(fileMenu, wxID_ANY, _L("Export &toolpaths as OBJ") + dots, _L("Export toolpaths as OBJ"), [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->export_toolpaths_to_obj(); }, "export_plater", nullptr, [this]() {return can_export_toolpaths(); }, this); -#if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION - fileMenu->AppendSeparator(); - append_menu_item(fileMenu, wxID_ANY, _L("Exit &G-code preview"), _L("Switch to editor mode"), - [this](wxCommandEvent&) { set_mode(EMode::Editor); }); -#endif // !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_EXIT, _L("&Quit"), wxString::Format(_L("Quit %s"), SLIC3R_APP_NAME), [this](wxCommandEvent&) { Close(false); }); @@ -1407,28 +1350,16 @@ void MainFrame::init_menubar_as_gcodeviewer() // helpmenu auto helpMenu = generate_help_menu(); -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION m_menubar = new wxMenuBar(); m_menubar->Append(fileMenu, _L("&File")); if (viewMenu != nullptr) m_menubar->Append(viewMenu, _L("&View")); m_menubar->Append(helpMenu, _L("&Help")); SetMenuBar(m_menubar); -#else - m_gcodeviewer_menubar = new wxMenuBar(); - m_gcodeviewer_menubar->Append(fileMenu, _L("&File")); - if (viewMenu != nullptr) - m_gcodeviewer_menubar->Append(viewMenu, _L("&View")); - m_gcodeviewer_menubar->Append(helpMenu, _L("&Help")); -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #ifdef __APPLE__ // This fixes a bug on Mac OS where the quit command doesn't emit window close events // wx bug: https://trac.wxwidgets.org/ticket/18328 -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION wxMenu* apple_menu = m_menubar->OSXGetAppleMenu(); -#else - wxMenu* apple_menu = m_gcodeviewer_menubar->OSXGetAppleMenu(); -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION if (apple_menu != nullptr) { apple_menu->Bind(wxEVT_MENU, [this](wxCommandEvent&) { Close(); @@ -1436,150 +1367,14 @@ void MainFrame::init_menubar_as_gcodeviewer() } #endif // __APPLE__ } - -#if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION -void MainFrame::set_mode(EMode mode) -{ - if (m_mode == mode) - return; - - wxBusyCursor busy; - - m_mode = mode; - switch (m_mode) - { - default: - case EMode::Editor: - { - update_layout(); - select_tab(0); - - m_plater->reset(); - m_plater->reset_gcode_toolpaths(); - - m_plater->Freeze(); - - // reinitialize undo/redo stack - m_plater->clear_undo_redo_stack_main(); - m_plater->take_snapshot(_L("New Project")); - - // restore sla printer if it was deselected when switching to gcode viewer mode - if (m_restore_from_gcode_viewer.sla_technology) { - m_plater->set_printer_technology(ptSLA); - m_restore_from_gcode_viewer.sla_technology = false; - } - - // switch view - m_plater->select_view_3D("3D"); - m_plater->select_view("iso"); - - // switch printbed - m_plater->set_bed_shape(); - - // switch menubar - SetMenuBar(m_editor_menubar); - - // show toolbars - m_plater->enable_view_toolbar(true); - - if (m_restore_from_gcode_viewer.collapse_toolbar_enabled) { - m_plater->get_collapse_toolbar().set_enabled(true); - m_restore_from_gcode_viewer.collapse_toolbar_enabled = false; - } - - // show sidebar - if (m_restore_from_gcode_viewer.collapsed_sidebar) { - m_plater->collapse_sidebar(false); - m_restore_from_gcode_viewer.collapsed_sidebar = false; - } - - m_plater->Thaw(); - -// SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); - // Load the icon either from the exe, or from the ico file. -#if _WIN32 - { - - TCHAR szExeFileName[MAX_PATH]; - GetModuleFileName(nullptr, szExeFileName, MAX_PATH); - SetIcon(wxIcon(szExeFileName, wxBITMAP_TYPE_ICO)); - } -#else - SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); -#endif // _WIN32 -#if ENABLE_GCODE_VIEWER_TASKBAR_ICON - if (m_taskbar_icon != nullptr) { - m_taskbar_icon->RemoveIcon(); - m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG), "PrusaSlicer"); - } -#endif // ENABLE_GCODE_VIEWER_TASKBAR_ICON - - break; - } - case EMode::GCodeViewer: - { - update_layout(); - - m_plater->reset(); - m_plater->reset_last_loaded_gcode(); - m_plater->reset_gcode_toolpaths(); - - m_plater->Freeze(); - - // reinitialize undo/redo stack - m_plater->clear_undo_redo_stack_main(); - m_plater->take_snapshot(_L("New Project")); - - // switch to FFF printer mode - m_restore_from_gcode_viewer.sla_technology = m_plater->set_printer_technology(ptFFF); - - // switch view - m_plater->select_view_3D("Preview"); - m_plater->select_view("iso"); - - // switch printbed - m_plater->set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, "", "", true); - - // switch menubar - SetMenuBar(m_gcodeviewer_menubar); - - // hide toolbars - m_plater->enable_view_toolbar(false); - - if (wxGetApp().app_config->get("show_collapse_button") == "1") { - m_plater->get_collapse_toolbar().set_enabled(false); - m_restore_from_gcode_viewer.collapse_toolbar_enabled = true; - } - - // hide sidebar - if (wxGetApp().app_config->get("collapsed_sidebar") != "1") { - m_plater->collapse_sidebar(true); - m_restore_from_gcode_viewer.collapsed_sidebar = true; - } - - m_plater->Thaw(); - - SetIcon(wxIcon(Slic3r::var("PrusaSlicer-gcodeviewer_128px.png"), wxBITMAP_TYPE_PNG)); -#if ENABLE_GCODE_VIEWER_TASKBAR_ICON - if (m_taskbar_icon != nullptr) { - m_taskbar_icon->RemoveIcon(); - m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer-gcodeviewer_128px.png"), wxBITMAP_TYPE_PNG), "PrusaSlicer-GCode viewer"); - } -#endif // ENABLE_GCODE_VIEWER_TASKBAR_ICON - - break; - } - } -} -#endif // !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #endif // ENABLE_GCODE_VIEWER void MainFrame::update_menubar() { -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER if (wxGetApp().is_gcode_viewer()) return; -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER const bool is_fff = plater()->printer_technology() == ptFFF; @@ -2069,10 +1864,10 @@ SettingsDialog::SettingsDialog(MainFrame* mainframe) wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMINIMIZE_BOX | wxMAXIMIZE_BOX, "settings_dialog"), m_main_frame(mainframe) { -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER if (wxGetApp().is_gcode_viewer()) return; -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER #if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT && defined(__WXMSW__) // ys_FIXME! temporary workaround for correct font scaling @@ -2146,10 +1941,10 @@ SettingsDialog::SettingsDialog(MainFrame* mainframe) void SettingsDialog::on_dpi_changed(const wxRect& suggested_rect) { -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER if (wxGetApp().is_gcode_viewer()) return; -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER const int& em = em_unit(); const wxSize& size = wxSize(85 * em, 50 * em); diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 867e11e86b..868a684925 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -72,21 +72,7 @@ class MainFrame : public DPIFrame wxString m_qs_last_output_file = wxEmptyString; wxString m_last_config = wxEmptyString; #if ENABLE_GCODE_VIEWER -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION - wxMenuBar* m_menubar{ nullptr }; -#else - wxMenuBar* m_editor_menubar{ nullptr }; - wxMenuBar* m_gcodeviewer_menubar{ nullptr }; - - struct RestoreFromGCodeViewer - { - bool collapsed_sidebar{ false }; - bool collapse_toolbar_enabled{ false }; - bool sla_technology{ false }; - }; - - RestoreFromGCodeViewer m_restore_from_gcode_viewer; -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION + wxMenuBar* m_menubar{ nullptr }; #endif // ENABLE_GCODE_VIEWER #if 0 @@ -149,20 +135,6 @@ class MainFrame : public DPIFrame ESettingsLayout m_layout{ ESettingsLayout::Unknown }; -#if ENABLE_GCODE_VIEWER -#if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION -public: - enum class EMode : unsigned char - { - Editor, - GCodeViewer - }; - -private: - EMode m_mode{ EMode::Editor }; -#endif // !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION -#endif // ENABLE_GCODE_VIEWER - protected: virtual void on_dpi_changed(const wxRect &suggested_rect); virtual void on_sys_color_changed() override; @@ -190,11 +162,6 @@ public: #if ENABLE_GCODE_VIEWER void init_menubar_as_editor(); void init_menubar_as_gcodeviewer(); - -#if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION - EMode get_mode() const { return m_mode; } - void set_mode(EMode mode); -#endif // !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #else void init_menubar(); #endif // ENABLE_GCODE_VIEWER diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 061084f57d..b4f900b0ce 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1369,9 +1369,7 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi this->MSWUpdateDragImageOnLeave(); #endif // WIN32 -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION if (wxGetApp().is_gcode_viewer()) { -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION // gcode section for (const auto& filename : filenames) { fs::path path(into_path(filename)); @@ -1385,33 +1383,11 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi return false; } else if (paths.size() == 1) { -#if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION - if (wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer) { -#endif // !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION plater->load_gcode(from_path(paths.front())); return true; -#if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION - } - else { - if (wxMessageDialog((wxWindow*)plater, _L("Do you want to switch to G-code preview ?"), - wxString(SLIC3R_APP_NAME) + " - " + _L("Drag and drop G-code file"), wxYES_NO | wxICON_QUESTION | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES) { - - if (plater->model().objects.empty() || - wxMessageDialog((wxWindow*)plater, _L("Switching to G-code preview mode will remove all objects, continue?"), - wxString(SLIC3R_APP_NAME) + " - " + _L("Switch to G-code preview mode"), wxYES_NO | wxICON_QUESTION | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES) { - wxGetApp().mainframe->set_mode(MainFrame::EMode::GCodeViewer); - plater->load_gcode(from_path(paths.front())); - return true; - } - } - return false; - } -#endif // !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION } -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION return false; } -#endif //ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION #endif // ENABLE_GCODE_VIEWER // editor section @@ -1423,18 +1399,6 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi return false; } -#if ENABLE_GCODE_VIEWER -#if !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION - if (wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer) { - if (wxMessageDialog((wxWindow*)plater, _L("Do you want to exit G-code preview ?"), - wxString(SLIC3R_APP_NAME) + " - " + _L("Drag and drop model file"), wxYES_NO | wxICON_QUESTION | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES) - wxGetApp().mainframe->set_mode(MainFrame::EMode::Editor); - else - return false; - } -#endif // !ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION -#endif // ENABLE_GCODE_VIEWER - wxString snapshot_label; assert(! paths.empty()); if (paths.size() == 1) { @@ -1983,13 +1947,13 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) q->SetDropTarget(new PlaterDropTarget(q)); // if my understanding is right, wxWindow takes the owenership q->Layout(); -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER set_current_panel(wxGetApp().is_editor() ? (wxPanel*)view3D : (wxPanel*)preview); if (wxGetApp().is_gcode_viewer()) preview->hide_layers_slider(); #else set_current_panel(view3D); -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER // updates camera type from .ini file camera.set_type(get_config("use_perspective_camera")); @@ -2009,9 +1973,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) #endif /* _WIN32 */ notification_manager = new NotificationManager(this->q); -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER if (wxGetApp().is_editor()) { -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER this->q->Bind(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED, [this](EjectDriveNotificationClickedEvent&) { this->q->eject_drive(); }); this->q->Bind(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED, [this](ExportGcodeNotificationClickedEvent&) { this->q->export_gcode(true); }); this->q->Bind(EVT_PRESET_UPDATE_AVIABLE_CLICKED, [this](PresetUpdateAviableClickedEvent&) { wxGetApp().get_preset_updater()->on_update_notification_confirm(); }); @@ -2038,9 +2002,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) this->q->Bind(EVT_VOLUME_ATTACHED, [this](VolumeAttachedEvent &evt) { wxGetApp().removable_drive_manager()->volumes_changed(); }); this->q->Bind(EVT_VOLUME_DETACHED, [this](VolumeDetachedEvent &evt) { wxGetApp().removable_drive_manager()->volumes_changed(); }); #endif /* _WIN32 */ -#if ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#if ENABLE_GCODE_VIEWER } -#endif // ENABLE_GCODE_VIEWER_AS_STANDALONE_APPLICATION +#endif // ENABLE_GCODE_VIEWER // Initialize the Undo / Redo stack with a first snapshot. this->take_snapshot(_L("New Project")); @@ -5408,7 +5372,9 @@ void Plater::on_config_change(const DynamicPrintConfig &config) this->set_printer_technology(config.opt_enum(opt_key)); // print technology is changed, so we should to update a search list p->sidebar->update_searcher(); +#if ENABLE_GCODE_VIEWER p->reset_gcode_toolpaths(); +#endif // ENABLE_GCODE_VIEWER } else if ((opt_key == "bed_shape") || (opt_key == "bed_custom_texture") || (opt_key == "bed_custom_model")) { bed_shape_changed = true; From ea9a8b7e93942be8a3335c99b76a474e5bb480fc Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 10 Sep 2020 09:43:45 +0200 Subject: [PATCH 466/503] Hides view toolbar in gcode viewer --- src/slic3r/GUI/MainFrame.cpp | 1 - src/slic3r/GUI/Plater.cpp | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 2589691a3b..06cb75efa3 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -391,7 +391,6 @@ void MainFrame::update_layout() { m_main_sizer->Add(m_plater, 1, wxEXPAND); m_plater->set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, "", "", true); - m_plater->enable_view_toolbar(false); m_plater->get_collapse_toolbar().set_enabled(false); m_plater->collapse_sidebar(true); m_plater->Show(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b4f900b0ce..f7fd608ba8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4001,7 +4001,11 @@ bool Plater::priv::init_view_toolbar() return false; view_toolbar.select_item("3D"); - view_toolbar.set_enabled(true); + +#if ENABLE_GCODE_VIEWER + if (wxGetApp().is_editor()) +#endif // ENABLE_GCODE_VIEWER + view_toolbar.set_enabled(true); return true; } From a9a99de93926d19d8e3fac93d706a3e3f5b81f7b Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 31 Aug 2020 10:37:42 +0200 Subject: [PATCH 467/503] Enable all tests for support point generator --- tests/sla_print/sla_supptgen_tests.cpp | 29 +++++++++++++------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/tests/sla_print/sla_supptgen_tests.cpp b/tests/sla_print/sla_supptgen_tests.cpp index 1d7a3ebee2..024f115b0b 100644 --- a/tests/sla_print/sla_supptgen_tests.cpp +++ b/tests/sla_print/sla_supptgen_tests.cpp @@ -105,26 +105,25 @@ TEST_CASE("Overhanging edge should be supported", "[SupGen]") { REQUIRE(min_point_distance(pts) >= cfg.minimal_distance); } -// FIXME: Not working yet -//TEST_CASE("Hollowed cube should be supported from the inside", "[SupGen][Hollowed]") { -// TriangleMesh mesh = make_cube(20., 20., 20.); +TEST_CASE("Hollowed cube should be supported from the inside", "[SupGen][Hollowed]") { + TriangleMesh mesh = make_cube(20., 20., 20.); -// hollow_mesh(mesh, HollowingConfig{}); + hollow_mesh(mesh, HollowingConfig{}); -// mesh.WriteOBJFile("cube_hollowed.obj"); + mesh.WriteOBJFile("cube_hollowed.obj"); -// auto bb = mesh.bounding_box(); -// auto h = float(bb.max.z() - bb.min.z()); -// Vec3f mv = bb.center().cast() - Vec3f{0.f, 0.f, 0.5f * h}; -// mesh.translate(-mv); -// mesh.require_shared_vertices(); + auto bb = mesh.bounding_box(); + auto h = float(bb.max.z() - bb.min.z()); + Vec3f mv = bb.center().cast() - Vec3f{0.f, 0.f, 0.5f * h}; + mesh.translate(-mv); + mesh.require_shared_vertices(); -// sla::SupportPointGenerator::Config cfg; -// sla::SupportPoints pts = calc_support_pts(mesh, cfg); -// sla::remove_bottom_points(pts, mesh.bounding_box().min.z() + EPSILON); + sla::SupportPointGenerator::Config cfg; + sla::SupportPoints pts = calc_support_pts(mesh, cfg); + sla::remove_bottom_points(pts, mesh.bounding_box().min.z() + EPSILON); -// REQUIRE(!pts.empty()); -//} + REQUIRE(!pts.empty()); +} TEST_CASE("Two parallel plates should be supported", "[SupGen][Hollowed]") { From 26d5c3036623f508bd4517e7258b1e1ea7cb44df Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 31 Aug 2020 10:38:24 +0200 Subject: [PATCH 468/503] Improvements to support point generator - Separate the 3 bands -- dangling, sloping and full overhanging -- regions and handle them with different support force deficits. - Use a heuristic for overhanging edges to increase the number of support points generated for them - Try to make overhangs and slopes deficit depend on stable area. --- src/libslic3r/SLA/SupportPointGenerator.cpp | 171 ++++++++++++++------ src/libslic3r/SLA/SupportPointGenerator.hpp | 24 ++- 2 files changed, 143 insertions(+), 52 deletions(-) diff --git a/src/libslic3r/SLA/SupportPointGenerator.cpp b/src/libslic3r/SLA/SupportPointGenerator.cpp index 449269445d..7e884b6e3c 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.cpp +++ b/src/libslic3r/SLA/SupportPointGenerator.cpp @@ -163,10 +163,10 @@ static std::vector make_layers( SupportPointGenerator::MyLayer &layer_below = layers[layer_id - 1]; //FIXME WTF? const float layer_height = (layer_id!=0 ? heights[layer_id]-heights[layer_id-1] : heights[0]); - const float safe_angle = 5.f * (float(M_PI)/180.f); // smaller number - less supports - const float between_layers_offset = float(scale_(layer_height / std::tan(safe_angle))); + const float safe_angle = 35.f * (float(M_PI)/180.f); // smaller number - less supports + const float between_layers_offset = scaled(layer_height * std::tan(safe_angle)); const float slope_angle = 75.f * (float(M_PI)/180.f); // smaller number - less supports - const float slope_offset = float(scale_(layer_height / std::tan(slope_angle))); + const float slope_offset = scaled(layer_height * std::tan(slope_angle)); //FIXME This has a quadratic time complexity, it will be excessively slow for many tiny islands. for (SupportPointGenerator::Structure &top : layer_above.islands) { for (SupportPointGenerator::Structure &bottom : layer_below.islands) { @@ -181,6 +181,25 @@ static std::vector make_layers( Polygons bottom_polygons = top.polygons_below(); top.overhangs = diff_ex(top_polygons, bottom_polygons); if (! top.overhangs.empty()) { + + // Produce 2 bands around the island, a safe band for dangling overhangs + // and an unsafe band for sloped overhangs. + // These masks include the original island + auto dangl_mask = offset(bottom_polygons, between_layers_offset, ClipperLib::jtSquare); + auto overh_mask = offset(bottom_polygons, slope_offset, ClipperLib::jtSquare); + + // Absolutely hopeless overhangs are those outside the unsafe band + top.overhangs = diff_ex(top_polygons, overh_mask); + + // Now cut out the supported core from the safe band + // and cut the safe band from the unsafe band to get distinct + // zones. + overh_mask = diff(overh_mask, dangl_mask); + dangl_mask = diff(dangl_mask, bottom_polygons); + + top.dangling_areas = intersection_ex(top_polygons, dangl_mask); + top.overhangs_slopes = intersection_ex(top_polygons, overh_mask); + top.overhangs_area = 0.f; std::vector> expolys_with_areas; for (ExPolygon &ex : top.overhangs) { @@ -196,8 +215,6 @@ static std::vector make_layers( overhangs_sorted.emplace_back(std::move(*p.first)); top.overhangs = std::move(overhangs_sorted); top.overhangs_area *= float(SCALING_FACTOR * SCALING_FACTOR); - top.overhangs_slopes = diff_ex(top_polygons, offset(bottom_polygons, slope_offset)); - top.dangling_areas = diff_ex(top_polygons, offset(bottom_polygons, between_layers_offset)); } } } @@ -256,21 +273,9 @@ void SupportPointGenerator::process(const std::vector& slices, const // Now iterate over all polygons and append new points if needed. for (Structure &s : layer_top->islands) { // Penalization resulting from large diff from the last layer: -// s.supports_force_inherited /= std::max(1.f, (layer_height / 0.3f) * e_area / s.area); s.supports_force_inherited /= std::max(1.f, 0.17f * (s.overhangs_area) / s.area); - //float force_deficit = s.support_force_deficit(m_config.tear_pressure()); - if (s.islands_below.empty()) { // completely new island - needs support no doubt - uniformly_cover({ *s.polygon }, s, point_grid, true); - } else if (! s.dangling_areas.empty()) { - // Let's see if there's anything that overlaps enough to need supports: - // What we now have in polygons needs support, regardless of what the forces are, so we can add them. - //FIXME is it an island point or not? Vojtech thinks it is. - uniformly_cover(s.dangling_areas, s, point_grid); - } else if (! s.overhangs_slopes.empty()) { - //FIXME add the support force deficit as a parameter, only cover until the defficiency is covered. - uniformly_cover(s.overhangs_slopes, s, point_grid); - } + add_support_points(s, point_grid); } m_throw_on_cancel(); @@ -288,6 +293,42 @@ void SupportPointGenerator::process(const std::vector& slices, const } } +void SupportPointGenerator::add_support_points(SupportPointGenerator::Structure &s, SupportPointGenerator::PointGrid3D &grid3d) +{ + // Select each type of surface (overrhang, dangling, slope), derive the support + // force deficit for it and call uniformly conver with the right params + + float tp = m_config.tear_pressure(); + float current = s.supports_force_total(); + static constexpr float SLOPE_DAMPING = .0015f; + static constexpr float DANGL_DAMPING = .09f; + + if (s.islands_below.empty()) { + // completely new island - needs support no doubt + // deficit is full, there is nothing below that would hold this island + uniformly_cover({ *s.polygon }, s, s.area * tp, grid3d, IslandCoverageFlags(icfIsNew | icfBoundaryOnly) ); + return; + } + + auto areafn = [](double sum, auto &p) { return sum + p.area() * SCALING_FACTOR * SCALING_FACTOR; }; + if (! s.dangling_areas.empty()) { + // Let's see if there's anything that overlaps enough to need supports: + // What we now have in polygons needs support, regardless of what the forces are, so we can add them. + + double a = std::accumulate(s.dangling_areas.begin(), s.dangling_areas.end(), 0., areafn); + uniformly_cover(s.dangling_areas, s, a * tp - current * DANGL_DAMPING * std::sqrt(1. - a / s.area), grid3d); + } + + if (! s.overhangs_slopes.empty()) { + double a = std::accumulate(s.overhangs_slopes.begin(), s.overhangs_slopes.end(), 0., areafn); + uniformly_cover(s.overhangs_slopes, s, a * tp - current * SLOPE_DAMPING * std::sqrt(1. - a / s.area), grid3d); + } + + if (! s.overhangs.empty()) { + uniformly_cover(s.overhangs, s, s.overhangs_area * tp, grid3d); + } +} + std::vector sample_expolygon(const ExPolygon &expoly, float samples_per_mm2, std::mt19937 &rng) { // Triangulate the polygon with holes into triplets of 3D points. @@ -297,16 +338,16 @@ std::vector sample_expolygon(const ExPolygon &expoly, float samples_per_m if (! triangles.empty()) { // Calculate area of each triangle. - std::vector areas; - areas.reserve(triangles.size() / 3); + auto areas = reserve_vector(triangles.size() / 3); + double aback = 0.; for (size_t i = 0; i < triangles.size(); ) { const Vec2f &a = triangles[i ++]; const Vec2f v1 = triangles[i ++] - a; const Vec2f v2 = triangles[i ++] - a; - areas.emplace_back(0.5f * std::abs(cross2(v1, v2))); - if (i != 3) - // Prefix sum of the areas. - areas.back() += areas[areas.size() - 2]; + + // Prefix sum of the areas. + areas.emplace_back(aback + 0.5f * std::abs(cross2(v1, v2))); + aback = areas.back(); } size_t num_samples = size_t(ceil(areas.back() * samples_per_mm2)); @@ -316,7 +357,7 @@ std::vector sample_expolygon(const ExPolygon &expoly, float samples_per_m double r = random_triangle(rng); size_t idx_triangle = std::min(std::upper_bound(areas.begin(), areas.end(), (float)r) - areas.begin(), areas.size() - 1) * 3; // Select a random point on the triangle. - double u = float(sqrt(random_float(rng))); + double u = float(std::sqrt(random_float(rng))); double v = float(random_float(rng)); const Vec2f &a = triangles[idx_triangle ++]; const Vec2f &b = triangles[idx_triangle++]; @@ -328,16 +369,37 @@ std::vector sample_expolygon(const ExPolygon &expoly, float samples_per_m return out; } + +std::vector sample_expolygon(const ExPolygons &expolys, float samples_per_mm2, std::mt19937 &rng) +{ + std::vector out; + for (const ExPolygon &expoly : expolys) + append(out, sample_expolygon(expoly, samples_per_mm2, rng)); + + return out; +} + +void sample_expolygon_boundary(const ExPolygon & expoly, + float samples_per_mm, + std::vector &out, + std::mt19937 & rng) +{ + double point_stepping_scaled = scale_(1.f) / samples_per_mm; + for (size_t i_contour = 0; i_contour <= expoly.holes.size(); ++ i_contour) { + const Polygon &contour = (i_contour == 0) ? expoly.contour : + expoly.holes[i_contour - 1]; + + const Points pts = contour.equally_spaced_points(point_stepping_scaled); + for (size_t i = 0; i < pts.size(); ++ i) + out.emplace_back(unscale(pts[i].x()), + unscale(pts[i].y())); + } +} + std::vector sample_expolygon_with_boundary(const ExPolygon &expoly, float samples_per_mm2, float samples_per_mm_boundary, std::mt19937 &rng) { std::vector out = sample_expolygon(expoly, samples_per_mm2, rng); - double point_stepping_scaled = scale_(1.f) / samples_per_mm_boundary; - for (size_t i_contour = 0; i_contour <= expoly.holes.size(); ++ i_contour) { - const Polygon &contour = (i_contour == 0) ? expoly.contour : expoly.holes[i_contour - 1]; - const Points pts = contour.equally_spaced_points(point_stepping_scaled); - for (size_t i = 0; i < pts.size(); ++ i) - out.emplace_back(unscale(pts[i].x()), unscale(pts[i].y())); - } + sample_expolygon_boundary(expoly, samples_per_mm_boundary, out, rng); return out; } @@ -359,17 +421,17 @@ static inline std::vector poisson_disk_from_samples(const std::vector raw_samples_sorted; - RawSample sample; - for (const Vec2f &pt : raw_samples) { - sample.coord = pt; - sample.cell_id = ((pt - corner_min) / radius).cast(); - raw_samples_sorted.emplace_back(sample); - } + + auto raw_samples_sorted = reserve_vector(raw_samples.size()); + for (const Vec2f &pt : raw_samples) + raw_samples_sorted.emplace_back(pt, ((pt - corner_min) / radius).cast()); + std::sort(raw_samples_sorted.begin(), raw_samples_sorted.end(), [](const RawSample &lhs, const RawSample &rhs) { return lhs.cell_id.x() < rhs.cell_id.x() || (lhs.cell_id.x() == rhs.cell_id.x() && lhs.cell_id.y() < rhs.cell_id.y()); }); @@ -464,11 +526,22 @@ static inline std::vector poisson_disk_from_samples(const std::vector bbdim.y()) std::swap(bbdim.x(), bbdim.y()); + double aspectr = bbdim.y() / bbdim.x(); + + support_force_deficit *= (1 + aspectr / 2.); + } + if (support_force_deficit < 0) return; @@ -485,13 +558,18 @@ void SupportPointGenerator::uniformly_cover(const ExPolygons& islands, Structure float min_spacing = poisson_radius; //FIXME share the random generator. The random generator may be not so cheap to initialize, also we don't want the random generator to be restarted for each polygon. - - std::vector raw_samples = sample_expolygon_with_boundary(islands, samples_per_mm2, 5.f / poisson_radius, m_rng); + + std::vector raw_samples = + flags & icfBoundaryOnly ? + sample_expolygon_with_boundary(islands, samples_per_mm2, + 5.f / poisson_radius, m_rng) : + sample_expolygon(islands, samples_per_mm2, m_rng); + std::vector poisson_samples; for (size_t iter = 0; iter < 4; ++ iter) { poisson_samples = poisson_disk_from_samples(raw_samples, poisson_radius, [&structure, &grid3d, min_spacing](const Vec2f &pos) { - return grid3d.collides_with(pos, &structure, min_spacing); + return grid3d.collides_with(pos, structure.layer->print_z, min_spacing); }); if (poisson_samples.size() >= poisson_samples_target || m_config.minimal_distance > poisson_radius-EPSILON) break; @@ -521,12 +599,13 @@ void SupportPointGenerator::uniformly_cover(const ExPolygons& islands, Structure poisson_samples.erase(poisson_samples.begin() + poisson_samples_target, poisson_samples.end()); } for (const Vec2f &pt : poisson_samples) { - m_output.emplace_back(float(pt(0)), float(pt(1)), structure.height, m_config.head_diameter/2.f, is_new_island); + m_output.emplace_back(float(pt(0)), float(pt(1)), structure.zlevel, m_config.head_diameter/2.f, flags & icfIsNew); structure.supports_force_this_layer += m_config.support_force(); grid3d.insert(pt, &structure); } } + void remove_bottom_points(std::vector &pts, float lvl) { // get iterator to the reorganized vector end diff --git a/src/libslic3r/SLA/SupportPointGenerator.hpp b/src/libslic3r/SLA/SupportPointGenerator.hpp index f1b3770254..4c809dba30 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.hpp +++ b/src/libslic3r/SLA/SupportPointGenerator.hpp @@ -38,8 +38,8 @@ public: struct MyLayer; struct Structure { - Structure(MyLayer &layer, const ExPolygon& poly, const BoundingBox &bbox, const Vec2f ¢roid, float area, float h) : - layer(&layer), polygon(&poly), bbox(bbox), centroid(centroid), area(area), height(h) + Structure(MyLayer &layer, const ExPolygon& poly, const BoundingBox &bbox, const Vec2f ¢roid, float area, float h) : + layer(&layer), polygon(&poly), bbox(bbox), centroid(centroid), area(area), zlevel(h) #ifdef SLA_SUPPORTPOINTGEN_DEBUG , unique_id(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch())) #endif /* SLA_SUPPORTPOINTGEN_DEBUG */ @@ -49,7 +49,7 @@ public: const BoundingBox bbox; const Vec2f centroid = Vec2f::Zero(); const float area = 0.f; - float height = 0; + float zlevel = 0; // How well is this ExPolygon held to the print base? // Positive number, the higher the better. float supports_force_this_layer = 0.f; @@ -159,8 +159,8 @@ public: grid.emplace(cell_id(pt.position), pt); } - bool collides_with(const Vec2f &pos, Structure *island, float radius) { - Vec3f pos3d(pos.x(), pos.y(), float(island->layer->print_z)); + bool collides_with(const Vec2f &pos, float print_z, float radius) { + Vec3f pos3d(pos.x(), pos.y(), print_z); Vec3i cell = cell_id(pos3d); std::pair it_pair = grid.equal_range(cell); if (collides_with(pos3d, radius, it_pair.first, it_pair.second)) @@ -198,7 +198,16 @@ private: SupportPointGenerator::Config m_config; void process(const std::vector& slices, const std::vector& heights); - void uniformly_cover(const ExPolygons& islands, Structure& structure, PointGrid3D &grid3d, bool is_new_island = false, bool just_one = false); + +public: + enum IslandCoverageFlags : uint8_t { icfNone = 0x0, icfIsNew = 0x1, icfBoundaryOnly = 0x2 }; + +private: + + void uniformly_cover(const ExPolygons& islands, Structure& structure, float deficit, PointGrid3D &grid3d, IslandCoverageFlags flags = icfNone); + + void add_support_points(Structure& structure, PointGrid3D &grid3d); + void project_onto_mesh(std::vector& points) const; #ifdef SLA_SUPPORTPOINTGEN_DEBUG @@ -215,6 +224,9 @@ private: void remove_bottom_points(std::vector &pts, float lvl); +std::vector sample_expolygon(const ExPolygon &expoly, float samples_per_mm2, std::mt19937 &rng); +void sample_expolygon_boundary(const ExPolygon &expoly, float samples_per_mm, std::vector &out, std::mt19937 &rng); + }} // namespace Slic3r::sla #endif // SUPPORTPOINTGENERATOR_HPP From a21ff4141be541f3fc3936b65a2b2f77ca3e6212 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 25 Aug 2020 13:40:06 +0200 Subject: [PATCH 469/503] Fix failing test due to changes in support point genertion --- src/libslic3r/ExPolygon.hpp | 8 ++++++++ src/libslic3r/Polygon.hpp | 8 ++++++++ tests/sla_print/sla_supptgen_tests.cpp | 5 ++--- tests/sla_print/sla_test_utils.cpp | 5 ++++- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp index 4aad3603fc..373853f972 100644 --- a/src/libslic3r/ExPolygon.hpp +++ b/src/libslic3r/ExPolygon.hpp @@ -333,6 +333,14 @@ extern std::list expoly_to_polypartition_input(const ExPolygons &expp) extern std::list expoly_to_polypartition_input(const ExPolygon &ex); extern std::vector polypartition_output_to_triangles(const std::list &output); +inline double area(const ExPolygons &polys) +{ + double s = 0.; + for (auto &p : polys) s += p.area(); + + return s; +} + } // namespace Slic3r // start Boost diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index ab7c171e3c..48dd1b64dd 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -86,6 +86,14 @@ inline double total_length(const Polygons &polylines) { return total; } +inline double area(const Polygons &polys) +{ + double s = 0.; + for (auto &p : polys) s += p.area(); + + return s; +} + // Remove sticks (tentacles with zero area) from the polygon. extern bool remove_sticks(Polygon &poly); extern bool remove_sticks(Polygons &polys); diff --git a/tests/sla_print/sla_supptgen_tests.cpp b/tests/sla_print/sla_supptgen_tests.cpp index 024f115b0b..ee9013a44c 100644 --- a/tests/sla_print/sla_supptgen_tests.cpp +++ b/tests/sla_print/sla_supptgen_tests.cpp @@ -89,8 +89,6 @@ TEST_CASE("Overhanging edge should be supported", "[SupGen]") { sla::SupportPointGenerator::Config cfg; sla::SupportPoints pts = calc_support_pts(mesh, cfg); - REQUIRE(min_point_distance(pts) >= cfg.minimal_distance); - Linef3 overh{ {0.f, -depth / 2.f, 0.f}, {0.f, depth / 2.f, 0.f}}; // Get all the points closer that 1 mm to the overhanging edge: @@ -102,7 +100,8 @@ TEST_CASE("Overhanging edge should be supported", "[SupGen]") { }); REQUIRE(overh_pts.size() * cfg.support_force() > overh.length() * cfg.tear_pressure()); - REQUIRE(min_point_distance(pts) >= cfg.minimal_distance); + double ddiff = min_point_distance(pts) - cfg.minimal_distance; + REQUIRE(ddiff > - 0.1 * cfg.minimal_distance); } TEST_CASE("Hollowed cube should be supported from the inside", "[SupGen][Hollowed]") { diff --git a/tests/sla_print/sla_test_utils.cpp b/tests/sla_print/sla_test_utils.cpp index f6b548fa05..a6a0f4304c 100644 --- a/tests/sla_print/sla_test_utils.cpp +++ b/tests/sla_print/sla_test_utils.cpp @@ -38,7 +38,10 @@ void test_support_model_collision(const std::string &obj_filename, Polygons intersections = intersection(sup_slice, mod_slice); - notouch = notouch && intersections.empty(); + double pinhead_r = scaled(input_supportcfg.head_front_radius_mm); + + // TODO:: make it strict without a threshold of PI * pihead_radius ^ 2 + notouch = notouch && area(intersections) < PI * pinhead_r * pinhead_r; } /*if (!notouch) */export_failed_case(support_slices, byproducts); From 50836914fc24290f20c886f563df21c1fd4e99df Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 10 Sep 2020 13:37:58 +0200 Subject: [PATCH 470/503] Calibration changes to address new algorithm behavior. --- src/libslic3r/SLA/SupportPointGenerator.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/SLA/SupportPointGenerator.hpp b/src/libslic3r/SLA/SupportPointGenerator.hpp index 4c809dba30..30c221c041 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.hpp +++ b/src/libslic3r/SLA/SupportPointGenerator.hpp @@ -22,8 +22,9 @@ public: float density_relative {1.f}; float minimal_distance {1.f}; float head_diameter {0.4f}; - /////////////// - inline float support_force() const { return 7.7f / density_relative; } // a force one point can support (arbitrary force unit) + + // Originally calibrated to 7.7f, reduced density by Tamas to 70% which is 11.1 (7.7 / 0.7) to adjust for new algorithm changes in tm_suppt_gen_improve + inline float support_force() const { return 11.1f / density_relative; } // a force one point can support (arbitrary force unit) inline float tear_pressure() const { return 1.f; } // pressure that the display exerts (the force unit per mm2) }; From 7713a55d458a92468dac2d9c9e4802cf72644150 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 10 Sep 2020 13:39:43 +0200 Subject: [PATCH 471/503] Do a mesh split before openvdb conversion, unify each part's grid Do a mesh redistance after the part splitting and openvdb csgUnion --- src/libslic3r/OpenVDBUtils.cpp | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/OpenVDBUtils.cpp b/src/libslic3r/OpenVDBUtils.cpp index c30052036e..53a71f194f 100644 --- a/src/libslic3r/OpenVDBUtils.cpp +++ b/src/libslic3r/OpenVDBUtils.cpp @@ -2,6 +2,7 @@ #include "OpenVDBUtils.hpp" #include #include +#include #include //#include "MTUtils.hpp" @@ -57,17 +58,42 @@ void Contour3DDataAdapter::getIndexSpacePoint(size_t n, // TODO: Do I need to call initialize? Seems to work without it as well but the // docs say it should be called ones. It does a mutex lock-unlock sequence all // even if was called previously. - openvdb::FloatGrid::Ptr mesh_to_grid(const TriangleMesh &mesh, const openvdb::math::Transform &tr, float exteriorBandWidth, float interiorBandWidth, int flags) { +// openvdb::initialize(); +// return openvdb::tools::meshToVolume( +// TriangleMeshDataAdapter{mesh}, tr, exteriorBandWidth, +// interiorBandWidth, flags); + openvdb::initialize(); - return openvdb::tools::meshToVolume( - TriangleMeshDataAdapter{mesh}, tr, exteriorBandWidth, - interiorBandWidth, flags); + + TriangleMeshPtrs meshparts = mesh.split(); + + auto it = std::remove_if(meshparts.begin(), meshparts.end(), + [](TriangleMesh *m){ + m->require_shared_vertices(); + return !m->is_manifold() || m->volume() < EPSILON; + }); + + meshparts.erase(it, meshparts.end()); + + openvdb::FloatGrid::Ptr grid; + for (TriangleMesh *m : meshparts) { + auto gridptr = openvdb::tools::meshToVolume( + TriangleMeshDataAdapter{*m}, tr, exteriorBandWidth, + interiorBandWidth, flags); + + if (grid && gridptr) openvdb::tools::csgUnion(*grid, *gridptr); + else if (gridptr) grid = std::move(gridptr); + } + + grid = openvdb::tools::levelSetRebuild(*grid, 0., exteriorBandWidth, interiorBandWidth); + + return grid; } openvdb::FloatGrid::Ptr mesh_to_grid(const sla::Contour3D &mesh, From b4e30cc8ad08de86fee89b947c35cd401ca9021a Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 26 Aug 2020 10:25:09 +0200 Subject: [PATCH 472/503] rotation finder experiments wip --- src/libslic3r/Optimizer.hpp | 1 + src/libslic3r/SLA/Rotfinder.cpp | 155 +++++++++++++++++-------- src/libslic3r/SLA/Rotfinder.hpp | 2 +- src/slic3r/GUI/Jobs/RotoptimizeJob.cpp | 4 +- 4 files changed, 108 insertions(+), 54 deletions(-) diff --git a/src/libslic3r/Optimizer.hpp b/src/libslic3r/Optimizer.hpp index 6495ae7ff4..1c94f3c1ed 100644 --- a/src/libslic3r/Optimizer.hpp +++ b/src/libslic3r/Optimizer.hpp @@ -368,6 +368,7 @@ template auto score_gradient(double s, const double (&grad)[N]) using AlgNLoptGenetic = detail::NLoptAlgComb; using AlgNLoptSubplex = detail::NLoptAlg; using AlgNLoptSimplex = detail::NLoptAlg; +using AlgNLoptDIRECT = detail::NLoptAlg; // TODO: define others if needed... diff --git a/src/libslic3r/SLA/Rotfinder.cpp b/src/libslic3r/SLA/Rotfinder.cpp index 81ef00e6b3..b4b1fae391 100644 --- a/src/libslic3r/SLA/Rotfinder.cpp +++ b/src/libslic3r/SLA/Rotfinder.cpp @@ -1,33 +1,113 @@ #include #include -#include +//#include +#include #include #include +#include +#include #include "Model.hpp" namespace Slic3r { namespace sla { -std::array find_best_rotation(const ModelObject& modelobj, +double area(const Vec3d &p1, const Vec3d &p2, const Vec3d &p3) { + Vec3d a = p2 - p1; + Vec3d b = p3 - p1; + Vec3d c = a.cross(b); + return 0.5 * c.norm(); +} + +using VertexFaceMap = std::vector>; + +VertexFaceMap create_vertex_face_map(const TriangleMesh &mesh) { + std::vector> vmap(mesh.its.vertices.size()); + + size_t fi = 0; + for (const Vec3i &tri : mesh.its.indices) { + for (int vi = 0; vi < tri.size(); ++vi) { + auto from = vmap[tri(vi)].begin(), to = vmap[tri(vi)].end(); + vmap[tri(vi)].insert(std::lower_bound(from, to, fi), fi); + } + } + + return vmap; +} + +// Try to guess the number of support points needed to support a mesh +double calculate_model_supportedness(const TriangleMesh & mesh, + const VertexFaceMap &vmap, + const Transform3d & tr) +{ + static const double POINTS_PER_UNIT_AREA = 1.; + static const Vec3d DOWN = {0., 0., -1.}; + + double score = 0.; + +// double zmin = mesh.bounding_box().min.z(); + +// std::vector normals(mesh.its.indices.size(), Vec3d::Zero()); + + double zmin = 0; + for (auto & v : mesh.its.vertices) + zmin = std::min(zmin, double((tr * v.cast()).z())); + + for (size_t fi = 0; fi < mesh.its.indices.size(); ++fi) { + const auto &face = mesh.its.indices[fi]; + Vec3d p1 = tr * mesh.its.vertices[face(0)].cast(); + Vec3d p2 = tr * mesh.its.vertices[face(1)].cast(); + Vec3d p3 = tr * mesh.its.vertices[face(2)].cast(); + +// auto triang = std::array {p1, p2, p3}; +// double a = area(triang.begin(), triang.end()); + double a = area(p1, p2, p3); + + double zlvl = zmin + 0.1; + if (p1.z() <= zlvl && p2.z() <= zlvl && p3.z() <= zlvl) { + score += a * POINTS_PER_UNIT_AREA; + continue; + } + + + Eigen::Vector3d U = p2 - p1; + Eigen::Vector3d V = p3 - p1; + Vec3d N = U.cross(V).normalized(); + + double phi = std::acos(N.dot(DOWN)) / PI; + + std::cout << "area: " << a << std::endl; + + score += a * POINTS_PER_UNIT_AREA * phi; +// normals[fi] = N; + } + +// for (size_t vi = 0; vi < mesh.its.vertices.size(); ++vi) { +// const std::vector &neighbors = vmap[vi]; + +// const auto &v = mesh.its.vertices[vi]; +// Vec3d vt = tr * v.cast(); +// } + + return score; +} + +std::array find_best_rotation(const ModelObject& modelobj, float accuracy, std::function statuscb, std::function stopcond) { - using libnest2d::opt::Method; - using libnest2d::opt::bound; - using libnest2d::opt::Optimizer; - using libnest2d::opt::TOptimizer; - using libnest2d::opt::StopCriteria; - - static const unsigned MAX_TRIES = 100000; + static const unsigned MAX_TRIES = 1000000; // return value - std::array rot; + std::array rot; // We will use only one instance of this converted mesh to examine different // rotations - const TriangleMesh& mesh = modelobj.raw_mesh(); + TriangleMesh mesh = modelobj.raw_mesh(); + mesh.require_shared_vertices(); +// auto vmap = create_vertex_face_map(mesh); +// simplify_mesh(mesh); // For current iteration number unsigned status = 0; @@ -44,40 +124,15 @@ std::array find_best_rotation(const ModelObject& modelobj, // the same for subsequent iterations (status goes from 0 to 100 but // iterations can be many more) auto objfunc = [&mesh, &status, &statuscb, &stopcond, max_tries] - (double rx, double ry, double rz) + (const opt::Input<2> &in) { - const TriangleMesh& m = mesh; - // prepare the rotation transformation Transform3d rt = Transform3d::Identity(); + rt.rotate(Eigen::AngleAxisd(in[1], Vec3d::UnitY())); + rt.rotate(Eigen::AngleAxisd(in[0], Vec3d::UnitX())); - rt.rotate(Eigen::AngleAxisd(rz, Vec3d::UnitZ())); - rt.rotate(Eigen::AngleAxisd(ry, Vec3d::UnitY())); - rt.rotate(Eigen::AngleAxisd(rx, Vec3d::UnitX())); - - double score = 0; - - // For all triangles we calculate the normal and sum up the dot product - // (a scalar indicating how much are two vectors aligned) with each axis - // this will result in a value that is greater if a normal is aligned - // with all axes. If the normal is aligned than the triangle itself is - // orthogonal to the axes and that is good for print quality. - - // TODO: some applications optimize for minimum z-axis cross section - // area. The current function is only an example of how to optimize. - - // Later we can add more criteria like the number of overhangs, etc... - for(size_t i = 0; i < m.stl.facet_start.size(); i++) { - Vec3d n = m.stl.facet_start[i].normal.cast(); - - // rotate the normal with the current rotation given by the solver - n = rt * n; - - // We should score against the alignment with the reference planes - score += std::abs(n.dot(Vec3d::UnitX())); - score += std::abs(n.dot(Vec3d::UnitY())); - score += std::abs(n.dot(Vec3d::UnitZ())); - } + double score = sla::calculate_model_supportedness(mesh, {}, rt); + std::cout << score << std::endl; // report status if(!stopcond()) statuscb( unsigned(++status * 100.0/max_tries) ); @@ -86,26 +141,24 @@ std::array find_best_rotation(const ModelObject& modelobj, }; // Firing up the genetic optimizer. For now it uses the nlopt library. - StopCriteria stc; - stc.max_iterations = max_tries; - stc.relative_score_difference = 1e-3; - stc.stop_condition = stopcond; // stop when stopcond returns true - TOptimizer solver(stc); + + opt::Optimizer solver(opt::StopCriteria{} + .max_iterations(max_tries) + .rel_score_diff(1e-3) + .stop_condition(stopcond)); // We are searching rotations around the three axes x, y, z. Thus the // problem becomes a 3 dimensional optimization task. // We can specify the bounds for a dimension in the following way: - auto b = bound(-PI/2, PI/2); + auto b = opt::Bound{-PI, PI}; // Now we start the optimization process with initial angles (0, 0, 0) - auto result = solver.optimize_max(objfunc, - libnest2d::opt::initvals(0.0, 0.0, 0.0), - b, b, b); + auto result = solver.to_max().optimize(objfunc, opt::initvals({0.0, 0.0}), + opt::bounds({b, b})); // Save the result and fck off rot[0] = std::get<0>(result.optimum); rot[1] = std::get<1>(result.optimum); - rot[2] = std::get<2>(result.optimum); return rot; } diff --git a/src/libslic3r/SLA/Rotfinder.hpp b/src/libslic3r/SLA/Rotfinder.hpp index 4469f9731d..583703203a 100644 --- a/src/libslic3r/SLA/Rotfinder.hpp +++ b/src/libslic3r/SLA/Rotfinder.hpp @@ -25,7 +25,7 @@ namespace sla { * * @return Returns the rotations around each axis (x, y, z) */ -std::array find_best_rotation( +std::array find_best_rotation( const ModelObject& modelobj, float accuracy = 1.0f, std::function statuscb = [] (unsigned) {}, diff --git a/src/slic3r/GUI/Jobs/RotoptimizeJob.cpp b/src/slic3r/GUI/Jobs/RotoptimizeJob.cpp index c847c84b48..3fd86b13f5 100644 --- a/src/slic3r/GUI/Jobs/RotoptimizeJob.cpp +++ b/src/slic3r/GUI/Jobs/RotoptimizeJob.cpp @@ -18,7 +18,7 @@ void RotoptimizeJob::process() auto r = sla::find_best_rotation( *o, - .005f, + 1.f, [this](unsigned s) { if (s < 100) update_status(int(s), @@ -31,7 +31,7 @@ void RotoptimizeJob::process() if (!was_canceled()) { for(ModelInstance * oi : o->instances) { - oi->set_rotation({r[X], r[Y], r[Z]}); + oi->set_rotation({r[X], r[Y], 0.}); auto trmatrix = oi->get_transformation().get_matrix(); Polygon trchull = o->convex_hull_2d(trmatrix); From c193d7c93081e0cbe6c9510436f413fe759e2b10 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 27 Aug 2020 23:13:05 +0200 Subject: [PATCH 473/503] Brute force optimization code, buggy yet wip wip wip refactor --- src/libslic3r/CMakeLists.txt | 3 +- .../Optimize/BruteforceOptimizer.hpp | 120 ++++++++++++ .../NLoptOptimizer.hpp} | 156 +-------------- src/libslic3r/Optimize/Optimizer.hpp | 182 ++++++++++++++++++ src/libslic3r/SLA/Concurrency.hpp | 80 +++++--- src/libslic3r/SLA/Rotfinder.cpp | 111 ++++++----- src/libslic3r/SLA/SupportTreeBuildsteps.cpp | 2 +- 7 files changed, 427 insertions(+), 227 deletions(-) create mode 100644 src/libslic3r/Optimize/BruteforceOptimizer.hpp rename src/libslic3r/{Optimizer.hpp => Optimize/NLoptOptimizer.hpp} (59%) create mode 100644 src/libslic3r/Optimize/Optimizer.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 09f75c747c..263920ecbb 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -215,7 +215,8 @@ add_library(libslic3r STATIC SimplifyMeshImpl.hpp SimplifyMesh.cpp MarchingSquares.hpp - Optimizer.hpp + Optimize/Optimizer.hpp + Optimize/NLoptOptimizer.hpp ${OpenVDBUtils_SOURCES} SLA/Pad.hpp SLA/Pad.cpp diff --git a/src/libslic3r/Optimize/BruteforceOptimizer.hpp b/src/libslic3r/Optimize/BruteforceOptimizer.hpp new file mode 100644 index 0000000000..da44725688 --- /dev/null +++ b/src/libslic3r/Optimize/BruteforceOptimizer.hpp @@ -0,0 +1,120 @@ +#ifndef BRUTEFORCEOPTIMIZER_HPP +#define BRUTEFORCEOPTIMIZER_HPP + +#include + +namespace Slic3r { namespace opt { + +namespace detail { +// Implementing a bruteforce optimizer + +template +constexpr long num_iter(const std::array &idx, size_t gridsz) +{ + long ret = 0; + for (size_t i = 0; i < N; ++i) ret += idx[i] * std::pow(gridsz, i); + return ret; +} + +struct AlgBurteForce { + bool to_min; + StopCriteria stc; + size_t gridsz; + + AlgBurteForce(const StopCriteria &cr, size_t gs): stc{cr}, gridsz{gs} {} + + template + void run(std::array &idx, + Result &result, + const Bounds &bounds, + Fn &&fn, + Cmp &&cmp) + { + if (stc.stop_condition()) return; + + if constexpr (D < 0) { + Input inp; + + auto max_iter = stc.max_iterations(); + if (max_iter && num_iter(idx, gridsz) >= max_iter) return; + + for (size_t d = 0; d < N; ++d) { + const Bound &b = bounds[d]; + double step = (b.max() - b.min()) / (gridsz - 1); + inp[d] = b.min() + idx[d] * step; + } + + auto score = fn(inp); + if (cmp(score, result.score)) { + result.score = score; + result.optimum = inp; + } + + } else { + for (size_t i = 0; i < gridsz; ++i) { + idx[D] = i; + run(idx, result, bounds, std::forward(fn), + std::forward(cmp)); + } + } + } + + template + Result optimize(Fn&& fn, + const Input &/*initvals*/, + const Bounds& bounds) + { + std::array idx = {}; + Result result; + + if (to_min) { + result.score = std::numeric_limits::max(); + run(idx, result, bounds, std::forward(fn), + std::less{}); + } + else { + result.score = std::numeric_limits::lowest(); + run(idx, result, bounds, std::forward(fn), + std::greater{}); + } + + return result; + } +}; + +} // namespace bruteforce_detail + +using AlgBruteForce = detail::AlgBurteForce; + +template<> +class Optimizer { + AlgBruteForce m_alg; + +public: + + Optimizer(const StopCriteria &cr = {}, size_t gridsz = 100) + : m_alg{cr, gridsz} + {} + + Optimizer& to_max() { m_alg.to_min = false; return *this; } + Optimizer& to_min() { m_alg.to_min = true; return *this; } + + template + Result optimize(Func&& func, + const Input &initvals, + const Bounds& bounds) + { + return m_alg.optimize(std::forward(func), initvals, bounds); + } + + Optimizer &set_criteria(const StopCriteria &cr) + { + m_alg.stc = cr; return *this; + } + + const StopCriteria &get_criteria() const { return m_alg.stc; } +}; + +}} // namespace Slic3r::opt + +#endif // BRUTEFORCEOPTIMIZER_HPP diff --git a/src/libslic3r/Optimizer.hpp b/src/libslic3r/Optimize/NLoptOptimizer.hpp similarity index 59% rename from src/libslic3r/Optimizer.hpp rename to src/libslic3r/Optimize/NLoptOptimizer.hpp index 1c94f3c1ed..826b1632ae 100644 --- a/src/libslic3r/Optimizer.hpp +++ b/src/libslic3r/Optimize/NLoptOptimizer.hpp @@ -12,134 +12,11 @@ #endif #include -#include -#include -#include -#include -#include -#include + +#include namespace Slic3r { namespace opt { -// A type to hold the complete result of the optimization. -template struct Result { - int resultcode; - std::array optimum; - double score; -}; - -// An interval of possible input values for optimization -class Bound { - double m_min, m_max; - -public: - Bound(double min = std::numeric_limits::min(), - double max = std::numeric_limits::max()) - : m_min(min), m_max(max) - {} - - double min() const noexcept { return m_min; } - double max() const noexcept { return m_max; } -}; - -// Helper types for optimization function input and bounds -template using Input = std::array; -template using Bounds = std::array; - -// A type for specifying the stop criteria. Setter methods can be concatenated -class StopCriteria { - - // If the absolute value difference between two scores. - double m_abs_score_diff = std::nan(""); - - // If the relative value difference between two scores. - double m_rel_score_diff = std::nan(""); - - // Stop if this value or better is found. - double m_stop_score = std::nan(""); - - // A predicate that if evaluates to true, the optimization should terminate - // and the best result found prior to termination should be returned. - std::function m_stop_condition = [] { return false; }; - - // The max allowed number of iterations. - unsigned m_max_iterations = 0; - -public: - - StopCriteria & abs_score_diff(double val) - { - m_abs_score_diff = val; return *this; - } - - double abs_score_diff() const { return m_abs_score_diff; } - - StopCriteria & rel_score_diff(double val) - { - m_rel_score_diff = val; return *this; - } - - double rel_score_diff() const { return m_rel_score_diff; } - - StopCriteria & stop_score(double val) - { - m_stop_score = val; return *this; - } - - double stop_score() const { return m_stop_score; } - - StopCriteria & max_iterations(double val) - { - m_max_iterations = val; return *this; - } - - double max_iterations() const { return m_max_iterations; } - - template StopCriteria & stop_condition(Fn &&cond) - { - m_stop_condition = cond; return *this; - } - - bool stop_condition() { return m_stop_condition(); } -}; - -// Helper class to use optimization methods involving gradient. -template struct ScoreGradient { - double score; - std::optional> gradient; - - ScoreGradient(double s, const std::array &grad) - : score{s}, gradient{grad} - {} -}; - -// Helper to be used in static_assert. -template struct always_false { enum { value = false }; }; - -// Basic interface to optimizer object -template class Optimizer { -public: - - Optimizer(const StopCriteria &) - { - static_assert (always_false::value, - "Optimizer unimplemented for given method!"); - } - - Optimizer &to_min() { return *this; } - Optimizer &to_max() { return *this; } - Optimizer &set_criteria(const StopCriteria &) { return *this; } - StopCriteria get_criteria() const { return {}; }; - - template - Result optimize(Func&& func, - const Input &initvals, - const Bounds& bounds) { return {}; } - - // optional for randomized methods: - void seed(long /*s*/) {} -}; - namespace detail { // Helper types for NLopt algorithm selection in template contexts @@ -166,19 +43,6 @@ struct IsNLoptAlg> { template using NLoptOnly = std::enable_if_t::value, T>; -// Helper to convert C style array to std::array. The copy should be optimized -// away with modern compilers. -template auto to_arr(const T *a) -{ - std::array r; - std::copy(a, a + N, std::begin(r)); - return r; -} - -template auto to_arr(const T (&a) [N]) -{ - return to_arr(static_cast(a)); -} enum class OptDir { MIN, MAX }; // Where to optimize @@ -357,24 +221,12 @@ public: void seed(long s) { m_opt.seed(s); } }; -template Bounds bounds(const Bound (&b) [N]) { return detail::to_arr(b); } -template Input initvals(const double (&a) [N]) { return detail::to_arr(a); } -template auto score_gradient(double s, const double (&grad)[N]) -{ - return ScoreGradient(s, detail::to_arr(grad)); -} - -// Predefinded NLopt algorithms that are used in the codebase +// Predefinded NLopt algorithms using AlgNLoptGenetic = detail::NLoptAlgComb; using AlgNLoptSubplex = detail::NLoptAlg; using AlgNLoptSimplex = detail::NLoptAlg; using AlgNLoptDIRECT = detail::NLoptAlg; - -// TODO: define others if needed... - -// Helper defs for pre-crafted global and local optimizers that work well. -using DefaultGlobalOptimizer = Optimizer; -using DefaultLocalOptimizer = Optimizer; +using AlgNLoptMLSL = detail::NLoptAlg; }} // namespace Slic3r::opt diff --git a/src/libslic3r/Optimize/Optimizer.hpp b/src/libslic3r/Optimize/Optimizer.hpp new file mode 100644 index 0000000000..05191eba26 --- /dev/null +++ b/src/libslic3r/Optimize/Optimizer.hpp @@ -0,0 +1,182 @@ +#ifndef OPTIMIZER_HPP +#define OPTIMIZER_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace Slic3r { namespace opt { + +// A type to hold the complete result of the optimization. +template struct Result { + int resultcode; // Method dependent + std::array optimum; + double score; +}; + +// An interval of possible input values for optimization +class Bound { + double m_min, m_max; + +public: + Bound(double min = std::numeric_limits::min(), + double max = std::numeric_limits::max()) + : m_min(min), m_max(max) + {} + + double min() const noexcept { return m_min; } + double max() const noexcept { return m_max; } +}; + +// Helper types for optimization function input and bounds +template using Input = std::array; +template using Bounds = std::array; + +// A type for specifying the stop criteria. Setter methods can be concatenated +class StopCriteria { + + // If the absolute value difference between two scores. + double m_abs_score_diff = std::nan(""); + + // If the relative value difference between two scores. + double m_rel_score_diff = std::nan(""); + + // Stop if this value or better is found. + double m_stop_score = std::nan(""); + + // A predicate that if evaluates to true, the optimization should terminate + // and the best result found prior to termination should be returned. + std::function m_stop_condition = [] { return false; }; + + // The max allowed number of iterations. + unsigned m_max_iterations = 0; + +public: + + StopCriteria & abs_score_diff(double val) + { + m_abs_score_diff = val; return *this; + } + + double abs_score_diff() const { return m_abs_score_diff; } + + StopCriteria & rel_score_diff(double val) + { + m_rel_score_diff = val; return *this; + } + + double rel_score_diff() const { return m_rel_score_diff; } + + StopCriteria & stop_score(double val) + { + m_stop_score = val; return *this; + } + + double stop_score() const { return m_stop_score; } + + StopCriteria & max_iterations(double val) + { + m_max_iterations = val; return *this; + } + + double max_iterations() const { return m_max_iterations; } + + template StopCriteria & stop_condition(Fn &&cond) + { + m_stop_condition = cond; return *this; + } + + bool stop_condition() { return m_stop_condition(); } +}; + +// Helper class to use optimization methods involving gradient. +template struct ScoreGradient { + double score; + std::optional> gradient; + + ScoreGradient(double s, const std::array &grad) + : score{s}, gradient{grad} + {} +}; + +// Helper to be used in static_assert. +template struct always_false { enum { value = false }; }; + +// Basic interface to optimizer object +template class Optimizer { +public: + + Optimizer(const StopCriteria &) + { + static_assert (always_false::value, + "Optimizer unimplemented for given method!"); + } + + // Switch optimization towards function minimum + Optimizer &to_min() { return *this; } + + // Switch optimization towards function maximum + Optimizer &to_max() { return *this; } + + // Set criteria for successive optimizations + Optimizer &set_criteria(const StopCriteria &) { return *this; } + + // Get current criteria + StopCriteria get_criteria() const { return {}; }; + + // Find function minimum or maximum for Func which has has signature: + // double(const Input &input) and input with dimension N + // + // Initial starting point can be given as the second parameter. + // + // For each dimension an interval (Bound) has to be given marking the bounds + // for that dimension. + // + // initvals have to be within the specified bounds, otherwise its undefined + // behavior. + // + // Func can return a score of type double or optionally a ScoreGradient + // class to indicate the function gradient for a optimization methods that + // make use of the gradient. + template + Result optimize(Func&& /*func*/, + const Input &/*initvals*/, + const Bounds& /*bounds*/) { return {}; } + + // optional for randomized methods: + void seed(long /*s*/) {} +}; + +namespace detail { + +// Helper to convert C style array to std::array. The copy should be optimized +// away with modern compilers. +template auto to_arr(const T *a) +{ + std::array r; + std::copy(a, a + N, std::begin(r)); + return r; +} + +template auto to_arr(const T (&a) [N]) +{ + return to_arr(static_cast(a)); +} + +} // namespace detail + +// Helper functions to create bounds, initial value +template Bounds bounds(const Bound (&b) [N]) { return detail::to_arr(b); } +template Input initvals(const double (&a) [N]) { return detail::to_arr(a); } +template auto score_gradient(double s, const double (&grad)[N]) +{ + return ScoreGradient(s, detail::to_arr(grad)); +} + +}} // namespace Slic3r::opt + +#endif // OPTIMIZER_HPP diff --git a/src/libslic3r/SLA/Concurrency.hpp b/src/libslic3r/SLA/Concurrency.hpp index 93ba8c4ebd..f82d6a39ee 100644 --- a/src/libslic3r/SLA/Concurrency.hpp +++ b/src/libslic3r/SLA/Concurrency.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -21,28 +22,43 @@ template<> struct _ccr using SpinningMutex = tbb::spin_mutex; using BlockingMutex = tbb::mutex; + template + static IteratorOnly loop_(const tbb::blocked_range &range, Fn &&fn) + { + for (auto &el : range) fn(el); + } + + template + static IntegerOnly loop_(const tbb::blocked_range &range, Fn &&fn) + { + for (I i = range.begin(); i < range.end(); ++i) fn(i); + } + template - static IteratorOnly for_each(It from, - It to, - Fn && fn, - size_t granularity = 1) + static void for_each(It from, It to, Fn &&fn, size_t granularity = 1) { tbb::parallel_for(tbb::blocked_range{from, to, granularity}, [&fn, from](const auto &range) { - for (auto &el : range) fn(el); + loop_(range, std::forward(fn)); }); } - template - static IntegerOnly for_each(I from, - I to, - Fn && fn, - size_t granularity = 1) + template + static T reduce(I from, + I to, + const T & init, + Fn && fn, + MergeFn &&mergefn, + size_t granularity = 1) { - tbb::parallel_for(tbb::blocked_range{from, to, granularity}, - [&fn](const auto &range) { - for (I i = range.begin(); i < range.end(); ++i) fn(i); - }); + return tbb::parallel_reduce( + tbb::blocked_range{from, to, granularity}, init, + [&](const auto &range, T subinit) { + T acc = subinit; + loop_(range, [&](auto &i) { acc = mergefn(acc, fn(i, acc)); }); + return acc; + }, + std::forward(mergefn)); } }; @@ -55,23 +71,39 @@ public: using SpinningMutex = _Mtx; using BlockingMutex = _Mtx; - template - static IteratorOnly for_each(It from, - It to, - Fn &&fn, - size_t /* ignore granularity */ = 1) + template + static IteratorOnly loop_(It from, It to, Fn &&fn) { for (auto it = from; it != to; ++it) fn(*it); } - template - static IntegerOnly for_each(I from, - I to, - Fn &&fn, - size_t /* ignore granularity */ = 1) + template + static IntegerOnly loop_(I from, I to, Fn &&fn) { for (I i = from; i < to; ++i) fn(i); } + + template + static void for_each(It from, + It to, + Fn &&fn, + size_t /* ignore granularity */ = 1) + { + loop_(from, to, std::forward(fn)); + } + + template + static IntegerOnly reduce(I from, + I to, + const T & init, + Fn && fn, + MergeFn &&mergefn, + size_t /*granularity*/ = 1) + { + T acc = init; + loop_(from, to, [&](auto &i) { acc = mergefn(acc, fn(i, acc)); }); + return acc; + } }; using ccr = _ccr; diff --git a/src/libslic3r/SLA/Rotfinder.cpp b/src/libslic3r/SLA/Rotfinder.cpp index b4b1fae391..723e50eeb5 100644 --- a/src/libslic3r/SLA/Rotfinder.cpp +++ b/src/libslic3r/SLA/Rotfinder.cpp @@ -2,23 +2,19 @@ #include //#include -#include +#include #include +#include #include #include #include #include "Model.hpp" +#include + namespace Slic3r { namespace sla { -double area(const Vec3d &p1, const Vec3d &p2, const Vec3d &p3) { - Vec3d a = p2 - p1; - Vec3d b = p3 - p1; - Vec3d c = a.cross(b); - return 0.5 * c.norm(); -} - using VertexFaceMap = std::vector>; VertexFaceMap create_vertex_face_map(const TriangleMesh &mesh) { @@ -35,61 +31,75 @@ VertexFaceMap create_vertex_face_map(const TriangleMesh &mesh) { return vmap; } +// Find transformed mesh ground level without copy and with parallell reduce. +double find_ground_level(const TriangleMesh &mesh, + const Transform3d & tr, + size_t threads) +{ + size_t vsize = mesh.its.vertices.size(); + + auto minfn = [](double a, double b) { return std::min(a, b); }; + + auto findminz = [&mesh, &tr] (size_t vi, double submin) { + Vec3d v = tr * mesh.its.vertices[vi].template cast(); + return std::min(submin, v.z()); + }; + + double zmin = mesh.its.vertices.front().z(); + + return ccr_par::reduce(size_t(0), vsize, zmin, findminz, minfn, + vsize / threads); +} + // Try to guess the number of support points needed to support a mesh double calculate_model_supportedness(const TriangleMesh & mesh, - const VertexFaceMap &vmap, +// const VertexFaceMap &vmap, const Transform3d & tr) { - static const double POINTS_PER_UNIT_AREA = 1.; - static const Vec3d DOWN = {0., 0., -1.}; + static constexpr double POINTS_PER_UNIT_AREA = 1.; - double score = 0.; + if (mesh.its.vertices.empty()) return std::nan(""); -// double zmin = mesh.bounding_box().min.z(); + size_t Nthr = std::thread::hardware_concurrency(); + size_t facesize = mesh.its.indices.size(); -// std::vector normals(mesh.its.indices.size(), Vec3d::Zero()); + double zmin = find_ground_level(mesh, tr, Nthr); - double zmin = 0; - for (auto & v : mesh.its.vertices) - zmin = std::min(zmin, double((tr * v.cast()).z())); + auto score_mergefn = [&mesh, &tr, zmin](size_t fi, double subscore) { + + static const Vec3d DOWN = {0., 0., -1.}; - for (size_t fi = 0; fi < mesh.its.indices.size(); ++fi) { const auto &face = mesh.its.indices[fi]; - Vec3d p1 = tr * mesh.its.vertices[face(0)].cast(); - Vec3d p2 = tr * mesh.its.vertices[face(1)].cast(); - Vec3d p3 = tr * mesh.its.vertices[face(2)].cast(); + Vec3d p1 = tr * mesh.its.vertices[face(0)].template cast(); + Vec3d p2 = tr * mesh.its.vertices[face(1)].template cast(); + Vec3d p3 = tr * mesh.its.vertices[face(2)].template cast(); -// auto triang = std::array {p1, p2, p3}; -// double a = area(triang.begin(), triang.end()); - double a = area(p1, p2, p3); + Vec3d U = p2 - p1; + Vec3d V = p3 - p1; + Vec3d C = U.cross(V); + Vec3d N = C.normalized(); + double area = 0.5 * C.norm(); double zlvl = zmin + 0.1; if (p1.z() <= zlvl && p2.z() <= zlvl && p3.z() <= zlvl) { - score += a * POINTS_PER_UNIT_AREA; - continue; + // score += area * POINTS_PER_UNIT_AREA; + return subscore; } + double phi = 1. - std::acos(N.dot(DOWN)) / PI; + phi = phi * (phi > 0.5); - Eigen::Vector3d U = p2 - p1; - Eigen::Vector3d V = p3 - p1; - Vec3d N = U.cross(V).normalized(); + // std::cout << "area: " << area << std::endl; - double phi = std::acos(N.dot(DOWN)) / PI; + subscore += area * POINTS_PER_UNIT_AREA * phi; - std::cout << "area: " << a << std::endl; + return subscore; + }; - score += a * POINTS_PER_UNIT_AREA * phi; -// normals[fi] = N; - } + double score = ccr_seq::reduce(size_t(0), facesize, 0., score_mergefn, + std::plus{}, facesize / Nthr); -// for (size_t vi = 0; vi < mesh.its.vertices.size(); ++vi) { -// const std::vector &neighbors = vmap[vi]; - -// const auto &v = mesh.its.vertices[vi]; -// Vec3d vt = tr * v.cast(); -// } - - return score; + return score / mesh.its.indices.size(); } std::array find_best_rotation(const ModelObject& modelobj, @@ -97,7 +107,7 @@ std::array find_best_rotation(const ModelObject& modelobj, std::function statuscb, std::function stopcond) { - static const unsigned MAX_TRIES = 1000000; + static const unsigned MAX_TRIES = 100; // return value std::array rot; @@ -126,12 +136,14 @@ std::array find_best_rotation(const ModelObject& modelobj, auto objfunc = [&mesh, &status, &statuscb, &stopcond, max_tries] (const opt::Input<2> &in) { + std::cout << "in: " << in[0] << " " << in[1] << std::endl; + // prepare the rotation transformation Transform3d rt = Transform3d::Identity(); rt.rotate(Eigen::AngleAxisd(in[1], Vec3d::UnitY())); rt.rotate(Eigen::AngleAxisd(in[0], Vec3d::UnitX())); - double score = sla::calculate_model_supportedness(mesh, {}, rt); + double score = sla::calculate_model_supportedness(mesh, rt); std::cout << score << std::endl; // report status @@ -142,10 +154,11 @@ std::array find_best_rotation(const ModelObject& modelobj, // Firing up the genetic optimizer. For now it uses the nlopt library. - opt::Optimizer solver(opt::StopCriteria{} - .max_iterations(max_tries) - .rel_score_diff(1e-3) - .stop_condition(stopcond)); + opt::Optimizer solver(opt::StopCriteria{} + .max_iterations(max_tries) + .rel_score_diff(1e-6) + .stop_condition(stopcond), + 10 /*grid size*/); // We are searching rotations around the three axes x, y, z. Thus the // problem becomes a 3 dimensional optimization task. @@ -153,7 +166,7 @@ std::array find_best_rotation(const ModelObject& modelobj, auto b = opt::Bound{-PI, PI}; // Now we start the optimization process with initial angles (0, 0, 0) - auto result = solver.to_max().optimize(objfunc, opt::initvals({0.0, 0.0}), + auto result = solver.to_min().optimize(objfunc, opt::initvals({0.0, 0.0}), opt::bounds({b, b})); // Save the result and fck off diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp index 0e7af8d508..3c39c64e6b 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include namespace Slic3r { From c10ff4f503208f965219d901234baa44b6d6123f Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 31 Aug 2020 18:53:44 +0200 Subject: [PATCH 474/503] fixing optimizer and concurrency::reduce --- .../Optimize/BruteforceOptimizer.hpp | 20 +++++-- src/libslic3r/SLA/Concurrency.hpp | 58 +++++++++++++----- src/libslic3r/SLA/Rotfinder.cpp | 33 +++++------ tests/libslic3r/CMakeLists.txt | 1 + tests/libslic3r/test_optimizers.cpp | 59 +++++++++++++++++++ tests/sla_print/sla_print_tests.cpp | 12 ++++ 6 files changed, 142 insertions(+), 41 deletions(-) create mode 100644 tests/libslic3r/test_optimizers.cpp diff --git a/src/libslic3r/Optimize/BruteforceOptimizer.hpp b/src/libslic3r/Optimize/BruteforceOptimizer.hpp index da44725688..960676e7b0 100644 --- a/src/libslic3r/Optimize/BruteforceOptimizer.hpp +++ b/src/libslic3r/Optimize/BruteforceOptimizer.hpp @@ -1,7 +1,7 @@ #ifndef BRUTEFORCEOPTIMIZER_HPP #define BRUTEFORCEOPTIMIZER_HPP -#include +#include namespace Slic3r { namespace opt { @@ -24,19 +24,19 @@ struct AlgBurteForce { AlgBurteForce(const StopCriteria &cr, size_t gs): stc{cr}, gridsz{gs} {} template - void run(std::array &idx, + bool run(std::array &idx, Result &result, const Bounds &bounds, Fn &&fn, Cmp &&cmp) { - if (stc.stop_condition()) return; + if (stc.stop_condition()) return false; if constexpr (D < 0) { Input inp; auto max_iter = stc.max_iterations(); - if (max_iter && num_iter(idx, gridsz) >= max_iter) return; + if (max_iter && num_iter(idx, gridsz) >= max_iter) return false; for (size_t d = 0; d < N; ++d) { const Bound &b = bounds[d]; @@ -46,17 +46,25 @@ struct AlgBurteForce { auto score = fn(inp); if (cmp(score, result.score)) { + double absdiff = std::abs(score - result.score); + result.score = score; result.optimum = inp; + + if (absdiff < stc.abs_score_diff() || + absdiff < stc.rel_score_diff() * std::abs(score)) + return false; } } else { for (size_t i = 0; i < gridsz; ++i) { idx[D] = i; - run(idx, result, bounds, std::forward(fn), - std::forward(cmp)); + if (!run(idx, result, bounds, std::forward(fn), + std::forward(cmp))) return false; } } + + return true; } template diff --git a/src/libslic3r/SLA/Concurrency.hpp b/src/libslic3r/SLA/Concurrency.hpp index f82d6a39ee..a7b5b6c61d 100644 --- a/src/libslic3r/SLA/Concurrency.hpp +++ b/src/libslic3r/SLA/Concurrency.hpp @@ -43,23 +43,36 @@ template<> struct _ccr }); } - template - static T reduce(I from, - I to, - const T & init, - Fn && fn, - MergeFn &&mergefn, - size_t granularity = 1) + template + static T reduce(I from, + I to, + const T &init, + MergeFn &&mergefn, + AccessFn &&access, + size_t granularity = 1 + ) { return tbb::parallel_reduce( tbb::blocked_range{from, to, granularity}, init, [&](const auto &range, T subinit) { T acc = subinit; - loop_(range, [&](auto &i) { acc = mergefn(acc, fn(i, acc)); }); + loop_(range, [&](auto &i) { acc = mergefn(acc, access(i)); }); return acc; }, std::forward(mergefn)); } + + template + static IteratorOnly reduce(I from, + I to, + const T & init, + MergeFn &&mergefn, + size_t granularity = 1) + { + return reduce( + from, to, init, std::forward(mergefn), + [](typename I::value_type &i) { return i; }, granularity); + } }; template<> struct _ccr @@ -92,18 +105,31 @@ public: loop_(from, to, std::forward(fn)); } - template - static IntegerOnly reduce(I from, - I to, - const T & init, - Fn && fn, - MergeFn &&mergefn, - size_t /*granularity*/ = 1) + template + static T reduce(I from, + I to, + const T & init, + MergeFn &&mergefn, + AccessFn &&access, + size_t /*granularity*/ = 1 + ) { T acc = init; - loop_(from, to, [&](auto &i) { acc = mergefn(acc, fn(i, acc)); }); + loop_(from, to, [&](auto &i) { acc = mergefn(acc, access(i)); }); return acc; } + + template + static IteratorOnly reduce(I from, + I to, + const T &init, + MergeFn &&mergefn, + size_t /*granularity*/ = 1 + ) + { + return reduce(from, to, init, std::forward(mergefn), + [](typename I::value_type &i) { return i; }); + } }; using ccr = _ccr; diff --git a/src/libslic3r/SLA/Rotfinder.cpp b/src/libslic3r/SLA/Rotfinder.cpp index 723e50eeb5..b7bed1c911 100644 --- a/src/libslic3r/SLA/Rotfinder.cpp +++ b/src/libslic3r/SLA/Rotfinder.cpp @@ -31,7 +31,7 @@ VertexFaceMap create_vertex_face_map(const TriangleMesh &mesh) { return vmap; } -// Find transformed mesh ground level without copy and with parallell reduce. +// Find transformed mesh ground level without copy and with parallel reduce. double find_ground_level(const TriangleMesh &mesh, const Transform3d & tr, size_t threads) @@ -40,15 +40,13 @@ double find_ground_level(const TriangleMesh &mesh, auto minfn = [](double a, double b) { return std::min(a, b); }; - auto findminz = [&mesh, &tr] (size_t vi, double submin) { - Vec3d v = tr * mesh.its.vertices[vi].template cast(); - return std::min(submin, v.z()); + auto accessfn = [&mesh, &tr] (size_t vi) { + return (tr * mesh.its.vertices[vi].template cast()).z(); }; double zmin = mesh.its.vertices.front().z(); - - return ccr_par::reduce(size_t(0), vsize, zmin, findminz, minfn, - vsize / threads); + size_t granularity = vsize / threads; + return ccr_par::reduce(size_t(0), vsize, zmin, minfn, accessfn, granularity); } // Try to guess the number of support points needed to support a mesh @@ -65,7 +63,7 @@ double calculate_model_supportedness(const TriangleMesh & mesh, double zmin = find_ground_level(mesh, tr, Nthr); - auto score_mergefn = [&mesh, &tr, zmin](size_t fi, double subscore) { + auto accessfn = [&mesh, &tr, zmin](size_t fi) { static const Vec3d DOWN = {0., 0., -1.}; @@ -83,21 +81,18 @@ double calculate_model_supportedness(const TriangleMesh & mesh, double zlvl = zmin + 0.1; if (p1.z() <= zlvl && p2.z() <= zlvl && p3.z() <= zlvl) { // score += area * POINTS_PER_UNIT_AREA; - return subscore; + return 0.; } double phi = 1. - std::acos(N.dot(DOWN)) / PI; - phi = phi * (phi > 0.5); +// phi = phi * (phi > 0.5); // std::cout << "area: " << area << std::endl; - subscore += area * POINTS_PER_UNIT_AREA * phi; - - return subscore; + return area * POINTS_PER_UNIT_AREA * phi; }; - double score = ccr_seq::reduce(size_t(0), facesize, 0., score_mergefn, - std::plus{}, facesize / Nthr); + double score = ccr_par::reduce(size_t(0), facesize, 0., std::plus{}, accessfn, facesize / Nthr); return score / mesh.its.indices.size(); } @@ -107,7 +102,7 @@ std::array find_best_rotation(const ModelObject& modelobj, std::function statuscb, std::function stopcond) { - static const unsigned MAX_TRIES = 100; + static const unsigned MAX_TRIES = 10000; // return value std::array rot; @@ -158,10 +153,10 @@ std::array find_best_rotation(const ModelObject& modelobj, .max_iterations(max_tries) .rel_score_diff(1e-6) .stop_condition(stopcond), - 10 /*grid size*/); + 100 /*grid size*/); - // We are searching rotations around the three axes x, y, z. Thus the - // problem becomes a 3 dimensional optimization task. + // We are searching rotations around only two axes x, y. Thus the + // problem becomes a 2 dimensional optimization task. // We can specify the bounds for a dimension in the following way: auto b = opt::Bound{-PI, PI}; diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index 30b93eafc9..501af0c6f3 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -17,6 +17,7 @@ add_executable(${_TEST_NAME}_tests test_marchingsquares.cpp test_timeutils.cpp test_voronoi.cpp + test_optimizers.cpp test_png_io.cpp test_timeutils.cpp ) diff --git a/tests/libslic3r/test_optimizers.cpp b/tests/libslic3r/test_optimizers.cpp new file mode 100644 index 0000000000..6e84f6a691 --- /dev/null +++ b/tests/libslic3r/test_optimizers.cpp @@ -0,0 +1,59 @@ +#include +#include + +#include + +#include + +void check_opt_result(double score, double ref, double abs_err, double rel_err) +{ + double abs_diff = std::abs(score - ref); + double rel_diff = std::abs(abs_diff / std::abs(ref)); + + bool abs_reached = abs_diff < abs_err; + bool rel_reached = rel_diff < rel_err; + bool precision_reached = abs_reached || rel_reached; + REQUIRE(precision_reached); +} + +template void test_sin(Opt &&opt) +{ + using namespace Slic3r::opt; + + auto optfunc = [](const auto &in) { + auto [phi] = in; + + return std::sin(phi); + }; + + auto init = initvals({PI}); + auto optbounds = bounds({ {0., 2 * PI}}); + + Result result_min = opt.to_min().optimize(optfunc, init, optbounds); + Result result_max = opt.to_max().optimize(optfunc, init, optbounds); + + check_opt_result(result_min.score, -1., 1e-2, 1e-4); + check_opt_result(result_max.score, 1., 1e-2, 1e-4); +} + +template void test_sphere_func(Opt &&opt) +{ + using namespace Slic3r::opt; + + Result result = opt.to_min().optimize([](const auto &in) { + auto [x, y] = in; + + return x * x + y * y + 1.; + }, initvals({.6, -0.2}), bounds({{-1., 1.}, {-1., 1.}})); + + check_opt_result(result.score, 1., 1e-2, 1e-4); +} + +TEST_CASE("Test brute force optimzer for basic 1D and 2D functions", "[Opt]") { + using namespace Slic3r::opt; + + Optimizer opt; + + test_sin(opt); + test_sphere_func(opt); +} diff --git a/tests/sla_print/sla_print_tests.cpp b/tests/sla_print/sla_print_tests.cpp index dad2b90971..1575ee0e6b 100644 --- a/tests/sla_print/sla_print_tests.cpp +++ b/tests/sla_print/sla_print_tests.cpp @@ -5,6 +5,7 @@ #include "sla_test_utils.hpp" #include +#include namespace { @@ -239,3 +240,14 @@ TEST_CASE("halfcone test", "[halfcone]") { m.require_shared_vertices(); m.WriteOBJFile("Halfcone.obj"); } + +TEST_CASE("Test concurrency") +{ + std::vector vals = grid(0., 100., 10.); + + double ref = std::accumulate(vals.begin(), vals.end(), 0.); + + double s = sla::ccr_par::reduce(vals.begin(), vals.end(), 0., std::plus{}); + + REQUIRE(s == Approx(ref)); +} From b4b9af410037fff27cfa0682e1deeb5744515d86 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 1 Sep 2020 20:08:42 +0200 Subject: [PATCH 475/503] cosmethics Comments and cosmethics --- src/libslic3r/CMakeLists.txt | 1 + .../Optimize/BruteforceOptimizer.hpp | 26 ++++++++++++++----- src/libslic3r/SLA/Rotfinder.cpp | 20 +++++--------- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 263920ecbb..e30811133a 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -217,6 +217,7 @@ add_library(libslic3r STATIC MarchingSquares.hpp Optimize/Optimizer.hpp Optimize/NLoptOptimizer.hpp + Optimize/BruteforceOptimizer.hpp ${OpenVDBUtils_SOURCES} SLA/Pad.hpp SLA/Pad.cpp diff --git a/src/libslic3r/Optimize/BruteforceOptimizer.hpp b/src/libslic3r/Optimize/BruteforceOptimizer.hpp index 960676e7b0..2daef538e7 100644 --- a/src/libslic3r/Optimize/BruteforceOptimizer.hpp +++ b/src/libslic3r/Optimize/BruteforceOptimizer.hpp @@ -8,14 +8,18 @@ namespace Slic3r { namespace opt { namespace detail { // Implementing a bruteforce optimizer +// Return the number of iterations needed to reach a specific grid position (idx) template -constexpr long num_iter(const std::array &idx, size_t gridsz) +long num_iter(const std::array &idx, size_t gridsz) { long ret = 0; for (size_t i = 0; i < N; ++i) ret += idx[i] * std::pow(gridsz, i); return ret; } +// Implementation of a grid search where the search interval is sampled in +// equidistant points for each dimension. Grid size determines the number of +// samples for one dimension so the number of function calls is gridsize ^ dimension. struct AlgBurteForce { bool to_min; StopCriteria stc; @@ -23,6 +27,11 @@ struct AlgBurteForce { AlgBurteForce(const StopCriteria &cr, size_t gs): stc{cr}, gridsz{gs} {} + // This function is called recursively for each dimension and generates + // the grid values for the particular dimension. If D is less than zero, + // the object function input values are generated for each dimension and it + // can be evaluated. The current best score is compared with the newly + // returned score and changed appropriately. template bool run(std::array &idx, Result &result, @@ -32,11 +41,12 @@ struct AlgBurteForce { { if (stc.stop_condition()) return false; - if constexpr (D < 0) { + if constexpr (D < 0) { // Let's evaluate fn Input inp; auto max_iter = stc.max_iterations(); - if (max_iter && num_iter(idx, gridsz) >= max_iter) return false; + if (max_iter && num_iter(idx, gridsz) >= max_iter) + return false; for (size_t d = 0; d < N; ++d) { const Bound &b = bounds[d]; @@ -45,12 +55,13 @@ struct AlgBurteForce { } auto score = fn(inp); - if (cmp(score, result.score)) { + if (cmp(score, result.score)) { // Change current score to the new double absdiff = std::abs(score - result.score); result.score = score; result.optimum = inp; + // Check if the required precision is reached. if (absdiff < stc.abs_score_diff() || absdiff < stc.rel_score_diff() * std::abs(score)) return false; @@ -58,9 +69,10 @@ struct AlgBurteForce { } else { for (size_t i = 0; i < gridsz; ++i) { - idx[D] = i; + idx[D] = i; // Mark the current grid position and dig down if (!run(idx, result, bounds, std::forward(fn), - std::forward(cmp))) return false; + std::forward(cmp))) + return false; } } @@ -90,7 +102,7 @@ struct AlgBurteForce { } }; -} // namespace bruteforce_detail +} // namespace detail using AlgBruteForce = detail::AlgBurteForce; diff --git a/src/libslic3r/SLA/Rotfinder.cpp b/src/libslic3r/SLA/Rotfinder.cpp index b7bed1c911..e8cc7df1b3 100644 --- a/src/libslic3r/SLA/Rotfinder.cpp +++ b/src/libslic3r/SLA/Rotfinder.cpp @@ -6,14 +6,11 @@ #include #include #include -#include -#include #include "Model.hpp" #include -namespace Slic3r { -namespace sla { +namespace Slic3r { namespace sla { using VertexFaceMap = std::vector>; @@ -51,7 +48,6 @@ double find_ground_level(const TriangleMesh &mesh, // Try to guess the number of support points needed to support a mesh double calculate_model_supportedness(const TriangleMesh & mesh, -// const VertexFaceMap &vmap, const Transform3d & tr) { static constexpr double POINTS_PER_UNIT_AREA = 1.; @@ -111,8 +107,6 @@ std::array find_best_rotation(const ModelObject& modelobj, // rotations TriangleMesh mesh = modelobj.raw_mesh(); mesh.require_shared_vertices(); -// auto vmap = create_vertex_face_map(mesh); -// simplify_mesh(mesh); // For current iteration number unsigned status = 0; @@ -147,21 +141,20 @@ std::array find_best_rotation(const ModelObject& modelobj, return score; }; - // Firing up the genetic optimizer. For now it uses the nlopt library. - + // Firing up the optimizer. opt::Optimizer solver(opt::StopCriteria{} .max_iterations(max_tries) .rel_score_diff(1e-6) .stop_condition(stopcond), - 100 /*grid size*/); + std::sqrt(max_tries)/*grid size*/); // We are searching rotations around only two axes x, y. Thus the // problem becomes a 2 dimensional optimization task. // We can specify the bounds for a dimension in the following way: auto b = opt::Bound{-PI, PI}; - // Now we start the optimization process with initial angles (0, 0, 0) - auto result = solver.to_min().optimize(objfunc, opt::initvals({0.0, 0.0}), + // Now we start the optimization process with initial angles (0, 0) + auto result = solver.to_min().optimize(objfunc, opt::initvals({0., 0.}), opt::bounds({b, b})); // Save the result and fck off @@ -171,5 +164,4 @@ std::array find_best_rotation(const ModelObject& modelobj, return rot; } -} -} +}} // namespace Slic3r::sla From 9f3e7617d86cd5c53a161a606c113b1c2e2b658a Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 1 Sep 2020 20:08:59 +0200 Subject: [PATCH 476/503] Add Imgui popup for rotation gizmo under SLA --- src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 64 +++++++++++++++++++++++++ src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp | 64 ++++++++++++++++++------- src/slic3r/GUI/ImGuiWrapper.cpp | 4 +- 3 files changed, 112 insertions(+), 20 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index f3e5656860..8365875d9a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -1,9 +1,15 @@ // Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. #include "GLGizmoRotate.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/ImGuiWrapper.hpp" #include +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/GUI.hpp" +#include "libslic3r/PresetBundle.hpp" + +#include "libslic3r/SLA/Rotfinder.hpp" namespace Slic3r { namespace GUI { @@ -194,6 +200,64 @@ void GLGizmoRotate::on_render_for_picking() const glsafe(::glPopMatrix()); } +GLGizmoRotate3D::RotoptimzeWindow::RotoptimzeWindow(ImGuiWrapper * imgui, + State & state, + const Alignment &alignment) + : m_imgui{imgui} +{ + imgui->begin(_L("Rotation"), ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_NoCollapse); + + // adjust window position to avoid overlap the view toolbar + float win_h = ImGui::GetWindowHeight(); + float x = alignment.x, y = alignment.y; + y = std::min(y, alignment.bottom_limit - win_h); + ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always); + + ImGui::SliderFloat(_L("Accuracy").c_str(), &state.accuracy, 0.01f, 1.f, "%.1f"); + + if (imgui->button(_L("Optimize orientation"))) { + std::cout << "Blip" << std::endl; + } + + static const std::vector options = { + _L("Least supports").ToStdString(), + _L("Suface quality").ToStdString() + }; + + if (imgui->combo(_L("Choose method"), options, state.method) ) { + std::cout << "method: " << state.method << std::endl; + } + + +} + +GLGizmoRotate3D::RotoptimzeWindow::~RotoptimzeWindow() +{ + m_imgui->end(); +} + +void GLGizmoRotate3D::on_render_input_window(float x, float y, float bottom_limit) +{ + if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA) + return; + +// m_rotoptimizewin_state.mobj = ; + RotoptimzeWindow popup{m_imgui, m_rotoptimizewin_state, {x, y, bottom_limit}}; + +// if ((last_h != win_h) || (last_y != y)) +// { +// // ask canvas for another frame to render the window in the correct position +// m_parent.request_extra_frame(); +// if (last_h != win_h) +// last_h = win_h; +// if (last_y != y) +// last_y = y; +// } + +} + void GLGizmoRotate::render_circle() const { ::glBegin(GL_LINE_LOOP); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp index 7365a20c36..c547dfbc0b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp @@ -52,12 +52,12 @@ public: std::string get_tooltip() const override; protected: - virtual bool on_init(); - virtual std::string on_get_name() const { return ""; } - virtual void on_start_dragging(); - virtual void on_update(const UpdateData& data); - virtual void on_render() const; - virtual void on_render_for_picking() const; + bool on_init() override; + std::string on_get_name() const override { return ""; } + void on_start_dragging() override; + void on_update(const UpdateData& data) override; + void on_render() const override; + void on_render_for_picking() const override; private: void render_circle() const; @@ -94,46 +94,74 @@ public: } protected: - virtual bool on_init(); - virtual std::string on_get_name() const; - virtual void on_set_state() + bool on_init() override; + std::string on_get_name() const override; + void on_set_state() override { for (GLGizmoRotate& g : m_gizmos) g.set_state(m_state); } - virtual void on_set_hover_id() + void on_set_hover_id() override { for (int i = 0; i < 3; ++i) m_gizmos[i].set_hover_id((m_hover_id == i) ? 0 : -1); } - virtual void on_enable_grabber(unsigned int id) + void on_enable_grabber(unsigned int id) override { if (id < 3) m_gizmos[id].enable_grabber(0); } - virtual void on_disable_grabber(unsigned int id) + void on_disable_grabber(unsigned int id) override { if (id < 3) m_gizmos[id].disable_grabber(0); } - virtual bool on_is_activable() const; - virtual void on_start_dragging(); - virtual void on_stop_dragging(); - virtual void on_update(const UpdateData& data) + bool on_is_activable() const override; + void on_start_dragging() override; + void on_stop_dragging() override; + void on_update(const UpdateData& data) override { for (GLGizmoRotate& g : m_gizmos) { g.update(data); } } - virtual void on_render() const; - virtual void on_render_for_picking() const + void on_render() const override; + void on_render_for_picking() const override { for (const GLGizmoRotate& g : m_gizmos) { g.render_for_picking(); } } + + void on_render_input_window(float x, float y, float bottom_limit) override; +private: + + class RotoptimzeWindow { + ImGuiWrapper *m_imgui = nullptr; + + public: + struct State { + enum Metods { mMinSupportPoints, mLegacy }; + + float accuracy = 1.f; + int method = mMinSupportPoints; + ModelObject *mobj = nullptr; + }; + + struct Alignment { float x, y, bottom_limit; }; + + RotoptimzeWindow(ImGuiWrapper *imgui, State &settings, const Alignment &bottom_limit); + ~RotoptimzeWindow(); + + RotoptimzeWindow(const RotoptimzeWindow&) = delete; + RotoptimzeWindow(RotoptimzeWindow &&) = delete; + RotoptimzeWindow& operator=(const RotoptimzeWindow &) = delete; + RotoptimzeWindow& operator=(RotoptimzeWindow &&) = delete; + }; + + RotoptimzeWindow::State m_rotoptimizewin_state = {}; }; } // namespace GUI diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index e839fdf9b2..0fecc822db 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -425,10 +425,10 @@ bool ImGuiWrapper::combo(const wxString& label, const std::vector& text(label); ImGui::SameLine(); - int selection_out = -1; + int selection_out = selection; bool res = false; - const char *selection_str = selection < (int)options.size() ? options[selection].c_str() : ""; + const char *selection_str = selection < int(options.size()) && selection >= 0 ? options[selection].c_str() : ""; if (ImGui::BeginCombo("", selection_str)) { for (int i = 0; i < (int)options.size(); i++) { if (ImGui::Selectable(options[i].c_str(), i == selection)) { From 0d4c67b9a34ed5699c2676ebb079c681c763b39a Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 4 Sep 2020 17:51:22 +0200 Subject: [PATCH 477/503] Mostly working, inefficiencies remain, status indication partly broken --- src/libslic3r/SLA/Rotfinder.cpp | 316 ++++++++++++++++-------- src/libslic3r/SLA/Rotfinder.hpp | 13 +- src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 32 +-- src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp | 7 +- src/slic3r/GUI/Jobs/RotoptimizeJob.cpp | 20 +- 5 files changed, 264 insertions(+), 124 deletions(-) diff --git a/src/libslic3r/SLA/Rotfinder.cpp b/src/libslic3r/SLA/Rotfinder.cpp index e8cc7df1b3..e033009aa4 100644 --- a/src/libslic3r/SLA/Rotfinder.cpp +++ b/src/libslic3r/SLA/Rotfinder.cpp @@ -5,27 +5,23 @@ #include #include #include -#include + +#include "libslic3r/SLAPrint.hpp" +#include "libslic3r/PrintConfig.hpp" + +#include #include "Model.hpp" #include namespace Slic3r { namespace sla { -using VertexFaceMap = std::vector>; +inline bool is_on_floor(const SLAPrintObject &mo) +{ + auto opt_elevation = mo.config().support_object_elevation.getFloat(); + auto opt_padaround = mo.config().pad_around_object.getBool(); -VertexFaceMap create_vertex_face_map(const TriangleMesh &mesh) { - std::vector> vmap(mesh.its.vertices.size()); - - size_t fi = 0; - for (const Vec3i &tri : mesh.its.indices) { - for (int vi = 0; vi < tri.size(); ++vi) { - auto from = vmap[tri(vi)].begin(), to = vmap[tri(vi)].end(); - vmap[tri(vi)].insert(std::lower_bound(from, to, fi), fi); - } - } - - return vmap; + return opt_elevation < EPSILON || opt_padaround; } // Find transformed mesh ground level without copy and with parallel reduce. @@ -41,62 +37,163 @@ double find_ground_level(const TriangleMesh &mesh, return (tr * mesh.its.vertices[vi].template cast()).z(); }; - double zmin = mesh.its.vertices.front().z(); + double zmin = std::numeric_limits::max(); size_t granularity = vsize / threads; return ccr_par::reduce(size_t(0), vsize, zmin, minfn, accessfn, granularity); } -// Try to guess the number of support points needed to support a mesh -double calculate_model_supportedness(const TriangleMesh & mesh, - const Transform3d & tr) +// Get the vertices of a triangle directly in an array of 3 points +std::array get_triangle_vertices(const TriangleMesh &mesh, + size_t faceidx) { - static constexpr double POINTS_PER_UNIT_AREA = 1.; - - if (mesh.its.vertices.empty()) return std::nan(""); - - size_t Nthr = std::thread::hardware_concurrency(); - size_t facesize = mesh.its.indices.size(); - - double zmin = find_ground_level(mesh, tr, Nthr); - - auto accessfn = [&mesh, &tr, zmin](size_t fi) { - - static const Vec3d DOWN = {0., 0., -1.}; - - const auto &face = mesh.its.indices[fi]; - Vec3d p1 = tr * mesh.its.vertices[face(0)].template cast(); - Vec3d p2 = tr * mesh.its.vertices[face(1)].template cast(); - Vec3d p3 = tr * mesh.its.vertices[face(2)].template cast(); - - Vec3d U = p2 - p1; - Vec3d V = p3 - p1; - Vec3d C = U.cross(V); - Vec3d N = C.normalized(); - double area = 0.5 * C.norm(); - - double zlvl = zmin + 0.1; - if (p1.z() <= zlvl && p2.z() <= zlvl && p3.z() <= zlvl) { - // score += area * POINTS_PER_UNIT_AREA; - return 0.; - } - - double phi = 1. - std::acos(N.dot(DOWN)) / PI; -// phi = phi * (phi > 0.5); - - // std::cout << "area: " << area << std::endl; - - return area * POINTS_PER_UNIT_AREA * phi; - }; - - double score = ccr_par::reduce(size_t(0), facesize, 0., std::plus{}, accessfn, facesize / Nthr); - - return score / mesh.its.indices.size(); + const auto &face = mesh.its.indices[faceidx]; + return {Vec3d{mesh.its.vertices[face(0)].cast()}, + Vec3d{mesh.its.vertices[face(1)].cast()}, + Vec3d{mesh.its.vertices[face(2)].cast()}}; } -std::array find_best_rotation(const ModelObject& modelobj, - float accuracy, - std::function statuscb, - std::function stopcond) +std::array get_transformed_triangle(const TriangleMesh &mesh, + const Transform3d & tr, + size_t faceidx) +{ + const auto &tri = get_triangle_vertices(mesh, faceidx); + return {tr * tri[0], tr * tri[1], tr * tri[2]}; +} + +// Get area and normal of a triangle +struct Face { Vec3d normal; double area; }; +inline Face facestats(const std::array &triangle) +{ + Vec3d U = triangle[1] - triangle[0]; + Vec3d V = triangle[2] - triangle[0]; + Vec3d C = U.cross(V); + Vec3d N = C.normalized(); + double area = 0.5 * C.norm(); + + return {N, area}; +} + +inline const Vec3d DOWN = {0., 0., -1.}; +constexpr double POINTS_PER_UNIT_AREA = 1.; + +inline double get_score(const Face &fc) +{ + double phi = 1. - std::acos(fc.normal.dot(DOWN)) / PI; + phi = phi * (phi > 0.5); + phi = phi * phi * phi; + + return fc.area * POINTS_PER_UNIT_AREA * phi; +} + +template +double sum_score(AccessFn &&accessfn, size_t facecount, size_t Nthreads) +{ + double initv = 0.; + auto mergefn = std::plus{}; + size_t grainsize = facecount / Nthreads; + size_t from = 0, to = facecount; + + return ccr_par::reduce(from, to, initv, mergefn, accessfn, grainsize); +} + +// Try to guess the number of support points needed to support a mesh +double get_model_supportedness(const TriangleMesh &mesh, const Transform3d &tr) +{ + if (mesh.its.vertices.empty()) return std::nan(""); + + auto accessfn = [&mesh, &tr](size_t fi) { + Face fc = facestats(get_transformed_triangle(mesh, tr, fi)); + return get_score(fc); + }; + + size_t facecount = mesh.its.indices.size(); + size_t Nthreads = std::thread::hardware_concurrency(); + return sum_score(accessfn, facecount, Nthreads) / facecount; +} + +double get_model_supportedness_onfloor(const TriangleMesh &mesh, + const Transform3d & tr) +{ + if (mesh.its.vertices.empty()) return std::nan(""); + + size_t Nthreads = std::thread::hardware_concurrency(); + + double zmin = find_ground_level(mesh, tr, Nthreads); + double zlvl = zmin + 0.1; // Set up a slight tolerance from z level + + auto accessfn = [&mesh, &tr, zlvl](size_t fi) { + std::array tri = get_transformed_triangle(mesh, tr, fi); + Face fc = facestats(tri); + + if (tri[0].z() <= zlvl && tri[1].z() <= zlvl && tri[2].z() <= zlvl) + return -fc.area * POINTS_PER_UNIT_AREA; + + return get_score(fc); + }; + + size_t facecount = mesh.its.indices.size(); + return sum_score(accessfn, facecount, Nthreads) / facecount; +} + +using XYRotation = std::array; + +// prepare the rotation transformation +Transform3d to_transform3d(const XYRotation &rot) +{ + Transform3d rt = Transform3d::Identity(); + rt.rotate(Eigen::AngleAxisd(rot[1], Vec3d::UnitY())); + rt.rotate(Eigen::AngleAxisd(rot[0], Vec3d::UnitX())); + return rt; +} + +XYRotation from_transform3d(const Transform3d &tr) +{ + Vec3d rot3d = Geometry::Transformation {tr}.get_rotation(); + return {rot3d.x(), rot3d.y()}; +} + +// Find the best score from a set of function inputs. Evaluate for every point. +template +std::array find_min_score(Fn &&fn, Cmp &&cmp, It from, It to) +{ + std::array ret; + + double score = std::numeric_limits::max(); + + for (auto it = from; it != to; ++it) { + double sc = fn(*it); + if (cmp(sc, score)) { + score = sc; + ret = *it; + } + } + + return ret; +} + +// collect the rotations for each face of the convex hull +std::vector get_chull_rotations(const TriangleMesh &mesh) +{ + TriangleMesh chull = mesh.convex_hull_3d(); + chull.require_shared_vertices(); + + size_t facecount = chull.its.indices.size(); + auto inputs = reserve_vector(facecount); + + for (size_t fi = 0; fi < facecount; ++fi) { + Face fc = facestats(get_triangle_vertices(chull, fi)); + + auto q = Eigen::Quaterniond{}.FromTwoVectors(fc.normal, DOWN); + inputs.emplace_back(from_transform3d(Transform3d::Identity() * q)); + } + + return inputs; +} + +XYRotation find_best_rotation(const SLAPrintObject & po, + float accuracy, + std::function statuscb, + std::function stopcond) { static const unsigned MAX_TRIES = 10000; @@ -105,10 +202,10 @@ std::array find_best_rotation(const ModelObject& modelobj, // We will use only one instance of this converted mesh to examine different // rotations - TriangleMesh mesh = modelobj.raw_mesh(); + TriangleMesh mesh = po.model_object()->raw_mesh(); mesh.require_shared_vertices(); - // For current iteration number + // To keep track of the number of iterations unsigned status = 0; // The maximum number of iterations @@ -117,51 +214,66 @@ std::array find_best_rotation(const ModelObject& modelobj, // call status callback with zero, because we are at the start statuscb(status); - // So this is the object function which is called by the solver many times - // It has to yield a single value representing the current score. We will - // call the status callback in each iteration but the actual value may be - // the same for subsequent iterations (status goes from 0 to 100 but - // iterations can be many more) - auto objfunc = [&mesh, &status, &statuscb, &stopcond, max_tries] - (const opt::Input<2> &in) - { - std::cout << "in: " << in[0] << " " << in[1] << std::endl; - - // prepare the rotation transformation - Transform3d rt = Transform3d::Identity(); - rt.rotate(Eigen::AngleAxisd(in[1], Vec3d::UnitY())); - rt.rotate(Eigen::AngleAxisd(in[0], Vec3d::UnitX())); - - double score = sla::calculate_model_supportedness(mesh, rt); - std::cout << score << std::endl; - + auto statusfn = [&statuscb, &status, max_tries] { // report status - if(!stopcond()) statuscb( unsigned(++status * 100.0/max_tries) ); - - return score; + statuscb(unsigned(++status * 100.0/max_tries) ); }; - // Firing up the optimizer. - opt::Optimizer solver(opt::StopCriteria{} - .max_iterations(max_tries) - .rel_score_diff(1e-6) - .stop_condition(stopcond), - std::sqrt(max_tries)/*grid size*/); + // Different search methods have to be used depending on the model elevation + if (is_on_floor(po)) { - // We are searching rotations around only two axes x, y. Thus the - // problem becomes a 2 dimensional optimization task. - // We can specify the bounds for a dimension in the following way: - auto b = opt::Bound{-PI, PI}; + // If the model can be placed on the bed directly, we only need to + // check the 3D convex hull face rotations. - // Now we start the optimization process with initial angles (0, 0) - auto result = solver.to_min().optimize(objfunc, opt::initvals({0., 0.}), - opt::bounds({b, b})); + auto inputs = get_chull_rotations(mesh); - // Save the result and fck off - rot[0] = std::get<0>(result.optimum); - rot[1] = std::get<1>(result.optimum); + auto cmpfn = [](double a, double b) { return a < b; }; + auto objfn = [&mesh, &statusfn](const XYRotation &rot) { + statusfn(); + // We actually need the reverserotation to make the object lie on + // this face + Transform3d tr = to_transform3d(rot); + return get_model_supportedness_onfloor(mesh, tr); + }; + + rot = find_min_score<2>(objfn, cmpfn, inputs.begin(), inputs.end()); + } else { + + // Preparing the optimizer. + size_t grid_size = std::sqrt(max_tries); + opt::Optimizer solver(opt::StopCriteria{} + .max_iterations(max_tries) + .stop_condition(stopcond), + grid_size); + + // We are searching rotations around only two axes x, y. Thus the + // problem becomes a 2 dimensional optimization task. + // We can specify the bounds for a dimension in the following way: + auto bounds = opt::bounds({ {-PI, PI}, {-PI, PI} }); + + auto result = solver.to_min().optimize( + [&mesh, &statusfn] (const XYRotation &rot) + { + statusfn(); + return get_model_supportedness(mesh, to_transform3d(rot)); + }, opt::initvals({0., 0.}), bounds); + + // Save the result and fck off + rot = result.optimum; + + std::cout << "best score: " << result.score << std::endl; + } return rot; } +double get_model_supportedness(const SLAPrintObject &po, const Transform3d &tr) +{ + TriangleMesh mesh = po.model_object()->raw_mesh(); + mesh.require_shared_vertices(); + + return is_on_floor(po) ? get_model_supportedness_onfloor(mesh, tr) : + get_model_supportedness(mesh, tr); +} + }} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/Rotfinder.hpp b/src/libslic3r/SLA/Rotfinder.hpp index 583703203a..4fa529600b 100644 --- a/src/libslic3r/SLA/Rotfinder.hpp +++ b/src/libslic3r/SLA/Rotfinder.hpp @@ -4,9 +4,11 @@ #include #include +#include + namespace Slic3r { -class ModelObject; +class SLAPrintObject; namespace sla { @@ -26,13 +28,16 @@ namespace sla { * @return Returns the rotations around each axis (x, y, z) */ std::array find_best_rotation( - const ModelObject& modelobj, + const SLAPrintObject& modelobj, float accuracy = 1.0f, std::function statuscb = [] (unsigned) {}, std::function stopcond = [] () { return false; } ); -} -} +double get_model_supportedness(const SLAPrintObject &mesh, + const Transform3d & tr); + +} // namespace sla +} // namespace Slic3r #endif // SLAROTFINDER_HPP diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 8365875d9a..77366c6335 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -200,6 +200,8 @@ void GLGizmoRotate::on_render_for_picking() const glsafe(::glPopMatrix()); } + + GLGizmoRotate3D::RotoptimzeWindow::RotoptimzeWindow(ImGuiWrapper * imgui, State & state, const Alignment &alignment) @@ -215,20 +217,26 @@ GLGizmoRotate3D::RotoptimzeWindow::RotoptimzeWindow(ImGuiWrapper * imgui, y = std::min(y, alignment.bottom_limit - win_h); ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always); - ImGui::SliderFloat(_L("Accuracy").c_str(), &state.accuracy, 0.01f, 1.f, "%.1f"); + static constexpr const char * button_txt = L("Optimize orientation"); + static constexpr const char * slider_txt = L("Accuracy"); - if (imgui->button(_L("Optimize orientation"))) { + float button_width = imgui->calc_text_size(_(button_txt)).x; + ImGui::PushItemWidth(100.); + //if (imgui->button(_(button_txt))) { + if (ImGui::ArrowButton(_(button_txt).c_str(), ImGuiDir_Down)){ std::cout << "Blip" << std::endl; } + ImGui::SliderFloat(_(slider_txt).c_str(), &state.accuracy, 0.01f, 1.f, "%.1f"); + static const std::vector options = { _L("Least supports").ToStdString(), _L("Suface quality").ToStdString() }; - if (imgui->combo(_L("Choose method"), options, state.method) ) { - std::cout << "method: " << state.method << std::endl; - } +// if (imgui->combo(_L("Choose method"), options, state.method) ) { +// std::cout << "method: " << state.method << std::endl; +// } } @@ -243,18 +251,10 @@ void GLGizmoRotate3D::on_render_input_window(float x, float y, float bottom_limi if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA) return; -// m_rotoptimizewin_state.mobj = ; - RotoptimzeWindow popup{m_imgui, m_rotoptimizewin_state, {x, y, bottom_limit}}; +// TODO: -// if ((last_h != win_h) || (last_y != y)) -// { -// // ask canvas for another frame to render the window in the correct position -// m_parent.request_extra_frame(); -// if (last_h != win_h) -// last_h = win_h; -// if (last_y != y) -// last_y = y; -// } +// m_rotoptimizewin_state.mobj = ?; +// RotoptimzeWindow popup{m_imgui, m_rotoptimizewin_state, {x, y, bottom_limit}}; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp index c547dfbc0b..c418c4b316 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp @@ -136,12 +136,14 @@ protected: } void on_render_input_window(float x, float y, float bottom_limit) override; + private: class RotoptimzeWindow { ImGuiWrapper *m_imgui = nullptr; public: + struct State { enum Metods { mMinSupportPoints, mLegacy }; @@ -152,7 +154,10 @@ private: struct Alignment { float x, y, bottom_limit; }; - RotoptimzeWindow(ImGuiWrapper *imgui, State &settings, const Alignment &bottom_limit); + RotoptimzeWindow(ImGuiWrapper * imgui, + State & state, + const Alignment &bottom_limit); + ~RotoptimzeWindow(); RotoptimzeWindow(const RotoptimzeWindow&) = delete; diff --git a/src/slic3r/GUI/Jobs/RotoptimizeJob.cpp b/src/slic3r/GUI/Jobs/RotoptimizeJob.cpp index 3fd86b13f5..91a67cfa27 100644 --- a/src/slic3r/GUI/Jobs/RotoptimizeJob.cpp +++ b/src/slic3r/GUI/Jobs/RotoptimizeJob.cpp @@ -4,6 +4,7 @@ #include "libslic3r/SLA/Rotfinder.hpp" #include "libslic3r/MinAreaBoundingBox.hpp" #include "libslic3r/Model.hpp" +#include "libslic3r/SLAPrint.hpp" #include "slic3r/GUI/Plater.hpp" @@ -15,9 +16,26 @@ void RotoptimizeJob::process() if (obj_idx < 0) { return; } ModelObject *o = m_plater->model().objects[size_t(obj_idx)]; + const SLAPrintObject *po = m_plater->sla_print().objects()[size_t(obj_idx)]; + + if (!o || !po) return; + + TriangleMesh mesh = o->raw_mesh(); + mesh.require_shared_vertices(); + +// for (auto inst : o->instances) { +// Transform3d tr = Transform3d::Identity(); +// tr.rotate(Eigen::AngleAxisd(inst->get_rotation(Z), Vec3d::UnitZ())); +// tr.rotate(Eigen::AngleAxisd(inst->get_rotation(Y), Vec3d::UnitY())); +// tr.rotate(Eigen::AngleAxisd(inst->get_rotation(X), Vec3d::UnitX())); + +// double score = sla::get_model_supportedness(*po, tr); + +// std::cout << "Model supportedness before: " << score << std::endl; +// } auto r = sla::find_best_rotation( - *o, + *po, 1.f, [this](unsigned s) { if (s < 100) From 3b7ea5587e5b6c7d6938c746dd2855b95d242aa7 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 4 Sep 2020 19:43:07 +0200 Subject: [PATCH 478/503] Fix build on win --- src/libslic3r/SLA/Concurrency.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libslic3r/SLA/Concurrency.hpp b/src/libslic3r/SLA/Concurrency.hpp index a7b5b6c61d..300024c76d 100644 --- a/src/libslic3r/SLA/Concurrency.hpp +++ b/src/libslic3r/SLA/Concurrency.hpp @@ -5,7 +5,10 @@ #include #include #include + #include +#include + #include namespace Slic3r { From d5271220464fb8da07bdc805c68318ece201d3bb Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 4 Sep 2020 20:20:06 +0200 Subject: [PATCH 479/503] Performance optimizations and bugfix --- src/libslic3r/SLA/Rotfinder.cpp | 16 ++++++++++++++-- src/slic3r/GUI/Jobs/RotoptimizeJob.cpp | 3 ++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/SLA/Rotfinder.cpp b/src/libslic3r/SLA/Rotfinder.cpp index e033009aa4..db8c0b9a8d 100644 --- a/src/libslic3r/SLA/Rotfinder.cpp +++ b/src/libslic3r/SLA/Rotfinder.cpp @@ -76,12 +76,20 @@ inline Face facestats(const std::array &triangle) inline const Vec3d DOWN = {0., 0., -1.}; constexpr double POINTS_PER_UNIT_AREA = 1.; +// The score function for a particular face inline double get_score(const Face &fc) { + // Simply get the angle (acos of dot product) between the face normal and + // the DOWN vector. double phi = 1. - std::acos(fc.normal.dot(DOWN)) / PI; + + // Only consider faces that have have slopes below 90 deg: phi = phi * (phi > 0.5); + + // Make the huge slopes more significant than the smaller slopes phi = phi * phi * phi; + // Multiply with the area of the current face return fc.area * POINTS_PER_UNIT_AREA * phi; } @@ -176,6 +184,8 @@ std::vector get_chull_rotations(const TriangleMesh &mesh) { TriangleMesh chull = mesh.convex_hull_3d(); chull.require_shared_vertices(); + double chull2d_area = chull.convex_hull().area(); + double area_threshold = chull2d_area / (scaled(1e3) * scaled(1.)); size_t facecount = chull.its.indices.size(); auto inputs = reserve_vector(facecount); @@ -183,8 +193,10 @@ std::vector get_chull_rotations(const TriangleMesh &mesh) for (size_t fi = 0; fi < facecount; ++fi) { Face fc = facestats(get_triangle_vertices(chull, fi)); - auto q = Eigen::Quaterniond{}.FromTwoVectors(fc.normal, DOWN); - inputs.emplace_back(from_transform3d(Transform3d::Identity() * q)); + if (fc.area > area_threshold) { + auto q = Eigen::Quaterniond{}.FromTwoVectors(fc.normal, DOWN); + inputs.emplace_back(from_transform3d(Transform3d::Identity() * q)); + } } return inputs; diff --git a/src/slic3r/GUI/Jobs/RotoptimizeJob.cpp b/src/slic3r/GUI/Jobs/RotoptimizeJob.cpp index 91a67cfa27..10c09275c3 100644 --- a/src/slic3r/GUI/Jobs/RotoptimizeJob.cpp +++ b/src/slic3r/GUI/Jobs/RotoptimizeJob.cpp @@ -13,7 +13,8 @@ namespace Slic3r { namespace GUI { void RotoptimizeJob::process() { int obj_idx = m_plater->get_selected_object_idx(); - if (obj_idx < 0) { return; } + if (obj_idx < 0 || m_plater->sla_print().objects().size() <= obj_idx) + return; ModelObject *o = m_plater->model().objects[size_t(obj_idx)]; const SLAPrintObject *po = m_plater->sla_print().objects()[size_t(obj_idx)]; From b991b613de893e0caa113abf8f3a56b2808306ab Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 10 Sep 2020 14:33:55 +0200 Subject: [PATCH 480/503] Updated titlebar and splash screen + hidden statusbar for gcode viewer --- src/libslic3r/libslic3r_version.h.in | 3 +++ src/slic3r/GUI/GUI_App.cpp | 8 ++++++ src/slic3r/GUI/MainFrame.cpp | 38 ++++++++++++++++++++++------ version.inc | 3 +++ 4 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/libslic3r_version.h.in b/src/libslic3r/libslic3r_version.h.in index 26a67362b7..90f0812aba 100644 --- a/src/libslic3r/libslic3r_version.h.in +++ b/src/libslic3r/libslic3r_version.h.in @@ -6,4 +6,7 @@ #define SLIC3R_VERSION "@SLIC3R_VERSION@" #define SLIC3R_BUILD_ID "@SLIC3R_BUILD_ID@" +#define GCODEVIEWER_APP_NAME "@GCODEVIEWER_APP_NAME@" +#define GCODEVIEWER_BUILD_ID "@GCODEVIEWER_BUILD_ID@" + #endif /* __SLIC3R_VERSION_H */ diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index f6b0a44146..e50d4015e7 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -161,7 +161,15 @@ static void DecorateSplashScreen(wxBitmap& bmp) memDc.DrawRectangle(banner_rect); // title +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_GCODE_VIEWER + wxString title_string = wxGetApp().is_editor() ? SLIC3R_APP_NAME : GCODEVIEWER_APP_NAME; +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ wxString title_string = SLIC3R_APP_NAME; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ wxFont title_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); title_font.SetPointSize(24); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 06cb75efa3..4d242dec88 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -116,12 +116,17 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S #endif // _WIN32 // initialize status bar - m_statusbar = std::make_shared(this); + m_statusbar = std::make_shared(this); m_statusbar->set_font(GUI::wxGetApp().normal_font()); - m_statusbar->embed(this); - m_statusbar->set_status_text(_(L("Version")) + " " + - SLIC3R_VERSION + - _(L(" - Remember to check for updates at https://github.com/prusa3d/PrusaSlicer/releases"))); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_GCODE_VIEWER + if (wxGetApp().is_editor()) +#endif // ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + m_statusbar->embed(this); + m_statusbar->set_status_text(_L("Version") + " " + + SLIC3R_VERSION + + _L(" - Remember to check for updates at https://github.com/prusa3d/PrusaSlicer/releases")); // initialize tabpanel and menubar init_tabpanel(); @@ -526,8 +531,7 @@ void MainFrame::shutdown() void MainFrame::update_title() { wxString title = wxEmptyString; - if (m_plater != nullptr) - { + if (m_plater != nullptr) { // m_plater->get_project_filename() produces file name including path, but excluding extension. // Don't try to remove the extension, it would remove part of the file name after the last dot! wxString project = from_path(into_path(m_plater->get_project_filename()).filename()); @@ -535,7 +539,15 @@ void MainFrame::update_title() title += (project + " - "); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_GCODE_VIEWER + std::string build_id = wxGetApp().is_editor() ? SLIC3R_BUILD_ID : GCODEVIEWER_BUILD_ID; +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ std::string build_id = SLIC3R_BUILD_ID; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ size_t idx_plus = build_id.find('+'); if (idx_plus != build_id.npos) { // Parse what is behind the '+'. If there is a number, then it is a build number after the label, and full build ID is shown. @@ -550,7 +562,17 @@ void MainFrame::update_title() #endif } } - title += (wxString(build_id) + " " + _(L("based on Slic3r"))); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_GCODE_VIEWER + title += wxString(build_id); + if (wxGetApp().is_editor()) + title += (" " + _L("based on Slic3r")); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + title += (wxString(build_id) + " " + _L("based on Slic3r")); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ SetTitle(title); } diff --git a/version.inc b/version.inc index 30b373bdfd..e5985fcd0d 100644 --- a/version.inc +++ b/version.inc @@ -7,3 +7,6 @@ set(SLIC3R_VERSION "2.3.0-alpha0") set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN") set(SLIC3R_RC_VERSION "2,3,0,0") set(SLIC3R_RC_VERSION_DOTS "2.3.0.0") + +set(GCODEVIEWER_APP_NAME "Prusa GCode Viewer") +set(GCODEVIEWER_BUILD_ID "Prusa GCode Viewer-${SLIC3R_VERSION}+UNKNOWN") From 70cb67430cf8d5284e6ee101d1f2ac189c52699e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Tue, 8 Sep 2020 11:40:09 +0200 Subject: [PATCH 481/503] Move rotation from building octree to infill generating --- src/libslic3r/Fill/FillAdaptive.cpp | 27 ++++++++++++++------------- src/libslic3r/Fill/FillAdaptive.hpp | 2 +- src/libslic3r/PrintObject.cpp | 9 ++++++++- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index b1a40047d4..db7cab50a0 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -97,11 +97,14 @@ void FillAdaptive::_fill_surface_single( ExPolygon &expolygon, Polylines &polylines_out) { + Vec3d rotation = Vec3d((5.0 * M_PI) / 4.0, Geometry::deg2rad(215.264), M_PI / 6.0); + Transform3d rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), rotation, Vec3d::Ones(), Vec3d::Ones()); + // Store grouped lines by its direction (multiple of 120°) std::vector infill_lines_dir(3); this->generate_infill_lines(this->adapt_fill_octree->root_cube.get(), - this->z, this->adapt_fill_octree->origin,infill_lines_dir, - this->adapt_fill_octree->cubes_properties, + this->z, this->adapt_fill_octree->origin, rotation_matrix, + infill_lines_dir, this->adapt_fill_octree->cubes_properties, int(this->adapt_fill_octree->cubes_properties.size()) - 1); Polylines all_polylines; @@ -186,6 +189,7 @@ void FillAdaptive::generate_infill_lines( FillAdaptive_Internal::Cube *cube, double z_position, const Vec3d &origin, + const Transform3d &rotation_matrix, std::vector &dir_lines_out, const std::vector &cubes_properties, int depth) @@ -197,7 +201,8 @@ void FillAdaptive::generate_infill_lines( return; } - double z_diff = std::abs(z_position - cube->center.z()); + Vec3d cube_center_tranformed = rotation_matrix * cube->center; + double z_diff = std::abs(z_position - cube_center_tranformed.z()); if (z_diff > cubes_properties[depth].height / 2) { @@ -208,14 +213,14 @@ void FillAdaptive::generate_infill_lines( { Point from( scale_((cubes_properties[depth].diagonal_length / 2) * (cubes_properties[depth].line_z_distance - z_diff) / cubes_properties[depth].line_z_distance), - scale_(cubes_properties[depth].line_xy_distance - ((z_position - (cube->center.z() - cubes_properties[depth].line_z_distance)) / sqrt(2)))); + scale_(cubes_properties[depth].line_xy_distance - ((z_position - (cube_center_tranformed.z() - cubes_properties[depth].line_z_distance)) / sqrt(2)))); Point to(-from.x(), from.y()); // Relative to cube center double rotation_angle = (2.0 * M_PI) / 3.0; for (Lines &lines : dir_lines_out) { - Vec3d offset = cube->center - origin; + Vec3d offset = cube_center_tranformed - (rotation_matrix * origin); Point from_abs(from), to_abs(to); from_abs.x() += int(scale_(offset.x())); @@ -235,7 +240,7 @@ void FillAdaptive::generate_infill_lines( { if(child != nullptr) { - generate_infill_lines(child.get(), z_position, origin, dir_lines_out, cubes_properties, depth - 1); + generate_infill_lines(child.get(), z_position, origin, rotation_matrix, dir_lines_out, cubes_properties, depth - 1); } } } @@ -301,14 +306,11 @@ std::unique_ptr FillAdaptive::build_octree( triangle_mesh.require_shared_vertices(); } - Vec3d rotation = Vec3d((5.0 * M_PI) / 4.0, Geometry::deg2rad(215.264), M_PI / 6.0); - Transform3d rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), rotation, Vec3d::Ones(), Vec3d::Ones()); - AABBTreeIndirect::Tree3f aabbTree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set( triangle_mesh.its.vertices, triangle_mesh.its.indices); auto octree = std::make_unique(std::make_unique(cube_center), cube_center, cubes_properties); - FillAdaptive::expand_cube(octree->root_cube.get(), cubes_properties, rotation_matrix, aabbTree, triangle_mesh, int(cubes_properties.size()) - 1); + FillAdaptive::expand_cube(octree->root_cube.get(), cubes_properties, aabbTree, triangle_mesh, int(cubes_properties.size()) - 1); return octree; } @@ -316,7 +318,6 @@ std::unique_ptr FillAdaptive::build_octree( void FillAdaptive::expand_cube( FillAdaptive_Internal::Cube *cube, const std::vector &cubes_properties, - const Transform3d &rotation_matrix, const AABBTreeIndirect::Tree3f &distance_tree, const TriangleMesh &triangle_mesh, int depth) { @@ -337,13 +338,13 @@ void FillAdaptive::expand_cube( for (size_t i = 0; i < 8; ++i) { const Vec3d &child_center = child_centers[i]; - Vec3d child_center_transformed = cube->center + rotation_matrix * (child_center * (cubes_properties[depth].edge_length / 4)); + Vec3d child_center_transformed = cube->center + (child_center * (cubes_properties[depth].edge_length / 4)); if(AABBTreeIndirect::is_any_triangle_in_radius(triangle_mesh.its.vertices, triangle_mesh.its.indices, distance_tree, child_center_transformed, cube_radius_squared)) { cube->children[i] = std::make_unique(child_center_transformed); - FillAdaptive::expand_cube(cube->children[i].get(), cubes_properties, rotation_matrix, distance_tree, triangle_mesh, depth - 1); + FillAdaptive::expand_cube(cube->children[i].get(), cubes_properties, distance_tree, triangle_mesh, depth - 1); } } } diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index dd7394384c..f337832238 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -63,6 +63,7 @@ protected: FillAdaptive_Internal::Cube *cube, double z_position, const Vec3d & origin, + const Transform3d & rotation_matrix, std::vector & dir_lines_out, const std::vector &cubes_properties, int depth); @@ -78,7 +79,6 @@ public: static void expand_cube( FillAdaptive_Internal::Cube *cube, const std::vector &cubes_properties, - const Transform3d & rotation_matrix, const AABBTreeIndirect::Tree3f &distance_tree, const TriangleMesh & triangle_mesh, int depth); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 474417b1ec..6ebfbc9247 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -444,7 +444,14 @@ std::unique_ptr PrintObject::prepare_adaptive_inf mesh.translate(- unscale(m_center_offset.x()), - unscale(m_center_offset.y()), 0); // Center of the first cube in octree Vec3d mesh_origin = mesh.bounding_box().center(); - return FillAdaptive::build_octree(mesh, adaptive_line_spacing, mesh_origin); + + Vec3d rotation = Vec3d((5.0 * M_PI) / 4.0, Geometry::deg2rad(215.264), M_PI / 6.0); + Transform3d rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), rotation, Vec3d::Ones(), Vec3d::Ones()).inverse(); + + // Rotate mesh and build octree on it with axis-aligned (standart base) cubes + mesh.transform(rotation_matrix); + + return FillAdaptive::build_octree(mesh, adaptive_line_spacing, rotation_matrix * mesh_origin); } void PrintObject::clear_layers() From e55d184a7d4ca992d06575a2230a09dcb4ee910f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Tue, 8 Sep 2020 11:48:24 +0200 Subject: [PATCH 482/503] Fix missing initialization in TriangleMesh constructor --- src/libslic3r/TriangleMesh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 17edf1b5a8..49fc625af9 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -70,7 +70,7 @@ TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector &fac stl_get_size(&stl); } -TriangleMesh::TriangleMesh(const indexed_triangle_set &M) +TriangleMesh::TriangleMesh(const indexed_triangle_set &M) : repaired(false) { stl.stats.type = inmemory; From c26162499908dfb7d86be6cf04cae3bd2b67a46e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Tue, 8 Sep 2020 11:49:26 +0200 Subject: [PATCH 483/503] A simple version of adaptive cubic support, for testing purposes --- src/libslic3r/Fill/FillAdaptive.hpp | 6 +++++ src/libslic3r/PrintObject.cpp | 38 +++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index f337832238..67a2d0f3fa 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -48,6 +48,12 @@ class FillAdaptive : public Fill public: virtual ~FillAdaptive() {} + static void insert_octant( + FillAdaptive_Internal::Cube * i_cube, + FillAdaptive_Internal::Cube * current, + int depth, + const std::vector &cubes_properties); + protected: virtual Fill* clone() const { return new FillAdaptive(*this); }; virtual void _fill_surface_single( diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 6ebfbc9247..645d36a38c 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -11,6 +11,7 @@ #include "Utils.hpp" #include "AABBTreeIndirect.hpp" #include "Fill/FillAdaptive.hpp" +#include "Format/STL.hpp" #include #include @@ -432,6 +433,8 @@ void PrintObject::generate_support_material() } } +#define ADAPTIVE_SUPPORT_SIMPLE + std::unique_ptr PrintObject::prepare_adaptive_infill_data() { auto [adaptive_line_spacing, support_line_spacing] = adaptive_fill_line_spacing(*this); @@ -445,6 +448,41 @@ std::unique_ptr PrintObject::prepare_adaptive_inf // Center of the first cube in octree Vec3d mesh_origin = mesh.bounding_box().center(); +#ifdef ADAPTIVE_SUPPORT_SIMPLE + if (mesh.its.vertices.empty()) + { + mesh.require_shared_vertices(); + } + + Vec3f vertical(0, 0, 1); + + indexed_triangle_set its_set; + its_set.vertices = mesh.its.vertices; + + // Filter out non overhanging faces + for (size_t i = 0; i < mesh.its.indices.size(); ++i) { + stl_triangle_vertex_indices vertex_idx = mesh.its.indices[i]; + + auto its_calculate_normal = [](const stl_triangle_vertex_indices &index, const std::vector &vertices) { + stl_normal normal = (vertices[index.y()] - vertices[index.x()]).cross(vertices[index.z()] - vertices[index.x()]); + return normal; + }; + + stl_normal normal = its_calculate_normal(vertex_idx, mesh.its.vertices); + stl_normalize_vector(normal); + + if(normal.dot(vertical) >= 0.707) { + its_set.indices.push_back(vertex_idx); + } + } + + mesh = TriangleMesh(its_set); + +#ifdef SLIC3R_DEBUG_SLICE_PROCESSING + Slic3r::store_stl(debug_out_path("overhangs.stl").c_str(), &mesh, false); +#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ +#endif /* ADAPTIVE_SUPPORT_SIMPLE */ + Vec3d rotation = Vec3d((5.0 * M_PI) / 4.0, Geometry::deg2rad(215.264), M_PI / 6.0); Transform3d rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), rotation, Vec3d::Ones(), Vec3d::Ones()).inverse(); From 680b1b98093264c7307e6694053f323437040a42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Wed, 9 Sep 2020 09:20:06 +0200 Subject: [PATCH 484/503] Construct octree based on inserted points --- src/libslic3r/Fill/FillAdaptive.cpp | 43 +++++++++++++++++++++++++---- src/libslic3r/Fill/FillAdaptive.hpp | 17 ++++++++---- 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index db7cab50a0..4667c30c90 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -91,10 +91,10 @@ std::pair adaptive_fill_line_spacing(const PrintObject &print_ob } void FillAdaptive::_fill_surface_single( - const FillParams ¶ms, + const FillParams ¶ms, unsigned int thickness_layers, - const std::pair &direction, - ExPolygon &expolygon, + const std::pair &direction, + ExPolygon &expolygon, Polylines &polylines_out) { Vec3d rotation = Vec3d((5.0 * M_PI) / 4.0, Geometry::deg2rad(215.264), M_PI / 6.0); @@ -329,8 +329,8 @@ void FillAdaptive::expand_cube( } std::vector child_centers = { - Vec3d(-1, -1, -1), Vec3d( 1, -1, -1), Vec3d(-1, 1, -1), Vec3d(-1, -1, 1), - Vec3d( 1, 1, 1), Vec3d(-1, 1, 1), Vec3d( 1, -1, 1), Vec3d( 1, 1, -1) + Vec3d(-1, -1, -1), Vec3d( 1, -1, -1), Vec3d(-1, 1, -1), Vec3d( 1, 1, -1), + Vec3d(-1, -1, 1), Vec3d( 1, -1, 1), Vec3d(-1, 1, 1), Vec3d( 1, 1, 1) }; double cube_radius_squared = (cubes_properties[depth].height * cubes_properties[depth].height) / 16; @@ -349,4 +349,37 @@ void FillAdaptive::expand_cube( } } +void FillAdaptive_Internal::Octree::propagate_point( + Vec3d point, + FillAdaptive_Internal::Cube * current, + int depth, + const std::vector &cubes_properties) +{ + using namespace FillAdaptive_Internal; + + if(depth <= 0) + { + return; + } + + size_t octant_idx = Octree::find_octant(point, current->center); + Cube * child = current->children[octant_idx].get(); + + // Octant not exists, then create it + if(child == nullptr) { + std::vector child_centers = { + Vec3d(-1, -1, -1), Vec3d( 1, -1, -1), Vec3d(-1, 1, -1), Vec3d( 1, 1, -1), + Vec3d(-1, -1, 1), Vec3d( 1, -1, 1), Vec3d(-1, 1, 1), Vec3d( 1, 1, 1) + }; + + const Vec3d &child_center = child_centers[octant_idx]; + Vec3d child_center_transformed = current->center + (child_center * (cubes_properties[depth].edge_length / 4)); + + current->children[octant_idx] = std::make_unique(child_center_transformed); + child = current->children[octant_idx].get(); + } + + Octree::propagate_point(point, child, (depth - 1), cubes_properties); +} + } // namespace Slic3r diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index 67a2d0f3fa..d384776543 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -35,6 +35,17 @@ namespace FillAdaptive_Internal Octree(std::unique_ptr rootCube, const Vec3d &origin, const std::vector &cubes_properties) : root_cube(std::move(rootCube)), origin(origin), cubes_properties(cubes_properties) {} + + inline static int find_octant(const Vec3d &i_cube, const Vec3d ¤t) + { + return (i_cube.z() > current.z()) * 4 + (i_cube.y() > current.y()) * 2 + (i_cube.x() > current.x()); + } + + static void propagate_point( + Vec3d point, + FillAdaptive_Internal::Cube *current_cube, + int depth, + const std::vector &cubes_properties); }; }; // namespace FillAdaptive_Internal @@ -48,12 +59,6 @@ class FillAdaptive : public Fill public: virtual ~FillAdaptive() {} - static void insert_octant( - FillAdaptive_Internal::Cube * i_cube, - FillAdaptive_Internal::Cube * current, - int depth, - const std::vector &cubes_properties); - protected: virtual Fill* clone() const { return new FillAdaptive(*this); }; virtual void _fill_surface_single( From 8fb9b290b261d3deb86fb7795b54fbd184110167 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Wed, 9 Sep 2020 09:29:50 +0200 Subject: [PATCH 485/503] A prototype of adaptive support infill --- src/libslic3r/Fill/FillAdaptive.cpp | 115 ++++++++++++++++++++++++++++ src/libslic3r/Fill/FillAdaptive.hpp | 6 ++ src/libslic3r/PrintObject.cpp | 5 ++ 3 files changed, 126 insertions(+) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index 4667c30c90..d921d8b919 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -382,4 +382,119 @@ void FillAdaptive_Internal::Octree::propagate_point( Octree::propagate_point(point, child, (depth - 1), cubes_properties); } +std::unique_ptr FillAdaptive::build_octree_for_adaptive_support( + TriangleMesh & triangle_mesh, + coordf_t line_spacing, + const Vec3d & cube_center, + const Transform3d &rotation_matrix) +{ + using namespace FillAdaptive_Internal; + + if(line_spacing <= 0 || std::isnan(line_spacing)) + { + return nullptr; + } + + Vec3d bb_size = triangle_mesh.bounding_box().size(); + // The furthest point from the center of the bottom of the mesh bounding box. + double furthest_point = std::sqrt(((bb_size.x() * bb_size.x()) / 4.0) + + ((bb_size.y() * bb_size.y()) / 4.0) + + (bb_size.z() * bb_size.z())); + double max_cube_edge_length = furthest_point * 2; + + std::vector cubes_properties; + for (double edge_length = (line_spacing * 2); edge_length < (max_cube_edge_length * 2); edge_length *= 2) + { + CubeProperties props{}; + props.edge_length = edge_length; + props.height = edge_length * sqrt(3); + props.diagonal_length = edge_length * sqrt(2); + props.line_z_distance = edge_length / sqrt(3); + props.line_xy_distance = edge_length / sqrt(6); + cubes_properties.push_back(props); + } + + if (triangle_mesh.its.vertices.empty()) + { + triangle_mesh.require_shared_vertices(); + } + + AABBTreeIndirect::Tree3f aabbTree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set( + triangle_mesh.its.vertices, triangle_mesh.its.indices); + + auto octree = std::make_unique(std::make_unique(cube_center), cube_center, cubes_properties); + + double cube_edge_length = line_spacing; + size_t max_depth = octree->cubes_properties.size() - 1; + BoundingBoxf3 mesh_bb = triangle_mesh.bounding_box(); + Vec3f vertical(0, 0, 1); + + for (size_t facet_idx = 0; facet_idx < triangle_mesh.stl.facet_start.size(); ++facet_idx) + { + if(triangle_mesh.stl.facet_start[facet_idx].normal.dot(vertical) <= 0.707) + { + // The angle is smaller than PI/4, than infill don't to be there + continue; + } + + stl_vertex v_1 = triangle_mesh.stl.facet_start[facet_idx].vertex[0]; + stl_vertex v_2 = triangle_mesh.stl.facet_start[facet_idx].vertex[1]; + stl_vertex v_3 = triangle_mesh.stl.facet_start[facet_idx].vertex[2]; + + std::vector triangle_vertices = + {Vec3d(v_1.x(), v_1.y(), v_1.z()), + Vec3d(v_2.x(), v_2.y(), v_2.z()), + Vec3d(v_3.x(), v_3.y(), v_3.z())}; + + BoundingBoxf3 triangle_bb(triangle_vertices); + + Vec3d triangle_start_relative = triangle_bb.min - mesh_bb.min; + Vec3d triangle_end_relative = triangle_bb.max - mesh_bb.min; + + Vec3crd triangle_start_idx = Vec3crd( + std::floor(triangle_start_relative.x() / cube_edge_length), + std::floor(triangle_start_relative.y() / cube_edge_length), + std::floor(triangle_start_relative.z() / cube_edge_length)); + Vec3crd triangle_end_idx = Vec3crd( + std::floor(triangle_end_relative.x() / cube_edge_length), + std::floor(triangle_end_relative.y() / cube_edge_length), + std::floor(triangle_end_relative.z() / cube_edge_length)); + + for (int z = triangle_start_idx.z(); z <= triangle_end_idx.z(); ++z) + { + for (int y = triangle_start_idx.y(); y <= triangle_end_idx.y(); ++y) + { + for (int x = triangle_start_idx.x(); x <= triangle_end_idx.x(); ++x) + { + Vec3d cube_center_relative(x * cube_edge_length + (cube_edge_length / 2.0), y * cube_edge_length + (cube_edge_length / 2.0), z * cube_edge_length); + Vec3d cube_center_absolute = cube_center_relative + mesh_bb.min; + + double cube_center_absolute_arr[3] = {cube_center_absolute.x(), cube_center_absolute.y(), cube_center_absolute.z()}; + double distance = 0, cord_u = 0, cord_v = 0; + + double dir[3] = {0.0, 0.0, 1.0}; + + double vert_0[3] = {triangle_vertices[0].x(), + triangle_vertices[0].y(), + triangle_vertices[0].z()}; + double vert_1[3] = {triangle_vertices[1].x(), + triangle_vertices[1].y(), + triangle_vertices[1].z()}; + double vert_2[3] = {triangle_vertices[2].x(), + triangle_vertices[2].y(), + triangle_vertices[2].z()}; + + if(intersect_triangle(cube_center_absolute_arr, dir, vert_0, vert_1, vert_2, &distance, &cord_u, &cord_v) && distance > 0 && distance <= cube_edge_length) + { + Vec3d cube_center_transformed(cube_center_absolute.x(), cube_center_absolute.y(), cube_center_absolute.z() + (cube_edge_length / 2.0)); + Octree::propagate_point(cube_center_transformed, octree->root_cube.get(), max_depth, octree->cubes_properties); + } + } + } + } + } + + return octree; +} + } // namespace Slic3r diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index d384776543..63043ce4e0 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -93,6 +93,12 @@ public: const AABBTreeIndirect::Tree3f &distance_tree, const TriangleMesh & triangle_mesh, int depth); + + static std::unique_ptr build_octree_for_adaptive_support( + TriangleMesh & triangle_mesh, + coordf_t line_spacing, + const Vec3d & cube_center, + const Transform3d &rotation_matrix); }; // Calculate line spacing for diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 645d36a38c..05debe8abb 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -433,6 +433,7 @@ void PrintObject::generate_support_material() } } +#define ADAPTIVE_SUPPORT #define ADAPTIVE_SUPPORT_SIMPLE std::unique_ptr PrintObject::prepare_adaptive_infill_data() @@ -489,7 +490,11 @@ std::unique_ptr PrintObject::prepare_adaptive_inf // Rotate mesh and build octree on it with axis-aligned (standart base) cubes mesh.transform(rotation_matrix); +#if defined(ADAPTIVE_SUPPORT) && !defined(ADAPTIVE_SUPPORT_SIMPLE) + return FillAdaptive::build_octree_for_adaptive_support(mesh, adaptive_line_spacing, rotation_matrix * mesh_origin, rotation_matrix); +#else return FillAdaptive::build_octree(mesh, adaptive_line_spacing, rotation_matrix * mesh_origin); +#endif } void PrintObject::clear_layers() From f49144a9ef3701e77c2ff812fddfb394c53134ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 10 Sep 2020 16:53:08 +0200 Subject: [PATCH 486/503] Move support cubic infill to separate class. Support infill is enabled in the GUI. --- src/libslic3r/Fill/Fill.cpp | 3 +- src/libslic3r/Fill/FillAdaptive.cpp | 44 +++++++++++++++++++++-------- src/libslic3r/Fill/FillAdaptive.hpp | 26 +++++++++++++++++ src/libslic3r/Fill/FillBase.cpp | 1 + src/libslic3r/Fill/FillBase.hpp | 3 ++ src/libslic3r/Layer.hpp | 4 +-- src/libslic3r/Print.hpp | 2 +- src/libslic3r/PrintConfig.cpp | 2 ++ src/libslic3r/PrintConfig.hpp | 3 +- src/libslic3r/PrintObject.cpp | 32 ++++++++++++--------- 10 files changed, 90 insertions(+), 30 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 9d468a6aa9..d68bc7afb3 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -318,7 +318,7 @@ void export_group_fills_to_svg(const char *path, const std::vector #endif // friend to Layer -void Layer::make_fills(FillAdaptive_Internal::Octree* adaptive_fill_octree) +void Layer::make_fills(FillAdaptive_Internal::Octree* adaptive_fill_octree, FillAdaptive_Internal::Octree* support_fill_octree) { for (LayerRegion *layerm : m_regions) layerm->fills.clear(); @@ -346,6 +346,7 @@ void Layer::make_fills(FillAdaptive_Internal::Octree* adaptive_fill_octree) f->z = this->print_z; f->angle = surface_fill.params.angle; f->adapt_fill_octree = adaptive_fill_octree; + f->support_fill_octree = support_fill_octree; // calculate flow spacing for infill pattern generation bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.flow.bridge; diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index d921d8b919..fb8c665ebd 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -35,7 +35,7 @@ std::pair adaptive_fill_line_spacing(const PrintObject &print_ob const PrintRegionConfig &config = region->config(); bool nonempty = config.fill_density > 0; bool has_adaptive_infill = nonempty && config.fill_pattern == ipAdaptiveCubic; - bool has_support_infill = nonempty && false; // config.fill_pattern == icSupportCubic; + bool has_support_infill = nonempty && config.fill_pattern == ipSupportCubic; region_fill_data.push_back(RegionFillData({ has_adaptive_infill ? Tristate::Maybe : Tristate::No, has_support_infill ? Tristate::Maybe : Tristate::No, @@ -90,22 +90,32 @@ std::pair adaptive_fill_line_spacing(const PrintObject &print_ob return std::make_pair(adaptive_line_spacing, support_line_spacing); } -void FillAdaptive::_fill_surface_single( - const FillParams ¶ms, - unsigned int thickness_layers, - const std::pair &direction, - ExPolygon &expolygon, - Polylines &polylines_out) +void FillAdaptive::_fill_surface_single(const FillParams & params, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon & expolygon, + Polylines & polylines_out) +{ + if(this->adapt_fill_octree != nullptr) + this->generate_infill(params, thickness_layers, direction, expolygon, polylines_out, this->adapt_fill_octree); +} + +void FillAdaptive::generate_infill(const FillParams & params, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon & expolygon, + Polylines & polylines_out, + FillAdaptive_Internal::Octree *octree) { Vec3d rotation = Vec3d((5.0 * M_PI) / 4.0, Geometry::deg2rad(215.264), M_PI / 6.0); Transform3d rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), rotation, Vec3d::Ones(), Vec3d::Ones()); // Store grouped lines by its direction (multiple of 120°) std::vector infill_lines_dir(3); - this->generate_infill_lines(this->adapt_fill_octree->root_cube.get(), - this->z, this->adapt_fill_octree->origin, rotation_matrix, - infill_lines_dir, this->adapt_fill_octree->cubes_properties, - int(this->adapt_fill_octree->cubes_properties.size()) - 1); + this->generate_infill_lines(octree->root_cube.get(), + this->z, octree->origin, rotation_matrix, + infill_lines_dir, octree->cubes_properties, + int(octree->cubes_properties.size()) - 1); Polylines all_polylines; all_polylines.reserve(infill_lines_dir[0].size() * 3); @@ -382,7 +392,7 @@ void FillAdaptive_Internal::Octree::propagate_point( Octree::propagate_point(point, child, (depth - 1), cubes_properties); } -std::unique_ptr FillAdaptive::build_octree_for_adaptive_support( +std::unique_ptr FillSupportCubic::build_octree_for_adaptive_support( TriangleMesh & triangle_mesh, coordf_t line_spacing, const Vec3d & cube_center, @@ -497,4 +507,14 @@ std::unique_ptr FillAdaptive::build_octree_for_ad return octree; } +void FillSupportCubic::_fill_surface_single(const FillParams & params, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon & expolygon, + Polylines & polylines_out) +{ + if (this->support_fill_octree != nullptr) + this->generate_infill(params, thickness_layers, direction, expolygon, polylines_out, this->support_fill_octree); +} + } // namespace Slic3r diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index 63043ce4e0..45bfde8026 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -81,6 +81,13 @@ protected: static void connect_lines(Lines &lines, Line new_line); + void generate_infill(const FillParams & params, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon & expolygon, + Polylines & polylines_out, + FillAdaptive_Internal::Octree *octree); + public: static std::unique_ptr build_octree( TriangleMesh &triangle_mesh, @@ -93,7 +100,26 @@ public: const AABBTreeIndirect::Tree3f &distance_tree, const TriangleMesh & triangle_mesh, int depth); +}; +class FillSupportCubic : public FillAdaptive +{ +public: + virtual ~FillSupportCubic() = default; + +protected: + virtual Fill* clone() const { return new FillSupportCubic(*this); }; + + virtual bool no_sort() const { return true; } + + virtual void _fill_surface_single( + const FillParams ¶ms, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon &expolygon, + Polylines &polylines_out); + +public: static std::unique_ptr build_octree_for_adaptive_support( TriangleMesh & triangle_mesh, coordf_t line_spacing, diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index c1f38dad5c..9001330aae 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -39,6 +39,7 @@ Fill* Fill::new_from_type(const InfillPattern type) case ipHilbertCurve: return new FillHilbertCurve(); case ipOctagramSpiral: return new FillOctagramSpiral(); case ipAdaptiveCubic: return new FillAdaptive(); + case ipSupportCubic: return new FillSupportCubic(); default: throw std::invalid_argument("unknown type"); } } diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index 9f70b69e08..dd887b8c3e 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -73,7 +73,10 @@ public: // In scaled coordinates. Bounding box of the 2D projection of the object. BoundingBox bounding_box; + // Octree builds on mesh for usage in the adaptive cubic infill FillAdaptive_Internal::Octree* adapt_fill_octree = nullptr; + // Octree builds on mesh for usage in the support cubic infill + FillAdaptive_Internal::Octree* support_fill_octree = nullptr; public: virtual ~Fill() {} diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 014d2623af..8d5db42fc0 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -138,8 +138,8 @@ public: return false; } void make_perimeters(); - void make_fills() { this->make_fills(nullptr); }; - void make_fills(FillAdaptive_Internal::Octree* adaptive_fill_octree); + void make_fills() { this->make_fills(nullptr, nullptr); }; + void make_fills(FillAdaptive_Internal::Octree* adaptive_fill_octree, FillAdaptive_Internal::Octree* support_fill_octree); void make_ironing(); void export_region_slices_to_svg(const char *path) const; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index effb6bde90..98a1314112 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -239,7 +239,7 @@ private: void discover_horizontal_shells(); void combine_infill(); void _generate_support_material(); - std::unique_ptr prepare_adaptive_infill_data(); + std::pair, std::unique_ptr> prepare_adaptive_infill_data(); // XYZ in scaled coordinates Vec3crd m_size; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 718fae365b..72393a3f5a 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -882,6 +882,7 @@ void PrintConfigDef::init_fff_params() def->enum_values.push_back("archimedeanchords"); def->enum_values.push_back("octagramspiral"); def->enum_values.push_back("adaptivecubic"); + def->enum_values.push_back("supportcubic"); def->enum_labels.push_back(L("Rectilinear")); def->enum_labels.push_back(L("Grid")); def->enum_labels.push_back(L("Triangles")); @@ -896,6 +897,7 @@ void PrintConfigDef::init_fff_params() def->enum_labels.push_back(L("Archimedean Chords")); def->enum_labels.push_back(L("Octagram Spiral")); def->enum_labels.push_back(L("Adaptive Cubic")); + def->enum_labels.push_back(L("Support Cubic")); def->set_default_value(new ConfigOptionEnum(ipStars)); def = this->add("first_layer_acceleration", coFloat); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 3726444fab..fa7edd10e1 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -39,7 +39,7 @@ enum AuthorizationType { enum InfillPattern : int { ipRectilinear, ipMonotonous, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, - ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipCount, + ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipCount, }; enum class IroningType { @@ -140,6 +140,7 @@ template<> inline const t_config_enum_values& ConfigOptionEnum::g keys_map["archimedeanchords"] = ipArchimedeanChords; keys_map["octagramspiral"] = ipOctagramSpiral; keys_map["adaptivecubic"] = ipAdaptiveCubic; + keys_map["supportcubic"] = ipSupportCubic; } return keys_map; } diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 05debe8abb..a102c32813 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -372,15 +372,15 @@ void PrintObject::infill() this->prepare_infill(); if (this->set_started(posInfill)) { - std::unique_ptr octree = this->prepare_adaptive_infill_data(); + auto [adaptive_fill_octree, support_fill_octree] = this->prepare_adaptive_infill_data(); BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start"; tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), - [this, &octree](const tbb::blocked_range& range) { + [this, &adaptive_fill_octree, &support_fill_octree](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { m_print->throw_if_canceled(); - m_layers[layer_idx]->make_fills(octree.get()); + m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get()); } } ); @@ -433,14 +433,18 @@ void PrintObject::generate_support_material() } } -#define ADAPTIVE_SUPPORT -#define ADAPTIVE_SUPPORT_SIMPLE +//#define ADAPTIVE_SUPPORT_SIMPLE -std::unique_ptr PrintObject::prepare_adaptive_infill_data() +std::pair, std::unique_ptr> PrintObject::prepare_adaptive_infill_data() { + using namespace FillAdaptive_Internal; + auto [adaptive_line_spacing, support_line_spacing] = adaptive_fill_line_spacing(*this); - if (adaptive_line_spacing == 0.) - return std::unique_ptr{}; + + std::unique_ptr adaptive_fill_octree = {}, support_fill_octree = {}; + + if (adaptive_line_spacing == 0. && support_line_spacing == 0.) + return std::make_pair(std::move(adaptive_fill_octree), std::move(support_fill_octree)); TriangleMesh mesh = this->model_object()->raw_mesh(); mesh.transform(m_trafo, true); @@ -490,11 +494,13 @@ std::unique_ptr PrintObject::prepare_adaptive_inf // Rotate mesh and build octree on it with axis-aligned (standart base) cubes mesh.transform(rotation_matrix); -#if defined(ADAPTIVE_SUPPORT) && !defined(ADAPTIVE_SUPPORT_SIMPLE) - return FillAdaptive::build_octree_for_adaptive_support(mesh, adaptive_line_spacing, rotation_matrix * mesh_origin, rotation_matrix); -#else - return FillAdaptive::build_octree(mesh, adaptive_line_spacing, rotation_matrix * mesh_origin); -#endif + if (adaptive_line_spacing != 0.) + adaptive_fill_octree = FillAdaptive::build_octree(mesh, adaptive_line_spacing, rotation_matrix * mesh_origin); + + if (support_line_spacing != 0.) + support_fill_octree = FillSupportCubic::build_octree_for_adaptive_support(mesh, support_line_spacing, rotation_matrix * mesh_origin, rotation_matrix); + + return std::make_pair(std::move(adaptive_fill_octree), std::move(support_fill_octree)); } void PrintObject::clear_layers() From f1f9785a8a3d10ad7a3bb91194e0d758cab5aee3 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 10 Sep 2020 16:03:43 +0200 Subject: [PATCH 487/503] SplashScreen: * Show it on the display same as an Application * Code refactoring : All related functions moved to the SplashScreen class * Add a possibility o hide/show splash scree in Preferences --- src/libslic3r/AppConfig.cpp | 3 + src/slic3r/GUI/GUI_App.cpp | 370 ++++++++++++++++++++------------- src/slic3r/GUI/Preferences.cpp | 9 + 3 files changed, 234 insertions(+), 148 deletions(-) diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index 2d96e0b50d..5892b4a30d 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -101,6 +101,9 @@ void AppConfig::set_defaults() if (get("use_inches").empty()) set("use_inches", "0"); + if (get("show_splash_screen").empty()) + set("show_splash_screen", "1"); + // Remove legacy window positions/sizes erase("", "main_frame_maximized"); erase("", "main_frame_pos"); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index e50d4015e7..17b21783c8 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -78,176 +78,233 @@ namespace GUI { class MainFrame; -static float get_scale_for_main_display() -{ - // ysFIXME : Workaround : - // wxFrame is created on the main monitor, so we can take a scale factor from this one - // before The Application and the Mainframe are created - wxFrame fr(nullptr, wxID_ANY, wxEmptyString); - -#if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT && !defined(__WXGTK__) - int dpi = get_dpi_for_window(&fr); - float sf = dpi != DPI_DEFAULT ? sf = (float)dpi / DPI_DEFAULT : 1.0; -#else - printf("dpi = %d\n", get_dpi_for_window(&fr)); - // initialize default width_unit according to the width of the one symbol ("m") of the currently active font of this window. - float sf = 0.1 * std::max(10, fr.GetTextExtent("m").x - 1); -#endif // ENABLE_WX_3_1_3_DPI_CHANGED_EVENT - - printf("scale factor = %f\n", sf); - return sf; -} - -// scale input bitmap and return scale factor -static float scale_bitmap(wxBitmap& bmp) -{ - float sf = get_scale_for_main_display(); - - // scale bitmap if needed - if (sf > 1.0) { - wxImage image = bmp.ConvertToImage(); - if (image.IsOk() && image.GetWidth() != 0 && image.GetHeight() != 0) - { - int width = int(sf * image.GetWidth()); - int height = int(sf * image.GetHeight()); - image.Rescale(width, height, wxIMAGE_QUALITY_BILINEAR); - - bmp = wxBitmap(std::move(image)); - } - } - - return sf; -} - -static void word_wrap_string(wxString& input, int line_px_len, float scalef) -{ - // calculate count od symbols in one line according to the scale - int line_len = std::roundf( (float)line_px_len / (scalef * 10)) + 10; - - int idx = -1; - int cur_len = 0; - for (size_t i = 0; i < input.Len(); i++) - { - cur_len++; - if (input[i] == ' ') - idx = i; - if (input[i] == '\n') - { - idx = -1; - cur_len = 0; - } - if (cur_len >= line_len && idx >= 0) - { - input[idx] = '\n'; - cur_len = static_cast(i) - idx; - } - } -} - -static void DecorateSplashScreen(wxBitmap& bmp) -{ - wxASSERT(bmp.IsOk()); - float scale_factor = scale_bitmap(bmp); - - // use a memory DC to draw directly onto the bitmap - wxMemoryDC memDc(bmp); - - // draw an dark grey box at the left of the splashscreen. - // this box will be 2/5 of the weight of the bitmap, and be at the left. - int banner_width = (bmp.GetWidth() / 5) * 2 - 2; - const wxRect banner_rect(wxPoint(0, (bmp.GetHeight() / 9) * 2), wxPoint(banner_width, bmp.GetHeight())); - wxDCBrushChanger bc(memDc, wxBrush(wxColour(51, 51, 51))); - wxDCPenChanger pc(memDc, wxPen(wxColour(51, 51, 51))); - memDc.DrawRectangle(banner_rect); - - // title -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_GCODE_VIEWER - wxString title_string = wxGetApp().is_editor() ? SLIC3R_APP_NAME : GCODEVIEWER_APP_NAME; -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - wxString title_string = SLIC3R_APP_NAME; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - wxFont title_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - title_font.SetPointSize(24); - - // dynamically get the version to display - wxString version_string = _L("Version") + " " + std::string(SLIC3R_VERSION); - wxFont version_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Larger().Larger(); - - // create a copyright notice that uses the year that this file was compiled - wxString year(__DATE__); - wxString cr_symbol = wxString::FromUTF8("\xc2\xa9"); - wxString copyright_string = wxString::Format("%s 2016-%s Prusa Research.\n" - "%s 2011-2018 Alessandro Ranellucci.", - cr_symbol, year.Mid(year.Length() - 4), cr_symbol) + "\n\n"; - wxFont copyright_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Larger(); - - copyright_string += //"Slic3r" + _L("is licensed under the") + _L("GNU Affero General Public License, version 3") + "\n\n" + - _L("PrusaSlicer is based on Slic3r by Alessandro Ranellucci and the RepRap community.") + "\n\n" + - _L("Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and numerous others."); - - word_wrap_string(copyright_string, banner_width, scale_factor); - - wxCoord margin = int(scale_factor * 20); - - // draw the (orange) labels inside of our black box (at the left of the splashscreen) - memDc.SetTextForeground(wxColour(237, 107, 33)); - - memDc.SetFont(title_font); - memDc.DrawLabel(title_string, banner_rect.Deflate(margin, 0), wxALIGN_TOP | wxALIGN_LEFT); - - memDc.SetFont(version_font); - memDc.DrawLabel(version_string, banner_rect.Deflate(margin, 2 * margin), wxALIGN_TOP | wxALIGN_LEFT); - - memDc.SetFont(copyright_font); - memDc.DrawLabel(copyright_string, banner_rect.Deflate(margin, 2 * margin), wxALIGN_BOTTOM | wxALIGN_LEFT); -} - class SplashScreen : public wxSplashScreen { public: - SplashScreen(const wxBitmap& bitmap, long splashStyle, int milliseconds, wxWindow* parent) - : wxSplashScreen(bitmap, splashStyle, milliseconds, parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, - wxSIMPLE_BORDER | wxFRAME_NO_TASKBAR) + SplashScreen(const wxBitmap& bitmap, long splashStyle, int milliseconds, wxPoint pos = wxDefaultPosition, bool is_decorated = false) + : wxSplashScreen(bitmap, splashStyle, milliseconds, nullptr, wxID_ANY, + wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER | wxFRAME_NO_TASKBAR ) { wxASSERT(bitmap.IsOk()); m_main_bitmap = bitmap; - m_scale_factor = get_scale_for_main_display(); + if (!is_decorated) + Decorate(m_main_bitmap, pos, true); + + m_scale = get_display_scale(pos); + m_font = get_scaled_sys_font(get_splashscreen_display_scale_factor(pos)).Bold().Larger(); + + if (pos != wxDefaultPosition) { + this->SetPosition(pos); + this->CenterOnScreen(); + } } void SetText(const wxString& text) { - SetBmp(m_main_bitmap); + set_bitmap(m_main_bitmap); if (!text.empty()) { wxBitmap bitmap(m_main_bitmap); wxMemoryDC memDC; memDC.SelectObject(bitmap); - wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold().Larger(); - memDC.SetFont(font); + wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + memDC.SetFont(m_font); memDC.SetTextForeground(wxColour(237, 107, 33)); - memDC.DrawText(text, int(m_scale_factor * 45), int(m_scale_factor * 215)); + memDC.DrawText(text, int(m_scale * 45), int(m_scale * 200)); memDC.SelectObject(wxNullBitmap); - SetBmp(bitmap); + set_bitmap(bitmap); } } - void SetBmp(wxBitmap& bmp) + static bool Decorate(wxBitmap& bmp, wxPoint screen_pos = wxDefaultPosition, bool force_decor = false) + { + if (!bmp.IsOk()) + return false; + + float screen_sf = get_splashscreen_display_scale_factor(screen_pos); + float screen_scale = get_display_scale(screen_pos); + + if (screen_sf == 1.0) { + // it means that we have just one display or all displays have a same scale + // Scale bitmap for this display and continue the decoration + scale_bitmap(bmp, screen_scale); + } + else if (force_decor) { + // if we are here, it means that bitmap is already scaled for the main display + // and now we should just scale it th the secondary monitor and continue the decoration + scale_bitmap(bmp, screen_sf); + } + else { + // if screens have different scale and this function is called with force_decor == false + // then just rescale the bitmap for the main display scale + scale_bitmap(bmp, get_display_scale()); + return false; + // Decoration will be continued later, from the SplashScreen constructor + } + + // use a memory DC to draw directly onto the bitmap + wxMemoryDC memDc(bmp); + + // draw an dark grey box at the left of the splashscreen. + // this box will be 2/5 of the weight of the bitmap, and be at the left. + int banner_width = (bmp.GetWidth() / 5) * 2 - 2; + const wxRect banner_rect(wxPoint(0, (bmp.GetHeight() / 9) * 2), wxPoint(banner_width, bmp.GetHeight())); + wxDCBrushChanger bc(memDc, wxBrush(wxColour(51, 51, 51))); + wxDCPenChanger pc(memDc, wxPen(wxColour(51, 51, 51))); + memDc.DrawRectangle(banner_rect); + + wxFont sys_font = get_scaled_sys_font(screen_sf); + + // title +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_GCODE_VIEWER + wxString title_string = wxGetApp().is_editor() ? SLIC3R_APP_NAME : GCODEVIEWER_APP_NAME; +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + wxString title_string = SLIC3R_APP_NAME; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_GCODE_VIEWER +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + wxFont title_font = sys_font; + title_font.SetPointSize(3 * sys_font.GetPointSize()); + + + // dynamically get the version to display + wxString version_string = _L("Version") + " " + std::string(SLIC3R_VERSION); + wxFont version_font = sys_font.Larger().Larger(); + + // create a copyright notice that uses the year that this file was compiled + wxString year(__DATE__); + wxString cr_symbol = wxString::FromUTF8("\xc2\xa9"); + wxString copyright_string = wxString::Format("%s 2016-%s Prusa Research.\n" + "%s 2011-2018 Alessandro Ranellucci.", + cr_symbol, year.Mid(year.Length() - 4), cr_symbol) + "\n\n"; + wxFont copyright_font = sys_font.Larger(); + + copyright_string += //"Slic3r" + _L("is licensed under the") + _L("GNU Affero General Public License, version 3") + "\n\n" + + _L("PrusaSlicer is based on Slic3r by Alessandro Ranellucci and the RepRap community.") + "\n\n" + + _L("Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and numerous others.") + "\n\n" + + _L("Splash screen could be desabled from the \"Preferences\""); + + word_wrap_string(copyright_string, banner_width, screen_scale); + + wxCoord margin = int(screen_scale * 20); + + // draw the (orange) labels inside of our black box (at the left of the splashscreen) + memDc.SetTextForeground(wxColour(237, 107, 33)); + + memDc.SetFont(title_font); + memDc.DrawLabel(title_string, banner_rect.Deflate(margin, 0), wxALIGN_TOP | wxALIGN_LEFT); + + memDc.SetFont(version_font); + memDc.DrawLabel(version_string, banner_rect.Deflate(margin, 2 * margin), wxALIGN_TOP | wxALIGN_LEFT); + + memDc.SetFont(copyright_font); + memDc.DrawLabel(copyright_string, banner_rect.Deflate(margin, margin), wxALIGN_BOTTOM | wxALIGN_LEFT); + + return true; + } + +private: + wxBitmap m_main_bitmap; + wxFont m_font; + float m_scale {1.0}; + + void set_bitmap(wxBitmap& bmp) { m_window->SetBitmap(bmp); m_window->Refresh(); m_window->Update(); } -private: - wxBitmap m_main_bitmap; - float m_scale_factor {1.0}; + static float get_splashscreen_display_scale_factor(wxPoint pos = wxDefaultPosition) + { + if (wxDisplay::GetCount() == 1) + return 1.0; + + wxFrame main_screen_fr(nullptr, wxID_ANY, wxEmptyString); + wxFrame splash_screen_fr(nullptr, wxID_ANY, wxEmptyString, pos); + +#if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT && !defined(__WXGTK__) + int main_dpi = get_dpi_for_window(&main_screen_fr); + int splash_dpi = get_dpi_for_window(&splash_screen_fr); + float sf = (float)splash_dpi / (float)main_dpi; +#else + // initialize default width_unit according to the width of the one symbol ("m") of the currently active font of this window. + float sf = (float)splash_screen_fr.GetTextExtent("m").x / (float)main_screen_fr.GetTextExtent("m").x; +#endif // ENABLE_WX_3_1_3_DPI_CHANGED_EVENT + + return sf; + } + + static float get_display_scale(wxPoint pos = wxDefaultPosition) + { + // pos equals to wxDefaultPosition, when display is main + wxFrame fr(nullptr, wxID_ANY, wxEmptyString, pos); + +#if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT && !defined(__WXGTK__) + int dpi = get_dpi_for_window(&fr); + float scale = dpi != DPI_DEFAULT ? (float)dpi / DPI_DEFAULT : 1.0; +#else + // initialize default width_unit according to the width of the one symbol ("m") of the currently active font of this window. + float scale = 0.1 * std::max(10, fr.GetTextExtent("m").x - 1); +#endif // ENABLE_WX_3_1_3_DPI_CHANGED_EVENT + + return scale; + } + + static void scale_bitmap(wxBitmap& bmp, float scale) + { + if (scale == 1.0) + return; + + wxImage image = bmp.ConvertToImage(); + if (!image.IsOk() || image.GetWidth() == 0 || image.GetHeight() == 0) + return; + + int width = int(scale * image.GetWidth()); + int height = int(scale * image.GetHeight()); + image.Rescale(width, height, wxIMAGE_QUALITY_BILINEAR); + + bmp = wxBitmap(std::move(image)); + } + + static wxFont get_scaled_sys_font(float screen_sf) + { + wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + if (screen_sf != 1.0) + font.SetPointSize(int(screen_sf * (float)font.GetPointSize())); + + return font; + } + + static void word_wrap_string(wxString& input, int line_px_len, float scalef) + { + // calculate count od symbols in one line according to the scale + int line_len = int((float)line_px_len / (scalef * 10) + 0.5) + 10; + + int idx = -1; + int cur_len = 0; + for (size_t i = 0; i < input.Len(); i++) + { + cur_len++; + if (input[i] == ' ') + idx = i; + if (input[i] == '\n') + { + idx = -1; + cur_len = 0; + } + if (cur_len >= line_len && idx >= 0) + { + input[idx] = '\n'; + cur_len = static_cast(i) - idx; + } + } + } }; wxString file_wildcards(FileType file_type, const std::string &custom_extension) @@ -584,17 +641,32 @@ bool GUI_App::on_init_inner() */ wxInitAllImageHandlers(); - wxBitmap bitmap = create_scaled_bitmap("prusa_slicer_logo", nullptr, 400); + SplashScreen* scrn = nullptr; + if (app_config->get("show_splash_screen") == "1") + { #if ENABLE_GCODE_VIEWER - wxBitmap bmp(is_editor() ? from_u8(var("splashscreen.jpg")) : from_u8(var("splashscreen-gcodeviewer.jpg")), wxBITMAP_TYPE_JPEG); + wxBitmap bmp(is_editor() ? from_u8(var("splashscreen.jpg")) : from_u8(var("splashscreen-gcodeviewer.jpg")), wxBITMAP_TYPE_JPEG); #else - wxBitmap bmp(from_u8(var("splashscreen.jpg")), wxBITMAP_TYPE_JPEG); + wxBitmap bmp(from_u8(var("splashscreen.jpg")), wxBITMAP_TYPE_JPEG); #endif // ENABLE_GCODE_VIEWER - DecorateSplashScreen(bmp); + // Detect position (display) to show the splash screen + // Now this position is equal to the mainframe position + wxPoint splashscreen_pos = wxDefaultPosition; + if (app_config->has("window_mainframe")) { + auto metrics = WindowMetrics::deserialize(app_config->get("window_mainframe")); + if (metrics) + splashscreen_pos = metrics->get_rect().GetPosition(); + } - SplashScreen* scrn = new SplashScreen(bmp.IsOk() ? bmp : bitmap, wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_TIMEOUT, 4000, nullptr); - scrn->SetText(_L("Loading configuration...")); + // try to decorate and/or scale the bitmap before splash screen creation + bool is_decorated = SplashScreen::Decorate(bmp, splashscreen_pos); + + // create splash screen with updated bmp + scrn = new SplashScreen(bmp.IsOk() ? bmp : create_scaled_bitmap("prusa_slicer_logo", nullptr, 400), + wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_TIMEOUT, 4000, splashscreen_pos, is_decorated); + scrn->SetText(_L("Loading configuration...")); + } preset_bundle = new PresetBundle(); @@ -650,7 +722,9 @@ bool GUI_App::on_init_inner() // application frame #if ENABLE_GCODE_VIEWER - if (is_editor()) + if (scrn && is_editor()) +#else + if (scrn) #endif // ENABLE_GCODE_VIEWER scrn->SetText(_L("Creating settings tabs...")); diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 242c3d851d..a3a23fd851 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -131,6 +131,15 @@ void PreferencesDialog::build() option = Option(def, "use_inches"); m_optgroup_general->append_single_option_line(option); */ + + // Show/Hide splash screen + def.label = L("Show splash screen"); + def.type = coBool; + def.tooltip = L("Show splash screen"); + def.set_default_value(new ConfigOptionBool{ app_config->get("show_splash_screen") == "1" }); + option = Option(def, "show_splash_screen"); + m_optgroup_general->append_single_option_line(option); + m_optgroup_camera = std::make_shared(this, _(L("Camera"))); m_optgroup_camera->label_width = 40; m_optgroup_camera->m_on_change = [this](t_config_option_key opt_key, boost::any value) { From 20bd7b99f9c8d4ded94e6c01450ffdbfadc36c0a Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 10 Sep 2020 19:35:26 +0200 Subject: [PATCH 488/503] Significant performance improvements for elevated and non-elevated case Apply bruteforce for elevated models --- src/libslic3r/Fill/FillAdaptive.hpp | 1 + src/libslic3r/SLA/Rotfinder.cpp | 135 +++++++++++++++---------- src/libslic3r/SLA/Rotfinder.hpp | 2 +- src/slic3r/GUI/Jobs/RotoptimizeJob.cpp | 11 +- 4 files changed, 89 insertions(+), 60 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index dd7394384c..23786530e3 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -4,6 +4,7 @@ #include "../AABBTreeIndirect.hpp" #include "FillBase.hpp" +#include "TriangleMesh.hpp" namespace Slic3r { diff --git a/src/libslic3r/SLA/Rotfinder.cpp b/src/libslic3r/SLA/Rotfinder.cpp index db8c0b9a8d..9378977663 100644 --- a/src/libslic3r/SLA/Rotfinder.cpp +++ b/src/libslic3r/SLA/Rotfinder.cpp @@ -1,11 +1,10 @@ #include -#include -//#include -#include #include #include +#include + #include "libslic3r/SLAPrint.hpp" #include "libslic3r/PrintConfig.hpp" @@ -61,23 +60,25 @@ std::array get_transformed_triangle(const TriangleMesh &mesh, } // Get area and normal of a triangle -struct Face { Vec3d normal; double area; }; -inline Face facestats(const std::array &triangle) -{ - Vec3d U = triangle[1] - triangle[0]; - Vec3d V = triangle[2] - triangle[0]; - Vec3d C = U.cross(V); - Vec3d N = C.normalized(); - double area = 0.5 * C.norm(); +struct Facestats { + Vec3d normal; + double area; - return {N, area}; -} + explicit Facestats(const std::array &triangle) + { + Vec3d U = triangle[1] - triangle[0]; + Vec3d V = triangle[2] - triangle[0]; + Vec3d C = U.cross(V); + normal = C.normalized(); + area = 0.5 * C.norm(); + } +}; inline const Vec3d DOWN = {0., 0., -1.}; constexpr double POINTS_PER_UNIT_AREA = 1.; // The score function for a particular face -inline double get_score(const Face &fc) +inline double get_score(const Facestats &fc) { // Simply get the angle (acos of dot product) between the face normal and // the DOWN vector. @@ -110,7 +111,7 @@ double get_model_supportedness(const TriangleMesh &mesh, const Transform3d &tr) if (mesh.its.vertices.empty()) return std::nan(""); auto accessfn = [&mesh, &tr](size_t fi) { - Face fc = facestats(get_transformed_triangle(mesh, tr, fi)); + Facestats fc{get_transformed_triangle(mesh, tr, fi)}; return get_score(fc); }; @@ -131,7 +132,7 @@ double get_model_supportedness_onfloor(const TriangleMesh &mesh, auto accessfn = [&mesh, &tr, zlvl](size_t fi) { std::array tri = get_transformed_triangle(mesh, tr, fi); - Face fc = facestats(tri); + Facestats fc{tri}; if (tri[0].z() <= zlvl && tri[1].z() <= zlvl && tri[2].z() <= zlvl) return -fc.area * POINTS_PER_UNIT_AREA; @@ -161,56 +162,91 @@ XYRotation from_transform3d(const Transform3d &tr) } // Find the best score from a set of function inputs. Evaluate for every point. -template -std::array find_min_score(Fn &&fn, Cmp &&cmp, It from, It to) +template +std::array find_min_score(Fn &&fn, It from, It to, StopCond &&stopfn) { std::array ret; double score = std::numeric_limits::max(); - for (auto it = from; it != to; ++it) { - double sc = fn(*it); - if (cmp(sc, score)) { - score = sc; - ret = *it; - } - } + size_t Nthreads = std::thread::hardware_concurrency(); + size_t dist = std::distance(from, to); + std::vector scores(dist, score); + + ccr_par::for_each(size_t(0), dist, [&stopfn, &scores, &fn, &from](size_t i) { + if (stopfn()) return; + + scores[i] = fn(*(from + i)); + }, dist / Nthreads); + + auto it = std::min_element(scores.begin(), scores.end()); + + if (it != scores.end()) ret = *(from + std::distance(scores.begin(), it)); return ret; } // collect the rotations for each face of the convex hull -std::vector get_chull_rotations(const TriangleMesh &mesh) +std::vector get_chull_rotations(const TriangleMesh &mesh, size_t max_count) { TriangleMesh chull = mesh.convex_hull_3d(); chull.require_shared_vertices(); double chull2d_area = chull.convex_hull().area(); - double area_threshold = chull2d_area / (scaled(1e3) * scaled(1.)); + double area_threshold = chull2d_area / (scaled(1e3) * scaled(1.)); size_t facecount = chull.its.indices.size(); - auto inputs = reserve_vector(facecount); + + struct RotArea { XYRotation rot; double area; }; + + auto inputs = reserve_vector(facecount); + + auto rotcmp = [](const RotArea &r1, const RotArea &r2) { + double xdiff = r1.rot[X] - r2.rot[X], ydiff = r1.rot[Y] - r2.rot[Y]; + return std::abs(xdiff) < EPSILON ? ydiff < 0. : xdiff < 0.; + }; + + auto eqcmp = [](const XYRotation &r1, const XYRotation &r2) { + double xdiff = r1[X] - r2[X], ydiff = r1[Y] - r2[Y]; + return std::abs(xdiff) < EPSILON && std::abs(ydiff) < EPSILON; + }; for (size_t fi = 0; fi < facecount; ++fi) { - Face fc = facestats(get_triangle_vertices(chull, fi)); + Facestats fc{get_triangle_vertices(chull, fi)}; if (fc.area > area_threshold) { auto q = Eigen::Quaterniond{}.FromTwoVectors(fc.normal, DOWN); - inputs.emplace_back(from_transform3d(Transform3d::Identity() * q)); + XYRotation rot = from_transform3d(Transform3d::Identity() * q); + RotArea ra = {rot, fc.area}; + + auto it = std::lower_bound(inputs.begin(), inputs.end(), ra, rotcmp); + + if (it == inputs.end() || !eqcmp(it->rot, rot)) + inputs.insert(it, ra); } } - return inputs; + inputs.shrink_to_fit(); + if (!max_count) max_count = inputs.size(); + std::sort(inputs.begin(), inputs.end(), + [](const RotArea &ra, const RotArea &rb) { + return ra.area > rb.area; + }); + + auto ret = reserve_vector(std::min(max_count, inputs.size())); + for (const RotArea &ra : inputs) ret.emplace_back(ra.rot); + + return ret; } -XYRotation find_best_rotation(const SLAPrintObject & po, - float accuracy, - std::function statuscb, - std::function stopcond) +Vec2d find_best_rotation(const SLAPrintObject & po, + float accuracy, + std::function statuscb, + std::function stopcond) { - static const unsigned MAX_TRIES = 10000; + static const unsigned MAX_TRIES = 1000; // return value - std::array rot; + XYRotation rot; // We will use only one instance of this converted mesh to examine different // rotations @@ -226,7 +262,7 @@ XYRotation find_best_rotation(const SLAPrintObject & po, // call status callback with zero, because we are at the start statuscb(status); - auto statusfn = [&statuscb, &status, max_tries] { + auto statusfn = [&statuscb, &status, &max_tries] { // report status statuscb(unsigned(++status * 100.0/max_tries) ); }; @@ -234,29 +270,26 @@ XYRotation find_best_rotation(const SLAPrintObject & po, // Different search methods have to be used depending on the model elevation if (is_on_floor(po)) { + std::vector inputs = get_chull_rotations(mesh, max_tries); + max_tries = inputs.size(); + // If the model can be placed on the bed directly, we only need to // check the 3D convex hull face rotations. - auto inputs = get_chull_rotations(mesh); - - auto cmpfn = [](double a, double b) { return a < b; }; auto objfn = [&mesh, &statusfn](const XYRotation &rot) { statusfn(); - // We actually need the reverserotation to make the object lie on - // this face Transform3d tr = to_transform3d(rot); return get_model_supportedness_onfloor(mesh, tr); }; - rot = find_min_score<2>(objfn, cmpfn, inputs.begin(), inputs.end()); + rot = find_min_score<2>(objfn, inputs.begin(), inputs.end(), stopcond); } else { - // Preparing the optimizer. - size_t grid_size = std::sqrt(max_tries); + size_t gridsize = std::sqrt(max_tries); // 2D grid has gridsize^2 calls opt::Optimizer solver(opt::StopCriteria{} - .max_iterations(max_tries) - .stop_condition(stopcond), - grid_size); + .max_iterations(max_tries) + .stop_condition(stopcond), + gridsize); // We are searching rotations around only two axes x, y. Thus the // problem becomes a 2 dimensional optimization task. @@ -272,11 +305,9 @@ XYRotation find_best_rotation(const SLAPrintObject & po, // Save the result and fck off rot = result.optimum; - - std::cout << "best score: " << result.score << std::endl; } - return rot; + return {rot[0], rot[1]}; } double get_model_supportedness(const SLAPrintObject &po, const Transform3d &tr) diff --git a/src/libslic3r/SLA/Rotfinder.hpp b/src/libslic3r/SLA/Rotfinder.hpp index 4fa529600b..96561a890f 100644 --- a/src/libslic3r/SLA/Rotfinder.hpp +++ b/src/libslic3r/SLA/Rotfinder.hpp @@ -27,7 +27,7 @@ namespace sla { * * @return Returns the rotations around each axis (x, y, z) */ -std::array find_best_rotation( +Vec2d find_best_rotation( const SLAPrintObject& modelobj, float accuracy = 1.0f, std::function statuscb = [] (unsigned) {}, diff --git a/src/slic3r/GUI/Jobs/RotoptimizeJob.cpp b/src/slic3r/GUI/Jobs/RotoptimizeJob.cpp index 10c09275c3..978ccf8fcf 100644 --- a/src/slic3r/GUI/Jobs/RotoptimizeJob.cpp +++ b/src/slic3r/GUI/Jobs/RotoptimizeJob.cpp @@ -13,7 +13,7 @@ namespace Slic3r { namespace GUI { void RotoptimizeJob::process() { int obj_idx = m_plater->get_selected_object_idx(); - if (obj_idx < 0 || m_plater->sla_print().objects().size() <= obj_idx) + if (obj_idx < 0 || int(m_plater->sla_print().objects().size()) <= obj_idx) return; ModelObject *o = m_plater->model().objects[size_t(obj_idx)]; @@ -35,15 +35,12 @@ void RotoptimizeJob::process() // std::cout << "Model supportedness before: " << score << std::endl; // } - auto r = sla::find_best_rotation( - *po, - 1.f, + Vec2d r = sla::find_best_rotation(*po, 0.75f, [this](unsigned s) { if (s < 100) - update_status(int(s), - _(L("Searching for optimal orientation"))); + update_status(int(s), _(L("Searching for optimal orientation"))); }, - [this]() { return was_canceled(); }); + [this] () { return was_canceled(); }); double mindist = 6.0; // FIXME From e9a325c9ca7203161282eedfe134bd7ff0563adf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 10 Sep 2020 22:30:49 +0200 Subject: [PATCH 489/503] Fix rotation in support cubic infill --- src/libslic3r/Fill/FillAdaptive.cpp | 6 +++--- src/libslic3r/Fill/FillAdaptive.hpp | 2 +- src/libslic3r/PrintObject.cpp | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index fb8c665ebd..f1fa56c5bd 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -392,7 +392,7 @@ void FillAdaptive_Internal::Octree::propagate_point( Octree::propagate_point(point, child, (depth - 1), cubes_properties); } -std::unique_ptr FillSupportCubic::build_octree_for_adaptive_support( +std::unique_ptr FillSupportCubic::build_octree( TriangleMesh & triangle_mesh, coordf_t line_spacing, const Vec3d & cube_center, @@ -434,7 +434,7 @@ std::unique_ptr FillSupportCubic::build_octree_fo auto octree = std::make_unique(std::make_unique(cube_center), cube_center, cubes_properties); - double cube_edge_length = line_spacing; + double cube_edge_length = line_spacing / 2.0; size_t max_depth = octree->cubes_properties.size() - 1; BoundingBoxf3 mesh_bb = triangle_mesh.bounding_box(); Vec3f vertical(0, 0, 1); @@ -497,7 +497,7 @@ std::unique_ptr FillSupportCubic::build_octree_fo if(intersect_triangle(cube_center_absolute_arr, dir, vert_0, vert_1, vert_2, &distance, &cord_u, &cord_v) && distance > 0 && distance <= cube_edge_length) { Vec3d cube_center_transformed(cube_center_absolute.x(), cube_center_absolute.y(), cube_center_absolute.z() + (cube_edge_length / 2.0)); - Octree::propagate_point(cube_center_transformed, octree->root_cube.get(), max_depth, octree->cubes_properties); + Octree::propagate_point(rotation_matrix * cube_center_transformed, octree->root_cube.get(), max_depth, octree->cubes_properties); } } } diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index 45bfde8026..96f4467732 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -120,7 +120,7 @@ protected: Polylines &polylines_out); public: - static std::unique_ptr build_octree_for_adaptive_support( + static std::unique_ptr build_octree_for( TriangleMesh & triangle_mesh, coordf_t line_spacing, const Vec3d & cube_center, diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index a102c32813..2df7994eef 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -491,14 +491,14 @@ std::pair, std::unique_ptr Date: Thu, 10 Sep 2020 22:38:37 +0200 Subject: [PATCH 490/503] Fix typo in function build_octree --- src/libslic3r/Fill/FillAdaptive.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index 96f4467732..4bb80fa063 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -120,7 +120,7 @@ protected: Polylines &polylines_out); public: - static std::unique_ptr build_octree_for( + static std::unique_ptr build_octree( TriangleMesh & triangle_mesh, coordf_t line_spacing, const Vec3d & cube_center, From 137e7a0712923080ca93949e73ab139fd04c5b56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 10 Sep 2020 22:57:58 +0200 Subject: [PATCH 491/503] Fix compiler warnings and failing compilation on macOS --- src/libslic3r/Fill/FillAdaptive.cpp | 14 +++++++------- src/libslic3r/PrintObject.cpp | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index f1fa56c5bd..3b9212230d 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -435,7 +435,7 @@ std::unique_ptr FillSupportCubic::build_octree( auto octree = std::make_unique(std::make_unique(cube_center), cube_center, cubes_properties); double cube_edge_length = line_spacing / 2.0; - size_t max_depth = octree->cubes_properties.size() - 1; + int max_depth = int(octree->cubes_properties.size()) - 1; BoundingBoxf3 mesh_bb = triangle_mesh.bounding_box(); Vec3f vertical(0, 0, 1); @@ -462,13 +462,13 @@ std::unique_ptr FillSupportCubic::build_octree( Vec3d triangle_end_relative = triangle_bb.max - mesh_bb.min; Vec3crd triangle_start_idx = Vec3crd( - std::floor(triangle_start_relative.x() / cube_edge_length), - std::floor(triangle_start_relative.y() / cube_edge_length), - std::floor(triangle_start_relative.z() / cube_edge_length)); + int(std::floor(triangle_start_relative.x() / cube_edge_length)), + int(std::floor(triangle_start_relative.y() / cube_edge_length)), + int(std::floor(triangle_start_relative.z() / cube_edge_length))); Vec3crd triangle_end_idx = Vec3crd( - std::floor(triangle_end_relative.x() / cube_edge_length), - std::floor(triangle_end_relative.y() / cube_edge_length), - std::floor(triangle_end_relative.z() / cube_edge_length)); + int(std::floor(triangle_end_relative.x() / cube_edge_length)), + int(std::floor(triangle_end_relative.y() / cube_edge_length)), + int(std::floor(triangle_end_relative.z() / cube_edge_length))); for (int z = triangle_start_idx.z(); z <= triangle_end_idx.z(); ++z) { diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 2df7994eef..e2dba5bb2a 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -377,7 +377,7 @@ void PrintObject::infill() BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start"; tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), - [this, &adaptive_fill_octree, &support_fill_octree](const tbb::blocked_range& range) { + [this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { m_print->throw_if_canceled(); m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get()); From 95b918f01d2374ae4a4e60b4aa9fe528fb019262 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 11 Sep 2020 08:03:13 +0200 Subject: [PATCH 492/503] Updated Sys Info dialog, About dialog, Keyboard shortcuts dialog for gcode viewer --- .../icons/PrusaSlicer-gcodeviewer_192px.png | Bin 0 -> 11900 bytes src/slic3r/GUI/AboutDialog.cpp | 24 ++++++++++++++-- src/slic3r/GUI/GUI_App.cpp | 4 --- src/slic3r/GUI/KBShortcutsDialog.cpp | 4 +++ src/slic3r/GUI/MainFrame.cpp | 27 +++++++++--------- src/slic3r/GUI/SysInfoDialog.cpp | 24 ++++++++++++++-- 6 files changed, 62 insertions(+), 21 deletions(-) create mode 100644 resources/icons/PrusaSlicer-gcodeviewer_192px.png diff --git a/resources/icons/PrusaSlicer-gcodeviewer_192px.png b/resources/icons/PrusaSlicer-gcodeviewer_192px.png new file mode 100644 index 0000000000000000000000000000000000000000..0e02430127a8e2ca1f3ae2b4e3329c5c6b198ec9 GIT binary patch literal 11900 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 P!T~CZ8VWUUEJFSlY)lBo literal 0 HcmV?d00001 diff --git a/src/slic3r/GUI/AboutDialog.cpp b/src/slic3r/GUI/AboutDialog.cpp index f95b8d93ba..8d9ea97b98 100644 --- a/src/slic3r/GUI/AboutDialog.cpp +++ b/src/slic3r/GUI/AboutDialog.cpp @@ -37,10 +37,17 @@ void AboutDialogLogo::onRepaint(wxEvent &event) // CopyrightsDialog // ----------------------------------------- CopyrightsDialog::CopyrightsDialog() +#if ENABLE_GCODE_VIEWER + : DPIDialog(NULL, wxID_ANY, from_u8((boost::format("%1% - %2%") + % (wxGetApp().is_editor() ? SLIC3R_APP_NAME : GCODEVIEWER_APP_NAME) + % _utf8(L("Portions copyright"))).str()), + wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +#else : DPIDialog(NULL, wxID_ANY, from_u8((boost::format("%1% - %2%") % SLIC3R_APP_NAME % _utf8(L("Portions copyright"))).str()), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +#endif // ENABLE_GCODE_VIEWER { this->SetFont(wxGetApp().normal_font()); this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); @@ -201,8 +208,13 @@ void CopyrightsDialog::onCloseDialog(wxEvent &) } AboutDialog::AboutDialog() +#if ENABLE_GCODE_VIEWER + : DPIDialog(NULL, wxID_ANY, from_u8((boost::format(_utf8(L("About %s"))) % (wxGetApp().is_editor() ? SLIC3R_APP_NAME : GCODEVIEWER_APP_NAME)).str()), wxDefaultPosition, + wxDefaultSize, /*wxCAPTION*/wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +#else : DPIDialog(NULL, wxID_ANY, from_u8((boost::format(_utf8(L("About %s"))) % SLIC3R_APP_NAME).str()), wxDefaultPosition, wxDefaultSize, /*wxCAPTION*/wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +#endif // ENABLE_GCODE_VIEWER { SetFont(wxGetApp().normal_font()); @@ -214,7 +226,11 @@ AboutDialog::AboutDialog() main_sizer->Add(hsizer, 0, wxEXPAND | wxALL, 20); // logo +#if ENABLE_GCODE_VIEWER + m_logo_bitmap = ScalableBitmap(this, wxGetApp().is_editor() ? "PrusaSlicer_192px.png" : "PrusaSlicer-gcodeviewer_192px.png", 192); +#else m_logo_bitmap = ScalableBitmap(this, "PrusaSlicer_192px.png", 192); +#endif // ENABLE_GCODE_VIEWER m_logo = new wxStaticBitmap(this, wxID_ANY, m_logo_bitmap.bmp()); hsizer->Add(m_logo, 1, wxALIGN_CENTER_VERTICAL); @@ -223,7 +239,11 @@ AboutDialog::AboutDialog() // title { +#if ENABLE_GCODE_VIEWER + wxStaticText* title = new wxStaticText(this, wxID_ANY, wxGetApp().is_editor() ? SLIC3R_APP_NAME : GCODEVIEWER_APP_NAME, wxDefaultPosition, wxDefaultSize); +#else wxStaticText* title = new wxStaticText(this, wxID_ANY, SLIC3R_APP_NAME, wxDefaultPosition, wxDefaultSize); +#endif // ENABLE_GCODE_VIEWER wxFont title_font = GUI::wxGetApp().bold_font(); title_font.SetFamily(wxFONTFAMILY_ROMAN); title_font.SetPointSize(24); @@ -233,7 +253,7 @@ AboutDialog::AboutDialog() // version { - auto version_string = _(L("Version"))+ " " + std::string(SLIC3R_VERSION); + auto version_string = _L("Version")+ " " + std::string(SLIC3R_VERSION); wxStaticText* version = new wxStaticText(this, wxID_ANY, version_string.c_str(), wxDefaultPosition, wxDefaultSize); wxFont version_font = GetFont(); #ifdef __WXMSW__ @@ -294,7 +314,7 @@ AboutDialog::AboutDialog() wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCLOSE); m_copy_rights_btn_id = NewControlId(); - auto copy_rights_btn = new wxButton(this, m_copy_rights_btn_id, _(L("Portions copyright"))+dots); + auto copy_rights_btn = new wxButton(this, m_copy_rights_btn_id, _L("Portions copyright")+dots); buttons->Insert(0, copy_rights_btn, 0, wxLEFT, 5); copy_rights_btn->Bind(wxEVT_BUTTON, &AboutDialog::onCopyrightBtn, this); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index e50d4015e7..37ec10f1d2 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -161,15 +161,11 @@ static void DecorateSplashScreen(wxBitmap& bmp) memDc.DrawRectangle(banner_rect); // title -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER wxString title_string = wxGetApp().is_editor() ? SLIC3R_APP_NAME : GCODEVIEWER_APP_NAME; #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ wxString title_string = SLIC3R_APP_NAME; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ wxFont title_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); title_font.SetPointSize(24); diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 0875b76a48..4affd13269 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -33,7 +33,11 @@ namespace Slic3r { namespace GUI { KBShortcutsDialog::KBShortcutsDialog() +#if ENABLE_GCODE_VIEWER + : DPIDialog(NULL, wxID_ANY, wxString(wxGetApp().is_editor() ? SLIC3R_APP_NAME : GCODEVIEWER_APP_NAME) + " - " + _L("Keyboard Shortcuts"), +#else : DPIDialog(NULL, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _L("Keyboard Shortcuts"), +#endif // ENABLE_GCODE_VIEWER wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 4d242dec88..fbb7a190f0 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -118,11 +118,9 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S // initialize status bar m_statusbar = std::make_shared(this); m_statusbar->set_font(GUI::wxGetApp().normal_font()); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER if (wxGetApp().is_editor()) #endif // ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_statusbar->embed(this); m_statusbar->set_status_text(_L("Version") + " " + SLIC3R_VERSION + @@ -539,15 +537,11 @@ void MainFrame::update_title() title += (project + " - "); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER std::string build_id = wxGetApp().is_editor() ? SLIC3R_BUILD_ID : GCODEVIEWER_BUILD_ID; #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ std::string build_id = SLIC3R_BUILD_ID; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ size_t idx_plus = build_id.find('+'); if (idx_plus != build_id.npos) { // Parse what is behind the '+'. If there is a number, then it is a build number after the label, and full build ID is shown. @@ -562,17 +556,13 @@ void MainFrame::update_title() #endif } } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_GCODE_VIEWER title += wxString(build_id); if (wxGetApp().is_editor()) title += (" " + _L("based on Slic3r")); #else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ title += (wxString(build_id) + " " + _L("based on Slic3r")); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_GCODE_VIEWER -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ SetTitle(title); } @@ -763,6 +753,9 @@ bool MainFrame::can_change_view() const int page_id = m_tabpanel->GetSelection(); return page_id != wxNOT_FOUND && dynamic_cast(m_tabpanel->GetPage((size_t)page_id)) != nullptr; } +#if ENABLE_GCODE_VIEWER + case ESettingsLayout::GCodeViewer: { return true; } +#endif // ENABLE_GCODE_VIEWER } } @@ -889,9 +882,17 @@ static wxMenu* generate_help_menu() [](wxCommandEvent&) { Slic3r::GUI::desktop_open_datadir_folder(); }); append_menu_item(helpMenu, wxID_ANY, _(L"Report an I&ssue"), wxString::Format(_L("Report an issue on %s"), SLIC3R_APP_NAME), [](wxCommandEvent&) { wxLaunchDefaultBrowser("https://github.com/prusa3d/slic3r/issues/new"); }); - append_menu_item(helpMenu, wxID_ANY, wxString::Format(_L("&About %s"), SLIC3R_APP_NAME), _L("Show about dialog"), - [](wxCommandEvent&) { Slic3r::GUI::about(); }); - helpMenu->AppendSeparator(); +#if ENABLE_GCODE_VIEWER + if (wxGetApp().is_editor()) +#endif // ENABLE_GCODE_VIEWER + append_menu_item(helpMenu, wxID_ANY, wxString::Format(_L("&About %s"), SLIC3R_APP_NAME), _L("Show about dialog"), + [](wxCommandEvent&) { Slic3r::GUI::about(); }); +#if ENABLE_GCODE_VIEWER + else + append_menu_item(helpMenu, wxID_ANY, wxString::Format(_L("&About %s"), GCODEVIEWER_APP_NAME), _L("Show about dialog"), + [](wxCommandEvent&) { Slic3r::GUI::about(); }); +#endif // ENABLE_GCODE_VIEWER + helpMenu->AppendSeparator(); append_menu_item(helpMenu, wxID_ANY, _L("Keyboard Shortcuts") + sep + "&?", _L("Show the list of the keyboard shortcuts"), [](wxCommandEvent&) { wxGetApp().keyboard_shortcuts(); }); #if ENABLE_THUMBNAIL_GENERATOR_DEBUG diff --git a/src/slic3r/GUI/SysInfoDialog.cpp b/src/slic3r/GUI/SysInfoDialog.cpp index 7a41aca1c3..34905fa6d4 100644 --- a/src/slic3r/GUI/SysInfoDialog.cpp +++ b/src/slic3r/GUI/SysInfoDialog.cpp @@ -34,9 +34,17 @@ std::string get_main_info(bool format_as_html) std::string line_end = format_as_html ? "
" : "\n"; if (!format_as_html) +#if ENABLE_GCODE_VIEWER + out << b_start << (wxGetApp().is_editor() ? SLIC3R_APP_NAME : GCODEVIEWER_APP_NAME) << b_end << line_end; +#else out << b_start << SLIC3R_APP_NAME << b_end << line_end; +#endif // ENABLE_GCODE_VIEWER out << b_start << "Version: " << b_end << SLIC3R_VERSION << line_end; +#if ENABLE_GCODE_VIEWER + out << b_start << "Build: " << b_end << (wxGetApp().is_editor() ? SLIC3R_BUILD_ID : GCODEVIEWER_BUILD_ID) << line_end; +#else out << b_start << "Build: " << b_end << SLIC3R_BUILD_ID << line_end; +#endif // ENABLE_GCODE_VIEWER out << line_end; out << b_start << "Operating System: " << b_end << wxPlatformInfo::Get().GetOperatingSystemFamilyName() << line_end; out << b_start << "System Architecture: " << b_end << wxPlatformInfo::Get().GetArchName() << line_end; @@ -78,7 +86,11 @@ std::string get_mem_info(bool format_as_html) } SysInfoDialog::SysInfoDialog() - : DPIDialog(NULL, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _(L("System Information")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) +#if ENABLE_GCODE_VIEWER + : DPIDialog(NULL, wxID_ANY, (wxGetApp().is_editor() ? wxString(SLIC3R_APP_NAME) : wxString(GCODEVIEWER_APP_NAME)) + " - " + _L("System Information"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +#else + : DPIDialog(NULL, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _L("System Information"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) +#endif // ENABLE_GCODE_VIEWER { wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); SetBackgroundColour(bgr_clr); @@ -91,7 +103,11 @@ SysInfoDialog::SysInfoDialog() main_sizer->Add(hsizer, 1, wxEXPAND | wxALL, 10); // logo +#if ENABLE_GCODE_VIEWER + m_logo_bmp = ScalableBitmap(this, wxGetApp().is_editor() ? "PrusaSlicer_192px.png" : "PrusaSlicer-gcodeviewer_192px.png", 192); +#else m_logo_bmp = ScalableBitmap(this, "PrusaSlicer_192px.png", 192); +#endif // ENABLE_GCODE_VIEWER m_logo = new wxStaticBitmap(this, wxID_ANY, m_logo_bmp.bmp()); hsizer->Add(m_logo, 0, wxALIGN_CENTER_VERTICAL); @@ -100,7 +116,11 @@ SysInfoDialog::SysInfoDialog() // title { +#if ENABLE_GCODE_VIEWER + wxStaticText* title = new wxStaticText(this, wxID_ANY, wxGetApp().is_editor() ? SLIC3R_APP_NAME : GCODEVIEWER_APP_NAME, wxDefaultPosition, wxDefaultSize); +#else wxStaticText* title = new wxStaticText(this, wxID_ANY, SLIC3R_APP_NAME, wxDefaultPosition, wxDefaultSize); +#endif // ENABLE_GCODE_VIEWER wxFont title_font = wxGetApp().bold_font(); title_font.SetFamily(wxFONTFAMILY_ROMAN); title_font.SetPointSize(22); @@ -154,7 +174,7 @@ SysInfoDialog::SysInfoDialog() } wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxOK); - m_btn_copy_to_clipboard = new wxButton(this, wxID_ANY, _(L("Copy to Clipboard")), wxDefaultPosition, wxDefaultSize); + m_btn_copy_to_clipboard = new wxButton(this, wxID_ANY, _L("Copy to Clipboard"), wxDefaultPosition, wxDefaultSize); buttons->Insert(0, m_btn_copy_to_clipboard, 0, wxLEFT, 5); m_btn_copy_to_clipboard->Bind(wxEVT_BUTTON, &SysInfoDialog::onCopyToClipboard, this); From a57fe34a763878a4423e1aa4b8504d8fc022f0e8 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 11 Sep 2020 12:18:03 +0200 Subject: [PATCH 493/503] Updated icons for the top bar + Added icon for "Seam editing" --- resources/icons/add.svg | 31 ++++++---- resources/icons/arrange.svg | 21 +++---- resources/icons/copy.svg | 46 ++++++-------- resources/icons/delete_all.svg | 30 +++------ resources/icons/instance_add.svg | 78 ++++++++++++------------ resources/icons/instance_remove.svg | 73 ++++++++++------------ resources/icons/paste.svg | 33 +++++----- resources/icons/redo_toolbar.svg | 20 +++--- resources/icons/remove.svg | 94 +++++++++++++++++------------ resources/icons/seam.svg | 67 +++++++++----------- resources/icons/split_objects.svg | 21 ++++--- resources/icons/split_parts.svg | 20 +++--- resources/icons/undo_toolbar.svg | 20 +++--- 13 files changed, 266 insertions(+), 288 deletions(-) diff --git a/resources/icons/add.svg b/resources/icons/add.svg index 8a9b253de7..37050d7481 100644 --- a/resources/icons/add.svg +++ b/resources/icons/add.svg @@ -1,17 +1,22 @@ - + - - + + + + + + + diff --git a/resources/icons/arrange.svg b/resources/icons/arrange.svg index 4f30e979e3..62cf939e9f 100644 --- a/resources/icons/arrange.svg +++ b/resources/icons/arrange.svg @@ -1,24 +1,23 @@ - + - - + - + - + - + - + diff --git a/resources/icons/copy.svg b/resources/icons/copy.svg index 9b8430dd79..345c2590be 100644 --- a/resources/icons/copy.svg +++ b/resources/icons/copy.svg @@ -1,37 +1,29 @@ - + - - - - - + + - - - - - + + diff --git a/resources/icons/delete_all.svg b/resources/icons/delete_all.svg index 80e2e503cb..dfa9438129 100644 --- a/resources/icons/delete_all.svg +++ b/resources/icons/delete_all.svg @@ -1,31 +1,17 @@ - + - - + - + - - - - - - - - - - + diff --git a/resources/icons/instance_add.svg b/resources/icons/instance_add.svg index 5ef492cfae..a466c51dbf 100644 --- a/resources/icons/instance_add.svg +++ b/resources/icons/instance_add.svg @@ -1,50 +1,46 @@ - + - + - + - + + + + diff --git a/resources/icons/instance_remove.svg b/resources/icons/instance_remove.svg index 466752ea89..7f9b4f7e1c 100644 --- a/resources/icons/instance_remove.svg +++ b/resources/icons/instance_remove.svg @@ -1,49 +1,42 @@ - + - + - + - + diff --git a/resources/icons/paste.svg b/resources/icons/paste.svg index 028ffb8ea0..bcfe567de3 100644 --- a/resources/icons/paste.svg +++ b/resources/icons/paste.svg @@ -1,27 +1,22 @@ - + - + - - - - - + + diff --git a/resources/icons/redo_toolbar.svg b/resources/icons/redo_toolbar.svg index d005f83736..2853d4eaa8 100644 --- a/resources/icons/redo_toolbar.svg +++ b/resources/icons/redo_toolbar.svg @@ -1,13 +1,13 @@ - + - + viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve"> + diff --git a/resources/icons/remove.svg b/resources/icons/remove.svg index acd21256cd..1bb830d91c 100644 --- a/resources/icons/remove.svg +++ b/resources/icons/remove.svg @@ -1,44 +1,60 @@ - + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/seam.svg b/resources/icons/seam.svg index 119fb6afcc..a7e7980cc0 100644 --- a/resources/icons/seam.svg +++ b/resources/icons/seam.svg @@ -1,42 +1,35 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/split_objects.svg b/resources/icons/split_objects.svg index a7ccc5df86..e822fd35aa 100644 --- a/resources/icons/split_objects.svg +++ b/resources/icons/split_objects.svg @@ -1,19 +1,20 @@ - + - + - + - - + + + + diff --git a/resources/icons/split_parts.svg b/resources/icons/split_parts.svg index 82a2927706..5cfef0f330 100644 --- a/resources/icons/split_parts.svg +++ b/resources/icons/split_parts.svg @@ -1,18 +1,20 @@ - + - + - + - - + + + + diff --git a/resources/icons/undo_toolbar.svg b/resources/icons/undo_toolbar.svg index 15778a7baf..c9e277b5f1 100644 --- a/resources/icons/undo_toolbar.svg +++ b/resources/icons/undo_toolbar.svg @@ -1,13 +1,13 @@ - + - + viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve"> + From dd6994c3b2c0e23350ec674e6d37f00ad55ef3f6 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 11 Sep 2020 15:19:23 +0200 Subject: [PATCH 494/503] Logging of memory used by the gcode processor and viewer --- src/libslic3r/GCode.cpp | 17 ++-- src/libslic3r/Print.cpp | 2 +- src/slic3r/GUI/GCodeViewer.cpp | 166 +++++++++++++++++++++------------ src/slic3r/GUI/GCodeViewer.hpp | 12 ++- 4 files changed, 126 insertions(+), 71 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index cfde3a03a7..0c4e76cd7a 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -787,10 +787,12 @@ void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_ } #if ENABLE_GCODE_VIEWER + BOOST_LOG_TRIVIAL(debug) << "Start processing gcode, " << log_memory_info(); m_processor.process_file(path_tmp, [print]() { print->throw_if_canceled(); }); DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics); if (result != nullptr) *result = std::move(m_processor.extract_result()); + BOOST_LOG_TRIVIAL(debug) << "Finished processing gcode, " << log_memory_info(); #else GCodeTimeEstimator::PostProcessData normal_data = m_normal_time_estimator.get_post_process_data(); GCodeTimeEstimator::PostProcessData silent_data = m_silent_time_estimator.get_post_process_data(); @@ -2452,14 +2454,17 @@ void GCode::process_layer( #endif /* HAS_PRESSURE_EQUALIZER */ _write(file, gcode); -#if !ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER + BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z << + log_memory_info(); +#else BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z << ", time estimator memory: " << - format_memsize_MB(m_normal_time_estimator.memory_used() + (m_silent_time_estimator_enabled ? m_silent_time_estimator.memory_used() : 0)) << - ", analyzer memory: " << - format_memsize_MB(m_analyzer.memory_used()) << - log_memory_info(); -#endif // !ENABLE_GCODE_VIEWER + format_memsize_MB(m_normal_time_estimator.memory_used() + (m_silent_time_estimator_enabled ? m_silent_time_estimator.memory_used() : 0)) << + ", analyzer memory: " << + format_memsize_MB(m_analyzer.memory_used()) << + log_memory_info(); +#endif // ENABLE_GCODE_VIEWER } void GCode::apply_print_config(const PrintConfig &print_config) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index c405c764ef..50b752984b 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1583,7 +1583,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const // Slicing process, running at a background thread. void Print::process() { - BOOST_LOG_TRIVIAL(info) << "Staring the slicing process." << log_memory_info(); + BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info(); for (PrintObject *obj : m_objects) obj->make_perimeters(); this->set_status(70, L("Infilling layers")); diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 5984afaa45..76b0e02fc9 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -440,6 +440,19 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: // update buffers' render paths refresh_render_paths(false, false); + + if (Slic3r::get_logging_level() >= 5) { + long long paths_size = 0; + for (const TBuffer& buffer : m_buffers) { + paths_size += SLIC3R_STDVEC_MEMSIZE(buffer.paths, Path); + } + long long layers_zs_size = SLIC3R_STDVEC_MEMSIZE(m_layers_zs, double); + long long roles_size = SLIC3R_STDVEC_MEMSIZE(m_roles, Slic3r::ExtrusionRole); + long long extruder_ids_size = SLIC3R_STDVEC_MEMSIZE(m_extruder_ids, unsigned char); + BOOST_LOG_TRIVIAL(trace) << "Refreshed G-code extrusion paths, " + << format_memsize_MB(paths_size + layers_zs_size + roles_size + extruder_ids_size) + << log_memory_info(); + } } void GCodeViewer::reset() @@ -1209,6 +1222,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) buffer.vertices.count = buffer_vertices.size() / buffer.vertices.vertex_size_floats(); #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.vertices_gpu_size += buffer_vertices.size() * sizeof(float); + m_statistics.max_vertices_in_vertex_buffer = std::max(m_statistics.max_vertices_in_vertex_buffer, static_cast(buffer.vertices.count)); #endif // ENABLE_GCODE_VIEWER_STATISTICS glsafe(::glGenBuffers(1, &buffer.vertices.id)); @@ -1221,6 +1235,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) buffer.indices.count = buffer_indices.size(); #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.indices_gpu_size += buffer.indices.count * sizeof(unsigned int); + m_statistics.max_indices_in_index_buffer = std::max(m_statistics.max_indices_in_index_buffer, static_cast(buffer.indices.count)); #endif // ENABLE_GCODE_VIEWER_STATISTICS if (buffer.indices.count > 0) { @@ -1278,6 +1293,27 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) std::sort(m_extruder_ids.begin(), m_extruder_ids.end()); m_extruder_ids.erase(std::unique(m_extruder_ids.begin(), m_extruder_ids.end()), m_extruder_ids.end()); + if (Slic3r::get_logging_level() >= 5) { + long long vertices_size = 0; + for (size_t i = 0; i < vertices.size(); ++i) { + vertices_size += SLIC3R_STDVEC_MEMSIZE(vertices[i], float); + } + long long indices_size = 0; + for (size_t i = 0; i < indices.size(); ++i) { + indices_size += SLIC3R_STDVEC_MEMSIZE(indices[i], unsigned int); + } + long long paths_size = 0; + for (const TBuffer& buffer : m_buffers) { + paths_size += SLIC3R_STDVEC_MEMSIZE(buffer.paths, Path); + } + long long layers_zs_size = SLIC3R_STDVEC_MEMSIZE(m_layers_zs, double); + long long roles_size = SLIC3R_STDVEC_MEMSIZE(m_roles, Slic3r::ExtrusionRole); + long long extruder_ids_size = SLIC3R_STDVEC_MEMSIZE(m_extruder_ids, unsigned char); + BOOST_LOG_TRIVIAL(trace) << "Loaded G-code extrusion paths, " + << format_memsize_MB(vertices_size + indices_size + paths_size + layers_zs_size + roles_size + extruder_ids_size) + << log_memory_info(); + } + #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.load_time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start_time).count(); #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -2266,77 +2302,87 @@ void GCodeViewer::render_statistics() const ImGuiWrapper& imgui = *wxGetApp().imgui(); + auto add_time = [this, &imgui](const std::string& label, long long time) { + char buf[1024]; + sprintf(buf, "%lld ms (%s)", time, get_time_dhms(static_cast(time) * 0.001f).c_str()); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); + ImGui::SameLine(offset); + imgui.text(buf); + }; + + auto add_memory = [this, &imgui](const std::string& label, long long memory) { + static const float mb = 1024.0f * 1024.0f; + static const float inv_mb = 1.0f / mb; + static const float gb = 1024.0f * mb; + static const float inv_gb = 1.0f / gb; + char buf[1024]; + if (static_cast(memory) < gb) + sprintf(buf, "%lld bytes (%.3f MB)", memory, static_cast(memory) * inv_mb); + else + sprintf(buf, "%lld bytes (%.3f GB)", memory, static_cast(memory) * inv_gb); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); + ImGui::SameLine(offset); + imgui.text(buf); + }; + + auto add_counter = [this, &imgui](const std::string& label, long long counter) { + char buf[1024]; + sprintf(buf, "%lld", counter); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); + ImGui::SameLine(offset); + imgui.text(buf); + }; + imgui.set_next_window_pos(0.5f * wxGetApp().plater()->get_current_canvas3D()->get_canvas_size().get_width(), 0.0f, ImGuiCond_Once, 0.5f, 0.0f); + ImGui::SetNextWindowSizeConstraints({ 300, -1 }, { 600, -1 }); imgui.begin(std::string("GCodeViewer Statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize); ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("GCodeProcessor time:")); - ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.results_time) + " ms"); + if (ImGui::CollapsingHeader("Time")) { + add_time(std::string("GCodeProcessor:"), m_statistics.results_time); - ImGui::Separator(); + ImGui::Separator(); + add_time(std::string("Load:"), m_statistics.load_time); + add_time(std::string("Refresh:"), m_statistics.refresh_time); + add_time(std::string("Refresh paths:"), m_statistics.refresh_paths_time); + wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); + wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); + } - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Load time:")); - ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.load_time) + " ms"); + if (ImGui::CollapsingHeader("OpenGL calls")) { + add_counter(std::string("Multi GL_POINTS:"), m_statistics.gl_multi_points_calls_count); + add_counter(std::string("Multi GL_LINES:"), m_statistics.gl_multi_lines_calls_count); + add_counter(std::string("Multi GL_TRIANGLES:"), m_statistics.gl_multi_triangles_calls_count); + wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); + wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); + } - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Refresh time:")); - ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.refresh_time) + " ms"); + if (ImGui::CollapsingHeader("CPU memory")) { + add_memory(std::string("GCodeProcessor results:"), m_statistics.results_size); - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Refresh paths time:")); - ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.refresh_paths_time) + " ms"); + ImGui::Separator(); + add_memory(std::string("Paths:"), m_statistics.paths_size); + add_memory(std::string("Render paths:"), m_statistics.render_paths_size); + wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); + wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); + } - ImGui::Separator(); + if (ImGui::CollapsingHeader("GPU memory")) { + add_memory(std::string("Vertices:"), m_statistics.vertices_gpu_size); + add_memory(std::string("Indices:"), m_statistics.indices_gpu_size); + wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); + wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); + } - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Multi GL_POINTS calls:")); - ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.gl_multi_points_calls_count)); + if (ImGui::CollapsingHeader("Other")) { + add_counter(std::string("Travel segments count:"), m_statistics.travel_segments_count); + add_counter(std::string("Extrude segments count:"), m_statistics.extrude_segments_count); + add_counter(std::string("Max vertices in vertex buffer:"), m_statistics.max_vertices_in_vertex_buffer); + add_counter(std::string("Max indices in index buffer:"), m_statistics.max_indices_in_index_buffer); - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Multi GL_LINES calls:")); - ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.gl_multi_lines_calls_count)); - - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Multi GL_TRIANGLES calls:")); - ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.gl_multi_triangles_calls_count)); - - ImGui::Separator(); - - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("GCodeProcessor results:")); - ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.results_size) + " bytes"); - - ImGui::Separator(); - - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Paths CPU:")); - ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.paths_size) + " bytes"); - - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Render paths CPU:")); - ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.render_paths_size) + " bytes"); - - ImGui::Separator(); - - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Vertices GPU:")); - ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.vertices_gpu_size) + " bytes"); - - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Indices GPU:")); - ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.indices_gpu_size) + " bytes"); - - ImGui::Separator(); - - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Travel segments count:")); - ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.travel_segments_count)); - - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, std::string("Extrude segments count:")); - ImGui::SameLine(offset); - imgui.text(std::to_string(m_statistics.extrude_segments_count)); + wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); + wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); + } imgui.end(); } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 68fed6f334..abda780af7 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -264,7 +264,7 @@ class GCodeViewer #if ENABLE_GCODE_VIEWER_STATISTICS struct Statistics { - // times + // time long long results_time{ 0 }; long long load_time{ 0 }; long long refresh_time{ 0 }; @@ -279,15 +279,17 @@ class GCodeViewer long long indices_gpu_size{ 0 }; long long paths_size{ 0 }; long long render_paths_size{ 0 }; - // others + // other long long travel_segments_count{ 0 }; long long extrude_segments_count{ 0 }; + long long max_vertices_in_vertex_buffer{ 0 }; + long long max_indices_in_index_buffer{ 0 }; void reset_all() { reset_times(); reset_opengl(); reset_sizes(); - reset_counters(); + reset_others(); } void reset_times() { @@ -311,9 +313,11 @@ class GCodeViewer render_paths_size = 0; } - void reset_counters() { + void reset_others() { travel_segments_count = 0; extrude_segments_count = 0; + max_vertices_in_vertex_buffer = 0; + max_indices_in_index_buffer = 0; } }; #endif // ENABLE_GCODE_VIEWER_STATISTICS From 776a775996f7c85308254ea6c01919b6a989d806 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 10 Sep 2020 19:37:31 +0200 Subject: [PATCH 495/503] Add missing forward declarations --- src/libslic3r/Fill/FillAdaptive.hpp | 1 + src/slic3r/Utils/Process.hpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp index 4bb80fa063..b24f206da2 100644 --- a/src/libslic3r/Fill/FillAdaptive.hpp +++ b/src/libslic3r/Fill/FillAdaptive.hpp @@ -8,6 +8,7 @@ namespace Slic3r { class PrintObject; +class TriangleMesh; namespace FillAdaptive_Internal { diff --git a/src/slic3r/Utils/Process.hpp b/src/slic3r/Utils/Process.hpp index c6acaa643e..65f90222dd 100644 --- a/src/slic3r/Utils/Process.hpp +++ b/src/slic3r/Utils/Process.hpp @@ -2,6 +2,7 @@ #define GUI_PROCESS_HPP class wxWindow; +class wxString; namespace Slic3r { namespace GUI { From ad20e369faa7757eb0ac032df3fb9a815f2f4dcd Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 10 Sep 2020 19:37:43 +0200 Subject: [PATCH 496/503] Include PrintConfig for the definition of AuthorizationType --- src/slic3r/Utils/OctoPrint.cpp | 1 - src/slic3r/Utils/OctoPrint.hpp | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index 82d8a6bb63..fad45f8225 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -11,7 +11,6 @@ #include -#include "libslic3r/PrintConfig.hpp" #include "slic3r/GUI/I18N.hpp" #include "slic3r/GUI/GUI.hpp" #include "Http.hpp" diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index 4f8e4819fc..ed1c61bd60 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -6,6 +6,7 @@ #include #include "PrintHost.hpp" +#include "libslic3r/PrintConfig.hpp" namespace Slic3r { From a32bb59d8e77578d37c65b1052ed1d05bca85559 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sat, 12 Sep 2020 18:17:03 +0200 Subject: [PATCH 497/503] Do not include (incorrect!) seconds in get_time_dhm --- src/libslic3r/Utils.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index ef531169d1..99b105a6b1 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -348,11 +348,11 @@ inline std::string get_time_dhm(float time_in_secs) char buffer[64]; if (days > 0) - ::sprintf(buffer, "%dd %dh %dm %ds", days, hours, minutes, (int)time_in_secs); + ::sprintf(buffer, "%dd %dh %dm", days, hours, minutes); else if (hours > 0) - ::sprintf(buffer, "%dh %dm %ds", hours, minutes, (int)time_in_secs); + ::sprintf(buffer, "%dh %dm", hours, minutes); else if (minutes > 0) - ::sprintf(buffer, "%dm %ds", minutes, (int)time_in_secs); + ::sprintf(buffer, "%dm", minutes); return buffer; } From 6434f54b74cb7242fb76ac238c6258a4086f682a Mon Sep 17 00:00:00 2001 From: charlie Date: Sat, 12 Sep 2020 02:18:36 +0200 Subject: [PATCH 498/503] fix build on arch linux --- src/PrusaSlicer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 05e84b9416..a12ad8bb73 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -589,7 +589,7 @@ int CLI::run(int argc, char **argv) #if ENABLE_GCODE_VIEWER if (start_as_gcodeviewer) { if (!m_input_files.empty()) - gui->plater()->load_gcode(wxString::FromUTF8(m_input_files[0])); + gui->plater()->load_gcode(wxString::FromUTF8(m_input_files[0].c_str())); } else { #endif // ENABLE_GCODE_VIEWER_AS #if 0 From 349dd60940dc7fcd3bce127a3a88952ed13f9e0f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 14 Sep 2020 09:18:20 +0200 Subject: [PATCH 499/503] Small refactoring --- src/slic3r/GUI/GCodeViewer.cpp | 20 ++++++++++---------- src/slic3r/GUI/GCodeViewer.hpp | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 76b0e02fc9..831a3885e8 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -392,7 +392,7 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: auto start_time = std::chrono::high_resolution_clock::now(); #endif // ENABLE_GCODE_VIEWER_STATISTICS - if (m_vertices_count == 0) + if (m_moves_count == 0) return; wxBusyCursor busy; @@ -406,7 +406,7 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: // update ranges for coloring / legend m_extrusions.reset_ranges(); - for (size_t i = 0; i < m_vertices_count; ++i) { + for (size_t i = 0; i < m_moves_count; ++i) { // skip first vertex if (i == 0) continue; @@ -457,7 +457,7 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: void GCodeViewer::reset() { - m_vertices_count = 0; + m_moves_count = 0; for (TBuffer& buffer : m_buffers) { buffer.reset(); } @@ -882,11 +882,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) #endif // ENABLE_GCODE_VIEWER_STATISTICS // vertices data - m_vertices_count = gcode_result.moves.size(); - if (m_vertices_count == 0) + m_moves_count = gcode_result.moves.size(); + if (m_moves_count == 0) return; - for (size_t i = 0; i < m_vertices_count; ++i) { + for (size_t i = 0; i < m_moves_count; ++i) { const GCodeProcessor::MoveVertex& move = gcode_result.moves[i]; if (wxGetApp().is_gcode_viewer()) // for the gcode viewer we need all moves to correctly size the printbed @@ -1174,7 +1174,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) // toolpaths data -> extract from result std::vector> vertices(m_buffers.size()); std::vector> indices(m_buffers.size()); - for (size_t i = 0; i < m_vertices_count; ++i) { + for (size_t i = 0; i < m_moves_count; ++i) { // skip first vertex if (i == 0) continue; @@ -1257,7 +1257,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) #endif // ENABLE_GCODE_VIEWER_STATISTICS // layers zs / roles / extruder ids / cp color ids -> extract from result - for (size_t i = 0; i < m_vertices_count; ++i) { + for (size_t i = 0; i < m_moves_count; ++i) { const GCodeProcessor::MoveVertex& move = gcode_result.moves[i]; if (move.type == EMoveType::Extrude) m_layers_zs.emplace_back(static_cast(move.position[2])); @@ -1410,12 +1410,12 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool m_statistics.render_paths_size = 0; #endif // ENABLE_GCODE_VIEWER_STATISTICS - m_sequential_view.endpoints.first = m_vertices_count; + m_sequential_view.endpoints.first = m_moves_count; m_sequential_view.endpoints.last = 0; if (!keep_sequential_current_first) m_sequential_view.current.first = 0; if (!keep_sequential_current_last) - m_sequential_view.current.last = m_vertices_count; + m_sequential_view.current.last = m_moves_count; // first pass: collect visible paths and update sequential view data std::vector> paths; diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index abda780af7..50e41f4cf4 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -375,7 +375,7 @@ public: private: unsigned int m_last_result_id{ 0 }; - size_t m_vertices_count{ 0 }; + size_t m_moves_count{ 0 }; mutable std::vector m_buffers{ static_cast(EMoveType::Extrude) }; // bounding box of toolpaths BoundingBoxf3 m_paths_bounding_box; From 6ac19359326a01ea95744115cce1f0ebdc599d06 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 14 Sep 2020 17:25:47 +0200 Subject: [PATCH 500/503] Updated "undo/redo" and "search' icons for the toolbar * added "settings" and "search_blink" icons * suppress the icons scaling update when Plater is in the Preview mode * switched "layers_height" and "search" buttons in the toolbar --- resources/icons/redo_toolbar.svg | 20 ++++++---- resources/icons/search_.svg | 30 ++++++++++++-- resources/icons/search_blink.svg | 13 ++++++ resources/icons/settings.svg | 68 ++++++++++++++++++++++++++++++++ resources/icons/undo_toolbar.svg | 20 ++++++---- src/slic3r/GUI/GLCanvas3D.cpp | 58 +++++++++++++++------------ src/slic3r/GUI/wxExtensions.hpp | 2 +- 7 files changed, 164 insertions(+), 47 deletions(-) create mode 100644 resources/icons/search_blink.svg create mode 100644 resources/icons/settings.svg diff --git a/resources/icons/redo_toolbar.svg b/resources/icons/redo_toolbar.svg index 2853d4eaa8..d2aca2cc7d 100644 --- a/resources/icons/redo_toolbar.svg +++ b/resources/icons/redo_toolbar.svg @@ -2,12 +2,16 @@ - + + + + + + + + diff --git a/resources/icons/search_.svg b/resources/icons/search_.svg index 679bb30f71..2985ceb561 100644 --- a/resources/icons/search_.svg +++ b/resources/icons/search_.svg @@ -1,4 +1,26 @@ - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/search_blink.svg b/resources/icons/search_blink.svg new file mode 100644 index 0000000000..d005f83736 --- /dev/null +++ b/resources/icons/search_blink.svg @@ -0,0 +1,13 @@ + + + + + diff --git a/resources/icons/settings.svg b/resources/icons/settings.svg new file mode 100644 index 0000000000..db5bf458d7 --- /dev/null +++ b/resources/icons/settings.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/undo_toolbar.svg b/resources/icons/undo_toolbar.svg index c9e277b5f1..2fc25bf737 100644 --- a/resources/icons/undo_toolbar.svg +++ b/resources/icons/undo_toolbar.svg @@ -2,12 +2,16 @@ - + + + + + + + + diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 00034087c7..e0c8c4c5bd 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4999,7 +4999,7 @@ bool GLCanvas3D::_init_main_toolbar() return false; item.name = "settings"; - item.icon_filename = "cog_.svg"; + item.icon_filename = "settings.svg"; item.tooltip = _u8L("Switch to Settings") + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "2] - " + _u8L("Print Settings Tab") + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "3] - " + (m_process->current_printer_technology() == ptFFF ? _u8L("Filament Settings Tab") : _u8L("Material Settings Tab")) + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "4] - " + _u8L("Printer Settings Tab") ; @@ -5011,35 +5011,16 @@ bool GLCanvas3D::_init_main_toolbar() if (!m_main_toolbar.add_item(item)) return false; + /* if (!m_main_toolbar.add_separator()) return false; - - item.name = "layersediting"; - item.icon_filename = "layers_white.svg"; - item.tooltip = _utf8(L("Variable layer height")); - item.sprite_id = 11; - item.left.toggable = true; - item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); }; - item.visibility_callback = [this]()->bool - { - bool res = m_process->current_printer_technology() == ptFFF; - // turns off if changing printer technology - if (!res && m_main_toolbar.is_item_visible("layersediting") && m_main_toolbar.is_item_pressed("layersediting")) - force_main_toolbar_left_action(get_main_toolbar_item_id("layersediting")); - - return res; - }; - item.enabling_callback = []()->bool { return wxGetApp().plater()->can_layers_editing(); }; - if (!m_main_toolbar.add_item(item)) - return false; - - if (!m_main_toolbar.add_separator()) - return false; + */ item.name = "search"; item.icon_filename = "search_.svg"; item.tooltip = _utf8(L("Search")) + " [" + GUI::shortkey_ctrl_prefix() + "F]"; - item.sprite_id = 12; + item.sprite_id = 11; + item.left.toggable = true; item.left.render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) { @@ -5053,6 +5034,27 @@ bool GLCanvas3D::_init_main_toolbar() if (!m_main_toolbar.add_item(item)) return false; + if (!m_main_toolbar.add_separator()) + return false; + + item.name = "layersediting"; + item.icon_filename = "layers_white.svg"; + item.tooltip = _utf8(L("Variable layer height")); + item.sprite_id = 12; + item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); }; + item.visibility_callback = [this]()->bool { + bool res = m_process->current_printer_technology() == ptFFF; + // turns off if changing printer technology + if (!res && m_main_toolbar.is_item_visible("layersediting") && m_main_toolbar.is_item_pressed("layersediting")) + force_main_toolbar_left_action(get_main_toolbar_item_id("layersediting")); + + return res; + }; + item.enabling_callback = []()->bool { return wxGetApp().plater()->can_layers_editing(); }; + item.left.render_callback = GLToolbarItem::Default_Render_Callback; + if (!m_main_toolbar.add_item(item)) + return false; + return true; } @@ -5161,10 +5163,10 @@ bool GLCanvas3D::_init_undoredo_toolbar() if (!m_undoredo_toolbar.add_item(item)) return false; - + /* if (!m_undoredo_toolbar.add_separator()) return false; - + */ return true; } @@ -5553,6 +5555,10 @@ void GLCanvas3D::_render_selection_center() const void GLCanvas3D::_check_and_update_toolbar_icon_scale() const { + // Don't update a toolbar scale, when we are on a Preview + if (wxGetApp().plater()->is_preview_shown()) + return; + float scale = wxGetApp().toolbar_icon_scale(); Size cnv_size = get_canvas_size(); diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index e20e5c8bda..4f04bc5b20 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -338,7 +338,7 @@ class BlinkingBitmap : public wxStaticBitmap { public: BlinkingBitmap() {}; - BlinkingBitmap(wxWindow* parent, const std::string& icon_name = "redo_toolbar"); + BlinkingBitmap(wxWindow* parent, const std::string& icon_name = "search_blink"); ~BlinkingBitmap() {} From 067cde85f14107882327c2b29de671c914bd1153 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 14 Sep 2020 16:27:55 +0200 Subject: [PATCH 501/503] WIP Refactoring of exceptions: 1) All slicer's exceptions are now derived from Slic3r::Exception. 2) New exceptions are defined for slicing errors. 3) Exceptions are propagated to the Plater to show. It remains to modify the slicing back-end to throw the new SlicingError exceptions instead of std::runtime_error and to show the other exceptions by a message dialog instead of a notification. --- src/libslic3r/AppConfig.cpp | 3 +- src/libslic3r/BoundingBox.hpp | 5 +- src/libslic3r/Config.cpp | 15 ++-- src/libslic3r/Config.hpp | 85 +++++++++++---------- src/libslic3r/ExPolygon.cpp | 7 +- src/libslic3r/Exception.hpp | 28 +++++++ src/libslic3r/ExtrusionEntityCollection.hpp | 5 +- src/libslic3r/FileParserError.hpp | 8 +- src/libslic3r/Fill/FillBase.cpp | 3 +- src/libslic3r/Fill/FillBase.hpp | 6 +- src/libslic3r/Flow.cpp | 6 +- src/libslic3r/Flow.hpp | 7 +- src/libslic3r/Format/3mf.cpp | 17 +++-- src/libslic3r/Format/AMF.cpp | 13 ++-- src/libslic3r/Format/PRUS.cpp | 10 +-- src/libslic3r/Format/SL1.cpp | 11 +-- src/libslic3r/GCode.cpp | 21 ++--- src/libslic3r/GCode/GCodeProcessor.cpp | 10 +-- src/libslic3r/GCode/PostProcessor.cpp | 12 +-- src/libslic3r/GCode/PressureEqualizer.cpp | 8 +- src/libslic3r/GCode/PrintExtents.cpp | 2 +- src/libslic3r/GCodeSender.cpp | 2 +- src/libslic3r/GCodeTimeEstimator.cpp | 11 +-- src/libslic3r/Geometry.cpp | 3 +- src/libslic3r/MeshBoolean.cpp | 5 +- src/libslic3r/Model.cpp | 15 ++-- src/libslic3r/ModelArrange.hpp | 2 +- src/libslic3r/PlaceholderParser.cpp | 5 +- src/libslic3r/PlaceholderParser.hpp | 4 +- src/libslic3r/Polygon.cpp | 3 +- src/libslic3r/Polyline.cpp | 5 +- src/libslic3r/Preset.cpp | 19 ++--- src/libslic3r/PresetBundle.cpp | 14 ++-- src/libslic3r/Print.cpp | 5 +- src/libslic3r/PrintBase.cpp | 3 +- src/libslic3r/PrintObject.cpp | 5 +- src/libslic3r/PrintRegion.cpp | 5 +- src/libslic3r/SLAPrintSteps.cpp | 11 +-- src/libslic3r/Semver.hpp | 4 +- src/libslic3r/TriangleMesh.cpp | 5 +- src/libslic3r/Zipper.cpp | 3 +- src/slic3r/Config/Snapshot.cpp | 8 +- src/slic3r/Config/Version.cpp | 2 +- src/slic3r/GUI/3DScene.cpp | 2 +- src/slic3r/GUI/BackgroundSlicingProcess.cpp | 61 ++++++++++----- src/slic3r/GUI/BackgroundSlicingProcess.hpp | 30 ++++++++ src/slic3r/GUI/ConfigExceptions.hpp | 10 +-- src/slic3r/GUI/ConfigWizard.cpp | 2 +- src/slic3r/GUI/FirmwareDialog.cpp | 2 +- src/slic3r/GUI/GUI_App.cpp | 2 +- src/slic3r/GUI/ImGuiWrapper.cpp | 2 +- src/slic3r/GUI/OptionsGroup.cpp | 5 +- src/slic3r/GUI/Plater.cpp | 26 +++---- src/slic3r/GUI/wxExtensions.cpp | 2 +- src/slic3r/Utils/FixModelByWin10.cpp | 26 +++---- src/slic3r/Utils/Http.cpp | 2 +- src/slic3r/Utils/Serial.cpp | 8 +- src/slic3r/Utils/UndoRedo.cpp | 2 +- tests/fff_print/test_data.cpp | 2 +- 59 files changed, 356 insertions(+), 249 deletions(-) create mode 100644 src/libslic3r/Exception.hpp diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index 5892b4a30d..72c4fd0e92 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -1,6 +1,7 @@ #include "libslic3r/libslic3r.h" #include "libslic3r/Utils.hpp" #include "AppConfig.hpp" +#include "Exception.hpp" #include #include @@ -126,7 +127,7 @@ std::string AppConfig::load() // ! But to avoid the use of _utf8 (related to use of wxWidgets) // we will rethrow this exception from the place of load() call, if returned value wouldn't be empty /* - throw std::runtime_error( + throw Slic3r::RuntimeError( _utf8(L("Error parsing PrusaSlicer config file, it is probably corrupted. " "Try to manually delete the file to recover from the error. Your user profiles will not be affected.")) + "\n\n" + AppConfig::config_path() + "\n\n" + ex.what()); diff --git a/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp index 08f01d8d85..71746f32ed 100644 --- a/src/libslic3r/BoundingBox.hpp +++ b/src/libslic3r/BoundingBox.hpp @@ -2,6 +2,7 @@ #define slic3r_BoundingBox_hpp_ #include "libslic3r.h" +#include "Exception.hpp" #include "Point.hpp" #include "Polygon.hpp" @@ -22,7 +23,7 @@ public: { if (points.empty()) { this->defined = false; - // throw std::invalid_argument("Empty point set supplied to BoundingBoxBase constructor"); + // throw Slic3r::InvalidArgument("Empty point set supplied to BoundingBoxBase constructor"); } else { typename std::vector::const_iterator it = points.begin(); this->min = *it; @@ -68,7 +69,7 @@ public: BoundingBox3Base(const std::vector& points) { if (points.empty()) - throw std::invalid_argument("Empty point set supplied to BoundingBox3Base constructor"); + throw Slic3r::InvalidArgument("Empty point set supplied to BoundingBox3Base constructor"); typename std::vector::const_iterator it = points.begin(); this->min = *it; this->max = *it; diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index f3f365b478..25ef93430f 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -5,7 +5,6 @@ #include #include #include -#include // std::runtime_error #include #include #include @@ -218,7 +217,7 @@ ConfigOption* ConfigOptionDef::create_empty_option() const case coInts: return new ConfigOptionIntsNullable(); case coPercents: return new ConfigOptionPercentsNullable(); case coBools: return new ConfigOptionBoolsNullable(); - default: throw std::runtime_error(std::string("Unknown option type for nullable option ") + this->label); + default: throw Slic3r::RuntimeError(std::string("Unknown option type for nullable option ") + this->label); } } else { switch (this->type) { @@ -238,7 +237,7 @@ ConfigOption* ConfigOptionDef::create_empty_option() const case coBool: return new ConfigOptionBool(); case coBools: return new ConfigOptionBools(); case coEnum: return new ConfigOptionEnumGeneric(this->enum_keys_map); - default: throw std::runtime_error(std::string("Unknown option type for option ") + this->label); + default: throw Slic3r::RuntimeError(std::string("Unknown option type for option ") + this->label); } } } @@ -535,7 +534,7 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key) const return opt_def->ratio_over.empty() ? 0. : static_cast(raw_opt)->get_abs_value(this->get_abs_value(opt_def->ratio_over)); } - throw std::runtime_error("ConfigBase::get_abs_value(): Not a valid option type for get_abs_value()"); + throw Slic3r::RuntimeError("ConfigBase::get_abs_value(): Not a valid option type for get_abs_value()"); } // Return an absolute value of a possibly relative config variable. @@ -546,7 +545,7 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key, double rati const ConfigOption *raw_opt = this->option(opt_key); assert(raw_opt != nullptr); if (raw_opt->type() != coFloatOrPercent) - throw std::runtime_error("ConfigBase::get_abs_value(): opt_key is not of coFloatOrPercent"); + throw Slic3r::RuntimeError("ConfigBase::get_abs_value(): opt_key is not of coFloatOrPercent"); // Compute absolute value. return static_cast(raw_opt)->get_abs_value(ratio_over); } @@ -609,7 +608,7 @@ void ConfigBase::load_from_gcode_file(const std::string &file) std::getline(ifs, firstline); if (strncmp(slic3r_gcode_header, firstline.c_str(), strlen(slic3r_gcode_header)) != 0 && strncmp(prusaslicer_gcode_header, firstline.c_str(), strlen(prusaslicer_gcode_header)) != 0) - throw std::runtime_error("Not a PrusaSlicer / Slic3r PE generated g-code."); + throw Slic3r::RuntimeError("Not a PrusaSlicer / Slic3r PE generated g-code."); } ifs.seekg(0, ifs.end); auto file_length = ifs.tellg(); @@ -621,7 +620,7 @@ void ConfigBase::load_from_gcode_file(const std::string &file) size_t key_value_pairs = load_from_gcode_string(data.data()); if (key_value_pairs < 80) - throw std::runtime_error(format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs)); + throw Slic3r::RuntimeError(format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs)); } // Load the config keys from the given string. @@ -750,7 +749,7 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre throw NoDefinitionException(opt_key); const ConfigOptionDef *optdef = def->get(opt_key); if (optdef == nullptr) -// throw std::runtime_error(std::string("Invalid option name: ") + opt_key); +// throw Slic3r::RuntimeError(std::string("Invalid option name: ") + opt_key); // Let the parent decide what to do if the opt_key is not defined by this->def(). return nullptr; ConfigOption *opt = optdef->create_default_option(); diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 87e0208986..28b28b405a 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -13,6 +13,7 @@ #include #include "libslic3r.h" #include "clonable_ptr.hpp" +#include "Exception.hpp" #include "Point.hpp" #include @@ -34,31 +35,31 @@ extern bool unescape_string_cstyle(const std::string &str, std::string & extern bool unescape_strings_cstyle(const std::string &str, std::vector &out); /// Specialization of std::exception to indicate that an unknown config option has been encountered. -class UnknownOptionException : public std::runtime_error { +class UnknownOptionException : public Slic3r::RuntimeError { public: UnknownOptionException() : - std::runtime_error("Unknown option exception") {} + Slic3r::RuntimeError("Unknown option exception") {} UnknownOptionException(const std::string &opt_key) : - std::runtime_error(std::string("Unknown option exception: ") + opt_key) {} + Slic3r::RuntimeError(std::string("Unknown option exception: ") + opt_key) {} }; /// Indicate that the ConfigBase derived class does not provide config definition (the method def() returns null). -class NoDefinitionException : public std::runtime_error +class NoDefinitionException : public Slic3r::RuntimeError { public: NoDefinitionException() : - std::runtime_error("No definition exception") {} + Slic3r::RuntimeError("No definition exception") {} NoDefinitionException(const std::string &opt_key) : - std::runtime_error(std::string("No definition exception: ") + opt_key) {} + Slic3r::RuntimeError(std::string("No definition exception: ") + opt_key) {} }; /// Indicate that an unsupported accessor was called on a config option. -class BadOptionTypeException : public std::runtime_error +class BadOptionTypeException : public Slic3r::RuntimeError { public: - BadOptionTypeException() : std::runtime_error("Bad option type exception") {} - BadOptionTypeException(const std::string &message) : std::runtime_error(message) {} - BadOptionTypeException(const char* message) : std::runtime_error(message) {} + BadOptionTypeException() : Slic3r::RuntimeError("Bad option type exception") {} + BadOptionTypeException(const std::string &message) : Slic3r::RuntimeError(message) {} + BadOptionTypeException(const char* message) : Slic3r::RuntimeError(message) {} }; // Type of a configuration value. @@ -167,7 +168,7 @@ public: void set(const ConfigOption *rhs) override { if (rhs->type() != this->type()) - throw std::runtime_error("ConfigOptionSingle: Assigning an incompatible type"); + throw Slic3r::RuntimeError("ConfigOptionSingle: Assigning an incompatible type"); assert(dynamic_cast*>(rhs)); this->value = static_cast*>(rhs)->value; } @@ -175,7 +176,7 @@ public: bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) - throw std::runtime_error("ConfigOptionSingle: Comparing incompatible types"); + throw Slic3r::RuntimeError("ConfigOptionSingle: Comparing incompatible types"); assert(dynamic_cast*>(&rhs)); return this->value == static_cast*>(&rhs)->value; } @@ -239,7 +240,7 @@ public: void set(const ConfigOption *rhs) override { if (rhs->type() != this->type()) - throw std::runtime_error("ConfigOptionVector: Assigning an incompatible type"); + throw Slic3r::RuntimeError("ConfigOptionVector: Assigning an incompatible type"); assert(dynamic_cast*>(rhs)); this->values = static_cast*>(rhs)->values; } @@ -256,12 +257,12 @@ public: if (opt->type() == this->type()) { auto other = static_cast*>(opt); if (other->values.empty()) - throw std::runtime_error("ConfigOptionVector::set(): Assigning from an empty vector"); + throw Slic3r::RuntimeError("ConfigOptionVector::set(): Assigning from an empty vector"); this->values.emplace_back(other->values.front()); } else if (opt->type() == this->scalar_type()) this->values.emplace_back(static_cast*>(opt)->value); else - throw std::runtime_error("ConfigOptionVector::set():: Assigning an incompatible type"); + throw Slic3r::RuntimeError("ConfigOptionVector::set():: Assigning an incompatible type"); } } @@ -280,12 +281,12 @@ public: // Assign the first value of the rhs vector. auto other = static_cast*>(rhs); if (other->values.empty()) - throw std::runtime_error("ConfigOptionVector::set_at(): Assigning from an empty vector"); + throw Slic3r::RuntimeError("ConfigOptionVector::set_at(): Assigning from an empty vector"); this->values[i] = other->get_at(j); } else if (rhs->type() == this->scalar_type()) this->values[i] = static_cast*>(rhs)->value; else - throw std::runtime_error("ConfigOptionVector::set_at(): Assigning an incompatible type"); + throw Slic3r::RuntimeError("ConfigOptionVector::set_at(): Assigning an incompatible type"); } const T& get_at(size_t i) const @@ -310,9 +311,9 @@ public: else if (n > this->values.size()) { if (this->values.empty()) { if (opt_default == nullptr) - throw std::runtime_error("ConfigOptionVector::resize(): No default value provided."); + throw Slic3r::RuntimeError("ConfigOptionVector::resize(): No default value provided."); if (opt_default->type() != this->type()) - throw std::runtime_error("ConfigOptionVector::resize(): Extending with an incompatible type."); + throw Slic3r::RuntimeError("ConfigOptionVector::resize(): Extending with an incompatible type."); this->values.resize(n, static_cast*>(opt_default)->values.front()); } else { // Resize by duplicating the last value. @@ -329,7 +330,7 @@ public: bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) - throw std::runtime_error("ConfigOptionVector: Comparing incompatible types"); + throw Slic3r::RuntimeError("ConfigOptionVector: Comparing incompatible types"); assert(dynamic_cast*>(&rhs)); return this->values == static_cast*>(&rhs)->values; } @@ -341,9 +342,9 @@ public: // An option overrides another option if it is not nil and not equal. bool overriden_by(const ConfigOption *rhs) const override { if (this->nullable()) - throw std::runtime_error("Cannot override a nullable ConfigOption."); + throw Slic3r::RuntimeError("Cannot override a nullable ConfigOption."); if (rhs->type() != this->type()) - throw std::runtime_error("ConfigOptionVector.overriden_by() applied to different types."); + throw Slic3r::RuntimeError("ConfigOptionVector.overriden_by() applied to different types."); auto rhs_vec = static_cast*>(rhs); if (! rhs->nullable()) // Overridding a non-nullable object with another non-nullable object. @@ -361,9 +362,9 @@ public: // Apply an override option, possibly a nullable one. bool apply_override(const ConfigOption *rhs) override { if (this->nullable()) - throw std::runtime_error("Cannot override a nullable ConfigOption."); + throw Slic3r::RuntimeError("Cannot override a nullable ConfigOption."); if (rhs->type() != this->type()) - throw std::runtime_error("ConfigOptionVector.apply_override() applied to different types."); + throw Slic3r::RuntimeError("ConfigOptionVector.apply_override() applied to different types."); auto rhs_vec = static_cast*>(rhs); if (! rhs->nullable()) { // Overridding a non-nullable object with another non-nullable object. @@ -452,7 +453,7 @@ public: bool operator==(const ConfigOptionFloatsTempl &rhs) const { return vectors_equal(this->values, rhs.values); } bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) - throw std::runtime_error("ConfigOptionFloatsTempl: Comparing incompatible types"); + throw Slic3r::RuntimeError("ConfigOptionFloatsTempl: Comparing incompatible types"); assert(dynamic_cast*>(&rhs)); return vectors_equal(this->values, static_cast*>(&rhs)->values); } @@ -499,7 +500,7 @@ public: if (NULLABLE) this->values.push_back(nil_value()); else - throw std::runtime_error("Deserializing nil into a non-nullable object"); + throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object"); } else { std::istringstream iss(item_str); double value; @@ -524,9 +525,9 @@ protected: if (NULLABLE) ss << "nil"; else - throw std::runtime_error("Serializing NaN"); + throw Slic3r::RuntimeError("Serializing NaN"); } else - throw std::runtime_error("Serializing invalid number"); + throw Slic3r::RuntimeError("Serializing invalid number"); } static bool vectors_equal(const std::vector &v1, const std::vector &v2) { if (NULLABLE) { @@ -645,7 +646,7 @@ public: if (NULLABLE) this->values.push_back(nil_value()); else - throw std::runtime_error("Deserializing nil into a non-nullable object"); + throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object"); } else { std::istringstream iss(item_str); int value; @@ -662,7 +663,7 @@ private: if (NULLABLE) ss << "nil"; else - throw std::runtime_error("Serializing NaN"); + throw Slic3r::RuntimeError("Serializing NaN"); } else ss << v; } @@ -847,7 +848,7 @@ public: bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) - throw std::runtime_error("ConfigOptionFloatOrPercent: Comparing incompatible types"); + throw Slic3r::RuntimeError("ConfigOptionFloatOrPercent: Comparing incompatible types"); assert(dynamic_cast(&rhs)); return *this == *static_cast(&rhs); } @@ -858,7 +859,7 @@ public: void set(const ConfigOption *rhs) override { if (rhs->type() != this->type()) - throw std::runtime_error("ConfigOptionFloatOrPercent: Assigning an incompatible type"); + throw Slic3r::RuntimeError("ConfigOptionFloatOrPercent: Assigning an incompatible type"); assert(dynamic_cast(rhs)); *this = *static_cast(rhs); } @@ -1126,7 +1127,7 @@ public: if (NULLABLE) this->values.push_back(nil_value()); else - throw std::runtime_error("Deserializing nil into a non-nullable object"); + throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object"); } else this->values.push_back(item_str.compare("1") == 0); } @@ -1139,7 +1140,7 @@ protected: if (NULLABLE) ss << "nil"; else - throw std::runtime_error("Serializing NaN"); + throw Slic3r::RuntimeError("Serializing NaN"); } else ss << (v ? "1" : "0"); } @@ -1175,14 +1176,14 @@ public: bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) - throw std::runtime_error("ConfigOptionEnum: Comparing incompatible types"); + throw Slic3r::RuntimeError("ConfigOptionEnum: Comparing incompatible types"); // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum return this->value == (T)rhs.getInt(); } void set(const ConfigOption *rhs) override { if (rhs->type() != this->type()) - throw std::runtime_error("ConfigOptionEnum: Assigning an incompatible type"); + throw Slic3r::RuntimeError("ConfigOptionEnum: Assigning an incompatible type"); // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum this->value = (T)rhs->getInt(); } @@ -1259,14 +1260,14 @@ public: bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) - throw std::runtime_error("ConfigOptionEnumGeneric: Comparing incompatible types"); + throw Slic3r::RuntimeError("ConfigOptionEnumGeneric: Comparing incompatible types"); // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum return this->value == rhs.getInt(); } void set(const ConfigOption *rhs) override { if (rhs->type() != this->type()) - throw std::runtime_error("ConfigOptionEnumGeneric: Assigning an incompatible type"); + throw Slic3r::RuntimeError("ConfigOptionEnumGeneric: Assigning an incompatible type"); // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum this->value = rhs->getInt(); } @@ -1321,7 +1322,7 @@ public: case coInts: { auto opt = new ConfigOptionIntsNullable(); archive(*opt); return opt; } case coPercents: { auto opt = new ConfigOptionPercentsNullable();archive(*opt); return opt; } case coBools: { auto opt = new ConfigOptionBoolsNullable(); archive(*opt); return opt; } - default: throw std::runtime_error(std::string("ConfigOptionDef::load_option_from_archive(): Unknown nullable option type for option ") + this->opt_key); + default: throw Slic3r::RuntimeError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown nullable option type for option ") + this->opt_key); } } else { switch (this->type) { @@ -1340,7 +1341,7 @@ public: case coBool: { auto opt = new ConfigOptionBool(); archive(*opt); return opt; } case coBools: { auto opt = new ConfigOptionBools(); archive(*opt); return opt; } case coEnum: { auto opt = new ConfigOptionEnumGeneric(this->enum_keys_map); archive(*opt); return opt; } - default: throw std::runtime_error(std::string("ConfigOptionDef::load_option_from_archive(): Unknown option type for option ") + this->opt_key); + default: throw Slic3r::RuntimeError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown option type for option ") + this->opt_key); } } } @@ -1352,7 +1353,7 @@ public: case coInts: archive(*static_cast(opt)); break; case coPercents: archive(*static_cast(opt));break; case coBools: archive(*static_cast(opt)); break; - default: throw std::runtime_error(std::string("ConfigOptionDef::save_option_to_archive(): Unknown nullable option type for option ") + this->opt_key); + default: throw Slic3r::RuntimeError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown nullable option type for option ") + this->opt_key); } } else { switch (this->type) { @@ -1371,7 +1372,7 @@ public: case coBool: archive(*static_cast(opt)); break; case coBools: archive(*static_cast(opt)); break; case coEnum: archive(*static_cast(opt)); break; - default: throw std::runtime_error(std::string("ConfigOptionDef::save_option_to_archive(): Unknown option type for option ") + this->opt_key); + default: throw Slic3r::RuntimeError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown option type for option ") + this->opt_key); } } // Make the compiler happy, shut up the warnings. diff --git a/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp index daaab47555..5bdd5055ec 100644 --- a/src/libslic3r/ExPolygon.cpp +++ b/src/libslic3r/ExPolygon.cpp @@ -1,5 +1,6 @@ #include "BoundingBox.hpp" #include "ExPolygon.hpp" +#include "Exception.hpp" #include "Geometry.hpp" #include "Polygon.hpp" #include "Line.hpp" @@ -435,7 +436,7 @@ void ExPolygon::triangulate_pp(Polygons* polygons) const std::list output; int res = TPPLPartition().Triangulate_MONO(&input, &output); if (res != 1) - throw std::runtime_error("Triangulation failed"); + throw Slic3r::RuntimeError("Triangulation failed"); // convert output polygons for (std::list::iterator poly = output.begin(); poly != output.end(); ++poly) { @@ -548,7 +549,7 @@ void ExPolygon::triangulate_pp(Points *triangles) const int res = TPPLPartition().Triangulate_MONO(&input, &output); // int TPPLPartition::Triangulate_EC(TPPLPolyList *inpolys, TPPLPolyList *triangles) { if (res != 1) - throw std::runtime_error("Triangulation failed"); + throw Slic3r::RuntimeError("Triangulation failed"); *triangles = polypartition_output_to_triangles(output); } @@ -591,7 +592,7 @@ void ExPolygon::triangulate_p2t(Polygons* polygons) const } polygons->push_back(p); } - } catch (const std::runtime_error & /* err */) { + } catch (const Slic3r::RuntimeError & /* err */) { assert(false); // just ignore, don't triangulate } diff --git a/src/libslic3r/Exception.hpp b/src/libslic3r/Exception.hpp new file mode 100644 index 0000000000..8ec9f20c81 --- /dev/null +++ b/src/libslic3r/Exception.hpp @@ -0,0 +1,28 @@ +#ifndef _libslic3r_Exception_h_ +#define _libslic3r_Exception_h_ + +#include + +namespace Slic3r { + +// PrusaSlicer's own exception hierarchy is derived from std::runtime_error. +// Base for Slicer's own exceptions. +class Exception : public std::runtime_error { using std::runtime_error::runtime_error; }; +#define SLIC3R_DERIVE_EXCEPTION(DERIVED_EXCEPTION, PARENT_EXCEPTION) \ + class DERIVED_EXCEPTION : public PARENT_EXCEPTION { using PARENT_EXCEPTION::PARENT_EXCEPTION; } +// Critical exception produced by Slicer, such exception shall never propagate up to the UI thread. +// If that happens, an ugly fat message box with an ugly fat exclamation mark is displayed. +SLIC3R_DERIVE_EXCEPTION(CriticalException, Exception); +SLIC3R_DERIVE_EXCEPTION(RuntimeError, CriticalException); +SLIC3R_DERIVE_EXCEPTION(LogicError, CriticalException); +SLIC3R_DERIVE_EXCEPTION(InvalidArgument, LogicError); +SLIC3R_DERIVE_EXCEPTION(OutOfRange, LogicError); +SLIC3R_DERIVE_EXCEPTION(IOError, CriticalException); +SLIC3R_DERIVE_EXCEPTION(FileIOError, IOError); +// Runtime exception produced by Slicer. Such exception cancels the slicing process and it shall be shown in notifications. +SLIC3R_DERIVE_EXCEPTION(SlicingError, Exception); +#undef SLIC3R_DERIVE_EXCEPTION + +} // namespace Slic3r + +#endif // _libslic3r_Exception_h_ diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp index dfece6949b..5e40ab32ec 100644 --- a/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/src/libslic3r/ExtrusionEntityCollection.hpp @@ -2,6 +2,7 @@ #define slic3r_ExtrusionEntityCollection_hpp_ #include "libslic3r.h" +#include "Exception.hpp" #include "ExtrusionEntity.hpp" namespace Slic3r { @@ -107,7 +108,7 @@ public: // Following methods shall never be called on an ExtrusionEntityCollection. Polyline as_polyline() const override { - throw std::runtime_error("Calling as_polyline() on a ExtrusionEntityCollection"); + throw Slic3r::RuntimeError("Calling as_polyline() on a ExtrusionEntityCollection"); return Polyline(); }; @@ -117,7 +118,7 @@ public: } double length() const override { - throw std::runtime_error("Calling length() on a ExtrusionEntityCollection"); + throw Slic3r::RuntimeError("Calling length() on a ExtrusionEntityCollection"); return 0.; } }; diff --git a/src/libslic3r/FileParserError.hpp b/src/libslic3r/FileParserError.hpp index 3f560fa4f5..b7e63d84e0 100644 --- a/src/libslic3r/FileParserError.hpp +++ b/src/libslic3r/FileParserError.hpp @@ -10,14 +10,14 @@ namespace Slic3r { // Generic file parser error, mostly copied from boost::property_tree::file_parser_error -class file_parser_error: public std::runtime_error +class file_parser_error: public Slic3r::RuntimeError { public: file_parser_error(const std::string &msg, const std::string &file, unsigned long line = 0) : - std::runtime_error(format_what(msg, file, line)), + Slic3r::RuntimeError(format_what(msg, file, line)), m_message(msg), m_filename(file), m_line(line) {} file_parser_error(const std::string &msg, const boost::filesystem::path &file, unsigned long line = 0) : - std::runtime_error(format_what(msg, file.string(), line)), + Slic3r::RuntimeError(format_what(msg, file.string(), line)), m_message(msg), m_filename(file.string()), m_line(line) {} // gcc 3.4.2 complains about lack of throw specifier on compiler // generated dtor @@ -35,7 +35,7 @@ private: std::string m_filename; unsigned long m_line; - // Format error message to be returned by std::runtime_error::what() + // Format error message to be returned by Slic3r::RuntimeError::what() static std::string format_what(const std::string &msg, const std::string &file, unsigned long l) { std::stringstream stream; diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index 9001330aae..b0319efded 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -2,6 +2,7 @@ #include "../ClipperUtils.hpp" #include "../EdgeGrid.hpp" +#include "../Exception.hpp" #include "../Geometry.hpp" #include "../Surface.hpp" #include "../PrintConfig.hpp" @@ -40,7 +41,7 @@ Fill* Fill::new_from_type(const InfillPattern type) case ipOctagramSpiral: return new FillOctagramSpiral(); case ipAdaptiveCubic: return new FillAdaptive(); case ipSupportCubic: return new FillSupportCubic(); - default: throw std::invalid_argument("unknown type"); + default: throw Slic3r::InvalidArgument("unknown type"); } } diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index dd887b8c3e..77620e1181 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -11,6 +11,7 @@ #include "../libslic3r.h" #include "../BoundingBox.hpp" +#include "../Exception.hpp" #include "../Utils.hpp" namespace Slic3r { @@ -23,9 +24,10 @@ namespace FillAdaptive_Internal { struct Octree; }; -class InfillFailedException : public std::runtime_error { +// Infill shall never fail, therefore the error is classified as RuntimeError, not SlicingError. +class InfillFailedException : public Slic3r::RuntimeError { public: - InfillFailedException() : std::runtime_error("Infill failed") {} + InfillFailedException() : Slic3r::RuntimeError("Infill failed") {} }; struct FillParams diff --git a/src/libslic3r/Flow.cpp b/src/libslic3r/Flow.cpp index 1678be999c..e5dcf07310 100644 --- a/src/libslic3r/Flow.cpp +++ b/src/libslic3r/Flow.cpp @@ -53,7 +53,7 @@ static inline FlowRole opt_key_to_flow_role(const std::string &opt_key) else if (opt_key == "support_material_extrusion_width") return frSupportMaterial; else - throw std::runtime_error("opt_key_to_flow_role: invalid argument"); + throw Slic3r::RuntimeError("opt_key_to_flow_role: invalid argument"); }; static inline void throw_on_missing_variable(const std::string &opt_key, const char *dependent_opt_key) @@ -126,7 +126,7 @@ Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent { // we need layer height unless it's a bridge if (height <= 0 && bridge_flow_ratio == 0) - throw std::invalid_argument("Invalid flow height supplied to new_from_config_width()"); + throw Slic3r::InvalidArgument("Invalid flow height supplied to new_from_config_width()"); float w; if (bridge_flow_ratio > 0) { @@ -151,7 +151,7 @@ Flow Flow::new_from_spacing(float spacing, float nozzle_diameter, float height, { // we need layer height unless it's a bridge if (height <= 0 && !bridge) - throw std::invalid_argument("Invalid flow height supplied to new_from_spacing()"); + throw Slic3r::InvalidArgument("Invalid flow height supplied to new_from_spacing()"); // Calculate width from spacing. // For normal extrusons, extrusion width is wider than the spacing due to the rounding and squishing of the extrusions. // For bridge extrusions, the extrusions are placed with a tiny BRIDGE_EXTRA_SPACING gaps between the threads. diff --git a/src/libslic3r/Flow.hpp b/src/libslic3r/Flow.hpp index 7d6e35873d..9e57ce9079 100644 --- a/src/libslic3r/Flow.hpp +++ b/src/libslic3r/Flow.hpp @@ -3,6 +3,7 @@ #include "libslic3r.h" #include "Config.hpp" +#include "Exception.hpp" #include "ExtrusionEntity.hpp" namespace Slic3r { @@ -27,11 +28,11 @@ enum FlowRole { frSupportMaterialInterface, }; -class FlowError : public std::invalid_argument +class FlowError : public Slic3r::InvalidArgument { public: - FlowError(const std::string& what_arg) : invalid_argument(what_arg) {} - FlowError(const char* what_arg) : invalid_argument(what_arg) {} + FlowError(const std::string& what_arg) : Slic3r::InvalidArgument(what_arg) {} + FlowError(const char* what_arg) : Slic3r::InvalidArgument(what_arg) {} }; class FlowErrorNegativeSpacing : public FlowError diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 66dd0049cf..46a6c02af1 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -1,4 +1,5 @@ #include "../libslic3r.h" +#include "../Exception.hpp" #include "../Model.hpp" #include "../Utils.hpp" #include "../GCode.hpp" @@ -123,11 +124,11 @@ const char* INVALID_OBJECT_TYPES[] = "other" }; -class version_error : public std::runtime_error +class version_error : public Slic3r::FileIOError { public: - version_error(const std::string& what_arg) : std::runtime_error(what_arg) {} - version_error(const char* what_arg) : std::runtime_error(what_arg) {} + version_error(const std::string& what_arg) : Slic3r::FileIOError(what_arg) {} + version_error(const char* what_arg) : Slic3r::FileIOError(what_arg) {} }; const char* get_attribute_value_charptr(const char** attributes, unsigned int attributes_size, const char* attribute_key) @@ -607,7 +608,7 @@ namespace Slic3r { { // ensure the zip archive is closed and rethrow the exception close_zip_reader(&archive); - throw std::runtime_error(e.what()); + throw Slic3r::FileIOError(e.what()); } } } @@ -780,7 +781,7 @@ namespace Slic3r { { char error_buf[1024]; ::sprintf(error_buf, "Error (%s) while parsing '%s' at line %d", XML_ErrorString(XML_GetErrorCode(data->parser)), data->stat.m_filename, (int)XML_GetCurrentLineNumber(data->parser)); - throw std::runtime_error(error_buf); + throw Slic3r::FileIOError(error_buf); } return n; @@ -789,7 +790,7 @@ namespace Slic3r { catch (const version_error& e) { // rethrow the exception - throw std::runtime_error(e.what()); + throw Slic3r::FileIOError(e.what()); } catch (std::exception& e) { @@ -2360,9 +2361,9 @@ namespace Slic3r { continue; if (!volume->mesh().repaired) - throw std::runtime_error("store_3mf() requires repair()"); + throw Slic3r::FileIOError("store_3mf() requires repair()"); if (!volume->mesh().has_shared_vertices()) - throw std::runtime_error("store_3mf() requires shared vertices"); + throw Slic3r::FileIOError("store_3mf() requires shared vertices"); volumes_offsets.insert(VolumeToOffsetsMap::value_type(volume, Offsets(vertices_count))).first; diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index af7b9b1b60..1a706afa92 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -7,6 +7,7 @@ #include #include "../libslic3r.h" +#include "../Exception.hpp" #include "../Model.hpp" #include "../GCode.hpp" #include "../PrintConfig.hpp" @@ -923,7 +924,7 @@ bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_fi { char error_buf[1024]; ::sprintf(error_buf, "Error (%s) while parsing '%s' at line %d", XML_ErrorString(XML_GetErrorCode(data->parser)), data->stat.m_filename, (int)XML_GetCurrentLineNumber(data->parser)); - throw std::runtime_error(error_buf); + throw Slic3r::FileIOError(error_buf); } return n; @@ -948,9 +949,9 @@ bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_fi if (check_version && (ctx.m_version > VERSION_AMF_COMPATIBLE)) { // std::string msg = _(L("The selected amf file has been saved with a newer version of " + std::string(SLIC3R_APP_NAME) + " and is not compatible.")); - // throw std::runtime_error(msg.c_str()); + // throw Slic3r::FileIOError(msg.c_str()); const std::string msg = (boost::format(_(L("The selected amf file has been saved with a newer version of %1% and is not compatible."))) % std::string(SLIC3R_APP_NAME)).str(); - throw std::runtime_error(msg); + throw Slic3r::FileIOError(msg); } return true; @@ -994,7 +995,7 @@ bool load_amf_archive(const char* path, DynamicPrintConfig* config, Model* model { // ensure the zip archive is closed and rethrow the exception close_zip_reader(&archive); - throw std::runtime_error(e.what()); + throw Slic3r::FileIOError(e.what()); } break; @@ -1147,9 +1148,9 @@ bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config, for (ModelVolume *volume : object->volumes) { vertices_offsets.push_back(num_vertices); if (! volume->mesh().repaired) - throw std::runtime_error("store_amf() requires repair()"); + throw Slic3r::FileIOError("store_amf() requires repair()"); if (! volume->mesh().has_shared_vertices()) - throw std::runtime_error("store_amf() requires shared vertices"); + throw Slic3r::FileIOError("store_amf() requires shared vertices"); const indexed_triangle_set &its = volume->mesh().its; const Transform3d& matrix = volume->get_matrix(); for (size_t i = 0; i < its.vertices.size(); ++i) { diff --git a/src/libslic3r/Format/PRUS.cpp b/src/libslic3r/Format/PRUS.cpp index d6f87197df..e2c38d9576 100644 --- a/src/libslic3r/Format/PRUS.cpp +++ b/src/libslic3r/Format/PRUS.cpp @@ -147,7 +147,7 @@ static void extract_model_from_archive( } } if (! trafo_set) - throw std::runtime_error(std::string("Archive ") + path + " does not contain a valid entry in scene.xml for " + name); + throw Slic3r::FileIOError(std::string("Archive ") + path + " does not contain a valid entry in scene.xml for " + name); // Extract the STL. StlHeader header; @@ -266,7 +266,7 @@ static void extract_model_from_archive( } if (! mesh_valid) - throw std::runtime_error(std::string("Archive ") + path + " does not contain a valid mesh for " + name); + throw Slic3r::FileIOError(std::string("Archive ") + path + " does not contain a valid mesh for " + name); // Add this mesh to the model. ModelVolume *volume = nullptr; @@ -303,7 +303,7 @@ bool load_prus(const char *path, Model *model) mz_bool res = MZ_FALSE; try { if (!open_zip_reader(&archive, path)) - throw std::runtime_error(std::string("Unable to init zip reader for ") + path); + throw Slic3r::FileIOError(std::string("Unable to init zip reader for ") + path); std::vector scene_xml_data; // For grouping multiple STLs into a single ModelObject for multi-material prints. std::map group_to_model_object; @@ -316,10 +316,10 @@ bool load_prus(const char *path, Model *model) buffer.assign((size_t)stat.m_uncomp_size, 0); res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (char*)buffer.data(), (size_t)stat.m_uncomp_size, 0); if (res == MZ_FALSE) - std::runtime_error(std::string("Error while extracting a file from ") + path); + throw Slic3r::FileIOError(std::string("Error while extracting a file from ") + path); if (strcmp(stat.m_filename, "scene.xml") == 0) { if (! scene_xml_data.empty()) - throw std::runtime_error(std::string("Multiple scene.xml were found in the archive.") + path); + throw Slic3r::FileIOError(std::string("Multiple scene.xml were found in the archive.") + path); scene_xml_data = std::move(buffer); } else if (boost::iends_with(stat.m_filename, ".stl")) { // May throw std::exception diff --git a/src/libslic3r/Format/SL1.cpp b/src/libslic3r/Format/SL1.cpp index ff1af5d8b1..274f84f002 100644 --- a/src/libslic3r/Format/SL1.cpp +++ b/src/libslic3r/Format/SL1.cpp @@ -10,6 +10,7 @@ #include +#include "libslic3r/Exception.hpp" #include "libslic3r/SlicesToTriangleMesh.hpp" #include "libslic3r/MarchingSquares.hpp" #include "libslic3r/ClipperUtils.hpp" @@ -64,7 +65,7 @@ boost::property_tree::ptree read_ini(const mz_zip_archive_file_stat &entry, if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename, buf.data(), buf.size(), 0)) - throw std::runtime_error(zip.get_errorstr()); + throw Slic3r::FileIOError(zip.get_errorstr()); boost::property_tree::ptree tree; std::stringstream ss(buf); @@ -80,7 +81,7 @@ PNGBuffer read_png(const mz_zip_archive_file_stat &entry, if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename, buf.data(), buf.size(), 0)) - throw std::runtime_error(zip.get_errorstr()); + throw Slic3r::FileIOError(zip.get_errorstr()); return {std::move(buf), (name.empty() ? entry.m_filename : name)}; } @@ -94,7 +95,7 @@ ArchiveData extract_sla_archive(const std::string &zipfname, struct Arch: public MZ_Archive { Arch(const std::string &fname) { if (!open_zip_reader(&arch, fname)) - throw std::runtime_error(get_errorstr()); + throw Slic3r::FileIOError(get_errorstr()); } ~Arch() { close_zip_reader(&arch); } @@ -202,7 +203,7 @@ RasterParams get_raster_params(const DynamicPrintConfig &cfg) if (!opt_disp_cols || !opt_disp_rows || !opt_disp_w || !opt_disp_h || !opt_mirror_x || !opt_mirror_y || !opt_orient) - throw std::runtime_error("Invalid SL1 file"); + throw Slic3r::FileIOError("Invalid SL1 file"); RasterParams rstp; @@ -228,7 +229,7 @@ SliceParams get_slice_params(const DynamicPrintConfig &cfg) auto *opt_init_layerh = cfg.option("initial_layer_height"); if (!opt_layerh || !opt_init_layerh) - throw std::runtime_error("Invalid SL1 file"); + throw Slic3r::FileIOError("Invalid SL1 file"); return SliceParams{opt_layerh->getFloat(), opt_init_layerh->getFloat()}; } diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 0c4e76cd7a..1788250f8a 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1,6 +1,7 @@ #include "libslic3r.h" #include "I18N.hpp" #include "GCode.hpp" +#include "Exception.hpp" #include "ExtrusionEntity.hpp" #include "EdgeGrid.hpp" #include "Geometry.hpp" @@ -286,7 +287,7 @@ namespace Slic3r { std::string WipeTowerIntegration::append_tcr(GCode& gcodegen, const WipeTower::ToolChangeResult& tcr, int new_extruder_id, double z) const { if (new_extruder_id != -1 && new_extruder_id != tcr.new_tool) - throw std::invalid_argument("Error: WipeTowerIntegration::append_tcr was asked to do a toolchange it didn't expect."); + throw Slic3r::InvalidArgument("Error: WipeTowerIntegration::append_tcr was asked to do a toolchange it didn't expect."); std::string gcode; @@ -539,7 +540,7 @@ namespace Slic3r { if (!m_brim_done || gcodegen.writer().need_toolchange(extruder_id) || finish_layer) { if (m_layer_idx < (int)m_tool_changes.size()) { if (!(size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size())) - throw std::runtime_error("Wipe tower generation failed, possibly due to empty first layer."); + throw Slic3r::RuntimeError("Wipe tower generation failed, possibly due to empty first layer."); // Calculate where the wipe tower layer will be printed. -1 means that print z will not change, @@ -628,7 +629,7 @@ std::vector GCode::collect_layers_to_print(const PrintObjec // Check that there are extrusions on the very first layer. if (layers_to_print.size() == 1u) { if (!has_extrusions) - throw std::runtime_error(_(L("There is an object with no extrusions on the first layer."))); + throw Slic3r::RuntimeError(_(L("There is an object with no extrusions on the first layer."))); } // In case there are extrusions on this layer, check there is a layer to lay it on. @@ -749,7 +750,7 @@ void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_ FILE *file = boost::nowide::fopen(path_tmp.c_str(), "wb"); if (file == nullptr) - throw std::runtime_error(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n"); + throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n"); #if !ENABLE_GCODE_VIEWER m_enable_analyzer = preview_data != nullptr; @@ -762,7 +763,7 @@ void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_ if (ferror(file)) { fclose(file); boost::nowide::remove(path_tmp.c_str()); - throw std::runtime_error(std::string("G-code export to ") + path + " failed\nIs the disk full?\n"); + throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed\nIs the disk full?\n"); } } catch (std::exception & /* ex */) { // Rethrow on any exception. std::runtime_exception and CanceledException are expected to be thrown. @@ -783,7 +784,7 @@ void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_ msg += " !!!!! Failed to process the custom G-code template ...\n"; msg += "and\n"; msg += " !!!!! End of an error report for the custom G-code template ...\n"; - throw std::runtime_error(msg); + throw Slic3r::RuntimeError(msg); } #if ENABLE_GCODE_VIEWER @@ -817,7 +818,7 @@ void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_ #endif // ENABLE_GCODE_VIEWER if (rename_file(path_tmp, path)) - throw std::runtime_error( + throw Slic3r::RuntimeError( std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' + "Is " + path_tmp + " locked?" + '\n'); @@ -3006,7 +3007,7 @@ std::string GCode::extrude_entity(const ExtrusionEntity &entity, std::string des else if (const ExtrusionLoop* loop = dynamic_cast(&entity)) return this->extrude_loop(*loop, description, speed, lower_layer_edge_grid); else - throw std::invalid_argument("Invalid argument supplied to extrude()"); + throw Slic3r::InvalidArgument("Invalid argument supplied to extrude()"); return ""; } @@ -3211,7 +3212,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } else if (path.role() == erGapFill) { speed = m_config.get_abs_value("gap_fill_speed"); } else { - throw std::invalid_argument("Invalid speed"); + throw Slic3r::InvalidArgument("Invalid speed"); } } if (this->on_first_layer()) @@ -3632,7 +3633,7 @@ void GCode::ObjectByExtruder::Island::Region::append(const Type type, const Extr perimeters_or_infills_overrides = &infills_overrides; break; default: - throw std::invalid_argument("Unknown parameter!"); + throw Slic3r::InvalidArgument("Unknown parameter!"); } // First we append the entities, there are eec->entities.size() of them: diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index db69f4f0ba..0a5617559d 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -319,13 +319,13 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) { boost::nowide::ifstream in(filename); if (!in.good()) - throw std::runtime_error(std::string("Time estimator post process export failed.\nCannot open file for reading.\n")); + throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for reading.\n")); // temporary file to contain modified gcode std::string out_path = filename + ".postprocess"; FILE* out = boost::nowide::fopen(out_path.c_str(), "wb"); if (out == nullptr) - throw std::runtime_error(std::string("Time estimator post process export failed.\nCannot open file for writing.\n")); + throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for writing.\n")); auto time_in_minutes = [](float time_in_seconds) { return int(::roundf(time_in_seconds / 60.0f)); @@ -418,7 +418,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) in.close(); fclose(out); boost::nowide::remove(out_path.c_str()); - throw std::runtime_error(std::string("Time estimator post process export failed.\nIs the disk full?\n")); + throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nIs the disk full?\n")); } export_line.clear(); }; @@ -426,7 +426,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) while (std::getline(in, gcode_line)) { if (!in.good()) { fclose(out); - throw std::runtime_error(std::string("Time estimator post process export failed.\nError while reading from file.\n")); + throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nError while reading from file.\n")); } gcode_line += "\n"; @@ -460,7 +460,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) in.close(); if (rename_file(out_path, filename)) - throw std::runtime_error(std::string("Failed to rename the output G-code file from ") + out_path + " to " + filename + '\n' + + throw Slic3r::RuntimeError(std::string("Failed to rename the output G-code file from ") + out_path + " to " + filename + '\n' + "Is " + out_path + " locked?" + '\n'); } diff --git a/src/libslic3r/GCode/PostProcessor.cpp b/src/libslic3r/GCode/PostProcessor.cpp index 25982959be..17aa76fb9d 100644 --- a/src/libslic3r/GCode/PostProcessor.cpp +++ b/src/libslic3r/GCode/PostProcessor.cpp @@ -79,7 +79,7 @@ static DWORD execute_process_winapi(const std::wstring &command_line) if (! ::CreateProcessW( nullptr /* lpApplicationName */, (LPWSTR)command_line.c_str(), nullptr /* lpProcessAttributes */, nullptr /* lpThreadAttributes */, false /* bInheritHandles */, CREATE_UNICODE_ENVIRONMENT /* | CREATE_NEW_CONSOLE */ /* dwCreationFlags */, (LPVOID)envstr.c_str(), nullptr /* lpCurrentDirectory */, &startup_info, &process_info)) - throw std::runtime_error(std::string("Failed starting the script ") + boost::nowide::narrow(command_line) + ", Win32 error: " + std::to_string(int(::GetLastError()))); + throw Slic3r::RuntimeError(std::string("Failed starting the script ") + boost::nowide::narrow(command_line) + ", Win32 error: " + std::to_string(int(::GetLastError()))); ::WaitForSingleObject(process_info.hProcess, INFINITE); ULONG rc = 0; ::GetExitCodeProcess(process_info.hProcess, &rc); @@ -98,13 +98,13 @@ static int run_script(const std::string &script, const std::string &gcode, std:: LPWSTR *szArglist = CommandLineToArgvW(boost::nowide::widen(script).c_str(), &nArgs); if (szArglist == nullptr || nArgs <= 0) { // CommandLineToArgvW failed. Maybe the command line escapment is invalid? - throw std::runtime_error(std::string("Post processing script ") + script + " on file " + gcode + " failed. CommandLineToArgvW() refused to parse the command line path."); + throw Slic3r::RuntimeError(std::string("Post processing script ") + script + " on file " + gcode + " failed. CommandLineToArgvW() refused to parse the command line path."); } std::wstring command_line; std::wstring command = szArglist[0]; if (! boost::filesystem::exists(boost::filesystem::path(command))) - throw std::runtime_error(std::string("The configured post-processing script does not exist: ") + boost::nowide::narrow(command)); + throw Slic3r::RuntimeError(std::string("The configured post-processing script does not exist: ") + boost::nowide::narrow(command)); if (boost::iends_with(command, L".pl")) { // This is a perl script. Run it through the perl interpreter. // The current process may be slic3r.exe or slic3r-console.exe. @@ -115,7 +115,7 @@ static int run_script(const std::string &script, const std::string &gcode, std:: boost::filesystem::path path_perl = path_exe.parent_path() / "perl" / "perl.exe"; if (! boost::filesystem::exists(path_perl)) { LocalFree(szArglist); - throw std::runtime_error(std::string("Perl interpreter ") + path_perl.string() + " does not exist."); + throw Slic3r::RuntimeError(std::string("Perl interpreter ") + path_perl.string() + " does not exist."); } // Replace it with the current perl interpreter. quote_argv_winapi(boost::nowide::widen(path_perl.string()), command_line); @@ -187,7 +187,7 @@ void run_post_process_scripts(const std::string &path, const PrintConfig &config config.setenv_(); auto gcode_file = boost::filesystem::path(path); if (! boost::filesystem::exists(gcode_file)) - throw std::runtime_error(std::string("Post-processor can't find exported gcode file")); + throw Slic3r::RuntimeError(std::string("Post-processor can't find exported gcode file")); for (const std::string &scripts : config.post_process.values) { std::vector lines; @@ -205,7 +205,7 @@ void run_post_process_scripts(const std::string &path, const PrintConfig &config const std::string msg = std_err.empty() ? (boost::format("Post-processing script %1% on file %2% failed.\nError code: %3%") % script % path % result).str() : (boost::format("Post-processing script %1% on file %2% failed.\nError code: %3%\nOutput:\n%4%") % script % path % result % std_err).str(); BOOST_LOG_TRIVIAL(error) << msg; - throw std::runtime_error(msg); + throw Slic3r::RuntimeError(msg); } } } diff --git a/src/libslic3r/GCode/PressureEqualizer.cpp b/src/libslic3r/GCode/PressureEqualizer.cpp index 3b2a58a884..33601e5e9f 100644 --- a/src/libslic3r/GCode/PressureEqualizer.cpp +++ b/src/libslic3r/GCode/PressureEqualizer.cpp @@ -148,7 +148,7 @@ static inline int parse_int(const char *&line) char *endptr = NULL; long result = strtol(line, &endptr, 10); if (endptr == NULL || !is_ws_or_eol(*endptr)) - throw std::runtime_error("PressureEqualizer: Error parsing an int"); + throw Slic3r::RuntimeError("PressureEqualizer: Error parsing an int"); line = endptr; return int(result); }; @@ -160,7 +160,7 @@ static inline float parse_float(const char *&line) char *endptr = NULL; float result = strtof(line, &endptr); if (endptr == NULL || !is_ws_or_eol(*endptr)) - throw std::runtime_error("PressureEqualizer: Error parsing a float"); + throw Slic3r::RuntimeError("PressureEqualizer: Error parsing a float"); line = endptr; return result; }; @@ -229,7 +229,7 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi assert(false); } if (i == -1) - throw std::runtime_error(std::string("GCode::PressureEqualizer: Invalid axis for G0/G1: ") + axis); + throw Slic3r::RuntimeError(std::string("GCode::PressureEqualizer: Invalid axis for G0/G1: ") + axis); buf.pos_provided[i] = true; new_pos[i] = parse_float(line); if (i == 3 && m_config->use_relative_e_distances.value) @@ -298,7 +298,7 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi set = true; break; default: - throw std::runtime_error(std::string("GCode::PressureEqualizer: Incorrect axis in a G92 G-code: ") + axis); + throw Slic3r::RuntimeError(std::string("GCode::PressureEqualizer: Incorrect axis in a G92 G-code: ") + axis); } eatws(line); } diff --git a/src/libslic3r/GCode/PrintExtents.cpp b/src/libslic3r/GCode/PrintExtents.cpp index 4a6624531b..a86411519f 100644 --- a/src/libslic3r/GCode/PrintExtents.cpp +++ b/src/libslic3r/GCode/PrintExtents.cpp @@ -94,7 +94,7 @@ static BoundingBoxf extrusionentity_extents(const ExtrusionEntity *extrusion_ent auto *extrusion_entity_collection = dynamic_cast(extrusion_entity); if (extrusion_entity_collection != nullptr) return extrusionentity_extents(*extrusion_entity_collection); - throw std::runtime_error("Unexpected extrusion_entity type in extrusionentity_extents()"); + throw Slic3r::RuntimeError("Unexpected extrusion_entity type in extrusionentity_extents()"); return BoundingBoxf(); } diff --git a/src/libslic3r/GCodeSender.cpp b/src/libslic3r/GCodeSender.cpp index 9567e07d28..7bda299923 100644 --- a/src/libslic3r/GCodeSender.cpp +++ b/src/libslic3r/GCodeSender.cpp @@ -153,7 +153,7 @@ GCodeSender::set_baud_rate(unsigned int baud_rate) if (::tcsetattr(handle, TCSAFLUSH, &ios) != 0) printf("Failed to set baud rate: %s\n", strerror(errno)); #else - //throw invalid_argument ("OS does not currently support custom bauds"); + //throw Slic3r::InvalidArgument("OS does not currently support custom bauds"); #endif } } diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp index aa9ee2f643..a3e20ca2f4 100644 --- a/src/libslic3r/GCodeTimeEstimator.cpp +++ b/src/libslic3r/GCodeTimeEstimator.cpp @@ -1,3 +1,4 @@ +#include "Exception.hpp" #include "GCodeTimeEstimator.hpp" #include "Utils.hpp" #include @@ -254,13 +255,13 @@ namespace Slic3r { { boost::nowide::ifstream in(filename); if (!in.good()) - throw std::runtime_error(std::string("Time estimator post process export failed.\nCannot open file for reading.\n")); + throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for reading.\n")); std::string path_tmp = filename + ".postprocess"; FILE* out = boost::nowide::fopen(path_tmp.c_str(), "wb"); if (out == nullptr) - throw std::runtime_error(std::string("Time estimator post process export failed.\nCannot open file for writing.\n")); + throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for writing.\n")); std::string normal_time_mask = "M73 P%s R%s\n"; std::string silent_time_mask = "M73 Q%s S%s\n"; @@ -278,7 +279,7 @@ namespace Slic3r { in.close(); fclose(out); boost::nowide::remove(path_tmp.c_str()); - throw std::runtime_error(std::string("Time estimator post process export failed.\nIs the disk full?\n")); + throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nIs the disk full?\n")); } export_line.clear(); }; @@ -326,7 +327,7 @@ namespace Slic3r { if (!in.good()) { fclose(out); - throw std::runtime_error(std::string("Time estimator post process export failed.\nError while reading from file.\n")); + throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nError while reading from file.\n")); } // check tags @@ -383,7 +384,7 @@ namespace Slic3r { in.close(); if (rename_file(path_tmp, filename)) - throw std::runtime_error(std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + filename + '\n' + + throw Slic3r::RuntimeError(std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + filename + '\n' + "Is " + path_tmp + " locked?" + '\n'); return true; diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 00a4ad47c3..3b9fcd6176 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -1,4 +1,5 @@ #include "libslic3r.h" +#include "Exception.hpp" #include "Geometry.hpp" #include "ClipperUtils.hpp" #include "ExPolygon.hpp" @@ -471,7 +472,7 @@ Pointfs arrange(size_t num_parts, const Vec2d &part_size, coordf_t gap, const Bo size_t cellw = size_t(floor((bed_bbox.size()(0) + gap) / cell_size(0))); size_t cellh = size_t(floor((bed_bbox.size()(1) + gap) / cell_size(1))); if (num_parts > cellw * cellh) - throw std::invalid_argument("%zu parts won't fit in your print area!\n", num_parts); + throw Slic3r::InvalidArgument("%zu parts won't fit in your print area!\n", num_parts); // Get a bounding box of cellw x cellh cells, centered at the center of the bed. Vec2d cells_size(cellw * cell_size(0) - gap, cellh * cell_size(1) - gap); diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp index 66167c7209..dd34b28300 100644 --- a/src/libslic3r/MeshBoolean.cpp +++ b/src/libslic3r/MeshBoolean.cpp @@ -1,3 +1,4 @@ +#include "Exception.hpp" #include "MeshBoolean.hpp" #include "libslic3r/TriangleMesh.hpp" #undef PI @@ -136,7 +137,7 @@ template void triangle_mesh_to_cgal(const TriangleMesh &M, _Mesh &o if(CGAL::is_closed(out)) CGALProc::orient_to_bound_a_volume(out); else - std::runtime_error("Mesh not watertight"); + throw Slic3r::RuntimeError("Mesh not watertight"); } inline Vec3d to_vec3d(const _EpicMesh::Point &v) @@ -222,7 +223,7 @@ template void _cgal_do(Op &&op, CGALMesh &A, CGALMesh &B) } if (! success) - throw std::runtime_error("CGAL mesh boolean operation failed."); + throw Slic3r::RuntimeError("CGAL mesh boolean operation failed."); } void minus(CGALMesh &A, CGALMesh &B) { _cgal_do(_cgal_diff, A, B); } diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 67fe63871d..dc06e3102d 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1,3 +1,4 @@ +#include "Exception.hpp" #include "Model.hpp" #include "ModelArrange.hpp" #include "Geometry.hpp" @@ -116,13 +117,13 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c else if (boost::algorithm::iends_with(input_file, ".prusa")) result = load_prus(input_file.c_str(), &model); else - throw std::runtime_error("Unknown file format. Input file must have .stl, .obj, .amf(.xml) or .prusa extension."); + throw Slic3r::RuntimeError("Unknown file format. Input file must have .stl, .obj, .amf(.xml) or .prusa extension."); if (! result) - throw std::runtime_error("Loading of a model file failed."); + throw Slic3r::RuntimeError("Loading of a model file failed."); if (model.objects.empty()) - throw std::runtime_error("The supplied file couldn't be read because it's empty"); + throw Slic3r::RuntimeError("The supplied file couldn't be read because it's empty"); for (ModelObject *o : model.objects) o->input_file = input_file; @@ -146,13 +147,13 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig else if (boost::algorithm::iends_with(input_file, ".zip.amf")) result = load_amf(input_file.c_str(), config, &model, check_version); else - throw std::runtime_error("Unknown file format. Input file must have .3mf or .zip.amf extension."); + throw Slic3r::RuntimeError("Unknown file format. Input file must have .3mf or .zip.amf extension."); if (!result) - throw std::runtime_error("Loading of a model file failed."); + throw Slic3r::RuntimeError("Loading of a model file failed."); if (model.objects.empty()) - throw std::runtime_error("The supplied file couldn't be read because it's empty"); + throw Slic3r::RuntimeError("The supplied file couldn't be read because it's empty"); for (ModelObject *o : model.objects) { @@ -817,7 +818,7 @@ const BoundingBoxf3& ModelObject::raw_bounding_box() const m_raw_bounding_box_valid = true; m_raw_bounding_box.reset(); if (this->instances.empty()) - throw std::invalid_argument("Can't call raw_bounding_box() with no instances"); + throw Slic3r::InvalidArgument("Can't call raw_bounding_box() with no instances"); const Transform3d& inst_matrix = this->instances.front()->get_transformation().get_matrix(true); for (const ModelVolume *v : this->volumes) diff --git a/src/libslic3r/ModelArrange.hpp b/src/libslic3r/ModelArrange.hpp index afe146d438..124c5c018f 100644 --- a/src/libslic3r/ModelArrange.hpp +++ b/src/libslic3r/ModelArrange.hpp @@ -20,7 +20,7 @@ using VirtualBedFn = std::function; [[noreturn]] inline void throw_if_out_of_bed(arrangement::ArrangePolygon&) { - throw std::runtime_error("Objects could not fit on the bed"); + throw Slic3r::RuntimeError("Objects could not fit on the bed"); } ArrangePolygons get_arrange_polys(const Model &model, ModelInstancePtrs &instances); diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 527d82b4cb..0434e3a0aa 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -1,4 +1,5 @@ #include "PlaceholderParser.hpp" +#include "Exception.hpp" #include "Flow.hpp" #include #include @@ -1303,7 +1304,7 @@ static std::string process_macro(const std::string &templ, client::MyContext &co if (!context.error_message.empty()) { if (context.error_message.back() != '\n' && context.error_message.back() != '\r') context.error_message += '\n'; - throw std::runtime_error(context.error_message); + throw Slic3r::RuntimeError(context.error_message); } return output; } @@ -1319,7 +1320,7 @@ std::string PlaceholderParser::process(const std::string &templ, unsigned int cu } // Evaluate a boolean expression using the full expressive power of the PlaceholderParser boolean expression syntax. -// Throws std::runtime_error on syntax or runtime error. +// Throws Slic3r::RuntimeError on syntax or runtime error. bool PlaceholderParser::evaluate_boolean_expression(const std::string &templ, const DynamicConfig &config, const DynamicConfig *config_override) { client::MyContext context; diff --git a/src/libslic3r/PlaceholderParser.hpp b/src/libslic3r/PlaceholderParser.hpp index d744dba220..14be020aca 100644 --- a/src/libslic3r/PlaceholderParser.hpp +++ b/src/libslic3r/PlaceholderParser.hpp @@ -40,11 +40,11 @@ public: const DynamicConfig* external_config() const { return m_external_config; } // Fill in the template using a macro processing language. - // Throws std::runtime_error on syntax or runtime error. + // Throws Slic3r::RuntimeError on syntax or runtime error. std::string process(const std::string &templ, unsigned int current_extruder_id = 0, const DynamicConfig *config_override = nullptr) const; // Evaluate a boolean expression using the full expressive power of the PlaceholderParser boolean expression syntax. - // Throws std::runtime_error on syntax or runtime error. + // Throws Slic3r::RuntimeError on syntax or runtime error. static bool evaluate_boolean_expression(const std::string &templ, const DynamicConfig &config, const DynamicConfig *config_override = nullptr); // Update timestamp, year, month, day, hour, minute, second variables at the provided config. diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index 48e63dab31..fc83ae8d47 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -1,5 +1,6 @@ #include "BoundingBox.hpp" #include "ClipperUtils.hpp" +#include "Exception.hpp" #include "Polygon.hpp" #include "Polyline.hpp" @@ -16,7 +17,7 @@ Polyline Polygon::split_at_vertex(const Point &point) const for (const Point &pt : this->points) if (pt == point) return this->split_at_index(int(&pt - &this->points.front())); - throw std::invalid_argument("Point not found"); + throw Slic3r::InvalidArgument("Point not found"); return Polyline(); } diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index 26aad83d2b..d24788c7bc 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -1,5 +1,6 @@ #include "BoundingBox.hpp" #include "Polyline.hpp" +#include "Exception.hpp" #include "ExPolygon.hpp" #include "ExPolygonCollection.hpp" #include "Line.hpp" @@ -19,7 +20,7 @@ Polyline::operator Polylines() const Polyline::operator Line() const { if (this->points.size() > 2) - throw std::invalid_argument("Can't convert polyline with more than two points to a line"); + throw Slic3r::InvalidArgument("Can't convert polyline with more than two points to a line"); return Line(this->points.front(), this->points.back()); } @@ -207,7 +208,7 @@ BoundingBox get_extents(const Polylines &polylines) const Point& leftmost_point(const Polylines &polylines) { if (polylines.empty()) - throw std::invalid_argument("leftmost_point() called on empty PolylineCollection"); + throw Slic3r::InvalidArgument("leftmost_point() called on empty PolylineCollection"); Polylines::const_iterator it = polylines.begin(); const Point *p = &it->leftmost_point(); for (++ it; it != polylines.end(); ++it) { diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 284c37435a..18a6e387cf 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1,5 +1,6 @@ #include +#include "Exception.hpp" #include "Preset.hpp" #include "AppConfig.hpp" @@ -107,7 +108,7 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem const std::string id = path.stem().string(); if (! boost::filesystem::exists(path)) { - throw std::runtime_error((boost::format("Cannot load Vendor Config Bundle `%1%`: File not found: `%2%`.") % id % path).str()); + throw Slic3r::RuntimeError((boost::format("Cannot load Vendor Config Bundle `%1%`: File not found: `%2%`.") % id % path).str()); } VendorProfile res(id); @@ -117,7 +118,7 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem { auto res = tree.find(key); if (res == tree.not_found()) { - throw std::runtime_error((boost::format("Vendor Config Bundle `%1%` is not valid: Missing secion or key: `%2%`.") % id % key).str()); + throw Slic3r::RuntimeError((boost::format("Vendor Config Bundle `%1%` is not valid: Missing secion or key: `%2%`.") % id % key).str()); } return res; }; @@ -129,7 +130,7 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem auto config_version_str = get_or_throw(vendor_section, "config_version")->second.data(); auto config_version = Semver::parse(config_version_str); if (! config_version) { - throw std::runtime_error((boost::format("Vendor Config Bundle `%1%` is not valid: Cannot parse config_version: `%2%`.") % id % config_version_str).str()); + throw Slic3r::RuntimeError((boost::format("Vendor Config Bundle `%1%` is not valid: Cannot parse config_version: `%2%`.") % id % config_version_str).str()); } else { res.config_version = std::move(*config_version); } @@ -672,9 +673,9 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri preset.file << "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed"; preset.loaded = true; } catch (const std::ifstream::failure &err) { - throw std::runtime_error(std::string("The selected preset cannot be loaded: ") + preset.file + "\n\tReason: " + err.what()); + throw Slic3r::RuntimeError(std::string("The selected preset cannot be loaded: ") + preset.file + "\n\tReason: " + err.what()); } catch (const std::runtime_error &err) { - throw std::runtime_error(std::string("Failed loading the preset file: ") + preset.file + "\n\tReason: " + err.what()); + throw Slic3r::RuntimeError(std::string("Failed loading the preset file: ") + preset.file + "\n\tReason: " + err.what()); } presets_loaded.emplace_back(preset); } catch (const std::runtime_error &err) { @@ -686,7 +687,7 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri std::sort(m_presets.begin() + m_num_default_presets, m_presets.end()); this->select_preset(first_visible_idx()); if (! errors_cummulative.empty()) - throw std::runtime_error(errors_cummulative); + throw Slic3r::RuntimeError(errors_cummulative); } // Load a preset from an already parsed config file, insert it into the sorted sequence of presets @@ -1557,10 +1558,10 @@ void PhysicalPrinterCollection::load_printers(const std::string& dir_path, const printer.loaded = true; } catch (const std::ifstream::failure& err) { - throw std::runtime_error(std::string("The selected preset cannot be loaded: ") + printer.file + "\n\tReason: " + err.what()); + throw Slic3r::RuntimeError(std::string("The selected preset cannot be loaded: ") + printer.file + "\n\tReason: " + err.what()); } catch (const std::runtime_error& err) { - throw std::runtime_error(std::string("Failed loading the preset file: ") + printer.file + "\n\tReason: " + err.what()); + throw Slic3r::RuntimeError(std::string("Failed loading the preset file: ") + printer.file + "\n\tReason: " + err.what()); } printers_loaded.emplace_back(printer); } @@ -1572,7 +1573,7 @@ void PhysicalPrinterCollection::load_printers(const std::string& dir_path, const m_printers.insert(m_printers.end(), std::make_move_iterator(printers_loaded.begin()), std::make_move_iterator(printers_loaded.end())); std::sort(m_printers.begin(), m_printers.end()); if (!errors_cummulative.empty()) - throw std::runtime_error(errors_cummulative); + throw Slic3r::RuntimeError(errors_cummulative); } // if there is saved user presets, contains information about "Print Host upload", diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index ac1b0a7176..c100e6971a 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -157,7 +157,7 @@ void PresetBundle::setup_directories() subdir.make_preferred(); if (! boost::filesystem::is_directory(subdir) && ! boost::filesystem::create_directory(subdir)) - throw std::runtime_error(std::string("Slic3r was unable to create its data directory at ") + subdir.string()); + throw Slic3r::RuntimeError(std::string("Slic3r was unable to create its data directory at ") + subdir.string()); } } @@ -207,7 +207,7 @@ void PresetBundle::load_presets(AppConfig &config, const std::string &preferred_ this->update_multi_material_filament_presets(); this->update_compatible(PresetSelectCompatibleType::Never); if (! errors_cummulative.empty()) - throw std::runtime_error(errors_cummulative); + throw Slic3r::RuntimeError(errors_cummulative); this->load_selections(config, preferred_model_id); } @@ -679,21 +679,21 @@ void PresetBundle::load_config_file(const std::string &path) boost::nowide::ifstream ifs(path); boost::property_tree::read_ini(ifs, tree); } catch (const std::ifstream::failure &err) { - throw std::runtime_error(std::string("The Config Bundle cannot be loaded: ") + path + "\n\tReason: " + err.what()); + throw Slic3r::RuntimeError(std::string("The Config Bundle cannot be loaded: ") + path + "\n\tReason: " + err.what()); } catch (const boost::property_tree::file_parser_error &err) { - throw std::runtime_error((boost::format("Failed loading the Config Bundle \"%1%\": %2% at line %3%") + throw Slic3r::RuntimeError((boost::format("Failed loading the Config Bundle \"%1%\": %2% at line %3%") % err.filename() % err.message() % err.line()).str()); } catch (const std::runtime_error &err) { - throw std::runtime_error(std::string("Failed loading the preset file: ") + path + "\n\tReason: " + err.what()); + throw Slic3r::RuntimeError(std::string("Failed loading the preset file: ") + path + "\n\tReason: " + err.what()); } // 2) Continue based on the type of the configuration file. ConfigFileType config_file_type = guess_config_file_type(tree); switch (config_file_type) { case CONFIG_FILE_TYPE_UNKNOWN: - throw std::runtime_error(std::string("Unknown configuration file type: ") + path); + throw Slic3r::RuntimeError(std::string("Unknown configuration file type: ") + path); case CONFIG_FILE_TYPE_APP_CONFIG: - throw std::runtime_error(std::string("Invalid configuration file: ") + path + ". This is an application config file."); + throw Slic3r::RuntimeError(std::string("Invalid configuration file: ") + path + ". This is an application config file."); case CONFIG_FILE_TYPE_CONFIG: { // Initialize a config from full defaults. diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 50b752984b..a73b7016fb 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1,5 +1,6 @@ #include "clipper/clipper_z.hpp" +#include "Exception.hpp" #include "Print.hpp" #include "BoundingBox.hpp" #include "ClipperUtils.hpp" @@ -1507,7 +1508,7 @@ BoundingBox Print::total_bounding_box() const double Print::skirt_first_layer_height() const { if (m_objects.empty()) - throw std::invalid_argument("skirt_first_layer_height() can't be called without PrintObjects"); + throw Slic3r::InvalidArgument("skirt_first_layer_height() can't be called without PrintObjects"); return m_objects.front()->config().get_abs_value("first_layer_height"); } @@ -1603,7 +1604,7 @@ void Print::process() // Initialize the tool ordering, so it could be used by the G-code preview slider for planning tool changes and filament switches. m_tool_ordering = ToolOrdering(*this, -1, false); if (m_tool_ordering.empty() || m_tool_ordering.last_extruder() == unsigned(-1)) - throw std::runtime_error("The print is empty. The model is not printable with current print settings."); + throw Slic3r::RuntimeError("The print is empty. The model is not printable with current print settings."); } this->set_done(psWipeTower); } diff --git a/src/libslic3r/PrintBase.cpp b/src/libslic3r/PrintBase.cpp index ab6ca3d350..7cdf6448c3 100644 --- a/src/libslic3r/PrintBase.cpp +++ b/src/libslic3r/PrintBase.cpp @@ -1,3 +1,4 @@ +#include "Exception.hpp" #include "PrintBase.hpp" #include @@ -68,7 +69,7 @@ std::string PrintBase::output_filename(const std::string &format, const std::str filename = boost::filesystem::change_extension(filename, default_ext); return filename.string(); } catch (std::runtime_error &err) { - throw std::runtime_error(L("Failed processing of the output_filename_format template.") + "\n" + err.what()); + throw Slic3r::RuntimeError(L("Failed processing of the output_filename_format template.") + "\n" + err.what()); } } diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index e2dba5bb2a..30c070338d 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1,3 +1,4 @@ +#include "Exception.hpp" #include "Print.hpp" #include "BoundingBox.hpp" #include "ClipperUtils.hpp" @@ -138,7 +139,7 @@ void PrintObject::slice() } }); if (m_layers.empty()) - throw std::runtime_error("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"); + throw Slic3r::RuntimeError("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"); this->set_done(posSlice); } @@ -426,7 +427,7 @@ void PrintObject::generate_support_material() // therefore they cannot be printed without supports. for (const Layer *layer : m_layers) if (layer->empty()) - throw std::runtime_error("Levitating objects cannot be printed without supports."); + throw Slic3r::RuntimeError("Levitating objects cannot be printed without supports."); #endif } this->set_done(posSupportMaterial); diff --git a/src/libslic3r/PrintRegion.cpp b/src/libslic3r/PrintRegion.cpp index b3ac6a4a5b..2a75cd621d 100644 --- a/src/libslic3r/PrintRegion.cpp +++ b/src/libslic3r/PrintRegion.cpp @@ -1,3 +1,4 @@ +#include "Exception.hpp" #include "Print.hpp" namespace Slic3r { @@ -13,7 +14,7 @@ unsigned int PrintRegion::extruder(FlowRole role) const else if (role == frSolidInfill || role == frTopSolidInfill) extruder = m_config.solid_infill_extruder; else - throw std::invalid_argument("Unknown role"); + throw Slic3r::InvalidArgument("Unknown role"); return extruder; } @@ -40,7 +41,7 @@ Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool fir } else if (role == frTopSolidInfill) { config_width = m_config.top_infill_extrusion_width; } else { - throw std::invalid_argument("Unknown role"); + throw Slic3r::InvalidArgument("Unknown role"); } } diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index eaf9698198..19bd854881 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -187,7 +188,7 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) } if (MeshBoolean::cgal::does_self_intersect(*holes_mesh_cgal)) - throw std::runtime_error(L("Too much overlapping holes.")); + throw Slic3r::RuntimeError(L("Too many overlapping holes.")); auto hollowed_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal(hollowed_mesh); @@ -195,7 +196,7 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) MeshBoolean::cgal::minus(*hollowed_mesh_cgal, *holes_mesh_cgal); hollowed_mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*hollowed_mesh_cgal); } catch (const std::runtime_error &) { - throw std::runtime_error(L( + throw Slic3r::RuntimeError(L( "Drilling holes into the mesh failed. " "This is usually caused by broken model. Try to fix it first.")); } @@ -241,7 +242,7 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) if(slindex_it == po.m_slice_index.end()) //TRN To be shown at the status bar on SLA slicing error. - throw std::runtime_error( + throw Slic3r::RuntimeError( L("Slicing had to be stopped due to an internal error: " "Inconsistent slice index.")); @@ -445,7 +446,7 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) { auto &pad_mesh = po.m_supportdata->support_tree_ptr->retrieve_mesh(sla::MeshType::Pad); if (!validate_pad(pad_mesh, pcfg)) - throw std::runtime_error( + throw Slic3r::RuntimeError( L("No pad can be generated for this model with the " "current configuration")); @@ -613,7 +614,7 @@ void SLAPrint::Steps::initialize_printer_input() for(const SliceRecord& slicerecord : o->get_slice_index()) { if (!slicerecord.is_valid()) - throw std::runtime_error( + throw Slic3r::RuntimeError( L("There are unprintable objects. Try to " "adjust support settings to make the " "objects printable.")); diff --git a/src/libslic3r/Semver.hpp b/src/libslic3r/Semver.hpp index 24ca74f837..f55fa9f9f4 100644 --- a/src/libslic3r/Semver.hpp +++ b/src/libslic3r/Semver.hpp @@ -10,6 +10,8 @@ #include "semver/semver.h" +#include "Exception.hpp" + namespace Slic3r { @@ -38,7 +40,7 @@ public: { auto parsed = parse(str); if (! parsed) { - throw std::runtime_error(std::string("Could not parse version string: ") + str); + throw Slic3r::RuntimeError(std::string("Could not parse version string: ") + str); } ver = parsed->ver; parsed->ver = semver_zero(); diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 49fc625af9..8ba34e5160 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -1,3 +1,4 @@ +#include "Exception.hpp" #include "TriangleMesh.hpp" #include "ClipperUtils.hpp" #include "Geometry.hpp" @@ -420,7 +421,7 @@ std::deque TriangleMesh::find_unvisited_neighbors(std::vectorrepaired) - throw std::runtime_error("find_unvisited_neighbors() requires repair()"); + throw Slic3r::RuntimeError("find_unvisited_neighbors() requires repair()"); // If the visited list is empty, populate it with false for every facet. if (facet_visited.empty()) @@ -683,7 +684,7 @@ void TriangleMeshSlicer::init(const TriangleMesh *_mesh, throw_on_cancel_callbac { mesh = _mesh; if (! mesh->has_shared_vertices()) - throw std::invalid_argument("TriangleMeshSlicer was passed a mesh without shared vertices."); + throw Slic3r::InvalidArgument("TriangleMeshSlicer was passed a mesh without shared vertices."); throw_on_cancel(); facets_edges.assign(_mesh->stl.stats.number_of_facets * 3, -1); diff --git a/src/libslic3r/Zipper.cpp b/src/libslic3r/Zipper.cpp index 02f022083b..7a95829cd0 100644 --- a/src/libslic3r/Zipper.cpp +++ b/src/libslic3r/Zipper.cpp @@ -1,5 +1,6 @@ #include +#include "Exception.hpp" #include "Zipper.hpp" #include "miniz_extension.hpp" #include @@ -29,7 +30,7 @@ public: SLIC3R_NORETURN void blow_up() const { - throw std::runtime_error(formatted_errorstr()); + throw Slic3r::RuntimeError(formatted_errorstr()); } bool is_alive() diff --git a/src/slic3r/Config/Snapshot.cpp b/src/slic3r/Config/Snapshot.cpp index 30596b614d..45dc998741 100644 --- a/src/slic3r/Config/Snapshot.cpp +++ b/src/slic3r/Config/Snapshot.cpp @@ -315,7 +315,7 @@ size_t SnapshotDB::load_db() // Sort the snapshots by their date/time. std::sort(m_snapshots.begin(), m_snapshots.end(), [](const Snapshot &s1, const Snapshot &s2) { return s1.time_captured < s2.time_captured; }); if (! errors_cummulative.empty()) - throw std::runtime_error(errors_cummulative); + throw Slic3r::RuntimeError(errors_cummulative); return m_snapshots.size(); } @@ -339,7 +339,7 @@ static void copy_config_dir_single_level(const boost::filesystem::path &path_src { if (! boost::filesystem::is_directory(path_dst) && ! boost::filesystem::create_directory(path_dst)) - throw std::runtime_error(std::string("Slic3r was unable to create a directory at ") + path_dst.string()); + throw Slic3r::RuntimeError(std::string("Slic3r was unable to create a directory at ") + path_dst.string()); for (auto &dir_entry : boost::filesystem::directory_iterator(path_src)) if (Slic3r::is_ini_file(dir_entry)) @@ -429,7 +429,7 @@ const Snapshot& SnapshotDB::restore_snapshot(const std::string &id, AppConfig &a this->restore_snapshot(snapshot, app_config); return snapshot; } - throw std::runtime_error(std::string("Snapshot with id " + id + " was not found.")); + throw Slic3r::RuntimeError(std::string("Snapshot with id " + id + " was not found.")); } void SnapshotDB::restore_snapshot(const Snapshot &snapshot, AppConfig &app_config) @@ -501,7 +501,7 @@ boost::filesystem::path SnapshotDB::create_db_dir() subdir.make_preferred(); if (! boost::filesystem::is_directory(subdir) && ! boost::filesystem::create_directory(subdir)) - throw std::runtime_error(std::string("Slic3r was unable to create a directory at ") + subdir.string()); + throw Slic3r::RuntimeError(std::string("Slic3r was unable to create a directory at ") + subdir.string()); } return snapshots_dir; } diff --git a/src/slic3r/Config/Version.cpp b/src/slic3r/Config/Version.cpp index d00e4a2abf..04ce05ab5b 100644 --- a/src/slic3r/Config/Version.cpp +++ b/src/slic3r/Config/Version.cpp @@ -324,7 +324,7 @@ std::vector Index::load_db() } if (! errors_cummulative.empty()) - throw std::runtime_error(errors_cummulative); + throw Slic3r::RuntimeError(errors_cummulative); return index_db; } diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 70fec670c7..ca96af49c1 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1926,7 +1926,7 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, if (extrusion_entity_collection != nullptr) extrusionentity_to_verts(*extrusion_entity_collection, print_z, copy, volume); else { - throw std::runtime_error("Unexpected extrusion_entity type in to_verts()"); + throw Slic3r::RuntimeError("Unexpected extrusion_entity type in to_verts()"); } } } diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 9675db10ef..9470359d04 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -41,6 +41,36 @@ namespace Slic3r { +bool SlicingProcessCompletedEvent::critical_error() const +{ + try { + this->rethrow_exception(); + } catch (const Slic3r::SlicingError &ex) { + // Exception derived from SlicingError is non-critical. + return false; + } catch (...) { + return true; + } +} + +std::string SlicingProcessCompletedEvent::format_error_message() const +{ + std::string error; + try { + this->rethrow_exception(); + } catch (const std::bad_alloc& ex) { + wxString errmsg = GUI::from_u8((boost::format(_utf8(L("%s has encountered an error. It was likely caused by running out of memory. " + "If you are sure you have enough RAM on your system, this may also be a bug and we would " + "be glad if you reported it."))) % SLIC3R_APP_NAME).str()); + error = std::string(errmsg.ToUTF8()) + "\n\n" + std::string(ex.what()); + } catch (std::exception &ex) { + error = ex.what(); + } catch (...) { + error = "Unknown C++ exception."; + } + return error; +} + BackgroundSlicingProcess::BackgroundSlicingProcess() { boost::filesystem::path temp_path(wxStandardPaths::Get().GetTempDir().utf8_str().data()); @@ -109,19 +139,19 @@ void BackgroundSlicingProcess::process_fff() switch (copy_ret_val) { case SUCCESS: break; // no error case FAIL_COPY_FILE: - throw std::runtime_error(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?"))); + throw Slic3r::RuntimeError(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?"))); break; case FAIL_FILES_DIFFERENT: - throw std::runtime_error((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. There might be problem with target device, please try exporting again or using different device. The corrupted output G-code is at %1%.tmp."))) % export_path).str()); + throw Slic3r::RuntimeError((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. There might be problem with target device, please try exporting again or using different device. The corrupted output G-code is at %1%.tmp."))) % export_path).str()); break; case FAIL_RENAMING: - throw std::runtime_error((boost::format(_utf8(L("Renaming of the G-code after copying to the selected destination folder has failed. Current path is %1%.tmp. Please try exporting again."))) % export_path).str()); + throw Slic3r::RuntimeError((boost::format(_utf8(L("Renaming of the G-code after copying to the selected destination folder has failed. Current path is %1%.tmp. Please try exporting again."))) % export_path).str()); break; case FAIL_CHECK_ORIGIN_NOT_OPENED: - throw std::runtime_error((boost::format(_utf8(L("Copying of the temporary G-code has finished but the original code at %1% couldn't be opened during copy check. The output G-code is at %2%.tmp."))) % m_temp_output_path % export_path).str()); + throw Slic3r::RuntimeError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the original code at %1% couldn't be opened during copy check. The output G-code is at %2%.tmp."))) % m_temp_output_path % export_path).str()); break; case FAIL_CHECK_TARGET_NOT_OPENED: - throw std::runtime_error((boost::format(_utf8(L("Copying of the temporary G-code has finished but the exported code couldn't be opened during copy check. The output G-code is at %1%.tmp."))) % export_path).str()); + throw Slic3r::RuntimeError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the exported code couldn't be opened during copy check. The output G-code is at %1%.tmp."))) % export_path).str()); break; default: BOOST_LOG_TRIVIAL(warning) << "Unexpected fail code(" << (int)copy_ret_val << ") durring copy_file() to " << export_path << "."; @@ -210,7 +240,7 @@ void BackgroundSlicingProcess::thread_proc() // Process the background slicing task. m_state = STATE_RUNNING; lck.unlock(); - std::string error; + std::exception_ptr exception; try { assert(m_print != nullptr); switch(m_print->technology()) { @@ -221,15 +251,8 @@ void BackgroundSlicingProcess::thread_proc() } catch (CanceledException & /* ex */) { // Canceled, this is all right. assert(m_print->canceled()); - } catch (const std::bad_alloc& ex) { - wxString errmsg = GUI::from_u8((boost::format(_utf8(L("%s has encountered an error. It was likely caused by running out of memory. " - "If you are sure you have enough RAM on your system, this may also be a bug and we would " - "be glad if you reported it."))) % SLIC3R_APP_NAME).str()); - error = std::string(errmsg.ToUTF8()) + "\n\n" + std::string(ex.what()); - } catch (std::exception &ex) { - error = ex.what(); } catch (...) { - error = "Unknown C++ exception."; + exception = std::current_exception(); } m_print->finalize(); lck.lock(); @@ -237,9 +260,9 @@ void BackgroundSlicingProcess::thread_proc() if (m_print->cancel_status() != Print::CANCELED_INTERNAL) { // Only post the canceled event, if canceled by user. // Don't post the canceled event, if canceled from Print::apply(). - wxCommandEvent evt(m_event_finished_id); - evt.SetString(GUI::from_u8(error)); - evt.SetInt(m_print->canceled() ? -1 : (error.empty() ? 1 : 0)); + SlicingProcessCompletedEvent evt(m_event_finished_id, 0, + (m_state == STATE_CANCELED) ? SlicingProcessCompletedEvent::Cancelled : + exception ? SlicingProcessCompletedEvent::Error : SlicingProcessCompletedEvent::Finished, exception); wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone()); } m_print->restart(); @@ -299,7 +322,7 @@ bool BackgroundSlicingProcess::start() // The background processing thread is already running. return false; if (! this->idle()) - throw std::runtime_error("Cannot start a background task, the worker thread is not idle."); + throw Slic3r::RuntimeError("Cannot start a background task, the worker thread is not idle."); m_state = STATE_STARTED; m_print->set_cancel_callback([this](){ this->stop_internal(); }); lck.unlock(); @@ -494,7 +517,7 @@ void BackgroundSlicingProcess::prepare_upload() if (m_print == m_fff_print) { m_print->set_status(95, _utf8(L("Running post-processing scripts"))); if (copy_file(m_temp_output_path, source_path.string()) != SUCCESS) { - throw std::runtime_error(_utf8(L("Copying of the temporary G-code to the output G-code failed"))); + throw Slic3r::RuntimeError(_utf8(L("Copying of the temporary G-code to the output G-code failed"))); } run_post_process_scripts(source_path.string(), m_fff_print->config()); m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string()); diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index 9fe1157b6e..1b2687e63a 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -37,6 +37,36 @@ public: PrintBase::SlicingStatus status; }; +class SlicingProcessCompletedEvent : public wxEvent +{ +public: + enum StatusType { + Finished, + Cancelled, + Error + }; + + SlicingProcessCompletedEvent(wxEventType eventType, int winid, StatusType status, std::exception_ptr exception) : + wxEvent(winid, eventType), m_status(status), m_exception(exception) {} + virtual wxEvent* Clone() const { return new SlicingProcessCompletedEvent(*this); } + + StatusType status() const { return m_status; } + bool finished() const { return m_status == Finished; } + bool success() const { return m_status == Finished; } + bool cancelled() const { return m_status == Cancelled; } + bool error() const { return m_status == Error; } + // Unhandled error produced by stdlib or a Win32 structured exception, or unhandled Slic3r's own critical exception. + bool critical_error() const; + // Only valid if error() + void rethrow_exception() const { assert(this->error()); assert(m_exception); std::rethrow_exception(m_exception); } + // Produce a human readable message to be displayed by a notification or a message box. + std::string format_error_message() const; + +private: + StatusType m_status; + std::exception_ptr m_exception; +}; + wxDEFINE_EVENT(EVT_SLICING_UPDATE, SlicingStatusEvent); // Print step IDs for keeping track of the print state. diff --git a/src/slic3r/GUI/ConfigExceptions.hpp b/src/slic3r/GUI/ConfigExceptions.hpp index 9038d3445e..181442d4e3 100644 --- a/src/slic3r/GUI/ConfigExceptions.hpp +++ b/src/slic3r/GUI/ConfigExceptions.hpp @@ -1,15 +1,15 @@ #include namespace Slic3r { -class ConfigError : public std::runtime_error { -using std::runtime_error::runtime_error; +class ConfigError : public Slic3r::RuntimeError { + using Slic3r::RuntimeError::RuntimeError; }; namespace GUI { class ConfigGUITypeError : public ConfigError { -using ConfigError::ConfigError; + using ConfigError::ConfigError; }; -} -} +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 2cedbfdf78..c98b736b7c 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -123,7 +123,7 @@ Bundle& BundleMap::prusa_bundle() { auto it = find(PresetBundle::PRUSA_BUNDLE); if (it == end()) { - throw std::runtime_error("ConfigWizard: Internal error in BundleMap: PRUSA_BUNDLE not loaded"); + throw Slic3r::RuntimeError("ConfigWizard: Internal error in BundleMap: PRUSA_BUNDLE not loaded"); } return it->second; diff --git a/src/slic3r/GUI/FirmwareDialog.cpp b/src/slic3r/GUI/FirmwareDialog.cpp index fe7ff4e5de..5441c84ed5 100644 --- a/src/slic3r/GUI/FirmwareDialog.cpp +++ b/src/slic3r/GUI/FirmwareDialog.cpp @@ -766,7 +766,7 @@ const char* FirmwareDialog::priv::avr109_dev_name(Avr109Pid usb_pid) { return "Original Prusa CW1"; break; - default: throw std::runtime_error((boost::format("Invalid avr109 device USB PID: %1%") % usb_pid.boot).str()); + default: throw Slic3r::RuntimeError((boost::format("Invalid avr109 device USB PID: %1%") % usb_pid.boot).str()); } } diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index d59a83e875..40c8f96e59 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -571,7 +571,7 @@ void GUI_App::init_app_config() std::string error = app_config->load(); if (!error.empty()) // Error while parsing config file. We'll customize the error message and rethrow to be displayed. - throw std::runtime_error( + throw Slic3r::RuntimeError( _u8L("Error parsing PrusaSlicer config file, it is probably corrupted. " "Try to manually delete the file to recover from the error. Your user profiles will not be affected.") + "\n\n" + AppConfig::config_path() + "\n\n" + error); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 0fecc822db..f9a23cb51c 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -927,7 +927,7 @@ void ImGuiWrapper::init_font(bool compress) if (font == nullptr) { font = io.Fonts->AddFontDefault(); if (font == nullptr) { - throw std::runtime_error("ImGui: Could not load deafult font"); + throw Slic3r::RuntimeError("ImGui: Could not load deafult font"); } } diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index dd00b3d688..14defd9a96 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -7,6 +7,7 @@ #include #include #include +#include "libslic3r/Exception.hpp" #include "libslic3r/Utils.hpp" #include "I18N.hpp" @@ -64,7 +65,7 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co break; case coNone: break; default: - throw /*//!ConfigGUITypeError("")*/std::logic_error("This control doesn't exist till now"); break; + throw Slic3r::LogicError("This control doesn't exist till now"); break; } } // Grab a reference to fields for convenience @@ -620,7 +621,7 @@ boost::any ConfigOptionsGroup::config_value(const std::string& opt_key, int opt_ // Aggregate the strings the old way. // Currently used for the post_process config value only. if (opt_index != -1) - throw std::out_of_range("Can't deserialize option indexed value"); + throw Slic3r::OutOfRange("Can't deserialize option indexed value"); // return join(';', m_config->get(opt_key)}); return get_config_value(*m_config, opt_key); } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f7fd608ba8..6d856960db 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -107,7 +107,7 @@ namespace GUI { wxDEFINE_EVENT(EVT_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); wxDEFINE_EVENT(EVT_SLICING_UPDATE, SlicingStatusEvent); wxDEFINE_EVENT(EVT_SLICING_COMPLETED, wxCommandEvent); -wxDEFINE_EVENT(EVT_PROCESS_COMPLETED, wxCommandEvent); +wxDEFINE_EVENT(EVT_PROCESS_COMPLETED, SlicingProcessCompletedEvent); wxDEFINE_EVENT(EVT_EXPORT_BEGAN, wxCommandEvent); // Sidebar widgets @@ -1682,7 +1682,7 @@ struct Plater::priv void on_select_preset(wxCommandEvent&); void on_slicing_update(SlicingStatusEvent&); void on_slicing_completed(wxCommandEvent&); - void on_process_completed(wxCommandEvent&); + void on_process_completed(SlicingProcessCompletedEvent&); void on_export_began(wxCommandEvent&); void on_layer_editing_toggled(bool enable); void on_slicing_began(); @@ -3510,7 +3510,7 @@ bool Plater::priv::warnings_dialog() return res == wxID_OK; } -void Plater::priv::on_process_completed(wxCommandEvent &evt) +void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt) { // Stop the background task, wait until the thread goes into the "Idle" state. // At this point of time the thread should be either finished or canceled, @@ -3519,27 +3519,23 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt) this->statusbar()->reset_cancel_callback(); this->statusbar()->stop_busy(); - const bool canceled = evt.GetInt() < 0; - const bool error = evt.GetInt() == 0; - const bool success = evt.GetInt() > 0; // Reset the "export G-code path" name, so that the automatic background processing will be enabled again. this->background_process.reset_export(); - if (error) { - wxString message = evt.GetString(); - if (message.IsEmpty()) - message = _L("Export failed."); - notification_manager->push_slicing_error_notification(boost::nowide::narrow(message), *q->get_current_canvas3D()); - this->statusbar()->set_status_text(message); + if (evt.error()) { + std::string message = evt.format_error_message(); + //FIXME show a messagebox if evt.critical_error(). + notification_manager->push_slicing_error_notification(message, *q->get_current_canvas3D()); + this->statusbar()->set_status_text(from_u8(message)); const wxString invalid_str = _L("Invalid data"); for (auto btn : { ActionButtonType::abReslice, ActionButtonType::abSendGCode, ActionButtonType::abExport }) sidebar->set_btn_label(btn, invalid_str); process_completed_with_error = true; } - if (canceled) + if (evt.cancelled()) this->statusbar()->set_status_text(_L("Cancelled")); - this->sidebar->show_sliced_info_sizer(success); + this->sidebar->show_sliced_info_sizer(evt.success()); // This updates the "Slice now", "Export G-code", "Arrange" buttons status. // Namely, it refreshes the "Out of print bed" property of all the ModelObjects, and it enables @@ -3560,7 +3556,7 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt) default: break; } - if (canceled) { + if (evt.cancelled()) { if (wxGetApp().get_mode() == comSimple) sidebar->set_btn_label(ActionButtonType::abReslice, "Slice now"); show_action_buttons(true); diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index d23c3415fa..6f79db5916 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -436,7 +436,7 @@ wxBitmap create_scaled_bitmap( const std::string& bmp_name_in, if (bmp == nullptr) { // Neither SVG nor PNG has been found, raise error - throw std::runtime_error("Could not load bitmap: " + bmp_name); + throw Slic3r::RuntimeError("Could not load bitmap: " + bmp_name); } return *bmp; diff --git a/src/slic3r/Utils/FixModelByWin10.cpp b/src/slic3r/Utils/FixModelByWin10.cpp index 86ff79aaaf..bcab6daaf8 100644 --- a/src/slic3r/Utils/FixModelByWin10.cpp +++ b/src/slic3r/Utils/FixModelByWin10.cpp @@ -209,10 +209,10 @@ typedef std::functionGetResults(model.GetAddressOf()); else - throw std::runtime_error(L("Failed loading the input model.")); + throw Slic3r::RuntimeError(L("Failed loading the input model.")); Microsoft::WRL::ComPtr> meshes; hr = model->get_Meshes(meshes.GetAddressOf()); @@ -245,7 +245,7 @@ void fix_model_by_win10_sdk(const std::string &path_src, const std::string &path hr = model->RepairAsync(repairAsync.GetAddressOf()); status = winrt_async_await(repairAsync, throw_on_cancel); if (status != AsyncStatus::Completed) - throw std::runtime_error(L("Mesh repair failed.")); + throw Slic3r::RuntimeError(L("Mesh repair failed.")); repairAsync->GetResults(); on_progress(L("Loading repaired model"), 60); @@ -260,14 +260,14 @@ void fix_model_by_win10_sdk(const std::string &path_src, const std::string &path hr = printing3d3mfpackage->SaveModelToPackageAsync(model.Get(), saveToPackageAsync.GetAddressOf()); status = winrt_async_await(saveToPackageAsync, throw_on_cancel); if (status != AsyncStatus::Completed) - throw std::runtime_error(L("Saving mesh into the 3MF container failed.")); + throw Slic3r::RuntimeError(L("Saving mesh into the 3MF container failed.")); hr = saveToPackageAsync->GetResults(); Microsoft::WRL::ComPtr> generatorStreamAsync; hr = printing3d3mfpackage->SaveAsync(generatorStreamAsync.GetAddressOf()); status = winrt_async_await(generatorStreamAsync, throw_on_cancel); if (status != AsyncStatus::Completed) - throw std::runtime_error(L("Saving mesh into the 3MF container failed.")); + throw Slic3r::RuntimeError(L("Saving mesh into the 3MF container failed.")); Microsoft::WRL::ComPtr generatorStream; hr = generatorStreamAsync->GetResults(generatorStream.GetAddressOf()); @@ -299,7 +299,7 @@ void fix_model_by_win10_sdk(const std::string &path_src, const std::string &path hr = inputStream->ReadAsync(buffer.Get(), 65536 * 2048, ABI::Windows::Storage::Streams::InputStreamOptions_ReadAhead, asyncRead.GetAddressOf()); status = winrt_async_await(asyncRead, throw_on_cancel); if (status != AsyncStatus::Completed) - throw std::runtime_error(L("Saving mesh into the 3MF container failed.")); + throw Slic3r::RuntimeError(L("Saving mesh into the 3MF container failed.")); hr = buffer->get_Length(&length); if (length == 0) break; @@ -365,7 +365,7 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx) model_object->add_instance(); if (!Slic3r::store_3mf(path_src.string().c_str(), &model, nullptr, false)) { boost::filesystem::remove(path_src); - throw std::runtime_error(L("Export of a temporary 3mf file failed")); + throw Slic3r::RuntimeError(L("Export of a temporary 3mf file failed")); } model.clear_objects(); model.clear_materials(); @@ -380,15 +380,15 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx) bool loaded = Slic3r::load_3mf(path_dst.string().c_str(), &config, &model, false); boost::filesystem::remove(path_dst); if (! loaded) - throw std::runtime_error(L("Import of the repaired 3mf file failed")); + throw Slic3r::RuntimeError(L("Import of the repaired 3mf file failed")); if (model.objects.size() == 0) - throw std::runtime_error(L("Repaired 3MF file does not contain any object")); + throw Slic3r::RuntimeError(L("Repaired 3MF file does not contain any object")); if (model.objects.size() > 1) - throw std::runtime_error(L("Repaired 3MF file contains more than one object")); + throw Slic3r::RuntimeError(L("Repaired 3MF file contains more than one object")); if (model.objects.front()->volumes.size() == 0) - throw std::runtime_error(L("Repaired 3MF file does not contain any volume")); + throw Slic3r::RuntimeError(L("Repaired 3MF file does not contain any volume")); if (model.objects.front()->volumes.size() > 1) - throw std::runtime_error(L("Repaired 3MF file contains more than one volume")); + throw Slic3r::RuntimeError(L("Repaired 3MF file contains more than one volume")); meshes_repaired.emplace_back(std::move(model.objects.front()->volumes.front()->mesh())); } for (size_t i = 0; i < volumes.size(); ++ i) { diff --git a/src/slic3r/Utils/Http.cpp b/src/slic3r/Utils/Http.cpp index e55c21fe17..8c79a478a1 100644 --- a/src/slic3r/Utils/Http.cpp +++ b/src/slic3r/Utils/Http.cpp @@ -156,7 +156,7 @@ Http::priv::priv(const std::string &url) Http::tls_global_init(); if (curl == nullptr) { - throw std::runtime_error(std::string("Could not construct Curl object")); + throw Slic3r::RuntimeError(std::string("Could not construct Curl object")); } set_timeout_connect(DEFAULT_TIMEOUT_CONNECT); diff --git a/src/slic3r/Utils/Serial.cpp b/src/slic3r/Utils/Serial.cpp index 737e76c0b5..959c60c373 100644 --- a/src/slic3r/Utils/Serial.cpp +++ b/src/slic3r/Utils/Serial.cpp @@ -298,7 +298,7 @@ void Serial::set_baud_rate(unsigned baud_rate) auto handle_errno = [](int retval) { if (retval != 0) { - throw std::runtime_error( + throw Slic3r::RuntimeError( (boost::format("Could not set baud rate: %1%") % strerror(errno)).str() ); } @@ -346,7 +346,7 @@ void Serial::set_baud_rate(unsigned baud_rate) handle_errno(::cfsetspeed(&ios, baud_rate)); handle_errno(::tcsetattr(handle, TCSAFLUSH, &ios)); #else - throw std::runtime_error("Custom baud rates are not currently supported on this OS"); + throw Slic3r::RuntimeError("Custom baud rates are not currently supported on this OS"); #endif } } @@ -358,7 +358,7 @@ void Serial::set_DTR(bool on) auto handle = native_handle(); #if defined(_WIN32) && !defined(__SYMBIAN32__) if (! EscapeCommFunction(handle, on ? SETDTR : CLRDTR)) { - throw std::runtime_error("Could not set serial port DTR"); + throw Slic3r::RuntimeError("Could not set serial port DTR"); } #else int status; @@ -369,7 +369,7 @@ void Serial::set_DTR(bool on) } } - throw std::runtime_error( + throw Slic3r::RuntimeError( (boost::format("Could not set serial port DTR: %1%") % strerror(errno)).str() ); #endif diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index 9c8d7a8c68..10b8062f7b 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -847,7 +847,7 @@ void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model& model, Slic3r::GU // Find the snapshot by time. It must exist. const auto it_snapshot = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(timestamp)); if (it_snapshot == m_snapshots.end() || it_snapshot->timestamp != timestamp) - throw std::runtime_error((boost::format("Snapshot with timestamp %1% does not exist") % timestamp).str()); + throw Slic3r::RuntimeError((boost::format("Snapshot with timestamp %1% does not exist") % timestamp).str()); m_active_snapshot_time = timestamp; model.clear_objects(); diff --git a/tests/fff_print/test_data.cpp b/tests/fff_print/test_data.cpp index 09ca730ec7..8e5f6bafdc 100644 --- a/tests/fff_print/test_data.cpp +++ b/tests/fff_print/test_data.cpp @@ -137,7 +137,7 @@ TriangleMesh mesh(TestMesh m) { {0,1,2}, {2,1,3}, {4,0,5}, {4,1,0}, {6,4,7}, {7,4,5}, {4,8,1}, {0,2,5}, {5,2,9}, {2,10,9}, {10,3,11}, {2,3,10}, {9,10,12}, {13,9,12}, {3,1,8}, {11,3,8}, {10,11,8}, {4,10,8}, {6,12,10}, {4,6,10}, {7,13,12}, {6,7,12}, {7,5,9}, {13,7,9} }); break; default: - throw std::invalid_argument("Slic3r::Test::mesh(): called with invalid mesh ID"); + throw Slic3r::InvalidArgument("Slic3r::Test::mesh(): called with invalid mesh ID"); break; } From 1eadb6a1a937624b9bbfc33e6e4e2e4589c0e68c Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 14 Sep 2020 18:01:25 +0200 Subject: [PATCH 502/503] Replaced some of Slic3r::RuntimeError exceptions with Slic3r::SlicingError. Only Slic3r::SlicingError are now displayed by a notification, other exceptions are shown by a pop-up dialog. --- src/libslic3r/Fill/FillBase.cpp | 1 - src/libslic3r/Print.cpp | 2 +- src/libslic3r/PrintObject.cpp | 4 ++-- src/libslic3r/SLAPrintSteps.cpp | 8 ++++---- src/slic3r/GUI/Plater.cpp | 11 +++++++++-- 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index b0319efded..077555d2ca 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -2,7 +2,6 @@ #include "../ClipperUtils.hpp" #include "../EdgeGrid.hpp" -#include "../Exception.hpp" #include "../Geometry.hpp" #include "../Surface.hpp" #include "../PrintConfig.hpp" diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index a73b7016fb..a82ab3dddc 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1604,7 +1604,7 @@ void Print::process() // Initialize the tool ordering, so it could be used by the G-code preview slider for planning tool changes and filament switches. m_tool_ordering = ToolOrdering(*this, -1, false); if (m_tool_ordering.empty() || m_tool_ordering.last_extruder() == unsigned(-1)) - throw Slic3r::RuntimeError("The print is empty. The model is not printable with current print settings."); + throw Slic3r::SlicingError("The print is empty. The model is not printable with current print settings."); } this->set_done(psWipeTower); } diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 30c070338d..ddd41af012 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -139,7 +139,7 @@ void PrintObject::slice() } }); if (m_layers.empty()) - throw Slic3r::RuntimeError("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"); + throw Slic3r::SlicingError("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"); this->set_done(posSlice); } @@ -427,7 +427,7 @@ void PrintObject::generate_support_material() // therefore they cannot be printed without supports. for (const Layer *layer : m_layers) if (layer->empty()) - throw Slic3r::RuntimeError("Levitating objects cannot be printed without supports."); + throw Slic3r::SlicingError("Levitating objects cannot be printed without supports."); #endif } this->set_done(posSupportMaterial); diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 19bd854881..d94bc682b3 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -188,7 +188,7 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) } if (MeshBoolean::cgal::does_self_intersect(*holes_mesh_cgal)) - throw Slic3r::RuntimeError(L("Too many overlapping holes.")); + throw Slic3r::SlicingError(L("Too many overlapping holes.")); auto hollowed_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal(hollowed_mesh); @@ -196,7 +196,7 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) MeshBoolean::cgal::minus(*hollowed_mesh_cgal, *holes_mesh_cgal); hollowed_mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*hollowed_mesh_cgal); } catch (const std::runtime_error &) { - throw Slic3r::RuntimeError(L( + throw Slic3r::SlicingError(L( "Drilling holes into the mesh failed. " "This is usually caused by broken model. Try to fix it first.")); } @@ -446,7 +446,7 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) { auto &pad_mesh = po.m_supportdata->support_tree_ptr->retrieve_mesh(sla::MeshType::Pad); if (!validate_pad(pad_mesh, pcfg)) - throw Slic3r::RuntimeError( + throw Slic3r::SlicingError( L("No pad can be generated for this model with the " "current configuration")); @@ -614,7 +614,7 @@ void SLAPrint::Steps::initialize_printer_input() for(const SliceRecord& slicerecord : o->get_slice_index()) { if (!slicerecord.is_valid()) - throw Slic3r::RuntimeError( + throw Slic3r::SlicingError( L("There are unprintable objects. Try to " "adjust support settings to make the " "objects printable.")); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 6d856960db..ec632611f0 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3524,8 +3524,15 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt) if (evt.error()) { std::string message = evt.format_error_message(); - //FIXME show a messagebox if evt.critical_error(). - notification_manager->push_slicing_error_notification(message, *q->get_current_canvas3D()); + if (evt.critical_error()) { + if (q->m_tracking_popup_menu) + // We don't want to pop-up a message box when tracking a pop-up menu. + // We postpone the error message instead. + q->m_tracking_popup_menu_error_message = message; + else + show_error(q, message); + } else + notification_manager->push_slicing_error_notification(message, *q->get_current_canvas3D()); this->statusbar()->set_status_text(from_u8(message)); const wxString invalid_str = _L("Invalid data"); for (auto btn : { ActionButtonType::abReslice, ActionButtonType::abSendGCode, ActionButtonType::abExport }) From 5d8c4b4476d83e0ea2a152074a4d4d9524e9c60a Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 14 Sep 2020 16:27:38 +0200 Subject: [PATCH 503/503] Fixed missing return --- src/slic3r/GUI/BackgroundSlicingProcess.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 9470359d04..605a98eea0 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -49,8 +49,8 @@ bool SlicingProcessCompletedEvent::critical_error() const // Exception derived from SlicingError is non-critical. return false; } catch (...) { - return true; } + return true; } std::string SlicingProcessCompletedEvent::format_error_message() const