diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index b834bc202..e9668225f 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -41,6 +41,7 @@ our $PROCESS_COMPLETED_EVENT = Wx::NewEventType; my $PreventListEvents = 0; our $appController; +# XXX: VK: done, except callback handling and timer sub new { my ($class, $parent, %params) = @_; my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); @@ -813,12 +814,14 @@ sub new { return $self; } +# XXX: VK: WIP # sets the callback sub on_select_preset { my ($self, $cb) = @_; $self->{on_select_preset} = $cb; } +# XXX: merged with on_select_preset # Called from the platter combo boxes selecting the active print, filament or printer. sub _on_select_preset { my ($self, $group, $choice, $idx) = @_; @@ -855,6 +858,7 @@ sub _on_select_preset { $self->on_config_change(wxTheApp->{preset_bundle}->full_config); } +# XXX: VK: done sub on_layer_editing_toggled { my ($self, $new_state) = @_; Slic3r::GUI::_3DScene::enable_layers_editing($self->{canvas3D}, $new_state); @@ -873,11 +877,13 @@ sub on_layer_editing_toggled { $self->{canvas3D}->Update; } -sub GetFrame { # XXX: main_frame in C++ Plater +# XXX: VK: done (Plater::priv::main_frame) +sub GetFrame { my ($self) = @_; return &Wx::GetTopLevelParent($self); } +# XXX: not done # Called after the Preferences dialog is closed and the program settings are saved. # Update the UI based on the current preferences. sub update_ui_from_settings @@ -889,6 +895,7 @@ sub update_ui_from_settings } } +# XXX: VK: done # Update preset combo boxes (Print settings, Filament, Material, Printer) from their respective tabs. # Called by # Slic3r::GUI::Tab::Print::_on_presets_changed @@ -934,12 +941,14 @@ sub update_presets { wxTheApp->{preset_bundle}->export_selections(wxTheApp->{app_config}); } +# XXX: VK: done, in on_action_add() sub add { my ($self) = @_; my @input_files = wxTheApp->open_model($self); $self->load_files(\@input_files); } +# XXX: VK: done sub load_files { my ($self, $input_files) = @_; @@ -1030,6 +1039,7 @@ sub load_files { return @obj_idx; } +# XXX: VK: done, except a few todos sub load_model_objects { my ($self, @model_objects) = @_; @@ -1112,6 +1122,7 @@ sub load_model_objects { return @obj_idx; } +# XXX: Removed sub bed_centerf { my ($self) = @_; @@ -1120,6 +1131,7 @@ sub bed_centerf { return Slic3r::Pointf->new(unscale($bed_center->x), unscale($bed_center->y)); #) } +# XXX: VK: done sub remove { my ($self, $obj_idx) = @_; @@ -1146,6 +1158,7 @@ sub remove { $self->update; } +# XXX: VK: done sub reset { my ($self) = @_; @@ -1166,6 +1179,7 @@ sub reset { $self->update; } +# XXX: not done sub increase { my ($self, $copies) = @_; $copies //= 1; @@ -1197,6 +1211,7 @@ sub increase { $self->schedule_background_process; } +# XXX: not done sub decrease { my ($self, $copies_asked) = @_; my $copies = $copies_asked // 1; @@ -1224,6 +1239,7 @@ sub decrease { $self->update; } +# XXX: not done sub set_number_of_copies { my ($self) = @_; # get current number of copies @@ -1242,6 +1258,7 @@ sub set_number_of_copies { } } +# XXX: not done (?) sub _get_number_from_user { # XXX: Enrico my ($self, $title, $prompt_message, $error_message, $default, $only_positive) = @_; for (;;) { @@ -1264,6 +1281,7 @@ sub _get_number_from_user { # XXX: Enrico } } +# XXX: not done sub rotate { my ($self, $angle, $axis, $relative_key, $axis_x, $axis_y, $axis_z) = @_; $relative_key //= 'absolute'; # relative or absolute coordinates @@ -1334,6 +1352,7 @@ sub rotate { $self->update; } +# XXX: not done sub mirror { my ($self, $axis) = @_; @@ -1363,6 +1382,7 @@ sub mirror { $self->update; } +# XXX: not done sub changescale { my ($self, $axis, $tosize) = @_; @@ -1437,6 +1457,7 @@ sub changescale { $self->update; } +# XXX: not done sub arrange { my ($self) = @_; @@ -1454,6 +1475,7 @@ sub arrange { $self->update(0); } +# XXX: not done sub split_object { my $self = shift; @@ -1485,6 +1507,7 @@ sub split_object { } } +# XXX: not done # Trigger $self->async_apply_config() after 500ms. # The call is delayed to avoid restarting the background processing during typing into an edit field. sub schedule_background_process { @@ -1492,6 +1515,7 @@ sub schedule_background_process { $self->{apply_config_timer}->Start(0.5 * 1000, 1); # 1 = one shot, every half a second. } +# XXX: not done # Executed asynchronously by a timer every PROCESS_DELAY (0.5 second). # The timer is started by schedule_background_process(), sub async_apply_config { @@ -1526,6 +1550,7 @@ sub async_apply_config { } } +# XXX: not done # Background processing is started either by the "Slice now" button, by the "Export G-code button" or by async_apply_config(). sub start_background_process { my ($self) = @_; @@ -1546,6 +1571,7 @@ sub start_background_process { $self->{background_slicing_process}->start; } +# XXX: not done # Stop the background processing sub stop_background_process { my ($self) = @_; @@ -1554,6 +1580,7 @@ sub stop_background_process { # $self->{preview3D}->reload_print if $self->{preview3D}; } +# XXX: not done # Called by the "Slice now" button, which is visible only if the background processing is disabled. sub reslice { # explicitly cancel a previous thread and start a new one. @@ -1574,6 +1601,7 @@ sub reslice { } } +# XXX: VK: done sub export_gcode { my ($self, $output_file) = @_; @@ -1654,6 +1682,7 @@ sub export_gcode { return $self->{export_gcode_output_file}; } +# XXX: not done # This message should be called by the background process synchronously. sub on_update_print_preview { my ($self) = @_; @@ -1666,6 +1695,7 @@ sub on_update_print_preview { Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1); } +# XXX: not done # This gets called also if we have no threads. sub on_progress_event { my ($self, $percent, $message) = @_; @@ -1676,6 +1706,7 @@ sub on_progress_event { $self->statusbar->SetStatusText("$message..."); } +# XXX: not done # Called when the G-code export finishes, either successfully or with an error. # This gets called also if we don't have threads. sub on_process_completed { @@ -1743,6 +1774,7 @@ sub on_process_completed { # $self->{preview3D}->reload_print if $self->{preview3D}; } +# XXX: partially done in the Sidebar # Fill in the "Sliced info" box with the result of the G-code generator. sub print_info_box_show { my ($self, $show) = @_; @@ -1819,6 +1851,7 @@ sub print_info_box_show { $panel->Refresh; } +# XXX: not done - to be removed sub do_print { my ($self) = @_; @@ -1832,6 +1865,7 @@ sub do_print { $printer_panel->load_print_job($self->{print_file}, $filament_stats); } +# XXX: VK: done sub export_stl { my ($self) = @_; return if !@{$self->{objects}}; @@ -1842,6 +1876,7 @@ sub export_stl { $self->statusbar->SetStatusText(L("STL file exported to ").$output_file); } +# XXX: not done sub reload_from_disk { my ($self) = @_; @@ -1873,6 +1908,7 @@ sub reload_from_disk { $self->remove($obj_idx); } +# XXX: VK: done sub export_object_stl { my ($self) = @_; my ($obj_idx, $object) = $self->selected_object; @@ -1884,6 +1920,7 @@ sub export_object_stl { $self->statusbar->SetStatusText(L("STL file exported to ").$output_file); } +# XXX: not done sub fix_through_netfabb { my ($self) = @_; my ($obj_idx, $object) = $self->selected_object; @@ -1912,6 +1949,7 @@ sub fix_through_netfabb { $self->remove($obj_idx); } +# XXX: VK: done sub export_amf { my ($self) = @_; return if !@{$self->{objects}}; @@ -1928,6 +1966,7 @@ sub export_amf { } } +# XXX: VK: done sub export_3mf { my ($self) = @_; return if !@{$self->{objects}}; @@ -1944,6 +1983,7 @@ sub export_3mf { } } +# XXX: VK: done # Ask user to select an output file for a given file format (STl, AMF, 3MF). # Propose a default file name based on the 'output_filename_format' configuration value. sub _get_export_file { @@ -1993,6 +2033,7 @@ sub _get_export_file { # $self->{objects}[$obj_idx]->thumbnail(undef); #} +# XXX: VK: done # this method gets called whenever print center is changed or the objects' bounding box changes # (i.e. when an object is added/removed/moved/rotated/scaled) sub update { @@ -2016,6 +2057,7 @@ sub update { $self->Thaw; } +# XXX: done in sidebar? # When a printer technology is changed, the UI needs to be updated to show/hide needed preset combo boxes. sub show_preset_comboboxes{ my ($self, $showSLA) = @_; #if showSLA is oposite value to "ptFFF" @@ -2034,6 +2076,7 @@ sub show_preset_comboboxes{ $self->Layout; } +# XXX: not done # When a number of extruders changes, the UI needs to be updated to show a single filament selection combo box per extruder. # Also the wxTheApp->{preset_bundle}->filament_presets needs to be resized accordingly # and some reasonable default has to be selected for the additional extruders. @@ -2078,6 +2121,7 @@ sub on_extruders_change { $self->Layout; } +# XXX: not done sub on_config_change { my ($self, $config) = @_; @@ -2144,6 +2188,7 @@ sub on_config_change { $self->schedule_background_process; } +# XXX: not done sub item_changed_selection { my ($self, $obj_idx) = @_; @@ -2159,6 +2204,7 @@ sub item_changed_selection { } } +# XXX: VK: done sub collect_selections { my ($self) = @_; my $selections = []; @@ -2168,6 +2214,7 @@ sub collect_selections { return $selections; } +# XXX: not done # Called when clicked on the filament preset combo box. # When clicked on the icon, show the color picker. sub filament_color_box_lmouse_down @@ -2221,6 +2268,7 @@ sub filament_color_box_lmouse_down # } #} +# XXX: not done sub changed_object_settings { my ($self, $obj_idx, $parts_changed, $part_settings_changed) = @_; @@ -2246,6 +2294,7 @@ sub changed_object_settings { } } +# XXX: VK: done # Called to update various buttons depending on whether there are any objects or # whether background processing (export of a G-code, sending to Octoprint, forced background re-slicing) is active. sub object_list_changed { @@ -2276,6 +2325,7 @@ sub object_list_changed { for grep $self->{"btn_$_"}, qw(reslice export_gcode print send_gcode); } +# XXX: VK: WIP # Selection of an active 3D object changed. sub selection_changed { my ($self) = @_; @@ -2393,6 +2443,7 @@ sub selection_changed { $self->{right_panel}->Thaw; } +# XXX: VK: done sub select_object { my ($self, $obj_idx, $child) = @_; @@ -2413,6 +2464,7 @@ sub select_object { $self->selection_changed(1); } +# XXX: not done sub select_object_from_cpp { my ($self, $obj_idx, $vol_idx) = @_; @@ -2457,16 +2509,19 @@ sub select_object_from_cpp { $self->selection_changed(1); } +# XXX: VK: done sub selected_object { my ($self) = @_; my $obj_idx = first { $self->{objects}[$_]->selected } 0..$#{ $self->{objects} }; return defined $obj_idx ? ($obj_idx, $self->{objects}[$obj_idx]) : undef; } +# XXX: VK: done sub statusbar { return $_[0]->GetFrame->{statusbar}; } +# XXX: not done, to be removed (?) sub object_menu { my ($self) = @_; @@ -2577,6 +2632,7 @@ sub object_menu { return $menu; } +# XXX: not done # Set a camera direction, zoom to all objects. sub select_view { my ($self, $direction) = @_; @@ -2594,6 +2650,8 @@ sub select_view { } } + +# XXX: VK: done, in PlaterDropTarget package Slic3r::GUI::Plater::DropTarget; use Wx::DND; use base 'Wx::FileDropTarget'; diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index ee66fd293..90817fadc 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -1350,7 +1350,7 @@ namespace Slic3r { return false; } - m_curr_config.volume_id = object->second.volumes.size(); + m_curr_config.volume_id = (int)object->second.volumes.size(); unsigned int first_triangle_id = (unsigned int)get_attribute_value_int(attributes, num_attributes, FIRST_TRIANGLE_ID_ATTR); unsigned int last_triangle_id = (unsigned int)get_attribute_value_int(attributes, num_attributes, LAST_TRIANGLE_ID_ATTR); @@ -1408,7 +1408,7 @@ namespace Slic3r { return false; } - unsigned int geo_tri_count = geometry.triangles.size() / 3; + unsigned int geo_tri_count = (unsigned int)geometry.triangles.size() / 3; for (const ObjectMetadata::VolumeMetadata& volume_data : volumes) { @@ -1429,7 +1429,7 @@ namespace Slic3r { unsigned int src_start_id = volume_data.first_triangle_id * 3; - for (size_t i = 0; i < triangles_count; ++i) + for (unsigned int i = 0; i < triangles_count; ++i) { unsigned int ii = i * 3; stl_facet& facet = stl.facet_start[i]; diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index 68c747cda..96befdabf 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -911,7 +911,7 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c if (volume->is_modifier()) stream << " 1\n"; stream << " " << ModelVolume::type_to_string(volume->type()) << "\n"; - for (int i = 0; i < volume->mesh.stl.stats.number_of_facets; ++i) { + for (int i = 0; i < (int)volume->mesh.stl.stats.number_of_facets; ++i) { stream << " \n"; for (int j = 0; j < 3; ++j) stream << " " << volume->mesh.stl.v_indices[i].vertex[j] + vertices_offset << "\n"; diff --git a/src/libslic3r/Format/PRUS.cpp b/src/libslic3r/Format/PRUS.cpp index d86016361..b68726a37 100644 --- a/src/libslic3r/Format/PRUS.cpp +++ b/src/libslic3r/Format/PRUS.cpp @@ -37,7 +37,7 @@ static_assert(sizeof(StlHeader) == 84, "StlHeader size not correct"); class LineReader { public: - LineReader(std::vector &data) : m_buffer(data), m_pos(0), m_len(data.size()) {} + LineReader(std::vector &data) : m_buffer(data), m_pos(0), m_len((int)data.size()) {} const char* next_line() { // Skip empty lines. @@ -154,7 +154,7 @@ static void extract_model_from_archive( #if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM instance_offset = Vec3d((double)(position[0] - zero[0]), (double)(position[1] - zero[1]), (double)(position[2] - zero[2])); // CHECK_ME -> Is the following correct ? - trafo[2][3] = position[2] / instance_scaling_factor(2); + trafo[2][3] = position[2] / (float)instance_scaling_factor(2); #else instance_offset(0) = position[0] - zero[0]; instance_offset(1) = position[1] - zero[1]; @@ -291,8 +291,8 @@ static void extract_model_from_archive( if (! facets.empty() && solid_name.empty()) { stl_file &stl = mesh.stl; stl.stats.type = inmemory; - stl.stats.number_of_facets = facets.size(); - stl.stats.original_num_facets = facets.size(); + stl.stats.number_of_facets = (uint32_t)facets.size(); + stl.stats.original_num_facets = (int)facets.size(); stl_allocate(&stl); memcpy((void*)stl.facet_start, facets.data(), facets.size() * 50); stl_get_size(&stl); diff --git a/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp index 51d5b1a06..e3cd67e7c 100644 --- a/src/libslic3r/GCode/Analyzer.cpp +++ b/src/libslic3r/GCode/Analyzer.cpp @@ -698,7 +698,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ // update current values data = move.data; - z = move.start_position.z(); + z = (float)move.start_position.z(); volumetric_rate = move.data.feedrate * (float)move.data.mm3_per_mm; height_range.update_from(move.data.height); width_range.update_from(move.data.width); diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp index f97265ee3..4bfd5d63f 100644 --- a/src/libslic3r/GCodeTimeEstimator.cpp +++ b/src/libslic3r/GCodeTimeEstimator.cpp @@ -484,14 +484,14 @@ namespace Slic3r { { _state.filament_load_times.clear(); for (double t : filament_load_times) - _state.filament_load_times.push_back(t); + _state.filament_load_times.push_back((float)t); } void GCodeTimeEstimator::set_filament_unload_times(const std::vector &filament_unload_times) { _state.filament_unload_times.clear(); for (double t : filament_unload_times) - _state.filament_unload_times.push_back(t); + _state.filament_unload_times.push_back((float)t); } float GCodeTimeEstimator::get_filament_load_time(unsigned int id_extruder) @@ -731,7 +731,7 @@ namespace Slic3r { #endif // ENABLE_MOVE_STATS } - _last_st_synchronized_block_id = _blocks.size() - 1; + _last_st_synchronized_block_id = (int)_blocks.size() - 1; // The additional time has been consumed (added to the total time), reset it to zero. set_additional_time(0.); } diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index aad94ee3c..88e4c551a 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -707,16 +707,23 @@ void ModelObject::center_around_origin() if (v->is_model_part()) bb.merge(v->mesh.bounding_box()); +#if ENABLE_EXTENDED_SELECTION + // Shift is the vector from the center of the bounding box to the origin + Vec3d shift = -bb.center(); +#else // Shift is the vector from the center of the bottom face of the bounding box to the origin Vec3d shift = -bb.center(); shift(2) = -bb.min(2); +#endif // ENABLE_EXTENDED_SELECTION this->translate(shift); this->origin_translation += shift; #if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM +#if !ENABLE_EXTENDED_SELECTION // set z to zero, translation in z has already been done within the mesh shift(2) = 0.0; +#endif // !ENABLE_EXTENDED_SELECTION #endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM if (!this->instances.empty()) { diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index 87cdc3777..100178108 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -56,10 +56,10 @@ inline Vec2i64 to_2d(const Vec3i64 &pt3) { return Vec2i64(pt3(0), pt3(1)); } inline Vec2f to_2d(const Vec3f &pt3) { return Vec2f (pt3(0), pt3(1)); } inline Vec2d to_2d(const Vec3d &pt3) { return Vec2d (pt3(0), pt3(1)); } -// inline Vec3crd to_3d(const Vec2crd &pt2, coord_t z) { return Vec3crd(pt2(0), pt2(1), z); } -// inline Vec3i64 to_3d(const Vec2i64 &pt2, int64_t z) { return Vec3i64(pt2(0), pt2(1), z); } -// inline Vec3f to_3d(const Vec2f &pt2, float z) { return Vec3f (pt2(0), pt2(1), z); } -// inline Vec3d to_3d(const Vec2d &pt2, double z) { return Vec3d (pt2(0), pt2(1), z); } +inline Vec3d to_3d(const Vec2d &v, double z) { return Vec3d(v(0), v(1), z); } +inline Vec3f to_3d(const Vec2f &v, float z) { return Vec3f(v(0), v(1), z); } +inline Vec3i64 to_3d(const Vec2i64 &v, float z) { return Vec3i64(v(0), v(1), z); } +inline Vec3crd to_3d(const Vec3crd &p, coord_t z) { return Vec3crd(p(0), p(1), z); } inline Vec2d unscale(coord_t x, coord_t y) { return Vec2d(unscale(x), unscale(y)); } inline Vec2d unscale(const Vec2crd &pt) { return Vec2d(unscale(pt(0)), unscale(pt(1))); } diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 7f88110bc..a646ab8fd 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -419,6 +419,7 @@ public: const PrintObject* get_object(int idx) const { return m_objects[idx]; } const PrintRegionPtrs& regions() const { return m_regions; } const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; } + PlaceholderParser& placeholder_parser() { return m_placeholder_parser; } // Returns extruder this eec should be printed with, according to PrintRegion config: static int get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion ®ion); diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 9b9067af3..77802d811 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -5,13 +5,15 @@ #define ENABLE_1_42_0 1 // Add z coordinate to model instances' offset -// Add x and y rotation components to model instances' offset +// Add x and y rotation components to model instances' rotation // Add scaling factors for all the three axes to model instances #define ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM (1 && ENABLE_1_42_0) // Add double click on gizmo grabbers to reset transformation components to their default value #define ENABLE_GIZMOS_RESET (1 && ENABLE_1_42_0) // Uses a unique opengl context #define ENABLE_USE_UNIQUE_GLCONTEXT (1 && ENABLE_1_42_0) +// New selections +#define ENABLE_EXTENDED_SELECTION (0 && ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM) #endif // _technologies_h_ diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 54c90c20e..4e9edfbdc 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -39,6 +39,8 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/GUI/GUI_PreviewIface.hpp ${LIBDIR}/slic3r/GUI/GUI_App.cpp ${LIBDIR}/slic3r/GUI/GUI_App.hpp + ${LIBDIR}/slic3r/GUI/GUI_Utils.cpp + ${LIBDIR}/slic3r/GUI/GUI_Utils.hpp ${LIBDIR}/slic3r/GUI/MainFrame.cpp ${LIBDIR}/slic3r/GUI/MainFrame.hpp ${LIBDIR}/slic3r/GUI/Plater.cpp diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 44a068303..2ae900e00 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -36,7 +36,7 @@ void GLIndexedVertexArray::load_mesh_flat_shading(const TriangleMesh &mesh) this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count()); - for (int i = 0; i < mesh.stl.stats.number_of_facets; ++ i) { + for (int i = 0; i < (int)mesh.stl.stats.number_of_facets; ++i) { const stl_facet &facet = mesh.stl.facet_start[i]; for (int j = 0; j < 3; ++ j) this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2)); @@ -52,7 +52,7 @@ void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh &mesh) this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count()); unsigned int vertices_count = 0; - for (int i = 0; i < mesh.stl.stats.number_of_facets; ++i) { + for (int i = 0; i < (int)mesh.stl.stats.number_of_facets; ++i) { const stl_facet &facet = mesh.stl.facet_start[i]; for (int j = 0; j < 3; ++j) this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2)); @@ -209,8 +209,10 @@ GLVolume::GLVolume(float r, float g, float b, float a) , m_transformed_convex_hull_bounding_box_dirty(true) , m_convex_hull(nullptr) , composite_id(-1) +#if !ENABLE_EXTENDED_SELECTION , select_group_id(-1) , drag_group_id(-1) +#endif // !ENABLE_EXTENDED_SELECTION , extruder_id(0) , selected(false) , is_active(true) @@ -242,7 +244,7 @@ void GLVolume::set_render_color(float r, float g, float b, float a) void GLVolume::set_render_color(const float* rgba, unsigned int size) { size = std::min((unsigned int)4, size); - for (int i = 0; i < size; ++i) + for (unsigned int i = 0; i < size; ++i) { render_color[i] = rgba[i]; } @@ -311,6 +313,13 @@ void GLVolume::set_offset(const Vec3d& offset) } #if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM +#if ENABLE_EXTENDED_SELECTION +const Vec3d& GLVolume::get_scaling_factor() const +{ + return m_scaling_factor; +} +#endif // ENABLE_EXTENDED_SELECTION + void GLVolume::set_scaling_factor(const Vec3d& scaling_factor) { if (m_scaling_factor != scaling_factor) @@ -339,6 +348,7 @@ void GLVolume::set_convex_hull(const TriangleMesh& convex_hull) m_convex_hull = &convex_hull; } +#if !ENABLE_EXTENDED_SELECTION void GLVolume::set_select_group_id(const std::string& select_by) { if (select_by == "object") @@ -356,6 +366,7 @@ void GLVolume::set_drag_group_id(const std::string& drag_by) else if (drag_by == "instance") drag_group_id = object_idx() * 1000 + instance_idx(); } +#endif // !ENABLE_EXTENDED_SELECTION const Transform3f& GLVolume::world_matrix() const { @@ -682,6 +693,14 @@ void GLVolume::generate_layer_height_texture(const PrintObject *print_object, bo #define LAYER_HEIGHT_TEXTURE_WIDTH 1024 #define LAYER_HEIGHT_TEXTURE_HEIGHT 1024 +#if ENABLE_EXTENDED_SELECTION +std::vector GLVolumeCollection::load_object( + const ModelObject *model_object, + int obj_idx, + const std::vector &instance_idxs, + const std::string &color_by, + bool use_VBOs) +#else std::vector GLVolumeCollection::load_object( const ModelObject *model_object, int obj_idx, @@ -690,6 +709,7 @@ std::vector GLVolumeCollection::load_object( const std::string &select_by, const std::string &drag_by, bool use_VBOs) +#endif // ENABLE_EXTENDED_SELECTION { static float colors[4][4] = { { 1.0f, 1.0f, 0.0f, 1.f }, @@ -740,8 +760,10 @@ std::vector GLVolumeCollection::load_object( v.bounding_box = v.indexed_vertex_array.bounding_box(); v.indexed_vertex_array.finalize_geometry(use_VBOs); v.composite_id = obj_idx * 1000000 + volume_idx * 1000 + instance_idx; +#if !ENABLE_EXTENDED_SELECTION v.set_select_group_id(select_by); v.set_drag_group_id(drag_by); +#endif // !ENABLE_EXTENDED_SELECTION if (model_volume->is_model_part()) { v.set_convex_hull(model_volume->get_convex_hull()); @@ -789,9 +811,9 @@ int GLVolumeCollection::load_wipe_tower_preview( // edge has y=0 and centerline of the back edge has y=depth: Pointf3s points; std::vector facets; - float out_points_idx[][3] = {{0, -depth, 0}, {0, 0, 0}, {38.453, 0, 0}, {61.547, 0, 0}, {100, 0, 0}, {100, -depth, 0}, {55.7735, -10, 0}, {44.2265, 10, 0}, - {38.453, 0, 1}, {0, 0, 1}, {0, -depth, 1}, {100, -depth, 1}, {100, 0, 1}, {61.547, 0, 1}, {55.7735, -10, 1}, {44.2265, 10, 1}}; - int out_facets_idx[][3] = {{0, 1, 2}, {3, 4, 5}, {6, 5, 0}, {3, 5, 6}, {6, 2, 7}, {6, 0, 2}, {8, 9, 10}, {11, 12, 13}, {10, 11, 14}, {14, 11, 13}, {15, 8, 14}, + float out_points_idx[][3] = { { 0, -depth, 0 }, { 0, 0, 0 }, { 38.453f, 0, 0 }, { 61.547f, 0, 0 }, { 100.0f, 0, 0 }, { 100.0f, -depth, 0 }, { 55.7735f, -10.0f, 0 }, { 44.2265f, 10.0f, 0 }, + { 38.453f, 0, 1 }, { 0, 0, 1 }, { 0, -depth, 1 }, { 100.0f, -depth, 1 }, { 100.0f, 0, 1 }, { 61.547f, 0, 1 }, { 55.7735f, -10.0f, 1 }, { 44.2265f, 10.0f, 1 } }; + int out_facets_idx[][3] = { { 0, 1, 2 }, { 3, 4, 5 }, { 6, 5, 0 }, { 3, 5, 6 }, { 6, 2, 7 }, { 6, 0, 2 }, { 8, 9, 10 }, { 11, 12, 13 }, { 10, 11, 14 }, { 14, 11, 13 }, { 15, 8, 14 }, {8, 10, 14}, {3, 12, 4}, {3, 13, 12}, {6, 13, 3}, {6, 14, 13}, {7, 14, 6}, {7, 15, 14}, {2, 15, 7}, {2, 8, 15}, {1, 8, 2}, {1, 9, 8}, {0, 9, 1}, {0, 10, 9}, {5, 10, 0}, {5, 11, 10}, {4, 11, 5}, {4, 12, 11}}; for (int i=0;i<16;++i) @@ -834,8 +856,10 @@ int GLVolumeCollection::load_wipe_tower_preview( v.bounding_box = v.indexed_vertex_array.bounding_box(); v.indexed_vertex_array.finalize_geometry(use_VBOs); v.composite_id = obj_idx * 1000000; +#if !ENABLE_EXTENDED_SELECTION v.select_group_id = obj_idx * 1000000; v.drag_group_id = obj_idx * 1000; +#endif // !ENABLE_EXTENDED_SELECTION v.is_wipe_tower = true; v.shader_outside_printer_detection_enabled = ! size_unknown; return int(this->volumes.size() - 1); @@ -1017,7 +1041,7 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con continue; int extruder_id = volume->extruder_id - 1; - if ((extruder_id < 0) || ((unsigned int)colors.size() <= extruder_id)) + if ((extruder_id < 0) || ((int)colors.size() <= extruder_id)) extruder_id = 0; const Color& color = colors[extruder_id]; @@ -1031,6 +1055,7 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con } } +#if !ENABLE_EXTENDED_SELECTION void GLVolumeCollection::set_select_by(const std::string& select_by) { for (GLVolume *vol : this->volumes) @@ -1048,6 +1073,7 @@ void GLVolumeCollection::set_drag_by(const std::string& drag_by) vol->set_drag_group_id(drag_by); } } +#endif // !ENABLE_EXTENDED_SELECTION std::vector GLVolumeCollection::get_current_print_zs(bool active_only) const { @@ -1820,6 +1846,7 @@ void _3DScene::reset_volumes(wxGLCanvas* canvas) s_canvas_mgr.reset_volumes(canvas); } +#if !ENABLE_EXTENDED_SELECTION void _3DScene::deselect_volumes(wxGLCanvas* canvas) { s_canvas_mgr.deselect_volumes(canvas); @@ -1834,6 +1861,7 @@ void _3DScene::update_volumes_selection(wxGLCanvas* canvas, const std::vector& selections) { s_canvas_mgr.set_objects_selections(canvas, selections); } +#endif // !ENABLE_EXTENDED_SELECTION void _3DScene::set_config(wxGLCanvas* canvas, DynamicPrintConfig* config) { @@ -1900,6 +1930,7 @@ void _3DScene::set_color_by(wxGLCanvas* canvas, const std::string& value) s_canvas_mgr.set_color_by(canvas, value); } +#if !ENABLE_EXTENDED_SELECTION void _3DScene::set_select_by(wxGLCanvas* canvas, const std::string& value) { s_canvas_mgr.set_select_by(canvas, value); @@ -1914,6 +1945,7 @@ std::string _3DScene::get_select_by(wxGLCanvas* canvas) { return s_canvas_mgr.get_select_by(canvas); } +#endif // !ENABLE_EXTENDED_SELECTION bool _3DScene::is_layers_editing_enabled(wxGLCanvas* canvas) { diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 971083ab6..09f425ba0 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -292,10 +292,12 @@ public: float render_color[4]; // An ID containing the object ID, volume ID and instance ID. int composite_id; +#if !ENABLE_EXTENDED_SELECTION // An ID for group selection. It may be the same for all meshes of all object instances, or for just a single object instance. int select_group_id; // An ID for group dragging. It may be the same for all meshes of all object instances, or for just a single object instance. int drag_group_id; +#endif // !ENABLE_EXTENDED_SELECTION // An ID containing the extruder ID (used to select color). int extruder_id; // Is this object selected? @@ -338,6 +340,9 @@ public: const Vec3d& get_rotation() const; void set_rotation(const Vec3d& rotation); +#if ENABLE_EXTENDED_SELECTION + const Vec3d& get_scaling_factor() const; +#endif // ENABLE_EXTENDED_SELECTION void set_scaling_factor(const Vec3d& scaling_factor); #else double get_rotation() const; @@ -351,8 +356,10 @@ public: void set_convex_hull(const TriangleMesh& convex_hull); +#if !ENABLE_EXTENDED_SELECTION void set_select_group_id(const std::string& select_by); void set_drag_group_id(const std::string& drag_by); +#endif // !ENABLE_EXTENDED_SELECTION int object_idx() const { return this->composite_id / 1000000; } int volume_idx() const { return (this->composite_id / 1000) % 1000; } @@ -410,6 +417,10 @@ public: void reset_layer_height_texture_data() { layer_height_texture_data.reset(); } }; +#if ENABLE_EXTENDED_SELECTION +typedef std::vector GLVolumePtrs; +#endif // ENABLE_EXTENDED_SELECTION + class GLVolumeCollection { // min and max vertex of the print box volume @@ -417,11 +428,23 @@ class GLVolumeCollection float print_box_max[3]; public: +#if ENABLE_EXTENDED_SELECTION + GLVolumePtrs volumes; +#else std::vector volumes; - +#endif // ENABLE_EXTENDED_SELECTION + GLVolumeCollection() {}; ~GLVolumeCollection() { clear(); }; +#if ENABLE_EXTENDED_SELECTION + std::vector load_object( + const ModelObject *model_object, + int obj_idx, + const std::vector &instance_idxs, + const std::string &color_by, + bool use_VBOs); +#else std::vector load_object( const ModelObject *model_object, int obj_idx, @@ -430,6 +453,7 @@ public: const std::string &select_by, const std::string &drag_by, bool use_VBOs); +#endif // ENABLE_EXTENDED_SELECTION int load_wipe_tower_preview( int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs, bool size_unknown, float brim_width); @@ -463,8 +487,10 @@ public: void update_colors_by_extruder(const DynamicPrintConfig* config); +#if !ENABLE_EXTENDED_SELECTION void set_select_by(const std::string& select_by); void set_drag_by(const std::string& drag_by); +#endif // !ENABLE_EXTENDED_SELECTION // Returns a vector containing the sorted list of all the print_zs of the volumes contained in this collection std::vector get_current_print_zs(bool active_only) const; @@ -493,14 +519,18 @@ public: static unsigned int get_volumes_count(wxGLCanvas* canvas); static void reset_volumes(wxGLCanvas* canvas); +#if !ENABLE_EXTENDED_SELECTION static void deselect_volumes(wxGLCanvas* canvas); static void select_volume(wxGLCanvas* canvas, unsigned int id); static void update_volumes_selection(wxGLCanvas* canvas, const std::vector& selections); +#endif // !ENABLE_EXTENDED_SELECTION static int check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config); static bool move_volume_up(wxGLCanvas* canvas, unsigned int id); static bool move_volume_down(wxGLCanvas* canvas, unsigned int id); +#if !ENABLE_EXTENDED_SELECTION static void set_objects_selections(wxGLCanvas* canvas, const std::vector& selections); +#endif // !ENABLE_EXTENDED_SELECTION static void set_config(wxGLCanvas* canvas, DynamicPrintConfig* config); static void set_print(wxGLCanvas* canvas, Print* print); @@ -516,10 +546,12 @@ public: static void set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons); static void set_color_by(wxGLCanvas* canvas, const std::string& value); +#if !ENABLE_EXTENDED_SELECTION static void set_select_by(wxGLCanvas* canvas, const std::string& value); static void set_drag_by(wxGLCanvas* canvas, const std::string& value); static std::string get_select_by(wxGLCanvas* canvas); +#endif // !ENABLE_EXTENDED_SELECTION static bool is_layers_editing_enabled(wxGLCanvas* canvas); static bool is_layers_editing_allowed(wxGLCanvas* canvas); diff --git a/src/slic3r/GUI/BedShapeDialog.cpp b/src/slic3r/GUI/BedShapeDialog.cpp index 7c019599a..407da7ae3 100644 --- a/src/slic3r/GUI/BedShapeDialog.cpp +++ b/src/slic3r/GUI/BedShapeDialog.cpp @@ -292,7 +292,7 @@ void BedShapePanel::update_shape() void BedShapePanel::load_stl() { auto dialog = new wxFileDialog(this, _(L("Choose a file to import bed shape from (STL/OBJ/AMF/3MF/PRUSA):")), "", "", - MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST); + file_wildcards[FT_MODEL], wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (dialog->ShowModal() != wxID_OK) { dialog->Destroy(); return; diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 02fc9b8b6..5dbc65877 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -611,8 +611,9 @@ void ColourPicker::BUILD() if (m_opt.width >= 0) size.SetWidth(m_opt.width); wxString clr(static_cast(m_opt.default_value)->get_at(m_opt_idx)); + // FIXME: verify clr is valid, otherwise this causes an assert auto temp = new wxColourPickerCtrl(m_parent, wxID_ANY, clr, wxDefaultPosition, size); - + // // recast as a wxWindow to fit the calling convention window = dynamic_cast(temp); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 7eccf3af7..116d8cb3b 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -414,7 +414,7 @@ void GLCanvas3D::Bed::_calc_gridlines(const ExPolygon& poly, const BoundingBox& } // clip with a slightly grown expolygon because our lines lay on the contours and may get erroneously clipped - Lines gridlines = to_lines(intersection_pl(axes_lines, offset(poly, SCALED_EPSILON))); + Lines gridlines = to_lines(intersection_pl(axes_lines, offset(poly, (float)SCALED_EPSILON))); // append bed contours Lines contour_lines = to_lines(poly); @@ -644,7 +644,7 @@ bool GLCanvas3D::CuttingPlane::set(float z, const ExPolygons& polygons) m_z = z; // grow slices in order to display them better - ExPolygons expolygons = offset_ex(polygons, scale_(0.1)); + ExPolygons expolygons = offset_ex(polygons, (float)scale_(0.1)); Lines lines = to_lines(expolygons); return m_lines.set_from_lines(lines, m_z); } @@ -1089,10 +1089,14 @@ const Vec3d GLCanvas3D::Mouse::Drag::Invalid_3D_Point(DBL_MAX, DBL_MAX, DBL_MAX) GLCanvas3D::Mouse::Drag::Drag() : start_position_2D(Invalid_2D_Point) , start_position_3D(Invalid_3D_Point) +#if !ENABLE_EXTENDED_SELECTION , volume_center_offset(0, 0, 0) , move_with_shift(false) +#endif // !ENABLE_EXTENDED_SELECTION , move_volume_idx(-1) +#if !ENABLE_EXTENDED_SELECTION , gizmo_volume_idx(-1) +#endif // !ENABLE_EXTENDED_SELECTION { } @@ -1125,6 +1129,421 @@ bool GLCanvas3D::Mouse::is_start_position_3D_defined() const return (drag.start_position_3D != Drag::Invalid_3D_Point); } +#if ENABLE_EXTENDED_SELECTION +GLCanvas3D::Selection::VolumeCache::VolumeCache() + : position(Vec3d::Zero()) + , rotation(Vec3d::Zero()) + , scaling_factor(Vec3d::Ones()) +{ + m_rotation_matrix = Transform3d::Identity(); +} + +GLCanvas3D::Selection::VolumeCache::VolumeCache(const Vec3d& position, const Vec3d& rotation, const Vec3d& scaling_factor) + : position(position) + , rotation(rotation) + , scaling_factor(scaling_factor) +{ + m_rotation_matrix = Transform3d::Identity(); + m_rotation_matrix.rotate(Eigen::AngleAxisd(rotation(2), Vec3d::UnitZ())); + m_rotation_matrix.rotate(Eigen::AngleAxisd(rotation(1), Vec3d::UnitY())); + m_rotation_matrix.rotate(Eigen::AngleAxisd(rotation(0), Vec3d::UnitX())); +} + +GLCanvas3D::Selection::Selection() + : m_volumes(nullptr) + , m_model(nullptr) + , m_mode(Instance) + , m_valid(false) + , m_bounding_box_dirty(true) +{ +} + +void GLCanvas3D::Selection::set_volumes(GLVolumePtrs* volumes) +{ + m_volumes = volumes; + update_valid(); +} + +void GLCanvas3D::Selection::set_model(Model* model) +{ + m_model = model; + update_valid(); +} + +void GLCanvas3D::Selection::add(unsigned int volume_idx, bool as_single_selection) +{ + if (!m_valid || ((unsigned int)m_volumes->size() <= volume_idx)) + return; + + // resets the current list if needed + const GLVolume* volume = (*m_volumes)[volume_idx]; + if (as_single_selection || volume->is_wipe_tower || is_wipe_tower() || volume->is_modifier || is_modifier()) + clear(); + + switch (m_mode) + { + case Volume: + { + add_volume(volume_idx); + break; + } + case Instance: + { + add_instance(volume_idx); + break; + } + case Object: + { + add_object(volume_idx); + break; + } + } + + m_bounding_box_dirty = true; +} + +void GLCanvas3D::Selection::remove(unsigned int volume_idx) +{ + if (!m_valid || ((unsigned int)m_volumes->size() <= volume_idx)) + return; + + switch (m_mode) + { + case Volume: + { + remove_volume(volume_idx); + break; + } + case Instance: + { + remove_instance(volume_idx); + break; + } + case Object: + { + remove_object(volume_idx); + break; + } + } + + m_bounding_box_dirty = true; +} + +void GLCanvas3D::Selection::clear() +{ + if (!m_valid) + return; + + for (unsigned int i : m_list) + { + (*m_volumes)[i]->selected = false; + } + + m_list.clear(); + m_bounding_box_dirty = true; +} + +bool GLCanvas3D::Selection::is_single_full_instance(int& object_idx_out, int& instance_idx_out) const +{ + if (!m_valid || is_empty() || is_wipe_tower()) + { + object_idx_out = -1; + instance_idx_out = -1; + return false; + } + + const GLVolume* first = (*m_volumes)[*m_list.begin()]; + int object_idx = first->object_idx(); + int instance_idx = first->instance_idx(); + unsigned int count = 0; + + for (unsigned int i : m_list) + { + const GLVolume* v = (*m_volumes)[i]; + if ((v->object_idx() != object_idx) || (v->instance_idx() != instance_idx)) + return false; + else + ++count; + } + + bool res = (count == (unsigned int)m_model->objects[object_idx]->volumes.size()); + object_idx_out = res ? object_idx : -1; + instance_idx_out = res ? instance_idx : -1; + return res; +} + +bool GLCanvas3D::Selection::is_single_full_object(int& object_idx_out) const +{ + if (!m_valid || is_empty() || is_wipe_tower()) + { + object_idx_out = -1; + return false; + } + + int object_idx = (*m_volumes)[*m_list.begin()]->object_idx(); + unsigned int count = 0; + + for (unsigned int i : m_list) + { + const GLVolume* v = (*m_volumes)[i]; + if (v->object_idx() != object_idx) + return false; + else + ++count; + } + + bool res = (count == (unsigned int)m_model->objects[object_idx]->volumes.size() * (unsigned int)m_model->objects[object_idx]->instances.size()); + object_idx_out = res ? object_idx : -1; + return res; +} + +bool GLCanvas3D::Selection::is_from_single_instance(int& object_idx_out, int& instance_idx_out) const +{ + if (!m_valid || is_empty() || is_wipe_tower()) + { + object_idx_out = -1; + instance_idx_out = -1; + return false; + } + + const GLVolume* first = (*m_volumes)[*m_list.begin()]; + int object_idx = first->object_idx(); + int instance_idx = first->instance_idx(); + + for (unsigned int i : m_list) + { + const GLVolume* v = (*m_volumes)[i]; + if ((v->object_idx() != object_idx) || (v->instance_idx() != instance_idx)) + return false; + } + + object_idx_out = object_idx; + instance_idx_out = instance_idx; + return true; +} + +bool GLCanvas3D::Selection::is_from_single_object(int& object_idx_out) const +{ + if (!m_valid || is_empty() || is_wipe_tower()) + { + object_idx_out = -1; + return false; + } + + int object_idx = (*m_volumes)[*m_list.begin()]->object_idx(); + + for (unsigned int i : m_list) + { + const GLVolume* v = (*m_volumes)[i]; + if (v->object_idx() != object_idx) + return false; + } + + object_idx_out = object_idx; + return true; +} + +const GLVolume* GLCanvas3D::Selection::get_volume(unsigned int volume_idx) const +{ + if (!m_valid) + return nullptr; + + return (volume_idx < (unsigned int)m_volumes->size()) ? (*m_volumes)[volume_idx] : nullptr; +} + +const BoundingBoxf3& GLCanvas3D::Selection::get_bounding_box() const +{ + if (m_bounding_box_dirty) + calc_bounding_box(); + + return m_bounding_box; +} + +void GLCanvas3D::Selection::start_dragging() +{ + if (!m_valid) + return; + + set_caches(); +} + +void GLCanvas3D::Selection::translate(const Vec3d& displacement) +{ + if (!m_valid) + return; + + for (unsigned int i : m_list) + { + (*m_volumes)[i]->set_offset(m_cache.volumes_data[i].position + displacement); + } + + m_bounding_box_dirty = true; +} + +void GLCanvas3D::Selection::render() const +{ + if (is_empty()) + return; + + float color[3] = { 1.0f, 1.0f, 1.0f }; + render_bounding_box(get_bounding_box(), color); +} + +void GLCanvas3D::Selection::update_valid() +{ + m_valid = (m_volumes != nullptr) && (m_model != nullptr); +} + +void GLCanvas3D::Selection::set_caches() +{ + m_cache.volumes_data.clear(); + for (unsigned int i : m_list) + { + const GLVolume* v = (*m_volumes)[i]; + m_cache.volumes_data.emplace(i, VolumeCache(v->get_offset(), v->get_rotation(), v->get_scaling_factor())); + } + m_cache.dragging_center = get_bounding_box().center(); +} + +void GLCanvas3D::Selection::add_volume(unsigned int volume_idx) +{ + // check if the given idx is already selected + if (m_list.find(volume_idx) != m_list.end()) + return; + + m_list.insert(volume_idx); + (*m_volumes)[volume_idx]->selected = true; +} + +void GLCanvas3D::Selection::add_instance(unsigned int volume_idx) +{ + GLVolume* volume = (*m_volumes)[volume_idx]; + int object_idx = volume->object_idx(); + int instance_idx = volume->instance_idx(); + + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) + { + GLVolume* v = (*m_volumes)[i]; + if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) + add_volume(i); + } +} + +void GLCanvas3D::Selection::add_object(unsigned int volume_idx) +{ + GLVolume* volume = (*m_volumes)[volume_idx]; + int object_idx = volume->object_idx(); + + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) + { + GLVolume* v = (*m_volumes)[i]; + if (v->object_idx() == object_idx) + add_volume(i); + } +} + +void GLCanvas3D::Selection::remove_volume(unsigned int volume_idx) +{ + IndicesList::iterator v_it = m_list.find(volume_idx); + if (v_it == m_list.end()) + return; + + m_list.erase(v_it); + + (*m_volumes)[volume_idx]->selected = false; +} + +void GLCanvas3D::Selection::remove_instance(unsigned int volume_idx) +{ + GLVolume* volume = (*m_volumes)[volume_idx]; + int object_idx = volume->object_idx(); + int instance_idx = volume->instance_idx(); + + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) + { + GLVolume* v = (*m_volumes)[i]; + if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) + remove_volume(i); + } +} + +void GLCanvas3D::Selection::remove_object(unsigned int volume_idx) +{ + GLVolume* volume = (*m_volumes)[volume_idx]; + int object_idx = volume->object_idx(); + + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) + { + GLVolume* v = (*m_volumes)[i]; + if (v->object_idx() == object_idx) + remove_volume(i); + } +} + +void GLCanvas3D::Selection::calc_bounding_box() const +{ + m_bounding_box = BoundingBoxf3(); + if (m_valid) + { + for (unsigned int i : m_list) + { + m_bounding_box.merge((*m_volumes)[i]->transformed_bounding_box()); + } + } + m_bounding_box_dirty = false; +} + +void GLCanvas3D::Selection::render_bounding_box(const BoundingBoxf3& box, float* color) const +{ + if (color == nullptr) + return; + + Vec3f b_min = box.min.cast(); + Vec3f b_max = box.max.cast(); + Vec3f size = 0.25f * box.size().cast(); + + ::glEnable(GL_DEPTH_TEST); + + ::glColor3fv(color); + + ::glBegin(GL_LINES); + + ::glVertex3f(b_min(0), b_min(1), b_min(2)); ::glVertex3f(b_min(0) + size(0), b_min(1), b_min(2)); + ::glVertex3f(b_min(0), b_min(1), b_min(2)); ::glVertex3f(b_min(0), b_min(1) + size(1), b_min(2)); + ::glVertex3f(b_min(0), b_min(1), b_min(2)); ::glVertex3f(b_min(0), b_min(1), b_min(2) + size(2)); + + ::glVertex3f(b_max(0), b_min(1), b_min(2)); ::glVertex3f(b_max(0) - size(0), b_min(1), b_min(2)); + ::glVertex3f(b_max(0), b_min(1), b_min(2)); ::glVertex3f(b_max(0), b_min(1) + size(1), b_min(2)); + ::glVertex3f(b_max(0), b_min(1), b_min(2)); ::glVertex3f(b_max(0), b_min(1), b_min(2) + size(2)); + + ::glVertex3f(b_max(0), b_max(1), b_min(2)); ::glVertex3f(b_max(0) - size(0), b_max(1), b_min(2)); + ::glVertex3f(b_max(0), b_max(1), b_min(2)); ::glVertex3f(b_max(0), b_max(1) - size(1), b_min(2)); + ::glVertex3f(b_max(0), b_max(1), b_min(2)); ::glVertex3f(b_max(0), b_max(1), b_min(2) + size(2)); + + ::glVertex3f(b_min(0), b_max(1), b_min(2)); ::glVertex3f(b_min(0) + size(0), b_max(1), b_min(2)); + ::glVertex3f(b_min(0), b_max(1), b_min(2)); ::glVertex3f(b_min(0), b_max(1) - size(1), b_min(2)); + ::glVertex3f(b_min(0), b_max(1), b_min(2)); ::glVertex3f(b_min(0), b_max(1), b_min(2) + size(2)); + + ::glVertex3f(b_min(0), b_min(1), b_max(2)); ::glVertex3f(b_min(0) + size(0), b_min(1), b_max(2)); + ::glVertex3f(b_min(0), b_min(1), b_max(2)); ::glVertex3f(b_min(0), b_min(1) + size(1), b_max(2)); + ::glVertex3f(b_min(0), b_min(1), b_max(2)); ::glVertex3f(b_min(0), b_min(1), b_max(2) - size(2)); + + ::glVertex3f(b_max(0), b_min(1), b_max(2)); ::glVertex3f(b_max(0) - size(0), b_min(1), b_max(2)); + ::glVertex3f(b_max(0), b_min(1), b_max(2)); ::glVertex3f(b_max(0), b_min(1) + size(1), b_max(2)); + ::glVertex3f(b_max(0), b_min(1), b_max(2)); ::glVertex3f(b_max(0), b_min(1), b_max(2) - size(2)); + + ::glVertex3f(b_max(0), b_max(1), b_max(2)); ::glVertex3f(b_max(0) - size(0), b_max(1), b_max(2)); + ::glVertex3f(b_max(0), b_max(1), b_max(2)); ::glVertex3f(b_max(0), b_max(1) - size(1), b_max(2)); + ::glVertex3f(b_max(0), b_max(1), b_max(2)); ::glVertex3f(b_max(0), b_max(1), b_max(2) - size(2)); + + ::glVertex3f(b_min(0), b_max(1), b_max(2)); ::glVertex3f(b_min(0) + size(0), b_max(1), b_max(2)); + ::glVertex3f(b_min(0), b_max(1), b_max(2)); ::glVertex3f(b_min(0), b_max(1) - size(1), b_max(2)); + ::glVertex3f(b_min(0), b_max(1), b_max(2)); ::glVertex3f(b_min(0), b_max(1), b_max(2) - size(2)); + + ::glEnd(); +} +#endif // ENABLE_EXTENDED_SELECTION + const float GLCanvas3D::Gizmos::OverlayTexturesScale = 0.75f; const float GLCanvas3D::Gizmos::OverlayOffsetX = 10.0f * OverlayTexturesScale; const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayTexturesScale; @@ -1223,7 +1642,11 @@ void GLCanvas3D::Gizmos::set_enabled(bool enable) m_enabled = enable; } +#if ENABLE_EXTENDED_SELECTION +void GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const GLCanvas3D::Selection& selection) +#else void GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos) +#endif // ENABLE_EXTENDED_SELECTION { if (!m_enabled) return; @@ -1240,7 +1663,12 @@ void GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, const Vec2 float half_tex_size = 0.5f * tex_size; // we currently use circular icons for gizmo, so we check the radius +#if ENABLE_EXTENDED_SELECTION + bool no_wipe_tower = selection.is_wipe_tower() && !it->second->get_accept_wipe_tower(); + if (!no_wipe_tower && (it->second->get_state() != GLGizmoBase::On)) +#else if (it->second->get_state() != GLGizmoBase::On) +#endif // ENABLE_EXTENDED_SELECTION { bool inside = (mouse_pos - Vec2d(OverlayOffsetX + half_tex_size, top_y + half_tex_size)).norm() < half_tex_size; it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off); @@ -1249,7 +1677,11 @@ void GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, const Vec2 } } +#if ENABLE_EXTENDED_SELECTION +void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const GLCanvas3D::Selection& selection) +#else void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos) +#endif // ENABLE_EXTENDED_SELECTION { if (!m_enabled) return; @@ -1266,7 +1698,12 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec float half_tex_size = 0.5f * tex_size; // we currently use circular icons for gizmo, so we check the radius +#if ENABLE_EXTENDED_SELECTION + bool no_wipe_tower = selection.is_wipe_tower() && !it->second->get_accept_wipe_tower(); + if (!no_wipe_tower && ((mouse_pos - Vec2d(OverlayOffsetX + half_tex_size, top_y + half_tex_size)).norm() < half_tex_size)) +#else if ((mouse_pos - Vec2d(OverlayOffsetX + half_tex_size, top_y + half_tex_size)).norm() < half_tex_size) +#endif // ENABLE_EXTENDED_SELECTION { if ((it->second->get_state() == GLGizmoBase::On)) { @@ -1315,6 +1752,28 @@ void GLCanvas3D::Gizmos::set_hover_id(int id) } } +#if ENABLE_EXTENDED_SELECTION +void GLCanvas3D::Gizmos::enable_grabber(EType type, unsigned int id) +{ + if (!m_enabled) + return; + + GizmosMap::const_iterator it = m_gizmos.find(type); + if (it != m_gizmos.end()) + it->second->enable_grabber(id); +} + +void GLCanvas3D::Gizmos::disable_grabber(EType type, unsigned int id) +{ + if (!m_enabled) + return; + + GizmosMap::const_iterator it = m_gizmos.find(type); + if (it != m_gizmos.end()) + it->second->disable_grabber(id); +} +#endif // ENABLE_EXTENDED_SELECTION + bool GLCanvas3D::Gizmos::overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const { if (!m_enabled) @@ -1406,6 +1865,16 @@ void GLCanvas3D::Gizmos::stop_dragging() curr->stop_dragging(); } +#if ENABLE_EXTENDED_SELECTION +Vec3d GLCanvas3D::Gizmos::get_displacement() const +{ + if (!m_enabled) + return Vec3d::Zero(); + + GizmosMap::const_iterator it = m_gizmos.find(Move); + return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_displacement() : Vec3d::Zero(); +} +#else Vec3d GLCanvas3D::Gizmos::get_position() const { if (!m_enabled) @@ -1424,6 +1893,7 @@ void GLCanvas3D::Gizmos::set_position(const Vec3d& position) if (it != m_gizmos.end()) reinterpret_cast(it->second)->set_position(position); } +#endif // ENABLE_EXTENDED_SELECTION #if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM Vec3d GLCanvas3D::Gizmos::get_scale() const @@ -1620,6 +2090,12 @@ float GLCanvas3D::Gizmos::_get_total_overlay_height() const return height; } +GLGizmoBase* GLCanvas3D::Gizmos::_get_current() const +{ + GizmosMap::const_iterator it = m_gizmos.find(m_current); + return (it != m_gizmos.end()) ? it->second : nullptr; +} + const unsigned char GLCanvas3D::WarningTexture::Background_Color[3] = { 9, 91, 134 }; const unsigned char GLCanvas3D::WarningTexture::Opacity = 255; @@ -1933,12 +2409,6 @@ void GLCanvas3D::LegendTexture::render(const GLCanvas3D& canvas) const } } -GLGizmoBase* GLCanvas3D::Gizmos::_get_current() const -{ - GizmosMap::const_iterator it = m_gizmos.find(m_current); - return (it != m_gizmos.end()) ? it->second : nullptr; -} - wxDEFINE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, ObjectSelectEvent); wxDEFINE_EVENT(EVT_GLCANVAS_VIEWPORT_CHANGED, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_DOUBLE_CLICK, SimpleEvent); @@ -1980,9 +2450,14 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) , m_shader_enabled(false) , m_dynamic_background_enabled(false) , m_multisample_allowed(false) +#if ENABLE_EXTENDED_SELECTION + , m_regenerate_volumes(true) +#endif // ENABLE_EXTENDED_SELECTION , m_color_by("volume") +#if !ENABLE_EXTENDED_SELECTION , m_select_by("object") , m_drag_by("instance") +#endif // !ENABLE_EXTENDED_SELECTION , m_reload_delayed(false) { if (m_canvas != nullptr) @@ -1992,6 +2467,10 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) #endif // !ENABLE_USE_UNIQUE_GLCONTEXT m_timer = new wxTimer(m_canvas); } + +#if ENABLE_EXTENDED_SELECTION + m_selection.set_volumes(&m_volumes.volumes); +#endif // ENABLE_EXTENDED_SELECTION } GLCanvas3D::~GLCanvas3D() @@ -2132,6 +2611,9 @@ void GLCanvas3D::reset_volumes() m_volumes.release_geometry(); m_volumes.clear(); +#if ENABLE_EXTENDED_SELECTION + m_selection.clear(); +#endif // ENABLE_EXTENDED_SELECTION m_dirty = true; } @@ -2139,6 +2621,7 @@ void GLCanvas3D::reset_volumes() _reset_warning_texture(); } +#if !ENABLE_EXTENDED_SELECTION void GLCanvas3D::deselect_volumes() { for (GLVolume* vol : m_volumes.volumes) @@ -2178,6 +2661,7 @@ void GLCanvas3D::update_volumes_selection(const std::vector& selections) } } } +#endif // !ENABLE_EXTENDED_SELECTION int GLCanvas3D::check_volumes_outside_state(const DynamicPrintConfig* config) const { @@ -2192,8 +2676,10 @@ bool GLCanvas3D::move_volume_up(unsigned int id) { std::swap(m_volumes.volumes[id - 1], m_volumes.volumes[id]); std::swap(m_volumes.volumes[id - 1]->composite_id, m_volumes.volumes[id]->composite_id); +#if !ENABLE_EXTENDED_SELECTION std::swap(m_volumes.volumes[id - 1]->select_group_id, m_volumes.volumes[id]->select_group_id); std::swap(m_volumes.volumes[id - 1]->drag_group_id, m_volumes.volumes[id]->drag_group_id); +#endif // !ENABLE_EXTENDED_SELECTION return true; } @@ -2206,18 +2692,22 @@ bool GLCanvas3D::move_volume_down(unsigned int id) { std::swap(m_volumes.volumes[id + 1], m_volumes.volumes[id]); std::swap(m_volumes.volumes[id + 1]->composite_id, m_volumes.volumes[id]->composite_id); +#if !ENABLE_EXTENDED_SELECTION std::swap(m_volumes.volumes[id + 1]->select_group_id, m_volumes.volumes[id]->select_group_id); std::swap(m_volumes.volumes[id + 1]->drag_group_id, m_volumes.volumes[id]->drag_group_id); +#endif // !ENABLE_EXTENDED_SELECTION return true; } return false; } +#if !ENABLE_EXTENDED_SELECTION void GLCanvas3D::set_objects_selections(const std::vector& selections) { m_objects_selections = selections; } +#endif // !ENABLE_EXTENDED_SELECTION void GLCanvas3D::set_config(DynamicPrintConfig* config) { @@ -2232,6 +2722,9 @@ void GLCanvas3D::set_print(Print* print) void GLCanvas3D::set_model(Model* model) { m_model = model; +#if ENABLE_EXTENDED_SELECTION + m_selection.set_model(m_model); +#endif // ENABLE_EXTENDED_SELECTION } void GLCanvas3D::set_bed_shape(const Pointfs& shape) @@ -2289,6 +2782,7 @@ void GLCanvas3D::set_color_by(const std::string& value) m_color_by = value; } +#if !ENABLE_EXTENDED_SELECTION void GLCanvas3D::set_select_by(const std::string& value) { m_select_by = value; @@ -2310,6 +2804,7 @@ const std::string& GLCanvas3D::get_drag_by() const { return m_drag_by; } +#endif // !ENABLE_EXTENDED_SELECTION float GLCanvas3D::get_camera_zoom() const { @@ -2365,6 +2860,9 @@ void GLCanvas3D::enable_legend_texture(bool enable) void GLCanvas3D::enable_picking(bool enable) { m_picking_enabled = enable; +#if ENABLE_EXTENDED_SELECTION + m_selection.set_mode(Selection::Instance); +#endif // ENABLE_EXTENDED_SELECTION } void GLCanvas3D::enable_moving(bool enable) @@ -2478,6 +2976,32 @@ void GLCanvas3D::update_gizmos_data() if (!m_gizmos.is_enabled()) return; +#if ENABLE_EXTENDED_SELECTION + if (m_gizmos.get_current_type() == Gizmos::Move) + { + if (m_selection.is_wipe_tower()) + m_gizmos.disable_grabber(Gizmos::Move, 2); + else + m_gizmos.enable_grabber(Gizmos::Move, 2); + } + + int object_idx = -1; + int instance_idx = -1; + if (m_selection.is_single_full_instance(object_idx, instance_idx)) + { + ModelObject* model_object = m_model->objects[object_idx]; + ModelInstance* model_instance = model_object->instances[instance_idx]; + m_gizmos.set_scale(model_instance->get_scaling_factor()); + m_gizmos.set_rotation(model_instance->get_rotation()); + m_gizmos.set_flattening_data(model_object); + } + else + { + m_gizmos.set_scale(Vec3d::Ones()); + m_gizmos.set_rotation(Vec3d::Zero()); + m_gizmos.set_flattening_data(nullptr); + } +#else int id = _get_first_selected_object_id(); if ((id != -1) && (m_model != nullptr)) { @@ -2512,6 +3036,7 @@ void GLCanvas3D::update_gizmos_data() #endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM m_gizmos.set_flattening_data(nullptr); } +#endif // ENABLE_EXTENDED_SELECTION } void GLCanvas3D::render() @@ -2556,6 +3081,9 @@ void GLCanvas3D::render() _render_axes(false); } _render_objects(); +#if ENABLE_EXTENDED_SELECTION + _render_selection(); +#endif // ENABLE_EXTENDED_SELECTION if (!is_custom_bed) // textured bed needs to be rendered after objects { _render_axes(true); @@ -2594,7 +3122,11 @@ std::vector GLCanvas3D::load_object(const ModelObject& model_object, int ob instance_idxs.push_back(i); } } +#if ENABLE_EXTENDED_SELECTION + return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_color_by, m_use_VBOs && m_initialized); +#else return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_color_by, m_select_by, m_drag_by, m_use_VBOs && m_initialized); +#endif // ENABLE_EXTENDED_SELECTION } std::vector GLCanvas3D::load_object(const Model& model, int obj_idx) @@ -2630,7 +3162,9 @@ void GLCanvas3D::reload_scene(bool force) if ((m_canvas == nullptr) || (m_config == nullptr) || (m_model == nullptr)) return; +#if !ENABLE_EXTENDED_SELECTION reset_volumes(); +#endif // !ENABLE_EXTENDED_SELECTION #if !ENABLE_USE_UNIQUE_GLCONTEXT // ensures this canvas is current @@ -2638,6 +3172,11 @@ void GLCanvas3D::reload_scene(bool force) return; #endif // !ENABLE_USE_UNIQUE_GLCONTEXT +#if ENABLE_EXTENDED_SELECTION + if (m_regenerate_volumes) + reset_volumes(); +#endif // ENABLE_EXTENDED_SELECTION + set_bed_shape(dynamic_cast(m_config->option("bed_shape"))->values); if (!m_canvas->IsShown() && !force) @@ -2648,6 +3187,17 @@ void GLCanvas3D::reload_scene(bool force) m_reload_delayed = false; +#if ENABLE_EXTENDED_SELECTION + if (m_regenerate_volumes) + { + for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++obj_idx) + { + load_object(*m_model, obj_idx); + } + } + + update_gizmos_data(); +#else m_objects_volumes_idxs.clear(); for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++obj_idx) @@ -2661,36 +3211,44 @@ void GLCanvas3D::reload_scene(bool force) // 2nd call to restore selection, if any if (!m_objects_selections.empty()) update_gizmos_data(); +#endif // ENABLE_EXTENDED_SELECTION - if (m_config->has("nozzle_diameter")) +#if ENABLE_EXTENDED_SELECTION + if (m_regenerate_volumes) { - // Should the wipe tower be visualized ? - unsigned int extruders_count = (unsigned int)dynamic_cast(m_config->option("nozzle_diameter"))->values.size(); - - bool semm = dynamic_cast(m_config->option("single_extruder_multi_material"))->value; - bool wt = dynamic_cast(m_config->option("wipe_tower"))->value; - bool co = dynamic_cast(m_config->option("complete_objects"))->value; - - if ((extruders_count > 1) && semm && wt && !co) +#endif // ENABLE_EXTENDED_SELECTION + if (m_config->has("nozzle_diameter")) { - // Height of a print (Show at least a slab) - double height = std::max(m_model->bounding_box().max(2), 10.0); + // Should the wipe tower be visualized ? + unsigned int extruders_count = (unsigned int)dynamic_cast(m_config->option("nozzle_diameter"))->values.size(); - float x = dynamic_cast(m_config->option("wipe_tower_x"))->value; - float y = dynamic_cast(m_config->option("wipe_tower_y"))->value; - float w = dynamic_cast(m_config->option("wipe_tower_width"))->value; - float a = dynamic_cast(m_config->option("wipe_tower_rotation_angle"))->value; + bool semm = dynamic_cast(m_config->option("single_extruder_multi_material"))->value; + bool wt = dynamic_cast(m_config->option("wipe_tower"))->value; + bool co = dynamic_cast(m_config->option("complete_objects"))->value; - float depth = m_print->get_wipe_tower_depth(); - if (!m_print->is_step_done(psWipeTower)) - depth = (900.f/w) * (float)(extruders_count - 1) ; + if ((extruders_count > 1) && semm && wt && !co) + { + // Height of a print (Show at least a slab) + double height = std::max(m_model->bounding_box().max(2), 10.0); - m_volumes.load_wipe_tower_preview(1000, x, y, w, depth, (float)height, a, m_use_VBOs && m_initialized, !m_print->is_step_done(psWipeTower), - m_print->config().nozzle_diameter.values[0] * 1.25f * 4.5f); + float x = dynamic_cast(m_config->option("wipe_tower_x"))->value; + float y = dynamic_cast(m_config->option("wipe_tower_y"))->value; + float w = dynamic_cast(m_config->option("wipe_tower_width"))->value; + float a = dynamic_cast(m_config->option("wipe_tower_rotation_angle"))->value; + + float depth = m_print->get_wipe_tower_depth(); + if (!m_print->is_step_done(psWipeTower)) + depth = (900.f/w) * (float)(extruders_count - 1) ; + + m_volumes.load_wipe_tower_preview(1000, x, y, w, depth, (float)height, a, m_use_VBOs && m_initialized, !m_print->is_step_done(psWipeTower), + m_print->config().nozzle_diameter.values[0] * 1.25f * 4.5f); + } } - } - update_volumes_colors_by_extruder(); + update_volumes_colors_by_extruder(); +#if ENABLE_EXTENDED_SELECTION + } +#endif // ENABLE_EXTENDED_SELECTION // checks for geometry outside the print volume to render it accordingly if (!m_volumes.empty()) @@ -2718,6 +3276,11 @@ void GLCanvas3D::reload_scene(bool force) _reset_warning_texture(); post_event(Event(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, false)); } + +#if ENABLE_EXTENDED_SELECTION + // restore to default value + m_regenerate_volumes = true; +#endif // ENABLE_EXTENDED_SELECTION } void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const std::vector& str_tool_colors) @@ -2913,7 +3476,12 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) // Performs layers editing updates, if enabled if (is_layers_editing_enabled()) { +#if ENABLE_EXTENDED_SELECTION + int object_idx_selected = -1; + m_selection.is_from_single_object(object_idx_selected); +#else int object_idx_selected = _get_first_selected_object_id(); +#endif // ENABLE_EXTENDED_SELECTION if (object_idx_selected != -1) { // A volume is selected. Test, whether hovering over a layer thickness bar. @@ -2957,8 +3525,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { Point pos(evt.GetX(), evt.GetY()); +#if ENABLE_EXTENDED_SELECTION + int selected_object_idx = -1; + m_selection.is_from_single_object(selected_object_idx); + int layer_editing_object_idx = is_layers_editing_enabled() ? selected_object_idx : -1; +#else int selected_object_idx = _get_first_selected_object_id(); int layer_editing_object_idx = is_layers_editing_enabled() ? selected_object_idx : -1; +#endif // ENABLE_EXTENDED_SELECTION m_layers_editing.last_object_id = layer_editing_object_idx; bool gizmos_overlay_contains_mouse = m_gizmos.overlay_contains_mouse(*this, m_mouse.position); int toolbar_contains_mouse = m_toolbar.contains_mouse(m_mouse.position); @@ -3026,7 +3600,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { // If user pressed left or right button we first check whether this happened // on a volume or not. +#if !ENABLE_EXTENDED_SELECTION int volume_idx = m_hover_volume_id; +#endif // !ENABLE_EXTENDED_SELECTION m_layers_editing.state = LayersEditing::Unknown; if ((layer_editing_object_idx != -1) && m_layers_editing.bar_rect_contains(*this, pos(0), pos(1))) { @@ -3049,17 +3625,35 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_dirty = true; } } +#if ENABLE_EXTENDED_SELECTION + else if (!m_selection.is_empty() && gizmos_overlay_contains_mouse) + { + m_gizmos.update_on_off_state(*this, m_mouse.position, m_selection); + update_gizmos_data(); + m_dirty = true; + } +#else else if ((selected_object_idx != -1) && gizmos_overlay_contains_mouse) { update_gizmos_data(); m_gizmos.update_on_off_state(*this, m_mouse.position); m_dirty = true; } +#endif // ENABLE_EXTENDED_SELECTION +#if ENABLE_EXTENDED_SELECTION + else if (!m_selection.is_empty() && m_gizmos.grabber_contains_mouse()) +#else else if ((selected_object_idx != -1) && m_gizmos.grabber_contains_mouse()) +#endif // ENABLE_EXTENDED_SELECTION { update_gizmos_data(); +#if ENABLE_EXTENDED_SELECTION + m_selection.start_dragging(); + m_gizmos.start_dragging(m_selection.get_bounding_box()); +#else m_gizmos.start_dragging(_selected_volumes_bounding_box()); m_mouse.drag.gizmo_volume_idx = _get_first_selected_volume_id(selected_object_idx); +#endif // ENABLE_EXTENDED_SELECTION if (m_gizmos.get_current_type() == Gizmos::Flatten) { #if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM @@ -3089,8 +3683,25 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // Don't deselect a volume if layer editing is enabled. We want the object to stay selected // during the scene manipulation. +#if ENABLE_EXTENDED_SELECTION + if (m_picking_enabled && ((m_hover_volume_id != -1) || !is_layers_editing_enabled())) +#else if (m_picking_enabled && ((volume_idx != -1) || !is_layers_editing_enabled())) +#endif // ENABLE_EXTENDED_SELECTION { +#if ENABLE_EXTENDED_SELECTION + if (m_hover_volume_id != -1) + { + if (evt.ControlDown()) + m_selection.remove(m_hover_volume_id); + else + m_selection.add(m_hover_volume_id, !evt.ShiftDown()); + + update_gizmos_data(); + wxGetApp().obj_manipul()->update_settings_value(m_selection); + m_dirty = true; + } +#else if (volume_idx != -1) { deselect_volumes(); @@ -3108,32 +3719,61 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) update_gizmos_data(); m_dirty = true; } +#endif // ENABLE_EXTENDED_SELECTION } // propagate event through callback +#if ENABLE_EXTENDED_SELECTION + if (m_picking_enabled && (m_hover_volume_id != -1)) + { + int object_idx = -1; + m_selection.is_from_single_object(object_idx); + _on_select(m_hover_volume_id, object_idx); + } +#else if (m_picking_enabled && (volume_idx != -1)) _on_select(volume_idx, selected_object_idx); +#endif // ENABLE_EXTENDED_SELECTION +#if ENABLE_EXTENDED_SELECTION + if (m_hover_volume_id != -1) +#else if (volume_idx != -1) +#endif // ENABLE_EXTENDED_SELECTION { +#if ENABLE_EXTENDED_SELECTION + if (evt.LeftDown() && m_moving_enabled && (m_mouse.drag.move_volume_idx == -1)) +#else if (evt.LeftDown() && m_moving_enabled) +#endif // ENABLE_EXTENDED_SELECTION { // The mouse_to_3d gets the Z coordinate from the Z buffer at the screen coordinate pos x, y, // an converts the screen space coordinate to unscaled object space. - Vec3d pos3d = (volume_idx == -1) ? Vec3d(DBL_MAX, DBL_MAX, DBL_MAX) : _mouse_to_3d(pos); + Vec3d pos3d = _mouse_to_3d(pos); // Only accept the initial position, if it is inside the volume bounding box. +#if ENABLE_EXTENDED_SELECTION + BoundingBoxf3 volume_bbox = m_volumes.volumes[m_hover_volume_id]->transformed_bounding_box(); +#else BoundingBoxf3 volume_bbox = m_volumes.volumes[volume_idx]->transformed_bounding_box(); +#endif // ENABLE_EXTENDED_SELECTION volume_bbox.offset(1.0); if (volume_bbox.contains(pos3d)) { // The dragging operation is initiated. +#if ENABLE_EXTENDED_SELECTION + m_mouse.drag.move_volume_idx = m_hover_volume_id; + m_selection.start_dragging(); +#else m_mouse.drag.move_with_shift = evt.ShiftDown(); m_mouse.drag.move_volume_idx = volume_idx; +#endif // ENABLE_EXTENDED_SELECTION m_mouse.drag.start_position_3D = pos3d; +#if !ENABLE_EXTENDED_SELECTION // Remember the shift to to the object center.The object center will later be used // to limit the object placement close to the bed. m_mouse.drag.volume_center_offset = volume_bbox.center() - pos3d; +#endif // !ENABLE_EXTENDED_SELECTION } } else if (evt.RightDown()) @@ -3145,7 +3785,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (m_hover_volume_id != -1) { // if right clicking on volume, propagate event through callback (shows context menu) +#if ENABLE_EXTENDED_SELECTION + if (m_volumes.volumes[m_hover_volume_id]->hover) +#else if (m_volumes.volumes[volume_idx]->hover) +#endif // ENABLE_EXTENDED_SELECTION post_event(Vec2dEvent(EVT_GLCANVAS_RIGHT_CLICK, pos.cast())); } } @@ -3161,6 +3805,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) float z1 = 1.0f; Vec3d cur_pos = Linef3(_mouse_to_3d(pos, &z0), _mouse_to_3d(pos, &z1)).intersect_plane(m_mouse.drag.start_position_3D(2)); +#if !ENABLE_EXTENDED_SELECTION // Clip the new position, so the object center remains close to the bed. cur_pos += m_mouse.drag.volume_center_offset; Point cur_pos2(scale_(cur_pos(0)), scale_(cur_pos(1))); @@ -3171,9 +3816,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) cur_pos(1) = unscale(ip(1)); } cur_pos -= m_mouse.drag.volume_center_offset; +#endif // !ENABLE_EXTENDED_SELECTION // Calculate the translation vector. - Vec3d vector = cur_pos - m_mouse.drag.start_position_3D; + Vec3d displacement = cur_pos - m_mouse.drag.start_position_3D; +#if ENABLE_EXTENDED_SELECTION + m_selection.translate(displacement); + wxGetApp().obj_manipul()->update_settings_value(m_selection); +#else // Get the volume being dragged. GLVolume* volume = m_volumes.volumes[m_mouse.drag.move_volume_idx]; // Get all volumes belonging to the same group, if any. @@ -3196,11 +3846,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // Apply new temporary volume origin and ignore Z. for (GLVolume* v : volumes) { - v->set_offset(v->get_offset() + Vec3d(vector(0), vector(1), 0.0)); + v->set_offset(v->get_offset() + Vec3d(displacement(0), displacement(1), 0.0)); } wxGetApp().obj_manipul()->update_position_values(volume->get_offset()); m_mouse.drag.start_position_3D = cur_pos; +#endif // ENABLE_EXTENDED_SELECTION m_dirty = true; } @@ -3212,6 +3863,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_mouse.dragging = true; m_gizmos.update(mouse_ray(pos)); +#if !ENABLE_EXTENDED_SELECTION std::vector volumes; if (m_mouse.drag.gizmo_volume_idx != -1) { @@ -3228,12 +3880,17 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } } } +#endif // !ENABLE_EXTENDED_SELECTION switch (m_gizmos.get_current_type()) { case Gizmos::Move: { // Apply new temporary offset +#if ENABLE_EXTENDED_SELECTION + m_selection.translate(m_gizmos.get_displacement()); + wxGetApp().obj_manipul()->update_settings_value(m_selection); +#else GLVolume* volume = m_volumes.volumes[m_mouse.drag.gizmo_volume_idx]; Vec3d offset = m_gizmos.get_position() - volume->get_offset(); for (GLVolume* v : volumes) @@ -3241,11 +3898,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) v->set_offset(v->get_offset() + offset); } wxGetApp().obj_manipul()->update_position_values(volume->get_offset()); +#endif // ENABLE_EXTENDED_SELECTION break; } case Gizmos::Scale: { #if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM +#if ENABLE_EXTENDED_SELECTION +#else // Apply new temporary scale factors const Vec3d& scale = m_gizmos.get_scale(); for (GLVolume* v : volumes) @@ -3253,6 +3913,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) v->set_scaling_factor(scale); } wxGetApp().obj_manipul()->update_scale_values(scale); +#endif // ENABLE_EXTENDED_SELECTION #else // Apply new temporary scale factor float scale_factor = m_gizmos.get_scale(); @@ -3267,6 +3928,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) case Gizmos::Rotate: { #if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM +#if ENABLE_EXTENDED_SELECTION +#else // Apply new temporary rotation const Vec3d& rotation = m_gizmos.get_rotation(); for (GLVolume* v : volumes) @@ -3274,6 +3937,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) v->set_rotation(rotation); } wxGetApp().obj_manipul()->update_rotation_value(rotation); +#endif // ENABLE_EXTENDED_SELECTION #else // Apply new temporary angle_z float angle_z = m_gizmos.get_angle_z(); @@ -3289,6 +3953,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) break; } +#if ENABLE_EXTENDED_SELECTION +#else if (!volumes.empty()) { BoundingBoxf3 bb; @@ -3304,6 +3970,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_on_update_geometry_info_callback.call(size(0), size(1), size(2), m_gizmos.get_scale()); #endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM } +#endif // ENABLE_EXTENDED_SELECTION m_dirty = true; } @@ -3362,6 +4029,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } else if ((m_mouse.drag.move_volume_idx != -1) && m_mouse.dragging) { +#if ENABLE_EXTENDED_SELECTION + m_regenerate_volumes = false; + _on_move(); + wxGetApp().obj_manipul()->update_settings_value(m_selection); +#else // get all volumes belonging to the same group, if any std::vector volume_idxs; int vol_id = m_mouse.drag.move_volume_idx; @@ -3382,6 +4054,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // force re-selection of the wipe tower, if needed if ((volume_idxs.size() == 1) && m_volumes.volumes[volume_idxs[0]]->is_wipe_tower) select_volume(volume_idxs[0]); +#endif // ENABLE_EXTENDED_SELECTION } else if (evt.LeftUp() && !m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !m_gizmos.is_dragging() && !is_layers_editing_enabled()) { @@ -3392,7 +4065,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (m_picking_enabled && !m_toolbar_action_running) #endif // ENABLE_GIZMOS_RESET { +#if ENABLE_EXTENDED_SELECTION + m_selection.clear(); + wxGetApp().obj_manipul()->update_settings_value(m_selection); +#else deselect_volumes(); +#endif // ENABLE_EXTENDED_SELECTION _on_select(-1, -1); update_gizmos_data(); } @@ -3407,6 +4085,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { case Gizmos::Move: { +#if ENABLE_EXTENDED_SELECTION + m_regenerate_volumes = false; + _on_move(); +#else // get all volumes belonging to the same group, if any std::vector volume_idxs; int vol_id = m_mouse.drag.gizmo_volume_idx; @@ -3423,6 +4105,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } _on_move(volume_idxs); +#endif // ENABLE_EXTENDED_SELECTION break; } @@ -3448,11 +4131,17 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) break; } m_gizmos.stop_dragging(); +#if ENABLE_EXTENDED_SELECTION + wxGetApp().obj_manipul()->update_settings_value(m_selection); +#else wxGetApp().obj_manipul()->update_values(); +#endif // ENABLE_EXTENDED_SELECTION } m_mouse.drag.move_volume_idx = -1; +#if !ENABLE_EXTENDED_SELECTION m_mouse.drag.gizmo_volume_idx = -1; +#endif // !ENABLE_EXTENDED_SELECTION m_mouse.set_start_position_3D_as_invalid(); m_mouse.set_start_position_2D_as_invalid(); m_mouse.dragging = false; @@ -3656,6 +4345,7 @@ bool GLCanvas3D::_init_toolbar() if (!m_toolbar.add_separator()) return false; +#if !ENABLE_EXTENDED_SELECTION item.name = "selectbyparts"; item.tooltip = GUI::L_str("Select by parts"); item.sprite_id = 10; @@ -3663,6 +4353,7 @@ bool GLCanvas3D::_init_toolbar() item.action_event = EVT_GLTOOLBAR_SELECTBYPARTS; if (!m_toolbar.add_item(item)) return false; +#endif // !ENABLE_EXTENDED_SELECTION enable_toolbar_item("add", true); @@ -3758,6 +4449,7 @@ BoundingBoxf3 GLCanvas3D::_max_bounding_box() const return bb; } +#if !ENABLE_EXTENDED_SELECTION BoundingBoxf3 GLCanvas3D::_selected_volumes_bounding_box() const { BoundingBoxf3 bb; @@ -3802,6 +4494,7 @@ BoundingBoxf3 GLCanvas3D::_selected_volumes_bounding_box() const return bb; } +#endif // !ENABLE_EXTENDED_SELECTION void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) { @@ -3893,7 +4586,11 @@ void GLCanvas3D::_mark_volumes_for_layer_height() const for (GLVolume* vol : m_volumes.volumes) { +#if ENABLE_EXTENDED_SELECTION + int object_id = vol->object_idx(); +#else int object_id = int(vol->select_group_id / 1000000); +#endif // ENABLE_EXTENDED_SELECTION int shader_id = m_layers_editing.get_shader_program_id(); if (is_layers_editing_enabled() && (shader_id != -1) && vol->selected && @@ -3949,16 +4646,22 @@ void GLCanvas3D::_picking_pass() const ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); _render_volumes(true); +#if ENABLE_EXTENDED_SELECTION + m_gizmos.render_current_gizmo_for_picking_pass(m_selection.get_bounding_box()); +#else m_gizmos.render_current_gizmo_for_picking_pass(_selected_volumes_bounding_box()); +#endif // ENABLE_EXTENDED_SELECTION if (m_multisample_allowed) ::glEnable(GL_MULTISAMPLE); int volume_id = -1; +#if !ENABLE_EXTENDED_SELECTION for (GLVolume* vol : m_volumes.volumes) { vol->hover = false; } +#endif // !ENABLE_EXTENDED_SELECTION GLubyte color[4] = { 0, 0, 0, 0 }; const Size& cnv_size = get_canvas_size(); @@ -3972,6 +4675,7 @@ void GLCanvas3D::_picking_pass() const if ((0 <= volume_id) && (volume_id < (int)m_volumes.volumes.size())) { m_hover_volume_id = volume_id; +#if !ENABLE_EXTENDED_SELECTION m_volumes.volumes[volume_id]->hover = true; int group_id = m_volumes.volumes[volume_id]->select_group_id; if (group_id != -1) @@ -3982,6 +4686,7 @@ void GLCanvas3D::_picking_pass() const vol->hover = true; } } +#endif // !ENABLE_EXTENDED_SELECTION m_gizmos.set_hover_id(-1); } else @@ -3990,9 +4695,18 @@ void GLCanvas3D::_picking_pass() const m_gizmos.set_hover_id(inside ? (254 - (int)color[2]) : -1); } +#if ENABLE_EXTENDED_SELECTION + _update_volumes_hover_state(); +#endif // ENABLE_EXTENDED_SELECTION + // updates gizmos overlay +#if ENABLE_EXTENDED_SELECTION + if (!m_selection.is_empty()) + m_gizmos.update_hover_state(*this, pos, m_selection); +#else if (_get_first_selected_object_id() != -1) m_gizmos.update_hover_state(*this, pos); +#endif // ENABLE_EXTENDED_SELECTION else m_gizmos.reset_all_states(); @@ -4092,6 +4806,13 @@ void GLCanvas3D::_render_objects() const ::glDisable(GL_LIGHTING); } +#if ENABLE_EXTENDED_SELECTION +void GLCanvas3D::_render_selection() const +{ + m_selection.render(); +} +#endif // ENABLE_EXTENDED_SELECTION + void GLCanvas3D::_render_cutting_plane() const { m_cutting_plane.render(volumes_bounding_box()); @@ -4134,7 +4855,11 @@ void GLCanvas3D::_render_layer_editing_overlay() const // If the active object was not allocated at the Print, go away.This should only be a momentary case between an object addition / deletion // and an update by Platter::async_apply_config. +#if ENABLE_EXTENDED_SELECTION + int object_idx = volume->object_idx(); +#else int object_idx = int(volume->select_group_id / 1000000); +#endif // ENABLE_EXTENDED_SELECTION if ((int)m_print->objects().size() < object_idx) return; @@ -4194,7 +4919,11 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const void GLCanvas3D::_render_current_gizmo() const { +#if ENABLE_EXTENDED_SELECTION + m_gizmos.render_current_gizmo(m_selection.get_bounding_box()); +#else m_gizmos.render_current_gizmo(_selected_volumes_bounding_box()); +#endif // ENABLE_EXTENDED_SELECTION } void GLCanvas3D::_render_gizmos_overlay() const @@ -4208,6 +4937,43 @@ void GLCanvas3D::_render_toolbar() const m_toolbar.render(); } +#if ENABLE_EXTENDED_SELECTION +void GLCanvas3D::_update_volumes_hover_state() const +{ + for (GLVolume* v : m_volumes.volumes) + { + v->hover = false; + } + + if (m_hover_volume_id == -1) + return; + + GLVolume* volume = m_volumes.volumes[m_hover_volume_id]; + + switch (m_selection.get_mode()) + { + case Selection::Volume: + { + volume->hover = true; + break; + } + case Selection::Instance: + { + int object_idx = volume->object_idx(); + int instance_idx = volume->instance_idx(); + + for (GLVolume* v : m_volumes.volumes) + { + if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) + v->hover = true; + } + + break; + } + } +} +#endif // ENABLE_EXTENDED_SELECTION + float GLCanvas3D::_get_layers_editing_cursor_z_relative() const { return m_layers_editing.get_cursor_z_relative(*this); @@ -4312,6 +5078,7 @@ void GLCanvas3D::_stop_timer() m_timer->Stop(); } +#if !ENABLE_EXTENDED_SELECTION int GLCanvas3D::_get_first_selected_object_id() const { if (m_print != nullptr) @@ -4345,6 +5112,7 @@ int GLCanvas3D::_get_first_selected_volume_id(int object_id) const return -1; } +#endif // !ENABLE_EXTENDED_SELECTION void GLCanvas3D::_load_print_toolpaths() { @@ -4473,7 +5241,11 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c tbb::parallel_for( tbb::blocked_range(0, ctxt.layers.size(), grain_size), [&ctxt, &new_volume](const tbb::blocked_range& range) { +#if ENABLE_EXTENDED_SELECTION + GLVolumePtrs vols; +#else std::vector vols; +#endif // ENABLE_EXTENDED_SELECTION if (ctxt.color_by_tool()) { for (size_t i = 0; i < ctxt.number_tools(); ++i) vols.emplace_back(new_volume(ctxt.color_tool(i))); @@ -4632,7 +5404,11 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector& str_ tbb::blocked_range(0, n_items, grain_size), [&ctxt, &new_volume](const tbb::blocked_range& range) { // Bounding box of this slab of a wipe tower. +#if ENABLE_EXTENDED_SELECTION + GLVolumePtrs vols; +#else std::vector vols; +#endif // ENABLE_EXTENDED_SELECTION if (ctxt.color_by_tool()) { for (size_t i = 0; i < ctxt.number_tools(); ++i) vols.emplace_back(new_volume(ctxt.color_tool(i))); @@ -4858,9 +5634,15 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat m_gcode_preview_volume_index.first_volumes.pop_back(); if (initial_volumes_count != m_volumes.volumes.size()) { +#if ENABLE_EXTENDED_SELECTION + GLVolumePtrs::iterator begin = m_volumes.volumes.begin() + initial_volumes_count; + GLVolumePtrs::iterator end = m_volumes.volumes.end(); + for (GLVolumePtrs::iterator it = begin; it < end; ++it) +#else std::vector::iterator begin = m_volumes.volumes.begin() + initial_volumes_count; std::vector::iterator end = m_volumes.volumes.end(); for (std::vector::iterator it = begin; it < end; ++it) +#endif // ENABLE_EXTENDED_SELECTION { GLVolume* volume = *it; delete volume; @@ -4931,9 +5713,15 @@ void GLCanvas3D::_load_gcode_travel_paths(const GCodePreviewData& preview_data, // an error occourred - restore to previous state and return if (initial_volumes_count != m_volumes.volumes.size()) { +#if ENABLE_EXTENDED_SELECTION + GLVolumePtrs::iterator begin = m_volumes.volumes.begin() + initial_volumes_count; + GLVolumePtrs::iterator end = m_volumes.volumes.end(); + for (GLVolumePtrs::iterator it = begin; it < end; ++it) +#else std::vector::iterator begin = m_volumes.volumes.begin() + initial_volumes_count; std::vector::iterator end = m_volumes.volumes.end(); for (std::vector::iterator it = begin; it < end; ++it) +#endif // ENABLE_EXTENDED_SELECTION { GLVolume* volume = *it; delete volume; @@ -5237,7 +6025,11 @@ void GLCanvas3D::_load_shells() instance_ids[i] = i; } +#if ENABLE_EXTENDED_SELECTION + m_volumes.load_object(model_obj, object_id, instance_ids, "object", m_use_VBOs && m_initialized); +#else m_volumes.load_object(model_obj, object_id, instance_ids, "object", "object", "object", m_use_VBOs && m_initialized); +#endif // ENABLE_EXTENDED_SELECTION ++object_id; } @@ -5260,10 +6052,17 @@ void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& previe unsigned int size = (unsigned int)m_gcode_preview_volume_index.first_volumes.size(); for (unsigned int i = 0; i < size; ++i) { +#if ENABLE_EXTENDED_SELECTION + GLVolumePtrs::iterator begin = m_volumes.volumes.begin() + m_gcode_preview_volume_index.first_volumes[i].id; + GLVolumePtrs::iterator end = (i + 1 < size) ? m_volumes.volumes.begin() + m_gcode_preview_volume_index.first_volumes[i + 1].id : m_volumes.volumes.end(); + + for (GLVolumePtrs::iterator it = begin; it != end; ++it) +#else std::vector::iterator begin = m_volumes.volumes.begin() + m_gcode_preview_volume_index.first_volumes[i].id; std::vector::iterator end = (i + 1 < size) ? m_volumes.volumes.begin() + m_gcode_preview_volume_index.first_volumes[i + 1].id : m_volumes.volumes.end(); for (std::vector::iterator it = begin; it != end; ++it) +#endif // ENABLE_EXTENDED_SELECTION { GLVolume* volume = *it; @@ -5352,6 +6151,54 @@ void GLCanvas3D::_show_warning_texture_if_needed() } } +#if ENABLE_EXTENDED_SELECTION +void GLCanvas3D::_on_move() +{ + if (m_model == nullptr) + return; + + std::set done; // prevent moving instances twice + bool object_moved = false; + Vec3d wipe_tower_origin = Vec3d::Zero(); + const Selection::IndicesList& selection = m_selection.get_volume_idxs(); + + for (unsigned int i : selection) + { + const GLVolume* v = m_volumes.volumes[i]; + int object_idx = v->object_idx(); + int instance_idx = v->instance_idx(); + + // prevent moving instances twice + char done_id[64]; + ::sprintf(done_id, "%d_%d", object_idx, instance_idx); + if (done.find(done_id) != done.end()) + continue; + + done.insert(done_id); + + if (object_idx < 1000) + { + // Move a regular object. + ModelObject* model_object = m_model->objects[object_idx]; + if (model_object != nullptr) + { + model_object->instances[instance_idx]->set_offset(v->get_offset()); + model_object->invalidate_bounding_box(); + object_moved = true; + } + } + else if (object_idx == 1000) + // Move a wipe tower proxy. + wipe_tower_origin = v->get_offset(); + } + + if (object_moved) + post_event(SimpleEvent(EVT_GLCANVAS_WIPETOWER_MOVED)); + + if (wipe_tower_origin != Vec3d::Zero()) + post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_MOVED, std::move(wipe_tower_origin))); +} +#else void GLCanvas3D::_on_move(const std::vector& volume_idxs) { if (m_model == nullptr) @@ -5402,9 +6249,13 @@ void GLCanvas3D::_on_move(const std::vector& volume_idxs) if (wipe_tower_origin != Vec3d::Zero()) post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_MOVED, std::move(wipe_tower_origin))); } +#endif // ENABLE_EXTENDED_SELECTION void GLCanvas3D::_on_select(int volume_idx, int object_idx) { +#if ENABLE_EXTENDED_SELECTION + post_event(ObjectSelectEvent(object_idx, -1)); +#else int vol_id = -1; int obj_id = -1; @@ -5433,6 +6284,7 @@ void GLCanvas3D::_on_select(int volume_idx, int object_idx) post_event(ObjectSelectEvent(obj_id, vol_id)); wxGetApp().obj_list()->select_current_volume(obj_id, vol_id); +#endif // !ENABLE_EXTENDED_SELECTION } std::vector GLCanvas3D::_parse_colors(const std::vector& colors) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 59893368e..d900a2621 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -346,11 +346,15 @@ class GLCanvas3D Point start_position_2D; Vec3d start_position_3D; +#if !ENABLE_EXTENDED_SELECTION Vec3d volume_center_offset; bool move_with_shift; +#endif // !ENABLE_EXTENDED_SELECTION int move_volume_idx; +#if !ENABLE_EXTENDED_SELECTION int gizmo_volume_idx; +#endif // !ENABLE_EXTENDED_SELECTION public: Drag(); @@ -372,6 +376,104 @@ class GLCanvas3D bool is_start_position_3D_defined() const; }; +#if ENABLE_EXTENDED_SELECTION +public: + class Selection + { + public: + typedef std::set IndicesList; + + enum EMode : unsigned char + { + Volume, + Instance, + Object + }; + + private: + struct VolumeCache + { + private: + Transform3d m_rotation_matrix; + + public: + Vec3d position; + Vec3d rotation; + Vec3d scaling_factor; + + VolumeCache(); + VolumeCache(const Vec3d& position, const Vec3d& rotation, const Vec3d& scaling_factor); + + const Transform3d& get_rotation_matrix() const { return m_rotation_matrix; } + }; + + typedef std::map VolumesCache; + + struct Cache + { + VolumesCache volumes_data; + Vec3d dragging_center; + }; + + GLVolumePtrs* m_volumes; + Model* m_model; + + bool m_valid; + EMode m_mode; + IndicesList m_list; + Cache m_cache; + mutable BoundingBoxf3 m_bounding_box; + mutable bool m_bounding_box_dirty; + + public: + Selection(); + + void set_volumes(GLVolumePtrs* volumes); + void set_model(Model* model); + + EMode get_mode() const { return m_mode; } + void set_mode(EMode mode) { m_mode = mode; } + + void add(unsigned int volume_idx, bool as_single_selection = true); + void remove(unsigned int volume_idx); + void clear(); + + bool is_empty() const { return m_list.empty(); } + bool is_wipe_tower() const { return m_valid && (m_list.size() == 1) && (*m_volumes)[*m_list.begin()]->is_wipe_tower; } + bool is_modifier() const { return m_valid && (m_list.size() == 1) && (*m_volumes)[*m_list.begin()]->is_modifier; } + bool is_single_full_instance(int& object_idx_out, int& instance_idx_out) const; + bool is_single_full_object(int& object_idx_out) const; + bool is_from_single_instance(int& object_idx_out, int& instance_idx_out) const; + bool is_from_single_object(int& object_idx_out) const; + + const IndicesList& get_volume_idxs() const { return m_list; } + const GLVolume* get_volume(unsigned int volume_idx) const; + + unsigned int volumes_count() const { return (unsigned int)m_list.size(); } + const BoundingBoxf3& get_bounding_box() const; + + void start_dragging(); + + void translate(const Vec3d& displacement); + + void render() const; + + private: + void update_valid(); + void set_caches(); + void add_volume(unsigned int volume_idx); + void add_instance(unsigned int volume_idx); + void add_object(unsigned int volume_idx); + void remove_volume(unsigned int volume_idx); + void remove_instance(unsigned int volume_idx); + void remove_object(unsigned int volume_idx); + void calc_bounding_box() const; + void render_bounding_box(const BoundingBoxf3& box, float* color) const; + }; + +private: +#endif // ENABLE_EXTENDED_SELECTION + class Gizmos { static const float OverlayTexturesScale; @@ -404,11 +506,20 @@ class GLCanvas3D bool is_enabled() const; void set_enabled(bool enable); +#if ENABLE_EXTENDED_SELECTION + void update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection); + void update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection); +#else void update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos); void update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos); +#endif // ENABLE_EXTENDED_SELECTION void reset_all_states(); void set_hover_id(int id); +#if ENABLE_EXTENDED_SELECTION + void enable_grabber(EType type, unsigned int id); + void disable_grabber(EType type, unsigned int id); +#endif // ENABLE_EXTENDED_SELECTION bool overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const; bool grabber_contains_mouse() const; @@ -425,8 +536,12 @@ class GLCanvas3D void start_dragging(const BoundingBoxf3& box); void stop_dragging(); +#if ENABLE_EXTENDED_SELECTION + Vec3d get_displacement() const; +#else Vec3d get_position() const; void set_position(const Vec3d& position); +#endif // ENABLE_EXTENDED_SELECTION #if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM Vec3d get_scale() const; @@ -517,6 +632,9 @@ class GLCanvas3D mutable GLToolbar m_toolbar; mutable GLVolumeCollection m_volumes; +#if ENABLE_EXTENDED_SELECTION + Selection m_selection; +#endif // ENABLE_EXTENDED_SELECTION DynamicPrintConfig* m_config; Print* m_print; Model* m_model; @@ -535,14 +653,21 @@ class GLCanvas3D bool m_shader_enabled; bool m_dynamic_background_enabled; bool m_multisample_allowed; +#if ENABLE_EXTENDED_SELECTION + bool m_regenerate_volumes; +#endif // ENABLE_EXTENDED_SELECTION std::string m_color_by; +#if !ENABLE_EXTENDED_SELECTION std::string m_select_by; std::string m_drag_by; +#endif // !ENABLE_EXTENDED_SELECTION bool m_reload_delayed; +#if !ENABLE_EXTENDED_SELECTION std::vector> m_objects_volumes_idxs; std::vector m_objects_selections; +#endif // !ENABLE_EXTENDED_SELECTION GCodePreviewVolumeIndex m_gcode_preview_volume_index; @@ -568,19 +693,27 @@ public: unsigned int get_volumes_count() const; void reset_volumes(); +#if !ENABLE_EXTENDED_SELECTION void deselect_volumes(); void select_volume(unsigned int id); void update_volumes_selection(const std::vector& selections); +#endif // !ENABLE_EXTENDED_SELECTION int check_volumes_outside_state(const DynamicPrintConfig* config) const; bool move_volume_up(unsigned int id); bool move_volume_down(unsigned int id); +#if !ENABLE_EXTENDED_SELECTION void set_objects_selections(const std::vector& selections); +#endif // !ENABLE_EXTENDED_SELECTION void set_config(DynamicPrintConfig* config); void set_print(Print* print); void set_model(Model* model); +#if ENABLE_EXTENDED_SELECTION + const Selection& get_selection() const { return m_selection; } +#endif // ENABLE_EXTENDED_SELECTION + // 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, // fills the m_bed.m_grid_lines and sets m_bed.m_origin. @@ -594,11 +727,13 @@ public: void set_cutting_plane(float z, const ExPolygons& polygons); void set_color_by(const std::string& value); +#if !ENABLE_EXTENDED_SELECTION void set_select_by(const std::string& value); void set_drag_by(const std::string& value); const std::string& get_select_by() const; const std::string& get_drag_by() const; +#endif // !ENABLE_EXTENDED_SELECTION float get_camera_zoom() const; @@ -680,7 +815,9 @@ private: void _resize(unsigned int w, unsigned int h); BoundingBoxf3 _max_bounding_box() const; +#if !ENABLE_EXTENDED_SELECTION BoundingBoxf3 _selected_volumes_bounding_box() const; +#endif // !ENABLE_EXTENDED_SELECTION void _zoom_to_bounding_box(const BoundingBoxf3& bbox); float _get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) const; @@ -694,6 +831,9 @@ private: void _render_bed(float theta) const; void _render_axes(bool depth_test) const; void _render_objects() const; +#if ENABLE_EXTENDED_SELECTION + void _render_selection() const; +#endif // ENABLE_EXTENDED_SELECTION void _render_cutting_plane() const; void _render_warning_texture() const; void _render_legend_texture() const; @@ -703,6 +843,10 @@ private: void _render_gizmos_overlay() const; void _render_toolbar() const; +#if ENABLE_EXTENDED_SELECTION + void _update_volumes_hover_state() const; +#endif // ENABLE_EXTENDED_SELECTION + float _get_layers_editing_cursor_z_relative() const; void _perform_layer_editing_action(wxMouseEvent* evt = nullptr); @@ -719,8 +863,10 @@ private: void _start_timer(); void _stop_timer(); +#if !ENABLE_EXTENDED_SELECTION int _get_first_selected_object_id() const; int _get_first_selected_volume_id(int object_id) const; +#endif // !ENABLE_EXTENDED_SELECTION // Create 3D thick extrusion lines for a skirt and brim. // Adds a new Slic3r::GUI::3DScene::Volume to volumes. @@ -750,7 +896,11 @@ private: void _update_toolpath_volumes_outside_state(); void _show_warning_texture_if_needed(); +#if ENABLE_EXTENDED_SELECTION + void _on_move(); +#else void _on_move(const std::vector& volume_idxs); +#endif // ENABLE_EXTENDED_SELECTION void _on_select(int volume_idx, int object_idx); // generates the legend texture in dependence of the current shown view type diff --git a/src/slic3r/GUI/GLCanvas3DManager.cpp b/src/slic3r/GUI/GLCanvas3DManager.cpp index 8720caba5..87909110e 100644 --- a/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -246,6 +246,7 @@ void GLCanvas3DManager::reset_volumes(wxGLCanvas* canvas) it->second->reset_volumes(); } +#if !ENABLE_EXTENDED_SELECTION void GLCanvas3DManager::deselect_volumes(wxGLCanvas* canvas) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -266,6 +267,7 @@ void GLCanvas3DManager::update_volumes_selection(wxGLCanvas* canvas, const std:: if (it != m_canvases.end()) it->second->update_volumes_selection(selections); } +#endif // !ENABLE_EXTENDED_SELECTION int GLCanvas3DManager::check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) const { @@ -285,12 +287,14 @@ bool GLCanvas3DManager::move_volume_down(wxGLCanvas* canvas, unsigned int id) return (it != m_canvases.end()) ? it->second->move_volume_down(id) : false; } +#if !ENABLE_EXTENDED_SELECTION void GLCanvas3DManager::set_objects_selections(wxGLCanvas* canvas, const std::vector& selections) { CanvasesMap::iterator it = _get_canvas(canvas); if (it != m_canvases.end()) it->second->set_objects_selections(selections); } +#endif // !ENABLE_EXTENDED_SELECTION void GLCanvas3DManager::set_config(wxGLCanvas* canvas, DynamicPrintConfig* config) { @@ -354,6 +358,7 @@ void GLCanvas3DManager::set_color_by(wxGLCanvas* canvas, const std::string& valu it->second->set_color_by(value); } +#if !ENABLE_EXTENDED_SELECTION void GLCanvas3DManager::set_select_by(wxGLCanvas* canvas, const std::string& value) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -373,6 +378,7 @@ std::string GLCanvas3DManager::get_select_by(wxGLCanvas* canvas) const CanvasesMap::const_iterator it = _get_canvas(canvas); return (it != m_canvases.end()) ? it->second->get_select_by() : ""; } +#endif // !ENABLE_EXTENDED_SELECTION bool GLCanvas3DManager::is_layers_editing_enabled(wxGLCanvas* canvas) const { diff --git a/src/slic3r/GUI/GLCanvas3DManager.hpp b/src/slic3r/GUI/GLCanvas3DManager.hpp index 960bb6d2a..ca882d4ae 100644 --- a/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -87,14 +87,18 @@ public: unsigned int get_volumes_count(wxGLCanvas* canvas) const; void reset_volumes(wxGLCanvas* canvas); +#if !ENABLE_EXTENDED_SELECTION void deselect_volumes(wxGLCanvas* canvas); void select_volume(wxGLCanvas* canvas, unsigned int id); void update_volumes_selection(wxGLCanvas* canvas, const std::vector& selections); +#endif // !ENABLE_EXTENDED_SELECTION int check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) const; bool move_volume_up(wxGLCanvas* canvas, unsigned int id); bool move_volume_down(wxGLCanvas* canvas, unsigned int id); +#if !ENABLE_EXTENDED_SELECTION void set_objects_selections(wxGLCanvas* canvas, const std::vector& selections); +#endif // !ENABLE_EXTENDED_SELECTION void set_config(wxGLCanvas* canvas, DynamicPrintConfig* config); void set_print(wxGLCanvas* canvas, Print* print); @@ -110,10 +114,12 @@ public: void set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons); void set_color_by(wxGLCanvas* canvas, const std::string& value); +#if !ENABLE_EXTENDED_SELECTION void set_select_by(wxGLCanvas* canvas, const std::string& value); void set_drag_by(wxGLCanvas* canvas, const std::string& value); std::string get_select_by(wxGLCanvas* canvas) const; +#endif // !ENABLE_EXTENDED_SELECTION bool is_layers_editing_enabled(wxGLCanvas* canvas) const; bool is_layers_editing_allowed(wxGLCanvas* canvas) const; diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index 55729ac87..72bf18de9 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -216,6 +216,9 @@ GLGizmoBase::GLGizmoBase(GLCanvas3D& parent) : m_parent(parent) , m_group_id(-1) , m_state(Off) +#if ENABLE_EXTENDED_SELECTION + , m_accept_wipe_tower(false) +#endif // ENABLE_EXTENDED_SELECTION , m_hover_id(-1) , m_dragging(false) { @@ -1050,7 +1053,11 @@ const double GLGizmoMove3D::Offset = 10.0; GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent) : GLGizmoBase(parent) +#if ENABLE_EXTENDED_SELECTION + , m_displacement(Vec3d::Zero()) +#else , m_position(Vec3d::Zero()) +#endif // ENABLE_EXTENDED_SELECTION , m_starting_drag_position(Vec3d::Zero()) , m_starting_box_center(Vec3d::Zero()) , m_starting_box_bottom_center(Vec3d::Zero()) @@ -1078,6 +1085,10 @@ bool GLGizmoMove3D::on_init() m_grabbers.push_back(Grabber()); } +#if ENABLE_EXTENDED_SELECTION + m_accept_wipe_tower = true; +#endif // ENABLE_EXTENDED_SELECTION + return true; } @@ -1085,6 +1096,9 @@ void GLGizmoMove3D::on_start_dragging(const BoundingBoxf3& box) { if (m_hover_id != -1) { +#if ENABLE_EXTENDED_SELECTION + m_displacement = Vec3d::Zero(); +#endif // ENABLE_EXTENDED_SELECTION m_starting_drag_position = m_grabbers[m_hover_id].center; m_starting_box_center = box.center(); m_starting_box_bottom_center = box.center(); @@ -1094,22 +1108,40 @@ void GLGizmoMove3D::on_start_dragging(const BoundingBoxf3& box) void GLGizmoMove3D::on_update(const Linef3& mouse_ray) { +#if ENABLE_EXTENDED_SELECTION + if (m_hover_id == 0) + m_displacement(0) = calc_projection(X, 1, mouse_ray) - (m_starting_drag_position(0) - m_starting_box_center(0)); + else if (m_hover_id == 1) + m_displacement(1) = calc_projection(Y, 2, mouse_ray) - (m_starting_drag_position(1) - m_starting_box_center(1)); + else if (m_hover_id == 2) + m_displacement(2) = calc_projection(Z, 1, mouse_ray) - (m_starting_drag_position(2) - m_starting_box_bottom_center(2)); +#else if (m_hover_id == 0) m_position(0) = 2.0 * m_starting_box_center(0) + calc_projection(X, 1, mouse_ray) - m_starting_drag_position(0); else if (m_hover_id == 1) m_position(1) = 2.0 * m_starting_box_center(1) + calc_projection(Y, 2, mouse_ray) - m_starting_drag_position(1); else if (m_hover_id == 2) m_position(2) = 2.0 * m_starting_box_bottom_center(2) + calc_projection(Z, 1, mouse_ray) - m_starting_drag_position(2); +#endif // ENABLE_EXTENDED_SELECTION } void GLGizmoMove3D::on_render(const BoundingBoxf3& box) const { +#if ENABLE_EXTENDED_SELECTION + if (m_grabbers[0].dragging) + set_tooltip("X: " + format(m_displacement(0), 2)); + else if (m_grabbers[1].dragging) + set_tooltip("Y: " + format(m_displacement(1), 2)); + else if (m_grabbers[2].dragging) + set_tooltip("Z: " + format(m_displacement(2), 2)); +#else if (m_grabbers[0].dragging) set_tooltip("X: " + format(m_position(0), 2)); else if (m_grabbers[1].dragging) set_tooltip("Y: " + format(m_position(1), 2)); else if (m_grabbers[2].dragging) set_tooltip("Z: " + format(m_position(2), 2)); +#endif // ENABLE_EXTENDED_SELECTION ::glEnable(GL_DEPTH_TEST); diff --git a/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp index ac85caa18..0ad40a8b3 100644 --- a/src/slic3r/GUI/GLGizmo.hpp +++ b/src/slic3r/GUI/GLGizmo.hpp @@ -56,6 +56,9 @@ protected: int m_group_id; EState m_state; +#if ENABLE_EXTENDED_SELECTION + bool m_accept_wipe_tower; +#endif // ENABLE_EXTENDED_SELECTION // textures are assumed to be square and all with the same size in pixels, no internal check is done GLTexture m_textures[Num_States]; int m_hover_id; @@ -77,6 +80,11 @@ public: EState get_state() const { return m_state; } void set_state(EState state) { m_state = state; on_set_state(); } +#if ENABLE_EXTENDED_SELECTION + bool get_accept_wipe_tower() { return m_accept_wipe_tower; } + void set_accept_wipe_tower(bool accept) { m_accept_wipe_tower = accept; } +#endif // ENABLE_EXTENDED_SELECTION + unsigned int get_texture_id() const { return m_textures[m_state].get_id(); } int get_textures_size() const { return m_textures[Off].get_width(); } @@ -318,7 +326,11 @@ class GLGizmoMove3D : public GLGizmoBase { static const double Offset; +#if ENABLE_EXTENDED_SELECTION + Vec3d m_displacement; +#else Vec3d m_position; +#endif // ENABLE_EXTENDED_SELECTION Vec3d m_starting_drag_position; Vec3d m_starting_box_center; Vec3d m_starting_box_bottom_center; @@ -326,8 +338,12 @@ class GLGizmoMove3D : public GLGizmoBase public: explicit GLGizmoMove3D(GLCanvas3D& parent); +#if ENABLE_EXTENDED_SELECTION + const Vec3d& get_displacement() const { return m_displacement; } +#else const Vec3d& get_position() const { return m_position; } void set_position(const Vec3d& position) { m_position = position; } +#endif // ENABLE_EXTENDED_SELECTION protected: virtual bool on_init(); diff --git a/src/slic3r/GUI/GLShader.cpp b/src/slic3r/GUI/GLShader.cpp index e2995f7c3..623e02a05 100644 --- a/src/slic3r/GUI/GLShader.cpp +++ b/src/slic3r/GUI/GLShader.cpp @@ -135,7 +135,7 @@ bool GLShader::load_from_file(const char* fragment_shader_filename, const char* return false; vs.seekg(0, vs.end); - int file_length = vs.tellg(); + int file_length = (int)vs.tellg(); vs.seekg(0, vs.beg); std::string vertex_shader(file_length, '\0'); vs.read(const_cast(vertex_shader.data()), file_length); @@ -149,7 +149,7 @@ bool GLShader::load_from_file(const char* fragment_shader_filename, const char* return false; fs.seekg(0, fs.end); - file_length = fs.tellg(); + file_length = (int)fs.tellg(); fs.seekg(0, fs.beg); std::string fragment_shader(file_length, '\0'); fs.read(const_cast(fragment_shader.data()), file_length); diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index f2d882e22..479238368 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -26,7 +26,9 @@ wxDEFINE_EVENT(EVT_GLTOOLBAR_SPLIT, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_CUT, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_SETTINGS, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_LAYERSEDITING, SimpleEvent); +#if !ENABLE_EXTENDED_SELECTION wxDEFINE_EVENT(EVT_GLTOOLBAR_SELECTBYPARTS, SimpleEvent); +#endif // !ENABLE_EXTENDED_SELECTION GLToolbarItem::Data::Data() diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index 43097c007..dc4d10f37 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -26,7 +26,9 @@ wxDECLARE_EVENT(EVT_GLTOOLBAR_SPLIT, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_CUT, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_SETTINGS, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_LAYERSEDITING, SimpleEvent); +#if !ENABLE_EXTENDED_SELECTION wxDECLARE_EVENT(EVT_GLTOOLBAR_SELECTBYPARTS, SimpleEvent); +#endif // !ENABLE_EXTENDED_SELECTION class GLToolbarItem { diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index e06f6b721..0e23585f7 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -347,53 +347,6 @@ std::string into_u8(const wxString &str) return std::string(buffer_utf8.data()); } -wxWindow* export_option_creator(wxWindow* parent) -{ - wxPanel* panel = new wxPanel(parent, -1); - wxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); - wxCheckBox* cbox = new wxCheckBox(panel, wxID_HIGHEST + 1, L("Export print config")); - cbox->SetValue(true); - sizer->AddSpacer(5); - sizer->Add(cbox, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); - panel->SetSizer(sizer); - sizer->SetSizeHints(panel); - return panel; -} - -void add_export_option(wxFileDialog* dlg, const std::string& format) -{ - if ((dlg != nullptr) && (format == "AMF") || (format == "3MF")) - { - if (dlg->SupportsExtraControl()) - dlg->SetExtraControlCreator(export_option_creator); - } -} - -int get_export_option(wxFileDialog* dlg) -{ - if (dlg != nullptr) - { - wxWindow* wnd = dlg->GetExtraControl(); - if (wnd != nullptr) - { - wxPanel* panel = dynamic_cast(wnd); - if (panel != nullptr) - { - wxWindow* child = panel->FindWindow(wxID_HIGHEST + 1); - if (child != nullptr) - { - wxCheckBox* cbox = dynamic_cast(child); - if (cbox != nullptr) - return cbox->IsChecked() ? 1 : 0; - } - } - } - } - - return 0; - -} - bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height) { const auto idx = wxDisplay::GetFromWindow(window); diff --git a/src/slic3r/GUI/GUI.hpp b/src/slic3r/GUI/GUI.hpp index 1f2107d59..3203fd8b2 100644 --- a/src/slic3r/GUI/GUI.hpp +++ b/src/slic3r/GUI/GUI.hpp @@ -102,9 +102,6 @@ std::string into_u8(const wxString &str); // Callback to trigger a configuration update timer on the Plater. static PerlCallback g_on_request_update_callback; - -void add_export_option(wxFileDialog* dlg, const std::string& format); -int get_export_option(wxFileDialog* dlg); // Returns the dimensions of the screen on which the main frame is displayed bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 50de1cb4a..3bf068b56 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -34,6 +34,21 @@ namespace Slic3r { namespace GUI { + +const wxString file_wildcards[FT_SIZE] = { + /* FT_STL */ "STL files (*.stl)|*.stl;*.STL", + /* FT_OBJ */ "OBJ files (*.obj)|*.obj;*.OBJ", + /* FT_AMF */ "AMF files (*.amf)|*.zip.amf;*.amf;*.AMF;*.xml;*.XML", + /* FT_3MF */ "3MF files (*.3mf)|*.3mf;*.3MF;", + /* FT_PRUSA */ "Prusa Control files (*.prusa)|*.prusa;*.PRUSA", + /* FT_GCODE */ "G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC", + /* FT_MODEL */ "Known files (*.stl, *.obj, *.amf, *.xml, *.3mf, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.3mf;*.3MF;*.prusa;*.PRUSA", + + /* FT_INI */ "INI files *.ini|*.ini;*.INI", + /* FT_SVG */ "SVG files *.svg|*.svg;*.SVG", +}; + + static std::string libslic3r_translate_callback(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str().data(); } IMPLEMENT_APP(GUI_App) @@ -291,7 +306,7 @@ void GUI_App::open_model(wxWindow *parent, wxArrayString& input_files) auto dialog = new wxFileDialog(parent ? parent : GetTopWindow(), _(L("Choose one or more files (STL/OBJ/AMF/3MF/PRUSA):")), app_config->get_last_dir(), "", - MODEL_WILDCARD, wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST); + file_wildcards[FT_MODEL], wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST); if (dialog->ShowModal() != wxID_OK) { dialog->Destroy(); return; diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 1e4d13004..451b89147 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -25,26 +25,24 @@ class ModelObject; namespace GUI { -// Map from an file_type name to full file wildcard name. -const std::map FILE_WILDCARDS{ - std::make_pair("known", "Known files (*.stl, *.obj, *.amf, *.xml, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.prusa;*.PRUSA"), - std::make_pair("stl", "STL files (*.stl)|*.stl;*.STL"), - std::make_pair("obj", "OBJ files (*.obj)|*.obj;*.OBJ"), - std::make_pair("amf", "AMF files (*.amf)|*.zip.amf;*.amf;*.AMF;*.xml;*.XML"), - std::make_pair("3mf", "3MF files (*.3mf)|*.3mf;*.3MF;"), - std::make_pair("prusa", "Prusa Control files (*.prusa)|*.prusa;*.PRUSA"), - std::make_pair("ini", "INI files *.ini|*.ini;*.INI"), - std::make_pair("gcode", "G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC"), - std::make_pair("svg", "SVG files *.svg|*.svg;*.SVG") + +enum FileType +{ + FT_STL, + FT_OBJ, + FT_AMF, + FT_3MF, + FT_PRUSA, + FT_GCODE, + FT_MODEL, + + FT_INI, + FT_SVG, + + FT_SIZE, }; -const std::string MODEL_WILDCARD{ FILE_WILDCARDS.at("known") + std::string("|") + - FILE_WILDCARDS.at("stl") + std::string("|") + - FILE_WILDCARDS.at("obj") + std::string("|") + - FILE_WILDCARDS.at("amf") + std::string("|") + - FILE_WILDCARDS.at("3mf") + std::string("|") + - FILE_WILDCARDS.at("prusa") }; - +extern const wxString file_wildcards[FT_SIZE]; enum ConfigMenuIDs { ConfigMenuWizard, diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 589f4ce8b..bad81a22e 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -510,7 +510,7 @@ void ObjectList::menu_item_add_generic(wxMenuItem* &menu, int id) { sub_menu->Append(new wxMenuItem(sub_menu, ++id, _(item))); #ifndef __WXMSW__ - sub_menu->Bind(wxEVT_MENU, [sub_menu](wxEvent &event) { + sub_menu->Bind(wxEVT_MENU, [this, sub_menu](wxEvent &event) { load_lambda(sub_menu->GetLabel(event.GetId()).ToStdString()); }); #endif //no __WXMSW__ @@ -632,7 +632,7 @@ wxMenu* ObjectList::create_add_settings_popupmenu(bool is_part) menu->Append(menu_item); } #ifndef __WXMSW__ - menu->Bind(wxEVT_MENU, [menu, is_part](wxEvent &event) { + menu->Bind(wxEVT_MENU, [this, menu, is_part](wxEvent &event) { get_settings_choice(menu, event.GetId(), is_part); }); #endif //no __WXMSW__ @@ -1032,7 +1032,9 @@ void ObjectList::add_object_to_list(size_t obj_idx) auto model_object = (*m_objects)[obj_idx]; wxString item_name = model_object->name; auto item = m_objects_model->Add(item_name, model_object->instances.size()); +#if !ENABLE_EXTENDED_SELECTION Select(item); +#endif // !ENABLE_EXTENDED_SELECTION // Add error icon if detected auto-repaire auto stats = model_object->volumes[0]->mesh.stl.stats; diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index c37d1a1a7..afdc60806 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -262,6 +262,51 @@ void ObjectManipulation::update_settings_list() parent->GetParent()->Layout(); } +#if ENABLE_EXTENDED_SELECTION +void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& selection) +{ + int object_idx = -1; + int instance_idx = -1; + if (selection.is_single_full_instance(object_idx, instance_idx)) + { + // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first + const GLCanvas3D::Selection::IndicesList& idxs = selection.get_volume_idxs(); + update_position_values(selection.get_volume(*idxs.begin())->get_offset()); + m_og->enable(); + } + else if (selection.is_wipe_tower()) + { + // the selection contains a single volume + const GLCanvas3D::Selection::IndicesList& idxs = selection.get_volume_idxs(); + update_position_values(selection.get_volume(*idxs.begin())->get_offset()); + m_og->enable(); + } + else if (selection.is_modifier()) + { + // the selection contains a single volume + const GLCanvas3D::Selection::IndicesList& idxs = selection.get_volume_idxs(); + update_position_values(selection.get_volume(*idxs.begin())->get_offset()); + m_og->enable(); + } + else + reset_settings_value(); +} + +void ObjectManipulation::reset_settings_value() +{ + m_og->set_value("position_x", 0); + m_og->set_value("position_y", 0); + m_og->set_value("position_z", 0); + m_og->set_value("scale_x", 0); + m_og->set_value("scale_y", 0); + m_og->set_value("scale_z", 0); + m_og->set_value("rotation_x", 0); + m_og->set_value("rotation_y", 0); + m_og->set_value("rotation_z", 0); + m_og->disable(); +} +#endif // ENABLE_EXTENDED_SELECTION + void ObjectManipulation::update_values() { int selection = ol_selection(); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index 19683362f..2a9ec004e 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -6,6 +6,9 @@ #include #include "Preset.hpp" +#if ENABLE_EXTENDED_SELECTION +#include "GLCanvas3D.hpp" +#endif // ENABLE_EXTENDED_SELECTION class wxBoxSizer; @@ -44,6 +47,11 @@ public: int ol_selection(); void update_settings_list(); +#if ENABLE_EXTENDED_SELECTION + void update_settings_value(const GLCanvas3D::Selection& selection); + void reset_settings_value(); +#endif // ENABLE_EXTENDED_SELECTION + void update_values(); // update position values displacements or "gizmos" void update_position_values(); diff --git a/src/slic3r/GUI/GUI_Utils.cpp b/src/slic3r/GUI/GUI_Utils.cpp new file mode 100644 index 000000000..b8cd60944 --- /dev/null +++ b/src/slic3r/GUI/GUI_Utils.cpp @@ -0,0 +1,54 @@ +#include "GUI_Utils.hpp" + +#include +#include +#include + + +namespace Slic3r { +namespace GUI { + + +CheckboxFileDialog::CheckboxFileDialog(wxWindow *parent, + const wxString &checkbox_label, + bool checkbox_value, + const wxString &message, + const wxString &default_dir, + const wxString &default_file, + const wxString &wildcard, + long style, + const wxPoint &pos, + const wxSize &size, + const wxString &name +) + : wxFileDialog(parent, message, default_dir, default_file, wildcard, style, pos, size, name) + , cbox(nullptr) +{ + if (checkbox_label.IsEmpty()) { + return; + } + + extra_control_creator = [this, checkbox_label](wxWindow *parent) -> wxWindow* { + wxPanel* panel = new wxPanel(parent, -1); + wxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); + this->cbox = new wxCheckBox(panel, wxID_HIGHEST + 1, checkbox_label); + this->cbox->SetValue(true); + sizer->AddSpacer(5); + sizer->Add(this->cbox, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); + panel->SetSizer(sizer); + sizer->SetSizeHints(panel); + + return panel; + }; + + SetExtraControlCreator(*extra_control_creator.target()); +} + +bool CheckboxFileDialog::get_checkbox_value() const +{ + return this->cbox != nullptr ? cbox->IsChecked() : false; +} + + +} +} diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp new file mode 100644 index 000000000..bf2ee961f --- /dev/null +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -0,0 +1,41 @@ +#ifndef slic3r_GUI_Utils_hpp_ +#define slic3r_GUI_Utils_hpp_ + +#include + +#include + +class wxCheckBox; + + +namespace Slic3r { +namespace GUI { + + +class CheckboxFileDialog : public wxFileDialog +{ +public: + CheckboxFileDialog(wxWindow *parent, + const wxString &checkbox_label, + bool checkbox_value, + const wxString &message = wxFileSelectorPromptStr, + const wxString &default_dir = wxEmptyString, + const wxString &default_file = wxEmptyString, + const wxString &wildcard = wxFileSelectorDefaultWildcardStr, + long style = wxFD_DEFAULT_STYLE, + const wxPoint &pos = wxDefaultPosition, + const wxSize &size = wxDefaultSize, + const wxString &name = wxFileDialogNameStr + ); + + bool get_checkbox_value() const; + +private: + std::function extra_control_creator; + wxCheckBox *cbox; +}; + + +}} + +#endif diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 1210d5faa..8df8e338a 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -441,7 +441,7 @@ void MainFrame::quick_slice(const int qs){ if (!(qs & qsReslice)) { auto dlg = new wxFileDialog(this, _(L("Choose a file to slice (STL/OBJ/AMF/3MF/PRUSA):")), wxGetApp().app_config->get_last_dir(), "", - MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST); + file_wildcards[FT_MODEL], wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (dlg->ShowModal() != wxID_OK) { dlg->Destroy(); return; @@ -500,7 +500,7 @@ void MainFrame::quick_slice(const int qs){ // output_file = ~s / \.[gG][cC][oO][dD][eE]$ / .svg /; auto dlg = new wxFileDialog(this, _(L("Save ")) + (qs & qsExportSVG ? _(L("SVG")) : _(L("G-code"))) + _(L(" file as:")), wxGetApp().app_config->get_last_output_dir(get_dir_name(output_file)), get_base_name(input_file), - qs & qsExportSVG ? FILE_WILDCARDS.at("svg") : FILE_WILDCARDS.at("gcode"), + qs & qsExportSVG ? file_wildcards[FT_SVG] : file_wildcards[FT_GCODE], wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if (dlg->ShowModal() != wxID_OK) { dlg->Destroy(); @@ -568,7 +568,7 @@ void MainFrame::repair_stl() { auto dlg = new wxFileDialog(this, _(L("Select the STL file to repair:")), wxGetApp().app_config->get_last_dir(), "", - FILE_WILDCARDS.at("stl"), wxFD_OPEN | wxFD_FILE_MUST_EXIST); + file_wildcards[FT_STL], wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (dlg->ShowModal() != wxID_OK) { dlg->Destroy(); return; @@ -582,7 +582,7 @@ void MainFrame::repair_stl() // output_file = ~s / \.[sS][tT][lL]$ / _fixed.obj / ; auto dlg = new wxFileDialog( this, L("Save OBJ file (less prone to coordinate errors than STL) as:"), get_dir_name(output_file), get_base_name(output_file), - FILE_WILDCARDS.at("obj"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + file_wildcards[FT_OBJ], wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if (dlg->ShowModal() != wxID_OK) { dlg->Destroy(); return /*undef*/; @@ -612,7 +612,7 @@ void MainFrame::export_config() auto dlg = new wxFileDialog(this, _(L("Save configuration as:")), !m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(), !m_last_config.IsEmpty() ? get_base_name(m_last_config) : "config.ini", - FILE_WILDCARDS.at("ini"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + file_wildcards[FT_INI], wxFD_SAVE | wxFD_OVERWRITE_PROMPT); wxString file; if (dlg->ShowModal() == wxID_OK) file = dlg->GetPath(); @@ -664,7 +664,7 @@ void MainFrame::export_configbundle() auto dlg = new wxFileDialog(this, _(L("Save presets bundle as:")), !m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(), "Slic3r_config_bundle.ini", - FILE_WILDCARDS.at("ini"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + file_wildcards[FT_INI], wxFD_SAVE | wxFD_OVERWRITE_PROMPT); wxString file; if (dlg->ShowModal() == wxID_OK) file = dlg->GetPath(); @@ -688,7 +688,7 @@ void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool re if (file.IsEmpty()) { auto dlg = new wxFileDialog(this, _(L("Select configuration to load:")), !m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(), - "config.ini", FILE_WILDCARDS.at("ini"), wxFD_OPEN | wxFD_FILE_MUST_EXIST); + "config.ini", file_wildcards[FT_INI], wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (dlg->ShowModal() != wxID_OK) return; file = dlg->GetPath(); @@ -755,7 +755,7 @@ void MainFrame::on_presets_changed(SimpleEvent &event) // Update preset combo boxes(Print settings, Filament, Material, Printer) from their respective tabs. auto presets = tab->get_presets(); - if (presets) { + if (m_plater != nullptr && presets != nullptr) { auto reload_dependent_tabs = tab->get_dependent_tabs(); // FIXME: The preset type really should be a property of Tab instead diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 2a185205e..23e26a73f 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -25,10 +26,14 @@ #include "libslic3r/GCode/PreviewData.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/Polygon.hpp" +#include "libslic3r/Format/STL.hpp" +#include "libslic3r/Format/AMF.hpp" +#include "libslic3r/Format/3mf.hpp" #include "GUI.hpp" #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" #include "GUI_ObjectManipulation.hpp" +#include "GUI_Utils.hpp" #include "MainFrame.hpp" #include "3DScene.hpp" #include "GLCanvas3D.hpp" @@ -43,6 +48,7 @@ #include // Needs to be last because reasons :-/ #include "WipeTowerDialog.hpp" +using boost::optional; namespace fs = boost::filesystem; using Slic3r::_3DScene; using Slic3r::Preset; @@ -340,7 +346,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) : struct Sidebar::priv { - // Sidebar *q; // PIMPL back pointer ("Q-Pointer") + Plater *plater; wxScrolledWindow *scrolled; @@ -363,6 +369,8 @@ struct Sidebar::priv // wxButton *btn_print; // XXX: remove wxButton *btn_send_gcode; + priv(Plater *plater) : plater(plater) {} + bool show_manifold_warning_icon = false; bool show_print_info = false; }; @@ -370,8 +378,8 @@ struct Sidebar::priv // Sidebar / public -Sidebar::Sidebar(wxWindow *parent) - : wxPanel(parent), p(new priv) +Sidebar::Sidebar(Plater *parent) + : wxPanel(parent), p(new priv(parent)) { p->scrolled = new wxScrolledWindow(this); @@ -460,6 +468,11 @@ Sidebar::Sidebar(wxWindow *parent) sizer->Add(p->scrolled, 1, wxEXPAND | wxTOP, 5); sizer->Add(btns_sizer, 0, wxEXPAND | wxLEFT, 20); SetSizer(sizer); + + // Events + p->btn_export_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(); }); + p->btn_reslice->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->reslice(); }); + p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->send_gcode(); }); } Sidebar::~Sidebar() {} @@ -558,6 +571,13 @@ void Sidebar::show_buttons(const bool show) } } +void Sidebar::enable_buttons(bool enable) +{ + p->btn_reslice->Enable(enable); + p->btn_export_gcode->Enable(enable); + p->btn_send_gcode->Enable(enable); +} + // Plater::Object struct PlaterObject @@ -579,13 +599,28 @@ public: private: Plater *plater; + + static const std::regex pattern_drop; }; +const std::regex PlaterDropTarget::pattern_drop("[.](stl|obj|amf|3mf|prusa)$", std::regex::icase); + bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &filenames) { - // TODO - // return false; - throw 0; + std::vector paths; + + for (const auto &filename : filenames) { + fs::path path(filename); + + if (std::regex_match(path.string(), pattern_drop)) { + paths.push_back(std::move(path)); + } else { + return false; + } + } + + plater->load_files(paths); + return true; } // Plater / private @@ -603,8 +638,8 @@ struct Plater::priv Slic3r::GCodePreviewData gcode_preview_data; std::vector objects; - std::string export_gcode_output_file; - std::string send_gcode_file; + fs::path export_gcode_output_file; + fs::path send_gcode_file; // GUI elements wxNotebook *notebook; @@ -613,14 +648,15 @@ struct Plater::priv Preview *preview; BackgroundSlicingProcess background_process; - static const int gl_attrs[]; static const std::regex pattern_bundle; static const std::regex pattern_3mf; static const std::regex pattern_zip_amf; priv(Plater *q, MainFrame *main_frame); +#if !ENABLE_EXTENDED_SELECTION std::vector collect_selections(); +#endif // !ENABLE_EXTENDED_SELECTION void update(bool force_autocenter = false); void update_ui_from_settings(); ProgressStatusBar* statusbar(); @@ -629,19 +665,47 @@ struct Plater::priv BoundingBox scaled_bed_shape_bb() const; std::vector load_files(const std::vector &input_files); std::vector load_model_objects(const ModelObjectPtrs &model_objects); + std::unique_ptr get_export_file(GUI::FileType file_type); - void on_notebook_changed(wxBookCtrlEvent &); - void on_select_preset(wxCommandEvent &); - void on_update_print_preview(wxCommandEvent &); - void on_process_completed(wxCommandEvent &); + void select_object(optional obj_idx); + optional selected_object() const; + void selection_changed(); + void object_list_changed(); + + void remove(size_t obj_idx); + void reset(); + void increase(size_t num = 1); + void decrease(size_t num = 1); + + void on_notebook_changed(wxBookCtrlEvent&); + void on_select_preset(wxCommandEvent&); + void on_update_print_preview(wxCommandEvent&); + void on_process_completed(wxCommandEvent&); void on_layer_editing_toggled(bool enable); - void on_action_add(SimpleEvent&); - void on_viewport_changed(SimpleEvent& evt); + + void on_action_add(SimpleEvent&); + void on_action_arrange(SimpleEvent&); + void on_action_more(SimpleEvent&); + void on_action_fewer(SimpleEvent&); + void on_action_split(SimpleEvent&); + void on_action_cut(SimpleEvent&); + void on_action_settings(SimpleEvent&); + void on_action_layersediting(SimpleEvent&); + void on_action_selectbyparts(SimpleEvent&); + + void on_viewport_changed(SimpleEvent&); + void on_right_click(Vec2dEvent&); + void on_model_update(SimpleEvent&); + void on_remove_object(SimpleEvent&); + void on_arrange(SimpleEvent&); + void on_scale_uniformly(SimpleEvent&); + void on_instance_moves(SimpleEvent&); + void on_wipetower_moved(Vec3dEvent&); + void on_enable_action_buttons(Event&); + void on_update_geometry(Vec3dsEvent<2>&); }; -// TODO: multisample, see 3DScene.pm -const int Plater::priv::gl_attrs[] = {WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 24, 0}; const std::regex Plater::priv::pattern_bundle("[.](amf|amf[.]xml|zip[.]amf|3mf|prusa)$", std::regex::icase); const std::regex Plater::priv::pattern_3mf("[.]3mf$", std::regex::icase); const std::regex Plater::priv::pattern_zip_amf("[.]zip[.]amf$", std::regex::icase); @@ -674,8 +738,10 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) : _3DScene::enable_picking(canvas3D, true); _3DScene::enable_moving(canvas3D, true); // XXX: more config from 3D.pm +#if !ENABLE_EXTENDED_SELECTION _3DScene::set_select_by(canvas3D, "object"); _3DScene::set_drag_by(canvas3D, "instance"); +#endif // !ENABLE_EXTENDED_SELECTION _3DScene::set_model(canvas3D, &model); _3DScene::set_print(canvas3D, &print); _3DScene::set_config(canvas3D, config); @@ -714,18 +780,35 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) : // Preset change event sidebar->Bind(wxEVT_COMBOBOX, &priv::on_select_preset, this); - // Sidebar button events - sidebar->p->btn_export_gcode->Bind(wxEVT_BUTTON, [q](wxCommandEvent&) { q->export_gcode(""); }); - sidebar->p->btn_reslice->Bind(wxEVT_BUTTON, [q](wxCommandEvent&) { q->reslice(); }); - sidebar->p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { - this->send_gcode_file = this->q->export_gcode(""); - }); - // 3DScene events: - // TODO: more - canvas3D->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this); + canvas3D->Bind(EVT_GLCANVAS_OBJECT_SELECT, [](ObjectSelectEvent&) { /*TODO*/ }); canvas3D->Bind(EVT_GLCANVAS_VIEWPORT_CHANGED, &priv::on_viewport_changed, this); + // canvas3D->Bind(EVT_GLCANVAS_DOUBLE_CLICK, [](SimpleEvent&) { }); // XXX: remove? + canvas3D->Bind(EVT_GLCANVAS_RIGHT_CLICK, &priv::on_right_click, this); + canvas3D->Bind(EVT_GLCANVAS_MODEL_UPDATE, &priv::on_model_update, this); + canvas3D->Bind(EVT_GLCANVAS_REMOVE_OBJECT, &priv::on_remove_object, this); + canvas3D->Bind(EVT_GLCANVAS_ARRANGE, &priv::on_arrange, this); + canvas3D->Bind(EVT_GLCANVAS_ROTATE_OBJECT, [this](Event &evt) { /*TODO: call rotate */ }); + canvas3D->Bind(EVT_GLCANVAS_SCALE_UNIFORMLY, &priv::on_scale_uniformly, this); + canvas3D->Bind(EVT_GLCANVAS_INCREASE_OBJECTS, [this](Event &evt) { evt.data == 1 ? increase() : decrease(); }); + canvas3D->Bind(EVT_GLCANVAS_INSTANCE_MOVES, &priv::on_instance_moves, this); + canvas3D->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this); + canvas3D->Bind(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, &priv::on_enable_action_buttons, this); + canvas3D->Bind(EVT_GLCANVAS_UPDATE_GEOMETRY, &priv::on_update_geometry, this); + // 3DScene/Toolbar: + canvas3D->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this); + canvas3D->Bind(EVT_GLTOOLBAR_DELETE, [q](SimpleEvent&) { q->remove_selected(); } ); + canvas3D->Bind(EVT_GLTOOLBAR_DELETE_ALL, [this](SimpleEvent&) { reset(); }); + canvas3D->Bind(EVT_GLTOOLBAR_ARRANGE, &priv::on_action_arrange, this); + canvas3D->Bind(EVT_GLTOOLBAR_MORE, &priv::on_action_more, this); + canvas3D->Bind(EVT_GLTOOLBAR_FEWER, &priv::on_action_fewer, this); + canvas3D->Bind(EVT_GLTOOLBAR_SPLIT, &priv::on_action_split, this); + canvas3D->Bind(EVT_GLTOOLBAR_CUT, &priv::on_action_cut, this); + canvas3D->Bind(EVT_GLTOOLBAR_SETTINGS, &priv::on_action_settings, this); + canvas3D->Bind(EVT_GLTOOLBAR_LAYERSEDITING, &priv::on_action_layersediting, this); + canvas3D->Bind(EVT_GLTOOLBAR_SELECTBYPARTS, &priv::on_action_selectbyparts, this); + // Preview events: preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_VIEWPORT_CHANGED, &priv::on_viewport_changed, this); q->Bind(EVT_SLICING_COMPLETED, &priv::on_update_print_preview, this); @@ -738,6 +821,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) : q->Layout(); } +#if !ENABLE_EXTENDED_SELECTION std::vector Plater::priv::collect_selections() { std::vector res; @@ -746,6 +830,7 @@ std::vector Plater::priv::collect_selections() } return res; } +#endif // !ENABLE_EXTENDED_SELECTION void Plater::priv::update(bool force_autocenter) { @@ -761,8 +846,10 @@ void Plater::priv::update(bool force_autocenter) // stop_background_process(); // TODO print.reload_model_instances(); +#if !ENABLE_EXTENDED_SELECTION const auto selections = collect_selections(); _3DScene::set_objects_selections(canvas3D, selections); +#endif // !ENABLE_EXTENDED_SELECTION _3DScene::reload_scene(canvas3D, false); preview->reset_gcode_preview_data(); preview->reload_print(); @@ -805,7 +892,7 @@ BoundingBox Plater::priv::scaled_bed_shape_bb() const std::vector Plater::priv::load_files(const std::vector &input_files) { - if (input_files.size() < 1) { return std::vector(); } + if (input_files.empty()) { return std::vector(); } auto *nozzle_dmrs = config->opt("nozzle_diameter"); @@ -878,9 +965,6 @@ std::vector Plater::priv::load_files(const std::vector &input_ } if (one_by_one) { - // TODO: - // push @obj_idx, $self->load_model_objects(@{$model->objects}); - // obj_idx.push_back(load_model_objects(model.objects); auto loaded_idxs = load_model_objects(model.objects); obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end()); } else { @@ -901,9 +985,6 @@ std::vector Plater::priv::load_files(const std::vector &input_ new_model->convert_multipart_object(nozzle_dmrs->values.size()); } - // TODO: - // push @obj_idx, $self->load_model_objects(@{$new_model->objects}); - // obj_idx.push_back(load_model_objects(new_model->objects); auto loaded_idxs = load_model_objects(model.objects); obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end()); } @@ -914,18 +995,11 @@ std::vector Plater::priv::load_files(const std::vector &input_ return obj_idxs; } - -// TODO: move to Point.hpp -Vec3d to_3d(const Vec2d &v, double z) { return Vec3d(v(0), v(1), z); } -Vec3f to_3d(const Vec2f &v, float z) { return Vec3f(v(0), v(1), z); } -Vec3i64 to_3d(const Vec2i64 &v, float z) { return Vec3i64(v(0), v(1), z); } -Vec3crd to_3d(const Point &p, coord_t z) { return Vec3crd(p(0), p(1), z); } - -std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &model_objects) +std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &model_objects) { const BoundingBoxf bed_shape = bed_shape_bb(); - const Vec3d bed_center = to_3d(bed_shape.center().cast(), 0.0); - const Vec3d bed_size = to_3d(bed_shape.size().cast(), 1.0); + const Vec3d bed_center = Slic3r::to_3d(bed_shape.center().cast(), 0.0); + const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast(), 1.0); bool need_arrange = false; bool scaled_down = false; @@ -937,7 +1011,7 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mod objects.emplace_back(std::move(object_name)); obj_idxs.push_back(objects.size() - 1); - if (model_object->instances.size() == 0) { + if (model_object->instances.empty()) { // if object has no defined position(s) we need to rearrange everything after loading need_arrange = true; @@ -993,12 +1067,229 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mod return obj_idxs; } +std::unique_ptr Plater::priv::get_export_file(GUI::FileType file_type) +{ + wxString wildcard; + switch (file_type) { + case FT_STL: + case FT_AMF: + case FT_3MF: + wildcard = file_wildcards[FT_STL]; + break; + + default: + wildcard = file_wildcards[FT_MODEL]; + break; + } + + fs::path output_file(print.output_filepath(std::string())); + + switch (file_type) { + case FT_STL: output_file.replace_extension("stl"); break; + case FT_AMF: output_file.replace_extension("zip.amf"); break; // XXX: Problem on OS X with double extension? + case FT_3MF: output_file.replace_extension("3mf"); break; + default: break; + } + + wxGetApp().preset_bundle->export_selections(print.placeholder_parser()); + + auto dlg = Slic3r::make_unique(q, + _(L("Export print config")), + true, + _(L("Save file as:")), + output_file.parent_path().string(), + output_file.filename().string(), + wildcard, + wxFD_SAVE | wxFD_OVERWRITE_PROMPT + ); + + if (dlg->ShowModal() != wxID_OK) { + return nullptr; + } + + fs::path path(dlg->GetPath()); + wxGetApp().app_config->update_last_output_dir(path.parent_path().string()); + export_gcode_output_file = path; + + return dlg; +} + +void Plater::priv::select_object(optional obj_idx) +{ + for (auto &obj : objects) { + obj.selected = false; + } + + if (obj_idx) { + objects[*obj_idx].selected = true; + } + + selection_changed(); +} + +optional Plater::priv::selected_object() const +{ + for (size_t i = 0; i < objects.size(); i++) { + if (objects[i].selected) { return i; } + } + + return boost::none; +} + +void Plater::priv::selection_changed() +{ + // TODO + + const auto obj_idx = selected_object(); + const bool have_sel = !!obj_idx; + const bool layers_height_allowed = config->opt("variable_layer_height")->value; + + wxWindowUpdateLocker freeze_guard(sidebar); + + _3DScene::enable_toolbar_item(canvas3D, "delete", have_sel); + _3DScene::enable_toolbar_item(canvas3D, "more", have_sel); + _3DScene::enable_toolbar_item(canvas3D, "fewer", have_sel); + _3DScene::enable_toolbar_item(canvas3D, "split", have_sel); + _3DScene::enable_toolbar_item(canvas3D, "cut", have_sel); + _3DScene::enable_toolbar_item(canvas3D, "settings", have_sel); + + _3DScene::enable_toolbar_item(canvas3D, "layersediting", layers_height_allowed); + + bool can_select_by_parts = false; + + if (have_sel) { + const auto *model_object = model.objects[*obj_idx]; + // XXX: ? + can_select_by_parts = *obj_idx < 1000 && model_object->volumes.size() > 1; + _3DScene::enable_toolbar_item(canvas3D, "fewer", model_object->instances.size() > 1); + } + + if (can_select_by_parts) { + // first disable to let the item in the toolbar to switch to the unpressed state // XXX: ? + _3DScene::enable_toolbar_item(canvas3D, "selectbyparts", false); + _3DScene::enable_toolbar_item(canvas3D, "selectbyparts", true); + } else { + _3DScene::enable_toolbar_item(canvas3D, "selectbyparts", false); + _3DScene::set_select_by(canvas3D, "object"); + } + + if (have_sel) { + const auto *model_object = model.objects[*obj_idx]; + // FIXME print_info runs model fixing in two rounds, it is very slow, it should not be performed here! + // # $model_object->print_info; + + // my $model_instance = $model_object->instances->[0]; + const auto *model_instance = model_object->instances[0]; + // TODO + // $self->{object_info_size}->SetLabel(sprintf("%.2f x %.2f x %.2f", @{$model_object->instance_bounding_box(0)->size})); + // $self->{object_info_materials}->SetLabel($model_object->materials_count); + + // if (my $stats = $model_object->mesh_stats) { + // $self->{object_info_volume}->SetLabel(sprintf('%.2f', $stats->{volume} * ($model_instance->scaling_factor**3))); + // $self->{object_info_facets}->SetLabel(sprintf(L('%d (%d shells)'), $model_object->facets_count, $stats->{number_of_parts})); + // if (my $errors = sum(@$stats{qw(degenerate_facets edges_fixed facets_removed facets_added facets_reversed backwards_edges)})) { + // $self->{object_info_manifold}->SetLabel(sprintf(L("Auto-repaired (%d errors)"), $errors)); + // #$self->{object_info_manifold_warning_icon}->Show; + // $self->{"object_info_manifold_warning_icon_show"}->(1); + + // # we don't show normals_fixed because we never provide normals + // # to admesh, so it generates normals for all facets + // my $message = sprintf L('%d degenerate facets, %d edges fixed, %d facets removed, %d facets added, %d facets reversed, %d backwards edges'), + // @$stats{qw(degenerate_facets edges_fixed facets_removed facets_added facets_reversed backwards_edges)}; + // $self->{object_info_manifold}->SetToolTipString($message); + // $self->{object_info_manifold_warning_icon}->SetToolTipString($message); + // } else { + // $self->{object_info_manifold}->SetLabel(L("Yes")); + // #$self->{object_info_manifold_warning_icon}->Hide; + // $self->{"object_info_manifold_warning_icon_show"}->(0); + // $self->{object_info_manifold}->SetToolTipString(""); + // $self->{object_info_manifold_warning_icon}->SetToolTipString(""); + // } + // } else { + // $self->{object_info_facets}->SetLabel($object->facets); + // } + } else { + // $self->{"object_info_$_"}->SetLabel("") for qw(size volume facets materials manifold); + // $self->{"object_info_manifold_warning_icon_show"}->(0); + // $self->{object_info_manifold}->SetToolTipString(""); + // $self->{object_info_manifold_warning_icon}->SetToolTipString(""); + } + + q->Layout(); +} + +void Plater::priv::object_list_changed() +{ + // Enable/disable buttons depending on whether there are any objects on the platter. + const bool have_objects = !objects.empty(); + + _3DScene::enable_toolbar_item(canvas3D, "deleteall", have_objects); + _3DScene::enable_toolbar_item(canvas3D, "arrange", have_objects); + + const bool export_in_progress = !(export_gcode_output_file.empty() && send_gcode_file.empty()); + // XXX: is this right? + const bool model_fits = _3DScene::check_volumes_outside_state(canvas3D, config) == ModelInstance::PVS_Inside; + + sidebar->enable_buttons(have_objects && !export_in_progress && model_fits); +} + +void Plater::priv::remove(size_t obj_idx) +{ + // $self->stop_background_process; // TODO + + // Prevent toolpaths preview from rendering while we modify the Print object + preview->set_enabled(false); + + objects.erase(objects.begin() + obj_idx); + model.delete_object(obj_idx); + print.delete_object(obj_idx); + // Delete object from Sidebar list + sidebar->obj_list()->delete_object_from_list(); + + object_list_changed(); + + select_object(boost::none); + update(); +} + +void Plater::priv::reset() +{ + // $self->stop_background_process; // TODO + + // Prevent toolpaths preview from rendering while we modify the Print object + preview->set_enabled(false); + + objects.clear(); + model.clear_objects(); + print.clear_objects(); + + // Delete all objects from list on c++ side + sidebar->obj_list()->delete_all_objects_from_list(); + object_list_changed(); + + select_object(boost::none); + update(); +} + +void Plater::priv::increase(size_t num) +{ + // TODO +} + +void Plater::priv::decrease(size_t num) +{ + // TODO +} + + void Plater::priv::on_notebook_changed(wxBookCtrlEvent&) { const auto current_id = notebook->GetCurrentPage()->GetId(); if (current_id == canvas3D->GetId()) { if (_3DScene::is_reload_delayed(canvas3D)) { +#if !ENABLE_EXTENDED_SELECTION _3DScene::set_objects_selections(canvas3D, collect_selections()); +#endif // !ENABLE_EXTENDED_SELECTION _3DScene::reload_scene(canvas3D, true); } // sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) @@ -1071,6 +1362,47 @@ void Plater::priv::on_action_add(SimpleEvent&) load_files(input_paths); } +void Plater::priv::on_action_arrange(SimpleEvent&) +{ + // TODO +} + +void Plater::priv::on_action_more(SimpleEvent&) +{ + // TODO +} + +void Plater::priv::on_action_fewer(SimpleEvent&) +{ + // TODO +} + +void Plater::priv::on_action_split(SimpleEvent&) +{ + // TODO +} + +void Plater::priv::on_action_cut(SimpleEvent&) +{ + // TODO +} + +void Plater::priv::on_action_settings(SimpleEvent&) +{ + // TODO +} + +void Plater::priv::on_action_layersediting(SimpleEvent&) +{ + // TODO +} + +void Plater::priv::on_action_selectbyparts(SimpleEvent&) +{ + // TODO +} + + void Plater::priv::on_viewport_changed(SimpleEvent& evt) { wxObject* o = evt.GetEventObject(); @@ -1080,6 +1412,53 @@ void Plater::priv::on_viewport_changed(SimpleEvent& evt) preview->set_viewport_from_scene(canvas3D); } +void Plater::priv::on_right_click(Vec2dEvent&) +{ + // TODO +} + +void Plater::priv::on_model_update(SimpleEvent&) +{ + // TODO +} + +void Plater::priv::on_remove_object(SimpleEvent&) +{ + // TODO +} + +void Plater::priv::on_arrange(SimpleEvent&) +{ + // TODO +} + +void Plater::priv::on_scale_uniformly(SimpleEvent&) +{ + // TODO +} + +void Plater::priv::on_instance_moves(SimpleEvent&) +{ + // TODO +} + +void Plater::priv::on_wipetower_moved(Vec3dEvent&) +{ + // TODO +} + +void Plater::priv::on_enable_action_buttons(Event&) +{ + // TODO +} + +void Plater::priv::on_update_geometry(Vec3dsEvent<2>&) +{ + // TODO +} + + + // Plater / Public Plater::Plater(wxWindow *parent, MainFrame *main_frame) @@ -1096,64 +1475,136 @@ Plater::~Plater() Sidebar& Plater::sidebar() { return *p->sidebar; } Model& Plater::model() { return p->model; } -std::string Plater::export_gcode(const std::string &output_path) +void Plater::update(bool force_autocenter) { p->update(force_autocenter); } +void Plater::remove(size_t obj_idx) { p->remove(obj_idx); } + +void Plater::remove_selected() { - if (p->objects.size() == 0) { return ""; } + const auto selected = p->selected_object(); + if (selected) { + remove(*selected); + } +} + +void Plater::load_files(const std::vector &input_files) { p->load_files(input_files); } + +fs::path Plater::export_gcode(const fs::path &output_path) +{ + if (p->objects.empty()) { return ""; } if (! p->export_gcode_output_file.empty()) { GUI::show_error(this, _(L("Another export job is currently running."))); return ""; } - // wxTheApp->{preset_bundle}->full_config->validate; // FIXME - const std::string err = p->print.validate(); + std::string err = wxGetApp().preset_bundle->full_config().validate(); + if (err.empty()) { + err = p->print.validate(); + } if (! err.empty()) { // The config is not valid GUI::show_error(this, _(err)); - return ""; + return fs::path(); } // Copy the names of active presets into the placeholder parser. - // wxTheApp->{preset_bundle}->export_selections_pp($self->{print}->placeholder_parser); // FIXME + wxGetApp().preset_bundle->export_selections(p->print.placeholder_parser()); // select output file if (! output_path.empty()) { - p->export_gcode_output_file = p->print.output_filepath(output_path); + p->export_gcode_output_file = fs::path(p->print.output_filepath(output_path.string())); // FIXME: ^ errors to handle? } else { - // FIXME: - std::string default_output_file; // FIXME: tmp - // my $default_output_file = eval { $self->{print}->output_filepath($main::opt{output} // '') }; - // Slic3r::GUI::catch_error($self) and return; + + // XXX: take output path from CLI opts? Ancient Slic3r versions used to do that... // If possible, remove accents from accented latin characters. // This function is useful for generating file names to be processed by legacy firmwares. - default_output_file = Slic3r::fold_utf8_to_ascii(default_output_file); + auto default_output_file = fs::path(Slic3r::fold_utf8_to_ascii( + p->print.output_filepath(output_path.string()) + // FIXME: ^ errors to handle? + )); + auto start_dir = wxGetApp().app_config->get_last_output_dir(default_output_file.parent_path().string()); wxFileDialog dlg(this, _(L("Save G-code file as:")), - wxEmptyString, - wxEmptyString, - Slic3r::GUI::FILE_WILDCARDS.at("gcode"), + start_dir, + default_output_file.filename().string(), + GUI::file_wildcards[FT_GCODE], wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); - // FIXME: ^ defaultDir: - // wxTheApp->{app_config}->get_last_output_dir(dirname($default_output_file)), - // FIXME: ^ defaultFile: - // basename($default_output_file), &Slic3r::GUI::FILE_WILDCARDS->{gcode}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if (dlg.ShowModal() != wxID_OK) { return ""; } - auto path = dlg.GetPath(); - // wxTheApp->{app_config}->update_last_output_dir(dirname($path)); // FIXME + fs::path path(dlg.GetPath()); + wxGetApp().app_config->update_last_output_dir(path.parent_path().string()); p->export_gcode_output_file = path; } return p->export_gcode_output_file; } +void Plater::export_stl() +{ + if (p->objects.empty()) { return; } + + auto dialog = p->get_export_file(FT_STL); + if (! dialog) { return; } + + // Store a binary STL + wxString path = dialog->GetPath(); + auto path_cstr = path.c_str(); + auto mesh = p->model.mesh(); + Slic3r::store_stl(path_cstr, &mesh, true); + p->statusbar()->set_status_text(wxString::Format(_(L("STL file exported to %s")), path)); +} + +void Plater::export_amf() +{ + if (p->objects.empty()) { return; } + + auto dialog = p->get_export_file(FT_AMF); + if (! dialog) { return; } + + wxString path = dialog->GetPath(); + auto path_cstr = path.c_str(); + + if (Slic3r::store_amf(path_cstr, &p->model, &p->print, dialog->get_checkbox_value())) { + // Success + p->statusbar()->set_status_text(wxString::Format(_(L("AMF file exported to %s")), path)); + } else { + // Failure + p->statusbar()->set_status_text(wxString::Format(_(L("Error exporting AMF file %s")), path)); + } +} + +void Plater::export_3mf() +{ + if (p->objects.empty()) { return; } + + auto dialog = p->get_export_file(FT_3MF); + if (! dialog) { return; } + + wxString path = dialog->GetPath(); + auto path_cstr = path.c_str(); + + if (Slic3r::store_3mf(path_cstr, &p->model, &p->print, dialog->get_checkbox_value())) { + // Success + p->statusbar()->set_status_text(wxString::Format(_(L("3MF file exported to %s")), path)); + } else { + // Failure + p->statusbar()->set_status_text(wxString::Format(_(L("Error exporting 3MF file %s")), path)); + } +} + + void Plater::reslice() { // TODO } +void Plater::send_gcode() +{ + p->send_gcode_file = export_gcode(); +} + void Plater::changed_object_settings(int obj_idx) { if (obj_idx < 0) @@ -1163,7 +1614,6 @@ void Plater::changed_object_settings(int obj_idx) if (list == nullptr) return; - if (list->is_parts_changed()) { // recenter and re - align to Z = 0 auto model_object = p->model.objects[list->get_sel_obj_id()]; diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index c05b454ff..0592579db 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -2,11 +2,16 @@ #define slic3r_Plater_hpp_ #include +#include +#include #include #include "Preset.hpp" +class wxButton; + + namespace Slic3r { class Model; @@ -20,10 +25,12 @@ class ObjectList; using t_optgroups = std::vector >; +class Plater; + class Sidebar : public wxPanel { public: - Sidebar(wxWindow *parent); + Sidebar(Plater *parent); Sidebar(Sidebar &&) = delete; Sidebar(const Sidebar &) = delete; Sidebar &operator=(Sidebar &&) = delete; @@ -41,12 +48,11 @@ public: int get_ol_selection(); void show_info_sizers(const bool show); void show_buttons(const bool show); + void enable_buttons(bool enable); private: struct priv; std::unique_ptr p; - - friend class Plater; // XXX: better encapsulation? }; @@ -63,11 +69,20 @@ public: Sidebar& sidebar(); Model& model(); - // TODO: use fs::path - // Note: empty string means request default path - std::string export_gcode(const std::string &output_path); + void update(bool force_autocenter = false); + void remove(size_t obj_idx); + void remove_selected(); + + void load_files(const std::vector &input_files); + + // Note: empty path means "use the default" + boost::filesystem::path export_gcode(const boost::filesystem::path &output_path = boost::filesystem::path()); + void export_stl(); + void export_amf(); + void export_3mf(); void reslice(); void changed_object_settings(int obj_idx); + void send_gcode(); private: struct priv; std::unique_ptr p;