Fixed conflicts after merge with master

This commit is contained in:
enricoturri1966 2020-12-03 15:27:34 +01:00
commit cc0688678c
51 changed files with 7369 additions and 6120 deletions

View file

@ -229,7 +229,7 @@ if(WIN32)
add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS)
if(MSVC)
# BOOST_ALL_NO_LIB: Avoid the automatic linking of Boost libraries on Windows. Rather rely on explicit linking.
add_definitions(-DBOOST_ALL_NO_LIB -DBOOST_USE_WINAPI_VERSION=0x601 )
add_definitions(-DBOOST_ALL_NO_LIB -DBOOST_USE_WINAPI_VERSION=0x601 -DBOOST_SYSTEM_USE_UTF8 )
endif(MSVC)
endif(WIN32)

View file

@ -22,16 +22,4 @@ endif()
# if the installed project requested no architecture check, don't perform the check
if("FALSE")
return()
endif()
# if the installed or the using project don't have CMAKE_SIZEOF_VOID_P set, ignore it:
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "" OR "8" STREQUAL "")
return()
endif()
# check that the installed version has the same 32/64bit-ness as the one which is currently searching:
if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "8")
math(EXPR installedBits "8 * 8")
set(PACKAGE_VERSION "${PACKAGE_VERSION} (${installedBits}bit)")
set(PACKAGE_VERSION_UNSUITABLE TRUE)
endif()
endif()

13
deps/GMP/GMP.cmake vendored
View file

@ -19,15 +19,18 @@ if (MSVC)
else ()
set(_gmp_ccflags "-O2 -DNDEBUG -fPIC -DPIC -Wall -Wmissing-prototypes -Wpointer-arith -pedantic -fomit-frame-pointer -fno-common")
set(_gmp_build_tgt "${CMAKE_SYSTEM_PROCESSOR}")
if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm")
set(_gmp_ccflags "${_gmp_ccflags} -march=armv7-a") # Works on RPi-4
set(_gmp_build_tgt armv7)
endif()
if (APPLE)
if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm")
set(_gmp_build_tgt aarch64)
endif()
set(_gmp_ccflags "${_gmp_ccflags} -mmacosx-version-min=${DEP_OSX_TARGET}")
set(_gmp_build_tgt "--build=${_gmp_build_tgt}-apple-darwin")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm")
set(_gmp_ccflags "${_gmp_ccflags} -march=armv7-a") # Works on RPi-4
set(_gmp_build_tgt armv7)
endif()
set(_gmp_build_tgt "--build=${_gmp_build_tgt}-pc-linux-gnu")
else ()
set(_gmp_build_tgt "") # let it guess
@ -35,7 +38,7 @@ else ()
ExternalProject_Add(dep_GMP
# URL https://gmplib.org/download/gmp/gmp-6.1.2.tar.bz2
URL https://gmplib.org/download/gmp/gmp-6.2.0.tar.lz
URL https://gmplib.org/download/gmp/gmp-6.2.1.tar.bz2
BUILD_IN_SOURCE ON
CONFIGURE_COMMAND env "CFLAGS=${_gmp_ccflags}" "CXXFLAGS=${_gmp_ccflags}" ./configure --enable-shared=no --enable-cxx=yes --enable-static=yes "--prefix=${DESTDIR}/usr/local" ${_gmp_build_tgt}
BUILD_COMMAND make -j

8
deps/PNG/PNG.cmake vendored
View file

@ -1,3 +1,9 @@
if (APPLE)
# Only disable NEON extension for Apple ARM builds, leave it enabled for Raspberry PI.
set(_disable_neon_extension "-DPNG_ARM_NEON=off")
else ()
set(_disable_neon_extension "")
endif ()
prusaslicer_add_cmake_project(PNG
GIT_REPOSITORY https://github.com/glennrp/libpng.git
@ -8,6 +14,8 @@ prusaslicer_add_cmake_project(PNG
-DPNG_STATIC=ON
-DPNG_PREFIX=prusaslicer_
-DPNG_TESTS=OFF
-DDISABLE_DEPENDENCY_TRACKING=OFF
${_disable_neon_extension}
)
if (MSVC)

View file

@ -1,4 +1,11 @@
set(_wx_git_tag v3.1.3-patched)
if (APPLE AND ${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm")
# The new OSX 11 (Big Sur) is not compatible with wxWidgets 3.1.3.
# Let's use patched wxWidgets 3.1.4, even though it is not quite tested.
set(_wx_git_tag v3.1.4-patched)
else ()
# Use the tested patched wxWidgets 3.1.3 everywhere else.
set(_wx_git_tag v3.1.3-patched)
endif ()
# set(_patch_command "")
set(_wx_toolkit "")

File diff suppressed because it is too large Load diff

View file

@ -4902,7 +4902,7 @@ msgstr "%s encontrou um erro"
#: src/slic3r/GUI/NotificationManager.hpp:317
msgid "Exporting finished."
msgstr "Falha na exportação."
msgstr "Exportação concluída."
#: src/slic3r/GUI/NotificationManager.hpp:317
msgid "Eject drive."

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -167,6 +167,8 @@ struct NfpPConfig {
const ItemGroup& // remaining items
)> before_packing;
std::function<void(const ItemGroup &, NfpPConfig &config)> on_preload;
NfpPConfig(): rotations({0.0, Pi/2.0, Pi, 3*Pi/2}),
alignment(Alignment::CENTER), starting_point(Alignment::CENTER) {}
};
@ -577,6 +579,12 @@ public:
Base::clearItems();
}
void preload(const ItemGroup& packeditems) {
Base::preload(packeditems);
if (config_.on_preload)
config_.on_preload(packeditems, config_);
}
private:
using Shapes = TMultiShape<RawShape>;
@ -663,62 +671,79 @@ private:
remlist.insert(remlist.end(), remaining.from, remaining.to);
}
double global_score = std::numeric_limits<double>::max();
auto initial_tr = item.translation();
auto initial_rot = item.rotation();
Vertex final_tr = {0, 0};
Radians final_rot = initial_rot;
Shapes nfps;
auto& bin = bin_;
double norm = norm_;
auto pbb = sl::boundingBox(merged_pile_);
auto binbb = sl::boundingBox(bin);
// This is the kernel part of the object function that is
// customizable by the library client
std::function<double(const Item&)> _objfunc;
if(config_.object_function) _objfunc = config_.object_function;
else {
// Inside check has to be strict if no alignment was enabled
std::function<double(const Box&)> ins_check;
if(config_.alignment == Config::Alignment::DONT_ALIGN)
ins_check = [&binbb, norm](const Box& fullbb) {
double ret = 0;
if(!sl::isInside(fullbb, binbb))
ret += norm;
return ret;
};
else
ins_check = [&bin](const Box& fullbb) {
double miss = overfit(fullbb, bin);
miss = miss > 0? miss : 0;
return std::pow(miss, 2);
};
_objfunc = [norm, binbb, pbb, ins_check](const Item& item)
{
auto ibb = item.boundingBox();
auto fullbb = sl::boundingBox(pbb, ibb);
double score = pl::distance(ibb.center(),
binbb.center());
score /= norm;
score += ins_check(fullbb);
return score;
};
}
if(items_.empty()) {
setInitialPosition(item);
auto best_tr = item.translation();
auto best_rot = item.rotation();
best_overfit = overfit(item.transformedShape(), bin_);
can_pack = best_overfit <= 0;
} else {
double global_score = std::numeric_limits<double>::max();
auto initial_tr = item.translation();
auto initial_rot = item.rotation();
Vertex final_tr = {0, 0};
Radians final_rot = initial_rot;
Shapes nfps;
auto& bin = bin_;
double norm = norm_;
auto pbb = sl::boundingBox(merged_pile_);
auto binbb = sl::boundingBox(bin);
// This is the kernel part of the object function that is
// customizable by the library client
std::function<double(const Item&)> _objfunc;
if(config_.object_function) _objfunc = config_.object_function;
else {
// Inside check has to be strict if no alignment was enabled
std::function<double(const Box&)> ins_check;
if(config_.alignment == Config::Alignment::DONT_ALIGN)
ins_check = [&binbb, norm](const Box& fullbb) {
double ret = 0;
if(!sl::isInside(fullbb, binbb))
ret += norm;
return ret;
};
else
ins_check = [&bin](const Box& fullbb) {
double miss = overfit(fullbb, bin);
miss = miss > 0? miss : 0;
return std::pow(miss, 2);
};
_objfunc = [norm, binbb, pbb, ins_check](const Item& item)
{
auto ibb = item.boundingBox();
auto fullbb = sl::boundingBox(pbb, ibb);
double score = pl::distance(ibb.center(),
binbb.center());
score /= norm;
score += ins_check(fullbb);
return score;
};
for(auto rot : config_.rotations) {
item.translation(initial_tr);
item.rotation(initial_rot + rot);
setInitialPosition(item);
double of = 0.;
if ((of = overfit(item.transformedShape(), bin_)) < best_overfit) {
best_overfit = of;
best_tr = item.translation();
best_rot = item.rotation();
}
}
can_pack = best_overfit <= 0;
item.rotation(best_rot);
item.translation(best_tr);
} else {
Pile merged_pile = merged_pile_;
for(auto rot : config_.rotations) {
@ -948,10 +973,9 @@ private:
if(items_.empty() ||
config_.alignment == Config::Alignment::DONT_ALIGN) return;
nfp::Shapes<RawShape> m;
m.reserve(items_.size());
for(Item& item : items_) m.emplace_back(item.transformedShape());
auto&& bb = sl::boundingBox(m);
Box bb = items_.front().get().boundingBox();
for(Item& item : items_)
bb = sl::boundingBox(item.boundingBox(), bb);
Vertex ci, cb;
@ -988,7 +1012,7 @@ private:
for(Item& item : items_) item.translate(d);
}
void setInitialPosition(Item& item) {
void setInitialPosition(Item& item) {
Box bb = item.boundingBox();
Vertex ci, cb;

View file

@ -109,6 +109,7 @@ void fill_config(PConf& pcfg, const ArrangeParams &params) {
// Apply penalty to object function result. This is used only when alignment
// after arrange is explicitly disabled (PConfig::Alignment::DONT_ALIGN)
// Also, this will only work well for Box shaped beds.
static double fixed_overfit(const std::tuple<double, Box>& result, const Box &binbb)
{
double score = std::get<0>(result);
@ -348,6 +349,17 @@ public:
m_pconf.object_function = get_objfn();
m_pconf.on_preload = [this](const ItemGroup &items, PConfig &cfg) {
if (items.empty()) return;
cfg.alignment = PConfig::Alignment::DONT_ALIGN;
auto bb = sl::boundingBox(m_bin);
auto bbcenter = bb.center();
cfg.object_function = [this, bb, bbcenter](const Item &item) {
return fixed_overfit(objfunc(item, bbcenter), bb);
};
};
auto on_packed = params.on_packed;
if (progressind || on_packed)
@ -383,22 +395,12 @@ public:
PConfig& config() { return m_pconf; }
const PConfig& config() const { return m_pconf; }
inline void preload(std::vector<Item>& fixeditems) {
m_pconf.alignment = PConfig::Alignment::DONT_ALIGN;
auto bb = sl::boundingBox(m_bin);
auto bbcenter = bb.center();
m_pconf.object_function = [this, bb, bbcenter](const Item &item) {
return fixed_overfit(objfunc(item, bbcenter), bb);
};
// Build the rtree for queries to work
inline void preload(std::vector<Item>& fixeditems) {
for(unsigned idx = 0; idx < fixeditems.size(); ++idx) {
Item& itm = fixeditems[idx];
itm.markAsFixedInBin(itm.binId());
}
m_pck.configure(m_pconf);
m_item_count += fixeditems.size();
}
};
@ -412,13 +414,10 @@ template<> std::function<double(const Item&)> AutoArranger<Box>::get_objfn()
double score = std::get<0>(result);
auto& fullbb = std::get<1>(result);
auto bin = m_bin;
sl::offset(bin, -EPSILON * (m_bin.width() + m_bin.height()));
double miss = Placer::overfit(fullbb, bin);
double miss = Placer::overfit(fullbb, m_bin);
miss = miss > 0? miss : 0;
score += miss*miss;
score += miss * miss;
return score;
};
@ -486,7 +485,7 @@ void _arrange(
{
// Integer ceiling the min distance from the bed perimeters
coord_t md = params.min_obj_distance;
md = (md % 2) ? md / 2 + 1 : md / 2;
md = md / 2;
auto corrected_bin = bin;
sl::offset(corrected_bin, md);
@ -573,10 +572,13 @@ static void process_arrangeable(const ArrangePolygon &arrpoly,
clppr::Polygon clpath(Slic3rMultiPoint_to_ClipperPath(p));
if (!clpath.Contour.empty()) {
auto firstp = clpath.Contour.front();
clpath.Contour.emplace_back(firstp);
}
// This fixes:
// https://github.com/prusa3d/PrusaSlicer/issues/2209
if (clpath.Contour.size() < 3)
return;
auto firstp = clpath.Contour.front();
clpath.Contour.emplace_back(firstp);
outp.emplace_back(std::move(clpath));
outp.back().rotation(rotation);

View file

@ -62,6 +62,15 @@ struct ArrangePolygon {
/// Test if arrange() was called previously and gave a successful result.
bool is_arranged() const { return bed_idx != UNARRANGED; }
inline ExPolygon transformed_poly() const
{
ExPolygon ret = poly;
ret.rotate(rotation);
ret.translate(translation.x(), translation.y());
return ret;
}
};
using ArrangePolygons = std::vector<ArrangePolygon>;

View file

@ -53,6 +53,9 @@ public:
return point(0) >= this->min(0) && point(0) <= this->max(0)
&& point(1) >= this->min(1) && point(1) <= this->max(1);
}
bool contains(const BoundingBoxBase<PointClass> &other) const {
return contains(other.min) && contains(other.max);
}
bool overlap(const BoundingBoxBase<PointClass> &other) const {
return ! (this->max(0) < other.min(0) || this->min(0) > other.max(0) ||
this->max(1) < other.min(1) || this->min(1) > other.max(1));

View file

@ -20,7 +20,8 @@ SLIC3R_DERIVE_EXCEPTION(OutOfRange, LogicError);
SLIC3R_DERIVE_EXCEPTION(IOError, CriticalException);
SLIC3R_DERIVE_EXCEPTION(FileIOError, IOError);
SLIC3R_DERIVE_EXCEPTION(HostNetworkError, IOError);
SLIC3R_DERIVE_EXCEPTION(ExportError, CriticalException);
SLIC3R_DERIVE_EXCEPTION(ExportError, CriticalException);
SLIC3R_DERIVE_EXCEPTION(PlaceholderParserError, RuntimeError);
// Runtime exception produced by Slicer. Such exception cancels the slicing process and it shall be shown in notifications.
SLIC3R_DERIVE_EXCEPTION(SlicingError, Exception);
#undef SLIC3R_DERIVE_EXCEPTION

View file

@ -748,15 +748,17 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re
if (! m_placeholder_parser_failed_templates.empty()) {
// G-code export proceeded, but some of the PlaceholderParser substitutions failed.
//FIXME localize!
std::string msg = std::string("G-code export to ") + path + " failed due to invalid custom G-code sections:\n\n";
for (const std::string &name : m_placeholder_parser_failed_templates)
msg += std::string("\t") + name + "\n";
for (const auto &name_and_error : m_placeholder_parser_failed_templates)
msg += name_and_error.first + "\n" + name_and_error.second + "\n";
msg += "\nPlease inspect the file ";
msg += path_tmp + " for error messages enclosed between\n";
msg += " !!!!! Failed to process the custom G-code template ...\n";
msg += "and\n";
msg += " !!!!! End of an error report for the custom G-code template ...\n";
throw Slic3r::RuntimeError(msg);
msg += "for all macro processing errors.";
throw Slic3r::PlaceholderParserError(msg);
}
BOOST_LOG_TRIVIAL(debug) << "Start processing gcode, " << log_memory_info();
@ -1434,7 +1436,11 @@ std::string GCode::placeholder_parser_process(const std::string &name, const std
return m_placeholder_parser.process(templ, current_extruder_id, config_override);
} catch (std::runtime_error &err) {
// Collect the names of failed template substitutions for error reporting.
m_placeholder_parser_failed_templates.insert(name);
auto it = m_placeholder_parser_failed_templates.find(name);
if (it == m_placeholder_parser_failed_templates.end())
// Only if there was no error reported for this template, store the first error message into the map to be reported.
// We don't want to collect error message for each and every occurence of a single custom G-code section.
m_placeholder_parser_failed_templates.insert(it, std::make_pair(name, std::string(err.what())));
// Insert the macro error message into the G-code.
return
std::string("\n!!!!! Failed to process the custom G-code template ") + name + "\n" +

View file

@ -19,6 +19,7 @@
#include "GCode/ThumbnailData.hpp"
#include <memory>
#include <map>
#include <string>
#ifdef HAS_PRESSURE_EQUALIZER
@ -323,7 +324,7 @@ private:
GCodeWriter m_writer;
PlaceholderParser m_placeholder_parser;
// Collection of templates, on which the placeholder substitution failed.
std::set<std::string> m_placeholder_parser_failed_templates;
std::map<std::string, std::string> m_placeholder_parser_failed_templates;
OozePrevention m_ooze_prevention;
Wipe m_wipe;
AvoidCrossingPerimeters m_avoid_crossing_perimeters;

View file

@ -266,8 +266,9 @@ inline bool liang_barsky_line_clipping(
// Clipped successfully.
x1 = x0 + interval.second * v;
x0 += interval.first * v;
return true;
}
return true;
return false;
}
// Based on Liang-Barsky function by Daniel White @ http://www.skytopia.com/project/articles/compsci/clipping.html

View file

@ -1273,6 +1273,10 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
ModelVolume* volume = this->volumes.front();
TriangleMeshPtrs meshptrs = volume->mesh().split();
for (TriangleMesh *mesh : meshptrs) {
// FIXME: crashes if not satisfied
if (mesh->facets_count() < 3) continue;
mesh->repair();
// XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed?
@ -1846,7 +1850,7 @@ void ModelInstance::transform_polygon(Polygon* polygon) const
arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
{
static const double SIMPLIFY_TOLERANCE_MM = 0.1;
// static const double SIMPLIFY_TOLERANCE_MM = 0.1;
Vec3d rotation = get_rotation();
rotation.z() = 0.;
@ -1858,13 +1862,11 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
assert(!p.points.empty());
// this may happen for malformed models, see:
// https://github.com/prusa3d/PrusaSlicer/issues/2209
if (!p.points.empty()) {
Polygons pp{p};
pp = p.simplify(scaled<double>(SIMPLIFY_TOLERANCE_MM));
if (!pp.empty()) p = pp.front();
}
// if (!p.points.empty()) {
// Polygons pp{p};
// pp = p.simplify(scaled<double>(SIMPLIFY_TOLERANCE_MM));
// if (!pp.empty()) p = pp.front();
// }
arrangement::ArrangePolygon ret;
ret.poly.contour = std::move(p);

View file

@ -6,6 +6,7 @@
#include <iomanip>
#include <sstream>
#include <map>
#include <boost/nowide/convert.hpp>
#ifdef _MSC_VER
#include <stdlib.h> // provides **_environ
#else
@ -870,7 +871,9 @@ namespace client
}
}
msg += '\n';
msg += error_line;
// This hack removes all non-UTF8 characters from the source line, so that the upstream wxWidgets conversions
// from UTF8 to UTF16 don't bail out.
msg += boost::nowide::narrow(boost::nowide::widen(error_line));
msg += '\n';
for (size_t i = 0; i < error_pos; ++ i)
msg += ' ';
@ -1304,7 +1307,7 @@ static std::string process_macro(const std::string &templ, client::MyContext &co
if (!context.error_message.empty()) {
if (context.error_message.back() != '\n' && context.error_message.back() != '\r')
context.error_message += '\n';
throw Slic3r::RuntimeError(context.error_message);
throw Slic3r::PlaceholderParserError(context.error_message);
}
return output;
}

View file

@ -40,11 +40,11 @@ public:
const DynamicConfig* external_config() const { return m_external_config; }
// Fill in the template using a macro processing language.
// Throws Slic3r::RuntimeError on syntax or runtime error.
// Throws Slic3r::PlaceholderParserError on syntax or runtime error.
std::string process(const std::string &templ, unsigned int current_extruder_id = 0, const DynamicConfig *config_override = nullptr) const;
// Evaluate a boolean expression using the full expressive power of the PlaceholderParser boolean expression syntax.
// Throws Slic3r::RuntimeError on syntax or runtime error.
// Throws Slic3r::PlaceholderParserError on syntax or runtime error.
static bool evaluate_boolean_expression(const std::string &templ, const DynamicConfig &config, const DynamicConfig *config_override = nullptr);
// Update timestamp, year, month, day, hour, minute, second variables at the provided config.

View file

@ -69,7 +69,7 @@ std::string PrintBase::output_filename(const std::string &format, const std::str
filename = boost::filesystem::change_extension(filename, default_ext);
return filename.string();
} catch (std::runtime_error &err) {
throw Slic3r::RuntimeError(L("Failed processing of the output_filename_format template.") + "\n" + err.what());
throw Slic3r::PlaceholderParserError(L("Failed processing of the output_filename_format template.") + "\n" + err.what());
}
}

View file

@ -98,7 +98,9 @@ void PrintConfigDef::init_common_params()
def = this->add("print_host", coString);
def->label = L("Hostname, IP or URL");
def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain "
"the hostname, IP address or URL of the printer host instance.");
"the hostname, IP address or URL of the printer host instance. "
"Print host behind HAProxy with basic auth enabled can be accessed by putting the user name and password into the URL "
"in the following format: https://username:password@your-octopi-address/");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString(""));
@ -159,8 +161,8 @@ void PrintConfigDef::init_common_params()
def->enum_keys_map = &ConfigOptionEnum<AuthorizationType>::get_enum_values();
def->enum_values.push_back("key");
def->enum_values.push_back("user");
def->enum_labels.push_back("API key");
def->enum_labels.push_back("HTTP digest");
def->enum_labels.push_back(L("API key"));
def->enum_labels.push_back(L("HTTP digest"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<AuthorizationType>(atKeyPassword));
}
@ -534,7 +536,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("The extruder to use (unless more specific extruder settings are specified). "
"This value overrides perimeter and infill extruders, but not the support extruders.");
def->min = 0; // 0 = inherit defaults
def->enum_labels.push_back("default"); // override label for item 0
def->enum_labels.push_back(L("default")); // override label for item 0
def->enum_labels.push_back("1");
def->enum_labels.push_back("2");
def->enum_labels.push_back("3");
@ -1207,9 +1209,9 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("top");
def->enum_values.push_back("topmost");
def->enum_values.push_back("solid");
def->enum_labels.push_back("All top surfaces");
def->enum_labels.push_back("Topmost surface only");
def->enum_labels.push_back("All solid surfaces");
def->enum_labels.push_back(L("All top surfaces"));
def->enum_labels.push_back(L("Topmost surface only"));
def->enum_labels.push_back(L("All solid surfaces"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<IroningType>(IroningType::TopSurfaces));

View file

@ -552,6 +552,7 @@ std::string encode_path(const char *src)
}
// Encode an 8-bit string from a local code page to UTF-8.
// Multibyte to utf8
std::string decode_path(const char *src)
{
#ifdef WIN32

View file

@ -804,6 +804,10 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
BoundingBoxf3 print_volume(Vec3d(unscale<double>(bed_box_2D.min(0)), unscale<double>(bed_box_2D.min(1)), 0.0), Vec3d(unscale<double>(bed_box_2D.max(0)), unscale<double>(bed_box_2D.max(1)), config->opt_float("max_print_height")));
// Allow the objects to protrude below the print bed
print_volume.min(2) = -1e10;
print_volume.min(0) -= BedEpsilon;
print_volume.min(1) -= BedEpsilon;
print_volume.max(0) += BedEpsilon;
print_volume.max(1) += BedEpsilon;
ModelInstanceEPrintVolumeState state = ModelInstancePVS_Inside;

View file

@ -611,6 +611,8 @@ struct _3DScene
static void point3_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume);
};
static constexpr float BedEpsilon = EPSILON;
}
#endif

View file

@ -68,9 +68,10 @@ bool SlicingProcessCompletedEvent::invalidate_plater() const
return false;
}
std::string SlicingProcessCompletedEvent::format_error_message() const
std::pair<std::string, bool> SlicingProcessCompletedEvent::format_error_message() const
{
std::string error;
bool monospace = false;
try {
this->rethrow_exception();
} catch (const std::bad_alloc& ex) {
@ -78,12 +79,15 @@ std::string SlicingProcessCompletedEvent::format_error_message() const
"If you are sure you have enough RAM on your system, this may also be a bug and we would "
"be glad if you reported it."))) % SLIC3R_APP_NAME).str());
error = std::string(errmsg.ToUTF8()) + "\n\n" + std::string(ex.what());
} catch (PlaceholderParserError &ex) {
error = ex.what();
monospace = true;
} catch (std::exception &ex) {
error = ex.what();
} catch (...) {
error = "Unknown C++ exception.";
}
return error;
return std::make_pair(std::move(error), monospace);
}
BackgroundSlicingProcess::BackgroundSlicingProcess()
@ -149,30 +153,37 @@ void BackgroundSlicingProcess::process_fff()
// Perform the final post-processing of the export path by applying the print statistics over the file name.
std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path);
std::string error_message;
int copy_ret_val = copy_file(m_temp_output_path, export_path, error_message, m_export_path_on_removable_media);
int copy_ret_val = CopyFileResult::SUCCESS;
try
{
copy_ret_val = copy_file(m_temp_output_path, export_path, error_message, m_export_path_on_removable_media);
}
catch (...)
{
throw Slic3r::ExportError(_utf8(L("Unknown error occured during exporting G-code.")));
}
switch (copy_ret_val) {
case SUCCESS: break; // no error
case FAIL_COPY_FILE:
case CopyFileResult::SUCCESS: break; // no error
case CopyFileResult::FAIL_COPY_FILE:
throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?\nError message: %1%"))) % error_message).str());
break;
case FAIL_FILES_DIFFERENT:
case CopyFileResult::FAIL_FILES_DIFFERENT:
throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. There might be problem with target device, please try exporting again or using different device. The corrupted output G-code is at %1%.tmp."))) % export_path).str());
break;
case FAIL_RENAMING:
case CopyFileResult::FAIL_RENAMING:
throw Slic3r::ExportError((boost::format(_utf8(L("Renaming of the G-code after copying to the selected destination folder has failed. Current path is %1%.tmp. Please try exporting again."))) % export_path).str());
break;
case FAIL_CHECK_ORIGIN_NOT_OPENED:
case CopyFileResult::FAIL_CHECK_ORIGIN_NOT_OPENED:
throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the original code at %1% couldn't be opened during copy check. The output G-code is at %2%.tmp."))) % m_temp_output_path % export_path).str());
break;
case FAIL_CHECK_TARGET_NOT_OPENED:
case CopyFileResult::FAIL_CHECK_TARGET_NOT_OPENED:
throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the exported code couldn't be opened during copy check. The output G-code is at %1%.tmp."))) % export_path).str());
break;
default:
throw Slic3r::RuntimeError(_utf8(L("Unknown error occured during exporting G-code.")));
throw Slic3r::ExportError(_utf8(L("Unknown error occured during exporting G-code.")));
BOOST_LOG_TRIVIAL(error) << "Unexpected fail code(" << (int)copy_ret_val << ") durring copy_file() to " << export_path << ".";
break;
}
m_print->set_status(95, _utf8(L("Running post-processing scripts")));
run_post_process_scripts(export_path, m_fff_print->config());
m_print->set_status(100, (boost::format(_utf8(L("G-code file exported to %1%"))) % export_path).str());

View file

@ -57,7 +57,8 @@ public:
// Only valid if error()
void rethrow_exception() const { assert(this->error()); assert(m_exception); std::rethrow_exception(m_exception); }
// Produce a human readable message to be displayed by a notification or a message box.
std::string format_error_message() const;
// 2nd parameter defines whether the output should be displayed with a monospace font.
std::pair<std::string, bool> format_error_message() const;
private:
StatusType m_status;

View file

@ -1095,23 +1095,48 @@ wxDEFINE_EVENT(EVT_GLCANVAS_RELOAD_FROM_DISK, SimpleEvent);
const double GLCanvas3D::DefaultCameraZoomToBoxMarginFactor = 1.25;
static GLCanvas3D::ArrangeSettings load_arrange_settings()
void GLCanvas3D::load_arrange_settings()
{
GLCanvas3D::ArrangeSettings settings;
std::string dist_fff_str =
wxGetApp().app_config->get("arrange", "min_object_distance_fff");
std::string dist_str =
wxGetApp().app_config->get("arrange", "min_object_distance");
std::string dist_fff_seq_print_str =
wxGetApp().app_config->get("arrange", "min_object_distance_fff_seq_print");
std::string en_rot_str =
wxGetApp().app_config->get("arrange", "enable_rotation");
std::string dist_sla_str =
wxGetApp().app_config->get("arrange", "min_object_distance_sla");
if (!dist_str.empty())
settings.distance = std::stof(dist_str);
std::string en_rot_fff_str =
wxGetApp().app_config->get("arrange", "enable_rotation_fff");
if (!en_rot_str.empty())
settings.enable_rotation = (en_rot_str == "1" || en_rot_str == "yes");
std::string en_rot_fff_seqp_str =
wxGetApp().app_config->get("arrange", "enable_rotation_fff_seq_print");
return settings;
std::string en_rot_sla_str =
wxGetApp().app_config->get("arrange", "enable_rotation_sla");
if (!dist_fff_str.empty())
m_arrange_settings_fff.distance = std::stof(dist_fff_str);
if (!dist_fff_seq_print_str.empty())
m_arrange_settings_fff_seq_print.distance = std::stof(dist_fff_seq_print_str);
if (!dist_sla_str.empty())
m_arrange_settings_sla.distance = std::stof(dist_sla_str);
if (!en_rot_fff_str.empty())
m_arrange_settings_fff.enable_rotation = (en_rot_fff_str == "1" || en_rot_fff_str == "yes");
if (!en_rot_fff_seqp_str.empty())
m_arrange_settings_fff_seq_print.enable_rotation = (en_rot_fff_seqp_str == "1" || en_rot_fff_seqp_str == "yes");
if (!en_rot_sla_str.empty())
m_arrange_settings_sla.enable_rotation = (en_rot_sla_str == "1" || en_rot_sla_str == "yes");
}
PrinterTechnology GLCanvas3D::current_printer_technology() const
{
return m_process->current_printer_technology();
}
GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas)
@ -1156,7 +1181,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas)
#endif // ENABLE_RETINA_GL
}
m_arrange_settings = load_arrange_settings();
load_arrange_settings();
m_selection.set_volumes(&m_volumes.volumes);
}
@ -2554,7 +2579,13 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
post_event(SimpleEvent(EVT_GLTOOLBAR_DELETE));
break;
case WXK_ESCAPE: { deselect_all(); break; }
case WXK_F5: { post_event(SimpleEvent(EVT_GLCANVAS_RELOAD_FROM_DISK)); break; }
case WXK_F5:
{
if ((wxGetApp().is_editor() && !wxGetApp().plater()->model().objects.empty()) ||
(wxGetApp().is_gcode_viewer() && !wxGetApp().plater()->get_last_loaded_gcode().empty()))
post_event(SimpleEvent(EVT_GLCANVAS_RELOAD_FROM_DISK));
break;
}
case '0': { select_view("iso"); break; }
case '1': { select_view("top"); break; }
case '2': { select_view("bottom"); break; }
@ -3921,37 +3952,60 @@ bool GLCanvas3D::_render_arrange_menu(float pos_x)
imgui->set_next_window_pos(x, m_main_toolbar.get_height(), ImGuiCond_Always, 0.5f, 0.0f);
imgui->begin(_L("Arrange options"), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
ArrangeSettings settings = m_arrange_settings;
ArrangeSettings settings = get_arrange_settings();
ArrangeSettings &settings_out = get_arrange_settings();
auto &appcfg = wxGetApp().app_config;
PrinterTechnology ptech = m_process->current_printer_technology();
bool settings_changed = false;
float dist_min = 0.f;
std::string dist_key = "min_object_distance", rot_key = "enable_rotation";
std::string postfix;
if (ImGui::DragFloat(_L("Gal size").ToUTF8().data(), &settings.distance, .01f, 0.0f, 100.0f, "%5.2f")) {
m_arrange_settings.distance = settings.distance;
if (ptech == ptSLA) {
dist_min = 0.f;
postfix = "_sla";
} else if (ptech == ptFFF) {
auto co_opt = m_config->option<ConfigOptionBool>("complete_objects");
if (co_opt && co_opt->value) {
dist_min = float(min_object_distance(*m_config));
postfix = "_fff_seq_print";
} else {
dist_min = 0.f;
postfix = "_fff";
}
}
dist_key += postfix;
rot_key += postfix;
imgui->text(_L("Use CTRL+left mouse key to enter text edit mode:"));
if (imgui->slider_float(_L("Clearance size"), &settings.distance, dist_min, 100.0f, "%5.2f") || dist_min > settings.distance) {
settings.distance = std::max(dist_min, settings.distance);
settings_out.distance = settings.distance;
appcfg->set("arrange", dist_key.c_str(), std::to_string(settings_out.distance));
settings_changed = true;
}
if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", _L("Use CTRL+Left mouse button to enter text edit mode.\nUse SHIFT key to increase stepping.").ToUTF8().data());
if (imgui->checkbox(_L("Enable rotations (slow)"), settings.enable_rotation)) {
m_arrange_settings.enable_rotation = settings.enable_rotation;
settings_out.enable_rotation = settings.enable_rotation;
appcfg->set("arrange", rot_key.c_str(), settings_out.enable_rotation? "1" : "0");
settings_changed = true;
}
ImGui::Separator();
if (imgui->button(_L("Reset"))) {
m_arrange_settings = ArrangeSettings{};
settings_out = ArrangeSettings{};
settings_out.distance = std::max(dist_min, settings_out.distance);
appcfg->set("arrange", dist_key.c_str(), std::to_string(settings_out.distance));
appcfg->set("arrange", rot_key.c_str(), settings_out.enable_rotation? "1" : "0");
settings_changed = true;
}
if (settings_changed) {
appcfg->set("arrange", "min_object_distance", std::to_string(m_arrange_settings.distance));
appcfg->set("arrange", "enable_rotation", m_arrange_settings.enable_rotation? "1" : "0");
}
ImGui::SameLine();
if (imgui->button(_L("Arrange"))) {
@ -4943,7 +4997,7 @@ void GLCanvas3D::_render_objects() const
if (m_config != nullptr) {
const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false);
m_volumes.set_print_box((float)bed_bb.min(0), (float)bed_bb.min(1), 0.0f, (float)bed_bb.max(0), (float)bed_bb.max(1), (float)m_config->opt_float("max_print_height"));
m_volumes.set_print_box((float)bed_bb.min(0) - BedEpsilon, (float)bed_bb.min(1) - BedEpsilon, 0.0f, (float)bed_bb.max(0) + BedEpsilon, (float)bed_bb.max(1) + BedEpsilon, (float)m_config->opt_float("max_print_height"));
m_volumes.check_outside_state(m_config, nullptr);
}
}

View file

@ -387,9 +387,11 @@ public:
struct ArrangeSettings
{
float distance = 6.;
float accuracy = 0.65f;
bool enable_rotation = false;
float distance = 6.;
// float distance_seq_print = 6.; // Used when sequential print is ON
// float distance_sla = 6.;
float accuracy = 0.65f; // Unused currently
bool enable_rotation = false;
};
private:
@ -463,7 +465,35 @@ private:
mutable bool m_tooltip_enabled{ true };
Slope m_slope;
ArrangeSettings m_arrange_settings;
ArrangeSettings m_arrange_settings_fff, m_arrange_settings_sla,
m_arrange_settings_fff_seq_print;
PrinterTechnology current_printer_technology() const;
template<class Self>
static auto & get_arrange_settings(Self *self)
{
PrinterTechnology ptech = self->current_printer_technology();
auto *ptr = &self->m_arrange_settings_fff;
if (ptech == ptSLA) {
ptr = &self->m_arrange_settings_sla;
} else if (ptech == ptFFF) {
auto co_opt = self->m_config->template option<ConfigOptionBool>("complete_objects");
if (co_opt && co_opt->value) {
ptr = &self->m_arrange_settings_fff_seq_print;
} else {
ptr = &self->m_arrange_settings_fff;
}
}
return *ptr;
}
ArrangeSettings &get_arrange_settings() { return get_arrange_settings(this); }
void load_arrange_settings();
public:
explicit GLCanvas3D(wxGLCanvas* canvas);
@ -684,7 +714,17 @@ public:
void use_slope(bool use) { m_slope.use(use); }
void set_slope_normal_angle(float angle_in_deg) { m_slope.set_normal_angle(angle_in_deg); }
const ArrangeSettings& get_arrange_settings() const { return m_arrange_settings; }
ArrangeSettings get_arrange_settings() const
{
const ArrangeSettings &settings = get_arrange_settings(this);
ArrangeSettings ret = settings;
if (&settings == &m_arrange_settings_fff_seq_print) {
ret.distance = std::max(ret.distance,
float(min_object_distance(*m_config)));
}
return ret;
}
private:
bool _is_shown_on_screen() const;

View file

@ -221,16 +221,16 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
}
}
void show_error(wxWindow* parent, const wxString& message)
void show_error(wxWindow* parent, const wxString& message, bool monospaced_font)
{
ErrorDialog msg(parent, message);
ErrorDialog msg(parent, message, monospaced_font);
msg.ShowModal();
}
void show_error(wxWindow* parent, const char* message)
void show_error(wxWindow* parent, const char* message, bool monospaced_font)
{
assert(message);
show_error(parent, wxString::FromUTF8(message));
show_error(parent, wxString::FromUTF8(message), monospaced_font);
}
void show_error_id(int id, const std::string& message)

View file

@ -39,9 +39,11 @@ extern void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_
// Change option value in config
void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index = 0);
void show_error(wxWindow* parent, const wxString& message);
void show_error(wxWindow* parent, const char* message);
inline void show_error(wxWindow* parent, const std::string& message) { show_error(parent, message.c_str()); }
// If monospaced_font is true, the error message is displayed using html <code><pre></pre></code> tags,
// so that the code formatting will be preserved. This is useful for reporting errors from the placeholder parser.
void show_error(wxWindow* parent, const wxString& message, bool monospaced_font = false);
void show_error(wxWindow* parent, const char* message, bool monospaced_font = false);
inline void show_error(wxWindow* parent, const std::string& message, bool monospaced_font = false) { show_error(parent, message.c_str(), monospaced_font); }
void show_error_id(int id, const std::string& message); // For Perl
void show_info(wxWindow* parent, const wxString& message, const wxString& title = wxString());
void show_info(wxWindow* parent, const char* message, const char* title = nullptr);

View file

@ -1710,11 +1710,12 @@ bool GUI_App::checked_tab(Tab* tab)
}
// Update UI / Tabs to reflect changes in the currently loaded presets
void GUI_App::load_current_presets()
void GUI_App::load_current_presets(bool check_printer_presets_ /*= true*/)
{
// check printer_presets for the containing information about "Print Host upload"
// and create physical printer from it, if any exists
check_printer_presets();
if (check_printer_presets_)
check_printer_presets();
PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology();
this->plater()->set_printer_technology(printer_technology);
@ -1872,6 +1873,7 @@ wxString GUI_App::current_language_code_safe() const
{ "pl", "pl_PL", },
{ "uk", "uk_UA", },
{ "zh", "zh_CN", },
{ "ru", "ru_RU", },
};
wxString language_code = this->current_language_code().BeforeFirst('_');
auto it = mapping.find(language_code);

View file

@ -206,7 +206,7 @@ public:
void add_config_menu(wxMenuBar *menu);
bool check_unsaved_changes(const wxString &header = wxString());
bool checked_tab(Tab* tab);
void load_current_presets();
void load_current_presets(bool check_printer_presets = true);
wxString current_language_code() const { return m_wxLocale->GetCanonicalName(); }
// Translate the language code to a code, for which Prusa Research maintains translations. Defaults to "en_US".

View file

@ -1007,7 +1007,7 @@ ManipulationEditor::ManipulationEditor(ObjectManipulation* parent,
const std::string& opt_key,
int axis) :
wxTextCtrl(parent->parent(), wxID_ANY, wxEmptyString, wxDefaultPosition,
wxSize(5*int(wxGetApp().em_unit()), wxDefaultCoord), wxTE_PROCESS_ENTER),
wxSize((wxOSX ? 5 : 6)*int(wxGetApp().em_unit()), wxDefaultCoord), wxTE_PROCESS_ENTER),
m_opt_key(opt_key),
m_axis(axis)
{

View file

@ -169,27 +169,11 @@ void View3D::render()
Preview::Preview(
wxWindow* parent, Model* model, DynamicPrintConfig* config,
BackgroundSlicingProcess* process, GCodeProcessor::Result* gcode_result, std::function<void()> schedule_background_process_func)
: m_canvas_widget(nullptr)
, m_canvas(nullptr)
, m_left_sizer(nullptr)
, m_layers_slider_sizer(nullptr)
, m_bottom_toolbar_panel(nullptr)
, m_label_view_type(nullptr)
, m_choice_view_type(nullptr)
, m_label_show(nullptr)
, m_combochecklist_features(nullptr)
, m_combochecklist_features_pos(0)
, m_combochecklist_options(nullptr)
, m_config(config)
: m_config(config)
, m_process(process)
, m_gcode_result(gcode_result)
, m_number_extruders(1)
, m_preferred_color_mode("feature")
, m_loaded(false)
, m_schedule_background_process(schedule_background_process_func)
#ifdef __linux__
, m_volumes_cleanup_required(false)
#endif // __linux__
{
if (init(parent, model))
load_print();

View file

@ -76,17 +76,17 @@ private:
class Preview : public wxPanel
{
wxGLCanvas* m_canvas_widget;
GLCanvas3D* m_canvas;
wxBoxSizer* m_left_sizer;
wxBoxSizer* m_layers_slider_sizer;
wxPanel* m_bottom_toolbar_panel;
wxStaticText* m_label_view_type;
wxChoice* m_choice_view_type;
wxStaticText* m_label_show;
wxComboCtrl* m_combochecklist_features;
size_t m_combochecklist_features_pos;
wxComboCtrl* m_combochecklist_options;
wxGLCanvas* m_canvas_widget { nullptr };
GLCanvas3D* m_canvas { nullptr };
wxBoxSizer* m_left_sizer { nullptr };
wxBoxSizer* m_layers_slider_sizer { nullptr };
wxPanel* m_bottom_toolbar_panel { nullptr };
wxStaticText* m_label_view_type { nullptr };
wxChoice* m_choice_view_type { nullptr };
wxStaticText* m_label_show { nullptr };
wxComboCtrl* m_combochecklist_features { nullptr };
size_t m_combochecklist_features_pos { 0 };
wxComboCtrl* m_combochecklist_options { nullptr };
DynamicPrintConfig* m_config;
BackgroundSlicingProcess* m_process;
@ -95,16 +95,16 @@ class Preview : public wxPanel
#ifdef __linux__
// We are getting mysterious crashes on Linux in gtk due to OpenGL context activation GH #1874 #1955.
// So we are applying a workaround here.
bool m_volumes_cleanup_required;
bool m_volumes_cleanup_required { false };
#endif /* __linux__ */
// Calling this function object forces Plater::schedule_background_process.
std::function<void()> m_schedule_background_process;
unsigned int m_number_extruders;
unsigned int m_number_extruders { 1 };
std::string m_preferred_color_mode;
bool m_loaded;
bool m_loaded { false };
DoubleSlider::Control* m_layers_slider{ nullptr };
DoubleSlider::Control* m_moves_slider{ nullptr };

View file

@ -624,7 +624,7 @@ static bool selectable(const char* label, bool selected, ImGuiSelectableFlags fl
}
// mark a label with a ImGui::ColorMarkerHovered, if item is hovered
char* marked_label = new char[255];
char* marked_label = new char[512]; //255 symbols is not enough for translated string (e.t. to Russian)
if (hovered)
sprintf(marked_label, "%c%s", ImGui::ColorMarkerHovered, label);
else

View file

@ -145,12 +145,13 @@ void ArrangeJob::process()
{
static const auto arrangestr = _(L("Arranging"));
GLCanvas3D::ArrangeSettings settings =
m_plater->canvas3D()->get_arrange_settings();
const GLCanvas3D::ArrangeSettings &settings =
static_cast<const GLCanvas3D*>(m_plater->canvas3D())->get_arrange_settings();
arrangement::ArrangeParams params;
params.min_obj_distance = scaled(settings.distance);
params.allow_rotations = settings.enable_rotation;
params.min_obj_distance = scaled(settings.distance);
auto count = unsigned(m_selected.size() + m_unprintable.size());
Points bedpts = get_bed_shape(*m_plater->config());

View file

@ -29,7 +29,9 @@ void FillBedJob::prepare()
for (ModelInstance *inst : model_object->instances)
if (inst->printable) {
ArrangePolygon ap = get_arrange_poly(PtrWrapper{inst}, m_plater);
++ap.priority; // need to be included in the result
// Existing objects need to be included in the result. Only
// the needed amount of object will be added, no more.
++ap.priority;
m_selected.emplace_back(ap);
}
@ -38,11 +40,18 @@ void FillBedJob::prepare()
m_bedpts = get_bed_shape(*m_plater->config());
auto &objects = m_plater->model().objects;
BoundingBox bedbb = get_extents(m_bedpts);
for (size_t idx = 0; idx < objects.size(); ++idx)
if (int(idx) != m_object_idx)
for (ModelInstance *mi : objects[idx]->instances) {
m_unselected.emplace_back(get_arrange_poly(PtrWrapper{mi}, m_plater));
m_unselected.back().bed_idx = 0;
ArrangePolygon ap = get_arrange_poly(PtrWrapper{mi}, m_plater);
auto ap_bb = ap.transformed_poly().contour.bounding_box();
if (ap.bed_idx == 0 && !bedbb.contains(ap_bb))
ap.bed_idx = arrangement::UNARRANGED;
m_unselected.emplace_back(ap);
}
if (auto wt = get_wipe_tower_arrangepoly(*m_plater))
@ -55,18 +64,17 @@ void FillBedJob::prepare()
double unsel_area = std::accumulate(m_unselected.begin(),
m_unselected.end(), 0.,
[](double s, const auto &ap) {
return s + ap.poly.area();
return s + (ap.bed_idx == 0) * ap.poly.area();
}) / sc;
double fixed_area = unsel_area + m_selected.size() * poly_area;
double bed_area = Polygon{m_bedpts}.area() / sc;
// This is the maximum range, the real number will always be close but less.
double bed_area = Polygon{m_bedpts}.area() / sc;
m_status_range = (bed_area - fixed_area) / poly_area;
// This is the maximum number of items, the real number will always be close but less.
int needed_items = (bed_area - fixed_area) / poly_area;
ModelInstance *mi = model_object->instances[0];
for (int i = 0; i < m_status_range; ++i) {
for (int i = 0; i < needed_items; ++i) {
ArrangePolygon ap;
ap.poly = m_selected.front().poly;
ap.bed_idx = arrangement::UNARRANGED;
@ -77,18 +85,28 @@ void FillBedJob::prepare()
};
m_selected.emplace_back(ap);
}
m_status_range = m_selected.size();
// The strides have to be removed from the fixed items. For the
// arrangeable (selected) items bed_idx is ignored and the
// translation is irrelevant.
double stride = bed_stride(m_plater);
for (auto &p : m_unselected)
if (p.bed_idx > 0)
p.translation(X) -= p.bed_idx * stride;
}
void FillBedJob::process()
{
if (m_object_idx == -1 || m_selected.empty()) return;
GLCanvas3D::ArrangeSettings settings =
m_plater->canvas3D()->get_arrange_settings();
const GLCanvas3D::ArrangeSettings &settings =
static_cast<const GLCanvas3D*>(m_plater->canvas3D())->get_arrange_settings();
arrangement::ArrangeParams params;
params.min_obj_distance = scaled(settings.distance);
params.allow_rotations = settings.enable_rotation;
params.min_obj_distance = scaled(settings.distance);
bool do_stop = false;
params.stopcondition = [this, &do_stop]() {

View file

@ -34,6 +34,8 @@
#include "format.hpp"
#include <fstream>
#include <string_view>
#include "GUI_App.hpp"
#ifdef _WIN32
@ -84,6 +86,31 @@ public:
};
#endif // __APPLE__
// Load the icon either from the exe, or from the ico file.
static wxIcon main_frame_icon(GUI_App::EAppMode app_mode)
{
#if _WIN32
std::wstring path(size_t(MAX_PATH), wchar_t(0));
int len = int(::GetModuleFileName(nullptr, path.data(), MAX_PATH));
if (len > 0 && len < MAX_PATH) {
path.erase(path.begin() + len, path.end());
if (app_mode == GUI_App::EAppMode::GCodeViewer) {
// Only in case the slicer was started with --gcodeviewer parameter try to load the icon from prusa-gcodeviewer.exe
// Otherwise load it from the exe.
for (const std::wstring_view exe_name : { std::wstring_view(L"prusa-slicer.exe"), std::wstring_view(L"prusa-slicer-console.exe") })
if (boost::iends_with(path, exe_name)) {
path.erase(path.end() - exe_name.size(), path.end());
path += L"prusa-gcodeviewer.exe";
break;
}
}
}
return wxIcon(path, wxBITMAP_TYPE_ICO);
#else // _WIN32
return wxIcon(Slic3r::var(app_mode == GUI_App::EAppMode::Editor ? "PrusaSlicer_128px.png" : "PrusaSlicer-gcodeviewer_128px.png"), wxBITMAP_TYPE_PNG);
#endif // _WIN32
}
MainFrame::MainFrame() :
DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "mainframe"),
m_printhost_queue_dlg(new PrintHostQueueDialog(this))
@ -115,35 +142,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
#endif // __APPLE__
// Load the icon either from the exe, or from the ico file.
#if _WIN32
{
wxString src_path;
wxFileName::SplitPath(wxStandardPaths::Get().GetExecutablePath(), &src_path, nullptr, nullptr, wxPATH_NATIVE);
switch (wxGetApp().get_app_mode()) {
default:
case GUI_App::EAppMode::Editor: { src_path += "\\prusa-slicer.exe"; break; }
case GUI_App::EAppMode::GCodeViewer: { src_path += "\\prusa-gcodeviewer.exe"; break; }
}
wxIconLocation icon_location;
icon_location.SetFileName(src_path);
SetIcon(icon_location);
}
#else
switch (wxGetApp().get_app_mode())
{
default:
case GUI_App::EAppMode::Editor:
{
SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG));
break;
}
case GUI_App::EAppMode::GCodeViewer:
{
SetIcon(wxIcon(Slic3r::var("PrusaSlicer-gcodeviewer_128px.png"), wxBITMAP_TYPE_PNG));
break;
}
}
#endif // _WIN32
SetIcon(main_frame_icon(wxGetApp().get_app_mode()));
// initialize status bar
m_statusbar = std::make_shared<ProgressStatusBar>(this);
@ -1243,6 +1242,9 @@ void MainFrame::init_menubar_as_gcodeviewer()
append_menu_item(fileMenu, wxID_ANY, _L("&Open G-code") + dots + "\tCtrl+O", _L("Open a G-code file"),
[this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->load_gcode(); }, "open", nullptr,
[this]() {return m_plater != nullptr; }, this);
append_menu_item(fileMenu, wxID_ANY, _L("Re&load from disk") + sep + "F5",
_L("Reload the plater from disk"), [this](wxCommandEvent&) { m_plater->reload_gcode_from_disk(); },
"", nullptr, [this]() { return !m_plater->get_last_loaded_gcode().empty(); }, this);
fileMenu->AppendSeparator();
append_menu_item(fileMenu, wxID_ANY, _L("Export &toolpaths as OBJ") + dots, _L("Export toolpaths as OBJ"),
[this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->export_toolpaths_to_obj(); }, "export_plater", nullptr,

View file

@ -64,12 +64,9 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he
SetSizerAndFit(topsizer);
}
MsgDialog::~MsgDialog() {}
// ErrorDialog
ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg)
ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg, bool monospaced_font)
: MsgDialog(parent, wxString::Format(_(L("%s error")), SLIC3R_APP_NAME),
wxString::Format(_(L("%s has encountered an error")), SLIC3R_APP_NAME),
wxID_NONE)
@ -78,19 +75,23 @@ ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg)
// Text shown as HTML, so that mouse selection and Ctrl-V to copy will work.
wxHtmlWindow* html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO);
{
html->SetMinSize(wxSize(40 * wxGetApp().em_unit(), -1));
html->SetMinSize(wxSize(40 * wxGetApp().em_unit(), monospaced_font ? 30 * wxGetApp().em_unit() : -1));
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
wxFont monospace = wxSystemSettings::GetFont(wxSYS_ANSI_FIXED_FONT);
wxColour text_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue());
auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue());
const int font_size = font.GetPointSize()-1;
int size[] = {font_size, font_size, font_size, font_size, font_size, font_size, font_size};
html->SetFonts(font.GetFaceName(), font.GetFaceName(), size);
html->SetFonts(font.GetFaceName(), monospace.GetFaceName(), size);
html->SetBorders(2);
std::string msg_escaped = xml_escape(msg.ToUTF8().data());
boost::replace_all(msg_escaped, "\r\n", "<br>");
boost::replace_all(msg_escaped, "\n", "<br>");
if (monospaced_font)
// Code formatting will be preserved. This is useful for reporting errors from the placeholder parser.
msg_escaped = std::string("<pre><code>") + msg_escaped + "</code></pre>";
html->SetPage("<html><body bgcolor=\"" + bgr_clr_str + "\"><font color=\"" + text_clr_str + "\">" + wxString::FromUTF8(msg_escaped.data()) + "</font></body></html>");
content_sizer->Add(html, 1, wxEXPAND);
}
@ -99,15 +100,12 @@ ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg)
btn_ok->SetFocus();
btn_sizer->Add(btn_ok, 0, wxRIGHT, HORIZ_SPACING);
logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, 192));
// Use a small bitmap with monospaced font, as the error text will not be wrapped.
logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, monospaced_font ? 48 : 192));
SetMaxSize(wxSize(-1, CONTENT_MAX_HEIGHT*wxGetApp().em_unit()));
Fit();
}
ErrorDialog::~ErrorDialog() {}
}
}

View file

@ -25,7 +25,7 @@ struct MsgDialog : wxDialog
MsgDialog(const MsgDialog &) = delete;
MsgDialog &operator=(MsgDialog &&) = delete;
MsgDialog &operator=(const MsgDialog &) = delete;
virtual ~MsgDialog();
virtual ~MsgDialog() = default;
// TODO: refactor with CreateStdDialogButtonSizer usage
@ -52,12 +52,14 @@ protected:
class ErrorDialog : public MsgDialog
{
public:
ErrorDialog(wxWindow *parent, const wxString &msg);
// If monospaced_font is true, the error message is displayed using html <code><pre></pre></code> tags,
// so that the code formatting will be preserved. This is useful for reporting errors from the placeholder parser.
ErrorDialog(wxWindow *parent, const wxString &msg, bool courier_font);
ErrorDialog(ErrorDialog &&) = delete;
ErrorDialog(const ErrorDialog &) = delete;
ErrorDialog &operator=(ErrorDialog &&) = delete;
ErrorDialog &operator=(const ErrorDialog &) = delete;
virtual ~ErrorDialog();
virtual ~ErrorDialog() = default;
private:
wxString msg;

View file

@ -387,7 +387,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) :
option = m_og->get_option("fill_density");
option.opt.label = L("Infill");
option.opt.width = 7/*6*/;
option.opt.width = 8;
option.opt.sidetext = " ";
line.append_option(option);
@ -2002,9 +2002,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
sidebar->Bind(EVT_SCHEDULE_BACKGROUND_PROCESS, [this](SimpleEvent&) { this->schedule_background_process(); });
}
wxGLCanvas* view3D_canvas = view3D->get_wxglcanvas();
wxGLCanvas* view3D_canvas = view3D->get_wxglcanvas();
if (wxGetApp().is_editor()) {
if (wxGetApp().is_editor()) {
// 3DScene events:
view3D_canvas->Bind(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, [this](SimpleEvent&) { this->schedule_background_process(); });
view3D_canvas->Bind(EVT_GLCANVAS_OBJECT_SELECT, &priv::on_object_select, this);
@ -2046,8 +2046,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
view3D_canvas->Bind(EVT_GLTOOLBAR_SPLIT_OBJECTS, &priv::on_action_split_objects, this);
view3D_canvas->Bind(EVT_GLTOOLBAR_SPLIT_VOLUMES, &priv::on_action_split_volumes, this);
view3D_canvas->Bind(EVT_GLTOOLBAR_LAYERSEDITING, &priv::on_action_layersediting, this);
}
view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [q](SimpleEvent&) { q->set_bed_shape(); });
}
view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [q](SimpleEvent&) { q->set_bed_shape(); });
// Preview events:
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); });
@ -2064,6 +2064,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_MOVE_LAYERS_SLIDER, [this](wxKeyEvent& evt) { preview->move_layers_slider(evt); });
#endif // ENABLE_ARROW_KEYS_WITH_SLIDERS
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_EDIT_COLOR_CHANGE, [this](wxKeyEvent& evt) { preview->edit_layers_slider(evt); });
if (wxGetApp().is_gcode_viewer())
preview->Bind(EVT_GLCANVAS_RELOAD_FROM_DISK, [this](SimpleEvent&) { this->q->reload_gcode_from_disk(); });
if (wxGetApp().is_editor()) {
q->Bind(EVT_SLICING_COMPLETED, &priv::on_slicing_completed, this);
@ -2363,7 +2365,8 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
wxGetApp().preset_bundle->load_config_model(filename.string(), std::move(config));
if (printer_technology == ptFFF)
CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, &wxGetApp().preset_bundle->project_config);
wxGetApp().load_current_presets();
// For exporting from the amf/3mf we shouldn't check printer_presets for the containing information about "Print Host upload"
wxGetApp().load_current_presets(false);
is_project_file = true;
}
wxGetApp().app_config->update_config_dir(path.parent_path().string());
@ -3640,19 +3643,20 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
// Reset the "export G-code path" name, so that the automatic background processing will be enabled again.
this->background_process.reset_export();
// This bool stops showing export finished notification even when process_completed_with_error is false
bool has_error = false;
if (evt.error()) {
std::string message = evt.format_error_message();
std::pair<std::string, bool> message = evt.format_error_message();
if (evt.critical_error()) {
if (q->m_tracking_popup_menu)
// We don't want to pop-up a message box when tracking a pop-up menu.
// We postpone the error message instead.
q->m_tracking_popup_menu_error_message = message;
q->m_tracking_popup_menu_error_message = message.first;
else
show_error(q, message);
show_error(q, message.first, message.second);
} else
notification_manager->push_slicing_error_notification(message);
this->statusbar()->set_status_text(from_u8(message));
notification_manager->push_slicing_error_notification(message.first);
this->statusbar()->set_status_text(from_u8(message.first));
if (evt.invalidate_plater())
{
const wxString invalid_str = _L("Invalid data");
@ -3660,7 +3664,7 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
sidebar->set_btn_label(btn, invalid_str);
process_completed_with_error = true;
}
has_error = true;
}
if (evt.cancelled())
this->statusbar()->set_status_text(_L("Cancelled"));
@ -3695,11 +3699,11 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
show_action_buttons(false);
}
// If writing to removable drive was scheduled, show notification with eject button
if (exporting_status == ExportingStatus::EXPORTING_TO_REMOVABLE && !this->process_completed_with_error) {
if (exporting_status == ExportingStatus::EXPORTING_TO_REMOVABLE && !has_error) {
show_action_buttons(false);
notification_manager->push_exporting_finished_notification(last_output_path, last_output_dir_path, true);
wxGetApp().removable_drive_manager()->set_exporting_finished(true);
}else if (exporting_status == ExportingStatus::EXPORTING_TO_LOCAL && !this->process_completed_with_error)
}else if (exporting_status == ExportingStatus::EXPORTING_TO_LOCAL && !has_error)
notification_manager->push_exporting_finished_notification(last_output_path, last_output_dir_path, false);
}
exporting_status = ExportingStatus::NOT_EXPORTING;
@ -4805,6 +4809,13 @@ void Plater::load_gcode(const wxString& filename)
set_project_filename(filename);
}
void Plater::reload_gcode_from_disk()
{
wxString filename(m_last_loaded_gcode);
m_last_loaded_gcode.clear();
load_gcode(filename);
}
void Plater::refresh_print()
{
p->preview->refresh_print();
@ -5215,9 +5226,12 @@ void Plater::export_gcode(bool prefer_removable)
if (state & priv::UPDATE_BACKGROUND_PROCESS_INVALID)
return;
default_output_file = this->p->background_process.output_filepath_for_project(into_path(get_project_filename(".3mf")));
}
catch (const std::exception &ex) {
show_error(this, ex.what());
} catch (const Slic3r::PlaceholderParserError &ex) {
// Show the error with monospaced font.
show_error(this, ex.what(), true);
return;
} catch (const std::exception &ex) {
show_error(this, ex.what(), false);
return;
}
default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string()));
@ -5575,9 +5589,12 @@ void Plater::send_gcode()
if (state & priv::UPDATE_BACKGROUND_PROCESS_INVALID)
return;
default_output_file = this->p->background_process.output_filepath_for_project(into_path(get_project_filename(".3mf")));
}
catch (const std::exception &ex) {
show_error(this, ex.what());
} catch (const Slic3r::PlaceholderParserError& ex) {
// Show the error with monospaced font.
show_error(this, ex.what(), true);
return;
} catch (const std::exception& ex) {
show_error(this, ex.what(), false);
return;
}
default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string()));

View file

@ -144,6 +144,7 @@ public:
void extract_config_from_project();
void load_gcode();
void load_gcode(const wxString& filename);
void reload_gcode_from_disk();
void refresh_print();
std::vector<size_t> load_files(const std::vector<boost::filesystem::path>& input_files, bool load_model = true, bool load_config = true, bool imperial_units = false);
@ -154,6 +155,8 @@ public:
bool load_files(const wxArrayString& filenames);
#endif // ENABLE_DRAG_AND_DROP_FIX
const wxString& get_last_loaded_gcode() const { return m_last_loaded_gcode; }
void update();
void stop_jobs();
void select_view(const std::string& direction);

View file

@ -20,6 +20,7 @@
#include <glob.h>
#include <pwd.h>
#include <boost/filesystem.hpp>
#include <boost/system/error_code.hpp>
#include <boost/filesystem/convenience.hpp>
#include <boost/process.hpp>
#endif
@ -187,8 +188,9 @@ namespace search_for_drives_internal
//if not same file system - could be removable drive
if (! compare_filesystem_id(path, parent_path)) {
//free space
boost::filesystem::space_info si = boost::filesystem::space(path);
if (si.available != 0) {
boost::system::error_code ec;
boost::filesystem::space_info si = boost::filesystem::space(path, ec);
if (!ec && si.available != 0) {
//user id
struct stat buf;
stat(path.c_str(), &buf);

View file

@ -133,7 +133,7 @@ void SavePresetDialog::Item::update()
if (existing->is_compatible)
info_line = from_u8((boost::format(_u8L("Preset with name \"%1%\" already exists.")) % m_preset_name).str());
else
info_line = from_u8((boost::format(_u8L("Preset with name \"%1%\" already exists and is imcopatible with selected printer.")) % m_preset_name).str());
info_line = from_u8((boost::format(_u8L("Preset with name \"%1%\" already exists and is incopatible with selected printer.")) % m_preset_name).str());
info_line += "\n" + _L("Note: This preset will be replaced after saving");
m_valid_type = Warning;
}

View file

@ -83,7 +83,7 @@ void OptionsSearcher::append_options(DynamicPrintConfig* config, Preset::Type ty
options.emplace_back(Option{ boost::nowide::widen(opt_key), type,
(label + suffix).ToStdWstring(), (_(label) + suffix_local).ToStdWstring(),
gc.group.ToStdWstring(), _(gc.group).ToStdWstring(),
gc.category.ToStdWstring(), _(gc.category).ToStdWstring() });
gc.category.ToStdWstring(), GUI::Tab::translate_category(gc.category, type).ToStdWstring() });
};
for (std::string opt_key : config->keys())

View file

@ -432,6 +432,17 @@ Slic3r::GUI::PageShp Tab::add_options_page(const wxString& title, const std::str
return page;
}
// Names of categories is save in English always. We translate them only for UI.
// But category "Extruder n" can't be translated regularly (using _()), so
// just for this category we should splite the title and translate "Extruder" word separately
wxString Tab::translate_category(const wxString& title, Preset::Type preset_type)
{
if (preset_type == Preset::TYPE_PRINTER && title.Contains("Extruder ")) {
return _("Extruder") + title.SubString(8, title.Last());
}
return _(title);
}
void Tab::OnActivate()
{
wxWindowUpdateLocker noUpdates(this);
@ -509,7 +520,7 @@ void Tab::update_labels_colour()
auto title = m_treectrl->GetItemText(cur_item);
for (auto page : m_pages)
{
if (_(page->title()) != title)
if (translate_category(page->title(), m_type) != title)
continue;
const wxColor *clr = !page->m_is_nonsys_values ? &m_sys_label_clr :
@ -736,7 +747,7 @@ void Tab::update_changed_tree_ui()
auto title = m_treectrl->GetItemText(cur_item);
for (auto page : m_pages)
{
if (_(page->title()) != title)
if (translate_category(page->title(), m_type) != title)
continue;
bool sys_page = true;
bool modified_page = false;
@ -1132,7 +1143,7 @@ void Tab::update_wiping_button_visibility() {
void Tab::activate_option(const std::string& opt_key, const wxString& category)
{
wxString page_title = _(category);
wxString page_title = translate_category(category, m_type);
auto cur_item = m_treectrl->GetFirstVisibleItem();
if (!cur_item || !m_treectrl->IsVisible(cur_item))
@ -1803,7 +1814,6 @@ void TabFilament::build()
on_value_change(opt_key, value);
};
// optgroup = page->new_optgroup(_(L("Temperature")) + wxString(" °C", wxConvUTF8));
optgroup = page->new_optgroup(L("Temperature"));
Line line = { L("Nozzle"), "" };
line.append_option(optgroup->get_option("first_layer_temperature"));
@ -2515,7 +2525,7 @@ void TabPrinter::build_unregular_pages()
// Build missed extruder pages
for (auto extruder_idx = m_extruders_count_old; extruder_idx < m_extruders_count; ++extruder_idx) {
//# build page
const wxString& page_name = wxString::Format(L("Extruder %d"), int(extruder_idx + 1));
const wxString& page_name = wxString::Format("Extruder %d", int(extruder_idx + 1));
auto page = add_options_page(page_name, "funnel", true);
m_pages.insert(m_pages.begin() + n_before_extruders + extruder_idx, page);
@ -2947,7 +2957,7 @@ void Tab::rebuild_page_tree()
{
if (!p->get_show())
continue;
auto itemId = m_treectrl->AppendItem(rootItem, _(p->title()), p->iconID());
auto itemId = m_treectrl->AppendItem(rootItem, translate_category(p->title(), m_type), p->iconID());
m_treectrl->SetItemTextColour(itemId, p->get_item_colour());
if (p->title() == selected)
item = itemId;
@ -3286,7 +3296,7 @@ bool Tab::tree_sel_change_delayed()
const auto sel_item = m_treectrl->GetSelection();
const auto selection = sel_item ? m_treectrl->GetItemText(sel_item) : "";
for (auto p : m_pages)
if (_(p->title()) == selection)
if (translate_category(p->title(), m_type) == selection)
{
page = p.get();
m_is_nonsys_values = page->m_is_nonsys_values;

View file

@ -309,6 +309,7 @@ public:
void on_roll_back_value(const bool to_sys = false);
PageShp add_options_page(const wxString& title, const std::string& icon, bool is_extruder_pages = false);
static wxString translate_category(const wxString& title, Preset::Type preset_type);
virtual void OnActivate();
virtual void on_preset_loaded() {}

View file

@ -16,7 +16,11 @@
#include "Plater.hpp"
#include "../Utils/MacDarkMode.hpp"
#ifndef __WXGTK__// msw_menuitem_bitmaps is used for MSW and OSX
#ifdef __linux__
#define wxLinux true
#else
#define wxLinux false
// msw_menuitem_bitmaps is used for MSW and OSX
static std::map<int, std::string> msw_menuitem_bitmaps;
#ifdef __WXMSW__
void msw_rescale_menu(wxMenu* menu)
@ -653,7 +657,7 @@ void ModeButton::focus_button(const bool focus)
Slic3r::GUI::wxGetApp().normal_font();
SetFont(new_font);
SetForegroundColour(wxSystemSettings::GetColour(focus ? wxSYS_COLOUR_BTNTEXT : wxSYS_COLOUR_BTNSHADOW));
SetForegroundColour(wxSystemSettings::GetColour(focus ? wxSYS_COLOUR_BTNTEXT : wxLinux ? wxSYS_COLOUR_GRAYTEXT : wxSYS_COLOUR_BTNSHADOW));
Refresh();
Update();

File diff suppressed because it is too large Load diff