diff --git a/build_win.bat b/build_win.bat index a5af27609e..1a735d7cdd 100644 --- a/build_win.bat +++ b/build_win.bat @@ -6,7 +6,8 @@ @ECHO Performs initial build or rebuild of the app (build) and deps (build/deps). @ECHO Default options are determined from build directories and system state. @ECHO. -@ECHO Usage: build_win [-ARCH ^] [-CONFIG ^] [-DESTDIR ^] +@ECHO Usage: build_win [-ARCH ^] [-CONFIG ^] [-VERSION ^] +@ECHO [-PRODUCT ^] [-DESTDIR ^] @ECHO [-STEPS ^] @ECHO [-RUN ^] @ECHO. @@ -14,6 +15,10 @@ @ECHO Default: %PS_ARCH_HOST% @ECHO -c -CONFIG MSVC project config @ECHO Default: %PS_CONFIG_DEFAULT% +@ECHO -v -VERSION Major version number of MSVC installation to use for build +@ECHO Default: %PS_VERSION_SUPPORTED% +@ECHO -p -PRODUCT Product ID of MSVC installation to use for build +@ECHO Default: %PS_PRODUCT_DEFAULT% @ECHO -s -STEPS Performs only the specified build steps: @ECHO all - clean and build deps and app @ECHO all-dirty - build deps and app without cleaning @@ -55,6 +60,23 @@ SET PS_DEPS_PATH_FILE_NAME=.DEPS_PATH.txt SET PS_DEPS_PATH_FILE=%~dp0deps\build\%PS_DEPS_PATH_FILE_NAME% SET PS_CONFIG_LIST="Debug;MinSizeRel;Release;RelWithDebInfo" +REM The officially supported toolchain version is 16 (Visual Studio 2019) +REM TODO: Update versions after Boost gets rolled to 1.78 or later +SET PS_VERSION_SUPPORTED=16 +SET PS_VERSION_EXCEEDED=17 +SET VSWHERE=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe +IF NOT EXIST "%VSWHERE%" SET VSWHERE=%ProgramFiles%\Microsoft Visual Studio\Installer\vswhere.exe +FOR /F "tokens=4 USEBACKQ delims=." %%I IN (`"%VSWHERE%" -nologo -property productId`) DO SET PS_PRODUCT_DEFAULT=%%I +IF "%PS_PRODUCT_DEFAULT%" EQU "" ( + SET EXIT_STATUS=-1 + @ECHO ERROR: No Visual Studio installation found. 1>&2 + GOTO :HELP +) +REM Default to the latest supported version if multiple are available +FOR /F "tokens=1 USEBACKQ delims=." %%I IN ( + `^""%VSWHERE%" -version "[%PS_VERSION_SUPPORTED%,%PS_VERSION_EXCEEDED%)" -latest -nologo -property catalog_buildVersion^"` +) DO SET PS_VERSION_SUPPORTED=%%I + REM Probe build directories and system state for reasonable default arguments pushd %~dp0 SET PS_CONFIG=RelWithDebInfo @@ -62,6 +84,8 @@ SET PS_ARCH=%PROCESSOR_ARCHITECTURE:amd64=x64% CALL :TOLOWER PS_ARCH SET PS_RUN=none SET PS_DESTDIR= +SET PS_VERSION= +SET PS_PRODUCT=%PS_PRODUCT_DEFAULT% CALL :RESOLVE_DESTDIR_CACHE REM Set up parameters used by help menu @@ -75,7 +99,7 @@ SET EXIT_STATUS=1 SET PS_CURRENT_STEP=arguments SET PARSER_STATE= SET PARSER_FAIL= -FOR %%I in (%*) DO CALL :PARSE_OPTION "ARCH CONFIG DESTDIR STEPS RUN" PARSER_STATE "%%~I" +FOR %%I in (%*) DO CALL :PARSE_OPTION "ARCH CONFIG DESTDIR STEPS RUN VERSION PRODUCT" PARSER_STATE "%%~I" IF "%PARSER_FAIL%" NEQ "" ( @ECHO ERROR: Invalid switch: %PARSER_FAIL% 1>&2 GOTO :HELP @@ -124,6 +148,15 @@ IF "%PS_RUN%" NEQ "none" IF "%PS_STEPS:~0,4%" EQU "deps" ( @ECHO ERROR: RUN=none is the only valid option for STEPS "deps" or "deps-dirty" GOTO :HELP ) +IF DEFINED PS_VERSION ( + SET /A PS_VERSION_EXCEEDED=%PS_VERSION% + 1 +) ELSE SET PS_VERSION=%PS_VERSION_SUPPORTED% +SET MSVC_FILTER=-products Microsoft.VisualStudio.Product.%PS_PRODUCT% -version "[%PS_VERSION%,%PS_VERSION_EXCEEDED%)" +FOR /F "tokens=* USEBACKQ" %%I IN (`^""%VSWHERE%" %MSVC_FILTER% -nologo -property installationPath^"`) DO SET MSVC_DIR=%%I +IF NOT EXIST "%MSVC_DIR%" ( + @ECHO ERROR: Compatible Visual Studio installation not found. 1>&2 + GOTO :HELP +) REM Give the user a chance to cancel if we found something odd. IF "%PS_ASK_TO_CONTINUE%" EQU "" GOTO :BUILD_ENV @ECHO. @@ -142,9 +175,6 @@ SET PS_CURRENT_STEP=environment @ECHO ** Run App: %PS_RUN% @ECHO ** Deps path: %PS_DESTDIR% @ECHO ** Using Microsoft Visual Studio installation found at: -SET VSWHERE=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe -IF NOT EXIST "%VSWHERE%" SET VSWHERE=%ProgramFiles%\Microsoft Visual Studio\Installer\vswhere.exe -FOR /F "tokens=* USEBACKQ" %%I IN (`"%VSWHERE%" -nologo -property installationPath`) DO SET MSVC_DIR=%%I @ECHO ** %MSVC_DIR% CALL "%MSVC_DIR%\Common7\Tools\vsdevcmd.bat" -arch=%PS_ARCH% -host_arch=%PS_ARCH_HOST% -app_platform=Desktop IF %ERRORLEVEL% NEQ 0 GOTO :END @@ -276,7 +306,7 @@ REM Functions and stubs start here. SET PS_DEPS_PATH_FILE_FOR_CONFIG=%~dp0build\.vs\%PS_ARCH%\%PS_CONFIG%\%PS_DEPS_PATH_FILE_NAME% mkdir "%~dp0build\.vs\%PS_ARCH%\%PS_CONFIG%" > nul 2> nul REM Copy a legacy file if we don't have one in the proper location. -echo f|xcopy /D "%~dp0build\%PS_ARCH%\%PS_CONFIG%\%PS_DEPS_PATH_FILE_NAME%" "%PS_DEPS_PATH_FILE_FOR_CONFIG%" +echo f|xcopy /D "%~dp0build\%PS_ARCH%\%PS_CONFIG%\%PS_DEPS_PATH_FILE_NAME%" "%PS_DEPS_PATH_FILE_FOR_CONFIG%" > nul 2> nul CALL :CANONICALIZE_PATH PS_DEPS_PATH_FILE_FOR_CONFIG IF EXIST "%PS_DEPS_PATH_FILE_FOR_CONFIG%" ( FOR /F "tokens=* USEBACKQ" %%I IN ("%PS_DEPS_PATH_FILE_FOR_CONFIG%") DO ( diff --git a/deps/wxWidgets/wxWidgets.cmake b/deps/wxWidgets/wxWidgets.cmake index 73d841014d..62222f8127 100644 --- a/deps/wxWidgets/wxWidgets.cmake +++ b/deps/wxWidgets/wxWidgets.cmake @@ -13,7 +13,7 @@ prusaslicer_add_cmake_project(wxWidgets # GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets" # GIT_TAG tm_cross_compile #${_wx_git_tag} URL https://github.com/prusa3d/wxWidgets/archive/refs/heads/v3.1.4-patched.zip - URL_HASH SHA256=ed36a2159c781cce07b06378664e683ebd8cb2f51914aba9acd3bfca3d63d7d3 + URL_HASH SHA256=78adc312e645d738945172d5ddcee16b1a55cca08e82b43379192262377a206a DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} dep_TIFF dep_JPEG CMAKE_ARGS -DwxBUILD_PRECOMP=ON diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 6d082a431a..f74775a9a9 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -498,7 +498,7 @@ Point SeamPlacer::calculate_seam(const Layer& layer, const SeamPosition seam_pos else if (seam_position == spRear) { // Object is centered around (0,0) in its current coordinate system. last_pos.x() = 0; - last_pos.y() += coord_t(3. * po->bounding_box().radius()); + last_pos.y() = coord_t(3. * po->bounding_box().radius()); last_pos_weight = 5.f; } if (seam_position == spNearest) { // last_pos already contains current nozzle position diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index 5c661ed68b..d273fde963 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -92,6 +92,25 @@ void Layer::restore_untyped_slices() } } +// Similar to Layer::restore_untyped_slices() +// To improve robustness of detect_surfaces_type() when reslicing (working with typed slices), see GH issue #7442. +// Only resetting layerm->slices if Slice::extra_perimeters is always zero or it will not be used anymore +// after the perimeter generator. +void Layer::restore_untyped_slices_no_extra_perimeters() +{ + if (layer_needs_raw_backup(this)) { + for (LayerRegion *layerm : m_regions) + if (! layerm->region().config().extra_perimeters.value) + layerm->slices.set(layerm->raw_slices, stInternal); + } else { + assert(m_regions.size() == 1); + LayerRegion *layerm = m_regions.front(); + // This optimization is correct, as extra_perimeters are only reused by prepare_infill() with multi-regions. + //if (! layerm->region().config().extra_perimeters.value) + layerm->slices.set(this->lslices, stInternal); + } +} + ExPolygons Layer::merged(float offset_scaled) const { assert(offset_scaled >= 0.f); @@ -179,7 +198,7 @@ void Layer::make_perimeters() // group slices (surfaces) according to number of extra perimeters std::map slices; // extra_perimeters => [ surface, surface... ] for (LayerRegion *layerm : layerms) { - for (Surface &surface : layerm->slices.surfaces) + for (const Surface &surface : layerm->slices.surfaces) slices[surface.extra_perimeters].emplace_back(surface); if (layerm->region().config().fill_density > layerm_config->region().config().fill_density) layerm_config = layerm; diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 516b6da9b1..0071c7f6e1 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -137,6 +137,8 @@ public: //FIXME Review whether not to simplify the code by keeping the raw_slices all the time. void backup_untyped_slices(); void restore_untyped_slices(); + // To improve robustness of detect_surfaces_type() when reslicing (working with typed slices), see GH issue #7442. + void restore_untyped_slices_no_extra_perimeters(); // Slices merged into islands, to be used by the elephant foot compensation to trim the individual surfaces with the shrunk merged slices. ExPolygons merged(float offset) const; template bool any_internal_region_slice_contains(const T &item) const { diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 4dbffe7b0f..fd29d6d54c 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -51,8 +51,7 @@ void LayerRegion::slices_to_fill_surfaces_clipped() // so we're safe. This guarantees idempotence of prepare_infill() also in case // that combine_infill() turns some fill_surface into VOID surfaces. // Collect polygons per surface type. - std::vector by_surface; - by_surface.assign(size_t(stCount), SurfacesPtr()); + std::array by_surface; for (Surface &surface : this->slices.surfaces) by_surface[size_t(surface.surface_type)].emplace_back(&surface); // Trim surfaces by the fill_boundaries. diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 011539aa4e..b2e6457143 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1210,6 +1210,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("When printing with very low layer heights, you might still want to print a thicker " "bottom layer to improve adhesion and tolerance for non perfect build plates."); def->sidetext = L("mm"); + def->min = 0; def->ratio_over = "layer_height"; def->set_default_value(new ConfigOptionFloatOrPercent(0.35, false)); @@ -3772,7 +3773,7 @@ void PrintConfigDef::init_sla_params() def->enum_labels.push_back(L("Slow")); def->enum_labels.push_back(L("Fast")); def->mode = comAdvanced; - def->set_default_value(new ConfigOptionEnum(slamsSlow)); + def->set_default_value(new ConfigOptionEnum(slamsFast)); } void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index eee0c2abab..05b8c9eb68 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -6,6 +6,7 @@ #include "Geometry.hpp" #include "I18N.hpp" #include "Layer.hpp" +#include "MutablePolygon.hpp" #include "SupportMaterial.hpp" #include "Surface.hpp" #include "Slicing.hpp" @@ -226,6 +227,17 @@ void PrintObject::prepare_infill() m_print->set_status(30, L("Preparing infill")); + if (m_typed_slices) { + // To improve robustness of detect_surfaces_type() when reslicing (working with typed slices), see GH issue #7442. + // The preceding step (perimeter generator) only modifies extra_perimeters and the extra perimeters are only used by discover_vertical_shells() + // with more than a single region. If this step does not use Surface::extra_perimeters or Surface::extra_perimeters is always zero, it is safe + // to reset to the untyped slices before re-runnning detect_surfaces_type(). + for (Layer* layer : m_layers) { + layer->restore_untyped_slices_no_extra_perimeters(); + m_print->throw_if_canceled(); + } + } + // This will assign a type (top/bottom/internal) to $layerm->slices. // Then the classifcation of $layerm->slices is transfered onto // the $layerm->fill_surfaces by clipping $layerm->fill_surfaces @@ -1709,9 +1721,6 @@ void PrintObject::clip_fill_surfaces() Layer *layer = m_layers[layer_id]; Layer *lower_layer = m_layers[layer_id - 1]; // Detect things that we need to support. - // Cummulative slices. - Polygons slices; - polygons_append(slices, layer->lslices); // Cummulative fill surfaces. Polygons fill_surfaces; // Solid surfaces to be supported. @@ -1736,7 +1745,7 @@ void PrintObject::clip_fill_surfaces() { // Get perimeters area as the difference between slices and fill_surfaces // Only consider the area that is not supported by lower perimeters - Polygons perimeters = intersection(diff(slices, fill_surfaces), lower_layer_fill_surfaces); + Polygons perimeters = intersection(diff(layer->lslices, fill_surfaces), lower_layer_fill_surfaces); // Only consider perimeter areas that are at least one extrusion width thick. //FIXME Offset2 eats out from both sides, while the perimeters are create outside in. //Should the pw not be half of the current value? @@ -1746,9 +1755,15 @@ void PrintObject::clip_fill_surfaces() // Append such thick perimeters to the areas that need support polygons_append(overhangs, opening(perimeters, pw)); } - // Find new internal infill. - polygons_append(overhangs, std::move(upper_internal)); - upper_internal = intersection(overhangs, lower_layer_internal_surfaces); + // Merge the new overhangs, find new internal infill. + polygons_append(upper_internal, std::move(overhangs)); + static constexpr const auto closing_radius = scaled(2.f); + upper_internal = intersection( + // Regularize the overhang regions, so that the infill areas will not become excessively jagged. + smooth_outward( + closing(upper_internal, closing_radius, ClipperLib::jtSquare, 0.), + scaled(0.1)), + lower_layer_internal_surfaces); // Apply new internal infill to regions. for (LayerRegion *layerm : lower_layer->m_regions) { if (layerm->region().config().fill_density.value == 0) diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 0b2af37b8e..fe108b1a6b 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -47,8 +47,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con if (config->opt_float("layer_height") < EPSILON) { const wxString msg_text = _(L("Layer height is not valid.\n\nThe layer height will be reset to 0.01.")); - //wxMessageDialog dialog(nullptr, msg_text, _(L("Layer height")), wxICON_WARNING | wxOK); - MessageDialog dialog(nullptr, msg_text, _(L("Layer height")), wxICON_WARNING | wxOK); + MessageDialog dialog(m_msg_dlg_parent, msg_text, _(L("Layer height")), wxICON_WARNING | wxOK); DynamicPrintConfig new_conf = *config; is_msg_dlg_already_exist = true; dialog.ShowModal(); @@ -60,8 +59,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con if (config->option("first_layer_height")->value < EPSILON) { const wxString msg_text = _(L("First layer height is not valid.\n\nThe first layer height will be reset to 0.01.")); - //wxMessageDialog dialog(nullptr, msg_text, _(L("First layer height")), wxICON_WARNING | wxOK); - MessageDialog dialog(nullptr, msg_text, _(L("First layer height")), wxICON_WARNING | wxOK); + MessageDialog dialog(m_msg_dlg_parent, msg_text, _(L("First layer height")), wxICON_WARNING | wxOK); DynamicPrintConfig new_conf = *config; is_msg_dlg_already_exist = true; dialog.ShowModal(); @@ -90,8 +88,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con "- Detect thin walls disabled")); if (is_global_config) msg_text += "\n\n" + _(L("Shall I adjust those settings in order to enable Spiral Vase?")); - //wxMessageDialog dialog(nullptr, msg_text, _(L("Spiral Vase")), - MessageDialog dialog(nullptr, msg_text, _(L("Spiral Vase")), + MessageDialog dialog(m_msg_dlg_parent, msg_text, _(L("Spiral Vase")), wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK)); DynamicPrintConfig new_conf = *config; auto answer = dialog.ShowModal(); @@ -126,8 +123,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con "(both support_material_extruder and support_material_interface_extruder need to be set to 0).")); if (is_global_config) msg_text += "\n\n" + _(L("Shall I adjust those settings in order to enable the Wipe Tower?")); - //wxMessageDialog dialog (nullptr, msg_text, _(L("Wipe Tower")), - MessageDialog dialog (nullptr, msg_text, _(L("Wipe Tower")), + MessageDialog dialog (m_msg_dlg_parent, msg_text, _(L("Wipe Tower")), wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK)); DynamicPrintConfig new_conf = *config; auto answer = dialog.ShowModal(); @@ -147,8 +143,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con "need to be synchronized with the object layers.")); if (is_global_config) msg_text += "\n\n" + _(L("Shall I synchronize support layers in order to enable the Wipe Tower?")); - //wxMessageDialog dialog(nullptr, msg_text, _(L("Wipe Tower")), - MessageDialog dialog(nullptr, msg_text, _(L("Wipe Tower")), + MessageDialog dialog(m_msg_dlg_parent, msg_text, _(L("Wipe Tower")), wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK)); DynamicPrintConfig new_conf = *config; auto answer = dialog.ShowModal(); @@ -169,7 +164,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con "- Detect bridging perimeters")); if (is_global_config) msg_text += "\n\n" + _(L("Shall I adjust those settings for supports?")); - MessageDialog dialog(nullptr, msg_text, _L("Support Generator"), wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK)); + MessageDialog dialog(m_msg_dlg_parent, msg_text, _L("Support Generator"), wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK)); DynamicPrintConfig new_conf = *config; auto answer = dialog.ShowModal(); if (!is_global_config || answer == wxID_YES) { @@ -200,8 +195,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con _(fill_pattern_def->enum_labels[it_pattern - fill_pattern_def->enum_values.begin()])); if (is_global_config) msg_text += "\n\n" + _L("Shall I switch to rectilinear fill pattern?"); - //wxMessageDialog dialog(nullptr, msg_text, _L("Infill"), - MessageDialog dialog(nullptr, msg_text, _L("Infill"), + MessageDialog dialog(m_msg_dlg_parent, msg_text, _L("Infill"), wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK) ); DynamicPrintConfig new_conf = *config; auto answer = dialog.ShowModal(); @@ -331,8 +325,7 @@ void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, con if (head_penetration > head_width) { wxString msg_text = _(L("Head penetration should not be greater than the head width.")); - //wxMessageDialog dialog(nullptr, msg_text, _(L("Invalid Head penetration")), wxICON_WARNING | wxOK); - MessageDialog dialog(nullptr, msg_text, _(L("Invalid Head penetration")), wxICON_WARNING | wxOK); + MessageDialog dialog(m_msg_dlg_parent, msg_text, _(L("Invalid Head penetration")), wxICON_WARNING | wxOK); DynamicPrintConfig new_conf = *config; if (dialog.ShowModal() == wxID_OK) { new_conf.set_key_value("support_head_penetration", new ConfigOptionFloat(head_width)); @@ -345,8 +338,7 @@ void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, con if (pinhead_d > pillar_d) { wxString msg_text = _(L("Pinhead diameter should be smaller than the pillar diameter.")); - //wxMessageDialog dialog(nullptr, msg_text, _(L("Invalid pinhead diameter")), wxICON_WARNING | wxOK); - MessageDialog dialog(nullptr, msg_text, _(L("Invalid pinhead diameter")), wxICON_WARNING | wxOK); + MessageDialog dialog(m_msg_dlg_parent, msg_text, _(L("Invalid pinhead diameter")), wxICON_WARNING | wxOK); DynamicPrintConfig new_conf = *config; if (dialog.ShowModal() == wxID_OK) { diff --git a/src/slic3r/GUI/ConfigManipulation.hpp b/src/slic3r/GUI/ConfigManipulation.hpp index 0e6815753e..32ddb52a91 100644 --- a/src/slic3r/GUI/ConfigManipulation.hpp +++ b/src/slic3r/GUI/ConfigManipulation.hpp @@ -30,15 +30,18 @@ class ConfigManipulation // callback to propagation of changed value, if needed std::function cb_value_change = nullptr; ModelConfig* local_config = nullptr; + wxWindow* m_msg_dlg_parent {nullptr}; public: ConfigManipulation(std::function load_config, std::function cb_toggle_field, std::function cb_value_change, - ModelConfig* local_config = nullptr) : + ModelConfig* local_config = nullptr, + wxWindow* msg_dlg_parent = nullptr) : load_config(load_config), cb_toggle_field(cb_toggle_field), cb_value_change(cb_value_change), + m_msg_dlg_parent(msg_dlg_parent), local_config(local_config) {} ConfigManipulation() {} diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 67f78d26ed..5ec622b872 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -291,6 +291,16 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true case coString: case coStrings: case coFloatOrPercent: { + if (m_opt.type == coFloatOrPercent && m_opt.opt_key == "first_layer_height" && !str.IsEmpty() && str.Last() == '%') { + // Workaroud to avoid of using of the % for first layer height + // see https://github.com/prusa3d/PrusaSlicer/issues/7418 + wxString label = m_opt.full_label.empty() ? _(m_opt.label) : _(m_opt.full_label); + show_error(m_parent, from_u8((boost::format(_utf8(L("%s doesn't support percentage"))) % label).str())); + const wxString stVal = double_to_string(0.01, 2); + set_value(stVal, true); + m_value = into_u8(stVal);; + break; + } if (m_opt.type == coFloatOrPercent && !str.IsEmpty() && str.Last() != '%') { double val = 0.; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index cdc9c33a4f..574ba6ec53 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -977,8 +977,7 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const for (const RenderPath& path : t_buffer.render_paths) { colors.push_back(path.color); } - std::sort(colors.begin(), colors.end()); - colors.erase(std::unique(colors.begin(), colors.end()), colors.end()); + sort_remove_duplicates(colors); // save materials file boost::filesystem::path mat_filename(filename); @@ -1447,7 +1446,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) static const unsigned int progress_threshold = 1000; wxProgressDialog* progress_dialog = wxGetApp().is_gcode_viewer() ? new wxProgressDialog(_L("Generating toolpaths"), "...", - 100, wxGetApp().plater(), wxPD_AUTO_HIDE | wxPD_APP_MODAL) : nullptr; + 100, wxGetApp().mainframe, wxPD_AUTO_HIDE | wxPD_APP_MODAL) : nullptr; wxBusyCursor busy; @@ -2020,13 +2019,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) } // 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()); + sort_remove_duplicates(m_roles); m_roles.shrink_to_fit(); // 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()); + sort_remove_duplicates(m_extruder_ids); m_extruder_ids.shrink_to_fit(); // set layers z range @@ -2374,8 +2371,10 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool } RenderPath key{ tbuffer_id, color, static_cast(ibuffer_id), path_id }; - if (render_path == nullptr || !RenderPathPropertyEqual()(*render_path, key)) - render_path = const_cast(&(*buffer.render_paths.emplace(key).first)); + if (render_path == nullptr || !RenderPathPropertyEqual()(*render_path, key)) { + buffer.render_paths.emplace_back(key); + render_path = const_cast(&buffer.render_paths.back()); + } unsigned int delta_1st = 0; if (sub_path.first.s_id < m_sequential_view.current.first && m_sequential_view.current.first <= sub_path.last.s_id) @@ -2433,6 +2432,14 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool #endif } + // Removes empty render paths and sort. + for (size_t b = 0; b < m_buffers.size(); ++b) { + TBuffer* buffer = const_cast(&m_buffers[b]); + buffer->render_paths.erase(std::remove_if(buffer->render_paths.begin(), buffer->render_paths.end(), + [](const auto &path){ return path.sizes.empty() || path.offsets.empty(); }), + buffer->render_paths.end()); + } + // second pass: for buffers using instanced and batched models, update the instances render ranges for (size_t b = 0; b < m_buffers.size(); ++b) { TBuffer& buffer = const_cast(m_buffers[b]); @@ -2624,12 +2631,7 @@ void GCodeViewer::render_toolpaths() float near_plane_height = camera.get_type() == Camera::EType::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; -#if ENABLE_GCODE_VIEWER_STATISTICS - auto render_as_points = [this, zoom, point_size, near_plane_height] -#else - auto render_as_points = [zoom, point_size, near_plane_height] -#endif // ENABLE_GCODE_VIEWER_STATISTICS - (const TBuffer& buffer, unsigned int ibuffer_id, GLShaderProgram& shader) { + auto shader_init_as_points = [zoom, point_size, near_plane_height](GLShaderProgram& shader) { #if ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS shader.set_uniform("use_fixed_screen_size", 1); #else @@ -2640,65 +2642,67 @@ void GCodeViewer::render_toolpaths() shader.set_uniform("percent_center_radius", 0.33f); shader.set_uniform("point_size", point_size); shader.set_uniform("near_plane_height", near_plane_height); + }; + auto render_as_points = [ +#if ENABLE_GCODE_VIEWER_STATISTICS + this +#endif // ENABLE_GCODE_VIEWER_STATISTICS + ](std::vector::iterator it_path, std::vector::iterator it_end, GLShaderProgram& shader, int uniform_color) { glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); glsafe(::glEnable(GL_POINT_SPRITE)); - for (const RenderPath& path : buffer.render_paths) { - if (path.ibuffer_id == ibuffer_id) { - shader.set_uniform("uniform_color", path.color); - glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); + for (auto it = it_path; it != it_end && it_path->ibuffer_id == it->ibuffer_id; ++it) { + const RenderPath& path = *it; + glsafe(::glUniform4fv(uniform_color, 1, static_cast(path.color.data()))); + glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.gl_multi_points_calls_count; + ++m_statistics.gl_multi_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS - } } glsafe(::glDisable(GL_POINT_SPRITE)); glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); }; -#if ENABLE_GCODE_VIEWER_STATISTICS - auto render_as_lines = [this, light_intensity] -#else - auto render_as_lines = [light_intensity] -#endif // ENABLE_GCODE_VIEWER_STATISTICS - (const TBuffer& buffer, unsigned int ibuffer_id, GLShaderProgram& shader) { + auto shader_init_as_lines = [light_intensity](GLShaderProgram &shader) { shader.set_uniform("light_intensity", light_intensity); - for (const RenderPath& path : buffer.render_paths) { - if (path.ibuffer_id == ibuffer_id) { - shader.set_uniform("uniform_color", path.color); - glsafe(::glMultiDrawElements(GL_LINES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); + }; + auto render_as_lines = [ #if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.gl_multi_lines_calls_count; + this +#endif // ENABLE_GCODE_VIEWER_STATISTICS + ](std::vector::iterator it_path, std::vector::iterator it_end, GLShaderProgram& shader, int uniform_color) { + for (auto it = it_path; it != it_end && it_path->ibuffer_id == it->ibuffer_id; ++it) { + const RenderPath& path = *it; + glsafe(::glUniform4fv(uniform_color, 1, static_cast(path.color.data()))); + glsafe(::glMultiDrawElements(GL_LINES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_multi_lines_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS - } } }; + auto render_as_triangles = [ #if ENABLE_GCODE_VIEWER_STATISTICS - auto render_as_triangles = [this] -#else - auto render_as_triangles = [] + this #endif // ENABLE_GCODE_VIEWER_STATISTICS - (const TBuffer& buffer, unsigned int ibuffer_id, GLShaderProgram& shader) { - for (const RenderPath& path : buffer.render_paths) { - if (path.ibuffer_id == ibuffer_id) { - shader.set_uniform("uniform_color", path.color); - glsafe(::glMultiDrawElements(GL_TRIANGLES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); + ](std::vector::iterator it_path, std::vector::iterator it_end, GLShaderProgram& shader, int uniform_color) { + for (auto it = it_path; it != it_end && it_path->ibuffer_id == it->ibuffer_id; ++it) { + const RenderPath& path = *it; + glsafe(::glUniform4fv(uniform_color, 1, static_cast(path.color.data()))); + glsafe(::glMultiDrawElements(GL_TRIANGLES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.gl_multi_triangles_calls_count; + ++m_statistics.gl_multi_triangles_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS - } } }; + auto render_as_instanced_model = [ #if ENABLE_GCODE_VIEWER_STATISTICS - auto render_as_instanced_model = [this] -#else - auto render_as_instanced_model = [] + this #endif // ENABLE_GCODE_VIEWER_STATISTICS - (TBuffer& buffer, GLShaderProgram & shader) { + ](TBuffer& buffer, GLShaderProgram & shader) { for (auto& range : buffer.model.instances.render_ranges.ranges) { if (range.vbo == 0 && range.count > 0) { glsafe(::glGenBuffers(1, &range.vbo)); @@ -2803,8 +2807,20 @@ void GCodeViewer::render_toolpaths() shader->set_uniform("emission_factor", 0.0f); } else { - for (size_t j = 0; j < buffer.indices.size(); ++j) { - const IBuffer& i_buffer = buffer.indices[j]; + switch (buffer.render_primitive_type) { + case TBuffer::ERenderPrimitiveType::Point: shader_init_as_points(*shader); break; + case TBuffer::ERenderPrimitiveType::Line: shader_init_as_lines(*shader); break; + default: break; + } + int uniform_color = shader->get_uniform_location("uniform_color"); + auto it_path = buffer.render_paths.begin(); + for (unsigned int ibuffer_id = 0; ibuffer_id < static_cast(buffer.indices.size()); ++ibuffer_id) { + const IBuffer& i_buffer = buffer.indices[ibuffer_id]; + // Skip all paths with ibuffer_id < ibuffer_id. + for (; it_path != buffer.render_paths.end() && it_path->ibuffer_id < ibuffer_id; ++ it_path) ; + if (it_path == buffer.render_paths.end() || it_path->ibuffer_id > ibuffer_id) + // Not found. This shall not happen. + continue; glsafe(::glBindBuffer(GL_ARRAY_BUFFER, i_buffer.vbo)); glsafe(::glVertexPointer(buffer.vertices.position_size_floats(), GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.position_offset_bytes())); @@ -2817,19 +2833,20 @@ void GCodeViewer::render_toolpaths() glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_buffer.ibo)); + // Render all elements with it_path->ibuffer_id == ibuffer_id, possible with varying colors. switch (buffer.render_primitive_type) { case TBuffer::ERenderPrimitiveType::Point: { - render_as_points(buffer, static_cast(j), *shader); + render_as_points(it_path, buffer.render_paths.end(), *shader, uniform_color); break; } case TBuffer::ERenderPrimitiveType::Line: { glsafe(::glLineWidth(static_cast(line_width(zoom)))); - render_as_lines(buffer, static_cast(j), *shader); + render_as_lines(it_path, buffer.render_paths.end(), *shader, uniform_color); break; } case TBuffer::ERenderPrimitiveType::Triangle: { - render_as_triangles(buffer, static_cast(j), *shader); + render_as_triangles(it_path, buffer.render_paths.end(), *shader, uniform_color); break; } default: { break; } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 9147eec84d..c56e88c880 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -259,14 +259,6 @@ class GCodeViewer return false; } }; -// // for unordered_set implementation of render_paths -// struct RenderPathPropertyHash { -// size_t operator() (const RenderPath &p) const { -// // Convert the RGB value to an integer hash. -//// return (size_t(int(p.color[0] * 255) + 255 * int(p.color[1] * 255) + (255 * 255) * int(p.color[2] * 255)) * 7919) ^ size_t(p.ibuffer_id); -// return size_t(int(p.color[0] * 255) + 255 * int(p.color[1] * 255) + (255 * 255) * int(p.color[2] * 255)) ^ size_t(p.ibuffer_id); -// } -// }; struct RenderPathPropertyLower { bool operator() (const RenderPath &l, const RenderPath &r) const { if (l.tbuffer_id < r.tbuffer_id) @@ -319,9 +311,7 @@ class GCodeViewer std::string shader; std::vector paths; - // std::set seems to perform significantly better, at least on Windows. -// std::unordered_set render_paths; - std::set render_paths; + std::vector render_paths; bool visible{ false }; void reset(); diff --git a/src/slic3r/GUI/GLShader.cpp b/src/slic3r/GUI/GLShader.cpp index 0473e53445..9c1e936525 100644 --- a/src/slic3r/GUI/GLShader.cpp +++ b/src/slic3r/GUI/GLShader.cpp @@ -358,12 +358,38 @@ bool GLShaderProgram::set_uniform(const char* name, const Vec3d& value) const int GLShaderProgram::get_attrib_location(const char* name) const { - return (m_id > 0) ? ::glGetAttribLocation(m_id, name) : -1; + assert(m_id > 0); + + if (m_id <= 0) + // Shader program not loaded. This should not happen. + return -1; + + auto it = std::find_if(m_attrib_location_cache.begin(), m_attrib_location_cache.end(), [name](const auto& p) { return p.first == name; }); + if (it != m_attrib_location_cache.end()) + // Attrib ID cached. + return it->second; + + int id = ::glGetAttribLocation(m_id, name); + const_cast(this)->m_attrib_location_cache.push_back({ name, id }); + return id; } int GLShaderProgram::get_uniform_location(const char* name) const { - return (m_id > 0) ? ::glGetUniformLocation(m_id, name) : -1; + assert(m_id > 0); + + if (m_id <= 0) + // Shader program not loaded. This should not happen. + return -1; + + auto it = std::find_if(m_uniform_location_cache.begin(), m_uniform_location_cache.end(), [name](const auto &p) { return p.first == name; }); + if (it != m_uniform_location_cache.end()) + // Uniform ID cached. + return it->second; + + int id = ::glGetUniformLocation(m_id, name); + const_cast(this)->m_uniform_location_cache.push_back({ name, id }); + return id; } } // namespace Slic3r diff --git a/src/slic3r/GUI/GLShader.hpp b/src/slic3r/GUI/GLShader.hpp index c92ea274aa..d7b92000df 100644 --- a/src/slic3r/GUI/GLShader.hpp +++ b/src/slic3r/GUI/GLShader.hpp @@ -29,6 +29,8 @@ public: private: std::string m_name; unsigned int m_id{ 0 }; + std::vector> m_attrib_location_cache; + std::vector> m_uniform_location_cache; public: ~GLShaderProgram(); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index c7654fcd65..bda7394ed6 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -883,6 +883,8 @@ void GUI_App::init_app_config() dir = wxFileName::GetHomeDir() + wxS("/.config"); set_data_dir((dir + "/" + GetAppName()).ToUTF8().data()); #endif + } else { + m_datadir_redefined = true; } if (!app_config) @@ -915,6 +917,10 @@ void GUI_App::init_app_config() // returns true if found newer version and user agreed to use it bool GUI_App::check_older_app_config(Semver current_version, bool backup) { + // If the config folder is redefined - do not check + if (m_datadir_redefined) + return false; + // find other version app config (alpha / beta / release) std::string config_path = app_config->config_path(); boost::filesystem::path parent_file_path(config_path); diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 82feb32827..b1b0a77862 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -352,6 +352,7 @@ private: void check_updates(const bool verbose); bool m_init_app_config_from_older { false }; + bool m_datadir_redefined { false }; std::string m_older_data_dir_path; boost::optional m_last_config_version; }; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 7d689ff6a2..f4d27b0b27 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1446,7 +1446,7 @@ void ObjectList::load_part(ModelObject& model_object, std::vector& else wxGetApp().import_model(parent, input_files); - wxProgressDialog dlg(_L("Loading") + dots, "", 100, wxGetApp().plater(), wxPD_AUTO_HIDE); + wxProgressDialog dlg(_L("Loading") + dots, "", 100, wxGetApp().mainframe wxPD_AUTO_HIDE); wxBusyCursor busy; for (size_t i = 0; i < input_files.size(); ++i) { @@ -1506,7 +1506,7 @@ void ObjectList::load_modifier(ModelObject& model_object, std::vectorLayout(); }); - - /* Check overriden options list after deleting. - * Some options couldn't be deleted because of another one. - * Like, we couldn't delete fill pattern, if fill density is set to 100% - */ - update_config_values(config); }); return btn; }; @@ -227,11 +221,12 @@ void ObjectSettings::update_config_values(ModelConfig* config) update_config_values(config); if (is_added) { - wxTheApp->CallAfter([this]() { +// #ysFIXME - Delete after testing! Very likely this CallAfret is no needed +// wxTheApp->CallAfter([this]() { wxWindowUpdateLocker noUpdates(m_parent); update_settings_list(); m_parent->Layout(); - }); +// }); } }; @@ -253,9 +248,9 @@ void ObjectSettings::update_config_values(ModelConfig* config) { const int obj_idx = objects_model->GetObjectIdByItem(item); assert(obj_idx >= 0); + // for object's part first of all update konfiguration from object main_config.apply(wxGetApp().model().objects[obj_idx]->config.get(), true); - printer_technology == ptFFF ? config_manipulation.update_print_fff_config(&main_config) : - config_manipulation.update_print_sla_config(&main_config) ; + // and then from its own config } main_config.apply(config->get(), true); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 614e838114..691a867063 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -550,24 +550,24 @@ RENDER_AGAIN: ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x); ImGui::PushItemWidth(window_width - settings_sliders_left); m_imgui->slider_float("##offset", &offset, offset_min, offset_max, "%.1f mm"); - if (ImGui::IsItemHovered()) + if (m_imgui->get_last_slider_status().hovered) m_imgui->tooltip((_utf8(opts[0].second->tooltip)).c_str(), max_tooltip_width); - bool slider_clicked = ImGui::IsItemClicked(); // someone clicked the slider - bool slider_edited = ImGui::IsItemEdited(); // someone is dragging the slider - bool slider_released = ImGui::IsItemDeactivatedAfterEdit(); // someone has just released the slider + bool slider_clicked = m_imgui->get_last_slider_status().clicked; // someone clicked the slider + bool slider_edited =m_imgui->get_last_slider_status().edited; // someone is dragging the slider + bool slider_released =m_imgui->get_last_slider_status().deactivated_after_edit; // someone has just released the slider if (current_mode >= quality_mode) { ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("quality")); ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x); m_imgui->slider_float("##quality", &quality, quality_min, quality_max, "%.1f"); - if (ImGui::IsItemHovered()) + if (m_imgui->get_last_slider_status().hovered) m_imgui->tooltip((_utf8(opts[1].second->tooltip)).c_str(), max_tooltip_width); - slider_clicked |= ImGui::IsItemClicked(); - slider_edited |= ImGui::IsItemEdited(); - slider_released |= ImGui::IsItemDeactivatedAfterEdit(); + slider_clicked |= m_imgui->get_last_slider_status().clicked; + slider_edited |= m_imgui->get_last_slider_status().edited; + slider_released |= m_imgui->get_last_slider_status().deactivated_after_edit; } if (current_mode >= closing_d_mode) { @@ -575,12 +575,12 @@ RENDER_AGAIN: m_imgui->text(m_desc.at("closing_distance")); ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x); m_imgui->slider_float("##closing_distance", &closing_d, closing_d_min, closing_d_max, "%.1f mm"); - if (ImGui::IsItemHovered()) + if (m_imgui->get_last_slider_status().hovered) m_imgui->tooltip((_utf8(opts[2].second->tooltip)).c_str(), max_tooltip_width); - slider_clicked |= ImGui::IsItemClicked(); - slider_edited |= ImGui::IsItemEdited(); - slider_released |= ImGui::IsItemDeactivatedAfterEdit(); + slider_clicked |= m_imgui->get_last_slider_status().clicked; + slider_edited |= m_imgui->get_last_slider_status().edited; + slider_released |= m_imgui->get_last_slider_status().deactivated_after_edit; } if (slider_clicked) { @@ -627,9 +627,9 @@ RENDER_AGAIN: //complete non-sense. diam = std::clamp(diam, 0.1f, diameter_upper_cap); m_new_hole_radius = diam / 2.f; - bool clicked = ImGui::IsItemClicked(); - bool edited = ImGui::IsItemEdited(); - bool deactivated = ImGui::IsItemDeactivatedAfterEdit(); + bool clicked = m_imgui->get_last_slider_status().clicked; + bool edited = m_imgui->get_last_slider_status().edited; + bool deactivated = m_imgui->get_last_slider_status().deactivated_after_edit; ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc["hole_depth"]); @@ -638,9 +638,9 @@ RENDER_AGAIN: // Same as above: m_new_hole_height = std::clamp(m_new_hole_height, 0.f, 100.f); - clicked |= ImGui::IsItemClicked(); - edited |= ImGui::IsItemEdited(); - deactivated |= ImGui::IsItemDeactivatedAfterEdit(); + clicked |= m_imgui->get_last_slider_status().clicked; + edited |= m_imgui->get_last_slider_status().edited; + deactivated |= m_imgui->get_last_slider_status().deactivated_after_edit;; // Following is a nasty way to: // - save the initial value of the slider before one starts messing with it diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index e164caee6d..8b866c7c99 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -859,9 +859,9 @@ void GLPaintContour::finalize_geometry() if (!this->contour_indices.empty()) { glsafe(::glGenBuffers(1, &this->m_contour_EBO_id)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->m_contour_EBO_id)); - glsafe(::glBufferData(GL_ARRAY_BUFFER, this->contour_indices.size() * sizeof(unsigned int), this->contour_indices.data(), GL_STATIC_DRAW)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->m_contour_EBO_id)); + glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->contour_indices.size() * sizeof(unsigned int), this->contour_indices.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); this->contour_indices.clear(); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 9170db6039..29e1fd2f30 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -687,16 +687,16 @@ RENDER_AGAIN: // - take correct undo/redo snapshot after the user is done with moving the slider float initial_value = m_new_point_head_diameter; m_imgui->slider_float("##head_diameter", &m_new_point_head_diameter, 0.1f, diameter_upper_cap, "%.1f"); - if (ImGui::IsItemClicked()) { + if (m_imgui->get_last_slider_status().clicked) { if (m_old_point_head_diameter == 0.f) m_old_point_head_diameter = initial_value; } - if (ImGui::IsItemEdited()) { + if (m_imgui->get_last_slider_status().edited) { for (auto& cache_entry : m_editing_cache) if (cache_entry.selected) cache_entry.support_point.head_front_radius = m_new_point_head_diameter / 2.f; } - if (ImGui::IsItemDeactivatedAfterEdit()) { + if (m_imgui->get_last_slider_status().deactivated_after_edit) { // momentarily restore the old value to take snapshot for (auto& cache_entry : m_editing_cache) if (cache_entry.selected) @@ -747,18 +747,18 @@ RENDER_AGAIN: float minimal_point_distance = static_cast(opts[1])->value; m_imgui->slider_float("##minimal_point_distance", &minimal_point_distance, 0.f, 20.f, "%.f mm"); - bool slider_clicked = ImGui::IsItemClicked(); // someone clicked the slider - bool slider_edited = ImGui::IsItemEdited(); // someone is dragging the slider - bool slider_released = ImGui::IsItemDeactivatedAfterEdit(); // someone has just released the slider + bool slider_clicked = m_imgui->get_last_slider_status().clicked; // someone clicked the slider + bool slider_edited = m_imgui->get_last_slider_status().edited; // someone is dragging the slider + bool slider_released = m_imgui->get_last_slider_status().deactivated_after_edit; // someone has just released the slider ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("points_density")); ImGui::SameLine(settings_sliders_left); m_imgui->slider_float("##points_density", &density, 0.f, 200.f, "%.f %%"); - slider_clicked |= ImGui::IsItemClicked(); - slider_edited |= ImGui::IsItemEdited(); - slider_released |= ImGui::IsItemDeactivatedAfterEdit(); + slider_clicked |= m_imgui->get_last_slider_status().clicked; + slider_edited |= m_imgui->get_last_slider_status().edited; + slider_released |= m_imgui->get_last_slider_status().deactivated_after_edit; if (slider_clicked) { // stash the values of the settings so we know what to revert to after undo m_minimal_point_distance_stash = minimal_point_distance; diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 325067aea0..a01e2ed697 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -503,6 +503,12 @@ bool ImGuiWrapper::slider_float(const char* label, float* v, float v_min, float str_label = str_label.substr(0, pos) + str_label.substr(pos + 2); bool ret = ImGui::SliderFloat(str_label.c_str(), v, v_min, v_max, format, power); + + m_last_slider_status.hovered = ImGui::IsItemHovered(); + m_last_slider_status.edited = ImGui::IsItemEdited(); + m_last_slider_status.clicked = ImGui::IsItemClicked(); + m_last_slider_status.deactivated_after_edit = ImGui::IsItemDeactivatedAfterEdit(); + if (!tooltip.empty() && ImGui::IsItemHovered()) this->tooltip(into_u8(tooltip).c_str(), max_tooltip_width); diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 590ef95b02..255c4171bf 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -39,6 +39,13 @@ class ImGuiWrapper std::string m_clipboard_text; public: + struct LastSliderStatus { + bool hovered { false }; + bool edited { false }; + bool clicked { false }; + bool deactivated_after_edit { false }; + }; + ImGuiWrapper(); ~ImGuiWrapper(); @@ -63,6 +70,7 @@ public: ImVec2 get_item_spacing() const; float get_slider_float_height() const; + const LastSliderStatus& get_last_slider_status() const { return m_last_slider_status; } 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); @@ -146,6 +154,8 @@ private: static const char* clipboard_get(void* user_data); static void clipboard_set(void* user_data, const char* text); + + LastSliderStatus m_last_slider_status; }; diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 32061fd32a..8a9702c400 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -2101,21 +2101,6 @@ void MainFrame::technology_changed() } -#if defined(__linux__) || defined(_WIN32) -// wxWidgets callback to enable / disable window and all its children windows. -// called by wxWindowDisabler when entering / leaving modal dialog loop. -// Unfortunately the wxWindowDisabler calls Enable(true) after the wxEVT_ACTIVATE event is processed -// while MainFrame is not yet enabled, thus restoring focus in OnActivate() handler fails -// and we need to do it now. -bool MainFrame::Enable(bool enable) -{ - bool retval = DPIFrame::Enable(enable); - if (enable && retval) - this->plater()->restore_keyboard_focus(); - return retval; -} -#endif - // // Called after the Preferences dialog is closed and the program settings are saved. // Update the UI based on the current preferences. diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 9e09bf0884..951ed70a1d 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -148,15 +148,6 @@ public: void update_title(); -#if defined(__linux__) || defined(_WIN32) - // wxWidgets callback to enable / disable window and all its children windows. - // called by wxWindowDisabler when entering / leaving modal dialog loop. - // Unfortunately the wxWindowDisabler calls Enable(true) after the wxEVT_ACTIVATE event is processed - // while MainFrame is not yet enabled, thus restoring focus in OnActivate() handler fails - // and we need to do it now. - bool Enable(bool enable = true) override; -#endif - void init_tabpanel(); void create_preset_tabs(); void add_created_tab(Tab* panel, const std::string& bmp_name = ""); @@ -211,7 +202,7 @@ public: SettingsDialog m_settings_dialog; DiffPresetDialog diff_dialog; wxWindow* m_plater_page{ nullptr }; - wxProgressDialog* m_progress_dialog { nullptr }; +// wxProgressDialog* m_progress_dialog { nullptr }; PrintHostQueueDialog* m_printhost_queue_dlg; // std::shared_ptr m_statusbar; diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index a505c2507d..56e9905774 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -136,7 +136,8 @@ static void add_msg_content(wxWindow* parent, wxBoxSizer* content_sizer, wxStrin int em = wxGetApp().em_unit(); // if message containes the table - if (msg.Contains("")) { + bool is_marked = msg.Contains(""); + if (is_marked) { int lines = msg.Freq('\n') + 1; int pos = 0; while (pos < (int)msg.Len() && pos != wxNOT_FOUND) { @@ -154,7 +155,7 @@ static void add_msg_content(wxWindow* parent, wxBoxSizer* content_sizer, wxStrin } html->SetMinSize(page_size); - std::string msg_escaped = xml_escape(msg.ToUTF8().data()); + std::string msg_escaped = xml_escape(msg.ToUTF8().data(), is_marked); boost::replace_all(msg_escaped, "\r\n", "
"); boost::replace_all(msg_escaped, "\n", "
"); if (monospaced_font) diff --git a/src/slic3r/GUI/OG_CustomCtrl.cpp b/src/slic3r/GUI/OG_CustomCtrl.cpp index e9153c70f4..ee19e5da54 100644 --- a/src/slic3r/GUI/OG_CustomCtrl.cpp +++ b/src/slic3r/GUI/OG_CustomCtrl.cpp @@ -79,7 +79,6 @@ void OG_CustomCtrl::init_ctrl_lines() // if we have a single option with no label, no sidetext just add it directly to sizer if (option_set.size() == 1 && opt_group->label_width == 0 && option_set.front().opt.full_width && - option_set.front().opt.label.empty() && option_set.front().opt.sidetext.size() == 0 && option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0) { @@ -157,7 +156,6 @@ wxPoint OG_CustomCtrl::get_pos(const Line& line, Field* field_in/* = nullptr*/) // If we have a single option with no sidetext const std::vector