Merge branch 'master' into fs_simplify_multipart_object

# Conflicts:
#	src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp
This commit is contained in:
Filip Sykala 2021-11-22 15:50:53 +01:00
commit 74eb4ec042
26 changed files with 251 additions and 106 deletions

View File

@ -288,7 +288,7 @@ set(CGAL_DO_NOT_WARN_ABOUT_CMAKE_BUILD_TYPE ON CACHE BOOL "" FORCE)
cmake_policy(PUSH)
cmake_policy(SET CMP0011 NEW)
find_package(CGAL REQUIRED)
find_package(CGAL 4.13.2 REQUIRED)
cmake_policy(POP)
add_library(libslic3r_cgal STATIC MeshBoolean.cpp MeshBoolean.hpp TryCatchSignal.hpp

View File

@ -382,6 +382,7 @@ void fill_iniconf(ConfMap &m, const SLAPrint &print)
m["layerHeight"] = get_cfg_value(cfg, "layer_height");
m["expTime"] = get_cfg_value(cfg, "exposure_time");
m["expTimeFirst"] = get_cfg_value(cfg, "initial_exposure_time");
m["expUserProfile"] = get_cfg_value(cfg, "material_print_speed") == "slow" ? "1" : "0";
m["materialName"] = get_cfg_value(cfg, "sla_material_settings_id");
m["printerModel"] = get_cfg_value(cfg, "printer_model");
m["printerVariant"] = get_cfg_value(cfg, "printer_variant");

View File

@ -475,6 +475,7 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
std::vector<GCode::LayerToPrint> layers_to_print;
layers_to_print.reserve(object.layers().size() + object.support_layers().size());
/*
// Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um.
// This is the same logic as in support generator.
//FIXME should we use the printing extruders instead?
@ -488,7 +489,7 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
for (auto lh : object.print()->config().min_layer_height.values)
support_layer_height_min = std::min(support_layer_height_min, std::max(0.01, lh));
gap_over_supports += support_layer_height_min;
}
}*/
std::vector<std::pair<double, double>> warning_ranges;
@ -528,22 +529,23 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
if ((layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions())
// Allow empty support layers, as the support generator may produce no extrusions for non-empty support regions.
|| (layer_to_print.support_layer /* && layer_to_print.support_layer->has_extrusions() */)) {
double support_contact_z = (last_extrusion_layer && last_extrusion_layer->support_layer)
? gap_over_supports
: 0.;
double top_cd = object.config().support_material_contact_distance;
double bottom_cd = object.config().support_material_bottom_contact_distance == 0. ? top_cd : object.config().support_material_bottom_contact_distance;
double extra_gap = (layer_to_print.support_layer ? bottom_cd : top_cd);
double maximal_print_z = (last_extrusion_layer ? last_extrusion_layer->print_z() : 0.)
+ layer_to_print.layer()->height
+ support_contact_z;
+ std::max(0., extra_gap);
// Negative support_contact_z is not taken into account, it can result in false positives in cases
// where previous layer has object extrusions too (https://github.com/prusa3d/PrusaSlicer/issues/2752)
if (has_extrusions && layer_to_print.print_z() > maximal_print_z + 2. * EPSILON)
warning_ranges.emplace_back(std::make_pair((last_extrusion_layer ? last_extrusion_layer->print_z() : 0.), layers_to_print.back().print_z()));
// Remember last layer with extrusions.
if (has_extrusions)
last_extrusion_layer = &layers_to_print.back();
}
// Remember last layer with extrusions.
if (has_extrusions)
last_extrusion_layer = &layers_to_print.back();
}
if (! warning_ranges.empty()) {

View File

@ -551,6 +551,7 @@ static std::vector<std::string> s_Preset_sla_material_options {
"material_correction_z",
"material_notes",
"material_vendor",
"material_print_speed",
"default_sla_material_profile",
"compatible_prints", "compatible_prints_condition",
"compatible_printers", "compatible_printers_condition", "inherits"

View File

@ -604,12 +604,24 @@ void print_objects_regions_invalidate_keep_some_volumes(PrintObjectRegions &prin
print_object_regions.cached_volume_ids.erase(print_object_regions.cached_volume_ids.begin() + last_cached_volume, print_object_regions.cached_volume_ids.end());
}
// Find a bounding box of a volume's part intersecting layer_range. Such a bounding box will likely be smaller in XY than the full bounding box,
// thus it will intersect with lower number of other volumes.
const PrintObjectRegions::BoundingBox* find_volume_extents(const PrintObjectRegions::LayerRangeRegions &layer_range, const ModelVolume &volume)
{
auto it = lower_bound_by_predicate(layer_range.volumes.begin(), layer_range.volumes.end(), [&volume](const PrintObjectRegions::VolumeExtents &l){ return l.volume_id < volume.id(); });
return it != layer_range.volumes.end() && it->volume_id == volume.id() ? &it->bbox : nullptr;
}
// Find a bounding box of a topmost printable volume referenced by this modifier given this_region_id.
const PrintObjectRegions::BoundingBox* find_modifier_volume_extents(const PrintObjectRegions::LayerRangeRegions &layer_range, const int this_region_id)
{
// Find the top-most printable volume of this modifier, or the printable volume itself.
int parent_region_id = this_region_id;
for (; ! layer_range.volume_regions[parent_region_id].model_volume->is_model_part(); parent_region_id = layer_range.volume_regions[parent_region_id].parent)
assert(parent_region_id >= 0);
return find_volume_extents(layer_range, *layer_range.volume_regions[parent_region_id].model_volume);
}
PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_or_parent_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders);
void print_region_ref_inc(PrintRegion &r) { ++ r.m_ref_cnt; }
@ -634,11 +646,54 @@ bool verify_update_print_object_regions(
print_region_ref_reset(*region);
// Verify and / or update PrintRegions produced by ModelVolumes, layer range modifiers, modifier volumes.
for (PrintObjectRegions::LayerRangeRegions &layer_range : print_object_regions.layer_ranges)
for (PrintObjectRegions::LayerRangeRegions &layer_range : print_object_regions.layer_ranges) {
// Each modifier ModelVolume intersecting this layer_range shall be referenced here at least once if it intersects some
// printable ModelVolume at this layer_range even if it does not modify its overlapping printable ModelVolume configuration yet.
// VolumeRegions reference ModelVolumes in layer_range.volume_regions the order they are stored in ModelObject volumes.
// Remember whether a given modifier ModelVolume was visited already.
auto it_model_volume_modifier_last = model_volumes.end();
for (PrintObjectRegions::VolumeRegion &region : layer_range.volume_regions)
if (region.model_volume->is_model_part() || region.model_volume->is_modifier()) {
auto it_model_volume = lower_bound_by_predicate(model_volumes.begin(), model_volumes.end(), [&region](const ModelVolume *l){ return l->id() < region.model_volume->id(); });
assert(it_model_volume != model_volumes.end() && (*it_model_volume)->id() == region.model_volume->id());
if (region.model_volume->is_modifier() && it_model_volume != it_model_volume_modifier_last) {
// A modifier ModelVolume is visited for the first time.
// A visited modifier may not have had parent volume_regions created overlapping with some model parts or modifiers,
// if the visited modifier did not modify their properties. Now the visited modifier's configuration may have changed,
// which may require new regions to be created.
it_model_volume_modifier_last = it_model_volume;
int this_region_id = int(&region - layer_range.volume_regions.data());
int next_region_id = this_region_id + 1;
const PrintObjectRegions::BoundingBox *bbox = find_volume_extents(layer_range, *region.model_volume);
assert(bbox);
for (int parent_region_id = this_region_id - 1; parent_region_id >= 0; -- parent_region_id) {
const PrintObjectRegions::VolumeRegion &parent_region = layer_range.volume_regions[parent_region_id];
assert(parent_region.model_volume != region.model_volume);
if (parent_region.model_volume->is_model_part() || parent_region.model_volume->is_modifier()) {
// volume_regions are produced in decreasing order of parent volume_regions ids.
// Some regions may not have been generated the last time by generate_print_object_regions().
assert(next_region_id == int(layer_range.volume_regions.size()) ||
layer_range.volume_regions[next_region_id].model_volume != region.model_volume ||
layer_range.volume_regions[next_region_id].parent <= parent_region_id);
if (next_region_id < int(layer_range.volume_regions.size()) &&
layer_range.volume_regions[next_region_id].model_volume == region.model_volume &&
layer_range.volume_regions[next_region_id].parent == parent_region_id) {
// A parent region is already overridden.
++ next_region_id;
} else {
// Such parent region does not exist. If it is needed, then we need to reslice.
const PrintObjectRegions::BoundingBox *parent_bbox = find_modifier_volume_extents(layer_range, parent_region_id);
assert(parent_bbox != nullptr);
if (parent_bbox->intersects(*bbox))
// Only create new region for a modifier, which actually modifies config of it's parent.
if (PrintRegionConfig config = region_config_from_model_volume(parent_region.region->config(), nullptr, **it_model_volume, num_extruders);
config != parent_region.region->config())
// This modifier newly overrides a region, which it did not before. We need to reslice.
return false;
}
}
}
}
PrintRegionConfig cfg = region.parent == -1 ?
region_config_from_model_volume(default_region_config, layer_range.config, **it_model_volume, num_extruders) :
region_config_from_model_volume(layer_range.volume_regions[region.parent].region->config(), nullptr, **it_model_volume, num_extruders);
@ -657,6 +712,7 @@ bool verify_update_print_object_regions(
}
print_region_ref_inc(*region.region);
}
}
// Verify and / or update PrintRegions produced by color painting.
for (const PrintObjectRegions::LayerRangeRegions &layer_range : print_object_regions.layer_ranges)
@ -851,17 +907,28 @@ static PrintObjectRegions* generate_print_object_regions(
} else {
assert(volume.is_modifier());
// Modifiers may be chained one over the other. Check for overlap, merge DynamicPrintConfigs.
for (int parent_region_id = int(layer_range.volume_regions.size()) - 1; parent_region_id >= 0; -- parent_region_id)
if (const PrintObjectRegions::VolumeRegion &parent_region = layer_range.volume_regions[parent_region_id];
parent_region.model_volume->is_model_part() || parent_region.model_volume->is_modifier()) {
const PrintObjectRegions::BoundingBox *parent_bbox = find_volume_extents(layer_range, *parent_region.model_volume);
bool added = false;
int parent_model_part_id = -1;
for (int parent_region_id = int(layer_range.volume_regions.size()) - 1; parent_region_id >= 0; -- parent_region_id) {
const PrintObjectRegions::VolumeRegion &parent_region = layer_range.volume_regions[parent_region_id];
const ModelVolume &parent_volume = *parent_region.model_volume;
if (parent_volume.is_model_part() || parent_volume.is_modifier()) {
const PrintObjectRegions::BoundingBox *parent_bbox = find_modifier_volume_extents(layer_range, parent_region_id);
assert(parent_bbox != nullptr);
if (parent_bbox->intersects(*bbox))
// Only create new region for a modifier, which actually modifies config of it's parent.
if (PrintRegionConfig config = region_config_from_model_volume(parent_region.region->config(), nullptr, volume, num_extruders);
config != parent_region.region->config())
config != parent_region.region->config()) {
added = true;
layer_range.volume_regions.push_back({ &volume, parent_region_id, get_create_region(std::move(config)), bbox });
} else if (parent_model_part_id == -1 && parent_volume.is_model_part())
parent_model_part_id = parent_region_id;
}
}
if (! added && parent_model_part_id >= 0)
// This modifier does not override any printable volume's configuration, however it may in the future.
// Store it so that verify_update_print_object_regions() will handle this modifier correctly if its configuration changes.
layer_range.volume_regions.push_back({ &volume, parent_model_part_id, layer_range.volume_regions[parent_model_part_id].region, bbox });
}
}
}

View File

@ -165,6 +165,12 @@ static const t_config_enum_values s_keys_map_SLAPillarConnectionMode = {
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SLAPillarConnectionMode)
static const t_config_enum_values s_keys_map_SLAMaterialSpeed = {
{"slow", slamsSlow},
{"fast", slamsFast}
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SLAMaterialSpeed);
static const t_config_enum_values s_keys_map_BrimType = {
{"no_brim", btNoBrim},
{"outer_only", btOuterOnly},
@ -3730,6 +3736,19 @@ void PrintConfigDef::init_sla_params()
def->max = 10;
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloat(2.0));
def = this->add("material_print_speed", coEnum);
def->label = L("Print speed");
def->tooltip = L(
"A slower printing profile might be necessary when using materials with higher viscosity "
"or with some hollowed parts. It slows down the tilt movement and adds a delay before exposure.");
def->enum_keys_map = &ConfigOptionEnum<SLAMaterialSpeed>::get_enum_values();
def->enum_values.push_back("slow");
def->enum_values.push_back("fast");
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));
}
void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value)

View File

@ -915,6 +915,8 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, hollowing_closing_distance))
)
enum SLAMaterialSpeed { slamsSlow, slamsFast };
PRINT_CONFIG_CLASS_DEFINE(
SLAMaterialConfig,
@ -929,6 +931,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, material_correction_x))
((ConfigOptionFloat, material_correction_y))
((ConfigOptionFloat, material_correction_z))
((ConfigOptionEnum<SLAMaterialSpeed>, material_print_speed))
)
PRINT_CONFIG_CLASS_DEFINE(

View File

@ -165,11 +165,6 @@ typedef struct NSVGimage
// Parses SVG file from a file, returns SVG image as paths.
NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi);
// Parses SVG file from a file, returns SVG image as paths.
// And makes replases befor parsing
// replace_map containes old_value->new_value
NSVGimage* nsvgParseFromFileWithReplace(const char* filename, const char* units, float dpi, const std::map<std::string, std::string>& replace_map);
// Parses SVG file from a null terminated string, returns SVG image as paths.
// Important note: changes the string.
NSVGimage* nsvgParse(char* input, const char* units, float dpi);
@ -2908,12 +2903,6 @@ NSVGimage* nsvgParse(char* input, const char* units, float dpi)
NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi)
{
return nsvgParseFromFileWithReplace(filename, units, dpi, { {} });
}
NSVGimage* nsvgParseFromFileWithReplace(const char* filename, const char* units, float dpi, const std::map<std::string, std::string>& replaces)
{
std::string str;
FILE* fp = NULL;
size_t size;
char* data = NULL;
@ -2930,14 +2919,7 @@ NSVGimage* nsvgParseFromFileWithReplace(const char* filename, const char* units,
data[size] = '\0'; // Must be null terminated.
fclose(fp);
if(replaces.empty())
image = nsvgParse(data, units, dpi);
else {
str.assign(data);
for (auto val : replaces)
boost::replace_all(str, val.first, val.second);
image = nsvgParse(str.data(), units, dpi);
}
image = nsvgParse(data, units, dpi);
free(data);
return image;

View File

@ -252,12 +252,16 @@ void Bed3D::render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
BoundingBoxf3 Bed3D::calc_extended_bounding_box() const
{
BoundingBoxf3 out { m_build_volume.bounding_volume() };
const Vec3d size = out.size();
// ensures that the bounding box is set as defined or the following calls to merge() will not work as intented
if (size.x() > 0.0 && size.y() > 0.0 && !out.defined)
out.defined = true;
// Reset the build volume Z, we don't want to zoom to the top of the build volume if it is empty.
out.min.z() = 0;
out.max.z() = 0;
out.min.z() = 0.0;
out.max.z() = 0.0;
// extend to contain axes
out.merge(m_axes.get_origin() + m_axes.get_total_length() * Vec3d::Ones());
out.merge(out.min + Vec3d(-Axes::DefaultTipRadius, -Axes::DefaultTipRadius, out.max(2)));
out.merge(out.min + Vec3d(-Axes::DefaultTipRadius, -Axes::DefaultTipRadius, out.max.z()));
// extend to contain model, if any
BoundingBoxf3 model_bb = m_model.get_bounding_box();
if (model_bb.defined) {

View File

@ -260,6 +260,43 @@ wxBitmap* BitmapCache::load_png(const std::string &bitmap_name, unsigned width,
return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image)));
}
NSVGimage* BitmapCache::nsvgParseFromFileWithReplace(const char* filename, const char* units, float dpi, const std::map<std::string, std::string>& replaces)
{
std::string str;
FILE* fp = NULL;
size_t size;
char* data = NULL;
NSVGimage* image = NULL;
fp = boost::nowide::fopen(filename, "rb");
if (!fp) goto error;
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);
data = (char*)malloc(size + 1);
if (data == NULL) goto error;
if (fread(data, 1, size, fp) != size) goto error;
data[size] = '\0'; // Must be null terminated.
fclose(fp);
if (replaces.empty())
image = nsvgParse(data, units, dpi);
else {
str.assign(data);
for (auto val : replaces)
boost::replace_all(str, val.first, val.second);
image = nsvgParse(str.data(), units, dpi);
}
free(data);
return image;
error:
if (fp) fclose(fp);
if (data) free(data);
if (image) nsvgDelete(image);
return NULL;
}
wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_width, unsigned target_height,
const bool grayscale/* = false*/, const bool dark_mode/* = false*/, const std::string& new_color /*= ""*/)
{
@ -278,11 +315,11 @@ wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_
// map of color replaces
std::map<std::string, std::string> replaces;
if (dark_mode)
replaces["#808080"] = "#FFFFFF";
replaces["\"#808080\""] = "\"#FFFFFF\"";
if (!new_color.empty())
replaces["#ED6B21"] = new_color;
replaces["\"#ED6B21\""] = "\"" + new_color + "\"";
NSVGimage *image = ::nsvgParseFromFileWithReplace(Slic3r::var(bitmap_name + ".svg").c_str(), "px", 96.0f, replaces);
NSVGimage *image = nsvgParseFromFileWithReplace(Slic3r::var(bitmap_name + ".svg").c_str(), "px", 96.0f, replaces);
if (image == nullptr)
return nullptr;

View File

@ -9,6 +9,8 @@
#include <wx/wx.h>
#endif
struct NSVGimage;
namespace Slic3r { namespace GUI {
class BitmapCache
@ -32,6 +34,11 @@ public:
// Load png from resources/icons. bitmap_key is given without the .png suffix. Bitmap will be rescaled to provided height/width if nonzero.
wxBitmap* load_png(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false);
// Parses SVG file from a file, returns SVG image as paths.
// And makes replases befor parsing
// replace_map containes old_value->new_value
static NSVGimage* nsvgParseFromFileWithReplace(const char* filename, const char* units, float dpi, const std::map<std::string, std::string>& replaces);
// Load svg from resources/icons. bitmap_key is given without the .svg suffix. SVG will be rasterized to provided height/width.
wxBitmap* load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false, const bool dark_mode = false, const std::string& new_color = "");

View File

@ -271,8 +271,10 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const
ImGui::SetCursorPosX(widget_align);
ImGui::PushItemWidth(imgui.get_style_scaling() * 120.0f);
int radius = (int)m_smooth_params.radius;
if (ImGui::SliderInt("##1", &radius, 1, 10))
if (ImGui::SliderInt("##1", &radius, 1, 10)) {
radius = std::clamp(radius, 1, 10);
m_smooth_params.radius = (unsigned int)radius;
}
ImGui::SetCursorPosX(text_align);
ImGui::AlignTextToFramePadding();
@ -1119,6 +1121,8 @@ void GLCanvas3D::reset_volumes()
ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state() const
{
assert(m_initialized);
ModelInstanceEPrintVolumeState state;
m_volumes.check_outside_state(m_bed.build_volume(), &state);
return state;

View File

@ -812,6 +812,7 @@ public:
void schedule_extra_frame(int miliseconds);
float get_main_toolbar_height() { return m_main_toolbar.get_height(); }
int get_main_toolbar_item_id(const std::string& name) const { return m_main_toolbar.get_item_id(name); }
void force_main_toolbar_left_action(int item_id) { m_main_toolbar.force_left_action(item_id, *this); }
void force_main_toolbar_right_action(int item_id) { m_main_toolbar.force_right_action(item_id, *this); }

View File

@ -431,52 +431,46 @@ bool static check_old_linux_datadir(const wxString& app_name) {
static bool run_updater_win()
{
// find updater exe
boost::filesystem::path path_to_binary = boost::dll::program_location();
for (const auto& dir_entry : boost::filesystem::directory_iterator(path_to_binary.parent_path())) {
if (dir_entry.path().filename() == "prusaslicer-updater.exe") {
// run updater. Original args: /silent -restartapp prusa-slicer.exe -startappfirst
boost::filesystem::path path_updater = boost::dll::program_location().parent_path() / "prusaslicer-updater.exe";
if (boost::filesystem::exists(path_updater)) {
// run updater. Original args: /silent -restartapp prusa-slicer.exe -startappfirst
// Using quoted string as mentioned in CreateProcessW docs.
std::wstring wcmd = L"\"" + dir_entry.path().wstring() + L"\"";
wcmd += L" /silent";
// Using quoted string as mentioned in CreateProcessW docs, silent execution parameter.
std::wstring wcmd = L"\"" + path_updater.wstring() + L"\" /silent";
// additional information
STARTUPINFOW si;
PROCESS_INFORMATION pi;
// additional information
STARTUPINFOW si;
PROCESS_INFORMATION pi;
// set the size of the structures
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// set the size of the structures
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// start the program up
if (CreateProcessW(NULL, // the path
wcmd.data(), // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi // Pointer to PROCESS_INFORMATION structure (removed extra parentheses)
)) {
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return true;
} else {
BOOST_LOG_TRIVIAL(error) << "Failed to start prusaslicer-updater.exe with command " << wcmd;
}
break;
// start the program up
if (CreateProcessW(NULL, // the path
wcmd.data(), // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi // Pointer to PROCESS_INFORMATION structure (removed extra parentheses)
)) {
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return true;
} else {
BOOST_LOG_TRIVIAL(error) << "Failed to start prusaslicer-updater.exe with command " << wcmd;
}
}
return false;
}
#endif //_WIN32
wxString file_wildcards(FileType file_type, const std::string &custom_extension)
{
static const std::string defaults[FT_SIZE] = {
@ -738,14 +732,11 @@ void GUI_App::post_init()
// sees something else than "we want something" on the first start.
show_send_system_info_dialog_if_needed();
}
bool updater_running =
#ifdef _WIN32
// Run external updater on Windows.
run_updater_win();
#else
false;
if (! run_updater_win())
// "prusaslicer-updater.exe" was not started, run our own update check.
#endif // _WIN32
if (!updater_running)
this->preset_updater->slic3r_update_notify();
});
}

View File

@ -916,7 +916,7 @@ void ObjectList::list_manipulation(const wxPoint& mouse_pos, bool evt_context_me
if (is_windows10() && m_objects_model->HasWarningIcon(item) &&
mouse_pos.x > 2 * wxGetApp().em_unit() && mouse_pos.x < 4 * wxGetApp().em_unit())
fix_through_netfabb();
else
else if (evt_context_menu)
show_context_menu(evt_context_menu); // show context menu for "Name" column too
}
// workaround for extruder editing under OSX

View File

@ -3,6 +3,7 @@
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/GUI_ObjectManipulation.hpp"
#include "slic3r/GUI/GUI_ObjectList.hpp"
#include "slic3r/GUI/MsgDialog.hpp"
#include "slic3r/GUI/NotificationManager.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/format.hpp"
@ -199,6 +200,12 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
if (act_volume_ids.empty()) {
stop_worker_thread_request();
close();
if (! m_parent.get_selection().is_single_volume()) {
MessageDialog msg((wxWindow*)wxGetApp().mainframe,
_L("Simplification is currently only allowed when a single part is selected"),
_L("Error"));
msg.ShowModal();
}
return;
}
@ -264,7 +271,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
pos.y -= m_gui_cfg->window_offset_y;
// minimal top left value
ImVec2 tl(m_gui_cfg->window_padding,
m_gui_cfg->window_padding);
m_gui_cfg->window_padding + m_parent.get_main_toolbar_height());
if (pos.x < tl.x) pos.x = tl.x;
if (pos.y < tl.y) pos.y = tl.y;
// maximal bottom right value

View File

@ -271,11 +271,9 @@ void HollowedMesh::on_update()
m_old_hollowing_timestamp = timestamp;
indexed_triangle_set interior = print_object->hollowed_interior_mesh();
if (!interior.empty()) {
its_flip_triangles(interior);
m_hollowed_interior_transformed = std::make_unique<TriangleMesh>(std::move(interior));
m_hollowed_interior_transformed->transform(trafo_inv);
}
its_flip_triangles(interior);
m_hollowed_interior_transformed = std::make_unique<TriangleMesh>(std::move(interior));
m_hollowed_interior_transformed->transform(trafo_inv);
}
else {
m_hollowed_mesh_transformed.reset(nullptr);

View File

@ -31,6 +31,7 @@
#include "GUI.hpp"
#include "I18N.hpp"
#include "Search.hpp"
#include "BitmapCache.hpp"
#include "../Utils/MacDarkMode.hpp"
#include "nanosvg/nanosvg.h"
@ -1030,7 +1031,7 @@ std::vector<unsigned char> ImGuiWrapper::load_svg(const std::string& bitmap_name
{
std::vector<unsigned char> empty_vector;
NSVGimage* image = ::nsvgParseFromFileWithReplace(Slic3r::var(bitmap_name + ".svg").c_str(), "px", 96.0f, { { "#808080", "#FFFFFF" } });
NSVGimage* image = BitmapCache::nsvgParseFromFileWithReplace(Slic3r::var(bitmap_name + ".svg").c_str(), "px", 96.0f, { { "\"#808080\"", "\"#FFFFFF\"" } });
if (image == nullptr)
return empty_vector;

View File

@ -229,7 +229,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
}
// check unsaved changes only if project wasn't saved
else if (plater()->is_project_dirty() && saved_project == wxID_NO && event.CanVeto() &&
!wxGetApp().check_and_save_current_preset_changes(_L("PrusaSlicer is closing"), _L("Closing PrusaSlicer while some presets are modified."))) {
(plater()->is_presets_dirty() && !wxGetApp().check_and_save_current_preset_changes(_L("PrusaSlicer is closing"), _L("Closing PrusaSlicer while some presets are modified.")))) {
event.Veto();
return;
}
@ -486,7 +486,7 @@ void MainFrame::update_layout()
case ESettingsLayout::GCodeViewer:
{
m_main_sizer->Add(m_plater, 1, wxEXPAND);
m_plater->set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, 0, {}, {}, true);
m_plater->set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, 0.0, {}, {}, true);
m_plater->get_collapse_toolbar().set_enabled(false);
m_plater->collapse_sidebar(true);
m_plater->Show();

View File

@ -375,7 +375,7 @@ void Mouse3DController::load_config(const AppConfig &appconfig)
appconfig.get_mouse_device_swap_yz(device_name, swap_yz);
// clamp to valid values
Params params;
params.translation.scale = Params::DefaultTranslationScale * std::clamp(translation_speed, 0.1, 10.0);
params.translation.scale = Params::DefaultTranslationScale * std::clamp(translation_speed, Params::MinTranslationScale, Params::MaxTranslationScale);
params.translation.deadzone = std::clamp(translation_deadzone, 0.0, Params::MaxTranslationDeadzone);
params.rotation.scale = Params::DefaultRotationScale * std::clamp(rotation_speed, 0.1f, 10.0f);
params.rotation.deadzone = std::clamp(rotation_deadzone, 0.0f, Params::MaxRotationDeadzone);
@ -469,7 +469,7 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const
imgui.text_colored(color, _L("Speed:"));
float translation_scale = (float)params_copy.translation.scale / Params::DefaultTranslationScale;
if (imgui.slider_float(_L("Translation") + "##1", &translation_scale, 0.1f, 10.0f, "%.1f")) {
if (imgui.slider_float(_L("Translation") + "##1", &translation_scale, Params::MinTranslationScale, Params::MaxTranslationScale, "%.1f")) {
params_copy.translation.scale = Params::DefaultTranslationScale * (double)translation_scale;
params_changed = true;
}

View File

@ -33,6 +33,8 @@ class Mouse3DController
// to copy the parameters.
struct Params
{
static constexpr double MinTranslationScale = 0.1;
static constexpr double MaxTranslationScale = 30.;
static constexpr double DefaultTranslationScale = 2.5;
static constexpr double MaxTranslationDeadzone = 0.2;
static constexpr double DefaultTranslationDeadzone = 0.0;

View File

@ -1214,7 +1214,7 @@ void Sidebar::show_info_sizer()
ModelObjectPtrs objects = p->plater->model().objects;
int obj_idx = selection.get_object_idx();
if (m_mode < comExpert || objects.empty() || obj_idx < 0 || obj_idx > 1000 ||
if (m_mode < comExpert || objects.empty() || obj_idx < 0 || obj_idx == 1000 ||
objects[obj_idx]->volumes.empty() || // hack to avoid crash when deleting the last object on the bed
(selection.is_single_full_object() && objects[obj_idx]->instances.size()> 1) ||
!(selection.is_single_full_instance() || selection.is_single_volume())) {
@ -1675,6 +1675,7 @@ struct Plater::priv
~priv();
bool is_project_dirty() const { return dirty_state.is_dirty(); }
bool is_presets_dirty() const { return dirty_state.is_presets_dirty(); }
void update_project_dirty_from_presets() { dirty_state.update_from_presets(); }
int save_project_if_dirty(const wxString& reason) {
int res = wxID_NO;
@ -3866,18 +3867,20 @@ void Plater::priv::set_current_panel(wxPanel* panel)
preview->get_canvas3d()->bind_event_handlers();
// see: Plater::priv::object_list_changed()
// FIXME: it may be better to have a single function making this check and let it be called wherever needed
bool export_in_progress = this->background_process.is_export_scheduled();
bool model_fits = view3D->get_canvas3d()->check_volumes_outside_state() != ModelInstancePVS_Partly_Outside;
if (!model.objects.empty() && !export_in_progress && model_fits) {
if (wxGetApp().is_editor()) {
// see: Plater::priv::object_list_changed()
// FIXME: it may be better to have a single function making this check and let it be called wherever needed
bool export_in_progress = this->background_process.is_export_scheduled();
bool model_fits = view3D->get_canvas3d()->check_volumes_outside_state() != ModelInstancePVS_Partly_Outside;
if (!model.objects.empty() && !export_in_progress && model_fits) {
#if ENABLE_SEAMS_USING_MODELS
preview->get_canvas3d()->init_gcode_viewer();
preview->get_canvas3d()->init_gcode_viewer();
#endif // ENABLE_SEAMS_USING_MODELS
q->reslice();
q->reslice();
}
// keeps current gcode preview, if any
preview->reload_print(true);
}
// keeps current gcode preview, if any
preview->reload_print(true);
preview->set_as_dirty();
// reset cached size to force a resize on next call to render() to keep imgui in synch with canvas size
@ -5016,6 +5019,7 @@ Plater::Plater(wxWindow *parent, MainFrame *main_frame)
}
bool Plater::is_project_dirty() const { return p->is_project_dirty(); }
bool Plater::is_presets_dirty() const { return p->is_presets_dirty(); }
void Plater::update_project_dirty_from_presets() { p->update_project_dirty_from_presets(); }
int Plater::save_project_if_dirty(const wxString& reason) { return p->save_project_if_dirty(reason); }
void Plater::reset_project_dirty_after_save() { p->reset_project_dirty_after_save(); }

View File

@ -140,6 +140,7 @@ public:
~Plater() = default;
bool is_project_dirty() const;
bool is_presets_dirty() const;
void update_project_dirty_from_presets();
int save_project_if_dirty(const wxString& reason);
void reset_project_dirty_after_save();

View File

@ -15,6 +15,7 @@ public:
void reset_initial_presets();
bool is_dirty() const { return m_plater_dirty || m_project_config_dirty || m_presets_dirty; }
bool is_presets_dirty() const { return m_presets_dirty; }
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
void render_debug_window() const;

View File

@ -4239,6 +4239,11 @@ void TabSLAMaterial::build()
optgroup->append_single_option_line(option);
build_preset_description_line(optgroup.get());
page = add_options_page(L("Material printing profile"), "note.png");
optgroup = page->new_optgroup(L("Material printing profile"));
option = optgroup->get_option("material_print_speed");
optgroup->append_single_option_line(option);
}
// Reload current config (aka presets->edited_preset->config) into the UI fields.
@ -4249,6 +4254,13 @@ void TabSLAMaterial::reload_config()
Tab::reload_config();
}
void TabSLAMaterial::toggle_options()
{
const Preset &current_printer = wxGetApp().preset_bundle->printers.get_edited_preset();
std::string model = current_printer.config.opt_string("printer_model");
m_config_manipulation.toggle_field("material_print_speed", model != "SL1");
}
void TabSLAMaterial::update()
{
if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptFFF)

View File

@ -480,7 +480,7 @@ public:
void build() override;
void reload_config() override;
void toggle_options() override {};
void toggle_options() override;
void update() override;
void init_options_list() override;
bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptSLA; }