Merge branch 'master-remote' into feature/1.5

Signed-off-by: SoftFever <softfeverever@gmail.com>

# Conflicts:
#	bbl/i18n/BambuStudio.pot
#	bbl/i18n/de/BambuStudio_de.po
#	bbl/i18n/en/BambuStudio_en.po
#	bbl/i18n/es/BambuStudio_es.po
#	bbl/i18n/fr/BambuStudio_fr.po
#	bbl/i18n/hu/BambuStudio_hu.po
#	bbl/i18n/it/BambuStudio_it.po
#	bbl/i18n/nl/BambuStudio_nl.po
#	bbl/i18n/sv/BambuStudio_sv.po
#	bbl/i18n/zh_cn/BambuStudio_zh_CN.po
#	deps/Boost/Boost.cmake
#	deps/wxWidgets/wxWidgets.cmake
#	resources/config.json
#	resources/i18n/de/BambuStudio.mo
#	resources/i18n/en/BambuStudio.mo
#	resources/i18n/es/BambuStudio.mo
#	resources/i18n/fr/BambuStudio.mo
#	resources/i18n/hu/BambuStudio.mo
#	resources/i18n/it/BambuStudio.mo
#	resources/i18n/nl/BambuStudio.mo
#	resources/i18n/sv/BambuStudio.mo
#	resources/i18n/zh_cn/BambuStudio.mo
#	resources/images/tips_arrow.svg
#	resources/profiles/Anycubic.json
#	resources/profiles/Anycubic/filament/Anycubic Generic ABS.json
#	resources/profiles/Anycubic/filament/Anycubic Generic ASA.json
#	resources/profiles/Anycubic/filament/Anycubic Generic PA-CF.json
#	resources/profiles/Anycubic/filament/Anycubic Generic PA.json
#	resources/profiles/Anycubic/filament/Anycubic Generic PC.json
#	resources/profiles/Anycubic/filament/Anycubic Generic PETG.json
#	resources/profiles/Anycubic/filament/Anycubic Generic PLA-CF.json
#	resources/profiles/Anycubic/filament/Anycubic Generic PLA.json
#	resources/profiles/Anycubic/filament/Anycubic Generic PVA.json
#	resources/profiles/Anycubic/filament/Anycubic Generic TPU.json
#	resources/profiles/Anycubic/filament/fdm_filament_common.json
#	resources/profiles/Anycubic/machine/Anycubic 4Max Pro 0.4 nozzle.json
#	resources/profiles/Anycubic/machine/Anycubic 4Max Pro.json
#	resources/profiles/Anycubic/process/0.20mm Standard @4MaxPro.json
#	resources/profiles/Anycubic/process/fdm_process_common.json
#	resources/profiles/BBL.json
#	resources/profiles/BBL/machine/Bambu Lab P1P 0.2 nozzle.json
#	resources/profiles/BBL/machine/Bambu Lab P1P 0.4 nozzle.json
#	resources/profiles/BBL/machine/Bambu Lab P1P 0.6 nozzle.json
#	resources/profiles/BBL/machine/Bambu Lab P1P 0.8 nozzle.json
#	resources/profiles/BBL/machine/Bambu Lab X1 0.2 nozzle.json
#	resources/profiles/BBL/machine/Bambu Lab X1 0.4 nozzle.json
#	resources/profiles/BBL/machine/Bambu Lab X1 0.6 nozzle.json
#	resources/profiles/BBL/machine/Bambu Lab X1 0.8 nozzle.json
#	resources/profiles/BBL/machine/Bambu Lab X1 Carbon 0.2 nozzle.json
#	resources/profiles/BBL/machine/Bambu Lab X1 Carbon 0.4 nozzle.json
#	resources/profiles/BBL/machine/Bambu Lab X1 Carbon 0.6 nozzle.json
#	resources/profiles/BBL/machine/Bambu Lab X1 Carbon 0.8 nozzle.json
#	resources/profiles/BBL/machine/fdm_bbl_3dp_001_common.json
#	resources/profiles/Voron.json
#	resources/web/data/text.js
#	resources/web/image/printer/Anycubic 4Max Pro_cover.png
#	src/BambuStudio.cpp
#	src/libslic3r/GCode.cpp
#	src/libslic3r/GCode.hpp
#	src/libslic3r/GCode/GCodeProcessor.cpp
#	src/libslic3r/GCodeWriter.hpp
#	src/libslic3r/PerimeterGenerator.cpp
#	src/libslic3r/PresetBundle.cpp
#	src/libslic3r/Print.cpp
#	src/libslic3r/Print.hpp
#	src/libslic3r/PrintConfig.cpp
#	src/libslic3r/PrintConfig.hpp
#	src/libslic3r/PrintObject.cpp
#	src/slic3r/GUI/AMSMaterialsSetting.cpp
#	src/slic3r/GUI/AMSMaterialsSetting.hpp
#	src/slic3r/GUI/AmsMappingPopup.cpp
#	src/slic3r/GUI/AmsMappingPopup.hpp
#	src/slic3r/GUI/Auxiliary.cpp
#	src/slic3r/GUI/BackgroundSlicingProcess.cpp
#	src/slic3r/GUI/ConfigManipulation.cpp
#	src/slic3r/GUI/DeviceManager.cpp
#	src/slic3r/GUI/DeviceManager.hpp
#	src/slic3r/GUI/ExtrusionCalibration.cpp
#	src/slic3r/GUI/GCodeViewer.cpp
#	src/slic3r/GUI/GCodeViewer.hpp
#	src/slic3r/GUI/GUI_App.cpp
#	src/slic3r/GUI/IMSlider.cpp
#	src/slic3r/GUI/Jobs/PrintJob.cpp
#	src/slic3r/GUI/Jobs/PrintJob.hpp
#	src/slic3r/GUI/Jobs/SendJob.cpp
#	src/slic3r/GUI/Jobs/SendJob.hpp
#	src/slic3r/GUI/MainFrame.cpp
#	src/slic3r/GUI/MainFrame.hpp
#	src/slic3r/GUI/MediaPlayCtrl.cpp
#	src/slic3r/GUI/OptionsGroup.cpp
#	src/slic3r/GUI/PhysicalPrinterDialog.cpp
#	src/slic3r/GUI/Plater.cpp
#	src/slic3r/GUI/PrintHostDialogs.cpp
#	src/slic3r/GUI/Printer/BambuTunnel.h
#	src/slic3r/GUI/Printer/PrinterFileSystem.cpp
#	src/slic3r/GUI/Printer/gstbambusrc.c
#	src/slic3r/GUI/Printer/gstbambusrc.h
#	src/slic3r/GUI/ReleaseNote.cpp
#	src/slic3r/GUI/ReleaseNote.hpp
#	src/slic3r/GUI/SelectMachine.cpp
#	src/slic3r/GUI/SendToPrinter.cpp
#	src/slic3r/GUI/SetBedTypeDialog.cpp
#	src/slic3r/GUI/StatusPanel.cpp
#	src/slic3r/GUI/StatusPanel.hpp
#	src/slic3r/GUI/Tab.cpp
#	src/slic3r/GUI/Widgets/AMSControl.cpp
#	src/slic3r/GUI/Widgets/AMSControl.hpp
#	src/slic3r/GUI/Widgets/ImageSwitchButton.cpp
#	src/slic3r/GUI/Widgets/Label.cpp
#	src/slic3r/GUI/WipeTowerDialog.cpp
#	src/slic3r/Utils/Process.cpp
#	src/slic3r/Utils/bambu_networking.hpp
#	version.inc
This commit is contained in:
SoftFever 2023-03-08 00:08:26 +08:00
commit 5ef51f6c8a
339 changed files with 37169 additions and 5445 deletions

View file

@ -324,6 +324,11 @@ static void glfw_callback(int error_code, const char* description)
BOOST_LOG_TRIVIAL(error) << "error_code " <<error_code <<", description: " <<description<< std::endl;
}
const float bed3d_ax3s_default_stem_radius = 0.5f;
const float bed3d_ax3s_default_stem_length = 25.0f;
const float bed3d_ax3s_default_tip_radius = 2.5f * bed3d_ax3s_default_stem_radius;
const float bed3d_ax3s_default_tip_length = 5.0f;
int CLI::run(int argc, char **argv)
{
// Mark the main thread for the debugger and for runtime checks.
@ -334,7 +339,7 @@ int CLI::run(int argc, char **argv)
// startup if gtk3 is used. This env var has to be set explicitly to
// instruct the window manager to fall back to X server mode.
::setenv("GDK_BACKEND", "x11", /* replace */ true);
// Also on Linux, we need to tell Xlib that we will be using threads,
// lest we crash when we fire up GStreamer.
XInitThreads();
@ -360,17 +365,17 @@ int CLI::run(int argc, char **argv)
/*BOOST_LOG_TRIVIAL(info) << "begin to setup params, argc=" << argc << std::endl;
for (int index=0; index < argc; index++)
BOOST_LOG_TRIVIAL(info) << "index="<< index <<", arg is "<< argv[index] <<std::endl;
int debug_argc = 9;
int debug_argc = 5;
char *debug_argv[] = {
"E:\work\projects\bambu_release\bamboo_slicer\build_debug\src\Debug\bambu-studio.exe",
"--slice",
"0",
"--load-settings",
"machine.json;process.json",
"--load-filaments",
"filament.json",
"9",
//"--load-settings",
//"machine.json;process.json",
//"--load-filaments",
//"filament.json",
"--export-3mf=output.3mf",
"boat.stl"
"test_outside.3mf"
};
if (! this->setup(debug_argc, debug_argv))*/
if (!this->setup(argc, argv))
@ -398,6 +403,8 @@ int CLI::run(int argc, char **argv)
boost::algorithm::iends_with(boost::filesystem::path(argv[0]).filename().string(), "gcodeviewer");
#endif // _WIN32*/
bool translate_old = false;
int current_width, current_depth, current_height;
const std::vector<std::string> &load_configs = m_config.option<ConfigOptionStrings>("load_settings", true)->values;
//BBS: always use ForwardCompatibilitySubstitutionRule::Enable
//const ForwardCompatibilitySubstitutionRule config_substitution_rule = m_config.option<ConfigOptionEnum<ForwardCompatibilitySubstitutionRule>>("config_compatibility", true)->value;
@ -457,6 +464,15 @@ int CLI::run(int argc, char **argv)
return (argc == 0) ? 0 : 1;
#endif // SLIC3R_GUI
}
else {
const ConfigOptionInt *opt_loglevel = m_config.opt<ConfigOptionInt>("debug");
if (opt_loglevel) {
set_logging_level(opt_loglevel->value);
}
else {
set_logging_level(2);
}
}
BOOST_LOG_TRIVIAL(info) << "start_gui="<< start_gui << std::endl;
@ -531,6 +547,12 @@ int CLI::run(int argc, char **argv)
BOOST_LOG_TRIVIAL(info) << "object "<<o->name <<", id :" << o->id().id << ", from bbl 3mf\n";
}
Semver old_version(1, 5, 9);
if ((file_version < old_version) && !config.empty()) {
translate_old = true;
BOOST_LOG_TRIVIAL(info) << boost::format("old 3mf version %1%, need to translate")%file_version.to_string();
}
/*for (ModelObject *model_object : model.objects)
for (ModelInstance *model_instance : model_object->instances)
{
@ -991,6 +1013,7 @@ int CLI::run(int argc, char **argv)
for (const t_config_option_key &opt_key : config.keys()) {
if (!diff_key_sets.empty() && (diff_key_sets.find(opt_key) != diff_key_sets.end())) {
//uptodate, diff keys, continue
BOOST_LOG_TRIVIAL(info) << boost::format("keep key %1%")%opt_key;
continue;
}
const ConfigOption *source_opt = config.option(opt_key);
@ -1035,6 +1058,7 @@ int CLI::run(int argc, char **argv)
}
std::set<std::string> different_keys_set(different_keys.begin(), different_keys.end());
BOOST_LOG_TRIVIAL(info) << boost::format("update printer config to newest, different size %1%")%different_keys_set.size();
int ret = update_full_config(m_print_config, load_machine_config, different_keys_set);
if (ret)
flush_and_exit(ret);
@ -1072,6 +1096,7 @@ int CLI::run(int argc, char **argv)
}
std::set<std::string> different_keys_set(different_keys.begin(), different_keys.end());
BOOST_LOG_TRIVIAL(info) << boost::format("update process config to newest, different size %1%")%different_keys_set.size();
int ret = update_full_config(m_print_config, load_machine_config, different_keys_set);
if (ret)
flush_and_exit(ret);
@ -1137,9 +1162,11 @@ int CLI::run(int argc, char **argv)
//parse the filament value to index th
//loop through options and apply them
std::set<std::string> different_keys_set(different_keys.begin(), different_keys.end());
BOOST_LOG_TRIVIAL(info) << boost::format("update filament %1%'s config to newest, different size %2%")%filament_index%different_keys_set.size();
for (const t_config_option_key &opt_key : config.keys()) {
if (!different_keys_set.empty() && (different_keys_set.find(opt_key) != different_keys_set.end())) {
//uptodate, diff keys, continue
BOOST_LOG_TRIVIAL(info) << boost::format("keep key %1%")%opt_key;
continue;
}
// Create a new option with default value for the key.
@ -1149,7 +1176,7 @@ int CLI::run(int argc, char **argv)
const ConfigOption *source_opt = config.option(opt_key);
if (source_opt == nullptr) {
// The key was not found in the source config, therefore it will not be initialized!
boost::nowide::cerr << "can not found option " <<opt_key<<"from filament file "<< load_filaments[filament_index -1] <<std::endl;
BOOST_LOG_TRIVIAL(error) << boost::format("can not find %1% from filament %2%: %3%")%opt_key%filament_index%load_filaments_name[index];
flush_and_exit(CLI_CONFIG_FILE_ERROR);
}
if (source_opt->is_scalar()) {
@ -1169,7 +1196,7 @@ int CLI::run(int argc, char **argv)
}
else {
//skip the scalar values
BOOST_LOG_TRIVIAL(warning) << "skip scalar option " <<opt_key<<" from filament file "<< load_filaments[filament_index -1] <<std::endl;
BOOST_LOG_TRIVIAL(info) << boost::format("skip scalar option %1% from filament %2%: %3%")%opt_key%filament_index%load_filaments_name[index];;
continue;
}
}
@ -1179,7 +1206,7 @@ int CLI::run(int argc, char **argv)
if (opt == nullptr) {
// opt_key does not exist in this ConfigBase and it cannot be created, because it is not defined by this->def().
// This is only possible if other is of DynamicConfig type.
boost::nowide::cerr << "can not create option " <<opt_key<<"to config, from filament file "<< load_filaments[filament_index -1] <<std::endl;
BOOST_LOG_TRIVIAL(error) << boost::format("can not create option %1% to config, from filament %2%: %3%")%opt_key%filament_index%load_filaments_name[index];
flush_and_exit(CLI_CONFIG_FILE_ERROR);
}
ConfigOptionVectorBase* opt_vec_dst = static_cast<ConfigOptionVectorBase*>(opt);
@ -1250,10 +1277,12 @@ int CLI::run(int argc, char **argv)
m_print_config.apply(sla_print_config, true);*/
}
std::string validity = m_print_config.validate();
std::map<std::string, std::string> validity = m_print_config.validate(true);
if (!validity.empty()) {
boost::nowide::cerr <<"Error: The composite configation is not valid: " << validity << std::endl;
flush_and_exit(CLI_INVALID_PRINTER_TECH);
boost::nowide::cerr << "Param values in 3mf/config error: "<< std::endl;
for (std::map<std::string, std::string>::iterator it=validity.begin(); it!=validity.end(); ++it)
boost::nowide::cerr << it->first <<": "<< it->second << std::endl;
flush_and_exit(CLI_INVALID_VALUES_IN_3MF);
}
//BBS: partplate list
@ -1266,10 +1295,19 @@ int CLI::run(int argc, char **argv)
double height_to_lid = m_print_config.opt_float("extruder_clearance_height_to_lid");
double height_to_rod = m_print_config.opt_float("extruder_clearance_height_to_rod");
double plate_stride;
std::string bed_texture;
if (m_models.size() > 0)
{
std::string bed_texture;
partplate_list.reset_size(bedfs[2].x() - bedfs[0].x(), bedfs[2].y() - bedfs[0].y(), print_height);
if (translate_old) {
current_width = bedfs[2].x() - bedfs[0].x();
current_depth = bedfs[2].y() - bedfs[0].y();
current_height = print_height;
BOOST_LOG_TRIVIAL(info) << boost::format("translate old 3mf, switch to old bed size,{%1%, %2%, %3%}")%(current_width + bed3d_ax3s_default_tip_radius)%(current_depth+bed3d_ax3s_default_tip_radius) %current_height;
partplate_list.reset_size(current_width + bed3d_ax3s_default_tip_radius, current_depth + bed3d_ax3s_default_tip_radius, current_height, false);
}
else {
partplate_list.reset_size(bedfs[2].x() - bedfs[0].x(), bedfs[2].y() - bedfs[0].y(), print_height, false);
}
partplate_list.set_shapes(bedfs, excluse_areas, bed_texture, height_to_lid, height_to_rod);
plate_stride = partplate_list.plate_stride_x();
BOOST_LOG_TRIVIAL(info) << "bed size, x="<<bedfs[2].x() - bedfs[0].x()<<",y="<<bedfs[2].y() - bedfs[0].y()<<",z="<< print_height <<"\n";
@ -1277,6 +1315,21 @@ int CLI::run(int argc, char **argv)
if (plate_data_src.size() > 0)
{
partplate_list.load_from_3mf_structure(plate_data_src);
//BBS: translate old 3mf to correct positions
if (translate_old) {
//translate the objects
int plate_count = partplate_list.get_plate_count();
for (int index = 1; index < plate_count; index ++) {
Slic3r::GUI::PartPlate* cur_plate = (Slic3r::GUI::PartPlate *)partplate_list.get_plate(index);
Vec3d cur_origin = cur_plate->get_origin();
Vec3d new_origin = partplate_list.compute_origin_using_new_size(index, current_width, current_depth);
cur_plate->translate_all_instance(new_origin - cur_origin);
}
BOOST_LOG_TRIVIAL(info) << boost::format("translate old 3mf, switch back to current bed size,{%1%, %2%, %3%}")%current_width %current_depth %current_height;
partplate_list.reset_size(current_width, current_depth, current_height, true, true);
}
}
/*for (ModelObject *model_object : m_models[0].objects)
for (ModelInstance *model_instance : model_object->instances)
@ -1840,6 +1893,7 @@ int CLI::run(int argc, char **argv)
}*/
DynamicPrintConfig new_print_config = m_print_config;
new_print_config.apply(*part_plate->config());
new_print_config.apply(m_extra_config, true);
print->apply(model, new_print_config);
StringObjectException warning;
auto err = print->validate(&warning);
@ -2326,17 +2380,6 @@ int CLI::run(int argc, char **argv)
bool CLI::setup(int argc, char **argv)
{
{
Slic3r::set_logging_level(1);
const char *loglevel = boost::nowide::getenv("BBL_LOGLEVEL");
if (loglevel != nullptr) {
if (loglevel[0] >= '0' && loglevel[0] <= '9' && loglevel[1] == 0)
set_logging_level(loglevel[0] - '0');
else
boost::nowide::cerr << "Invalid BBL_LOGLEVEL environment variable: " << loglevel << std::endl;
}
}
// Detect the operating system flavor after SLIC3R_LOGLEVEL is set.
detect_platform();
@ -2365,7 +2408,7 @@ bool CLI::setup(int argc, char **argv)
#ifdef __APPLE__
// The application is packed in the .dmg archive as 'Slic3r.app/Contents/MacOS/Slic3r'
// The resources are packed to 'Slic3r.app/Contents/Resources'
boost::filesystem::path path_resources = boost::filesystem::canonical(path_to_binary).parent_path() / "../Resources";
boost::filesystem::path path_resources = boost::filesystem::canonical(path_to_binary).parent_path().parent_path() / "Resources";
#elif defined _WIN32
// The application is packed in the .zip archive in the root,
// The resources are packed to 'resources'
@ -2379,7 +2422,7 @@ bool CLI::setup(int argc, char **argv)
// The application is packed in the .tar.bz archive (or in AppImage) as 'bin/slic3r',
// The resources are packed to 'resources'
// Path from Slic3r binary to resources:
boost::filesystem::path path_resources = boost::filesystem::canonical(path_to_binary).parent_path() / "../resources";
boost::filesystem::path path_resources = boost::filesystem::canonical(path_to_binary).parent_path().parent_path() / "resources";
#endif
set_resources_dir(path_resources.string());
@ -2404,17 +2447,8 @@ bool CLI::setup(int argc, char **argv)
m_transforms.emplace_back(opt_key);
}
#if !BBL_RELEASE_TO_PUBLIC
{
const ConfigOptionInt *opt_loglevel = m_config.opt<ConfigOptionInt>("debug");
if (opt_loglevel != 0) {
set_logging_level(opt_loglevel->value);
}
}
#endif
//FIXME Validating at this stage most likely does not make sense, as the config is not fully initialized yet.
std::string validity = m_config.validate();
std::map<std::string, std::string> validity = m_config.validate(true);
// Initialize with defaults.
for (const t_optiondef_map *options : { &cli_actions_config_def.options, &cli_transform_config_def.options, &cli_misc_config_def.options })
@ -2425,7 +2459,9 @@ bool CLI::setup(int argc, char **argv)
//FIXME Validating at this stage most likely does not make sense, as the config is not fully initialized yet.
if (!validity.empty()) {
boost::nowide::cerr << "error: " << validity << std::endl;
boost::nowide::cerr << "Params in command line error: "<< std::endl;
for (std::map<std::string, std::string>::iterator it=validity.begin(); it!=validity.end(); ++it)
boost::nowide::cerr << it->first <<": "<< it->second << std::endl;
return false;
}
@ -2608,7 +2644,7 @@ extern "C" {
argv_ptrs[i] = argv_narrow[i].data();
//BBS: register default exception handler
#if 1
#if BBL_RELEASE_TO_PUBLIC
SET_DEFULTER_HANDLER();
#else
AddVectoredExceptionHandler(1, CBaseException::UnhandledExceptionFilter);

View file

@ -186,6 +186,9 @@ namespace ImGui
const wchar_t SphereButtonDarkIcon = 0x0826;
const wchar_t GapFillDarkIcon = 0x0827;
const wchar_t TextSearchIcon = 0x0828;
const wchar_t TextSearchCloseIcon = 0x0829;
// void MyFunction(const char* name, const MyMatrix44& v);
}

View file

@ -391,7 +391,7 @@ IM_MSVC_RUNTIME_CHECKS_OFF
static inline float ImPow(float x, float y) { return powf(x, y); } // DragBehaviorT/SliderBehaviorT uses ImPow with either float/double and need the precision
static inline double ImPow(double x, double y) { return pow(x, y); }
static inline float ImLog(float x) { return logf(x); } // DragBehaviorT/SliderBehaviorT uses ImLog with either float/double and need the precision
static inline double ImLog(double x) { return log(x); }
static inline double ImLog(double x) { return logf(x); }
static inline int ImAbs(int x) { return x < 0 ? -x : x; }
static inline float ImAbs(float x) { return fabsf(x); }
static inline double ImAbs(double x) { return fabs(x); }

View file

@ -2126,7 +2126,7 @@ bool ImGui::BBLBeginCombo(const char *label, const char *preview_value, ImGuiCom
const ImVec2 label_size = CalcTextSize(label, NULL, true);
const float expected_w = CalcItemWidth();
const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : expected_w;
const ImRect frame_bb(window->DC.CursorPos - ImVec2(0.0, style.FramePadding.y), window->DC.CursorPos + ImVec2(w - arrow_size * 2, label_size.y + style.FramePadding.y));
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w - arrow_size * 2, label_size.y + style.FramePadding.y * 2));
const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
ItemSize(total_bb, style.FramePadding.y);
if (!ItemAdd(total_bb, id, &frame_bb)) return false;
@ -4175,7 +4175,7 @@ bool ImGui::BBLInputScalar(const char *label, ImGuiDataType data_type, void *p_d
if (format == NULL) format = DataTypeGetInfo(data_type)->PrintFmt;
char buf[8];
char buf[64];
DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format);
bool value_changed = false;
@ -7185,7 +7185,9 @@ bool ImGui::BBLImageSelectable(ImTextureID user_texture_id, const ImVec2& size_a
// Render
if (held && (flags & ImGuiSelectableFlags_DrawHoveredWhenHeld)) hovered = true;
if (hovered || selected) {
const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
if (hovered && selected)
col = GetColorU32(ImGuiCol_Header);
if (arrow_size == 0) {
RenderFrame(bb.Min, ImVec2(bb.Max.x - style.WindowPadding.x, bb.Max.y), col, false, 0.0f);
}
@ -7204,9 +7206,9 @@ bool ImGui::BBLImageSelectable(ImTextureID user_texture_id, const ImVec2& size_a
// Render
const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
ImVec2 p_min = bb.Min + ImVec2(2 * style.ItemSpacing.x, (bb.Max.y - bb.Min.y - font_size.y) / 2);
ImVec2 p_min = bb.Min + ImVec2(style.ItemInnerSpacing.x, (bb.Max.y - bb.Min.y - font_size.y) / 2);
ImVec2 p_max = p_min + font_size;
window->DrawList->AddImage(user_texture_id, p_min, p_max, uv0, uv1, GetColorU32(tint_col));
window->DrawList->AddImage(user_texture_id, p_min, p_max, uv0, uv1, selected || (held && hovered) ? GetColorU32(ImVec4(1.f, 1.f, 1.f, 1.f)) : GetColorU32(tint_col));
if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor();

View file

@ -75,7 +75,7 @@ class _Item {
public:
int itemid_{ 0 };
int extrude_id{ 1 };
std::vector<int> extrude_ids;
double height{ 0 };
double print_temp{ 0 };
double bed_temp{ 0 };

View file

@ -114,13 +114,14 @@ public:
double score = LARGE_COST_TO_REJECT+1, best_score = LARGE_COST_TO_REJECT+1;
double score_all_plates = 0, score_all_plates_best = std::numeric_limits<double>::max();
typename Placer::PackResult result, result_best, result_firstfit;
size_t j = 0;
int j = 0;
while(!was_packed && !cancelled()) {
for(; j < placers.size() && !was_packed && !cancelled(); j++) {
result = placers[j].pack(*it, rem(it, store_));
score = result.score();
score_all_plates = std::accumulate(placers.begin(), placers.begin() + j, score,
[](double sum, const Placer& elem) { return sum + elem.score(); });
if (this->unfitindicator_) this->unfitindicator_(it->get().name + " bed_id="+std::to_string(j) + ",score=" + std::to_string(score));
if(score >= 0 && score < LARGE_COST_TO_REJECT) {
if (bed_id_firstfit == -1) {

View file

@ -257,6 +257,10 @@ void AppConfig::set_defaults()
set("mouse_supported", "mouse left/mouse middle/mouse right");
}
if (get("privacy_version").empty()) {
set("privacy_version", "00.00.00.00");
}
if (get("rotate_view").empty()) {
set("rotate_view", "none/mouse left");
}
@ -462,8 +466,11 @@ std::string AppConfig::load()
for(auto& element: iter.value()) {
if (idx == 0)
m_storage[it.key()]["filament"] = element;
else
m_storage[it.key()]["filament_" + std::to_string(idx)] = element;
else {
auto n = std::to_string(idx);
if (n.length() == 1) n = "0" + n;
m_storage[it.key()]["filament_" + n] = element;
}
idx++;
}
} else {
@ -483,8 +490,6 @@ std::string AppConfig::load()
m_filament_presets = iter.value().get<std::vector<std::string>>();
} else if (iter.key() == "filament_colors") {
m_filament_colors = iter.value().get<std::vector<std::string>>();
} else if (iter.key() == "flushing_volumes") {
m_flush_volumes_matrix = iter.value().get<std::vector<float>>();
}
else {
if (iter.value().is_string())
@ -577,10 +582,6 @@ void AppConfig::save()
j["app"]["filament_colors"].push_back(filament_color);
}
for (double flushing_volume : m_flush_volumes_matrix) {
j["app"]["flushing_volumes"].push_back(flushing_volume);
}
// Write the other categories.
for (const auto& category : m_storage) {
if (category.first.empty())

View file

@ -177,11 +177,6 @@ public:
m_filament_colors = filament_colors;
m_dirty = true;
}
const std::vector<float> &get_flush_volumes_matrix() const { return m_flush_volumes_matrix; }
void set_flush_volumes_matrix(const std::vector<float> &flush_volumes_matrix){
m_flush_volumes_matrix = flush_volumes_matrix;
m_dirty = true;
}
// return recent/last_opened_folder or recent/settings_folder or empty string.
std::string get_last_dir() const;
@ -281,7 +276,6 @@ private:
std::vector<std::string> m_filament_presets;
std::vector<std::string> m_filament_colors;
std::vector<float> m_flush_volumes_matrix;
};
} // namespace Slic3r

View file

@ -452,20 +452,25 @@ protected:
}
std::set<int> extruder_ids;
int non_virt_cnt = 0;
for (int i = 0; i < m_items.size(); i++) {
Item& p = m_items[i];
if (p.is_virt_object) continue;
extruder_ids.insert(p.extrude_id);
// add a large cost if not multi materials on same plate is not allowed
if (!params.allow_multi_materials_on_same_plate)
score += LARGE_COST_TO_REJECT * (item.extrude_id != p.extrude_id);
extruder_ids.insert(p.extrude_ids.begin(),p.extrude_ids.end());
non_virt_cnt++;
}
extruder_ids.insert(item.extrude_ids.begin(),item.extrude_ids.end());
// add a large cost if not multi materials on same plate is not allowed
if (!params.allow_multi_materials_on_same_plate) {
// non_virt_cnt==0 means it's the first object, which can be multi-color
if (extruder_ids.size() > 1 && non_virt_cnt > 0)
score += LARGE_COST_TO_REJECT * 1.1;
}
// for layered printing, we want extruder change as few as possible
// this has very weak effect, CAN NOT use a large weight
if (!params.is_seq_print) {
extruder_ids.insert(item.extrude_id);
score += 1 * std::max(0, ((int)extruder_ids.size() - 1));
score += 1 * std::max(0, ((int) extruder_ids.size() - 1));
}
return std::make_tuple(score, fullbb);
@ -601,7 +606,7 @@ public:
}
else {
return i1.bed_temp != i2.bed_temp ? (i1.bed_temp > i2.bed_temp) :
(i1.extrude_id != i2.extrude_id ? (i1.extrude_id < i2.extrude_id) : (i1.area() > i2.area()));
(i1.extrude_ids != i2.extrude_ids ? (i1.extrude_ids.front() < i2.extrude_ids.front()) : (i1.area() > i2.area()));
}
};
@ -850,7 +855,7 @@ static void process_arrangeable(const ArrangePolygon &arrpoly,
item.binId(arrpoly.bed_idx);
item.priority(arrpoly.priority);
item.itemId(arrpoly.itemid);
item.extrude_id = arrpoly.extrude_ids.front();
item.extrude_ids = arrpoly.extrude_ids;
item.height = arrpoly.height;
item.name = arrpoly.name;
//BBS: add virtual object logic

View file

@ -165,17 +165,6 @@ static Polygons top_level_outer_brim_islands(const ConstPrintObjectPtrs &top_lev
}
}
// BBS
if (!object->tree_support_layers().empty()) {
for (const ExPolygon& ex_poly : object->tree_support_layers().front()->lslices) {
Polygons contour_offset = offset(ex_poly.contour, brim_object_gap, ClipperLib::jtSquare);
for (Polygon& poly : contour_offset)
poly.douglas_peucker(scaled_resolution);
polygons_append(islands_object, std::move(contour_offset));
}
}
for (const PrintInstance &instance : object->instances())
append_and_translate(islands, islands_object, instance);
}
@ -248,19 +237,6 @@ static ExPolygons top_level_outer_brim_area(const Print &print
}
}
// BBS
if (!object->tree_support_layers().empty()) {
for (const ExPolygon& ex_poly : object->tree_support_layers().front()->lslices) {
if ((brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner || brim_type == BrimType::btAutoBrim) && is_top_outer_brim)
append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_object_gap, jtRound, scaled_resolution), offset(ex_poly.contour, brim_object_gap)));
if (brim_type != BrimType::btNoBrim)
append(no_brim_area_object, offset_ex(ExPolygon(ex_poly.contour), brim_object_gap));
no_brim_area_object.emplace_back(ex_poly.contour);
}
}
for (const PrintInstance &instance : object->instances()) {
append_and_translate(brim_area, brim_area_object, instance);
append_and_translate(no_brim_area, no_brim_area_object, instance);
@ -360,21 +336,6 @@ static ExPolygons top_level_outer_brim_area(const Print& print, const ConstPrint
}
}
// BBS
if (!object->tree_support_layers().empty()) {
for (const ExPolygon& ex_poly : object->tree_support_layers().front()->lslices) {
if ((brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner || brim_type == BrimType::btAutoBrim) && is_top_outer_brim)
append(brim_area_support, diff_ex(offset(ex_poly.contour, brim_width, jtRound, SCALED_RESOLUTION), offset(ex_poly.contour, 0)));
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim)
append(no_brim_area_support, offset_ex(ex_poly.holes, -no_brim_offset));
if (brim_type != BrimType::btNoBrim)
append(no_brim_area_support, offset_ex(ex_poly.contour, 0));
no_brim_area_support.emplace_back(ex_poly.contour);
}
}
brimToWrite.at(object->id()).sup = false;
for (const PrintInstance& instance : object->instances()) {
if (!brim_area_support.empty())
@ -543,26 +504,6 @@ static ExPolygons inner_brim_area(const Print& print, const ConstPrintObjectPtrs
no_brim_area_support.emplace_back(support_contour);
}
}
// BBS
if (!object->tree_support_layers().empty()) {
for (const ExPolygon& ex_poly : object->tree_support_layers().front()->lslices) {
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner || brim_type == BrimType::btAutoBrim) {
if (!top_outer_brim)
append(brim_area_support, diff_ex(offset_ex(ex_poly.contour, brim_width + brim_offset, jtRound, SCALED_RESOLUTION), offset_ex(ex_poly.contour, brim_offset)));
}
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btOuterAndInner)
append(brim_area_support, diff_ex(offset_ex(ex_poly.holes, -brim_offset), offset_ex(ex_poly.holes, -brim_width - brim_offset)));
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btNoBrim)
append(no_brim_area_support, diff_ex(offset(ex_poly.contour, no_brim_offset), ex_poly.holes));
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim)
append(no_brim_area_support, offset_ex(ex_poly.holes, -no_brim_offset));
append(holes_support, ex_poly.holes);
if (brim_type != BrimType::btNoBrim)
append(no_brim_area_support, offset_ex(ex_poly.contour, 0));
no_brim_area_support.emplace_back(ex_poly.contour);
}
}
}
brimToWrite.at(object->id()).sup = false;
for (const PrintInstance& instance : object->instances()) {
@ -927,7 +868,6 @@ static ExPolygons outer_inner_brim_area(const Print& print,
double brimWidthRaw = configBrimWidthByVolumeGroups(adhension, maxSpeed, groupVolumePtrs, volumeGroup.slices, groupHeight);
brim_width = scale_(floor(brimWidthRaw / flowWidth / 2) * flowWidth * 2);
}
for (const ExPolygon& ex_poly : volumeGroup.slices) {
// BBS: additional brim width will be added if part's adhension area is too small and brim is not generated
float brim_width_mod;
@ -986,7 +926,7 @@ static ExPolygons outer_inner_brim_area(const Print& print,
support_material_extruder = printExtruders.front() + 1;
}
if (support_material_extruder == extruderNo && brimToWrite.at(object->id()).sup) {
if (!object->support_layers().empty()) {
if (!object->support_layers().empty() && object->support_layers().front()->support_type==stInnerNormal) {
for (const Polygon& support_contour : object->support_layers().front()->support_fills.polygons_covered_by_spacing()) {
// Brim will not be generated for supports
/*
@ -1000,8 +940,8 @@ static ExPolygons outer_inner_brim_area(const Print& print,
}
}
// BBS
if (!object->tree_support_layers().empty()) {
for (const ExPolygon& ex_poly : object->tree_support_layers().front()->lslices) {
if (!object->support_layers().empty() && object->support_layers().front()->support_type == stInnerTree) {
for (const ExPolygon &ex_poly : object->support_layers().front()->lslices) {
// BBS: additional brim width will be added if adhension area is too small without brim
float brim_width_mod = ex_poly.area() / ex_poly.contour.length() < scaled_half_min_adh_length
&& brim_width < scaled_flow_width ? brim_width + scaled_additional_brim_width : brim_width;
@ -1036,11 +976,16 @@ static ExPolygons outer_inner_brim_area(const Print& print,
}
}
}
if (!bedExPoly.empty())
if (!bedExPoly.empty()){
auto plateOffset = print.get_plate_origin();
bedExPoly.front().translate(scale_(plateOffset.x()), scale_(plateOffset.y()));
no_brim_area.push_back(bedExPoly.front());
for (const PrintObject* object : print.objects())
if (brimAreaMap.find(object->id()) != brimAreaMap.end()) {
}
for (const PrintObject* object : print.objects()) {
if (brimAreaMap.find(object->id()) != brimAreaMap.end())
{
brimAreaMap[object->id()] = diff_ex(brimAreaMap[object->id()], no_brim_area);
}
if (supportBrimAreaMap.find(object->id()) != supportBrimAreaMap.end())
supportBrimAreaMap[object->id()] = diff_ex(supportBrimAreaMap[object->id()], no_brim_area);
@ -1220,13 +1165,6 @@ static void make_inner_island_brim(const Print& print, const ConstPrintObjectPtr
}
}
if (!object->tree_support_layers().empty()) {
for (const ExPolygon& ex_poly : object->tree_support_layers().front()->lslices) {
Polygon counter = ex_poly.contour;
save_polygon_if_is_inner_island(holes_area, counter, hole_island_pair);
}
}
//BBS: 3 generate loops, only save part of loop which inside hole
const float brim_offset = scale_(object->config().brim_object_gap.value);
const float brim_width = scale_(object->config().brim_width.value);
@ -1369,13 +1307,6 @@ static void make_inner_island_brim(const Print& print, const ConstPrintObjectPtr
}
}
if (!object->tree_support_layers().empty()) {
for (const ExPolygon& ex_poly : object->tree_support_layers().front()->lslices) {
Polygon counter = ex_poly.contour;
save_polygon_if_is_inner_island(holes_area, counter, hole_island_pair_supports);
}
}
//BBS: 3 generate loops, only save part of loop which inside hole
const float brim_offset = scale_(object->config().brim_object_gap.value);
const float brim_width = floor(scale_(object->config().brim_width.value) / 2 / flow.scaled_spacing()) * 2 * flow.scaled_spacing();
@ -1587,10 +1518,10 @@ Polygons tryExPolygonOffset(const ExPolygons islandAreaEx, const Print& print)
for (ExPolygon& poly_ex : islands_ex)
poly_ex.douglas_peucker(resolution);
polygons_append(loops, to_polygons(islands_ex));
islands_ex = offset_ex(std::move(islands_ex), -1.4f*float(flow.scaled_spacing()), jtRound, resolution);
islands_ex = offset_ex(std::move(islands_ex), -1.3f*float(flow.scaled_spacing()), jtRound, resolution);
for (ExPolygon& poly_ex : islands_ex)
poly_ex.douglas_peucker(resolution);
islands_ex = offset_ex(std::move(islands_ex), 0.4f*float(flow.scaled_spacing()), jtRound, resolution);
islands_ex = offset_ex(std::move(islands_ex), 0.3f*float(flow.scaled_spacing()), jtRound, resolution);
}
return loops;
}
@ -1663,13 +1594,6 @@ void make_brim(const Print& print, PrintTryCancel try_cancel, Polygons& islands_
ex_poly_translated.translate(instance.shift.x(), instance.shift.y());
bbx.merge(get_extents(ex_poly_translated));
}
if (!object->tree_support_layers().empty())
for (const Polygon& ex_poly : object->tree_support_layers().front()->support_fills.polygons_covered_by_spacing())
for (const PrintInstance& instance : object->instances()) {
auto ex_poly_translated = ex_poly;
ex_poly_translated.translate(instance.shift.x(), instance.shift.y());
bbx.merge(get_extents(ex_poly_translated));
}
if (supportBrimAreaMap.find(printObjID) != supportBrimAreaMap.end()) {
for (const ExPolygon& ex_poly : supportBrimAreaMap.at(printObjID))
bbx.merge(get_extents(ex_poly.contour));

View file

@ -3,6 +3,9 @@ project(libslic3r)
include(PrecompiledHeader)
string(TIMESTAMP COMPILE_TIME %Y%m%d-%H%M%S)
set(SLIC3R_BUILD_TIME ${COMPILE_TIME})
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libslic3r_version.h.in ${CMAKE_CURRENT_BINARY_DIR}/libslic3r_version.h @ONLY)
if (MINGW)

View file

@ -298,9 +298,16 @@ ConfigOption* ConfigOptionDef::create_default_option() const
return new ConfigOptionEnumGeneric(this->enum_keys_map, this->default_value->getInt());
if (type == coEnums) {
ConfigOptionEnumsGeneric* opt = dynamic_cast<ConfigOptionEnumsGeneric*>(this->default_value->clone());
opt->keys_map = this->enum_keys_map;
return opt;
auto dft = this->default_value->clone();
if (dft->nullable()) {
ConfigOptionEnumsGenericNullable *opt = dynamic_cast<ConfigOptionEnumsGenericNullable *>(this->default_value->clone());
opt->keys_map = this->enum_keys_map;
return opt;
} else {
ConfigOptionEnumsGeneric *opt = dynamic_cast<ConfigOptionEnumsGeneric *>(this->default_value->clone());
opt->keys_map = this->enum_keys_map;
return opt;
}
}
return this->default_value->clone();
@ -743,6 +750,9 @@ ConfigSubstitutions ConfigBase::load_from_json(const std::string &file, ForwardC
int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContext& substitution_context, bool load_inherits_to_config, std::map<std::string, std::string>& key_values, std::string& reason)
{
json j;
std::list<std::string> different_settings_append;
std::string new_support_style;
bool is_project_settings = false;
try {
boost::nowide::ifstream ifs(file);
ifs >> j;
@ -762,6 +772,8 @@ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContex
}
else if (boost::iequals(it.key(), BBL_JSON_KEY_NAME)) {
key_values.emplace(BBL_JSON_KEY_NAME, it.value());
if (it.value() == "project_settings")
is_project_settings = true;
}
else if (boost::iequals(it.key(), BBL_JSON_KEY_URL)) {
key_values.emplace(BBL_JSON_KEY_URL, it.value());
@ -791,6 +803,15 @@ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContex
if (it.value().is_string()) {
//bool test1 = (it.key() == std::string("end_gcode"));
this->set_deserialize(opt_key, it.value(), substitution_context);
//some logic for special values
if (opt_key == "support_type") {
//std::string new_value = dynamic_cast<ConfigOptionString*>(this->option(opt_key))->value;
if (it.value() == "hybrid(auto)") {
different_settings_append.push_back(opt_key);
different_settings_append.push_back("support_style");
new_support_style = "tree_hybrid";
}
}
}
else if (it.value().is_array()) {
t_config_option_key opt_key_src = opt_key;
@ -856,6 +877,62 @@ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContex
}
}
}
if (!different_settings_append.empty()) {
if (!new_support_style.empty()) {
ConfigOptionEnum<SupportMaterialStyle>* opt = this->option<ConfigOptionEnum<SupportMaterialStyle>>("support_style", true);
opt->value = smsTreeHybrid;
}
if (is_project_settings) {
std::vector<std::string>& different_settings = this->option<ConfigOptionStrings>("different_settings_to_system", true)->values;
size_t size = different_settings.size();
if (size == 0) {
size = this->option<ConfigOptionStrings>("filament_settings_id")->values.size() + 2;
different_settings.resize(size);
}
std::vector<bool> is_first(size, false);
std::vector<std::vector<std::string>> original_diffs(size);
for (int index = 0; index < size; index++)
{
if (different_settings[index].empty()) {
is_first[index] = true;
}
else {
Slic3r::unescape_strings_cstyle(different_settings[index], original_diffs[index]);
}
}
for (auto diff_key : different_settings_append)
{
//get the index in the group
int index = 0;
bool need_insert = true;
if (diff_key == "support_type")
index = 0;
else if (diff_key == "support_style")
index = 0;
//check whether exist firstly
if (!original_diffs[index].empty()) {
for (int j = 0; j < original_diffs[index].size(); j++) {
if (original_diffs[index][j] == diff_key) {
need_insert = false;
break;
}
}
}
if (!need_insert)
continue;
//insert this key
if (!is_first[index])
different_settings[index] += ";";
else
is_first[index] = false;
different_settings[index] += diff_key;
}
}
}
return 0;
}
catch (const std::ifstream::failure &err) {

View file

@ -771,6 +771,8 @@ public:
ConfigOptionIntsTempl() : ConfigOptionVector<int>() {}
explicit ConfigOptionIntsTempl(size_t n, int value) : ConfigOptionVector<int>(n, value) {}
explicit ConfigOptionIntsTempl(std::initializer_list<int> il) : ConfigOptionVector<int>(std::move(il)) {}
explicit ConfigOptionIntsTempl(const std::vector<int> &vec) : ConfigOptionVector<int>(vec) {}
explicit ConfigOptionIntsTempl(std::vector<int> &&vec) : ConfigOptionVector<int>(std::move(vec)) {}
static ConfigOptionType static_type() { return coInts; }
ConfigOptionType type() const override { return static_type(); }
@ -1640,33 +1642,37 @@ private:
};
// BBS
class ConfigOptionEnumsGeneric : public ConfigOptionInts
template <bool NULLABLE>
class ConfigOptionEnumsGenericTempl : public ConfigOptionInts
{
public:
ConfigOptionEnumsGeneric(const t_config_enum_values* keys_map = nullptr) : keys_map(keys_map) {}
explicit ConfigOptionEnumsGeneric(const t_config_enum_values* keys_map, size_t size, int value) : ConfigOptionInts(size, value), keys_map(keys_map) {}
explicit ConfigOptionEnumsGeneric(std::initializer_list<int> il) : ConfigOptionInts(std::move(il)), keys_map(keys_map) {}
ConfigOptionEnumsGenericTempl(const t_config_enum_values *keys_map = nullptr) : keys_map(keys_map) {}
explicit ConfigOptionEnumsGenericTempl(const t_config_enum_values *keys_map, size_t size, int value) : ConfigOptionInts(size, value), keys_map(keys_map) {}
explicit ConfigOptionEnumsGenericTempl(std::initializer_list<int> il) : ConfigOptionInts(std::move(il)), keys_map(keys_map) {}
explicit ConfigOptionEnumsGenericTempl(const std::vector<int> &vec) : ConfigOptionInts(vec) {}
explicit ConfigOptionEnumsGenericTempl(std::vector<int> &&vec) : ConfigOptionInts(std::move(vec)) {}
const t_config_enum_values* keys_map = nullptr;
static ConfigOptionType static_type() { return coEnums; }
ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionEnumsGeneric(*this); }
ConfigOptionEnumsGeneric& operator= (const ConfigOption* opt) { this->set(opt); return *this; }
bool operator< (const ConfigOptionIntsTempl& rhs) const throw() { return this->values < rhs.values; }
ConfigOption* clone() const override { return new ConfigOptionEnumsGenericTempl(*this); }
ConfigOptionEnumsGenericTempl& operator= (const ConfigOption* opt) { this->set(opt); return *this; }
bool operator< (const ConfigOptionInts& rhs) const throw() { return this->values < rhs.values; }
bool operator==(const ConfigOptionIntsTempl& rhs) const throw()
bool operator==(const ConfigOptionInts& rhs) const throw()
{
if (rhs.type() != this->type())
throw ConfigurationError("ConfigOptionEnumsGeneric: Comparing incompatible types");
return this->values == rhs.values;
}
bool nullable() const override { return NULLABLE; }
void set(const ConfigOption* rhs) override {
if (rhs->type() != this->type())
throw ConfigurationError("ConfigOptionEnumGeneric: Assigning an incompatible type");
// rhs could be of the following type: ConfigOptionEnumsGeneric
this->values = dynamic_cast<const ConfigOptionEnumsGeneric*>(rhs)->values;
this->values = dynamic_cast<const ConfigOptionEnumsGenericTempl *>(rhs)->values;
}
std::string serialize() const override
@ -1701,7 +1707,10 @@ public:
while (std::getline(is, item_str, ',')) {
boost::trim(item_str);
if (item_str == "nil") {
return false;
if (NULLABLE)
this->values.push_back(nil_value());
else
throw ConfigurationError("Deserializing nil into a non-nullable object");
}
else {
auto it = this->keys_map->find(item_str);
@ -1716,15 +1725,26 @@ public:
private:
void serialize_single_value(std::ostringstream& ss, const int v) const
{
for (const auto& kvp : *this->keys_map)
if (kvp.second == v)
ss << kvp.first;
if (v == nil_value()) {
if (NULLABLE)
ss << "nil";
else
throw ConfigurationError("Serializing NaN");
}
else {
for (const auto& kvp : *this->keys_map)
if (kvp.second == v)
ss << kvp.first;
}
}
friend class cereal::access;
template<class Archive> void serialize(Archive& ar) { ar(cereal::base_class<ConfigOptionVector<int>>(this)); }
};
using ConfigOptionEnumsGeneric = ConfigOptionEnumsGenericTempl<false>;
using ConfigOptionEnumsGenericNullable = ConfigOptionEnumsGenericTempl<true>;
// Definition of a configuration value for the purpose of GUI presentation, editing, value mapping and config file handling.
class ConfigOptionDef
{

View file

@ -1367,61 +1367,61 @@ ModelVolumeType type_from_string(const std::string &s)
void _3MF_Importer::_extract_custom_gcode_per_print_z_from_archive(::mz_zip_archive &archive, const mz_zip_archive_file_stat &stat)
{
if (stat.m_uncomp_size > 0) {
std::string buffer((size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
if (res == 0) {
add_error("Error while reading custom Gcodes per height data to buffer");
return;
}
//if (stat.m_uncomp_size > 0) {
// std::string buffer((size_t)stat.m_uncomp_size, 0);
// mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
// if (res == 0) {
// add_error("Error while reading custom Gcodes per height data to buffer");
// return;
// }
std::istringstream iss(buffer); // wrap returned xml to istringstream
pt::ptree main_tree;
pt::read_xml(iss, main_tree);
// std::istringstream iss(buffer); // wrap returned xml to istringstream
// pt::ptree main_tree;
// pt::read_xml(iss, main_tree);
if (main_tree.front().first != "custom_gcodes_per_print_z")
return;
pt::ptree code_tree = main_tree.front().second;
// if (main_tree.front().first != "custom_gcodes_per_print_z")
// return;
// pt::ptree code_tree = main_tree.front().second;
m_model->custom_gcode_per_print_z.gcodes.clear();
// m_model->custom_gcode_per_print_z.gcodes.clear();
for (const auto& code : code_tree) {
if (code.first == "mode") {
pt::ptree tree = code.second;
std::string mode = tree.get<std::string>("<xmlattr>.value");
m_model->custom_gcode_per_print_z.mode = mode == CustomGCode::SingleExtruderMode ? CustomGCode::Mode::SingleExtruder :
mode == CustomGCode::MultiAsSingleMode ? CustomGCode::Mode::MultiAsSingle :
CustomGCode::Mode::MultiExtruder;
}
if (code.first != "code")
continue;
// for (const auto& code : code_tree) {
// if (code.first == "mode") {
// pt::ptree tree = code.second;
// std::string mode = tree.get<std::string>("<xmlattr>.value");
// m_model->custom_gcode_per_print_z.mode = mode == CustomGCode::SingleExtruderMode ? CustomGCode::Mode::SingleExtruder :
// mode == CustomGCode::MultiAsSingleMode ? CustomGCode::Mode::MultiAsSingle :
// CustomGCode::Mode::MultiExtruder;
// }
// if (code.first != "code")
// continue;
pt::ptree tree = code.second;
double print_z = tree.get<double> ("<xmlattr>.print_z" );
int extruder = tree.get<int> ("<xmlattr>.extruder");
std::string color = tree.get<std::string> ("<xmlattr>.color" );
// pt::ptree tree = code.second;
// double print_z = tree.get<double> ("<xmlattr>.print_z" );
// int extruder = tree.get<int> ("<xmlattr>.extruder");
// std::string color = tree.get<std::string> ("<xmlattr>.color" );
CustomGCode::Type type;
std::string extra;
pt::ptree attr_tree = tree.find("<xmlattr>")->second;
if (attr_tree.find("type") == attr_tree.not_found()) {
// It means that data was saved in old version (2.2.0 and older) of PrusaSlicer
// read old data ...
std::string gcode = tree.get<std::string> ("<xmlattr>.gcode");
// ... and interpret them to the new data
type = gcode == "M600" ? CustomGCode::ColorChange :
gcode == "M601" ? CustomGCode::PausePrint :
gcode == "tool_change" ? CustomGCode::ToolChange : CustomGCode::Custom;
extra = type == CustomGCode::PausePrint ? color :
type == CustomGCode::Custom ? gcode : "";
}
else {
type = static_cast<CustomGCode::Type>(tree.get<int>("<xmlattr>.type"));
extra = tree.get<std::string>("<xmlattr>.extra");
}
m_model->custom_gcode_per_print_z.gcodes.push_back(CustomGCode::Item{print_z, type, extruder, color, extra}) ;
}
}
// CustomGCode::Type type;
// std::string extra;
// pt::ptree attr_tree = tree.find("<xmlattr>")->second;
// if (attr_tree.find("type") == attr_tree.not_found()) {
// // It means that data was saved in old version (2.2.0 and older) of PrusaSlicer
// // read old data ...
// std::string gcode = tree.get<std::string> ("<xmlattr>.gcode");
// // ... and interpret them to the new data
// type = gcode == "M600" ? CustomGCode::ColorChange :
// gcode == "M601" ? CustomGCode::PausePrint :
// gcode == "tool_change" ? CustomGCode::ToolChange : CustomGCode::Custom;
// extra = type == CustomGCode::PausePrint ? color :
// type == CustomGCode::Custom ? gcode : "";
// }
// else {
// type = static_cast<CustomGCode::Type>(tree.get<int>("<xmlattr>.type"));
// extra = tree.get<std::string>("<xmlattr>.extra");
// }
// m_model->custom_gcode_per_print_z.gcodes.push_back(CustomGCode::Item{print_z, type, extruder, color, extra}) ;
// }
//}
}
void _3MF_Importer::_handle_start_model_xml_element(const char* name, const char** attributes)
@ -3182,54 +3182,55 @@ ModelVolumeType type_from_string(const std::string &s)
bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archive& archive, Model& model, const DynamicPrintConfig* config)
{
std::string out = "";
if (!model.custom_gcode_per_print_z.gcodes.empty()) {
pt::ptree tree;
pt::ptree& main_tree = tree.add("custom_gcodes_per_print_z", "");
for (const CustomGCode::Item& code : model.custom_gcode_per_print_z.gcodes) {
pt::ptree& code_tree = main_tree.add("code", "");
// store data of custom_gcode_per_print_z
code_tree.put("<xmlattr>.print_z" , code.print_z );
code_tree.put("<xmlattr>.type" , static_cast<int>(code.type));
code_tree.put("<xmlattr>.extruder" , code.extruder );
code_tree.put("<xmlattr>.color" , code.color );
code_tree.put("<xmlattr>.extra" , code.extra );
//BBS
std::string gcode = //code.type == CustomGCode::ColorChange ? config->opt_string("color_change_gcode") :
code.type == CustomGCode::PausePrint ? config->opt_string("machine_pause_gcode") :
code.type == CustomGCode::Template ? config->opt_string("template_custom_gcode") :
code.type == CustomGCode::ToolChange ? "tool_change" : code.extra;
code_tree.put("<xmlattr>.gcode" , gcode );
}
pt::ptree& mode_tree = main_tree.add("mode", "");
// store mode of a custom_gcode_per_print_z
mode_tree.put("<xmlattr>.value", model.custom_gcode_per_print_z.mode == CustomGCode::Mode::SingleExtruder ? CustomGCode::SingleExtruderMode :
model.custom_gcode_per_print_z.mode == CustomGCode::Mode::MultiAsSingle ? CustomGCode::MultiAsSingleMode :
CustomGCode::MultiExtruderMode);
if (!tree.empty()) {
std::ostringstream oss;
boost::property_tree::write_xml(oss, tree);
out = oss.str();
// Post processing("beautification") of the output string
boost::replace_all(out, "><", ">\n<");
}
}
if (!out.empty()) {
if (!mz_zip_writer_add_mem(&archive, CUSTOM_GCODE_PER_PRINT_Z_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
add_error("Unable to add custom Gcodes per print_z file to archive");
return false;
}
}
return true;
//std::string out = "";
//if (!model.custom_gcode_per_print_z.gcodes.empty()) {
// pt::ptree tree;
// pt::ptree& main_tree = tree.add("custom_gcodes_per_print_z", "");
// for (const CustomGCode::Item& code : model.custom_gcode_per_print_z.gcodes) {
// pt::ptree& code_tree = main_tree.add("code", "");
// // store data of custom_gcode_per_print_z
// code_tree.put("<xmlattr>.print_z" , code.print_z );
// code_tree.put("<xmlattr>.type" , static_cast<int>(code.type));
// code_tree.put("<xmlattr>.extruder" , code.extruder );
// code_tree.put("<xmlattr>.color" , code.color );
// code_tree.put("<xmlattr>.extra" , code.extra );
// //BBS
// std::string gcode = //code.type == CustomGCode::ColorChange ? config->opt_string("color_change_gcode") :
// code.type == CustomGCode::PausePrint ? config->opt_string("machine_pause_gcode") :
// code.type == CustomGCode::Template ? config->opt_string("template_custom_gcode") :
// code.type == CustomGCode::ToolChange ? "tool_change" : code.extra;
// code_tree.put("<xmlattr>.gcode" , gcode );
// }
// pt::ptree& mode_tree = main_tree.add("mode", "");
// // store mode of a custom_gcode_per_print_z
// mode_tree.put("<xmlattr>.value", model.custom_gcode_per_print_z.mode == CustomGCode::Mode::SingleExtruder ? CustomGCode::SingleExtruderMode :
// model.custom_gcode_per_print_z.mode == CustomGCode::Mode::MultiAsSingle ? CustomGCode::MultiAsSingleMode :
// CustomGCode::MultiExtruderMode);
// if (!tree.empty()) {
// std::ostringstream oss;
// boost::property_tree::write_xml(oss, tree);
// out = oss.str();
// // Post processing("beautification") of the output string
// boost::replace_all(out, "><", ">\n<");
// }
//}
//if (!out.empty()) {
// if (!mz_zip_writer_add_mem(&archive, CUSTOM_GCODE_PER_PRINT_Z_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
// add_error("Unable to add custom Gcodes per print_z file to archive");
// return false;
// }
//}
//return true;
}
// Perform conversions based on the config values available.

View file

@ -28,8 +28,8 @@
#include "TopExp_Explorer.hxx"
#include "BRep_Tool.hxx"
const double STEP_TRANS_CHORD_ERROR = 0.005;
const double STEP_TRANS_ANGLE_RES = 1;
const double STEP_TRANS_CHORD_ERROR = 0.0025;
const double STEP_TRANS_ANGLE_RES = 0.5;
namespace Slic3r {
@ -210,13 +210,15 @@ static void getNamedSolids(const TopLoc_Location& location, const std::string& p
}
}
bool load_step(const char *path, Model *model, ImportStepProgressFn stepFn, StepIsUtf8Fn isUtf8Fn)
bool load_step(const char *path, Model *model, bool& is_cancel, ImportStepProgressFn stepFn, StepIsUtf8Fn isUtf8Fn)
{
bool cb_cancel = false;
if (stepFn) {
stepFn(LOAD_STEP_STAGE_READ_FILE, 0, 1, cb_cancel);
if (cb_cancel)
is_cancel = cb_cancel;
if (cb_cancel) {
return false;
}
}
if (!StepPreProcessor::isUtf8File(path) && isUtf8Fn)
@ -248,6 +250,7 @@ bool load_step(const char *path, Model *model, ImportStepProgressFn stepFn, Step
if (stepFn) {
if ((iLabel % stage_unit2) == 0) {
stepFn(LOAD_STEP_STAGE_GET_SOLID, iLabel, topShapeLength, cb_cancel);
is_cancel = cb_cancel;
}
if (cb_cancel) {
shapeTool.reset(nullptr);
@ -345,6 +348,7 @@ bool load_step(const char *path, Model *model, ImportStepProgressFn stepFn, Step
if (stepFn) {
if ((i % stage_unit3) == 0) {
stepFn(LOAD_STEP_STAGE_GET_MESH, i, stl.size(), cb_cancel);
is_cancel = cb_cancel;
}
if (cb_cancel) {
model->delete_object(new_object);

View file

@ -17,7 +17,7 @@ typedef std::function<void(int load_stage, int current, int total, bool& cancel)
typedef std::function<void(bool isUtf8)> StepIsUtf8Fn;
//BBS: Load an step file into a provided model.
extern bool load_step(const char *path, Model *model, ImportStepProgressFn proFn = nullptr, StepIsUtf8Fn isUtf8Fn = nullptr);
extern bool load_step(const char *path, Model *model, bool& is_cancel, ImportStepProgressFn proFn = nullptr, StepIsUtf8Fn isUtf8Fn = nullptr);
//BBS: Used to detect what kind of encoded type is used in name field of step
// If is encoded in UTF8, the file don't need to be handled, then return the original path directly.

View file

@ -240,6 +240,7 @@ static constexpr const char* LAST_TRIANGLE_ID_ATTR = "lastid";
static constexpr const char* SUBTYPE_ATTR = "subtype";
static constexpr const char* LOCK_ATTR = "locked";
static constexpr const char* BED_TYPE_ATTR = "bed_type";
static constexpr const char* PRINT_SEQUENCE_ATTR = "print_sequence";
static constexpr const char* GCODE_FILE_ATTR = "gcode_file";
static constexpr const char* THUMBNAIL_FILE_ATTR = "thumbnail_file";
static constexpr const char* PATTERN_FILE_ATTR = "pattern_file";
@ -1776,6 +1777,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
plate_data_list[it->first-1]->pattern_file = (m_load_restore || it->second->pattern_file.empty()) ? it->second->pattern_file : m_backup_path + "/" + it->second->pattern_file;
plate_data_list[it->first-1]->pattern_bbox_file = (m_load_restore || it->second->pattern_bbox_file.empty()) ? it->second->pattern_bbox_file : m_backup_path + "/" + it->second->pattern_bbox_file;
plate_data_list[it->first-1]->config = it->second->config;
current_plate_data = plate_data_list[it->first - 1];
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", plate %1%, thumbnail_file=%2%")%it->first %plate_data_list[it->first-1]->thumbnail_file;
it++;
@ -2499,6 +2501,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
void _BBS_3MF_Importer::_extract_custom_gcode_per_print_z_from_archive(::mz_zip_archive &archive, const mz_zip_archive_file_stat &stat)
{
//BBS: add plate tree related logic
if (stat.m_uncomp_size > 0) {
std::string buffer((size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
@ -2513,45 +2516,71 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
if (main_tree.front().first != "custom_gcodes_per_layer")
return;
pt::ptree code_tree = main_tree.front().second;
m_model->custom_gcode_per_print_z.gcodes.clear();
auto extract_code = [this](int plate_id, pt::ptree code_tree) {
for (const auto& code : code_tree) {
if (code.first == "mode") {
pt::ptree tree = code.second;
std::string mode = tree.get<std::string>("<xmlattr>.value");
m_model->plates_custom_gcodes[plate_id - 1].mode = mode == CustomGCode::SingleExtruderMode ? CustomGCode::Mode::SingleExtruder :
mode == CustomGCode::MultiAsSingleMode ? CustomGCode::Mode::MultiAsSingle :
CustomGCode::Mode::MultiExtruder;
}
if (code.first == "layer") {
pt::ptree tree = code.second;
double print_z = tree.get<double>("<xmlattr>.top_z");
int extruder = tree.get<int>("<xmlattr>.extruder");
std::string color = tree.get<std::string>("<xmlattr>.color");
for (const auto& code : code_tree) {
if (code.first == "mode") {
pt::ptree tree = code.second;
std::string mode = tree.get<std::string>("<xmlattr>.value");
m_model->custom_gcode_per_print_z.mode = mode == CustomGCode::SingleExtruderMode ? CustomGCode::Mode::SingleExtruder :
mode == CustomGCode::MultiAsSingleMode ? CustomGCode::Mode::MultiAsSingle :
CustomGCode::Mode::MultiExtruder;
CustomGCode::Type type;
std::string extra;
pt::ptree attr_tree = tree.find("<xmlattr>")->second;
if (attr_tree.find("type") == attr_tree.not_found()) {
// It means that data was saved in old version (2.2.0 and older) of PrusaSlicer
// read old data ...
std::string gcode = tree.get<std::string>("<xmlattr>.gcode");
// ... and interpret them to the new data
type = gcode == "M600" ? CustomGCode::ColorChange :
gcode == "M601" ? CustomGCode::PausePrint :
gcode == "tool_change" ? CustomGCode::ToolChange : CustomGCode::Custom;
extra = type == CustomGCode::PausePrint ? color :
type == CustomGCode::Custom ? gcode : "";
}
else {
type = static_cast<CustomGCode::Type>(tree.get<int>("<xmlattr>.type"));
extra = tree.get<std::string>("<xmlattr>.extra");
}
m_model->plates_custom_gcodes[plate_id - 1].gcodes.push_back(CustomGCode::Item{ print_z, type, extruder, color, extra });
}
}
if (code.first != "layer")
continue;
};
pt::ptree tree = code.second;
double print_z = tree.get<double> ("<xmlattr>.top_z" );
int extruder = tree.get<int> ("<xmlattr>.extruder");
std::string color = tree.get<std::string> ("<xmlattr>.color" );
m_model->plates_custom_gcodes.clear();
CustomGCode::Type type;
std::string extra;
pt::ptree attr_tree = tree.find("<xmlattr>")->second;
if (attr_tree.find("type") == attr_tree.not_found()) {
// It means that data was saved in old version (2.2.0 and older) of PrusaSlicer
// read old data ...
std::string gcode = tree.get<std::string> ("<xmlattr>.gcode");
// ... and interpret them to the new data
type = gcode == "M600" ? CustomGCode::ColorChange :
gcode == "M601" ? CustomGCode::PausePrint :
gcode == "tool_change" ? CustomGCode::ToolChange : CustomGCode::Custom;
extra = type == CustomGCode::PausePrint ? color :
type == CustomGCode::Custom ? gcode : "";
bool has_plate_info = false;
for (const auto& element : main_tree.front().second) {
if (element.first == "plate") {
has_plate_info = true;
int plate_id = -1;
pt::ptree code_tree = element.second;
for (const auto& code : code_tree) {
if (code.first == "plate_info") {
plate_id = code.second.get<int>("<xmlattr>.id");
}
}
if (plate_id == -1)
continue;
extract_code(plate_id, code_tree);
}
else {
type = static_cast<CustomGCode::Type>(tree.get<int>("<xmlattr>.type"));
extra = tree.get<std::string>("<xmlattr>.extra");
}
m_model->custom_gcode_per_print_z.gcodes.push_back(CustomGCode::Item{print_z, type, extruder, color, extra}) ;
}
if (!has_plate_info) {
int plate_id = 1;
pt::ptree code_tree = main_tree.front().second;
extract_code(plate_id, code_tree);
}
}
}
@ -3444,6 +3473,12 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
ConfigOptionEnum<BedType>::from_string(value, bed_type);
m_curr_plater->config.set_key_value("curr_bed_type", new ConfigOptionEnum<BedType>(bed_type));
}
else if (key == PRINT_SEQUENCE_ATTR)
{
PrintSequence print_sequence = PrintSequence::ByLayer;
ConfigOptionEnum<PrintSequence>::from_string(value, print_sequence);
m_curr_plater->config.set_key_value("print_sequence", new ConfigOptionEnum<PrintSequence>(print_sequence));
}
else if (key == GCODE_FILE_ATTR)
{
m_curr_plater->gcode_file = value;
@ -3805,6 +3840,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
//add the shared mesh logic
shared_mesh_id = ::atoi(metadata.value.c_str());
found_count++;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": line %1%, shared_mesh_id %2%")%__LINE__%shared_mesh_id;
}
if (found_count >= 2)
@ -3822,6 +3858,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
std::map<int, ModelVolume*>::iterator iter = m_shared_meshes.find(shared_mesh_id);
if (iter != m_shared_meshes.end()) {
shared_volume = iter->second;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": line %1%, found shared mesh, id %2%, mesh %3%")%__LINE__%shared_mesh_id%shared_volume;
}
}
@ -3874,11 +3911,17 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
volume = object.add_volume(std::move(triangle_mesh));
m_shared_meshes[sub_object->id] = volume;
if (shared_mesh_id != -1)
//for some cases the shared mesh is in other plate and not loaded in cli slicing
//we need to use the first one in the same plate instead
m_shared_meshes[shared_mesh_id] = volume;
else
m_shared_meshes[sub_object->id] = volume;
}
else {
//create volume to use shared mesh
volume = object.add_volume_with_shared_mesh(*shared_volume);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": line %1%, create volume using shared_mesh %2%")%__LINE__%shared_volume;
}
// stores the volume matrix taken from the metadata, if present
if (has_transform)
@ -5347,6 +5390,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
bool sub_model = !objects_data.empty();
bool write_object = sub_model || !m_split_model;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", filename %1%, m_split_model %2%, sub_model %3%")%filename % m_split_model % sub_model;
#if WRITE_ZIP_LANGUAGE_ENCODING
auto & zip_filename = filename;
#else
@ -6388,6 +6433,11 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
if (bed_type_opt != nullptr && bed_type_names.size() > bed_type_opt->getInt())
stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << BED_TYPE_ATTR << "\" " << VALUE_ATTR << "=\"" << bed_type_names[bed_type_opt->getInt()] << "\"/>\n";
ConfigOption* print_sequence_opt = plate_data->config.option("print_sequence");
t_config_enum_names print_sequence_names = ConfigOptionEnum<PrintSequence>::get_enum_names();
if (print_sequence_opt != nullptr && print_sequence_names.size() > print_sequence_opt->getInt())
stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PRINT_SEQUENCE_ATTR << "\" " << VALUE_ATTR << "=\"" << print_sequence_names[print_sequence_opt->getInt()] << "\"/>\n";
if (save_gcode)
stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << GCODE_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << xml_escape(plate_data->gcode_file) << "\"/>\n";
if (!plate_data->gcode_file.empty()) {
@ -6621,46 +6671,50 @@ bool _BBS_3MF_Exporter::_add_gcode_file_to_archive(mz_zip_archive& archive, cons
return result;
}
bool _BBS_3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archive& archive, Model& model, const DynamicPrintConfig* config)
bool _BBS_3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model, const DynamicPrintConfig* config)
{
//BBS: add plate tree related logic
std::string out = "";
if (!model.custom_gcode_per_print_z.gcodes.empty()) {
pt::ptree tree;
pt::ptree& main_tree = tree.add("custom_gcodes_per_layer", "");
for (const CustomGCode::Item& code : model.custom_gcode_per_print_z.gcodes) {
pt::ptree& code_tree = main_tree.add("layer", "");
bool has_custom_gcode = false;
pt::ptree tree;
pt::ptree& main_tree = tree.add("custom_gcodes_per_layer", "");
for (auto custom_gcodes : model.plates_custom_gcodes) {
has_custom_gcode = true;
pt::ptree& plate_tree = main_tree.add("plate", "");
pt::ptree& plate_idx_tree = plate_tree.add("plate_info", "");
plate_idx_tree.put("<xmlattr>.id", custom_gcodes.first + 1);
// store data of custom_gcode_per_print_z
code_tree.put("<xmlattr>.top_z" , code.print_z );
code_tree.put("<xmlattr>.type" , static_cast<int>(code.type));
code_tree.put("<xmlattr>.extruder" , code.extruder );
code_tree.put("<xmlattr>.color" , code.color );
code_tree.put("<xmlattr>.extra" , code.extra );
for (const CustomGCode::Item& code : custom_gcodes.second.gcodes) {
pt::ptree& code_tree = plate_tree.add("layer", "");
code_tree.put("<xmlattr>.top_z", code.print_z);
code_tree.put("<xmlattr>.type", static_cast<int>(code.type));
code_tree.put("<xmlattr>.extruder", code.extruder);
code_tree.put("<xmlattr>.color", code.color);
code_tree.put("<xmlattr>.extra", code.extra);
//BBS
std::string gcode = //code.type == CustomGCode::ColorChange ? config->opt_string("color_change_gcode") :
code.type == CustomGCode::PausePrint ? config->opt_string("machine_pause_gcode") :
code.type == CustomGCode::Template ? config->opt_string("template_custom_gcode") :
code.type == CustomGCode::ToolChange ? "tool_change" : code.extra;
code_tree.put("<xmlattr>.gcode" , gcode );
}
//BBS
std::string gcode = //code.type == CustomGCode::ColorChange ? config->opt_string("color_change_gcode") :
code.type == CustomGCode::PausePrint ? config->opt_string("machine_pause_gcode") :
code.type == CustomGCode::Template ? config->opt_string("template_custom_gcode") :
code.type == CustomGCode::ToolChange ? "tool_change" : code.extra;
code_tree.put("<xmlattr>.gcode", gcode);
}
pt::ptree& mode_tree = main_tree.add("mode", "");
// store mode of a custom_gcode_per_print_z
mode_tree.put("<xmlattr>.value", model.custom_gcode_per_print_z.mode == CustomGCode::Mode::SingleExtruder ? CustomGCode::SingleExtruderMode :
model.custom_gcode_per_print_z.mode == CustomGCode::Mode::MultiAsSingle ? CustomGCode::MultiAsSingleMode :
CustomGCode::MultiExtruderMode);
pt::ptree& mode_tree = plate_tree.add("mode", "");
// store mode of a custom_gcode_per_print_z
mode_tree.put("<xmlattr>.value", custom_gcodes.second.mode == CustomGCode::Mode::SingleExtruder ? CustomGCode::SingleExtruderMode :
custom_gcodes.second.mode == CustomGCode::Mode::MultiAsSingle ? CustomGCode::MultiAsSingleMode :
CustomGCode::MultiExtruderMode);
if (!tree.empty()) {
std::ostringstream oss;
boost::property_tree::write_xml(oss, tree);
out = oss.str();
}
if (has_custom_gcode) {
std::ostringstream oss;
boost::property_tree::write_xml(oss, tree);
out = oss.str();
// Post processing("beautification") of the output string
boost::replace_all(out, "><", ">\n<");
}
// Post processing("beautification") of the output string
boost::replace_all(out, "><", ">\n<");
}
if (!out.empty()) {

View file

@ -22,6 +22,9 @@ struct ThumbnailData;
#define EMBEDDED_FILAMENT_FILE_FORMAT "Metadata/filament_settings_%1%.config"
#define EMBEDDED_PRINTER_FILE_FORMAT "Metadata/machine_settings_%1%.config"
#define BBL_DESIGNER_PROFILE_ID_TAG "DesignProfileId"
#define BBL_DESIGNER_MODEL_ID_TAG "DesignModelId"
//BBS: define assistant struct to store temporary variable during exporting 3mf
class PackingTemporaryData

View file

@ -82,6 +82,132 @@ static const int g_max_flush_count = 4;
bool GCode::gcode_label_objects = true;
Vec2d travel_point_1;
Vec2d travel_point_2;
Vec2d travel_point_3;
static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
{
// give safe value in case there is no start_end_points in config
std::vector<Vec2d> out_points;
out_points.emplace_back(Vec2d(54, 0));
out_points.emplace_back(Vec2d(54, 0));
out_points.emplace_back(Vec2d(54, 245));
// get the start_end_points from config (20, -3) (54, 245)
Pointfs points = print.config().start_end_points.values;
if (points.size() != 2)
return out_points;
Vec2d start_point = points[0];
Vec2d end_point = points[1];
// the cutter area size(18, 28)
Pointfs excluse_area = print.config().bed_exclude_area.values;
if (excluse_area.size() != 4)
return out_points;
double cutter_area_x = excluse_area[2].x() + 2;
double cutter_area_y = excluse_area[2].y() + 2;
double start_x_position = start_point.x();
double end_x_position = end_point.x();
double end_y_position = end_point.y();
bool can_travel_form_left = true;
// step 1: get the x-range intervals of all objects
std::vector<std::pair<double, double>> object_intervals;
for (PrintObject *print_object : print.objects()) {
const PrintInstances &print_instances = print_object->instances();
BoundingBoxf3 bounding_box = print_instances[0].model_instance->get_object()->bounding_box();
if (bounding_box.min.x() < start_x_position && bounding_box.min.y() < cutter_area_y)
can_travel_form_left = false;
std::pair<double, double> object_scope = std::make_pair(bounding_box.min.x() - 2, bounding_box.max.x() + 2);
if (object_intervals.empty())
object_intervals.push_back(object_scope);
else {
std::vector<std::pair<double, double>> new_object_intervals;
bool intervals_intersect = false;
std::pair<double, double> new_merged_scope;
for (auto object_interval : object_intervals) {
if (object_interval.second >= object_scope.first && object_interval.first <= object_scope.second) {
if (intervals_intersect) {
new_merged_scope = std::make_pair(std::min(object_interval.first, new_merged_scope.first), std::max(object_interval.second, new_merged_scope.second));
} else { // it is the first intersection
new_merged_scope = std::make_pair(std::min(object_interval.first, object_scope.first), std::max(object_interval.second, object_scope.second));
}
intervals_intersect = true;
} else {
new_object_intervals.push_back(object_interval);
}
}
if (intervals_intersect) {
new_object_intervals.push_back(new_merged_scope);
object_intervals = new_object_intervals;
} else
object_intervals.push_back(object_scope);
}
}
// step 2: get the available x-range
std::sort(object_intervals.begin(), object_intervals.end(),
[](const std::pair<double, double> &left, const std::pair<double, double> &right) {
return left.first < right.first;
});
std::vector<std::pair<double, double>> available_intervals;
double start_position = 0;
for (auto object_interval : object_intervals) {
if (object_interval.first > start_position)
available_intervals.push_back(std::make_pair(start_position, object_interval.first));
start_position = object_interval.second;
}
available_intervals.push_back(std::make_pair(start_position, 255));
// step 3: get the nearest path
double new_path = 255;
for (auto available_interval : available_intervals) {
if (available_interval.first > end_x_position) {
double distance = available_interval.first - end_x_position;
new_path = abs(end_x_position - new_path) < distance ? new_path : available_interval.first;
break;
} else {
if (available_interval.second >= end_x_position) {
new_path = end_x_position;
break;
} else if (!can_travel_form_left && available_interval.second < start_x_position) {
continue;
} else {
new_path = available_interval.second;
}
}
}
// step 4: generate path points (new_path == start_x_position means not need to change path)
Vec2d out_point_1;
Vec2d out_point_2;
Vec2d out_point_3;
if (new_path < start_x_position) {
out_point_1 = Vec2d(start_x_position, cutter_area_y);
out_point_2 = Vec2d(new_path, cutter_area_y);
out_point_3 = Vec2d(new_path, end_y_position);
} else {
out_point_1 = Vec2d(new_path, 0);
out_point_2 = Vec2d(new_path, 0);
out_point_3 = Vec2d(new_path, end_y_position);
}
out_points.clear();
out_points.emplace_back(out_point_1);
out_points.emplace_back(out_point_2);
out_points.emplace_back(out_point_3);
return out_points;
}
// Only add a newline in case the current G-code does not end with a newline.
static inline void check_add_eol(std::string& gcode)
{
@ -378,6 +504,12 @@ bool GCode::gcode_label_objects = true;
config.set_key_value("second_flush_volume", new ConfigOptionFloat(purge_length / 2.f));
config.set_key_value("old_filament_e_feedrate", new ConfigOptionInt(old_filament_e_feedrate));
config.set_key_value("new_filament_e_feedrate", new ConfigOptionInt(new_filament_e_feedrate));
config.set_key_value("travel_point_1_x", new ConfigOptionFloat(float(travel_point_1.x())));
config.set_key_value("travel_point_1_y", new ConfigOptionFloat(float(travel_point_1.y())));
config.set_key_value("travel_point_2_x", new ConfigOptionFloat(float(travel_point_2.x())));
config.set_key_value("travel_point_2_y", new ConfigOptionFloat(float(travel_point_2.y())));
config.set_key_value("travel_point_3_x", new ConfigOptionFloat(float(travel_point_3.x())));
config.set_key_value("travel_point_3_y", new ConfigOptionFloat(float(travel_point_3.y())));
int flush_count = std::min(g_max_flush_count, (int)std::round(purge_volume / g_purge_volume_one_time));
float flush_unit = purge_length / flush_count;
@ -624,7 +756,7 @@ bool GCode::gcode_label_objects = true;
std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObject& object)
{
std::vector<GCode::LayerToPrint> layers_to_print;
layers_to_print.reserve(object.layers().size() + object.support_layers().size() + object.tree_support_layers().size());
layers_to_print.reserve(object.layers().size() + object.support_layers().size());
/*
// Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um.
@ -647,9 +779,8 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
// Pair the object layers with the support layers by z.
size_t idx_object_layer = 0;
size_t idx_support_layer = 0;
size_t idx_tree_support_layer = 0;
const LayerToPrint* last_extrusion_layer = nullptr;
while (idx_object_layer < object.layers().size() || idx_support_layer < object.support_layers().size() || idx_tree_support_layer < object.tree_support_layers().size()) {
while (idx_object_layer < object.layers().size() || idx_support_layer < object.support_layers().size()) {
LayerToPrint layer_to_print;
double print_z_min = std::numeric_limits<double>::max();
if (idx_object_layer < object.layers().size()) {
@ -662,11 +793,6 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
print_z_min = std::min(print_z_min, layer_to_print.support_layer->print_z);
}
if (idx_tree_support_layer < object.tree_support_layers().size()) {
layer_to_print.tree_support_layer = object.tree_support_layers()[idx_tree_support_layer++];
print_z_min = std::min(print_z_min, layer_to_print.tree_support_layer->print_z);
}
if (layer_to_print.object_layer && layer_to_print.object_layer->print_z > print_z_min + EPSILON) {
layer_to_print.object_layer = nullptr;
--idx_object_layer;
@ -677,17 +803,11 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
--idx_support_layer;
}
if (layer_to_print.tree_support_layer && layer_to_print.tree_support_layer->print_z > print_z_min + EPSILON) {
layer_to_print.tree_support_layer = nullptr;
--idx_tree_support_layer;
}
layer_to_print.original_object = &object;
layers_to_print.push_back(layer_to_print);
bool has_extrusions = (layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions())
|| (layer_to_print.support_layer && layer_to_print.support_layer->has_extrusions()
|| (layer_to_print.tree_support_layer && layer_to_print.tree_support_layer->has_extrusions()));
|| (layer_to_print.support_layer && layer_to_print.support_layer->has_extrusions());
// Check that there are extrusions on the very first layer. The case with empty
// first layer may result in skirt/brim in the air and maybe other issues.
@ -699,13 +819,12 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
// In case there are extrusions on this layer, check there is a layer to lay it on.
if ((layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions())
// Allow empty support layers, as the support generator may produce no extrusions for non-empty support regions.
|| (layer_to_print.support_layer /* && layer_to_print.support_layer->has_extrusions() */)
|| (layer_to_print.tree_support_layer)) {
|| (layer_to_print.support_layer /* && layer_to_print.support_layer->has_extrusions() */)) {
double top_cd = object.config().support_top_z_distance;
//double bottom_cd = object.config().support_bottom_z_distance == 0. ? top_cd : object.config().support_bottom_z_distance;
double bottom_cd = top_cd;
double extra_gap = ((layer_to_print.support_layer || layer_to_print.tree_support_layer) ? bottom_cd : top_cd);
double extra_gap = (layer_to_print.support_layer ? bottom_cd : top_cd);
double maximal_print_z = (last_extrusion_layer ? last_extrusion_layer->print_z() : 0.)
+ layer_to_print.layer()->height
@ -914,6 +1033,9 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
{
PROFILE_CLEAR();
// BBS
m_curr_print = print;
CNumericLocalesSetter locales_setter;
// Does the file exist? If so, we hope that it is still valid.
@ -1333,7 +1455,14 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
for (auto layer : object->support_layers())
zs.push_back((coord_t)(layer->print_z / EPSILON));
std::sort(zs.begin(), zs.end());
m_layer_count += (unsigned int)(object->instances().size() * (std::unique(zs.begin(), zs.end()) - zs.begin()));
//BBS: merge numerically very close Z values.
auto end_it = std::unique(zs.begin(), zs.end());
unsigned int temp_layer_count = (unsigned int)(end_it - zs.begin());
for (auto it = zs.begin(); it != end_it - 1; it++) {
if (abs(*it - *(it + 1)) < EPSILON)
temp_layer_count--;
}
m_layer_count += (unsigned int)(object->instances().size() * temp_layer_count);
}
} else {
// Print all objects with the same print_z together.
@ -1347,7 +1476,13 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
zs.push_back((coord_t)(layer->print_z / EPSILON));
}
std::sort(zs.begin(), zs.end());
m_layer_count = (unsigned int)(std::unique(zs.begin(), zs.end()) - zs.begin());
//BBS: merge numerically very close Z values.
auto end_it = std::unique(zs.begin(), zs.end());
m_layer_count = (unsigned int)(end_it - zs.begin());
for (auto it = zs.begin(); it != end_it - 1; it++) {
if (abs(*it - *(it + 1)) < EPSILON)
m_layer_count--;
}
}
print.throw_if_canceled();
@ -1374,6 +1509,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
//BBS: total estimated printing time
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str());
//BBS: total layer number
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Total_Layer_Number_Placeholder).c_str());
file.write_format("; total layer number: %d\n", m_layer_count);
file.write_format("; HEADER_BLOCK_END\n\n");
@ -1450,6 +1586,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
// For a print by objects, find the 1st printing object.
ToolOrdering tool_ordering;
unsigned int initial_extruder_id = (unsigned int)-1;
//BBS: first non-support filament extruder
unsigned int initial_non_support_extruder_id;
unsigned int final_extruder_id = (unsigned int)-1;
bool has_wipe_tower = false;
std::vector<const PrintInstance*> print_object_instances_ordering;
@ -1462,8 +1600,33 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
print_object_instance_sequential_active = print_object_instances_ordering.begin();
for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++ print_object_instance_sequential_active) {
tool_ordering = ToolOrdering(*(*print_object_instance_sequential_active)->print_object, initial_extruder_id);
if ((initial_extruder_id = tool_ordering.first_extruder()) != static_cast<unsigned int>(-1))
if ((initial_extruder_id = tool_ordering.first_extruder()) != static_cast<unsigned int>(-1)) {
//BBS: try to find the non-support filament extruder if is multi color and initial_extruder is support filament
initial_non_support_extruder_id = initial_extruder_id;
if (tool_ordering.all_extruders().size() > 1 && print.config().filament_is_support.get_at(initial_extruder_id)) {
bool has_non_support_filament = false;
for (unsigned int extruder : tool_ordering.all_extruders()) {
if (!print.config().filament_is_support.get_at(extruder)) {
has_non_support_filament = true;
break;
}
}
//BBS: find the non-support filament extruder of object
if (has_non_support_filament)
for (LayerTools layer_tools : tool_ordering.layer_tools()) {
if (!layer_tools.has_object)
continue;
for (unsigned int extruder : layer_tools.extruders) {
if (print.config().filament_is_support.get_at(extruder))
continue;
initial_non_support_extruder_id = extruder;
break;
}
}
}
break;
}
}
if (initial_extruder_id == static_cast<unsigned int>(-1))
// No object to print was found, cancel the G-code export.
@ -1471,6 +1634,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
// We don't allow switching of extruders per layer by Model::custom_gcode_per_print_z in sequential mode.
// Use the extruder IDs collected from Regions.
this->set_extruders(print.extruders());
has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower();
} else {
// Find tool ordering for all the objects at once, and the initial extruder ID.
// If the tool ordering has been pre-calculated by Print class for wipe tower already, reuse it.
@ -1490,6 +1655,32 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
#else
initial_extruder_id = tool_ordering.first_extruder();
#endif
//BBS: try to find the non-support filament extruder if is multi color and initial_extruder is support filament
if (initial_extruder_id != static_cast<unsigned int>(-1)) {
initial_non_support_extruder_id = initial_extruder_id;
if (tool_ordering.all_extruders().size() > 1 && print.config().filament_is_support.get_at(initial_extruder_id)) {
bool has_non_support_filament = false;
for (unsigned int extruder : tool_ordering.all_extruders()) {
if (!print.config().filament_is_support.get_at(extruder)) {
has_non_support_filament = true;
break;
}
}
//BBS: find the non-support filament extruder of object
if (has_non_support_filament)
for (LayerTools layer_tools : tool_ordering.layer_tools()) {
if (!layer_tools.has_object)
continue;
for (unsigned int extruder : layer_tools.extruders) {
if (print.config().filament_is_support.get_at(extruder))
continue;
initial_non_support_extruder_id = extruder;
break;
}
}
}
}
// In non-sequential print, the printing extruders may have been modified by the extruder switches stored in Model::custom_gcode_per_print_z.
// Therefore initialize the printing extruders from there.
this->set_extruders(tool_ordering.all_extruders());
@ -1499,6 +1690,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
if (initial_extruder_id == (unsigned int)-1) {
// Nothing to print!
initial_extruder_id = 0;
initial_non_support_extruder_id = 0;
final_extruder_id = 0;
} else {
final_extruder_id = tool_ordering.last_extruder();
@ -1522,6 +1714,9 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
// Let the start-up script prime the 1st printing tool.
m_placeholder_parser.set("initial_tool", initial_extruder_id);
m_placeholder_parser.set("initial_extruder", initial_extruder_id);
//BBS
m_placeholder_parser.set("initial_no_support_tool", initial_non_support_extruder_id);
m_placeholder_parser.set("initial_no_support_extruder", initial_non_support_extruder_id);
m_placeholder_parser.set("current_extruder", initial_extruder_id);
//Set variable for total layer count so it can be used in custom gcode.
m_placeholder_parser.set("total_layer_count", m_layer_count);
@ -1531,10 +1726,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
m_placeholder_parser.set("has_wipe_tower", has_wipe_tower);
//m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming);
m_placeholder_parser.set("total_toolchanges", std::max(0, print.wipe_tower_data().number_of_toolchanges)); // Check for negative toolchanges (single extruder mode) and set to 0 (no tool change).
Vec2f plate_offset = m_writer.get_xy_offset();
{
BoundingBoxf bbox(print.config().printable_area.values);
m_placeholder_parser.set("print_bed_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() }));
m_placeholder_parser.set("print_bed_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() }));
m_placeholder_parser.set("print_bed_min", new ConfigOptionFloats({ bbox.min.x() - plate_offset.x(), bbox.min.y() - plate_offset.y() }));
m_placeholder_parser.set("print_bed_max", new ConfigOptionFloats({ bbox.max.x() - plate_offset.x(), bbox.max.y() - plate_offset.y() }));
m_placeholder_parser.set("print_bed_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() }));
}
{
@ -1549,8 +1745,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
pts->values.emplace_back(unscale(pt) - Vec2d(print.get_plate_origin().x(), print.get_plate_origin().y()));
BoundingBoxf bbox(pts->values);
m_placeholder_parser.set("first_layer_print_convex_hull", pts.release());
m_placeholder_parser.set("first_layer_print_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() }));
m_placeholder_parser.set("first_layer_print_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() }));
m_placeholder_parser.set("first_layer_print_min", new ConfigOptionFloats({bbox.min.x() - plate_offset.x(), bbox.min.y() - plate_offset.y()}));
m_placeholder_parser.set("first_layer_print_max", new ConfigOptionFloats({bbox.max.x() - plate_offset.x(), bbox.max.y() - plate_offset.y()}));
m_placeholder_parser.set("first_layer_print_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() }));
}
float outer_wall_volumetric_speed = 0.0f;
@ -1575,13 +1771,13 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
//BBS: calculate the volumetric speed of outer wall. Ignore pre-object setting and multi-filament, and just use the default setting
{
float filament_max_volumetric_speed = m_config.option<ConfigOptionFloats>("filament_max_volumetric_speed")->get_at(initial_extruder_id);
float filament_max_volumetric_speed = m_config.option<ConfigOptionFloats>("filament_max_volumetric_speed")->get_at(initial_non_support_extruder_id);
float outer_wall_line_width = print.default_region_config().outer_wall_line_width.value;
if (outer_wall_line_width == 0.0) {
float default_line_width = print.default_object_config().line_width.value;
outer_wall_line_width = default_line_width == 0.0 ? m_config.nozzle_diameter.get_at(initial_extruder_id) : default_line_width;
outer_wall_line_width = default_line_width == 0.0 ? m_config.nozzle_diameter.get_at(initial_non_support_extruder_id) : default_line_width;
}
Flow outer_wall_flow = Flow(outer_wall_line_width, m_config.layer_height, m_config.nozzle_diameter.get_at(initial_extruder_id));
Flow outer_wall_flow = Flow(outer_wall_line_width, m_config.layer_height, m_config.nozzle_diameter.get_at(initial_non_support_extruder_id));
float outer_wall_speed = print.default_region_config().outer_wall_speed.value;
outer_wall_volumetric_speed = outer_wall_speed * outer_wall_flow.mm3_per_mm();
if (outer_wall_volumetric_speed > filament_max_volumetric_speed)
@ -1637,7 +1833,17 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
std::function<void(void)> throw_if_canceled_func = [&print]() { print.throw_if_canceled(); };
m_seam_placer.init(print, throw_if_canceled_func);
// BBS: priming logic is removed, always set first extruder here.
// BBS: get path for change filament
if (m_writer.multiple_extruders) {
std::vector<Vec2d> points = get_path_of_change_filament(print);
if (points.size() == 3) {
travel_point_1 = points[0];
travel_point_2 = points[1];
travel_point_3 = points[2];
}
}
// BBS: priming logic is removed, always set first extruer here.
//if (! (has_wipe_tower && print.config().single_extruder_multi_material_priming))
{
// Set initial extruder only after custom start G-code.
@ -1676,8 +1882,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
gcode += pa_test.generate_test(params.start, params.step, std::llround(std::ceil((params.end - params.start) / params.step)));
file.write(gcode);
}
else {
} else {
//BBS: open spaghetti detector
if (is_bbl_printers) {
// if (print.config().spaghetti_detector.value)
@ -1685,11 +1890,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
}
// Do all objects for each layer.
if (print.config().print_sequence == PrintSequence::ByObject) {
if (print.config().print_sequence == PrintSequence::ByObject && !has_wipe_tower) {
size_t finished_objects = 0;
const PrintObject* prev_object = (*print_object_instance_sequential_active)->print_object;
for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++print_object_instance_sequential_active) {
const PrintObject& object = *(*print_object_instance_sequential_active)->print_object;
const PrintObject *prev_object = (*print_object_instance_sequential_active)->print_object;
for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++ print_object_instance_sequential_active) {
const PrintObject &object = *(*print_object_instance_sequential_active)->print_object;
if (&object != prev_object || tool_ordering.first_extruder() != final_extruder_id) {
tool_ordering = ToolOrdering(object, final_extruder_id);
unsigned int new_extruder_id = tool_ordering.first_extruder();
@ -1697,7 +1902,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
// Skip this object.
continue;
initial_extruder_id = new_extruder_id;
final_extruder_id = tool_ordering.last_extruder();
final_extruder_id = tool_ordering.last_extruder();
assert(final_extruder_id != (unsigned int)-1);
}
print.throw_if_canceled();
@ -1751,34 +1956,33 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
file.write("M1003 S0\n");
}
}
#ifdef HAS_PRESSURE_EQUALIZER
#ifdef HAS_PRESSURE_EQUALIZER
if (m_pressure_equalizer)
file.write(m_pressure_equalizer->process("", true));
#endif /* HAS_PRESSURE_EQUALIZER */
++finished_objects;
#endif /* HAS_PRESSURE_EQUALIZER */
++ finished_objects;
// Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed.
// Reset it when starting another object from 1st layer.
m_second_layer_things_done = false;
prev_object = &object;
}
}
else {
} else {
// Sort layers by Z.
// All extrusion moves with the same top layer height are extruded uninterrupted.
std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print = collect_layers_to_print(print);
// Prusa Multi-Material wipe tower.
if (has_wipe_tower && !layers_to_print.empty()) {
m_wipe_tower.reset(new WipeTowerIntegration(print.config(), print.get_plate_index(), print.get_plate_origin(), *print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get()));
if (has_wipe_tower && ! layers_to_print.empty()) {
m_wipe_tower.reset(new WipeTowerIntegration(print.config(), print.get_plate_index(), print.get_plate_origin(), * print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get()));
//BBS
//file.write(m_writer.travel_to_z(initial_layer_print_height + m_config.z_offset.value, "Move to the first layer height"));
file.write(m_writer.travel_to_z(initial_layer_print_height, "Move to the first layer height"));
#if 0
#if 0
if (print.config().single_extruder_multi_material_priming) {
file.write(m_wipe_tower->prime(*this));
// Verify, whether the print overaps the priming extrusions.
BoundingBoxf bbox_print(get_print_extrusions_extents(print));
coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON;
for (const PrintObject* print_object : print.objects())
for (const PrintObject *print_object : print.objects())
bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz));
bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz));
BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
@ -1791,8 +1995,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
if (overlap) {
// Wait for the user to remove the priming extrusions.
file.write("M1 Remove priming towers and click button.\n");
}
else {
} else {
// Just wait for a bit to let the user check, that the priming succeeded.
//TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
file.write("M1 S10\n");
@ -1810,7 +2013,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
//}
//}
}
#endif
#endif
print.throw_if_canceled();
}
// Process all layers of all objects (non-sequential mode) with a parallel pipeline:
@ -1824,10 +2027,10 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
file.write("M1003 S0\n");
}
}
#ifdef HAS_PRESSURE_EQUALIZER
#ifdef HAS_PRESSURE_EQUALIZER
if (m_pressure_equalizer)
file.write(m_pressure_equalizer->process("", true));
#endif /* HAS_PRESSURE_EQUALIZER */
#endif /* HAS_PRESSURE_EQUALIZER */
if (m_wipe_tower)
// Purge the extruder, pull out the active filament.
file.write(m_wipe_tower->finalize(*this));
@ -2502,7 +2705,6 @@ GCode::LayerResult GCode::process_layer(
// First object, support and raft layer, if available.
const Layer *object_layer = nullptr;
const SupportLayer *support_layer = nullptr;
const TreeSupportLayer* tree_support_layer = nullptr;
const SupportLayer *raft_layer = nullptr;
for (const LayerToPrint &l : layers) {
if (l.object_layer && ! object_layer)
@ -2513,16 +2715,6 @@ GCode::LayerResult GCode::process_layer(
if (! raft_layer && support_layer->id() < support_layer->object()->slicing_parameters().raft_layers())
raft_layer = support_layer;
}
if (l.tree_support_layer) {
if (!tree_support_layer)
tree_support_layer = l.tree_support_layer;
// BBS: to be checked.
#if 0
if (!raft_layer && tree_support_layer->id() < tree_support_layer->object()->slicing_parameters().raft_layers())
raft_layer = tree_support_layer;
#endif
}
}
const Layer* layer_ptr = nullptr;
@ -2530,8 +2722,6 @@ GCode::LayerResult GCode::process_layer(
layer_ptr = object_layer;
else if (support_layer != nullptr)
layer_ptr = support_layer;
else
layer_ptr = tree_support_layer;
const Layer& layer = *layer_ptr;
GCode::LayerResult result { {}, layer.id(), false, last_layer };
if (layer_tools.extruders.empty())
@ -2552,7 +2742,7 @@ GCode::LayerResult GCode::process_layer(
// Check whether it is possible to apply the spiral vase logic for this layer.
// Just a reminder: A spiral vase mode is allowed for a single object, single material print only.
m_enable_loop_clipping = true;
if (m_spiral_vase && layers.size() == 1 && support_layer == nullptr && tree_support_layer == nullptr) {
if (m_spiral_vase && layers.size() == 1 && support_layer == nullptr) {
bool enable = (layer.id() > 0 || !print.has_brim()) && (layer.id() >= (size_t)print.config().skirt_height.value && ! print.has_infinite_skirt());
if (enable) {
for (const LayerRegion *layer_region : layer.regions())
@ -2790,90 +2980,6 @@ GCode::LayerResult GCode::process_layer(
}
}
// BBS
if (layer_to_print.tree_support_layer != nullptr) {
const TreeSupportLayer& tree_support_layer = *layer_to_print.tree_support_layer;
const PrintObject& object = *layer_to_print.original_object;
if (!tree_support_layer.support_fills.entities.empty()) {
ExtrusionRole role = tree_support_layer.support_fills.role();
bool has_support = role == erMixed || role == erSupportMaterial || role == erSupportTransition;
bool has_interface = role == erMixed || role == erSupportMaterialInterface;
// Extruder ID of the support base. -1 if "don't care".
unsigned int support_extruder = object.config().support_filament.value - 1;
// Shall the support be printed with the active extruder, preferably with non-soluble, to avoid tool changes?
bool support_dontcare = object.config().support_filament.value == 0;
// Extruder ID of the support interface. -1 if "don't care".
unsigned int interface_extruder = object.config().support_interface_filament.value - 1;
// Shall the support interface be printed with the active extruder, preferably with non-soluble, to avoid tool changes?
bool interface_dontcare = object.config().support_interface_filament.value == 0;
// BBS: apply wiping overridden extruders
WipingExtrusions& wiping_extrusions = const_cast<LayerTools&>(layer_tools).wiping_extrusions();
if (support_dontcare) {
int extruder_override = wiping_extrusions.get_support_extruder_overrides(&object);
if (extruder_override >= 0) {
support_extruder = extruder_override;
support_dontcare = false;
}
}
if (interface_dontcare) {
int extruder_override = wiping_extrusions.get_support_interface_extruder_overrides(&object);
if (extruder_override >= 0) {
interface_extruder = extruder_override;
interface_dontcare = false;
}
}
// BBS: try to print support base with a filament other than interface filament
if (support_dontcare && !interface_dontcare) {
unsigned int dontcare_extruder = first_extruder_id;
for (unsigned int extruder_id : layer_tools.extruders) {
if (print.config().filament_soluble.get_at(extruder_id)) continue;
if (extruder_id == interface_extruder) continue;
dontcare_extruder = extruder_id;
break;
}
if (support_dontcare) support_extruder = dontcare_extruder;
}
else if (support_dontcare || interface_dontcare) {
// Some support will be printed with "don't care" material, preferably non-soluble.
// Is the current extruder assigned a soluble filament?
unsigned int dontcare_extruder = first_extruder_id;
if (print.config().filament_soluble.get_at(dontcare_extruder)) {
// The last extruder printed on the previous layer extrudes soluble filament.
// Try to find a non-soluble extruder on the same layer.
for (unsigned int extruder_id : layer_tools.extruders)
if (!print.config().filament_soluble.get_at(extruder_id)) {
dontcare_extruder = extruder_id;
break;
}
}
if (support_dontcare)
support_extruder = dontcare_extruder;
if (interface_dontcare)
interface_extruder = dontcare_extruder;
}
// Both the support and the support interface are printed with the same extruder, therefore
// the interface may be interleaved with the support base.
bool single_extruder = !has_support || support_extruder == interface_extruder;
// Assign an extruder to the base.
ObjectByExtruder& obj = object_by_extruder(by_extruder, has_support ? support_extruder : interface_extruder, &layer_to_print - layers.data(), layers.size());
obj.support = &tree_support_layer.support_fills;
obj.support_extrusion_role = single_extruder ? erMixed : erSupportMaterial;
if (!single_extruder && has_interface) {
ObjectByExtruder& obj_interface = object_by_extruder(by_extruder, interface_extruder, &layer_to_print - layers.data(), layers.size());
obj_interface.support = &tree_support_layer.support_fills;
obj_interface.support_extrusion_role = erSupportMaterialInterface;
}
}
}
if (layer_to_print.object_layer != nullptr) {
const Layer &layer = *layer_to_print.object_layer;
// We now define a strategy for building perimeters and fills. The separation
@ -3024,8 +3130,9 @@ GCode::LayerResult GCode::process_layer(
// BBS: ordering instances by extruder
std::vector<InstanceToPrint> instances_to_print;
bool has_prime_tower = print.config().enable_prime_tower
&& print.config().print_sequence == PrintSequence::ByLayer
&& print.extruders().size() > 1;
&& print.extruders().size() > 1
&& (print.config().print_sequence == PrintSequence::ByLayer
|| (print.config().print_sequence == PrintSequence::ByObject && print.objects().size() == 1));
if (has_prime_tower) {
int plate_idx = print.get_plate_index();
Point wt_pos(print.config().wipe_tower_x.get_at(plate_idx), print.config().wipe_tower_y.get_at(plate_idx));
@ -3097,12 +3204,7 @@ GCode::LayerResult GCode::process_layer(
m_last_obj_copy = this_object_copy;
this->set_origin(unscale(offset));
if (instance_to_print.object_by_extruder.support != nullptr) {
if (layers[instance_to_print.layer_id].support_layer) {
m_layer = layers[instance_to_print.layer_id].support_layer;
}
else {
m_layer = layers[instance_to_print.layer_id].tree_support_layer;
}
m_layer = layers[instance_to_print.layer_id].support_layer;
m_object_layer_over_raft = false;
//BBS: print supports' brims first
@ -3303,8 +3405,11 @@ std::string GCode::change_layer(coordf_t print_z, bool lazy_raise)
//BBS
//coordf_t z = print_z + m_config.z_offset.value; // in unscaled coordinates
coordf_t z = print_z; // in unscaled coordinates
if (EXTRUDER_CONFIG(retract_when_changing_layer) && m_writer.will_move_z(z))
gcode += this->retract();
if (EXTRUDER_CONFIG(retract_when_changing_layer) && m_writer.will_move_z(z)) {
LiftType lift_type = this->to_lift_type(ZHopType(EXTRUDER_CONFIG(z_hop_types)));
//BBS: force to use SpiralLift when change layer if lift type is auto
gcode += this->retract(false, false, ZHopType(EXTRUDER_CONFIG(z_hop_types)) == ZHopType::zhtAuto ? LiftType::SpiralLift : lift_type);
}
if (!lazy_raise) {
std::ostringstream comment;
@ -3421,6 +3526,12 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
if (was_clockwise) {
// swap points
Point c = a; a = b; b = c;
// double angle = paths.front().first_point().ccw_angle(a, b) / 3;
// // turn left if contour, turn right if hole
// if (was_clockwise) angle *= -1;
}
double angle = paths.front().first_point().ccw_angle(a, b) / 3;
@ -4067,7 +4178,8 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
Polyline travel { this->last_pos(), point };
// check whether a straight travel move would need retraction
bool needs_retraction = this->needs_retraction(travel, role);
LiftType lift_type = LiftType::SpiralLift;
bool needs_retraction = this->needs_retraction(travel, role, lift_type);
// check whether wipe could be disabled without causing visible stringing
bool could_be_wipe_disabled = false;
// Save state of use_external_mp_once for the case that will be needed to call twice m_avoid_crossing_perimeters.travel_to.
@ -4104,10 +4216,12 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
// multi-hop travel path inside the configuration space
if (needs_retraction
&& m_config.reduce_crossing_wall
&& ! m_avoid_crossing_perimeters.disabled_once()) {
&& ! m_avoid_crossing_perimeters.disabled_once()
//BBS: don't generate detour travel paths when current position is unclear
&& m_writer.is_current_position_clear()) {
travel = m_avoid_crossing_perimeters.travel_to(*this, point, &could_be_wipe_disabled);
// check again whether the new travel path still needs a retraction
needs_retraction = this->needs_retraction(travel, role);
needs_retraction = this->needs_retraction(travel, role, lift_type);
//if (needs_retraction && m_layer_index > 1) exit(0);
}
@ -4120,7 +4234,7 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
m_wipe.reset_path();
Point last_post_before_retract = this->last_pos();
gcode += this->retract();
gcode += this->retract(false, false, lift_type);
// When "Wipe while retracting" is enabled, then extruder moves to another position, and travel from this position can cross perimeters.
// Because of it, it is necessary to call avoid crossing perimeters again with new starting point after calling retraction()
// FIXME Lukas H.: Try to predict if this second calling of avoid crossing perimeters will be needed or not. It could save computations.
@ -4155,36 +4269,102 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
return gcode;
}
bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role)
//BBS
LiftType GCode::to_lift_type(ZHopType z_hop_types) {
switch (z_hop_types)
{
case ZHopType::zhtNormal:
return LiftType::NormalLift;
case ZHopType::zhtSlope:
return LiftType::LazyLift;
case ZHopType::zhtSpiral:
return LiftType::SpiralLift;
default:
// if no corresponding lift type, use normal lift
return LiftType::NormalLift;
}
};
bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role, LiftType& lift_type)
{
if (travel.length() < scale_(EXTRUDER_CONFIG(retraction_minimum_travel))) {
// skip retraction if the move is shorter than the configured threshold
return false;
}
auto is_through_overhang = [this](const Polyline& travel) {
const float protect_z_scaled = scale_(0.4);
std::pair<float, float> z_range;
z_range.second = m_layer ? m_layer->print_z : 0.f;
z_range.first = std::max(0.f, z_range.second - protect_z_scaled);
for (auto object : m_curr_print->objects()) {
BoundingBox obj_bbox = object->bounding_box();
BoundingBox travel_bbox = get_extents(travel);
obj_bbox.offset(scale_(EPSILON));
if (!obj_bbox.overlap(travel_bbox))
continue;
for (auto layer : object->layers()) {
if (layer->print_z < z_range.first)
continue;
if (layer->print_z > z_range.second + EPSILON)
break;
for (ExPolygon& overhang : layer->loverhangs) {
if (overhang.contains(travel))
return true;
}
}
}
return false;
};
float max_z_hop = 0.f;
for (int i = 0; i < m_config.z_hop.size(); i++)
max_z_hop = std::max(max_z_hop, (float)m_config.z_hop.get_at(i));
float travel_len_thresh = max_z_hop / tan(GCodeWriter::slope_threshold);
float accum_len = 0.f;
Polyline clipped_travel;
for (auto line : travel.lines()) {
if (accum_len + line.length() > travel_len_thresh + EPSILON) {
Point end_pnt = line.a + line.normal() * (travel_len_thresh - accum_len);
clipped_travel.append(Polyline(line.a, end_pnt));
break;
}
else {
clipped_travel.append(Polyline(line.a, line.b));
accum_len += line.length();
}
}
//BBS: force to retract when leave from external perimeter for a long travel
//Better way is judging whether the travel move direction is same with last extrusion move.
if (is_perimeter(m_last_processor_extrusion_role) && m_last_processor_extrusion_role != erPerimeter)
if (is_perimeter(m_last_processor_extrusion_role) && m_last_processor_extrusion_role != erPerimeter) {
if (ZHopType(EXTRUDER_CONFIG(z_hop_types)) == ZHopType::zhtAuto) {
lift_type = is_through_overhang(clipped_travel) ? LiftType::SpiralLift : LiftType::LazyLift;
}
else {
lift_type = to_lift_type(ZHopType(EXTRUDER_CONFIG(z_hop_types)));
}
return true;
}
if (role == erSupportMaterial || role == erSupportTransition) {
const SupportLayer* support_layer = dynamic_cast<const SupportLayer*>(m_layer);
//FIXME support_layer->support_islands.contains should use some search structure!
if (support_layer != NULL && support_layer->support_islands.contains(travel))
// skip retraction if this is a travel move inside a support material island
//FIXME not retracting over a long path may cause oozing, which in turn may result in missing material
// at the end of the extrusion path!
return false;
//reduce the retractions in lightning infills for tree support
const TreeSupportLayer* ts_layer = dynamic_cast<const TreeSupportLayer*>(m_layer);
if (ts_layer != NULL)
for (auto& area : ts_layer->base_areas)
if(area.contains(travel))
if (support_layer != NULL && support_layer->support_type==stInnerTree)
for (auto &area : support_layer->base_areas)
if (area.contains(travel))
return false;
}
//BBS: need retract when long moving to print perimeter to avoid dropping of material
if (!is_perimeter(role) && m_config.reduce_infill_retraction && m_layer != nullptr &&
m_config.sparse_infill_density.value > 0 && m_layer->any_internal_region_slice_contains(travel))
@ -4194,10 +4374,16 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role)
return false;
// retract if reduce_infill_retraction is disabled or doesn't apply when role is perimeter
if (ZHopType(EXTRUDER_CONFIG(z_hop_types)) == ZHopType::zhtAuto) {
lift_type = is_through_overhang(clipped_travel) ? LiftType::SpiralLift : LiftType::LazyLift;
}
else {
lift_type = to_lift_type(ZHopType(EXTRUDER_CONFIG(z_hop_types)));
}
return true;
}
std::string GCode::retract(bool toolchange, bool is_last_retraction)
std::string GCode::retract(bool toolchange, bool is_last_retraction, LiftType lift_type)
{
std::string gcode;
@ -4221,11 +4407,7 @@ std::string GCode::retract(bool toolchange, bool is_last_retraction)
if (m_writer.extruder()->retraction_length() > 0 || m_config.use_firmware_retraction) {
// BBS: don't do lazy_lift when enable spiral vase
size_t extruder_id = m_writer.extruder()->id();
auto _lift = m_config.z_lift_type.value;
if(m_spiral_vase)
_lift = NormalLift;
gcode += m_writer.lift(_lift);
gcode += m_writer.lift(!m_spiral_vase ? lift_type : LiftType::NormalLift);
}
return gcode;
@ -4340,6 +4522,12 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z)
dyn_config.set_key_value("second_flush_volume", new ConfigOptionFloat(wipe_length / 2.f));
dyn_config.set_key_value("old_filament_e_feedrate", new ConfigOptionInt(old_filament_e_feedrate));
dyn_config.set_key_value("new_filament_e_feedrate", new ConfigOptionInt(new_filament_e_feedrate));
dyn_config.set_key_value("travel_point_1_x", new ConfigOptionFloat(float(travel_point_1.x())));
dyn_config.set_key_value("travel_point_1_y", new ConfigOptionFloat(float(travel_point_1.y())));
dyn_config.set_key_value("travel_point_2_x", new ConfigOptionFloat(float(travel_point_2.x())));
dyn_config.set_key_value("travel_point_2_y", new ConfigOptionFloat(float(travel_point_2.y())));
dyn_config.set_key_value("travel_point_3_x", new ConfigOptionFloat(float(travel_point_3.x())));
dyn_config.set_key_value("travel_point_3_y", new ConfigOptionFloat(float(travel_point_3.y())));
int flush_count = std::min(g_max_flush_count, (int)std::round(wipe_volume / g_purge_volume_one_time));
float flush_unit = wipe_length / flush_count;

View file

@ -197,8 +197,8 @@ public:
void apply_print_config(const PrintConfig &print_config);
std::string travel_to(const Point& point, ExtrusionRole role, std::string comment);
bool needs_retraction(const Polyline& travel, ExtrusionRole role = erNone);
std::string retract(bool toolchange = false, bool is_last_retraction = false);
bool needs_retraction(const Polyline& travel, ExtrusionRole role, LiftType& lift_type);
std::string retract(bool toolchange = false, bool is_last_retraction = false, LiftType lift_type = LiftType::SpiralLift);
std::string unretract() { return m_writer.unlift() + m_writer.unretract(); }
std::string set_extruder(unsigned int extruder_id, double print_z);
@ -209,10 +209,9 @@ public:
// public, so that it could be accessed by free helper functions from GCode.cpp
struct LayerToPrint
{
LayerToPrint() : object_layer(nullptr), support_layer(nullptr), tree_support_layer(nullptr), original_object(nullptr) {}
LayerToPrint() : object_layer(nullptr), support_layer(nullptr), original_object(nullptr) {}
const Layer* object_layer;
const SupportLayer* support_layer;
const TreeSupportLayer* tree_support_layer;
const PrintObject* original_object; //BBS: used for shared object logic
const Layer* layer() const
{
@ -222,9 +221,6 @@ public:
if (support_layer != nullptr)
return support_layer;
if (tree_support_layer != nullptr)
return tree_support_layer;
return nullptr;
}
@ -246,10 +242,6 @@ public:
count++;
}
if (tree_support_layer != nullptr) {
sum_z += tree_support_layer->print_z;
count++;
}
return sum_z / count;
}
};
@ -409,6 +401,10 @@ private:
std::string extrude_perimeters(const Print& print, const std::vector<ObjectByExtruder::Island::Region>& by_region);
std::string extrude_infill(const Print& print, const std::vector<ObjectByExtruder::Island::Region>& by_region, bool ironing);
std::string extrude_support(const ExtrusionEntityCollection& support_fills);
// BBS
LiftType to_lift_type(ZHopType z_hop_types);
std::set<ObjectID> m_objsWithBrim; // indicates the objs with brim
std::set<ObjectID> m_objSupportsWithBrim; // indicates the objs' supports with brim
// Cache for custom seam enforcers/blockers for each layer.
@ -491,6 +487,7 @@ private:
GCodeProcessor m_processor;
// BBS
Print* m_curr_print = nullptr;
unsigned int m_toolchange_count;
coordf_t m_nominal_z;
bool m_need_change_layer_lift_z = false;

View file

@ -1135,8 +1135,7 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &
const ExPolygons &lslices = gcodegen.layer()->lslices;
const std::vector<BoundingBox> &lslices_bboxes = gcodegen.layer()->lslices_bboxes;
bool is_support_layer = (dynamic_cast<const SupportLayer *>(gcodegen.layer()) != nullptr) ||
(dynamic_cast<const TreeSupportLayer *>(gcodegen.layer()) != nullptr);
bool is_support_layer = (dynamic_cast<const SupportLayer *>(gcodegen.layer()) != nullptr);
if (!use_external && (is_support_layer || (!lslices.empty() && !any_expolygon_contains(lslices, lslices_bboxes, m_grid_lslice, travel)))) {
// Initialize m_internal only when it is necessary.
if (m_internal.boundaries.empty())

View file

@ -244,22 +244,22 @@ float new_feedrate_to_reach_time_stretch(
{
float new_feedrate = min_feedrate;
for (size_t iter = 0; iter < max_iter; ++ iter) {
float nomin = 0;
float denom = time_stretch;
double nomin = 0;
double denom = time_stretch;
for (auto it = it_begin; it != it_end; ++ it) {
assert((*it)->slow_down_min_speed < min_feedrate + EPSILON);
for (size_t i = 0; i < (*it)->n_lines_adjustable; ++i) {
const CoolingLine &line = (*it)->lines[i];
if (line.feedrate > min_feedrate) {
nomin += line.time * line.feedrate;
denom += line.time;
nomin += (double)line.time * (double)line.feedrate;
denom += (double)line.time;
}
}
}
assert(denom > 0);
if (denom < 0)
return min_feedrate;
new_feedrate = nomin / denom;
new_feedrate = (float)(nomin / denom);
assert(new_feedrate > min_feedrate - EPSILON);
if (new_feedrate < min_feedrate + EPSILON)
goto finished;

View file

@ -55,7 +55,8 @@ const std::vector<std::string> GCodeProcessor::Reserved_Tags = {
" CUSTOM_GCODE",
"_GP_FIRST_LINE_M73_PLACEHOLDER",
"_GP_LAST_LINE_M73_PLACEHOLDER",
"_GP_ESTIMATED_PRINTING_TIME_PLACEHOLDER"
"_GP_ESTIMATED_PRINTING_TIME_PLACEHOLDER",
"_GP_TOTAL_LAYER_NUMBER_PLACEHOLDER"
};
const std::vector<std::string> GCodeProcessor::Reserved_Tags_compatible = {
@ -227,6 +228,7 @@ void GCodeProcessor::TimeMachine::reset()
std::fill(moves_time.begin(), moves_time.end(), 0.0f);
std::fill(roles_time.begin(), roles_time.end(), 0.0f);
layers_time = std::vector<float>();
prepare_time = 0.0f;
}
void GCodeProcessor::TimeMachine::simulate_st_synchronize(float additional_time)
@ -334,7 +336,9 @@ void GCodeProcessor::TimeMachine::calculate_time(size_t keep_last_n_blocks, floa
time += block_time;
gcode_time.cache += block_time;
moves_time[static_cast<size_t>(block.move_type)] += block_time;
//BBS: don't calculate travel of start gcode into travel time
if (!block.flags.prepare_stage || block.move_type != EMoveType::Travel)
moves_time[static_cast<size_t>(block.move_type)] += block_time;
roles_time[static_cast<size_t>(block.role)] += block_time;
if (block.layer_id >= layers_time.size()) {
const size_t curr_size = layers_time.size();
@ -344,6 +348,9 @@ void GCodeProcessor::TimeMachine::calculate_time(size_t keep_last_n_blocks, floa
}
}
layers_time[block.layer_id - 1] += block_time;
//BBS
if (block.flags.prepare_stage)
prepare_time += block_time;
g1_times_cache.push_back({ block.g1_line_id, time });
// update times for remaining time to printer stop placeholders
auto it_stop_time = std::lower_bound(stop_times.begin(), stop_times.end(), block.g1_line_id,
@ -371,7 +378,7 @@ void GCodeProcessor::TimeProcessor::reset()
machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].enabled = true;
}
void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector<GCodeProcessorResult::MoveVertex>& moves, std::vector<size_t>& lines_ends)
void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector<GCodeProcessorResult::MoveVertex>& moves, std::vector<size_t>& lines_ends, size_t total_layer_num)
{
FilePtr in{ boost::nowide::fopen(filename.c_str(), "rb") };
if (in.f == nullptr)
@ -480,14 +487,20 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
//sprintf(buf, "; estimated printing time (%s mode) = %s\n",
// (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
// get_time_dhms(machine.time).c_str());
sprintf(buf, "; model printing time: %s; total estimated time: %s\n",
get_time_dhms(machine.time - machine.roles_time[ExtrusionRole::erCustom]).c_str(),
get_time_dhms(machine.time).c_str());
sprintf(buf, "; model printing time: %s; total estimated time: %s\n",
get_time_dhms(machine.time - machine.prepare_time).c_str(),
get_time_dhms(machine.time).c_str());
}
ret += buf;
}
}
}
//BBS: write total layer number
else if (line == reserved_tag(ETags::Total_Layer_Number_Placeholder)) {
char buf[128];
sprintf(buf, "; total layer number: %zd\n", total_layer_num);
ret += buf;
}
}
if (! ret.empty())
@ -783,6 +796,7 @@ void GCodeProcessorResult::reset() {
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
spiral_vase_layers = std::vector<std::pair<float, std::pair<size_t, size_t>>>();
time = 0;
//BBS: add mutex for protection of gcode result
@ -808,6 +822,7 @@ void GCodeProcessorResult::reset() {
required_nozzle_HRC = std::vector<int>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_HRC);
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
spiral_vase_layers = std::vector<std::pair<float, std::pair<size_t, size_t>>>();
warnings.clear();
//BBS: add mutex for protection of gcode result
@ -962,6 +977,10 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_first_layer_height = std::abs(initial_layer_print_height->value);
m_result.printable_height = config.printable_height;
const ConfigOptionBool* spiral_vase = config.option<ConfigOptionBool>("spiral_mode");
if (spiral_vase != nullptr)
m_spiral_vase_active = spiral_vase->value;
}
void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
@ -1224,6 +1243,10 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
const ConfigOptionFloat* printable_height = config.option<ConfigOptionFloat>("printable_height");
if (printable_height != nullptr)
m_result.printable_height = printable_height->value;
const ConfigOptionBool* spiral_vase = config.option<ConfigOptionBool>("spiral_mode");
if (spiral_vase != nullptr)
m_spiral_vase_active = spiral_vase->value;
}
void GCodeProcessor::enable_stealth_time_estimator(bool enabled)
@ -1294,6 +1317,8 @@ void GCodeProcessor::reset()
m_options_z_corrector.reset();
m_spiral_vase_active = false;
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
m_mm3_per_mm_compare.reset();
m_height_compare.reset();
@ -1449,7 +1474,7 @@ void GCodeProcessor::finalize(bool post_process)
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
if (post_process)
m_time_processor.post_process(m_result.filename, m_result.moves, m_result.lines_ends);
m_time_processor.post_process(m_result.filename, m_result.moves, m_result.lines_ends, m_layer_id);
#if ENABLE_GCODE_VIEWER_STATISTICS
m_result.time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - m_start_time).count();
#endif // ENABLE_GCODE_VIEWER_STATISTICS
@ -1462,6 +1487,11 @@ float GCodeProcessor::get_time(PrintEstimatedStatistics::ETimeMode mode) const
return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? m_time_processor.machines[static_cast<size_t>(mode)].time : 0.0f;
}
float GCodeProcessor::get_prepare_time(PrintEstimatedStatistics::ETimeMode mode) const
{
return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? m_time_processor.machines[static_cast<size_t>(mode)].prepare_time : 0.0f;
}
std::string GCodeProcessor::get_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const
{
return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? short_time(get_time_dhms(m_time_processor.machines[static_cast<size_t>(mode)].time)) : std::string("N/A");
@ -2044,6 +2074,18 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
// layer change tag
if (comment == reserved_tag(ETags::Layer_Change)) {
++m_layer_id;
if (m_spiral_vase_active) {
if (m_result.moves.empty() || m_result.spiral_vase_layers.empty())
// add a placeholder for layer height. the actual value will be set inside process_G1() method
m_result.spiral_vase_layers.push_back({ FLT_MAX, { 0, 0 } });
else {
const size_t move_id = m_result.moves.size() - 1;
if (!m_result.spiral_vase_layers.empty())
m_result.spiral_vase_layers.back().second.second = move_id;
// add a placeholder for layer height. the actual value will be set inside process_G1() method
m_result.spiral_vase_layers.push_back({ FLT_MAX, { move_id, move_id } });
}
}
return;
}
@ -2703,10 +2745,12 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
TimeBlock block;
block.move_type = type;
block.role = m_extrusion_role;
//BBS: don't calculate travel time into extrusion path, except travel inside start and end gcode.
block.role = (type != EMoveType::Travel || m_extrusion_role == erCustom) ? m_extrusion_role : erNone;
block.distance = distance;
block.g1_line_id = m_g1_line_id;
block.layer_id = std::max<unsigned int>(1, m_layer_id);
block.flags.prepare_stage = m_processing_start_custom_gcode;
//BBS: limite the cruise according to centripetal acceleration
//Only need to handle when both prev and curr segment has movement in x-y plane
@ -2924,6 +2968,14 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset);
}
if (m_spiral_vase_active && !m_result.spiral_vase_layers.empty()) {
if (m_result.spiral_vase_layers.back().first == FLT_MAX && delta_pos[Z] > 0.0)
// replace layer height placeholder with correct value
m_result.spiral_vase_layers.back().first = static_cast<float>(m_end_position[Z]);
if (!m_result.moves.empty())
m_result.spiral_vase_layers.back().second.second = m_result.moves.size() - 1;
}
// store move
store_move_vertex(type);
}
@ -3125,10 +3177,12 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line)
TimeBlock block;
block.move_type = type;
block.role = m_extrusion_role;
//BBS: don't calculate travel time into extrusion path, except travel inside start and end gcode.
block.role = (type != EMoveType::Travel || m_extrusion_role == erCustom) ? m_extrusion_role : erNone;
block.distance = delta_xyz;
block.g1_line_id = m_g1_line_id;
block.layer_id = std::max<unsigned int>(1, m_layer_id);
block.flags.prepare_stage = m_processing_start_custom_gcode;
// BBS: calculates block cruise feedrate
// For arc move, we need to limite the cruise according to centripetal acceleration which is
@ -4103,6 +4157,7 @@ void GCodeProcessor::update_estimated_times_stats()
auto update_mode = [this](PrintEstimatedStatistics::ETimeMode mode) {
PrintEstimatedStatistics::Mode& data = m_result.print_statistics.modes[static_cast<size_t>(mode)];
data.time = get_time(mode);
data.prepare_time = get_prepare_time(mode);
data.custom_gcode_times = get_custom_gcode_times(mode, true);
data.moves_times = get_moves_time(mode);
data.roles_times = get_roles_time(mode);
@ -4176,4 +4231,3 @@ void GCodeProcessor::update_slice_warnings()
}
} /* namespace Slic3r */

View file

@ -49,6 +49,7 @@ namespace Slic3r {
struct Mode
{
float time;
float prepare_time;
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> custom_gcode_times;
std::vector<std::pair<EMoveType, float>> moves_times;
std::vector<std::pair<ExtrusionRole, float>> roles_times;
@ -56,6 +57,7 @@ namespace Slic3r {
void reset() {
time = 0.0f;
prepare_time = 0.0f;
custom_gcode_times.clear();
moves_times.clear();
roles_times.clear();
@ -163,6 +165,7 @@ namespace Slic3r {
std::vector<int> filament_vitrification_temperature;
PrintEstimatedStatistics print_statistics;
std::vector<CustomGCode::Item> custom_gcode_per_print_z;
std::vector<std::pair<float, std::pair<size_t, size_t>>> spiral_vase_layers;
//BBS
std::vector<SliceWarning> warnings;
int nozzle_hrc;
@ -191,6 +194,7 @@ namespace Slic3r {
filament_densities = other.filament_densities;
print_statistics = other.print_statistics;
custom_gcode_per_print_z = other.custom_gcode_per_print_z;
spiral_vase_layers = other.spiral_vase_layers;
warnings = other.warnings;
#if ENABLE_GCODE_VIEWER_STATISTICS
time = other.time;
@ -223,7 +227,8 @@ namespace Slic3r {
Custom_Code,
First_Line_M73_Placeholder,
Last_Line_M73_Placeholder,
Estimated_Printing_Time_Placeholder
Estimated_Printing_Time_Placeholder,
Total_Layer_Number_Placeholder
};
static const std::string& reserved_tag(ETags tag) { return s_IsBBLPrinter ? Reserved_Tags[static_cast<unsigned char>(tag)] : Reserved_Tags_compatible[static_cast<unsigned char>(tag)]; }
@ -301,6 +306,7 @@ namespace Slic3r {
{
bool recalculate{ false };
bool nominal_length{ false };
bool prepare_stage{ false };
};
EMoveType move_type{ EMoveType::Noop };
@ -384,6 +390,8 @@ namespace Slic3r {
std::array<float, static_cast<size_t>(EMoveType::Count)> moves_time;
std::array<float, static_cast<size_t>(ExtrusionRole::erCount)> roles_time;
std::vector<float> layers_time;
//BBS: prepare stage time before print model, including start gcode time and mostly same with start gcode time
float prepare_time;
void reset();
@ -420,7 +428,7 @@ namespace Slic3r {
// post process the file with the given filename to add remaining time lines M73
// and updates moves' gcode ids accordingly
void post_process(const std::string& filename, std::vector<GCodeProcessorResult::MoveVertex>& moves, std::vector<size_t>& lines_ends);
void post_process(const std::string& filename, std::vector<GCodeProcessorResult::MoveVertex>& moves, std::vector<size_t>& lines_ends, size_t total_layer_num);
};
struct UsedFilaments // filaments per ColorChange
@ -627,6 +635,7 @@ namespace Slic3r {
SeamsDetector m_seams_detector;
OptionsZCorrector m_options_z_corrector;
size_t m_last_default_color_id;
bool m_spiral_vase_active;
#if ENABLE_GCODE_VIEWER_STATISTICS
std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time;
#endif // ENABLE_GCODE_VIEWER_STATISTICS
@ -684,6 +693,7 @@ namespace Slic3r {
void finalize(bool post_process);
float get_time(PrintEstimatedStatistics::ETimeMode mode) const;
float get_prepare_time(PrintEstimatedStatistics::ETimeMode mode) const;
std::string get_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const;
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> get_custom_gcode_times(PrintEstimatedStatistics::ETimeMode mode, bool include_remaining) const;

View file

@ -467,6 +467,8 @@ void process_perimeter_polygon(
if (orig_point) {
Vec3f pos_of_next = orig_polygon_points.empty() ? first : orig_polygon_points.front();
float distance_to_next = (position - pos_of_next).norm();
if (distance_to_next > perimeter.flow_width * perimeter.flow_width * 4)
oversampled_points.push((position + pos_of_next) / 2);
if (global_model_info.is_enforced(position, distance_to_next)) {
Vec3f vec_to_next = (pos_of_next - position).normalized();
float step_size = SeamPlacer::enforcer_oversampling_distance;

View file

@ -175,10 +175,6 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
for (auto layer : object->support_layers())
zs.emplace_back(layer->print_z);
// BBS
for (auto layer : object->tree_support_layers())
zs.emplace_back(layer->print_z);
// Find first object layer that is not empty and save its print_z
for (const Layer* layer : object->layers())
if (layer->has_extrusions()) {
@ -199,10 +195,11 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
// BBS
if (auto num_filaments = unsigned(print.config().filament_diameter.size());
num_filaments > 1 && print.object_extruders().size() == 1 && // the current Print's configuration is CustomGCode::MultiAsSingle
print.model().custom_gcode_per_print_z.mode == CustomGCode::MultiAsSingle) {
//BBS: replace model custom gcode with current plate custom gcode
print.model().get_curr_plate_custom_gcodes().mode == CustomGCode::MultiAsSingle) {
// Printing a single extruder platter on a printer with more than 1 extruder (or single-extruder multi-material).
// There may be custom per-layer tool changes available at the model.
per_layer_extruder_switches = custom_tool_changes(print.model().custom_gcode_per_print_z, num_filaments);
per_layer_extruder_switches = custom_tool_changes(print.model().get_curr_plate_custom_gcodes(), num_filaments);
}
// Collect extruders reuqired to print the layers.
@ -348,24 +345,6 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
}
}
// BBS
for (auto tree_support_layer : object.tree_support_layers()) {
LayerTools &layer_tools = this->tools_for_layer(tree_support_layer->print_z);
ExtrusionRole role = tree_support_layer->support_fills.role();
bool has_support = role == erMixed || role == erSupportMaterial || role == erSupportTransition;;
bool has_interface = role == erMixed || role == erSupportMaterialInterface;
unsigned int extruder_support = object.config().support_filament.value;
unsigned int extruder_interface = object.config().support_interface_filament.value;
if (has_support)
layer_tools.extruders.push_back(extruder_support);
if (has_interface)
layer_tools.extruders.push_back(extruder_interface);
if (has_support || has_interface) {
layer_tools.has_support = true;
layer_tools.wiping_extrusions().is_support_overriddable_and_mark(role, object);
}
}
// Extruder overrides are ordered by print_z.
std::vector<std::pair<double, unsigned int>>::const_iterator it_per_layer_extruder_override;
it_per_layer_extruder_override = per_layer_extruder_switches.begin();
@ -781,12 +760,15 @@ void ToolOrdering::mark_skirt_layers(const PrintConfig &config, coordf_t max_lay
// Assign a pointer to a custom G-code to the respective ToolOrdering::LayerTools.
// Ignore color changes, which are performed on a layer and for such an extruder, that the extruder will not be printing above that layer.
// If multiple events are planned over a span of a single layer, use the last one.
// BBS: replace model custom gcode with current plate custom gcode
static CustomGCode::Info custom_gcode_per_print_z;
void ToolOrdering::assign_custom_gcodes(const Print &print)
{
// Only valid for non-sequential print.
assert(print.config().print_sequence == PrintSequence::ByLayer);
const CustomGCode::Info &custom_gcode_per_print_z = print.model().custom_gcode_per_print_z;
custom_gcode_per_print_z = print.model().get_curr_plate_custom_gcodes();
if (custom_gcode_per_print_z.gcodes.empty())
return;
@ -795,7 +777,7 @@ void ToolOrdering::assign_custom_gcodes(const Print &print)
CustomGCode::Mode mode =
(num_filaments == 1) ? CustomGCode::SingleExtruder :
print.object_extruders().size() == 1 ? CustomGCode::MultiAsSingle : CustomGCode::MultiExtruder;
CustomGCode::Mode model_mode = print.model().custom_gcode_per_print_z.mode;
CustomGCode::Mode model_mode = print.model().get_curr_plate_custom_gcodes().mode;
std::vector<unsigned char> extruder_printing_above(num_filaments, false);
auto custom_gcode_it = custom_gcode_per_print_z.gcodes.rbegin();
// Tool changes and color changes will be ignored, if the model's tool/color changes were entered in mm mode and the print is in non mm mode
@ -890,7 +872,7 @@ int WipingExtrusions::first_nonsoluble_extruder_on_layer(const PrintConfig& prin
{
const LayerTools& lt = *m_layer_tools;
for (auto extruders_it = lt.extruders.begin(); extruders_it != lt.extruders.end(); ++extruders_it)
if (!print_config.filament_soluble.get_at(*extruders_it))
if (!print_config.filament_soluble.get_at(*extruders_it) && !print_config.filament_is_support.get_at(*extruders_it))
return (*extruders_it);
return (-1);
@ -901,7 +883,7 @@ int WipingExtrusions::last_nonsoluble_extruder_on_layer(const PrintConfig& print
{
const LayerTools& lt = *m_layer_tools;
for (auto extruders_it = lt.extruders.rbegin(); extruders_it != lt.extruders.rend(); ++extruders_it)
if (!print_config.filament_soluble.get_at(*extruders_it))
if (!print_config.filament_soluble.get_at(*extruders_it) && !print_config.filament_is_support.get_at(*extruders_it))
return (*extruders_it);
return (-1);
@ -1042,10 +1024,9 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
if (object->config().flush_into_support) {
auto& object_config = object->config();
const SupportLayer* this_support_layer = object->get_support_layer_at_printz(lt.print_z, EPSILON);
const TreeSupportLayer* this_tree_support_layer = object->get_tree_support_layer_at_printz(lt.print_z, EPSILON);
do {
if (this_support_layer == nullptr && this_tree_support_layer == nullptr)
if (this_support_layer == nullptr)
break;
bool support_overriddable = object_config.support_filament == 0;
@ -1053,7 +1034,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
if (!support_overriddable && !support_intf_overriddable)
break;
auto& entities = this_support_layer != nullptr ? this_support_layer->support_fills.entities : this_tree_support_layer->support_fills.entities;
auto &entities = this_support_layer->support_fills.entities;
if (support_overriddable && !is_support_overridden(object)) {
set_support_extruder_override(object, copy, new_extruder, num_of_copies);
for (const ExtrusionEntity* ee : entities) {

View file

@ -398,14 +398,26 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co
slop_move = w0.string();
}
}
std::string xy_z_move;
{
GCodeG1Formatter w0;
if (this->is_current_position_clear()) {
w0.emit_xyz(target);
w0.emit_f(this->config.travel_speed.value * 60.0);
w0.emit_comment(GCodeWriter::full_gcode_comment, comment);
xy_z_move = w0.string();
}
else {
w0.emit_xy(Vec2d(target.x(), target.y()));
w0.emit_f(this->config.travel_speed.value * 60.0);
w0.emit_comment(GCodeWriter::full_gcode_comment, comment);
xy_z_move = w0.string() + _travel_to_z(target.z(), comment);
}
}
m_pos = dest_point;
this->set_current_position_clear(true);
GCodeG1Formatter w1;
w1.emit_xyz(target);
w1.emit_f(this->config.travel_speed.value * 60.0);
//BBS
w1.emit_comment(GCodeWriter::full_gcode_comment, comment);
return slop_move + w1.string();
return slop_move + xy_z_move;
}
else if (!this->will_move_z(point(2))) {
double nominal_z = m_pos(2) - m_lifted;

View file

@ -79,7 +79,7 @@ public:
//BBS: set offset for gcode writer
void set_xy_offset(double x, double y) { m_x_offset = x; m_y_offset = y; }
Vec2f get_xy_offset() { return Vec2f{m_x_offset, m_y_offset}; };
// To be called by the CoolingBuffer from another thread.
static std::string set_fan(const GCodeFlavor gcode_flavor, unsigned int speed);
// To be called by the main thread. It always emits the G-code, it does not remember the previous state.
@ -93,7 +93,8 @@ public:
bool is_current_position_clear() const { return m_is_current_pos_clear; };
//BBS:
static const bool full_gcode_comment;
//Radian threshold of slope for lazy lift and spiral lift;
static const double slope_threshold;
//SoftFever
void set_is_bbl_machine(bool bval) {m_is_bbl_printers = bval;}
const bool is_bbl_printers() const {return m_is_bbl_printers;}
@ -134,9 +135,6 @@ private:
double m_x_offset{ 0 };
double m_y_offset{ 0 };
//Radian threshold of slope for lazy lift and spiral lift;
static const double slope_threshold;
//SoftFever
bool m_is_bbl_printers = false;
double m_current_speed;

View file

@ -352,6 +352,50 @@ void Layer::export_region_fill_surfaces_to_svg_debug(const char *name) const
this->export_region_fill_surfaces_to_svg(debug_out_path("Layer-fill_surfaces-%s-%d.svg", name, idx ++).c_str());
}
coordf_t Layer::get_sparse_infill_max_void_area()
{
double max_void_area = 0.;
for (auto layerm : m_regions) {
Flow flow = layerm->flow(frInfill);
float density = layerm->region().config().sparse_infill_density;
InfillPattern pattern = layerm->region().config().sparse_infill_pattern;
if (density == 0.)
return -1;
//BBS: rough estimation and need to be optimized
double spacing = flow.scaled_spacing() * (100 - density) / density;
switch (pattern) {
case ipConcentric:
case ipRectilinear:
case ipLine:
case ipGyroid:
case ipAlignedRectilinear:
case ipOctagramSpiral:
case ipHilbertCurve:
case ip3DHoneycomb:
case ipArchimedeanChords:
max_void_area = std::max(max_void_area, spacing * spacing);
break;
case ipGrid:
case ipHoneycomb:
case ipLightning:
max_void_area = std::max(max_void_area, 4.0 * spacing * spacing);
break;
case ipCubic:
case ipAdaptiveCubic:
case ipTriangles:
case ipStars:
case ipSupportCubic:
max_void_area = std::max(max_void_area, 4.5 * spacing * spacing);
break;
default:
max_void_area = std::max(max_void_area, spacing * spacing);
break;
}
};
return max_void_area;
}
BoundingBox get_extents(const LayerRegion &layer_region)
{
BoundingBox bbox;

View file

@ -143,6 +143,9 @@ public:
ExPolygons lslices;
std::vector<BoundingBox> lslices_bboxes;
// BBS
ExPolygons loverhangs;
size_t region_count() const { return m_regions.size(); }
const LayerRegion* get_region(int idx) const { return m_regions[idx]; }
LayerRegion* get_region(int idx) { return m_regions[idx]; }
@ -184,6 +187,8 @@ public:
//BBS
void simplify_extrusion_path() { for (auto layerm : m_regions) layerm->simplify_extrusion_entity();}
//BBS: this function calculate the maximum void grid area of sparse infill of this layer. Just estimated value
coordf_t get_sparse_infill_max_void_area();
protected:
friend class PrintObject;
@ -209,6 +214,11 @@ private:
LayerRegionPtrs m_regions;
};
enum SupportInnerType {
stInnerNormal,
stInnerTree
};
class SupportLayer : public Layer
{
public:
@ -217,6 +227,10 @@ public:
ExPolygonCollection support_islands;
// Extrusion paths for the support base and for the support interface and contacts.
ExtrusionEntityCollection support_fills;
SupportInnerType support_type = stInnerNormal;
// for tree supports
ExPolygons base_areas;
// Is there any valid extrusion assigned to this LayerRegion?
@ -229,33 +243,23 @@ public:
protected:
friend class PrintObject;
friend class TreeSupport;
// The constructor has been made public to be able to insert additional support layers for the skirt or a wipe tower
// between the raft and the object first layer.
SupportLayer(size_t id, size_t interface_id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) :
Layer(id, object, height, print_z, slice_z), m_interface_id(interface_id) {}
Layer(id, object, height, print_z, slice_z), m_interface_id(interface_id), support_type(stInnerNormal) {}
virtual ~SupportLayer() = default;
size_t m_interface_id;
};
class TreeSupportLayer : public Layer
{
public:
ExtrusionEntityCollection support_fills;
ExPolygons overhang_areas;
ExPolygons roof_areas;
ExPolygons roof_1st_layer; // the layer just below roof. When working with PolySupport, this layer should be printed with regular material
ExPolygons floor_areas;
ExPolygons base_areas;
ExPolygons roof_gap_areas; // the areas in the gap between support roof and overhang
enum AreaType {
BaseType=0,
RoofType=1,
FloorType=2,
Roof1stLayer=3
};
// for tree support
ExPolygons overhang_areas;
ExPolygons roof_areas;
ExPolygons roof_1st_layer; // the layer just below roof. When working with PolySupport, this layer should be printed with regular material
ExPolygons floor_areas;
ExPolygons roof_gap_areas; // the areas in the gap between support roof and overhang
enum AreaType { BaseType = 0, RoofType = 1, FloorType = 2, Roof1stLayer = 3 };
struct AreaGroup
{
ExPolygon *area;
@ -263,23 +267,9 @@ public:
coordf_t dist_to_top; // mm dist to top
AreaGroup(ExPolygon *a, int t, coordf_t d) : area(a), type(t), dist_to_top(d) {}
};
std::vector<AreaGroup> area_groups;
enum OverhangType {
Detected=0,
Enforced
};
enum OverhangType { Detected = 0, Enforced };
std::vector<AreaGroup> area_groups;
std::map<const ExPolygon *, OverhangType> overhang_types;
virtual bool has_extrusions() const { return !support_fills.empty(); }
void simplify_support_extrusion_path() { this->simplify_support_entity_collection(&support_fills);}
protected:
friend class PrintObject;
TreeSupportLayer(size_t id, PrintObject* object, coordf_t height, coordf_t print_z, coordf_t slice_z) :
Layer(id, object, height, print_z, slice_z) {}
virtual ~TreeSupportLayer() = default;
};
template<typename LayerContainer>

View file

@ -158,11 +158,20 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
{
// Voids are sparse infills if infill rate is zero.
Polygons voids;
double max_grid_area = -1;
if (this->layer()->lower_layer != nullptr)
max_grid_area = this->layer()->lower_layer->get_sparse_infill_max_void_area();
for (const Surface &surface : this->fill_surfaces.surfaces) {
if (surface.is_top()) {
// Collect the top surfaces, inflate them and trim them by the bottom surfaces.
// This gives the priority to bottom surfaces.
surfaces_append(top, offset_ex(surface.expolygon, margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS), surface);
if (max_grid_area < 0 || surface.expolygon.area() < max_grid_area)
surfaces_append(top, offset_ex(surface.expolygon, margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS), surface);
else
//BBS: Don't need to expand too much in this situation. Expand 3mm to eliminate hole and 1mm for contour
surfaces_append(top, intersection_ex(offset(surface.expolygon.contour, margin / 3.0, EXTERNAL_SURFACES_OFFSET_PARAMETERS),
offset_ex(surface.expolygon, margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS)), surface);
} else if (surface.surface_type == stBottom || (surface.surface_type == stBottomBridge && lower_layer == nullptr)) {
// Grown by 3mm.
surfaces_append(bottom, offset_ex(surface.expolygon, margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS), surface);

View file

@ -63,7 +63,9 @@ Model& Model::assign_copy(const Model &rhs)
}
// copy custom code per height
this->custom_gcode_per_print_z = rhs.custom_gcode_per_print_z;
// BBS
this->plates_custom_gcodes = rhs.plates_custom_gcodes;
this->curr_plate_index = rhs.curr_plate_index;
// BBS: for design info
this->design_info = rhs.design_info;
@ -89,7 +91,9 @@ Model& Model::assign_copy(Model &&rhs)
rhs.objects.clear();
// copy custom code per height
this->custom_gcode_per_print_z = std::move(rhs.custom_gcode_per_print_z);
// BBS
this->plates_custom_gcodes = std::move(rhs.plates_custom_gcodes);
this->curr_plate_index = rhs.curr_plate_index;
//BBS: add auxiliary path logic
// BBS: backup, all in one temp dir
@ -161,10 +165,11 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
file_version = &temp_version;
bool result = false;
bool is_cb_cancel = false;
std::string message;
if (boost::algorithm::iends_with(input_file, ".stp") ||
boost::algorithm::iends_with(input_file, ".step"))
result = load_step(input_file.c_str(), &model, stepFn, stepIsUtf8Fn);
result = load_step(input_file.c_str(), &model, is_cb_cancel, stepFn, stepIsUtf8Fn);
else if (boost::algorithm::iends_with(input_file, ".stl"))
result = load_stl(input_file.c_str(), &model, nullptr, stlFn);
else if (boost::algorithm::iends_with(input_file, ".obj"))
@ -185,6 +190,11 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
else
throw Slic3r::RuntimeError("Unknown file format. Input file must have .stl, .obj, .amf(.xml) extension.");
if (is_cb_cancel) {
Model empty_model;
return empty_model;
}
if (!result) {
if (message.empty())
throw Slic3r::RuntimeError("Loading of a model file failed.");
@ -203,7 +213,9 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
//BBS
//CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config);
CustomGCode::check_mode_for_custom_gcode_per_print_z(model.custom_gcode_per_print_z);
//BBS
for (auto& plate_gcodes : model.plates_custom_gcodes)
CustomGCode::check_mode_for_custom_gcode_per_print_z(plate_gcodes.second);
sort_remove_duplicates(config_substitutions->substitutions);
return model;
@ -277,7 +289,9 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig
throw Slic3r::RuntimeError("Canceled");
}
CustomGCode::check_mode_for_custom_gcode_per_print_z(model.custom_gcode_per_print_z);
//BBS
for (auto& plate_gcodes : model.plates_custom_gcodes)
CustomGCode::check_mode_for_custom_gcode_per_print_z(plate_gcodes.second);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("import 3mf IMPORT_STAGE_CHECK_MODE_GCODE\n");
if (proFn) {
@ -2352,6 +2366,12 @@ bool ModelVolume::is_splittable() const
// BBS
std::vector<int> ModelVolume::get_extruders() const
{
if (m_type == ModelVolumeType::INVALID
|| m_type == ModelVolumeType::NEGATIVE_VOLUME
|| m_type == ModelVolumeType::SUPPORT_BLOCKER
|| m_type == ModelVolumeType::SUPPORT_ENFORCER)
return std::vector<int>();
if (mmu_segmentation_facets.timestamp() != mmuseg_ts) {
std::vector<indexed_triangle_set> its_per_type;
mmuseg_extruders.clear();
@ -2564,6 +2584,7 @@ size_t ModelVolume::split(unsigned int max_extruders)
if (idx == 0) {
this->set_mesh(std::move(mesh));
this->calculate_convex_hull();
this->invalidate_convex_hull_2d();
// Assign a new unique ID, so that a new GLVolume will be generated.
this->set_new_unique_id();
// reset the source to disable reload from disk

View file

@ -650,6 +650,38 @@ private:
friend class ModelVolume;
};
struct RaycastResult
{
Vec2d mouse_position = Vec2d::Zero();
int mesh_id = -1;
Vec3f hit = Vec3f::Zero();
Vec3f normal = Vec3f::Zero();
template<typename Archive> void serialize(Archive &ar) { ar(mouse_position, mesh_id, hit, normal); }
};
struct TextInfo
{
std::string m_font_name;
float m_font_size = 16.f;
int m_curr_font_idx = 0;
bool m_bold = true;
bool m_italic = false;
float m_thickness = 2.f;
float m_embeded_depth = 0.f;
float m_rotate_angle = 0;
float m_text_gap = 0.f;
bool m_is_surface_text = false;
bool m_keep_horizontal = false;
std::string m_text;
RaycastResult m_rr;
template<typename Archive> void serialize(Archive &ar) {
ar(m_font_name, m_font_size, m_curr_font_idx, m_bold, m_italic, m_thickness, m_embeded_depth, m_rotate_angle, m_text_gap, m_is_surface_text, m_keep_horizontal, m_text, m_rr);
}
};
// An object STL, or a modifier volume, over which a different set of parameters shall be applied.
// ModelVolume instances are owned by a ModelObject.
class ModelVolume final : public ObjectBase
@ -756,6 +788,10 @@ public:
const std::shared_ptr<const TriangleMesh>& get_convex_hull_shared_ptr() const { return m_convex_hull; }
//BBS: add convex_hell_2d related logic
const Polygon& get_convex_hull_2d(const Transform3d &trafo_instance) const;
void invalidate_convex_hull_2d()
{
m_convex_hull_2d.clear();
}
// Get count of errors in the mesh
int get_repaired_errors_count() const;
@ -795,6 +831,9 @@ public:
void convert_from_imperial_units();
void convert_from_meters();
void set_text_info(const TextInfo& text_info) { m_text_info = text_info; }
const TextInfo& get_text_info() const { return m_text_info; }
const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const { return m_transformation.get_matrix(dont_translate, dont_rotate, dont_scale, dont_mirror); }
void set_new_unique_id() {
@ -839,6 +878,8 @@ private:
mutable Polygon m_cached_2d_polygon; //BBS, used for convex_hell_2d acceleration
Geometry::Transformation m_transformation;
TextInfo m_text_info;
//BBS: add convex_hell_2d related logic
void calculate_convex_hull_2d(const Geometry::Transformation &transformation) const;
@ -892,7 +933,8 @@ private:
ObjectBase(other),
name(other.name), source(other.source), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull),
config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation),
supported_facets(other.supported_facets), seam_facets(other.seam_facets), mmu_segmentation_facets(other.mmu_segmentation_facets)
supported_facets(other.supported_facets), seam_facets(other.seam_facets), mmu_segmentation_facets(other.mmu_segmentation_facets),
m_text_info(other.m_text_info)
{
assert(this->id().valid());
assert(this->config.id().valid());
@ -957,7 +999,7 @@ private:
// BBS: add backup, check modify
bool mesh_changed = false;
auto tr = m_transformation;
ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull);
ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull, m_text_info);
mesh_changed |= !(tr == m_transformation);
if (mesh_changed) m_transformation.get_matrix(true, true, true, true); // force dirty
auto t = supported_facets.timestamp();
@ -983,7 +1025,7 @@ private:
}
template<class Archive> void save(Archive &ar) const {
bool has_convex_hull = m_convex_hull.get() != nullptr;
ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull);
ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull, m_text_info);
cereal::save_by_value(ar, supported_facets);
cereal::save_by_value(ar, seam_facets);
cereal::save_by_value(ar, mmu_segmentation_facets);
@ -1280,7 +1322,17 @@ public:
}
// Extensions for color print
CustomGCode::Info custom_gcode_per_print_z;
// CustomGCode::Info custom_gcode_per_print_z;
//BBS: replace model custom gcode with current plate custom gcode
int curr_plate_index{ 0 };
std::map<int, CustomGCode::Info> plates_custom_gcodes; //map<plate_index, CustomGCode::Info>
const CustomGCode::Info get_curr_plate_custom_gcodes() const {
if (plates_custom_gcodes.find(curr_plate_index) != plates_custom_gcodes.end()) {
return plates_custom_gcodes.at(curr_plate_index);
}
return CustomGCode::Info();
}
// Default constructor assigns a new ID to the model.
Model() { assert(this->id().valid()); }

View file

@ -1136,21 +1136,8 @@ static std::vector<std::vector<const MMU_Graph::Arc *>> get_all_next_arcs(
continue;
Vec2d arc_line = graph.nodes[arc.to_idx].point - graph.nodes[arc.from_idx].point;
if (arc_line.norm() < 5) { // two points whose distance is less than 5 are considered as one point
Linef process_line_1(graph.nodes[arc.from_idx].point, graph.nodes[arc.to_idx].point);
std::vector<std::vector<const MMU_Graph::Arc *>> next_arcs = get_all_next_arcs(graph, used_arcs, process_line_1, arc, color);
if (next_arcs.empty())
continue;
for (std::vector<const MMU_Graph::Arc *> &next_arc : next_arcs) {
next_continue_arc.emplace_back(&arc);
next_continue_arc.insert(next_continue_arc.end(), next_arc.begin(), next_arc.end());
all_next_arcs.emplace_back(next_continue_arc);
}
} else {
next_continue_arc.emplace_back(&arc);
all_next_arcs.emplace_back(next_continue_arc);
}
next_continue_arc.emplace_back(&arc);
all_next_arcs.emplace_back(next_continue_arc);
}
return all_next_arcs;
}

View file

@ -965,26 +965,19 @@ void PerimeterGenerator::process_classic()
if (is_outer_wall_first ||
//BBS: always print outer wall first when there indeed has brim.
(this->layer_id == 0 &&
this->object_config->brim_type == BrimType::btOuterOnly &&
this->object_config->brim_width.value > 0))
{
if (this->config->wall_infill_order == WallInfillOrder::InnerOuterInnerInfill) {
if (entities.entities.size() > 1) {
std::vector<int> extPs;
for (int i = 0; i < entities.entities.size(); ++i) {
if (entities.entities[i]->role() == erExternalPerimeter)
extPs.push_back(i);
this->object_config->brim_type == BrimType::btOuterOnly &&
this->object_config->brim_width.value > 0))
entities.reverse();
else if (this->config->wall_infill_order == WallInfillOrder::InnerOuterInnerInfill)
if (entities.entities.size() > 1){
int last_outer=0;
int outer = 0;
for (; outer < entities.entities.size(); ++outer)
if (entities.entities[outer]->role() == erExternalPerimeter && outer - last_outer > 1) {
std::swap(entities.entities[outer], entities.entities[outer - 1]);
last_outer = outer;
}
for (int i = 0; i < extPs.size(); ++i) {
if (extPs[i] == 0 || (i > 0 && extPs[i] - 1 == extPs[i - 1]))
continue;
std::swap(entities.entities[extPs[i]], entities.entities[extPs[i] - 1]);
}
}
}
else
entities.reverse();
}
// append perimeters for this slice as a collection
if (! entities.empty())
this->loops->append(entities);
@ -1327,6 +1320,17 @@ void PerimeterGenerator::process_arachne()
}
}
}
// BBS. adjust wall generate seq
if (this->config->wall_infill_order == WallInfillOrder::InnerOuterInnerInfill)
if (ordered_extrusions.size() > 1) {
int last_outer = 0;
int outer = 0;
for (; outer < ordered_extrusions.size(); ++outer)
if (ordered_extrusions[outer].extrusion->inset_idx == 0 && outer - last_outer > 1) {
std::swap(ordered_extrusions[outer], ordered_extrusions[outer - 1]);
last_outer = outer;
}
}
if (this->config->wall_infill_order == WallInfillOrder::InnerOuterInnerInfill) {

View file

@ -161,6 +161,21 @@ public:
Point& operator-=(const Point& rhs) { this->x() -= rhs.x(); this->y() -= rhs.y(); return *this; }
Point& operator*=(const double &rhs) { this->x() = coord_t(this->x() * rhs); this->y() = coord_t(this->y() * rhs); return *this; }
Point operator*(const double &rhs) { return Point(this->x() * rhs, this->y() * rhs); }
bool both_comp(const Point &rhs, const std::string& op) {
if (op == ">")
return this->x() > rhs.x() && this->y() > rhs.y();
else if (op == "<")
return this->x() < rhs.x() && this->y() < rhs.y();
return false;
}
bool any_comp(const Point &rhs, const std::string &op)
{
if (op == ">")
return this->x() > rhs.x() || this->y() > rhs.y();
else if (op == "<")
return this->x() < rhs.x() || this->y() < rhs.y();
return false;
}
void rotate(double angle) { this->rotate(std::cos(angle), std::sin(angle)); }
void rotate(double cos_a, double sin_a) {

View file

@ -771,7 +771,7 @@ static std::vector<std::string> s_Preset_filament_options {
"fan_max_speed", "enable_overhang_bridge_fan", "overhang_fan_speed", "overhang_fan_threshold", "close_fan_the_first_x_layers", "full_fan_speed_layer", "fan_cooling_layer_time", "slow_down_layer_time", "slow_down_min_speed",
"filament_start_gcode", "filament_end_gcode",
// Retract overrides
"filament_retraction_length", "filament_z_hop", "filament_retraction_speed", "filament_deretraction_speed", "filament_retract_restart_extra", "filament_retraction_minimum_travel",
"filament_retraction_length", "filament_z_hop", "filament_z_hop_types", "filament_retraction_speed", "filament_deretraction_speed", "filament_retract_restart_extra", "filament_retraction_minimum_travel",
"filament_retract_when_changing_layer", "filament_wipe", "filament_retract_before_wipe",
// Profile compatibility
"filament_vendor", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits",
@ -799,7 +799,7 @@ static std::vector<std::string> s_Preset_printer_options {
"silent_mode",
// BBS
"scan_first_layer", "machine_load_filament_time", "machine_unload_filament_time", "machine_pause_gcode", "template_custom_gcode",
"nozzle_type", "nozzle_hrc","auxiliary_fan", "nozzle_volume","upward_compatible_machine",
"nozzle_type", "nozzle_hrc","auxiliary_fan", "nozzle_volume","upward_compatible_machine", "z_hop_types",
//SoftFever
"host_type", "print_host", "printhost_apikey",
"printhost_cafile","printhost_port","printhost_authorization_type",
@ -1128,7 +1128,7 @@ void PresetCollection::load_presets(
std::sort(m_presets.begin() + m_num_default_presets, m_presets.end());
//BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": loaded %1% presets from %2%, type %3%")%presets_loaded.size() %dir %Preset::get_type_string(m_type);
this->select_preset(first_visible_idx());
//this->select_preset(first_visible_idx());
if (! errors_cummulative.empty())
throw Slic3r::RuntimeError(errors_cummulative);
}
@ -1555,6 +1555,10 @@ bool PresetCollection::load_user_preset(std::string name, std::map<std::string,
BOOST_LOG_TRIVIAL(error) << "Error in a preset file: The preset \"" <<
name << "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed";
if (need_update) {
if (iter->name == m_edited_preset.name && iter->is_dirty) {
// Keep modifies when update from remote
new_config.apply_only(m_edited_preset.config, m_edited_preset.config.diff(iter->config));
}
iter->config = new_config;
iter->updated_time = cloud_update_time;
iter->version = cloud_version.value();

View file

@ -241,44 +241,17 @@ PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, Forward
//BBS: change system config to json
std::tie(substitutions, errors_cummulative) = this->load_system_presets_from_json(substitution_rule);
//BBS load preset from user's folder, load system default if
//BBS: change directories by design
std::string dir_user_presets;
if (!config.get("preset_folder").empty()) {
dir_user_presets = data_dir() + "/" + PRESET_USER_DIR + "/" + config.get("preset_folder");
// Load default user presets always
load_user_presets(DEFAULT_USER_FOLDER_NAME, substitution_rule);
// BBS load preset from user's folder, load system default if
// BBS: change directories by design
std::string dir_user_presets = config.get("preset_folder");
if (!dir_user_presets.empty()) {
load_user_presets(dir_user_presets, substitution_rule);
}
else {
dir_user_presets = data_dir() + "/" + PRESET_USER_DIR + "/" + DEFAULT_USER_FOLDER_NAME;
}
fs::path user_folder(data_dir() + "/" + PRESET_USER_DIR);
if (!fs::exists(user_folder))
fs::create_directory(user_folder);
fs::path folder(dir_user_presets);
if (!fs::exists(folder))
fs::create_directory(folder);
// BBS do not load sla_print
//BBS: change directoties by design
try {
this->prints.load_presets(dir_user_presets, PRESET_PRINT_NAME, substitutions, substitution_rule);
} catch (const std::runtime_error &err) {
errors_cummulative += err.what();
}
try {
this->filaments.load_presets(dir_user_presets, PRESET_FILAMENT_NAME, substitutions, substitution_rule);
} catch (const std::runtime_error &err) {
errors_cummulative += err.what();
}
try {
this->printers.load_presets(dir_user_presets, PRESET_PRINTER_NAME, substitutions, substitution_rule);
} catch (const std::runtime_error &err) {
errors_cummulative += err.what();
}
this->update_multi_material_filament_presets();
this->update_compatible(PresetSelectCompatibleType::Never);
if (! errors_cummulative.empty())
throw Slic3r::RuntimeError(errors_cummulative);
this->load_selections(config, preferred_selection);
@ -534,7 +507,44 @@ std::string PresetBundle::get_hotend_model_for_printer_model(std::string model_n
return out;
}
PresetsConfigSubstitutions PresetBundle::load_user_presets(AppConfig &config, std::map<std::string, std::map<std::string, std::string>>& my_presets, ForwardCompatibilitySubstitutionRule substitution_rule)
PresetsConfigSubstitutions PresetBundle::load_user_presets(std::string user, ForwardCompatibilitySubstitutionRule substitution_rule)
{
PresetsConfigSubstitutions substitutions;
std::string errors_cummulative;
fs::path user_folder(data_dir() + "/" + PRESET_USER_DIR);
if (!fs::exists(user_folder)) fs::create_directory(user_folder);
std::string dir_user_presets = data_dir() + "/" + PRESET_USER_DIR + "/" + user;
fs::path folder(user_folder / user);
if (!fs::exists(folder)) fs::create_directory(folder);
// BBS do not load sla_print
// BBS: change directoties by design
try {
this->prints.load_presets(dir_user_presets, PRESET_PRINT_NAME, substitutions, substitution_rule);
} catch (const std::runtime_error &err) {
errors_cummulative += err.what();
}
try {
this->filaments.load_presets(dir_user_presets, PRESET_FILAMENT_NAME, substitutions, substitution_rule);
} catch (const std::runtime_error &err) {
errors_cummulative += err.what();
}
try {
this->printers.load_presets(dir_user_presets, PRESET_PRINTER_NAME, substitutions, substitution_rule);
} catch (const std::runtime_error &err) {
errors_cummulative += err.what();
}
if (!errors_cummulative.empty()) throw Slic3r::RuntimeError(errors_cummulative);
this->update_multi_material_filament_presets();
this->update_compatible(PresetSelectCompatibleType::Never);
return PresetsConfigSubstitutions();
}
PresetsConfigSubstitutions PresetBundle::load_user_presets(AppConfig & config,
std::map<std::string, std::map<std::string, std::string>> &my_presets,
ForwardCompatibilitySubstitutionRule substitution_rule)
{
// First load the vendor specific system presets.
PresetsConfigSubstitutions substitutions;
@ -545,6 +555,10 @@ PresetsConfigSubstitutions PresetBundle::load_user_presets(AppConfig &config, st
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" print's selected_idx %1%, selected_name %2%") %prints.get_selected_idx() %prints.get_selected_preset_name();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" filament's selected_idx %1%, selected_name %2%") %filaments.get_selected_idx() %filaments.get_selected_preset_name();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" printers's selected_idx %1%, selected_name %2%") %printers.get_selected_idx() %printers.get_selected_preset_name();
// Sync removing
remove_users_preset(config, &my_presets);
std::map<std::string, std::map<std::string, std::string>>::iterator it;
for (it = my_presets.begin(); it != my_presets.end(); it++) {
std::string name = it->first;
@ -830,13 +844,26 @@ bool PresetBundle::validate_printers(const std::string &name, DynamicPrintConfig
#endif
}
void PresetBundle::remove_users_preset(AppConfig& config)
void PresetBundle::remove_users_preset(AppConfig &config, std::map<std::string, std::map<std::string, std::string>> *my_presets)
{
auto check_removed = [my_presets, this](Preset &preset) -> bool {
if (my_presets == nullptr) return true;
if (my_presets->find(preset.name) != my_presets->end()) return false;
if (!preset.sync_info.empty()) return false; // syncing, not remove
if (preset.setting_id.empty()) return false; // no id, not remove
// Saved preset is removed by another session
if (preset.is_dirty) {
preset.setting_id.clear();
return false;
}
preset.remove_files();
return true;
};
std::string preset_folder_user_id = config.get("preset_folder");
std::string printer_selected_preset_name = printers.get_selected_preset().name;
bool need_reset_printer_preset = false;
for (auto it = printers.begin(); it != printers.end();) {
if (it->is_user() && !it->user_id.empty() && it->user_id.compare(preset_folder_user_id) == 0) {
if (it->is_user() && !it->user_id.empty() && it->user_id.compare(preset_folder_user_id) == 0 && check_removed(*it)) {
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":printers erase %1%, type %2% user_id %3%") % it->name % Preset::get_type_string(it->type) % it->user_id;
if (it->name == printer_selected_preset_name)
need_reset_printer_preset = true;
@ -867,7 +894,7 @@ void PresetBundle::remove_users_preset(AppConfig& config)
bool need_reset_print_preset = false;
// remove preset if user_id is not current user
for (auto it = prints.begin(); it != prints.end();) {
if (it->is_user() && !it->user_id.empty() && it->user_id.compare(preset_folder_user_id) == 0) {
if (it->is_user() && !it->user_id.empty() && it->user_id.compare(preset_folder_user_id) == 0 && check_removed(*it)) {
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":prints erase %1%, type %2% user_id %3%")%it->name %Preset::get_type_string(it->type) %it->user_id;
if (it->name == selected_print_name)
need_reset_print_preset = true;
@ -887,7 +914,7 @@ void PresetBundle::remove_users_preset(AppConfig& config)
std::string selected_filament_name = filaments.get_selected_preset().name;
bool need_reset_filament_preset = false;
for (auto it = filaments.begin(); it != filaments.end();) {
if (it->is_user() && !it->user_id.empty() && it->user_id.compare(preset_folder_user_id) == 0) {
if (it->is_user() && !it->user_id.empty() && it->user_id.compare(preset_folder_user_id) == 0 && check_removed(*it)) {
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":filaments erase %1%, type %2% user_id %3%")%it->name %Preset::get_type_string(it->type) %it->user_id;
if (it->name == selected_filament_name)
need_reset_filament_preset = true;
@ -905,6 +932,8 @@ void PresetBundle::remove_users_preset(AppConfig& config)
filaments.select_preset_by_name(selected_filament_name, false);
}
update_compatible(PresetSelectCompatibleType::Always);
/* set selected preset */
for (size_t i = 0; i < filament_presets.size(); ++i)
{
@ -1268,7 +1297,7 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
this->filament_presets = { filaments.get_selected_preset_name() };
for (unsigned int i = 1; i < 1000; ++ i) {
char name[64];
sprintf(name, "filament_%u", i);
sprintf(name, "filament_%02u", i);
if (! config.has("presets", name))
break;
this->filament_presets.emplace_back(remove_ini_suffix(config.get("presets", name)));
@ -1276,8 +1305,9 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
std::vector<std::string> filament_colors;
if (config.has("presets", "filament_colors")) {
boost::algorithm::split(filament_colors, config.get("presets", "filament_colors"), boost::algorithm::is_any_of(","));
project_config.option<ConfigOptionStrings>("filament_colour")->values = filament_colors;
}
filament_colors.resize(filament_presets.size());
project_config.option<ConfigOptionStrings>("filament_colour")->values = filament_colors;
std::vector<std::string> matrix;
if (config.has("presets", "flush_volumes_matrix")) {
boost::algorithm::split(matrix, config.get("presets", "flush_volumes_matrix"), boost::algorithm::is_any_of("|"));
@ -1340,13 +1370,14 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
void PresetBundle::export_selections(AppConfig &config)
{
assert(this->printers.get_edited_preset().printer_technology() != ptFFF || filament_presets.size() >= 1);
// assert(this->printers.get_edited_preset().printer_technology() != ptFFF || filament_presets.size() > 1 || filaments.get_selected_preset_name() == filament_presets.front());
//assert(this->printers.get_edited_preset().printer_technology() != ptFFF || filament_presets.size() > 1 || filaments.get_selected_preset_name() == filament_presets.front());
config.clear_section("presets");
config.set("presets", PRESET_PRINT_NAME, prints.get_selected_preset_name());
config.set("presets", PRESET_FILAMENT_NAME, filament_presets.front());
for (unsigned i = 1; i < filament_presets.size(); ++i) {
char name[64];
sprintf(name, "filament_%u", i);
assert(!filament_presets[i].empty());
sprintf(name, "filament_%02u", i);
config.set("presets", name, filament_presets[i]);
}
CNumericLocalesSetter locales_setter;
@ -1402,6 +1433,13 @@ unsigned int PresetBundle::sync_ams_list(unsigned int &unknowns)
for (auto &ams : filament_ams_list) {
auto filament_id = ams.opt_string("filament_id", 0u);
auto filament_color = ams.opt_string("filament_colour", 0u);
auto filament_changed = !ams.has("filament_changed") || ams.opt_bool("filament_changed");
if (filament_id.empty()) continue;
if (!filament_changed && this->filament_presets.size() > filament_presets.size()) {
filament_presets.push_back(this->filament_presets[filament_presets.size()]);
filament_colors.push_back(filament_color);
continue;
}
auto iter = std::find_if(filaments.begin(), filaments.end(), [&filament_id](auto &f) { return f.is_compatible && f.is_system && f.filament_id == filament_id; });
if (iter == filaments.end()) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": filament_id %1% not found or system or compatible") % filament_id;
@ -3473,9 +3511,9 @@ std::vector<std::string> PresetBundle::export_current_configs(const std::string
std::string file = path + "/" + preset->name + ".json";
if (boost::filesystem::exists(file) && overwrite < 2) {
overwrite = override_confirm(preset->name);
if (overwrite == 0 || overwrite == 2)
continue;
}
if (overwrite == 0 || overwrite == 2)
continue;
preset->config.save_to_json(file, preset->name, "", preset->version.to_string());
result.push_back(file);
}

View file

@ -47,10 +47,11 @@ public:
void load_selections(AppConfig &config, const PresetPreferences& preferred_selection = PresetPreferences());
// BBS Load user presets
PresetsConfigSubstitutions load_user_presets(std::string user, ForwardCompatibilitySubstitutionRule rule);
PresetsConfigSubstitutions load_user_presets(AppConfig &config, std::map<std::string, std::map<std::string, std::string>>& my_presets, ForwardCompatibilitySubstitutionRule rule);
PresetsConfigSubstitutions import_presets(std::vector<std::string> &files, std::function<int(std::string const &)> override_confirm, ForwardCompatibilitySubstitutionRule rule);
void save_user_presets(AppConfig& config, std::vector<std::string>& need_to_delete_list);
void remove_users_preset(AppConfig &config);
void remove_users_preset(AppConfig &config, std::map<std::string, std::map<std::string, std::string>> * my_presets = nullptr);
void update_user_presets_directory(const std::string preset_folder);
void remove_user_presets_directory(const std::string preset_folder);
void update_system_preset_setting_ids(std::map<std::string, std::map<std::string, std::string>>& system_presets);

View file

@ -246,6 +246,9 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
osteps.emplace_back(posSimplifyPath);
osteps.emplace_back(posSimplifySupportPath);
steps.emplace_back(psSkirtBrim);
}
else if (opt_key == "z_hop_types") {
osteps.emplace_back(posDetectOverhangsForLift);
} else {
// for legacy, if we can't handle this option let's invalidate all steps
//FIXME invalidate all steps of all objects as well?
@ -354,10 +357,21 @@ std::vector<unsigned int> Print::support_material_extruders() const
}
// returns 0-based indices of used extruders
std::vector<unsigned int> Print::extruders() const
std::vector<unsigned int> Print::extruders(bool conside_custom_gcode) const
{
std::vector<unsigned int> extruders = this->object_extruders();
append(extruders, this->support_material_extruders());
if (conside_custom_gcode) {
//BBS
for (auto plate_data : m_model.plates_custom_gcodes) {
for (auto item : plate_data.second.gcodes) {
if (item.type == CustomGCode::Type::ToolChange)
extruders.push_back((unsigned int)item.extruder);
}
}
}
sort_remove_duplicates(extruders);
return extruders;
}
@ -1135,34 +1149,35 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
}
}
const ConfigOptionDef* bed_type_def = print_config_def.get("curr_bed_type");
assert(bed_type_def != nullptr);
if (is_BBL_printer()) {
const t_config_enum_values* bed_type_keys_map = bed_type_def->enum_keys_map;
for (unsigned int extruder_id : extruders) {
const ConfigOptionInts* bed_temp_opt = m_config.option<ConfigOptionInts>(get_bed_temp_key(m_config.curr_bed_type));
for (unsigned int extruder_id : extruders) {
int curr_bed_temp = bed_temp_opt->get_at(extruder_id);
if (curr_bed_temp == 0 && bed_type_keys_map != nullptr) {
std::string bed_type_name;
for (auto item : *bed_type_keys_map) {
if (item.second == m_config.curr_bed_type) {
bed_type_name = item.first;
break;
}
}
if (is_BBL_printer()) {
const t_config_enum_values* bed_type_keys_map = bed_type_def->enum_keys_map;
for (unsigned int extruder_id : extruders) {
const ConfigOptionInts* bed_temp_opt = m_config.option<ConfigOptionInts>(get_bed_temp_key(m_config.curr_bed_type));
for (unsigned int extruder_id : extruders) {
int curr_bed_temp = bed_temp_opt->get_at(extruder_id);
if (curr_bed_temp == 0 && bed_type_keys_map != nullptr) {
std::string bed_type_name;
for (auto item : *bed_type_keys_map) {
if (item.second == m_config.curr_bed_type) {
bed_type_name = item.first;
break;
}
}
StringObjectException except;
except.string = format(L("Plate %d: %s does not support filament %s"), this->get_plate_index() + 1, L(bed_type_name), extruder_id + 1);
except.string += "\n";
except.type = STRING_EXCEPT_FILAMENT_NOT_MATCH_BED_TYPE;
except.params.push_back(std::to_string(this->get_plate_index() + 1));
except.params.push_back(L(bed_type_name));
except.params.push_back(std::to_string(extruder_id + 1));
except.object = nullptr;
return except;
}
StringObjectException except;
except.string = format(L("Plate %d: %s does not support filament %s"), this->get_plate_index() + 1, L(bed_type_name), extruder_id + 1);
except.string += "\n";
except.type = STRING_EXCEPT_FILAMENT_NOT_MATCH_BED_TYPE;
except.params.push_back(std::to_string(this->get_plate_index() + 1));
except.params.push_back(L(bed_type_name));
except.params.push_back(std::to_string(extruder_id+1));
except.object = nullptr;
return except;
}
}
}
}
@ -1307,7 +1322,6 @@ void PrintObject::clear_shared_object()
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, clear previous shared object data %2%")%this %m_shared_object;
m_layers.clear();
m_support_layers.clear();
m_tree_support_layers.clear();
m_shared_object = nullptr;
@ -1320,7 +1334,6 @@ void PrintObject::copy_layers_from_shared_object()
if (m_shared_object) {
m_layers.clear();
m_support_layers.clear();
m_tree_support_layers.clear();
firstLayerObjSliceByVolume.clear();
firstLayerObjSliceByGroups.clear();
@ -1328,7 +1341,6 @@ void PrintObject::copy_layers_from_shared_object()
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, copied layers from object %2%")%this%m_shared_object;
m_layers = m_shared_object->layers();
m_support_layers = m_shared_object->support_layers();
m_tree_support_layers = m_shared_object->tree_support_layers();
firstLayerObjSliceByVolume = m_shared_object->firstLayerObjSlice();
firstLayerObjSliceByGroups = m_shared_object->firstLayerObjGroups();
@ -1660,6 +1672,17 @@ void Print::process(bool use_cache)
}
}
// BBS
for (PrintObject* obj : m_objects) {
if (need_slicing_objects.count(obj) != 0) {
obj->detect_overhangs_for_lift();
}
else {
if (obj->set_started(posDetectOverhangsForLift))
obj->set_done(posDetectOverhangsForLift);
}
}
BOOST_LOG_TRIVIAL(info) << "Slicing process finished." << log_memory_info();
}
@ -1732,13 +1755,6 @@ void Print::_make_skirt()
break;
layer->support_fills.collect_points(object_points);
}
// BBS
for (const TreeSupportLayer* layer : object->m_tree_support_layers) {
if (layer->print_z > skirt_height_z)
break;
layer->support_fills.collect_points(object_points);
}
object_convex_hulls.insert({ object, Slic3r::Geometry::convex_hull(object_points) });
@ -1885,12 +1901,12 @@ Polygons Print::first_layer_islands() const
Polygons object_islands;
for (ExPolygon &expoly : object->m_layers.front()->lslices)
object_islands.push_back(expoly.contour);
if (! object->support_layers().empty())
object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON));
if (! object->tree_support_layers().empty()) {
ExPolygons& expolys_first_layer = object->m_tree_support_layers.front()->lslices;
for (ExPolygon &expoly : expolys_first_layer) {
object_islands.push_back(expoly.contour);
if (!object->support_layers().empty()) {
if (object->support_layers().front()->support_type==stInnerNormal)
object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON));
else if(object->support_layers().front()->support_type==stInnerTree) {
ExPolygons &expolys_first_layer = object->m_support_layers.front()->lslices;
for (ExPolygon &expoly : expolys_first_layer) { object_islands.push_back(expoly.contour); }
}
}
islands.reserve(islands.size() + object_islands.size() * object->instances().size());
@ -2241,7 +2257,7 @@ std::string PrintStatistics::finalize_output_path(const std::string &path_in) co
#define JSON_SUPPORT_LAYER_ISLANDS "support_islands"
#define JSON_SUPPORT_LAYER_FILLS "support_fills"
#define JSON_SUPPORT_LAYER_INTERFACE_ID "interface_id"
#define JSON_SUPPORT_LAYER_TYPE "support_type"
#define JSON_LAYER_REGION_CONFIG_HASH "config_hash"
#define JSON_LAYER_REGION_SLICES "slices"
@ -2872,6 +2888,7 @@ void extract_layer(const json& layer_json, Layer& layer) {
void extract_support_layer(const json& support_layer_json, SupportLayer& support_layer) {
extract_layer(support_layer_json, support_layer);
support_layer.support_type = support_layer_json[JSON_SUPPORT_LAYER_TYPE];
//support_islands
int islands_count = support_layer_json[JSON_SUPPORT_LAYER_ISLANDS].size();
for (int islands_index = 0; islands_index < islands_count; islands_index++)
@ -2900,27 +2917,6 @@ void extract_support_layer(const json& support_layer_json, SupportLayer& support
return;
}
void extract_tree_support_layer(const json& tree_support_layer_json, TreeSupportLayer& tree_support_layer) {
extract_layer(tree_support_layer_json, tree_support_layer);
//support_fills
tree_support_layer.support_fills.no_sort = tree_support_layer_json[JSON_SUPPORT_LAYER_FILLS][JSON_EXTRUSION_NO_SORT];
int treesupport_fills_entities_count = tree_support_layer_json[JSON_SUPPORT_LAYER_FILLS][JSON_EXTRUSION_ENTITIES].size();
for (int treesupport_fills_entities_index = 0; treesupport_fills_entities_index < treesupport_fills_entities_count; treesupport_fills_entities_index++)
{
const json& extrusion_entity_json = tree_support_layer_json[JSON_SUPPORT_LAYER_FILLS][JSON_EXTRUSION_ENTITIES][treesupport_fills_entities_index];
bool ret = convert_extrusion_from_json(extrusion_entity_json, tree_support_layer.support_fills);
if (!ret) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": error parsing fills found at tree_support_layer %1%, print_z %2%")%tree_support_layer.id() %tree_support_layer.print_z;
char error_buf[1024];
::sprintf(error_buf, "Error while parsing fills at tree_support_layer %d, print_z %f", tree_support_layer.id(), tree_support_layer.print_z);
throw Slic3r::FileIOError(error_buf);
}
}
return;
}
int Print::export_cached_data(const std::string& directory, bool with_space)
{
int ret = 0;
@ -2986,7 +2982,7 @@ int Print::export_cached_data(const std::string& directory, bool with_space)
std::string file_name = directory +"/obj_"+std::to_string(arrange_order)+".json";
try {
json root_json, layers_json = json::array(), support_layers_json = json::array(), tree_support_layers_json = json::array();
json root_json, layers_json = json::array(), support_layers_json = json::array();
root_json[JSON_OBJECT_NAME] = model_obj->name;
root_json[JSON_ARRANGE_ORDER] = arrange_order;
@ -3031,6 +3027,7 @@ int Print::export_cached_data(const std::string& directory, bool with_space)
convert_layer_to_json(support_layer_json, support_layer);
support_layer_json[JSON_SUPPORT_LAYER_INTERFACE_ID] = support_layer->interface_id();
support_layer_json[JSON_SUPPORT_LAYER_TYPE] = support_layer->support_type;
//support_islands
for (const ExPolygon& support_island : support_layer->support_islands.expolygons) {
@ -3094,136 +3091,6 @@ int Print::export_cached_data(const std::string& directory, bool with_space)
} // for each layer*/
root_json[JSON_SUPPORT_LAYERS] = std::move(support_layers_json);
//export the tree support layers
std::vector<json> tree_support_layers_json_vector(obj->tree_support_layer_count());
tbb::parallel_for(
tbb::blocked_range<size_t>(0, obj->tree_support_layer_count()),
[&tree_support_layers_json_vector, obj, convert_layer_to_json](const tbb::blocked_range<size_t>& tree_support_layer_range) {
for (size_t ts_layer_index = tree_support_layer_range.begin(); ts_layer_index < tree_support_layer_range.end(); ++ ts_layer_index) {
const TreeSupportLayer *tree_support_layer = obj->get_tree_support_layer(ts_layer_index);
json treesupport_layer_json, treesupport_fills_json, treesupportfills_entities_json = json::array();
//json overhang_areas_json = json::array(), roof_areas_json = json::array(), roof_1st_layer_json = json::array(), floor_areas_json = json::array(), base_areas_json = json::array();
convert_layer_to_json(treesupport_layer_json, tree_support_layer);
//tree_support_fills
treesupport_fills_json[JSON_EXTRUSION_NO_SORT] = tree_support_layer->support_fills.no_sort;
treesupport_fills_json[JSON_EXTRUSION_ENTITY_TYPE] = JSON_EXTRUSION_TYPE_COLLECTION;
for (const ExtrusionEntity* extrusion_entity : tree_support_layer->support_fills.entities) {
json treesupportfill_entity_json, treesupportfill_entity_paths_json = json::array();
bool ret = convert_extrusion_to_json(treesupportfill_entity_json, treesupportfill_entity_paths_json, extrusion_entity);
if (!ret)
continue;
treesupportfills_entities_json.push_back(std::move(treesupportfill_entity_json));
}
treesupport_fills_json[JSON_EXTRUSION_ENTITIES] = std::move(treesupportfills_entities_json);
treesupport_layer_json[JSON_SUPPORT_LAYER_FILLS] = std::move(treesupport_fills_json);
//following data are not needed in the later stage
//overhang_areas
/*for (const ExPolygon& overhang_area : tree_support_layer->overhang_areas) {
json overhang_area_json = overhang_area;
overhang_areas_json.push_back(std::move(overhang_area_json));
}
treesupport_layer_json["overhang_areas"] = std::move(overhang_areas_json);
//roof_areas
for (const ExPolygon& roof_area : tree_support_layer->roof_areas) {
json roof_area_json = roof_area;
roof_areas_json.push_back(std::move(roof_area_json));
}
treesupport_layer_json["roof_areas"] = std::move(roof_areas_json);
//roof_1st_layer
for (const ExPolygon& layer_poly : tree_support_layer->roof_1st_layer) {
json layer_poly_json = layer_poly;
roof_1st_layer_json.push_back(std::move(layer_poly_json));
}
treesupport_layer_json["roof_1st_layer"] = std::move(roof_1st_layer_json);
//floor_areas
for (const ExPolygon& floor_area : tree_support_layer->floor_areas) {
json floor_area_json = floor_area;
floor_areas_json.push_back(std::move(floor_area_json));
}
treesupport_layer_json["floor_areas"] = std::move(floor_areas_json);
//base_areas
for (const ExPolygon& base_area : tree_support_layer->base_areas) {
json base_area_json = base_area;
base_areas_json.push_back(std::move(base_area_json));
}
treesupport_layer_json["base_areas"] = std::move(base_areas_json);*/
tree_support_layers_json_vector[ts_layer_index] = std::move(treesupport_layer_json);
}
}
);
for (int ts_index = 0; ts_index < tree_support_layers_json_vector.size(); ts_index++) {
tree_support_layers_json.push_back(std::move(tree_support_layers_json_vector[ts_index]));
}
tree_support_layers_json_vector.clear();
#if 0
for (const TreeSupportLayer *tree_support_layer : obj->tree_support_layers()) {
json treesupport_layer_json, treesupport_fills_json, treesupportfills_entities_json = json::array();
json overhang_areas_json = json::array(), roof_areas_json = json::array(), roof_1st_layer_json = json::array(), floor_areas_json = json::array(), base_areas_json = json::array();
convert_layer_to_json(treesupport_layer_json, tree_support_layer);
//tree_support_fills
treesupport_fills_json[JSON_EXTRUSION_NO_SORT] = tree_support_layer->support_fills.no_sort;
treesupport_fills_json[JSON_EXTRUSION_ENTITY_TYPE] = JSON_EXTRUSION_TYPE_COLLECTION;
for (const ExtrusionEntity* extrusion_entity : tree_support_layer->support_fills.entities) {
json treesupportfill_entity_json, treesupportfill_entity_paths_json = json::array();
bool ret = convert_extrusion_to_json(treesupportfill_entity_json, treesupportfill_entity_paths_json, extrusion_entity);
if (!ret)
continue;
treesupportfills_entities_json.push_back(std::move(treesupportfill_entity_json));
}
treesupport_fills_json[JSON_EXTRUSION_ENTITIES] = std::move(treesupportfills_entities_json);
treesupport_layer_json[JSON_SUPPORT_LAYER_FILLS] = std::move(treesupport_fills_json);
//overhang_areas
/*for (const ExPolygon& overhang_area : tree_support_layer->overhang_areas) {
json overhang_area_json = overhang_area;
overhang_areas_json.push_back(std::move(overhang_area_json));
}
treesupport_layer_json["overhang_areas"] = std::move(overhang_areas_json);
//roof_areas
for (const ExPolygon& roof_area : tree_support_layer->roof_areas) {
json roof_area_json = roof_area;
roof_areas_json.push_back(std::move(roof_area_json));
}
treesupport_layer_json["roof_areas"] = std::move(roof_areas_json);
//roof_1st_layer
for (const ExPolygon& layer_poly : tree_support_layer->roof_1st_layer) {
json layer_poly_json = layer_poly;
roof_1st_layer_json.push_back(std::move(layer_poly_json));
}
treesupport_layer_json["roof_1st_layer"] = std::move(roof_1st_layer_json);
//floor_areas
for (const ExPolygon& floor_area : tree_support_layer->floor_areas) {
json floor_area_json = floor_area;
floor_areas_json.push_back(std::move(floor_area_json));
}
treesupport_layer_json["floor_areas"] = std::move(floor_areas_json);
//base_areas
for (const ExPolygon& base_area : tree_support_layer->base_areas) {
json base_area_json = base_area;
base_areas_json.push_back(std::move(base_area_json));
}
treesupport_layer_json["base_areas"] = std::move(base_areas_json);*/
tree_support_layers_json.push_back(std::move(treesupport_layer_json));
} // for each layer
#endif
root_json[JSON_TREE_SUPPORT_LAYERS] = std::move(tree_support_layers_json);
filename_vector.push_back(file_name);
json_vector.push_back(std::move(root_json));
@ -3302,7 +3169,6 @@ int Print::load_cached_data(const std::string& directory)
obj->clear_layers();
obj->clear_support_layers();
obj->clear_tree_support_layers();
int arrange_order = model_instance->arrange_order;
if (arrange_order <= 0) {
@ -3354,13 +3220,12 @@ int Print::load_cached_data(const std::string& directory)
std::string name = root_json.at(JSON_OBJECT_NAME);
int order = root_json.at(JSON_ARRANGE_ORDER);
int layer_count = 0, support_layer_count = 0, treesupport_layer_count = 0;
int layer_count = 0, support_layer_count = 0;
layer_count = root_json[JSON_LAYERS].size();
support_layer_count = root_json[JSON_SUPPORT_LAYERS].size();
treesupport_layer_count = root_json[JSON_TREE_SUPPORT_LAYERS].size();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<<boost::format(":will load %1%, arrange_order %2%, layer_count %3%, support_layer_count %4%, treesupport_layer_count %5%")%name %order %layer_count %support_layer_count %treesupport_layer_count;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<<boost::format(":will load %1%, arrange_order %2%, layer_count %3%, support_layer_count %4%")%name %order %layer_count %support_layer_count;
Layer* previous_layer = NULL;
//create layer and layer regions
@ -3441,35 +3306,6 @@ int Print::load_cached_data(const std::string& directory)
}
);
//tree support layers
Layer* previous_tree_support_layer = NULL;
//create tree_support_layers
for (int index = 0; index < treesupport_layer_count; index++)
{
json& layer_json = root_json[JSON_TREE_SUPPORT_LAYERS][index];
TreeSupportLayer* new_tree_support_layer = obj->add_tree_support_layer(layer_json[JSON_LAYER_ID], layer_json[JSON_LAYER_HEIGHT], layer_json[JSON_LAYER_PRINT_Z], layer_json[JSON_LAYER_SLICE_Z]);
if (!new_tree_support_layer) {
BOOST_LOG_TRIVIAL(error) <<__FUNCTION__<< boost::format(":add_support_layer failed, out of memory");
return CLI_OUT_OF_MEMORY;
}
if (previous_tree_support_layer) {
previous_tree_support_layer->upper_layer = new_tree_support_layer;
new_tree_support_layer->lower_layer = previous_tree_support_layer;
}
previous_tree_support_layer = new_tree_support_layer;
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(": finished load support_layers, start to load treesupport_layers.");
tbb::parallel_for(
tbb::blocked_range<size_t>(0, obj->tree_support_layer_count()),
[&root_json, &obj](const tbb::blocked_range<size_t>& tree_support_layer_range) {
for (size_t layer_index = tree_support_layer_range.begin(); layer_index < tree_support_layer_range.end(); ++ layer_index) {
const json& layer_json = root_json[JSON_TREE_SUPPORT_LAYERS][layer_index];
TreeSupportLayer* tree_support_layer = obj->get_tree_support_layer(layer_index);
extract_tree_support_layer(layer_json, *tree_support_layer);
}
}
);
count ++;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(": load object %1% from %2% successfully.")%count%object_filenames[obj_index].first;
}
@ -3489,4 +3325,4 @@ int Print::load_cached_data(const std::string& directory)
return ret;
}
} // namespace Slic3r
} // namespace Slic3r

View file

@ -32,7 +32,6 @@ class Print;
class PrintObject;
class SupportLayer;
// BBS
class TreeSupportLayer;
class TreeSupportData;
class TreeSupport;
@ -86,7 +85,10 @@ enum PrintStep {
enum PrintObjectStep {
posSlice, posPerimeters, posPrepareInfill,
posInfill, posIroning, posSupportMaterial, posSimplifyPath, posSimplifySupportPath, posCount,
posInfill, posIroning, posSupportMaterial, posSimplifyPath, posSimplifySupportPath,
// BBS
posDetectOverhangsForLift,
posCount,
};
// A PrintRegion object represents a group of volumes to print
@ -176,13 +178,6 @@ class ConstSupportLayerPtrsAdaptor : public ConstVectorOfPtrsAdaptor<SupportLaye
ConstSupportLayerPtrsAdaptor(const SupportLayerPtrs *data) : ConstVectorOfPtrsAdaptor<SupportLayer>(data) {}
};
// BBS
typedef std::vector<TreeSupportLayer*> TreeSupportLayerPtrs;
class ConstTreeSupportLayerPtrsAdaptor : public ConstVectorOfPtrsAdaptor<TreeSupportLayer> {
friend PrintObject;
ConstTreeSupportLayerPtrsAdaptor(const TreeSupportLayerPtrs* data) : ConstVectorOfPtrsAdaptor<TreeSupportLayer>(data) {}
};
class BoundingBoxf3; // TODO: for temporary constructor parameter
// Single instance of a PrintObject.
@ -297,14 +292,10 @@ public:
Transform3d trafo_centered() const
{ Transform3d t = this->trafo(); t.pretranslate(Vec3d(- unscale<double>(m_center_offset.x()), - unscale<double>(m_center_offset.y()), 0)); return t; }
const PrintInstances& instances() const { return m_instances; }
// BBS
ConstTreeSupportLayerPtrsAdaptor tree_support_layers() const { return ConstTreeSupportLayerPtrsAdaptor(&m_tree_support_layers); }
// Whoever will get a non-const pointer to PrintObject will be able to modify its layers.
LayerPtrs& layers() { return m_layers; }
SupportLayerPtrs& support_layers() { return m_support_layers; }
// BBS
TreeSupportLayerPtrs& tree_support_layers() { return m_tree_support_layers; }
template<typename PolysType>
static void remove_bridges_from_contacts(
@ -327,7 +318,9 @@ public:
// BBS
void generate_support_preview();
const std::vector<VolumeSlices>& firstLayerObjSlice() const { return firstLayerObjSliceByVolume; }
std::vector<VolumeSlices>& firstLayerObjSliceMod() { return firstLayerObjSliceByVolume; }
const std::vector<groupedVolumeSlices>& firstLayerObjGroups() const { return firstLayerObjSliceByGroups; }
std::vector<groupedVolumeSlices>& firstLayerObjGroupsMod() { return firstLayerObjSliceByGroups; }
bool has_brim() const {
return ((this->config().brim_type != btNoBrim && this->config().brim_width.value > 0.) || this->config().brim_type == btAutoBrim)
@ -365,12 +358,7 @@ public:
Layer* add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z);
// BBS
TreeSupportLayer* get_tree_support_layer(int idx) { return m_tree_support_layers[idx]; }
const TreeSupportLayer* get_tree_support_layer_at_printz(coordf_t print_z, coordf_t epsilon) const;
TreeSupportLayer* get_tree_support_layer_at_printz(coordf_t print_z, coordf_t epsilon);
TreeSupportLayer* add_tree_support_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z);
void clear_tree_support_layers();
size_t tree_support_layer_count() const { return m_tree_support_layers.size(); }
SupportLayer* add_tree_support_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z);
std::shared_ptr<TreeSupportData> alloc_tree_support_preview_cache();
void clear_tree_support_preview_cache() { m_tree_support_preview_cache.reset(); }
@ -381,7 +369,6 @@ public:
SupportLayer* get_support_layer_at_printz(coordf_t print_z, coordf_t epsilon);
SupportLayer* add_support_layer(int id, int interface_id, coordf_t height, coordf_t print_z);
SupportLayerPtrs::iterator insert_support_layer(SupportLayerPtrs::iterator pos, size_t id, size_t interface_id, coordf_t height, coordf_t print_z, coordf_t slice_z);
void delete_support_layer(int idx);
// Initialize the layer_height_profile from the model_object's layer_height_profile, from model_object's layer height table, or from slicing parameters.
// Returns true, if the layer_height_profile was changed.
@ -464,6 +451,10 @@ private:
void slice_volumes();
//BBS
ExPolygons _shrink_contour_holes(double contour_delta, double hole_delta, const ExPolygons& polys) const;
// BBS
void detect_overhangs_for_lift();
void clear_overhangs_for_lift();
// Has any support (not counting the raft).
void detect_surfaces_type();
void process_external_surfaces();
@ -498,7 +489,6 @@ private:
LayerPtrs m_layers;
SupportLayerPtrs m_support_layers;
// BBS
TreeSupportLayerPtrs m_tree_support_layers;
std::shared_ptr<TreeSupportData> m_tree_support_preview_cache;
// this is set to true when LayerRegion->slices is split in top/internal/bottom
@ -661,7 +651,7 @@ public:
std::vector<unsigned int> object_extruders() const;
std::vector<unsigned int> support_material_extruders() const;
std::vector<unsigned int> extruders() const;
std::vector<unsigned int> extruders(bool conside_custom_gcode = false) const;
double max_allowed_layer_height() const;
bool has_support_material() const;
// Make sure the background processing has no access to this model_object during this call!

View file

@ -1020,10 +1020,11 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
new_full_config.option("filament_settings_id", true);
new_full_config.option("printer_settings_id", true);
// BBS
int used_filaments = this->extruders().size();
int used_filaments = this->extruders(true).size();
//new_full_config.normalize_fdm(used_filaments);
new_full_config.normalize_fdm_1();
t_config_option_keys changed_keys = new_full_config.normalize_fdm_2(used_filaments);
t_config_option_keys changed_keys = new_full_config.normalize_fdm_2(objects().size(), used_filaments);
if (changed_keys.size() > 0) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", got changed_keys, size=%1%")%changed_keys.size();
for (int i = 0; i < changed_keys.size(); i++)
@ -1117,17 +1118,19 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
for (const ModelObject *model_object : m_model.objects)
model_object_status_db.add(*model_object, ModelObjectStatus::New);
} else {
if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) {
update_apply_status(num_extruders_changed ||
// Tool change G-codes are applied as color changes for a single extruder printer, no need to invalidate tool ordering.
//FIXME The tool ordering may be invalidated unnecessarily if the custom_gcode_per_print_z.mode is not applicable
// to the active print / model state, and then it is reset, so it is being applicable, but empty, thus the effect is the same.
(num_extruders > 1 && custom_per_printz_gcodes_tool_changes_differ(m_model.custom_gcode_per_print_z.gcodes, model.custom_gcode_per_print_z.gcodes)) ?
// The Tool Ordering and the Wipe Tower are no more valid.
this->invalidate_steps({ psWipeTower, psGCodeExport }) :
// There is no change in Tool Changes stored in custom_gcode_per_print_z, therefore there is no need to update Tool Ordering.
this->invalidate_step(psGCodeExport));
m_model.custom_gcode_per_print_z = model.custom_gcode_per_print_z;
//BBS: replace model custom gcode with current plate custom gcode
m_model.curr_plate_index = model.curr_plate_index;
if (m_model.get_curr_plate_custom_gcodes() != model.get_curr_plate_custom_gcodes()) {
update_apply_status(num_extruders_changed ||
// Tool change G-codes are applied as color changes for a single extruder printer, no need to invalidate tool ordering.
//FIXME The tool ordering may be invalidated unnecessarily if the custom_gcode_per_print_z.mode is not applicable
// to the active print / model state, and then it is reset, so it is being applicable, but empty, thus the effect is the same.
(num_extruders > 1 && custom_per_printz_gcodes_tool_changes_differ(m_model.get_curr_plate_custom_gcodes().gcodes, model.get_curr_plate_custom_gcodes().gcodes)) ?
// The Tool Ordering and the Wipe Tower are no more valid.
this->invalidate_steps({ psWipeTower, psGCodeExport }) :
// There is no change in Tool Changes stored in custom_gcode_per_print_z, therefore there is no need to update Tool Ordering.
this->invalidate_step(psGCodeExport));
m_model.plates_custom_gcodes[m_model.curr_plate_index] = model.get_curr_plate_custom_gcodes();
}
if (model_object_list_equal(m_model, model)) {
// The object list did not change.
@ -1413,8 +1416,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
}
//BBS: check the config again
int new_used_filaments = this->extruders().size();
t_config_option_keys new_changed_keys = new_full_config.normalize_fdm_2(new_used_filaments);
int new_used_filaments = this->extruders(true).size();
t_config_option_keys new_changed_keys = new_full_config.normalize_fdm_2(objects().size(), new_used_filaments);
if (new_changed_keys.size() > 0) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", got new_changed_keys, size=%1%")%new_changed_keys.size();
for (int i = 0; i < new_changed_keys.size(); i++)

View file

@ -159,7 +159,8 @@ static t_config_enum_values s_keys_map_WallInfillOrder {
{ "outer wall/inner wall/infill", int(WallInfillOrder::OuterInnerInfill) },
{ "inner-outer-inner wall/infill", int(WallInfillOrder::InnerOuterInnerInfill) },
{ "infill/inner wall/outer wall", int(WallInfillOrder::InfillInnerOuter) },
{ "infill/outer wall/inner wall", int(WallInfillOrder::InfillOuterInner) }
{ "infill/outer wall/inner wall", int(WallInfillOrder::InfillOuterInner) },
{ "inner-outer-inner wall/infill", int(WallInfillOrder::InnerOuterInnerInfill)}
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(WallInfillOrder)
@ -303,6 +304,14 @@ static t_config_enum_values s_keys_map_PerimeterGeneratorType{
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PerimeterGeneratorType)
static const t_config_enum_values s_keys_map_ZHopType = {
{ "Auto Lift", zhtAuto },
{ "Normal Lift", zhtNormal },
{ "Slope Lift", zhtSlope },
{ "Spiral Lift", zhtSpiral }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(ZHopType)
static void assign_printer_technology_to_unknown(t_optiondef_map &options, PrinterTechnology printer_technology)
{
for (std::pair<const t_config_option_key, ConfigOptionDef> &kvp : options)
@ -813,8 +822,8 @@ void PrintConfigDef::init_fff_params()
def = this->add("brim_type", coEnum);
def->label = L("Brim type");
def->category = L("Support");
def->tooltip = L("This controls brim position including outer side of models, inner side of holes or both. "
"Auto means both the brim position and brim width is analysed and calculated automatically");
def->tooltip = L("This controls the generation of the brim at outer side of models. "
"Auto means the brim width is analysed and calculated automatically.");
def->enum_keys_map = &ConfigOptionEnum<BrimType>::get_enum_values();
def->enum_values.emplace_back("auto_brim");
def->enum_values.emplace_back("outer_only");
@ -1325,6 +1334,7 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("PA-CF");
def->enum_values.push_back("PLA-CF");
def->enum_values.push_back("PET-CF");
def->enum_values.push_back("PETG-CF");
def->enum_values.push_back("PVA");
def->mode = comSimple;
def->set_default_value(new ConfigOptionStrings { "PLA" });
@ -2149,6 +2159,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Diameter of nozzle");
def->sidetext = L("mm");
def->mode = comAdvanced;
def->max = 1.0;
def->set_default_value(new ConfigOptionFloats { 0.4 });
def = this->add("host_type", coEnum);
@ -2183,6 +2194,14 @@ void PrintConfigDef::init_fff_params()
def->readonly = true;
def->set_default_value(new ConfigOptionFloat { 0.0 });
def = this->add("start_end_points", coPoints);
def->label = L("Start end points");
def->tooltip = L("The start and end points which is from cutter area to garbage can.");
def->mode = comDevelop;
def->readonly = true;
// start and end point is from the change_filament_gcode
def->set_default_value(new ConfigOptionPoints{Vec2d(30, -3), Vec2d(54, 245)});
def = this->add("reduce_infill_retraction", coBool);
def->label = L("Reduce infill retraction");
def->tooltip = L("Don't retract when the travel is in infill area absolutely. That means the oozing can't been seen. "
@ -2397,6 +2416,21 @@ void PrintConfigDef::init_fff_params()
def->mode = comSimple;
def->set_default_value(new ConfigOptionFloats { 0.4 });
def = this->add("z_hop_types", coEnums);
def->label = L("Z Hop Type");
def->tooltip = L("");
def->enum_keys_map = &ConfigOptionEnum<ZHopType>::get_enum_values();
def->enum_values.push_back("Auto Lift");
def->enum_values.push_back("Normal Lift");
def->enum_values.push_back("Slope Lift");
def->enum_values.push_back("Spiral Lift");
def->enum_labels.push_back(L("Auto"));
def->enum_labels.push_back(L("Normal"));
def->enum_labels.push_back(L("Slope"));
def->enum_labels.push_back(L("Spiral"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnumsGeneric{ ZHopType::zhtSpiral });
def = this->add("retract_restart_extra", coFloats);
def->label = L("Extra length on restart");
def->tooltip = L("When the retraction is compensated after the travel move, the extruder will push "
@ -2495,11 +2529,11 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionFloat(2));
def = this->add("skirt_height", coInt);
//def->label = L("Skirt height");
def->label = "Skirt height";
//def->tooltip = L("How many layers of skirt. Usually only one layer");
def->label = L("Skirt height");
//def->label = "Skirt height";
def->tooltip = L("How many layers of skirt. Usually only one layer");
def->sidetext = L("layers");
def->mode = comAdvanced;
def->mode = comSimple;
def->max = 10000;
def->set_default_value(new ConfigOptionInt(1));
@ -3226,7 +3260,7 @@ void PrintConfigDef::init_fff_params()
//def->sidetext = L("mm");
def->mode = comDevelop;
// BBS: change data type to floats to add partplate logic
def->set_default_value(new ConfigOptionFloats{ 240. });
def->set_default_value(new ConfigOptionFloats{ 220. });
def = this->add("prime_tower_width", coFloat);
def->label = L("Width");
@ -3409,7 +3443,7 @@ void PrintConfigDef::init_fff_params()
// Declare retract values for filament profile, overriding the printer's extruder profile.
for (const char *opt_key : {
// floats
"retraction_length", "z_hop", "retraction_speed", "deretraction_speed", "retract_restart_extra", "retraction_minimum_travel",
"retraction_length", "z_hop", "z_hop_types", "retraction_speed", "deretraction_speed", "retract_restart_extra", "retraction_minimum_travel",
// BBS: floats
"wipe_distance",
// bools
@ -3423,6 +3457,9 @@ void PrintConfigDef::init_fff_params()
def->full_label = it_opt->second.full_label;
def->tooltip = it_opt->second.tooltip;
def->sidetext = it_opt->second.sidetext;
def->enum_keys_map = it_opt->second.enum_keys_map;
def->enum_labels = it_opt->second.enum_labels;
def->enum_values = it_opt->second.enum_values;
//BBS: shown specific filament retract config because we hide the machine retract into comDevelop mode
if ((strcmp(opt_key, "retraction_length") == 0) ||
(strcmp(opt_key, "z_hop") == 0))
@ -3433,6 +3470,7 @@ void PrintConfigDef::init_fff_params()
case coFloats : def->set_default_value(new ConfigOptionFloatsNullable (static_cast<const ConfigOptionFloats* >(it_opt->second.default_value.get())->values)); break;
case coPercents : def->set_default_value(new ConfigOptionPercentsNullable(static_cast<const ConfigOptionPercents*>(it_opt->second.default_value.get())->values)); break;
case coBools : def->set_default_value(new ConfigOptionBoolsNullable (static_cast<const ConfigOptionBools* >(it_opt->second.default_value.get())->values)); break;
case coEnums : def->set_default_value(new ConfigOptionEnumsGenericNullable(static_cast<const ConfigOptionEnumsGeneric* >(it_opt->second.default_value.get())->values)); break;
default: assert(false);
}
}
@ -3452,7 +3490,7 @@ void PrintConfigDef::init_extruder_option_keys()
// ConfigOptionFloats, ConfigOptionPercents, ConfigOptionBools, ConfigOptionStrings
m_extruder_option_keys = {
"nozzle_diameter", "min_layer_height", "max_layer_height", "extruder_offset",
"retraction_length", "z_hop", "retraction_speed", "deretraction_speed",
"retraction_length", "z_hop", "z_hop_types", "retraction_speed", "deretraction_speed",
"retract_before_wipe", "retract_restart_extra", "retraction_minimum_travel", "wipe", "wipe_distance",
"retract_when_changing_layer", "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour",
"default_filament_profile"
@ -3468,7 +3506,8 @@ void PrintConfigDef::init_extruder_option_keys()
"retraction_speed",
"wipe",
"wipe_distance",
"z_hop"
"z_hop",
"z_hop_types"
};
assert(std::is_sorted(m_extruder_retract_keys.begin(), m_extruder_retract_keys.end()));
}
@ -3477,7 +3516,7 @@ void PrintConfigDef::init_filament_option_keys()
{
m_filament_option_keys = {
"filament_diameter", "min_layer_height", "max_layer_height",
"retraction_length", "z_hop", "retraction_speed", "deretraction_speed",
"retraction_length", "z_hop", "z_hop_types", "retraction_speed", "deretraction_speed",
"retract_before_wipe", "retract_restart_extra", "retraction_minimum_travel", "wipe", "wipe_distance",
"retract_when_changing_layer", "retract_length_toolchange", "retract_restart_extra_toolchange", "filament_colour",
"default_filament_profile"/*,"filament_seam_gap"*/
@ -3493,7 +3532,8 @@ void PrintConfigDef::init_filament_option_keys()
"retraction_speed",
"wipe",
"wipe_distance",
"z_hop"
"z_hop",
"z_hop_types"
};
assert(std::is_sorted(m_filament_retract_keys.begin(), m_filament_retract_keys.end()));
}
@ -4219,7 +4259,8 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
"support_closing_radius",
"remove_freq_sweep", "remove_bed_leveling", "remove_extrusion_calibration",
"support_transition_line_width", "support_transition_speed", "bed_temperature", "bed_temperature_initial_layer",
"can_switch_nozzle_type", "can_add_auxiliary_fan", "extra_flush_volume", "spaghetti_detector", "adaptive_layer_height"
"can_switch_nozzle_type", "can_add_auxiliary_fan", "extra_flush_volume", "spaghetti_detector", "adaptive_layer_height",
"z_hop_type"
};
if (ignore.find(opt_key) != ignore.end()) {
@ -4394,7 +4435,7 @@ void DynamicPrintConfig::normalize_fdm_1()
return;
}
t_config_option_keys DynamicPrintConfig::normalize_fdm_2(int used_filaments)
t_config_option_keys DynamicPrintConfig::normalize_fdm_2(int num_objects, int used_filaments)
{
t_config_option_keys changed_keys;
ConfigOptionBool* ept_opt = this->option<ConfigOptionBool>("enable_prime_tower");
@ -4405,7 +4446,7 @@ t_config_option_keys DynamicPrintConfig::normalize_fdm_2(int used_filaments)
ConfigOptionEnum<TimelapseType>* timelapse_opt = this->option<ConfigOptionEnum<TimelapseType>>("timelapse_type");
bool is_smooth_timelapse = timelapse_opt != nullptr && timelapse_opt->value == TimelapseType::tlSmooth;
if (!is_smooth_timelapse && (used_filaments == 1 || ps_opt->value == PrintSequence::ByObject)) {
if (!is_smooth_timelapse && (used_filaments == 1 || (ps_opt->value == PrintSequence::ByObject && num_objects > 1))) {
if (ept_opt->value) {
ept_opt->value = false;
changed_keys.push_back("enable_prime_tower");
@ -4498,7 +4539,8 @@ void DynamicPrintConfig::set_num_filaments(unsigned int num_filaments)
}
}
std::string DynamicPrintConfig::validate()
//BBS: pass map to recording all invalid valies
std::map<std::string, std::string> DynamicPrintConfig::validate(bool under_cli)
{
// Full print config is initialized from the defaults.
const ConfigOption *opt = this->option("printer_technology", false);
@ -4509,11 +4551,11 @@ std::string DynamicPrintConfig::validate()
FullPrintConfig fpc;
fpc.apply(*this, true);
// Verify this print options through the FullPrintConfig.
return Slic3r::validate(fpc);
return Slic3r::validate(fpc, under_cli);
}
default:
//FIXME no validation on SLA data?
return std::string();
return std::map<std::string, std::string>();
}
}
@ -4584,38 +4626,50 @@ bool DynamicPrintConfig::is_custom_defined()
return false;
}
//BBS: pass map to recording all invalid valies
//FIXME localize this function.
std::string validate(const FullPrintConfig &cfg)
std::map<std::string, std::string> validate(const FullPrintConfig &cfg, bool under_cli)
{
std::map<std::string, std::string> error_message;
// --layer-height
if (cfg.get_abs_value("layer_height") <= 0)
return "Invalid value for --layer-height";
if (fabs(fmod(cfg.get_abs_value("layer_height"), SCALING_FACTOR)) > 1e-4)
return "--layer-height must be a multiple of print resolution";
if (cfg.get_abs_value("layer_height") <= 0) {
error_message.emplace("layer_height", L("invalid value ") + std::to_string(cfg.get_abs_value("layer_height")));
}
else if (fabs(fmod(cfg.get_abs_value("layer_height"), SCALING_FACTOR)) > 1e-4) {
error_message.emplace("layer_height", L("invalid value ") + std::to_string(cfg.get_abs_value("layer_height")));
}
// --first-layer-height
if (cfg.initial_layer_print_height.value <= 0)
return "Invalid value for --first-layer-height";
if (cfg.initial_layer_print_height.value <= 0) {
error_message.emplace("initial_layer_print_height", L("invalid value ") + std::to_string(cfg.initial_layer_print_height.value));
}
// --filament-diameter
for (double fd : cfg.filament_diameter.values)
if (fd < 1)
return "Invalid value for --filament-diameter";
if (fd < 1) {
error_message.emplace("filament_diameter", L("invalid value ") + cfg.filament_diameter.serialize());
break;
}
// --nozzle-diameter
for (double nd : cfg.nozzle_diameter.values)
if (nd < 0.005)
return "Invalid value for --nozzle-diameter";
if (nd < 0.005) {
error_message.emplace("nozzle_diameter", L("invalid value ") + cfg.nozzle_diameter.serialize());
break;
}
// --perimeters
if (cfg.wall_loops.value < 0)
return "Invalid value for --wall_loops";
if (cfg.wall_loops.value < 0) {
error_message.emplace("wall_loops", L("invalid value ") + std::to_string(cfg.wall_loops.value));
}
// --solid-layers
if (cfg.top_shell_layers < 0)
return "Invalid value for --top-solid-layers";
if (cfg.bottom_shell_layers < 0)
return "Invalid value for --bottom-solid-layers";
if (cfg.top_shell_layers < 0) {
error_message.emplace("top_shell_layers", L("invalid value ") + std::to_string(cfg.top_shell_layers));
}
if (cfg.bottom_shell_layers < 0) {
error_message.emplace("bottom_shell_layers", L("invalid value ") + std::to_string(cfg.bottom_shell_layers));
}
if (cfg.use_firmware_retraction.value &&
cfg.gcode_flavor.value != gcfKlipper &&
@ -4626,69 +4680,96 @@ std::string validate(const FullPrintConfig &cfg)
cfg.gcode_flavor.value != gcfMarlinFirmware &&
cfg.gcode_flavor.value != gcfMachinekit &&
cfg.gcode_flavor.value != gcfRepetier)
return "--use-firmware-retraction is only supported by Klipper, Marlin, Smoothie, RepRapFirmware, Repetier and Machinekit firmware";
error_message.emplace("use_firmware_retraction","--use-firmware-retraction is only supported by Klipper, Marlin, Smoothie, RepRapFirmware, Repetier and Machinekit firmware");
if (cfg.use_firmware_retraction.value)
for (unsigned char wipe : cfg.wipe.values)
if (wipe)
return "--use-firmware-retraction is not compatible with --wipe";
error_message.emplace("use_firmware_retraction", "--use-firmware-retraction is not compatible with --wipe");
// --gcode-flavor
if (! print_config_def.get("gcode_flavor")->has_enum_value(cfg.gcode_flavor.serialize()))
return "Invalid value for --gcode-flavor";
if (! print_config_def.get("gcode_flavor")->has_enum_value(cfg.gcode_flavor.serialize())) {
error_message.emplace("gcode_flavor", L("invalid value ") + cfg.gcode_flavor.serialize());
}
// --fill-pattern
if (! print_config_def.get("sparse_infill_pattern")->has_enum_value(cfg.sparse_infill_pattern.serialize()))
return "Invalid value for --fill-pattern";
if (! print_config_def.get("sparse_infill_pattern")->has_enum_value(cfg.sparse_infill_pattern.serialize())) {
error_message.emplace("sparse_infill_pattern", L("invalid value ") + cfg.sparse_infill_pattern.serialize());
}
// --top-fill-pattern
if (! print_config_def.get("top_surface_pattern")->has_enum_value(cfg.top_surface_pattern.serialize()))
return "Invalid value for --top-fill-pattern";
if (! print_config_def.get("top_surface_pattern")->has_enum_value(cfg.top_surface_pattern.serialize())) {
error_message.emplace("top_surface_pattern", L("invalid value ") + cfg.top_surface_pattern.serialize());
}
// --bottom-fill-pattern
if (! print_config_def.get("bottom_surface_pattern")->has_enum_value(cfg.bottom_surface_pattern.serialize()))
return "Invalid value for --bottom-fill-pattern";
if (! print_config_def.get("bottom_surface_pattern")->has_enum_value(cfg.bottom_surface_pattern.serialize())) {
error_message.emplace("bottom_surface_pattern", L("invalid value ") + cfg.bottom_surface_pattern.serialize());
}
// --fill-density
if (fabs(cfg.sparse_infill_density.value - 100.) < EPSILON &&
! print_config_def.get("top_surface_pattern")->has_enum_value(cfg.sparse_infill_pattern.serialize()))
return "The selected fill pattern is not supposed to work at 100% density";
! print_config_def.get("top_surface_pattern")->has_enum_value(cfg.sparse_infill_pattern.serialize())) {
error_message.emplace("sparse_infill_pattern", cfg.sparse_infill_pattern.serialize() + L(" doesn't work at 100%% density "));
}
// --skirt-height
if (cfg.skirt_height < 0)
return "Invalid value for --skirt-height";
if (cfg.skirt_height < 0) {
error_message.emplace("skirt_height", L("invalid value ") + std::to_string(cfg.skirt_height));
}
// --bridge-flow-ratio
if (cfg.bridge_flow <= 0)
return "Invalid value for --bridge-flow-ratio";
if (cfg.bridge_flow <= 0) {
error_message.emplace("bridge_flow", L("invalid value ") + std::to_string(cfg.bridge_flow));
}
// extruder clearance
if (cfg.extruder_clearance_radius <= 0)
return "Invalid value for --extruder-clearance-radius";
if (cfg.extruder_clearance_height_to_rod <= 0)
return "Invalid value for --extruder-clearance-height-to-rod";
if (cfg.extruder_clearance_height_to_lid <= 0)
return "Invalid value for --extruder-clearance-height-to-lid";
if (cfg.extruder_clearance_radius <= 0) {
error_message.emplace("extruder_clearance_radius", L("invalid value ") + std::to_string(cfg.extruder_clearance_radius));
}
if (cfg.extruder_clearance_height_to_rod <= 0) {
error_message.emplace("extruder_clearance_height_to_rod", L("invalid value ") + std::to_string(cfg.extruder_clearance_height_to_rod));
}
if (cfg.extruder_clearance_height_to_lid <= 0) {
error_message.emplace("extruder_clearance_height_to_lid", L("invalid value ") + std::to_string(cfg.extruder_clearance_height_to_lid));
}
// --extrusion-multiplier
for (double em : cfg.filament_flow_ratio.values)
if (em <= 0)
return "Invalid value for --filament-flow-ratio";
if (em <= 0) {
error_message.emplace("filament_flow_ratio", L("invalid value ") + cfg.filament_flow_ratio.serialize());
break;
}
// --spiral-vase
if (cfg.spiral_mode) {
//for non-cli case, we will popup dialog for spiral mode correction
if (cfg.spiral_mode && under_cli) {
// Note that we might want to have more than one perimeter on the bottom
// solid layers.
if (cfg.wall_loops > 1)
return "Can't make more than one perimeter when spiral vase mode is enabled";
else if (cfg.wall_loops < 1)
return "Can't make less than one perimeter when spiral vase mode is enabled";
if (cfg.sparse_infill_density > 0)
return "Spiral vase mode can only print hollow objects, so you need to set Fill density to 0";
if (cfg.top_shell_layers > 0)
return "Spiral vase mode is not compatible with top solid layers";
if (cfg.enable_support || cfg.enforce_support_layers > 0)
return "Spiral vase mode is not compatible with support";
if (cfg.wall_loops != 1) {
error_message.emplace("wall_loops", L("Invalid value when spiral vase mode is enabled: ") + std::to_string(cfg.wall_loops));
//return "Can't make more than one perimeter when spiral vase mode is enabled";
//return "Can't make less than one perimeter when spiral vase mode is enabled";
}
if (cfg.sparse_infill_density > 0) {
error_message.emplace("sparse_infill_density", L("Invalid value when spiral vase mode is enabled: ") + std::to_string(cfg.sparse_infill_density));
//return "Spiral vase mode can only print hollow objects, so you need to set Fill density to 0";
}
if (cfg.top_shell_layers > 0) {
error_message.emplace("top_shell_layers", L("Invalid value when spiral vase mode is enabled: ") + std::to_string(cfg.top_shell_layers));
//return "Spiral vase mode is not compatible with top solid layers";
}
if (cfg.enable_support ) {
error_message.emplace("enable_support", L("Invalid value when spiral vase mode is enabled: ") + std::to_string(cfg.enable_support));
//return "Spiral vase mode is not compatible with support";
}
if (cfg.enforce_support_layers > 0) {
error_message.emplace("enforce_support_layers", L("Invalid value when spiral vase mode is enabled: ") + std::to_string(cfg.enforce_support_layers));
//return "Spiral vase mode is not compatible with support";
}
}
// extrusion widths
@ -4706,8 +4787,10 @@ std::string validate(const FullPrintConfig &cfg)
"initial_layer_line_width" };
for (size_t i = 0; i < sizeof(widths) / sizeof(widths[i]); ++ i) {
std::string key(widths[i]);
if (cfg.get_abs_value(key) > 2.5 * max_nozzle_diameter)
return std::string("Too Large line width: ") + key;
if (cfg.get_abs_value(key) > 2.5 * max_nozzle_diameter) {
error_message.emplace(key, L("too large line width ") + std::to_string(cfg.get_abs_value(key)));
//return std::string("Too Large line width: ") + key;
}
}
}
@ -4750,12 +4833,15 @@ std::string validate(const FullPrintConfig &cfg)
break;
default:;
}
if (out_of_range)
return std::string("Value out of range: " + opt_key);
if (out_of_range) {
if (error_message.find(opt_key) == error_message.end())
error_message.emplace(opt_key, opt->serialize() + L(" not in range ") +"[" + std::to_string(optdef->min) + "," + std::to_string(optdef->max) + "]");
//return std::string("Value out of range: " + opt_key);
}
}
// The configuration is valid.
return "";
return error_message;
}
// Declare and initialize static caches of StaticPrintConfig derived classes.
@ -5127,6 +5213,26 @@ Points get_bed_shape(const PrintConfig &cfg)
Points get_bed_shape(const SLAPrinterConfig &cfg) { return to_points(cfg.printable_area.values); }
Polygon get_bed_shape_with_excluded_area(const PrintConfig& cfg)
{
Polygon bed_poly;
bed_poly.points = get_bed_shape(cfg);
Points excluse_area_points = to_points(cfg.bed_exclude_area.values);
Polygons exclude_polys;
Polygon exclude_poly;
for (int i = 0; i < excluse_area_points.size(); i++) {
auto pt = excluse_area_points[i];
exclude_poly.points.emplace_back(pt);
if (i % 4 == 3) { // exclude areas are always rectangle
exclude_polys.push_back(exclude_poly);
exclude_poly.points.clear();
}
}
auto tmp = diff({ bed_poly }, exclude_polys);
if (!tmp.empty()) bed_poly = tmp[0];
return bed_poly;
}
} // namespace Slic3r
#include <cereal/types/polymorphic.hpp>

View file

@ -18,7 +18,7 @@
#include "libslic3r.h"
#include "Config.hpp"
#include "Polygon.hpp"
#include <boost/preprocessor/facilities/empty.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
@ -78,6 +78,7 @@ enum class WallInfillOrder {
enum class PrintSequence {
ByLayer,
ByObject,
ByDefault,
Count,
};
@ -207,6 +208,15 @@ enum NozzleType {
ntCount
};
// BBS
enum ZHopType {
zhtAuto = 0,
zhtNormal,
zhtSlope,
zhtSpiral,
zhtCount
};
static std::string bed_type_to_gcode_string(const BedType type)
{
std::string type_str;
@ -368,15 +378,16 @@ public:
void normalize_fdm(int used_filaments = 0);
void normalize_fdm_1();
//return the changed param set
t_config_option_keys normalize_fdm_2(int used_filaments = 0);
t_config_option_keys normalize_fdm_2(int num_objects, int used_filaments = 0);
void set_num_extruders(unsigned int num_extruders);
// BBS
void set_num_filaments(unsigned int num_filaments);
//BBS
// Validate the PrintConfig. Returns an empty string on success, otherwise an error message is returned.
std::string validate();
std::map<std::string, std::string> validate(bool under_cli = false);
// Verify whether the opt_key has not been obsoleted or renamed.
// Both opt_key and value may be modified by handle_legacy().
@ -842,7 +853,8 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloats, retraction_length))
((ConfigOptionFloats, retract_length_toolchange))
((ConfigOptionFloats, z_hop))
((ConfigOptionEnum<LiftType>, z_lift_type))
// BBS
((ConfigOptionEnumsGeneric, z_hop_types))
((ConfigOptionFloats, retract_restart_extra))
((ConfigOptionFloats, retract_restart_extra_toolchange))
((ConfigOptionFloats, retraction_speed))
@ -978,6 +990,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
// BBS: not in any preset, calculated before slicing
((ConfigOptionBool, has_prime_tower))
((ConfigOptionFloat, nozzle_volume))
((ConfigOptionPoints, start_end_points))
((ConfigOptionEnum<TimelapseType>, timelapse_type))
((ConfigOptionPoints, thumbnails))
// BBS: move from PrintObjectConfig
@ -994,7 +1007,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE0(
)
// Validate the FullPrintConfig. Returns an empty string on success, otherwise an error message is returned.
std::string validate(const FullPrintConfig &config);
std::map<std::string, std::string> validate(const FullPrintConfig &config, bool under_cli = false);
PRINT_CONFIG_CLASS_DEFINE(
SLAPrintConfig,
@ -1282,6 +1295,7 @@ private:
Points get_bed_shape(const DynamicPrintConfig &cfg);
Points get_bed_shape(const PrintConfig &cfg);
Points get_bed_shape(const SLAPrinterConfig &cfg);
Slic3r::Polygon get_bed_shape_with_excluded_area(const PrintConfig& cfg);
// ModelConfig is a wrapper around DynamicPrintConfig with an addition of a timestamp.
// Each change of ModelConfig is tracked by assigning a new timestamp from a global counter.

View file

@ -91,7 +91,6 @@ PrintObject::~PrintObject()
if (m_shared_regions && -- m_shared_regions->m_ref_cnt == 0) delete m_shared_regions;
clear_layers();
clear_support_layers();
clear_tree_support_layers();
}
PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances)
@ -410,11 +409,49 @@ void PrintObject::ironing()
}
}
// BBS
void PrintObject::clear_overhangs_for_lift()
{
if (!m_shared_object) {
for (Layer* l : m_layers)
l->loverhangs.clear();
}
}
static const float g_min_overhang_percent_for_lift = 0.3f;
void PrintObject::detect_overhangs_for_lift()
{
if (this->set_started(posDetectOverhangsForLift)) {
const float min_overlap = m_config.line_width * g_min_overhang_percent_for_lift;
size_t num_layers = this->layer_count();
size_t num_raft_layers = m_slicing_params.raft_layers();
m_print->set_status(78, L("Detect overhangs for auto-lift"));
this->clear_overhangs_for_lift();
tbb::spin_mutex layer_storage_mutex;
tbb::parallel_for(tbb::blocked_range<size_t>(num_raft_layers + 1, num_layers),
[this, min_overlap](const tbb::blocked_range<size_t>& range)
{
for (size_t layer_id = range.begin(); layer_id < range.end(); ++layer_id) {
Layer& layer = *m_layers[layer_id];
Layer& lower_layer = *layer.lower_layer;
ExPolygons overhangs = diff_ex(layer.lslices, offset_ex(lower_layer.lslices, scale_(min_overlap)));
layer.loverhangs = std::move(offset2_ex(overhangs, -0.1f * scale_(m_config.line_width), 0.1f * scale_(m_config.line_width)));
}
});
this->set_done(posDetectOverhangsForLift);
}
}
void PrintObject::generate_support_material()
{
if (this->set_started(posSupportMaterial)) {
this->clear_support_layers();
this->clear_tree_support_layers();
if ((this->has_support() && m_layers.size() > 1) || (this->has_raft() && ! m_layers.empty())) {
m_print->set_status(50, L("Generating support"));
@ -483,16 +520,6 @@ void PrintObject::simplify_extrusion_path()
}
);
m_print->throw_if_canceled();
tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_tree_support_layers.size()),
[this](const tbb::blocked_range<size_t>& range) {
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
m_print->throw_if_canceled();
m_tree_support_layers[layer_idx]->simplify_support_extrusion_path();
}
}
);
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Simplify extrusion path of support in parallel - end";
this->set_done(posSimplifySupportPath);
}
@ -564,19 +591,6 @@ Layer* PrintObject::add_layer(int id, coordf_t height, coordf_t print_z, coordf_
return m_layers.back();
}
// BBS
const TreeSupportLayer* PrintObject::get_tree_support_layer_at_printz(coordf_t print_z, coordf_t epsilon) const
{
coordf_t limit = print_z - epsilon;
auto it = Slic3r::lower_bound_by_predicate(m_tree_support_layers.begin(), m_tree_support_layers.end(), [limit](const TreeSupportLayer* layer) { return layer->print_z < limit; });
return (it == m_tree_support_layers.end() || (*it)->print_z > print_z + epsilon) ? nullptr : *it;
}
TreeSupportLayer* PrintObject::get_tree_support_layer_at_printz(coordf_t print_z, coordf_t epsilon)
{
return const_cast<TreeSupportLayer*>(std::as_const(*this).get_tree_support_layer_at_printz(print_z, epsilon));
}
const SupportLayer* PrintObject::get_support_layer_at_printz(coordf_t print_z, coordf_t epsilon) const
{
coordf_t limit = print_z - epsilon;
@ -589,12 +603,17 @@ SupportLayer* PrintObject::get_support_layer_at_printz(coordf_t print_z, coordf_
return const_cast<SupportLayer*>(std::as_const(*this).get_support_layer_at_printz(print_z, epsilon));
}
void PrintObject::clear_tree_support_layers()
void PrintObject::clear_support_layers()
{
if (!m_shared_object) {
for (TreeSupportLayer* l : m_tree_support_layers)
for (SupportLayer* l : m_support_layers)
delete l;
m_tree_support_layers.clear();
m_support_layers.clear();
for (auto l : m_layers) {
l->sharp_tails.clear();
l->sharp_tails_height.clear();
l->cantilevers.clear();
}
}
}
@ -614,19 +633,11 @@ std::shared_ptr<TreeSupportData> PrintObject::alloc_tree_support_preview_cache()
return m_tree_support_preview_cache;
}
TreeSupportLayer* PrintObject::add_tree_support_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z)
SupportLayer* PrintObject::add_tree_support_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z)
{
m_tree_support_layers.emplace_back(new TreeSupportLayer(id, this, height, print_z, slice_z));
return m_tree_support_layers.back();
}
void PrintObject::clear_support_layers()
{
if (!m_shared_object) {
for (Layer *l : m_support_layers)
delete l;
m_support_layers.clear();
}
m_support_layers.emplace_back(new SupportLayer(id, 0, this, height, print_z, slice_z));
m_support_layers.back()->support_type = stInnerTree;
return m_support_layers.back();
}
SupportLayer* PrintObject::add_support_layer(int id, int interface_id, coordf_t height, coordf_t print_z)
@ -717,7 +728,14 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "support_top_z_distance"
|| opt_key == "support_bottom_z_distance"
|| opt_key == "xy_hole_compensation"
|| opt_key == "xy_contour_compensation") {
|| opt_key == "xy_contour_compensation"
//BBS: [Arthur] the following params affect bottomBridge surface type detection
|| opt_key == "support_type"
|| opt_key == "bridge_no_support"
|| opt_key == "max_bridge_length"
|| opt_key == "support_interface_top_layers"
|| opt_key == "support_critical_regions_only"
) {
steps.emplace_back(posSlice);
} else if (opt_key == "enable_support") {
steps.emplace_back(posSupportMaterial);
@ -762,9 +780,23 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "tree_support_branch_angle"
|| opt_key == "tree_support_wall_count") {
steps.emplace_back(posSupportMaterial);
} else if (opt_key == "bottom_shell_layers") {
} else if (
opt_key == "bottom_shell_layers"
|| opt_key == "top_shell_layers") {
steps.emplace_back(posPrepareInfill);
if (m_print->config().spiral_mode) {
const auto *old_shell_layers = old_config.option<ConfigOptionInt>(opt_key);
const auto *new_shell_layers = new_config.option<ConfigOptionInt>(opt_key);
assert(old_shell_layers && new_shell_layers);
bool value_changed = (old_shell_layers->value == 0 && new_shell_layers->value > 0) ||
(old_shell_layers->value > 0 && new_shell_layers->value == 0);
if (value_changed && this->object_extruders().size() > 1) {
steps.emplace_back(posSlice);
}
else if (m_print->config().spiral_mode && opt_key == "bottom_shell_layers") {
// Changing the number of bottom layers when a spiral vase is enabled requires re-slicing the object again.
// Otherwise, holes in the bottom layers could be filled, as is reported in GH #5528.
steps.emplace_back(posSlice);
@ -773,7 +805,6 @@ bool PrintObject::invalidate_state_by_config_options(
opt_key == "interface_shells"
|| opt_key == "infill_combination"
|| opt_key == "bottom_shell_thickness"
|| opt_key == "top_shell_layers"
|| opt_key == "top_shell_thickness"
|| opt_key == "minimum_sparse_infill_area"
|| opt_key == "sparse_infill_filament"
@ -971,9 +1002,17 @@ void PrintObject::detect_surfaces_type()
[this, region_id, interface_shells, &surfaces_new](const tbb::blocked_range<size_t>& range) {
// If we have soluble support material, don't bridge. The overhang will be squished against a soluble layer separating
// the support from the print.
SurfaceType surface_type_bottom_other =
(this->has_support() && m_config.support_top_z_distance.value == 0) ?
stBottom : stBottomBridge;
// BBS: the above logic only applys for normal(auto) support. Complete logic:
// 1. has support, top z distance=0 (soluble material), auto support
// 2. for normal(auto), bridge_no_support is off
// 3. for tree(auto), interface top layers=0, max bridge length=0, support_critical_regions_only=false (only in this way the bridge is fully supported)
bool bottom_is_fully_supported = this->has_support() && m_config.support_top_z_distance.value == 0 && is_auto(m_config.support_type.value);
if (m_config.support_type.value == stNormalAuto)
bottom_is_fully_supported &= !m_config.bridge_no_support.value;
else if (m_config.support_type.value == stTreeAuto) {
bottom_is_fully_supported &= (m_config.support_interface_top_layers.value > 0 && m_config.max_bridge_length.value == 0 && m_config.support_critical_regions_only.value==false);
}
SurfaceType surface_type_bottom_other = bottom_is_fully_supported ? stBottom : stBottomBridge;
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
m_print->throw_if_canceled();
// BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << region_id << " and layer " << layer->print_z;
@ -2389,7 +2428,7 @@ void PrintObject::_generate_support_material()
support_material.generate(*this);
TreeSupport tree_support(*this, m_slicing_params);
tree_support.generate_support_areas();
tree_support.generate();
}
// BBS
@ -2545,6 +2584,7 @@ template void PrintObject::remove_bridges_from_contacts<Polygons>(
SupportNecessaryType PrintObject::is_support_necessary()
{
#if 0
static const double super_overhang_area_threshold = SQ(scale_(5.0));
double threshold_rad = (m_config.support_threshold_angle.value < EPSILON ? 30 : m_config.support_threshold_angle.value + 1) * M_PI / 180.;
@ -2627,7 +2667,16 @@ SupportNecessaryType PrintObject::is_support_necessary()
if (!exceed_overhang.empty())
return LargeOverhang;
}
#else
TreeSupport tree_support(*this, m_slicing_params);
tree_support.support_type = SupportType::stTreeAuto; // need to set support type to fully utilize the power of feature detection
tree_support.detect_overhangs();
this->clear_support_layers();
if (tree_support.has_sharp_tails)
return SharpTail;
else if (tree_support.has_cantilever)
return LargeOverhang;
#endif
return NoNeedSupp;
}
@ -2820,10 +2869,6 @@ static void project_triangles_to_slabs(ConstLayerPtrsAdaptor layers, const index
void PrintObject::project_and_append_custom_facets(
bool seam, EnforcerBlockerType type, std::vector<Polygons>& out) const
{
// BBS: Approve adding enforcer support on vertical faces
SlabSlicingConfig config;
config.isVertical = type == EnforcerBlockerType::ENFORCER ? true : false;
for (const ModelVolume* mv : this->model_object()->volumes)
if (mv->is_model_part()) {
const indexed_triangle_set custom_facets = seam
@ -2837,7 +2882,7 @@ void PrintObject::project_and_append_custom_facets(
else {
std::vector<Polygons> projected;
// Support blockers or enforcers. Project downward facing painted areas upwards to their respective slicing plane.
slice_mesh_slabs(custom_facets, zs_from_layers(this->layers()), this->trafo_centered() * mv->get_matrix(), nullptr, &projected, [](){}, config);
slice_mesh_slabs(custom_facets, zs_from_layers(this->layers()), this->trafo_centered() * mv->get_matrix(), nullptr, &projected, [](){});
// Merge these projections with the output, layer by layer.
assert(! projected.empty());
assert(out.empty() || out.size() == projected.size());

View file

@ -458,6 +458,151 @@ static std::vector<std::vector<ExPolygons>> slices_to_regions(
return slices_by_region;
}
//BBS: justify whether a volume is connected to another one
bool doesVolumeIntersect(VolumeSlices& vs1, VolumeSlices& vs2)
{
if (vs1.volume_id == vs2.volume_id) return true;
if (vs1.slices.size() != vs2.slices.size()) return false;
for (int i = 0; i != vs1.slices.size(); ++i) {
if (vs1.slices[i].empty()) continue;
if (!vs2.slices[i].empty() && !intersection_ex(vs1.slices[i], vs2.slices[i]).empty()) return true;
if (i + 1 != vs2.slices.size() && !vs2.slices[i + 1].empty()) {
if (!intersection_ex(vs1.slices[i], vs2.slices[i + 1]).empty()) return true;
}
if (i - 1 >= 0 && !vs2.slices[i - 1].empty()) {
if (!intersection_ex(vs1.slices[i], vs2.slices[i - 1]).empty()) return true;
}
}
return false;
}
//BBS: grouping the volumes of an object according to their connection relationship
bool groupingVolumes(std::vector<VolumeSlices> objSliceByVolume, std::vector<groupedVolumeSlices>& groups, double resolution, int firstLayerReplacedBy)
{
std::vector<int> groupIndex(objSliceByVolume.size(), -1);
double offsetValue = 0.05 / SCALING_FACTOR;
for (int i = 0; i != objSliceByVolume.size(); ++i) {
for (int j = 0; j != objSliceByVolume[i].slices.size(); ++j) {
objSliceByVolume[i].slices[j] = offset_ex(objSliceByVolume[i].slices[j], offsetValue);
for (ExPolygon& poly_ex : objSliceByVolume[i].slices[j])
poly_ex.douglas_peucker(resolution);
}
}
for (int i = 0; i != objSliceByVolume.size(); ++i) {
if (groupIndex[i] < 0) {
groupIndex[i] = i;
}
for (int j = i + 1; j != objSliceByVolume.size(); ++j) {
if (doesVolumeIntersect(objSliceByVolume[i], objSliceByVolume[j])) {
if (groupIndex[j] < 0) groupIndex[j] = groupIndex[i];
if (groupIndex[j] != groupIndex[i]) {
int retain = std::min(groupIndex[i], groupIndex[j]);
int cover = std::max(groupIndex[i], groupIndex[j]);
for (int k = 0; k != objSliceByVolume.size(); ++k) {
if (groupIndex[k] == cover) groupIndex[k] = retain;
}
}
}
}
}
std::vector<int> groupVector{};
for (int gi : groupIndex) {
bool exist = false;
for (int gv : groupVector) {
if (gv == gi) {
exist = true;
break;
}
}
if (!exist) groupVector.push_back(gi);
}
// group volumes and their slices according to the grouping Vector
groups.clear();
for (int gv : groupVector) {
groupedVolumeSlices gvs;
gvs.groupId = gv;
for (int i = 0; i != objSliceByVolume.size(); ++i) {
if (groupIndex[i] == gv) {
gvs.volume_ids.push_back(objSliceByVolume[i].volume_id);
append(gvs.slices, objSliceByVolume[i].slices[firstLayerReplacedBy]);
}
}
// the slices of a group should be unioned
gvs.slices = offset_ex(union_ex(gvs.slices), -offsetValue);
for (ExPolygon& poly_ex : gvs.slices)
poly_ex.douglas_peucker(resolution);
groups.push_back(gvs);
}
return true;
}
//BBS: filter the members of "objSliceByVolume" such that only "model_part" are included
std::vector<VolumeSlices> findPartVolumes(const std::vector<VolumeSlices>& objSliceByVolume, ModelVolumePtrs model_volumes) {
std::vector<VolumeSlices> outPut;
for (const auto& vs : objSliceByVolume) {
for (const auto& mv : model_volumes) {
if (vs.volume_id == mv->id() && mv->is_model_part()) outPut.push_back(vs);
}
}
return outPut;
}
void applyNegtiveVolumes(ModelVolumePtrs model_volumes, const std::vector<VolumeSlices>& objSliceByVolume, std::vector<groupedVolumeSlices>& groups, double resolution) {
ExPolygons negTotal;
for (const auto& vs : objSliceByVolume) {
for (const auto& mv : model_volumes) {
if (vs.volume_id == mv->id() && mv->is_negative_volume()) {
if (vs.slices.size() > 0) {
append(negTotal, vs.slices.front());
}
}
}
}
for (auto& g : groups) {
g.slices = diff_ex(g.slices, negTotal);
for (ExPolygon& poly_ex : g.slices)
poly_ex.douglas_peucker(resolution);
}
}
void reGroupingLayerPolygons(std::vector<groupedVolumeSlices>& gvss, ExPolygons eps)
{
std::vector<int> epsIndex;
epsIndex.resize(eps.size(), -1);
for (int ie = 0; ie != eps.size(); ie++) {
for (int iv = 0; iv != gvss.size(); iv++) {
auto clipedExPolys = diff_ex(eps[ie], gvss[iv].slices);
double area = 0;
for (const auto& ce : clipedExPolys) {
area += ce.area();
}
if (eps[ie].area() > 0 && area / eps[ie].area() < 0.3) {
epsIndex[ie] = iv;
break;
}
}
}
for (int iv = 0; iv != gvss.size(); iv++)
gvss[iv].slices.clear();
for (int ie = 0; ie != eps.size(); ie++) {
if (epsIndex[ie] >= 0)
gvss[epsIndex[ie]].slices.push_back(eps[ie]);
}
}
std::string fix_slicing_errors(PrintObject* object, LayerPtrs &layers, const std::function<void()> &throw_if_canceled)
{
std::string error_msg;//BBS
@ -567,6 +712,21 @@ std::string fix_slicing_errors(PrintObject* object, LayerPtrs &layers, const std
//BBS
if(error_msg.empty() && !buggy_layers.empty())
error_msg = L("The model has too many empty layers.");
// BBS: first layer slices are sorted by volume group, if the first layer is empty and replaced by the 2nd layer
// the later will be stored in "object->firstLayerObjGroupsMod()"
int firstLayerReplacedBy = 0;
if (!buggy_layers.empty() && buggy_layers.front() == 0)
firstLayerReplacedBy = 1;
const auto scaled_resolution = scaled<double>(object->print()->config().resolution.value);
auto partsObjSliceByVolume = findPartVolumes(object->firstLayerObjSliceMod(), object->model_object()->volumes);
groupingVolumes(partsObjSliceByVolume, object->firstLayerObjGroupsMod(), scaled_resolution, firstLayerReplacedBy);
applyNegtiveVolumes(object->model_object()->volumes, object->firstLayerObjSliceMod(), object->firstLayerObjGroupsMod(), scaled_resolution);
// BBS: the actual first layer slices stored in layers are re-sorted by volume group and will be used to generate brim
reGroupingLayerPolygons(object->firstLayerObjGroupsMod(), layers.front()->lslices);
return error_msg;
}
@ -616,7 +776,7 @@ void PrintObject::slice()
}
});
if (m_layers.empty())
throw Slic3r::SlicingError("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n");
throw Slic3r::SlicingError(L("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"));
// BBS
this->set_done(posSlice);
@ -749,127 +909,7 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance
});
}
//BBS: justify whether a volume is connected to another one
bool doesVolumeIntersect(VolumeSlices& vs1, VolumeSlices& vs2)
{
if (vs1.volume_id == vs2.volume_id) return true;
if (vs1.slices.size() != vs2.slices.size()) return false;
for (int i = 0; i != vs1.slices.size(); ++i) {
if (vs1.slices[i].empty()) continue;
if (!vs2.slices[i].empty() && !intersection_ex(vs1.slices[i], vs2.slices[i]).empty()) return true;
if (i + 1 != vs2.slices.size() && !vs2.slices[i + 1].empty()) {
if (!intersection_ex(vs1.slices[i], vs2.slices[i + 1]).empty()) return true;
}
if (i - 1 >= 0 && !vs2.slices[i - 1].empty()) {
if (!intersection_ex(vs1.slices[i], vs2.slices[i - 1]).empty()) return true;
}
}
return false;
}
//BBS: grouping the volumes of an object according to their connection relationship
bool groupingVolumes(std::vector<VolumeSlices> objSliceByVolume, std::vector<groupedVolumeSlices>& groups, double resolution)
{
int existGroups = 0;
std::vector<int> groupIndex(objSliceByVolume.size(), -1);
double offsetValue = 0.15 / SCALING_FACTOR;
for (int i = 0; i != objSliceByVolume.size(); ++i) {
for (int j = 0; j != objSliceByVolume[i].slices.size(); ++j) {
objSliceByVolume[i].slices[j] = offset_ex(objSliceByVolume[i].slices[j], offsetValue);
for (ExPolygon& poly_ex : objSliceByVolume[i].slices[j])
poly_ex.douglas_peucker(resolution);
}
}
for (int i = 0; i != objSliceByVolume.size(); ++i) {
if (groupIndex[i] < 0) {
groupIndex[i] = i;
++existGroups;
}
for (int j = i + 1; j != objSliceByVolume.size(); ++j) {
if (doesVolumeIntersect(objSliceByVolume[i], objSliceByVolume[j])) {
if (groupIndex[j] < 0) groupIndex[j] = groupIndex[i];
if (groupIndex[j] != groupIndex[i]) {
int retain = std::min(groupIndex[i], groupIndex[j]);
int cover = std::max(groupIndex[i], groupIndex[j]);
for (int k = 0; k != objSliceByVolume.size(); ++k) {
if (groupIndex[k] == cover) groupIndex[k] = retain;
}
--existGroups;
}
}
}
}
std::vector<int> groupVector{};
for (int gi : groupIndex) {
bool exist = false;
for (int gv : groupVector) {
if (gv == gi) {
exist = true;
break;
}
}
if (!exist) groupVector.push_back(gi);
}
// group volumes and their slices according to the grouping Vector
groups.clear();
for (int gv : groupVector) {
groupedVolumeSlices gvs;
gvs.groupId = gv;
for (int i = 0; i != objSliceByVolume.size(); ++i) {
if (groupIndex[i] == gv) {
gvs.volume_ids.push_back(objSliceByVolume[i].volume_id);
append(gvs.slices, objSliceByVolume[i].slices.front());
}
}
// the slices of a group should be unioned
gvs.slices = offset_ex(union_ex(gvs.slices), -offsetValue);
for (ExPolygon& poly_ex : gvs.slices)
poly_ex.douglas_peucker(resolution);
groups.push_back(gvs);
}
return true;
}
//BBS: filter the members of "objSliceByVolume" such that only "model_part" are included
std::vector<VolumeSlices> findPartVolumes(const std::vector<VolumeSlices>& objSliceByVolume, ModelVolumePtrs model_volumes) {
std::vector<VolumeSlices> outPut;
for (const auto& vs : objSliceByVolume) {
for (const auto& mv : model_volumes) {
if (vs.volume_id == mv->id() && mv->is_model_part()) outPut.push_back(vs);
}
}
return outPut;
}
void applyNegtiveVolumes(ModelVolumePtrs model_volumes, const std::vector<VolumeSlices>& objSliceByVolume, std::vector<groupedVolumeSlices>& groups, double resolution) {
ExPolygons negTotal;
for (const auto& vs : objSliceByVolume) {
for (const auto& mv : model_volumes) {
if (vs.volume_id == mv->id() && mv->is_negative_volume()) {
if (vs.slices.size() > 0) {
append(negTotal, vs.slices.front());
}
}
}
}
for (auto& g : groups) {
g.slices = diff_ex(g.slices, negTotal);
for (ExPolygon& poly_ex : g.slices)
poly_ex.douglas_peucker(resolution);
}
}
// 1) Decides Z positions of the layers,
// 2) Initializes layers and their regions
// 3) Slices the object meshes
@ -897,14 +937,8 @@ void PrintObject::slice_volumes()
for (const std::unique_ptr<PrintRegion> &pr : m_shared_regions->all_regions)
layer->m_regions.emplace_back(new LayerRegion(layer, pr.get()));
}
// BBS: first layer slices are sorted by volume
std::vector<float> slice_zs = zs_from_layers(m_layers);
if (!slice_zs.empty()) {
firstLayerObjSliceByVolume = slice_volumes_inner(
print->config(), this->config(), this->trafo_centered(),
this->model_object()->volumes, m_shared_regions->layer_ranges, {slice_zs.front()}, throw_on_cancel_callback);
}
std::vector<float> slice_zs = zs_from_layers(m_layers);
std::vector<VolumeSlices> objSliceByVolume;
if (!slice_zs.empty()) {
objSliceByVolume = slice_volumes_inner(
@ -913,10 +947,11 @@ void PrintObject::slice_volumes()
}
//BBS: "model_part" volumes are grouded according to their connections
const auto scaled_resolution = scaled<double>(print->config().resolution.value);
std::vector<VolumeSlices> objSliceByVolumeParts = findPartVolumes(objSliceByVolume, this->model_object()->volumes);
groupingVolumes(objSliceByVolumeParts, firstLayerObjSliceByGroups, scaled_resolution);
applyNegtiveVolumes(this->model_object()->volumes, objSliceByVolume, firstLayerObjSliceByGroups, scaled_resolution);
//const auto scaled_resolution = scaled<double>(print->config().resolution.value);
//firstLayerObjSliceByVolume = findPartVolumes(objSliceByVolume, this->model_object()->volumes);
//groupingVolumes(objSliceByVolumeParts, firstLayerObjSliceByGroups, scaled_resolution);
//applyNegtiveVolumes(this->model_object()->volumes, objSliceByVolume, firstLayerObjSliceByGroups, scaled_resolution);
firstLayerObjSliceByVolume = objSliceByVolume;
std::vector<std::vector<ExPolygons>> region_slices =
slices_to_regions(print->config(), *this, this->model_object()->volumes, *m_shared_regions, slice_zs,
@ -949,10 +984,10 @@ void PrintObject::slice_volumes()
// If XY Size compensation is also enabled, notify the user that XY Size compensation
// would not be used because the object is multi-material painted.
if (m_config.xy_hole_compensation.value != 0.f || m_config.xy_contour_compensation.value != 0.f) {
//this->active_step_add_warning(
// PrintStateBase::WarningLevel::CRITICAL,
// L("An object has enabled XY Size compensation which will not be used because it is also multi-material painted.\nXY Size "
// "compensation cannot be combined with multi-material painting."));
this->active_step_add_warning(
PrintStateBase::WarningLevel::CRITICAL,
L("An object's XY size compensation will not be used because it is also color-painted.\nXY Size "
"compensation can not be combined with color-painting."));
BOOST_LOG_TRIVIAL(info) << "xy compensation will not work for object " << this->model_object()->name << " for multi filament.";
}

View file

@ -1482,7 +1482,7 @@ static const double length_thresh_well_supported = scale_(6); // min: 6mm
static const double area_thresh_well_supported = SQ(length_thresh_well_supported); // min: 6x6=36mm^2
static const double sharp_tail_xy_gap = 0.2f;
static const double no_overlap_xy_gap = 0.2f;
static const double sharp_tail_max_support_height = 8.f;
static const double sharp_tail_max_support_height = 16.f;
// Tuple: overhang_polygons, contact_polygons, enforcer_polygons, no_interface_offset
// no_interface_offset: minimum of external perimeter widths
@ -1598,7 +1598,7 @@ static inline Polygons detect_overhangs(
// Check whether this is a sharp tail region.
// Should use lower_layer_expolys without any offset. Otherwise, it may missing sharp tails near the main body.
if (intersection_ex({ expoly }, lower_layer_expolys).empty()) {
is_sharp_tail = expoly.area() < area_thresh_well_supported;
is_sharp_tail = expoly.area() < area_thresh_well_supported && !offset_ex(expoly,-0.5*fw).empty();
break;
}
@ -1646,7 +1646,8 @@ static inline Polygons detect_overhangs(
// 2.4 if the area grows fast than threshold, it get connected to other part or
// it has a sharp slop and will be auto supported.
ExPolygons new_overhang_expolys = diff_ex({ expoly }, lower_layer_sharptails);
if (!offset_ex(new_overhang_expolys, -5.0 * fw).empty()) {
Point size_diff = get_extents(new_overhang_expolys).size() - get_extents(lower_layer_sharptails).size();
if (size_diff.both_comp(Point(scale_(5),scale_(5)),">") || !offset_ex(new_overhang_expolys, -5.0 * fw).empty()) {
is_sharp_tail = false;
break;
}

View file

@ -27,7 +27,7 @@
#define TAU (2.0 * M_PI)
#define NO_INDEX (std::numeric_limits<unsigned int>::max())
//#define SUPPORT_TREE_DEBUG_TO_SVG
// #define SUPPORT_TREE_DEBUG_TO_SVG
namespace Slic3r
{
@ -285,7 +285,7 @@ static void draw_layer_mst
svg.draw(spanning_tree.vertices(), "black", coord_t(scale_(0.1)));
}
static void draw_two_overhangs_to_svg(TreeSupportLayer* ts_layer, const ExPolygons& overhangs1, const ExPolygons& overhangs2)
static void draw_two_overhangs_to_svg(SupportLayer* ts_layer, const ExPolygons& overhangs1, const ExPolygons& overhangs2)
{
if (overhangs1.empty() && overhangs2.empty())
return;
@ -300,7 +300,7 @@ static void draw_two_overhangs_to_svg(TreeSupportLayer* ts_layer, const ExPolygo
svg.draw(union_ex(overhangs2), "red");
}
static void draw_polylines(TreeSupportLayer* ts_layer, Polylines& polylines)
static void draw_polylines(SupportLayer* ts_layer, Polylines& polylines)
{
if (polylines.empty())
return;
@ -698,32 +698,41 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p
ipConcentric :
(m_support_params.interface_density > 0.95 ? ipRectilinear : ipSupportBase);
m_support_params.support_extrusion_width = m_object_config->support_line_width.value > 0 ? m_object_config->support_line_width : m_object_config->line_width;
is_slim = is_tree_slim(m_object_config->support_type, m_object_config->support_style);
MAX_BRANCH_RADIUS = is_slim ? 5.0 : 10.0;
support_type = m_object_config->support_type;
is_slim = is_tree_slim(support_type, m_object_config->support_style);
MAX_BRANCH_RADIUS = 10.0;
tree_support_branch_diameter_angle = 5.0;//is_slim ? 10.0 : 5.0;
// by default tree support needs no infill, unless it's tree hybrid which contains normal nodes.
with_infill = support_pattern != smpNone && support_pattern != smpDefault;
const PrintConfig& print_config = m_object->print()->config();
m_machine_border.contour = get_bed_shape_with_excluded_area(print_config);
Vec3d plate_offset = m_object->print()->get_plate_origin();
// align with the centered object in current plate (may not be the 1st plate, so need to add the plate offset)
m_machine_border.translate(Point(scale_(plate_offset(0)), scale_(plate_offset(1))) - m_object->instances().front().shift);
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
SVG svg("SVG/machine_boarder.svg", m_object->bounding_box());
if (svg.is_opened()) svg.draw(m_machine_border, "yellow");
#endif
}
#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0.
void TreeSupport::detect_object_overhangs()
void TreeSupport::detect_overhangs()
{
// overhangs are already detected
if (m_object->tree_support_layer_count() >= m_object->layer_count())
if (m_object->support_layer_count() >= m_object->layer_count())
return;
// Clear and create Tree Support Layers
m_object->clear_tree_support_layers();
m_object->clear_support_layers();
m_object->clear_tree_support_preview_cache();
create_tree_support_layers();
m_ts_data = m_object->alloc_tree_support_preview_cache();
m_ts_data->is_slim = is_slim;
const PrintObjectConfig& config = m_object->config();
SupportType stype = config.support_type.value;
const coordf_t radius_sample_resolution = m_ts_data->m_radius_sample_resolution;
SupportType stype = support_type;
const coordf_t radius_sample_resolution = g_config_tree_support_collision_resolution;
const coordf_t extrusion_width = config.line_width.value;
const coordf_t extrusion_width_scaled = scale_(extrusion_width);
const coordf_t max_bridge_length = scale_(config.max_bridge_length.value);
@ -732,7 +741,7 @@ void TreeSupport::detect_object_overhangs()
const int enforce_support_layers = config.enforce_support_layers.value;
const double area_thresh_well_supported = SQ(scale_(6));
const double length_thresh_well_supported = scale_(6);
static const double sharp_tail_max_support_height = 8.f;
static const double sharp_tail_max_support_height = 16.f;
// a region is considered well supported if the number of layers below it exceeds this threshold
const int thresh_layers_below = 10 / config.layer_height;
double obj_height = m_object->size().z();
@ -922,9 +931,9 @@ void TreeSupport::detect_object_overhangs()
float accum_height = layer->height;
do {
// 1. nothing below
// check whether this is a sharp tail region
// this is a sharp tail region if it's small but non-ignorable
if (intersection_ex({expoly}, lower_polys).empty()) {
is_sharp_tail = expoly.area() < area_thresh_well_supported;
is_sharp_tail = expoly.area() < area_thresh_well_supported && !offset_ex(expoly,-0.5*extrusion_width_scaled).empty();
break;
}
@ -968,7 +977,7 @@ void TreeSupport::detect_object_overhangs()
// 2.4 if the area grows fast than threshold, it get connected to other part or
// it has a sharp slop and will be auto supported.
ExPolygons new_overhang_expolys = diff_ex({expoly}, lower_layer_sharptails);
if (!offset_ex(new_overhang_expolys, -5.0 * extrusion_width_scaled).empty()) {
if ((get_extents(new_overhang_expolys).size()-get_extents(lower_layer_sharptails).size()).both_comp(Point(scale_(5),scale_(5)),">") || !offset_ex(new_overhang_expolys, -5.0 * extrusion_width_scaled).empty()) {
is_sharp_tail = false;
break;
}
@ -982,6 +991,9 @@ void TreeSupport::detect_object_overhangs()
layer->sharp_tails.push_back(expoly);
layer->sharp_tails_height.insert({ &expoly, accum_height });
append(overhang_areas, overhang);
if (!overhang.empty())
has_sharp_tails = true;
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
SVG svg(get_svg_filename(std::to_string(layer->print_z), "sharp_tail"), m_object->bounding_box());
if (svg.is_opened()) svg.draw(overhang, "yellow");
@ -992,11 +1004,13 @@ void TreeSupport::detect_object_overhangs()
}
if (bridge_no_support && overhang_areas.size()>0) {
m_object->remove_bridges_from_contacts(lower_layer, layer, extrusion_width_scaled, &overhang_areas, max_bridge_length, true);
if (max_bridge_length > 0 && overhang_areas.size()>0) {
// do not break bridge for normal part in TreeHybrid
bool break_bridge = !(config.support_style == smsTreeHybrid && area(overhang_areas) > m_support_params.thresh_big_overhang);
m_object->remove_bridges_from_contacts(lower_layer, layer, extrusion_width_scaled, &overhang_areas, max_bridge_length, break_bridge);
}
TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers);
SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
for (ExPolygon& poly : overhang_areas) {
if (!offset_ex(poly, -0.1 * extrusion_width_scaled).empty())
ts_layer->overhang_areas.emplace_back(poly);
@ -1146,9 +1160,9 @@ void TreeSupport::detect_object_overhangs()
if (m_object->print()->canceled())
break;
TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers);
SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
auto layer = m_object->get_layer(layer_nr);
if (support_critical_regions_only) {
auto layer = m_object->get_layer(layer_nr);
auto lower_layer = layer->lower_layer;
if (lower_layer == nullptr)
ts_layer->overhang_areas = layer->sharp_tails;
@ -1164,7 +1178,7 @@ void TreeSupport::detect_object_overhangs()
}
for (auto &area : ts_layer->overhang_areas) {
ts_layer->overhang_types.emplace(&area, TreeSupportLayer::Detected);
ts_layer->overhang_types.emplace(&area, SupportLayer::Detected);
}
// enforcers
if (layer_nr < enforcers.size()) {
@ -1175,15 +1189,16 @@ void TreeSupport::detect_object_overhangs()
enforcer = offset(enforcer, 0.1 * extrusion_width_scaled);
for (const Polygon& poly : enforcer) {
ts_layer->overhang_areas.emplace_back(poly);
ts_layer->overhang_types.emplace(&ts_layer->overhang_areas.back(), TreeSupportLayer::Enforced);
ts_layer->overhang_types.emplace(&ts_layer->overhang_areas.back(), SupportLayer::Enforced);
}
}
if (!ts_layer->overhang_areas.empty()) has_overhangs = true;
if (!layer->cantilevers.empty()) has_cantilever = true;
}
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
for (const TreeSupportLayer* layer : m_object->tree_support_layers()) {
for (const SupportLayer* layer : m_object->support_layers()) {
if (layer->overhang_areas.empty())
continue;
@ -1220,9 +1235,9 @@ void TreeSupport::create_tree_support_layers()
}
for (Layer *layer : m_object->layers()) {
TreeSupportLayer* ts_layer = m_object->add_tree_support_layer(layer->id(), layer->height, layer->print_z, layer->slice_z);
SupportLayer* ts_layer = m_object->add_tree_support_layer(layer->id(), layer->height, layer->print_z, layer->slice_z);
if (ts_layer->id() > m_raft_layers) {
TreeSupportLayer* lower_layer = m_object->get_tree_support_layer(ts_layer->id() - 1);
SupportLayer* lower_layer = m_object->get_support_layer(ts_layer->id() - 1);
lower_layer->upper_layer = ts_layer;
ts_layer->lower_layer = lower_layer;
}
@ -1434,7 +1449,7 @@ void TreeSupport::generate_toolpaths()
const coordf_t branch_radius = object_config.tree_support_branch_diameter.value / 2;
const coordf_t branch_radius_scaled = scale_(branch_radius);
if (m_object->tree_support_layers().empty())
if (m_object->support_layers().empty())
return;
// calculate fill areas for raft layers
@ -1446,8 +1461,8 @@ void TreeSupport::generate_toolpaths()
}
}
if (m_object->tree_support_layer_count() > m_raft_layers) {
const TreeSupportLayer *ts_layer = m_object->get_tree_support_layer(m_raft_layers);
if (m_object->support_layer_count() > m_raft_layers) {
const SupportLayer *ts_layer = m_object->get_support_layer(m_raft_layers);
for (const ExPolygon expoly : ts_layer->floor_areas)
raft_areas.push_back(expoly);
for (const ExPolygon expoly : ts_layer->roof_areas)
@ -1462,7 +1477,7 @@ void TreeSupport::generate_toolpaths()
if (m_raft_layers > 0)
{
ExtrusionRole raft_contour_er = m_slicing_params.base_raft_layers > 0 ? erSupportMaterial : erSupportMaterialInterface;
TreeSupportLayer *ts_layer = m_object->tree_support_layers().front();
SupportLayer *ts_layer = m_object->support_layers().front();
Flow flow = m_object->print()->brim_flow();
Polygons loops;
@ -1476,7 +1491,7 @@ void TreeSupport::generate_toolpaths()
}
for (size_t layer_nr = 0; layer_nr < m_slicing_params.base_raft_layers; layer_nr++) {
TreeSupportLayer *ts_layer = m_object->get_tree_support_layer(layer_nr);
SupportLayer *ts_layer = m_object->get_support_layer(layer_nr);
coordf_t expand_offset = (layer_nr == 0 ? 0. : -1.);
Flow support_flow = layer_nr == 0 ? m_object->print()->brim_flow() : Flow(support_extrusion_width, ts_layer->height, nozzle_diameter);
@ -1496,7 +1511,7 @@ void TreeSupport::generate_toolpaths()
layer_nr < m_slicing_params.base_raft_layers + m_slicing_params.interface_raft_layers;
layer_nr++)
{
TreeSupportLayer *ts_layer = m_object->get_tree_support_layer(layer_nr);
SupportLayer *ts_layer = m_object->get_support_layer(layer_nr);
coordf_t expand_offset = (layer_nr == 0 ? 0. : -1.);
Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter);
@ -1528,7 +1543,7 @@ void TreeSupport::generate_toolpaths()
// generate tree support tool paths
tbb::parallel_for(
tbb::blocked_range<size_t>(m_raft_layers, m_object->tree_support_layer_count()),
tbb::blocked_range<size_t>(m_raft_layers, m_object->support_layer_count()),
[&](const tbb::blocked_range<size_t>& range)
{
for (size_t layer_id = range.begin(); layer_id < range.end(); layer_id++) {
@ -1537,7 +1552,7 @@ void TreeSupport::generate_toolpaths()
m_object->print()->set_status(70, (boost::format(_L("Support: generate toolpath at layer %d")) % layer_id).str());
TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_id);
SupportLayer* ts_layer = m_object->get_support_layer(layer_id);
Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter);
coordf_t support_spacing = object_config.support_base_pattern_spacing.value + support_flow.spacing();
coordf_t support_density = std::min(1., support_flow.spacing() / support_spacing);
@ -1548,14 +1563,14 @@ void TreeSupport::generate_toolpaths()
ExPolygon& poly = *area_group.area;
ExPolygons polys;
FillParams fill_params;
if (area_group.type != TreeSupportLayer::BaseType) {
if (area_group.type != SupportLayer::BaseType) {
// interface
if (layer_id == 0) {
Flow flow = m_raft_layers == 0 ? m_object->print()->brim_flow() : support_flow;
make_perimeter_and_inner_brim(ts_layer->support_fills.entities, poly, wall_count, flow,
area_group.type == TreeSupportLayer::RoofType ? erSupportMaterialInterface : erSupportMaterial);
area_group.type == SupportLayer::RoofType ? erSupportMaterialInterface : erSupportMaterial);
polys = std::move(offset_ex(poly, -flow.scaled_spacing()));
} else if (area_group.type == TreeSupportLayer::Roof1stLayer) {
} else if (area_group.type == SupportLayer::Roof1stLayer) {
polys = std::move(offset_ex(poly, 0.5*support_flow.scaled_width()));
}
else {
@ -1564,7 +1579,7 @@ void TreeSupport::generate_toolpaths()
fill_params.density = interface_density;
fill_params.dont_adjust = true;
}
if (area_group.type == TreeSupportLayer::Roof1stLayer) {
if (area_group.type == SupportLayer::Roof1stLayer) {
// roof_1st_layer
fill_params.density = interface_density;
// Note: spacing means the separation between two lines as if they are tightly extruded
@ -1578,13 +1593,13 @@ void TreeSupport::generate_toolpaths()
ts_layer->support_fills.entities.push_back(temp_support_fills);
else
delete temp_support_fills;
} else if (area_group.type == TreeSupportLayer::FloorType) {
} else if (area_group.type == SupportLayer::FloorType) {
// floor_areas
fill_params.density = bottom_interface_density;
filler_interface->spacing = m_support_material_interface_flow.spacing();
fill_expolygons_generate_paths(ts_layer->support_fills.entities, std::move(polys),
filler_interface.get(), fill_params, erSupportMaterialInterface, m_support_material_interface_flow);
} else if (area_group.type == TreeSupportLayer::RoofType) {
} else if (area_group.type == SupportLayer::RoofType) {
// roof_areas
fill_params.density = interface_density;
filler_interface->spacing = m_support_material_interface_flow.spacing();
@ -1886,7 +1901,7 @@ Polygons TreeSupport::contact_nodes_to_polygon(const std::vector<Node*>& contact
}
void TreeSupport::generate_support_areas()
void TreeSupport::generate()
{
bool tree_support_enable = m_object_config->enable_support.value && is_tree(m_object_config->support_type.value);
if (!tree_support_enable)
@ -1899,9 +1914,14 @@ void TreeSupport::generate_support_areas()
// Generate overhang areas
profiler.stage_start(STAGE_DETECT_OVERHANGS);
m_object->print()->set_status(55, _L("Support: detect overhangs"));
detect_object_overhangs();
detect_overhangs();
profiler.stage_finish(STAGE_DETECT_OVERHANGS);
if (!has_overhangs) return;
m_ts_data = m_object->alloc_tree_support_preview_cache();
m_ts_data->is_slim = is_slim;
// Generate contact points of tree support
profiler.stage_start(STAGE_GENERATE_CONTACT_NODES);
m_object->print()->set_status(56, _L("Support: generate contact points"));
@ -2071,7 +2091,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
break;
const std::vector<Node*>& curr_layer_nodes = contact_nodes[layer_nr];
TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers);
SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
assert(ts_layer != nullptr);
// skip if current layer has no points. This fixes potential crash in get_collision (see jira BBL001-355)
@ -2201,18 +2221,20 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
//roof_areas = std::move(diff_ex(roof_areas, avoid_region_interface));
//roof_1st_layer = std::move(diff_ex(roof_1st_layer, avoid_region_interface));
roof_areas = avoid_object_remove_extra_small_parts(roof_areas, avoid_region_interface);
roof_areas = intersection_ex(roof_areas, m_machine_border);
roof_1st_layer = avoid_object_remove_extra_small_parts(roof_1st_layer, avoid_region_interface);
// roof_1st_layer and roof_areas may intersect, so need to subtract roof_areas from roof_1st_layer
roof_1st_layer = std::move(diff_ex(roof_1st_layer, roof_areas));
roof_1st_layer = intersection_ex(roof_1st_layer, m_machine_border);
// let supports touch objects when brim is on
auto avoid_region = m_ts_data->get_collision((layer_nr == 0 && has_brim) ? config.brim_object_gap : m_ts_data->m_xy_distance, layer_nr);
// base_areas = std::move(diff_ex(base_areas, avoid_region));
base_areas = avoid_object_remove_extra_small_parts(base_areas, avoid_region);
base_areas = std::move(diff_ex(base_areas, roof_areas));
base_areas = std::move(diff_ex(base_areas, roof_1st_layer));
base_areas = std::move(diff_ex(base_areas, roof_gap_areas));
base_areas = intersection_ex(base_areas, m_machine_border);
if (SQUARE_SUPPORT) {
// simplify support contours
@ -2247,10 +2269,10 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
}
}
auto &area_groups = ts_layer->area_groups;
for (auto &area : ts_layer->base_areas) area_groups.emplace_back(&area, TreeSupportLayer::BaseType, max_layers_above_base);
for (auto &area : ts_layer->roof_areas) area_groups.emplace_back(&area, TreeSupportLayer::RoofType, max_layers_above_roof);
for (auto &area : ts_layer->floor_areas) area_groups.emplace_back(&area, TreeSupportLayer::FloorType, 10000);
for (auto &area : ts_layer->roof_1st_layer) area_groups.emplace_back(&area, TreeSupportLayer::Roof1stLayer, max_layers_above_roof1);
for (auto &area : ts_layer->base_areas) area_groups.emplace_back(&area, SupportLayer::BaseType, max_layers_above_base);
for (auto &area : ts_layer->roof_areas) area_groups.emplace_back(&area, SupportLayer::RoofType, max_layers_above_roof);
for (auto &area : ts_layer->floor_areas) area_groups.emplace_back(&area, SupportLayer::FloorType, 10000);
for (auto &area : ts_layer->roof_1st_layer) area_groups.emplace_back(&area, SupportLayer::Roof1stLayer, max_layers_above_roof1);
for (auto &area_group : area_groups) {
auto& expoly = area_group.area;
@ -2275,7 +2297,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
for (int layer_nr = 1; layer_nr < m_object->layer_count(); layer_nr++) {
if (print->canceled()) break;
const std::vector<Node*>& curr_layer_nodes = contact_nodes[layer_nr];
TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers);
SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
assert(ts_layer != nullptr);
// skip if current layer has no points. This fixes potential crash in get_collision (see jira BBL001-355)
@ -2287,10 +2309,10 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
int layer_nr_lower = layer_nr - 1;
for (layer_nr_lower; layer_nr_lower >= 0; layer_nr_lower--) {
if (!m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers)->area_groups.empty()) break;
if (!m_object->get_support_layer(layer_nr_lower + m_raft_layers)->area_groups.empty()) break;
}
TreeSupportLayer* lower_layer = m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers);
ExPolygons& base_areas_lower = m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers)->base_areas;
SupportLayer* lower_layer = m_object->get_support_layer(layer_nr_lower + m_raft_layers);
ExPolygons& base_areas_lower = m_object->get_support_layer(layer_nr_lower + m_raft_layers)->base_areas;
ExPolygons overhang;
@ -2324,7 +2346,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
printZ_to_lightninglayer[lower_layer->print_z] = overhangs.size() - 1;
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
draw_two_overhangs_to_svg(m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers), base_areas_lower, to_expolygons(overhangs.back()));
draw_two_overhangs_to_svg(m_object->get_support_layer(layer_nr_lower + m_raft_layers), base_areas_lower, to_expolygons(overhangs.back()));
#endif
}
@ -2364,7 +2386,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
m_object->print()->set_status(66, (boost::format(_L("Support: fix holes at layer %d")) % layer_nr).str());
const std::vector<Node*>& curr_layer_nodes = contact_nodes[layer_nr];
TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers);
SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
assert(ts_layer != nullptr);
// skip if current layer has no points. This fixes potential crash in get_collision (see jira BBL001-355)
@ -2374,18 +2396,18 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
int layer_nr_lower = layer_nr - 1;
for (layer_nr_lower; layer_nr_lower >= 0; layer_nr_lower--) {
if (!m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers)->area_groups.empty()) break;
if (!m_object->get_support_layer(layer_nr_lower + m_raft_layers)->area_groups.empty()) break;
}
if (layer_nr_lower < 0) continue;
auto& area_groups_lower = m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers)->area_groups;
auto& area_groups_lower = m_object->get_support_layer(layer_nr_lower + m_raft_layers)->area_groups;
for (const auto& area_group : ts_layer->area_groups) {
if (area_group.type != TreeSupportLayer::BaseType) continue;
if (area_group.type != SupportLayer::BaseType) continue;
const auto& area = area_group.area;
for (const auto& hole : area->holes) {
// auto hole_bbox = get_extents(hole).polygon();
for (auto& area_group_lower : area_groups_lower) {
if (area_group.type != TreeSupportLayer::BaseType) continue;
if (area_group.type != SupportLayer::BaseType) continue;
auto& base_area_lower = *area_group_lower.area;
Point pt_on_poly, pt_on_expoly, pt_far_on_poly;
// if a hole doesn't intersect with lower layer's contours, add a hole to lower layer and move it slightly to the contour
@ -2450,8 +2472,8 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
#endif
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
for (int layer_nr = m_object->layer_count() - 1; layer_nr > 0; layer_nr--) {
TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers);
for (int layer_nr = m_object->layer_count() - 1; layer_nr >= 0; layer_nr--) {
SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
ExPolygons& base_areas = ts_layer->base_areas;
ExPolygons& roof_areas = ts_layer->roof_areas;
ExPolygons& roof_1st_layer = ts_layer->roof_1st_layer;
@ -2466,7 +2488,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
draw_circles_layer_out.open("./SVG/layer_heights_draw_circles.txt");
if (draw_circles_layer_out.is_open()) {
for (int layer_nr = m_object->layer_count() - 1; layer_nr > 0; layer_nr--) {
TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers);
SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
ExPolygons& base_areas = ts_layer->base_areas;
ExPolygons& roof_areas = ts_layer->roof_areas;
ExPolygons& roof_1st_layer = ts_layer->roof_1st_layer;
@ -2477,8 +2499,8 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
}
#endif // SUPPORT_TREE_DEBUG_TO_SVG
TreeSupportLayerPtrs& ts_layers = m_object->tree_support_layers();
auto iter = std::remove_if(ts_layers.begin(), ts_layers.end(), [](TreeSupportLayer* ts_layer) { return ts_layer->height < EPSILON; });
SupportLayerPtrs& ts_layers = m_object->support_layers();
auto iter = std::remove_if(ts_layers.begin(), ts_layers.end(), [](SupportLayer* ts_layer) { return ts_layer->height < EPSILON; });
ts_layers.erase(iter, ts_layers.end());
for (int layer_nr = 0; layer_nr < ts_layers.size(); layer_nr++) {
ts_layers[layer_nr]->upper_layer = layer_nr != ts_layers.size() - 1 ? ts_layers[layer_nr + 1] : nullptr;
@ -2527,7 +2549,8 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
return move_dist;
};
std::vector<std::pair<coordf_t, coordf_t>> layer_heights = plan_layer_heights(contact_nodes);
m_ts_data->layer_heights = plan_layer_heights(contact_nodes);
std::vector<LayerHeightData> &layer_heights = m_ts_data->layer_heights;
if (layer_heights.empty()) return;
std::unordered_set<Node*> to_free_node_set;
@ -2547,9 +2570,10 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
for (Node *p_node : contact_nodes[layer_nr]) {
layer_node_dist.emplace(p_node->dist_mm_to_top);
}
if (layer_nr < m_highest_overhang_layer && layer_heights[layer_nr].second>0) {
for (auto node_dist : all_layer_node_dist[layer_nr + 1])
layer_node_dist.emplace(node_dist + layer_heights[layer_nr].second);
size_t layer_nr_next = layer_heights[layer_nr].next_layer_nr;
if (layer_nr < m_highest_overhang_layer && layer_heights[layer_nr].height>0) {
for (auto node_dist : all_layer_node_dist[layer_nr_next])
layer_node_dist.emplace(node_dist + layer_heights[layer_nr].height);
}
for (auto node_dist : layer_node_dist) {
layer_radius.emplace(calc_branch_radius(branch_radius, node_dist, diameter_angle_scale_factor));
@ -2577,14 +2601,12 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
if (layer_contact_nodes.empty())
continue;
int layer_nr_next = layer_nr - 1;
while (layer_nr_next>=0 && layer_heights[layer_nr_next].second < EPSILON)
layer_nr_next--;
coordf_t print_z_next = layer_heights[layer_nr_next].first;
coordf_t height_next = layer_heights[layer_nr_next].second;
int layer_nr_next = layer_heights[layer_nr].next_layer_nr;
coordf_t print_z_next = layer_heights[layer_nr_next].print_z;
coordf_t height_next = layer_heights[layer_nr_next].height;
std::deque<std::pair<size_t, Node*>> unsupported_branch_leaves; // All nodes that are leaves on this layer that would result in unsupported ('mid-air') branches.
const Layer* ts_layer = m_object->get_tree_support_layer(layer_nr);
const Layer* ts_layer = m_object->get_support_layer(layer_nr);
m_object->print()->set_status(60, (boost::format(_L("Support: propagate branches at layer %d")) % layer_nr).str());
@ -2620,7 +2642,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
if (node.distance_to_top < 0) {
// gap nodes do not merge or move
Node* next_node = new Node(p_node->position, p_node->distance_to_top + 1, p_node->skin_direction, p_node->support_roof_layers_below - 1, p_node->to_buildplate, p_node,
Node* next_node = new Node(p_node->position, p_node->distance_to_top + 1, layer_nr_next, p_node->support_roof_layers_below - 1, p_node->to_buildplate, p_node,
print_z_next, height_next);
get_max_move_dist(next_node);
next_node->is_merged = false;
@ -2755,8 +2777,8 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
parent = neighbour->parent;
const bool to_buildplate = !is_inside_ex(m_ts_data->get_avoidance(0, layer_nr_next), next_position);
Node * next_node = new Node(next_position, new_distance_to_top, node.skin_direction, new_support_roof_layers_below, to_buildplate, p_node,
layer_heights[layer_nr_next].first, layer_heights[layer_nr_next].second, new_dist_mm_to_top);
Node * next_node = new Node(next_position, new_distance_to_top, layer_nr_next, new_support_roof_layers_below, to_buildplate, p_node,
print_z_next, height_next, new_dist_mm_to_top);
next_node->movement = next_position - node.position;
get_max_move_dist(next_node);
next_node->is_merged = true;
@ -2801,7 +2823,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
if (node.type == ePolygon) {
// polygon node do not merge or move
const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[layer_nr], p_node->position);
Node * next_node = new Node(p_node->position, p_node->distance_to_top + 1, p_node->skin_direction, p_node->support_roof_layers_below - 1, to_buildplate,
Node * next_node = new Node(p_node->position, p_node->distance_to_top + 1, layer_nr_next, p_node->support_roof_layers_below - 1, to_buildplate,
p_node, print_z_next, height_next);
next_node->max_move_dist = 0;
next_node->is_merged = false;
@ -2868,13 +2890,13 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
if (is_line_cut_by_contour(node.position, neighbour)) continue;
if (is_slim)
if (/*is_slim*/1)
sum_direction += direction * (1 / dist2_to_neighbor);
else
sum_direction += direction;
}
if (is_slim)
if (/*is_slim*/1)
move_to_neighbor_center = sum_direction;
else {
if (vsize2_with_unscale(sum_direction) <= max_move_distance2) {
@ -2945,7 +2967,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
}
const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[layer_nr], next_layer_vertex);// !is_inside_ex(m_ts_data->get_avoidance(m_ts_data->m_xy_distance, layer_nr - 1), next_layer_vertex);
Node * next_node = new Node(next_layer_vertex, node.distance_to_top + 1, node.skin_direction, node.support_roof_layers_below - 1, to_buildplate, p_node,
Node * next_node = new Node(next_layer_vertex, node.distance_to_top + 1, layer_nr_next, node.support_roof_layers_below - 1, to_buildplate, p_node,
print_z_next, height_next);
next_node->movement = movement;
get_max_move_dist(next_node);
@ -2974,12 +2996,17 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
{
const auto& entry = unsupported_branch_leaves.back();
Node* i_node = entry.second;
for (size_t i_layer = entry.first; i_node != nullptr; ++i_layer, i_node = i_node->parent)
for (; i_node != nullptr; i_node = i_node->parent)
{
size_t i_layer = i_node->obj_layer_nr;
std::vector<Node*>::iterator to_erase = std::find(contact_nodes[i_layer].begin(), contact_nodes[i_layer].end(), i_node);
if (to_erase != contact_nodes[i_layer].end())
{
to_free_node_set.insert(*to_erase);
// update the parent-child chain
if(i_node->parent)
i_node->parent->child = i_node->child;
if(i_node->child)
i_node->child->parent = i_node->parent;
contact_nodes[i_layer].erase(to_erase);
to_free_node_set.insert(i_node);
@ -3191,7 +3218,7 @@ void TreeSupport::adjust_layer_heights(std::vector<std::vector<Node*>>& contact_
}
}
std::vector<std::pair<coordf_t, coordf_t>> TreeSupport::plan_layer_heights(std::vector<std::vector<Node*>>& contact_nodes)
std::vector<LayerHeightData> TreeSupport::plan_layer_heights(std::vector<std::vector<Node *>> &contact_nodes)
{
const PrintObjectConfig& config = m_object->config();
const PrintConfig & print_config = m_object->print()->config();
@ -3205,21 +3232,19 @@ std::vector<std::pair<coordf_t, coordf_t>> TreeSupport::plan_layer_heights(std::
}
const size_t support_roof_layers = config.support_interface_top_layers.value;
const int z_distance_top_layers = round_up_divide(scale_(z_distance_top), scale_(layer_height)) + 1;
std::vector<std::pair<coordf_t, coordf_t>> layer_heights(contact_nodes.size(), std::pair<coordf_t, coordf_t>(0.0, 0.0));
std::vector<LayerHeightData> layer_heights(contact_nodes.size());
std::vector<int> bounds;
if (!config.tree_support_adaptive_layer_height || layer_height == max_layer_height || !print_config.independent_support_layer_height) {
for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) {
layer_heights[layer_nr].first = m_object->get_layer(layer_nr)->print_z;
layer_heights[layer_nr].second = m_object->get_layer(layer_nr)->height;
layer_heights[layer_nr] = {m_object->get_layer(layer_nr)->print_z, m_object->get_layer(layer_nr)->height, layer_nr > 0 ? size_t(layer_nr - 1) : 0};
}
return layer_heights;
}
bounds.push_back(0);
// Keep first layer still
layer_heights[0].first = m_object->get_layer(0)->print_z;
layer_heights[0].second = m_object->get_layer(0)->height;
layer_heights[0] = {m_object->get_layer(0)->print_z, m_object->get_layer(0)->height, 0};
// Collect top contact layers
for (int layer_nr = 1; layer_nr < contact_nodes.size(); layer_nr++)
{
@ -3227,8 +3252,8 @@ std::vector<std::pair<coordf_t, coordf_t>> TreeSupport::plan_layer_heights(std::
for (int i = 0; i < support_roof_layers + z_distance_top_layers + 1; i++) {
if (layer_nr - i > 0) {
bounds.push_back(layer_nr - i);
layer_heights[layer_nr - i].first = m_object->get_layer(layer_nr - i)->print_z;
layer_heights[layer_nr - i].second = m_object->get_layer(layer_nr - i)->height;
layer_heights[layer_nr - i].print_z = m_object->get_layer(layer_nr - i)->print_z;
layer_heights[layer_nr - i].height = m_object->get_layer(layer_nr - i)->height;
}
else {
break;
@ -3259,19 +3284,29 @@ std::vector<std::pair<coordf_t, coordf_t>> TreeSupport::plan_layer_heights(std::
for (int layer_nr = extr1_layer_nr + 1; layer_nr < extr2_layer_nr; layer_nr++) {
// if (curr_layer_nodes.empty()) continue;
if (std::abs(print_z - m_object->get_layer(layer_nr)->print_z) < step / 2 + EPSILON || extr_layers_left < 1) {
layer_heights[layer_nr].first = print_z;
layer_heights[layer_nr].second = step;
layer_heights[layer_nr].print_z = print_z;
layer_heights[layer_nr].height = step;
print_z += step;
}
else {
// can't clear curr_layer_nodes, or the model will have empty layers
layer_heights[layer_nr].first = 0.0;
layer_heights[layer_nr].second = 0.0;
layer_heights[layer_nr].print_z = 0.0;
layer_heights[layer_nr].height = 0.0;
extr_layers_left--;
}
}
}
for (int i = layer_heights.size() - 1; i >= 0; i--) {
if (layer_heights[i].height < EPSILON) continue;
for (int j = i - 1; j >= 0; j--) {
if (layer_heights[j].height > EPSILON) {
layer_heights[i].next_layer_nr = j;
break;
}
}
}
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
// check bounds
if (1)
@ -3285,7 +3320,7 @@ std::vector<std::pair<coordf_t, coordf_t>> TreeSupport::plan_layer_heights(std::
}
}
#endif
for (int i = 0; i < layer_heights.size(); i++) { BOOST_LOG_TRIVIAL(info) << "plan_layer_heights print_z, height: "<< layer_heights[i].first << " " << layer_heights[i].second << std::endl; }
for (int i = 0; i < layer_heights.size(); i++) { BOOST_LOG_TRIVIAL(info) << "plan_layer_heights print_z, height: "<< layer_heights[i].print_z << " " << layer_heights[i].height << std::endl; }
return layer_heights;
}
@ -3299,12 +3334,11 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
BoundingBox bounding_box = m_object->bounding_box();
const Point bounding_box_size = bounding_box.max - bounding_box.min;
constexpr double rotate_angle = 22.0 / 180.0 * M_PI;
constexpr double thresh_big_overhang = SQ(scale_(10));
const auto center = bounding_box_middle(bounding_box);
const auto sin_angle = std::sin(rotate_angle);
const auto cos_angle = std::cos(rotate_angle);
const auto rotated_dims = Point(
const Point rotated_dims = Point(
bounding_box_size(0) * cos_angle + bounding_box_size(1) * sin_angle,
bounding_box_size(0) * sin_angle + bounding_box_size(1) * cos_angle) / 2;
@ -3346,7 +3380,7 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
{
if (m_object->print()->canceled())
break;
auto ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers);
auto ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
const ExPolygons &overhang = ts_layer->overhang_areas;
auto & curr_nodes = contact_nodes[layer_nr];
if (overhang.empty())
@ -3359,11 +3393,11 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
for (const ExPolygon &overhang_part : overhang)
{
BoundingBox overhang_bounds = get_extents(overhang_part);
if (config.support_style.value==smsTreeHybrid && overhang_part.area() > thresh_big_overhang) {
if (config.support_style.value==smsTreeHybrid && overhang_part.area() > m_support_params.thresh_big_overhang) {
Point candidate = overhang_bounds.center();
if (!overhang_part.contains(candidate))
move_inside_expoly(overhang_part, candidate);
Node *contact_node = new Node(candidate, -z_distance_top_layers, (layer_nr) % 2, support_roof_layers + z_distance_top_layers, true, Node::NO_PARENT, print_z,
Node *contact_node = new Node(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, true, Node::NO_PARENT, print_z,
height, z_distance_top);
contact_node->type = ePolygon;
contact_node->overhang = &overhang_part;
@ -3391,7 +3425,7 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
//if (!is_inside_ex(m_ts_data->get_collision(0, layer_nr), candidate))
{
constexpr bool to_buildplate = true;
Node * contact_node = new Node(candidate, -z_distance_top_layers, (layer_nr) % 2, support_roof_layers + z_distance_top_layers, to_buildplate,
Node * contact_node = new Node(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, to_buildplate,
Node::NO_PARENT, print_z, height, z_distance_top);
contact_node->overhang = &overhang_part;
curr_nodes.emplace_back(contact_node);
@ -3405,7 +3439,7 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
{
auto bbox = overhang_part.contour.bounding_box();
Points candidates;
if (ts_layer->overhang_types[&overhang_part] == TreeSupportLayer::Detected)
if (ts_layer->overhang_types[&overhang_part] == SupportLayer::Detected)
candidates = {bbox.min, bounding_box_middle(bbox), bbox.max};
else
candidates = {bounding_box_middle(bbox)};
@ -3414,13 +3448,13 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
if (!overhang_part.contains(candidate))
move_inside_expoly(overhang_part, candidate);
constexpr bool to_buildplate = true;
Node *contact_node = new Node(candidate, -z_distance_top_layers, layer_nr % 2, support_roof_layers + z_distance_top_layers, to_buildplate, Node::NO_PARENT,
Node *contact_node = new Node(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, to_buildplate, Node::NO_PARENT,
print_z, height, z_distance_top);
contact_node->overhang = &overhang_part;
curr_nodes.emplace_back(contact_node);
}
}
if (ts_layer->overhang_types[&overhang_part] == TreeSupportLayer::Detected) {
if (ts_layer->overhang_types[&overhang_part] == SupportLayer::Detected) {
// add points at corners
auto &points = overhang_part.contour.points;
int nSize = points.size();
@ -3429,7 +3463,7 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
auto v1 = (pt - points[(i - 1 + nSize) % nSize]).cast<double>().normalized();
auto v2 = (pt - points[(i + 1) % nSize]).cast<double>().normalized();
if (v1.dot(v2) > -0.7) { // angle smaller than 135 degrees
Node *contact_node = new Node(pt, -z_distance_top_layers, layer_nr % 2, support_roof_layers + z_distance_top_layers, true, Node::NO_PARENT, print_z,
Node *contact_node = new Node(pt, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, true, Node::NO_PARENT, print_z,
height, z_distance_top);
contact_node->overhang = &overhang_part;
contact_node->is_corner = true;
@ -3437,7 +3471,7 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
}
}
}
if(ts_layer->overhang_types[&overhang_part] == TreeSupportLayer::Enforced || is_slim){
if(ts_layer->overhang_types[&overhang_part] == SupportLayer::Enforced || is_slim){
// remove close points in Enforcers
// auto above_nodes = contact_nodes[layer_nr - 1];
if (!curr_nodes.empty() /*&& !above_nodes.empty()*/) {
@ -3487,16 +3521,6 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
BOOST_LOG_TRIVIAL(info) << "avg_node_per_layer=" << avg_node_per_layer << ", nodes_angle=" << nodes_angle;
}
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
std::ofstream contact_nodes_out;
contact_nodes_out.open("./SVG/contact_nodes.txt");
if (contact_nodes_out.is_open()) {
for (int i = 0; i < contact_nodes.size(); i++) {
if (!contact_nodes[i].empty())
contact_nodes_out << i << std::endl;
}
}
#endif // SUPPORT_TREE_DEBUG_TO_SVG
}
void TreeSupport::insert_dropped_node(std::vector<Node*>& nodes_layer, Node* p_node)
@ -3621,13 +3645,18 @@ const ExPolygons& TreeSupportData::calculate_avoidance(const RadiusLayerPair& ke
// if the layer at 2N below the current one but we won't exceed our limit unless there are N*N uncalculated layers
// below our current one.
constexpr auto max_recursion_depth = 100;
size_t layer_nr_next = layer_nr;
for (int i = 0; i < max_recursion_depth && layer_nr_next>0; i++) {
layer_nr_next = layer_heights[layer_nr_next].next_layer_nr;
}
// Check if we would exceed the recursion limit by trying to process this layer
if (layer_nr >= max_recursion_depth && m_avoidance_cache.find({radius, layer_nr - max_recursion_depth}) == m_avoidance_cache.end()) {
if (layer_nr >= max_recursion_depth && m_avoidance_cache.find({radius, layer_nr_next}) == m_avoidance_cache.end()) {
// Force the calculation of the layer `max_recursion_depth` below our current one, ignoring the result.
get_avoidance(radius, layer_nr - max_recursion_depth);
get_avoidance(radius, layer_nr_next);
}
ExPolygons avoidance_areas = std::move(offset_ex(get_avoidance(radius, layer_nr - 1), scale_(-m_max_move)));
layer_nr_next = layer_heights[layer_nr].next_layer_nr;
ExPolygons avoidance_areas = std::move(offset_ex(get_avoidance(radius, layer_nr_next), scale_(-m_max_move)));
const ExPolygons &collision = get_collision(radius, layer_nr);
avoidance_areas.insert(avoidance_areas.end(), collision.begin(), collision.end());
avoidance_areas = std::move(union_ex(avoidance_areas));

View file

@ -21,6 +21,15 @@ namespace Slic3r
class PrintObject;
class TreeSupport;
struct LayerHeightData
{
coordf_t print_z = 0;
coordf_t height = 0;
size_t next_layer_nr = 0;
LayerHeightData() = default;
LayerHeightData(coordf_t z, coordf_t h, size_t next_layer) : print_z(z), height(h), next_layer_nr(next_layer) {}
};
/*!
* \brief Lazily generates tree guidance volumes.
*
@ -79,6 +88,8 @@ public:
Polygons get_contours(size_t layer_nr) const;
Polygons get_contours_with_holes(size_t layer_nr) const;
std::vector<LayerHeightData> layer_heights;
private:
/*!
* \brief Convenience typedef for the keys to the caches
@ -114,11 +125,6 @@ private:
*/
const ExPolygons& calculate_avoidance(const RadiusLayerPair& key) const;
/*!
* \brief Polygons representing the limits of the printable area of the
* machine
*/
ExPolygon m_machine_border;
public:
bool is_slim = false;
@ -195,9 +201,9 @@ public:
* \param storage The data storage where the mesh data is gotten from and
* where the resulting support areas are stored.
*/
void generate_support_areas();
void generate();
void detect_object_overhangs();
void detect_overhangs();
enum NodeType {
eCircle,
@ -215,7 +221,7 @@ public:
Node()
: distance_to_top(0)
, position(Point(0, 0))
, skin_direction(false)
, obj_layer_nr(0)
, support_roof_layers_below(0)
, support_floor_layers_above(0)
, to_buildplate(true)
@ -224,11 +230,11 @@ public:
, height(0.0)
{}
Node(const Point position, const int distance_to_top, const bool skin_direction, const int support_roof_layers_below, const bool to_buildplate, Node* parent,
Node(const Point position, const int distance_to_top, const int obj_layer_nr, const int support_roof_layers_below, const bool to_buildplate, Node* parent,
coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_=0)
: distance_to_top(distance_to_top)
, position(position)
, skin_direction(skin_direction)
, obj_layer_nr(obj_layer_nr)
, support_roof_layers_below(support_roof_layers_below)
, support_floor_layers_above(0)
, to_buildplate(to_buildplate)
@ -292,6 +298,7 @@ public:
*/
int support_roof_layers_below;
int support_floor_layers_above;
int obj_layer_nr;
/*!
* \brief Whether to try to go towards the build plate.
@ -356,11 +363,15 @@ public:
InfillPattern interface_fill_pattern;
InfillPattern contact_fill_pattern;
bool with_sheath;
const double thresh_big_overhang = SQ(scale_(10));
};
int avg_node_per_layer = 0;
float nodes_angle = 0;
bool has_overhangs = false;
bool has_sharp_tails = false;
bool has_cantilever = false;
SupportType support_type;
std::unique_ptr<FillLightning::Generator> generator;
std::unordered_map<double, size_t> printZ_to_lightninglayer;
@ -388,6 +399,12 @@ private:
bool with_infill = false;
/*!
* \brief Polygons representing the limits of the printable area of the
* machine
*/
ExPolygon m_machine_border;
/*!
* \brief Draws circles around each node of the tree into the final support.
*
@ -423,7 +440,7 @@ private:
*
*/
std::vector<std::pair<coordf_t, coordf_t>> plan_layer_heights(std::vector<std::vector<Node*>>& contact_nodes);
std::vector<LayerHeightData> plan_layer_heights(std::vector<std::vector<Node *>> &contact_nodes);
/*!
* \brief Creates points where support contacts the model.
*

View file

@ -753,9 +753,7 @@ inline std::pair<SlabLines, SlabLines> slice_slabs_make_lines(
const std::vector<float> &zs,
bool top,
bool bottom,
const ThrowOnCancel throw_on_cancel_fn,
// BBS: solve conflicts (see declaration) and most elegant way I can get
SlabSlicingConfig config)
const ThrowOnCancel throw_on_cancel_fn)
{
std::pair<SlabLines, SlabLines> out;
SlabLines &lines_top = out.first;
@ -774,7 +772,7 @@ inline std::pair<SlabLines, SlabLines> slice_slabs_make_lines(
tbb::parallel_for(
tbb::blocked_range<int>(0, int(indices.size())),
[&vertices, &indices, &face_neighbors, &face_edge_ids, num_edges, &face_orientation, &zs, top, bottom, &lines_top, &lines_bottom, &lines_mutex_top, &lines_mutex_bottom, throw_on_cancel_fn, &config]
[&vertices, &indices, &face_neighbors, &face_edge_ids, num_edges, &face_orientation, &zs, top, bottom, &lines_top, &lines_bottom, &lines_mutex_top, &lines_mutex_bottom, throw_on_cancel_fn]
(const tbb::blocked_range<int> &range) {
for (int face_idx = range.begin(); face_idx < range.end(); ++ face_idx) {
if ((face_idx & 0x0ffff) == 0)
@ -793,7 +791,7 @@ inline std::pair<SlabLines, SlabLines> slice_slabs_make_lines(
slice_facet_with_slabs<true>(vertices, indices, face_idx, neighbors, edge_ids, num_edges, zs, lines_top, lines_mutex_top);
}
// BBS: add vertical faces option
if (bottom && (fo == FaceOrientation::Down || (config.isVertical && fo == FaceOrientation::Vertical) || fo == FaceOrientation::Degenerate)) {
if (bottom && (fo == FaceOrientation::Down || fo == FaceOrientation::Degenerate)) {
Vec3i neighbors = face_neighbors[face_idx];
// Reset neighborship of this triangle in case the other triangle is oriented backwards from this one.
for (int i = 0; i < 3; ++ i)
@ -1898,8 +1896,7 @@ void slice_mesh_slabs(
const Transform3d &trafo,
std::vector<Polygons> *out_top,
std::vector<Polygons> *out_bottom,
std::function<void()> throw_on_cancel,
SlabSlicingConfig config)
std::function<void()> throw_on_cancel)
{
BOOST_LOG_TRIVIAL(debug) << "slice_mesh_slabs to polygons";
@ -1978,7 +1975,7 @@ void slice_mesh_slabs(
std::vector<Vec3i> face_edge_ids = its_face_edge_ids(mesh, face_neighbors, true, &num_edges);
std::pair<SlabLines, SlabLines> lines = slice_slabs_make_lines(
vertices_transformed, mesh.indices, face_neighbors, face_edge_ids, num_edges, face_orientation, zs,
out_top != nullptr, out_bottom != nullptr, throw_on_cancel, config);
out_top != nullptr, out_bottom != nullptr, throw_on_cancel);
throw_on_cancel();

View file

@ -46,19 +46,6 @@ struct MeshSlicingParamsEx : public MeshSlicingParams
double resolution { 0 };
};
// BBS: MusangKing - NEW: add paint-on support on vertical-faces
// this SlabSlicingConfig aiming to distinguish if slice_slabs_make_lines() outputs lines by slab_slicing on vertical faces
// e.g., for support enforcer operation: isVertical = true; for other color painting operations: isVertical = false (default).
// solve conflicts STUDIO-1183/970/1285
struct SlabSlicingConfig
{
SlabSlicingConfig()
: isVertical(false)
{}
bool isVertical;
};
// All the following slicing functions shall produce consistent results with the same mesh, same transformation matrix and slicing parameters.
// Namely, slice_mesh_slabs() shall produce consistent results with slice_mesh() and slice_mesh_ex() in the sense, that projections made by
// slice_mesh_slabs() shall fall onto slicing planes produced by slice_mesh().
@ -120,9 +107,7 @@ void slice_mesh_slabs(
const Transform3d &trafo,
std::vector<Polygons> *out_top,
std::vector<Polygons> *out_bottom,
std::function<void()> throw_on_cancel,
// BBS: MusangKing
SlabSlicingConfig config = SlabSlicingConfig());
std::function<void()> throw_on_cancel);
// Project mesh upwards pointing surfaces / downwards pointing surfaces into 2D polygons.
void project_mesh(

View file

@ -34,6 +34,7 @@
#define CLI_3MF_NOT_SUPPORT_MACHINE_CHANGE -15
#define CLI_3MF_NEW_MACHINE_NOT_SUPPORTED -16
#define CLI_PROCESS_NOT_COMPATIBLE -17
#define CLI_INVALID_VALUES_IN_3MF -18
#define CLI_NO_SUITABLE_OBJECTS -50

View file

@ -6,6 +6,7 @@
#define SLIC3R_VERSION "@SLIC3R_VERSION@"
#define SoftFever_VERSION "@SoftFever_VERSION@"
#define SLIC3R_BUILD_ID "@SLIC3R_BUILD_ID@"
#define SLIC3R_BUILD_TIME "@SLIC3R_BUILD_TIME@"
//#define SLIC3R_RC_VERSION "@SLIC3R_VERSION@"
#define BBL_RELEASE_TO_PUBLIC @BBL_RELEASE_TO_PUBLIC@
#define BBL_INTERNAL_TESTING @BBL_INTERNAL_TESTING@

View file

@ -28,6 +28,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Widgets/SideMenuPopup.cpp
GUI/Widgets/DropDown.cpp
GUI/Widgets/DropDown.hpp
GUI/Widgets/PopupWindow.cpp
GUI/Widgets/PopupWindow.hpp
GUI/Widgets/Label.cpp
GUI/Widgets/Label.hpp
GUI/Widgets/Scrollbar.cpp
@ -205,6 +207,8 @@ set(SLIC3R_GUI_SOURCES
GUI/GUI_ObjectTableSettings.hpp
GUI/MeshUtils.cpp
GUI/MeshUtils.hpp
GUI/TickCode.cpp
GUI/TickCode.hpp
GUI/Tab.cpp
GUI/Tab.hpp
GUI/ParamsDialog.cpp
@ -273,12 +277,14 @@ set(SLIC3R_GUI_SOURCES
GUI/RemovableDriveManager.hpp
GUI/SendSystemInfoDialog.cpp
GUI/SendSystemInfoDialog.hpp
GUI/SetBedTypeDialog.cpp
GUI/SetBedTypeDialog.hpp
GUI/PlateSettingsDialog.cpp
GUI/PlateSettingsDialog.hpp
GUI/ImGuiWrapper.hpp
GUI/ImGuiWrapper.cpp
GUI/DeviceManager.hpp
GUI/DeviceManager.cpp
GUI/HttpServer.hpp
GUI/HttpServer.cpp
Config/Snapshot.cpp
Config/Snapshot.hpp
Config/Version.cpp
@ -364,6 +370,8 @@ set(SLIC3R_GUI_SOURCES
GUI/PublishDialog.hpp
GUI/RecenterDialog.cpp
GUI/RecenterDialog.hpp
GUI/PrivacyUpdateDialog.cpp
GUI/PrivacyUpdateDialog.hpp
GUI/BonjourDialog.cpp
GUI/BonjourDialog.hpp
GUI/BindDialog.cpp
@ -476,6 +484,10 @@ endif ()
add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES})
target_include_directories(libslic3r_gui PRIVATE Utils)
if (WIN32)
target_include_directories(libslic3r_gui PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../deps/WebView2/include)
endif()
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SLIC3R_GUI_SOURCES})
encoding_check(libslic3r_gui)

View file

@ -390,6 +390,8 @@ std::array<float, 4> GLVolume::MODEL_NEGTIVE_COL = {0.3f, 0.3f, 0.3f, 0.4f};
std::array<float, 4> GLVolume::SUPPORT_ENFORCER_COL = {0.3f, 0.3f, 1.0f, 0.4f};
std::array<float, 4> GLVolume::SUPPORT_BLOCKER_COL = {1.0f, 0.3f, 0.3f, 0.4f};
std::array<float, 4> GLVolume::MODEL_HIDDEN_COL = {0.f, 0.f, 0.f, 0.3f};
std::array<std::array<float, 4>, 5> GLVolume::MODEL_COLOR = { {
{ 1.0f, 1.0f, 0.0f, 1.f },
{ 1.0f, 0.5f, 0.5f, 1.f },
@ -430,6 +432,7 @@ GLVolume::GLVolume(float r, float g, float b, float a)
, selected(false)
, disabled(false)
, printable(true)
, visible(true)
, is_active(true)
, zoom_to_volumes(true)
, shader_outside_printer_detection_enabled(false)
@ -521,6 +524,14 @@ void GLVolume::set_render_color()
render_color[2] = UNPRINTABLE_COLOR[2];
render_color[3] = UNPRINTABLE_COLOR[3];
}
//BBS set invisible color
if (!visible) {
render_color[0] = MODEL_HIDDEN_COL[0];
render_color[1] = MODEL_HIDDEN_COL[1];
render_color[2] = MODEL_HIDDEN_COL[2];
render_color[3] = MODEL_HIDDEN_COL[3];
}
}
std::array<float, 4> color_from_model_volume(const ModelVolume& model_volume)
@ -684,6 +695,9 @@ void GLVolume::render(bool with_outline) const
break;
ModelObject* mo = model_objects[object_idx()];
if (volume_idx() >= mo->volumes.size())
break;
ModelVolume* mv = mo->volumes[volume_idx()];
if (mv->mmu_segmentation_facets.empty())
break;
@ -1149,7 +1163,7 @@ int GLVolumeCollection::load_wipe_tower_preview(
std::vector<std::array<float, 4>> extruder_colors = get_extruders_colors();
std::vector<std::array<float, 4>> colors;
GUI::PartPlateList& ppl = GUI::wxGetApp().plater()->get_partplate_list();
std::vector<int> plate_extruders = ppl.get_plate(plate_idx)->get_extruders();
std::vector<int> plate_extruders = ppl.get_plate(plate_idx)->get_extruders(true);
TriangleMesh wipe_tower_shell = make_cube(width, depth, height);
for (int extruder_id : plate_extruders) {
if (extruder_id <= extruder_colors.size())

View file

@ -269,6 +269,7 @@ public:
static std::array<float, 4> MODEL_NEGTIVE_COL;
static std::array<float, 4> SUPPORT_ENFORCER_COL;
static std::array<float, 4> SUPPORT_BLOCKER_COL;
static std::array<float, 4> MODEL_HIDDEN_COL;
static void update_render_colors();
static void load_render_colors();
@ -286,6 +287,7 @@ public:
GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f);
GLVolume(const std::array<float, 4>& rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {}
virtual ~GLVolume() = default;
// BBS
protected:
@ -363,6 +365,8 @@ public:
bool disabled : 1;
// Is this object printable?
bool printable : 1;
// Is this object visible(in assemble view)?
bool visible : 1;
// Whether or not this volume is active for rendering
bool is_active : 1;
// Whether or not to use this volume when applying zoom_to_volumes()

View file

@ -121,12 +121,13 @@ void AMSMaterialsSetting::create_panel_normal(wxWindow* parent)
m_sizer_filament->Add(m_comboBox_filament, 1, wxALIGN_CENTER, 0);
m_readonly_filament = new TextInput(parent, wxEmptyString, "", "", wxDefaultPosition, AMS_MATERIALS_SETTING_COMBOX_WIDTH, wxTE_READONLY);
m_readonly_filament = new TextInput(parent, wxEmptyString, "", "", wxDefaultPosition, AMS_MATERIALS_SETTING_COMBOX_WIDTH, wxTE_READONLY | wxRIGHT);
m_readonly_filament->SetBorderColor(StateColor(std::make_pair(0xDBDBDB, (int)StateColor::Focused), std::make_pair(0x009688, (int)StateColor::Hovered),
std::make_pair(0xDBDBDB, (int)StateColor::Normal)));
m_readonly_filament->GetTextCtrl()->Bind(wxEVT_SET_FOCUS, [](auto& e) {
;
});
m_readonly_filament->SetFont(::Label::Body_14);
m_readonly_filament->SetLabelColor(AMS_MATERIALS_SETTING_GREY800);
m_readonly_filament->GetTextCtrl()->Bind(wxEVT_SET_FOCUS, [](auto& e) {});
m_readonly_filament->GetTextCtrl()->Hide();
m_sizer_filament->Add(m_readonly_filament, 1, wxALIGN_CENTER, 0);
m_readonly_filament->Hide();
@ -367,6 +368,13 @@ void AMSMaterialsSetting::enable_confirm_button(bool en)
else {
m_comboBox_filament->Show(en);
m_readonly_filament->Show(!en);
if ( !is_virtual_tray() ) {
m_tip_readonly->SetLabelText(_L("Setting AMS slot information while printing is not supported"));
}
else {
m_tip_readonly->SetLabelText(_L("Setting Virtual slot information while printing is not supported"));
}
m_tip_readonly->Show(!en);
}
}
@ -376,7 +384,7 @@ void AMSMaterialsSetting::on_select_ok(wxCommandEvent &event)
wxString k_text = m_input_k_val->GetTextCtrl()->GetValue();
wxString n_text = m_input_n_val->GetTextCtrl()->GetValue();
if (is_virtual_tray()) {
if (is_virtual_tray() && obj && !obj->is_support_filament_edit_virtual_tray) {
if (!ExtrusionCalibration::check_k_validation(k_text)) {
wxString k_tips = _L("Please input a valid value (K in 0~0.5)");
wxString kn_tips = _L("Please input a valid value (K in 0~0.5, N in 0.6~2.0)");
@ -478,8 +486,12 @@ void AMSMaterialsSetting::on_select_ok(wxCommandEvent &event)
}
// set filament
if (!is_virtual_tray()) {
obj->command_ams_filament_settings(ams_id, tray_id, ams_filament_id, ams_setting_id, std::string(col_buf), m_filament_type, nozzle_temp_min_int, nozzle_temp_max_int);
if (obj->is_support_filament_edit_virtual_tray || !is_virtual_tray()) {
if (is_virtual_tray()) {
obj->command_ams_filament_settings(255, VIRTUAL_TRAY_ID, ams_filament_id, ams_setting_id, std::string(col_buf), m_filament_type, nozzle_temp_min_int, nozzle_temp_max_int);
} else {
obj->command_ams_filament_settings(ams_id, tray_id, ams_filament_id, ams_setting_id, std::string(col_buf), m_filament_type, nozzle_temp_min_int, nozzle_temp_max_int);
}
}
// set k / n value
@ -548,7 +560,10 @@ void AMSMaterialsSetting::update_widgets()
{
// virtual tray
if (is_virtual_tray()) {
m_panel_normal->Hide();
if (obj && obj->is_support_filament_edit_virtual_tray)
m_panel_normal->Show();
else
m_panel_normal->Hide();
m_panel_kn->Show();
} else if (obj && obj->is_function_supported(PrinterFunction::FUNC_EXTRUSION_CALI)) {
m_panel_normal->Show();
@ -583,7 +598,7 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi
m_input_k_val->GetTextCtrl()->SetValue(k);
m_input_n_val->GetTextCtrl()->SetValue(n);
if (is_virtual_tray()) {
if (is_virtual_tray() && obj && !obj->is_support_filament_edit_virtual_tray) {
m_button_confirm->Show();
update();
Layout();
@ -609,7 +624,8 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi
m_panel_SN->Show();
m_comboBox_filament->Hide();
m_readonly_filament->Show();
m_readonly_filament->GetTextCtrl()->SetLabel("Bambu " + filament);
//m_readonly_filament->GetTextCtrl()->SetLabel("Bambu " + filament);
m_readonly_filament->SetLabel("Bambu " + filament);
m_input_nozzle_min->GetTextCtrl()->SetValue(temp_min);
m_input_nozzle_max->GetTextCtrl()->SetValue(temp_max);
@ -712,6 +728,31 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt)
if (preset_bundle) {
for (auto it = preset_bundle->filaments.begin(); it != preset_bundle->filaments.end(); it++) {
if (it->alias.compare(m_comboBox_filament->GetValue().ToStdString()) == 0) {
//check is it in the filament blacklist
bool in_blacklist = false;
std::string action;
std::string info;
std::string filamnt_type;
it->get_filament_type(filamnt_type);
if (it->vendor) {
DeviceManager::check_filaments_in_blacklist(it->vendor->name, filamnt_type, in_blacklist, action, info);
}
if (in_blacklist) {
if (action == "prohibition") {
MessageDialog msg_wingow(nullptr, info, _L("Error"), wxICON_WARNING | wxOK);
msg_wingow.ShowModal();
m_comboBox_filament->SetSelection(m_filament_selection);
return;
}
else if (action == "warning") {
MessageDialog msg_wingow(nullptr, info, _L("Warning"), wxICON_INFORMATION | wxOK);
msg_wingow.ShowModal();
}
}
// ) if nozzle_temperature_range is found
ConfigOption* opt_min = it->config.option("nozzle_temperature_range_low");
if (opt_min) {
@ -742,6 +783,8 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt)
}
if (!found_filament_type)
m_filament_type = "";
break;
}
}
}
@ -751,6 +794,8 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt)
if (m_input_nozzle_max->GetTextCtrl()->GetValue().IsEmpty()) {
m_input_nozzle_max->GetTextCtrl()->SetValue("220");
}
m_filament_selection = evt.GetSelection();
}
void AMSMaterialsSetting::on_dpi_changed(const wxRect &suggested_rect) { this->Refresh(); }

View file

@ -98,13 +98,14 @@ protected:
TextInput* m_input_k_val;
wxStaticText* m_n_param;
TextInput* m_input_n_val;
int m_filament_selection;
#ifdef __APPLE__
wxComboBox *m_comboBox_filament;
#else
ComboBox *m_comboBox_filament;
#endif
TextInput* m_readonly_filament;
TextInput* m_readonly_filament;
};
}} // namespace Slic3r::GUI

View file

@ -53,9 +53,9 @@ void AMSSetting::create()
m_sizer_Insert_material_tip->Add(0, 0, 0, wxLEFT, 10);
// tip line1
m_tip_Insert_material_line1 = new wxStaticText(m_panel_body, wxID_ANY,
_L("The AMS will automatically read the filament information when inserting a new Bambu Lab filament. This takes about 20 seconds."),
wxDefaultPosition, wxDefaultSize, 0);
m_tip_Insert_material_line1 = new Label(m_panel_body,
_L("The AMS will automatically read the filament information when inserting a new Bambu Lab filament. This takes about 20 seconds.")
);
m_tip_Insert_material_line1->SetFont(::Label::Body_13);
m_tip_Insert_material_line1->SetForegroundColour(AMS_SETTING_GREY700);
m_tip_Insert_material_line1->SetSize(wxSize(AMS_SETTING_BODY_WIDTH, -1));
@ -64,9 +64,9 @@ void AMSSetting::create()
m_sizer_Insert_material_tip_inline->Add(m_tip_Insert_material_line1, 0, wxEXPAND, 0);
// tip line2
m_tip_Insert_material_line2 = new wxStaticText(m_panel_body, wxID_ANY,
_L("Note: if new filament is inserted during printing, the AMS will not automatically read any information until printing is completed."),
wxDefaultPosition, wxDefaultSize, 0);
m_tip_Insert_material_line2 = new Label(m_panel_body,
_L("Note: if new filament is inserted during printing, the AMS will not automatically read any information until printing is completed.")
);
m_tip_Insert_material_line2->SetFont(::Label::Body_13);
m_tip_Insert_material_line2->SetForegroundColour(AMS_SETTING_GREY700);
m_tip_Insert_material_line2->SetSize(wxSize(AMS_SETTING_BODY_WIDTH, -1));
@ -75,10 +75,9 @@ void AMSSetting::create()
m_sizer_Insert_material_tip_inline->Add(m_tip_Insert_material_line2, 0, wxEXPAND | wxTOP, 8);
// tip line2
m_tip_Insert_material_line3 =
new wxStaticText(m_panel_body, wxID_ANY,
_L("When inserting a new filament, the AMS will not automatically read its information, leaving it blank for you to enter manually."),
wxDefaultPosition, wxDefaultSize, 0);
m_tip_Insert_material_line3 = new Label(m_panel_body,
_L("When inserting a new filament, the AMS will not automatically read its information, leaving it blank for you to enter manually.")
);
m_tip_Insert_material_line3->SetFont(::Label::Body_13);
m_tip_Insert_material_line3->SetForegroundColour(AMS_SETTING_GREY700);
m_tip_Insert_material_line3->SetSize(wxSize(AMS_SETTING_BODY_WIDTH, -1));
@ -109,18 +108,18 @@ void AMSSetting::create()
// tip line
m_sizer_starting_tip_inline = new wxBoxSizer(wxVERTICAL);
m_tip_starting_line1 = new wxStaticText(m_panel_body, wxID_ANY,
_L("The AMS will automatically read the information of inserted filament on start-up. It will take about 1 minute.The reading process will roll filament spools."),
wxDefaultPosition, wxDefaultSize, 0);
m_tip_starting_line1 = new Label(m_panel_body,
_L("The AMS will automatically read the information of inserted filament on start-up. It will take about 1 minute.The reading process will roll filament spools.")
);
m_tip_starting_line1->SetFont(::Label::Body_13);
m_tip_starting_line1->SetForegroundColour(AMS_SETTING_GREY700);
m_tip_starting_line1->SetSize(wxSize(AMS_SETTING_BODY_WIDTH, -1));
m_tip_starting_line1->Wrap(AMS_SETTING_BODY_WIDTH);
m_sizer_starting_tip_inline->Add(m_tip_starting_line1, 0, wxEXPAND, 0);
m_tip_starting_line2 = new wxStaticText(m_panel_body, wxID_ANY,
_L("The AMS will not automatically read information from inserted filament during startup and will continue to use the information recorded before the last shutdown."),
wxDefaultPosition, wxDefaultSize, 0);
m_tip_starting_line2 = new Label(m_panel_body,
_L("The AMS will not automatically read information from inserted filament during startup and will continue to use the information recorded before the last shutdown.")
);
m_tip_starting_line2->SetFont(::Label::Body_13);
m_tip_starting_line2->SetForegroundColour(AMS_SETTING_GREY700);
m_tip_starting_line2->SetSize(wxSize(AMS_SETTING_BODY_WIDTH, -1));
@ -148,9 +147,9 @@ void AMSSetting::create()
// tip line
m_sizer_remain_inline = new wxBoxSizer(wxVERTICAL);
m_tip_remain_line1 = new wxStaticText(m_panel_body, wxID_ANY,
_L("The AMS will estimate Bambu filament's remaining capacity after the filament info is updated. During printing, remaining capacity will be updated automatically."),
wxDefaultPosition, wxDefaultSize, 0);
m_tip_remain_line1 = new Label(m_panel_body,
_L("The AMS will estimate Bambu filament's remaining capacity after the filament info is updated. During printing, remaining capacity will be updated automatically.")
);
m_tip_remain_line1->SetFont(::Label::Body_13);
m_tip_remain_line1->SetForegroundColour(AMS_SETTING_GREY700);
m_tip_remain_line1->SetSize(wxSize(AMS_SETTING_BODY_WIDTH, -1));
@ -178,9 +177,9 @@ void AMSSetting::create()
// tip line
m_sizer_switch_filament_inline = new wxBoxSizer(wxVERTICAL);
m_tip_switch_filament_line1 = new wxStaticText(m_panel_body, wxID_ANY,
_L("AMS will continue to another spool with the same properties of filament automatically when current filament runs out"),
wxDefaultPosition, wxDefaultSize, 0);
m_tip_switch_filament_line1 = new Label(m_panel_body,
_L("AMS will continue to another spool with the same properties of filament automatically when current filament runs out")
);
m_tip_switch_filament_line1->SetFont(::Label::Body_13);
m_tip_switch_filament_line1->SetForegroundColour(AMS_SETTING_GREY700);
m_tip_switch_filament_line1->SetSize(wxSize(AMS_SETTING_BODY_WIDTH, -1));

View file

@ -49,22 +49,22 @@ protected:
wxPanel * m_panel_body;
CheckBox * m_checkbox_Insert_material_auto_read;
wxStaticText *m_title_Insert_material_auto_read;
wxStaticText *m_tip_Insert_material_line1;
wxStaticText *m_tip_Insert_material_line2;
wxStaticText *m_tip_Insert_material_line3;
Label* m_tip_Insert_material_line1;
Label* m_tip_Insert_material_line2;
Label* m_tip_Insert_material_line3;
CheckBox * m_checkbox_starting_auto_read;
wxStaticText *m_title_starting_auto_read;
wxStaticText *m_tip_starting_line1;
wxStaticText *m_tip_starting_line2;
Label* m_tip_starting_line1;
Label* m_tip_starting_line2;
CheckBox * m_checkbox_remain;
wxStaticText *m_title_remain;
wxStaticText *m_tip_remain_line1;
Label* m_tip_remain_line1;
CheckBox* m_checkbox_switch_filament;
wxStaticText* m_title_switch_filament;
wxStaticText* m_tip_switch_filament_line1;
Label* m_tip_switch_filament_line1;
wxStaticText *m_tip_ams_img;
Button * m_button_auto_demarcate;

View file

@ -257,6 +257,13 @@ AboutDialog::AboutDialog()
version->SetBackgroundColour(wxColour("#009688"));
vesizer->Add(version, 0, wxALL | wxALIGN_CENTER_HORIZONTAL, FromDIP(5));
#if BBL_INTERNAL_TESTING
wxString build_time = wxString::Format("Build Time: %s", std::string(SLIC3R_BUILD_TIME));
wxStaticText* build_time_text = new wxStaticText(this, wxID_ANY, build_time, wxDefaultPosition, wxDefaultSize);
build_time_text->SetForegroundColour(wxColour("#FFFFFE"));
build_time_text->SetBackgroundColour(wxColour("#00AF42"));
vesizer->Add(build_time_text, 0, wxALL | wxALIGN_CENTER_HORIZONTAL, FromDIP(5));
#endif
vesizer->Add(0, 0, 1, wxEXPAND, FromDIP(5));
}

View file

@ -207,11 +207,11 @@ void MaterialItem::doRender(wxDC &dc)
}
AmsMapingPopup::AmsMapingPopup(wxWindow *parent)
:wxPopupTransientWindow(parent, wxBORDER_NONE)
: PopupWindow(parent, wxBORDER_NONE)
{
SetSize(wxSize(FromDIP(300), -1));
SetMinSize(wxSize(FromDIP(300), -1));
SetMaxSize(wxSize(FromDIP(300), -1));
SetSize(wxSize(FromDIP(252), -1));
SetMinSize(wxSize(FromDIP(252), -1));
SetMaxSize(wxSize(FromDIP(252), -1));
Bind(wxEVT_PAINT, &AmsMapingPopup::paintEvent, this);
@ -243,21 +243,36 @@ void MaterialItem::doRender(wxDC &dc)
title_panel->Fit();
m_sizer_list = new wxBoxSizer(wxVERTICAL);
for (auto i = 0; i < AMS_TOTAL_COUNT; i++) {
auto sizer_mapping_list = new wxBoxSizer(wxHORIZONTAL);
/*auto ams_mapping_item_container = new wxStaticBitmap(this, wxID_ANY, create_scaled_bitmap("ams_mapping_container", this, 78), wxDefaultPosition,
wxSize(FromDIP(230), FromDIP(78)), 0);*/
auto ams_mapping_item_container = new MappingContainer(this);
ams_mapping_item_container->SetSizer(sizer_mapping_list);
ams_mapping_item_container->Layout();
//ams_mapping_item_container->Hide();
m_amsmapping_container_sizer_list.push_back(sizer_mapping_list);
m_amsmapping_container_list.push_back(ams_mapping_item_container);
m_sizer_list->Add(ams_mapping_item_container, 0, wxALIGN_CENTER_HORIZONTAL|wxTOP|wxBOTTOM, FromDIP(5));
}
m_warning_text = new wxStaticText(this, wxID_ANY, wxEmptyString);
m_warning_text->SetForegroundColour(wxColour(0xFF, 0x6F, 0x00));
m_warning_text->SetFont(::Label::Body_12);
auto cant_not_match_tip = _L("Note: Only the AMS slots loaded with the same material type can be selected.");
m_warning_text->SetLabel(format_text(cant_not_match_tip));
m_warning_text->SetMinSize(wxSize(FromDIP(280), FromDIP(-1)));
m_warning_text->Wrap(FromDIP(280));
m_warning_text->SetMinSize(wxSize(FromDIP(248), FromDIP(-1)));
m_warning_text->Wrap(FromDIP(248));
m_sizer_main->Add(title_panel, 0, wxEXPAND | wxALL, FromDIP(2));
m_sizer_main->Add(0, 0, 0, wxTOP, FromDIP(5));
m_sizer_main->Add(m_sizer_list, 0, wxEXPAND | wxALL, FromDIP(0));
m_sizer_main->Add(m_warning_text, 0, wxEXPAND | wxALL, FromDIP(10));
m_sizer_main->Add(0, 0, 0, wxTOP, FromDIP(5));
m_sizer_main->Add(m_warning_text, 0, wxEXPAND | wxALL, FromDIP(6));
SetSizer(m_sizer_main);
Layout();
Fit();
}
wxString AmsMapingPopup::format_text(wxString &m_msg)
@ -315,15 +330,29 @@ void AmsMapingPopup::on_left_down(wxMouseEvent &evt)
void AmsMapingPopup::update_ams_data(std::map<std::string, Ams*> amsList)
{
m_has_unmatch_filament = false;
m_mapping_item_list.clear();
if (m_amsmapping_sizer_list.size() > 0) {
for (wxBoxSizer *bz : m_amsmapping_sizer_list) { bz->Clear(true); }
m_amsmapping_sizer_list.clear();
//m_mapping_item_list.clear();
for (auto& ams_container : m_amsmapping_container_list) {
ams_container->Hide();
}
for (wxWindow *mitem : m_mapping_item_list) {
mitem->Destroy();
mitem = nullptr;
}
m_mapping_item_list.clear();
if (m_amsmapping_container_sizer_list.size() > 0) {
for (wxBoxSizer *siz : m_amsmapping_container_sizer_list) {
siz->Clear(true);
}
}
std::map<std::string, Ams *>::iterator ams_iter;
BOOST_LOG_TRIVIAL(trace) << "ams_mapping total count " << amsList.size();
int m_amsmapping_container_list_index = 0;
for (ams_iter = amsList.begin(); ams_iter != amsList.end(); ams_iter++) {
@ -355,7 +384,10 @@ void AmsMapingPopup::update_ams_data(std::map<std::string, Ams*> amsList)
tray_datas.push_back(td);
}
add_ams_mapping(tray_datas);
m_amsmapping_container_list[m_amsmapping_container_list_index]->Show();
add_ams_mapping(tray_datas, m_amsmapping_container_list[m_amsmapping_container_list_index], m_amsmapping_container_sizer_list[m_amsmapping_container_list_index]);
m_amsmapping_container_list_index++;
}
@ -406,40 +438,38 @@ std::vector<TrayData> AmsMapingPopup::parse_ams_mapping(std::map<std::string, Am
return m_tray_data;
}
void AmsMapingPopup::add_ams_mapping(std::vector<TrayData> tray_data)
void AmsMapingPopup::add_ams_mapping(std::vector<TrayData> tray_data, wxWindow* container, wxBoxSizer* sizer)
{
auto sizer_mapping_list = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(0,0,0,wxLEFT,FromDIP(6));
for (auto i = 0; i < tray_data.size(); i++) {
wxBoxSizer *sizer_mapping_item = new wxBoxSizer(wxVERTICAL);
// set number
auto number = new wxStaticText(this, wxID_ANY, wxGetApp().transition_tridid(tray_data[i].id), wxDefaultPosition, wxDefaultSize, 0);
/* auto number = new wxStaticText(this, wxID_ANY, wxGetApp().transition_tridid(tray_data[i].id), wxDefaultPosition, wxDefaultSize, 0);
number->SetFont(::Label::Body_13);
number->SetForegroundColour(wxColour(0X6B, 0X6B, 0X6B));
number->Wrap(-1);
number->Wrap(-1);*/
// set button
MappingItem *m_filament_name = new MappingItem(this);
m_filament_name->SetSize(wxSize(FromDIP(62), FromDIP(22)));
m_filament_name->SetMinSize(wxSize(FromDIP(62), FromDIP(22)));
m_filament_name->SetMaxSize(wxSize(FromDIP(62), FromDIP(22)));
//m_filament_name->SetCornerRadius(5);
m_filament_name->SetFont(::Label::Body_12);
m_mapping_item_list.push_back(m_filament_name);
MappingItem *m_mapping_item = new MappingItem(container);
m_mapping_item->SetSize(wxSize(FromDIP(68 * 0.7), FromDIP(100 * 0.6)));
m_mapping_item->SetMinSize(wxSize(FromDIP(68 * 0.7), FromDIP(100 * 0.6)));
m_mapping_item->SetMaxSize(wxSize(FromDIP(68 * 0.7), FromDIP(100 * 0.6)));
//m_mapping_item->SetCornerRadius(5);
m_mapping_item->SetFont(::Label::Body_12);
m_mapping_item_list.push_back(m_mapping_item);
if (tray_data[i].type == NORMAL) {
if (is_match_material(tray_data[i].filament_type)) {
m_filament_name->set_data(tray_data[i].colour, tray_data[i].name, tray_data[i]);
m_mapping_item->set_data(tray_data[i].colour, tray_data[i].name, tray_data[i]);
} else {
m_filament_name->set_data(wxColour(0xEE,0xEE,0xEE), tray_data[i].name, tray_data[i], true);
m_mapping_item->set_data(wxColour(0xEE,0xEE,0xEE), tray_data[i].name, tray_data[i], true);
m_has_unmatch_filament = true;
}
m_filament_name->Bind(wxEVT_LEFT_DOWN, [this, tray_data, i, m_filament_name](wxMouseEvent &e) {
m_mapping_item->Bind(wxEVT_LEFT_DOWN, [this, tray_data, i, m_mapping_item](wxMouseEvent &e) {
if (!is_match_material(tray_data[i].filament_type)) return;
m_filament_name->send_event(m_current_filament_id);
m_mapping_item->send_event(m_current_filament_id);
Dismiss();
});
}
@ -447,29 +477,30 @@ void AmsMapingPopup::add_ams_mapping(std::vector<TrayData> tray_data)
// temp
if (tray_data[i].type == EMPTY) {
m_filament_name->set_data(wxColour(0xCE, 0xCE, 0xCE), "-", tray_data[i]);
m_filament_name->Bind(wxEVT_LEFT_DOWN, [this, tray_data, i, m_filament_name](wxMouseEvent &e) {
m_filament_name->send_event(m_current_filament_id);
m_mapping_item->set_data(wxColour(0xCE, 0xCE, 0xCE), "-", tray_data[i]);
m_mapping_item->Bind(wxEVT_LEFT_DOWN, [this, tray_data, i, m_mapping_item](wxMouseEvent &e) {
m_mapping_item->send_event(m_current_filament_id);
Dismiss();
});
}
// third party
if (tray_data[i].type == THIRD) {
m_filament_name->set_data(wxColour(0xCE, 0xCE, 0xCE), "?", tray_data[i]);
m_filament_name->Bind(wxEVT_LEFT_DOWN, [this, tray_data, i, m_filament_name](wxMouseEvent &e) {
m_filament_name->send_event(m_current_filament_id);
m_mapping_item->set_data(wxColour(0xCE, 0xCE, 0xCE), "?", tray_data[i]);
m_mapping_item->Bind(wxEVT_LEFT_DOWN, [this, tray_data, i, m_mapping_item](wxMouseEvent &e) {
m_mapping_item->send_event(m_current_filament_id);
Dismiss();
});
}
sizer_mapping_item->Add(number, 0, wxALIGN_CENTER_HORIZONTAL, 0);
sizer_mapping_item->Add(m_filament_name, 0, wxALIGN_CENTER_HORIZONTAL, 0);
sizer_mapping_list->Add(sizer_mapping_item, 0, wxALL, FromDIP(5));
m_amsmapping_sizer_list.push_back(sizer_mapping_list);
//sizer_mapping_item->Add(number, 0, wxALIGN_CENTER_HORIZONTAL, 0);
//sizer_mapping_item->Add(m_mapping_item, 0, wxALIGN_CENTER_HORIZONTAL, 0);
m_mapping_item->set_tray_index(wxGetApp().transition_tridid(tray_data[i].id));
sizer->Add(0,0,0,wxRIGHT,FromDIP(6));
sizer->Add(m_mapping_item, 0, wxTOP, FromDIP(1));
}
m_sizer_list->Add(sizer_mapping_list, 0, wxALIGN_CENTER_HORIZONTAL, 0);
}
void AmsMapingPopup::OnDismiss()
@ -479,7 +510,7 @@ void AmsMapingPopup::OnDismiss()
bool AmsMapingPopup::ProcessLeftDown(wxMouseEvent &event)
{
return wxPopupTransientWindow::ProcessLeftDown(event);
return PopupWindow::ProcessLeftDown(event);
}
void AmsMapingPopup::paintEvent(wxPaintEvent &evt)
@ -514,7 +545,7 @@ void MappingItem::send_event(int fliament_id)
wxString param = wxString::Format("%d|%d|%d|%s|%d", m_coloul.Red(), m_coloul.Green(), m_coloul.Blue(), number, fliament_id);
event.SetString(param);
event.SetEventObject(this->GetParent()->GetParent());
wxPostEvent(this->GetParent()->GetParent(), event);
wxPostEvent(this->GetParent()->GetParent()->GetParent(), event);
}
void MappingItem::msw_rescale()
@ -551,7 +582,7 @@ void MappingItem::render(wxDC &dc)
#endif
// materials name
dc.SetFont(::Label::Body_12);
dc.SetFont(::Label::Head_13);
auto txt_colour = m_coloul.GetLuminance() < 0.5 ? *wxWHITE : wxColour(0x26, 0x2E, 0x30);
txt_colour = m_unmatch ? wxColour(0xCE, 0xCE, 0xCE) : txt_colour;
@ -563,8 +594,15 @@ void MappingItem::render(wxDC &dc)
m_name = m_name.substr(0, 3) + "." + m_name.substr(m_name.length() - 1);
}*/
auto txt_size = dc.GetTextExtent(m_name);
dc.DrawText(m_name, wxPoint((GetSize().x - txt_size.x) / 2, (GetSize().y - txt_size.y) / 2));
auto txt_size = dc.GetTextExtent(m_tray_index);
auto top = (GetSize().y - MAPPING_ITEM_REAL_SIZE.y) / 2 + FromDIP(8);
dc.DrawText(m_tray_index, wxPoint((GetSize().x - txt_size.x) / 2, top));
top += txt_size.y + FromDIP(7);
dc.SetFont(::Label::Body_12);
txt_size = dc.GetTextExtent(m_name);
dc.DrawText(m_name, wxPoint((GetSize().x - txt_size.x) / 2, top));
}
void MappingItem::set_data(wxColour colour, wxString name, TrayData data, bool unmatch)
@ -582,21 +620,32 @@ void MappingItem::doRender(wxDC &dc)
{
dc.SetPen(m_coloul);
dc.SetBrush(wxBrush(m_coloul));
dc.DrawRoundedRectangle(0, 0, GetSize().x, GetSize().y,5);
if (m_coloul == *wxWHITE) {
dc.SetPen(wxPen(wxColour(0xAC, 0xAC, 0xAC),1));
#ifdef __APPLE__
dc.DrawRoundedRectangle(1, 1, GetSize().x - 1, GetSize().y - 1, 5);
#else
dc.DrawRoundedRectangle(0, 0, GetSize().x, GetSize().y, 5);
#endif // __APPLE__
dc.DrawRectangle(0, (GetSize().y - MAPPING_ITEM_REAL_SIZE.y) / 2, MAPPING_ITEM_REAL_SIZE.x, MAPPING_ITEM_REAL_SIZE.y);
}
// if (m_coloul == *wxWHITE) {
// dc.SetPen(wxPen(wxColour(0xAC, 0xAC, 0xAC), 1));
//#ifdef __APPLE__
// dc.DrawRectangle(1, 1, GetSize().x - 1, GetSize().y - 1);
//#else
// dc.DrawRectangle(0, 0, tray_size.x, tray_size.y);
//#endif // __APPLE__
// }
wxColour side_colour = wxColour(0xE4E4E4);
dc.SetPen(side_colour);
dc.SetBrush(wxBrush(side_colour));
#ifdef __APPLE__
dc.DrawRectangle(0, 0, FromDIP(4), GetSize().y);
dc.DrawRectangle(GetSize().x - FromDIP(4), 0, FromDIP(4), GetSize().y);
#else
dc.DrawRectangle(0, 0, FromDIP(4), GetSize().y);
dc.DrawRectangle(GetSize().x - FromDIP(4), 0, FromDIP(4), GetSize().y);
#endif // __APPLE__
}
AmsMapingTipPopup::AmsMapingTipPopup(wxWindow *parent)
:wxPopupTransientWindow(parent, wxBORDER_NONE)
:PopupWindow(parent, wxBORDER_NONE)
{
SetBackgroundColour(*wxWHITE);
wxBoxSizer *m_sizer_main = new wxBoxSizer(wxVERTICAL);
@ -696,11 +745,11 @@ void AmsMapingTipPopup::paintEvent(wxPaintEvent &evt)
void AmsMapingTipPopup::OnDismiss() {}
bool AmsMapingTipPopup::ProcessLeftDown(wxMouseEvent &event) {
return wxPopupTransientWindow::ProcessLeftDown(event); }
return PopupWindow::ProcessLeftDown(event); }
AmsHumidityTipPopup::AmsHumidityTipPopup(wxWindow* parent)
:wxPopupTransientWindow(parent, wxBORDER_NONE)
:PopupWindow(parent, wxBORDER_NONE)
{
SetBackgroundColour(*wxWHITE);
@ -727,7 +776,7 @@ AmsHumidityTipPopup::AmsHumidityTipPopup(wxWindow* parent)
m_staticText1->SetFont(::Label::Head_13);
m_staticText2 = new Label(this, _L("Green means that AMS humidity is normal, orange represent humidity is high, red represent humidity is too high.(Hygrometer: lower the better, The bars: higher the better)"));
m_staticText2 = new Label(this, _L("Green means that AMS humidity is normal, orange represent humidity is high, red represent humidity is too high.(Hygrometer: lower the better.)"));
m_staticText2->SetFont(::Label::Body_13);
m_staticText2->SetSize(wxSize(FromDIP(360), -1));
m_staticText2->SetMinSize(wxSize(FromDIP(360), -1));
@ -740,7 +789,7 @@ AmsHumidityTipPopup::AmsHumidityTipPopup(wxWindow* parent)
m_staticText3->SetFont(::Label::Head_13);
m_staticText4 = new Label(this, _L("A desiccant status lower than two bars indicates that desiccant may be inactive. Please change the desiccant. (Higher is better)"));
m_staticText4 = new Label(this, _L("A desiccant status lower than two bars indicates that desiccant may be inactive. Please change the desiccant.(The bars: higher the better.)"));
m_staticText4->SetFont(::Label::Body_13);
m_staticText4->SetSize(wxSize(FromDIP(360), -1));
m_staticText4->SetMinSize(wxSize(FromDIP(360), -1));
@ -766,9 +815,9 @@ AmsHumidityTipPopup::AmsHumidityTipPopup(wxWindow* parent)
main_sizer->Add(m_staticText_note, 0, wxALL | wxLEFT | wxRIGHT, 34);
m_button_confirm = new Button(this, _L("OK"));
StateColor btn_bg_green(std::pair<wxColour, int>(wxColour(27, 136, 68), StateColor::Pressed), std::pair<wxColour, int>(wxColour(0, 150, 136), StateColor::Normal));
StateColor btn_bg_green(std::pair<wxColour, int>(wxColour(27, 136, 68), StateColor::Pressed), std::pair<wxColour, int>(wxColour(0, 174, 66), StateColor::Normal));
m_button_confirm->SetBackgroundColor(btn_bg_green);
m_button_confirm->SetBorderColor(wxColour(0, 150, 136));
m_button_confirm->SetBorderColor(wxColour(0, 174, 66));
m_button_confirm->SetTextColor(wxColour(0xFFFFFE));
m_button_confirm->SetSize(wxSize(FromDIP(72), FromDIP(24)));
m_button_confirm->SetMinSize(wxSize(FromDIP(72), FromDIP(24)));
@ -814,11 +863,11 @@ void AmsHumidityTipPopup::paintEvent(wxPaintEvent& evt)
void AmsHumidityTipPopup::OnDismiss() {}
bool AmsHumidityTipPopup::ProcessLeftDown(wxMouseEvent& event) {
return wxPopupTransientWindow::ProcessLeftDown(event);
return PopupWindow::ProcessLeftDown(event);
}
AmsTutorialPopup::AmsTutorialPopup(wxWindow* parent)
:wxPopupTransientWindow(parent, wxBORDER_NONE)
:PopupWindow(parent, wxBORDER_NONE)
{
Bind(wxEVT_PAINT, &AmsTutorialPopup::paintEvent, this);
SetBackgroundColour(*wxWHITE);
@ -916,12 +965,12 @@ void AmsTutorialPopup::paintEvent(wxPaintEvent& evt)
void AmsTutorialPopup::OnDismiss() {}
bool AmsTutorialPopup::ProcessLeftDown(wxMouseEvent& event) {
return wxPopupTransientWindow::ProcessLeftDown(event);
return PopupWindow::ProcessLeftDown(event);
}
AmsIntroducePopup::AmsIntroducePopup(wxWindow* parent)
:wxPopupTransientWindow(parent, wxBORDER_NONE)
:PopupWindow(parent, wxBORDER_NONE)
{
Bind(wxEVT_PAINT, &AmsIntroducePopup::paintEvent, this);
SetBackgroundColour(*wxWHITE);
@ -1001,8 +1050,60 @@ void AmsIntroducePopup::paintEvent(wxPaintEvent& evt)
void AmsIntroducePopup::OnDismiss() {}
bool AmsIntroducePopup::ProcessLeftDown(wxMouseEvent& event) {
return wxPopupTransientWindow::ProcessLeftDown(event);
return PopupWindow::ProcessLeftDown(event);
}
MappingContainer::MappingContainer(wxWindow* parent)
: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize)
{
#ifdef __WINDOWS__
SetDoubleBuffered(true);
#endif //__WINDOWS__
SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE));
Bind(wxEVT_PAINT, &MappingContainer::paintEvent, this);
ams_mapping_item_container = create_scaled_bitmap("ams_mapping_container", this, 78);
SetMinSize(wxSize(FromDIP(230), FromDIP(78)));
SetMaxSize(wxSize(FromDIP(230), FromDIP(78)));
}
MappingContainer::~MappingContainer()
{
}
void MappingContainer::paintEvent(wxPaintEvent& evt)
{
wxPaintDC dc(this);
render(dc);
}
void MappingContainer::render(wxDC& dc)
{
#ifdef __WXMSW__
wxSize size = GetSize();
wxMemoryDC memdc;
wxBitmap bmp(size.x, size.y);
memdc.SelectObject(bmp);
memdc.Blit({ 0, 0 }, size, &dc, { 0, 0 });
{
wxGCDC dc2(memdc);
doRender(dc2);
}
memdc.SelectObject(wxNullBitmap);
dc.DrawBitmap(bmp, 0, 0);
#else
doRender(dc);
#endif
}
void MappingContainer::doRender(wxDC& dc)
{
dc.DrawBitmap(ams_mapping_item_container, 0, 0);
}
}} // namespace Slic3r::GUI

View file

@ -34,6 +34,7 @@
#include "Widgets/CheckBox.hpp"
#include "Widgets/ComboBox.hpp"
#include "Widgets/ScrolledWindow.hpp"
#include "Widgets/PopupWindow.hpp"
#include <wx/simplebook.h>
#include <wx/hashmap.h>
@ -41,6 +42,7 @@ namespace Slic3r { namespace GUI {
#define MATERIAL_ITEM_SIZE wxSize(FromDIP(64), FromDIP(34))
#define MATERIAL_ITEM_REAL_SIZE wxSize(FromDIP(62), FromDIP(32))
#define MAPPING_ITEM_REAL_SIZE wxSize(FromDIP(48), FromDIP(45))
#define AMS_TOTAL_COUNT 4
enum TrayType {
@ -95,8 +97,11 @@ public:
MappingItem(wxWindow *parent);
~MappingItem();
void update_data(TrayData data);
void send_event(int fliament_id);
void update_data(TrayData data);
void send_event(int fliament_id);
void set_tray_index(wxString t_index) {m_tray_index = t_index;};
wxString m_tray_index;
wxColour m_coloul;
wxString m_name;
TrayData m_tray_data;
@ -109,7 +114,18 @@ public:
void doRender(wxDC &dc);
};
class AmsMapingPopup : public wxPopupTransientWindow
class MappingContainer : public wxPanel
{
public:
wxBitmap ams_mapping_item_container;
MappingContainer(wxWindow* parent);
~MappingContainer();
void paintEvent(wxPaintEvent& evt);
void render(wxDC& dc);
void doRender(wxDC& dc);
};
class AmsMapingPopup : public PopupWindow
{
public:
AmsMapingPopup(wxWindow *parent);
@ -118,7 +134,8 @@ public:
wxStaticText * m_warning_text{nullptr};
std::vector<std::string> m_materials_list;
std::vector<wxBoxSizer*> m_amsmapping_sizer_list;
std::vector<wxBoxSizer*> m_amsmapping_container_sizer_list;
std::vector<wxWindow*> m_amsmapping_container_list;
std::vector<MappingItem*> m_mapping_item_list;
bool m_has_unmatch_filament {false};
@ -130,7 +147,7 @@ public:
void update_materials_list(std::vector<std::string> list);
void set_tag_texture(std::string texture);
void update_ams_data(std::map<std::string, Ams *> amsList);
void add_ams_mapping(std::vector<TrayData> tray_data);
void add_ams_mapping(std::vector<TrayData> tray_data, wxWindow* container, wxBoxSizer* sizer);
void set_current_filament_id(int id){m_current_filament_id = id;};
int get_current_filament_id(){return m_current_filament_id;};
bool is_match_material(std::string material);
@ -141,7 +158,7 @@ public:
std::vector<TrayData> parse_ams_mapping(std::map<std::string, Ams*> amsList);
};
class AmsMapingTipPopup : public wxPopupTransientWindow
class AmsMapingTipPopup : public PopupWindow
{
public:
AmsMapingTipPopup(wxWindow *parent);
@ -161,7 +178,7 @@ public:
wxStaticText * m_tip_disable_ams;
};
class AmsHumidityTipPopup : public wxPopupTransientWindow
class AmsHumidityTipPopup : public PopupWindow
{
public:
AmsHumidityTipPopup(wxWindow* parent);
@ -181,7 +198,7 @@ public:
Button* m_button_confirm;
};
class AmsTutorialPopup : public wxPopupTransientWindow
class AmsTutorialPopup : public PopupWindow
{
public:
Label* text_title;
@ -203,7 +220,7 @@ public:
};
class AmsIntroducePopup : public wxPopupTransientWindow
class AmsIntroducePopup : public PopupWindow
{
public:
bool is_enable_ams = {false};

View file

@ -492,6 +492,7 @@ void AuFile::on_set_cover()
thumbnail_img.SaveFile(encode_path(middle_img_path.c_str()));
}
wxGetApp().plater()->set_plater_dirty(true);
auto evt = wxCommandEvent(EVT_AUXILIARY_UPDATE_COVER);
evt.SetString(s_default_folders[m_type]);
evt.SetEventObject(m_parent);
@ -1099,26 +1100,26 @@ void AuxiliaryPanel::update_all_cover()
wxBoxSizer *m_sizer_body = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *m_sizer_designer = new wxBoxSizer(wxHORIZONTAL);
auto m_text_designer = new wxStaticText(this, wxID_ANY, _L("Author"), wxDefaultPosition, wxSize(120, -1), 0);
auto m_text_designer = new wxStaticText(this, wxID_ANY, _L("Author"), wxDefaultPosition, wxSize(180, -1), 0);
m_text_designer->Wrap(-1);
m_text_designer->SetForegroundColour(*wxBLACK);
m_sizer_designer->Add(m_text_designer, 0, wxALIGN_CENTER, 0);
m_input_designer = new ::TextInput(this, wxEmptyString, wxEmptyString, wxEmptyString, wxDefaultPosition, wxSize(FromDIP(450), FromDIP(30)), wxTE_PROCESS_ENTER);
m_input_designer->GetTextCtrl()->SetFont(::Label::Body_14);
m_input_designer->GetTextCtrl()->SetSize(wxSize(FromDIP(450), FromDIP(22)));
m_input_designer->GetTextCtrl()->SetSize(wxSize(FromDIP(450), -1));
m_sizer_designer->Add(m_input_designer, 0, wxALIGN_CENTER, 0);
wxBoxSizer *m_sizer_model_name = new wxBoxSizer(wxHORIZONTAL);
auto m_text_model_name = new wxStaticText(this, wxID_ANY, _L("Model Name"), wxDefaultPosition, wxSize(120, -1), 0);
auto m_text_model_name = new wxStaticText(this, wxID_ANY, _L("Model Name"), wxDefaultPosition, wxSize(180, -1), 0);
m_text_model_name->SetForegroundColour(*wxBLACK);
m_text_model_name->Wrap(-1);
m_sizer_model_name->Add(m_text_model_name, 0, wxALIGN_CENTER, 0);
m_imput_model_name = new ::TextInput(this, wxEmptyString, wxEmptyString, wxEmptyString, wxDefaultPosition,wxSize(FromDIP(450),FromDIP(30)), wxTE_PROCESS_ENTER);
m_imput_model_name->GetTextCtrl()->SetFont(::Label::Body_14);
m_imput_model_name->GetTextCtrl()->SetSize(wxSize(FromDIP(450), FromDIP(22)));
m_imput_model_name->GetTextCtrl()->SetSize(wxSize(FromDIP(450), -1));
m_sizer_model_name->Add(m_imput_model_name, 0, wxALIGN_CENTER, 0);
/*
@ -1224,8 +1225,8 @@ void DesignerPanel::update_info()
void DesignerPanel::msw_rescale()
{
m_input_designer->GetTextCtrl()->SetSize(wxSize(FromDIP(450), FromDIP(22)));
m_imput_model_name->GetTextCtrl()->SetSize(wxSize(FromDIP(450), FromDIP(22)));
m_input_designer->GetTextCtrl()->SetSize(wxSize(FromDIP(450), -1));
m_imput_model_name->GetTextCtrl()->SetSize(wxSize(FromDIP(450), -1));
}
}} // namespace Slic3r::GUI

View file

@ -119,7 +119,8 @@ void BBLStatusBarSend::set_range(int val)
void BBLStatusBarSend::clear_percent()
{
set_percent_text(wxEmptyString);
//set_percent_text(wxEmptyString);
m_cancelbutton->Hide();
}
void BBLStatusBarSend::show_progress(bool show)
@ -235,6 +236,7 @@ void BBLStatusBarSend::set_status_text(const wxString& txt)
wxString str;
format_text(m_status_text, m_self->FromDIP(280), txt, str);
m_status_text->SetLabelText(str);
m_self->Layout();
//if (is_english_text(str)) m_status_text->Wrap(m_self->FromDIP(280));
}
@ -279,6 +281,7 @@ bool BBLStatusBarSend::update_status(wxString &msg, bool &was_cancel, int percen
void BBLStatusBarSend::reset()
{
m_cancelbutton->Show();
set_status_text("");
m_was_cancelled = false;
set_progress(0);

View file

@ -263,7 +263,7 @@ void BBLTopbar::Init(wxFrame* parent)
m_publish_disable_bitmap = create_scaled_bitmap("topbar_publish_disable", nullptr, TOPBAR_ICON_SIZE);
m_publish_item->SetDisabledBitmap(m_publish_disable_bitmap);
this->EnableTool(m_publish_item->GetId(), false);
this->AddSpacer(FromDIP(12));
this->AddSpacer(FromDIP(4));
/*wxBitmap model_store_bitmap = create_scaled_bitmap("topbar_store", nullptr, TOPBAR_ICON_SIZE);
m_model_store_item = this->AddTool(ID_MODEL_STORE, "", model_store_bitmap);
@ -271,12 +271,12 @@ void BBLTopbar::Init(wxFrame* parent)
*/
//this->AddSeparator();
this->AddSpacer(FromDIP(6));
this->AddSpacer(FromDIP(4));
wxBitmap iconize_bitmap = create_scaled_bitmap("topbar_min", nullptr, TOPBAR_ICON_SIZE);
wxAuiToolBarItem* iconize_btn = this->AddTool(wxID_ICONIZE_FRAME, "", iconize_bitmap);
this->AddSpacer(FromDIP(6));
this->AddSpacer(FromDIP(4));
maximize_bitmap = create_scaled_bitmap("topbar_max", nullptr, TOPBAR_ICON_SIZE);
window_bitmap = create_scaled_bitmap("topbar_win", nullptr, TOPBAR_ICON_SIZE);
@ -287,7 +287,7 @@ void BBLTopbar::Init(wxFrame* parent)
maximize_btn = this->AddTool(wxID_MAXIMIZE_FRAME, "", maximize_bitmap);
}
this->AddSpacer(FromDIP(6));
this->AddSpacer(FromDIP(4));
wxBitmap close_bitmap = create_scaled_bitmap("topbar_close", nullptr, TOPBAR_ICON_SIZE);
wxAuiToolBarItem* close_btn = this->AddTool(wxID_CLOSE_FRAME, "", close_bitmap);

View file

@ -185,14 +185,9 @@ std::string BackgroundSlicingProcess::output_filepath_for_project(const boost::f
// from the G-code generator.
void BackgroundSlicingProcess::process_fff()
{
assert(m_print == m_fff_print);
PresetBundle &preset_bundle = *wxGetApp().preset_bundle;
m_fff_print->is_BBL_printer() =
preset_bundle.printers.get_edited_preset().is_bbl_vendor_preset(
&preset_bundle);
assert(m_print == m_fff_print);
PresetBundle &preset_bundle = *wxGetApp().preset_bundle;
m_fff_print->is_BBL_printer() = preset_bundle.printers.get_edited_preset().is_bbl_vendor_preset(&preset_bundle);
//BBS: add the logic to process from an existed gcode file
if (m_print->finished()) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%: skip slicing, to process previous gcode file")%__LINE__;

View file

@ -16,6 +16,7 @@
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/Utils/Bonjour.hpp"
#include "Widgets/Button.hpp"
namespace Slic3r {
@ -60,6 +61,8 @@ BonjourDialog::BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology tech)
, timer_state(0)
, tech(tech)
{
SetBackgroundColour(*wxWHITE);
const int em = GUI::wxGetApp().em_unit();
list->SetMinSize(wxSize(80 * em, 30 * em));
@ -78,10 +81,39 @@ BonjourDialog::BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology tech)
vsizer->Add(list, 1, wxEXPAND | wxALL, em);
wxBoxSizer *button_sizer = new wxBoxSizer(wxHORIZONTAL);
button_sizer->Add(new wxButton(this, wxID_OK, "OK"), 0, wxALL, em);
button_sizer->Add(new wxButton(this, wxID_CANCEL, "Cancel"), 0, wxALL, em);
// ^ Note: The Ok/Cancel labels are translated by wxWidgets
auto button_sizer = new wxBoxSizer(wxHORIZONTAL);
StateColor btn_bg_green(std::pair<wxColour, int>(wxColour(27, 136, 68), StateColor::Pressed), std::pair<wxColour, int>(wxColour(61, 203, 115), StateColor::Hovered),
std::pair<wxColour, int>(wxColour(0, 174, 66), StateColor::Normal));
StateColor btn_bg_white(std::pair<wxColour, int>(wxColour(206, 206, 206), StateColor::Pressed), std::pair<wxColour, int>(wxColour(238, 238, 238), StateColor::Hovered),
std::pair<wxColour, int>(*wxWHITE, StateColor::Normal));
auto m_button_ok = new Button(this, _L("OK"));
m_button_ok->SetBackgroundColor(btn_bg_green);
m_button_ok->SetBorderColor(*wxWHITE);
m_button_ok->SetTextColor(*wxWHITE);
m_button_ok->SetFont(Label::Body_12);
m_button_ok->SetSize(wxSize(FromDIP(58), FromDIP(24)));
m_button_ok->SetMinSize(wxSize(FromDIP(58), FromDIP(24)));
m_button_ok->SetCornerRadius(FromDIP(12));
m_button_ok->Bind(wxEVT_LEFT_DOWN, [this](auto &e) { this->EndModal(wxID_OK); });
auto m_button_cancel = new Button(this, _L("Cancel"));
m_button_cancel->SetBackgroundColor(btn_bg_white);
m_button_cancel->SetBorderColor(wxColour(38, 46, 48));
m_button_cancel->SetFont(Label::Body_12);
m_button_cancel->SetSize(wxSize(FromDIP(58), FromDIP(24)));
m_button_cancel->SetMinSize(wxSize(FromDIP(58), FromDIP(24)));
m_button_cancel->SetCornerRadius(FromDIP(12));
m_button_cancel->Bind(wxEVT_LEFT_DOWN, [this](auto &e) { this->EndModal(wxID_CANCEL); });
button_sizer->AddStretchSpacer();
button_sizer->Add(m_button_ok, 0, wxALL, FromDIP(5));
button_sizer->Add(m_button_cancel, 0, wxALL, FromDIP(5));
vsizer->Add(button_sizer, 0, wxALIGN_CENTER);
SetSizerAndFit(vsizer);

View file

@ -33,7 +33,7 @@ CalibrationDialog::CalibrationDialog(Plater *plater)
auto body_panel = new wxPanel(this, wxID_ANY);
body_panel->SetBackgroundColour(*wxWHITE);
auto cali_left_panel = new StaticBox(body_panel, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(303), FromDIP(243)));
auto cali_left_panel = new StaticBox(body_panel, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(303), -1));
cali_left_panel->SetBackgroundColor(BG_COLOR);
cali_left_panel->SetBorderColor(BG_COLOR);
@ -73,9 +73,7 @@ CalibrationDialog::CalibrationDialog(Plater *plater)
cali_left_sizer->Add(0, 0, 0, wxTOP, FromDIP(5));
auto cali_left_text_body =
new wxStaticText(cali_left_panel, wxID_ANY,
_L("The calibration program detects the status of your device automatically to minimize deviation.\nIt keeps the device performing optimally."),
wxDefaultPosition, wxSize(FromDIP(260), -1), 0);
new Label(cali_left_panel, _L("The calibration program detects the status of your device automatically to minimize deviation.\nIt keeps the device performing optimally."));
cali_left_text_body->Wrap(FromDIP(260));
cali_left_text_body->SetForegroundColour(wxColour(0x6B, 0x6B, 0x6B));
cali_left_text_body->SetBackgroundColour(BG_COLOR);
@ -174,7 +172,6 @@ CalibrationDialog::CalibrationDialog(Plater *plater)
Fit();
m_calibration_btn->Bind(wxEVT_LEFT_DOWN, &CalibrationDialog::on_start_calibration, this);
wxGetApp().UpdateDlgDarkUI(this);
}
CalibrationDialog::~CalibrationDialog() {}
@ -296,7 +293,10 @@ void CalibrationDialog::update_machine_obj(MachineObject *obj) { m_obj = obj; }
bool CalibrationDialog::Show(bool show)
{
if (show) { CentreOnParent(); }
if (show) {
wxGetApp().UpdateDlgDarkUI(this);
CentreOnParent();
}
return DPIDialog::Show(show);
}

View file

@ -12,9 +12,9 @@
namespace Slic3r {
namespace GUI {
wxIMPLEMENT_CLASS(CameraPopup, wxPopupTransientWindow);
wxIMPLEMENT_CLASS(CameraPopup, PopupWindow);
wxBEGIN_EVENT_TABLE(CameraPopup, wxPopupTransientWindow)
wxBEGIN_EVENT_TABLE(CameraPopup, PopupWindow)
EVT_MOUSE_EVENTS(CameraPopup::OnMouse )
EVT_SIZE(CameraPopup::OnSize)
EVT_SET_FOCUS(CameraPopup::OnSetFocus )
@ -24,10 +24,12 @@ wxEND_EVENT_TABLE()
wxDEFINE_EVENT(EVT_VCAMERA_SWITCH, wxMouseEvent);
wxDEFINE_EVENT(EVT_SDCARD_ABSENT_HINT, wxCommandEvent);
#define CAMERAPOPUP_CLICK_INTERVAL 20
const wxColour TEXT_COL = wxColour(43, 52, 54);
CameraPopup::CameraPopup(wxWindow *parent, MachineObject* obj)
: wxPopupTransientWindow(parent, wxBORDER_NONE | wxPU_CONTAINS_CONTROLS),
: PopupWindow(parent, wxBORDER_NONE | wxPU_CONTAINS_CONTROLS),
m_obj(obj)
{
#ifdef __WINDOWS__
@ -84,10 +86,26 @@ CameraPopup::CameraPopup(wxWindow *parent, MachineObject* obj)
main_sizer->Add(top_sizer, 0, wxALL, FromDIP(10));
auto url = wxString::Format(L"https://wiki.bambulab.com/%s/software/bambu-studio/virtual-camera", L"en");
vcamera_guide_link = new wxHyperlinkCtrl(m_panel, wxID_ANY, _L("Show \"Live Video\" guide page."),
url, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE);
auto text = _L("Show \"Live Video\" guide page.");
wxBoxSizer* link_sizer = new wxBoxSizer(wxVERTICAL);
vcamera_guide_link = new Label(m_panel, text);
vcamera_guide_link->Wrap(-1);
vcamera_guide_link->SetForegroundColour(wxColour(0x1F, 0x8E, 0xEA));
auto text_size = vcamera_guide_link->GetTextExtent(text);
vcamera_guide_link->Bind(wxEVT_LEFT_DOWN, [this, url](wxMouseEvent& e) {wxLaunchDefaultBrowser(url); });
link_underline = new wxPanel(m_panel, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL);
link_underline->SetBackgroundColour(wxColour(0x1F, 0x8E, 0xEA));
link_underline->SetSize(wxSize(text_size.x, 1));
link_underline->SetMinSize(wxSize(text_size.x, 1));
vcamera_guide_link->Hide();
main_sizer->Add(vcamera_guide_link, 0, wxALL, FromDIP(15));
link_underline->Hide();
link_sizer->Add(vcamera_guide_link, 0, wxALL, 0);
link_sizer->Add(link_underline, 0, wxALL, 0);
main_sizer->Add(link_sizer, 0, wxALL, FromDIP(15));
m_panel->SetSizer(main_sizer);
m_panel->Layout();
@ -105,6 +123,10 @@ CameraPopup::CameraPopup(wxWindow *parent, MachineObject* obj)
m_panel->Bind(wxEVT_LEFT_UP, &CameraPopup::OnLeftUp, this);
#endif //APPLE
this->Bind(wxEVT_TIMER, &CameraPopup::stop_interval, this);
m_interval_timer = new wxTimer();
m_interval_timer->SetOwner(this);
check_func_supported();
wxGetApp().UpdateDarkUIWin(this);
}
@ -141,7 +163,9 @@ void CameraPopup::Popup(wxWindow *WXUNUSED(focus))
wxSize win_size = this->GetSize();
curr_position.x -= win_size.x;
this->SetPosition(curr_position);
wxPopupTransientWindow::Popup();
if (!m_is_in_interval)
PopupWindow::Popup();
}
wxWindow* CameraPopup::create_item_radiobox(wxString title, wxWindow* parent, wxString tooltip, int padding_left)
@ -225,10 +249,12 @@ void CameraPopup::sync_vcamera_state(bool show_vcamera)
if (is_vcamera_show) {
m_switch_vcamera->SetValue(true);
vcamera_guide_link->Show();
link_underline->Show();
}
else {
m_switch_vcamera->SetValue(false);
vcamera_guide_link->Hide();
link_underline->Hide();
}
rescale();
@ -248,12 +274,15 @@ void CameraPopup::check_func_supported()
if (m_obj->is_function_supported(PrinterFunction::FUNC_VIRTUAL_CAMERA) && m_obj->has_ipcam) {
m_text_vcamera->Show();
m_switch_vcamera->Show();
if (is_vcamera_show)
if (is_vcamera_show) {
vcamera_guide_link->Show();
link_underline->Show();
}
} else {
m_text_vcamera->Hide();
m_switch_vcamera->Hide();
vcamera_guide_link->Hide();
link_underline->Hide();
}
allow_alter_resolution = (m_obj->is_function_supported(PrinterFunction::FUNC_ALTER_RESOLUTION) && m_obj->has_ipcam);
@ -318,7 +347,7 @@ void CameraPopup::rescale()
m_panel->Layout();
main_sizer->Fit(m_panel);
SetClientSize(m_panel->GetSize());
wxPopupTransientWindow::Update();
PopupWindow::Update();
}
void CameraPopup::OnLeftUp(wxMouseEvent &event)
@ -367,18 +396,31 @@ void CameraPopup::OnLeftUp(wxMouseEvent &event)
}
}
void CameraPopup::start_interval()
{
m_interval_timer->Start(CAMERAPOPUP_CLICK_INTERVAL);
m_is_in_interval = true;
}
void CameraPopup::stop_interval(wxTimerEvent& event)
{
m_is_in_interval = false;
m_interval_timer->Stop();
}
void CameraPopup::OnDismiss() {
wxPopupTransientWindow::OnDismiss();
PopupWindow::OnDismiss();
this->start_interval();
}
bool CameraPopup::ProcessLeftDown(wxMouseEvent &event)
{
return wxPopupTransientWindow::ProcessLeftDown(event);
return PopupWindow::ProcessLeftDown(event);
}
bool CameraPopup::Show(bool show)
{
return wxPopupTransientWindow::Show(show);
return PopupWindow::Show(show);
}
void CameraPopup::OnSize(wxSizeEvent &event)

View file

@ -13,6 +13,7 @@
#include <wx/hyperlink.h>
#include "Widgets/SwitchButton.hpp"
#include "Widgets/RadioBox.hpp"
#include "Widgets/PopupWindow.hpp"
namespace Slic3r {
namespace GUI {
@ -20,13 +21,13 @@ namespace GUI {
wxDECLARE_EVENT(EVT_VCAMERA_SWITCH, wxMouseEvent);
wxDECLARE_EVENT(EVT_SDCARD_ABSENT_HINT, wxCommandEvent);
class CameraPopup : public wxPopupTransientWindow
class CameraPopup : public PopupWindow
{
public:
CameraPopup(wxWindow *parent, MachineObject* obj = nullptr);
virtual ~CameraPopup() {}
// wxPopupTransientWindow virtual methods are all overridden to log them
// PopupWindow virtual methods are all overridden to log them
virtual void Popup(wxWindow *focus = NULL) wxOVERRIDE;
virtual void OnDismiss() wxOVERRIDE;
virtual bool ProcessLeftDown(wxMouseEvent &event) wxOVERRIDE;
@ -59,6 +60,8 @@ protected:
private:
MachineObject* m_obj { nullptr };
wxTimer* m_interval_timer{nullptr};
bool m_is_in_interval{ false };
wxStaticText* m_text_recording;
SwitchButton* m_switch_recording;
wxStaticText* m_text_vcamera;
@ -70,10 +73,13 @@ private:
std::vector<RadioBox*> resolution_rbtns;
std::vector<wxStaticText*> resolution_texts;
CameraResolution curr_sel_resolution = RESOLUTION_1080P;
wxHyperlinkCtrl* vcamera_guide_link { nullptr };
Label* vcamera_guide_link { nullptr };
wxPanel* link_underline{ nullptr };
bool is_vcamera_show = false;
bool allow_alter_resolution = false;
void start_interval();
void stop_interval(wxTimerEvent& event);
void OnMouse(wxMouseEvent &event);
void OnSize(wxSizeEvent &event);
void OnSetFocus(wxFocusEvent &event);

View file

@ -430,6 +430,19 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
m_support_material_overhangs_queried = false;
}
if (config->opt_bool("enable_support")) {
auto support_type = config->opt_enum<SupportType>("support_type");
auto support_style = config->opt_enum<SupportMaterialStyle>("support_style");
std::set<int> enum_set_normal = {0, 1, 2};
std::set<int> enum_set_tree = {0, 3, 4, 5};
auto & set = is_tree(support_type) ? enum_set_tree : enum_set_normal;
if (set.find(support_style) == set.end()) {
DynamicPrintConfig new_conf = *config;
new_conf.set_key_value("support_style", new ConfigOptionEnum<SupportMaterialStyle>(smsDefault));
apply(config, &new_conf);
}
}
if (config->option<ConfigOptionPercent>("sparse_infill_density")->value == 100) {
std::string sparse_infill_pattern = config->option<ConfigOptionEnum<InfillPattern>>("sparse_infill_pattern")->serialize();
const auto &top_fill_pattern_values = config->def()->get("top_surface_pattern")->enum_values;
@ -480,6 +493,17 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
}
}
}
if (config->opt_enum<PrintSequence>("print_sequence") == PrintSequence::ByObject && config->opt_int("skirt_height") > 1 && config->opt_int("skirt_loops") > 0) {
const wxString msg_text = _(L("While printing by Object, the extruder may collide skirt.\nThus, reset the skirt layer to 1 to avoid that."));
MessageDialog dialog(m_msg_dlg_parent, msg_text, "", wxICON_WARNING | wxOK);
DynamicPrintConfig new_conf = *config;
is_msg_dlg_already_exist = true;
dialog.ShowModal();
new_conf.set_key_value("skirt_height", new ConfigOptionInt(1));
apply(config, &new_conf);
is_msg_dlg_already_exist = false;
}
}
void ConfigManipulation::apply_null_fff_config(DynamicPrintConfig *config, std::vector<std::string> const &keys, std::map<ObjectBase *, ModelConfig *> const &configs)

View file

@ -148,7 +148,7 @@ void ConnectPrinterDialog::on_button_confirm(wxCommandEvent &event)
}
}
if (m_obj) {
m_obj->set_access_code(code.ToStdString());
m_obj->set_user_access_code(code.ToStdString());
wxGetApp().getDeviceManager()->set_selected_machine(m_obj->dev_id);
}
EndModal(wxID_OK);

View file

@ -136,6 +136,32 @@ wxColour AmsTray::get_color()
return AmsTray::decode_color(color);
}
void AmsTray::reset()
{
tag_uid = "";
setting_id = "";
filament_setting_id = "";
type = "";
sub_brands = "";
color = "";
weight = "";
diameter = "";
temp = "";
time = "";
bed_temp_type = "";
bed_temp = "";
nozzle_temp_max = "";
nozzle_temp_min = "";
xcam_info = "";
uuid = "";
k = 0.0f;
n = 0.0f;
is_bbl = false;
hold_count = 0;
remain = 0;
}
bool AmsTray::is_tray_info_ready()
{
if (color.empty())
@ -302,6 +328,11 @@ std::string MachineObject::get_printer_thumbnail_img_str()
return "printer_thumbnail";
}
std::string MachineObject::get_ftp_folder()
{
return DeviceManager::get_ftp_folder(printer_type);
}
void MachineObject::set_access_code(std::string code)
{
this->access_code = code;
@ -518,6 +549,10 @@ Ams *MachineObject::get_curr_Ams()
AmsTray *MachineObject::get_curr_tray()
{
if (m_tray_now.compare(std::to_string(VIRTUAL_TRAY_ID)) == 0) {
return &vt_tray;
}
Ams* curr_ams = get_curr_Ams();
if (!curr_ams) return nullptr;
@ -639,17 +674,6 @@ bool MachineObject::is_support_ams_mapping_version(std::string module, std::stri
return result;
}
bool MachineObject::is_only_support_cloud_print()
{
auto ap_ver_it = module_vers.find("rv1126");
if (ap_ver_it != module_vers.end()) {
if (ap_ver_it->second.sw_ver > "00.00.12.61") {
return false;
}
}
return true;
}
static float calc_color_distance(wxColour c1, wxColour c2)
{
float lab[2][3];
@ -1229,7 +1253,7 @@ void MachineObject::parse_state_changed_event()
{
// parse calibration done
if (last_mc_print_stage != mc_print_stage) {
if (mc_print_stage == 1 && boost::contains(m_gcode_file, "auto_cali_for_user.gcode")) {
if (mc_print_stage == 1 && boost::contains(m_gcode_file, "auto_cali_for_user")) {
calibration_done = true;
} else {
calibration_done = false;
@ -1322,6 +1346,7 @@ bool MachineObject::is_recording()
void MachineObject::parse_version_func()
{
auto ota_version = module_vers.find("ota");
auto esp32_version = module_vers.find("esp32");
if (printer_type == "BL-P001" ||
printer_type == "BL-P002") {
if (ota_version != module_vers.end()) {
@ -1348,9 +1373,13 @@ void MachineObject::parse_version_func()
}
} else if (printer_type == "C11") {
local_use_ssl = true;
is_cloud_print_only = true;
if (ota_version != module_vers.end()) {
is_support_send_to_sdcard = ota_version->second.sw_ver.compare("01.02.00.00") >= 0;
}
if (esp32_version != module_vers.end()) {
ams_support_auto_switch_filament_flag = esp32_version->second.sw_ver.compare("00.03.11.50") >= 0;
}
}
}
@ -1533,20 +1562,10 @@ int MachineObject::command_ams_switch(int tray_index, int old_temp, int new_temp
std::string gcode = "";
if (tray_index == 255) {
// unload gcode
gcode = "M620 S255\nM104 S250\nG28 X\nG91\nG1 Z3.0 F1200\nG90\n"
"G1 X70 F12000\nG1 Y245\nG1 Y265 F3000\nM109 S250\nG1 X120 F12000\n"
"G1 X20 Y50 F12000\nG1 Y-3\nT255\nM104 S0\nG1 X165 F5000\nG1 Y245\n"
"G91\nG1 Z-3.0 F1200\nG90\nM621 S255\n";
gcode = DeviceManager::load_gcode(printer_type, "ams_unload.gcode");
} else {
// load gcode
gcode = "M620 S[next_extruder]\nM104 S250\nG28 X\nG91\n\nG1 Z3.0 F1200\nG90\n"
"G1 X70 F12000\nG1 Y245\nG1 Y265 F3000\nM109 S250\nG1 X120 F12000\nG1 X20 Y50 F12000\nG1 Y-3"
"\nT[next_extruder]\nG1 X54 F12000\nG1 Y265\nM400\nM106 P1 S0\nG92 E0\nG1 E40 F200\nM400"
"\nM109 S[new_filament_temp]\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM400\nM106 P1 S0\nG1 X70 F9000"
"\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000\nG1 X70 F6000\nG1 X100 F5000\nG1 X70 F15000"
"\nG1 X100 F5000\nG1 X70 F15000\nG1 X165 F5000\nG1 Y245\nG91\nG1 Z-3.0 F1200\nG90\nM621 S[next_extruder]\n";
// include VIRTUAL_TRAY_ID
gcode = DeviceManager::load_gcode(printer_type, "ams_load.gcode");
boost::replace_all(gcode, "[next_extruder]", std::to_string(tray_index));
boost::replace_all(gcode, "[new_filament_temp]", std::to_string(new_temp));
}
@ -1649,7 +1668,7 @@ int MachineObject::command_ams_select_tray(std::string tray_id)
int MachineObject::command_ams_control(std::string action)
{
//valid actions
if (action == "resume" || action == "reset" || action == "pause") {
if (action == "resume" || action == "reset" || action == "pause" || action == "done") {
json j;
j["print"]["command"] = "ams_control";
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
@ -1840,6 +1859,22 @@ int MachineObject::command_unload_filament()
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
return this->publish_json(j.dump());
}
else if (printer_type == "C11") {
std::string gcode = DeviceManager::load_gcode(printer_type, "ams_unload.gcode");
if (gcode.empty()) {
return -1;
}
json j;
j["print"]["command"] = "gcode_line";
j["print"]["param"] = gcode;
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
if (m_agent)
j["print"]["user_id"] = m_agent->get_user_id();
return this->publish_json(j.dump());
}
else {
json j;
j["print"]["command"] = "unload_filament";
@ -2040,6 +2075,9 @@ void MachineObject::reset()
last_mc_print_stage = -1;
m_new_ver_list_exist = false;
extruder_axis_status = LOAD;
nozzle_diameter = 0.0f;
vt_tray.reset();
subtask_ = nullptr;
@ -2056,7 +2094,7 @@ int MachineObject::connect(bool is_anonymous)
std::string password;
if (!is_anonymous) {
username = "bblp";
password = access_code;
password = get_access_code();
}
if (m_agent) {
try {
@ -2624,6 +2662,19 @@ int MachineObject::parse_json(std::string payload)
}
#pragma endregion
try {
if (jj.contains("nozzle_diameter")) {
if (jj["nozzle_diameter"].is_number_float()) {
nozzle_diameter = jj["nozzle_diameter"].get<float>();
} else if (jj["nozzle_diameter"].is_string()) {
nozzle_diameter = stof(jj["nozzle_diameter"].get<std::string>().c_str());
}
}
}
catch(...) {
;
}
#pragma region upgrade
try {
if (jj.contains("upgrade_state")) {
@ -2656,29 +2707,36 @@ int MachineObject::parse_json(std::string payload)
this->command_get_version();
});
}
upgrade_display_state = jj["upgrade_state"]["dis_state"].get<int>();
if (upgrade_display_hold_count > 0)
upgrade_display_hold_count--;
else
upgrade_display_state = jj["upgrade_state"]["dis_state"].get<int>();
} else {
//BBS compatibility with old version
if (upgrade_status == "DOWNLOADING"
|| upgrade_status == "FLASHING"
|| upgrade_status == "UPGRADE_REQUEST"
|| upgrade_status == "PRE_FLASH_START"
|| upgrade_status == "PRE_FLASH_SUCCESS") {
upgrade_display_state = (int)UpgradingDisplayState::UpgradingInProgress;
}
else if (upgrade_status == "UPGRADE_SUCCESS"
|| upgrade_status == "DOWNLOAD_FAIL"
|| upgrade_status == "FLASH_FAIL"
|| upgrade_status == "PRE_FLASH_FAIL"
|| upgrade_status == "UPGRADE_FAIL") {
upgrade_display_state = (int)UpgradingDisplayState::UpgradingFinished;
}
if (upgrade_display_hold_count > 0)
upgrade_display_hold_count--;
else {
if (upgrade_new_version) {
upgrade_display_state = (int)UpgradingDisplayState::UpgradingAvaliable;
//BBS compatibility with old version
if (upgrade_status == "DOWNLOADING"
|| upgrade_status == "FLASHING"
|| upgrade_status == "UPGRADE_REQUEST"
|| upgrade_status == "PRE_FLASH_START"
|| upgrade_status == "PRE_FLASH_SUCCESS") {
upgrade_display_state = (int)UpgradingDisplayState::UpgradingInProgress;
}
else if (upgrade_status == "UPGRADE_SUCCESS"
|| upgrade_status == "DOWNLOAD_FAIL"
|| upgrade_status == "FLASH_FAIL"
|| upgrade_status == "PRE_FLASH_FAIL"
|| upgrade_status == "UPGRADE_FAIL") {
upgrade_display_state = (int)UpgradingDisplayState::UpgradingFinished;
}
else {
upgrade_display_state = (int)UpgradingDisplayState::UpgradingUnavaliable;
if (upgrade_new_version) {
upgrade_display_state = (int)UpgradingDisplayState::UpgradingAvaliable;
}
else {
upgrade_display_state = (int)UpgradingDisplayState::UpgradingUnavaliable;
}
}
}
}
@ -3130,6 +3188,89 @@ int MachineObject::parse_json(std::string payload)
vt_tray.n = jj["vt_tray"]["n"].get<float>();
}
ams_support_virtual_tray = true;
if (vt_tray.hold_count > 0) {
vt_tray.hold_count--;
} else {
if (jj["vt_tray"].contains("tag_uid"))
vt_tray.tag_uid = jj["vt_tray"]["tag_uid"].get<std::string>();
else
vt_tray.tag_uid = "0";
if (jj["vt_tray"].contains("tray_info_idx") && jj["vt_tray"].contains("tray_type")) {
vt_tray.setting_id = jj["vt_tray"]["tray_info_idx"].get<std::string>();
std::string type = jj["vt_tray"]["tray_type"].get<std::string>();
if (vt_tray.setting_id == "GFS00") {
vt_tray.type = "PLA-S";
}
else if (vt_tray.setting_id == "GFS01") {
vt_tray.type = "PA-S";
}
else {
vt_tray.type = type;
}
}
else {
vt_tray.setting_id = "";
vt_tray.type = "";
}
if (jj["vt_tray"].contains("tray_sub_brands"))
vt_tray.sub_brands = jj["vt_tray"]["tray_sub_brands"].get<std::string>();
else
vt_tray.sub_brands = "";
if (jj["vt_tray"].contains("tray_weight"))
vt_tray.weight = jj["vt_tray"]["tray_weight"].get<std::string>();
else
vt_tray.weight = "";
if (jj["vt_tray"].contains("tray_diameter"))
vt_tray.diameter = jj["vt_tray"]["tray_diameter"].get<std::string>();
else
vt_tray.diameter = "";
if (jj["vt_tray"].contains("tray_temp"))
vt_tray.temp = jj["vt_tray"]["tray_temp"].get<std::string>();
else
vt_tray.temp = "";
if (jj["vt_tray"].contains("tray_time"))
vt_tray.time = jj["vt_tray"]["tray_time"].get<std::string>();
else
vt_tray.time = "";
if (jj["vt_tray"].contains("bed_temp_type"))
vt_tray.bed_temp_type = jj["vt_tray"]["bed_temp_type"].get<std::string>();
else
vt_tray.bed_temp_type = "";
if (jj["vt_tray"].contains("bed_temp"))
vt_tray.bed_temp = jj["vt_tray"]["bed_temp"].get<std::string>();
else
vt_tray.bed_temp = "";
if (jj["vt_tray"].contains("nozzle_temp_max"))
vt_tray.nozzle_temp_max = jj["vt_tray"]["nozzle_temp_max"].get<std::string>();
else
vt_tray.nozzle_temp_max = "";
if (jj["vt_tray"].contains("nozzle_temp_min"))
vt_tray.nozzle_temp_min = jj["vt_tray"]["nozzle_temp_min"].get<std::string>();
else
vt_tray.nozzle_temp_min = "";
if (jj["vt_tray"].contains("xcam_info"))
vt_tray.xcam_info = jj["vt_tray"]["xcam_info"].get<std::string>();
else
vt_tray.xcam_info = "";
if (jj["vt_tray"].contains("tray_uuid"))
vt_tray.uuid = jj["vt_tray"]["tray_uuid"].get<std::string>();
else
vt_tray.uuid = "0";
if (jj["vt_tray"].contains("tray_color")) {
auto color = jj["vt_tray"]["tray_color"].get<std::string>();
vt_tray.update_color_from_str(color);
}
else {
vt_tray.color = "";
}
if (jj["vt_tray"].contains("remain")) {
vt_tray.remain = jj["vt_tray"]["remain"].get<int>();
}
else {
vt_tray.remain = -1;
}
}
} else {
ams_support_virtual_tray = false;
}
@ -3159,24 +3300,39 @@ int MachineObject::parse_json(std::string payload)
if (jj["ams_id"].is_number()) {
int ams_id = jj["ams_id"].get<int>();
auto ams_it = amsList.find(std::to_string(ams_id));
if (ams_it != amsList.end()) {
int tray_id = jj["tray_id"].get<int>();
auto tray_it = ams_it->second->trayList.find(std::to_string(tray_id));
if (tray_it != ams_it->second->trayList.end()) {
BOOST_LOG_TRIVIAL(trace) << "ams_filament_setting, parse tray info";
tray_it->second->nozzle_temp_max = std::to_string(jj["nozzle_temp_max"].get<int>());
tray_it->second->nozzle_temp_min = std::to_string(jj["nozzle_temp_min"].get<int>());
tray_it->second->type = jj["tray_type"].get<std::string>();
tray_it->second->color = jj["tray_color"].get<std::string>();
tray_it->second->setting_id = jj["tray_info_idx"].get<std::string>();
// delay update
tray_it->second->set_hold_count();
} else {
BOOST_LOG_TRIVIAL(warning) << "ams_filament_setting, can not find in trayList, tray_id=" << tray_id;
}
int tray_id = 0;
if (jj.contains("tray_id")) {
tray_id = jj["tray_id"].get<int>();
}
if (ams_id == 255 && tray_id == VIRTUAL_TRAY_ID) {
BOOST_LOG_TRIVIAL(trace) << "ams_filament_setting, parse tray info";
vt_tray.nozzle_temp_max = std::to_string(jj["nozzle_temp_max"].get<int>());
vt_tray.nozzle_temp_min = std::to_string(jj["nozzle_temp_min"].get<int>());
vt_tray.type = jj["tray_type"].get<std::string>();
vt_tray.color = jj["tray_color"].get<std::string>();
vt_tray.setting_id = jj["tray_info_idx"].get<std::string>();
// delay update
vt_tray.set_hold_count();
} else {
BOOST_LOG_TRIVIAL(warning) << "ams_filament_setting, can not find in amsList, ams_id=" << ams_id;
auto ams_it = amsList.find(std::to_string(ams_id));
if (ams_it != amsList.end()) {
tray_id = jj["tray_id"].get<int>();
auto tray_it = ams_it->second->trayList.find(std::to_string(tray_id));
if (tray_it != ams_it->second->trayList.end()) {
BOOST_LOG_TRIVIAL(trace) << "ams_filament_setting, parse tray info";
tray_it->second->nozzle_temp_max = std::to_string(jj["nozzle_temp_max"].get<int>());
tray_it->second->nozzle_temp_min = std::to_string(jj["nozzle_temp_min"].get<int>());
tray_it->second->type = jj["tray_type"].get<std::string>();
tray_it->second->color = jj["tray_color"].get<std::string>();
tray_it->second->setting_id = jj["tray_info_idx"].get<std::string>();
// delay update
tray_it->second->set_hold_count();
} else {
BOOST_LOG_TRIVIAL(warning) << "ams_filament_setting, can not find in trayList, tray_id=" << tray_id;
}
} else {
BOOST_LOG_TRIVIAL(warning) << "ams_filament_setting, can not find in amsList, ams_id=" << ams_id;
}
}
}
} else if (jj["command"].get<std::string>() == "xcam_control_set") {
@ -3336,6 +3492,21 @@ int MachineObject::parse_json(std::string payload)
}
} catch (...) {}
// upgrade
try {
if (j.contains("upgrade")) {
if (j["upgrade"].contains("command")) {
if (j["upgrade"]["command"].get<std::string>() == "upgrade_confirm") {
this->upgrade_display_state == UpgradingInProgress;
upgrade_display_hold_count = HOLD_COUNT_MAX;
BOOST_LOG_TRIVIAL(info) << "ack of upgrade_confirm";
}
}
}
}
catch (...) {
;
}
// event info
try {
@ -3645,6 +3816,7 @@ void DeviceManager::on_machine_alive(std::string json_str)
AppConfig* config = Slic3r::GUI::wxGetApp().app_config;
if (config) {
obj->set_access_code(Slic3r::GUI::wxGetApp().app_config->get("access_code", dev_id));
obj->set_user_access_code(Slic3r::GUI::wxGetApp().app_config->get("user_access_code", dev_id));
}
localMachineList.insert(std::make_pair(dev_id, obj));
@ -3789,6 +3961,18 @@ bool DeviceManager::set_selected_machine(std::string dev_id)
BOOST_LOG_TRIVIAL(info) << "set_selected_machine=" << dev_id;
auto my_machine_list = get_my_machine_list();
auto it = my_machine_list.find(dev_id);
// disconnect last
auto last_selected = my_machine_list.find(selected_machine);
if (last_selected != my_machine_list.end()) {
if (last_selected->second->connection_type() == "lan") {
if (last_selected->second->is_connecting())
return false;
m_agent->disconnect_printer();
}
}
// connect curr
if (it != my_machine_list.end()) {
if (selected_machine == dev_id) {
if (it->second->connection_type() != "lan") {
@ -3815,7 +3999,6 @@ bool DeviceManager::set_selected_machine(std::string dev_id)
it->second->reset_update_time();
}
} else {
m_agent->disconnect_printer();
it->second->reset();
it->second->connect();
it->second->set_lan_mode_connection_state(true);
@ -4007,6 +4190,7 @@ void DeviceManager::load_last_machine()
}
json DeviceManager::function_table = json::object();
json DeviceManager::filaments_blacklist = json::object();
std::string DeviceManager::parse_printer_type(std::string type_str)
{
@ -4036,6 +4220,20 @@ std::string DeviceManager::get_printer_display_name(std::string type_str)
return "";
}
std::string DeviceManager::get_ftp_folder(std::string type_str)
{
if (DeviceManager::function_table.contains("printers")) {
for (auto printer : DeviceManager::function_table["printers"]) {
if (printer.contains("model_id") && printer["model_id"].get<std::string>() == type_str) {
if (printer.contains("ftp_folder")) {
return printer["ftp_folder"].get<std::string>();
}
}
}
}
return "";
}
std::string DeviceManager::get_printer_thumbnail_img(std::string type_str)
{
if (DeviceManager::function_table.contains("printers")) {
@ -4115,4 +4313,96 @@ bool DeviceManager::load_functional_config(std::string config_file)
return true;
}
bool DeviceManager::load_filaments_blacklist_config(std::string config_file)
{
filaments_blacklist = json::object();
std::ifstream json_file(config_file.c_str());
try {
if (json_file.is_open()) {
json_file >> filaments_blacklist;
return true;
}
else {
BOOST_LOG_TRIVIAL(error) << "load filaments blacklist config failed, file = " << config_file;
}
}
catch (...) {
BOOST_LOG_TRIVIAL(error) << "load filaments blacklist config failed, file = " << config_file;
return false;
}
return true;
}
void DeviceManager::check_filaments_in_blacklist(std::string tag_vendor, std::string tag_type, bool& in_blacklist, std::string& ac, std::string& info)
{
in_blacklist = false;
if (filaments_blacklist.contains("blacklist")) {
for (auto prohibited_filament : filaments_blacklist["blacklist"]) {
std::string vendor;
std::string type;
std::string action;
std::string description;
if (prohibited_filament.contains("vendor") &&
prohibited_filament.contains("type") &&
prohibited_filament.contains("action") &&
prohibited_filament.contains("description"))
{
vendor = prohibited_filament["vendor"].get<std::string>();
type = prohibited_filament["type"].get<std::string>();
action = prohibited_filament["action"].get<std::string>();
description = prohibited_filament["description"].get<std::string>();
}
else {
return;
}
std::transform(vendor.begin(), vendor.end(), vendor.begin(), ::tolower);
std::transform(tag_vendor.begin(), tag_vendor.end(), tag_vendor.begin(), ::tolower);
std::transform(tag_type.begin(), tag_type.end(), tag_type.begin(), ::tolower);
std::transform(type.begin(), type.end(), type.begin(), ::tolower);
//third party
if (vendor == "third party") {
if ("bambulab" != vendor && tag_type == type) {
in_blacklist = true;
ac = action;
info = description;
return;
}
}
else {
if (vendor == tag_vendor && tag_type == type) {
in_blacklist = true;
ac = action;
info = description;
return;
}
}
}
}
}
std::string DeviceManager::load_gcode(std::string type_str, std::string gcode_file)
{
std::string gcode_full_path = Slic3r::resources_dir() + "/printers/" + gcode_file;
std::ifstream gcode(encode_path(gcode_full_path.c_str()).c_str());
try {
std::stringstream gcode_str;
if (gcode.is_open()) {
gcode_str << gcode.rdbuf();
gcode.close();
return gcode_str.str();
}
} catch(...) {
BOOST_LOG_TRIVIAL(error) << "load gcode file failed, file = " << gcode_file << ", path = " << gcode_full_path;
}
return "";
}
} // namespace Slic3r

View file

@ -178,6 +178,8 @@ public:
if (digit1 == -1 || digit2 == -1) break;
ret[j] = float(digit1 * 16 + digit2);
}
} else {
return wxColour(255, 255, 255);
}
return wxColour(ret[0], ret[1], ret[2]);
}
@ -216,6 +218,8 @@ public:
void update_color_from_str(std::string color);
wxColour get_color();
void reset();
bool is_tray_info_ready();
bool is_unset_third_filament();
std::string get_display_filament_type();
@ -401,15 +405,20 @@ public:
std::string dev_ip;
std::string dev_id;
bool local_use_ssl { false };
float nozzle_diameter { 0.0f };
std::string dev_connection_type; /* lan | cloud */
std::string connection_type() { return dev_connection_type; }
void set_dev_ip(std::string ip) {dev_ip = ip;};
bool has_access_right() { return !access_code.empty(); }
bool has_access_right() { return !get_access_code().empty(); }
std::string get_ftp_folder();
void set_access_code(std::string code);
std::string get_access_code();
void set_user_access_code(std::string code);
std::string get_user_access_code();
bool is_lan_mode_printer();
//PRINTER_TYPE printer_type = PRINTER_3DPrinter_UKNOWN;
std::string printer_type; /* model_id */
@ -460,7 +469,7 @@ public:
int ams_version = 0;
std::string m_ams_id; // local ams : "0" ~ "3"
std::string m_tray_id; // local tray id : "0" ~ "3"
std::string m_tray_now; // tray_now : "0" ~ "15" or "255"
std::string m_tray_now; // tray_now : "0" ~ "15" or "254", "255"
std::string m_tray_tar; // tray_tar : "0" ~ "15" or "255"
int extrusion_cali_hold_count = 0;
@ -484,7 +493,6 @@ public:
bool can_unload_filament();
bool is_U0_firmware();
bool is_support_ams_mapping();
bool is_only_support_cloud_print();
static bool is_support_ams_mapping_version(std::string module, std::string version);
int ams_filament_mapping(std::vector<FilamentInfo> filaments, std::vector<FilamentInfo> &result, std::vector<int> exclude_id = std::vector<int>());
@ -530,6 +538,7 @@ public:
bool upgrade_new_version { false };
bool upgrade_consistency_request { false };
int upgrade_display_state = 0; // 0 : upgrade unavailable, 1: upgrade idle, 2: upgrading, 3: upgrade_finished
int upgrade_display_hold_count = 0;
PrinterFirmwareType firmware_type; // engineer|production
std::string upgrade_progress;
std::string upgrade_message;
@ -630,6 +639,8 @@ public:
bool is_support_1080dpi {false};
bool is_support_ai_monitoring {false};
bool is_support_ams_humidity {true};
bool is_support_filament_edit_virtual_tray {true};
bool is_cloud_print_only {false};
/* sdcard */
MachineObject::SdcardState sdcard_state { NO_SDCARD };
@ -827,14 +838,20 @@ public:
void load_last_machine();
static json function_table;
static json filaments_blacklist;
static std::string parse_printer_type(std::string type_str);
static std::string get_printer_display_name(std::string type_str);
static std::string get_printer_thumbnail_img(std::string type_str);
static std::string get_ftp_folder(std::string type_str);
static bool is_function_supported(std::string type_str, std::string function_name);
static std::vector<std::string> get_resolution_supported(std::string type_str);
static bool get_bed_temperature_limit(std::string type_str, int& limit);
static bool load_functional_config(std::string config_file);
static bool load_filaments_blacklist_config(std::string config_file);
static void check_filaments_in_blacklist(std::string tag_vendor, std::string tag_type, bool& in_blacklist, std::string& ac, std::string& info);
static std::string load_gcode(std::string type_str, std::string gcode_file);
};
} // namespace Slic3r

View file

@ -65,10 +65,10 @@ void ExtrusionCalibration::create()
#else
m_comboBox_nozzle_dia = new ComboBox(m_step_1_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, EXTRUSION_CALIBRATION_BED_COMBOX, 0, nullptr, wxCB_READONLY);
#endif
m_comboBox_nozzle_dia->AppendString("0.2");
m_comboBox_nozzle_dia->AppendString("0.4");
m_comboBox_nozzle_dia->AppendString("0.6");
m_comboBox_nozzle_dia->AppendString("0.8");
m_comboBox_nozzle_dia->AppendString(wxString::Format("%1.1f", 0.2));
m_comboBox_nozzle_dia->AppendString(wxString::Format("%1.1f", 0.4));
m_comboBox_nozzle_dia->AppendString(wxString::Format("%1.1f", 0.6));
m_comboBox_nozzle_dia->AppendString(wxString::Format("%1.1f", 0.8));
select_sizer->Add(m_comboBox_nozzle_dia, 0, wxEXPAND);
select_sizer->Add(0, EXTRUSION_CALIBRATION_WIDGET_GAP, 0, 0);
@ -170,10 +170,10 @@ void ExtrusionCalibration::create()
m_button_cali = new Button(m_step_1_panel, _L("Start calibration"));
m_btn_bg_green = StateColor(std::pair<wxColour, int>(wxColour(238, 238, 238), StateColor::Disabled), std::pair<wxColour, int>(wxColour(27, 136, 68), StateColor::Pressed), std::pair<wxColour, int>(wxColour(61, 203, 115), StateColor::Hovered),
std::pair<wxColour, int>(wxColour(0, 150, 136), StateColor::Normal));
std::pair<wxColour, int>(wxColour(0, 174, 66), StateColor::Normal));
m_button_cali->SetBackgroundColor(m_btn_bg_green);
m_button_cali->SetFont(Label::Body_13);
m_button_cali->SetBorderColor({ std::pair<wxColour, int>(wxColour(238, 238, 238), StateColor::Disabled), std::pair<wxColour, int>(wxColour(0, 150, 136), StateColor::Enabled) });
m_button_cali->SetBorderColor({ std::pair<wxColour, int>(wxColour(238, 238, 238), StateColor::Disabled), std::pair<wxColour, int>(wxColour(0, 174, 66), StateColor::Enabled) });
m_button_cali->SetTextColor({ std::pair<wxColour, int>(wxColour(172, 172, 172), StateColor::Disabled), std::pair<wxColour, int>(EXTRUSION_CALIBRATION_GREY200, StateColor::Enabled) });
m_button_cali->SetCornerRadius(FromDIP(12));
m_button_cali->SetMinSize(wxSize(-1, FromDIP(24)));
@ -181,9 +181,9 @@ void ExtrusionCalibration::create()
m_cali_cancel = new Button(m_step_1_panel, _L("Cancel"));
m_btn_bg_green = StateColor(std::pair<wxColour, int>(wxColour(27, 136, 68), StateColor::Pressed), std::pair<wxColour, int>(wxColour(61, 203, 115), StateColor::Hovered),
std::pair<wxColour, int>(wxColour(0, 150, 136), StateColor::Normal));
std::pair<wxColour, int>(wxColour(0, 174, 66), StateColor::Normal));
m_cali_cancel->SetBackgroundColor(m_btn_bg_green);
m_cali_cancel->SetBorderColor(wxColour(0, 150, 136));
m_cali_cancel->SetBorderColor(wxColour(0, 174, 66));
m_cali_cancel->SetTextColor(EXTRUSION_CALIBRATION_GREY200);
m_cali_cancel->SetMinSize(EXTRUSION_CALIBRATION_BUTTON_SIZE);
m_cali_cancel->SetCornerRadius(FromDIP(12));
@ -256,10 +256,10 @@ void ExtrusionCalibration::create()
// save button
m_button_save_result = new Button(m_step_2_panel, _L("Save"));
m_btn_bg_green = StateColor(std::pair<wxColour, int>(wxColour(27, 136, 68), StateColor::Pressed), std::pair<wxColour, int>(wxColour(61, 203, 115), StateColor::Hovered),
std::pair<wxColour, int>(wxColour(0, 150, 136), StateColor::Normal));
std::pair<wxColour, int>(wxColour(0, 174, 66), StateColor::Normal));
m_button_save_result->SetBackgroundColor(m_btn_bg_green);
m_button_save_result->SetFont(Label::Body_13);
m_button_save_result->SetBorderColor(wxColour(0, 150, 136));
m_button_save_result->SetBorderColor(wxColour(0, 174, 66));
m_button_save_result->SetTextColor(EXTRUSION_CALIBRATION_GREY200);
m_button_save_result->SetMinSize(EXTRUSION_CALIBRATION_BUTTON_SIZE);
m_button_save_result->SetCornerRadius(FromDIP(12));
@ -660,8 +660,6 @@ void ExtrusionCalibration::update_combobox_filaments()
}
for (auto filament_it = preset_bundle->filaments.begin(); filament_it != preset_bundle->filaments.end(); filament_it++) {
if (filament_it->setting_id.empty()) continue;
ConfigOption* printer_opt = filament_it->config.option("compatible_printers");
ConfigOptionStrings* printer_strs = dynamic_cast<ConfigOptionStrings*>(printer_opt);
for (auto printer_str : printer_strs->values) {

View file

@ -1021,6 +1021,9 @@ void Choice::BUILD()
if (m_opt.height >= 0) size.SetHeight(m_opt.height*m_em_unit);
if (m_opt.width >= 0) size.SetWidth(m_opt.width*m_em_unit);
if (m_opt.nullable)
m_last_meaningful_value = dynamic_cast<ConfigOptionEnumsGenericNullable const *>(m_opt.default_value.get())->get_at(0);
choice_ctrl* temp;
auto dynamic_list = dynamic_lists.find(m_opt.opt_key);
if (dynamic_list != dynamic_lists.end())
@ -1209,7 +1212,7 @@ void Choice::set_selection()
void Choice::set_value(const std::string& value, bool change_event) //! Redundant?
{
m_disable_change_event = !change_event;
m_disable_change_event = !change_event;
size_t idx=0;
for (auto el : m_opt.enum_values)
@ -1291,10 +1294,10 @@ void Choice::set_value(const boost::any& value, bool change_event)
if (m_opt_id.compare("host_type") == 0 && val != 0 &&
m_opt.enum_values.size() > field->GetCount()) // for case, when PrusaLink isn't used as a HostType
val--;
if (m_opt_id == "top_surface_pattern" || m_opt_id == "bottom_surface_pattern" || m_opt_id == "sparse_infill_pattern")
if (m_opt_id == "top_surface_pattern" || m_opt_id == "bottom_surface_pattern" || m_opt_id == "sparse_infill_pattern" || m_opt_id == "support_style")
{
std::string key;
const t_config_enum_values& map_names = ConfigOptionEnum<InfillPattern>::get_enum_values();
const t_config_enum_values& map_names = *m_opt.enum_keys_map;
for (auto it : map_names)
if (val == it.second) {
key = it.first;
@ -1305,6 +1308,12 @@ void Choice::set_value(const boost::any& value, bool change_event)
auto it = std::find(values.begin(), values.end(), key);
val = it == values.end() ? 0 : it - values.begin();
}
if (m_opt.nullable) {
if (val != ConfigOptionEnumsGenericNullable::nil_value())
m_last_meaningful_value = value;
else
val = -1;
}
field->SetSelection(val);
break;
}
@ -1370,9 +1379,11 @@ boost::any& Choice::get_value()
// BBS
if (m_opt.type == coEnum || m_opt.type == coEnums)
{
if (m_opt_id == "top_surface_pattern" || m_opt_id == "bottom_surface_pattern" || m_opt_id == "sparse_infill_pattern") {
if (m_opt.nullable && field->GetSelection() == -1)
m_value = ConfigOptionEnumsGenericNullable::nil_value();
else if (m_opt_id == "top_surface_pattern" || m_opt_id == "bottom_surface_pattern" || m_opt_id == "sparse_infill_pattern" || m_opt_id == "support_style") {
const std::string& key = m_opt.enum_values[field->GetSelection()];
m_value = int(ConfigOptionEnum<InfillPattern>::get_enum_values().at(key));
m_value = int(m_opt.enum_keys_map->at(key));
}
// Support ThirdPartyPrinter
else if (m_opt_id.compare("host_type") == 0 && m_opt.enum_values.size() > field->GetCount()) {
@ -1404,6 +1415,20 @@ boost::any& Choice::get_value()
return m_value;
}
void Choice::set_last_meaningful_value()
{
if (m_opt.nullable) {
set_value(m_last_meaningful_value, false);
on_change_field();
}
}
void Choice::set_na_value()
{
dynamic_cast<choice_ctrl *>(window)->SetSelection(-1);
on_change_field();
}
void Choice::enable() { dynamic_cast<choice_ctrl*>(window)->Enable(); }
void Choice::disable() { dynamic_cast<choice_ctrl*>(window)->Disable(); }

View file

@ -386,6 +386,9 @@ public:
void set_values(const wxArrayString &values);
boost::any& get_value() override;
void set_last_meaningful_value() override;
void set_na_value() override;
void msw_rescale() override;
void enable() override ;//{ dynamic_cast<wxBitmapComboBox*>(window)->Enable(); };

View file

@ -357,7 +357,8 @@ void GCodeViewer::SequentialView::Marker::render(int canvas_width, int canvas_he
glsafe(::glDisable(GL_BLEND));
static float last_window_width = 0.0f;
static size_t last_text_length = 0;
size_t text_line = 0;
static size_t last_text_line = 0;
const ImU32 text_name_clr = m_is_dark ? IM_COL32(255, 255, 255, 0.88 * 255) : IM_COL32(38, 46, 48, 255);
const ImU32 text_value_clr = m_is_dark ? IM_COL32(255, 255, 255, 0.4 * 255) : IM_COL32(144, 144, 144, 255);
@ -466,6 +467,7 @@ void GCodeViewer::SequentialView::Marker::render(int canvas_width, int canvas_he
default:
break;
}
text_line = 2;
}
else {
sprintf(buf, "%s%.3f", x.c_str(), position.x() - plate->get_origin().x());
@ -485,14 +487,15 @@ void GCodeViewer::SequentialView::Marker::render(int canvas_width, int canvas_he
sprintf(buf, "%s%.0f", speed.c_str(), m_curr_move.feedrate);
ImGui::PushItemWidth(item_size);
imgui.text(buf);
text_line = 1;
}
// force extra frame to automatically update window size
float window_width = ImGui::GetWindowWidth();
//size_t length = strlen(buf);
if (window_width != last_window_width /*|| length != last_text_length*/) {
if (window_width != last_window_width || text_line != last_text_line) {
last_window_width = window_width;
//last_text_length = length;
last_text_line = text_line;
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
imgui.set_requires_extra_frame();
#else
@ -945,7 +948,6 @@ void GCodeViewer::update_by_mode(ConfigOptionMode mode)
// BBS for first layer inspection
view_type_items.push_back(EViewType::FilamentId);
options_items.push_back(EMoveType::Seam);
options_items.push_back(EMoveType::Travel);
options_items.push_back(EMoveType::Retract);
options_items.push_back(EMoveType::Unretract);
@ -953,6 +955,8 @@ void GCodeViewer::update_by_mode(ConfigOptionMode mode)
if (mode == ConfigOptionMode::comDevelop) {
options_items.push_back(EMoveType::Tool_change);
}
//BBS: seam is not real move and extrusion, put at last line
options_items.push_back(EMoveType::Seam);
}
std::vector<int> GCodeViewer::get_plater_extruder()
@ -1794,11 +1798,13 @@ void GCodeViewer::update_layers_slider_mode()
}
void GCodeViewer::update_marker_curr_move() {
auto it = std::find_if(m_gcode_result->moves.begin(), m_gcode_result->moves.end(), [this](auto move) {
return move.gcode_id == static_cast<uint64_t>(m_sequential_view.gcode_ids[m_sequential_view.current.last]);
});
m_sequential_view.marker.update_curr_move(*it);
if ((int)m_last_result_id != -1) {
auto it = std::find_if(m_gcode_result->moves.begin(), m_gcode_result->moves.end(), [this](auto move) {
return move.gcode_id == static_cast<uint64_t>(m_sequential_view.gcode_ids[m_sequential_view.current.last]);
});
if (it != m_gcode_result->moves.end())
m_sequential_view.marker.update_curr_move(*it);
}
}
bool GCodeViewer::is_toolpath_move_type_visible(EMoveType type) const
@ -3080,6 +3086,13 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result, const
}
m_plater_extruder = plater_extruder;
// replace layers for spiral vase mode
if (!gcode_result.spiral_vase_layers.empty()) {
m_layers.reset();
for (const auto& layer : gcode_result.spiral_vase_layers) {
m_layers.append(layer.first, { layer.second.first, layer.second.second });
}
}
// set layers z range
if (!m_layers.empty())
@ -3325,11 +3338,13 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
const bool top_layer_only = true;
SequentialView::Endpoints global_endpoints = { m_moves_count , 0 };
//BBS
SequentialView::Endpoints global_endpoints = { m_sequential_view.gcode_ids.size() , 0 };
SequentialView::Endpoints top_layer_endpoints = global_endpoints;
SequentialView* sequential_view = const_cast<SequentialView*>(&m_sequential_view);
if (top_layer_only || !keep_sequential_current_first) sequential_view->current.first = 0;
if (!keep_sequential_current_last) sequential_view->current.last = m_moves_count;
//BBS
if (!keep_sequential_current_last) sequential_view->current.last = m_sequential_view.gcode_ids.size();
// first pass: collect visible paths and update sequential view data
std::vector<std::tuple<unsigned char, unsigned int, unsigned int, unsigned int>> paths;
@ -3403,15 +3418,11 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
// update current sequential position
sequential_view->current.first = !top_layer_only && keep_sequential_current_first ? std::clamp(sequential_view->current.first, global_endpoints.first, global_endpoints.last) : global_endpoints.first;
if (global_endpoints.last == 0) {
m_no_render_path = true;
m_no_render_path = true;
sequential_view->current.last = global_endpoints.last;
} else {
m_no_render_path = false;
}
if (!m_no_render_path) {
sequential_view->current.last = keep_sequential_current_last ? std::clamp(sequential_view->current.last, global_endpoints.first, global_endpoints.last) : global_endpoints.last;
} else {
sequential_view->current.last = sequential_view->current.first;
}
// get the world position from the vertex buffer
@ -4125,6 +4136,214 @@ void GCodeViewer::render_shells()
// glsafe(::glDepthMask(GL_TRUE));
}
//BBS
void GCodeViewer::render_all_plates_stats(const std::vector<const GCodeProcessorResult*>& gcode_result_list, bool show /*= true*/) const {
if (!show)
return;
for (auto gcode_result : gcode_result_list) {
if (gcode_result->moves.size() == 0)
return;
}
ImGuiWrapper& imgui = *wxGetApp().imgui();
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0, 10.0 * m_scale));
ImGui::PushStyleColor(ImGuiCol_Separator, ImVec4(1.0f, 1.0f, 1.0f, 0.6f));
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.00f, 0.68f, 0.26f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.00f, 0.68f, 0.26f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrab, ImVec4(0.42f, 0.42f, 0.42f, 1.00f));
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabHovered, ImVec4(0.93f, 0.93f, 0.93f, 1.00f));
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabActive, ImVec4(0.93f, 0.93f, 0.93f, 1.00f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(340.f * m_scale * imgui.scaled(1.0f / 15.0f), 0));
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), 0, ImVec2(0.5f, 0.5f));
ImGui::Begin(_L("Statistics of All Plates").c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
ImDrawList* draw_list = ImGui::GetWindowDrawList();
std::vector<float> filament_diameters = gcode_result_list.front()->filament_diameters;
std::vector<float> filament_densities = gcode_result_list.front()->filament_densities;
std::vector<Color> filament_colors = decode_colors(wxGetApp().plater()->get_extruder_colors_from_plater_config(gcode_result_list.back()));
bool imperial_units = wxGetApp().app_config->get("use_inches") == "1";
float window_padding = 4.0f * m_scale;
const float icon_size = ImGui::GetTextLineHeight() * 0.7;
std::vector<float> offsets;
std::map<int, double> volume_of_extruders_all_plates; // map<extruder_idx, volume>
std::map<int, double> flushed_volume_of_extruders_all_plates; // map<extruder_idx, flushed volume>
std::vector<double> model_used_filaments_m_all_plates;
std::vector<double> model_used_filaments_g_all_plates;
std::vector<double> flushed_filaments_m_all_plates;
std::vector<double> flushed_filaments_g_all_plates;
float total_time_all_plates = 0.0f;
bool show_detailed_statistics_page = false;
auto max_width = [](const std::vector<std::string>& items, const std::string& title, float extra_size = 0.0f) {
float ret = ImGui::CalcTextSize(title.c_str()).x;
for (const std::string& item : items) {
ret = std::max(ret, extra_size + ImGui::CalcTextSize(item.c_str()).x);
}
return ret;
};
auto calculate_offsets = [max_width, window_padding](const std::vector<std::pair<std::string, std::vector<::string>>>& title_columns, float extra_size = 0.0f) {
const ImGuiStyle& style = ImGui::GetStyle();
std::vector<float> offsets;
offsets.push_back(max_width(title_columns[0].second, title_columns[0].first, extra_size) + 3.0f * style.ItemSpacing.x + style.WindowPadding.x);
for (size_t i = 1; i < title_columns.size() - 1; i++)
offsets.push_back(offsets.back() + max_width(title_columns[i].second, title_columns[i].first) + style.ItemSpacing.x);
if (title_columns.back().first == _u8L("Display"))
offsets.back() = ImGui::GetWindowWidth() - ImGui::CalcTextSize(_u8L("Display").c_str()).x - ImGui::GetFrameHeight() / 2 - 2 * window_padding;
float average_col_width = ImGui::GetWindowWidth() / static_cast<float>(title_columns.size());
std::vector<float> ret;
ret.push_back(0);
for (size_t i = 1; i < title_columns.size(); i++) {
ret.push_back(std::max(offsets[i - 1], i * average_col_width));
}
return ret;
};
auto append_item = [icon_size, &imgui, imperial_units, &window_padding, &draw_list, this](const Color& color, const std::vector<std::pair<std::string, float>>& columns_offsets)
{
// render icon
ImVec2 pos = ImVec2(ImGui::GetCursorScreenPos().x + window_padding * 3, ImGui::GetCursorScreenPos().y);
draw_list->AddRectFilled({ pos.x + 1.0f * m_scale, pos.y + 3.0f * m_scale }, { pos.x + icon_size - 1.0f * m_scale, pos.y + icon_size + 1.0f * m_scale },
ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(20.0 * m_scale, 6.0 * m_scale));
// render selectable
ImGui::Dummy({ 0.0, 0.0 });
ImGui::SameLine();
// render column item
{
float dummy_size = ImGui::GetStyle().ItemSpacing.x + icon_size;
ImGui::SameLine(dummy_size);
imgui.text(columns_offsets[0].first);
for (auto i = 1; i < columns_offsets.size(); i++) {
ImGui::SameLine(columns_offsets[i].second);
imgui.text(columns_offsets[i].first);
}
}
ImGui::PopStyleVar(1);
};
auto append_headers = [&imgui](const std::vector<std::pair<std::string, float>>& title_offsets) {
for (size_t i = 0; i < title_offsets.size(); i++) {
ImGui::SameLine(title_offsets[i].second);
imgui.bold_text(title_offsets[i].first);
}
ImGui::Separator();
};
auto get_used_filament_from_volume = [this, imperial_units, &filament_diameters, &filament_densities](double volume, int extruder_id) {
double koef = imperial_units ? 1.0 / GizmoObjectManipulation::in_to_mm : 0.001;
std::pair<double, double> ret = { koef * volume / (PI * sqr(0.5 * filament_diameters[extruder_id])),
volume * filament_densities[extruder_id] * 0.001 };
return ret;
};
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
// title and item data
{
PartPlateList& plate_list = wxGetApp().plater()->get_partplate_list();
for (auto plate : plate_list.get_nonempty_plate_list())
{
auto plate_print_statistics = plate->get_slice_result()->print_statistics;
auto plate_extruders = plate->get_extruders(true);
for (size_t extruder_id : plate_extruders) {
extruder_id -= 1;
if (plate_print_statistics.volumes_per_extruder.find(extruder_id) == plate_print_statistics.volumes_per_extruder.end())
continue;
double volume = plate_print_statistics.volumes_per_extruder.at(extruder_id);
volume_of_extruders_all_plates[extruder_id] += volume;
if (plate_print_statistics.flush_per_filament.find(extruder_id) == plate_print_statistics.flush_per_filament.end())
flushed_volume_of_extruders_all_plates[extruder_id] = 0;
else {
double flushed_volume = plate_print_statistics.flush_per_filament.at(extruder_id);
flushed_volume_of_extruders_all_plates[extruder_id] += flushed_volume;
}
}
const PrintEstimatedStatistics::Mode& plate_time_mode = plate_print_statistics.modes[static_cast<size_t>(m_time_estimate_mode)];
total_time_all_plates += plate_time_mode.time;
}
for (auto it = volume_of_extruders_all_plates.begin(); it != volume_of_extruders_all_plates.end(); it++) {
auto [model_used_filament_m, model_used_filament_g] = get_used_filament_from_volume(it->second, it->first);
model_used_filaments_m_all_plates.push_back(model_used_filament_m);
model_used_filaments_g_all_plates.push_back(model_used_filament_g);
}
for (auto it = flushed_volume_of_extruders_all_plates.begin(); it != flushed_volume_of_extruders_all_plates.end(); it++) {
auto [flushed_filament_m, flushed_filament_g] = get_used_filament_from_volume(it->second, it->first);
if (flushed_filament_m != 0.0 || flushed_filament_g != 0.0)
show_detailed_statistics_page = true;
flushed_filaments_m_all_plates.push_back(flushed_filament_m);
flushed_filaments_g_all_plates.push_back(flushed_filament_g);
}
char buff[64];
double longest_str = 0.0;
for (auto i : model_used_filaments_g_all_plates) {
if (i > longest_str)
longest_str = i;
}
::sprintf(buff, "%.2f", longest_str);
offsets = calculate_offsets({ {_u8L("Filament"), {""}}, {_u8L("Model"), {buff}}, {_u8L("Flushed"), {buff}}, /*{_u8L("Tower"), total_filaments},*/ {_u8L("Total"), {buff}} }, icon_size);
if (!show_detailed_statistics_page)
append_headers({ {_u8L("Filament"), offsets[0]}, {_u8L("Model"), offsets[2]} });
else
append_headers({ {_u8L("Filament"), offsets[0]}, {_u8L("Model"), offsets[1]}, {_u8L("Flushed"), offsets[2]}, /*{_u8L("Tower"), offsets[3]},*/ {_u8L("Total"), offsets[3]} });// to add Tower
}
// item
{
size_t i = 0;
for (auto it = volume_of_extruders_all_plates.begin(); it != volume_of_extruders_all_plates.end(); it++) {
if (i < model_used_filaments_m_all_plates.size() && i < model_used_filaments_g_all_plates.size()) {
std::vector<std::pair<std::string, float>> columns_offsets;
columns_offsets.push_back({ std::to_string(it->first + 1), offsets[0] });
char buf[64];
if (show_detailed_statistics_page) {
::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", model_used_filaments_m_all_plates[i], model_used_filaments_g_all_plates[i]);
columns_offsets.push_back({ buf, offsets[1] });
::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", flushed_filaments_m_all_plates[i], flushed_filaments_g_all_plates[i]);
columns_offsets.push_back({ buf, offsets[2] });
::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", model_used_filaments_m_all_plates[i] + flushed_filaments_m_all_plates[i], model_used_filaments_g_all_plates[i] + flushed_filaments_g_all_plates[i]);
columns_offsets.push_back({ buf, offsets[3] });
}
else {
::sprintf(buf, imperial_units ? "%.2f in %.2f oz" : "%.2f m %.2f g", model_used_filaments_m_all_plates[i], model_used_filaments_g_all_plates[i]);
columns_offsets.push_back({ buf, offsets[2] });
}
append_item(filament_colors[it->first], columns_offsets);
}
i++;
}
ImGui::Dummy(ImVec2(0.0f, ImGui::GetFontSize() * 0.1));
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
imgui.title(_u8L("Total Time Estimation"));
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
imgui.text(_u8L("Total time") + ":");
ImGui::SameLine();
imgui.text(short_time(get_time_dhms(total_time_all_plates)));
}
ImGui::End();
ImGui::PopStyleColor(6);
ImGui::PopStyleVar(3);
return;
}
void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canvas_height, int right_margin)
{
if (!m_legend_enabled)
@ -4283,8 +4502,6 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
const float step_size = range.step_size();
for (int i = static_cast<int>(Range_Colors.size()) - 1; i >= 0; --i) {
append_range_item(i, range.get_value_at_step(i), decimals);
}
}
};
@ -4376,6 +4593,11 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
return (it != time_mode.roles_times.end()) ? std::make_pair(it->second, it->second / time_mode.time) : std::make_pair(0.0f, 0.0f);
};
auto move_time_and_percent = [time_mode](EMoveType move_type) {
auto it = std::find_if(time_mode.moves_times.begin(), time_mode.moves_times.end(), [move_type](const std::pair<EMoveType, float>& item) { return move_type == item.first; });
return (it != time_mode.moves_times.end()) ? std::make_pair(it->second, it->second / time_mode.time) : std::make_pair(0.0f, 0.0f);
};
auto used_filament_per_role = [this, imperial_units](ExtrusionRole role) {
auto it = m_print_statistics.used_filaments_per_role.find(role);
if (it == m_print_statistics.used_filaments_per_role.end())
@ -4385,6 +4607,14 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
return std::make_pair(it->second.first * koef, it->second.second);
};
// get used filament (meters and grams) from used volume in respect to the active extruder
auto get_used_filament_from_volume = [this, imperial_units](double volume, int extruder_id) {
double koef = imperial_units ? 1.0 / GizmoObjectManipulation::in_to_mm : 0.001;
std::pair<double, double> ret = { koef * volume / (PI * sqr(0.5 * m_filament_diameters[extruder_id])),
volume * m_filament_densities[extruder_id] * 0.001 };
return ret;
};
//BBS display Color Scheme
ImGui::Dummy({ window_padding, window_padding });
ImGui::Dummy({ window_padding, window_padding });
@ -4449,7 +4679,9 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
std::vector<float> offsets;
std::vector<std::string> labels;
std::vector<std::string> times;
std::string travel_time;
std::vector<std::string> percents;
std::string travel_percent;
std::vector<double> model_used_filaments_m;
std::vector<double> model_used_filaments_g;
double total_model_used_filament_m = 0, total_model_used_filament_g = 0;
@ -4462,13 +4694,6 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
double koef = imperial_units ? GizmoObjectManipulation::in_to_mm : 1000.0;
double unit_conver = imperial_units ? GizmoObjectManipulation::oz_to_g : 1;
// get used filament (meters and grams) from used volume in respect to the active extruder
auto get_used_filament_from_volume = [this, imperial_units](double volume, int extruder_id) {
double koef = imperial_units ? 1.0 / GizmoObjectManipulation::in_to_mm : 0.001;
std::pair<double, double> ret = { koef * volume / (PI * sqr(0.5 * m_filament_diameters[extruder_id])),
volume * m_filament_densities[extruder_id] * 0.001 };
return ret;
};
// extrusion paths section -> title
ImGui::Dummy({ window_padding, window_padding });
@ -4496,6 +4721,17 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
}
}
//BBS: get travel time and percent
{
auto [time, percent] = move_time_and_percent(EMoveType::Travel);
travel_time = (time > 0.0f) ? short_time(get_time_dhms(time)) : "";
if (percent == 0)
::sprintf(buffer, "0%%");
else
percent > 0.001 ? ::sprintf(buffer, "%.1f%%", percent * 100) : ::sprintf(buffer, "<0.1%%");
travel_percent = buffer;
}
offsets = calculate_offsets({ {_u8L("Line Type"), labels}, {_u8L("Time"), times}, {_u8L("Percent"), percents}, {_u8L("Display"), {""}}}, icon_size);
append_headers({{_u8L("Line Type"), offsets[0]}, {_u8L("Time"), offsets[1]}, {_u8L("Percent"), offsets[2]}, {_u8L("Display"), offsets[3]}});
break;
@ -4584,7 +4820,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
};
const bool visible = m_buffers[buffer_id(type)].visible;
if (type == EMoveType::Travel) {
//TODO display travel time
//BBS: only display travel time in FeatureType view
append_option_item_with_type(type, Travel_Colors[0], _u8L("Travel"), visible);
}
else if (type == EMoveType::Seam)
@ -4624,7 +4860,23 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
}
for(auto item : options_items) {
append_option_item(item, offsets);
if (item != EMoveType::Travel) {
append_option_item(item, offsets);
} else {
//BBS: show travel time in FeatureType view
const bool visible = m_buffers[buffer_id(item)].visible;
std::vector<std::pair<std::string, float>> columns_offsets;
columns_offsets.push_back({ _u8L("Travel"), offsets[0] });
columns_offsets.push_back({ travel_time, offsets[1] });
columns_offsets.push_back({ travel_percent, offsets[2] });
append_item(EItemType::Rect, Travel_Colors[0], columns_offsets, true, visible, [this, item, visible]() {
m_buffers[buffer_id(item)].visible = !m_buffers[buffer_id(item)].visible;
// update buffers' render paths
refresh_render_paths(false, false);
update_moves_slider();
wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
});
}
}
break;
}
@ -4668,7 +4920,8 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
}
case EViewType::ColorPrint:
{
const std::vector<CustomGCode::Item>& custom_gcode_per_print_z = wxGetApp().is_editor() ? wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes : m_custom_gcode_per_print_z;
//BBS: replace model custom gcode with current plate custom gcode
const std::vector<CustomGCode::Item>& custom_gcode_per_print_z = wxGetApp().is_editor() ? wxGetApp().plater()->model().get_curr_plate_custom_gcodes().gcodes : m_custom_gcode_per_print_z;
size_t total_items = 1;
for (size_t extruder_id : m_extruder_ids) {
total_items += color_print_ranges(extruder_id, custom_gcode_per_print_z).size();
@ -4722,13 +4975,13 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
char buf[64];
if (show_flushed_filaments) {
::sprintf(buf, imperial_units ? "%.2f in\n%.2f g" : "%.2f m\n%.2f g", model_used_filaments_m[i], model_used_filaments_g[i]);
::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", model_used_filaments_m[i], model_used_filaments_g[i]);
columns_offsets.push_back({ buf, offsets[1] });
::sprintf(buf, imperial_units ? "%.2f in\n%.2f g" : "%.2f m\n%.2f g", flushed_filaments_m[i], flushed_filaments_g[i]);
::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", flushed_filaments_m[i], flushed_filaments_g[i]);
columns_offsets.push_back({ buf, offsets[2] });
::sprintf(buf, imperial_units ? "%.2f in\n%.2f g" : "%.2f m\n%.2f g", model_used_filaments_m[i] + flushed_filaments_m[i], model_used_filaments_g[i] + flushed_filaments_g[i]);
::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", model_used_filaments_m[i] + flushed_filaments_m[i], model_used_filaments_g[i] + flushed_filaments_g[i]);
columns_offsets.push_back({ buf, offsets[3] });
}
else {
@ -4850,7 +5103,8 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
auto generate_partial_times = [this, get_used_filament_from_volume](const TimesList& times, const std::vector<double>& used_filaments) {
PartialTimes items;
std::vector<CustomGCode::Item> custom_gcode_per_print_z = wxGetApp().is_editor() ? wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes : m_custom_gcode_per_print_z;
//BBS: replace model custom gcode with current plate custom gcode
std::vector<CustomGCode::Item> custom_gcode_per_print_z = wxGetApp().is_editor() ? wxGetApp().plater()->model().get_curr_plate_custom_gcodes().gcodes : m_custom_gcode_per_print_z;
std::vector<Color> last_color(m_extruders_count);
for (size_t i = 0; i < m_extruders_count; ++i) {
last_color[i] = m_tools.m_tool_colors[i];
@ -5220,19 +5474,19 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
auto it = std::find_if(time_mode.roles_times.begin(), time_mode.roles_times.end(), [role](const std::pair<ExtrusionRole, float>& item) { return role == item.first; });
return (it != time_mode.roles_times.end()) ? it->second : 0.0f;
};
//BBS: start gcode is prepeare time
if (role_time(erCustom) != 0.0f) {
//BBS: start gcode is mostly same with prepeare time
if (time_mode.prepare_time != 0.0f) {
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
imgui.text(prepare_str + ":");
ImGui::SameLine(max_len);
imgui.text(short_time(get_time_dhms(role_time(erCustom))));
imgui.text(short_time(get_time_dhms(time_mode.prepare_time)));
}
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
imgui.text(print_str + ":");
ImGui::SameLine(max_len);
imgui.text(short_time(get_time_dhms(time_mode.time - role_time(erCustom))));
imgui.text(short_time(get_time_dhms(time_mode.time - time_mode.prepare_time)));
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
imgui.text(total_str + ":");
@ -5264,16 +5518,16 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
}
default : { assert(false); break; }
}
}
if (m_view_type == EViewType::ColorPrint) {
ImGui::Spacing();
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
offsets = calculate_offsets({ { _u8L("Options"), { ""}}, { _u8L("Display"), {""}} }, icon_size);
append_headers({ {_u8L("Options"), offsets[0] }, { _u8L("Display"), offsets[1]} });
for (auto item : options_items)
append_option_item(item, offsets);
}
if (m_view_type == EViewType::ColorPrint) {
ImGui::Spacing();
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
offsets = calculate_offsets({ { _u8L("Options"), { ""}}, { _u8L("Display"), {""}} }, icon_size);
append_headers({ {_u8L("Options"), offsets[0] }, { _u8L("Display"), offsets[1]} });
for (auto item : options_items)
append_option_item(item, offsets);
}
legend_height = ImGui::GetCurrentWindow()->Size.y;
@ -5304,7 +5558,6 @@ void GCodeViewer::pop_combo_style()
}
void GCodeViewer::render_slider(int canvas_width, int canvas_height) {
m_moves_slider->render(canvas_width, canvas_height);
m_layers_slider->render(canvas_width, canvas_height);
}

View file

@ -809,6 +809,8 @@ public:
void reset_shell();
void load_shells(const Print& print, bool initialized, bool force_previewing = false);
void set_shells_on_preview(bool is_previewing) { m_shells.previewing = is_previewing; }
//BBS: add all plates filament statistics
void render_all_plates_stats(const std::vector<const GCodeProcessorResult*>& gcode_result_list, bool show = true) const;
//BBS: GUI refactor: add canvas width and height
void render(int canvas_width, int canvas_height, int right_margin);
//BBS

View file

@ -74,8 +74,8 @@ static constexpr const float TRACKBALLSIZE = 0.8f;
static const float SLIDER_DEFAULT_RIGHT_MARGIN = 10.0f;
static const float SLIDER_DEFAULT_BOTTOM_MARGIN = 10.0f;
static const float SLIDER_RIGHT_MARGIN = 115.0f;
static const float SLIDER_BOTTOM_MARGIN = 90.0f;
static const float SLIDER_RIGHT_MARGIN = 124.0f;
static const float SLIDER_BOTTOM_MARGIN = 64.0f;
float GLCanvas3D::DEFAULT_BG_LIGHT_COLOR[3] = { 0.906f, 0.906f, 0.906f };
float GLCanvas3D::DEFAULT_BG_LIGHT_COLOR_DARK[3] = { 0.329f, 0.329f, 0.353f };
@ -1160,6 +1160,7 @@ GLCanvas3D::~GLCanvas3D()
reset_volumes();
m_sel_plate_toolbar.del_all_item();
m_sel_plate_toolbar.del_stats_item();
}
void GLCanvas3D::post_event(wxEvent &&event)
@ -1608,6 +1609,11 @@ void GLCanvas3D::enable_main_toolbar(bool enable)
m_main_toolbar.set_enabled(enable);
}
void GLCanvas3D::reset_select_plate_toolbar_selection() {
if (m_sel_plate_toolbar.m_all_plates_stats_item)
m_sel_plate_toolbar.m_all_plates_stats_item->selected = false;
}
void GLCanvas3D::enable_select_plate_toolbar(bool enable)
{
m_sel_plate_toolbar.set_enabled(enable);
@ -1721,6 +1727,9 @@ float GLCanvas3D::get_collapse_toolbar_height()
return collapse_toolbar.is_enabled() ? collapse_toolbar.get_height() : 0;
}
bool GLCanvas3D::make_current_for_postinit() {
return _set_current();
}
void GLCanvas3D::render(bool only_init)
{
@ -1844,7 +1853,7 @@ void GLCanvas3D::render(bool only_init)
_render_platelist(!camera.is_looking_downward(), only_current, only_body, hover_id);
}
/* preview render */
else if (m_canvas_type == ECanvasType::CanvasPreview) {
else if (m_canvas_type == ECanvasType::CanvasPreview && m_render_preview) {
//BBS: add outline logic
_render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running());
//BBS: GUI refactor: add canvas size as parameters
@ -2045,6 +2054,17 @@ void GLCanvas3D::deselect_all()
post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT));
}
void GLCanvas3D::set_selected_visible(bool visible)
{
for (unsigned int i : m_selection.get_volume_idxs()) {
GLVolume* volume = const_cast<GLVolume*>(m_selection.get_volume(i));
volume->visible = visible;
volume->color[3] = visible ? 1.f : GLVolume::MODEL_HIDDEN_COL[3];
volume->render_color[3] = volume->color[3];
volume->force_native_color = !visible;
}
}
void GLCanvas3D::delete_selected()
{
m_selection.erase();
@ -2383,8 +2403,21 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
}
}
for (int temp_idx = vol_idx; temp_idx < m_volumes.volumes.size() && !update_object_list; temp_idx++) {
if (!m_volumes.volumes[temp_idx]->is_wipe_tower)
// Volumes in m_volumes might not exist anymore, so we cannot
// directly check if they are is_wipe_towers, for which we do
// not want to update the object list. Instead, we do a kind of
// slow thing of seeing if they were in the deleted list, and if
// so, if they were a wipe tower.
bool was_deleted_wipe_tower = false;
for (int del_idx = 0; del_idx < deleted_wipe_towers.size(); del_idx++) {
if (deleted_wipe_towers[del_idx].volume_idx == temp_idx) {
was_deleted_wipe_tower = true;
break;
}
}
if (!was_deleted_wipe_tower) {
update_object_list = true;
}
}
for (int temp_idx = vol_idx; temp_idx < glvolumes_new.size() && !update_object_list; temp_idx++) {
if (!glvolumes_new[temp_idx]->is_wipe_tower)
@ -2536,8 +2569,12 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
auto timelapse_type = dconfig.option<ConfigOptionEnum<TimelapseType>>("timelapse_type");
bool timelapse_enabled = timelapse_type ? (timelapse_type->value == TimelapseType::tlSmooth) : false;
if ((timelapse_enabled && wt) || (filaments_count > 1 && wt && co != nullptr && co->value != PrintSequence::ByObject)) {
if (wt && (timelapse_enabled || filaments_count > 1)) {
for (int plate_id = 0; plate_id < n_plates; plate_id++) {
// If print ByObject and there is only one object in the plate, the wipe tower is allowed to be generated.
if (co != nullptr && co->value == PrintSequence::ByObject && ppl.get_plate(plate_id)->printable_instance_size() != 1)
continue;
DynamicPrintConfig& proj_cfg = wxGetApp().preset_bundle->project_config;
float x = dynamic_cast<const ConfigOptionFloats*>(proj_cfg.option("wipe_tower_x"))->get_at(plate_id);
float y = dynamic_cast<const ConfigOptionFloats*>(proj_cfg.option("wipe_tower_y"))->get_at(plate_id);
@ -3458,6 +3495,7 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
}
}
}
else return;
}
if (keyCode != WXK_TAB
@ -3529,6 +3567,28 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
if (m_gizmos.on_mouse_wheel(evt))
return;
if (m_canvas_type == CanvasAssembleView && (evt.AltDown() || evt.CmdDown())) {
float rotation = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta();
if (evt.AltDown()) {
auto clp_dist = m_gizmos.m_assemble_view_data->model_objects_clipper()->get_position();
clp_dist = rotation < 0.f
? std::max(0., clp_dist - 0.01)
: std::min(1., clp_dist + 0.01);
m_gizmos.m_assemble_view_data->model_objects_clipper()->set_position(clp_dist, true);
}
else if (evt.CmdDown()) {
m_explosion_ratio = rotation < 0.f
? std::max(1., m_explosion_ratio - 0.01)
: std::min(3., m_explosion_ratio + 0.01);
if (m_explosion_ratio != GLVolume::explosion_ratio) {
for (GLVolume* volume : m_volumes.volumes) {
volume->set_bounding_boxes_as_dirty();
}
GLVolume::explosion_ratio = m_explosion_ratio;
}
}
return;
}
// Calculate the zoom delta and apply it to the current zoom factor
#ifdef SUPPORT_REVERSE_MOUSE_ZOOM
double direction_factor = (wxGetApp().app_config->get("reverse_mouse_wheel_zoom") == "1") ? -1.0 : 1.0;
@ -4235,6 +4295,10 @@ void GLCanvas3D::on_paint(wxPaintEvent& evt)
this->render();
}
void GLCanvas3D::force_set_focus() {
m_canvas->SetFocus();
};
void GLCanvas3D::on_set_focus(wxFocusEvent& evt)
{
m_tooltip_enabled = false;
@ -5753,6 +5817,9 @@ bool GLCanvas3D::_init_toolbars()
if (!_init_separator_toolbar())
return false;
if (!_init_select_plate_toolbar())
return false;
#if 0
if (!_init_view_toolbar())
return false;
@ -5918,7 +5985,24 @@ bool GLCanvas3D::_init_main_toolbar()
//BBS: GUI refactor: GLToolbar
bool GLCanvas3D::_init_select_plate_toolbar()
{
return true;
std::string path = resources_dir() + "/images/";
IMToolbarItem* item = new IMToolbarItem();
bool result = item->image_texture.load_from_svg_file(path + "im_all_plates_stats.svg", false, false, false, 128);
result = result && item->image_texture_transparent.load_from_svg_file(path + "im_all_plates_stats_transparent.svg", false, false, false, 128);
m_sel_plate_toolbar.m_all_plates_stats_item = item;
return result;
}
void GLCanvas3D::_update_select_plate_toolbar_stats_item(bool force_selected) {
PartPlateList& plate_list = wxGetApp().plater()->get_partplate_list();
if (plate_list.get_nonempty_plate_list().size() > 1)
m_sel_plate_toolbar.show_stats_item = true;
else
m_sel_plate_toolbar.show_stats_item = false;
if (force_selected && m_sel_plate_toolbar.show_stats_item)
m_sel_plate_toolbar.m_all_plates_stats_item->selected = true;
}
bool GLCanvas3D::_update_imgui_select_plate_toolbar()
@ -5926,6 +6010,8 @@ bool GLCanvas3D::_update_imgui_select_plate_toolbar()
bool result = true;
if (!m_sel_plate_toolbar.is_enabled()) return false;
_update_select_plate_toolbar_stats_item();
m_sel_plate_toolbar.del_all_item();
PartPlateList& plate_list = wxGetApp().plater()->get_partplate_list();
@ -5941,6 +6027,7 @@ bool GLCanvas3D::_update_imgui_select_plate_toolbar()
}
m_sel_plate_toolbar.m_items.push_back(item);
}
m_sel_plate_toolbar.is_display_scrollbar = false;
return result;
}
@ -6565,7 +6652,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
return true;
}
}, with_outline);
if (m_canvas_type == CanvasAssembleView) {
if (m_canvas_type == CanvasAssembleView && m_gizmos.m_assemble_view_data->model_objects_clipper()->get_position() > 0) {
const GLGizmosManager& gm = get_gizmos_manager();
shader->stop_using();
gm.render_painter_assemble_view();
@ -7031,20 +7118,26 @@ void GLCanvas3D::_render_main_toolbar()
//BBS: GUI refactor: GLToolbar adjust
//when rendering, {0, 0} is at the center, {-0.5, 0.5} at the left-up
void GLCanvas3D::_render_imgui_select_plate_toolbar() const
void GLCanvas3D::_render_imgui_select_plate_toolbar()
{
if (!m_sel_plate_toolbar.is_enabled())
if (!m_sel_plate_toolbar.is_enabled()) {
if (!m_render_preview)
m_render_preview = true;
return;
}
IMToolbarItem* all_plates_stats_item = m_sel_plate_toolbar.m_all_plates_stats_item;
PartPlateList& plate_list = wxGetApp().plater()->get_partplate_list();
for (int i = 0; i < plate_list.get_plate_count(); i++) {
if (i < m_sel_plate_toolbar.m_items.size()) {
if (i == plate_list.get_curr_plate_index())
if (i == plate_list.get_curr_plate_index() && !all_plates_stats_item->selected)
m_sel_plate_toolbar.m_items[i]->selected = true;
else
m_sel_plate_toolbar.m_items[i]->selected = false;
m_sel_plate_toolbar.m_items[i]->percent = plate_list.get_plate(i)->get_slicing_percent();
if (plate_list.get_plate(i)->is_slice_result_valid()) {
if (plate_list.get_plate(i)->is_slice_result_ready_for_print())
m_sel_plate_toolbar.m_items[i]->slice_state = IMToolbarItem::SliceState::SLICED;
@ -7058,6 +7151,46 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar() const
m_sel_plate_toolbar.m_items[i]->slice_state = IMToolbarItem::SliceState::SLICING;
}
}
if (m_sel_plate_toolbar.show_stats_item) {
all_plates_stats_item->percent = 0.0f;
size_t sliced_plates_cnt = 0;
bool slice_failed = false;
for (auto plate : plate_list.get_nonempty_plate_list()) {
if (plate->is_slice_result_valid() && plate->is_slice_result_ready_for_print())
sliced_plates_cnt++;
if (plate->is_slice_result_valid() && !plate->is_slice_result_ready_for_print())
slice_failed = true;
}
all_plates_stats_item->percent = (float)(sliced_plates_cnt) / (float)(plate_list.get_nonempty_plate_list().size()) * 100.0f;
if (all_plates_stats_item->percent == 0.0f)
all_plates_stats_item->slice_state = IMToolbarItem::SliceState::UNSLICED;
else if (sliced_plates_cnt == plate_list.get_nonempty_plate_list().size())
all_plates_stats_item->slice_state = IMToolbarItem::SliceState::SLICED;
else if (all_plates_stats_item->percent < 100.0f)
all_plates_stats_item->slice_state = IMToolbarItem::SliceState::SLICING;
if (slice_failed)
all_plates_stats_item->slice_state = IMToolbarItem::SliceState::SLICE_FAILED;
// Changing parameters does not invalid all plates, need extra logic to validate
bool gcode_result_valid = true;
for (auto gcode_result : plate_list.get_nonempty_plates_slice_results()) {
if (gcode_result->moves.size() == 0) {
gcode_result_valid = false;
}
}
if (all_plates_stats_item->selected && all_plates_stats_item->slice_state == IMToolbarItem::SliceState::SLICED && gcode_result_valid) {
m_gcode_viewer.render_all_plates_stats(plate_list.get_nonempty_plates_slice_results());
m_render_preview = false;
}
else{
m_gcode_viewer.render_all_plates_stats(plate_list.get_nonempty_plates_slice_results(), false);
m_render_preview = true;
}
}else
m_render_preview = true;
// places the toolbar on the top_left corner of the 3d scene
#if ENABLE_RETINA_GL
@ -7081,7 +7214,7 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar() const
float button_margin = frame_padding;
ImGuiWrapper& imgui = *wxGetApp().imgui();
int item_count = m_sel_plate_toolbar.m_items.size();
int item_count = m_sel_plate_toolbar.m_items.size() + (m_sel_plate_toolbar.show_stats_item ? 1 : 0);
bool show_scroll = item_count * (button_height + frame_padding * 2.0f + button_margin) - button_margin + 22.0f * f_scale > canvas_h ? true: false;
show_scroll = m_sel_plate_toolbar.is_display_scrollbar && show_scroll;
float window_height = std::min(item_count * (button_height + (frame_padding + margin_size) * 2.0f + button_margin) - button_margin + 28.0f * f_scale, canvas_h);
@ -7122,7 +7255,89 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar() const
ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint
ImVec2 margin = ImVec2(margin_size, margin_size);
for (int i = 0; i < item_count; i++) {
if(m_sel_plate_toolbar.show_stats_item)
{
// draw image
ImVec2 button_start_pos = ImGui::GetCursorScreenPos();
if (all_plates_stats_item->selected) {
ImGui::PushStyleColor(ImGuiCol_Button, button_active);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, button_active);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, button_active);
}
else {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(128.0f, 128.0f, 128.0f, 0.0f));
if (all_plates_stats_item->slice_state == IMToolbarItem::SliceState::SLICE_FAILED) {
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::GetStyleColorVec4(ImGuiCol_Button));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_Button));
}
else {
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, button_hover);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.42f, 0.42f, 0.42f, 1.0f));
}
}
ImVec4 text_clr;
ImTextureID btn_texture_id;
if (all_plates_stats_item->slice_state == IMToolbarItem::SliceState::UNSLICED || all_plates_stats_item->slice_state == IMToolbarItem::SliceState::SLICING || all_plates_stats_item->slice_state == IMToolbarItem::SliceState::SLICE_FAILED)
{
text_clr = ImVec4(0, 174.0f / 255.0f, 66.0f / 255.0f, 0.2f);
btn_texture_id = (ImTextureID)(intptr_t)(all_plates_stats_item->image_texture_transparent.get_id());
}
else
{
text_clr = ImVec4(0, 174.0f / 255.0f, 66.0f / 255.0f, 1);
btn_texture_id = (ImTextureID)(intptr_t)(all_plates_stats_item->image_texture.get_id());
}
if (ImGui::ImageButton2(btn_texture_id, size, {0,0}, {1,1}, frame_padding, bg_col, tint_col, margin)) {
if (all_plates_stats_item->slice_state != IMToolbarItem::SliceState::SLICE_FAILED) {
if (m_process && !m_process->running()) {
for (int i = 0; i < m_sel_plate_toolbar.m_items.size(); i++) {
m_sel_plate_toolbar.m_items[i]->selected = false;
}
all_plates_stats_item->selected = true;
wxCommandEvent evt = wxCommandEvent(EVT_GLTOOLBAR_SLICE_ALL);
wxPostEvent(wxGetApp().plater(), evt);
}
}
}
ImGui::PopStyleColor(3);
ImVec2 start_pos = ImVec2(button_start_pos.x + frame_padding + margin.x, button_start_pos.y + frame_padding + margin.y);
if (all_plates_stats_item->slice_state == IMToolbarItem::SliceState::UNSLICED) {
ImVec2 size = ImVec2(button_width, button_height);
ImVec2 end_pos = ImVec2(start_pos.x + size.x, start_pos.y + size.y);
ImGui::GetForegroundDrawList()->AddRectFilled(start_pos, end_pos, IM_COL32(0, 0, 0, 80));
}
else if (all_plates_stats_item->slice_state == IMToolbarItem::SliceState::SLICING) {
ImVec2 size = ImVec2(button_width, button_height * all_plates_stats_item->percent / 100.0f);
ImVec2 rect_start_pos = ImVec2(start_pos.x, start_pos.y + size.y);
ImVec2 rect_end_pos = ImVec2(start_pos.x + button_width, start_pos.y + button_height);
ImGui::GetForegroundDrawList()->AddRectFilled(rect_start_pos, rect_end_pos, IM_COL32(0, 0, 0, 80));
}
else if (all_plates_stats_item->slice_state == IMToolbarItem::SliceState::SLICE_FAILED) {
ImVec2 size = ImVec2(button_width, button_height);
ImVec2 end_pos = ImVec2(start_pos.x + size.x, start_pos.y + size.y);
ImGui::GetForegroundDrawList()->AddRectFilled(start_pos, end_pos, IM_COL32(40, 1, 1, 64));
ImGui::GetForegroundDrawList()->AddRect(start_pos, end_pos, IM_COL32(208, 27, 27, 255), 0.0f, 0, 1.0f);
}
// draw text
GImGui->FontSize = 15.0f;
ImGui::PushStyleColor(ImGuiCol_Text, text_clr);
ImVec2 text_size = ImGui::CalcTextSize(("All Plates"));
ImVec2 text_start_pos = ImVec2(start_pos.x + (button_width - text_size.x) / 2, start_pos.y + 3.0f * button_height / 5.0f);
ImGui::RenderText(text_start_pos, ("All Plates"));
text_size = ImGui::CalcTextSize(("Stats"));
text_start_pos = ImVec2(start_pos.x + (button_width - text_size.x) / 2, text_start_pos.y + ImGui::GetTextLineHeight());
ImGui::RenderText(text_start_pos, ("Stats"));
ImGui::PopStyleColor();
ImGui::SetWindowFontScale(1.2f);
}
for (int i = 0; i < m_sel_plate_toolbar.m_items.size(); i++) {
IMToolbarItem* item = m_sel_plate_toolbar.m_items[i];
// draw image
@ -7134,11 +7349,16 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar() const
if (item->selected) {
ImGui::PushStyleColor(ImGuiCol_Button, button_active);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, button_active);
} else
}
else {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(128.0f, 128.0f, 128.0f, 0.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.42f, 0.42f, 0.42f, 1.0f));
}
if (ImGui::ImageButton2(item->texture_id, size, uv0, uv1, frame_padding, bg_col, tint_col, margin)) {
if (m_process && !m_process->running()) {
all_plates_stats_item->selected = false;
item->selected = true;
// begin to slicing plate
wxCommandEvent* evt = new wxCommandEvent(EVT_GLTOOLBAR_SELECT_SLICED_PLATE);
evt->SetInt(i);
@ -7146,10 +7366,7 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar() const
}
}
if (item->selected)
ImGui::PopStyleColor(2);
else
ImGui::PopStyleColor(1);
ImGui::PopStyleColor(2);
ImVec2 start_pos = ImVec2(button_start_pos.x + frame_padding + margin.x, button_start_pos.y + frame_padding + margin.y);
if (item->slice_state == IMToolbarItem::SliceState::UNSLICED) {
@ -7341,60 +7558,59 @@ void GLCanvas3D::_render_paint_toolbar() const
float f_scale = 1.0;
#endif
std::vector<std::string> colors = wxGetApp().plater()->get_extruder_colors_from_plater_config();
ImGuiWrapper& imgui = *wxGetApp().imgui();
auto canvas_w = float(get_canvas_size().get_width());
int extruder_num = colors.size();
std::vector<std::string> filament_text_first_line;
std::vector<std::string> filament_text_second_line;
{
auto preset_bundle = wxGetApp().preset_bundle;
for (auto filament_name : preset_bundle->filament_presets) {
for (auto iter = preset_bundle->filaments.lbegin(); iter != preset_bundle->filaments.end(); iter++) {
if (filament_name.compare(iter->name) == 0) {
std::string display_filament_type;
iter->config.get_filament_type(display_filament_type);
auto pos = display_filament_type.find(' ');
if (pos != std::string::npos) {
filament_text_first_line.push_back(display_filament_type.substr(0, pos));
filament_text_second_line.push_back(display_filament_type.substr(pos + 1));
}
else {
filament_text_first_line.push_back(display_filament_type);
filament_text_second_line.push_back("");
}
}
}
}
}
ImGuiWrapper& imgui = *wxGetApp().imgui();
float canvas_w = float(get_canvas_size().get_width());
int item_spacing = 8 * wxGetApp().toolbar_icon_scale() * f_scale;
float button_size = GLToolbar::Default_Icons_Size * f_scale * wxGetApp().toolbar_icon_scale() + item_spacing;
std::vector<std::string> filament_types;
{
auto preset_bundle = wxGetApp().preset_bundle;
for (auto filament_name : preset_bundle->filament_presets) {
for (auto iter = preset_bundle->filaments.lbegin(); iter != preset_bundle->filaments.end(); iter++) {
if (filament_name.compare(iter->name) == 0) {
std::string display_filament_type;
iter->config.get_filament_type(display_filament_type);
filament_types.push_back(display_filament_type);
}
}
}
}
float button_size = GLToolbar::Default_Icons_Size * f_scale * wxGetApp().toolbar_icon_scale() + item_spacing;
imgui.set_next_window_pos(0.5f * (canvas_w + (button_size + item_spacing) * extruder_num), button_size + item_spacing * 2, ImGuiCond_Always, 1.0f, 1.0f);
imgui.set_next_window_pos(0.5f * canvas_w, 0, ImGuiCond_Always, 0.5f, 0.0f);
imgui.begin(_L("Paint Toolbar"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar);
bool disabled = !wxGetApp().plater()->can_fillcolor();
unsigned char rgb[3];
float cursor_y = ImGui::GetCursorPosY();
float max_text = 0;
for (int i = 0; i < extruder_num; i++) {
std::string item_text = (boost::format("%1%%2%") % (i + 1) % filament_types[i]).str();
ImVec2 label_size = ImGui::CalcTextSize(item_text.c_str(), NULL, true);
if (label_size.x > button_size)
label_size.x = button_size * 0.6;
max_text = std::max(max_text,label_size.x);
}
for (int i = 0; i < extruder_num; i++) {
if (filament_types.size() <= i) continue;
ImGui::SameLine(item_spacing / 2 + (button_size - max_text) / 2 + (button_size + item_spacing) * i);
ImGui::PushID(i);
if (i > 0)
ImGui::SameLine(0.0f, item_spacing);
Slic3r::GUI::BitmapCache::parse_color(colors[i], rgb);
ImGui::PushStyleColor(ImGuiCol_Button, ImColor(rgb[0], rgb[1], rgb[2]).Value);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImColor(rgb[0], rgb[1], rgb[2]).Value);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImColor(rgb[0], rgb[1], rgb[2]).Value);
if (disabled)
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
if (ImGui::Button("", ImVec2(button_size, button_size))) {
if (ImGui::Button(("##filament_button" + std::to_string(i)).c_str(), ImVec2(button_size, button_size))) {
wxPostEvent(m_canvas, IntEvent(EVT_GLTOOLBAR_FILLCOLOR, i + 1));
}
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 20.0f);
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.00f, 1.00f, 1.00f, 1.00f));
ImGui::TextUnformatted(_L((boost::format("Shortcut key %1%") % (i + 1)).str()).ToUTF8().data());
ImGui::TextUnformatted((boost::format(_u8L("Shortcut key %1%")) % (i + 1)).str().c_str());
ImGui::PopStyleColor(1);
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
@ -7402,82 +7618,30 @@ void GLCanvas3D::_render_paint_toolbar() const
ImGui::PopStyleColor(3);
if (disabled)
ImGui::PopItemFlag();
ImGui::PopID();
}
float text_offset_y = 3.0f * f_scale;
for (int i = 0; i < extruder_num; i++){
if (filament_types.size() <= i) continue;
//TODO use filament type from filament management, current use PLA by default
std::string item_text = (boost::format("%1%%2%") % (i + 1) % filament_types[i]).str();
const ImVec2 label_size = ImGui::CalcTextSize(item_text.c_str(), NULL, true);
int len = strlen(filament_types[i].c_str());
ImGui::SameLine(item_spacing / 2 + (button_size - max_text) / 2 + (button_size + item_spacing) * i);
int count = 0;
if (label_size.x > button_size)
{
for (int j = 0; j < filament_types[i].size(); j++)
{
if(std::isalpha(filament_types[i][j]))
count++;
else
break;
}
}
if (i > 8)
{
if (label_size.x > button_size)
{
if(count * ImGui::GetFontSize() > button_size){
if ((len - (count + 1)) <= 3)
item_text = "\t" + std::to_string(i + 1) + "\n" + filament_types[i].substr(0, count) + "\n" + "\t" + filament_types[i].substr(count, len);
else
item_text = "\t" + std::to_string(i + 1) + "\n" + filament_types[i].substr(0, count + 1) + "\n"+ filament_types[i].substr(count + 1, len);
} else {
if (count <= 4)
item_text = "\t" + std::to_string(i + 1) + "\n" + " " + filament_types[i].substr(0, count + 1) + "\n" + filament_types[i].substr(count + 1, len);
else
item_text = "\t" + std::to_string(i + 1) + "\n" + filament_types[i].substr(0, count + 1) + "\n" + filament_types[i].substr(count + 1, len);
}
}
else
{
item_text = (boost::format("\t%1%\n %2%") % (i + 1) % filament_types[i]).str();
}
}
else
{
if (label_size.x > button_size)
{
if(count * ImGui::GetFontSize() > button_size){
if ((len - (count + 1)) <= 3)
item_text = "\t " + std::to_string(i + 1) + "\n" + filament_types[i].substr(0, count) + "\n" + "\t" + filament_types[i].substr(count, len);
else
item_text = "\t " + std::to_string(i + 1) + "\n" + filament_types[i].substr(0, count + 1) + "\n"+ filament_types[i].substr(count + 1, len);
} else {
if (count <= 4)
item_text = "\t " + std::to_string(i + 1) + "\n" + " " + filament_types[i].substr(0, count + 1) + "\n" + filament_types[i].substr(count + 1, len);
else
item_text = "\t " + std::to_string(i + 1) + "\n" + filament_types[i].substr(0, count + 1) + "\n" + filament_types[i].substr(count + 1, len);
}
}
else
{
item_text = (boost::format("\t %1%\n\t%2%") % (i + 1) % filament_types[i]).str();
}
}
Slic3r::GUI::BitmapCache::parse_color(colors[i], rgb);
float gray = 0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2];
ImVec4 text_color = gray < 80 ? ImVec4(255, 255, 255, 255) : ImVec4(0, 0, 0, 255);
ImVec2 number_label_size = ImGui::CalcTextSize(std::to_string(i + 1).c_str());
ImGui::SetCursorPosY(cursor_y + text_offset_y);
ImGui::SetCursorPosX(item_spacing + i * (item_spacing + button_size) + (button_size - number_label_size.x) / 2);
ImGui::TextColored(text_color, std::to_string(i + 1).c_str());
if (gray < 80){
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 1.0f), item_text.c_str());
} else{
ImGui::TextColored(ImVec4(0.0f, 0.0f, 0.0f, 1.0f), item_text.c_str());
}
ImVec2 filament_first_line_label_size = ImGui::CalcTextSize(filament_text_first_line[i].c_str());
ImGui::SetCursorPosY(cursor_y + text_offset_y + number_label_size.y);
ImGui::SetCursorPosX(item_spacing + i * (item_spacing + button_size) + (button_size - filament_first_line_label_size.x) / 2);
ImGui::TextColored(text_color, filament_text_first_line[i].c_str());
ImVec2 filament_second_line_label_size = ImGui::CalcTextSize(filament_text_second_line[i].c_str());
ImGui::SetCursorPosY(cursor_y + text_offset_y + number_label_size.y + filament_first_line_label_size.y);
ImGui::SetCursorPosX(item_spacing + i * (item_spacing + button_size) + (button_size - filament_second_line_label_size.x) / 2);
ImGui::TextColored(text_color, filament_text_second_line[i].c_str());
}
ImGui::AlignTextToFramePadding();
imgui.end();
}

View file

@ -543,6 +543,7 @@ private:
bool m_dirty;
bool m_initialized;
//BBS: add flag to controll rendering
bool m_render_preview{ true };
bool m_enable_render { true };
bool m_apply_zoom_to_volumes_filter;
bool m_picking_enabled;
@ -785,6 +786,8 @@ public:
void enable_selection(bool enable);
void enable_main_toolbar(bool enable);
//BBS: GUI refactor: GLToolbar
void _update_select_plate_toolbar_stats_item(bool force_selected = false);
void reset_select_plate_toolbar_selection();
void enable_select_plate_toolbar(bool enable);
void enable_assemble_view_toolbar(bool enable);
void enable_return_toolbar(bool enable);
@ -847,6 +850,7 @@ public:
void select_all();
void deselect_all();
void set_selected_visible(bool visible);
void delete_selected();
void ensure_on_bed(unsigned int object_idx, bool allow_negative_z);
@ -893,6 +897,7 @@ public:
void on_gesture(wxGestureEvent& evt);
void on_paint(wxPaintEvent& evt);
void on_set_focus(wxFocusEvent& evt);
void force_set_focus();
Size get_canvas_size() const;
Vec2d get_local_mouse_position() const;
@ -1041,6 +1046,8 @@ public:
// If the Z screen space coordinate is not provided, a depth buffer value is substituted.
Vec3d _mouse_to_3d(const Point& mouse_pos, float* z = nullptr);
bool make_current_for_postinit();
private:
bool _is_shown_on_screen() const;
@ -1093,7 +1100,7 @@ private:
void _render_current_gizmo() const;
void _render_gizmos_overlay();
void _render_main_toolbar();
void _render_imgui_select_plate_toolbar() const;
void _render_imgui_select_plate_toolbar();
void _render_assemble_view_toolbar() const;
void _render_return_toolbar() const;
void _render_separator_toolbar_right() const;

View file

@ -584,12 +584,7 @@ bool GLTexture::generate_texture_from_text(const std::string& text_str, wxFont&
// draw message
memDC.SetTextForeground(*wxWHITE);
wxGCDC dc2(memDC);
dc2.SetFont(font);
dc2.SetBackground(wxBrush(background));
dc2.SetTextForeground(*wxWHITE);
dc2.DrawLabel(msg, wxRect(0, 0, m_width, m_height), wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
memDC.DrawLabel(msg, wxRect(0, 0, m_width, m_height), wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
memDC.SelectObject(wxNullBitmap);

View file

@ -39,6 +39,7 @@
#include <wx/textctrl.h>
#include <wx/splash.h>
#include <wx/fontutil.h>
#include <wx/glcanvas.h>
#include "libslic3r/Utils.hpp"
#include "libslic3r/Model.hpp"
@ -86,8 +87,8 @@
//BBS: DailyTip and UserGuide Dialog
#include "WebDownPluginDlg.hpp"
#include "WebGuideDialog.hpp"
#include "WebUserLoginDialog.hpp"
#include "ReleaseNote.hpp"
#include "PrivacyUpdateDialog.hpp"
#include "ModelMall.hpp"
//#ifdef WIN32
@ -1016,25 +1017,31 @@ void GUI_App::post_init()
mainframe->select_tab(size_t(MainFrame::tp3DEditor));
plater_->select_view_3D("3D");
//BBS init the opengl resource here
Size canvas_size = plater_->canvas3D()->get_canvas_size();
wxGetApp().imgui()->set_display_size(static_cast<float>(canvas_size.get_width()), static_cast<float>(canvas_size.get_height()));
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", start to init opengl";
wxGetApp().init_opengl();
#ifdef __linux__
if (plater_->canvas3D()->get_wxglcanvas()->IsShownOnScreen()&&plater_->canvas3D()->make_current_for_postinit()) {
#endif
Size canvas_size = plater_->canvas3D()->get_canvas_size();
wxGetApp().imgui()->set_display_size(static_cast<float>(canvas_size.get_width()), static_cast<float>(canvas_size.get_height()));
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", start to init opengl";
wxGetApp().init_opengl();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", finished init opengl";
plater_->canvas3D()->init();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", finished init opengl";
plater_->canvas3D()->init();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", finished init canvas3D";
wxGetApp().imgui()->new_frame();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", finished init canvas3D";
wxGetApp().imgui()->new_frame();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", finished init imgui frame";
plater_->canvas3D()->enable_render(true);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", finished init imgui frame";
plater_->canvas3D()->enable_render(true);
if (!slow_bootup) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", start to render a first frame for test";
plater_->canvas3D()->render(false);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", finished rendering a first frame for test";
if (!slow_bootup) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", start to render a first frame for test";
plater_->canvas3D()->render(false);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", finished rendering a first frame for test";
}
#ifdef __linux__
}
#endif
if (is_editor())
mainframe->select_tab(size_t(0));
mainframe->Thaw();
@ -1119,6 +1126,9 @@ void GUI_App::post_init()
//BBS: check new version
this->check_new_version_sf();
//BBS: check privacy version
if (is_user_login())
this->check_privacy_version(0);
});
}
@ -1155,6 +1165,9 @@ void GUI_App::post_init()
std::string functional_config_file = Slic3r::resources_dir() + "/config.json";
DeviceManager::load_functional_config(encode_path(functional_config_file.c_str()));
std::string filaments_blacklist_config_file = Slic3r::resources_dir() + "/printers/filaments_blacklist.json";
DeviceManager::load_filaments_blacklist_config(encode_path(filaments_blacklist_config_file.c_str()));
// remove old log files over LOG_FILES_MAX_NUM
std::string log_addr = data_dir();
if (!log_addr.empty()) {
@ -1228,6 +1241,13 @@ void GUI_App::shutdown()
removable_drive_manager()->shutdown();
}
// destroy login dialog
if (login_dlg != nullptr) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(": destroy login dialog");
delete login_dlg;
login_dlg = nullptr;
}
if (m_is_recreating_gui) return;
m_is_closing = true;
stop_sync_user_preset();
@ -1646,9 +1666,9 @@ void GUI_App::init_networking_callbacks()
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(": enter, m_agent=%1%")%m_agent;
if (m_agent) {
//set callbacks
m_agent->set_on_user_login_fn([this](int online_login, bool login) {
GUI::wxGetApp().request_user_login(online_login);
});
//m_agent->set_on_user_login_fn([this](int online_login, bool login) {
// GUI::wxGetApp().request_user_handle(online_login);
// });
m_agent->set_on_server_connected_fn([this]() {
if (m_is_closing) {
@ -1708,6 +1728,7 @@ void GUI_App::init_networking_callbacks()
event.SetString(obj->dev_id);
} else if (state == ConnectStatus::ConnectStatusFailed) {
obj->set_access_code("");
obj->set_user_access_code("");
m_device_manager->set_selected_machine("");
wxString text;
if (msg == "5") {
@ -1748,8 +1769,9 @@ void GUI_App::init_networking_callbacks()
obj->is_ams_need_update = false;
obj->parse_json(msg);
if (this->m_device_manager->get_selected_machine() == obj && obj->is_ams_need_update) {
GUI::wxGetApp().sidebar().load_ams_list(obj->amsList);
auto sel = this->m_device_manager->get_selected_machine();
if ((sel == obj || sel == nullptr) && obj->is_ams_need_update) {
GUI::wxGetApp().sidebar().load_ams_list(obj->dev_id, obj->amsList);
}
}
});
@ -1773,7 +1795,7 @@ void GUI_App::init_networking_callbacks()
if (obj) {
obj->parse_json(msg);
if (this->m_device_manager->get_selected_machine() == obj && obj->is_ams_need_update) {
GUI::wxGetApp().sidebar().load_ams_list(obj->amsList);
GUI::wxGetApp().sidebar().load_ams_list(obj->dev_id, obj->amsList);
}
}
});
@ -1856,10 +1878,8 @@ void GUI_App::init_download_path()
fs::path dp(down_path);
if (!fs::exists(dp)) {
if (!fs::create_directory(dp)) {
std::string user_down_path = wxStandardPaths::Get().GetUserDir(wxStandardPaths::Dir_Downloads).ToUTF8().data();
app_config->set("download_path", user_down_path);
}
std::string user_down_path = wxStandardPaths::Get().GetUserDir(wxStandardPaths::Dir_Downloads).ToUTF8().data();
app_config->set("download_path", user_down_path);
}
}
}
@ -1978,6 +1998,14 @@ void GUI_App::init_http_extra_header()
m_agent->set_extra_http_header(extra_headers);
}
void GUI_App::update_http_extra_header()
{
std::map<std::string, std::string> extra_headers = get_extra_header();
Slic3r::Http::set_extra_headers(extra_headers);
if (m_agent)
m_agent->set_extra_http_header(extra_headers);
}
std::string GUI_App::get_local_models_path()
{
std::string local_path = "";
@ -2064,7 +2092,7 @@ bool GUI_App::on_init_inner()
}
}
for (auto d : dialogStack)
d->EndModal(wxID_CANCEL);
d->EndModal(wxID_ABORT);
});
std::map<std::string, std::string> extra_headers = get_extra_header();
@ -2123,7 +2151,7 @@ bool GUI_App::on_init_inner()
#endif // __APPLE__
bool init_dark_color_mode = app_config->get("dark_color_mode") == "1";
bool init_dark_color_mode = dark_mode();
bool init_sys_menu_enabled = app_config->get("sys_menu_enabled") == "1";
#ifdef __WINDOWS__
NppDarkMode::InitDarkMode(init_dark_color_mode, init_sys_menu_enabled);
@ -2138,7 +2166,7 @@ bool GUI_App::on_init_inner()
#ifdef _MSW_DARK_MODE
// app_config can be updated in check_older_app_config(), so check if dark_color_mode and sys_menu_enabled was changed
if (bool new_dark_color_mode = app_config->get("dark_color_mode") == "1";
if (bool new_dark_color_mode = dark_mode();
init_dark_color_mode != new_dark_color_mode) {
#ifdef __WINDOWS__
@ -2325,7 +2353,13 @@ bool GUI_App::on_init_inner()
// Suppress the '- default -' presets.
preset_bundle->set_default_suppressed(true);
Bind(EVT_SET_SELECTED_MACHINE, &GUI_App::on_set_selected_machine, this);
Bind(EVT_USER_LOGIN, &GUI_App::on_user_login, this);
Bind(EVT_USER_LOGIN_HANDLE, &GUI_App::on_user_login_handle, this);
Bind(EVT_CHECK_PRIVACY_VER, &GUI_App::on_check_privacy_update, this);
Bind(EVT_CHECK_PRIVACY_SHOW, &GUI_App::show_check_privacy_dlg, this);
Bind(EVT_SHOW_IP_DIALOG, &GUI_App::show_ip_address_enter_dialog_handler, this);
Bind(EVT_SHOW_IP_DIALOG, &GUI_App::show_ip_address_enter_dialog_handler, this);
@ -2334,6 +2368,8 @@ bool GUI_App::on_init_inner()
if (m_agent && m_agent->is_user_login()) {
enable_user_preset_folder(true);
} else {
enable_user_preset_folder(false);
}
// BBS if load user preset failed
@ -2349,13 +2385,11 @@ bool GUI_App::on_init_inner()
}
//}
if (app_config->get("sync_user_preset") == "true") {
//BBS loading user preset
BOOST_LOG_TRIVIAL(info) << "Loading user presets...";
scrn->SetText(_L("Loading user presets..."));
// Always async, not such startup step
//BOOST_LOG_TRIVIAL(info) << "Loading user presets...";
//scrn->SetText(_L("Loading user presets..."));
if (m_agent) {
start_sync_user_preset();
}
@ -2798,6 +2832,7 @@ void GUI_App::UpdateDarkUIWin(wxWindow* win)
void GUI_App::Update_dark_mode_flag()
{
m_is_dark_mode = dark_mode();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": switch the current dark mode status to %1% ")%m_is_dark_mode;
}
void GUI_App::UpdateDlgDarkUI(wxDialog* dlg)
@ -3075,6 +3110,11 @@ void GUI_App::ShowUserGuide() {
void GUI_App::ShowDownNetPluginDlg() {
try {
auto iter = std::find_if(dialogStack.begin(), dialogStack.end(), [](auto dialog) {
return dynamic_cast<DownloadProgressDialog *>(dialog) != nullptr;
});
if (iter != dialogStack.end())
return;
DownloadProgressDialog dlg(_L("Downloading Bambu Network Plug-in"));
dlg.ShowModal();
} catch (std::exception &e) {
@ -3082,14 +3122,24 @@ void GUI_App::ShowDownNetPluginDlg() {
}
}
void GUI_App::ShowUserLogin()
void GUI_App::ShowUserLogin(bool show)
{
// BBS: User Login Dialog
try {
ZUserLogin LoginDlg;
LoginDlg.ShowModal();
} catch (std::exception &e) {
// wxMessageBox(e.what(), "", MB_OK);
if (show) {
try {
if (!login_dlg)
login_dlg = new ZUserLogin();
else {
delete login_dlg;
login_dlg = new ZUserLogin();
}
login_dlg->ShowModal();
} catch (std::exception &e) {
;
}
} else {
if (login_dlg)
login_dlg->EndModal(wxID_OK);
}
}
@ -3168,7 +3218,7 @@ void GUI_App::force_colors_update()
{
#ifdef _MSW_DARK_MODE
#ifdef __WINDOWS__
NppDarkMode::SetDarkMode(app_config->get("dark_color_mode") == "1");
NppDarkMode::SetDarkMode(dark_mode());
if (WXHWND wxHWND = wxToolTip::GetToolTipCtrl())
NppDarkMode::SetDarkExplorerTheme((HWND)wxHWND);
NppDarkMode::SetDarkTitleBar(mainframe->GetHWND());
@ -3321,6 +3371,13 @@ bool GUI_App::check_login()
return result;
}
void GUI_App::request_user_handle(int online_login)
{
auto evt = new wxCommandEvent(EVT_USER_LOGIN_HANDLE);
evt->SetInt(online_login);
wxQueueEvent(this, evt);
}
void GUI_App::request_user_login(int online_login)
{
auto evt = new wxCommandEvent(EVT_USER_LOGIN);
@ -3339,12 +3396,9 @@ void GUI_App::request_user_logout()
m_agent->user_logout();
m_agent->set_user_selected_machine("");
BOOST_LOG_TRIVIAL(info) << "preset_folder: set to empty, user_logout";
enable_user_preset_folder(false);
/* delete old user settings */
m_device_manager->clean_user_info();
GUI::wxGetApp().sidebar().load_ams_list({});
GUI::wxGetApp().remove_user_presets();
GUI::wxGetApp().sidebar().load_ams_list({}, {});
GUI::wxGetApp().stop_sync_user_preset();
#ifdef __WINDOWS__
@ -3375,11 +3429,6 @@ std::string GUI_App::handle_web_request(std::string cmd)
std::string web_cmd = j["command"].get<std::string>();
if (web_cmd == "request_model_download") {
/* json j_data = j["data"];
json import_j;*/
/* import_j["model_id"] = j["data"]["model_id"].get<std::string>();
import_j["profile_id"] = j["data"]["profile_id"].get<std::string>();*/
std::string download_url = "";
if (j["data"].contains("download_url"))
download_url = j["data"]["download_url"].get<std::string>();
@ -3575,7 +3624,7 @@ void GUI_App::request_model_download(std::string url, std::string filename)
if (!check_login()) return;
if (plater_) {
plater_->request_model_download(url, filename);
plater_->request_model_download();
}
}
@ -3685,7 +3734,15 @@ void GUI_App::enable_user_preset_folder(bool enable)
}
}
void GUI_App::on_user_login(wxCommandEvent &evt)
void GUI_App::on_set_selected_machine(wxCommandEvent &evt)
{
DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager();
if (!dev || m_agent) return;
dev->set_selected_machine(m_agent->get_user_selected_machine());
}
void GUI_App::on_user_login_handle(wxCommandEvent &evt)
{
if (!m_agent) { return; }
@ -3695,8 +3752,12 @@ void GUI_App::on_user_login(wxCommandEvent &evt)
// get machine list
DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager();
if (!dev) return;
dev->update_user_machine_list_info();
dev->set_selected_machine(m_agent->get_user_selected_machine());
boost::thread update_thread = boost::thread([this, dev] {
dev->update_user_machine_list_info();
auto evt = new wxCommandEvent(EVT_SET_SELECTED_MACHINE);
wxQueueEvent(this, evt);
});
// if (app_config->get("sync_user_preset") == "true") {
enable_user_preset_folder(true);
@ -3723,6 +3784,14 @@ void GUI_App::on_user_login(wxCommandEvent &evt)
}
}
void GUI_App::on_user_login(wxCommandEvent &evt)
{
if (!m_agent) { return; }
int online_login = evt.GetInt();
// check privacy before handle
check_privacy_version(online_login);
}
bool GUI_App::is_studio_active()
{
auto curr_time = std::chrono::system_clock::now();
@ -3947,6 +4016,113 @@ void GUI_App::set_skip_version(bool skip)
}
}
void GUI_App::show_check_privacy_dlg(wxCommandEvent& evt)
{
int online_login = evt.GetInt();
PrivacyUpdateDialog privacy_dlg(this->mainframe, wxID_ANY, _L("Privacy Policy Update"));
privacy_dlg.Bind(EVT_PRIVACY_UPDATE_CONFIRM, [this, online_login](wxCommandEvent &e) {
app_config->set("privacy_version", privacy_version_info.version_str);
app_config->set_bool("privacy_update_checked", true);
app_config->save();
request_user_handle(online_login);
});
privacy_dlg.Bind(EVT_PRIVACY_UPDATE_CANCEL, [this](wxCommandEvent &e) {
app_config->set_bool("privacy_update_checked", false);
app_config->save();
if (m_agent) {
m_agent->user_logout();
}
});
privacy_dlg.set_text(privacy_version_info.description);
privacy_dlg.on_show();
}
void GUI_App::on_show_check_privacy_dlg(int online_login)
{
auto evt = new wxCommandEvent(EVT_CHECK_PRIVACY_SHOW);
evt->SetInt(online_login);
wxQueueEvent(this, evt);
}
bool GUI_App::check_privacy_update()
{
if (privacy_version_info.version_str.empty() || privacy_version_info.description.empty()
|| privacy_version_info.url.empty()) {
return false;
}
std::string local_privacy_ver = app_config->get("privacy_version");
auto curr_version = Semver::parse(local_privacy_ver);
auto remote_version = Semver::parse(privacy_version_info.version_str);
if (curr_version && remote_version) {
if (*remote_version > *curr_version || app_config->get("privacy_update_checked") != "true") {
return true;
}
}
return false;
}
void GUI_App::on_check_privacy_update(wxCommandEvent& evt)
{
int online_login = evt.GetInt();
bool result = check_privacy_update();
if (result)
on_show_check_privacy_dlg(online_login);
else
request_user_handle(online_login);
}
void GUI_App::check_privacy_version(int online_login)
{
update_http_extra_header();
std::string query_params = "?policy/privacy=00.00.00.00";
std::string url = get_http_url(app_config->get_country_code()) + query_params;
Slic3r::Http http = Slic3r::Http::get(url);
http.header("accept", "application/json")
.timeout_connect(TIMEOUT_CONNECT)
.timeout_max(TIMEOUT_RESPONSE)
.on_complete([this, online_login](std::string body, unsigned) {
try {
json j = json::parse(body);
if (j.contains("message")) {
if (j["message"].get<std::string>() == "success") {
if (j.contains("resources")) {
for (auto it = j["resources"].begin(); it != j["resources"].end(); it++) {
if (it->contains("type")) {
if ((*it)["type"] == std::string("policy/privacy")
&& it->contains("version")
&& it->contains("description")
&& it->contains("url")
&& it->contains("force_update")) {
privacy_version_info.version_str = (*it)["version"].get<std::string>();
privacy_version_info.description = (*it)["description"].get<std::string>();
privacy_version_info.url = (*it)["url"].get<std::string>();
privacy_version_info.force_upgrade = (*it)["force_update"].get<bool>();
break;
}
}
}
CallAfter([this, online_login]() {
auto evt = new wxCommandEvent(EVT_CHECK_PRIVACY_VER);
evt->SetInt(online_login);
wxQueueEvent(this, evt);
});
}
}
}
}
catch (...) {
request_user_handle(online_login);
}
})
.on_error([this, online_login](std::string body, std::string error, unsigned int status) {
request_user_handle(online_login);
BOOST_LOG_TRIVIAL(error) << "check privacy version error" << body;
}).perform();
}
void GUI_App::no_new_version()
{
wxCommandEvent* evt = new wxCommandEvent(EVT_SHOW_NO_NEW_VERSION);
@ -3986,17 +4162,19 @@ void GUI_App::reload_settings()
m_agent->get_user_presets(&user_presets);
preset_bundle->load_user_presets(*app_config, user_presets, ForwardCompatibilitySubstitutionRule::Enable);
preset_bundle->save_user_presets(*app_config, get_delete_cache_presets());
mainframe->update_side_preset_ui();
}
}
//BBS reload when login
//BBS reload when logout
void GUI_App::remove_user_presets()
{
if (preset_bundle && m_agent) {
preset_bundle->remove_users_preset(*app_config);
std::string user_id = m_agent->get_user_id();
preset_bundle->remove_user_presets_directory(user_id);
// Not remove user preset cache
//std::string user_id = m_agent->get_user_id();
//preset_bundle->remove_user_presets_directory(user_id);
//update ui
mainframe->update_side_preset_ui();
@ -4110,9 +4288,9 @@ void GUI_App::sync_preset(Preset* preset)
}
}
void GUI_App::start_sync_user_preset(bool with_progress_dlg)
void GUI_App::start_sync_user_preset(bool load_immediately, bool with_progress_dlg)
{
if (!m_agent) return;
if (!m_agent || !m_agent->is_user_login()) return;
enable_user_preset_folder(true);
@ -4120,32 +4298,51 @@ void GUI_App::start_sync_user_preset(bool with_progress_dlg)
if (enable_sync)
return;
if (m_agent->is_user_login()) {
// get setting list, update setting list
std::string version = preset_bundle->get_vendor_profile_version(PresetBundle::BBL_BUNDLE).to_string();
if (with_progress_dlg) {
ProgressDialog dlg(_L("Loading"), "", 100, this->mainframe, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
dlg.Update(0, _L("Loading user preset"));
m_agent->get_setting_list(version,
[this, &dlg](int percent){
dlg.Update(percent, _L("Loading user preset"));
},
[this, &dlg]() {
dlg.GetValue();
bool cont = dlg.Update(dlg.GetValue(), _L("Loading user preset"));
return !cont;
});
} else {
m_agent->get_setting_list(version);
}
GUI::wxGetApp().reload_settings();
if (load_immediately) {
preset_bundle->load_user_presets(m_agent->get_user_id(), ForwardCompatibilitySubstitutionRule::Enable);
mainframe->update_side_preset_ui();
}
ProgressFn progressFn;
WasCancelledFn cancelFn;
std::function<void()> finishFn;
if (with_progress_dlg) {
auto dlg = new ProgressDialog(_L("Loading"), "", 100, this->mainframe, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
dlg->Update(0, _L("Loading user preset"));
progressFn = [this, dlg](int percent) {
CallAfter([=]{
dlg->Update(percent, _L("Loading user preset"));
});
};
cancelFn = [dlg]() {
return dlg->WasCanceled();
};
finishFn = [this, dlg] {
CallAfter([=]{
dlg->Destroy();
reload_settings();
});
};
}
else {
finishFn = [this] {
CallAfter([=] {
reload_settings();
});
};
}
BOOST_LOG_TRIVIAL(info) << "start_sync_service...";
//BBS
enable_sync = true;
m_sync_update_thread = Slic3r::create_thread(
[this] {
[this, progressFn, cancelFn, finishFn] {
// get setting list, update setting list
std::string version = preset_bundle->get_vendor_profile_version(PresetBundle::BBL_BUNDLE).to_string();
m_agent->get_setting_list(version, progressFn, cancelFn);
finishFn();
int count = 0, sync_count = 0;
std::vector<Preset> presets_to_sync;
while (enable_sync) {
@ -4182,12 +4379,13 @@ void GUI_App::start_sync_user_preset(bool with_progress_dlg)
unsigned int http_code = 200;
/* get list witch need to be deleted*/
std::vector<string>& delete_cache_presets = get_delete_cache_presets();
std::vector<string> delete_cache_presets = get_delete_cache_presets_lock();
for (auto it = delete_cache_presets.begin(); it != delete_cache_presets.end();) {
if ((*it).empty()) continue;
std::string del_setting_id = *it;
int result = m_agent->delete_setting(del_setting_id);
if (result == 0) {
preset_deleted_from_cloud(del_setting_id);
it = delete_cache_presets.erase(it);
BOOST_LOG_TRIVIAL(trace) << "sync_preset: sync operation: delete success! setting id = " << del_setting_id;
}
@ -4206,6 +4404,7 @@ void GUI_App::start_sync_user_preset(bool with_progress_dlg)
void GUI_App::stop_sync_user_preset()
{
remove_user_presets();
enable_user_preset_folder(false);
if (!enable_sync)
@ -4216,6 +4415,16 @@ void GUI_App::stop_sync_user_preset()
m_sync_update_thread.join();
}
void GUI_App::start_http_server()
{
if (!m_http_server.is_started())
m_http_server.start();
}
void GUI_App::stop_http_server()
{
m_http_server.stop();
}
bool GUI_App::switch_language()
{
if (select_language()) {
@ -4412,7 +4621,8 @@ bool GUI_App::load_language(wxString language, bool initial)
{"fr", wxString::FromUTF8("\x46\x72\x61\x6E\xC3\xA7\x61\x69\x73")},
{"it", wxString::FromUTF8("\x49\x74\x61\x6C\x69\x61\x6E\x6F")},
{"ru", wxString::FromUTF8("\xD1\x80\xD1\x83\xD1\x81\xD1\x81\xD0\xBA\xD0\xB8\xD0\xB9")},
{"hu", wxString::FromUTF8("Magyar")}
{"hu", wxString::FromUTF8("Magyar")},
{"ja", wxString::FromUTF8("\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E")}
};
for (auto l : language_descptions) {
const wxLanguageInfo *langinfo = wxLocale::FindLanguageInfo(l.first);
@ -4517,7 +4727,9 @@ bool GUI_App::load_language(wxString language, bool initial)
wxLANGUAGE_SPANISH,
wxLANGUAGE_SWEDISH,
wxLANGUAGE_DUTCH,
wxLANGUAGE_HUNGARIAN};
wxLANGUAGE_HUNGARIAN,
wxLANGUAGE_JAPANESE
};
std::string cur_language = app_config->get("language");
if (cur_language != "") {
//cleanup the language wrongly set before
@ -5027,7 +5239,12 @@ bool GUI_App::check_and_keep_current_preset_changes(const wxString& caption, con
static_cast<TabPrinter*>(tab)->cache_extruder_cnt();
}
}
tab->cache_config_diff(selected_options);
std::vector<std::string> selected_options2;
std::transform(selected_options.begin(), selected_options.end(), std::back_inserter(selected_options2), [](auto & o) {
auto i = o.find('#');
return i != std::string::npos ? o.substr(0, i) : o;
});
tab->cache_config_diff(selected_options2);
if (!is_called_from_configwizard)
tab->m_presets->discard_current_changes();
}
@ -5114,16 +5331,31 @@ void GUI_App::load_current_presets(bool active_preset_combox/*= false*/, bool ch
}
}
std::vector<std::string>& GUI_App::get_delete_cache_presets()
static std::mutex mutex_delete_cache_presets;
std::vector<std::string> & GUI_App::get_delete_cache_presets()
{
return need_delete_presets;
}
std::vector<std::string> GUI_App::get_delete_cache_presets_lock()
{
std::scoped_lock l(mutex_delete_cache_presets);
return need_delete_presets;
}
void GUI_App::delete_preset_from_cloud(std::string setting_id)
{
std::scoped_lock l(mutex_delete_cache_presets);
need_delete_presets.push_back(setting_id);
}
void GUI_App::preset_deleted_from_cloud(std::string setting_id)
{
std::scoped_lock l(mutex_delete_cache_presets);
need_delete_presets.erase(std::remove(need_delete_presets.begin(), need_delete_presets.end(), setting_id), need_delete_presets.end());
}
bool GUI_App::OnExceptionInMainLoop()
{
generic_exception_handle();

View file

@ -11,8 +11,10 @@
#include "slic3r/GUI/DeviceManager.hpp"
#include "slic3r/Utils/NetworkAgent.hpp"
#include "slic3r/GUI/WebViewDialog.hpp"
#include "slic3r/GUI/WebUserLoginDialog.hpp"
#include "slic3r/GUI/HMS.hpp"
#include "slic3r/GUI/Jobs/UpgradeNetworkJob.hpp"
#include "slic3r/GUI/HttpServer.hpp"
#include "../Utils/PrintHost.hpp"
#include <wx/app.h>
@ -274,7 +276,11 @@ private:
bool m_networking_cancel_update { false };
std::shared_ptr<UpgradeNetworkJob> m_upgrade_network_job;
// login widget
ZUserLogin* login_dlg { nullptr };
VersionInfo version_info;
VersionInfo privacy_version_info;
static std::string version_display;
HMSQuery *hms_query { nullptr };
@ -283,7 +289,9 @@ private:
bool m_is_dark_mode{ false };
bool m_adding_script_handler { false };
bool m_side_popup_status{false};
HttpServer m_http_server;
public:
void check_filaments_in_blacklist(std::string tag_supplier, std::string tag_material, bool& in_blacklist, std::string& action, std::string& info);
std::string get_local_models_path();
bool OnInit() override;
bool initialized() const { return m_initialized; }
@ -380,7 +388,7 @@ public:
wxString transition_tridid(int trid_id);
void ShowUserGuide();
void ShowDownNetPluginDlg();
void ShowUserLogin();
void ShowUserLogin(bool show = true);
void ShowOnlyFilament();
//BBS
void request_login(bool show_user_info = false);
@ -388,7 +396,8 @@ public:
void get_login_info();
bool is_user_login();
void request_user_login(int online_login);
void request_user_login(int online_login = 0);
void request_user_handle(int online_login = 0);
void request_user_logout();
int request_user_unbind(std::string dev_id);
std::string handle_web_request(std::string cmd);
@ -401,7 +410,9 @@ public:
void handle_http_error(unsigned int status, std::string body);
void on_http_error(wxCommandEvent &evt);
void on_set_selected_machine(wxCommandEvent& evt);
void on_user_login(wxCommandEvent &evt);
void on_user_login_handle(wxCommandEvent& evt);
void enable_user_preset_folder(bool enable);
// BBS
@ -422,8 +433,16 @@ public:
void reload_settings();
void remove_user_presets();
void sync_preset(Preset* preset);
void start_sync_user_preset(bool with_progress_dlg = false);
void start_sync_user_preset(bool load_immediately = false, bool with_progress_dlg = false);
void stop_sync_user_preset();
void start_http_server();
void stop_http_server();
void on_show_check_privacy_dlg(int online_login = 0);
void show_check_privacy_dlg(wxCommandEvent& evt);
void on_check_privacy_update(wxCommandEvent &evt);
bool check_privacy_update();
void check_privacy_version(int online_login = 0);
static bool catch_error(std::function<void()> cb, const std::string& err);
@ -457,8 +476,10 @@ public:
bool checked_tab(Tab* tab);
//BBS: add preset combox re-active logic
void load_current_presets(bool active_preset_combox = false, bool check_printer_presets = true);
std::vector<std::string>& get_delete_cache_presets();
std::vector<std::string> &get_delete_cache_presets();
std::vector<std::string> get_delete_cache_presets_lock();
void delete_preset_from_cloud(std::string setting_id);
void preset_deleted_from_cloud(std::string setting_id);
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".
@ -486,13 +507,21 @@ public:
Model& model();
NotificationManager * notification_manager();
std::string m_mall_model_download_url;
std::string m_mall_model_download_name;
ModelMallDialog* m_mall_home_dialog{ nullptr };
ModelMallDialog* m_mall_publish_dialog{ nullptr };
void set_download_model_url(std::string url) {m_mall_model_download_url = url;}
void set_download_model_name(std::string name) {m_mall_model_download_name = name;}
std::string get_download_model_url() {return m_mall_model_download_url;}
std::string get_download_model_name() {return m_mall_model_download_name;}
void load_url(wxString url);
void open_mall_page_dialog();
void open_publish_page_dialog();
void remove_mall_system_dialog();
void remove_mall_system_dialog();
void run_script(wxString js);
bool is_adding_script_handler() { return m_adding_script_handler; }
void set_adding_script_handler(bool status) { m_adding_script_handler = status; }
@ -574,6 +603,7 @@ private:
//BBS set extra header for http request
std::map<std::string, std::string> get_extra_header();
void init_http_extra_header();
void update_http_extra_header();
bool check_older_app_config(Semver current_version, bool backup);
void copy_older_config();
void window_pos_save(wxTopLevelWindow* window, const std::string &name);

View file

@ -432,6 +432,19 @@ std::vector<wxBitmap> MenuFactory::get_volume_bitmaps()
return volume_bmps;
}
void MenuFactory::append_menu_item_set_visible(wxMenu* menu)
{
bool has_one_shown = false;
const Selection& selection = plater()->canvas3D()->get_selection();
for (unsigned int i : selection.get_volume_idxs()) {
has_one_shown |= selection.get_volume(i)->visible;
}
append_menu_item(menu, wxID_ANY, has_one_shown ?_L("Hide") : _L("Show"), "",
[has_one_shown](wxCommandEvent&) { plater()->set_selected_visible(!has_one_shown); }, "", nullptr,
[]() { return true; }, m_parent);
}
void MenuFactory::append_menu_item_delete(wxMenu* menu)
{
#ifdef __WINDOWS__
@ -445,6 +458,19 @@ void MenuFactory::append_menu_item_delete(wxMenu* menu)
#endif
}
void MenuFactory::append_menu_item_edit_text(wxMenu *menu)
{
#ifdef __WINDOWS__
append_menu_item(
menu, wxID_ANY, _L("Edit Text"), "", [](wxCommandEvent &) { plater()->edit_text(); }, "", nullptr,
[]() { return plater()->can_edit_text(); }, m_parent);
#else
append_menu_item(
menu, wxID_ANY, _L("Edit Text"), "", [](wxCommandEvent &) { plater()->edit_text(); }, "", nullptr,
[]() { return plater()->can_edit_text(); }, m_parent);
#endif
}
wxMenu* MenuFactory::append_submenu_add_generic(wxMenu* menu, ModelVolumeType type) {
auto sub_menu = new wxMenu;
@ -735,6 +761,8 @@ void MenuFactory::append_menu_items_flush_options(wxMenu* menu)
bool show_flush_option_menu = false;
ObjectList* object_list = obj_list();
const Selection& selection = get_selection();
if (selection.get_object_idx() < 0)
return;
if (wxGetApp().plater()->get_partplate_list().get_curr_plate()->contains(selection.get_bounding_box())) {
auto plate_extruders = wxGetApp().plater()->get_partplate_list().get_curr_plate()->get_extruders();
for (auto extruder : plate_extruders) {
@ -784,7 +812,7 @@ void MenuFactory::append_menu_items_flush_options(wxMenu* menu)
{
i++;
wxMenuItem* item = node->GetData();
if (item->GetItemLabelText() == "Edit in Parameter Table")
if (item->GetItemLabelText() == _L("Edit in Parameter Table"))
break;
}
menu->Insert(i, wxID_ANY, _L("Flush Options"), flush_options_menu);
@ -1057,6 +1085,7 @@ void MenuFactory::create_bbl_part_menu()
wxMenu* menu = &m_part_menu;
append_menu_item_delete(menu);
append_menu_item_edit_text(menu);
append_menu_item_fix_through_netfabb(menu);
append_menu_item_simplify(menu);
append_menu_item_center(menu);
@ -1310,8 +1339,9 @@ wxMenu* MenuFactory::assemble_multi_selection_menu()
return nullptr;
wxMenu* menu = new MenuWithSeparators();
append_menu_item_fix_through_netfabb(menu);
append_menu_item_simplify(menu);
append_menu_item_set_visible(menu);
//append_menu_item_fix_through_netfabb(menu);
//append_menu_item_simplify(menu);
append_menu_item_delete(menu);
menu->AppendSeparator();
append_menu_item_change_extruder(menu);
@ -1328,18 +1358,36 @@ wxMenu* MenuFactory::plate_menu()
wxMenu* MenuFactory::assemble_object_menu()
{
wxMenu* menu = new MenuWithSeparators();
// Set Visible
append_menu_item_set_visible(menu);
// Delete
append_menu_item_delete(menu);
//// Object Repair
//append_menu_item_fix_through_netfabb(menu);
//// Object Simplify
//append_menu_item_simplify(menu);
menu->AppendSeparator();
// Set filament
append_menu_item_change_extruder(&m_assemble_object_menu);
// Enter per object parameters
append_menu_item_per_object_settings(&m_assemble_object_menu);
return &m_assemble_object_menu;
append_menu_item_change_extruder(menu);
//// Enter per object parameters
//append_menu_item_per_object_settings(menu);
return menu;
}
wxMenu* MenuFactory::assemble_part_menu()
{
append_menu_item_change_extruder(&m_assemble_part_menu);
append_menu_item_per_object_settings(&m_assemble_part_menu);
return &m_assemble_part_menu;
wxMenu* menu = new MenuWithSeparators();
append_menu_item_set_visible(menu);
append_menu_item_delete(menu);
//append_menu_item_simplify(menu);
menu->AppendSeparator();
append_menu_item_change_extruder(menu);
//append_menu_item_per_object_settings(menu);
return menu;
}
void MenuFactory::append_menu_item_clone(wxMenu* menu)
@ -1509,7 +1557,7 @@ void MenuFactory::append_menu_item_set_printable(wxMenu* menu)
}
}
wxString menu_text = all_printable ? L("Set Unprintable") : _L("Set Printable");
wxString menu_text = all_printable ? _L("Set Unprintable") : _L("Set Printable");
append_menu_item(menu, wxID_ANY, menu_text, "", [this, all_printable](wxCommandEvent&) {
Selection& selection = plater()->canvas3D()->get_selection();
selection.set_printable(!all_printable);

View file

@ -126,7 +126,9 @@ private:
void append_menu_item_reload_from_disk(wxMenu* menu);
void append_menu_item_replace_with_stl(wxMenu* menu);
void append_menu_item_change_extruder(wxMenu* menu);
void append_menu_item_set_visible(wxMenu* menu);
void append_menu_item_delete(wxMenu* menu);
void append_menu_item_edit_text(wxMenu *menu);
void append_menu_item_scale_selection_to_fit_print_volume(wxMenu* menu);
void append_menu_items_convert_unit(wxMenu* menu); // Add "Conver/Revert..." menu items (from/to inches/meters) after "Reload From Disk"
void append_menu_items_flush_options(wxMenu* menu);

View file

@ -30,6 +30,7 @@
#include "slic3r/Utils/FixModelByWin10.hpp"
#include "libslic3r/Format/bbs_3mf.hpp"
#include "libslic3r/PrintConfig.hpp"
#ifdef __WXMSW__
#include "wx/uiaction.h"
@ -91,6 +92,9 @@ ObjectList::ObjectList(wxWindow* parent) :
Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxDataViewEvent& event) {
// detect the current mouse position here, to pass it to list_manipulation() method
// if we detect it later, the user may have moved the mouse pointer while calculations are performed, and this would mess-up the HitTest() call performed into list_manipulation()
if (!GetScreenRect().Contains(wxGetMousePosition())) {
return;
}
#ifndef __WXOSX__
const wxPoint mouse_pos = this->get_mouse_position_in_control();
#endif
@ -731,7 +735,7 @@ void ObjectList::printable_state_changed(const std::vector<ObjectVolumeID>& ov_i
obj_idxs.erase(unique(obj_idxs.begin(), obj_idxs.end()), obj_idxs.end());
// update printable state on canvas
wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_objects(obj_idxs);
wxGetApp().plater()->get_view3D_canvas3D()->update_instance_printable_state_for_objects(obj_idxs);
// update scene
wxGetApp().plater()->update();
@ -1397,8 +1401,21 @@ void ObjectList::key_event(wxKeyEvent& event)
void ObjectList::OnBeginDrag(wxDataViewEvent &event)
{
bool sequential_print = (wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_enum<PrintSequence>("print_sequence") == PrintSequence::ByObject);
if (!sequential_print) {
int curr_obj_id = m_objects_model->GetIdByItem(event.GetItem());
PartPlateList& partplate_list = wxGetApp().plater()->get_partplate_list();
int from_plate = partplate_list.find_instance(curr_obj_id, 0);
if (from_plate == -1) {
event.Veto();
return;
}
auto curr_plate_seq = partplate_list.get_plate(from_plate)->get_print_seq();
if (curr_plate_seq == PrintSequence::ByDefault) {
auto curr_preset_config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
if (curr_preset_config.has("print_sequence"))
curr_plate_seq = curr_preset_config.option<ConfigOptionEnum<PrintSequence>>("print_sequence")->value;
}
if (curr_plate_seq != PrintSequence::ByObject) {
//drag forbidden under bylayer mode
event.Veto();
return;
@ -1777,7 +1794,7 @@ void ObjectList::load_subobject(ModelVolumeType type, bool from_galery/* = false
if (type == ModelVolumeType::MODEL_PART)
// update printable state on canvas
wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_object((size_t)obj_idx);
wxGetApp().plater()->get_view3D_canvas3D()->update_instance_printable_state_for_object((size_t)obj_idx);
if (items.size() > 1) {
m_selection_mode = smVolume;
@ -1923,11 +1940,9 @@ void ObjectList::load_modifier(const wxArrayString& input_files, ModelObject& mo
ModelVolume* new_volume = model_object.add_volume(std::move(mesh), type);
new_volume->name = boost::filesystem::path(input_file).filename().string();
// adjust the position according to the bounding box
const BoundingBoxf3 mesh_bb = new_volume->mesh().bounding_box();
new_volume->set_transformation(Geometry::Transformation::volume_to_bed_transformation(v->get_instance_transformation(), mesh_bb));
auto offset = Vec3d(instance_bb.max.x(), instance_bb.min.y(), instance_bb.min.z()) + 0.5 * mesh_bb.size() - v->get_instance_offset();
new_volume->set_offset(v->get_instance_transformation().get_matrix(true).inverse() * offset);
// BBS: object_mesh.get_init_shift() keep the relative position
TriangleMesh object_mesh = model_object.volumes[0]->mesh();
new_volume->set_offset(new_volume->mesh().get_init_shift() - object_mesh.get_init_shift());
// set a default extruder value, since user can't add it manually
// BBS
@ -2055,7 +2070,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
});
if (type == ModelVolumeType::MODEL_PART)
// update printable state on canvas
wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_object((size_t)obj_idx);
wxGetApp().plater()->get_view3D_canvas3D()->update_instance_printable_state_for_object((size_t)obj_idx);
// apply the instance transform to all volumes and reset instance transform except the offset
apply_object_instance_transfrom_to_all_volumes(&model_object);
@ -2158,50 +2173,54 @@ void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name
#endif /* _DEBUG */
}
void ObjectList::load_mesh_part(const TriangleMesh& mesh, const wxString& name, bool center)
int ObjectList::load_mesh_part(const TriangleMesh &mesh, const wxString &name, const TextInfo &text_info, bool is_temp)
{
wxDataViewItem item = GetSelection();
// we can add volumes for Object or Instance
if (!item || !(m_objects_model->GetItemType(item) & (itObject | itInstance)))
return;
return -1;
const int obj_idx = m_objects_model->GetObjectIdByItem(item);
if (obj_idx < 0) return;
if (obj_idx < 0)
return -1;
// Get object item, if Instance is selected
if (m_objects_model->GetItemType(item) & itInstance)
item = m_objects_model->GetItemById(obj_idx);
take_snapshot("Load Mesh Part");
ModelObject* mo = (*m_objects)[obj_idx];
Geometry::Transformation instance_transformation = mo->instances[0]->get_transformation();
// apply the instance transform to all volumes and reset instance transform except the offset
apply_object_instance_transfrom_to_all_volumes(mo);
apply_object_instance_transfrom_to_all_volumes(mo, !is_temp);
ModelVolume* mv = mo->add_volume(mesh);
Vec3d instance_bbox = mo->mesh().bounding_box().size();
Vec3d offset = Vec3d(0, 0, instance_bbox[2] / 2);
mv->set_offset(offset);
ModelVolume *mv = mo->add_volume(mesh);
mv->name = name.ToStdString();
if (!text_info.m_text.empty())
mv->set_text_info(text_info);
std::vector<ModelVolume*> volumes;
volumes.push_back(mv);
wxDataViewItemArray items = reorder_volumes_and_get_selection(obj_idx, [volumes](const ModelVolume* volume) {
return std::find(volumes.begin(), volumes.end(), volume) != volumes.end(); });
if (!is_temp) {
std::vector<ModelVolume *> volumes;
volumes.push_back(mv);
wxDataViewItemArray items = reorder_volumes_and_get_selection(obj_idx, [volumes](const ModelVolume *volume) {
return std::find(volumes.begin(), volumes.end(), volume) != volumes.end();
});
wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_object((size_t)obj_idx);
wxGetApp().plater()->get_view3D_canvas3D()->update_instance_printable_state_for_object((size_t) obj_idx);
if (items.size() > 1) {
m_selection_mode = smVolume;
m_last_selected_item = wxDataViewItem(nullptr);
if (items.size() > 1) {
m_selection_mode = smVolume;
m_last_selected_item = wxDataViewItem(nullptr);
}
select_items(items);
selection_changed();
}
select_items(items);
selection_changed();
//BBS: notify partplate the modify
notify_instance_updated(obj_idx);
return mo->volumes.size() - 1;
}
//BBS
@ -4563,7 +4582,7 @@ void ObjectList::instances_to_separated_object(const int obj_idx, const std::set
}
// update printable state for new volumes on canvas3D
wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_object(new_obj_indx);
wxGetApp().plater()->get_view3D_canvas3D()->update_instance_printable_state_for_object(new_obj_indx);
update_info_items(new_obj_indx);
}
@ -4596,7 +4615,7 @@ void ObjectList::instances_to_separated_objects(const int obj_idx)
}
// update printable state for new volumes on canvas3D
wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_objects(object_idxs);
wxGetApp().plater()->get_view3D_canvas3D()->update_instance_printable_state_for_objects(object_idxs);
for (size_t object : object_idxs)
update_info_items(object);
}
@ -4707,10 +4726,13 @@ void ObjectList::fix_through_netfabb()
std::string res;
if (!fix_model_by_win10_sdk_gui(*(object(obj_idx)), vol_idx, progress_dlg, msg, res))
return false;
wxGetApp().plater()->changed_mesh(obj_idx);
//wxGetApp().plater()->changed_mesh(obj_idx);
object(obj_idx)->ensure_on_bed();
plater->changed_mesh(obj_idx);
plater->get_partplate_list().notify_instance_update(obj_idx, 0);
plater->sidebar().obj_list()->update_plate_values_for_items();
if (res.empty())
succes_models.push_back(model_name);
else
@ -4719,8 +4741,6 @@ void ObjectList::fix_through_netfabb()
update_item_error_icon(obj_idx, vol_idx);
update_info_items(obj_idx);
object(obj_idx)->ensure_on_bed();
return true;
};
@ -5037,7 +5057,7 @@ void ObjectList::reload_all_plates(bool notify_partplate)
m_prevent_canvas_selection_update = false;
// update printable states on canvas
wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_objects(obj_idxs);
wxGetApp().plater()->get_view3D_canvas3D()->update_instance_printable_state_for_objects(obj_idxs);
// update scene
wxGetApp().plater()->update();
}
@ -5171,7 +5191,7 @@ void ObjectList::toggle_printable_state()
obj_idxs.erase(unique(obj_idxs.begin(), obj_idxs.end()), obj_idxs.end());
// update printable state on canvas
wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_objects(obj_idxs);
wxGetApp().plater()->get_view3D_canvas3D()->update_instance_printable_state_for_objects(obj_idxs);
// update scene
wxGetApp().plater()->update();
@ -5190,17 +5210,20 @@ bool ObjectList::has_paint_on_segmentation()
return m_objects_model->HasInfoItem(InfoItemType::MmuSegmentation);
}
void ObjectList::apply_object_instance_transfrom_to_all_volumes(ModelObject *model_object) {
void ObjectList::apply_object_instance_transfrom_to_all_volumes(ModelObject *model_object, bool need_update_assemble_matrix)
{
const Geometry::Transformation &instance_transformation = model_object->instances[0]->get_transformation();
Vec3d original_instance_center = instance_transformation.get_offset();
// apply the instance_transform(except offset) to assemble_transform
Geometry::Transformation instance_transformation_copy = instance_transformation;
instance_transformation_copy.set_offset(Vec3d(0, 0, 0)); // remove the effect of offset
const Transform3d & instance_inverse_matrix = instance_transformation_copy.get_matrix().inverse();
const Transform3d & assemble_matrix = model_object->instances[0]->get_assemble_transformation().get_matrix();
Transform3d new_assemble_transform = assemble_matrix * instance_inverse_matrix;
model_object->instances[0]->set_assemble_from_transform(new_assemble_transform);
if (need_update_assemble_matrix) {
// apply the instance_transform(except offset) to assemble_transform
Geometry::Transformation instance_transformation_copy = instance_transformation;
instance_transformation_copy.set_offset(Vec3d(0, 0, 0)); // remove the effect of offset
const Transform3d &instance_inverse_matrix = instance_transformation_copy.get_matrix().inverse();
const Transform3d &assemble_matrix = model_object->instances[0]->get_assemble_transformation().get_matrix();
Transform3d new_assemble_transform = assemble_matrix * instance_inverse_matrix;
model_object->instances[0]->set_assemble_from_transform(new_assemble_transform);
}
// apply the instance_transform to volumn
const Transform3d &transformation_matrix = instance_transformation.get_matrix();

View file

@ -27,6 +27,7 @@ class ModelConfig;
class ModelObject;
class ModelVolume;
class TriangleMesh;
struct TextInfo;
enum class ModelVolumeType : int;
// FIXME: broken build on mac os because of this is missing:
@ -283,7 +284,7 @@ public:
void load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center = true);
// BBS
void switch_to_object_process();
void load_mesh_part(const TriangleMesh& mesh, const wxString& name, bool center = true);
int load_mesh_part(const TriangleMesh &mesh, const wxString &name, const TextInfo &text_info, bool is_temp);
void del_object(const int obj_idx, bool refresh_immediately = true);
void del_subobject_item(wxDataViewItem& item);
void del_settings_from_config(const wxDataViewItem& parent_item);
@ -454,7 +455,7 @@ private:
void OnEditingDone(wxDataViewEvent &event);
// apply the instance transform to all volumes and reset instance transform except the offset
void apply_object_instance_transfrom_to_all_volumes(ModelObject *model_object);
void apply_object_instance_transfrom_to_all_volumes(ModelObject *model_object, bool need_update_assemble_matrix = true);
std::vector<int> m_columns_width;
};

View file

@ -344,7 +344,10 @@ void GridCellFilamentsRenderer::Draw(wxGrid &grid, wxGridCellAttr &attr, wxDC &d
dc.SetPen(*wxTRANSPARENT_PEN);
dc.SetBrush(wxBrush(attr.GetBackgroundColour()));
dc.DrawRectangle(rect);
dc.DrawBitmap(*bitmap, wxPoint(rect.x + offset_x, rect.y + offset_y));
if ( grid_row->model_volume_type != ModelVolumeType::NEGATIVE_VOLUME) {
dc.DrawBitmap(*bitmap, wxPoint(rect.x + offset_x, rect.y + offset_y));
}
text_rect.x += bitmap_width + grid_cell_border_width * 2;
text_rect.width -= (bitmap_width + grid_cell_border_width * 2);
}
@ -1968,6 +1971,7 @@ void ObjectGridTable::construct_object_configs(ObjectGrid *object_grid)
{
ModelVolume* volume = object->volumes[j];
ObjectGridRow* volume_grid = new ObjectGridRow(i, j, row_volume);
volume_grid->model_volume_type = volume->type();
volume_grid->config = &(volume->config);
volume_grid->name.value = volume->name;
size_t pos = volume_grid->name.value.find_first_not_of(' ');
@ -2952,11 +2956,21 @@ void ObjectTablePanel::load_data()
break;
case coEnum:
if (col == ObjectGridTable::col_filaments) {
GridCellFilamentsEditor *filament_editor = new GridCellFilamentsEditor(grid_col->choice_count, grid_col->choices, false, &m_color_bitmaps);
m_object_grid->SetCellEditor(row, col, filament_editor);
m_object_grid->SetCellRenderer(row, col, new GridCellFilamentsRenderer());
} else {
GridCellChoiceEditor *combo_editor = new GridCellChoiceEditor(grid_col->choice_count, grid_col->choices);
if (grid_row->model_volume_type != ModelVolumeType::NEGATIVE_VOLUME) {
GridCellFilamentsEditor* filament_editor = new GridCellFilamentsEditor(grid_col->choice_count, grid_col->choices, false, &m_color_bitmaps);
m_object_grid->SetCellEditor(row, col, filament_editor);
m_object_grid->SetCellRenderer(row, col, new GridCellFilamentsRenderer());
}
else {
m_object_grid->SetCellEditor(row, col, new GridCellTextEditor());
auto gcfil = new GridCellFilamentsRenderer();
m_object_grid->SetCellRenderer(row, col, gcfil);
m_object_grid->SetReadOnly(row, col);
//m_object_grid->SetCellFitMode(row, col, wxGridFitMode::Ellipsize());
}
}
else {
GridCellChoiceEditor* combo_editor = new GridCellChoiceEditor(grid_col->choice_count, grid_col->choices);
m_object_grid->SetCellEditor(row, col, combo_editor);
m_object_grid->SetCellRenderer(row, col, new wxGridCellChoiceRenderer());
}

View file

@ -348,6 +348,7 @@ public:
ConfigOptionFloat ori_speed_perimeter;
ModelConfig* config;
ModelVolumeType model_volume_type;
ObjectGridRow(int obj_id, int vol_id, GridRowType type)
: object_id(obj_id), volume_id(vol_id), row_type(type)

View file

@ -385,9 +385,9 @@ void Preview::sys_color_changed()
void Preview::on_tick_changed(Type type)
{
if (type == Type::PausePrint) {
m_schedule_background_process();
}
//if (type == Type::PausePrint) {
// m_schedule_background_process();
//}
m_keep_current_preview_type = false;
reload_print(false);
}
@ -485,8 +485,7 @@ void Preview::update_layers_slider_mode()
// check if whole model uses just only one extruder
if (!plate_extruders.empty()) {
//const int extruder = objects[0]->config.has("extruder") ? objects[0]->config.option("extruder")->getInt() : 0;
const int extruder = plate_extruders[0];
only_extruder = extruder;
only_extruder = plate_extruders[0];
// auto is_one_extruder_printed_model = [objects, extruder]() {
// for (ModelObject *object : objects) {
// if (object->config.has("extruder") && object->config.option("extruder")->getInt() != extruder) /*return false*/;
@ -552,15 +551,16 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
// Detect and set manipulation mode for double slider
update_layers_slider_mode();
Plater * plater = wxGetApp().plater();
CustomGCode::Info ticks_info_from_model;
Plater* plater = wxGetApp().plater();
//BBS: replace model custom gcode with current plate custom gcode
CustomGCode::Info ticks_info_from_curr_plate;
if (wxGetApp().is_editor())
ticks_info_from_model = plater->model().custom_gcode_per_print_z;
ticks_info_from_curr_plate = plater->model().get_curr_plate_custom_gcodes();
else {
ticks_info_from_model.mode = CustomGCode::Mode::SingleExtruder;
ticks_info_from_model.gcodes = m_canvas->get_custom_gcode_per_print_z();
ticks_info_from_curr_plate.mode = CustomGCode::Mode::SingleExtruder;
ticks_info_from_curr_plate.gcodes = m_canvas->get_custom_gcode_per_print_z();
}
check_layers_slider_values(ticks_info_from_model.gcodes, layers_z);
check_layers_slider_values(ticks_info_from_curr_plate.gcodes, layers_z);
// first of all update extruder colors to avoid crash, when we are switching printer preset from MM to SM
m_layers_slider->SetExtruderColors(plater->get_extruder_colors_from_plater_config(wxGetApp().is_editor() ? nullptr : m_gcode_result));
@ -581,9 +581,11 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
}
}
m_layers_slider->SetSelectionSpan(idx_low, idx_high);
m_layers_slider->SetTicksValues(ticks_info_from_model);
m_layers_slider->SetTicksValues(ticks_info_from_curr_plate);
bool sequential_print = (wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_enum<PrintSequence>("print_sequence") == PrintSequence::ByObject);
auto curr_plate = wxGetApp().plater()->get_partplate_list().get_curr_plate();
auto curr_print_seq = curr_plate->get_real_print_seq();
bool sequential_print = (curr_print_seq == PrintSequence::ByObject);
m_layers_slider->SetDrawMode(sequential_print);
auto print_mode_stat = m_gcode_result->print_statistics.modes.front();
@ -688,7 +690,8 @@ void Preview::load_print_as_fff(bool keep_z_range, bool only_gcode)
if (!gcode_preview_data_valid) {
if (wxGetApp().is_editor())
color_print_values = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
//BBS
color_print_values = wxGetApp().plater()->model().get_curr_plate_custom_gcodes().gcodes;
else
color_print_values = m_canvas->get_custom_gcode_per_print_z();
colors.push_back("#808080"); // gray color for pause print or custom G-code
@ -703,7 +706,8 @@ void Preview::load_print_as_fff(bool keep_z_range, bool only_gcode)
if (IsShown()) {
m_canvas->set_selected_extruder(0);
if (gcode_preview_data_valid) {
bool is_slice_result_valid = wxGetApp().plater()->get_partplate_list().get_curr_plate()->is_slice_result_valid();
if (gcode_preview_data_valid && (is_slice_result_valid || m_only_gcode)) {
// Load the real G-code preview.
//BBS: add more log
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": will load gcode_preview from result, moves count %1%") % m_gcode_result->moves.size();
@ -734,7 +738,8 @@ void Preview::load_print_as_fff(bool keep_z_range, bool only_gcode)
(unsigned int)print->extruders().size() :
m_canvas->get_gcode_extruders_count();
std::vector<Item> gcodes = wxGetApp().is_editor() ?
wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes :
//BBS
wxGetApp().plater()->model().get_curr_plate_custom_gcodes().gcodes :
m_canvas->get_custom_gcode_per_print_z();
const wxString choice = !gcodes.empty() ?
_L("Multicolor Print") :

View file

@ -430,7 +430,10 @@ bool load_image(const std::string &filename, wxImage &image)
result = image.LoadFile(wxString::FromUTF8(filename.c_str()), wxBITMAP_TYPE_BMP);
} else if (boost::algorithm::iends_with(filename, ".jpg")) {
result = image.LoadFile(wxString::FromUTF8(filename.c_str()), wxBITMAP_TYPE_JPEG);
} else {
} else if (boost::algorithm::iends_with(filename, ".jpeg")) {
result = image.LoadFile(wxString::FromUTF8(filename.c_str()), wxBITMAP_TYPE_JPEG);
}
else {
return false;
}
return result;

View file

@ -128,6 +128,29 @@ void GLGizmoFdmSupports::render_painter_gizmo() const
glsafe(::glDisable(GL_BLEND));
}
// BBS
bool GLGizmoFdmSupports::on_key_down_select_tool_type(int keyCode) {
switch (keyCode)
{
case 'F':
m_current_tool = ImGui::FillButtonIcon;
break;
case 'S':
m_current_tool = ImGui::SphereButtonIcon;
break;
case 'C':
m_current_tool = ImGui::CircleButtonIcon;
break;
case 'G':
m_current_tool = ImGui::GapFillIcon;
break;
default:
return false;
break;
}
return true;
}
// BBS
void GLGizmoFdmSupports::render_triangles(const Selection& selection) const
{
@ -865,42 +888,22 @@ void GLGizmoFdmSupports::run_thread()
goto _finished;
}
if (m_is_tree_support)
if (!m_print_instance.print_object->support_layers().size())
{
if (!m_print_instance.print_object->tree_support_layers().size())
{
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ",no tree support layer found, update status to 100%\n";
print->set_status(100, L("Support Generated"));
goto _finished;
}
for (const TreeSupportLayer *support_layer : m_print_instance.print_object->tree_support_layers())
{
for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities)
{
_3DScene::extrusionentity_to_verts(extrusion_entity, float(support_layer->print_z), m_print_instance.shift, *m_support_volume);
}
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", finished extrusionentity_to_verts, update status to 100%";
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ",no support layer found, update status to 100%\n";
print->set_status(100, L("Support Generated"));
goto _finished;
}
else
for (const SupportLayer *support_layer : m_print_instance.print_object->support_layers())
{
if (!m_print_instance.print_object->support_layers().size())
for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities)
{
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ",no support layer found, update status to 100%\n";
print->set_status(100, L("Support Generated"));
goto _finished;
_3DScene::extrusionentity_to_verts(extrusion_entity, float(support_layer->print_z), m_print_instance.shift, *m_support_volume);
}
for (const SupportLayer *support_layer : m_print_instance.print_object->support_layers())
{
for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities)
{
_3DScene::extrusionentity_to_verts(extrusion_entity, float(support_layer->print_z), m_print_instance.shift, *m_support_volume);
}
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", finished extrusionentity_to_verts, update status to 100%";
print->set_status(100, L("Support Generated"));
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", finished extrusionentity_to_verts, update status to 100%";
print->set_status(100, L("Support Generated"));
record_timestamp();
}
catch (...) {

View file

@ -24,6 +24,9 @@ public:
state_ready
};
//BBS
bool on_key_down_select_tool_type(int keyCode);
protected:
void on_render_input_window(float x, float y, float bottom_limit) override;
std::string on_get_name() const override;

View file

@ -241,6 +241,34 @@ bool GLGizmoMmuSegmentation::on_number_key_down(int number)
return true;
}
bool GLGizmoMmuSegmentation::on_key_down_select_tool_type(int keyCode) {
switch (keyCode)
{
case 'F':
m_current_tool = ImGui::FillButtonIcon;
break;
case 'T':
m_current_tool = ImGui::TriangleButtonIcon;
break;
case 'S':
m_current_tool = ImGui::SphereButtonIcon;
break;
case 'C':
m_current_tool = ImGui::CircleButtonIcon;
break;
case 'H':
m_current_tool = ImGui::HeightRangeIcon;
break;
case 'G':
m_current_tool = ImGui::GapFillIcon;
break;
default:
return false;
break;
}
return true;
}
static void render_extruders_combo(const std::string &label,
const std::vector<std::string> &extruders,
const std::vector<std::array<float, 4>> &extruders_colors,

View file

@ -83,6 +83,7 @@ public:
// BBS
bool on_number_key_down(int number);
bool on_key_down_select_tool_type(int keyCode);
protected:
// BBS

View file

@ -261,7 +261,7 @@ protected:
static constexpr float CursorRadiusMin = 0.4f; // cannot be zero
static constexpr float CursorRadiusMax = 8.f;
static constexpr float CursorRadiusStep = 0.2f;
static constexpr float CursorHeightMin = 0.2f; // cannot be zero
static constexpr float CursorHeightMin = 0.1f; // cannot be zero
static constexpr float CursorHeightMax = 8.f;
static constexpr float CursorHeightStep = 0.2f;

View file

@ -226,10 +226,11 @@ void GLGizmoScale3D::on_render()
//draw connections
if (single_instance || single_volume) {
// BBS: when select multiple objects, uniform scale can be deselected, display the connection(4,5)
//if (single_instance || single_volume) {
glsafe(::glColor4fv(m_grabbers[4].color.data()));
render_grabbers_connection(4, 5);
}
//}
glsafe(::glColor4fv(m_grabbers[2].color.data()));
render_grabbers_connection(6, 7);

View file

@ -78,6 +78,23 @@ void GLGizmoSeam::render_painter_gizmo() const
glsafe(::glDisable(GL_BLEND));
}
// BBS
bool GLGizmoSeam::on_key_down_select_tool_type(int keyCode) {
switch (keyCode)
{
case 'S':
m_current_tool = ImGui::SphereButtonIcon;
break;
case 'C':
m_current_tool = ImGui::CircleButtonIcon;
break;
default:
return false;
break;
}
return true;
}
void GLGizmoSeam::render_triangles(const Selection& selection) const
{
ClippingPlaneDataWrapper clp_data = this->get_clipping_plane_data();

Some files were not shown because too many files have changed in this diff Show more