mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-20 07:11:12 -06:00 
			
		
		
		
	Merge branch 'prusa3d:master' into ender2-pro
This commit is contained in:
		
						commit
						7e453380d6
					
				
					 36 changed files with 335 additions and 242 deletions
				
			
		|  | @ -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 ^<arch^>] [-CONFIG ^<config^>] [-DESTDIR ^<directory^>] | ||||
| @ECHO Usage: build_win [-ARCH ^<arch^>] [-CONFIG ^<config^>] [-VERSION ^<version^>] | ||||
| @ECHO                  [-PRODUCT ^<product^>] [-DESTDIR ^<directory^>] | ||||
| @ECHO                  [-STEPS ^<all^|all-dirty^|app^|app-dirty^|deps^|deps-dirty^>] | ||||
| @ECHO                  [-RUN ^<console^|custom^|none^|viewer^|window^>] | ||||
| @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 ( | ||||
|  |  | |||
							
								
								
									
										2
									
								
								deps/wxWidgets/wxWidgets.cmake
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								deps/wxWidgets/wxWidgets.cmake
									
										
									
									
										vendored
									
									
								
							|  | @ -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 | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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<unsigned short, Surfaces> 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; | ||||
|  |  | |||
|  | @ -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 <class T> bool any_internal_region_slice_contains(const T &item) const { | ||||
|  |  | |||
|  | @ -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<SurfacesPtr> by_surface; | ||||
|     by_surface.assign(size_t(stCount), SurfacesPtr()); | ||||
|     std::array<SurfacesPtr, size_t(stCount)> by_surface; | ||||
|     for (Surface &surface : this->slices.surfaces) | ||||
|         by_surface[size_t(surface.surface_type)].emplace_back(&surface); | ||||
|     // Trim surfaces by the fill_boundaries.
 | ||||
|  |  | |||
|  | @ -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<SLAMaterialSpeed>(slamsSlow)); | ||||
|     def->set_default_value(new ConfigOptionEnum<SLAMaterialSpeed>(slamsFast)); | ||||
| } | ||||
| 
 | ||||
| void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value) | ||||
|  |  | |||
|  | @ -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<float>(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<coord_t>(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) | ||||
|  |  | |||
|  | @ -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<ConfigOptionFloatOrPercent>("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) { | ||||
|  |  | |||
|  | @ -30,15 +30,18 @@ class ConfigManipulation | |||
|     // callback to propagation of changed value, if needed 
 | ||||
|     std::function<void(const std::string&, const boost::any&)>  cb_value_change = nullptr; | ||||
|     ModelConfig* local_config = nullptr; | ||||
|     wxWindow*    m_msg_dlg_parent {nullptr}; | ||||
| 
 | ||||
| public: | ||||
|     ConfigManipulation(std::function<void()> load_config, | ||||
|         std::function<void(const std::string&, bool toggle, int opt_index)> cb_toggle_field, | ||||
|         std::function<void(const std::string&, const boost::any&)>  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() {} | ||||
| 
 | ||||
|  |  | |||
|  | @ -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.; | ||||
|  |  | |||
|  | @ -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<unsigned int>(ibuffer_id), path_id }; | ||||
|         if (render_path == nullptr || !RenderPathPropertyEqual()(*render_path, key)) | ||||
|             render_path = const_cast<RenderPath*>(&(*buffer.render_paths.emplace(key).first)); | ||||
|         if (render_path == nullptr || !RenderPathPropertyEqual()(*render_path, key)) { | ||||
|             buffer.render_paths.emplace_back(key); | ||||
|             render_path = const_cast<RenderPath*>(&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<TBuffer*>(&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<TBuffer&>(m_buffers[b]); | ||||
|  | @ -2624,12 +2631,7 @@ void GCodeViewer::render_toolpaths() | |||
|     float near_plane_height = camera.get_type() == Camera::EType::Perspective ? static_cast<float>(viewport[3]) / (2.0f * static_cast<float>(2.0 * std::tan(0.5 * Geometry::deg2rad(camera.get_fov())))) : | ||||
|         static_cast<float>(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<RenderPath>::iterator it_path, std::vector<RenderPath>::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<const GLfloat*>(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<RenderPath>::iterator it_path, std::vector<RenderPath>::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<const GLfloat*>(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<RenderPath>::iterator it_path, std::vector<RenderPath>::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<const GLfloat*>(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<unsigned int>(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<unsigned int>(j), *shader); | ||||
|                         render_as_points(it_path, buffer.render_paths.end(), *shader, uniform_color); | ||||
|                         break; | ||||
|                     } | ||||
|                     case TBuffer::ERenderPrimitiveType::Line: { | ||||
|                         glsafe(::glLineWidth(static_cast<GLfloat>(line_width(zoom)))); | ||||
|                         render_as_lines(buffer, static_cast<unsigned int>(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<unsigned int>(j), *shader); | ||||
|                         render_as_triangles(it_path, buffer.render_paths.end(), *shader, uniform_color); | ||||
|                         break; | ||||
|                     } | ||||
|                     default: { break; } | ||||
|  |  | |||
|  | @ -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<Path> paths; | ||||
|         // std::set seems to perform significantly better, at least on Windows.
 | ||||
| //        std::unordered_set<RenderPath, RenderPathPropertyHash, RenderPathPropertyEqual> render_paths;
 | ||||
|         std::set<RenderPath, RenderPathPropertyLower> render_paths; | ||||
|         std::vector<RenderPath> render_paths; | ||||
|         bool visible{ false }; | ||||
| 
 | ||||
|         void reset(); | ||||
|  |  | |||
|  | @ -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<GLShaderProgram*>(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<GLShaderProgram*>(this)->m_uniform_location_cache.push_back({ name, id }); | ||||
|     return id; | ||||
| } | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
|  |  | |||
|  | @ -29,6 +29,8 @@ public: | |||
| private: | ||||
|     std::string m_name; | ||||
|     unsigned int m_id{ 0 }; | ||||
|     std::vector<std::pair<std::string, int>> m_attrib_location_cache; | ||||
|     std::vector<std::pair<std::string, int>> m_uniform_location_cache; | ||||
| 
 | ||||
| public: | ||||
|     ~GLShaderProgram(); | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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<Semver> m_last_config_version; | ||||
| }; | ||||
|  |  | |||
|  | @ -1446,7 +1446,7 @@ void ObjectList::load_part(ModelObject& model_object, std::vector<ModelVolume*>& | |||
|     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::vector<ModelVolum | |||
|     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; | ||||
| 
 | ||||
|     const int obj_idx = get_selected_obj_idx(); | ||||
|  | @ -4105,7 +4105,7 @@ void ObjectList::fix_through_netfabb() | |||
|     Plater::TakeSnapshot snapshot(plater, _L("Fix through NetFabb")); | ||||
| 
 | ||||
|     // Open a progress dialog.
 | ||||
|     wxProgressDialog progress_dlg(_L("Fixing through NetFabb"), "", 100, plater, | ||||
|     wxProgressDialog progress_dlg(_L("Fixing through NetFabb"), "", 100, find_toplevel_parent(plater), | ||||
|                                     wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); | ||||
|     int model_idx{ 0 }; | ||||
|     if (vol_idxs.empty()) { | ||||
|  |  | |||
|  | @ -110,12 +110,6 @@ bool ObjectSettings::update_settings_list() | |||
|                     update_settings_list();  | ||||
|                     m_parent->Layout();  | ||||
|                 }); | ||||
| 
 | ||||
|                 /* 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); | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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(); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -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<const ConfigOptionFloat*>(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; | ||||
|  |  | |||
|  | @ -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); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -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.
 | ||||
|  |  | |||
|  | @ -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<ProgressStatusBar>  m_statusbar;
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -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("<tr>")) { | ||||
|     bool is_marked = msg.Contains("<tr>"); | ||||
|     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", "<br>"); | ||||
|     boost::replace_all(msg_escaped, "\n", "<br>"); | ||||
|     if (monospaced_font) | ||||
|  |  | |||
|  | @ -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<Option>& option_set = line.get_options(); | ||||
|             if (option_set.size() == 1 && option_set.front().opt.sidetext.size() == 0 && | ||||
|                 option_set.front().opt.label.empty() && | ||||
|                 option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0) | ||||
|             { | ||||
|                 h_pos += 3 * blinking_button_width; | ||||
|  | @ -167,13 +165,14 @@ wxPoint OG_CustomCtrl::get_pos(const Line& line, Field* field_in/* = nullptr*/) | |||
|                 break; | ||||
|             } | ||||
| 
 | ||||
|             bool is_multioption_line = option_set.size() > 1; | ||||
|             for (auto opt : option_set) { | ||||
|                 Field* field = opt_group->get_field(opt.opt_id); | ||||
|                 correct_line_height(ctrl_line.height, field->getWindow()); | ||||
| 
 | ||||
|                 ConfigOptionDef option = opt.opt; | ||||
|                 // add label if any
 | ||||
|                 if (!option.label.empty()) { | ||||
|                 if (is_multioption_line && !option.label.empty()) { | ||||
|                     //!            To correct translation by context have to use wxGETTEXT_IN_CONTEXT macro from wxWidget 3.1.1
 | ||||
|                     label = (option.label == L_CONTEXT("Top", "Layers") || option.label == L_CONTEXT("Bottom", "Layers")) ? | ||||
|                         _CTX(option.label, "Layers") : _(option.label); | ||||
|  | @ -581,7 +580,6 @@ void OG_CustomCtrl::CtrlLine::render(wxDC& dc, wxCoord v_pos) | |||
| 
 | ||||
|     // If we have a single option with no sidetext just add it directly to the grid sizer
 | ||||
|     if (option_set.size() == 1 && option_set.front().opt.sidetext.size() == 0 && | ||||
|         option_set.front().opt.label.empty() && | ||||
|         option_set.front().side_widget == nullptr && og_line.get_extra_widgets().size() == 0) | ||||
|     { | ||||
|         if (field && field->undo_to_sys_bitmap()) | ||||
|  | @ -595,11 +593,12 @@ void OG_CustomCtrl::CtrlLine::render(wxDC& dc, wxCoord v_pos) | |||
|     } | ||||
| 
 | ||||
|     size_t bmp_rect_id = 0; | ||||
|     bool is_multioption_line = option_set.size() > 1; | ||||
|     for (const Option& opt : option_set) { | ||||
|         field = ctrl->opt_group->get_field(opt.opt_id); | ||||
|         ConfigOptionDef option = opt.opt; | ||||
|         // add label if any
 | ||||
|         if (!option.label.empty()) { | ||||
|         if (is_multioption_line && !option.label.empty()) { | ||||
|             //!            To correct translation by context have to use wxGETTEXT_IN_CONTEXT macro from wxWidget 3.1.1
 | ||||
|             label = (option.label == L_CONTEXT("Top", "Layers") || option.label == L_CONTEXT("Bottom", "Layers")) ? | ||||
|                     _CTX(option.label, "Layers") : _(option.label); | ||||
|  |  | |||
|  | @ -2,11 +2,13 @@ | |||
| #include "ConfigExceptions.hpp" | ||||
| #include "Plater.hpp" | ||||
| #include "GUI_App.hpp" | ||||
| #include "MainFrame.hpp" | ||||
| #include "OG_CustomCtrl.hpp" | ||||
| #include "MsgDialog.hpp" | ||||
| #include "format.hpp" | ||||
| 
 | ||||
| #include <utility> | ||||
| #include <wx/bookctrl.h> | ||||
| #include <wx/numformatter.h> | ||||
| #include <boost/algorithm/string/split.hpp> | ||||
| #include <boost/algorithm/string/classification.hpp> | ||||
|  | @ -247,7 +249,6 @@ void OptionsGroup::activate_line(Line& line) | |||
| 
 | ||||
| 	// if we have a single option with no label, no sidetext just add it directly to sizer
 | ||||
|     if (option_set.size() == 1 && 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) { | ||||
| 
 | ||||
|  | @ -326,7 +327,6 @@ void OptionsGroup::activate_line(Line& line) | |||
|         grid_sizer->Add(sizer, 0, wxEXPAND | (staticbox ? wxALL : wxBOTTOM | wxTOP | wxLEFT), staticbox ? 0 : 1); | ||||
|     // If we have a single option with no sidetext just add it directly to the grid sizer
 | ||||
|     if (option_set.size() == 1 && option_set.front().opt.sidetext.size() == 0 && | ||||
|         option_set.front().opt.label.empty() && | ||||
| 		option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0) { | ||||
| 		const auto& option = option_set.front(); | ||||
| 		const auto& field = build_field(option); | ||||
|  | @ -341,11 +341,12 @@ void OptionsGroup::activate_line(Line& line) | |||
|         return; | ||||
| 	} | ||||
| 
 | ||||
|     bool is_multioption_line = option_set.size() > 1; | ||||
|     for (auto opt : option_set) { | ||||
| 		ConfigOptionDef option = opt.opt; | ||||
|         wxSizer* sizer_tmp = sizer; | ||||
| 		// add label if any
 | ||||
| 		if (!option.label.empty() && !custom_ctrl) { | ||||
| 		if ((is_multioption_line || line.label.IsEmpty()) && !option.label.empty() && !custom_ctrl) { | ||||
| //!			To correct translation by context have to use wxGETTEXT_IN_CONTEXT macro from wxWidget 3.1.1
 | ||||
| 			wxString str_label = (option.label == L_CONTEXT("Top", "Layers") || option.label == L_CONTEXT("Bottom", "Layers")) ? | ||||
| 				_CTX(option.label, "Layers") : | ||||
|  | @ -507,15 +508,13 @@ void OptionsGroup::clear(bool destroy_custom_ctrl) | |||
| 	m_fields.clear(); | ||||
| } | ||||
| 
 | ||||
| Line OptionsGroup::create_single_option_line(const Option& option, const std::string& path/* = std::string()*/) const { | ||||
| // 	Line retval{ _(option.opt.label), _(option.opt.tooltip) };
 | ||||
| Line OptionsGroup::create_single_option_line(const Option& option, const std::string& path/* = std::string()*/) const | ||||
| { | ||||
|     wxString tooltip = _(option.opt.tooltip); | ||||
|     edit_tooltip(tooltip); | ||||
| 	Line retval{ _(option.opt.label), tooltip }; | ||||
| 	retval.label_path = path; | ||||
|     Option tmp(option); | ||||
|     tmp.opt.label = std::string(""); | ||||
|     retval.append_option(tmp); | ||||
|     retval.append_option(option); | ||||
|     return retval; | ||||
| } | ||||
| 
 | ||||
|  | @ -981,7 +980,8 @@ bool OptionsGroup::launch_browser(const std::string& path_end) | |||
|     bool launch = true; | ||||
| 
 | ||||
|     if (get_app_config()->get("suppress_hyperlinks").empty()) { | ||||
|         RichMessageDialog dialog(nullptr, _L("Open hyperlink in default browser?"), _L("PrusaSlicer: Open hyperlink"), wxYES_NO); | ||||
|         wxWindow* parent = wxGetApp().mainframe->m_tabpanel; | ||||
|         RichMessageDialog dialog(parent, _L("Open hyperlink in default browser?"), _L("PrusaSlicer: Open hyperlink"), wxYES_NO); | ||||
|         dialog.ShowCheckBox(_L("Remember my choice")); | ||||
|         int answer = dialog.ShowModal(); | ||||
| 
 | ||||
|  | @ -992,7 +992,7 @@ bool OptionsGroup::launch_browser(const std::string& path_end) | |||
|                 _L("You will not be asked about it again on label hovering.") + "\n\n" + | ||||
|                 format_wxstr(_L("Visit \"Preferences\" and check \"%1%\"\nto changes your choice."), preferences_item); | ||||
| 
 | ||||
|             MessageDialog msg_dlg(nullptr, msg, _L("PrusaSlicer: Don't ask me again"), wxOK | wxCANCEL | wxICON_INFORMATION); | ||||
|             MessageDialog msg_dlg(parent, msg, _L("PrusaSlicer: Don't ask me again"), wxOK | wxCANCEL | wxICON_INFORMATION); | ||||
|             if (msg_dlg.ShowModal() == wxID_CANCEL) | ||||
|                 return false; | ||||
| 
 | ||||
|  |  | |||
|  | @ -2351,7 +2351,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_ | |||
|     } | ||||
| 
 | ||||
|     const auto loading = _L("Loading") + dots; | ||||
|     wxProgressDialog dlg(loading, "", 100, q, wxPD_AUTO_HIDE); | ||||
|     wxProgressDialog dlg(loading, "", 100, find_toplevel_parent(q), wxPD_AUTO_HIDE); | ||||
|     wxBusyCursor busy; | ||||
| 
 | ||||
|     auto *new_model = (!load_model || one_by_one) ? nullptr : new Slic3r::Model(); | ||||
|  | @ -6306,36 +6306,9 @@ void Plater::force_print_bed_update() | |||
| 
 | ||||
| void Plater::on_activate() | ||||
| { | ||||
| #if defined(__linux__) || defined(_WIN32) | ||||
|     this->restore_keyboard_focus(); | ||||
| #endif | ||||
| 	this->p->show_delayed_error_message(); | ||||
| } | ||||
| 
 | ||||
| #if defined(__linux__) || defined(_WIN32) | ||||
| // wxWidgets callback to enable / disable window and all its children windows.
 | ||||
| // called by wxProgressDialog when entering / leaving modal dialog loop.
 | ||||
| // Unfortunately the wxProgressDialog 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 Plater::Enable(bool enable) | ||||
| { | ||||
|     bool retval = wxPanel::Enable(enable); | ||||
|     if (enable && retval) | ||||
|         this->restore_keyboard_focus(); | ||||
|     return retval; | ||||
| } | ||||
| void Plater::restore_keyboard_focus() | ||||
| { | ||||
|     // Activating the main frame, and no window has keyboard focus.
 | ||||
|     // Set the keyboard focus to the visible Canvas3D.
 | ||||
|     if (this->p->view3D->IsShown() && wxWindow::FindFocus() != this->p->view3D->get_wxglcanvas()) | ||||
|         this->p->view3D->get_wxglcanvas()->SetFocus(); | ||||
|     else if (this->p->preview->IsShown() && wxWindow::FindFocus() != this->p->view3D->get_wxglcanvas()) | ||||
|         this->p->preview->get_wxglcanvas()->SetFocus(); | ||||
| } | ||||
| #endif // Linux or Windows
 | ||||
| 
 | ||||
| // Get vector of extruder colors considering filament color, if extruder color is undefined.
 | ||||
| std::vector<std::string> Plater::get_extruder_colors_from_plater_config(const GCodeProcessorResult* const result) const | ||||
| { | ||||
|  |  | |||
|  | @ -149,16 +149,6 @@ public: | |||
|     void render_project_state_debug_window() const; | ||||
| #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
 | ||||
| 
 | ||||
| #if defined(__linux__) || defined(_WIN32) | ||||
| 	// wxWidgets callback to enable / disable window and all its children windows.
 | ||||
| 	// called by wxProgressDialog when entering / leaving modal dialog loop.
 | ||||
| 	// Unfortunately the wxProgressDialog 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) override; | ||||
|     void restore_keyboard_focus(); | ||||
| #endif | ||||
| 
 | ||||
|     Sidebar& sidebar(); | ||||
|     const Model& model() const; | ||||
|     Model& model(); | ||||
|  |  | |||
|  | @ -9,7 +9,6 @@ | |||
| #include "Notebook.hpp" | ||||
| #include "ButtonsDescription.hpp" | ||||
| #include "OG_CustomCtrl.hpp" | ||||
| #include <initializer_list> | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
|  | @ -466,7 +465,7 @@ void PreferencesDialog::build(size_t selected_tab) | |||
| 
 | ||||
| #ifdef _WIN32 | ||||
| 	// Add "Dark Mode" tab
 | ||||
| 	if (is_editor) { | ||||
| 	{ | ||||
| 		// Add "Dark Mode" tab
 | ||||
| 		m_optgroup_dark_mode = create_options_tab(_L("Dark mode (experimental)"), tabs); | ||||
| 		m_optgroup_dark_mode->m_on_change = [this](t_config_option_key opt_key, boost::any value) { | ||||
|  | @ -519,21 +518,29 @@ void PreferencesDialog::build(size_t selected_tab) | |||
| 	this->CenterOnParent(); | ||||
| } | ||||
| 
 | ||||
| void PreferencesDialog::update_ctrls_alignment() | ||||
| std::vector<ConfigOptionsGroup*> PreferencesDialog::optgroups() | ||||
| { | ||||
| 	int max_ctrl_width{ 0 }; | ||||
| 	std::initializer_list<ConfigOptionsGroup*> og_list = { m_optgroup_general.get(), m_optgroup_camera.get(), m_optgroup_gui.get()  | ||||
| 	std::vector<ConfigOptionsGroup*> out; | ||||
| 	out.reserve(4); | ||||
| 	for (ConfigOptionsGroup* opt : { m_optgroup_general.get(), m_optgroup_camera.get(), m_optgroup_gui.get() | ||||
| #ifdef _WIN32 | ||||
| 		, m_optgroup_dark_mode.get() | ||||
| #endif // _WIN32
 | ||||
| 	}; | ||||
| 	for (auto og : og_list) { | ||||
| 		}) | ||||
| 		if (opt) | ||||
| 			out.emplace_back(opt); | ||||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| void PreferencesDialog::update_ctrls_alignment() | ||||
| { | ||||
| 	int max_ctrl_width{ 0 }; | ||||
| 	for (ConfigOptionsGroup* og : this->optgroups()) | ||||
| 		if (int max = og->custom_ctrl->get_max_win_width(); | ||||
| 			max_ctrl_width < max) | ||||
| 			max_ctrl_width = max; | ||||
| 	} | ||||
| 	if (max_ctrl_width) | ||||
| 		for (auto og : og_list) | ||||
| 		for (ConfigOptionsGroup* og : this->optgroups()) | ||||
| 			og->custom_ctrl->set_max_win_width(max_ctrl_width); | ||||
| } | ||||
| 
 | ||||
|  | @ -622,9 +629,8 @@ void PreferencesDialog::accept(wxEvent&) | |||
| 
 | ||||
| void PreferencesDialog::on_dpi_changed(const wxRect &suggested_rect) | ||||
| { | ||||
| 	m_optgroup_general->msw_rescale(); | ||||
| 	m_optgroup_camera->msw_rescale(); | ||||
| 	m_optgroup_gui->msw_rescale(); | ||||
| 	for (ConfigOptionsGroup* og : this->optgroups()) | ||||
| 		og->msw_rescale(); | ||||
| 
 | ||||
|     msw_buttons_rescale(this, em_unit(), { wxID_OK, wxID_CANCEL }); | ||||
| 
 | ||||
|  | @ -788,7 +794,7 @@ void PreferencesDialog::init_highlighter(const t_config_option_key& opt_key) | |||
| 		}); | ||||
| 
 | ||||
| 	std::pair<OG_CustomCtrl*, bool*> ctrl = { nullptr, nullptr }; | ||||
| 	for (auto opt_group : { m_optgroup_general, m_optgroup_camera, m_optgroup_gui }) { | ||||
| 	for (ConfigOptionsGroup* opt_group : this->optgroups()) { | ||||
| 		ctrl = opt_group->get_custom_ctrl_with_blinking_ptr(opt_key, -1); | ||||
| 		if (ctrl.first && ctrl.second) { | ||||
| 			m_highlighter.init(ctrl); | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ | |||
| 
 | ||||
| #include <wx/dialog.h> | ||||
| #include <wx/timer.h> | ||||
| #include <vector> | ||||
| #include <map> | ||||
| 
 | ||||
| class wxColourPickerCtrl; | ||||
|  | @ -61,6 +62,7 @@ protected: | |||
|     void create_settings_mode_widget(); | ||||
|     void create_settings_text_color_widget(); | ||||
| 	void init_highlighter(const t_config_option_key& opt_key); | ||||
| 	std::vector<ConfigOptionsGroup*> optgroups(); | ||||
| 
 | ||||
| 	struct PreferencesHighlighter | ||||
| 	{ | ||||
|  |  | |||
|  | @ -98,8 +98,9 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUplo | |||
|     } | ||||
| 
 | ||||
|     if (post_actions.has(PrintHostPostUploadAction::StartSimulation)) { | ||||
|         auto* btn_print = add_button(wxID_YES, false, _L("Upload and Simulate")); | ||||
|         btn_print->Bind(wxEVT_BUTTON, [this, validate_path](wxCommandEvent&) { | ||||
|         // Using wxID_MORE as a button identifier to be different from the other buttons, wxID_MORE has no other meaning here.
 | ||||
|         auto* btn_simulate = add_button(wxID_MORE, false, _L("Upload and Simulate")); | ||||
|         btn_simulate->Bind(wxEVT_BUTTON, [this, validate_path](wxCommandEvent&) { | ||||
|             if (validate_path(txt_filename->GetValue())) { | ||||
|                 post_upload_action = PrintHostPostUploadAction::StartSimulation; | ||||
|                 EndDialog(wxID_OK); | ||||
|  |  | |||
|  | @ -885,6 +885,10 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/) | |||
|     } | ||||
| 
 | ||||
|     m_postpone_update_ui = false; | ||||
| 
 | ||||
|     // When all values are rolled, then we hane to update whole tab in respect to the reverted values
 | ||||
|     update(); | ||||
| 
 | ||||
|     update_changed_ui(); | ||||
| } | ||||
| 
 | ||||
|  | @ -1148,6 +1152,13 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) | |||
|     if (opt_key == "extruders_count") | ||||
|         wxGetApp().plater()->on_extruders_change(boost::any_cast<size_t>(value)); | ||||
| 
 | ||||
|     if (m_postpone_update_ui) { | ||||
|         // It means that not all values are rolled to the system/last saved values jet.
 | ||||
|         // And call of the update() can causes a redundant check of the config values,
 | ||||
|         // see https://github.com/prusa3d/PrusaSlicer/issues/7146
 | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     update(); | ||||
| } | ||||
| 
 | ||||
|  | @ -4481,7 +4492,7 @@ ConfigManipulation Tab::get_config_manipulation() | |||
|         return on_value_change(opt_key, value); | ||||
|     }; | ||||
| 
 | ||||
|     return ConfigManipulation(load_config, cb_toggle_field, cb_value_change); | ||||
|     return ConfigManipulation(load_config, cb_toggle_field, cb_value_change, nullptr, this); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -112,7 +112,17 @@ SCENARIO("2D convex hull of sinking object", "[3mf]") { | |||
|                 { -91501496, 4243 } | ||||
|             }; | ||||
| 
 | ||||
|             bool res = hull_2d.points == result; | ||||
|             // Allow 1um error due to floating point rounding.
 | ||||
|             bool res = hull_2d.points.size() == result.size(); | ||||
|             if (res) | ||||
|                 for (size_t i = 0; i < result.size(); ++ i) { | ||||
|                     const Point &p1 = result[i]; | ||||
|                     const Point &p2 = hull_2d.points[i]; | ||||
|                     if (std::abs(p1.x() - p2.x()) > 1 || std::abs(p1.y() - p2.y()) > 1) { | ||||
|                         res = false; | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|             THEN("2D convex hull should match with reference") { | ||||
|                 REQUIRE(res); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Spencer Owen
						Spencer Owen