mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-20 15:21:21 -06:00
Merge some BS1.7 changes:
Boolean operation feature
This commit is contained in:
parent
9e73dfeb5d
commit
da05ae02da
73 changed files with 2496 additions and 481 deletions
|
@ -60,6 +60,10 @@
|
|||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
|
||||
// For stl export
|
||||
#include "libslic3r/CSGMesh/ModelToCSGMesh.hpp"
|
||||
#include "libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp"
|
||||
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI_ObjectList.hpp"
|
||||
|
@ -169,6 +173,8 @@ wxDEFINE_EVENT(EVT_INSTALL_PLUGIN_HINT, wxCommandEvent);
|
|||
wxDEFINE_EVENT(EVT_PREVIEW_ONLY_MODE_HINT, wxCommandEvent);
|
||||
//BBS: change light/dark mode
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_COLOR_MODE_CHANGED, SimpleEvent);
|
||||
//BBS: print
|
||||
wxDEFINE_EVENT(EVT_PRINT_FROM_SDCARD_VIEW, SimpleEvent);
|
||||
|
||||
|
||||
bool Plater::has_illegal_filename_characters(const wxString& wxs_name)
|
||||
|
@ -528,8 +534,8 @@ Sidebar::Sidebar(Plater *parent)
|
|||
p->m_text_printer_settings = new Label(p->m_panel_printer_title, _L("Printer"), LB_PROPAGATE_MOUSE_EVENT);
|
||||
|
||||
p->m_printer_icon->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) {
|
||||
auto wizard_t = new ConfigWizard(wxGetApp().mainframe);
|
||||
wizard_t->run(ConfigWizard::RR_USER, ConfigWizard::SP_CUSTOM);
|
||||
//auto wizard_t = new ConfigWizard(wxGetApp().mainframe);
|
||||
//wizard_t->run(ConfigWizard::RR_USER, ConfigWizard::SP_CUSTOM);
|
||||
});
|
||||
|
||||
|
||||
|
@ -619,11 +625,8 @@ Sidebar::Sidebar(Plater *parent)
|
|||
m_bed_type_list = new ComboBox(p->m_panel_printer_content, wxID_ANY, wxString(""), wxDefaultPosition, {-1, FromDIP(30)}, 0, nullptr, wxCB_READONLY);
|
||||
const ConfigOptionDef* bed_type_def = print_config_def.get("curr_bed_type");
|
||||
if (bed_type_def && bed_type_def->enum_keys_map) {
|
||||
for (auto item : *bed_type_def->enum_keys_map) {
|
||||
if (item.first == "Default Plate")
|
||||
continue;
|
||||
|
||||
m_bed_type_list->AppendString(_L(item.first));
|
||||
for (auto item : bed_type_def->enum_labels) {
|
||||
m_bed_type_list->AppendString(_L(item));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1628,6 +1631,10 @@ bool Sidebar::show_object_list(bool show) const
|
|||
{
|
||||
if (!p->m_object_list->Show(show))
|
||||
return false;
|
||||
if (!show)
|
||||
p->object_layers->Show(false);
|
||||
else
|
||||
p->m_object_list->part_selection_changed();
|
||||
p->scrolled->Layout();
|
||||
return true;
|
||||
}
|
||||
|
@ -1731,6 +1738,7 @@ struct Plater::priv
|
|||
bool m_slice_all{false};
|
||||
bool m_is_slicing {false};
|
||||
bool m_is_publishing {false};
|
||||
int m_is_RightClickInLeftUI{-1};
|
||||
int m_cur_slice_plate;
|
||||
//BBS: m_slice_all in .gcode.3mf file case, set true when slice all
|
||||
bool m_slice_all_only_has_gcode{ false };
|
||||
|
@ -1906,6 +1914,12 @@ struct Plater::priv
|
|||
bool are_view3D_labels_shown() const { return (current_panel == view3D) && view3D->get_canvas3d()->are_labels_shown(); }
|
||||
void show_view3D_labels(bool show) { if (current_panel == view3D) view3D->get_canvas3d()->show_labels(show); }
|
||||
|
||||
bool is_view3D_overhang_shown() const { return (current_panel == view3D) && view3D->get_canvas3d()->is_overhang_shown(); }
|
||||
void show_view3D_overhang(bool show)
|
||||
{
|
||||
if (current_panel == view3D) view3D->get_canvas3d()->show_overhang(show);
|
||||
}
|
||||
|
||||
bool is_sidebar_collapsed() const { return sidebar->is_collapsed(); }
|
||||
void collapse_sidebar(bool collapse);
|
||||
|
||||
|
@ -2115,6 +2129,7 @@ struct Plater::priv
|
|||
//void show_action_buttons(const bool is_ready_to_slice) const;
|
||||
bool show_publish_dlg(bool show = true);
|
||||
void update_publish_dialog_status(wxString &msg, int percent = -1);
|
||||
void on_action_print_plate_from_sdcard(SimpleEvent&);
|
||||
|
||||
// Set the bed shape to a single closed 2D polygon(array of two element arrays),
|
||||
// triangulate the bed and store the triangles into m_bed.m_triangles,
|
||||
|
@ -2227,6 +2242,9 @@ private:
|
|||
std::vector<std::pair<Slic3r::PrintStateBase::Warning, size_t>> current_warnings;
|
||||
bool show_warning_dialog { false };
|
||||
|
||||
//record print preset
|
||||
void record_start_print_preset(std::string action);
|
||||
|
||||
};
|
||||
|
||||
const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf)", std::regex::icase);
|
||||
|
@ -2499,6 +2517,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
assemble_canvas->Bind(EVT_GLCANVAS_OBJECT_SELECT, &priv::on_object_select, this);
|
||||
assemble_canvas->Bind(EVT_GLVIEWTOOLBAR_3D, [q](SimpleEvent&) { q->select_view_3D("3D"); });
|
||||
assemble_canvas->Bind(EVT_GLCANVAS_RIGHT_CLICK, &priv::on_right_click, this);
|
||||
assemble_canvas->Bind(EVT_GLCANVAS_UNDO, [this](SimpleEvent&) { this->undo(); });
|
||||
assemble_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); });
|
||||
}
|
||||
|
||||
if (wxGetApp().is_editor()) {
|
||||
|
@ -2512,6 +2532,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
q->Bind(EVT_GLTOOLBAR_SLICE_PLATE, &priv::on_action_slice_plate, this);
|
||||
q->Bind(EVT_GLTOOLBAR_SLICE_ALL, &priv::on_action_slice_all, this);
|
||||
q->Bind(EVT_GLTOOLBAR_PRINT_PLATE, &priv::on_action_print_plate, this);
|
||||
q->Bind(EVT_PRINT_FROM_SDCARD_VIEW, &priv::on_action_print_plate_from_sdcard, this);
|
||||
q->Bind(EVT_GLTOOLBAR_SELECT_SLICED_PLATE, &priv::on_action_select_sliced_plate, this);
|
||||
q->Bind(EVT_GLTOOLBAR_PRINT_ALL, &priv::on_action_print_all, this);
|
||||
q->Bind(EVT_GLTOOLBAR_EXPORT_GCODE, &priv::on_action_export_gcode, this);
|
||||
|
@ -3701,12 +3722,13 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
// update printable state for new volumes on canvas3D
|
||||
wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_objects(obj_idxs);
|
||||
|
||||
Selection& selection = view3D->get_canvas3d()->get_selection();
|
||||
selection.clear();
|
||||
for (size_t idx : obj_idxs) {
|
||||
selection.add_object((unsigned int)idx, false);
|
||||
if (!load_config) {
|
||||
Selection& selection = view3D->get_canvas3d()->get_selection();
|
||||
selection.clear();
|
||||
for (size_t idx : obj_idxs) {
|
||||
selection.add_object((unsigned int)idx, false);
|
||||
}
|
||||
}
|
||||
|
||||
// BBS: update object list selection
|
||||
this->sidebar->obj_list()->update_selections();
|
||||
|
||||
|
@ -3776,17 +3798,15 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& mode
|
|||
const double max_ratio = std::max(ratio(0), ratio(1));
|
||||
if (max_ratio > 10000) {
|
||||
MessageDialog dlg(q, _L("Your object appears to be too large, Do you want to scale it down to fit the heat bed automatically?"), _L("Object too large"),
|
||||
wxICON_QUESTION | wxYES_NO);
|
||||
wxICON_QUESTION | wxYES);
|
||||
int answer = dlg.ShowModal();
|
||||
if (answer == wxID_YES) {
|
||||
// the size of the object is too big -> this could lead to overflow when moving to clipper coordinates,
|
||||
// so scale down the mesh
|
||||
object->scale_mesh_after_creation(1. / max_ratio);
|
||||
object->origin_translation = Vec3d::Zero();
|
||||
object->center_around_origin();
|
||||
scaled_down = true;
|
||||
break;
|
||||
}
|
||||
// the size of the object is too big -> this could lead to overflow when moving to clipper coordinates,
|
||||
// so scale down the mesh
|
||||
object->scale_mesh_after_creation(1. / max_ratio);
|
||||
object->origin_translation = Vec3d::Zero();
|
||||
object->center_around_origin();
|
||||
scaled_down = true;
|
||||
break;
|
||||
}
|
||||
else if (max_ratio > 10) {
|
||||
MessageDialog dlg(q, _L("Your object appears to be too large, Do you want to scale it down to fit the heat bed automatically?"), _L("Object too large"),
|
||||
|
@ -3836,8 +3856,14 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& mode
|
|||
for (auto& instance : new_instances) {
|
||||
auto offset = instance->get_offset();
|
||||
auto start_point = this->bed.build_volume().bounding_volume2d().center();
|
||||
auto empty_cell = wxGetApp().plater()->canvas3D()->get_nearest_empty_cell({ start_point(0),start_point(1) });
|
||||
Vec3d displacement = { empty_cell.x(),empty_cell.y(),offset(2)};
|
||||
bool plate_empty = partplate_list.get_curr_plate()->empty();
|
||||
Vec3d displacement;
|
||||
if (plate_empty)
|
||||
displacement = {start_point(0), start_point(1), offset(2)};
|
||||
else {
|
||||
auto empty_cell = wxGetApp().plater()->canvas3D()->get_nearest_empty_cell({start_point(0), start_point(1)});
|
||||
displacement = {empty_cell.x(), empty_cell.y(), offset(2)};
|
||||
}
|
||||
instance->set_offset(displacement);
|
||||
}
|
||||
#endif
|
||||
|
@ -3852,9 +3878,7 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& mode
|
|||
//}
|
||||
|
||||
notification_manager->close_notification_of_type(NotificationType::UpdatedItemsInfo);
|
||||
for (const size_t idx : obj_idxs) {
|
||||
wxGetApp().obj_list()->add_object_to_list(idx);
|
||||
}
|
||||
wxGetApp().obj_list()->add_objects_to_list(obj_idxs);
|
||||
|
||||
update();
|
||||
// Update InfoItems in ObjectList after update() to use of a correct value of the GLCanvas3D::is_sinking(),
|
||||
|
@ -4166,6 +4190,7 @@ void Plater::priv::reset(bool apply_presets_change)
|
|||
|
||||
if (view3D->is_layers_editing_enabled())
|
||||
view3D->get_canvas3d()->force_main_toolbar_left_action(view3D->get_canvas3d()->get_main_toolbar_item_id("layersediting"));
|
||||
view3D->get_canvas3d()->reset_all_gizmos();
|
||||
|
||||
reset_gcode_toolpaths();
|
||||
//BBS: update gcode to current partplate's
|
||||
|
@ -4185,6 +4210,7 @@ void Plater::priv::reset(bool apply_presets_change)
|
|||
// Stop and reset the Print content.
|
||||
this->background_process.reset();
|
||||
model.clear_objects();
|
||||
assemble_view->get_canvas3d()->reset_explosion_ratio();
|
||||
update();
|
||||
|
||||
//BBS
|
||||
|
@ -4505,13 +4531,13 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
|||
|
||||
if (invalidated != Print::APPLY_STATUS_UNCHANGED && was_running && ! this->background_process.running() &&
|
||||
(return_state & UPDATE_BACKGROUND_PROCESS_RESTART) == 0) {
|
||||
// The background processing was killed and it will not be restarted.
|
||||
// Post the "canceled" callback message, so that it will be processed after any possible pending status bar update messages.
|
||||
SlicingProcessCompletedEvent evt(EVT_PROCESS_COMPLETED, 0,
|
||||
SlicingProcessCompletedEvent::Cancelled, nullptr);
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%, post an EVT_PROCESS_COMPLETED to main, status %2%")%__LINE__ %evt.status();
|
||||
wxQueueEvent(q, evt.Clone());
|
||||
}
|
||||
// The background processing was killed and it will not be restarted.
|
||||
// Post the "canceled" callback message, so that it will be processed after any possible pending status bar update messages.
|
||||
SlicingProcessCompletedEvent evt(EVT_PROCESS_COMPLETED, 0,
|
||||
SlicingProcessCompletedEvent::Cancelled, nullptr);
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%, post an EVT_PROCESS_COMPLETED to main, status %2%")%__LINE__ %evt.status();
|
||||
wxQueueEvent(q, evt.Clone());
|
||||
}
|
||||
|
||||
if ((return_state & UPDATE_BACKGROUND_PROCESS_INVALID) != 0)
|
||||
{
|
||||
|
@ -4636,7 +4662,7 @@ void Plater::priv::export_gcode(fs::path output_path, bool output_path_on_remova
|
|||
if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) != 0)
|
||||
return;
|
||||
|
||||
show_warning_dialog = false;
|
||||
show_warning_dialog = true;
|
||||
if (! output_path.empty()) {
|
||||
background_process.schedule_export(output_path.string(), output_path_on_removable_media);
|
||||
notification_manager->push_delayed_notification(NotificationType::ExportOngoing, []() {return true; }, 1000, 0);
|
||||
|
@ -4668,7 +4694,7 @@ void Plater::priv::export_gcode(fs::path output_path, bool output_path_on_remova
|
|||
if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) != 0)
|
||||
return;
|
||||
|
||||
show_warning_dialog = false;
|
||||
show_warning_dialog = true;
|
||||
if (! output_path.empty()) {
|
||||
background_process.schedule_export(output_path.string(), output_path_on_removable_media);
|
||||
notification_manager->push_delayed_notification(NotificationType::ExportOngoing, []() {return true; }, 1000, 0);
|
||||
|
@ -5103,7 +5129,7 @@ void Plater::priv::reload_from_disk()
|
|||
if (has_source && old_volume->source.object_idx < int(new_model.objects.size())) {
|
||||
const ModelObject *obj = new_model.objects[old_volume->source.object_idx];
|
||||
if (old_volume->source.volume_idx < int(obj->volumes.size())) {
|
||||
if (obj->volumes[old_volume->source.volume_idx]->name == old_volume->name) {
|
||||
if (obj->volumes[old_volume->source.volume_idx]->source.input_file == old_volume->source.input_file) {
|
||||
new_volume_idx = old_volume->source.volume_idx;
|
||||
new_object_idx = old_volume->source.object_idx;
|
||||
match_found = true;
|
||||
|
@ -5574,15 +5600,16 @@ void Plater::priv::on_select_bed_type(wxCommandEvent &evt)
|
|||
{
|
||||
ComboBox* combo = static_cast<ComboBox*>(evt.GetEventObject());
|
||||
int selection = combo->GetSelection();
|
||||
wxString bed_type_name = combo->GetString(selection);
|
||||
std::string bed_type_name = print_config_def.get("curr_bed_type")->enum_values[selection];
|
||||
|
||||
PresetBundle& preset_bundle = *wxGetApp().preset_bundle;
|
||||
DynamicPrintConfig& proj_config = wxGetApp().preset_bundle->project_config;
|
||||
const t_config_enum_values* keys_map = print_config_def.get("curr_bed_type")->enum_keys_map;
|
||||
|
||||
if (keys_map) {
|
||||
BedType new_bed_type = btCount;
|
||||
for (auto item : *keys_map) {
|
||||
if (_L(item.first) == bed_type_name) {
|
||||
if (item.first == bed_type_name) {
|
||||
new_bed_type = (BedType)item.second;
|
||||
break;
|
||||
}
|
||||
|
@ -5924,21 +5951,24 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
|
|||
// This bool stops showing export finished notification even when process_completed_with_error is false
|
||||
bool has_error = false;
|
||||
if (evt.error()) {
|
||||
std::pair<std::string, size_t> message = evt.format_error_message();
|
||||
auto message = evt.format_error_message();
|
||||
if (evt.critical_error()) {
|
||||
if (q->m_tracking_popup_menu) {
|
||||
// We don't want to pop-up a message box when tracking a pop-up menu.
|
||||
// We postpone the error message instead.
|
||||
q->m_tracking_popup_menu_error_message = message.first;
|
||||
} else {
|
||||
show_error(q, message.first, message.second != 0);
|
||||
show_error(q, message.first, message.second.size() != 0 && message.second[0] != 0);
|
||||
notification_manager->set_slicing_progress_hidden();
|
||||
}
|
||||
} else {
|
||||
ModelObject const *model_object = nullptr;
|
||||
const PrintObject *print_object = this->background_process.m_fff_print->get_object(ObjectID(message.second));
|
||||
if (print_object) model_object = print_object->model_object();
|
||||
notification_manager->push_slicing_error_notification(message.first, model_object);
|
||||
std::vector<const ModelObject *> ptrs;
|
||||
for (auto oid : message.second)
|
||||
{
|
||||
const PrintObject *print_object = this->background_process.m_fff_print->get_object(ObjectID(oid));
|
||||
if (print_object) { ptrs.push_back(print_object->model_object()); }
|
||||
}
|
||||
notification_manager->push_slicing_error_notification(message.first, ptrs);
|
||||
}
|
||||
if (evt.invalidate_plater())
|
||||
{
|
||||
|
@ -6219,6 +6249,18 @@ void Plater::priv::on_action_print_plate(SimpleEvent&)
|
|||
m_select_machine_dlg->ShowModal();
|
||||
}
|
||||
|
||||
void Plater::priv::on_action_print_plate_from_sdcard(SimpleEvent&)
|
||||
{
|
||||
if (q != nullptr) {
|
||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ":received print plate event\n";
|
||||
}
|
||||
|
||||
//BBS
|
||||
if (!m_select_machine_dlg) m_select_machine_dlg = new SelectMachineDialog(q);
|
||||
m_select_machine_dlg->prepare(0);
|
||||
m_select_machine_dlg->ShowModal();
|
||||
}
|
||||
|
||||
|
||||
void Plater::priv::on_action_send_to_printer(bool isall)
|
||||
{
|
||||
|
@ -7278,18 +7320,19 @@ void Plater::priv::take_snapshot(const std::string& snapshot_name, const UndoRed
|
|||
tower.rotation = proj_cfg.opt_float("wipe_tower_rotation_angle");
|
||||
}
|
||||
}
|
||||
const GLGizmosManager& gizmos = view3D->get_canvas3d()->get_gizmos_manager();
|
||||
const GLGizmosManager& gizmos = get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_gizmos_manager() : view3D->get_canvas3d()->get_gizmos_manager();
|
||||
|
||||
if (snapshot_type == UndoRedo::SnapshotType::ProjectSeparator)
|
||||
this->undo_redo_stack().clear();
|
||||
this->undo_redo_stack().take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection(), view3D->get_canvas3d()->get_gizmos_manager(), partplate_list, snapshot_data);
|
||||
this->undo_redo_stack().take_snapshot(snapshot_name, model, get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_selection() : view3D->get_canvas3d()->get_selection(), gizmos, partplate_list, snapshot_data);
|
||||
if (snapshot_type == UndoRedo::SnapshotType::LeavingGizmoWithAction) {
|
||||
// Filter all but the last UndoRedo::SnapshotType::GizmoAction in a row between the last UndoRedo::SnapshotType::EnteringGizmo and UndoRedo::SnapshotType::LeavingGizmoWithAction.
|
||||
// The remaining snapshot will be renamed to a more generic name,
|
||||
// depending on what gizmo is being left.
|
||||
assert(gizmos.get_current() != nullptr);
|
||||
std::string new_name = gizmos.get_current()->get_action_snapshot_name();
|
||||
this->undo_redo_stack().reduce_noisy_snapshots(new_name);
|
||||
if (gizmos.get_current() != nullptr) {
|
||||
std::string new_name = gizmos.get_current()->get_action_snapshot_name();
|
||||
this->undo_redo_stack().reduce_noisy_snapshots(new_name);
|
||||
}
|
||||
} else if (snapshot_type == UndoRedo::SnapshotType::ProjectSeparator) {
|
||||
// Reset the "dirty project" flag.
|
||||
m_undo_redo_stack_main.mark_current_as_saved();
|
||||
|
@ -7311,6 +7354,13 @@ void Plater::priv::undo()
|
|||
// BBS: undo-redo until modify record
|
||||
while (--it_current != snapshots.begin() && !snapshot_modifies_project(*it_current));
|
||||
if (it_current == snapshots.begin()) return;
|
||||
if (get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
|
||||
if (it_current->snapshot_data.snapshot_type != UndoRedo::SnapshotType::GizmoAction &&
|
||||
it_current->snapshot_data.snapshot_type != UndoRedo::SnapshotType::EnteringGizmo &&
|
||||
it_current->snapshot_data.snapshot_type != UndoRedo::SnapshotType::LeavingGizmoNoAction &&
|
||||
it_current->snapshot_data.snapshot_type != UndoRedo::SnapshotType::LeavingGizmoWithAction)
|
||||
return;
|
||||
}
|
||||
this->undo_redo_to(it_current);
|
||||
}
|
||||
|
||||
|
@ -7413,8 +7463,8 @@ void Plater::priv::undo_redo_to(std::vector<UndoRedo::Snapshot>::const_iterator
|
|||
const UndoRedo::Snapshot snapshot_copy = *it_snapshot;
|
||||
// Do the jump in time.
|
||||
if (it_snapshot->timestamp < this->undo_redo_stack().active_snapshot_time() ?
|
||||
this->undo_redo_stack().undo(model, this->view3D->get_canvas3d()->get_selection(), this->view3D->get_canvas3d()->get_gizmos_manager(), this->partplate_list, top_snapshot_data, it_snapshot->timestamp) :
|
||||
this->undo_redo_stack().redo(model, this->view3D->get_canvas3d()->get_gizmos_manager(), this->partplate_list, it_snapshot->timestamp)) {
|
||||
this->undo_redo_stack().undo(model, get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_selection() : this->view3D->get_canvas3d()->get_selection(), get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_gizmos_manager() : this->view3D->get_canvas3d()->get_gizmos_manager(), this->partplate_list, top_snapshot_data, it_snapshot->timestamp) :
|
||||
this->undo_redo_stack().redo(model, get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_gizmos_manager() : this->view3D->get_canvas3d()->get_gizmos_manager(), this->partplate_list, it_snapshot->timestamp)) {
|
||||
if (printer_technology_changed) {
|
||||
// Switch to the other printer technology. Switch to the last printer active for that particular technology.
|
||||
AppConfig *app_config = wxGetApp().app_config;
|
||||
|
@ -7491,7 +7541,7 @@ void Plater::priv::undo_redo_to(std::vector<UndoRedo::Snapshot>::const_iterator
|
|||
|
||||
void Plater::priv::update_after_undo_redo(const UndoRedo::Snapshot& snapshot, bool /* temp_snapshot_was_taken */)
|
||||
{
|
||||
this->view3D->get_canvas3d()->get_selection().clear();
|
||||
get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_selection().clear() : this->view3D->get_canvas3d()->get_selection().clear();
|
||||
// Update volumes from the deserializd model, always stop / update the background processing (for both the SLA and FFF technologies).
|
||||
this->update((unsigned int)UpdateParams::FORCE_BACKGROUND_PROCESSING_UPDATE | (unsigned int)UpdateParams::POSTPONE_VALIDATION_ERROR_MESSAGE);
|
||||
// Release old snapshots if the memory allocated is excessive. This may remove the top most snapshot if jumping to the very first snapshot.
|
||||
|
@ -7500,8 +7550,12 @@ void Plater::priv::update_after_undo_redo(const UndoRedo::Snapshot& snapshot, bo
|
|||
// triangle meshes may have gotten released from the scene or the background processing, therefore now being calculated into the Undo / Redo stack size.
|
||||
this->undo_redo_stack().release_least_recently_used();
|
||||
//YS_FIXME update obj_list from the deserialized model (maybe store ObjectIDs into the tree?) (no selections at this point of time)
|
||||
this->view3D->get_canvas3d()->get_selection().set_deserialized(GUI::Selection::EMode(this->undo_redo_stack().selection_deserialized().mode), this->undo_redo_stack().selection_deserialized().volumes_and_instances);
|
||||
this->view3D->get_canvas3d()->get_gizmos_manager().update_after_undo_redo(snapshot);
|
||||
get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ?
|
||||
assemble_view->get_canvas3d()->get_selection().set_deserialized(GUI::Selection::EMode(this->undo_redo_stack().selection_deserialized().mode), this->undo_redo_stack().selection_deserialized().volumes_and_instances) :
|
||||
this->view3D->get_canvas3d()->get_selection().set_deserialized(GUI::Selection::EMode(this->undo_redo_stack().selection_deserialized().mode), this->undo_redo_stack().selection_deserialized().volumes_and_instances);
|
||||
get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ?
|
||||
assemble_view->get_canvas3d()->get_gizmos_manager().update_after_undo_redo(snapshot) :
|
||||
this->view3D->get_canvas3d()->get_gizmos_manager().update_after_undo_redo(snapshot);
|
||||
|
||||
wxGetApp().obj_list()->update_after_undo_redo();
|
||||
|
||||
|
@ -7653,6 +7707,7 @@ int Plater::new_project(bool skip_confirm, bool silent, const wxString& project_
|
|||
//get_partplate_list().update_slice_context_to_current_plate(p->background_process);
|
||||
//p->preview->update_gcode_result(p->partplate_list.get_current_slice_result());
|
||||
reset(transfer_preset_changes);
|
||||
reset_project_dirty_after_save();
|
||||
reset_project_dirty_initial_presets();
|
||||
wxGetApp().update_saved_preset_from_current_preset();
|
||||
update_project_dirty_from_presets();
|
||||
|
@ -7777,6 +7832,9 @@ void Plater::load_project(wxString const& filename2,
|
|||
p->camera.requires_zoom_to_plate = REQUIRES_ZOOM_TO_ALL_PLATE;
|
||||
wxGetApp().mainframe->select_tab(MainFrame::tp3DEditor);
|
||||
}
|
||||
else {
|
||||
p->partplate_list.select_plate_view();
|
||||
}
|
||||
|
||||
if (previous_gcode)
|
||||
collapse_sidebar(false);
|
||||
|
@ -7814,7 +7872,7 @@ int Plater::save_project(bool saveAs)
|
|||
return wxID_CANCEL;
|
||||
|
||||
//BBS export 3mf without gcode
|
||||
if (export_3mf(into_path(filename), SaveStrategy::SplitModel) < 0) {
|
||||
if (export_3mf(into_path(filename), SaveStrategy::SplitModel | SaveStrategy::ShareMesh) < 0) {
|
||||
MessageDialog(this, _L("Failed to save the project.\nPlease check whether the folder exists online or if other programs open the project file."),
|
||||
_L("Save project"), wxOK | wxICON_WARNING).ShowModal();
|
||||
return wxID_CANCEL;
|
||||
|
@ -7828,7 +7886,7 @@ int Plater::save_project(bool saveAs)
|
|||
up_to_date(true, true);
|
||||
|
||||
wxGetApp().update_saved_preset_from_current_preset();
|
||||
p->dirty_state.reset_after_save();
|
||||
reset_project_dirty_after_save();
|
||||
return wxID_YES;
|
||||
}
|
||||
|
||||
|
@ -7839,9 +7897,9 @@ void Plater::import_model_id(const std::string& download_info)
|
|||
std::string filename;
|
||||
|
||||
std::string download_origin_url = wxGetApp().url_decode(download_info);
|
||||
fs::path download_path = fs::path(download_origin_url);
|
||||
download_url = download_origin_url;
|
||||
filename = download_path.filename().string();
|
||||
fs::path download_path = fs::path(download_origin_url);
|
||||
download_url = download_origin_url;
|
||||
filename = download_path.filename().string();
|
||||
|
||||
|
||||
bool download_ok = false;
|
||||
|
@ -7937,7 +7995,8 @@ void Plater::import_model_id(const std::string& download_info)
|
|||
}
|
||||
|
||||
//target_path /= (boost::format("%1%_%2%.3mf") % filename % unique).str();
|
||||
target_path /= filename.c_str();
|
||||
target_path /= fs::path(wxString(filename).wc_str());
|
||||
|
||||
fs::path tmp_path = target_path;
|
||||
tmp_path += format(".%1%", ".download");
|
||||
|
||||
|
@ -8048,11 +8107,11 @@ void Plater::add_model(bool imperial_units/* = false*/, std::string fname/* = "
|
|||
std::vector<fs::path> paths;
|
||||
|
||||
if(fname.empty()){
|
||||
wxArrayString input_files;
|
||||
wxArrayString input_files;
|
||||
wxGetApp().import_model(this, input_files);
|
||||
if (input_files.empty())
|
||||
return;
|
||||
|
||||
|
||||
for (const auto &file : input_files)
|
||||
paths.emplace_back(into_path(file));
|
||||
}
|
||||
|
@ -8659,6 +8718,16 @@ void Plater::load_gcode(const wxString& filename)
|
|||
}
|
||||
*current_result = std::move(processor.extract_result());
|
||||
//current_result->filename = filename;
|
||||
|
||||
BedType bed_type = current_result->bed_type;
|
||||
if (bed_type != BedType::btCount) {
|
||||
DynamicPrintConfig &proj_config = wxGetApp().preset_bundle->project_config;
|
||||
proj_config.set_key_value("curr_bed_type", new ConfigOptionEnum<BedType>(bed_type));
|
||||
on_bed_type_change(bed_type);
|
||||
}
|
||||
|
||||
current_print.apply(this->model(), wxGetApp().preset_bundle->full_config());
|
||||
|
||||
current_print.set_gcode_file_ready();
|
||||
|
||||
// show results
|
||||
|
@ -9402,6 +9471,9 @@ bool Plater::is_view3D_shown() const { return p->is_view3D_shown(); }
|
|||
bool Plater::are_view3D_labels_shown() const { return p->are_view3D_labels_shown(); }
|
||||
void Plater::show_view3D_labels(bool show) { p->show_view3D_labels(show); }
|
||||
|
||||
bool Plater::is_view3D_overhang_shown() const { return p->is_view3D_overhang_shown(); }
|
||||
void Plater::show_view3D_overhang(bool show) { p->show_view3D_overhang(show); }
|
||||
|
||||
bool Plater::is_sidebar_collapsed() const { return p->is_sidebar_collapsed(); }
|
||||
void Plater::collapse_sidebar(bool show) { p->collapse_sidebar(show); }
|
||||
|
||||
|
@ -9446,8 +9518,12 @@ int GUI::Plater::close_with_confirm(std::function<bool(bool)> second_check)
|
|||
wxGetApp().app_config->set("save_project_choise", result == wxID_YES ? "yes" : "no");
|
||||
if (result == wxID_YES) {
|
||||
result = save_project();
|
||||
if (result == wxID_CANCEL)
|
||||
return result;
|
||||
if (result == wxID_CANCEL) {
|
||||
if (choise.empty())
|
||||
return result;
|
||||
else
|
||||
result = wxID_NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9938,38 +10014,92 @@ void Plater::export_core_3mf()
|
|||
export_3mf(path_u8, SaveStrategy::Silence);
|
||||
}
|
||||
|
||||
// Following lambda generates a combined mesh for export with normals pointing outwards.
|
||||
TriangleMesh Plater::combine_mesh_fff(const ModelObject& mo, int instance_id, std::function<void(const std::string&)> notify_func)
|
||||
{
|
||||
TriangleMesh mesh;
|
||||
|
||||
std::vector<csg::CSGPart> csgmesh;
|
||||
csgmesh.reserve(2 * mo.volumes.size());
|
||||
bool has_splitable_volume = csg::model_to_csgmesh(mo, Transform3d::Identity(), std::back_inserter(csgmesh),
|
||||
csg::mpartsPositive | csg::mpartsNegative | csg::mpartsDoSplits);
|
||||
|
||||
if (csg::check_csgmesh_booleans(Range{ std::begin(csgmesh), std::end(csgmesh) }) == csgmesh.end()) {
|
||||
try {
|
||||
MeshBoolean::mcut::McutMeshPtr meshPtr = csg::perform_csgmesh_booleans_mcut(Range{ std::begin(csgmesh), std::end(csgmesh) });
|
||||
mesh = MeshBoolean::mcut::mcut_to_triangle_mesh(*meshPtr);
|
||||
}
|
||||
catch (...) {}
|
||||
#if 0
|
||||
// if mcut fails, try again with CGAL
|
||||
if (mesh.empty()) {
|
||||
try {
|
||||
auto meshPtr = csg::perform_csgmesh_booleans(Range{ std::begin(csgmesh), std::end(csgmesh) });
|
||||
mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*meshPtr);
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (mesh.empty()) {
|
||||
if (notify_func)
|
||||
notify_func(_u8L("Unable to perform boolean operation on model meshes. "
|
||||
"Only positive parts will be exported."));
|
||||
|
||||
for (const ModelVolume* v : mo.volumes)
|
||||
if (v->is_model_part()) {
|
||||
TriangleMesh vol_mesh(v->mesh());
|
||||
vol_mesh.transform(v->get_matrix(), true);
|
||||
mesh.merge(vol_mesh);
|
||||
}
|
||||
}
|
||||
|
||||
if (instance_id == -1) {
|
||||
TriangleMesh vols_mesh(mesh);
|
||||
mesh = TriangleMesh();
|
||||
for (const ModelInstance* i : mo.instances) {
|
||||
TriangleMesh m = vols_mesh;
|
||||
m.transform(i->get_matrix(), true);
|
||||
mesh.merge(m);
|
||||
}
|
||||
}
|
||||
else if (0 <= instance_id && instance_id < int(mo.instances.size()))
|
||||
mesh.transform(mo.instances[instance_id]->get_matrix(), true);
|
||||
return mesh;
|
||||
}
|
||||
|
||||
// BBS export with/without boolean, however, stil merge mesh
|
||||
#define EXPORT_WITH_BOOLEAN 0
|
||||
void Plater::export_stl(bool extended, bool selection_only)
|
||||
{
|
||||
if (p->model.objects.empty()) { return; }
|
||||
|
||||
wxBusyCursor wait;
|
||||
|
||||
const auto &selection = p->get_selection();
|
||||
|
||||
// BBS support mulity objects
|
||||
// const auto obj_idx = selection.get_object_idx();
|
||||
// if (selection_only && (obj_idx == -1 || selection.is_wipe_tower()))
|
||||
// return;
|
||||
|
||||
if (selection_only && selection.is_wipe_tower())
|
||||
return;
|
||||
|
||||
//BBS
|
||||
if (selection_only) {
|
||||
// only support selection single full object and mulitiple full object
|
||||
if (!selection.is_single_full_object() && !selection.is_multiple_full_object())
|
||||
return;
|
||||
}
|
||||
|
||||
wxString path = p->get_export_file(FT_STL);
|
||||
if (path.empty()) { return; }
|
||||
const std::string path_u8 = into_u8(path);
|
||||
|
||||
wxBusyCursor wait;
|
||||
const auto& selection = p->get_selection();
|
||||
const auto obj_idx = selection.get_object_idx();
|
||||
|
||||
#if EXPORT_WITH_BOOLEAN
|
||||
if (selection_only && (obj_idx == -1 || selection.is_wipe_tower()))
|
||||
return;
|
||||
#else
|
||||
// BBS support selecting multiple objects
|
||||
if (selection_only && selection.is_wipe_tower()) return;
|
||||
|
||||
// BBS
|
||||
if (selection_only) {
|
||||
// only support selection single full object and mulitiple full object
|
||||
if (!selection.is_single_full_object() && !selection.is_multiple_full_object()) return;
|
||||
}
|
||||
|
||||
// Following lambda generates a combined mesh for export with normals pointing outwards.
|
||||
auto mesh_to_export = [](const ModelObject& mo, int instance_id) {
|
||||
auto mesh_to_export_fff_no_boolean = [](const ModelObject &mo, int instance_id) {
|
||||
TriangleMesh mesh;
|
||||
for (const ModelVolume* v : mo.volumes)
|
||||
for (const ModelVolume *v : mo.volumes)
|
||||
if (v->is_model_part()) {
|
||||
TriangleMesh vol_mesh(v->mesh());
|
||||
vol_mesh.transform(v->get_matrix(), true);
|
||||
|
@ -9978,125 +10108,72 @@ void Plater::export_stl(bool extended, bool selection_only)
|
|||
if (instance_id == -1) {
|
||||
TriangleMesh vols_mesh(mesh);
|
||||
mesh = TriangleMesh();
|
||||
for (const ModelInstance* i : mo.instances) {
|
||||
for (const ModelInstance *i : mo.instances) {
|
||||
TriangleMesh m = vols_mesh;
|
||||
m.transform(i->get_matrix(), true);
|
||||
mesh.merge(m);
|
||||
}
|
||||
}
|
||||
else if (0 <= instance_id && instance_id < int(mo.instances.size()))
|
||||
} else if (0 <= instance_id && instance_id < int(mo.instances.size()))
|
||||
mesh.transform(mo.instances[instance_id]->get_matrix(), true);
|
||||
return mesh;
|
||||
};
|
||||
#endif
|
||||
auto mesh_to_export_sla = [&, this](const ModelObject& mo, int instance_id) {
|
||||
TriangleMesh mesh;
|
||||
|
||||
TriangleMesh mesh;
|
||||
if (p->printer_technology == ptFFF) {
|
||||
if (selection_only) {
|
||||
if (selection.is_single_full_object()) {
|
||||
const auto obj_idx = selection.get_object_idx();
|
||||
const ModelObject* model_object = p->model.objects[obj_idx];
|
||||
if (selection.get_mode() == Selection::Instance)
|
||||
{
|
||||
mesh = std::move(mesh_to_export(*model_object, ( model_object->instances.size() > 1) ? -1 : selection.get_instance_idx()));
|
||||
}
|
||||
else
|
||||
{
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
mesh = model_object->volumes[volume->volume_idx()]->mesh();
|
||||
mesh.transform(volume->get_volume_transformation().get_matrix(), true);
|
||||
}
|
||||
const SLAPrintObject *object = this->p->sla_print.get_print_object_by_model_object_id(mo.id());
|
||||
|
||||
if (model_object->instances.size() == 1)
|
||||
mesh.translate(-model_object->origin_translation.cast<float>());
|
||||
}
|
||||
else if (selection.is_multiple_full_object()) {
|
||||
const std::set<std::pair<int, int>>& instances_idxs = p->get_selection().get_selected_object_instances();
|
||||
for (const std::pair<int, int>& i : instances_idxs)
|
||||
{
|
||||
ModelObject* object = p->model.objects[i.first];
|
||||
mesh.merge(mesh_to_export(*object, i.second));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (auto m = object->get_mesh_to_print(); m.empty())
|
||||
mesh = combine_mesh_fff(mo, instance_id, [this](const std::string& msg) {return get_notification_manager()->push_plater_error_notification(msg); });
|
||||
else {
|
||||
for (const ModelObject *o : p->model.objects)
|
||||
mesh.merge(mesh_to_export(*o, -1));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is SLA mode, all objects have only one volume.
|
||||
// However, we must have a look at the backend to load
|
||||
// hollowed mesh and/or supports
|
||||
const auto obj_idx = selection.get_object_idx();
|
||||
const PrintObjects& objects = p->sla_print.objects();
|
||||
for (const SLAPrintObject* object : objects)
|
||||
{
|
||||
const ModelObject* model_object = object->model_object();
|
||||
if (selection_only) {
|
||||
if (model_object->id() != p->model.objects[obj_idx]->id())
|
||||
continue;
|
||||
}
|
||||
Transform3d mesh_trafo_inv = object->trafo().inverse();
|
||||
bool is_left_handed = object->is_left_handed();
|
||||
const Transform3d mesh_trafo_inv = object->trafo().inverse();
|
||||
const bool is_left_handed = object->is_left_handed();
|
||||
|
||||
TriangleMesh pad_mesh;
|
||||
bool has_pad_mesh = extended && object->has_mesh(slaposPad);
|
||||
if (has_pad_mesh)
|
||||
{
|
||||
pad_mesh = object->get_mesh(slaposPad);
|
||||
pad_mesh.transform(mesh_trafo_inv);
|
||||
}
|
||||
auto pad_mesh = extended? object->pad_mesh() : TriangleMesh{};
|
||||
pad_mesh.transform(mesh_trafo_inv);
|
||||
|
||||
auto supports_mesh = extended ? object->support_mesh() : TriangleMesh{};
|
||||
supports_mesh.transform(mesh_trafo_inv);
|
||||
|
||||
TriangleMesh supports_mesh;
|
||||
bool has_supports_mesh = extended && object->has_mesh(slaposSupportTree);
|
||||
if (has_supports_mesh)
|
||||
{
|
||||
supports_mesh = object->get_mesh(slaposSupportTree);
|
||||
supports_mesh.transform(mesh_trafo_inv);
|
||||
}
|
||||
const std::vector<SLAPrintObject::Instance>& obj_instances = object->instances();
|
||||
for (const SLAPrintObject::Instance& obj_instance : obj_instances)
|
||||
{
|
||||
auto it = std::find_if(model_object->instances.begin(), model_object->instances.end(),
|
||||
[&obj_instance](const ModelInstance *mi) { return mi->id() == obj_instance.instance_id; });
|
||||
assert(it != model_object->instances.end());
|
||||
for (const SLAPrintObject::Instance& obj_instance : obj_instances) {
|
||||
auto it = std::find_if(object->model_object()->instances.begin(), object->model_object()->instances.end(),
|
||||
[&obj_instance](const ModelInstance *mi) { return mi->id() == obj_instance.instance_id; });
|
||||
assert(it != object->model_object()->instances.end());
|
||||
|
||||
if (it != model_object->instances.end())
|
||||
{
|
||||
bool one_inst_only = selection_only && ! selection.is_single_full_object();
|
||||
if (it != object->model_object()->instances.end()) {
|
||||
const bool one_inst_only = selection_only && ! selection.is_single_full_object();
|
||||
|
||||
int instance_idx = it - model_object->instances.begin();
|
||||
const int instance_idx = it - object->model_object()->instances.begin();
|
||||
const Transform3d& inst_transform = one_inst_only
|
||||
? Transform3d::Identity()
|
||||
: object->model_object()->instances[instance_idx]->get_transformation().get_matrix();
|
||||
? Transform3d::Identity()
|
||||
: object->model_object()->instances[instance_idx]->get_transformation().get_matrix();
|
||||
|
||||
TriangleMesh inst_mesh;
|
||||
|
||||
if (has_pad_mesh)
|
||||
{
|
||||
if (!pad_mesh.empty()) {
|
||||
TriangleMesh inst_pad_mesh = pad_mesh;
|
||||
inst_pad_mesh.transform(inst_transform, is_left_handed);
|
||||
inst_mesh.merge(inst_pad_mesh);
|
||||
}
|
||||
|
||||
if (has_supports_mesh)
|
||||
{
|
||||
if (!supports_mesh.empty()) {
|
||||
TriangleMesh inst_supports_mesh = supports_mesh;
|
||||
inst_supports_mesh.transform(inst_transform, is_left_handed);
|
||||
inst_mesh.merge(inst_supports_mesh);
|
||||
}
|
||||
|
||||
TriangleMesh inst_object_mesh = object->get_mesh_to_slice();
|
||||
TriangleMesh inst_object_mesh = object->get_mesh_to_print();
|
||||
|
||||
inst_object_mesh.transform(mesh_trafo_inv);
|
||||
inst_object_mesh.transform(inst_transform, is_left_handed);
|
||||
|
||||
inst_mesh.merge(inst_object_mesh);
|
||||
|
||||
// ensure that the instance lays on the bed
|
||||
inst_mesh.translate(0.0f, 0.0f, -inst_mesh.bounding_box().min[2]);
|
||||
// ensure that the instance lays on the bed
|
||||
inst_mesh.translate(0.0f, 0.0f, -inst_mesh.bounding_box().min.z());
|
||||
|
||||
// merge instance with global mesh
|
||||
// merge instance with global mesh
|
||||
mesh.merge(inst_mesh);
|
||||
|
||||
if (one_inst_only)
|
||||
|
@ -10104,6 +10181,50 @@ void Plater::export_stl(bool extended, bool selection_only)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mesh;
|
||||
};
|
||||
|
||||
std::function<TriangleMesh(const ModelObject& mo, int instance_id)>
|
||||
mesh_to_export;
|
||||
|
||||
if (p->printer_technology == ptFFF)
|
||||
#if EXPORT_WITH_BOOLEAN
|
||||
mesh_to_export = [this](const ModelObject& mo, int instance_id) {return Plater::combine_mesh_fff(mo, instance_id,
|
||||
[this](const std::string& msg) {return get_notification_manager()->push_plater_error_notification(msg); }); };
|
||||
#else
|
||||
mesh_to_export = mesh_to_export_fff_no_boolean;
|
||||
#endif
|
||||
else
|
||||
mesh_to_export = mesh_to_export_sla;
|
||||
|
||||
TriangleMesh mesh;
|
||||
if (selection_only) {
|
||||
if (selection.is_single_full_object()) {
|
||||
const auto obj_idx = selection.get_object_idx();
|
||||
const ModelObject* model_object = p->model.objects[obj_idx];
|
||||
if (selection.get_mode() == Selection::Instance)
|
||||
mesh = mesh_to_export(*model_object, (model_object->instances.size() > 1) ? -1 : selection.get_instance_idx());
|
||||
else {
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
mesh = model_object->volumes[volume->volume_idx()]->mesh();
|
||||
mesh.transform(volume->get_volume_transformation().get_matrix(), true);
|
||||
}
|
||||
|
||||
if (model_object->instances.size() == 1) mesh.translate(-model_object->origin_translation.cast<float>());
|
||||
}
|
||||
else if (selection.is_multiple_full_object()) {
|
||||
const std::set<std::pair<int, int>>& instances_idxs = p->get_selection().get_selected_object_instances();
|
||||
for (const std::pair<int, int>& i : instances_idxs) {
|
||||
ModelObject* object = p->model.objects[i.first];
|
||||
mesh.merge(mesh_to_export(*object, i.second));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const ModelObject* o : p->model.objects) {
|
||||
mesh.merge(mesh_to_export(*o, -1));
|
||||
}
|
||||
}
|
||||
|
||||
Slic3r::store_stl(path_u8.c_str(), &mesh, true);
|
||||
|
@ -11038,7 +11159,7 @@ std::vector<std::string> Plater::get_colors_for_color_print(const GCodeProcessor
|
|||
|
||||
return colors;
|
||||
}
|
||||
|
||||
|
||||
wxString Plater::get_project_filename(const wxString& extension) const
|
||||
{
|
||||
return p->get_project_filename(extension);
|
||||
|
@ -11469,7 +11590,8 @@ int Plater::select_plate(int plate_index, bool need_slice)
|
|||
{
|
||||
if (need_slice) { //from preview's thumbnail
|
||||
if ((invalidated & PrintBase::APPLY_STATUS_INVALIDATED) || (gcode_result->moves.empty())){
|
||||
//part_plate->update_slice_result_valid_state(false);
|
||||
if (invalidated & PrintBase::APPLY_STATUS_INVALIDATED)
|
||||
part_plate->update_slice_result_valid_state(false);
|
||||
p->process_completed_with_error = -1;
|
||||
p->m_slice_all = false;
|
||||
reset_gcode_toolpaths();
|
||||
|
@ -11897,11 +12019,23 @@ void Plater::show_object_info()
|
|||
{
|
||||
NotificationManager *notify_manager = get_notification_manager();
|
||||
const Selection& selection = get_selection();
|
||||
int selCount = selection.get_volume_idxs().size();
|
||||
ModelObjectPtrs objects = model().objects;
|
||||
int obj_idx = selection.get_object_idx();
|
||||
std::string info_text;
|
||||
|
||||
if (objects.empty() || (obj_idx < 0) || (obj_idx >= objects.size()) ||
|
||||
if (selCount > 1 && !selection.is_single_full_object()) {
|
||||
notify_manager->bbl_close_objectsinfo_notification();
|
||||
if (selection.get_mode() == Selection::EMode::Volume) {
|
||||
info_text += (boost::format(_utf8(L("Number of currently selected parts: %1%\n"))) % selCount).str();
|
||||
} else if (selection.get_mode() == Selection::EMode::Instance) {
|
||||
int content_count = selection.get_content().size();
|
||||
info_text += (boost::format(_utf8(L("Number of currently selected objects: %1%\n"))) % content_count).str();
|
||||
}
|
||||
notify_manager->bbl_show_objectsinfo_notification(info_text, false, !(p->current_panel == p->view3D));
|
||||
return;
|
||||
}
|
||||
else if (objects.empty() || (obj_idx < 0) || (obj_idx >= objects.size()) ||
|
||||
objects[obj_idx]->volumes.empty() ||// hack to avoid crash when deleting the last object on the bed
|
||||
(selection.is_single_full_object() && objects[obj_idx]->instances.size()> 1) ||
|
||||
!(selection.is_single_full_instance() || selection.is_single_volume()))
|
||||
|
@ -11939,12 +12073,12 @@ void Plater::show_object_info()
|
|||
size = vol->get_convex_hull().transformed_bounding_box(t).size();
|
||||
}
|
||||
else {
|
||||
int obj_idx, vol_idx;
|
||||
wxGetApp().obj_list()->get_selected_item_indexes(obj_idx, vol_idx);
|
||||
if (obj_idx < 0) {
|
||||
//corner case when merge/split/remove
|
||||
return;
|
||||
}
|
||||
//int obj_idx, vol_idx;
|
||||
//wxGetApp().obj_list()->get_selected_item_indexes(obj_idx, vol_idx);
|
||||
//if (obj_idx < 0) {
|
||||
// //corner case when merge/split/remove
|
||||
// return;
|
||||
//}
|
||||
info_text += (boost::format(_utf8(L("Object name: %1%\n"))) % model_object->name).str();
|
||||
face_count = static_cast<int>(model_object->facets_count());
|
||||
size = model_object->instance_convex_hull_bounding_box(inst_idx).size();
|
||||
|
@ -12277,7 +12411,8 @@ wxMenu* Plater::default_menu() { return p->menus.default_menu();
|
|||
wxMenu* Plater::instance_menu() { return p->menus.instance_menu(); }
|
||||
wxMenu* Plater::layer_menu() { return p->menus.layer_menu(); }
|
||||
wxMenu* Plater::multi_selection_menu() { return p->menus.multi_selection_menu(); }
|
||||
|
||||
int Plater::GetPlateIndexByRightMenuInLeftUI() { return p->m_is_RightClickInLeftUI; }
|
||||
void Plater::SetPlateIndexByRightMenuInLeftUI(int index) { p->m_is_RightClickInLeftUI = index; }
|
||||
SuppressBackgroundProcessingUpdate::SuppressBackgroundProcessingUpdate() :
|
||||
m_was_scheduled(wxGetApp().plater()->is_background_process_update_scheduled())
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue