mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-28 11:21:30 +00:00
Merge branch 'main' into belt/baseChanges
This commit is contained in:
@@ -1654,7 +1654,7 @@ msgid "Based on PrusaSlicer and BambuStudio"
|
||||
msgstr "Basierend auf PrusaSlicer und BambuStudio"
|
||||
|
||||
msgid "STEP files"
|
||||
msgstr "SETP Dateien"
|
||||
msgstr "STEP Dateien"
|
||||
|
||||
msgid "STL files"
|
||||
msgstr "STL Dateien"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Creality",
|
||||
"version": "02.03.02.60",
|
||||
"version": "02.03.02.61",
|
||||
"force_update": "0",
|
||||
"description": "Creality configurations",
|
||||
"machine_model_list": [
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -5,9 +5,9 @@
|
||||
"nozzle_diameter": "0.2;0.4;0.6;0.8",
|
||||
"machine_tech": "FFF",
|
||||
"family": "Creality",
|
||||
"bed_model": "creality_k1_buildplate_model.stl",
|
||||
"bed_model": "creality_k2_buildplate_model.stl",
|
||||
"default_bed_type": "Textured PEI Plate",
|
||||
"bed_texture": "creality_k1_buildplate_texture.png",
|
||||
"bed_texture": "creality_k2_buildplate_texture.png",
|
||||
"hotend_model": "",
|
||||
"default_materials": "Creality Generic ABS @K2-all;Creality Generic ASA @K2-all;Creality Generic PETG @K2-all;Creality Generic PLA @K2-all;Creality Generic PLA High Speed @K2-all;Creality Generic PLA Matte @K2-all;Creality Generic PLA Silk @K2-all"
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/bin/bash
|
||||
# these are the CachyOS Linux specific build functions
|
||||
|
||||
# Additional Dev packages for OrcaSlicer
|
||||
export REQUIRED_DEV_PACKAGES=(
|
||||
cmake
|
||||
curl
|
||||
dbus
|
||||
eglexternalplatform
|
||||
extra-cmake-modules
|
||||
file
|
||||
gettext
|
||||
git
|
||||
glew
|
||||
gstreamer
|
||||
gtk3
|
||||
libmspack
|
||||
libsecret
|
||||
libspnav
|
||||
mesa
|
||||
ninja
|
||||
openssl
|
||||
texinfo
|
||||
wayland-protocols
|
||||
webkit2gtk
|
||||
wget
|
||||
)
|
||||
|
||||
if [[ -n "$UPDATE_LIB" ]]
|
||||
then
|
||||
echo -n -e "Updating linux ...\n"
|
||||
NEEDED_PKGS=()
|
||||
for PKG in "${REQUIRED_DEV_PACKAGES[@]}"; do
|
||||
pacman -Q "${PKG}" > /dev/null || NEEDED_PKGS+=("${PKG}")
|
||||
done
|
||||
|
||||
if [[ "${#NEEDED_PKGS[*]}" -gt 0 ]]; then
|
||||
sudo pacman -Syy --noconfirm "${NEEDED_PKGS[@]}"
|
||||
fi
|
||||
echo -e "done\n"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
export FOUND_GTK3_DEV
|
||||
FOUND_GTK3_DEV=$(pacman -Q gtk3)
|
||||
@@ -5,7 +5,7 @@ Icon=OrcaSlicer
|
||||
Exec=orca-slicer %U
|
||||
Terminal=false
|
||||
Type=Application
|
||||
MimeType=model/stl;model/3mf;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;application/x-amf;x-scheme-handler/orcaslicer;
|
||||
MimeType=model/stl;model/3mf;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;application/x-amf;x-scheme-handler/orcaslicer;model/step;
|
||||
Categories=Graphics;3DGraphics;Engineering;
|
||||
Keywords=3D;Printing;Slicer;slice;3D;printer;convert;gcode;stl;obj;amf;SLA
|
||||
StartupNotify=false
|
||||
|
||||
+14
-10
@@ -587,26 +587,30 @@ public:
|
||||
auto rhs_opt = static_cast<const ConfigOptionVector<T>*>(rhs);
|
||||
auto inherits_opt = static_cast<const ConfigOptionVector<T>*>(inherits);
|
||||
|
||||
if (inherits->size() != rhs->size())
|
||||
throw ConfigurationError("ConfigOptionVector::set_with_nil(): rhs size different with inherits size");
|
||||
if (stride <= 0)
|
||||
throw ConfigurationError("ConfigOptionVector::set_with_nil(): invalid stride");
|
||||
|
||||
this->values.resize(inherits->size(), this->values.front());
|
||||
// Tolerate legacy/transitional presets where vector sizes may diverge
|
||||
// (for example after reducing extruder/variant count).
|
||||
// Keep rhs as source of truth and nil-mark only on overlapping range.
|
||||
this->values = rhs_opt->values;
|
||||
|
||||
for (size_t i = 0; i < inherits_opt->size(); i= i+stride) {
|
||||
const size_t overlap_size = std::min(rhs_opt->size(), inherits_opt->size());
|
||||
|
||||
for (size_t i = 0; i < overlap_size; i += size_t(stride)) {
|
||||
const size_t group_size = std::min(size_t(stride), overlap_size - i);
|
||||
bool set_nil = true;
|
||||
for (size_t j = 0; j < stride; j++) {
|
||||
if (inherits_opt->values[i +j] != rhs_opt->values[i +j]) {
|
||||
for (size_t j = 0; j < group_size; ++j) {
|
||||
if (inherits_opt->values[i + j] != rhs_opt->values[i + j]) {
|
||||
set_nil = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < stride; j++) {
|
||||
for (size_t j = 0; j < group_size; ++j) {
|
||||
if (set_nil) {
|
||||
this->set_at_to_nil(i +j);
|
||||
this->set_at_to_nil(i + j);
|
||||
}
|
||||
else
|
||||
this->values[i +j] = rhs_opt->values[i +j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ void group_region_by_fuzzify(PerimeterGenerator& g)
|
||||
surfaces.push_back(&surface);
|
||||
}
|
||||
|
||||
if (cfg.type != FuzzySkinType::None) {
|
||||
if (cfg.type != FuzzySkinType::None && cfg.type != FuzzySkinType::Disabled_fuzzy) {
|
||||
g.has_fuzzy_skin = true;
|
||||
if (cfg.type != FuzzySkinType::External) {
|
||||
g.has_fuzzy_hole = true;
|
||||
|
||||
@@ -5461,8 +5461,10 @@ void GCodeProcessor::process_M1020(const GCodeReader::GCodeLine &line)
|
||||
BOOST_LOG_TRIVIAL(error) << "Invalid M1020 command (" << line.raw() << ").";
|
||||
}
|
||||
else {
|
||||
if (eid >= m_result.filaments_count)
|
||||
if (eid >= m_result.filaments_count) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Invalid M1020 command (" << line.raw() << ").";
|
||||
return;
|
||||
}
|
||||
process_filament_change(eid);
|
||||
}
|
||||
}
|
||||
@@ -5490,8 +5492,10 @@ void GCodeProcessor::process_T(const std::string_view command)
|
||||
BOOST_LOG_TRIVIAL(error) << "Invalid T command (" << command << ").";
|
||||
}
|
||||
else {
|
||||
if (eid >= m_result.filaments_count)
|
||||
if (eid >= m_result.filaments_count) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Invalid T command (" << command << ").";
|
||||
return;
|
||||
}
|
||||
process_filament_change(eid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -653,7 +653,7 @@ public:
|
||||
int match_quality = -1;
|
||||
for (; i < n; ++i)
|
||||
// Since we use the filament selection from Wizard, it's needed to control the preset visibility too
|
||||
if (m_presets[i].is_compatible) {
|
||||
if (m_presets[i].is_compatible && m_presets[i].is_visible) {
|
||||
int this_match_quality = prefered_condition(m_presets[i]);
|
||||
if (this_match_quality > match_quality) {
|
||||
if (match_quality == std::numeric_limits<int>::max())
|
||||
|
||||
@@ -734,10 +734,17 @@ void PresetBundle::reset_project_embedded_presets()
|
||||
Preset& current_printer = this->printers.get_selected_preset();
|
||||
const std::vector<std::string> &prefered_filament_profiles = current_printer.config.option<ConfigOptionStrings>("default_filament_profile")->values;
|
||||
const std::string prefered_filament_profile = prefered_filament_profiles.empty() ? std::string() : prefered_filament_profiles.front();
|
||||
if (!prefered_filament_profile.empty())
|
||||
filament_presets[i] = prefered_filament_profile;
|
||||
else
|
||||
filament_presets[i] = this->filaments.first_visible().name;
|
||||
if (!prefered_filament_profile.empty()) {
|
||||
// Check if preferred filament exists and is visible
|
||||
const Preset* preferred_preset = this->filaments.find_preset(prefered_filament_profile, false);
|
||||
if (preferred_preset && preferred_preset->is_visible) {
|
||||
filament_presets[i] = prefered_filament_profile;
|
||||
} else {
|
||||
// Fall back to first visible filament
|
||||
filament_presets[i] = this->filaments.first_visible().name;
|
||||
}
|
||||
} else
|
||||
filament_presets[i] = this->filaments.first_visible().name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1970,8 +1977,14 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
|
||||
initial_print_profile_name = prefered_print_profile;
|
||||
|
||||
const std::vector<std::string>& prefered_filament_profiles = preferred_printer->config.option<ConfigOptionStrings>("default_filament_profile")->values;
|
||||
if ((!initial_filament_profile_name.compare("Default Filament")) && (prefered_filament_profiles.size() > 0))
|
||||
initial_filament_profile_name = prefered_filament_profiles[0];
|
||||
if ((!initial_filament_profile_name.compare("Default Filament")) && (prefered_filament_profiles.size() > 0)) {
|
||||
// Check if preferred filament is visible
|
||||
const Preset* preferred_preset = this->filaments.find_preset(prefered_filament_profiles[0], false);
|
||||
if (preferred_preset && preferred_preset->is_visible) {
|
||||
initial_filament_profile_name = prefered_filament_profiles[0];
|
||||
}
|
||||
// If not visible, keep the default "Default Filament" which will be resolved later
|
||||
}
|
||||
}
|
||||
|
||||
// Selects the profile, leaves it to -1 if the initial profile name is empty or if it was not found.
|
||||
@@ -4452,7 +4465,7 @@ void PresetBundle::update_compatible(PresetSelectCompatibleType select_other_pri
|
||||
int operator()(const Preset &preset) const
|
||||
{
|
||||
// Don't match any properties of the "-- default --" profile or the external profiles when switching printer profile.
|
||||
if (preset.is_default || preset.is_external)
|
||||
if (preset.is_default || preset.is_external || !preset.is_visible)
|
||||
return 0;
|
||||
if (! m_prefered_alias.empty() && m_prefered_alias == preset.alias)
|
||||
// Matching an alias, always take this preset with priority.
|
||||
|
||||
@@ -9338,11 +9338,8 @@ void DynamicPrintConfig::update_values_to_printer_extruders(DynamicPrintConfig&
|
||||
//variant index
|
||||
variant_index[e_index] = get_index_for_extruder(e_index+1, id_name, extruder_type, nozzle_volume_type, variant_name);
|
||||
if (variant_index[e_index] < 0) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", Line %1%: could not found extruder_type %2%, nozzle_volume_type %3%, extruder_index %4%")
|
||||
%__LINE__ %s_keys_names_ExtruderType[extruder_type] % s_keys_names_NozzleVolumeType[nozzle_volume_type] % (e_index+1);
|
||||
assert(false);
|
||||
//for some updates happens in a invalid state(caused by popup window)
|
||||
//we need to avoid crash
|
||||
// Orca: This is expected during transient UI states (e.g. popup windows),
|
||||
// fall back to 0 silently.
|
||||
variant_index[e_index] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Slic3r
|
||||
int m_nozzle_id = -1;
|
||||
NozzleFlowType m_nozzle_flow = NozzleFlowType::S_FLOW;// 0-common 1-high flow
|
||||
NozzleType m_nozzle_type = NozzleType::ntUndefine;// 0-stainless_steel 1-hardened_steel 5-tungsten_carbide
|
||||
float m_diameter = 0.4f;// 0.2mm 0.4mm 0.6mm 0.8mm
|
||||
float m_diameter = 0.0f;// unknown until reported by the printer
|
||||
};
|
||||
|
||||
class DevNozzleSystem
|
||||
|
||||
@@ -4285,7 +4285,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
p = p->GetParent();
|
||||
auto *top_level_wnd = dynamic_cast<wxTopLevelWindow*>(p);
|
||||
//Orca: Set focus so hotkeys like 'tab' work when a notification is shown.
|
||||
if (top_level_wnd != nullptr && top_level_wnd->IsActive())
|
||||
//But don't steal focus from text input controls.
|
||||
wxWindow* focused = wxWindow::FindFocus();
|
||||
bool focus_in_text_ctrl = dynamic_cast<wxTextCtrl*>(focused) != nullptr;
|
||||
if (top_level_wnd != nullptr && top_level_wnd->IsActive() && !focus_in_text_ctrl)
|
||||
m_canvas->SetFocus();
|
||||
}
|
||||
m_mouse.position = pos.cast<double>();
|
||||
@@ -8485,11 +8488,27 @@ void GLCanvas3D::_render_return_toolbar() const
|
||||
ImVec2 margin = ImVec2(10.0f, 5.0f);
|
||||
|
||||
if (ImGui::ImageTextButton(real_size,_utf8(L("Return")).c_str(), m_return_toolbar.get_return_texture_id(), button_icon_size, uv0, uv1, -1, bg_col, tint_col, margin)) {
|
||||
if (m_canvas != nullptr)
|
||||
wxPostEvent(m_canvas, SimpleEvent(EVT_GLVIEWTOOLBAR_3D));
|
||||
const_cast<GLGizmosManager*>(&m_gizmos)->reset_all_states();
|
||||
wxGetApp().plater()->get_view3D_canvas3D()->get_gizmos_manager().reset_all_states();
|
||||
wxGetApp().plater()->get_view3D_canvas3D()->reload_scene(true);
|
||||
if (m_canvas != nullptr && !wxGetApp().is_closing()) {
|
||||
m_canvas->CallAfter([]() {
|
||||
auto& app = wxGetApp();
|
||||
if (app.is_closing())
|
||||
return;
|
||||
|
||||
auto* plater = app.plater();
|
||||
if (plater == nullptr)
|
||||
return;
|
||||
|
||||
plater->select_view_3D("3D");
|
||||
|
||||
auto* view3d_canvas = plater->get_view3D_canvas3D();
|
||||
if (view3d_canvas == nullptr)
|
||||
return;
|
||||
|
||||
view3d_canvas->get_gizmos_manager().reset_all_states();
|
||||
view3d_canvas->reload_scene(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
ImGui::PopStyleColor(5);
|
||||
ImGui::PopStyleVar(1);
|
||||
|
||||
@@ -752,18 +752,51 @@ wxMenuItem* MenuFactory::append_menu_item_settings(wxMenu* menu_)
|
||||
|
||||
wxMenuItem* MenuFactory::append_menu_item_change_type(wxMenu* menu)
|
||||
{
|
||||
return append_menu_item(menu, wxID_ANY, _L("Change type"), "",
|
||||
[](wxCommandEvent&) { obj_list()->change_part_type(); }, "", menu,
|
||||
[]() {
|
||||
wxDataViewItemArray selections;
|
||||
obj_list()->GetSelections(selections);
|
||||
if (selections.empty()) return false;
|
||||
for (const auto& it : selections) {
|
||||
if (!(obj_list()->GetModel()->GetItemType(it) & itVolume))
|
||||
return false; // non-volume present -> disable
|
||||
}
|
||||
return true;
|
||||
}, m_parent);
|
||||
const wxString menu_name = _L("Change type");
|
||||
|
||||
// Delete old menu item if exists
|
||||
const int item_id = menu->FindItem(menu_name);
|
||||
if (item_id != wxNOT_FOUND)
|
||||
menu->Destroy(item_id);
|
||||
|
||||
// Create submenu
|
||||
wxMenu* type_menu = new wxMenu();
|
||||
|
||||
struct TypeInfo {
|
||||
ModelVolumeType type;
|
||||
wxString label;
|
||||
};
|
||||
|
||||
std::vector<TypeInfo> types = {
|
||||
{ ModelVolumeType::MODEL_PART, _L("Part") },
|
||||
{ ModelVolumeType::NEGATIVE_VOLUME, _L("Negative Part") },
|
||||
{ ModelVolumeType::PARAMETER_MODIFIER, _L("Modifier") },
|
||||
{ ModelVolumeType::SUPPORT_BLOCKER, _L("Support Blocker") },
|
||||
{ ModelVolumeType::SUPPORT_ENFORCER, _L("Support Enforcer") }
|
||||
};
|
||||
|
||||
for (const auto& info : types) {
|
||||
wxMenuItem* item = append_menu_check_item(type_menu, wxID_ANY, info.label, "",
|
||||
[type = info.type](wxCommandEvent&) { obj_list()->set_volume_type(type); }, type_menu);
|
||||
|
||||
// Update checkmark dynamically when menu is shown - check all selected volumes
|
||||
m_parent->Bind(wxEVT_UPDATE_UI, [type = info.type](wxUpdateUIEvent& evt) {
|
||||
bool has_type = false;
|
||||
wxDataViewItemArray sels;
|
||||
obj_list()->GetSelections(sels);
|
||||
for (auto item : sels) {
|
||||
ModelVolumeType vol_type = obj_list()->GetModel()->GetVolumeType(item);
|
||||
if (vol_type == type) {
|
||||
has_type = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
evt.Check(has_type);
|
||||
}, item->GetId());
|
||||
}
|
||||
|
||||
menu->Append(wxID_ANY, menu_name, type_menu, _L("Change part type"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
wxMenuItem* MenuFactory::append_menu_item_instance_to_object(wxMenu* menu)
|
||||
|
||||
@@ -5538,6 +5538,136 @@ void ObjectList::change_part_type()
|
||||
return;
|
||||
}
|
||||
|
||||
ModelVolumeType ObjectList::get_selected_volume_type()
|
||||
{
|
||||
ModelVolume* volume = get_selected_model_volume();
|
||||
if (volume)
|
||||
return volume->type();
|
||||
return ModelVolumeType::INVALID;
|
||||
}
|
||||
|
||||
void ObjectList::set_volume_type(ModelVolumeType new_type)
|
||||
{
|
||||
struct VolumeSelection {
|
||||
int object_idx;
|
||||
ModelVolume* volume;
|
||||
};
|
||||
|
||||
std::vector<VolumeSelection> volumes;
|
||||
auto add_volume = [&volumes](int obj_idx, ModelVolume* volume) {
|
||||
if (volume == nullptr)
|
||||
return;
|
||||
auto it = std::find_if(volumes.begin(), volumes.end(), [volume](const VolumeSelection& other) { return other.volume == volume; });
|
||||
if (it == volumes.end())
|
||||
volumes.push_back({ obj_idx, volume });
|
||||
};
|
||||
|
||||
wxDataViewItemArray sels;
|
||||
GetSelections(sels);
|
||||
for (auto item : sels) {
|
||||
wxDataViewItem volume_item = item;
|
||||
ItemType type = m_objects_model->GetItemType(item);
|
||||
if (!(type & itVolume)) {
|
||||
if ((type & itSettings) && (m_objects_model->GetItemType(m_objects_model->GetParent(item)) & itVolume))
|
||||
volume_item = m_objects_model->GetParent(item);
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
const int obj_idx = m_objects_model->GetObjectIdByItem(volume_item);
|
||||
const int vol_idx = m_objects_model->GetVolumeIdByItem(volume_item);
|
||||
if (obj_idx < 0 || vol_idx < 0 || obj_idx >= m_objects->size())
|
||||
continue;
|
||||
|
||||
const int real_idx = m_objects_model->get_real_volume_index_in_3d(obj_idx, vol_idx);
|
||||
if (real_idx < 0 || real_idx >= (*m_objects)[obj_idx]->volumes.size())
|
||||
continue;
|
||||
|
||||
add_volume(obj_idx, (*m_objects)[obj_idx]->volumes[real_idx]);
|
||||
}
|
||||
|
||||
auto collect_from_canvas = [&add_volume](GLCanvas3D* canvas) {
|
||||
if (canvas == nullptr)
|
||||
return;
|
||||
const Selection& selection = canvas->get_selection();
|
||||
for (auto idx : selection.get_volume_idxs()) {
|
||||
const GLVolume* gl_volume = selection.get_volume(idx);
|
||||
if (gl_volume == nullptr || gl_volume->object_idx() < 0)
|
||||
continue;
|
||||
ModelVolume* volume = get_model_volume(*gl_volume, selection.get_model()->objects);
|
||||
add_volume(gl_volume->object_idx(), volume);
|
||||
}
|
||||
};
|
||||
|
||||
if (volumes.empty()) {
|
||||
collect_from_canvas(wxGetApp().plater()->canvas3D());
|
||||
if (volumes.empty()) {
|
||||
auto canvas_type = wxGetApp().plater()->get_current_canvas3D()->get_canvas_type();
|
||||
if (canvas_type == GLCanvas3D::ECanvasType::CanvasView3D && is_connectors_item_selected())
|
||||
collect_from_canvas(wxGetApp().plater()->get_view3D_canvas3D());
|
||||
}
|
||||
if (volumes.empty())
|
||||
return;
|
||||
}
|
||||
|
||||
const bool any_diff = std::any_of(volumes.begin(), volumes.end(),
|
||||
[new_type](const VolumeSelection& sel) { return sel.volume->type() != new_type; });
|
||||
|
||||
if (!any_diff)
|
||||
return;
|
||||
|
||||
if (new_type != ModelVolumeType::MODEL_PART) {
|
||||
std::map<int, int> total_part_cnt;
|
||||
std::map<int, int> selected_part_cnt;
|
||||
|
||||
for (const auto& sel : volumes) {
|
||||
if (total_part_cnt.find(sel.object_idx) == total_part_cnt.end()) {
|
||||
int count = 0;
|
||||
for (auto vol : (*m_objects)[sel.object_idx]->volumes)
|
||||
if (vol->type() == ModelVolumeType::MODEL_PART)
|
||||
++count;
|
||||
total_part_cnt.emplace(sel.object_idx, count);
|
||||
}
|
||||
if (sel.volume->type() == ModelVolumeType::MODEL_PART)
|
||||
++selected_part_cnt[sel.object_idx];
|
||||
}
|
||||
|
||||
for (const auto& sel : selected_part_cnt) {
|
||||
auto it = total_part_cnt.find(sel.first);
|
||||
if (it != total_part_cnt.end() && it->second > 0 && sel.second == it->second) {
|
||||
Slic3r::GUI::show_error(nullptr, _(L("The type of the last solid object part is not to be changed.")));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
take_snapshot("Change part type");
|
||||
|
||||
std::set<const ModelVolume*> changed_volumes;
|
||||
std::set<int> touched_objects;
|
||||
for (const auto& sel : volumes) {
|
||||
sel.volume->set_type(new_type);
|
||||
changed_volumes.insert(sel.volume);
|
||||
touched_objects.insert(sel.object_idx);
|
||||
}
|
||||
|
||||
wxDataViewItemArray new_selection;
|
||||
for (int obj_idx : touched_objects) {
|
||||
wxDataViewItemArray sel_items = reorder_volumes_and_get_selection(obj_idx, [&changed_volumes](const ModelVolume* volume) {
|
||||
return changed_volumes.find(volume) != changed_volumes.end();
|
||||
});
|
||||
for (const auto& item : sel_items)
|
||||
new_selection.push_back(item);
|
||||
}
|
||||
|
||||
if (!new_selection.IsEmpty()) {
|
||||
m_prevent_list_events = true;
|
||||
UnselectAll();
|
||||
SetSelections(new_selection);
|
||||
m_prevent_list_events = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectList::last_volume_is_deleted(const int obj_idx)
|
||||
{
|
||||
// BBS: object (obj_idx calc in obj list) is already removed from m_objects in Plater::priv::remove().
|
||||
|
||||
@@ -414,6 +414,8 @@ public:
|
||||
|
||||
ModelVolume* get_selected_model_volume();
|
||||
void change_part_type();
|
||||
void set_volume_type(ModelVolumeType new_type);
|
||||
ModelVolumeType get_selected_volume_type();
|
||||
|
||||
void last_volume_is_deleted(const int obj_idx);
|
||||
void update_and_show_object_settings_item();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
@@ -32,6 +33,15 @@ wxDEFINE_EVENT(EVT_VOLUME_ATTACHED, VolumeAttachedEvent);
|
||||
wxDEFINE_EVENT(EVT_VOLUME_DETACHED, VolumeDetachedEvent);
|
||||
#endif // _WIN32
|
||||
|
||||
wxString format_nozzle_diameter(float diameter)
|
||||
{
|
||||
if (diameter <= 0.0f) {
|
||||
return _L("Unknown");
|
||||
}
|
||||
|
||||
return wxString::Format("%smm", wxString::FromDouble(diameter));
|
||||
}
|
||||
|
||||
CopyFileResult copy_file_gui(const std::string &from, const std::string &to, std::string& error_message, const bool with_check)
|
||||
{
|
||||
#ifdef WIN32
|
||||
|
||||
@@ -66,6 +66,7 @@ wxDECLARE_EVENT(EVT_VOLUME_DETACHED, VolumeDetachedEvent);
|
||||
#endif /* _WIN32 */
|
||||
|
||||
wxTopLevelWindow* find_toplevel_parent(wxWindow *window);
|
||||
wxString format_nozzle_diameter(float diameter);
|
||||
|
||||
void on_window_geometry(wxTopLevelWindow *tlw, std::function<void()> callback);
|
||||
|
||||
|
||||
@@ -4050,8 +4050,8 @@ void Sidebar::auto_calc_flushing_volumes_internal(const int modify_id, const int
|
||||
|
||||
const std::vector<int>& min_flush_volumes = get_min_flush_volumes(full_config, extruder_id);
|
||||
|
||||
ConfigOptionFloat* flush_multi_opt = project_config.option<ConfigOptionFloat>("flush_multiplier");
|
||||
float flush_multiplier = flush_multi_opt ? flush_multi_opt->getFloat() : 1.f;
|
||||
const auto* flush_multi_opt = project_config.option<ConfigOptionFloats>("flush_multiplier");
|
||||
float flush_multiplier = flush_multi_opt ? (float)flush_multi_opt->get_at(extruder_id) : 1.f;
|
||||
std::vector<double> matrix = init_matrix;
|
||||
int m_max_flush_volume = Slic3r::g_max_flush_volume;
|
||||
unsigned int m_number_of_extruders = (int)(sqrt(init_matrix.size()) + 0.001);
|
||||
|
||||
@@ -1345,7 +1345,7 @@ bool PrinterPartsDialog::Show(bool show)
|
||||
auto type = obj->GetExtderSystem()->GetNozzleType(MAIN_EXTRUDER_ID);
|
||||
auto diameter = obj->GetExtderSystem()->GetNozzleDiameter(MAIN_EXTRUDER_ID);
|
||||
nozzle_type_checkbox->SetValue(GetString(type));
|
||||
nozzle_diameter_checkbox->SetValue(GetString(diameter));
|
||||
nozzle_diameter_checkbox->SetValue(format_nozzle_diameter(diameter));
|
||||
|
||||
// nozzle flow type
|
||||
nozzle_flow_type_label->Show(obj->is_nozzle_flow_type_supported());
|
||||
@@ -1369,7 +1369,7 @@ bool PrinterPartsDialog::Show(bool show)
|
||||
auto diameter = obj->GetExtderSystem()->GetNozzleDiameter(DEPUTY_EXTRUDER_ID);
|
||||
auto flow_type = obj->GetExtderSystem()->GetNozzleFlowType(DEPUTY_EXTRUDER_ID);
|
||||
multiple_left_nozzle_type_checkbox->SetValue(GetString(type));
|
||||
multiple_left_nozzle_diameter_checkbox->SetValue(GetString(diameter));
|
||||
multiple_left_nozzle_diameter_checkbox->SetValue(format_nozzle_diameter(diameter));
|
||||
multiple_left_nozzle_flow_checkbox->SetValue(GetString(flow_type));
|
||||
|
||||
//right
|
||||
@@ -1377,7 +1377,7 @@ bool PrinterPartsDialog::Show(bool show)
|
||||
diameter = obj->GetExtderSystem()->GetNozzleDiameter(MAIN_EXTRUDER_ID);
|
||||
flow_type = obj->GetExtderSystem()->GetNozzleFlowType(MAIN_EXTRUDER_ID);
|
||||
multiple_right_nozzle_type_checkbox->SetValue(GetString(type));
|
||||
multiple_right_nozzle_diameter_checkbox->SetValue(GetString(diameter));
|
||||
multiple_right_nozzle_diameter_checkbox->SetValue(format_nozzle_diameter(diameter));
|
||||
multiple_right_nozzle_flow_checkbox->SetValue(GetString(flow_type));
|
||||
|
||||
if (obj->is_support_refresh_nozzle) {
|
||||
@@ -1421,7 +1421,7 @@ wxString PrinterPartsDialog::GetString(NozzleType nozzle_type) const {
|
||||
default: break;
|
||||
}
|
||||
|
||||
return wxEmptyString;
|
||||
return _L("Unknown");
|
||||
}
|
||||
|
||||
wxString PrinterPartsDialog::GetString(NozzleFlowType nozzle_flow_type) const {
|
||||
@@ -1483,7 +1483,7 @@ void PrinterPartsDialog::UpdateNozzleInfo(){
|
||||
auto type = obj->GetExtderSystem()->GetNozzleType(MAIN_EXTRUDER_ID);
|
||||
auto diameter = obj->GetExtderSystem()->GetNozzleDiameter(MAIN_EXTRUDER_ID);
|
||||
nozzle_type_checkbox->SetValue(GetString(type));
|
||||
nozzle_diameter_checkbox->SetValue(GetString(diameter));
|
||||
nozzle_diameter_checkbox->SetValue(format_nozzle_diameter(diameter));
|
||||
|
||||
// nozzle flow type
|
||||
nozzle_flow_type_label->Show(obj->is_nozzle_flow_type_supported());
|
||||
@@ -1499,7 +1499,7 @@ void PrinterPartsDialog::UpdateNozzleInfo(){
|
||||
auto diameter = obj->GetExtderSystem()->GetNozzleDiameter(DEPUTY_EXTRUDER_ID);
|
||||
auto flow_type = obj->GetExtderSystem()->GetNozzleFlowType(DEPUTY_EXTRUDER_ID);
|
||||
multiple_left_nozzle_type_checkbox->SetValue(GetString(type));
|
||||
multiple_left_nozzle_diameter_checkbox->SetValue(GetString(diameter));
|
||||
multiple_left_nozzle_diameter_checkbox->SetValue(format_nozzle_diameter(diameter));
|
||||
multiple_left_nozzle_flow_checkbox->SetValue(GetString(flow_type));
|
||||
|
||||
//right
|
||||
@@ -1507,7 +1507,7 @@ void PrinterPartsDialog::UpdateNozzleInfo(){
|
||||
diameter = obj->GetExtderSystem()->GetNozzleDiameter(MAIN_EXTRUDER_ID);
|
||||
flow_type = obj->GetExtderSystem()->GetNozzleFlowType(MAIN_EXTRUDER_ID);
|
||||
multiple_right_nozzle_type_checkbox->SetValue(GetString(type));
|
||||
multiple_right_nozzle_diameter_checkbox->SetValue(GetString(diameter));
|
||||
multiple_right_nozzle_diameter_checkbox->SetValue(format_nozzle_diameter(diameter));
|
||||
multiple_right_nozzle_flow_checkbox->SetValue(GetString(flow_type));
|
||||
}
|
||||
|
||||
|
||||
@@ -68,8 +68,7 @@ private:
|
||||
|
||||
wxString GetString(NozzleType nozzle_type) const;
|
||||
wxString GetString(NozzleFlowType nozzle_flow_type) const;
|
||||
wxString GetString(float diameter) const { return wxString::FromDouble(diameter); };
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
class PrintOptionsDialog : public DPIDialog
|
||||
|
||||
@@ -1823,7 +1823,15 @@ static bool _is_same_nozzle_diameters(MachineObject* obj, float &tag_nozzle_diam
|
||||
}
|
||||
|
||||
tag_nozzle_diameter = float(opt_nozzle_diameters->get_at(used_nozzle_idx));
|
||||
if (tag_nozzle_diameter != obj->GetExtderSystem()->GetNozzleDiameter(used_nozzle_idx))
|
||||
auto machine_nozzle_diameter = obj->GetExtderSystem()->GetNozzleDiameter(used_nozzle_idx);
|
||||
|
||||
// Assume matching if diameter is unknown
|
||||
if (machine_nozzle_diameter == 0.0f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tag_nozzle_diameter != machine_nozzle_diameter)
|
||||
{
|
||||
mismatch_nozzle_id = used_nozzle_idx;
|
||||
return false;
|
||||
@@ -1840,7 +1848,16 @@ static bool _is_same_nozzle_diameters(MachineObject* obj, float &tag_nozzle_diam
|
||||
|
||||
bool SelectMachineDialog::is_nozzle_hrc_matched(const DevExtder* extruder, std::string& filament_type) const
|
||||
{
|
||||
auto printer_nozzle_hrc = Print::get_hrc_by_nozzle_type(extruder->GetNozzleType());
|
||||
if (extruder == nullptr) return false;
|
||||
|
||||
auto printer_nozzle_type = extruder->GetNozzleType();
|
||||
|
||||
// Assume matching if nozzle type unknown
|
||||
if (printer_nozzle_type == NozzleType::ntUndefine) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto printer_nozzle_hrc = Print::get_hrc_by_nozzle_type(printer_nozzle_type);
|
||||
|
||||
auto preset_bundle = wxGetApp().preset_bundle;
|
||||
MaterialHash::const_iterator iter = m_materialList.begin();
|
||||
@@ -5154,13 +5171,11 @@ static wxString _get_tips(MachineObject* obj_)
|
||||
|
||||
wxString ext_diameter;
|
||||
if (obj_->GetExtderSystem()->GetTotalExtderCount() == 1) {
|
||||
ext_diameter += wxString::FromDouble(obj_->GetExtderSystem()->GetNozzleDiameter(0));
|
||||
ext_diameter += "mm";
|
||||
ext_diameter += format_nozzle_diameter(obj_->GetExtderSystem()->GetNozzleDiameter(0));
|
||||
} else if (obj_->GetExtderSystem()->GetTotalExtderCount() == 2) {
|
||||
ext_diameter += wxString::FromDouble(obj_->GetExtderSystem()->GetNozzleDiameter(1));//Left
|
||||
ext_diameter += format_nozzle_diameter(obj_->GetExtderSystem()->GetNozzleDiameter(1));//Left
|
||||
ext_diameter += "/";
|
||||
ext_diameter += wxString::FromDouble(obj_->GetExtderSystem()->GetNozzleDiameter(0));
|
||||
ext_diameter += "mm";
|
||||
ext_diameter += format_nozzle_diameter(obj_->GetExtderSystem()->GetNozzleDiameter(0));
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <string.h>
|
||||
#include "I18N.hpp"
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "slic3r/GUI/wxExtensions.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
@@ -438,6 +439,50 @@ void GuideFrame::OnScriptMessage(wxWebViewEvent &evt)
|
||||
wxString s2 = OneSelect["model"];
|
||||
if (s1.compare(s2) == 0) {
|
||||
m_ProfileJson["model"][m]["nozzle_selected"] = m_ProfileJson["model"][m]["nozzle_diameter"];
|
||||
|
||||
// Automatically select default materials for this printer model
|
||||
// This mirrors the behavior of the old ConfigWizard::select_default_materials_for_printer_model()
|
||||
if (TmpModel.contains("materials") && !TmpModel["materials"].is_null()) {
|
||||
std::string materials_str;
|
||||
|
||||
// Handle both string and JSON array formats for materials
|
||||
if (TmpModel["materials"].is_string()) {
|
||||
materials_str = TmpModel["materials"].get<std::string>();
|
||||
} else if (TmpModel["materials"].is_array()) {
|
||||
// Convert JSON array to semicolon-separated string for unescape_strings_cstyle
|
||||
for (const auto& material : TmpModel["materials"]) {
|
||||
if (!materials_str.empty()) materials_str += ";";
|
||||
materials_str += material.get<std::string>();
|
||||
}
|
||||
} else {
|
||||
materials_str = "";
|
||||
}
|
||||
|
||||
boost::trim(materials_str);
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Processing default_materials for printer: " << s1.ToStdString() << " - materials: " << materials_str;
|
||||
|
||||
// Use the same parsing logic as ConfigWizard::select_default_materials_for_printer_model()
|
||||
// This calls unescape_strings_cstyle() just like Preset.cpp:298 does
|
||||
std::vector<std::string> materials;
|
||||
if (Slic3r::unescape_strings_cstyle(materials_str, materials)) {
|
||||
for (const std::string& material : materials) {
|
||||
if (!material.empty()) {
|
||||
// Mark this filament as selected if it exists in our filament list
|
||||
// This mirrors appconfig_new.set(section, material, "true") from ConfigWizard.cpp:2150
|
||||
if (m_ProfileJson["filament"].contains(material)) {
|
||||
m_ProfileJson["filament"][material]["selected"] = 1;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Automatically selected default filament: " << material;
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << " Default filament '" << material << "' not found in available filaments for printer: " << s1.ToStdString();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " Malformed default_materials field: " << materials_str << " for printer: " << s1.ToStdString();
|
||||
}
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " No default_materials defined for printer: " << s1.ToStdString();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -838,45 +883,8 @@ bool GuideFrame::apply_config(AppConfig *app_config, PresetBundle *preset_bundle
|
||||
// Not switch filament
|
||||
//get_first_added_material_preset(AppConfig::SECTION_FILAMENTS, first_added_filament);
|
||||
|
||||
// For each @System filament, check if a vendor-specific override exists
|
||||
// in the loaded profiles. If so, replace the @System variant with the
|
||||
// override (e.g. replace "Generic ABS @System" with BBL "Generic ABS").
|
||||
// When printers from the default bundle are also selected, keep @System
|
||||
// too since those printers need it.
|
||||
static const std::string system_suffix = " @System";
|
||||
auto it_default = enabled_vendors.find(PresetBundle::ORCA_DEFAULT_BUNDLE);
|
||||
bool has_default_bundle_printer = it_default != enabled_vendors.end() && !it_default->second.empty();
|
||||
bool has_filament_profiles = m_ProfileJson.contains("filament");
|
||||
|
||||
// Check if any non-default vendor has selected printers
|
||||
bool has_vendor_printer = false;
|
||||
for (const auto& [vendor, models] : enabled_vendors) {
|
||||
if (vendor != PresetBundle::ORCA_DEFAULT_BUNDLE && !models.empty()) {
|
||||
has_vendor_printer = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> supplemented_filaments;
|
||||
for (const auto& [name, value] : enabled_filaments) {
|
||||
if (name.size() > system_suffix.size() &&
|
||||
name.compare(name.size() - system_suffix.size(), system_suffix.size(), system_suffix) == 0) {
|
||||
std::string short_name = name.substr(0, name.size() - system_suffix.size());
|
||||
if (has_vendor_printer && has_filament_profiles && m_ProfileJson["filament"].contains(short_name)) {
|
||||
supplemented_filaments[short_name] = value;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Replacing @System filament: '" << name << "' -> '" << short_name << "'";
|
||||
if (has_default_bundle_printer) {
|
||||
supplemented_filaments[name] = value;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Also keeping '" << name << "' for default bundle printers";
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
supplemented_filaments[name] = value;
|
||||
}
|
||||
|
||||
//update the app_config
|
||||
app_config->set_section(AppConfig::SECTION_FILAMENTS, supplemented_filaments);
|
||||
app_config->set_section(AppConfig::SECTION_FILAMENTS, enabled_filaments);
|
||||
app_config->set_vendors(m_appconfig_new);
|
||||
|
||||
if (check_unsaved_preset_changes)
|
||||
@@ -886,10 +894,10 @@ bool GuideFrame::apply_config(AppConfig *app_config, PresetBundle *preset_bundle
|
||||
// If the active filament is not in the wizard-selected filaments, switch to the first
|
||||
// compatible wizard-selected filament. This handles the first-run case where load_presets
|
||||
// falls back to "Generic PLA" even though the user selected a different filament.
|
||||
bool active_filament_selected = supplemented_filaments.empty()
|
||||
|| supplemented_filaments.count(preset_bundle->filament_presets.front()) > 0;
|
||||
bool active_filament_selected = enabled_filaments.empty()
|
||||
|| enabled_filaments.count(preset_bundle->filament_presets.front()) > 0;
|
||||
if (!active_filament_selected) {
|
||||
for (const auto& [filament_name, _] : supplemented_filaments) {
|
||||
for (const auto& [filament_name, _] : enabled_filaments) {
|
||||
const Preset* preset = preset_bundle->filaments.find_preset(filament_name);
|
||||
if (preset && preset->is_visible && preset->is_compatible) {
|
||||
preset_bundle->filaments.select_preset_by_name(filament_name, true);
|
||||
@@ -1124,21 +1132,23 @@ int GuideFrame::LoadProfileData()
|
||||
return 0;
|
||||
}
|
||||
|
||||
//sync to web
|
||||
std::string strAll = m_ProfileJson.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||
wxGetApp().CallAfter([this] {
|
||||
if (!m_destroy) {
|
||||
//sync to appconfig first to populate current selections
|
||||
SaveProfileData();
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", finished, json contents: " << std::endl << strAll;
|
||||
json m_Res = json::object();
|
||||
m_Res["command"] = "userguide_profile_load_finish";
|
||||
m_Res["sequence_id"] = "10001";
|
||||
wxString strJS = wxString::Format("HandleStudio(%s)", m_Res.dump(-1, ' ', true));
|
||||
if (!m_destroy)
|
||||
wxGetApp().CallAfter([this, strJS] { RunScript(strJS); });
|
||||
//sync to web after selections are populated
|
||||
std::string strAll = m_ProfileJson.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||
|
||||
//sync to appconfig
|
||||
if (!m_destroy)
|
||||
wxGetApp().CallAfter([this] { SaveProfileData(); });
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", finished, json contents: " << std::endl << strAll;
|
||||
json m_Res = json::object();
|
||||
m_Res["command"] = "userguide_profile_load_finish";
|
||||
m_Res["sequence_id"] = "10001";
|
||||
wxString strJS = wxString::Format("HandleStudio(%s)", m_Res.dump(-1, ' ', true));
|
||||
|
||||
RunScript(strJS);
|
||||
}
|
||||
});
|
||||
} catch (std::exception& e) {
|
||||
// wxLogMessage("GUIDE: load_profile_error %s ", e.what());
|
||||
// wxMessageBox(e.what(), "", MB_OK);
|
||||
|
||||
@@ -420,7 +420,9 @@ int DropDown::hoverIndex()
|
||||
{
|
||||
if (hover_item < 0)
|
||||
return -1;
|
||||
if (count == items.size())
|
||||
// BUG FIX: Can't take the shortcut if subDropDown exists (which means there are groups)
|
||||
// because we need to detect group headers which return negative indices
|
||||
if (count == items.size() && subDropDown == nullptr)
|
||||
return hover_item;
|
||||
int index = -1;
|
||||
std::set<wxString> groups;
|
||||
@@ -448,7 +450,9 @@ int DropDown::selectedItem()
|
||||
{
|
||||
if (selection < 0)
|
||||
return -1;
|
||||
if (count == items.size())
|
||||
// BUG FIX: Can't take the shortcut if subDropDown exists (which means there are groups)
|
||||
// because the visual position differs from the actual item index when groups are shown
|
||||
if (count == items.size() && subDropDown == nullptr)
|
||||
return selection;
|
||||
auto & sel = items[selection];
|
||||
if (group.IsEmpty() ? !sel.group.IsEmpty() : sel.group != group)
|
||||
|
||||
@@ -50,6 +50,10 @@ bool QidiPrinterAgent::fetch_filament_info(std::string dev_id)
|
||||
series_id = infer_series_id(info.model_id, info.dev_name);
|
||||
}
|
||||
}
|
||||
if (series_id.empty()) {
|
||||
// Fall back to the configured Orca model if Moonraker doesn't expose a usable identifier.
|
||||
series_id = infer_series_id(device_info.model_id, device_info.model_name);
|
||||
}
|
||||
|
||||
// 2. Fetch filament dictionary
|
||||
QidiFilamentDict dict;
|
||||
|
||||
Reference in New Issue
Block a user