mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-16 15:32:09 +00:00
Merge upstream/main into belt/rebase/may-18
Reconciles the belt-printer branch with upstream PRs through #13723. Six files had conflicts; three additional files needed manual follow-up fixes where the auto-merge produced code that referenced upstream-renamed fields or changed function signatures. Notable reconciliations: - TreeSupport.cpp: kept belt-floor early-exit branches around HEAD's drop-down logic, folded upstream's `(distance_to_top > 0 ? 1 : 0)` formula into the non-belt-floor path (upstream PR #11812). Dropped dead `roof_enabled`/`force_tip_to_roof` locals. - TreeSupport3D.cpp: combined upstream's safety-offset + remove_small changes with HEAD's belt-floor clip in the per-slice trim loop. Dropped HEAD's `else` block (superseded by upstream's rewritten bottom-contact propagation) and re-added the belt-floor clip into the new propagation loop. Gated the propagation on belt printers to prevent OOM when belt-floor clipping produces empty initial slices. - TriangleSelector.{cpp,hpp}: merged both new `select_patch` parameters (HEAD's `up_direction` and upstream's `select_partially`); body uses `dot(up_direction)` for the overhang angle check and forwards `select_partially` to `select_triangle`. - SupportMaterial.cpp: `slicing_params.soluble_interface` → `zero_gap_interface_bottom` in HEAD's `detect_belt_floor_bottom_contacts`, matching upstream's same-purpose rename at line 2495. - Custom.json, GCodeWriter.cpp: simple additive merges (kept entries / includes from both sides). Verified by building OrcaSlicer (RelWithDebInfo) after a full deps rebuild (Eigen v5.0.1, libigl v2.6.0 are now managed deps) and slicing a scaled Benchy on the NORMALIZER belt-printer profile without OOM. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -435,15 +435,25 @@ void ObjectList::create_objects_ctrl()
|
||||
|
||||
// column Extruder of the view control:
|
||||
BitmapChoiceRenderer* bmp_choice_renderer = new BitmapChoiceRenderer();
|
||||
bmp_choice_renderer->set_can_create_editor_ctrl_function([this]() {
|
||||
return m_objects_model->GetItemType(GetSelection()) & (itVolume | itLayer | itObject);
|
||||
const auto get_filament_context_item = [this]() {
|
||||
#ifdef __WXOSX__
|
||||
return m_filament_editor_item.IsOk() ? m_filament_editor_item : GetSelection();
|
||||
#else
|
||||
return GetSelection();
|
||||
#endif
|
||||
};
|
||||
bmp_choice_renderer->set_can_create_editor_ctrl_function([this, get_filament_context_item]() {
|
||||
const wxDataViewItem item = get_filament_context_item();
|
||||
return m_objects_model->GetItemType(item) & (itVolume | itLayer | itObject);
|
||||
});
|
||||
bmp_choice_renderer->set_default_extruder_idx([this]() {
|
||||
return m_objects_model->GetDefaultExtruderIdx(GetSelection());
|
||||
bmp_choice_renderer->set_default_extruder_idx([this, get_filament_context_item]() {
|
||||
const wxDataViewItem item = get_filament_context_item();
|
||||
return m_objects_model->GetDefaultExtruderIdx(item);
|
||||
});
|
||||
bmp_choice_renderer->set_has_default_extruder([this]() {
|
||||
return m_objects_model->GetVolumeType(GetSelection()) == ModelVolumeType::PARAMETER_MODIFIER ||
|
||||
m_objects_model->GetItemType(GetSelection()) == itLayer;
|
||||
bmp_choice_renderer->set_has_default_extruder([this, get_filament_context_item]() {
|
||||
const wxDataViewItem item = get_filament_context_item();
|
||||
return m_objects_model->GetVolumeType(item) == ModelVolumeType::PARAMETER_MODIFIER ||
|
||||
m_objects_model->GetItemType(item) == itLayer;
|
||||
});
|
||||
AppendColumn(new wxDataViewColumn(_L("Fila."), bmp_choice_renderer,
|
||||
colFilament, m_columns_width[colFilament] * em, wxALIGN_CENTER_HORIZONTAL, 0));
|
||||
@@ -461,11 +471,16 @@ void ObjectList::create_objects_ctrl()
|
||||
wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
|
||||
|
||||
// Open filament editor faster
|
||||
// On macOS, bypass wxDataViewCtrl::EditItem() for this custom renderer to avoid
|
||||
// native text editing of wxCustomRendererObject.
|
||||
this->Bind(wxEVT_DATAVIEW_ITEM_ACTIVATED, [this](wxDataViewEvent& event) {
|
||||
if (event.GetColumn() == colFilament) {
|
||||
#ifdef __WXOSX__
|
||||
start_filament_editor(event.GetItem());
|
||||
#else
|
||||
// Trigger the editor opening manually
|
||||
this->EditItem(event.GetItem(), GetColumn(colFilament));
|
||||
#endif
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1082,10 +1097,11 @@ void ObjectList::update_filament_in_config(const wxDataViewItem& item)
|
||||
if (m_prevent_update_filament_in_config)
|
||||
return;
|
||||
|
||||
ModelConfig* config = nullptr;
|
||||
const ItemType item_type = m_objects_model->GetItemType(item);
|
||||
if (item_type & itObject) {
|
||||
const int obj_idx = m_objects_model->GetIdByItem(item);
|
||||
m_config = &(*m_objects)[obj_idx]->config;
|
||||
config = &(*m_objects)[obj_idx]->config;
|
||||
}
|
||||
else {
|
||||
const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetObject(item));
|
||||
@@ -1094,15 +1110,17 @@ void ObjectList::update_filament_in_config(const wxDataViewItem& item)
|
||||
if (obj_idx < 0 || ui_volume_idx < 0)
|
||||
return;
|
||||
int volume_in3d_idx = m_objects_model->get_real_volume_index_in_3d(obj_idx,ui_volume_idx);
|
||||
m_config = &(*m_objects)[obj_idx]->volumes[volume_in3d_idx]->config;
|
||||
config = &(*m_objects)[obj_idx]->volumes[volume_in3d_idx]->config;
|
||||
}
|
||||
else if (item_type & itLayer)
|
||||
m_config = &get_item_config(item);
|
||||
config = &get_item_config(item);
|
||||
}
|
||||
|
||||
if (!m_config)
|
||||
if (!config)
|
||||
return;
|
||||
|
||||
m_config = config;
|
||||
|
||||
take_snapshot("Change Filament");
|
||||
|
||||
const int extruder = m_objects_model->GetExtruderNumber(item);
|
||||
@@ -2849,7 +2867,7 @@ void ObjectList::split()
|
||||
|
||||
take_snapshot("Split to parts");
|
||||
|
||||
volume->split(filament_cnt);
|
||||
volume->split(filament_cnt, wxGetApp().app_config->get_bool("keep_painting"));
|
||||
|
||||
wxBusyCursor wait;
|
||||
|
||||
@@ -3041,7 +3059,6 @@ void ObjectList::merge(bool to_multipart_object)
|
||||
auto opt = object->config.option("extruder");
|
||||
if (opt) { new_volume->config.set_key_value("extruder", new ConfigOptionInt(opt->getInt())); }
|
||||
}
|
||||
new_volume->mmu_segmentation_facets.assign(std::move(volume->mmu_segmentation_facets));
|
||||
}
|
||||
new_object->sort_volumes(true);
|
||||
|
||||
@@ -3226,6 +3243,22 @@ void ObjectList::boolean()
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), "boolean");
|
||||
|
||||
ModelObject* object = (*m_objects)[obj_idxs.front()];
|
||||
|
||||
const bool keep_painting = wxGetApp().app_config->get_bool("keep_painting");
|
||||
std::vector<std::optional<TriangleSelector::SavedPainting>> saved_paintings;
|
||||
if (keep_painting) {
|
||||
// Save painting of all the positive parts
|
||||
saved_paintings.reserve(object->volumes.size());
|
||||
for (const ModelVolume* vol : object->volumes) {
|
||||
if (vol && vol->mesh_ptr() && vol->is_model_part() && vol->is_any_painted()) {
|
||||
saved_paintings.emplace_back(vol->save_painting());
|
||||
if (saved_paintings.back()) {
|
||||
saved_paintings.back()->mesh.transform(vol->get_matrix(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TriangleMesh mesh = Plater::combine_mesh_fff(*object, -1, [this](const std::string& msg) {return wxGetApp().notification_manager()->push_plater_error_notification(msg); });
|
||||
|
||||
// add mesh to model as a new object, keep the original object's name and config
|
||||
@@ -3237,6 +3270,29 @@ void ObjectList::boolean()
|
||||
new_object->add_instance();
|
||||
ModelVolume* new_volume = new_object->add_volume(mesh);
|
||||
|
||||
// Remap paint
|
||||
if (keep_painting) {
|
||||
for (auto& saved_painting : saved_paintings) {
|
||||
if (saved_painting) {
|
||||
// For each original painted volume, we need to apply to each instance
|
||||
// because we merged all instances into one in `combine_mesh_fff`
|
||||
|
||||
// First we save the non-instance-translated mesh
|
||||
TriangleMesh vols_mesh(std::move(saved_painting->mesh));
|
||||
|
||||
for (const ModelInstance* i : object->instances) {
|
||||
// Then for each instance, we apply the paint at the given instance place
|
||||
saved_painting->mesh = vols_mesh;
|
||||
saved_painting->mesh.transform(i->get_matrix());
|
||||
|
||||
// Then paint it
|
||||
new_volume->restore_painting(saved_painting, true);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BBS: ensure on bed but no need to ensure locate in the center around origin
|
||||
new_object->ensure_on_bed();
|
||||
new_object->center_around_origin();
|
||||
@@ -6048,10 +6104,13 @@ void ObjectList::fix_through_cgal()
|
||||
msg += "\n";
|
||||
}
|
||||
|
||||
plater->clear_before_change_mesh(obj_idx);
|
||||
const bool keep_painting = GUI::wxGetApp().app_config->get_bool("keep_painting");
|
||||
if (!keep_painting) {
|
||||
plater->clear_before_change_mesh(obj_idx);
|
||||
}
|
||||
const size_t volumes_before = object(obj_idx)->volumes.size();
|
||||
std::string res;
|
||||
if (!fix_model_with_cgal_gui(*(object(obj_idx)), vol_idx, progress_dlg, msg, res))
|
||||
if (!fix_model_with_cgal_gui(*(object(obj_idx)), vol_idx, progress_dlg, msg, res, keep_painting))
|
||||
return false;
|
||||
//wxGetApp().plater()->changed_mesh(obj_idx);
|
||||
object(obj_idx)->ensure_on_bed();
|
||||
@@ -6169,6 +6228,7 @@ void GUI::ObjectList::smooth_mesh()
|
||||
WarningDialog dlg(static_cast<wxWindow *>(wxGetApp().mainframe), content, wxEmptyString, wxOK);
|
||||
dlg.ShowModal();
|
||||
};
|
||||
const bool keep_painting = GUI::wxGetApp().app_config->get_bool("keep_painting");
|
||||
bool has_show_smooth_mesh_error_dlg = false;
|
||||
if (vol_idxs.empty()) {
|
||||
obj = object(object_idx);
|
||||
@@ -6180,8 +6240,11 @@ void GUI::ObjectList::smooth_mesh()
|
||||
bool ok;
|
||||
auto result_mesh = TriangleMeshDeal::smooth_triangle_mesh(mv->mesh(), ok);
|
||||
if (ok) {
|
||||
const std::optional<TriangleSelector::SavedPainting> saved_painting = keep_painting ?
|
||||
mv->save_painting() :
|
||||
std::optional<TriangleSelector::SavedPainting>{};
|
||||
mv->set_mesh(result_mesh);
|
||||
mv->reset_extra_facets(); // reset paint color
|
||||
mv->restore_painting(saved_painting);
|
||||
mv->calculate_convex_hull();
|
||||
mv->invalidate_convex_hull_2d();
|
||||
mv->set_new_unique_id();
|
||||
@@ -6206,8 +6269,11 @@ void GUI::ObjectList::smooth_mesh()
|
||||
bool ok;
|
||||
auto result_mesh = TriangleMeshDeal::smooth_triangle_mesh(mv->mesh(),ok);
|
||||
if (ok) {
|
||||
const std::optional<TriangleSelector::SavedPainting> saved_painting = keep_painting ?
|
||||
mv->save_painting() :
|
||||
std::optional<TriangleSelector::SavedPainting>{};
|
||||
mv->set_mesh(result_mesh);
|
||||
mv->reset_extra_facets(); // reset paint color
|
||||
mv->restore_painting(saved_painting);
|
||||
mv->calculate_convex_hull();
|
||||
mv->invalidate_convex_hull_2d();
|
||||
mv->set_new_unique_id();
|
||||
@@ -6279,10 +6345,71 @@ void ObjectList::ItemValueChanged(wxDataViewEvent &event)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __WXOSX__
|
||||
bool ObjectList::is_live_model_item(wxDataViewItem item) const
|
||||
{
|
||||
if (!item.IsOk() || !m_objects_model)
|
||||
return false;
|
||||
|
||||
wxDataViewItemArray all_items;
|
||||
m_objects_model->GetAllChildren(wxDataViewItem(nullptr), all_items);
|
||||
for (const wxDataViewItem& live_item : all_items)
|
||||
if (live_item.GetID() == item.GetID())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ObjectList::start_filament_editor(wxDataViewItem item)
|
||||
{
|
||||
if (m_starting_filament_editor || !is_live_model_item(item))
|
||||
return;
|
||||
|
||||
const ItemType type = m_objects_model->GetItemType(item);
|
||||
if (!(type & (itVolume | itLayer | itObject)))
|
||||
return;
|
||||
|
||||
auto* column = GetColumn(colFilament);
|
||||
if (!column)
|
||||
return;
|
||||
|
||||
auto* custom_renderer = dynamic_cast<wxDataViewCustomRenderer*>(column->GetRenderer());
|
||||
if (!custom_renderer || custom_renderer->GetEditorCtrl())
|
||||
return;
|
||||
|
||||
m_starting_filament_editor = true;
|
||||
m_filament_editor_item = item;
|
||||
const bool started = custom_renderer->StartEditing(item, GetItemRect(item, column));
|
||||
m_filament_editor_item = wxDataViewItem(nullptr);
|
||||
m_starting_filament_editor = false;
|
||||
|
||||
if (!started)
|
||||
return;
|
||||
|
||||
SetCustomRendererPtr(custom_renderer);
|
||||
SetCustomRendererItem(item);
|
||||
}
|
||||
#endif // __WXOSX__
|
||||
|
||||
void GUI::ObjectList::OnStartEditing(wxDataViewEvent &event)
|
||||
{
|
||||
auto col = event.GetColumn();
|
||||
auto item = event.GetItem();
|
||||
#ifdef __WXOSX__
|
||||
if (col == colFilament) {
|
||||
if (m_starting_filament_editor)
|
||||
return;
|
||||
|
||||
event.Veto();
|
||||
CallAfter([this, item] {
|
||||
if (!is_live_model_item(item))
|
||||
return;
|
||||
start_filament_editor(item);
|
||||
});
|
||||
return;
|
||||
}
|
||||
#endif // __WXOSX__
|
||||
|
||||
if (col == colName) {
|
||||
ObjectDataViewModelNode* node = (ObjectDataViewModelNode*)item.GetID();
|
||||
if (node->GetType() & itPlate) {
|
||||
@@ -6350,6 +6477,12 @@ void ObjectList::OnEditingStarted(wxDataViewEvent &event)
|
||||
dynamic_cast<TabPrintModel*>(wxGetApp().get_model_tab(vol_idx >= 0))->reset_model_config();
|
||||
return;
|
||||
}
|
||||
#ifdef __WXOSX__
|
||||
if (col == colFilament) {
|
||||
start_filament_editor(item);
|
||||
return;
|
||||
}
|
||||
#endif // __WXOSX__
|
||||
if (col != colFilament && col != colName)
|
||||
return;
|
||||
auto column = GetColumn(col);
|
||||
|
||||
Reference in New Issue
Block a user