mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-08-04 04:24:04 -06:00
Merge branch 'master' into materials
This commit is contained in:
commit
eb93d2a32d
92 changed files with 1925 additions and 1073 deletions
|
@ -455,7 +455,7 @@ void BedShapePanel::update_shape()
|
|||
else if (page_idx == SHAPE_CUSTOM)
|
||||
m_shape = m_loaded_shape;
|
||||
|
||||
update_preview();
|
||||
update_preview();
|
||||
}
|
||||
|
||||
// Loads an stl file, projects it to the XY plane and calculates a polygon.
|
||||
|
|
|
@ -42,7 +42,7 @@ static wxString generate_html_row(const Config::Snapshot &snapshot, bool row_eve
|
|||
text += " (" + wxString::FromUTF8(snapshot.comment.data()) + ")";
|
||||
text += "</b></font><br>";
|
||||
// End of row header.
|
||||
text += _(L("slic3r version")) + ": " + snapshot.slic3r_version_captured.to_string() + "<br>";
|
||||
text += _(L("PrusaSlicer version")) + ": " + snapshot.slic3r_version_captured.to_string() + "<br>";
|
||||
text += _(L("print")) + ": " + snapshot.print + "<br>";
|
||||
text += _(L("filaments")) + ": " + snapshot.filaments.front() + "<br>";
|
||||
text += _(L("printer")) + ": " + snapshot.printer + "<br>";
|
||||
|
@ -50,9 +50,9 @@ static wxString generate_html_row(const Config::Snapshot &snapshot, bool row_eve
|
|||
bool compatible = true;
|
||||
for (const Config::Snapshot::VendorConfig &vc : snapshot.vendor_configs) {
|
||||
text += _(L("vendor")) + ": " + vc.name +", " + _(L("version")) + ": " + vc.version.config_version.to_string() +
|
||||
", " + _(L("min slic3r version")) + ": " + vc.version.min_slic3r_version.to_string();
|
||||
", " + _(L("min PrusaSlicer version")) + ": " + vc.version.min_slic3r_version.to_string();
|
||||
if (vc.version.max_slic3r_version != Semver::inf())
|
||||
text += ", " + _(L("max slic3r version")) + ": " + vc.version.max_slic3r_version.to_string();
|
||||
text += ", " + _(L("max PrusaSlicer version")) + ": " + vc.version.max_slic3r_version.to_string();
|
||||
text += "<br>";
|
||||
for (const std::pair<std::string, std::set<std::string>> &model : vc.models_variants_installed) {
|
||||
text += _(L("model")) + ": " + model.first + ", " + _(L("variants")) + ": ";
|
||||
|
|
|
@ -1792,13 +1792,16 @@ bool ConfigWizard::run(RunReason reason, StartPage start_page)
|
|||
|
||||
const wxString& ConfigWizard::name(const bool from_menu/* = false*/)
|
||||
{
|
||||
// A different naming convention is used for the Wizard on Windows vs. OSX & GTK.
|
||||
// A different naming convention is used for the Wizard on Windows & GTK vs. OSX.
|
||||
// Note: Don't call _() macro here.
|
||||
// This function just return the current name according to the OS.
|
||||
// Translation is implemented inside GUI_App::add_config_menu()
|
||||
#if __APPLE__
|
||||
static const wxString config_wizard_name = _(L("Configuration Assistant"));
|
||||
static const wxString config_wizard_name_menu = _(L("Configuration &Assistant"));
|
||||
static const wxString config_wizard_name = L("Configuration Assistant");
|
||||
static const wxString config_wizard_name_menu = L("Configuration &Assistant");
|
||||
#else
|
||||
static const wxString config_wizard_name = _(L("Configuration Wizard"));
|
||||
static const wxString config_wizard_name_menu = _(L("Configuration &Wizard"));
|
||||
static const wxString config_wizard_name = L("Configuration Wizard");
|
||||
static const wxString config_wizard_name_menu = L("Configuration &Wizard");
|
||||
#endif
|
||||
return from_menu ? config_wizard_name_menu : config_wizard_name;
|
||||
}
|
||||
|
|
|
@ -928,8 +928,8 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c
|
|||
if (items_count > 1)
|
||||
m_original_height += (items_count - 1) * scaled_square_contour;
|
||||
|
||||
m_width = (int)next_highest_power_of_2((uint32_t)m_original_width);
|
||||
m_height = (int)next_highest_power_of_2((uint32_t)m_original_height);
|
||||
m_width = (int)next_highest_power_of_2((uint32_t)m_original_width);
|
||||
m_height = (int)next_highest_power_of_2((uint32_t)m_original_height);
|
||||
|
||||
// generates bitmap
|
||||
wxBitmap bitmap(m_width, m_height);
|
||||
|
@ -1094,7 +1094,7 @@ void GLCanvas3D::LegendTexture::render(const GLCanvas3D& canvas) const
|
|||
wxDEFINE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, Vec2dEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_SELECT_ALL, SimpleEvent);
|
||||
|
@ -1882,7 +1882,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||
if (m_reload_delayed)
|
||||
return;
|
||||
|
||||
bool update_object_list = false;
|
||||
bool update_object_list = false;
|
||||
|
||||
if (m_volumes.volumes != glvolumes_new)
|
||||
update_object_list = true;
|
||||
|
@ -3012,15 +3012,16 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
wxGetApp().obj_manipul()->set_dirty();
|
||||
// forces a frame render to update the view before the context menu is shown
|
||||
render();
|
||||
|
||||
Vec2d logical_pos = pos.cast<double>();
|
||||
#if ENABLE_RETINA_GL
|
||||
const float factor = m_retina_helper->get_scale_factor();
|
||||
logical_pos = logical_pos.cwiseQuotient(Vec2d(factor, factor));
|
||||
#endif // ENABLE_RETINA_GL
|
||||
post_event(Vec2dEvent(EVT_GLCANVAS_RIGHT_CLICK, logical_pos));
|
||||
}
|
||||
}
|
||||
Vec2d logical_pos = pos.cast<double>();
|
||||
#if ENABLE_RETINA_GL
|
||||
const float factor = m_retina_helper->get_scale_factor();
|
||||
logical_pos = logical_pos.cwiseQuotient(Vec2d(factor, factor));
|
||||
#endif // ENABLE_RETINA_GL
|
||||
if (!m_mouse.dragging)
|
||||
// do not post the event if the user is panning the scene
|
||||
post_event(RBtnEvent(EVT_GLCANVAS_RIGHT_CLICK, { logical_pos, m_hover_volume_idxs.empty() }));
|
||||
}
|
||||
|
||||
mouse_up_cleanup();
|
||||
|
@ -3372,7 +3373,7 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type)
|
|||
void GLCanvas3D::set_camera_zoom(double zoom)
|
||||
{
|
||||
const Size& cnv_size = get_canvas_size();
|
||||
m_camera.set_zoom(zoom, _max_bounding_box(false, false), cnv_size.get_width(), cnv_size.get_height());
|
||||
m_camera.set_zoom(zoom, _max_bounding_box(false, true), cnv_size.get_width(), cnv_size.get_height());
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
|
@ -3388,10 +3389,9 @@ void GLCanvas3D::handle_sidebar_focus_event(const std::string& opt_key, bool foc
|
|||
m_sidebar_field = focus_on ? opt_key : "";
|
||||
|
||||
if (!m_sidebar_field.empty())
|
||||
{
|
||||
m_gizmos.reset_all_states();
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void GLCanvas3D::handle_layers_data_focus_event(const t_layer_height_range range, const EditorType type)
|
||||
|
@ -5000,6 +5000,8 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
|
|||
return path.width;
|
||||
case GCodePreviewData::Extrusion::Feedrate:
|
||||
return path.feedrate;
|
||||
case GCodePreviewData::Extrusion::FanSpeed:
|
||||
return path.fan_speed;
|
||||
case GCodePreviewData::Extrusion::VolumetricRate:
|
||||
return path.feedrate * (float)path.mm3_per_mm;
|
||||
case GCodePreviewData::Extrusion::Tool:
|
||||
|
@ -5025,6 +5027,8 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
|
|||
return data.get_width_color(value);
|
||||
case GCodePreviewData::Extrusion::Feedrate:
|
||||
return data.get_feedrate_color(value);
|
||||
case GCodePreviewData::Extrusion::FanSpeed:
|
||||
return data.get_fan_speed_color(value);
|
||||
case GCodePreviewData::Extrusion::VolumetricRate:
|
||||
return data.get_volumetric_rate_color(value);
|
||||
case GCodePreviewData::Extrusion::Tool:
|
||||
|
@ -5069,14 +5073,14 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
|
|||
for (const GCodePreviewData::Extrusion::Layer &layer : preview_data.extrusion.layers)
|
||||
for (const ExtrusionPath &path : layer.paths)
|
||||
++ num_paths_per_role[size_t(path.role())];
|
||||
std::vector<std::vector<float>> roles_values;
|
||||
std::vector<std::vector<float>> roles_values;
|
||||
roles_values.assign(size_t(erCount), std::vector<float>());
|
||||
for (size_t i = 0; i < roles_values.size(); ++ i)
|
||||
roles_values[i].reserve(num_paths_per_role[i]);
|
||||
for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers)
|
||||
for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers)
|
||||
for (const ExtrusionPath& path : layer.paths)
|
||||
roles_values[size_t(path.role())].emplace_back(Helper::path_filter(preview_data.extrusion.view_type, path));
|
||||
roles_filters.reserve(size_t(erCount));
|
||||
roles_filters.reserve(size_t(erCount));
|
||||
size_t num_buffers = 0;
|
||||
for (std::vector<float> &values : roles_values) {
|
||||
sort_remove_duplicates(values);
|
||||
|
|
|
@ -71,6 +71,8 @@ public:
|
|||
wxDECLARE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent);
|
||||
|
||||
using Vec2dEvent = Event<Vec2d>;
|
||||
// _bool_ value is used as a indicator of selection in the 3DScene
|
||||
using RBtnEvent = Event<std::pair<Vec2d, bool>>;
|
||||
template <size_t N> using Vec2dsEvent = ArrayEvent<Vec2d, N>;
|
||||
|
||||
using Vec3dEvent = Event<Vec3d>;
|
||||
|
@ -78,7 +80,7 @@ template <size_t N> using Vec3dsEvent = ArrayEvent<Vec3d, N>;
|
|||
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, Vec2dEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_SELECT_ALL, SimpleEvent);
|
||||
|
|
|
@ -725,7 +725,7 @@ bool GUI_App::load_language(wxString language, bool initial)
|
|||
#endif
|
||||
if (initial)
|
||||
message + "\n\nApplication will close.";
|
||||
wxMessageBox(message, "PrusaSlicer - Switching language failed", wxOK | wxICON_ERROR);
|
||||
wxMessageBox(message, "PrusaSlicer - Switching language failed", wxOK | wxICON_ERROR);
|
||||
if (initial)
|
||||
std::exit(EXIT_FAILURE);
|
||||
else
|
||||
|
|
|
@ -284,6 +284,9 @@ LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent,
|
|||
wxSize(8 * em_unit(parent->m_parent), wxDefaultCoord), wxTE_PROCESS_ENTER)
|
||||
{
|
||||
this->SetFont(wxGetApp().normal_font());
|
||||
|
||||
// Reset m_enter_pressed flag to _false_, when value is editing
|
||||
this->Bind(wxEVT_TEXT, [this](wxEvent&) { m_enter_pressed = false; }, this->GetId());
|
||||
|
||||
this->Bind(wxEVT_TEXT_ENTER, [this, edit_fn](wxEvent&)
|
||||
{
|
||||
|
@ -307,7 +310,7 @@ LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent,
|
|||
if (!m_enter_pressed) {
|
||||
#ifndef __WXGTK__
|
||||
/* Update data for next editor selection.
|
||||
* But under GTK it lucks like there is no information about selected control at e.GetWindow(),
|
||||
* But under GTK it looks like there is no information about selected control at e.GetWindow(),
|
||||
* so we'll take it from wxEVT_LEFT_DOWN event
|
||||
* */
|
||||
LayerRangeEditor* new_editor = dynamic_cast<LayerRangeEditor*>(e.GetWindow());
|
||||
|
|
|
@ -131,7 +131,7 @@ ObjectList::ObjectList(wxWindow* parent) :
|
|||
{
|
||||
wxDataViewItemArray sels;
|
||||
GetSelections(sels);
|
||||
if (sels.front() == m_last_selected_item)
|
||||
if (! sels.empty() && sels.front() == m_last_selected_item)
|
||||
m_last_selected_item = sels.back();
|
||||
else
|
||||
m_last_selected_item = event.GetItem();
|
||||
|
@ -255,21 +255,32 @@ void ObjectList::create_objects_ctrl()
|
|||
EnableDropTarget(wxDF_UNICODETEXT);
|
||||
#endif // wxUSE_DRAG_AND_DROP && wxUSE_UNICODE
|
||||
|
||||
const int em = wxGetApp().em_unit();
|
||||
|
||||
// column ItemName(Icon+Text) of the view control:
|
||||
// And Icon can be consisting of several bitmaps
|
||||
AppendColumn(new wxDataViewColumn(_(L("Name")), new BitmapTextRenderer(),
|
||||
colName, 20*wxGetApp().em_unit()/*200*/, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE));
|
||||
colName, 20*em, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE));
|
||||
|
||||
// column PrintableProperty (Icon) of the view control:
|
||||
AppendBitmapColumn(" ", colPrint, wxDATAVIEW_CELL_INERT, int(2 * wxGetApp().em_unit()),
|
||||
AppendBitmapColumn(" ", colPrint, wxDATAVIEW_CELL_INERT, 3*em,
|
||||
wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE);
|
||||
|
||||
// column Extruder of the view control:
|
||||
AppendColumn(create_objects_list_extruder_column(4));
|
||||
|
||||
// column ItemEditing of the view control:
|
||||
AppendBitmapColumn(_(L("Editing")), colEditing, wxDATAVIEW_CELL_INERT, int(2.5 * wxGetApp().em_unit())/*25*/,
|
||||
AppendBitmapColumn(_(L("Editing")), colEditing, wxDATAVIEW_CELL_INERT, 3*em,
|
||||
wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE);
|
||||
|
||||
// For some reason under OSX on 4K(5K) monitors in wxDataViewColumn constructor doesn't set width of column.
|
||||
// Therefore, force set column width.
|
||||
if (wxOSX)
|
||||
{
|
||||
GetColumn(colName)->SetWidth(20*em);
|
||||
GetColumn(colPrint)->SetWidth(3*em);
|
||||
GetColumn(colExtruder)->SetWidth(8*em);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectList::create_popup_menus()
|
||||
|
@ -279,6 +290,7 @@ void ObjectList::create_popup_menus()
|
|||
create_part_popupmenu(&m_menu_part);
|
||||
create_sla_object_popupmenu(&m_menu_sla_object);
|
||||
create_instance_popupmenu(&m_menu_instance);
|
||||
create_default_popupmenu(&m_menu_default);
|
||||
}
|
||||
|
||||
void ObjectList::get_selected_item_indexes(int& obj_idx, int& vol_idx, const wxDataViewItem& input_item/* = wxDataViewItem(nullptr)*/)
|
||||
|
@ -741,9 +753,9 @@ void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& vol
|
|||
}
|
||||
|
||||
select_items(items);
|
||||
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
|
||||
//#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
|
||||
selection_changed();
|
||||
#endif //no __WXOSX__ //__WXMSW__
|
||||
//#endif //no __WXOSX__ //__WXMSW__
|
||||
}
|
||||
|
||||
void ObjectList::paste_objects_into_list(const std::vector<size_t>& object_idxs)
|
||||
|
@ -761,9 +773,9 @@ void ObjectList::paste_objects_into_list(const std::vector<size_t>& object_idxs)
|
|||
wxGetApp().plater()->changed_objects(object_idxs);
|
||||
|
||||
select_items(items);
|
||||
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
|
||||
//#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
|
||||
selection_changed();
|
||||
#endif //no __WXOSX__ //__WXMSW__
|
||||
//#endif //no __WXOSX__ //__WXMSW__
|
||||
}
|
||||
|
||||
#ifdef __WXOSX__
|
||||
|
@ -783,18 +795,41 @@ void ObjectList::OnChar(wxKeyEvent& event)
|
|||
|
||||
void ObjectList::OnContextMenu(wxDataViewEvent&)
|
||||
{
|
||||
list_manipulation();
|
||||
list_manipulation(true);
|
||||
}
|
||||
|
||||
void ObjectList::list_manipulation()
|
||||
void ObjectList::list_manipulation(bool evt_context_menu/* = false*/)
|
||||
{
|
||||
wxDataViewItem item;
|
||||
wxDataViewColumn* col = nullptr;
|
||||
const wxPoint pt = get_mouse_position_in_control();
|
||||
HitTest(pt, item, col);
|
||||
|
||||
if (!item || col == nullptr) {
|
||||
return;
|
||||
/* Note: Under OSX right click doesn't send "selection changed" event.
|
||||
* It means that Selection() will be return still previously selected item.
|
||||
* Thus under OSX we should force UnselectAll(), when item and col are nullptr,
|
||||
* and select new item otherwise.
|
||||
*/
|
||||
|
||||
if (!item) {
|
||||
if (col == nullptr) {
|
||||
if (wxOSX)
|
||||
UnselectAll();
|
||||
else if (!evt_context_menu)
|
||||
// Case, when last item was deleted and under GTK was called wxEVT_DATAVIEW_SELECTION_CHANGED,
|
||||
// which invoked next list_manipulation(false)
|
||||
return;
|
||||
}
|
||||
|
||||
if (evt_context_menu) {
|
||||
show_context_menu(evt_context_menu);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (wxOSX && item && col) {
|
||||
UnselectAll();
|
||||
Select(item);
|
||||
}
|
||||
|
||||
const wxString title = col->GetTitle();
|
||||
|
@ -802,15 +837,21 @@ void ObjectList::list_manipulation()
|
|||
if (title == " ")
|
||||
toggle_printable_state(item);
|
||||
else if (title == _("Editing"))
|
||||
show_context_menu();
|
||||
show_context_menu(evt_context_menu);
|
||||
else if (title == _("Name"))
|
||||
{
|
||||
int obj_idx, vol_idx;
|
||||
get_selected_item_indexes(obj_idx, vol_idx, item);
|
||||
if (wxOSX)
|
||||
show_context_menu(evt_context_menu); // return context menu under OSX (related to #2909)
|
||||
|
||||
if (is_windows10() && get_mesh_errors_count(obj_idx, vol_idx) > 0 &&
|
||||
pt.x > 2*wxGetApp().em_unit() && pt.x < 4*wxGetApp().em_unit() )
|
||||
fix_through_netfabb();
|
||||
if (is_windows10())
|
||||
{
|
||||
int obj_idx, vol_idx;
|
||||
get_selected_item_indexes(obj_idx, vol_idx, item);
|
||||
|
||||
if (get_mesh_errors_count(obj_idx, vol_idx) > 0 &&
|
||||
pt.x > 2*wxGetApp().em_unit() && pt.x < 4*wxGetApp().em_unit() )
|
||||
fix_through_netfabb();
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef __WXMSW__
|
||||
|
@ -818,7 +859,7 @@ void ObjectList::list_manipulation()
|
|||
#endif //__WXMSW__
|
||||
}
|
||||
|
||||
void ObjectList::show_context_menu()
|
||||
void ObjectList::show_context_menu(const bool evt_context_menu)
|
||||
{
|
||||
if (multiple_selection())
|
||||
{
|
||||
|
@ -831,22 +872,26 @@ void ObjectList::show_context_menu()
|
|||
}
|
||||
|
||||
const auto item = GetSelection();
|
||||
wxMenu* menu {nullptr};
|
||||
if (item)
|
||||
{
|
||||
const ItemType type = m_objects_model->GetItemType(item);
|
||||
if (!(type & (itObject | itVolume | itLayer | itInstance)))
|
||||
return;
|
||||
|
||||
wxMenu* menu = type & itInstance ? &m_menu_instance :
|
||||
menu = type & itInstance ? &m_menu_instance :
|
||||
type & itLayer ? &m_menu_layer :
|
||||
m_objects_model->GetParent(item) != wxDataViewItem(nullptr) ? &m_menu_part :
|
||||
printer_technology() == ptFFF ? &m_menu_object : &m_menu_sla_object;
|
||||
|
||||
if (!(type & itInstance))
|
||||
append_menu_item_settings(menu);
|
||||
|
||||
wxGetApp().plater()->PopupMenu(menu);
|
||||
}
|
||||
else if (evt_context_menu)
|
||||
menu = &m_menu_default;
|
||||
|
||||
if (menu)
|
||||
wxGetApp().plater()->PopupMenu(menu);
|
||||
}
|
||||
|
||||
void ObjectList::copy()
|
||||
|
@ -1286,13 +1331,16 @@ void ObjectList::show_settings(const wxDataViewItem settings_item)
|
|||
wxMenu* ObjectList::append_submenu_add_generic(wxMenu* menu, const ModelVolumeType type) {
|
||||
auto sub_menu = new wxMenu;
|
||||
|
||||
if (wxGetApp().get_mode() == comExpert) {
|
||||
if (wxGetApp().get_mode() == comExpert && type != ModelVolumeType::INVALID) {
|
||||
append_menu_item(sub_menu, wxID_ANY, _(L("Load")) + " " + dots, "",
|
||||
[this, type](wxCommandEvent&) { load_subobject(type); }, "", menu);
|
||||
sub_menu->AppendSeparator();
|
||||
}
|
||||
|
||||
for (auto& item : { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") }) {
|
||||
for (auto& item : { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") })
|
||||
{
|
||||
if (type == ModelVolumeType::INVALID && strncmp(item, "Slab", 4) == 0)
|
||||
continue;
|
||||
append_menu_item(sub_menu, wxID_ANY, _(item), "",
|
||||
[this, type, item](wxCommandEvent&) { load_generic_subobject(item, type); }, "", menu);
|
||||
}
|
||||
|
@ -1579,6 +1627,12 @@ void ObjectList::create_instance_popupmenu(wxMenu*menu)
|
|||
}, m_menu_item_split_instances->GetId());
|
||||
}
|
||||
|
||||
void ObjectList::create_default_popupmenu(wxMenu*menu)
|
||||
{
|
||||
wxMenu* sub_menu = append_submenu_add_generic(menu, ModelVolumeType::INVALID);
|
||||
append_submenu(menu, sub_menu, wxID_ANY, _(L("Add Shape")), "", "add_part");
|
||||
}
|
||||
|
||||
wxMenu* ObjectList::create_settings_popupmenu(wxMenu *parent_menu)
|
||||
{
|
||||
wxMenu *menu = new wxMenu;
|
||||
|
@ -1668,9 +1722,9 @@ void ObjectList::load_subobject(ModelVolumeType type)
|
|||
if (sel_item)
|
||||
select_item(sel_item);
|
||||
|
||||
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
|
||||
//#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
|
||||
selection_changed();
|
||||
#endif //no __WXOSX__ //__WXMSW__
|
||||
//#endif //no __WXOSX__ //__WXMSW__
|
||||
}
|
||||
|
||||
void ObjectList::load_part( ModelObject* model_object,
|
||||
|
@ -1717,8 +1771,38 @@ void ObjectList::load_part( ModelObject* model_object,
|
|||
|
||||
}
|
||||
|
||||
static TriangleMesh create_mesh(const std::string& type_name, const BoundingBoxf3& bb)
|
||||
{
|
||||
TriangleMesh mesh;
|
||||
|
||||
const double side = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.1);
|
||||
|
||||
if (type_name == "Box")
|
||||
// Sitting on the print bed, left front front corner at (0, 0).
|
||||
mesh = make_cube(side, side, side);
|
||||
else if (type_name == "Cylinder")
|
||||
// Centered around 0, sitting on the print bed.
|
||||
// The cylinder has the same volume as the box above.
|
||||
mesh = make_cylinder(0.564 * side, side);
|
||||
else if (type_name == "Sphere")
|
||||
// Centered around 0, half the sphere below the print bed, half above.
|
||||
// The sphere has the same volume as the box above.
|
||||
mesh = make_sphere(0.62 * side, PI / 18);
|
||||
else if (type_name == "Slab")
|
||||
// Sitting on the print bed, left front front corner at (0, 0).
|
||||
mesh = make_cube(bb.size().x() * 1.5, bb.size().y() * 1.5, bb.size().z() * 0.5);
|
||||
mesh.repair();
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
void ObjectList::load_generic_subobject(const std::string& type_name, const ModelVolumeType type)
|
||||
{
|
||||
if (type == ModelVolumeType::INVALID) {
|
||||
load_shape_object(type_name);
|
||||
return;
|
||||
}
|
||||
|
||||
const int obj_idx = get_selected_obj_idx();
|
||||
if (obj_idx < 0)
|
||||
return;
|
||||
|
@ -1741,26 +1825,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
|
|||
// Bounding box of the selected instance in world coordinate system including the translation, without modifiers.
|
||||
BoundingBoxf3 instance_bb = model_object.instance_bounding_box(instance_idx);
|
||||
|
||||
const wxString name = _(L("Generic")) + "-" + _(type_name);
|
||||
TriangleMesh mesh;
|
||||
|
||||
double side = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.1);
|
||||
|
||||
if (type_name == "Box")
|
||||
// Sitting on the print bed, left front front corner at (0, 0).
|
||||
mesh = make_cube(side, side, side);
|
||||
else if (type_name == "Cylinder")
|
||||
// Centered around 0, sitting on the print bed.
|
||||
// The cylinder has the same volume as the box above.
|
||||
mesh = make_cylinder(0.564 * side, side);
|
||||
else if (type_name == "Sphere")
|
||||
// Centered around 0, half the sphere below the print bed, half above.
|
||||
// The sphere has the same volume as the box above.
|
||||
mesh = make_sphere(0.62 * side, PI / 18);
|
||||
else if (type_name == "Slab")
|
||||
// Sitting on the print bed, left front front corner at (0, 0).
|
||||
mesh = make_cube(instance_bb.size().x()*1.5, instance_bb.size().y()*1.5, instance_bb.size().z()*0.5);
|
||||
mesh.repair();
|
||||
TriangleMesh mesh = create_mesh(type_name, instance_bb);
|
||||
|
||||
// Mesh will be centered when loading.
|
||||
ModelVolume *new_volume = model_object.add_volume(std::move(mesh));
|
||||
|
@ -1782,6 +1847,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
|
|||
new_volume->set_offset(v->get_instance_transformation().get_matrix(true).inverse() * offset);
|
||||
}
|
||||
|
||||
const wxString name = _(L("Generic")) + "-" + _(type_name);
|
||||
new_volume->name = into_u8(name);
|
||||
// set a default extruder value, since user can't add it manually
|
||||
new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
|
||||
|
@ -1794,9 +1860,63 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
|
|||
const auto object_item = m_objects_model->GetTopParent(GetSelection());
|
||||
select_item(m_objects_model->AddVolumeChild(object_item, name, type,
|
||||
new_volume->get_mesh_errors_count()>0));
|
||||
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
|
||||
//#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
|
||||
selection_changed();
|
||||
#endif //no __WXOSX__ //__WXMSW__
|
||||
//#endif //no __WXOSX__ //__WXMSW__
|
||||
}
|
||||
|
||||
void ObjectList::load_shape_object(const std::string& type_name)
|
||||
{
|
||||
const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
|
||||
assert(selection.get_object_idx() == -1); // Add nothing is something is selected on 3DScene
|
||||
if (selection.get_object_idx() != -1)
|
||||
return;
|
||||
|
||||
const int obj_idx = m_objects->size();
|
||||
if (obj_idx < 0)
|
||||
return;
|
||||
|
||||
take_snapshot(_(L("Add Shape")));
|
||||
|
||||
// Create mesh
|
||||
BoundingBoxf3 bb;
|
||||
TriangleMesh mesh = create_mesh(type_name, bb);
|
||||
|
||||
// Add mesh to model as a new object
|
||||
Model& model = wxGetApp().plater()->model();
|
||||
const wxString name = _(L("Shape")) + "-" + _(type_name);
|
||||
|
||||
#ifdef _DEBUG
|
||||
check_model_ids_validity(model);
|
||||
#endif /* _DEBUG */
|
||||
|
||||
std::vector<size_t> object_idxs;
|
||||
ModelObject* new_object = model.add_object();
|
||||
new_object->name = into_u8(name);
|
||||
new_object->add_instance(); // each object should have at list one instance
|
||||
|
||||
ModelVolume* new_volume = new_object->add_volume(mesh);
|
||||
new_volume->name = into_u8(name);
|
||||
// set a default extruder value, since user can't add it manually
|
||||
new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
|
||||
new_object->invalidate_bounding_box();
|
||||
|
||||
new_object->center_around_origin();
|
||||
new_object->ensure_on_bed();
|
||||
|
||||
const BoundingBoxf bed_shape = wxGetApp().plater()->bed_shape_bb();
|
||||
new_object->instances[0]->set_offset(Slic3r::to_3d(bed_shape.center().cast<double>(), -new_object->origin_translation(2)));
|
||||
|
||||
object_idxs.push_back(model.objects.size() - 1);
|
||||
#ifdef _DEBUG
|
||||
check_model_ids_validity(model);
|
||||
#endif /* _DEBUG */
|
||||
|
||||
paste_objects_into_list(object_idxs);
|
||||
|
||||
#ifdef _DEBUG
|
||||
check_model_ids_validity(model);
|
||||
#endif /* _DEBUG */
|
||||
}
|
||||
|
||||
void ObjectList::del_object(const int obj_idx)
|
||||
|
@ -3569,10 +3689,10 @@ void ObjectList::msw_rescale()
|
|||
// update min size !!! A width of control shouldn't be a wxDefaultCoord
|
||||
SetMinSize(wxSize(1, 15 * em));
|
||||
|
||||
GetColumn(colName)->SetWidth(19 * em);
|
||||
GetColumn(colPrint)->SetWidth( 2 * em);
|
||||
GetColumn(colName )->SetWidth(20 * em);
|
||||
GetColumn(colPrint )->SetWidth( 3 * em);
|
||||
GetColumn(colExtruder)->SetWidth( 8 * em);
|
||||
GetColumn(colEditing)->SetWidth( 2 * em);
|
||||
GetColumn(colEditing )->SetWidth( 3 * em);
|
||||
|
||||
// rescale all icons, used by ObjectList
|
||||
msw_rescale_icons();
|
||||
|
@ -3585,7 +3705,8 @@ void ObjectList::msw_rescale()
|
|||
&m_menu_part,
|
||||
&m_menu_sla_object,
|
||||
&m_menu_instance,
|
||||
&m_menu_layer })
|
||||
&m_menu_layer,
|
||||
&m_menu_default})
|
||||
msw_rescale_menu(menu);
|
||||
|
||||
Layout();
|
||||
|
|
|
@ -132,6 +132,7 @@ private:
|
|||
MenuWithSeparators m_menu_sla_object;
|
||||
MenuWithSeparators m_menu_instance;
|
||||
MenuWithSeparators m_menu_layer;
|
||||
MenuWithSeparators m_menu_default;
|
||||
wxMenuItem* m_menu_item_settings { nullptr };
|
||||
wxMenuItem* m_menu_item_split_instances { nullptr };
|
||||
|
||||
|
@ -208,7 +209,7 @@ public:
|
|||
void set_tooltip_for_item(const wxPoint& pt);
|
||||
|
||||
void selection_changed();
|
||||
void show_context_menu();
|
||||
void show_context_menu(const bool evt_context_menu);
|
||||
#ifndef __WXOSX__
|
||||
void key_event(wxKeyEvent& event);
|
||||
#endif /* __WXOSX__ */
|
||||
|
@ -240,6 +241,7 @@ public:
|
|||
void create_sla_object_popupmenu(wxMenu*menu);
|
||||
void create_part_popupmenu(wxMenu*menu);
|
||||
void create_instance_popupmenu(wxMenu*menu);
|
||||
void create_default_popupmenu(wxMenu *menu);
|
||||
wxMenu* create_settings_popupmenu(wxMenu *parent_menu);
|
||||
void create_freq_settings_popupmenu(wxMenu *parent_menu, const bool is_object_settings = true);
|
||||
|
||||
|
@ -248,6 +250,7 @@ public:
|
|||
void load_subobject(ModelVolumeType type);
|
||||
void load_part(ModelObject* model_object, std::vector<std::pair<wxString, bool>> &volumes_info, ModelVolumeType type);
|
||||
void load_generic_subobject(const std::string& type_name, const ModelVolumeType type);
|
||||
void load_shape_object(const std::string &type_name);
|
||||
void del_object(const int obj_idx);
|
||||
void del_subobject_item(wxDataViewItem& item);
|
||||
void del_settings_from_config(const wxDataViewItem& parent_item);
|
||||
|
@ -362,7 +365,7 @@ private:
|
|||
// void OnChar(wxKeyEvent& event);
|
||||
#endif /* __WXOSX__ */
|
||||
void OnContextMenu(wxDataViewEvent &event);
|
||||
void list_manipulation();
|
||||
void list_manipulation(bool evt_context_menu = false);
|
||||
|
||||
void OnBeginDrag(wxDataViewEvent &event);
|
||||
void OnDropPossible(wxDataViewEvent &event);
|
||||
|
|
|
@ -221,6 +221,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view
|
|||
m_choice_view_type->Append(_(L("Height")));
|
||||
m_choice_view_type->Append(_(L("Width")));
|
||||
m_choice_view_type->Append(_(L("Speed")));
|
||||
m_choice_view_type->Append(_(L("Fan speed")));
|
||||
m_choice_view_type->Append(_(L("Volumetric flow rate")));
|
||||
m_choice_view_type->Append(_(L("Tool")));
|
||||
m_choice_view_type->Append(_(L("Color Print")));
|
||||
|
|
|
@ -252,7 +252,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||
const sla::SupportPoint& support_point = m_editing_mode ? m_editing_cache[i].support_point : m_normal_cache[i];
|
||||
const bool& point_selected = m_editing_mode ? m_editing_cache[i].selected : false;
|
||||
|
||||
if (is_point_clipped(support_point.pos.cast<double>()))
|
||||
if (is_mesh_point_clipped(support_point.pos.cast<double>()))
|
||||
continue;
|
||||
|
||||
// First decide about the color of the point.
|
||||
|
@ -335,14 +335,14 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||
|
||||
|
||||
|
||||
bool GLGizmoSlaSupports::is_point_clipped(const Vec3d& point) const
|
||||
bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const
|
||||
{
|
||||
if (m_clipping_plane_distance == 0.f)
|
||||
return false;
|
||||
|
||||
Vec3d transformed_point = m_model_object->instances.front()->get_transformation().get_matrix() * point;
|
||||
transformed_point(2) += m_z_shift;
|
||||
return m_clipping_plane->distance(transformed_point) < 0.;
|
||||
return m_clipping_plane->is_point_clipped(transformed_point);
|
||||
}
|
||||
|
||||
|
||||
|
@ -391,27 +391,15 @@ bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec
|
|||
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift));
|
||||
|
||||
// The raycaster query
|
||||
std::vector<Vec3f> hits;
|
||||
std::vector<Vec3f> normals;
|
||||
m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, &hits, &normals);
|
||||
|
||||
// We must also take care of the clipping plane (if active)
|
||||
unsigned i = 0;
|
||||
if (m_clipping_plane_distance != 0.f) {
|
||||
for (i=0; i<hits.size(); ++i)
|
||||
if (! is_point_clipped(hits[i].cast<double>()))
|
||||
break;
|
||||
Vec3f hit;
|
||||
Vec3f normal;
|
||||
if (m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get())) {
|
||||
// Return both the point and the facet normal.
|
||||
pos_and_normal = std::make_pair(hit, normal);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (i==hits.size() || (hits.size()-i) % 2 != 0) {
|
||||
// All hits are either clipped, or there is an odd number of unclipped
|
||||
// hits - meaning the nearest must be from inside the mesh.
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// Calculate and return both the point and the facet normal.
|
||||
pos_and_normal = std::make_pair(hits[i], normals[i]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event.
|
||||
|
@ -481,19 +469,15 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
std::vector<Vec3f> points_inside;
|
||||
std::vector<unsigned int> points_idxs = m_selection_rectangle.stop_dragging(m_parent, points);
|
||||
for (size_t idx : points_idxs)
|
||||
points_inside.push_back((trafo.get_matrix() * points[idx]).cast<float>());
|
||||
points_inside.push_back(points[idx].cast<float>());
|
||||
|
||||
// Only select/deselect points that are actually visible
|
||||
for (size_t idx : m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside,
|
||||
[this](const Vec3f& pt) { return is_point_clipped(pt.cast<double>()); }))
|
||||
for (size_t idx : m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_clipping_plane.get()))
|
||||
{
|
||||
const sla::SupportPoint &support_point = m_editing_cache[points_idxs[idx]].support_point;
|
||||
if (! is_point_clipped(support_point.pos.cast<double>())) {
|
||||
if (rectangle_status == GLSelectionRectangle::Deselect)
|
||||
unselect_point(points_idxs[idx]);
|
||||
else
|
||||
select_point(points_idxs[idx]);
|
||||
}
|
||||
if (rectangle_status == GLSelectionRectangle::Deselect)
|
||||
unselect_point(points_idxs[idx]);
|
||||
else
|
||||
select_point(points_idxs[idx]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -125,7 +125,7 @@ private:
|
|||
mutable std::unique_ptr<MeshClipper> m_supports_clipper;
|
||||
|
||||
std::vector<const ConfigOption*> get_config_options(const std::vector<std::string>& keys) const;
|
||||
bool is_point_clipped(const Vec3d& point) const;
|
||||
bool is_mesh_point_clipped(const Vec3d& point) const;
|
||||
//void find_intersecting_facets(const igl::AABB<Eigen::MatrixXf, 3>* aabb, const Vec3f& normal, double offset, std::vector<unsigned int>& out) const;
|
||||
|
||||
// Methods that do the model_object and editing cache synchronization,
|
||||
|
|
|
@ -192,7 +192,7 @@ ConfigOptionsGroupShp LambdaObjectDialog::init_modificator_options_page(const wx
|
|||
else
|
||||
panel->SetSizer(optgroup->sizer);
|
||||
|
||||
return optgroup;
|
||||
return optgroup;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -917,7 +917,7 @@ void MainFrame::load_config_file()
|
|||
wxString file;
|
||||
if (dlg.ShowModal() == wxID_OK)
|
||||
file = dlg.GetPath();
|
||||
if (! file.IsEmpty() && this->load_config_file(file.ToUTF8().data())) {
|
||||
if (! file.IsEmpty() && this->load_config_file(file.ToUTF8().data())) {
|
||||
wxGetApp().app_config->update_config_dir(get_dir_name(file));
|
||||
m_last_config = file;
|
||||
}
|
||||
|
|
|
@ -152,8 +152,8 @@ Vec3f MeshRaycaster::AABBWrapper::get_hit_normal(const igl::Hit& hit) const
|
|||
}
|
||||
|
||||
|
||||
bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo,
|
||||
const Camera& camera, std::vector<Vec3f>* positions, std::vector<Vec3f>* normals) const
|
||||
bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
|
||||
Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane) const
|
||||
{
|
||||
const std::array<int, 4>& viewport = camera.get_viewport();
|
||||
const Transform3d& model_mat = camera.get_view_matrix();
|
||||
|
@ -179,25 +179,30 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d&
|
|||
|
||||
std::sort(hits.begin(), hits.end(), [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; });
|
||||
|
||||
// Now stuff the points in the provided vector and calculate normals if asked about them:
|
||||
if (positions != nullptr) {
|
||||
positions->clear();
|
||||
if (normals != nullptr)
|
||||
normals->clear();
|
||||
for (const igl::Hit& hit : hits) {
|
||||
positions->push_back(m_AABB_wrapper->get_hit_pos(hit));
|
||||
unsigned i = 0;
|
||||
|
||||
if (normals != nullptr)
|
||||
normals->push_back(m_AABB_wrapper->get_hit_normal(hit));
|
||||
// Remove points that are obscured or cut by the clipping plane
|
||||
if (clipping_plane) {
|
||||
for (i=0; i<hits.size(); ++i)
|
||||
if (! clipping_plane->is_point_clipped(trafo * m_AABB_wrapper->get_hit_pos(hits[i]).cast<double>()))
|
||||
break;
|
||||
|
||||
if (i==hits.size() || (hits.size()-i) % 2 != 0) {
|
||||
// All hits are either clipped, or there is an odd number of unclipped
|
||||
// hits - meaning the nearest must be from inside the mesh.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Now stuff the points in the provided vector and calculate normals if asked about them:
|
||||
position = m_AABB_wrapper->get_hit_pos(hits[i]);
|
||||
normal = m_AABB_wrapper->get_hit_normal(hits[i]);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transformation& trafo, const Camera& camera, const std::vector<Vec3f>& points,
|
||||
std::function<bool(const Vec3f&)> fn_ignore_hit) const
|
||||
const ClippingPlane* clipping_plane) const
|
||||
{
|
||||
std::vector<unsigned> out;
|
||||
|
||||
|
@ -206,19 +211,24 @@ std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo
|
|||
Vec3f direction_to_camera_mesh = (instance_matrix_no_translation_no_scaling.inverse().cast<float>() * direction_to_camera).normalized().eval();
|
||||
Vec3f scaling = trafo.get_scaling_factor().cast<float>();
|
||||
direction_to_camera_mesh = Vec3f(direction_to_camera_mesh(0)*scaling(0), direction_to_camera_mesh(1)*scaling(1), direction_to_camera_mesh(2)*scaling(2));
|
||||
const Transform3f inverse_trafo = trafo.get_matrix().inverse().cast<float>();
|
||||
|
||||
for (size_t i=0; i<points.size(); ++i) {
|
||||
const Vec3f& pt = points[i];
|
||||
if (clipping_plane && clipping_plane->is_point_clipped(pt.cast<double>()))
|
||||
continue;
|
||||
|
||||
bool is_obscured = false;
|
||||
// Cast a ray in the direction of the camera and look for intersection with the mesh:
|
||||
std::vector<igl::Hit> hits;
|
||||
// Offset the start of the ray to the front of the ball + EPSILON to account for numerical inaccuracies.
|
||||
// Offset the start of the ray by EPSILON to account for numerical inaccuracies.
|
||||
if (m_AABB_wrapper->m_AABB.intersect_ray(
|
||||
AABBWrapper::MapMatrixXfUnaligned(m_mesh->its.vertices.front().data(), m_mesh->its.vertices.size(), 3),
|
||||
AABBWrapper::MapMatrixXiUnaligned(m_mesh->its.indices.front().data(), m_mesh->its.indices.size(), 3),
|
||||
pt + direction_to_camera_mesh * EPSILON, direction_to_camera_mesh, hits)) {
|
||||
inverse_trafo * pt + direction_to_camera_mesh * EPSILON, direction_to_camera_mesh, hits)) {
|
||||
|
||||
std::sort(hits.begin(), hits.end(), [](const igl::Hit& h1, const igl::Hit& h2) { return h1.t < h2.t; });
|
||||
|
||||
// If the closest hit facet normal points in the same direction as the ray,
|
||||
// we are looking through the mesh and should therefore discard the point:
|
||||
if (m_AABB_wrapper->get_hit_normal(hits.front()).dot(direction_to_camera_mesh) > 0.f)
|
||||
|
@ -227,11 +237,12 @@ std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo
|
|||
// Eradicate all hits that the caller wants to ignore
|
||||
for (unsigned j=0; j<hits.size(); ++j) {
|
||||
const igl::Hit& hit = hits[j];
|
||||
if (fn_ignore_hit(m_AABB_wrapper->get_hit_pos(hit))) {
|
||||
if (clipping_plane && clipping_plane->is_point_clipped(trafo.get_matrix() * m_AABB_wrapper->get_hit_pos(hit).cast<double>())) {
|
||||
hits.erase(hits.begin()+j);
|
||||
--j;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: the intersection could in theory be behind the camera, but as of now we only have camera direction.
|
||||
// Also, the threshold is in mesh coordinates, not in actual dimensions.
|
||||
if (! hits.empty())
|
||||
|
|
|
@ -50,6 +50,7 @@ public:
|
|||
return (-get_normal().dot(pt) + m_data[3]);
|
||||
}
|
||||
|
||||
bool is_point_clipped(const Vec3d& point) const { return distance(point) < 0.; }
|
||||
void set_normal(const Vec3d& normal) { for (size_t i=0; i<3; ++i) m_data[i] = normal(i); }
|
||||
void set_offset(double offset) { m_data[3] = offset; }
|
||||
Vec3d get_normal() const { return Vec3d(m_data[0], m_data[1], m_data[2]); }
|
||||
|
@ -98,10 +99,10 @@ public:
|
|||
void set_camera(const Camera& camera);
|
||||
|
||||
bool unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
|
||||
std::vector<Vec3f>* positions = nullptr, std::vector<Vec3f>* normals = nullptr) const;
|
||||
Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane = nullptr) const;
|
||||
|
||||
std::vector<unsigned> get_unobscured_idxs(const Geometry::Transformation& trafo, const Camera& camera,
|
||||
const std::vector<Vec3f>& points, std::function<bool(const Vec3f&)> fn_ignore_hit) const;
|
||||
const std::vector<Vec3f>& points, const ClippingPlane* clipping_plane = nullptr) const;
|
||||
|
||||
Vec3f get_closest_point(const Vec3f& point, Vec3f* normal = nullptr) const;
|
||||
|
||||
|
|
|
@ -133,7 +133,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
|
|||
m_options_mode.push_back(option_set[0].opt.mode);
|
||||
|
||||
// if we have a single option with no label, no sidetext just add it directly to sizer
|
||||
if (option_set.size() == 1 && label_width == 0 && option_set.front().opt.full_width &&
|
||||
if (option_set.size() == 1 && label_width == 0 && option_set.front().opt.full_width &&
|
||||
option_set.front().opt.label.empty() &&
|
||||
option_set.front().opt.sidetext.size() == 0 && option_set.front().side_widget == nullptr &&
|
||||
line.get_extra_widgets().size() == 0) {
|
||||
|
|
|
@ -1347,6 +1347,8 @@ struct Plater::priv
|
|||
MenuWithSeparators part_menu;
|
||||
// SLA-Object popup menu
|
||||
MenuWithSeparators sla_object_menu;
|
||||
// Default popup menu (when nothing is selected on 3DScene)
|
||||
MenuWithSeparators default_menu;
|
||||
|
||||
// Removed/Prepended Items according to the view mode
|
||||
std::vector<wxMenuItem*> items_increase;
|
||||
|
@ -1886,7 +1888,7 @@ struct Plater::priv
|
|||
void on_action_layersediting(SimpleEvent&);
|
||||
|
||||
void on_object_select(SimpleEvent&);
|
||||
void on_right_click(Vec2dEvent&);
|
||||
void on_right_click(RBtnEvent&);
|
||||
void on_wipetower_moved(Vec3dEvent&);
|
||||
void on_wipetower_rotated(Vec3dEvent&);
|
||||
void on_update_geometry(Vec3dsEvent<2>&);
|
||||
|
@ -2525,6 +2527,10 @@ wxString Plater::priv::get_export_file(GUI::FileType file_type)
|
|||
if (output_file.empty())
|
||||
// Find the file name of the first printable object.
|
||||
output_file = this->model.propose_export_file_name_and_path();
|
||||
|
||||
if (output_file.empty() && !model.objects.empty())
|
||||
// Find the file name of the first object.
|
||||
output_file = this->model.objects[0]->get_export_filename();
|
||||
}
|
||||
|
||||
wxString dlg_title;
|
||||
|
@ -3323,7 +3329,7 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
|
|||
this->statusbar()->set_progress(evt.status.percent);
|
||||
this->statusbar()->set_status_text(_(evt.status.text) + wxString::FromUTF8("…"));
|
||||
}
|
||||
if (evt.status.flags & (PrintBase::SlicingStatus::RELOAD_SCENE || PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS)) {
|
||||
if (evt.status.flags & (PrintBase::SlicingStatus::RELOAD_SCENE | PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS)) {
|
||||
switch (this->printer_technology) {
|
||||
case ptFFF:
|
||||
this->update_fff_scene();
|
||||
|
@ -3449,57 +3455,66 @@ void Plater::priv::on_object_select(SimpleEvent& evt)
|
|||
selection_changed();
|
||||
}
|
||||
|
||||
void Plater::priv::on_right_click(Vec2dEvent& evt)
|
||||
void Plater::priv::on_right_click(RBtnEvent& evt)
|
||||
{
|
||||
int obj_idx = get_selected_object_idx();
|
||||
|
||||
wxMenu* menu = nullptr;
|
||||
|
||||
if (obj_idx == -1)
|
||||
return;
|
||||
|
||||
wxMenu* menu = printer_technology == ptSLA ? &sla_object_menu :
|
||||
get_selection().is_single_full_instance() ? // show "Object menu" for each FullInstance instead of FullObject
|
||||
&object_menu : &part_menu;
|
||||
|
||||
sidebar->obj_list()->append_menu_item_settings(menu);
|
||||
|
||||
if (printer_technology != ptSLA)
|
||||
sidebar->obj_list()->append_menu_item_change_extruder(menu);
|
||||
|
||||
if (menu != &part_menu)
|
||||
menu = &default_menu;
|
||||
else
|
||||
{
|
||||
/* Remove/Prepend "increase/decrease instances" menu items according to the view mode.
|
||||
* Suppress to show those items for a Simple mode
|
||||
*/
|
||||
const MenuIdentifier id = printer_technology == ptSLA ? miObjectSLA : miObjectFFF;
|
||||
if (wxGetApp().get_mode() == comSimple) {
|
||||
if (menu->FindItem(_(L("Add instance"))) != wxNOT_FOUND)
|
||||
{
|
||||
/* Detach an items from the menu, but don't delete them
|
||||
* so that they can be added back later
|
||||
* (after switching to the Advanced/Expert mode)
|
||||
*/
|
||||
menu->Remove(items_increase[id]);
|
||||
menu->Remove(items_decrease[id]);
|
||||
menu->Remove(items_set_number_of_copies[id]);
|
||||
// If in 3DScene is(are) selected volume(s), but right button was clicked on empty space
|
||||
if (evt.data.second)
|
||||
return;
|
||||
|
||||
menu = printer_technology == ptSLA ? &sla_object_menu :
|
||||
get_selection().is_single_full_instance() ? // show "Object menu" for each FullInstance instead of FullObject
|
||||
&object_menu : &part_menu;
|
||||
|
||||
sidebar->obj_list()->append_menu_item_settings(menu);
|
||||
|
||||
if (printer_technology != ptSLA)
|
||||
sidebar->obj_list()->append_menu_item_change_extruder(menu);
|
||||
|
||||
if (menu != &part_menu)
|
||||
{
|
||||
/* Remove/Prepend "increase/decrease instances" menu items according to the view mode.
|
||||
* Suppress to show those items for a Simple mode
|
||||
*/
|
||||
const MenuIdentifier id = printer_technology == ptSLA ? miObjectSLA : miObjectFFF;
|
||||
if (wxGetApp().get_mode() == comSimple) {
|
||||
if (menu->FindItem(_(L("Add instance"))) != wxNOT_FOUND)
|
||||
{
|
||||
/* Detach an items from the menu, but don't delete them
|
||||
* so that they can be added back later
|
||||
* (after switching to the Advanced/Expert mode)
|
||||
*/
|
||||
menu->Remove(items_increase[id]);
|
||||
menu->Remove(items_decrease[id]);
|
||||
menu->Remove(items_set_number_of_copies[id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (menu->FindItem(_(L("Add instance"))) == wxNOT_FOUND)
|
||||
{
|
||||
// Prepend items to the menu, if those aren't not there
|
||||
menu->Prepend(items_set_number_of_copies[id]);
|
||||
menu->Prepend(items_decrease[id]);
|
||||
menu->Prepend(items_increase[id]);
|
||||
else {
|
||||
if (menu->FindItem(_(L("Add instance"))) == wxNOT_FOUND)
|
||||
{
|
||||
// Prepend items to the menu, if those aren't not there
|
||||
menu->Prepend(items_set_number_of_copies[id]);
|
||||
menu->Prepend(items_decrease[id]);
|
||||
menu->Prepend(items_increase[id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (q != nullptr) {
|
||||
if (q != nullptr && menu) {
|
||||
#ifdef __linux__
|
||||
// For some reason on Linux the menu isn't displayed if position is specified
|
||||
// (even though the position is sane).
|
||||
q->PopupMenu(menu);
|
||||
#else
|
||||
q->PopupMenu(menu, (int)evt.data.x(), (int)evt.data.y());
|
||||
q->PopupMenu(menu, (int)evt.data.first.x(), (int)evt.data.first.y());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -3551,12 +3566,14 @@ bool Plater::priv::init_object_menu()
|
|||
init_common_menu(&part_menu, true);
|
||||
complit_init_part_menu();
|
||||
|
||||
sidebar->obj_list()->create_default_popupmenu(&default_menu);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Plater::priv::msw_rescale_object_menu()
|
||||
{
|
||||
for (MenuWithSeparators* menu : { &object_menu, &sla_object_menu, &part_menu })
|
||||
for (MenuWithSeparators* menu : { &object_menu, &sla_object_menu, &part_menu, &default_menu })
|
||||
msw_rescale_menu(dynamic_cast<wxMenu*>(menu));
|
||||
}
|
||||
|
||||
|
@ -4278,11 +4295,10 @@ void Plater::increase_instances(size_t num)
|
|||
|
||||
sidebar().obj_list()->increase_object_instances(obj_idx, was_one_instance ? num + 1 : num);
|
||||
|
||||
if (p->get_config("autocenter") == "1") {
|
||||
if (p->get_config("autocenter") == "1")
|
||||
p->arrange();
|
||||
} else {
|
||||
p->update();
|
||||
}
|
||||
|
||||
p->update();
|
||||
|
||||
p->get_selection().add_instance(obj_idx, (int)model_object->instances.size() - 1);
|
||||
|
||||
|
@ -4326,14 +4342,14 @@ void Plater::set_number_of_copies(/*size_t num*/)
|
|||
|
||||
ModelObject* model_object = p->model.objects[obj_idx];
|
||||
|
||||
const auto num = wxGetNumberFromUser( " ", _("Enter the number of copies:"),
|
||||
const int num = wxGetNumberFromUser( " ", _("Enter the number of copies:"),
|
||||
_("Copies of the selected object"), model_object->instances.size(), 0, 1000, this );
|
||||
if (num < 0)
|
||||
return;
|
||||
|
||||
Plater::TakeSnapshot snapshot(this, wxString::Format(_(L("Set numbers of copies to %d")), num));
|
||||
|
||||
int diff = (int)num - (int)model_object->instances.size();
|
||||
int diff = num - (int)model_object->instances.size();
|
||||
if (diff > 0)
|
||||
increase_instances(diff);
|
||||
else if (diff < 0)
|
||||
|
@ -4833,6 +4849,34 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
|
|||
this->p->schedule_background_process();
|
||||
}
|
||||
|
||||
void Plater::force_filament_colors_update()
|
||||
{
|
||||
bool update_scheduled = false;
|
||||
DynamicPrintConfig* config = p->config;
|
||||
const std::vector<std::string> filament_presets = wxGetApp().preset_bundle->filament_presets;
|
||||
if (filament_presets.size() > 1 &&
|
||||
p->config->option<ConfigOptionStrings>("filament_colour")->values.size() == filament_presets.size())
|
||||
{
|
||||
const PresetCollection& filaments = wxGetApp().preset_bundle->filaments;
|
||||
std::vector<std::string> filament_colors;
|
||||
filament_colors.reserve(filament_presets.size());
|
||||
|
||||
for (const std::string& filament_preset : filament_presets)
|
||||
filament_colors.push_back(filaments.find_preset(filament_preset, true)->config.opt_string("filament_colour", (unsigned)0));
|
||||
|
||||
if (config->option<ConfigOptionStrings>("filament_colour")->values != filament_colors) {
|
||||
config->option<ConfigOptionStrings>("filament_colour")->values = filament_colors;
|
||||
update_scheduled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (update_scheduled)
|
||||
update();
|
||||
|
||||
if (p->main_frame->is_loaded())
|
||||
this->p->schedule_background_process();
|
||||
}
|
||||
|
||||
void Plater::on_activate()
|
||||
{
|
||||
#ifdef __linux__
|
||||
|
@ -4884,6 +4928,11 @@ GLCanvas3D* Plater::canvas3D()
|
|||
return p->view3D->get_canvas3d();
|
||||
}
|
||||
|
||||
BoundingBoxf Plater::bed_shape_bb() const
|
||||
{
|
||||
return p->bed_shape_bb();
|
||||
}
|
||||
|
||||
PrinterTechnology Plater::printer_technology() const
|
||||
{
|
||||
return p->printer_technology;
|
||||
|
|
|
@ -215,6 +215,7 @@ public:
|
|||
|
||||
void on_extruders_change(size_t extruders_count);
|
||||
void on_config_change(const DynamicPrintConfig &config);
|
||||
void force_filament_colors_update();
|
||||
// On activating the parent window.
|
||||
void on_activate();
|
||||
const DynamicPrintConfig* get_plater_config() const;
|
||||
|
@ -229,6 +230,7 @@ public:
|
|||
int get_selected_object_idx();
|
||||
bool is_single_full_object_selection() const;
|
||||
GLCanvas3D* canvas3D();
|
||||
BoundingBoxf bed_shape_bb() const;
|
||||
|
||||
PrinterTechnology printer_technology() const;
|
||||
void set_printer_technology(PrinterTechnology printer_technology);
|
||||
|
|
|
@ -282,7 +282,7 @@ std::string PresetBundle::load_system_presets()
|
|||
errors_cummulative += "\n";
|
||||
}
|
||||
}
|
||||
if (first) {
|
||||
if (first) {
|
||||
// No config bundle loaded, reset.
|
||||
this->reset(false);
|
||||
}
|
||||
|
|
|
@ -410,7 +410,7 @@ void Selection::set_deserialized(EMode mode, const std::vector<std::pair<size_t,
|
|||
if (! m_valid)
|
||||
return;
|
||||
|
||||
m_mode = mode;
|
||||
m_mode = mode;
|
||||
for (unsigned int i : m_list)
|
||||
(*m_volumes)[i]->selected = false;
|
||||
m_list.clear();
|
||||
|
|
|
@ -3032,6 +3032,12 @@ void Tab::save_preset(std::string name /*= ""*/)
|
|||
if (m_type == Preset::TYPE_PRINTER)
|
||||
static_cast<TabPrinter*>(this)->m_initial_extruders_count = static_cast<TabPrinter*>(this)->m_extruders_count;
|
||||
update_changed_ui();
|
||||
|
||||
/* If filament preset is saved for multi-material printer preset,
|
||||
* there are cases when filament comboboxs are updated for old (non-modified) colors,
|
||||
* but in full_config a filament_colors option aren't.*/
|
||||
if (m_type == Preset::TYPE_FILAMENT && wxGetApp().extruders_edited_cnt() > 1)
|
||||
wxGetApp().plater()->force_filament_colors_update();
|
||||
}
|
||||
|
||||
// Called for a currently selected preset.
|
||||
|
|
|
@ -371,7 +371,7 @@ void WipingPanel::toggle_advanced(bool user_action) {
|
|||
else
|
||||
m_advanced = !advanced_matches_simple(); // if called from constructor, show what is appropriate
|
||||
|
||||
(m_advanced ? m_page_advanced : m_page_simple)->Show();
|
||||
(m_advanced ? m_page_advanced : m_page_simple)->Show();
|
||||
(!m_advanced ? m_page_advanced : m_page_simple)->Hide();
|
||||
|
||||
m_widget_button->SetLabel(m_advanced ? _(L("Show simplified settings")) : _(L("Show advanced settings")));
|
||||
|
|
|
@ -669,7 +669,7 @@ wxDataViewItem ObjectDataViewModel::Add(const wxString &name,
|
|||
if (has_errors)
|
||||
root->m_bmp = *m_warning_bmp;
|
||||
|
||||
m_objects.push_back(root);
|
||||
m_objects.push_back(root);
|
||||
// notify control
|
||||
wxDataViewItem child((void*)root);
|
||||
wxDataViewItem parent((void*)NULL);
|
||||
|
@ -720,7 +720,7 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent
|
|||
root->SetBitmap(*m_warning_bmp);
|
||||
|
||||
// notify control
|
||||
const wxDataViewItem child((void*)node);
|
||||
const wxDataViewItem child((void*)node);
|
||||
ItemAdded(parent_item, child);
|
||||
root->m_volumes_cnt++;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue