Merge branch 'master' into fs_svg

This commit is contained in:
Filip Sykala - NTB T15p 2023-08-31 16:43:30 +02:00
commit d9b0fad80c
58 changed files with 24273 additions and 22954 deletions

View File

@ -36,7 +36,7 @@ Each string resource in PrusaSlicer available for translation needs to be explic
```C++
auto msg = L("This message to be localized")
```
To get translated text use one of needed macro/function (`_(s)` or `_CHB(s)` ).
To get translated text use needed macro/function (`_(s)`).
If you add new file resource, add it to the list of files containing macro `L()`
### Scenario 4. How do I use GNUgettext to localize my own application taking PrusaSlicer as an example

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -165,6 +165,7 @@ struct indexed_triangle_set
std::vector<stl_vertex> vertices;
bool empty() const { return indices.empty() || vertices.empty(); }
bool operator==(const indexed_triangle_set& other) const { return this->indices == other.indices && this->vertices == other.vertices; }
};
extern bool stl_open(stl_file *stl, const char *file);

View File

@ -86,7 +86,13 @@ inline IntPoint IntPoint2d(cInt x, cInt y)
inline cInt Round(double val)
{
return static_cast<cInt>((val < 0) ? (val - 0.5) : (val + 0.5));
double v = val < 0 ? val - 0.5 : val + 0.5;
#if defined(CLIPPERLIB_INT32) && ! defined(NDEBUG)
static constexpr const double hi = 65536 * 16383;
if (v > hi || -v > hi)
throw clipperException("Coordinate outside allowed range");
#endif
return static_cast<cInt>(v);
}
// Overriding the Eigen operators because we don't want to compare Z coordinate if IntPoint is 3 dimensional.

View File

@ -30,6 +30,9 @@ void EdgeCache::sample_contour(double accuracy, std::vector<ContourLocation> &sa
const auto N = m_contour.distances.size();
const auto S = stride(N, accuracy);
if (N == 0 || S == 0)
return;
samples.reserve(N / S + 1);
for(size_t i = 0; i < N; i += S) {
samples.emplace_back(
@ -41,6 +44,10 @@ void EdgeCache::sample_contour(double accuracy, std::vector<ContourLocation> &sa
const auto NH = hc.distances.size();
const auto SH = stride(NH, accuracy);
if (NH == 0 || SH == 0)
continue;
samples.reserve(samples.size() + NH / SH + 1);
for (size_t i = 0; i < NH; i += SH) {
samples.emplace_back(

View File

@ -48,8 +48,9 @@ public:
// when fetching corners.
static inline size_t stride(const size_t N, double accuracy)
{
size_t n = std::max(size_t{1}, N);
return static_cast<coord_t>(
std::round(N / std::pow(N, std::pow(accuracy, 1./3.)))
std::round(N / std::pow(n, std::pow(accuracy, 1./3.)))
);
}

View File

@ -155,10 +155,10 @@ It check_csgmesh_booleans(const Range<It> &csgrange, Visitor &&vfn)
if (!m || MeshBoolean::cgal::empty(*m))
return;
if (!MeshBoolean::cgal::does_bound_a_volume(*m))
if (MeshBoolean::cgal::does_self_intersect(*m))
return;
if (MeshBoolean::cgal::does_self_intersect(*m))
if (!MeshBoolean::cgal::does_bound_a_volume(*m))
return;
}
catch (...) { return; }

View File

@ -239,11 +239,12 @@ namespace Slic3r {
const bool needs_toolchange = gcodegen.writer().need_toolchange(new_extruder_id);
const bool will_go_down = ! is_approx(z, current_z);
const bool is_ramming = (gcodegen.config().single_extruder_multi_material && ! tcr.priming)
const bool is_ramming = (gcodegen.config().single_extruder_multi_material)
|| (! gcodegen.config().single_extruder_multi_material && gcodegen.config().filament_multitool_ramming.get_at(tcr.initial_tool));
const bool should_travel_to_tower = tcr.force_travel // wipe tower says so
|| ! needs_toolchange // this is just finishing the tower with no toolchange
|| is_ramming;
const bool should_travel_to_tower = ! tcr.priming
&& (tcr.force_travel // wipe tower says so
|| ! needs_toolchange // this is just finishing the tower with no toolchange
|| is_ramming);
if (should_travel_to_tower) {
// FIXME: It would be better if the wipe tower set the force_travel flag for all toolchanges,
// then we could simplify the condition and make it more readable.
@ -3256,7 +3257,7 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role)
{
if (travel.length() < scale_(EXTRUDER_CONFIG(retract_before_travel))) {
if (! m_writer.extruder() || travel.length() < scale_(EXTRUDER_CONFIG(retract_before_travel))) {
// skip retraction if the move is shorter than the configured threshold
return false;
}

View File

@ -450,6 +450,19 @@ MedialAxis::MedialAxis(double min_width, double max_width, const ExPolygon &expo
void MedialAxis::build(ThickPolylines* polylines)
{
#ifndef NDEBUG
// Verify the scaling of the coordinates of input line segments.
for (const Line& l : m_lines) {
auto test = [](int32_t v) {
static constexpr const int32_t hi = 65536 * 16383;
assert(v <= hi && -v < hi);
};
test(l.a.x());
test(l.a.y());
test(l.b.x());
test(l.b.y());
}
#endif // NDEBUG
construct_voronoi(m_lines.begin(), m_lines.end(), &m_vd);
Slic3r::Voronoi::annotate_inside_outside(m_vd, m_lines);
// static constexpr double threshold_alpha = M_PI / 12.; // 30 degrees

View File

@ -81,7 +81,8 @@ void Layer::make_slices()
// Top / bottom surfaces must overlap more than 2um to be chained into a Z graph.
// Also a larger offset will likely be more robust on non-manifold input polygons.
static constexpr const float delta = scaled<float>(0.001);
co.MiterLimit = scaled<double>(3.);
// Don't scale the miter limit, it is a factor, not an absolute length!
co.MiterLimit = 3.;
// Use the default zero edge merging distance. For this kind of safety offset the accuracy of normal direction is not important.
// co.ShortestEdgeLength = delta * ClipperOffsetShortestEdgeFactor;
// static constexpr const double accept_area_threshold_ccw = sqr(scaled<double>(0.1 * delta));

View File

@ -1475,7 +1475,8 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
if (std::vector<Polygons> &top = top_raw[color_idx]; ! top.empty() && ! top[layer_idx].empty())
if (ExPolygons top_ex = union_ex(top[layer_idx]); ! top_ex.empty()) {
// Clean up thin projections. They are not printable anyways.
top_ex = opening_ex(top_ex, stat.small_region_threshold);
if (stat.small_region_threshold > 0)
top_ex = opening_ex(top_ex, stat.small_region_threshold);
if (! top_ex.empty()) {
append(triangles_by_color_top[color_idx][layer_idx + layer_idx_offset], top_ex);
float offset = 0.f;
@ -1483,7 +1484,9 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
for (int last_idx = int(layer_idx) - 1; last_idx >= std::max(int(layer_idx - stat.top_solid_layers), int(0)); --last_idx) {
offset -= stat.extrusion_width;
layer_slices_trimmed = intersection_ex(layer_slices_trimmed, input_expolygons[last_idx]);
ExPolygons last = opening_ex(intersection_ex(top_ex, offset_ex(layer_slices_trimmed, offset)), stat.small_region_threshold);
ExPolygons last = intersection_ex(top_ex, offset_ex(layer_slices_trimmed, offset));
if (stat.small_region_threshold > 0)
last = opening_ex(last, stat.small_region_threshold);
if (last.empty())
break;
append(triangles_by_color_top[color_idx][last_idx + layer_idx_offset], std::move(last));
@ -1493,7 +1496,8 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
if (std::vector<Polygons> &bottom = bottom_raw[color_idx]; ! bottom.empty() && ! bottom[layer_idx].empty())
if (ExPolygons bottom_ex = union_ex(bottom[layer_idx]); ! bottom_ex.empty()) {
// Clean up thin projections. They are not printable anyways.
bottom_ex = opening_ex(bottom_ex, stat.small_region_threshold);
if (stat.small_region_threshold > 0)
bottom_ex = opening_ex(bottom_ex, stat.small_region_threshold);
if (! bottom_ex.empty()) {
append(triangles_by_color_bottom[color_idx][layer_idx + layer_idx_offset], bottom_ex);
float offset = 0.f;
@ -1501,7 +1505,9 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
for (size_t last_idx = layer_idx + 1; last_idx < std::min(layer_idx + stat.bottom_solid_layers, num_layers); ++last_idx) {
offset -= stat.extrusion_width;
layer_slices_trimmed = intersection_ex(layer_slices_trimmed, input_expolygons[last_idx]);
ExPolygons last = opening_ex(intersection_ex(bottom_ex, offset_ex(layer_slices_trimmed, offset)), stat.small_region_threshold);
ExPolygons last = intersection_ex(bottom_ex, offset_ex(layer_slices_trimmed, offset));
if (stat.small_region_threshold > 0)
last = opening_ex(last, stat.small_region_threshold);
if (last.empty())
break;
append(triangles_by_color_bottom[color_idx][last_idx + layer_idx_offset], std::move(last));

View File

@ -1376,6 +1376,10 @@ Preset& PresetCollection::select_preset(size_t idx)
if (idx >= m_presets.size())
idx = first_visible_idx();
m_idx_selected = idx;
if (!m_presets[idx].is_visible)
// The newly selected preset can be activated -> make it visible.
m_presets[idx].is_visible = true;
m_edited_preset = m_presets[idx];
bool default_visible = ! m_default_suppressed || m_idx_selected < m_num_default_presets;
for (size_t i = 0; i < m_num_default_presets; ++i)

View File

@ -1046,11 +1046,8 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
}
// Load the configs into this->filaments and make them active.
std::vector<std::string> extr_names = std::vector<std::string>(configs.size());
// To avoid incorrect selection of the first filament preset (means a value of Preset->m_idx_selected)
// in a case when next added preset take a place of previosly selected preset,
// we should add presets from last to first
bool any_modified = false;
for (int i = (int)configs.size()-1; i >= 0; i--) {
for (int i = 0; i < (int)configs.size(); i++) {
DynamicPrintConfig &cfg = configs[i];
// Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets.
cfg.opt_string("compatible_printers_condition", true) = compatible_printers_condition_values[i + 1];
@ -1059,15 +1056,18 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
// Load all filament presets, but only select the first one in the preset dialog.
auto [loaded, modified] = this->filaments.load_external_preset(name_or_path, name,
(i < int(old_filament_profile_names->values.size())) ? old_filament_profile_names->values[i] : "",
std::move(cfg),
i == 0 ?
PresetCollection::LoadAndSelect::Always :
any_modified ?
PresetCollection::LoadAndSelect::Never :
PresetCollection::LoadAndSelect::OnlyIfModified);
std::move(cfg),
any_modified ? PresetCollection::LoadAndSelect::Never :
PresetCollection::LoadAndSelect::OnlyIfModified);
any_modified |= modified;
extr_names[i] = loaded->name;
}
// Check if some preset was selected after loading from config file.
// ! Selected preset name is always the same as name of edited preset, if selection was applied
if (this->filaments.get_selected_preset_name() != this->filaments.get_edited_preset().name)
this->filaments.select_preset_by_name(extr_names[0], true);
// create extruders_filaments only when all filaments are loaded
for (size_t id = 0; id < extr_names.size(); ++id)
this->extruders_filaments.emplace_back(ExtruderFilaments(&filaments, id, extr_names[id]));
@ -1806,7 +1806,21 @@ void PresetBundle::update_filaments_compatible(PresetSelectCompatibleType select
else
update_filament_compatible(extruder_idx);
if (this->filaments.get_idx_selected() == size_t(-1))
// validate selection in filaments
bool invalid_selection = this->filaments.get_idx_selected() == size_t(-1);
if (!invalid_selection) {
invalid_selection = true;
const std::string selected_filament_name = this->filaments.get_selected_preset_name();
for (const auto& extruder : extruders_filaments)
if (const std::string& selected_extr_filament_name = extruder.get_selected_preset_name();
selected_extr_filament_name == selected_filament_name) {
invalid_selection = false;
break;
}
}
// select valid filament from first extruder
if (invalid_selection)
this->filaments.select_preset(extruders_filaments[0].get_selected_idx());
}

View File

@ -3457,7 +3457,9 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
SupportParameters support_params(print_object);
support_params.with_sheath = true;
support_params.support_density = 0;
// Don't override the support density of tree supports, as the support density is used for raft.
// The trees will have the density zeroed in tree_supports_generate_paths()
// support_params.support_density = 0;
SupportGeneratorLayerStorage layer_storage;
SupportGeneratorLayersPtr top_contacts;

View File

@ -52,14 +52,4 @@
// Enable OpenGL debug messages using debug context
#define ENABLE_OPENGL_DEBUG_OPTION (1 && ENABLE_GL_CORE_PROFILE)
//====================
// 2.6.0.alpha1 techs
//====================
#define ENABLE_2_6_0_ALPHA1 1
// Enable alternative version of file_wildcards()
#define ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR (1 && ENABLE_2_6_0_ALPHA1)
#endif // _prusaslicer_technologies_h_

View File

@ -496,87 +496,44 @@ static const FileWildcards file_wildcards_by_type[FT_SIZE] = {
/* FT_ZIP */ { "Zip files"sv, { ".zip"sv } },
};
static wxString file_wildcards(const FileWildcards& data)
{
std::string title;
std::string mask;
// Generate cumulative first item
for (const std::string_view& ext : data.file_extensions) {
if (title.empty()) {
title = "*";
title += ext;
mask = title;
}
else {
title += ", *";
title += ext;
mask += ";*";
mask += ext;
}
mask += ";*";
mask += boost::to_upper_copy(std::string(ext));
}
wxString ret = GUI::format_wxstr("%s (%s)|%s", data.title, title, mask);
// Adds an item for each of the extensions
if (data.file_extensions.size() > 1) {
for (const std::string_view& ext : data.file_extensions) {
title = "*";
title += ext;
ret += GUI::format_wxstr("|%s (%s)|%s", data.title, title, title);
}
}
return ret;
}
#if ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR
wxString file_wildcards(FileType file_type)
{
const FileWildcards& data = file_wildcards_by_type[file_type];
return file_wildcards(data);
}
#else
// This function produces a Win32 file dialog file template mask to be consumed by wxWidgets on all platforms.
// The function accepts a custom extension parameter. If the parameter is provided, the custom extension
// will be added as a fist to the list. This is important for a "file save" dialog on OSX, which strips
// an extension from the provided initial file name and substitutes it with the default extension (the first one in the template).
wxString file_wildcards(FileType file_type, const std::string &custom_extension)
static wxString file_wildcards(const FileWildcards &wildcards, const std::string &custom_extension)
{
const FileWildcards& data = file_wildcards_by_type[file_type];
std::string title;
std::string mask;
std::string custom_ext_lower;
// Collects items for each of the extensions one by one.
wxString out_one_by_one;
auto add_single = [&out_one_by_one](const std::string_view title, const std::string_view ext) {
out_one_by_one += GUI::format_wxstr("|%s (*%s)|*%s", title, ext, ext);
};
if (! custom_extension.empty()) {
// Generate an extension into the title mask and into the list of extensions.
// Generate a custom extension into the title mask and into the list of extensions.
// Add default version (upper, lower or mixed) first based on custom extension provided.
title = std::string("*") + custom_extension;
mask = title;
add_single(wildcards.title, custom_extension);
custom_ext_lower = boost::to_lower_copy(custom_extension);
const std::string custom_ext_upper = boost::to_upper_copy(custom_extension);
if (custom_ext_lower == custom_extension) {
// Add a lower case version.
title = std::string("*") + custom_ext_lower;
mask = title;
// Add an upper case version.
mask += ";*";
mask += custom_ext_upper;
// Add one more variant - the upper case extension.
mask += ";*";
mask += custom_ext_upper;
add_single(wildcards.title, custom_ext_upper);
} else if (custom_ext_upper == custom_extension) {
// Add an upper case version.
title = std::string("*") + custom_ext_upper;
mask = title;
// Add a lower case version.
// Add one more variant - the lower case extension.
mask += ";*";
mask += custom_ext_lower;
} else {
// Add the mixed case version only.
title = std::string("*") + custom_extension;
mask = title;
add_single(wildcards.title, custom_ext_lower);
}
}
for (const std::string_view &ext : data.file_extensions)
for (const std::string_view &ext : wildcards.file_extensions)
// Only add an extension if it was not added first as the custom extension.
if (ext != custom_ext_lower) {
if (title.empty()) {
@ -591,11 +548,16 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension)
}
mask += ";*";
mask += boost::to_upper_copy(std::string(ext));
add_single(wildcards.title, ext);
}
return GUI::format_wxstr("%s (%s)|%s", data.title, title, mask);
return GUI::format_wxstr("%s (%s)|%s", wildcards.title, title, mask) + out_one_by_one;
}
wxString file_wildcards(FileType file_type, const std::string &custom_extension)
{
return file_wildcards(file_wildcards_by_type[file_type], custom_extension);
}
#endif // ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR
wxString sla_wildcards(const char *formatid)
{
@ -617,7 +579,7 @@ wxString sla_wildcards(const char *formatid)
wc.file_extensions.emplace_back(ext);
}
ret = file_wildcards(wc);
ret = file_wildcards(wc, {});
}
if (ret.empty())
@ -2864,6 +2826,9 @@ void GUI_App::load_current_presets(bool check_printer_presets_ /*= true*/)
// Mark the plater to update print bed by tab->load_current_preset() from Plater::on_config_change().
this->plater()->force_print_bed_update();
}
else if (tab->type() == Preset::TYPE_FILAMENT)
// active extruder can be changed in a respect to the new loaded configurations, if some filament preset will be modified
static_cast<TabFilament*>(tab)->invalidate_active_extruder();
tab->load_current_preset();
}
}

View File

@ -78,11 +78,7 @@ enum FileType
FT_SIZE,
};
#if ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR
extern wxString file_wildcards(FileType file_type);
#else
extern wxString file_wildcards(FileType file_type, const std::string &custom_extension = std::string{});
#endif // ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR
extern wxString file_wildcards(FileType file_type, const std::string &custom_extension = {});
wxString sla_wildcards(const char *formatid);

View File

@ -2326,42 +2326,8 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors)
});
if (m_connector_type == CutConnectorType::Snap) {
const std::string format = "%.0f %%";
bool is_changed = false;
{
const std::string label = _u8L("Bulge");
ImGuiWrapper::text(label);
ImGui::SameLine(m_label_width);
ImGui::PushItemWidth(m_control_width * 0.7f);
float val = m_snap_bulge_proportion *100.f;
if (m_imgui->slider_float(("##snap_" + label).c_str(), &val, 5.f, 100.f * m_snap_space_proportion, format.c_str(), 1.f, true, _u8L("Bulge proportion related to radius"))) {
m_snap_bulge_proportion = val * 0.01f;
is_changed = true;
}
}
{
const std::string label = _u8L("Space");
ImGuiWrapper::text(label);
ImGui::SameLine(m_label_width);
ImGui::PushItemWidth(m_control_width * 0.7f);
float val = m_snap_space_proportion *100.f;
if (m_imgui->slider_float(("##snap_" + label).c_str(), &val, 10.f, 50.f, format.c_str(), 1.f, true, _u8L("Space proportion related to radius"))) {
m_snap_space_proportion = val * 0.01f;
is_changed = true;
}
}
if (is_changed) {
update_connector_shape();
update_raycasters_for_picking();
}
render_snap_specific_input(_u8L("Bulge"), _u8L("Bulge proportion related to radius"), m_snap_bulge_proportion, 0.15f, 5.f, 100.f * m_snap_space_proportion);
render_snap_specific_input(_u8L("Space"), _u8L("Space proportion related to radius"), m_snap_space_proportion, 0.3f, 10.f, 50.f);
}
ImGui::Separator();
@ -2616,6 +2582,37 @@ void GLGizmoCut3D::render_groove_angle_input(const std::string& label, float& in
}
}
void GLGizmoCut3D::render_snap_specific_input(const std::string& label, const std::string& tooltip, float& in_val, const float& init_val, const float min_val, const float max_val)
{
ImGuiWrapper::text(label);
ImGui::SameLine(m_label_width);
ImGui::PushItemWidth(m_control_width * 0.7f);
bool is_changed = false;
const std::string format = "%.0f %%";
float val = in_val * 100.f;
if (m_imgui->slider_float(("##snap_" + label).c_str(), &val, min_val, max_val, format.c_str(), 1.f, true, tooltip)) {
in_val = val * 0.01f;
is_changed = true;
}
ImGui::SameLine();
m_imgui->disabled_begin(is_approx(in_val, init_val));
const std::string act_name = _u8L("Reset");
if (render_reset_button(("##snap_" + label + act_name).c_str(), act_name)) {
in_val = init_val;
is_changed = true;
}
m_imgui->disabled_end();
if (is_changed) {
update_connector_shape();
update_raycasters_for_picking();
}
}
void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors)
{

View File

@ -302,6 +302,7 @@ protected:
void render_color_marker(float size, const ImU32& color);
void render_groove_float_input(const std::string &label, float &in_val, const float &init_val, float &in_tolerance);
void render_groove_angle_input(const std::string &label, float &in_val, const float &init_val, float min_val, float max_val);
void render_snap_specific_input(const std::string& label, const std::string& tooltip, float& in_val, const float& init_val, const float min_val, const float max_val);
void render_cut_plane_input_window(CutConnectors &connectors);
void init_input_window_data(CutConnectors &connectors);
void render_input_window_warning() const;

View File

@ -22,12 +22,6 @@
#define L_CONTEXT(s, context) s
#endif /* L */
#ifndef _CHB
//! macro used to localization, return wxScopedCharBuffer
//! With wxConvUTF8 explicitly specify that the source string is already in UTF-8 encoding
#define _CHB(s) wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str()
#endif /* _CHB */
#ifndef slic3r_GUI_I18N_hpp_
#define slic3r_GUI_I18N_hpp_

View File

@ -1147,6 +1147,19 @@ static const wxString sep_space = "";
static wxMenu* generate_help_menu()
{
wxMenu* helpMenu = new wxMenu();
append_menu_item(helpMenu, wxID_ANY, wxString::Format(_L("%s &Website"), SLIC3R_APP_NAME),
wxString::Format(_L("Open the %s website in your browser"), SLIC3R_APP_NAME),
[](wxCommandEvent&) { wxGetApp().open_web_page_localized("https://www.prusa3d.com/slicerweb"); });
// TRN Item from "Help" menu
append_menu_item(helpMenu, wxID_ANY, wxString::Format(_L("&Quick Start"), SLIC3R_APP_NAME),
wxString::Format(_L("Open the %s website in your browser"), SLIC3R_APP_NAME),
[](wxCommandEvent&) { wxGetApp().open_browser_with_warning_dialog("https://help.prusa3d.com/article/first-print-with-prusaslicer_1753", nullptr, false); });
// TRN Item from "Help" menu
append_menu_item(helpMenu, wxID_ANY, wxString::Format(_L("Sample &G-codes and Models"), SLIC3R_APP_NAME),
wxString::Format(_L("Open the %s website in your browser"), SLIC3R_APP_NAME),
[](wxCommandEvent&) { wxGetApp().open_browser_with_warning_dialog("https://help.prusa3d.com/article/sample-g-codes_529630", nullptr, false); });
helpMenu->AppendSeparator();
append_menu_item(helpMenu, wxID_ANY, _L("Prusa 3D &Drivers"), _L("Open the Prusa3D drivers download page in your browser"),
[](wxCommandEvent&) { wxGetApp().open_web_page_localized("https://www.prusa3d.com/downloads"); });
append_menu_item(helpMenu, wxID_ANY, _L("Software &Releases"), _L("Open the software releases page in your browser"),
@ -1155,9 +1168,6 @@ static wxMenu* generate_help_menu()
//# wxTheApp->check_version(1);
//# });
//# $versioncheck->Enable(wxTheApp->have_version_check);
append_menu_item(helpMenu, wxID_ANY, wxString::Format(_L("%s &Website"), SLIC3R_APP_NAME),
wxString::Format(_L("Open the %s website in your browser"), SLIC3R_APP_NAME),
[](wxCommandEvent&) { wxGetApp().open_web_page_localized("https://www.prusa3d.com/slicerweb"); });
// append_menu_item(helpMenu, wxID_ANY, wxString::Format(_L("%s &Manual"), SLIC3R_APP_NAME),
// wxString::Format(_L("Open the %s manual in your browser"), SLIC3R_APP_NAME),
// [this](wxCommandEvent&) { wxGetApp().open_browser_with_warning_dialog("http://manual.slic3r.org/"); });

View File

@ -2814,7 +2814,9 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& mode
#endif /* AUTOPLACEMENT_ON_LOAD */
}
for (size_t i = 0; i < object->instances.size(); ++i) {
for (size_t i = 0; i < object->instances.size()
&& !object->is_cut() // don't apply scaled_down functionality to cut objects
; ++i) {
ModelInstance* instance = object->instances[i];
const Vec3d size = object->instance_bounding_box(i).size();
const Vec3d ratio = size.cwiseQuotient(bed_size);
@ -3602,9 +3604,14 @@ bool Plater::priv::replace_volume_with_stl(int object_idx, int volume_idx, const
new_volume->convert_from_imperial_units();
else if (old_volume->source.is_converted_from_meters)
new_volume->convert_from_meters();
new_volume->supported_facets.assign(old_volume->supported_facets);
new_volume->seam_facets.assign(old_volume->seam_facets);
new_volume->mmu_segmentation_facets.assign(old_volume->mmu_segmentation_facets);
if (old_volume->mesh().its == new_volume->mesh().its) {
// This function is called both from reload_from_disk and replace_with_stl.
// We need to make sure that the painted data point to existing triangles.
new_volume->supported_facets.assign(old_volume->supported_facets);
new_volume->seam_facets.assign(old_volume->seam_facets);
new_volume->mmu_segmentation_facets.assign(old_volume->mmu_segmentation_facets);
}
std::swap(old_model_object->volumes[volume_idx], old_model_object->volumes.back());
old_model_object->delete_volume(old_model_object->volumes.size() - 1);
if (!sinking)
@ -4026,7 +4033,8 @@ void Plater::priv::set_current_panel(wxPanel* panel)
bool model_fits = view3D->get_canvas3d()->check_volumes_outside_state() != ModelInstancePVS_Partly_Outside;
if (!model.objects.empty() && !export_in_progress && model_fits) {
preview->get_canvas3d()->init_gcode_viewer();
preview->load_gcode_shells();
if (! this->background_process.finished())
preview->load_gcode_shells();
q->reslice();
}
// keeps current gcode preview, if any
@ -6467,19 +6475,12 @@ void Plater::export_gcode(bool prefer_removable)
fs::path output_path;
{
#if !ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR
std::string ext = default_output_file.extension().string();
#endif // !ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR
wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _L("Save G-code file as:") : _L("Save SL1 / SL1S file as:"),
start_dir,
from_path(default_output_file.filename()),
#if ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR
printer_technology() == ptFFF ? GUI::file_wildcards(FT_GCODE) :
GUI::sla_wildcards(p->sla_print.printer_config().sla_archive_format.value.c_str()),
#else
printer_technology() == ptFFF ? GUI::file_wildcards(FT_GCODE, ext) :
GUI::sla_wildcards(p->sla_print.printer_config().sla_archive_format.value.c_str()),
#endif // ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR
wxFD_SAVE | wxFD_OVERWRITE_PROMPT
);
if (dlg.ShowModal() == wxID_OK) {

View File

@ -1986,8 +1986,16 @@ void TabFilament::update_extruder_combobox()
m_extruders_cb->Append(format_wxstr("%1% %2%", _L("Extruder"), id), *get_bmp_bundle("funnel"));
}
if (m_active_extruder >= int(extruder_cnt))
if (m_active_extruder >= int(extruder_cnt)) {
m_active_extruder = 0;
// update selected and, as a result, editing preset
const std::string& preset_name = m_preset_bundle->extruders_filaments[0].get_selected_preset_name();
m_presets->select_preset_by_name(preset_name, true);
// To avoid inconsistance between value of active_extruder in FilamentTab and TabPresetComboBox,
// which can causes a crash on switch preset from MM printer to SM printer
m_presets_choice->set_active_extruder(m_active_extruder);
}
m_extruders_cb->SetSelection(m_active_extruder);
}
@ -2317,12 +2325,37 @@ void TabFilament::sys_color_changed()
void TabFilament::load_current_preset()
{
const std::string& selected_filament_name = m_presets->get_selected_preset_name();
if (m_active_extruder < 0) {
// active extruder was invalidated before load new project file or configuration,
// so we have to update active extruder selection from selected filament
const std::string& edited_filament_name = m_presets->get_edited_preset().name;
assert(!selected_filament_name.empty() && selected_filament_name == edited_filament_name);
for (int i = 0; i < int(m_preset_bundle->extruders_filaments.size()); i++) {
const std::string& selected_extr_filament_name = m_preset_bundle->extruders_filaments[i].get_selected_preset_name();
if (selected_extr_filament_name == edited_filament_name) {
m_active_extruder = i;
break;
}
}
assert(m_active_extruder >= 0);
m_presets_choice->set_active_extruder(m_active_extruder);
if (m_active_extruder != m_extruders_cb->GetSelection())
m_extruders_cb->Select(m_active_extruder);
}
assert(m_active_extruder >= 0 && m_active_extruder < m_preset_bundle->extruders_filaments.size());
const std::string& selected_extr_filament_name = m_preset_bundle->extruders_filaments[m_active_extruder].get_selected_preset_name();
const std::string& selected_filament_name = m_presets->get_selected_preset_name();
if (selected_extr_filament_name != selected_filament_name)
if (selected_extr_filament_name != selected_filament_name) {
m_presets->select_preset_by_name(selected_extr_filament_name, false);
// To avoid inconsistance between value of active_extruder in FilamentTab and TabPresetComboBox,
// which can causes a crash on switch preset from MM printer to SM printer
m_presets_choice->set_active_extruder(m_active_extruder);
}
Tab::load_current_preset();
}
@ -3639,8 +3672,17 @@ bool Tab::select_preset(std::string preset_name, bool delete_current /*=false*/,
for (PresetUpdate &pu : updates) {
pu.old_preset_dirty = (old_printer_technology == pu.technology) && pu.presets->current_is_dirty();
pu.new_preset_compatible = (new_printer_technology == pu.technology) && is_compatible_with_printer(pu.presets->get_edited_preset_with_vendor_profile(), new_printer_preset_with_vendor_profile);
bool force_update_edited_preset = false;
if (pu.tab_type == Preset::TYPE_FILAMENT && pu.new_preset_compatible) {
// check if edited preset will be still correct after selection new printer
const int active_extruder = dynamic_cast<const TabFilament*>(wxGetApp().get_tab(Preset::TYPE_FILAMENT))->get_active_extruder();
const int extruder_count_new = int(dynamic_cast<const ConfigOptionFloats*>(new_printer_preset.config.option("nozzle_diameter"))->size());
// if active_extruder is bigger than extruders_count,
// then it means that edited filament preset will be changed and we have to check this changes
force_update_edited_preset = active_extruder >= extruder_count_new;
}
if (!canceled)
canceled = pu.old_preset_dirty && !pu.new_preset_compatible && !may_discard_current_dirty_preset(pu.presets, preset_name);
canceled = pu.old_preset_dirty && (!pu.new_preset_compatible || force_update_edited_preset) && !may_discard_current_dirty_preset(pu.presets, preset_name);
}
if (!canceled) {
for (PresetUpdate &pu : updates) {

View File

@ -469,6 +469,7 @@ public:
// set actiev extruder and update preset combobox if needed
// return false, if new preset wasn't selected
bool set_active_extruder(int new_selected_extruder);
void invalidate_active_extruder() { m_active_extruder = -1; }
void update_extruder_combobox();
int get_active_extruder() const { return m_active_extruder; }

View File

@ -930,7 +930,7 @@ TEST_CASE("Optimal nfp position search with GravityKernel using RectangleItem an
Slic3r::Vec2crd D = bed.center - item.shape.center();
REQUIRE(item.translation == D);
REQUIRE(score == Approx(0.));
REQUIRE(score == Approx(0.).margin(EPSILON));
}
}
}