mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-27 02:41:17 +00:00
Merge branch 'main' into feature/wipetower-option
This commit is contained in:
@@ -23,10 +23,11 @@ template<> struct hash<Slic3r::GridPoint3>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
void InterlockingGenerator::generate_interlocking_structure(PrintObject* print_object)
|
||||
void InterlockingGenerator::generate_interlocking_structure(PrintObject* print_object, const std::function<void()>& throw_on_cancel)
|
||||
{
|
||||
const auto& config = print_object->config();
|
||||
if (!config.interlocking_beam) {
|
||||
// Check if interlocking is enabled, and avoid errors like division by zero due to invalid configuration.
|
||||
if (!config.interlocking_beam || config.interlocking_beam_layer_count < 1 || config.interlocking_depth < 1 || config.interlocking_beam_width < EPSILON ) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -55,8 +56,10 @@ void InterlockingGenerator::generate_interlocking_structure(PrintObject* print_o
|
||||
continue;
|
||||
}
|
||||
|
||||
throw_on_cancel();
|
||||
|
||||
InterlockingGenerator gen(*print_object, region_a_index, region_b_index, beam_width, boundary_avoidance, rotation, cell_size, beam_layer_count,
|
||||
interface_dilation, air_dilation, air_filtering);
|
||||
interface_dilation, air_dilation, air_filtering, throw_on_cancel);
|
||||
gen.generateInterlockingStructure();
|
||||
}
|
||||
}
|
||||
@@ -108,12 +111,14 @@ void InterlockingGenerator::handleThinAreas(const std::unordered_set<GridPoint3>
|
||||
}
|
||||
}
|
||||
for (auto& near_interlock : near_interlock_per_layer) {
|
||||
throw_on_cancel();
|
||||
near_interlock = offset(union_(closing(near_interlock, rounding_errors)), detect);
|
||||
polygons_rotate(near_interlock, rotation);
|
||||
}
|
||||
|
||||
// Only alter layers when they are present in both meshes, zip should take care if that.
|
||||
for (size_t layer_nr = 0; layer_nr < print_object.layer_count(); layer_nr++){
|
||||
throw_on_cancel();
|
||||
auto layer = print_object.get_layer(layer_nr);
|
||||
ExPolygons polys_a = to_expolygons(layer->get_region(region_a_index)->slices.surfaces);
|
||||
ExPolygons polys_b = to_expolygons(layer->get_region(region_b_index)->slices.surfaces);
|
||||
@@ -199,7 +204,8 @@ void InterlockingGenerator::addBoundaryCells(const std::vector<ExPolygons>& lay
|
||||
const DilationKernel& kernel,
|
||||
std::unordered_set<GridPoint3>& cells) const
|
||||
{
|
||||
auto voxel_emplacer = [&cells](GridPoint3 p) {
|
||||
auto voxel_emplacer = [this, &cells](GridPoint3 p) {
|
||||
this->throw_on_cancel();
|
||||
if (p.z() < 0) {
|
||||
return true;
|
||||
}
|
||||
@@ -313,6 +319,7 @@ void InterlockingGenerator::applyMicrostructureToOutlines(const std::unordered_s
|
||||
for (size_t region_idx = 0; region_idx < 2; region_idx++) {
|
||||
const size_t region = (region_idx == 0) ? region_a_index : region_b_index;
|
||||
for (size_t layer_nr = 0; layer_nr < max_layer_count; layer_nr++) {
|
||||
throw_on_cancel();
|
||||
ExPolygons layer_outlines = layer_regions[layer_nr];
|
||||
expolygons_rotate(layer_outlines, unapply_rotation);
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
/*!
|
||||
* Generate an interlocking structure between each two adjacent meshes.
|
||||
*/
|
||||
static void generate_interlocking_structure(PrintObject* print_object);
|
||||
static void generate_interlocking_structure(PrintObject* print_object, const std::function<void()>& throw_on_cancel);
|
||||
|
||||
private:
|
||||
/*!
|
||||
@@ -75,7 +75,8 @@ private:
|
||||
const coord_t beam_layer_count,
|
||||
const DilationKernel& interface_dilation,
|
||||
const DilationKernel& air_dilation,
|
||||
const bool air_filtering)
|
||||
const bool air_filtering,
|
||||
const std::function<void()>& throw_on_cancel)
|
||||
: print_object(print_object)
|
||||
, region_a_index(region_a_index)
|
||||
, region_b_index(region_b_index)
|
||||
@@ -88,6 +89,7 @@ private:
|
||||
, interface_dilation(interface_dilation)
|
||||
, air_dilation(air_dilation)
|
||||
, air_filtering(air_filtering)
|
||||
, throw_on_cancel(throw_on_cancel)
|
||||
{}
|
||||
|
||||
/*! Given two polygons, return the parts that border on air, and grow 'perpendicular' up to 'detect' distance.
|
||||
@@ -165,6 +167,8 @@ private:
|
||||
// Whether to fully remove all of the interlocking cells which would be visible on the outside. If no air filtering then those cells
|
||||
// will be cut off midway in a beam.
|
||||
const bool air_filtering;
|
||||
|
||||
const std::function<void()>& throw_on_cancel;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -882,6 +882,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
|
||||
if (surface.is_top()) {
|
||||
params.pattern = region_config.top_surface_pattern.value;
|
||||
params.density = float(region_config.top_surface_density);
|
||||
if (params.density <= 0.0f) continue;
|
||||
} else { // Surface is bottom
|
||||
params.pattern = region_config.bottom_surface_pattern.value;
|
||||
params.density = float(region_config.bottom_surface_density);
|
||||
|
||||
@@ -1321,19 +1321,18 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first
|
||||
return false;
|
||||
};
|
||||
|
||||
if (m_print->is_BBL_printer() || number_of_extruders == 1){
|
||||
auto maps_without_group = filament_maps;
|
||||
for (auto& item : maps_without_group)
|
||||
item = 0;
|
||||
|
||||
reorder_filaments_for_minimum_flush_volume(
|
||||
filament_lists,
|
||||
filament_maps,
|
||||
m_print->is_BBL_printer() ? filament_maps : maps_without_group, // non-bbl printers do not support filament group yet
|
||||
layer_filaments,
|
||||
nozzle_flush_mtx,
|
||||
get_custom_seq,
|
||||
&filament_sequences
|
||||
);
|
||||
} else {
|
||||
// For non-bbl multi-extruder printers we don't support filament group yet, so we keep the layer sequence because we don't flush based on order
|
||||
filament_sequences = layer_filaments;
|
||||
}
|
||||
|
||||
auto curr_flush_info = calc_filament_change_info_by_toolorder(print_config, filament_maps, nozzle_flush_mtx, filament_sequences);
|
||||
if (nozzle_nums <= 1)
|
||||
@@ -1349,9 +1348,6 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first
|
||||
// always calculate the info by one extruder
|
||||
{
|
||||
std::vector<std::vector<unsigned int>>filament_sequences_one_extruder;
|
||||
auto maps_without_group = filament_maps;
|
||||
for (auto& item : maps_without_group)
|
||||
item = 0;
|
||||
reorder_filaments_for_minimum_flush_volume(
|
||||
filament_lists,
|
||||
maps_without_group,
|
||||
|
||||
@@ -3979,7 +3979,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->enum_labels.push_back(L("No ironing"));
|
||||
def->enum_labels.push_back(L("Top surfaces"));
|
||||
def->enum_labels.push_back(L("Topmost surface"));
|
||||
def->enum_labels.push_back(L("All solid layer"));
|
||||
def->enum_labels.push_back(L("All solid layers"));
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionEnum<IroningType>(IroningType::NoIroning));
|
||||
|
||||
@@ -4561,7 +4561,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->set_default_value(new ConfigOptionFloat(0.));
|
||||
|
||||
def = this->add("detect_overhang_wall", coBool);
|
||||
def->label = L("Detect overhang wall");
|
||||
def->label = L("Detect overhang walls");
|
||||
def->category = L("Quality");
|
||||
def->tooltip = L("Detect the overhang percentage relative to line width and use different speed to print. "
|
||||
"For 100%% overhang, bridge speed is used.");
|
||||
@@ -5311,7 +5311,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def = this->add("minimum_sparse_infill_area", coFloat);
|
||||
def->label = L("Minimum sparse infill threshold");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("Sparse infill area which is smaller than threshold value is replaced by internal solid infill.");
|
||||
def->tooltip = L("Sparse infill areas smaller than this threshold value are replaced by internal solid infill.");
|
||||
def->sidetext = L(u8"mm²"); // square milimeters, CIS languages need translation
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
@@ -6163,10 +6163,10 @@ void PrintConfigDef::init_fff_params()
|
||||
def->set_default_value(new ConfigOptionPoints{});
|
||||
|
||||
def = this->add("detect_thin_wall", coBool);
|
||||
def->label = L("Detect thin wall");
|
||||
def->label = L("Detect thin walls");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("Detect thin wall which can't contain two line width. And use single line to print. "
|
||||
"Maybe printed not very well, because it's not closed loop.");
|
||||
def->tooltip = L("Detect thin walls which can't contain two line widths, and use single line to print. "
|
||||
"Maybe not printed very well, because it's not a closed loop.");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
@@ -6777,7 +6777,7 @@ void PrintConfigDef::init_fff_params()
|
||||
}
|
||||
|
||||
def = this->add("detect_narrow_internal_solid_infill", coBool);
|
||||
def->label = L("Detect narrow internal solid infill");
|
||||
def->label = L("Detect narrow internal solid infills");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("This option will auto-detect narrow internal solid infill areas. "
|
||||
"If enabled, the concentric pattern will be used for the area to speed up printing. "
|
||||
|
||||
@@ -1212,7 +1212,7 @@ void PrintObject::slice_volumes()
|
||||
apply_fuzzy_skin_segmentation(*this, [print]() { print->throw_if_canceled(); });
|
||||
}
|
||||
|
||||
InterlockingGenerator::generate_interlocking_structure(this);
|
||||
InterlockingGenerator::generate_interlocking_structure(this, [print]() { print->throw_if_canceled(); });
|
||||
m_print->throw_if_canceled();
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - make_slices in parallel - begin";
|
||||
|
||||
+82
-50
@@ -6199,7 +6199,7 @@ static const wxLanguageInfo* linux_get_existing_locale_language(const wxLanguage
|
||||
if (! it->empty()) {
|
||||
const std::string &locale = *it;
|
||||
const wxLanguageInfo* lang = wxLocale::FindLanguageInfo(from_u8(locale));
|
||||
if (wxLocale::IsAvailable(lang->Language))
|
||||
if (lang != nullptr && wxLocale::IsAvailable(lang->Language))
|
||||
return lang;
|
||||
}
|
||||
return language;
|
||||
@@ -6241,7 +6241,10 @@ bool GUI_App::select_language()
|
||||
names.Alloc(language_infos.size());
|
||||
|
||||
// Some valid language should be selected since the application start up.
|
||||
const wxLanguage current_language = wxLanguage(m_wxLocale->GetLanguage());
|
||||
const wxString active_language_code = current_language_code();
|
||||
const wxLanguageInfo* active_language_info = wxLocale::FindLanguageInfo(active_language_code);
|
||||
const wxLanguage current_language = active_language_info != nullptr ? wxLanguage(active_language_info->Language) : wxLanguage(m_wxLocale->GetLanguage());
|
||||
const wxString active_lang_prefix = active_language_code.BeforeFirst('_');
|
||||
int init_selection = -1;
|
||||
int init_selection_alt = -1;
|
||||
int init_selection_default = -1;
|
||||
@@ -6249,9 +6252,9 @@ bool GUI_App::select_language()
|
||||
if (wxLanguage(language_infos[i]->Language) == current_language)
|
||||
// The dictionary matches the active language and country.
|
||||
init_selection = i;
|
||||
else if ((language_infos[i]->CanonicalName.BeforeFirst('_') == m_wxLocale->GetCanonicalName().BeforeFirst('_')) ||
|
||||
else if ((language_infos[i]->CanonicalName.BeforeFirst('_') == active_lang_prefix) ||
|
||||
// if the active language is Slovak, mark the Czech language as active.
|
||||
(language_infos[i]->CanonicalName.BeforeFirst('_') == "cs" && m_wxLocale->GetCanonicalName().BeforeFirst('_') == "sk"))
|
||||
(language_infos[i]->CanonicalName.BeforeFirst('_') == "cs" && active_lang_prefix == "sk"))
|
||||
// The dictionary matches the active language, it does not necessarily match the country.
|
||||
init_selection_alt = i;
|
||||
if (language_infos[i]->CanonicalName.BeforeFirst('_') == "en")
|
||||
@@ -6369,7 +6372,10 @@ bool GUI_App::load_language(wxString language, bool initial)
|
||||
language_info = wxLocale::GetLanguageInfo(wxLANGUAGE_ENGLISH_US);
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << boost::format("Switching wxLocales to %1%") % language_info->CanonicalName.ToUTF8().data();
|
||||
const wxLanguageInfo *translation_language_info = language_info;
|
||||
const wxString requested_language_code = translation_language_info->CanonicalName;
|
||||
const wxLanguageInfo *locale_language_info = translation_language_info;
|
||||
BOOST_LOG_TRIVIAL(trace) << boost::format("Requested translation language %1%") % requested_language_code.ToUTF8().data();
|
||||
|
||||
// Select language for locales. This language may be different from the language of the dictionary.
|
||||
//if (language_info == m_language_info_best || language_info == m_language_info_system) {
|
||||
@@ -6382,8 +6388,8 @@ bool GUI_App::load_language(wxString language, bool initial)
|
||||
// language_info = m_language_info_system;
|
||||
|
||||
// Alternate language code.
|
||||
wxLanguage language_dict = wxLanguage(language_info->Language);
|
||||
if (language_info->CanonicalName.BeforeFirst('_') == "sk") {
|
||||
wxLanguage language_dict = wxLanguage(translation_language_info->Language);
|
||||
if (translation_language_info->CanonicalName.BeforeFirst('_') == "sk") {
|
||||
// Slovaks understand Czech well. Give them the Czech translation.
|
||||
language_dict = wxLANGUAGE_CZECH;
|
||||
BOOST_LOG_TRIVIAL(info) << "Using Czech dictionaries for Slovak language";
|
||||
@@ -6392,19 +6398,34 @@ bool GUI_App::load_language(wxString language, bool initial)
|
||||
#ifdef __linux__
|
||||
// If we can't find this locale , try to use different one for the language
|
||||
// instead of just reporting that it is impossible to switch.
|
||||
if (! wxLocale::IsAvailable(language_info->Language) && m_language_info_system) {
|
||||
std::string original_lang = into_u8(language_info->CanonicalName);
|
||||
language_info = linux_get_existing_locale_language(language_info, m_language_info_system);
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Can't switch language to %1% (missing locales). Using %2% instead.")
|
||||
% original_lang % language_info->CanonicalName.ToUTF8().data();
|
||||
if (!wxLocale::IsAvailable(locale_language_info->Language) && m_language_info_system) {
|
||||
std::string original_lang = into_u8(locale_language_info->CanonicalName);
|
||||
locale_language_info = linux_get_existing_locale_language(locale_language_info, m_language_info_system);
|
||||
if (locale_language_info != nullptr && locale_language_info != translation_language_info) {
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Can't use locale %1% directly (missing locales). Using locale %2% instead.")
|
||||
% original_lang % locale_language_info->CanonicalName.ToUTF8().data();
|
||||
}
|
||||
}
|
||||
|
||||
if (locale_language_info == nullptr || !wxLocale::IsAvailable(locale_language_info->Language)) {
|
||||
auto try_locale = [](const wxLanguageInfo* candidate) -> const wxLanguageInfo* {
|
||||
return (candidate && wxLocale::IsAvailable(candidate->Language)) ? candidate : nullptr;
|
||||
};
|
||||
const wxLanguageInfo* fallback_locale_info =
|
||||
try_locale(m_wxLocale ? wxLocale::GetLanguageInfo(wxLanguage(m_wxLocale->GetLanguage())) : nullptr);
|
||||
if (!fallback_locale_info) fallback_locale_info = try_locale(m_language_info_system);
|
||||
if (!fallback_locale_info) fallback_locale_info = try_locale(m_language_info_best);
|
||||
if (!fallback_locale_info) fallback_locale_info = try_locale(wxLocale::GetLanguageInfo(wxLANGUAGE_ENGLISH_US));
|
||||
if (!fallback_locale_info) fallback_locale_info = try_locale(wxLocale::GetLanguageInfo(wxLANGUAGE_ENGLISH_UK));
|
||||
if (fallback_locale_info != nullptr) {
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Using fallback locale %1% while keeping translation dictionary %2%.")
|
||||
% fallback_locale_info->CanonicalName.ToUTF8().data() % requested_language_code.ToUTF8().data();
|
||||
locale_language_info = fallback_locale_info;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (! wxLocale::IsAvailable(language_info->Language)&&initial) {
|
||||
language_info = wxLocale::GetLanguageInfo(wxLANGUAGE_ENGLISH_UK);
|
||||
app_config->set("language", language_info->CanonicalName.ToUTF8().data());
|
||||
}
|
||||
else if (initial) {
|
||||
if (initial) {
|
||||
// bbs supported languages
|
||||
//TODO: use a global one with Preference
|
||||
//wxLanguage supported_languages[]{
|
||||
@@ -6438,9 +6459,11 @@ bool GUI_App::load_language(wxString language, bool initial)
|
||||
//}
|
||||
}
|
||||
|
||||
if (! wxLocale::IsAvailable(language_info->Language)) {
|
||||
BOOST_LOG_TRIVIAL(trace) << boost::format("Switching wxLocales to %1%") % locale_language_info->CanonicalName.ToUTF8().data();
|
||||
|
||||
if (!wxLocale::IsAvailable(locale_language_info->Language)) {
|
||||
// Loading the language dictionary failed.
|
||||
wxString message = "Switching Orca Slicer to language " + language_info->CanonicalName + " failed.";
|
||||
wxString message = "Switching Orca Slicer to language " + requested_language_code + " failed.";
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
// likely some linux system
|
||||
message += "\nYou may need to reconfigure the missing locales, likely by running the \"locale-gen\" and \"dpkg-reconfigure locales\" commands.\n";
|
||||
@@ -6458,12 +6481,13 @@ bool GUI_App::load_language(wxString language, bool initial)
|
||||
//FIXME wxWidgets cause havoc if the current locale is deleted. We just forget it causing memory leaks for now.
|
||||
m_wxLocale.release();
|
||||
m_wxLocale = Slic3r::make_unique<wxLocale>();
|
||||
m_wxLocale->Init(language_info->Language);
|
||||
m_wxLocale->Init(locale_language_info->Language);
|
||||
// Override language at the active wxTranslations class (which is stored in the active m_wxLocale)
|
||||
// to load possibly different dictionary, for example, load Czech dictionary for Slovak language.
|
||||
wxTranslations::Get()->SetLanguage(language_dict);
|
||||
m_wxLocale->AddCatalog(SLIC3R_APP_KEY);
|
||||
m_imgui->set_language(into_u8(language_info->CanonicalName));
|
||||
m_active_language_code = requested_language_code;
|
||||
m_imgui->set_language(into_u8(requested_language_code));
|
||||
|
||||
//FIXME This is a temporary workaround, the correct solution is to switch to "C" locale during file import / export only.
|
||||
//wxSetlocale(LC_NUMERIC, "C");
|
||||
@@ -6751,49 +6775,57 @@ void GUI_App::show_ip_address_enter_dialog_handler(wxCommandEvent& evt)
|
||||
|
||||
void GUI_App::open_preferences(size_t open_on_tab, const std::string& highlight_option)
|
||||
{
|
||||
bool app_layout_changed = false;
|
||||
bool need_recreate_gui = false;
|
||||
std::string pending_language;
|
||||
{
|
||||
// the dialog needs to be destroyed before the call to recreate_GUI()
|
||||
// or sometimes the application crashes into wxDialogBase() destructor
|
||||
// so we put it into an inner scope
|
||||
PreferencesDialog dlg(mainframe, open_on_tab, highlight_option);
|
||||
dlg.ShowModal();
|
||||
this->plater_->get_current_canvas3D()->force_set_focus();
|
||||
// BBS
|
||||
//app_layout_changed = dlg.settings_layout_changed();
|
||||
need_recreate_gui = dlg.recreate_GUI();
|
||||
pending_language = dlg.pending_language();
|
||||
if (!need_recreate_gui) {
|
||||
this->plater_->get_current_canvas3D()->force_set_focus();
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed())
|
||||
if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed())
|
||||
#else
|
||||
if (dlg.seq_top_layer_only_changed())
|
||||
if (dlg.seq_top_layer_only_changed())
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
this->plater_->reload_print();
|
||||
this->plater_->reload_print();
|
||||
#ifdef _WIN32
|
||||
if (is_editor()) {
|
||||
if (app_config->get("associate_3mf") == "true")
|
||||
associate_files(L"3mf");
|
||||
if (app_config->get("associate_stl") == "true")
|
||||
associate_files(L"stl");
|
||||
if (app_config->get("associate_step") == "true") {
|
||||
associate_files(L"step");
|
||||
associate_files(L"stp");
|
||||
if (is_editor()) {
|
||||
if (app_config->get("associate_3mf") == "true")
|
||||
associate_files(L"3mf");
|
||||
if (app_config->get("associate_stl") == "true")
|
||||
associate_files(L"stl");
|
||||
if (app_config->get("associate_step") == "true") {
|
||||
associate_files(L"step");
|
||||
associate_files(L"stp");
|
||||
}
|
||||
associate_url(L"orcaslicer");
|
||||
}
|
||||
else {
|
||||
if (app_config->get("associate_gcode") == "true")
|
||||
associate_files(L"gcode");
|
||||
}
|
||||
associate_url(L"orcaslicer");
|
||||
}
|
||||
else {
|
||||
if (app_config->get("associate_gcode") == "true")
|
||||
associate_files(L"gcode");
|
||||
}
|
||||
#endif // _WIN32
|
||||
}
|
||||
}
|
||||
|
||||
// BBS
|
||||
/*
|
||||
if (app_layout_changed) {
|
||||
// hide full main_sizer for mainFrame
|
||||
mainframe->GetSizer()->Show(false);
|
||||
mainframe->update_layout();
|
||||
mainframe->select_tab(size_t(0));
|
||||
}*/
|
||||
if (!pending_language.empty()) {
|
||||
const std::string previous_language = app_config->get("language");
|
||||
app_config->set("language", pending_language);
|
||||
if (!load_language(wxString::FromUTF8(pending_language), false)) {
|
||||
app_config->set("language", previous_language);
|
||||
if (this->plater_)
|
||||
this->plater_->get_current_canvas3D()->force_set_focus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (need_recreate_gui)
|
||||
recreate_GUI(_L("Changing application language"));
|
||||
}
|
||||
|
||||
bool GUI_App::has_unsaved_preset_changes() const
|
||||
|
||||
@@ -273,6 +273,7 @@ private:
|
||||
const wxLanguageInfo *m_language_info_system = nullptr;
|
||||
// Best translation language, provided by Windows or OSX, owned by wxWidgets.
|
||||
const wxLanguageInfo *m_language_info_best = nullptr;
|
||||
wxString m_active_language_code;
|
||||
|
||||
OpenGLManager m_opengl_mgr;
|
||||
std::unique_ptr<RemovableDriveManager> m_removable_drive_manager;
|
||||
@@ -563,7 +564,7 @@ public:
|
||||
void preset_deleted_from_cloud(std::string setting_id);
|
||||
|
||||
wxString filter_string(wxString str);
|
||||
wxString current_language_code() const { return m_wxLocale->GetCanonicalName(); }
|
||||
wxString current_language_code() const { return m_active_language_code.empty() && m_wxLocale ? m_wxLocale->GetCanonicalName() : m_active_language_code; }
|
||||
// Translate the language code to a code, for which Prusa Research maintains translations. Defaults to "en_US".
|
||||
wxString current_language_code_safe() const;
|
||||
bool is_localized() const { return m_wxLocale->GetLocale() != "English"; }
|
||||
|
||||
@@ -2485,7 +2485,7 @@ static wxMenu* generate_help_menu()
|
||||
// //TODO
|
||||
// });
|
||||
// Check New Version
|
||||
append_menu_item(helpMenu, wxID_ANY, _L("Check for Update"), _L("Check for Update"),
|
||||
append_menu_item(helpMenu, wxID_ANY, _L("Check for Updates"), _L("Check for Updates"),
|
||||
[](wxCommandEvent&) {
|
||||
wxGetApp().check_new_version_sf(true, 1);
|
||||
}, "", nullptr, []() {
|
||||
@@ -3145,15 +3145,7 @@ void MainFrame::init_menubar_as_editor()
|
||||
append_menu_item(
|
||||
parent_menu, wxID_ANY, _L("Preferences") + "\t" + ctrl + ",", "",
|
||||
[this](wxCommandEvent &) {
|
||||
PreferencesDialog dlg(this);
|
||||
dlg.ShowModal();
|
||||
plater()->get_current_canvas3D()->force_set_focus();
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed())
|
||||
#else
|
||||
if (dlg.seq_top_layer_only_changed())
|
||||
#endif
|
||||
plater()->reload_print();
|
||||
wxGetApp().open_preferences();
|
||||
},
|
||||
"", nullptr, []() { return true; }, this, 1);
|
||||
//parent_menu->Insert(1, preference_item);
|
||||
@@ -3174,7 +3166,6 @@ void MainFrame::init_menubar_as_editor()
|
||||
[this](wxCommandEvent &) {
|
||||
// Orca: Use GUI_App::open_preferences instead of direct call so windows associations are updated on exit
|
||||
wxGetApp().open_preferences();
|
||||
plater()->get_current_canvas3D()->force_set_focus();
|
||||
},
|
||||
"", nullptr, []() { return true; }, this);
|
||||
//m_topbar->AddDropDownMenuItem(preference_item);
|
||||
@@ -3215,20 +3206,20 @@ void MainFrame::init_menubar_as_editor()
|
||||
// Flow rate (with submenu)
|
||||
auto flowrate_menu = new wxMenu();
|
||||
append_menu_item(
|
||||
flowrate_menu, wxID_ANY, _L("Pass 1"), _L("Flow rate test - Pass 1"),
|
||||
flowrate_menu, wxID_ANY, _L("Pass 1"), _L("Flow ratio test - Pass 1"),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->calib_flowrate(false, 1); }, "", nullptr,
|
||||
[this]() {return m_plater->is_view3D_shown();; }, this);
|
||||
append_menu_item(flowrate_menu, wxID_ANY, _L("Pass 2"), _L("Flow rate test - Pass 2"),
|
||||
append_menu_item(flowrate_menu, wxID_ANY, _L("Pass 2"), _L("Flow ratio test - Pass 2"),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->calib_flowrate(false, 2); }, "", nullptr,
|
||||
[this]() {return m_plater->is_view3D_shown();; }, this);
|
||||
flowrate_menu->AppendSeparator();
|
||||
append_menu_item(flowrate_menu, wxID_ANY, _L("YOLO (Recommended)"), _L("Orca YOLO flowrate calibration, 0.01 step"),
|
||||
append_menu_item(flowrate_menu, wxID_ANY, _L("YOLO (Recommended)"), _L("Orca YOLO flowratio calibration, 0.01 step"),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->calib_flowrate(true, 1); }, "", nullptr,
|
||||
[this]() {return m_plater->is_view3D_shown();; }, this);
|
||||
append_menu_item(flowrate_menu, wxID_ANY, _L("YOLO (perfectionist version)"), _L("Orca YOLO flowrate calibration, 0.005 step"),
|
||||
append_menu_item(flowrate_menu, wxID_ANY, _L("YOLO (perfectionist version)"), _L("Orca YOLO flowratio calibration, 0.005 step"),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->calib_flowrate(true, 2); }, "", nullptr,
|
||||
[this]() {return m_plater->is_view3D_shown();; }, this);
|
||||
m_topbar->GetCalibMenu()->AppendSubMenu(flowrate_menu, _L("Flow rate"));
|
||||
m_topbar->GetCalibMenu()->AppendSubMenu(flowrate_menu, _L("Flow ratio"));
|
||||
|
||||
// Retraction test
|
||||
append_menu_item(m_topbar->GetCalibMenu(), wxID_ANY, _L("Retraction test"), _L("Retraction test"),
|
||||
|
||||
+28
-23
@@ -469,7 +469,6 @@ struct Sidebar::priv
|
||||
ScalableButton * m_bpButton_ams_filament;
|
||||
ScalableButton * m_bpButton_set_filament;
|
||||
int m_menu_filament_id = -1;
|
||||
int filament_area_height;
|
||||
wxScrolledWindow* m_panel_filament_content;
|
||||
wxScrolledWindow* m_scrolledWindow_filament_content;
|
||||
wxStaticLine* m_staticline2;
|
||||
@@ -2106,14 +2105,10 @@ Sidebar::Sidebar(Plater *parent)
|
||||
bSizer39->AddSpacer(FromDIP(SidebarProps::TitlebarMargin()));
|
||||
|
||||
// add filament content
|
||||
// ORCA use a height with user preference
|
||||
int filament_count_user = std::stoi(wxGetApp().app_config->get("filaments_area_preferred_count"));
|
||||
p->filament_area_height = std::ceil(filament_count_user * 0.5) * (30 + SidebarProps::ElementSpacing()) - SidebarProps::ElementSpacing();
|
||||
|
||||
p->m_panel_filament_content = new wxScrolledWindow( p->scrolled, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
|
||||
p->m_panel_filament_content->SetScrollbars(0, 100, 1, 2);
|
||||
p->m_panel_filament_content->SetScrollRate(0, 5);
|
||||
p->m_panel_filament_content->SetMaxSize(wxSize{-1, FromDIP(p->filament_area_height)}); // ORCA
|
||||
//p->m_panel_filament_content->SetMaxSize(wxSize{-1, FromDIP(174)});
|
||||
p->m_panel_filament_content->SetBackgroundColour(wxColour(255, 255, 255));
|
||||
|
||||
//wxBoxSizer* bSizer_filament_content;
|
||||
@@ -2135,10 +2130,9 @@ Sidebar::Sidebar(Plater *parent)
|
||||
sizer_filaments2->Add(p->sizer_filaments, 0, wxEXPAND, 0);
|
||||
p->m_panel_filament_content->SetSizer(sizer_filaments2);
|
||||
p->m_panel_filament_content->Layout();
|
||||
auto min_size = sizer_filaments2->GetMinSize();
|
||||
if (min_size.y > p->m_panel_filament_content->GetMaxHeight())
|
||||
min_size.y = p->m_panel_filament_content->GetMaxHeight();
|
||||
p->m_panel_filament_content->SetMinSize(min_size);
|
||||
|
||||
update_filaments_area_height(); // ORCA
|
||||
|
||||
scrolled_sizer->Add(p->m_panel_filament_content, 0, wxEXPAND | wxTOP | wxBOTTOM, FromDIP(SidebarProps::ContentMarginV())); // ORCA use vertical margin on parent otherwise it shows scrollbar even on 1 filament
|
||||
}
|
||||
|
||||
@@ -2776,6 +2770,23 @@ void Sidebar::change_top_border_for_mode_sizer(bool increase_border)
|
||||
#endif
|
||||
}
|
||||
|
||||
void Sidebar::update_filaments_area_height()
|
||||
// ORCA
|
||||
{
|
||||
// ORCA use a height with user preference
|
||||
auto left_sizer = p->sizer_filaments->GetItem((size_t) 0)->GetSizer();
|
||||
auto combo_sizer = left_sizer->GetItem((size_t) 0)->GetSizer();
|
||||
int preferred_rows = std::ceil(0.5 * std::stoi(wxGetApp().app_config->get("filaments_area_preferred_count")));
|
||||
auto height_with_borders = combo_sizer->GetSize().GetHeight(); // gets height from sizer instead static numbers
|
||||
p->m_panel_filament_content->SetMaxSize(wxSize{-1, preferred_rows * height_with_borders});
|
||||
|
||||
// fixes wxScrolledWindow not shrinks its height to content size
|
||||
auto min_size = p->m_panel_filament_content->GetSizer()->GetMinSize();
|
||||
if (min_size.y > p->m_panel_filament_content->GetMaxHeight())
|
||||
min_size.y = p->m_panel_filament_content->GetMaxHeight();
|
||||
p->m_panel_filament_content->SetMinSize({-1, min_size.y});
|
||||
}
|
||||
|
||||
void Sidebar::msw_rescale()
|
||||
{
|
||||
SetMinSize(wxSize(42 * wxGetApp().em_unit(), -1));
|
||||
@@ -2846,6 +2857,9 @@ void Sidebar::msw_rescale()
|
||||
for (PlaterPresetComboBox* combo : p->combos_filament)
|
||||
combo->msw_rescale();
|
||||
|
||||
p->m_panel_filament_content->Layout();
|
||||
update_filaments_area_height(); // ORCA resize after combos scaled
|
||||
|
||||
// BBS
|
||||
//p->frequently_changed_parameters->msw_rescale();
|
||||
//obj_list()->msw_rescale();
|
||||
@@ -3024,10 +3038,7 @@ void Sidebar::on_filament_count_change(size_t num_filaments)
|
||||
}
|
||||
}
|
||||
|
||||
auto min_size = p->m_panel_filament_content->GetSizer()->GetMinSize();
|
||||
if (min_size.y > p->m_panel_filament_content->GetMaxHeight())
|
||||
min_size.y = p->m_panel_filament_content->GetMaxHeight();
|
||||
p->m_panel_filament_content->SetMinSize(min_size);
|
||||
update_filaments_area_height(); // ORCA
|
||||
|
||||
Layout();
|
||||
p->m_panel_filament_title->Refresh();
|
||||
@@ -3087,10 +3098,7 @@ void Sidebar::on_filaments_delete(size_t filament_id)
|
||||
p->combos_filament[idx]->update();
|
||||
}
|
||||
|
||||
auto min_size = p->m_panel_filament_content->GetSizer()->GetMinSize();
|
||||
if (min_size.y > p->m_panel_filament_content->GetMaxHeight())
|
||||
min_size.y = p->m_panel_filament_content->GetMaxHeight();
|
||||
p->m_panel_filament_content->SetMinSize(min_size);
|
||||
update_filaments_area_height(); // ORCA
|
||||
|
||||
Layout();
|
||||
p->m_panel_filament_title->Refresh();
|
||||
@@ -3524,11 +3532,8 @@ void Sidebar::sync_ams_list(bool is_from_big_sync_btn)
|
||||
for (auto& c : p->combos_filament)
|
||||
c->update();
|
||||
// Expand filament list
|
||||
p->m_panel_filament_content->SetMaxSize({-1, FromDIP(p->filament_area_height)}); // ORCA
|
||||
auto min_size = p->m_panel_filament_content->GetSizer()->GetMinSize();
|
||||
if (min_size.y > p->m_panel_filament_content->GetMaxHeight())
|
||||
min_size.y = p->m_panel_filament_content->GetMaxHeight();
|
||||
p->m_panel_filament_content->SetMinSize({-1, min_size.y});
|
||||
update_filaments_area_height(); // ORCA
|
||||
|
||||
// BBS:Synchronized consumables information
|
||||
// auto calculation of flushing volumes
|
||||
for (int i = 0; i < p->combos_filament.size(); ++i) {
|
||||
|
||||
@@ -172,6 +172,7 @@ public:
|
||||
void set_bed_type_accord_combox(BedType bed_type);
|
||||
bool reset_bed_type_combox_choices(bool is_sidebar_init = false);
|
||||
void change_top_border_for_mode_sizer(bool increase_border);
|
||||
void update_filaments_area_height();
|
||||
void msw_rescale();
|
||||
void sys_color_changed();
|
||||
void search();
|
||||
|
||||
@@ -318,13 +318,10 @@ wxBoxSizer *PreferencesDialog::create_item_language_combobox(wxString title, wxS
|
||||
|
||||
m_current_language_selected = combobox->GetSelection();
|
||||
if (m_current_language_selected >= 0 && m_current_language_selected < vlist.size()) {
|
||||
app_config->set(param, vlist[m_current_language_selected]->CanonicalName.ToUTF8().data());
|
||||
|
||||
wxGetApp().load_language(vlist[m_current_language_selected]->CanonicalName, false);
|
||||
Close();
|
||||
// Reparent(nullptr);
|
||||
GetParent()->RemoveChild(this);
|
||||
wxGetApp().recreate_GUI(_L("Changing application language"));
|
||||
m_pending_language = vlist[m_current_language_selected]->CanonicalName.ToUTF8().data();
|
||||
m_recreate_GUI = true;
|
||||
EndModal(wxID_OK);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -592,6 +589,14 @@ wxBoxSizer *PreferencesDialog::create_item_spinctrl(wxString title, wxString tit
|
||||
e.Skip();
|
||||
});
|
||||
|
||||
input->Bind(wxEVT_SPINCTRL, [this, param, input, onchange](wxCommandEvent& e) {
|
||||
auto value = input->GetValue();
|
||||
app_config->set(param, std::to_string(value));
|
||||
app_config->save();
|
||||
if (onchange != nullptr) onchange(value);
|
||||
e.Skip();
|
||||
});
|
||||
|
||||
input->Bind(wxEVT_KILL_FOCUS, [this, param, input, onchange](wxFocusEvent &e) {
|
||||
auto value = input->GetValue();
|
||||
app_config->set(param, std::to_string(value));
|
||||
@@ -1398,7 +1403,13 @@ void PreferencesDialog::create_items()
|
||||
"group_filament_presets", {_L("All"), _L("None"), _L("By type"), _L("By vendor")}, [](wxString value) {wxGetApp().plater()->sidebar().update_presets(Preset::TYPE_FILAMENT);});
|
||||
g_sizer->Add(item_filament_preset_grouping);
|
||||
|
||||
auto item_filament_area_height = create_item_spinctrl(_L("Optimize filaments area height for..."), _L("(Requires restart)"), _L("filaments"), _L("Optimizes filament area maximum height by chosen filament count."), "filaments_area_preferred_count", 8, 99);
|
||||
// prevent burst calling on keyboard / spin events
|
||||
m_filament_height_timer.Bind(wxEVT_TIMER, [this](wxTimerEvent&) {
|
||||
wxGetApp().plater()->sidebar().update_filaments_area_height();
|
||||
UpdateSidebarLayout();
|
||||
});
|
||||
auto item_filament_area_height = create_item_spinctrl(_L("Optimize filaments area height for..."), "", _L("filaments"), _L("Optimizes filament area maximum height by chosen filament count."),
|
||||
"filaments_area_preferred_count", 8, 99, [this](int value) {m_filament_height_timer.StartOnce(500);});
|
||||
g_sizer->Add(item_filament_area_height);
|
||||
|
||||
//// GENERAL > Features
|
||||
@@ -1950,4 +1961,22 @@ wxBoxSizer* PreferencesDialog::create_debug_page()
|
||||
return bSizer;
|
||||
}
|
||||
|
||||
void PreferencesDialog::UpdateSidebarLayout()
|
||||
{
|
||||
Plater* plater = wxGetApp().plater();
|
||||
if (!plater) return;
|
||||
|
||||
Sidebar& sidebar = plater->sidebar();
|
||||
|
||||
sidebar.Freeze();
|
||||
|
||||
sidebar.Layout();
|
||||
//plater->Layout();
|
||||
//wxGetApp().mainframe->Layout();
|
||||
|
||||
sidebar.Thaw();
|
||||
|
||||
plater->PostSizeEvent();
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/timer.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <map>
|
||||
@@ -43,10 +44,12 @@ protected:
|
||||
// bool m_settings_layout_changed {false};
|
||||
bool m_seq_top_layer_only_changed{false};
|
||||
bool m_recreate_GUI{false};
|
||||
std::string m_pending_language;
|
||||
|
||||
public:
|
||||
bool seq_top_layer_only_changed() const { return m_seq_top_layer_only_changed; }
|
||||
bool recreate_GUI() const { return m_recreate_GUI; }
|
||||
const std::string& pending_language() const { return m_pending_language; }
|
||||
void on_dpi_changed(const wxRect &suggested_rect) override;
|
||||
|
||||
public:
|
||||
@@ -60,6 +63,7 @@ public:
|
||||
~PreferencesDialog();
|
||||
|
||||
wxString m_backup_interval_time;
|
||||
wxTimer m_filament_height_timer;
|
||||
|
||||
void create();
|
||||
|
||||
@@ -106,6 +110,8 @@ public:
|
||||
void create_shortcuts_page();
|
||||
wxBoxSizer* create_debug_page();
|
||||
|
||||
void UpdateSidebarLayout();
|
||||
|
||||
// BBS
|
||||
void create_select_domain_widget();
|
||||
|
||||
|
||||
@@ -639,7 +639,7 @@ wxBoxSizer* PrintOptionsDialog::create_settings_group(wxWindow* parent)
|
||||
ai_refine_sizer->Add(line_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(18));
|
||||
|
||||
line_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
text_spaghetti_detection_caption0 = new Label(ai_refine_panel, _L("Detect spaghetti failure(scattered lose filament)."));
|
||||
text_spaghetti_detection_caption0 = new Label(ai_refine_panel, _L("Detect spaghetti failures (scattered lose filament)."));
|
||||
text_spaghetti_detection_caption0->SetFont(Label::Body_12);
|
||||
text_spaghetti_detection_caption0->SetForegroundColour(STATIC_TEXT_CAPTION_COL);
|
||||
text_spaghetti_detection_caption0->Wrap(-1);
|
||||
|
||||
@@ -1668,7 +1668,7 @@ void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* pres
|
||||
// process changes of extruders count
|
||||
if (type == Preset::TYPE_PRINTER && old_pt == ptFFF &&
|
||||
old_config.opt<ConfigOptionFloats>("nozzle_diameter")->values.size() != new_config.opt<ConfigOptionFloats>("nozzle_diameter")->values.size()) {
|
||||
wxString local_label = _L("Extruders count");
|
||||
wxString local_label = _L("Extruder count");
|
||||
wxString old_val = from_u8((boost::format("%1%") % old_config.opt<ConfigOptionFloats>("nozzle_diameter")->values.size()).str());
|
||||
wxString new_val = from_u8((boost::format("%1%") % new_config.opt<ConfigOptionFloats>("nozzle_diameter")->values.size()).str());
|
||||
|
||||
@@ -2228,7 +2228,7 @@ void DiffPresetDialog::update_tree()
|
||||
// process changes of extruders count
|
||||
if (type == Preset::TYPE_PRINTER && left_pt == ptFFF &&
|
||||
left_config.opt<ConfigOptionStrings>("extruder_colour")->values.size() != right_congig.opt<ConfigOptionStrings>("extruder_colour")->values.size()) {
|
||||
wxString local_label = _L("Extruders count");
|
||||
wxString local_label = _L("Extruder count");
|
||||
wxString left_val = from_u8((boost::format("%1%") % left_config.opt<ConfigOptionStrings>("extruder_colour")->values.size()).str());
|
||||
wxString right_val = from_u8((boost::format("%1%") % right_congig.opt<ConfigOptionStrings>("extruder_colour")->values.size()).str());
|
||||
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
#include <wx/dcclient.h>
|
||||
#include <wx/settings.h>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#ifdef __linux__
|
||||
#include <fontconfig/fontconfig.h>
|
||||
#endif
|
||||
|
||||
|
||||
wxFont Label::sysFont(int size, bool bold)
|
||||
@@ -58,27 +61,46 @@ wxFont Label::Body_10;
|
||||
wxFont Label::Body_9;
|
||||
wxFont Label::Body_8;
|
||||
|
||||
// Check if a font family is already available via fontconfig.
|
||||
#ifdef __linux__
|
||||
static bool fc_font_available(const char *family_name)
|
||||
{
|
||||
FcPattern *pat = FcPatternCreate();
|
||||
if (!pat)
|
||||
return false;
|
||||
FcPatternAddString(pat, FC_FAMILY, (const FcChar8 *) family_name);
|
||||
FcResult res;
|
||||
FcPattern *match = FcFontMatch(nullptr, pat, &res);
|
||||
bool available = false;
|
||||
if (match) {
|
||||
FcChar8 *matched_family = nullptr;
|
||||
if (FcPatternGetString(match, FC_FAMILY, 0, &matched_family) == FcResultMatch && matched_family)
|
||||
available = (strcasecmp((const char *) matched_family, family_name) == 0);
|
||||
FcPatternDestroy(match);
|
||||
}
|
||||
FcPatternDestroy(pat);
|
||||
return available;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Label::initSysFont()
|
||||
{
|
||||
#if defined(__linux__) || defined(_WIN32)
|
||||
const std::string &resource_path = Slic3r::resources_dir();
|
||||
wxString font_path = wxString::FromUTF8(resource_path + "/fonts/HarmonyOS_Sans_SC_Bold.ttf");
|
||||
bool result = wxFont::AddPrivateFont(font_path);
|
||||
// BOOST_LOG_TRIVIAL(info) << boost::format("add font of HarmonyOS_Sans_SC_Bold returns %1%")%result;
|
||||
// printf("add font of HarmonyOS_Sans_SC_Bold returns %d\n", result);
|
||||
font_path = wxString::FromUTF8(resource_path + "/fonts/HarmonyOS_Sans_SC_Regular.ttf");
|
||||
result = wxFont::AddPrivateFont(font_path);
|
||||
// BOOST_LOG_TRIVIAL(info) << boost::format("add font of HarmonyOS_Sans_SC_Regular returns %1%")%result;
|
||||
// printf("add font of HarmonyOS_Sans_SC_Regular returns %d\n", result);
|
||||
// Adding NanumGothic Regular and Bold
|
||||
font_path = wxString::FromUTF8(resource_path + "/fonts/NanumGothic-Regular.ttf");
|
||||
result = wxFont::AddPrivateFont(font_path);
|
||||
// BOOST_LOG_TRIVIAL(info) << boost::format("add font of NanumGothic-Regular returns %1%")%result;
|
||||
// printf("add font of NanumGothic-Regular returns %d\n", result);
|
||||
font_path = wxString::FromUTF8(resource_path + "/fonts/NanumGothic-Bold.ttf");
|
||||
result = wxFont::AddPrivateFont(font_path);
|
||||
// BOOST_LOG_TRIVIAL(info) << boost::format("add font of NanumGothic-Bold returns %1%")%result;
|
||||
// printf("add font of NanumGothic-Bold returns %d\n", result);
|
||||
// On Linux, skip AddPrivateFont for fonts already known to fontconfig
|
||||
// (e.g. installed system-wide in a Flatpak). Calling AddPrivateFont
|
||||
// triggers a Pango crash in ensure_faces() on Pango >= 1.48 (GNOME 49+),
|
||||
// because FcConfigAppFontAddFile invalidates Pango's cached font map.
|
||||
bool load_fonts = true;
|
||||
#ifdef __linux__
|
||||
load_fonts = !fc_font_available("HarmonyOS Sans SC") || !fc_font_available("NanumGothic");
|
||||
#endif
|
||||
if (load_fonts) {
|
||||
const std::string &resource_path = Slic3r::resources_dir();
|
||||
wxFont::AddPrivateFont(wxString::FromUTF8(resource_path + "/fonts/HarmonyOS_Sans_SC_Bold.ttf"));
|
||||
wxFont::AddPrivateFont(wxString::FromUTF8(resource_path + "/fonts/HarmonyOS_Sans_SC_Regular.ttf"));
|
||||
wxFont::AddPrivateFont(wxString::FromUTF8(resource_path + "/fonts/NanumGothic-Regular.ttf"));
|
||||
wxFont::AddPrivateFont(wxString::FromUTF8(resource_path + "/fonts/NanumGothic-Bold.ttf"));
|
||||
}
|
||||
#endif
|
||||
Head_48 = Label::sysFont(48, true);
|
||||
Head_32 = Label::sysFont(32, true);
|
||||
|
||||
@@ -67,7 +67,7 @@ std::vector<wxString> make_shaper_type_labels()
|
||||
}
|
||||
|
||||
PA_Calibration_Dlg::PA_Calibration_Dlg(wxWindow* parent, wxWindowID id, Plater* plater)
|
||||
: DPIDialog(parent, id, _L("PA Calibration"), wxDefaultPosition, parent->FromDIP(wxSize(-1, 280)), wxDEFAULT_DIALOG_STYLE), m_plater(plater)
|
||||
: DPIDialog(parent, id, _L("PA Calibration"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE), m_plater(plater)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE); // make sure background color set for dialog
|
||||
SetForegroundColour(wxColour("#363636"));
|
||||
@@ -196,6 +196,7 @@ PA_Calibration_Dlg::PA_Calibration_Dlg(wxWindow* parent, wxWindowID id, Plater*
|
||||
|
||||
Layout();
|
||||
Fit();
|
||||
v_sizer->SetSizeHints(this);
|
||||
}
|
||||
|
||||
PA_Calibration_Dlg::~PA_Calibration_Dlg() {
|
||||
@@ -328,7 +329,7 @@ enum FILAMENT_TYPE : int
|
||||
};
|
||||
|
||||
Temp_Calibration_Dlg::Temp_Calibration_Dlg(wxWindow* parent, wxWindowID id, Plater* plater)
|
||||
: DPIDialog(parent, id, _L("Temperature calibration"), wxDefaultPosition, parent->FromDIP(wxSize(-1, 280)), wxDEFAULT_DIALOG_STYLE), m_plater(plater)
|
||||
: DPIDialog(parent, id, _L("Temperature calibration"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE), m_plater(plater)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE); // make sure background color set for dialog
|
||||
SetForegroundColour(wxColour("#363636"));
|
||||
@@ -409,6 +410,7 @@ Temp_Calibration_Dlg::Temp_Calibration_Dlg(wxWindow* parent, wxWindowID id, Plat
|
||||
|
||||
Layout();
|
||||
Fit();
|
||||
v_sizer->SetSizeHints(this);
|
||||
|
||||
auto validate_text = [](TextInput* ti){
|
||||
unsigned long t = 0;
|
||||
@@ -518,7 +520,7 @@ void Temp_Calibration_Dlg::on_dpi_changed(const wxRect& suggested_rect) {
|
||||
//
|
||||
|
||||
MaxVolumetricSpeed_Test_Dlg::MaxVolumetricSpeed_Test_Dlg(wxWindow* parent, wxWindowID id, Plater* plater)
|
||||
: DPIDialog(parent, id, _L("Max volumetric speed test"), wxDefaultPosition, parent->FromDIP(wxSize(-1, 280)), wxDEFAULT_DIALOG_STYLE), m_plater(plater)
|
||||
: DPIDialog(parent, id, _L("Max volumetric speed test"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE), m_plater(plater)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE); // make sure background color set for dialog
|
||||
SetForegroundColour(wxColour("#363636"));
|
||||
@@ -587,6 +589,7 @@ MaxVolumetricSpeed_Test_Dlg::MaxVolumetricSpeed_Test_Dlg(wxWindow* parent, wxWin
|
||||
|
||||
Layout();
|
||||
Fit();
|
||||
v_sizer->SetSizeHints(this);
|
||||
}
|
||||
|
||||
MaxVolumetricSpeed_Test_Dlg::~MaxVolumetricSpeed_Test_Dlg() {
|
||||
@@ -622,7 +625,7 @@ void MaxVolumetricSpeed_Test_Dlg::on_dpi_changed(const wxRect& suggested_rect) {
|
||||
//
|
||||
|
||||
VFA_Test_Dlg::VFA_Test_Dlg(wxWindow* parent, wxWindowID id, Plater* plater)
|
||||
: DPIDialog(parent, id, _L("VFA test"), wxDefaultPosition, parent->FromDIP(wxSize(-1, 280)), wxDEFAULT_DIALOG_STYLE)
|
||||
: DPIDialog(parent, id, _L("VFA test"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE)
|
||||
, m_plater(plater)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE); // make sure background color set for dialog
|
||||
@@ -694,6 +697,7 @@ VFA_Test_Dlg::VFA_Test_Dlg(wxWindow* parent, wxWindowID id, Plater* plater)
|
||||
|
||||
Layout();
|
||||
Fit();
|
||||
v_sizer->SetSizeHints(this);
|
||||
}
|
||||
|
||||
VFA_Test_Dlg::~VFA_Test_Dlg()
|
||||
@@ -731,7 +735,7 @@ void VFA_Test_Dlg::on_dpi_changed(const wxRect& suggested_rect)
|
||||
//
|
||||
|
||||
Retraction_Test_Dlg::Retraction_Test_Dlg(wxWindow* parent, wxWindowID id, Plater* plater)
|
||||
: DPIDialog(parent, id, _L("Retraction test"), wxDefaultPosition, parent->FromDIP(wxSize(-1, 280)), wxDEFAULT_DIALOG_STYLE), m_plater(plater)
|
||||
: DPIDialog(parent, id, _L("Retraction test"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE), m_plater(plater)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE); // make sure background color set for dialog
|
||||
SetForegroundColour(wxColour("#363636"));
|
||||
@@ -802,6 +806,7 @@ Retraction_Test_Dlg::Retraction_Test_Dlg(wxWindow* parent, wxWindowID id, Plater
|
||||
|
||||
Layout();
|
||||
Fit();
|
||||
v_sizer->SetSizeHints(this);
|
||||
}
|
||||
|
||||
Retraction_Test_Dlg::~Retraction_Test_Dlg() {
|
||||
@@ -836,7 +841,7 @@ void Retraction_Test_Dlg::on_dpi_changed(const wxRect& suggested_rect) {
|
||||
//
|
||||
|
||||
Input_Shaping_Freq_Test_Dlg::Input_Shaping_Freq_Test_Dlg(wxWindow* parent, wxWindowID id, Plater* plater)
|
||||
: DPIDialog(parent, id, _L("Input shaping Frequency test"), wxDefaultPosition, parent->FromDIP(wxSize(-1, 280)), wxDEFAULT_DIALOG_STYLE), m_plater(plater)
|
||||
: DPIDialog(parent, id, _L("Input shaping Frequency test"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE), m_plater(plater)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE); // make sure background color set for dialog
|
||||
SetForegroundColour(wxColour("#363636"));
|
||||
@@ -981,6 +986,7 @@ Input_Shaping_Freq_Test_Dlg::Input_Shaping_Freq_Test_Dlg(wxWindow* parent, wxWin
|
||||
|
||||
Layout();
|
||||
Fit();
|
||||
v_sizer->SetSizeHints(this);
|
||||
}
|
||||
|
||||
Input_Shaping_Freq_Test_Dlg::~Input_Shaping_Freq_Test_Dlg() {
|
||||
@@ -1053,7 +1059,7 @@ void Input_Shaping_Freq_Test_Dlg::on_dpi_changed(const wxRect& suggested_rect) {
|
||||
//
|
||||
|
||||
Input_Shaping_Damp_Test_Dlg::Input_Shaping_Damp_Test_Dlg(wxWindow* parent, wxWindowID id, Plater* plater)
|
||||
: DPIDialog(parent, id, _L("Input shaping Damp test"), wxDefaultPosition, parent->FromDIP(wxSize(-1, 280)), wxDEFAULT_DIALOG_STYLE), m_plater(plater)
|
||||
: DPIDialog(parent, id, _L("Input shaping Damp test"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE), m_plater(plater)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE); // make sure background color set for dialog
|
||||
SetForegroundColour(wxColour("#363636"));
|
||||
@@ -1179,6 +1185,7 @@ Input_Shaping_Damp_Test_Dlg::Input_Shaping_Damp_Test_Dlg(wxWindow* parent, wxWin
|
||||
|
||||
Layout();
|
||||
Fit();
|
||||
v_sizer->SetSizeHints(this);
|
||||
}
|
||||
|
||||
Input_Shaping_Damp_Test_Dlg::~Input_Shaping_Damp_Test_Dlg() {
|
||||
@@ -1246,7 +1253,7 @@ void Input_Shaping_Damp_Test_Dlg::on_dpi_changed(const wxRect& suggested_rect) {
|
||||
//
|
||||
|
||||
Cornering_Test_Dlg::Cornering_Test_Dlg(wxWindow* parent, wxWindowID id, Plater* plater)
|
||||
: DPIDialog(parent, id, _L("Cornering test"), wxDefaultPosition, parent->FromDIP(wxSize(-1, 280)), wxDEFAULT_DIALOG_STYLE), m_plater(plater)
|
||||
: DPIDialog(parent, id, _L("Cornering test"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE), m_plater(plater)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE); // make sure background color set for dialog
|
||||
SetForegroundColour(wxColour("#363636"));
|
||||
@@ -1374,6 +1381,7 @@ Cornering_Test_Dlg::Cornering_Test_Dlg(wxWindow* parent, wxWindowID id, Plater*
|
||||
|
||||
Layout();
|
||||
Fit();
|
||||
v_sizer->SetSizeHints(this);
|
||||
}
|
||||
|
||||
Cornering_Test_Dlg::~Cornering_Test_Dlg() {
|
||||
|
||||
@@ -571,7 +571,18 @@ bool MoonrakerPrinterAgent::fetch_filament_info(std::string dev_id)
|
||||
std::vector<AmsTrayData> trays;
|
||||
int max_lane_index = 0;
|
||||
|
||||
// Try Happy Hare first (more widely adopted, supports more filament changers)
|
||||
// Try Moonraker filament data (more generic, supports any filament changer
|
||||
// software that reports lane data to Moonraker like AFC and recent Happy
|
||||
// Hare as of Feb 15, 2026)
|
||||
if (fetch_moonraker_filament_data(trays, max_lane_index)) {
|
||||
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::fetch_filament_info: Detected Moonraker filament system with "
|
||||
<< (max_lane_index + 1) << " lanes";
|
||||
int ams_count = (max_lane_index + 4) / 4;
|
||||
build_ams_payload(ams_count, max_lane_index, trays);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Attempt Happy Hare first (more widely adopted, supports more filament changers)
|
||||
if (fetch_hh_filament_info(trays, max_lane_index)) {
|
||||
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::fetch_filament_info: Detected Happy Hare MMU with "
|
||||
<< (max_lane_index + 1) << " gates";
|
||||
@@ -580,17 +591,8 @@ bool MoonrakerPrinterAgent::fetch_filament_info(std::string dev_id)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fallback to AFC
|
||||
if (fetch_afc_filament_info(trays, max_lane_index)) {
|
||||
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::fetch_filament_info: Detected AFC with "
|
||||
<< (max_lane_index + 1) << " lanes";
|
||||
int ams_count = (max_lane_index + 4) / 4;
|
||||
build_ams_payload(ams_count, max_lane_index, trays);
|
||||
return true;
|
||||
}
|
||||
|
||||
// No MMU detected - this is normal for printers without MMU, not an error
|
||||
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::fetch_filament_info: No MMU system detected (neither HH nor AFC)";
|
||||
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::fetch_filament_info: No MMU system detected (neither HH nor Moonraker)";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -721,10 +723,10 @@ std::string MoonrakerPrinterAgent::normalize_color_value(const std::string& colo
|
||||
return normalized;
|
||||
}
|
||||
|
||||
// Fetch filament info from Armored Turtle AFC
|
||||
bool MoonrakerPrinterAgent::fetch_afc_filament_info(std::vector<AmsTrayData>& trays, int& max_lane_index)
|
||||
// Fetch filament info from moonraker database
|
||||
bool MoonrakerPrinterAgent::fetch_moonraker_filament_data(std::vector<AmsTrayData>& trays, int& max_lane_index)
|
||||
{
|
||||
// Fetch AFC lane data from Moonraker database
|
||||
// Fetch lane data from Moonraker database
|
||||
std::string url = join_url(device_info.base_url, "/server/database/item?namespace=lane_data");
|
||||
|
||||
std::string response_body;
|
||||
@@ -754,19 +756,19 @@ bool MoonrakerPrinterAgent::fetch_afc_filament_info(std::vector<AmsTrayData>& tr
|
||||
.perform_sync();
|
||||
|
||||
if (!success) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "MoonrakerPrinterAgent::fetch_afc_filament_info: Failed to fetch lane data: " << http_error;
|
||||
BOOST_LOG_TRIVIAL(warning) << "MoonrakerPrinterAgent::fetch_moonraker_filament_data: Failed to fetch lane data: " << http_error;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto json = nlohmann::json::parse(response_body, nullptr, false, true);
|
||||
if (json.is_discarded()) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "MoonrakerPrinterAgent::fetch_afc_filament_info: Invalid JSON response";
|
||||
BOOST_LOG_TRIVIAL(warning) << "MoonrakerPrinterAgent::fetch_moonraker_filament_data: Invalid JSON response";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Expected structure: { "result": { "namespace": "lane_data", "value": { "lane1": {...}, ... } } }
|
||||
if (!json.contains("result") || !json["result"].contains("value") || !json["result"]["value"].is_object()) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "MoonrakerPrinterAgent::fetch_afc_filament_info: Unexpected JSON structure or no lane_data found";
|
||||
BOOST_LOG_TRIVIAL(warning) << "MoonrakerPrinterAgent::fetch_moonraker_filament_data: Unexpected JSON structure or no lane_data found";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -812,7 +814,7 @@ bool MoonrakerPrinterAgent::fetch_afc_filament_info(std::vector<AmsTrayData>& tr
|
||||
}
|
||||
|
||||
if (trays.empty()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::fetch_afc_filament_info: No AFC lanes found";
|
||||
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::fetch_moonraker_filament_data: No lanes found";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ private:
|
||||
|
||||
// System-specific filament fetch methods
|
||||
bool fetch_hh_filament_info(std::vector<AmsTrayData>& trays, int& max_lane_index);
|
||||
bool fetch_afc_filament_info(std::vector<AmsTrayData>& trays, int& max_lane_index);
|
||||
bool fetch_moonraker_filament_data(std::vector<AmsTrayData>& trays, int& max_lane_index);
|
||||
|
||||
// JSON helper methods
|
||||
static std::string safe_json_string(const nlohmann::json& obj, const char* key);
|
||||
|
||||
Reference in New Issue
Block a user