mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	Merge remote-tracking branch 'origin/dev_native' into tm_sla_supports
This commit is contained in:
		
						commit
						97b3d94760
					
				
					 55 changed files with 1903 additions and 808 deletions
				
			
		|  | @ -193,6 +193,7 @@ const float GLVolume::SELECTED_COLOR[4] = { 0.0f, 1.0f, 0.0f, 1.0f }; | |||
| const float GLVolume::HOVER_COLOR[4] = { 0.4f, 0.9f, 0.1f, 1.0f }; | ||||
| const float GLVolume::OUTSIDE_COLOR[4] = { 0.0f, 0.38f, 0.8f, 1.0f }; | ||||
| const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 0.19f, 0.58f, 1.0f, 1.0f }; | ||||
| const float GLVolume::DISABLED_COLOR[4] = { 0.25f, 0.25f, 0.25f, 1.0f }; | ||||
| 
 | ||||
| GLVolume::GLVolume(float r, float g, float b, float a) | ||||
| #if ENABLE_MODELVOLUME_TRANSFORM | ||||
|  | @ -211,6 +212,7 @@ GLVolume::GLVolume(float r, float g, float b, float a) | |||
|     , composite_id(-1) | ||||
|     , extruder_id(0) | ||||
|     , selected(false) | ||||
|     , disabled(false) | ||||
|     , is_active(true) | ||||
|     , zoom_to_volumes(true) | ||||
|     , shader_outside_printer_detection_enabled(false) | ||||
|  | @ -252,6 +254,8 @@ void GLVolume::set_render_color() | |||
|         set_render_color(is_outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4); | ||||
|     else if (hover) | ||||
|         set_render_color(HOVER_COLOR, 4); | ||||
|     else if (disabled) | ||||
|         set_render_color(DISABLED_COLOR, 4); | ||||
|     else if (is_outside && shader_outside_printer_detection_enabled) | ||||
|         set_render_color(OUTSIDE_COLOR, 4); | ||||
|     else | ||||
|  | @ -723,7 +727,11 @@ std::vector<int> GLVolumeCollection::load_object( | |||
| 
 | ||||
|         for (int instance_idx : instance_idxs) { | ||||
|             const ModelInstance *instance = model_object->instances[instance_idx]; | ||||
| #if ENABLE_MODELVOLUME_TRANSFORM | ||||
|             const TriangleMesh& mesh = model_volume->mesh; | ||||
| #else | ||||
|             TriangleMesh mesh = model_volume->mesh; | ||||
| #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||
|             volumes_idx.push_back(int(this->volumes.size())); | ||||
|             float color[4]; | ||||
|             memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3); | ||||
|  | @ -758,7 +766,8 @@ std::vector<int> GLVolumeCollection::load_object( | |||
|             v.is_modifier = ! model_volume->is_model_part(); | ||||
|             v.shader_outside_printer_detection_enabled = model_volume->is_model_part(); | ||||
| #if ENABLE_MODELVOLUME_TRANSFORM | ||||
|             v.set_transformation(instance->get_transformation()); | ||||
|             v.set_instance_transformation(instance->get_transformation()); | ||||
|             v.set_volume_transformation(model_volume->get_transformation()); | ||||
| #else | ||||
|             v.set_offset(instance->get_offset()); | ||||
|             v.set_rotation(instance->get_rotation()); | ||||
|  | @ -833,7 +842,11 @@ int GLVolumeCollection::load_wipe_tower_preview( | |||
|     else | ||||
|         v.indexed_vertex_array.load_mesh_flat_shading(mesh); | ||||
| 
 | ||||
| #if ENABLE_MODELVOLUME_TRANSFORM | ||||
|     v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0)); | ||||
| #else | ||||
|     v.set_offset(Vec3d(pos_x, pos_y, 0.0)); | ||||
| #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||
| 
 | ||||
|     // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().
 | ||||
|     v.bounding_box = v.indexed_vertex_array.bounding_box(); | ||||
|  |  | |||
|  | @ -249,13 +249,15 @@ public: | |||
|     static const float HOVER_COLOR[4]; | ||||
|     static const float OUTSIDE_COLOR[4]; | ||||
|     static const float SELECTED_OUTSIDE_COLOR[4]; | ||||
|     static const float DISABLED_COLOR[4]; | ||||
| 
 | ||||
|     GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f); | ||||
|     GLVolume(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {} | ||||
| 
 | ||||
| private: | ||||
| #if ENABLE_MODELVOLUME_TRANSFORM | ||||
|     Geometry::Transformation m_transformation; | ||||
|     Geometry::Transformation m_instance_transformation; | ||||
|     Geometry::Transformation m_volume_transformation; | ||||
| #else | ||||
|     // Offset of the volume to be rendered.
 | ||||
|     Vec3d                 m_offset; | ||||
|  | @ -294,6 +296,8 @@ public: | |||
|     int                 extruder_id; | ||||
|     // Is this object selected?
 | ||||
|     bool                selected; | ||||
|     // Is this object disabled from selection?
 | ||||
|     bool                disabled; | ||||
|     // Whether or not this volume is active for rendering
 | ||||
|     bool                is_active; | ||||
|     // Whether or not to use this volume when applying zoom_to_volumes()
 | ||||
|  | @ -329,32 +333,59 @@ public: | |||
|     void set_render_color(); | ||||
| 
 | ||||
| #if ENABLE_MODELVOLUME_TRANSFORM | ||||
|     const Geometry::Transformation& get_transformation() const { return m_transformation; } | ||||
|     void set_transformation(const Geometry::Transformation& transformation) { m_transformation = transformation; set_bounding_boxes_as_dirty(); } | ||||
|     const Geometry::Transformation& get_instance_transformation() const { return m_instance_transformation; } | ||||
|     void set_instance_transformation(const Geometry::Transformation& transformation) { m_instance_transformation = transformation; set_bounding_boxes_as_dirty(); } | ||||
| 
 | ||||
|     const Vec3d& get_offset() const { return m_transformation.get_offset(); } | ||||
|     double get_offset(Axis axis) const { return m_transformation.get_offset(axis); } | ||||
|     const Vec3d& get_instance_offset() const { return m_instance_transformation.get_offset(); } | ||||
|     double get_instance_offset(Axis axis) const { return m_instance_transformation.get_offset(axis); } | ||||
| 
 | ||||
|     void set_offset(const Vec3d& offset) { m_transformation.set_offset(offset); set_bounding_boxes_as_dirty(); } | ||||
|     void set_offset(Axis axis, double offset) { m_transformation.set_offset(axis, offset); set_bounding_boxes_as_dirty(); } | ||||
|     void set_instance_offset(const Vec3d& offset) { m_instance_transformation.set_offset(offset); set_bounding_boxes_as_dirty(); } | ||||
|     void set_instance_offset(Axis axis, double offset) { m_instance_transformation.set_offset(axis, offset); set_bounding_boxes_as_dirty(); } | ||||
| 
 | ||||
|     const Vec3d& get_rotation() const { return m_transformation.get_rotation(); } | ||||
|     double get_rotation(Axis axis) const { return m_transformation.get_rotation(axis); } | ||||
|     const Vec3d& get_instance_rotation() const { return m_instance_transformation.get_rotation(); } | ||||
|     double get_instance_rotation(Axis axis) const { return m_instance_transformation.get_rotation(axis); } | ||||
| 
 | ||||
|     void set_rotation(const Vec3d& rotation) { m_transformation.set_rotation(rotation); set_bounding_boxes_as_dirty(); } | ||||
|     void set_rotation(Axis axis, double rotation) { m_transformation.set_rotation(axis, rotation); set_bounding_boxes_as_dirty(); } | ||||
|     void set_instance_rotation(const Vec3d& rotation) { m_instance_transformation.set_rotation(rotation); set_bounding_boxes_as_dirty(); } | ||||
|     void set_instance_rotation(Axis axis, double rotation) { m_instance_transformation.set_rotation(axis, rotation); set_bounding_boxes_as_dirty(); } | ||||
| 
 | ||||
|     Vec3d get_scaling_factor() const { return m_transformation.get_scaling_factor(); } | ||||
|     double get_scaling_factor(Axis axis) const { return m_transformation.get_scaling_factor(axis); } | ||||
|     Vec3d get_instance_scaling_factor() const { return m_instance_transformation.get_scaling_factor(); } | ||||
|     double get_instance_scaling_factor(Axis axis) const { return m_instance_transformation.get_scaling_factor(axis); } | ||||
| 
 | ||||
|     void set_scaling_factor(const Vec3d& scaling_factor) { m_transformation.set_scaling_factor(scaling_factor); set_bounding_boxes_as_dirty(); } | ||||
|     void set_scaling_factor(Axis axis, double scaling_factor) { m_transformation.set_scaling_factor(axis, scaling_factor); set_bounding_boxes_as_dirty(); } | ||||
|     void set_instance_scaling_factor(const Vec3d& scaling_factor) { m_instance_transformation.set_scaling_factor(scaling_factor); set_bounding_boxes_as_dirty(); } | ||||
|     void set_instance_scaling_factor(Axis axis, double scaling_factor) { m_instance_transformation.set_scaling_factor(axis, scaling_factor); set_bounding_boxes_as_dirty(); } | ||||
| 
 | ||||
|     const Vec3d& get_mirror() const { return m_transformation.get_mirror(); } | ||||
|     double get_mirror(Axis axis) const { return m_transformation.get_mirror(axis); } | ||||
|     const Vec3d& get_instance_mirror() const { return m_instance_transformation.get_mirror(); } | ||||
|     double get_instance_mirror(Axis axis) const { return m_instance_transformation.get_mirror(axis); } | ||||
| 
 | ||||
|     void set_mirror(const Vec3d& mirror) { m_transformation.set_mirror(mirror); set_bounding_boxes_as_dirty(); } | ||||
|     void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); set_bounding_boxes_as_dirty(); } | ||||
|     void set_instance_mirror(const Vec3d& mirror) { m_instance_transformation.set_mirror(mirror); set_bounding_boxes_as_dirty(); } | ||||
|     void set_instance_mirror(Axis axis, double mirror) { m_instance_transformation.set_mirror(axis, mirror); set_bounding_boxes_as_dirty(); } | ||||
| 
 | ||||
|     const Geometry::Transformation& get_volume_transformation() const { return m_volume_transformation; } | ||||
|     void set_volume_transformation(const Geometry::Transformation& transformation) { m_volume_transformation = transformation; set_bounding_boxes_as_dirty(); } | ||||
| 
 | ||||
|     const Vec3d& get_volume_offset() const { return m_volume_transformation.get_offset(); } | ||||
|     double get_volume_offset(Axis axis) const { return m_volume_transformation.get_offset(axis); } | ||||
| 
 | ||||
|     void set_volume_offset(const Vec3d& offset) { m_volume_transformation.set_offset(offset); set_bounding_boxes_as_dirty(); } | ||||
|     void set_volume_offset(Axis axis, double offset) { m_volume_transformation.set_offset(axis, offset); set_bounding_boxes_as_dirty(); } | ||||
| 
 | ||||
|     const Vec3d& get_volume_rotation() const { return m_volume_transformation.get_rotation(); } | ||||
|     double get_volume_rotation(Axis axis) const { return m_volume_transformation.get_rotation(axis); } | ||||
| 
 | ||||
|     void set_volume_rotation(const Vec3d& rotation) { m_volume_transformation.set_rotation(rotation); set_bounding_boxes_as_dirty(); } | ||||
|     void set_volume_rotation(Axis axis, double rotation) { m_volume_transformation.set_rotation(axis, rotation); set_bounding_boxes_as_dirty(); } | ||||
| 
 | ||||
|     Vec3d get_volume_scaling_factor() const { return m_volume_transformation.get_scaling_factor(); } | ||||
|     double get_volume_scaling_factor(Axis axis) const { return m_volume_transformation.get_scaling_factor(axis); } | ||||
| 
 | ||||
|     void set_volume_scaling_factor(const Vec3d& scaling_factor) { m_volume_transformation.set_scaling_factor(scaling_factor); set_bounding_boxes_as_dirty(); } | ||||
|     void set_volume_scaling_factor(Axis axis, double scaling_factor) { m_volume_transformation.set_scaling_factor(axis, scaling_factor); set_bounding_boxes_as_dirty(); } | ||||
| 
 | ||||
|     const Vec3d& get_volume_mirror() const { return m_volume_transformation.get_mirror(); } | ||||
|     double get_volume_mirror(Axis axis) const { return m_volume_transformation.get_mirror(axis); } | ||||
| 
 | ||||
|     void set_volume_mirror(const Vec3d& mirror) { m_volume_transformation.set_mirror(mirror); set_bounding_boxes_as_dirty(); } | ||||
|     void set_volume_mirror(Axis axis, double mirror) { m_volume_transformation.set_mirror(axis, mirror); set_bounding_boxes_as_dirty(); } | ||||
| #else | ||||
|     const Vec3d& get_rotation() const; | ||||
|     void set_rotation(const Vec3d& rotation); | ||||
|  | @ -378,7 +409,7 @@ public: | |||
|     int                 instance_idx() const { return this->composite_id % 1000; } | ||||
| 
 | ||||
| #if ENABLE_MODELVOLUME_TRANSFORM | ||||
|     const Transform3d&   world_matrix() const { return m_transformation.get_matrix(); } | ||||
|     Transform3d world_matrix() const { return m_instance_transformation.get_matrix() * m_volume_transformation.get_matrix(); } | ||||
| #else | ||||
|     const Transform3f&   world_matrix() const; | ||||
| #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| #include "BackgroundSlicingProcess.hpp" | ||||
| #include "GUI_App.hpp" | ||||
| 
 | ||||
| #include <wx/app.h> | ||||
| #include <wx/event.h> | ||||
| #include <wx/panel.h> | ||||
| #include <wx/stdpaths.h> | ||||
|  | @ -103,6 +104,15 @@ void BackgroundSlicingProcess::thread_proc() | |||
| 	// End of the background processing thread. The UI thread should join m_thread now.
 | ||||
| } | ||||
| 
 | ||||
| void BackgroundSlicingProcess::thread_proc_safe() | ||||
| { | ||||
| 	try { | ||||
| 		this->thread_proc(); | ||||
| 	} catch (...) { | ||||
| 		wxTheApp->OnUnhandledException(); | ||||
|    	} | ||||
| } | ||||
| 
 | ||||
| void BackgroundSlicingProcess::join_background_thread() | ||||
| { | ||||
| 	std::unique_lock<std::mutex> lck(m_mutex); | ||||
|  | @ -127,7 +137,7 @@ bool BackgroundSlicingProcess::start() | |||
| 	if (m_state == STATE_INITIAL) { | ||||
| 		// The worker thread is not running yet. Start it.
 | ||||
| 		assert(! m_thread.joinable()); | ||||
| 		m_thread = std::thread([this]{this->thread_proc();}); | ||||
| 		m_thread = std::thread([this]{this->thread_proc_safe();}); | ||||
| 		// Wait until the worker thread is ready to execute the background processing task.
 | ||||
| 		m_condition.wait(lck, [this](){ return m_state == STATE_IDLE; }); | ||||
| 	} | ||||
|  |  | |||
|  | @ -80,6 +80,7 @@ public: | |||
| 
 | ||||
| private: | ||||
| 	void 	thread_proc(); | ||||
| 	void 	thread_proc_safe(); | ||||
| 	void 	join_background_thread(); | ||||
| 	// To be called by Print::apply() through the Print::m_cancel_callback to stop the background
 | ||||
| 	// processing before changing any data of running or finalized milestones.
 | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -358,9 +358,14 @@ public: | |||
| 
 | ||||
|         enum EMode : unsigned char | ||||
|         { | ||||
| #if ENABLE_MODELVOLUME_TRANSFORM | ||||
|             Volume, | ||||
|             Instance | ||||
| #else | ||||
|             Volume, | ||||
|             Instance, | ||||
|             Object | ||||
| #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||
|         }; | ||||
| 
 | ||||
|         enum EType : unsigned char | ||||
|  | @ -368,9 +373,13 @@ public: | |||
|             Invalid, | ||||
|             Empty, | ||||
|             WipeTower, | ||||
|             Modifier, | ||||
|             SingleModifier, | ||||
|             MultipleModifier, | ||||
|             SingleVolume, | ||||
|             MultipleVolume, | ||||
|             SingleFullObject, | ||||
|             SingleFullInstance, | ||||
|             MultipleFullInstance, | ||||
|             Mixed | ||||
|         }; | ||||
| 
 | ||||
|  | @ -378,21 +387,57 @@ public: | |||
|         struct VolumeCache | ||||
|         { | ||||
|         private: | ||||
| #if ENABLE_MODELVOLUME_TRANSFORM | ||||
|             struct TransformCache | ||||
|             { | ||||
|                 Vec3d position; | ||||
|                 Vec3d rotation; | ||||
|                 Vec3d scaling_factor; | ||||
|                 Transform3d rotation_matrix; | ||||
|                 Transform3d scale_matrix; | ||||
| 
 | ||||
|                 TransformCache(); | ||||
|                 explicit TransformCache(const Geometry::Transformation& transform); | ||||
|             }; | ||||
| 
 | ||||
|             TransformCache m_volume; | ||||
|             TransformCache m_instance; | ||||
| #else | ||||
|             Vec3d m_position; | ||||
|             Vec3d m_rotation; | ||||
|             Vec3d m_scaling_factor; | ||||
|             Transform3d m_rotation_matrix; | ||||
|             Transform3d m_scale_matrix; | ||||
| #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||
| 
 | ||||
|         public: | ||||
| #if ENABLE_MODELVOLUME_TRANSFORM | ||||
|             VolumeCache() {} | ||||
|             VolumeCache(const Geometry::Transformation& volume_transform, const Geometry::Transformation& instance_transform); | ||||
| #else | ||||
|             VolumeCache(); | ||||
|             VolumeCache(const Vec3d& position, const Vec3d& rotation, const Vec3d& scaling_factor); | ||||
| #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||
| 
 | ||||
| #if ENABLE_MODELVOLUME_TRANSFORM | ||||
|             const Vec3d& get_volume_position() const { return m_volume.position; } | ||||
|             const Vec3d& get_volume_rotation() const { return m_volume.rotation; } | ||||
|             const Vec3d& get_volume_scaling_factor() const { return m_volume.scaling_factor; } | ||||
|             const Transform3d& get_volume_rotation_matrix() const { return m_volume.rotation_matrix; } | ||||
|             const Transform3d& get_volume_scale_matrix() const { return m_volume.scale_matrix; } | ||||
| 
 | ||||
|             const Vec3d& get_instance_position() const { return m_instance.position; } | ||||
|             const Vec3d& get_instance_rotation() const { return m_instance.rotation; } | ||||
|             const Vec3d& get_instance_scaling_factor() const { return m_instance.scaling_factor; } | ||||
|             const Transform3d& get_instance_rotation_matrix() const { return m_instance.rotation_matrix; } | ||||
|             const Transform3d& get_instance_scale_matrix() const { return m_instance.scale_matrix; } | ||||
| #else | ||||
|             const Vec3d& get_position() const { return m_position; } | ||||
|             const Vec3d& get_rotation() const { return m_rotation; } | ||||
|             const Vec3d& get_scaling_factor() const { return m_scaling_factor; } | ||||
|             const Transform3d& get_rotation_matrix() const { return m_rotation_matrix; } | ||||
|             const Transform3d& get_scale_matrix() const { return m_scale_matrix; } | ||||
| #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||
|         }; | ||||
| 
 | ||||
|         typedef std::map<unsigned int, VolumeCache> VolumesCache; | ||||
|  | @ -435,14 +480,14 @@ public: | |||
|         void add_instance(unsigned int object_idx, unsigned int instance_idx, bool as_single_selection = true); | ||||
|         void remove_instance(unsigned int object_idx, unsigned int instance_idx); | ||||
| 
 | ||||
|         void add_volume(unsigned int object_idx, unsigned int volume_idx, bool as_single_selection = true); | ||||
|         void add_volume(unsigned int object_idx, unsigned int volume_idx, int instance_idx, bool as_single_selection = true); | ||||
|         void remove_volume(unsigned int object_idx, unsigned int volume_idx); | ||||
| 
 | ||||
|         void clear(); | ||||
| 
 | ||||
|         bool is_empty() const { return m_type == Empty; } | ||||
|         bool is_wipe_tower() const { return m_type == WipeTower; } | ||||
|         bool is_modifier() const { return m_type == Modifier; } | ||||
|         bool is_modifier() const { return (m_type == SingleModifier) || (m_type == MultipleModifier); } | ||||
|         bool is_single_full_instance() const; | ||||
|         bool is_single_full_object() const { return m_type == SingleFullObject; } | ||||
|         bool is_mixed() const { return m_type == Mixed; } | ||||
|  | @ -455,6 +500,9 @@ public: | |||
|         int get_object_idx() const; | ||||
|         // Returns the instance id if the selection is from a single object and from a single instance, otherwise is -1
 | ||||
|         int get_instance_idx() const; | ||||
|         // Returns the indices of selected instances.
 | ||||
|         // Can only be called if selection is from a single object.
 | ||||
|         const InstanceIdxsList& get_instance_idxs() const; | ||||
| 
 | ||||
|         const IndicesList& get_volume_idxs() const { return m_list; } | ||||
|         const GLVolume* get_volume(unsigned int volume_idx) const; | ||||
|  | @ -466,13 +514,14 @@ public: | |||
| 
 | ||||
|         void translate(const Vec3d& displacement); | ||||
|         void rotate(const Vec3d& rotation); | ||||
|         void flattening_rotate(const Vec3d& normal); | ||||
|         void scale(const Vec3d& scale); | ||||
|         void mirror(Axis axis); | ||||
| 
 | ||||
|         void translate(unsigned int object_idx, const Vec3d& displacement); | ||||
|         void translate(unsigned int object_idx, unsigned int instance_idx, const Vec3d& displacement); | ||||
| 
 | ||||
|         void render(bool show_indirect_selection) const; | ||||
|         void render() const; | ||||
| 
 | ||||
|     private: | ||||
|         void _update_valid(); | ||||
|  | @ -486,9 +535,10 @@ public: | |||
|         void _remove_object(unsigned int object_idx); | ||||
|         void _calc_bounding_box() const; | ||||
|         void _render_selected_volumes() const; | ||||
|         void _render_unselected_instances() const; | ||||
|         void _render_synchronized_volumes() const; | ||||
|         void _render_bounding_box(const BoundingBoxf3& box, float* color) const; | ||||
|         void _synchronize_unselected_instances(); | ||||
|         void _synchronize_unselected_volumes(); | ||||
|     }; | ||||
| 
 | ||||
| private: | ||||
|  | @ -556,7 +606,7 @@ private: | |||
|         Vec3d get_rotation() const; | ||||
|         void set_rotation(const Vec3d& rotation); | ||||
| 
 | ||||
|         Vec3d get_flattening_rotation() const; | ||||
|         Vec3d get_flattening_normal() const; | ||||
| 
 | ||||
|         void set_flattening_data(const ModelObject* model_object); | ||||
|          | ||||
|  |  | |||
|  | @ -720,7 +720,11 @@ void GLGizmoScale3D::on_process_double_click() | |||
| void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const | ||||
| { | ||||
|     bool single_instance = selection.is_single_full_instance(); | ||||
| #if ENABLE_MODELVOLUME_TRANSFORM | ||||
|     Vec3f scale = single_instance ? 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_scaling_factor().cast<float>() : 100.0f * m_scale.cast<float>(); | ||||
| #else | ||||
|     Vec3f scale = single_instance ? 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_scaling_factor().cast<float>() : 100.0f * m_scale.cast<float>(); | ||||
| #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||
| 
 | ||||
|     if ((single_instance && ((m_hover_id == 0) || (m_hover_id == 1))) || m_grabbers[0].dragging || m_grabbers[1].dragging) | ||||
|         set_tooltip("X: " + format(scale(0), 4) + "%"); | ||||
|  | @ -762,10 +766,18 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const | |||
| #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||
| 
 | ||||
|         // gets angles from first selected volume
 | ||||
| #if ENABLE_MODELVOLUME_TRANSFORM | ||||
|         angles = v->get_instance_rotation(); | ||||
| #else | ||||
|         angles = v->get_rotation(); | ||||
| #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||
| 
 | ||||
|         // consider rotation+mirror only components of the transform for offsets
 | ||||
| #if ENABLE_MODELVOLUME_TRANSFORM | ||||
|         offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); | ||||
| #else | ||||
|         offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_mirror()); | ||||
| #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||
|     } | ||||
|     else | ||||
|         box = selection.get_bounding_box(); | ||||
|  | @ -1165,38 +1177,41 @@ void GLGizmoFlatten::on_start_dragging(const GLCanvas3D::Selection& selection) | |||
| 
 | ||||
| void GLGizmoFlatten::on_render(const GLCanvas3D::Selection& selection) const | ||||
| { | ||||
|     // the dragged_offset is a vector measuring where was the object moved
 | ||||
|     // with the gizmo being on. This is reset in set_flattening_data and
 | ||||
|     // does not work correctly when there are multiple copies.
 | ||||
|     // The planes are rendered incorrectly when the object is being moved. We better won't render anything in that case.
 | ||||
|     // This indeed has a better solution (to be implemented when there is more time)
 | ||||
|     Vec3d dragged_offset(Vec3d::Zero()); | ||||
|     if (m_starting_center == Vec3d::Zero()) | ||||
|         m_starting_center = selection.get_bounding_box().center(); | ||||
|     dragged_offset = selection.get_bounding_box().center() - m_starting_center; | ||||
|     if (dragged_offset.norm() > 0.001) | ||||
|         return; | ||||
| 
 | ||||
|     ::glEnable(GL_BLEND); | ||||
|     ::glEnable(GL_DEPTH_TEST); | ||||
|     ::glDisable(GL_CULL_FACE); | ||||
| 
 | ||||
|     for (int i=0; i<(int)m_planes.size(); ++i) { | ||||
|         if (i == m_hover_id) | ||||
|             ::glColor4f(0.9f, 0.9f, 0.9f, 0.75f); | ||||
|         else | ||||
|             ::glColor4f(0.9f, 0.9f, 0.9f, 0.5f); | ||||
|     if (selection.is_from_single_object()) { | ||||
|         const std::set<int>& instances_list = selection.get_instance_idxs(); | ||||
| 
 | ||||
|         int instance_idx = selection.get_instance_idx(); | ||||
|         if ((instance_idx != -1) && (m_model_object != nullptr)) | ||||
|         { | ||||
|         if (!instances_list.empty() && m_model_object) { | ||||
|             for (const int instance_idx : instances_list) { | ||||
|             Transform3d m = m_model_object->instances[instance_idx]->get_matrix(); | ||||
|             m.pretranslate(dragged_offset); | ||||
|             ::glPushMatrix(); | ||||
|             ::glMultMatrixd(m.data()); | ||||
|             ::glBegin(GL_POLYGON); | ||||
|             for (const Vec3d& vertex : m_planes[i].vertices) | ||||
|             { | ||||
|                 ::glVertex3dv(vertex.data()); | ||||
|                 for (int i=0; i<(int)m_planes.size(); ++i) { | ||||
|                     if (i == m_hover_id) | ||||
|                         ::glColor4f(0.9f, 0.9f, 0.9f, 0.75f); | ||||
|                     else | ||||
|                         ::glColor4f(0.9f, 0.9f, 0.9f, 0.5f); | ||||
| 
 | ||||
|                     m.pretranslate(dragged_offset); | ||||
|                     ::glPushMatrix(); | ||||
|                     ::glMultMatrixd(m.data()); | ||||
|                     ::glBegin(GL_POLYGON); | ||||
|                     for (const Vec3d& vertex : m_planes[i].vertices) | ||||
|                         ::glVertex3dv(vertex.data()); | ||||
|                     ::glEnd(); | ||||
|                     ::glPopMatrix(); | ||||
|                 } | ||||
|             } | ||||
|             ::glEnd(); | ||||
|             ::glPopMatrix(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -1208,22 +1223,21 @@ void GLGizmoFlatten::on_render_for_picking(const GLCanvas3D::Selection& selectio | |||
| { | ||||
|     ::glEnable(GL_DEPTH_TEST); | ||||
|     ::glDisable(GL_CULL_FACE); | ||||
| 
 | ||||
|     for (unsigned int i = 0; i < m_planes.size(); ++i) | ||||
|     { | ||||
|         ::glColor3f(1.0f, 1.0f, picking_color_component(i)); | ||||
|         int instance_idx = selection.get_instance_idx(); | ||||
|         if ((instance_idx != -1) && (m_model_object != nullptr)) | ||||
|         { | ||||
|             ::glPushMatrix(); | ||||
|             ::glMultMatrixd(m_model_object->instances[instance_idx]->get_matrix().data()); | ||||
|             ::glBegin(GL_POLYGON); | ||||
|             for (const Vec3d& vertex : m_planes[i].vertices) | ||||
|             { | ||||
|                 ::glVertex3dv(vertex.data()); | ||||
|     if (selection.is_from_single_object()) { | ||||
|         const std::set<int>& instances_list = selection.get_instance_idxs(); | ||||
|         if (!instances_list.empty() && m_model_object) { | ||||
|             for (const int instance_idx : instances_list) { | ||||
|                 for (int i=0; i<(int)m_planes.size(); ++i) { | ||||
|                     ::glColor3f(1.0f, 1.0f, picking_color_component(i)); | ||||
|                     ::glPushMatrix(); | ||||
|                     ::glMultMatrixd(m_model_object->instances[instance_idx]->get_matrix().data()); | ||||
|                     ::glBegin(GL_POLYGON); | ||||
|                     for (const Vec3d& vertex : m_planes[i].vertices) | ||||
|                         ::glVertex3dv(vertex.data()); | ||||
|                     ::glEnd(); | ||||
|                     ::glPopMatrix(); | ||||
|                 } | ||||
|             } | ||||
|             ::glEnd(); | ||||
|             ::glPopMatrix(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -1233,9 +1247,10 @@ void GLGizmoFlatten::on_render_for_picking(const GLCanvas3D::Selection& selectio | |||
| void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object) | ||||
| { | ||||
|     m_starting_center = Vec3d::Zero(); | ||||
|     bool object_changed = m_model_object != model_object; | ||||
|     m_model_object = model_object; | ||||
| 
 | ||||
|     if (is_plane_update_necessary()) | ||||
|     if (object_changed && is_plane_update_necessary()) | ||||
|         update_planes(); | ||||
| } | ||||
| 
 | ||||
|  | @ -1243,7 +1258,15 @@ void GLGizmoFlatten::update_planes() | |||
| { | ||||
|     TriangleMesh ch; | ||||
|     for (const ModelVolume* vol : m_model_object->volumes) | ||||
| #if ENABLE_MODELVOLUME_TRANSFORM | ||||
|     { | ||||
|         TriangleMesh vol_ch = vol->get_convex_hull(); | ||||
|         vol_ch.transform(vol->get_matrix()); | ||||
|         ch.merge(vol_ch); | ||||
|     } | ||||
| #else | ||||
|         ch.merge(vol->get_convex_hull()); | ||||
| #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||
| 
 | ||||
|     ch = ch.convex_hull_3d(); | ||||
| 
 | ||||
|  | @ -1438,20 +1461,14 @@ bool GLGizmoFlatten::is_plane_update_necessary() const | |||
|     return false; | ||||
| } | ||||
| 
 | ||||
| Vec3d GLGizmoFlatten::get_flattening_rotation() const | ||||
| Vec3d GLGizmoFlatten::get_flattening_normal() const | ||||
| { | ||||
|     // calculates the rotations in model space, taking in account the scaling factors
 | ||||
|     Eigen::Matrix<double, 3, 3, Eigen::DontAlign> m = m_model_object->instances.front()->get_matrix(true, true).matrix().block(0, 0, 3, 3).inverse().transpose(); | ||||
|     Eigen::Quaterniond q; | ||||
|     Vec3d angles = Geometry::extract_euler_angles(q.setFromTwoVectors(m * m_normal, -Vec3d::UnitZ()).toRotationMatrix()); | ||||
|     Vec3d out = m_normal; | ||||
|     m_normal = Vec3d::Zero(); | ||||
|     m_starting_center = Vec3d::Zero(); | ||||
|     return angles; | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent) | ||||
|     : GLGizmoBase(parent), m_starting_center(Vec3d::Zero()) | ||||
| { | ||||
|  | @ -1478,11 +1495,14 @@ bool GLGizmoSlaSupports::on_init() | |||
| 
 | ||||
| void GLGizmoSlaSupports::set_model_object_ptr(ModelObject* model_object) | ||||
| { | ||||
|     m_starting_center = Vec3d::Zero(); | ||||
|     m_model_object = model_object; | ||||
|     m_model_object_matrix = model_object->instances.front()->get_matrix(); | ||||
|     if (is_mesh_update_necessary()) | ||||
|         update_mesh(); | ||||
|     if (model_object != nullptr) | ||||
|     { | ||||
|         m_starting_center = Vec3d::Zero(); | ||||
|         m_model_object = model_object; | ||||
|         m_model_object_matrix = model_object->instances.front()->get_matrix(); | ||||
|         if (is_mesh_update_necessary()) | ||||
|             update_mesh(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void GLGizmoSlaSupports::on_render(const GLCanvas3D::Selection& selection) const | ||||
|  |  | |||
|  | @ -359,6 +359,7 @@ private: | |||
|     std::vector<PlaneData> m_planes; | ||||
|     mutable Vec3d m_starting_center; | ||||
|     const ModelObject* m_model_object = nullptr; | ||||
|     std::vector<const Transform3d*> instances_matrices; | ||||
| 
 | ||||
|     void update_planes(); | ||||
|     bool is_plane_update_necessary() const; | ||||
|  | @ -367,12 +368,12 @@ public: | |||
|     explicit GLGizmoFlatten(GLCanvas3D& parent); | ||||
| 
 | ||||
|     void set_flattening_data(const ModelObject* model_object); | ||||
|     Vec3d get_flattening_rotation() const; | ||||
|     Vec3d get_flattening_normal() const; | ||||
| 
 | ||||
| protected: | ||||
|     virtual bool on_init(); | ||||
|     virtual std::string on_get_name() const; | ||||
|     virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return selection.is_single_full_instance(); } | ||||
|     virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return (selection.is_from_single_object() && !selection.is_wipe_tower() && !selection.is_modifier());  } | ||||
|     virtual void on_start_dragging(const GLCanvas3D::Selection& selection); | ||||
|     virtual void on_update(const Linef3& mouse_ray, const Point* mouse_pos) {} | ||||
|     virtual void on_render(const GLCanvas3D::Selection& selection) const; | ||||
|  |  | |||
|  | @ -60,7 +60,7 @@ enum ConfigMenuIDs { | |||
| 
 | ||||
| class Tab; | ||||
| 
 | ||||
| static wxString dots("…", wxConvUTF8); | ||||
| static wxString dots("…", wxConvUTF8); | ||||
| 
 | ||||
| class GUI_App : public wxApp | ||||
| { | ||||
|  |  | |||
|  | @ -36,11 +36,11 @@ ObjectList::ObjectList(wxWindow* parent) : | |||
| 		CATEGORY_ICON[L("Advanced")]				= wxBitmap(from_u8(var("wand.png")), wxBITMAP_TYPE_PNG); | ||||
|     } | ||||
| 
 | ||||
|     init_icons(); | ||||
| 
 | ||||
|     // create control
 | ||||
|     create_objects_ctrl(); | ||||
| 
 | ||||
|     init_icons(); | ||||
| 
 | ||||
|     // describe control behavior 
 | ||||
|     Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxEvent& event) { | ||||
|         selection_changed(); | ||||
|  | @ -213,17 +213,27 @@ void ObjectList::update_extruder_in_config(const wxString& selection) | |||
| 
 | ||||
| void ObjectList::init_icons() | ||||
| { | ||||
|     m_bmp_modifiermesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG);
 | ||||
|     m_bmp_solidmesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG);
 | ||||
|     m_bmp_modifiermesh      = wxBitmap(from_u8(var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG);
 | ||||
|     m_bmp_solidmesh         = wxBitmap(from_u8(var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG);
 | ||||
| 
 | ||||
|     m_bmp_support_enforcer  = wxBitmap(from_u8(var("support_enforcer_.png")), wxBITMAP_TYPE_PNG); | ||||
|     m_bmp_support_blocker   = wxBitmap(from_u8(var("support_blocker_.png")), wxBITMAP_TYPE_PNG); | ||||
| 
 | ||||
|     m_bmp_vector.reserve(4); // bitmaps for different types of parts 
 | ||||
|     m_bmp_vector.push_back(&m_bmp_solidmesh);         // Add part
 | ||||
|     m_bmp_vector.push_back(&m_bmp_modifiermesh);      // Add modifier
 | ||||
|     m_bmp_vector.push_back(&m_bmp_support_enforcer);  // Add support enforcer
 | ||||
|     m_bmp_vector.push_back(&m_bmp_support_blocker);   // Add support blocker
 | ||||
|     m_objects_model->SetVolumeBitmaps(m_bmp_vector); | ||||
| 
 | ||||
|     // init icon for manifold warning
 | ||||
|     m_bmp_manifold_warning = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("exclamation_mark_.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("error.png")), wxBITMAP_TYPE_PNG);
 | ||||
|     m_bmp_manifold_warning  = wxBitmap(from_u8(var("exclamation_mark_.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("error.png")), wxBITMAP_TYPE_PNG);
 | ||||
| 
 | ||||
|     // init bitmap for "Split to sub-objects" context menu
 | ||||
|     m_bmp_split = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("split.png")), wxBITMAP_TYPE_PNG); | ||||
|     m_bmp_split             = wxBitmap(from_u8(var("split.png")), wxBITMAP_TYPE_PNG); | ||||
| 
 | ||||
|     // init bitmap for "Add Settings" context menu
 | ||||
|     m_bmp_cog = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("cog.png")), wxBITMAP_TYPE_PNG); | ||||
|     m_bmp_cog               = wxBitmap(from_u8(var("cog.png")), wxBITMAP_TYPE_PNG); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -377,7 +387,8 @@ void ObjectList::on_drop(wxDataViewEvent &event) | |||
|     wxDataViewItem item(event.GetItem()); | ||||
| 
 | ||||
|     // only allow drops for item, not containers
 | ||||
|     if (item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) || | ||||
|     if (m_selected_object_id < 0 || | ||||
|         item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) || | ||||
|         event.GetDataFormat() != wxDF_UNICODETEXT || m_objects_model->GetItemType(item) != itVolume) { | ||||
|         event.Veto(); | ||||
|         return; | ||||
|  | @ -518,16 +529,24 @@ void ObjectList::get_settings_choice(wxMenu *menu, int id, bool is_part) | |||
|         wxGetApp().obj_manipul()->update_settings_list(); | ||||
| } | ||||
| 
 | ||||
| void ObjectList::menu_item_add_generic(wxMenuItem* &menu, int id) { | ||||
| void ObjectList::menu_item_add_generic(wxMenuItem* &menu, int id, const int type) { | ||||
|     auto sub_menu = new wxMenu; | ||||
| 
 | ||||
|     const wxString menu_load = _(L("Load")) +" "+ dots; | ||||
|     sub_menu->Append(new wxMenuItem(sub_menu, id++, menu_load)); | ||||
|     sub_menu->AppendSeparator(); | ||||
| 
 | ||||
|     std::vector<std::string> menu_items = { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") }; | ||||
|     for (auto& item : menu_items) | ||||
|         sub_menu->Append(new wxMenuItem(sub_menu, ++id, _(item))); | ||||
|         sub_menu->Append(new wxMenuItem(sub_menu, id++, _(item))); | ||||
| 
 | ||||
| #ifndef __WXMSW__ | ||||
|     sub_menu->Bind(wxEVT_MENU, [this, sub_menu](wxEvent &event) { | ||||
|         load_lambda(sub_menu->GetLabel(event.GetId()).ToStdString()); | ||||
|     sub_menu->Bind(wxEVT_MENU, [sub_menu, type, menu_load, this](wxEvent &event) { | ||||
|         auto selection = sub_menu->GetLabel(event.GetId()); | ||||
|         if (selection == menu_load) | ||||
|             load_subobject(type); | ||||
|         else | ||||
|             load_generic_subobject(selection.ToStdString(), type); | ||||
|     }); | ||||
| #endif //no __WXMSW__
 | ||||
| 
 | ||||
|  | @ -552,55 +571,53 @@ wxMenuItem* ObjectList::menu_item_settings(wxMenu* menu, int id, const bool is_p | |||
| wxMenu* ObjectList::create_add_part_popupmenu() | ||||
| { | ||||
|     wxMenu *menu = new wxMenu; | ||||
|     std::vector<std::string> menu_items = { L("Add part"), L("Add modifier"), L("Add generic") }; | ||||
|     // Note: id accords to type of the sub-object, so sequence of the menu items is important
 | ||||
|     std::vector<std::string> menu_object_types_items = {L("Add part"),              // ~ModelVolume::MODEL_PART
 | ||||
|                                                         L("Add modifier"),          // ~ModelVolume::PARAMETER_MODIFIER
 | ||||
|                                                         L("Add support enforcer"),  // ~ModelVolume::SUPPORT_ENFORCER
 | ||||
|                                                         L("Add support bloker") };  // ~ModelVolume::SUPPORT_BLOCKER
 | ||||
|      | ||||
|     const int obj_types_count = menu_object_types_items.size(); | ||||
|     const int generics_count = 5; // "Load ...", "Box", "Cylinder", "Sphere", "Slab"
 | ||||
| 
 | ||||
|     wxWindowID config_id_base = wxWindow::NewControlId(menu_items.size() + 4 + 2); | ||||
|     wxWindowID config_id_base = NewControlId(generics_count*obj_types_count + 2); | ||||
| 
 | ||||
|     int i = 0; | ||||
|     for (auto& item : menu_items) { | ||||
|         auto menu_item = new wxMenuItem(menu, config_id_base + i, _(item)); | ||||
|         menu_item->SetBitmap(i == 0 ? m_bmp_solidmesh : m_bmp_modifiermesh); | ||||
|         if (item == "Add generic") | ||||
|             menu_item_add_generic(menu_item, config_id_base + i); | ||||
|     // Add first 4 menu items
 | ||||
|     for (int type = 0; type < obj_types_count; type++) { | ||||
|         auto& item = menu_object_types_items[type]; | ||||
|         auto menu_item = new wxMenuItem(menu, config_id_base + type, _(item)); | ||||
|         menu_item->SetBitmap(*m_bmp_vector[type]); | ||||
|         menu_item_add_generic(menu_item, config_id_base + type*generics_count, type); | ||||
|         menu->Append(menu_item); | ||||
|         i++; | ||||
|     } | ||||
| 
 | ||||
|     // Split object to parts
 | ||||
|     menu->AppendSeparator(); | ||||
|     auto menu_item = menu_item_split(menu, config_id_base + i + 4); | ||||
|     auto menu_item = menu_item_split(menu, config_id_base + obj_types_count * generics_count); | ||||
|     menu->Append(menu_item); | ||||
|     menu_item->Enable(is_splittable_object(false)); | ||||
| 
 | ||||
|     // Settings
 | ||||
|     menu->AppendSeparator(); | ||||
|     // Append settings popupmenu
 | ||||
|     menu->Append(menu_item_settings(menu, config_id_base + i + 5, false)); | ||||
|     menu->Append(menu_item_settings(menu, config_id_base + obj_types_count * generics_count+1, false)); | ||||
| 
 | ||||
|     menu->Bind(wxEVT_MENU, [config_id_base, menu, this](wxEvent &event) { | ||||
|         switch (event.GetId() - config_id_base) { | ||||
|         case 0: | ||||
|             load_subobject(); | ||||
|             break; | ||||
|         case 1: | ||||
|             load_subobject(true); | ||||
|             break; | ||||
|         case 2: | ||||
|         case 3: | ||||
|         case 4: | ||||
|         case 5: | ||||
|         case 6: | ||||
| #ifdef __WXMSW__ | ||||
|             load_lambda(menu->GetLabel(event.GetId()).ToStdString()); | ||||
| #endif // __WXMSW__
 | ||||
|             break; | ||||
|         case 7: //3:
 | ||||
|     menu->Bind(wxEVT_MENU, [config_id_base, menu, obj_types_count, generics_count, this](wxEvent &event) { | ||||
|         auto selection = event.GetId() - config_id_base; | ||||
|          | ||||
|         if ( selection ==  0 * generics_count ||  // ~ModelVolume::MODEL_PART
 | ||||
|              selection ==  1 * generics_count ||  // ~ModelVolume::PARAMETER_MODIFIER
 | ||||
|              selection ==  2 * generics_count ||  // ~ModelVolume::SUPPORT_ENFORCER
 | ||||
|              selection ==  3 * generics_count  )  // ~ModelVolume::SUPPORT_BLOCKER
 | ||||
|             load_subobject(int(selection / generics_count)); | ||||
|         else if ( selection == obj_types_count * generics_count) | ||||
|             split(false); | ||||
|             break; | ||||
|         default: | ||||
| #ifdef __WXMSW__ | ||||
|         else if ( selection > obj_types_count * generics_count) // "Add Settings" is selected 
 | ||||
|             get_settings_choice(menu, event.GetId(), false); | ||||
|         else // Some generic model is selected
 | ||||
|             load_generic_subobject(menu->GetLabel(event.GetId()).ToStdString(), int(selection / generics_count)); | ||||
| #endif // __WXMSW__
 | ||||
|             break; | ||||
|         } | ||||
|     }); | ||||
| 
 | ||||
|     return menu; | ||||
|  | @ -609,24 +626,33 @@ wxMenu* ObjectList::create_add_part_popupmenu() | |||
| wxMenu* ObjectList::create_part_settings_popupmenu() | ||||
| { | ||||
|     wxMenu *menu = new wxMenu; | ||||
|     wxWindowID config_id_base = wxWindow::NewControlId(2); | ||||
|     wxWindowID config_id_base = NewControlId(3); | ||||
| 
 | ||||
|     auto menu_item = menu_item_split(menu, config_id_base); | ||||
|     menu->Append(menu_item); | ||||
|     menu_item->Enable(is_splittable_object(true)); | ||||
| 
 | ||||
|     // Append change part type
 | ||||
|     menu->AppendSeparator(); | ||||
|     menu->Append(new wxMenuItem(menu, config_id_base + 1, _(L("Change type")))); | ||||
| 
 | ||||
|     // Append settings popupmenu
 | ||||
|     menu->Append(menu_item_settings(menu, config_id_base + 1, true)); | ||||
|     menu->AppendSeparator(); | ||||
|     menu_item = menu_item_settings(menu, config_id_base + 2, true); | ||||
|     menu->Append(menu_item); | ||||
|     menu_item->Enable(get_selected_model_volume()->type() <= ModelVolume::PARAMETER_MODIFIER); | ||||
| 
 | ||||
|     menu->Bind(wxEVT_MENU, [config_id_base, menu, this](wxEvent &event) { | ||||
|         switch (event.GetId() - config_id_base) { | ||||
|         case 0: | ||||
|             split(true); | ||||
|             break; | ||||
|         default:{ | ||||
|         case 1: | ||||
|             change_part_type(); | ||||
|             break; | ||||
|         default: | ||||
|             get_settings_choice(menu, event.GetId(), true); | ||||
|             break; } | ||||
|             break; | ||||
|         } | ||||
|     }); | ||||
| 
 | ||||
|  | @ -655,31 +681,21 @@ wxMenu* ObjectList::create_add_settings_popupmenu(bool is_part) | |||
|     return menu; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // Load SubObjects (parts and modifiers)
 | ||||
| void ObjectList::load_subobject(bool is_modifier /*= false*/, bool is_lambda/* = false*/) | ||||
| void ObjectList::load_subobject(int type) | ||||
| { | ||||
|     auto item = GetSelection(); | ||||
|     if (!item) | ||||
|         return; | ||||
|     int obj_idx = -1; | ||||
|     if (m_objects_model->GetParent(item) == wxDataViewItem(0)) | ||||
|         obj_idx = m_objects_model->GetIdByItem(item); | ||||
|     else | ||||
|     if (!item || m_objects_model->GetParent(item) != wxDataViewItem(0)) | ||||
|         return; | ||||
|     int obj_idx = m_objects_model->GetIdByItem(item); | ||||
| 
 | ||||
|     if (obj_idx < 0) return; | ||||
|     wxArrayString part_names; | ||||
|     if (is_lambda) | ||||
|         load_lambda((*m_objects)[obj_idx], part_names, is_modifier); | ||||
|     else | ||||
|         load_part((*m_objects)[obj_idx], part_names, is_modifier); | ||||
|     load_part((*m_objects)[obj_idx], part_names, type); | ||||
| 
 | ||||
|     parts_changed(obj_idx); | ||||
| 
 | ||||
|     for (int i = 0; i < part_names.size(); ++i) { | ||||
|         const wxDataViewItem sel_item = m_objects_model->AddVolumeChild(item, part_names.Item(i), | ||||
|             is_modifier ? m_bmp_modifiermesh : m_bmp_solidmesh); | ||||
|         const wxDataViewItem sel_item = m_objects_model->AddVolumeChild(item, part_names.Item(i), /**m_bmp_vector[*/type/*]*/); | ||||
| 
 | ||||
|         if (i == part_names.size() - 1) | ||||
|             select_item(sel_item); | ||||
|  | @ -688,11 +704,12 @@ void ObjectList::load_subobject(bool is_modifier /*= false*/, bool is_lambda/* = | |||
| #ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
 | ||||
| //     selection_changed();
 | ||||
| #endif //no __WXOSX__//__WXMSW__
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void ObjectList::load_part( ModelObject* model_object, | ||||
|                             wxArrayString& part_names,  | ||||
|                             const bool is_modifier) | ||||
|                             int type) | ||||
| { | ||||
|     wxWindow* parent = wxGetApp().tab_panel()->GetPage(0); | ||||
| 
 | ||||
|  | @ -717,19 +734,27 @@ void ObjectList::load_part( ModelObject* model_object, | |||
|             if (model_object->origin_translation != Vec3d::Zero()) | ||||
|             { | ||||
|                 object->center_around_origin(); | ||||
| #if !ENABLE_MODELVOLUME_TRANSFORM | ||||
|                 object->ensure_on_bed(); | ||||
| #endif // !ENABLE_MODELVOLUME_TRANSFORM
 | ||||
|                 delta = model_object->origin_translation - object->origin_translation; | ||||
|             } | ||||
|             for (auto volume : object->volumes) { | ||||
| #if ENABLE_MODELVOLUME_TRANSFORM | ||||
|                 Vec3d shift = volume->mesh.bounding_box().center(); | ||||
|                 volume->translate_geometry(-shift); | ||||
|                 volume->translate(delta + shift); | ||||
| #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||
|                 auto new_volume = model_object->add_volume(*volume); | ||||
|                 new_volume->set_type(is_modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART); | ||||
|                 boost::filesystem::path(input_file).filename().string(); | ||||
|                 new_volume->set_type(static_cast<ModelVolume::Type>(type)); | ||||
|                 new_volume->name = boost::filesystem::path(input_file).filename().string(); | ||||
| 
 | ||||
|                 part_names.Add(new_volume->name); | ||||
| 
 | ||||
| #if !ENABLE_MODELVOLUME_TRANSFORM | ||||
|                 if (delta != Vec3d::Zero()) | ||||
|                     new_volume->translate(delta); | ||||
| #endif // !ENABLE_MODELVOLUME_TRANSFORM
 | ||||
| 
 | ||||
|                 // set a default extruder value, since user can't add it manually
 | ||||
|                 new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); | ||||
|  | @ -738,98 +763,46 @@ void ObjectList::load_part( ModelObject* model_object, | |||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void ObjectList::load_lambda(   ModelObject* model_object, | ||||
|                                 wxArrayString& part_names,  | ||||
|                                 const bool is_modifier) | ||||
| void ObjectList::load_generic_subobject(const std::string& type_name, const int type) | ||||
| { | ||||
|     auto dlg = new LambdaObjectDialog(GetMainWindow()); | ||||
|     if (dlg->ShowModal() == wxID_CANCEL) { | ||||
|         m_parts_changed = false; | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     std::string name = "lambda-"; | ||||
|     TriangleMesh mesh; | ||||
| 
 | ||||
|     auto params = dlg->ObjectParameters(); | ||||
|     switch (params.type) | ||||
|     { | ||||
|     case LambdaTypeBox:{ | ||||
|         mesh = make_cube(params.dim[0], params.dim[1], params.dim[2]); | ||||
|         name += "Box"; | ||||
|         break; } | ||||
|     case LambdaTypeCylinder:{ | ||||
|         mesh = make_cylinder(params.cyl_r, params.cyl_h); | ||||
|         name += "Cylinder"; | ||||
|         break; } | ||||
|     case LambdaTypeSphere:{ | ||||
|         mesh = make_sphere(params.sph_rho); | ||||
|         name += "Sphere"; | ||||
|         break; } | ||||
|     case LambdaTypeSlab:{ | ||||
|         const auto& size = model_object->bounding_box().size(); | ||||
|         mesh = make_cube(size(0)*1.5, size(1)*1.5, params.slab_h); | ||||
|         // box sets the base coordinate at 0, 0, move to center of plate and move it up to initial_z
 | ||||
|         mesh.translate(-size(0)*1.5 / 2.0, -size(1)*1.5 / 2.0, params.slab_z); | ||||
|         name += "Slab"; | ||||
|         break; } | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
|     mesh.repair(); | ||||
| 
 | ||||
|     auto new_volume = model_object->add_volume(mesh); | ||||
|     new_volume->set_type(is_modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART); | ||||
| 
 | ||||
|     new_volume->name = name; | ||||
|     // set a default extruder value, since user can't add it manually
 | ||||
|     new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); | ||||
| 
 | ||||
|     part_names.Add(name); | ||||
| 
 | ||||
|     m_parts_changed = true; | ||||
| } | ||||
| 
 | ||||
| void ObjectList::load_lambda(const std::string& type_name) | ||||
| { | ||||
|     if (m_selected_object_id < 0) return; | ||||
| 
 | ||||
|     auto dlg = new LambdaObjectDialog(GetMainWindow(), type_name); | ||||
|     if (dlg->ShowModal() == wxID_CANCEL) | ||||
|         return; | ||||
|     const auto obj_idx = get_selected_obj_idx(); | ||||
|     if (obj_idx < 0) return; | ||||
| 
 | ||||
|     const std::string name = "lambda-" + type_name; | ||||
|     TriangleMesh mesh; | ||||
| 
 | ||||
|     const auto params = dlg->ObjectParameters(); | ||||
|     auto& bed_shape = wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionPoints>("bed_shape")->values; | ||||
|     const auto& sz = BoundingBoxf(bed_shape).size(); | ||||
|     const auto side = 0.1 * std::max(sz(0), sz(1)); | ||||
| 
 | ||||
|     if (type_name == _("Box")) | ||||
|         mesh = make_cube(params.dim[0], params.dim[1], params.dim[2]); | ||||
|         mesh = make_cube(side, side, side); | ||||
|     else if (type_name == _("Cylinder")) | ||||
|         mesh = make_cylinder(params.cyl_r, params.cyl_h); | ||||
|         mesh = make_cylinder(0.5*side, side); | ||||
|     else if (type_name == _("Sphere")) | ||||
|         mesh = make_sphere(params.sph_rho); | ||||
|         mesh = make_sphere(side, PI/18); | ||||
|     else if (type_name == _("Slab")) { | ||||
|         const auto& size = (*m_objects)[m_selected_object_id]->bounding_box().size(); | ||||
|         mesh = make_cube(size(0)*1.5, size(1)*1.5, params.slab_h); | ||||
|         const auto& size = (*m_objects)[obj_idx]->bounding_box().size(); | ||||
|         mesh = make_cube(size(0)*1.5, size(1)*1.5, size(2)*0.5); | ||||
|         // box sets the base coordinate at 0, 0, move to center of plate and move it up to initial_z
 | ||||
|         mesh.translate(-size(0)*1.5 / 2.0, -size(1)*1.5 / 2.0, params.slab_z); | ||||
|         mesh.translate(-size(0)*1.5 / 2.0, -size(1)*1.5 / 2.0, 0); | ||||
|     } | ||||
|     mesh.repair(); | ||||
| 
 | ||||
|     auto new_volume = (*m_objects)[m_selected_object_id]->add_volume(mesh); | ||||
|     new_volume->set_type(ModelVolume::PARAMETER_MODIFIER); | ||||
|     auto new_volume = (*m_objects)[obj_idx]->add_volume(mesh); | ||||
|     new_volume->set_type(static_cast<ModelVolume::Type>(type)); | ||||
| 
 | ||||
|     new_volume->name = name; | ||||
|     // set a default extruder value, since user can't add it manually
 | ||||
|     new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); | ||||
| 
 | ||||
|     m_parts_changed = true; | ||||
|     parts_changed(m_selected_object_id); | ||||
|     parts_changed(obj_idx); | ||||
| 
 | ||||
|     select_item(m_objects_model->AddVolumeChild(GetSelection(), | ||||
|         name, m_bmp_modifiermesh)); | ||||
|     select_item(m_objects_model->AddVolumeChild(GetSelection(), name, type)); | ||||
| #ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
 | ||||
|     selection_changed(); | ||||
| #endif //no __WXOSX__ //__WXMSW__
 | ||||
|  | @ -927,8 +900,10 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con | |||
| void ObjectList::split(const bool split_part) | ||||
| { | ||||
|     const auto item = GetSelection(); | ||||
|     if (!item || m_selected_object_id < 0) | ||||
|     const int obj_idx = get_selected_obj_idx(); | ||||
|     if (!item || obj_idx < 0) | ||||
|         return; | ||||
| 
 | ||||
|     ModelVolume* volume; | ||||
|     if (!get_volume_by_item(split_part, item, volume)) return; | ||||
|     DynamicPrintConfig&	config = wxGetApp().preset_bundle->printers.get_edited_preset().config; | ||||
|  | @ -938,52 +913,54 @@ void ObjectList::split(const bool split_part) | |||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     auto model_object = (*m_objects)[m_selected_object_id]; | ||||
|     auto model_object = (*m_objects)[obj_idx]; | ||||
| 
 | ||||
|     if (split_part) { | ||||
|         auto parent = m_objects_model->GetParent(item); | ||||
|         m_objects_model->DeleteChildren(parent); | ||||
|     auto parent = m_objects_model->GetTopParent(item); | ||||
|     if (parent) | ||||
|         m_objects_model->DeleteVolumeChildren(parent); | ||||
|     else | ||||
|         parent = item; | ||||
| 
 | ||||
|         for (auto id = 0; id < model_object->volumes.size(); id++) | ||||
|             m_objects_model->AddVolumeChild(parent, model_object->volumes[id]->name, | ||||
|             model_object->volumes[id]->is_modifier() ? m_bmp_modifiermesh : m_bmp_solidmesh, | ||||
|             model_object->volumes[id]->config.has("extruder") ? | ||||
|             model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value : 0, | ||||
|             false); | ||||
|     for (auto id = 0; id < model_object->volumes.size(); id++) { | ||||
|         const auto vol_item = m_objects_model->AddVolumeChild(parent, model_object->volumes[id]->name, | ||||
|                                             model_object->volumes[id]->is_modifier() ?  | ||||
|                                                 ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART, | ||||
|                                             model_object->volumes[id]->config.has("extruder") ? | ||||
|                                                 model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value : 0, | ||||
|                                             false); | ||||
|         // add settings to the part, if it has those
 | ||||
|         auto opt_keys = model_object->volumes[id]->config.keys(); | ||||
|         if ( !(opt_keys.size() == 1 && opt_keys[0] == "extruder") ) { | ||||
|             select_item(m_objects_model->AddSettingsChild(vol_item)); | ||||
|             Collapse(vol_item); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (parent == item) | ||||
|         Expand(parent); | ||||
|     } | ||||
|     else { | ||||
|         for (auto id = 0; id < model_object->volumes.size(); id++) | ||||
|             m_objects_model->AddVolumeChild(item, model_object->volumes[id]->name, | ||||
|             m_bmp_solidmesh, | ||||
|             model_object->volumes[id]->config.has("extruder") ? | ||||
|             model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value : 0, | ||||
|             false); | ||||
|         Expand(item); | ||||
|     } | ||||
| 
 | ||||
|     m_parts_changed = true; | ||||
|     parts_changed(m_selected_object_id); | ||||
| 
 | ||||
|     // restores selection
 | ||||
|     _3DScene::get_canvas(wxGetApp().canvas3D())->get_selection().add_object(m_selected_object_id); | ||||
|     parts_changed(obj_idx); | ||||
| } | ||||
| 
 | ||||
| bool ObjectList::get_volume_by_item(const bool split_part, const wxDataViewItem& item, ModelVolume*& volume) | ||||
| { | ||||
|     if (!item || m_selected_object_id < 0) | ||||
|     auto obj_idx = get_selected_obj_idx(); | ||||
|     if (!item || obj_idx < 0) | ||||
|         return false; | ||||
|     const auto volume_id = m_objects_model->GetVolumeIdByItem(item); | ||||
| 
 | ||||
|     // object is selected
 | ||||
|     if (volume_id < 0) { | ||||
|         if (split_part) return false; | ||||
|         volume = (*m_objects)[m_selected_object_id]->volumes[0]; | ||||
|         if ( split_part || (*m_objects)[obj_idx]->volumes.size() > 1 )  | ||||
|             return false; | ||||
|         volume = (*m_objects)[obj_idx]->volumes[0]; | ||||
|     } | ||||
|     // volume is selected
 | ||||
|     else | ||||
|         volume = (*m_objects)[m_selected_object_id]->volumes[volume_id]; | ||||
|     if (volume) | ||||
|         return true; | ||||
|     return false; | ||||
|         volume = (*m_objects)[obj_idx]->volumes[volume_id]; | ||||
|      | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool ObjectList::is_splittable_object(const bool split_part) | ||||
|  | @ -991,20 +968,14 @@ bool ObjectList::is_splittable_object(const bool split_part) | |||
|     const wxDataViewItem item = GetSelection(); | ||||
|     if (!item) return false; | ||||
| 
 | ||||
|     wxDataViewItemArray children; | ||||
|     if (!split_part && m_objects_model->GetChildren(item, children) > 0) | ||||
|         return false; | ||||
| 
 | ||||
|     ModelVolume* volume; | ||||
|     if (!get_volume_by_item(split_part, item, volume) || !volume) | ||||
|         return false; | ||||
| 
 | ||||
|     TriangleMeshPtrs meshptrs = volume->mesh.split(); | ||||
|     bool splittable = meshptrs.size() > 1; | ||||
|     for (TriangleMesh* m : meshptrs) | ||||
|     { | ||||
|         delete m; | ||||
|     } | ||||
|     for (TriangleMesh* m : meshptrs) { delete m; } | ||||
| 
 | ||||
|     return splittable; | ||||
| } | ||||
| 
 | ||||
|  | @ -1017,7 +988,7 @@ void ObjectList::part_settings_changed() | |||
| 
 | ||||
| void ObjectList::parts_changed(int obj_idx) | ||||
| { | ||||
|     wxGetApp().plater()->changed_object(get_selected_obj_idx()); | ||||
|     wxGetApp().plater()->changed_object(obj_idx); | ||||
|     m_parts_changed = false; | ||||
| } | ||||
| 
 | ||||
|  | @ -1105,19 +1076,28 @@ void ObjectList::add_object_to_list(size_t obj_idx) | |||
|         m_objects_model->SetValue(variant, item, 0); | ||||
|     } | ||||
| 
 | ||||
|     // add volumes to the object
 | ||||
|     if (model_object->volumes.size() > 1) { | ||||
|         for (auto id = 0; id < model_object->volumes.size(); id++) | ||||
|             m_objects_model->AddVolumeChild(item, | ||||
|             model_object->volumes[id]->name, | ||||
|             m_bmp_solidmesh, | ||||
|             ModelVolume::MODEL_PART, | ||||
|             model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value, | ||||
|             false); | ||||
|         Expand(item); | ||||
|     } | ||||
| 
 | ||||
|     // add instances to the object, if it has those
 | ||||
|     if (model_object->instances.size()>1) | ||||
|         increase_object_instances(obj_idx, model_object->instances.size()); | ||||
| 
 | ||||
|     // add settings to the object, if it has those
 | ||||
|     auto opt_keys = model_object->config.keys(); | ||||
|     if ( !(opt_keys.size() == 1 && opt_keys[0] == "extruder") ) { | ||||
|         select_item(m_objects_model->AddSettingsChild(item)); | ||||
|         Collapse(item); | ||||
|     } | ||||
| 
 | ||||
| #ifndef __WXOSX__  | ||||
|     selection_changed(); | ||||
| #endif //__WXMSW__
 | ||||
|  | @ -1227,11 +1207,18 @@ void ObjectList::update_selections() | |||
|     auto& selection = _3DScene::get_canvas(wxGetApp().canvas3D())->get_selection(); | ||||
|     wxDataViewItemArray sels; | ||||
| 
 | ||||
|     for (auto idx: selection.get_volume_idxs()) | ||||
|     { | ||||
|         const auto gl_vol = selection.get_volume(idx); | ||||
|         sels.Add(m_objects_model->GetItemByVolumeId(gl_vol->object_idx(), gl_vol->volume_idx())); | ||||
|     if (selection.is_single_full_object()) { | ||||
|         for (auto idx : selection.get_volume_idxs()) { | ||||
|             const auto gl_vol = selection.get_volume(idx); | ||||
|             sels.Add(m_objects_model->GetItemByVolumeId(gl_vol->object_idx(), gl_vol->volume_idx())); | ||||
|         } | ||||
|     } | ||||
|     else if (selection.is_single_full_instance()) { | ||||
|         for (auto idx : selection.get_instance_idxs()) {             | ||||
|             sels.Add(m_objects_model->GetItemByInstanceId(selection.get_object_idx(), idx)); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     select_items(sels); | ||||
| } | ||||
| 
 | ||||
|  | @ -1256,7 +1243,7 @@ void ObjectList::update_selections_on_canvas() | |||
|         if (m_objects_model->GetItemType(item) == itVolume) { | ||||
|             const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetParent(item)); | ||||
|             const int vol_idx = m_objects_model->GetVolumeIdByItem(item); | ||||
|             selection.add_volume(obj_idx, vol_idx, as_single_selection); | ||||
|             selection.add_volume(obj_idx, vol_idx, 0, as_single_selection); | ||||
|         } | ||||
|         else if (m_objects_model->GetItemType(item) == itInstance) { | ||||
|             const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)); | ||||
|  | @ -1334,5 +1321,53 @@ void ObjectList::fix_multiselection_conflicts() | |||
|     m_prevent_list_events = false; | ||||
| } | ||||
| 
 | ||||
| ModelVolume* ObjectList::get_selected_model_volume() | ||||
| { | ||||
|     auto item = GetSelection(); | ||||
|     if (!item || m_objects_model->GetItemType(item) != itVolume) | ||||
|         return nullptr; | ||||
| 
 | ||||
|     const auto vol_idx = m_objects_model->GetVolumeIdByItem(item); | ||||
|     const auto obj_idx = get_selected_obj_idx(); | ||||
|     if (vol_idx < 0 || obj_idx < 0) | ||||
|         return nullptr; | ||||
| 
 | ||||
|     return (*m_objects)[obj_idx]->volumes[vol_idx]; | ||||
| } | ||||
| 
 | ||||
| void ObjectList::change_part_type() | ||||
| { | ||||
|     ModelVolume* volume = get_selected_model_volume(); | ||||
|     if (!volume) | ||||
|         return; | ||||
|     const auto type = volume->type(); | ||||
| 
 | ||||
|     const wxString names[] = { "Part", "Modifier", "Support Enforcer", "Support Blocker" }; | ||||
|      | ||||
|     auto new_type = wxGetSingleChoiceIndex("Type: ", _(L("Select type of part")), wxArrayString(4, names), type); | ||||
| 
 | ||||
|     if (new_type == type || new_type < 0) | ||||
|         return; | ||||
| 
 | ||||
|     const auto item = GetSelection(); | ||||
|     volume->set_type(static_cast<ModelVolume::Type>(new_type)); | ||||
|     m_objects_model->SetVolumeType(item, new_type); | ||||
| 
 | ||||
|     m_parts_changed = true; | ||||
|     parts_changed(get_selected_obj_idx()); | ||||
| 
 | ||||
|     // Update settings showing, if we have it
 | ||||
|     //(we show additional settings for Part and Modifier and hide it for Support Blocker/Enforcer)
 | ||||
|     const auto settings_item = m_objects_model->GetSettingsItem(item); | ||||
|     if (settings_item &&  | ||||
|         new_type == ModelVolume::SUPPORT_ENFORCER || new_type == ModelVolume::SUPPORT_BLOCKER) { | ||||
|         m_objects_model->Delete(settings_item); | ||||
|     } | ||||
|     else if (!settings_item &&  | ||||
|               new_type == ModelVolume::MODEL_PART || new_type == ModelVolume::PARAMETER_MODIFIER) { | ||||
|         select_item(m_objects_model->AddSettingsChild(item)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| } //namespace GUI
 | ||||
| } //namespace Slic3r 
 | ||||
|  | @ -28,10 +28,14 @@ class ObjectList : public wxDataViewCtrl | |||
| 
 | ||||
|     wxBitmap	m_bmp_modifiermesh; | ||||
|     wxBitmap	m_bmp_solidmesh; | ||||
|     wxBitmap	m_bmp_support_enforcer; | ||||
|     wxBitmap	m_bmp_support_blocker; | ||||
|     wxBitmap	m_bmp_manifold_warning; | ||||
|     wxBitmap	m_bmp_cog; | ||||
|     wxBitmap	m_bmp_split; | ||||
| 
 | ||||
|     std::vector<wxBitmap*> m_bmp_vector; | ||||
| 
 | ||||
|     int			m_selected_object_id = -1; | ||||
|     bool		m_prevent_list_events = false;		// We use this flag to avoid circular event handling Select() 
 | ||||
|                                                     // happens to fire a wxEVT_LIST_ITEM_SELECTED on OSX, whose event handler 
 | ||||
|  | @ -78,17 +82,16 @@ public: | |||
|     void                on_drop(wxDataViewEvent &event); | ||||
| 
 | ||||
|     void                get_settings_choice(wxMenu *menu, int id, bool is_part); | ||||
|     void                menu_item_add_generic(wxMenuItem* &menu, int id); | ||||
|     void                menu_item_add_generic(wxMenuItem* &menu, int id, const int type); | ||||
|     wxMenuItem*         menu_item_split(wxMenu* menu, int id); | ||||
|     wxMenuItem*         menu_item_settings(wxMenu* menu, int id, const bool is_part); | ||||
|     wxMenu*             create_add_part_popupmenu(); | ||||
|     wxMenu*             create_part_settings_popupmenu(); | ||||
|     wxMenu*             create_add_settings_popupmenu(bool is_part); | ||||
| 
 | ||||
|     void                load_subobject(bool is_modifier = false, bool is_lambda = false); | ||||
|     void                load_part(ModelObject* model_object, wxArrayString& part_names, const bool is_modifier); | ||||
|     void                load_lambda(ModelObject* model_object, wxArrayString& part_names, const bool is_modifier); | ||||
|     void                load_lambda(const std::string& type_name); | ||||
|     void                load_subobject(int type); | ||||
|     void                load_part(ModelObject* model_object, wxArrayString& part_names, int type); | ||||
|     void                load_generic_subobject(const std::string& type_name, const int type); | ||||
|     void                del_subobject_item(wxDataViewItem& item); | ||||
|     void                del_settings_from_config(); | ||||
|     void                del_instances_from_object(const int obj_idx); | ||||
|  | @ -142,6 +145,9 @@ public: | |||
|     void select_all(); | ||||
|     // correct current selections to avoid of the possible conflicts
 | ||||
|     void fix_multiselection_conflicts(); | ||||
| 
 | ||||
|     ModelVolume* get_selected_model_volume(); | ||||
|     void change_part_type(); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -268,6 +268,9 @@ void ObjectManipulation::update_settings_list() | |||
| 
 | ||||
| void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& selection) | ||||
| { | ||||
| #if ENABLE_MODELVOLUME_TRANSFORM | ||||
|     if (selection.is_single_full_instance()) | ||||
| #else | ||||
|     if (selection.is_single_full_object()) | ||||
|     { | ||||
|         auto obj_idx = selection.get_object_idx(); | ||||
|  | @ -284,30 +287,49 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele | |||
|             reset_settings_value(); | ||||
|     } | ||||
|     else if (selection.is_single_full_instance()) | ||||
| #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||
|     { | ||||
|         // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first
 | ||||
|         const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); | ||||
| #if ENABLE_MODELVOLUME_TRANSFORM | ||||
|         update_position_value(volume->get_instance_offset()); | ||||
|         update_rotation_value(volume->get_instance_rotation()); | ||||
|         update_scale_value(volume->get_instance_scaling_factor()); | ||||
| #else | ||||
|         update_position_value(volume->get_offset()); | ||||
|         update_rotation_value(volume->get_rotation()); | ||||
|         update_scale_value(volume->get_scaling_factor()); | ||||
| #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||
|         m_og->enable(); | ||||
|     } | ||||
|     else if (selection.is_wipe_tower()) | ||||
|     { | ||||
|         // the selection contains a single volume
 | ||||
|         const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); | ||||
| #if ENABLE_MODELVOLUME_TRANSFORM | ||||
|         update_position_value(volume->get_volume_offset()); | ||||
|         update_rotation_value(volume->get_volume_rotation()); | ||||
|         update_scale_value(volume->get_volume_scaling_factor()); | ||||
| #else | ||||
|         update_position_value(volume->get_offset()); | ||||
|         update_rotation_value(volume->get_rotation()); | ||||
|         update_scale_value(volume->get_scaling_factor()); | ||||
| #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||
|         m_og->enable(); | ||||
|     } | ||||
|     else if (selection.is_modifier()) | ||||
|     { | ||||
|         // the selection contains a single volume
 | ||||
|         const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); | ||||
| #if ENABLE_MODELVOLUME_TRANSFORM | ||||
|         update_position_value(volume->get_volume_offset()); | ||||
|         update_rotation_value(volume->get_volume_rotation()); | ||||
|         update_scale_value(volume->get_volume_scaling_factor()); | ||||
| #else | ||||
|         update_position_value(volume->get_offset()); | ||||
|         update_rotation_value(volume->get_rotation()); | ||||
|         update_scale_value(volume->get_scaling_factor()); | ||||
| #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||
|         m_og->enable(); | ||||
|     } | ||||
|     else | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| #include "../../libslic3r/libslic3r.h" | ||||
| #include "GUI_Preview.hpp" | ||||
| #include "GUI_App.hpp" | ||||
| #include "GUI.hpp" | ||||
| #include "AppConfig.hpp" | ||||
| #include "3DScene.hpp" | ||||
|  | @ -22,6 +23,7 @@ | |||
| namespace Slic3r { | ||||
| namespace GUI { | ||||
| 
 | ||||
| 
 | ||||
| Preview::Preview(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data) | ||||
|     : m_canvas(nullptr) | ||||
|     , m_double_slider_sizer(nullptr) | ||||
|  | @ -482,6 +484,11 @@ void Preview::create_double_slider() | |||
|         if (IsShown()) | ||||
|             m_canvas->Refresh(); | ||||
|     }); | ||||
| 
 | ||||
|     Bind(wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) { | ||||
|             auto& config = wxGetApp().preset_bundle->project_config; | ||||
|             ((config.option<ConfigOptionFloats>("colorprint_heights"))->values) = (m_slider->GetTicksValues()); | ||||
|         }); | ||||
| } | ||||
| 
 | ||||
| void Preview::update_double_slider(bool force_sliders_full_range) | ||||
|  | @ -495,6 +502,11 @@ void Preview::update_double_slider(bool force_sliders_full_range) | |||
|     m_slider->SetMaxValue(layers_z.size() - 1); | ||||
|     m_slider->SetSliderValues(values); | ||||
| 
 | ||||
|     const auto& config = wxGetApp().preset_bundle->project_config; | ||||
|     const std::vector<double> &ticks_from_config = (config.option<ConfigOptionFloats>("colorprint_heights"))->values; | ||||
| 
 | ||||
|     m_slider->SetTicksValues(ticks_from_config); | ||||
| 
 | ||||
|     set_double_slider_thumbs(force_sliders_full_range, layers_z, z_low, z_high); | ||||
| } | ||||
| 
 | ||||
|  | @ -515,6 +527,15 @@ void Preview::fill_slider_values(std::vector<std::pair<int, double>> &values, | |||
|                     break; | ||||
|                 } | ||||
|     } | ||||
| 
 | ||||
|     // All ticks that would end up outside the slider range should be erased.
 | ||||
|     // TODO: this should probably be placed into more appropriate part of code,
 | ||||
|     //  this way it relies on the Preview tab being active.
 | ||||
|     auto& config = wxGetApp().preset_bundle->project_config; | ||||
|     std::vector<double> &ticks_from_config = (config.option<ConfigOptionFloats>("colorprint_heights"))->values; | ||||
|     ticks_from_config.erase(std::remove_if(ticks_from_config.begin(), ticks_from_config.end(), | ||||
|                                            [values](double val) { return values.back().second < val; }), | ||||
|                             ticks_from_config.end()); | ||||
| } | ||||
| 
 | ||||
| void Preview::set_double_slider_thumbs(const bool force_sliders_full_range, | ||||
|  |  | |||
|  | @ -49,15 +49,15 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent, | |||
| 
 | ||||
| 		def.type = coFloat; | ||||
| 		def.default_value = new ConfigOptionFloat{ 1.0 }; | ||||
| 		def.label = L("L"); | ||||
| 		def.label = L("Length"); | ||||
| 		Option option(def, "l"); | ||||
| 		optgroup->append_single_option_line(option); | ||||
| 		 | ||||
| 		def.label = L("W"); | ||||
| 		def.label = L("Width"); | ||||
| 		option = Option(def, "w"); | ||||
| 		optgroup->append_single_option_line(option); | ||||
| 		 | ||||
| 		def.label = L("H"); | ||||
| 		def.label = L("Height"); | ||||
| 		option = Option(def, "h"); | ||||
| 		optgroup->append_single_option_line(option); | ||||
| 	} | ||||
|  | @ -112,7 +112,7 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent, | |||
| 
 | ||||
| 		def.type = coFloat; | ||||
| 		def.default_value = new ConfigOptionFloat{ 1.0 }; | ||||
| 		def.label = L("H"); | ||||
| 		def.label = L("Height"); | ||||
| 		auto option = Option(def, "slab_h"); | ||||
| 		optgroup->append_single_option_line(option); | ||||
| 
 | ||||
|  |  | |||
|  | @ -596,8 +596,7 @@ void MainFrame::load_config_file(wxString file/* = wxEmptyString*/) | |||
|         //     if (Slic3r::GUI::catch_error(this))
 | ||||
|         //         return;
 | ||||
|     } | ||||
|     for (auto tab : m_options_tabs ) | ||||
|         tab.second->load_current_preset(); | ||||
| 	wxGetApp().load_current_presets(); | ||||
|     wxGetApp().app_config->update_config_dir(get_dir_name(file)); | ||||
|     m_last_config = file; | ||||
| } | ||||
|  | @ -659,8 +658,7 @@ void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool re | |||
|     } | ||||
| 
 | ||||
|     // Load the currently selected preset into the GUI, update the preset selection box.
 | ||||
|     for (auto tab : m_options_tabs) | ||||
|         tab.second->load_current_preset(); | ||||
| 	wxGetApp().load_current_presets(); | ||||
| 
 | ||||
|     const auto message = wxString::Format(_(L("%d presets successfully imported.")), presets_imported); | ||||
|     Slic3r::GUI::show_info(this, message, "Info"); | ||||
|  |  | |||
|  | @ -22,18 +22,18 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co | |||
|     // is the normal type.
 | ||||
|     if (opt.gui_type.compare("select") == 0) { | ||||
|     } else if (opt.gui_type.compare("select_open") == 0) { | ||||
| 		m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(parent(), opt, id))); | ||||
| 		m_fields.emplace(id, std::move(Choice::Create<Choice>(parent(), opt, id))); | ||||
|     } else if (opt.gui_type.compare("color") == 0) { | ||||
| 		m_fields.emplace(id, STDMOVE(ColourPicker::Create<ColourPicker>(parent(), opt, id))); | ||||
| 		m_fields.emplace(id, std::move(ColourPicker::Create<ColourPicker>(parent(), opt, id))); | ||||
|     } else if (opt.gui_type.compare("f_enum_open") == 0 ||  | ||||
|                 opt.gui_type.compare("i_enum_open") == 0 || | ||||
|                 opt.gui_type.compare("i_enum_closed") == 0) { | ||||
| 		m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(parent(), opt, id))); | ||||
| 		m_fields.emplace(id, std::move(Choice::Create<Choice>(parent(), opt, id))); | ||||
|     } else if (opt.gui_type.compare("slider") == 0) { | ||||
| 		m_fields.emplace(id, STDMOVE(SliderCtrl::Create<SliderCtrl>(parent(), opt, id))); | ||||
| 		m_fields.emplace(id, std::move(SliderCtrl::Create<SliderCtrl>(parent(), opt, id))); | ||||
|     } else if (opt.gui_type.compare("i_spin") == 0) { // Spinctrl
 | ||||
|     } else if (opt.gui_type.compare("legend") == 0) { // StaticText
 | ||||
| 		m_fields.emplace(id, STDMOVE(StaticText::Create<StaticText>(parent(), opt, id))); | ||||
| 		m_fields.emplace(id, std::move(StaticText::Create<StaticText>(parent(), opt, id))); | ||||
|     } else {  | ||||
|         switch (opt.type) { | ||||
|             case coFloatOrPercent: | ||||
|  | @ -43,21 +43,21 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co | |||
| 			case coPercents: | ||||
| 			case coString: | ||||
| 			case coStrings: | ||||
| 				m_fields.emplace(id, STDMOVE(TextCtrl::Create<TextCtrl>(parent(), opt, id))); | ||||
| 				m_fields.emplace(id, std::move(TextCtrl::Create<TextCtrl>(parent(), opt, id))); | ||||
|                 break; | ||||
| 			case coBool: | ||||
| 			case coBools: | ||||
| 				m_fields.emplace(id, STDMOVE(CheckBox::Create<CheckBox>(parent(), opt, id))); | ||||
| 				m_fields.emplace(id, std::move(CheckBox::Create<CheckBox>(parent(), opt, id))); | ||||
| 				break; | ||||
| 			case coInt: | ||||
| 			case coInts: | ||||
| 				m_fields.emplace(id, STDMOVE(SpinCtrl::Create<SpinCtrl>(parent(), opt, id))); | ||||
| 				m_fields.emplace(id, std::move(SpinCtrl::Create<SpinCtrl>(parent(), opt, id))); | ||||
| 				break; | ||||
|             case coEnum: | ||||
| 				m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(parent(), opt, id))); | ||||
| 				m_fields.emplace(id, std::move(Choice::Create<Choice>(parent(), opt, id))); | ||||
| 				break; | ||||
|             case coPoints: | ||||
| 				m_fields.emplace(id, STDMOVE(PointCtrl::Create<PointCtrl>(parent(), opt, id))); | ||||
| 				m_fields.emplace(id, std::move(PointCtrl::Create<PointCtrl>(parent(), opt, id))); | ||||
| 				break; | ||||
|             case coNone:   break; | ||||
|             default: | ||||
|  |  | |||
|  | @ -1154,16 +1154,25 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path> &input_ | |||
|         try { | ||||
|             if (type_3mf || type_zip_amf) { | ||||
|                 DynamicPrintConfig config; | ||||
|                 config.apply(FullPrintConfig::defaults()); | ||||
|                 model = Slic3r::Model::read_from_archive(path.string(), &config, false); | ||||
|                 Preset::normalize(config); | ||||
|                 wxGetApp().preset_bundle->load_config_model(filename.string(), std::move(config)); | ||||
|                 for (const auto &kv : main_frame->options_tabs()) { kv.second->load_current_preset(); } | ||||
|                 { | ||||
|                     DynamicPrintConfig config_loaded; | ||||
|                     model = Slic3r::Model::read_from_archive(path.string(), &config_loaded, false); | ||||
|                     if (! config_loaded.empty()) { | ||||
| 						// Based on the printer technology field found in the loaded config, select the base for the config,
 | ||||
| 						PrinterTechnology printer_technology = Preset::printer_technology(config_loaded); | ||||
| 						config.apply(printer_technology == ptFFF ? | ||||
|                             static_cast<const ConfigBase&>(FullPrintConfig::defaults()) :  | ||||
|                             static_cast<const ConfigBase&>(SLAFullPrintConfig::defaults())); | ||||
|                         // and place the loaded config over the base.
 | ||||
|                         config += std::move(config_loaded); | ||||
|                     } | ||||
|                 } | ||||
|                 if (! config.empty()) { | ||||
|                     Preset::normalize(config); | ||||
|                     wxGetApp().preset_bundle->load_config_model(filename.string(), std::move(config)); | ||||
| 					wxGetApp().load_current_presets(); | ||||
| 				} | ||||
|                 wxGetApp().app_config->update_config_dir(path.parent_path().string()); | ||||
|                 // forces the update of the config here, or it will invalidate the imported layer heights profile if done using the timer
 | ||||
|                 // and if the config contains a "layer_height" different from the current defined one
 | ||||
|                 // TODO:
 | ||||
|                 // $self->async_apply_config;
 | ||||
|             } else { | ||||
|                 model = Slic3r::Model::read_from_file(path.string(), nullptr, false); | ||||
|                 for (auto obj : model.objects) | ||||
|  | @ -1230,7 +1239,9 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path> &input_ | |||
| std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &model_objects) | ||||
| { | ||||
|     const BoundingBoxf bed_shape = bed_shape_bb(); | ||||
| #if !ENABLE_MODELVOLUME_TRANSFORM | ||||
|     const Vec3d bed_center = Slic3r::to_3d(bed_shape.center().cast<double>(), 0.0); | ||||
| #endif // !ENABLE_MODELVOLUME_TRANSFORM
 | ||||
|     const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast<double>(), 1.0); | ||||
| 
 | ||||
|     bool need_arrange = false; | ||||
|  | @ -1249,8 +1260,12 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode | |||
| 
 | ||||
|             // add a default instance and center object around origin
 | ||||
|             object->center_around_origin();  // also aligns object to Z = 0
 | ||||
|             auto *instance = object->add_instance(); | ||||
|             ModelInstance* instance = object->add_instance(); | ||||
| #if ENABLE_MODELVOLUME_TRANSFORM | ||||
|             instance->set_offset(Slic3r::to_3d(bed_shape.center().cast<double>(), -object->origin_translation(2))); | ||||
| #else | ||||
|             instance->set_offset(bed_center); | ||||
| #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||
|         } | ||||
| 
 | ||||
|         const Vec3d size = object->bounding_box().size(); | ||||
|  | @ -1438,7 +1453,9 @@ void Plater::priv::mirror(Axis axis) | |||
| 
 | ||||
| void Plater::priv::arrange() | ||||
| { | ||||
| 	this->background_process.stop(); | ||||
|     main_frame->app_controller()->arrange_model(); | ||||
|     this->schedule_background_process(); | ||||
| 
 | ||||
|     // ignore arrange failures on purpose: user has visual feedback and we don't need to warn him
 | ||||
|     // when parts don't fit in print bed
 | ||||
|  | @ -1471,13 +1488,7 @@ void Plater::priv::split_object() | |||
|     { | ||||
|         unsigned int counter = 1; | ||||
|         for (ModelObject* m : new_objects) | ||||
|         { | ||||
|             m->name = current_model_object->name + "_" + std::to_string(counter++); | ||||
|             for (ModelInstance* i : current_model_object->instances) | ||||
|             { | ||||
|                 m->add_instance(*i); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         remove(obj_idx); | ||||
| 
 | ||||
|  | @ -2108,7 +2119,8 @@ void Plater::export_amf() | |||
|     wxString path = dialog->GetPath(); | ||||
|     auto path_cstr = path.c_str(); | ||||
| 
 | ||||
|     if (Slic3r::store_amf(path_cstr, &p->model, &p->print, dialog->get_checkbox_value())) { | ||||
| 	DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config(); | ||||
| 	if (Slic3r::store_amf(path_cstr, &p->model, dialog->get_checkbox_value() ? &cfg : nullptr)) { | ||||
|         // Success
 | ||||
|         p->statusbar()->set_status_text(wxString::Format(_(L("AMF file exported to %s")), path)); | ||||
|     } else { | ||||
|  | @ -2127,7 +2139,8 @@ void Plater::export_3mf() | |||
|     wxString path = dialog->GetPath(); | ||||
|     auto path_cstr = path.c_str(); | ||||
| 
 | ||||
|     if (Slic3r::store_3mf(path_cstr, &p->model, &p->print, dialog->get_checkbox_value())) { | ||||
| 	DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config(); | ||||
| 	if (Slic3r::store_3mf(path_cstr, &p->model, dialog->get_checkbox_value() ? &cfg : nullptr)) { | ||||
|         // Success
 | ||||
|         p->statusbar()->set_status_text(wxString::Format(_(L("3MF file exported to %s")), path)); | ||||
|     } else { | ||||
|  | @ -2256,7 +2269,9 @@ void Plater::changed_object(int obj_idx) | |||
|     if (list->is_parts_changed()) { | ||||
|         // recenter and re - align to Z = 0
 | ||||
|         auto model_object = p->model.objects[obj_idx]; | ||||
| #if !ENABLE_MODELVOLUME_TRANSFORM | ||||
|         model_object->center_around_origin(); | ||||
| #endif // !ENABLE_MODELVOLUME_TRANSFORM
 | ||||
|         model_object->ensure_on_bed(); | ||||
|         _3DScene::reload_scene(p->canvas3D, false); | ||||
|     } | ||||
|  |  | |||
|  | @ -170,10 +170,12 @@ void Preset::set_num_extruders(DynamicPrintConfig &config, unsigned int num_extr | |||
| { | ||||
|     const auto &defaults = FullPrintConfig::defaults(); | ||||
|     for (const std::string &key : Preset::nozzle_options()) { | ||||
| 		if (key == "default_filament_profile") | ||||
| 			continue; | ||||
|         auto *opt = config.option(key, false); | ||||
|         assert(opt != nullptr); | ||||
|         assert(opt->is_vector()); | ||||
|         if (opt != nullptr && opt->is_vector() && key != "default_filament_profile") | ||||
|         if (opt != nullptr && opt->is_vector()) | ||||
|             static_cast<ConfigOptionVectorBase*>(opt)->resize(num_extruders, defaults.option(key)); | ||||
|     } | ||||
| } | ||||
|  | @ -202,8 +204,7 @@ void Preset::normalize(DynamicPrintConfig &config) | |||
|         // The following keys are mandatory for the UI, but they are not part of FullPrintConfig, therefore they are handled separately.
 | ||||
|         for (const std::string &key : { "filament_settings_id" }) { | ||||
|             auto *opt = config.option(key, false); | ||||
|             assert(opt != nullptr); | ||||
|             assert(opt->type() == coStrings); | ||||
|             assert(opt == nullptr || opt->type() == coStrings); | ||||
|             if (opt != nullptr && opt->type() == coStrings) | ||||
|                 static_cast<ConfigOptionStrings*>(opt)->values.resize(n, std::string()); | ||||
|         } | ||||
|  | @ -534,7 +535,8 @@ Preset& PresetCollection::load_external_preset( | |||
|     cfg.apply_only(config, cfg.keys(), true); | ||||
|     // Is there a preset already loaded with the name stored inside the config?
 | ||||
|     std::deque<Preset>::iterator it = this->find_preset_internal(original_name); | ||||
|     if (it != m_presets.end() && it->name == original_name && profile_print_params_same(it->config, cfg)) { | ||||
| 	bool                         found = it != m_presets.end() && it->name == original_name; | ||||
|     if (found && profile_print_params_same(it->config, cfg)) { | ||||
|         // The preset exists and it matches the values stored inside config.
 | ||||
|         if (select) | ||||
|             this->select_preset(it - m_presets.begin()); | ||||
|  | @ -542,7 +544,7 @@ Preset& PresetCollection::load_external_preset( | |||
|     } | ||||
|     // Update the "inherits" field.
 | ||||
|     std::string &inherits = Preset::inherits(cfg); | ||||
|     if (it != m_presets.end() && inherits.empty()) { | ||||
|     if (found && inherits.empty()) { | ||||
|         // There is a profile with the same name already loaded. Should we update the "inherits" field?
 | ||||
|         if (it->vendor == nullptr) | ||||
|             inherits = it->inherits(); | ||||
|  |  | |||
|  | @ -36,6 +36,7 @@ | |||
| namespace Slic3r { | ||||
| 
 | ||||
| static std::vector<std::string> s_project_options { | ||||
|     "colorprint_heights", | ||||
|     "wiping_volumes_extruders", | ||||
|     "wiping_volumes_matrix" | ||||
| }; | ||||
|  | @ -617,18 +618,6 @@ void PresetBundle::load_config_file(const std::string &path) | |||
|     } | ||||
| } | ||||
| 
 | ||||
| void PresetBundle::load_config_string(const char* str, const char* source_filename) | ||||
| { | ||||
|     if (str != nullptr) | ||||
|     { | ||||
|         DynamicPrintConfig config; | ||||
|         config.apply(FullPrintConfig::defaults()); | ||||
|         config.load_from_gcode_string(str); | ||||
|         Preset::normalize(config); | ||||
|         load_config_file_config((source_filename == nullptr) ? "" : source_filename, true, std::move(config)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Load a config file from a boost property_tree. This is a private method called from load_config_file.
 | ||||
| void PresetBundle::load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config) | ||||
| { | ||||
|  | @ -676,7 +665,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool | |||
|         compatible_printers_condition = compatible_printers_condition_values[idx]; | ||||
| 		if (is_external) | ||||
|             presets.load_external_preset(name_or_path, name, | ||||
|                 config.opt_string((i_group == 0) ? ((printer_technology == ptFFF) ? "print_settings_id" : "sla_material_id") :  "printer_settings_id", true), | ||||
|                 config.opt_string((i_group == 0) ? ((printer_technology == ptFFF) ? "print_settings_id" : "sla_material_settings_id") :  "printer_settings_id", true), | ||||
|                 config); | ||||
|         else | ||||
|             presets.load_preset(presets.path_from_name(name), name, config).save(); | ||||
|  |  | |||
|  | @ -84,11 +84,6 @@ public: | |||
|     // If the file is loaded successfully, its print / filament / printer profiles will be activated.
 | ||||
|     void                        load_config_file(const std::string &path); | ||||
| 
 | ||||
|     // Load an external config source containing the print, filament and printer presets.
 | ||||
|     // The given string must contain the full set of parameters (same as those exported to gcode).
 | ||||
|     // If the string is parsed successfully, its print / filament / printer profiles will be activated.
 | ||||
|     void                        load_config_string(const char* str, const char* source_filename = nullptr); | ||||
| 
 | ||||
|     // Load a config bundle file, into presets and store the loaded presets into separate files
 | ||||
|     // of the local configuration directory.
 | ||||
|     // Load settings into the provided settings instance.
 | ||||
|  |  | |||
|  | @ -108,7 +108,7 @@ RammingPanel::RammingPanel(wxWindow* parent, const std::string& parameters) | |||
|     m_widget_time->Bind(wxEVT_CHAR,[](wxKeyEvent&){});      // do nothing - prevents the user to change the value
 | ||||
|     m_widget_volume->Bind(wxEVT_CHAR,[](wxKeyEvent&){});    // do nothing - prevents the user to change the value   
 | ||||
|     Bind(EVT_WIPE_TOWER_CHART_CHANGED,[this](wxCommandEvent&) {m_widget_volume->SetValue(m_chart->get_volume()); m_widget_time->SetValue(m_chart->get_time());} ); | ||||
|     Refresh(this); | ||||
|     Refresh(true); // erase background
 | ||||
| } | ||||
| 
 | ||||
| void RammingPanel::line_parameters_changed() { | ||||
|  |  | |||
|  | @ -9,6 +9,9 @@ | |||
| #include <wx/numformatter.h> | ||||
| #include "GUI_App.hpp" | ||||
| #include "GUI_ObjectList.hpp" | ||||
| #include "Model.hpp" | ||||
| 
 | ||||
| wxDEFINE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); | ||||
| 
 | ||||
| wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description, | ||||
|     std::function<void(wxCommandEvent& event)> cb, const std::string& icon, wxEvtHandler* event_handler) | ||||
|  | @ -400,10 +403,9 @@ bool PrusaObjectDataViewModelNode::update_settings_digest(const std::vector<std: | |||
| 
 | ||||
|     m_opt_categories = categories; | ||||
|     m_name = wxEmptyString; | ||||
| //     m_icon = m_empty_icon;
 | ||||
|     m_bmp = m_empty_bmp; | ||||
| 
 | ||||
|     std::map<std::string, wxBitmap>& categories_icon = Slic3r::GUI::wxGetApp().obj_list()->CATEGORY_ICON;//Slic3r::GUI::get_category_icon();
 | ||||
|     std::map<std::string, wxBitmap>& categories_icon = Slic3r::GUI::wxGetApp().obj_list()->CATEGORY_ICON; | ||||
| 
 | ||||
|     for (auto& cat : m_opt_categories) | ||||
|         m_name += cat + "; "; | ||||
|  | @ -453,29 +455,34 @@ wxDataViewItem PrusaObjectDataViewModel::Add(const wxString &name) | |||
| 
 | ||||
| wxDataViewItem PrusaObjectDataViewModel::AddVolumeChild(const wxDataViewItem &parent_item, | ||||
| 													const wxString &name, | ||||
|                                                     const wxBitmap& icon, | ||||
|                                                     const int volume_type, | ||||
|                                                     const int extruder/* = 0*/, | ||||
|                                                     const bool create_frst_child/* = true*/) | ||||
| { | ||||
| 	PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent_item.GetID(); | ||||
| 	if (!root) return wxDataViewItem(0); | ||||
| 
 | ||||
|     const wxString extruder_str = extruder == 0 ? "default" : wxString::Format("%d", extruder); | ||||
|     wxString extruder_str = extruder == 0 ? "default" : wxString::Format("%d", extruder); | ||||
| 
 | ||||
|     // because of istance_root is a last item of the object
 | ||||
|     int insert_position = root->GetChildCount() - 1; | ||||
|     if (insert_position < 0 || root->GetNthChild(insert_position)->m_type != itInstanceRoot) | ||||
|         insert_position = -1; | ||||
| 
 | ||||
|     if (create_frst_child && root->m_volumes_cnt == 0) | ||||
| 	{ | ||||
| 		const auto bmp_solid_mesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG); | ||||
| 		const auto node = new PrusaObjectDataViewModelNode(root, root->m_name, bmp_solid_mesh, extruder_str, 0); | ||||
| 		root->Append(node); | ||||
| 		const auto node = new PrusaObjectDataViewModelNode(root, root->m_name, *m_volume_bmps[0], extruder_str, 0); | ||||
|         insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position); | ||||
| 		// notify control
 | ||||
| 		const wxDataViewItem child((void*)node); | ||||
| 		ItemAdded(parent_item, child); | ||||
| 
 | ||||
|         root->m_volumes_cnt++; | ||||
|         if (insert_position > 0) insert_position++; | ||||
| 	} | ||||
| 
 | ||||
|     const auto node = new PrusaObjectDataViewModelNode(root, name, icon, extruder_str, root->m_volumes_cnt); | ||||
| 	root->Append(node); | ||||
|     const auto node = new PrusaObjectDataViewModelNode(root, name, *m_volume_bmps[volume_type], extruder_str, root->m_volumes_cnt); | ||||
|     insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position); | ||||
| 	// notify control
 | ||||
| 	const wxDataViewItem child((void*)node); | ||||
|     ItemAdded(parent_item, child); | ||||
|  | @ -499,15 +506,13 @@ wxDataViewItem PrusaObjectDataViewModel::AddSettingsChild(const wxDataViewItem & | |||
| 
 | ||||
| int get_istances_root_idx(PrusaObjectDataViewModelNode *parent_node) | ||||
| { | ||||
|     int inst_root_id = -1; | ||||
|     int stop_search_i = parent_node->GetChildCount(); | ||||
|     if (stop_search_i > 2) stop_search_i = 2; | ||||
|     for (int i = 0; i < stop_search_i; ++i) | ||||
|         if (parent_node->GetNthChild(i)->m_type & itInstanceRoot) { | ||||
|             inst_root_id = i; | ||||
|             break; | ||||
|         } | ||||
|     return inst_root_id; | ||||
|     // because of istance_root is a last item of the object
 | ||||
|     const int inst_root_idx = parent_node->GetChildCount()-1; | ||||
| 
 | ||||
|     if (inst_root_idx < 0 || parent_node->GetNthChild(inst_root_idx)->m_type == itInstanceRoot)  | ||||
|         return inst_root_idx; | ||||
|      | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| wxDataViewItem PrusaObjectDataViewModel::AddInstanceChild(const wxDataViewItem &parent_item, size_t num) | ||||
|  | @ -524,8 +529,7 @@ wxDataViewItem PrusaObjectDataViewModel::AddInstanceChild(const wxDataViewItem & | |||
|     const wxDataViewItem inst_root_item((void*)inst_root_node); | ||||
| 
 | ||||
|     if (inst_root_id < 0) { | ||||
|         const unsigned insert_pos = parent_node->GetChildCount() == 0 || parent_node->GetNthChild(0)->m_type != itSettings ? 0 : 1; | ||||
|         parent_node->Insert(inst_root_node, insert_pos); | ||||
|         parent_node->Append(inst_root_node); | ||||
|         // notify control
 | ||||
|         ItemAdded(parent_item, inst_root_item); | ||||
|         if (num == 1) num++; | ||||
|  | @ -563,6 +567,10 @@ wxDataViewItem PrusaObjectDataViewModel::Delete(const wxDataViewItem &item) | |||
| 		auto id = node_parent->GetChildren().Index(node); | ||||
|         auto idx = node->GetIdx(); | ||||
| 		node_parent->GetChildren().Remove(node); | ||||
| 
 | ||||
|         if (node->m_type == itVolume) | ||||
|             node_parent->m_volumes_cnt--; | ||||
| 
 | ||||
| 		if (id > 0) {  | ||||
| 			if(id == node_parent->GetChildCount()) id--; | ||||
| 			ret_item = wxDataViewItem(node_parent->GetChildren().Item(id)); | ||||
|  | @ -692,6 +700,9 @@ void PrusaObjectDataViewModel::DeleteChildren(wxDataViewItem& parent) | |||
|         auto item = wxDataViewItem(node); | ||||
|         children.RemoveAt(id); | ||||
| 
 | ||||
|         if (node->m_type == itVolume) | ||||
|             root->m_volumes_cnt--; | ||||
| 
 | ||||
|         // free the node
 | ||||
|         delete node; | ||||
| 
 | ||||
|  | @ -705,6 +716,39 @@ void PrusaObjectDataViewModel::DeleteChildren(wxDataViewItem& parent) | |||
| #endif //__WXGTK__
 | ||||
| } | ||||
| 
 | ||||
| void PrusaObjectDataViewModel::DeleteVolumeChildren(wxDataViewItem& parent) | ||||
| { | ||||
|     PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent.GetID(); | ||||
|     if (!root)      // happens if item.IsOk()==false
 | ||||
|         return; | ||||
| 
 | ||||
|     // first remove the node from the parent's array of children;
 | ||||
|     // NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_
 | ||||
|     //       thus removing the node from it doesn't result in freeing it
 | ||||
|     auto& children = root->GetChildren(); | ||||
|     for (int id = root->GetChildCount() - 1; id >= 0; --id) | ||||
|     { | ||||
|         auto node = children[id]; | ||||
|         if (node->m_type != itVolume) | ||||
|             continue; | ||||
| 
 | ||||
|         auto item = wxDataViewItem(node); | ||||
|         children.RemoveAt(id); | ||||
|         root->m_volumes_cnt--; | ||||
| 
 | ||||
|         // free the node
 | ||||
|         delete node; | ||||
| 
 | ||||
|         // notify control
 | ||||
|         ItemDeleted(parent, item); | ||||
|     } | ||||
| 
 | ||||
|     // set m_containet to FALSE if parent has no child
 | ||||
| #ifndef __WXGTK__ | ||||
|     root->m_container = false; | ||||
| #endif //__WXGTK__
 | ||||
| } | ||||
| 
 | ||||
| wxDataViewItem PrusaObjectDataViewModel::GetItemById(int obj_idx) | ||||
| { | ||||
| 	if (obj_idx >= m_objects.size()) | ||||
|  | @ -718,7 +762,7 @@ wxDataViewItem PrusaObjectDataViewModel::GetItemById(int obj_idx) | |||
| 
 | ||||
| wxDataViewItem PrusaObjectDataViewModel::GetItemByVolumeId(int obj_idx, int volume_idx) | ||||
| { | ||||
| 	if (obj_idx >= m_objects.size()) { | ||||
| 	if (obj_idx >= m_objects.size() || obj_idx < 0) { | ||||
| 		printf("Error! Out of objects range.\n"); | ||||
| 		return wxDataViewItem(0); | ||||
| 	} | ||||
|  | @ -740,6 +784,25 @@ wxDataViewItem PrusaObjectDataViewModel::GetItemByVolumeId(int obj_idx, int volu | |||
|     return wxDataViewItem(0); | ||||
| } | ||||
| 
 | ||||
| wxDataViewItem PrusaObjectDataViewModel::GetItemByInstanceId(int obj_idx, int inst_idx) | ||||
| { | ||||
|     if (obj_idx >= m_objects.size() || obj_idx < 0) { | ||||
|         printf("Error! Out of objects range.\n"); | ||||
|         return wxDataViewItem(0); | ||||
|     } | ||||
| 
 | ||||
|     auto instances_item = GetInstanceRootItem(wxDataViewItem(m_objects[obj_idx])); | ||||
|     if (!instances_item) | ||||
|         return wxDataViewItem(0); | ||||
| 
 | ||||
|     auto parent = (PrusaObjectDataViewModelNode*)instances_item.GetID();; | ||||
|     for (size_t i = 0; i < parent->GetChildCount(); i++) | ||||
|         if (parent->GetNthChild(i)->m_idx == inst_idx) | ||||
|             return wxDataViewItem(parent->GetNthChild(i)); | ||||
| 
 | ||||
|     return wxDataViewItem(0); | ||||
| } | ||||
| 
 | ||||
| int PrusaObjectDataViewModel::GetIdByItem(const wxDataViewItem& item) | ||||
| { | ||||
| 	wxASSERT(item.IsOk()); | ||||
|  | @ -1015,21 +1078,33 @@ ItemType PrusaObjectDataViewModel::GetItemType(const wxDataViewItem &item) const | |||
|     return node->m_type; | ||||
| } | ||||
| 
 | ||||
| wxDataViewItem PrusaObjectDataViewModel::GetSettingsItem(const wxDataViewItem &item) const | ||||
| wxDataViewItem PrusaObjectDataViewModel::GetItemByType(const wxDataViewItem &parent_item, ItemType type) const  | ||||
| { | ||||
|     if (!item.IsOk()) | ||||
|     if (!parent_item.IsOk()) | ||||
|         return wxDataViewItem(0); | ||||
| 
 | ||||
|     PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); | ||||
|     PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)parent_item.GetID(); | ||||
|     if (node->GetChildCount() == 0) | ||||
|         return wxDataViewItem(0); | ||||
| 
 | ||||
|     if (node->GetNthChild(0)->m_type == itSettings) | ||||
|         return wxDataViewItem((void*)node->GetNthChild(0)); | ||||
|     for (int i = 0; i < node->GetChildCount(); i++) { | ||||
|         if (node->GetNthChild(i)->m_type == type) | ||||
|             return wxDataViewItem((void*)node->GetNthChild(i)); | ||||
|     } | ||||
| 
 | ||||
|     return wxDataViewItem(0); | ||||
| } | ||||
| 
 | ||||
| wxDataViewItem PrusaObjectDataViewModel::GetSettingsItem(const wxDataViewItem &item) const | ||||
| { | ||||
|     return GetItemByType(item, itSettings); | ||||
| } | ||||
| 
 | ||||
| wxDataViewItem PrusaObjectDataViewModel::GetInstanceRootItem(const wxDataViewItem &item) const | ||||
| { | ||||
|     return GetItemByType(item, itInstanceRoot); | ||||
| } | ||||
| 
 | ||||
| bool PrusaObjectDataViewModel::IsSettingsItem(const wxDataViewItem &item) const | ||||
| { | ||||
|     if (!item.IsOk()) | ||||
|  | @ -1050,6 +1125,16 @@ void PrusaObjectDataViewModel::UpdateSettingsDigest(const wxDataViewItem &item, | |||
|     ItemChanged(item); | ||||
| } | ||||
| 
 | ||||
| void PrusaObjectDataViewModel::SetVolumeType(const wxDataViewItem &item, const int type) | ||||
| { | ||||
|     if (!item.IsOk() || GetItemType(item) != itVolume)  | ||||
|         return; | ||||
| 
 | ||||
|     PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); | ||||
|     node->SetBitmap(*m_volume_bmps[type]); | ||||
|     ItemChanged(item); | ||||
| } | ||||
| 
 | ||||
| //-----------------------------------------------------------------------------
 | ||||
| // PrusaDataViewBitmapText
 | ||||
| //-----------------------------------------------------------------------------
 | ||||
|  | @ -1106,7 +1191,6 @@ wxSize PrusaBitmapTextRenderer::GetSize() const | |||
| // ----------------------------------------------------------------------------
 | ||||
| // PrusaDoubleSlider
 | ||||
| // ----------------------------------------------------------------------------
 | ||||
| 
 | ||||
| PrusaDoubleSlider::PrusaDoubleSlider(wxWindow *parent, | ||||
|                                         wxWindowID id, | ||||
|                                         int lowerValue,  | ||||
|  | @ -1288,6 +1372,34 @@ double PrusaDoubleSlider::get_double_value(const SelectedSlider& selection) cons | |||
|     return m_values[selection == ssLower ? m_lower_value : m_higher_value].second; | ||||
| } | ||||
| 
 | ||||
| std::vector<double> PrusaDoubleSlider::GetTicksValues() const | ||||
| { | ||||
|     std::vector<double> values; | ||||
| 
 | ||||
|     if (!m_values.empty()) | ||||
|         for (auto tick : m_ticks) | ||||
|             values.push_back(m_values[tick].second); | ||||
| 
 | ||||
|     return values; | ||||
| } | ||||
| 
 | ||||
| void PrusaDoubleSlider::SetTicksValues(const std::vector<double>& heights) | ||||
| { | ||||
|     if (m_values.empty()) | ||||
|         return; | ||||
| 
 | ||||
|     m_ticks.clear(); | ||||
|     unsigned int i = 0; | ||||
|     for (auto h : heights) { | ||||
|         while (i < m_values.size() && m_values[i].second - 1e-6 < h) | ||||
|             ++i; | ||||
|         if (i == m_values.size()) | ||||
|             return; | ||||
|         m_ticks.insert(i); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void PrusaDoubleSlider::get_lower_and_higher_position(int& lower_pos, int& higher_pos) | ||||
| { | ||||
|     const double step = get_scroll_step(); | ||||
|  | @ -1712,10 +1824,13 @@ void PrusaDoubleSlider::action_tick(const TicksAction action) | |||
|             m_ticks.insert(tick); | ||||
|         else if (it != m_ticks.end() && action == taDel) | ||||
|             m_ticks.erase(tick); | ||||
|         else | ||||
|         else { | ||||
|             wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); | ||||
|     Refresh(); | ||||
|     Update(); | ||||
| } | ||||
|  |  | |||
|  | @ -424,7 +424,9 @@ private: | |||
| 
 | ||||
| class PrusaObjectDataViewModel :public wxDataViewModel | ||||
| { | ||||
| 	std::vector<PrusaObjectDataViewModelNode*> m_objects; | ||||
| 	std::vector<PrusaObjectDataViewModelNode*>  m_objects; | ||||
|     std::vector<wxBitmap*>                      m_volume_bmps; | ||||
| 
 | ||||
| public: | ||||
|     PrusaObjectDataViewModel(); | ||||
|     ~PrusaObjectDataViewModel(); | ||||
|  | @ -432,7 +434,7 @@ public: | |||
| 	wxDataViewItem Add(const wxString &name); | ||||
| 	wxDataViewItem AddVolumeChild(const wxDataViewItem &parent_item,  | ||||
| 							const wxString &name,  | ||||
|                             const wxBitmap& icon, | ||||
|                             const int volume_type, | ||||
|                             const int extruder = 0, | ||||
|                             const bool create_frst_child = true); | ||||
| 	wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item); | ||||
|  | @ -441,8 +443,10 @@ public: | |||
| 	wxDataViewItem DeleteLastInstance(const wxDataViewItem &parent_item, size_t num); | ||||
| 	void DeleteAll(); | ||||
|     void DeleteChildren(wxDataViewItem& parent); | ||||
|     void DeleteVolumeChildren(wxDataViewItem& parent); | ||||
| 	wxDataViewItem GetItemById(int obj_idx); | ||||
| 	wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx); | ||||
| 	wxDataViewItem GetItemByInstanceId(int obj_idx, int inst_idx); | ||||
| 	int GetIdByItem(const wxDataViewItem& item); | ||||
|     int GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const; | ||||
|     int GetVolumeIdByItem(const wxDataViewItem& item) const; | ||||
|  | @ -488,9 +492,14 @@ public: | |||
| 	virtual bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override {	return true; } | ||||
| 
 | ||||
|     ItemType GetItemType(const wxDataViewItem &item) const ; | ||||
|     wxDataViewItem    GetItemByType(const wxDataViewItem &parent_item, ItemType type) const; | ||||
|     wxDataViewItem    GetSettingsItem(const wxDataViewItem &item) const; | ||||
|     wxDataViewItem    GetInstanceRootItem(const wxDataViewItem &item) const; | ||||
|     bool    IsSettingsItem(const wxDataViewItem &item) const; | ||||
|     void    UpdateSettingsDigest(const wxDataViewItem &item, const std::vector<std::string>& categories); | ||||
| 
 | ||||
|     void    SetVolumeBitmaps(const std::vector<wxBitmap*>& volume_bmps) { m_volume_bmps = volume_bmps; } | ||||
|     void    SetVolumeType(const wxDataViewItem &item, const int type); | ||||
| }; | ||||
| 
 | ||||
| // ----------------------------------------------------------------------------
 | ||||
|  | @ -613,6 +622,9 @@ private: | |||
| // PrusaDoubleSlider
 | ||||
| // ----------------------------------------------------------------------------
 | ||||
| 
 | ||||
| // custom message the slider sends to its parent to notify a tick-change:
 | ||||
| wxDECLARE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); | ||||
| 
 | ||||
| enum SelectedSlider { | ||||
|     ssUndef, | ||||
|     ssLower, | ||||
|  | @ -623,6 +635,7 @@ enum TicksAction{ | |||
|     taAdd, | ||||
|     taDel | ||||
| }; | ||||
| 
 | ||||
| class PrusaDoubleSlider : public wxControl | ||||
| { | ||||
| public: | ||||
|  | @ -660,6 +673,8 @@ public: | |||
|         m_values = values; | ||||
|     } | ||||
|     void ChangeOneLayerLock(); | ||||
|     std::vector<double> GetTicksValues() const; | ||||
|     void SetTicksValues(const std::vector<double>& heights); | ||||
| 
 | ||||
|     void OnPaint(wxPaintEvent& ) { render();} | ||||
|     void OnLeftDown(wxMouseEvent& event); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 tamasmeszaros
						tamasmeszaros