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
Reference in a new issue