Merge branch 'prusa3d:master' into ender2-pro

This commit is contained in:
Spencer Owen 2021-12-08 09:04:20 -07:00 committed by GitHub
commit 7e453380d6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 335 additions and 242 deletions

View file

@ -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 (

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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 {

View file

@ -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.

View file

@ -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)

View file

@ -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)

View file

@ -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) {

View file

@ -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() {}

View file

@ -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.;

View file

@ -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; }

View file

@ -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();

View file

@ -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

View file

@ -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();

View file

@ -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);

View file

@ -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;
};

View file

@ -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()) {

View file

@ -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);

View file

@ -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

View file

@ -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();
}
}

View file

@ -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;

View file

@ -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);

View file

@ -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;
};

View file

@ -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.

View file

@ -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;

View file

@ -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)

View file

@ -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);

View file

@ -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;

View file

@ -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
{

View file

@ -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();

View file

@ -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);

View file

@ -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
{

View file

@ -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);

View file

@ -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);
}

View file

@ -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);