mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-03 22:20:39 +08:00
Implement generic object rotation in all 3 axes with a slider and box.
This commit is contained in:
parent
f3c652e322
commit
ada744d718
89
src/GUI/Dialogs/AnglePicker.hpp
Normal file
89
src/GUI/Dialogs/AnglePicker.hpp
Normal file
@ -0,0 +1,89 @@
|
||||
#ifndef ANGLEPICKER_HPP
|
||||
#define ANGLEPICKER_HPP
|
||||
|
||||
#include <cmath>
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/textctrl.h>
|
||||
#include <wx/slider.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/button.h>
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
/// Dialog to produce a special dialog with both a slider for +/- 360 degree rotation and a text box.
|
||||
/// Supports decimal numbers via integer scaling.
|
||||
template <int scaling = 10000>
|
||||
class AnglePicker : public wxDialog {
|
||||
public:
|
||||
AnglePicker(wxWindow* parent, const wxString& title, double initial_angle) :
|
||||
wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE, wxString("AnglePicker")), _angle(scaling * initial_angle) {
|
||||
|
||||
auto* lbl_min = new wxStaticText(this, wxID_ANY, wxString(L"-360°"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
|
||||
auto* lbl_max = new wxStaticText(this, wxID_ANY, wxString(L"360°"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
|
||||
auto* lbl_txt = new wxStaticText(this, wxID_ANY, wxString("Angle "), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
|
||||
|
||||
auto btn_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
btn_sizer->Add(
|
||||
new wxButton(this, wxID_OK, _("OK"), wxDefaultPosition, wxDefaultSize),
|
||||
0,
|
||||
wxALL,
|
||||
10);
|
||||
btn_sizer->Add(
|
||||
new wxButton(this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxDefaultSize),
|
||||
0,
|
||||
wxALL,
|
||||
10);
|
||||
|
||||
|
||||
this->slider = new wxSlider(this, wxID_ANY, _angle, -360*scaling, 360*scaling, wxDefaultPosition, wxSize(255, wxDefaultSize.y));
|
||||
this->manual_entry = new wxTextCtrl(this, wxID_ANY, wxString("") << angle(), wxDefaultPosition, wxDefaultSize);
|
||||
|
||||
|
||||
this->hsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
this->hsizer->Add(lbl_min, wxALIGN_LEFT);
|
||||
this->hsizer->Add(this->slider, wxALIGN_CENTER);
|
||||
this->hsizer->Add(lbl_max, wxALIGN_RIGHT);
|
||||
|
||||
auto text_sizer {new wxBoxSizer(wxHORIZONTAL)};
|
||||
text_sizer->Add(lbl_txt, 0);
|
||||
text_sizer->Add(this->manual_entry, 0);
|
||||
|
||||
this->vsizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
this->vsizer->Add(this->hsizer, 0);
|
||||
this->vsizer->Add(text_sizer, 0);
|
||||
this->vsizer->Add(btn_sizer, 0, wxALIGN_CENTER);
|
||||
|
||||
this->SetSizerAndFit(vsizer);
|
||||
|
||||
this->Bind(wxEVT_SLIDER, [this](wxCommandEvent &e){
|
||||
wxString str;
|
||||
_angle = this->slider->GetValue();
|
||||
str.Printf("%f", this->angle());
|
||||
this->manual_entry->SetValue(str);
|
||||
});
|
||||
this->Bind(wxEVT_TEXT, [this](wxCommandEvent &e){
|
||||
wxString str {this->manual_entry->GetValue()};
|
||||
double value {0.0};
|
||||
if (str.ToDouble(&value))
|
||||
if (value <= 360.0 && value >= -360.0)
|
||||
this->slider->SetValue(value * scaling);
|
||||
});
|
||||
}
|
||||
double angle() { return double(_angle) / double(scaling) ; }
|
||||
|
||||
private:
|
||||
/// Scaled integer
|
||||
int _angle {0};
|
||||
|
||||
wxSlider* slider {nullptr};
|
||||
wxTextCtrl* manual_entry {nullptr};
|
||||
wxSizer* hsizer {nullptr};
|
||||
wxSizer* vsizer {nullptr};
|
||||
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif// ANGLEPICKER_HPP
|
@ -120,11 +120,9 @@ void MainFrame::init_menubar()
|
||||
|
||||
wxMenu* menuPlater = new wxMenu();
|
||||
{
|
||||
append_menu_item(menuPlater, _(L"Arrange…"), _("Arrange models on plater"), [=](wxCommandEvent& e) { if (this->plater != nullptr) this->plater->arrange();}, wxID_ANY, "bricks.png", "Ctrl+G");
|
||||
}
|
||||
wxMenu* menuObject = new wxMenu();
|
||||
{
|
||||
append_menu_item(menuPlater, _(L"Arrange…"), _("Arrange models on plater"), [this](wxCommandEvent& e) { if (this->plater != nullptr) this->plater->arrange();}, wxID_ANY, "bricks.png", "Ctrl+G");
|
||||
}
|
||||
wxMenu* menuObject = this->plater->object_menu();
|
||||
wxMenu* menuSettings = new wxMenu();
|
||||
{
|
||||
}
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include "MainFrame.hpp"
|
||||
#include "BoundingBox.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "Dialogs/AnglePicker.hpp"
|
||||
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
@ -450,11 +452,6 @@ void Plater::object_settings_dialog(ObjRef obj) {
|
||||
|
||||
}
|
||||
|
||||
wxMenu* Plater::object_menu() {
|
||||
return new wxMenu();
|
||||
}
|
||||
|
||||
|
||||
void Plater::select_object(ObjRef obj) {
|
||||
for (auto& o : this->objects) {
|
||||
o.selected = false;
|
||||
@ -733,8 +730,26 @@ void Plater::decrease(size_t copies, bool dont_push) {
|
||||
this->on_model_change();
|
||||
}
|
||||
|
||||
void Plater::rotate(Axis axis, bool dont_push) {
|
||||
ObjRef obj {this->selected_object()};
|
||||
if (obj == this->objects.end()) return;
|
||||
double angle {0.0};
|
||||
|
||||
auto* model_object {this->model->objects.at(obj->identifier)};
|
||||
auto model_instance {model_object->instances.begin()};
|
||||
|
||||
// pop a menu to get the angle
|
||||
auto* pick = new AnglePicker<1000>(this, "Set Angle", angle);
|
||||
if (pick->ShowModal() == wxID_OK) {
|
||||
angle = pick->angle();
|
||||
pick->Destroy(); // cleanup afterwards.
|
||||
this->rotate(angle, axis, dont_push);
|
||||
} else {
|
||||
pick->Destroy(); // cleanup afterwards.
|
||||
}
|
||||
}
|
||||
|
||||
void Plater::rotate(double angle, Axis axis, bool dont_push) {
|
||||
//TODO
|
||||
ObjRef obj {this->selected_object()};
|
||||
if (obj == this->objects.end()) return;
|
||||
|
||||
@ -761,7 +776,7 @@ void Plater::rotate(double angle, Axis axis, bool dont_push) {
|
||||
this->print->add_model_object(model_object, obj->identifier);
|
||||
|
||||
if (!dont_push) {
|
||||
// TODO
|
||||
add_undo_operation(UndoCmd::Rotate, obj->identifier, angle, axis);
|
||||
}
|
||||
|
||||
this->selection_changed();
|
||||
@ -797,6 +812,9 @@ void Plater::add_undo_operation(UndoCmd cmd, int obj_id, Slic3r::Model& model) {
|
||||
void Plater::add_undo_operation(UndoCmd cmd, int obj_id, size_t copies) {
|
||||
}
|
||||
|
||||
void Plater::add_undo_operation(UndoCmd cmd, int obj_id, double angle, Axis axis) {
|
||||
}
|
||||
|
||||
void Plater::object_list_changed() {
|
||||
//TODO
|
||||
}
|
||||
@ -816,5 +834,127 @@ void Plater::resume_background_process() {
|
||||
//TODO
|
||||
}
|
||||
|
||||
wxMenu* Plater::object_menu() {
|
||||
auto* frame {this->GetFrame()};
|
||||
auto* menu {new wxMenu()};
|
||||
|
||||
append_menu_item(menu, _("Delete"), _("Remove the selected object."), [=](wxCommandEvent& e) { this->remove();}, wxID_ANY, "brick_delete.png", "Ctrl+Del");
|
||||
/*
|
||||
wxTheApp->append_menu_item($menu, "Delete\tCtrl+Del", 'Remove the selected object', sub {
|
||||
$self->remove;
|
||||
}, undef, 'brick_delete.png');
|
||||
wxTheApp->append_menu_item($menu, "Increase copies\tCtrl++", 'Place one more copy of the selected object', sub {
|
||||
$self->increase;
|
||||
}, undef, 'add.png');
|
||||
wxTheApp->append_menu_item($menu, "Decrease copies\tCtrl+-", 'Remove one copy of the selected object', sub {
|
||||
$self->decrease;
|
||||
}, undef, 'delete.png');
|
||||
wxTheApp->append_menu_item($menu, "Set number of copies…", 'Change the number of copies of the selected object', sub {
|
||||
$self->set_number_of_copies;
|
||||
}, undef, 'textfield.png');
|
||||
$menu->AppendSeparator();
|
||||
wxTheApp->append_menu_item($menu, "Move to bed center", 'Center object around bed center', sub {
|
||||
$self->center_selected_object_on_bed;
|
||||
}, undef, 'arrow_in.png');
|
||||
wxTheApp->append_menu_item($menu, "Rotate 45° clockwise", 'Rotate the selected object by 45° clockwise', sub {
|
||||
$self->rotate(-45);
|
||||
}, undef, 'arrow_rotate_clockwise.png');
|
||||
wxTheApp->append_menu_item($menu, "Rotate 45° counter-clockwise", 'Rotate the selected object by 45° counter-clockwise', sub {
|
||||
$self->rotate(+45);
|
||||
}, undef, 'arrow_rotate_anticlockwise.png');
|
||||
*/
|
||||
{
|
||||
auto* rotateMenu {new wxMenu};
|
||||
append_menu_item(rotateMenu, _(L"Around X axis…"), _("Rotate the selected object by an arbitrary angle around X axis."), [this](wxCommandEvent& e) { this->rotate(X); }, wxID_ANY, "bullet_red.png");
|
||||
|
||||
append_menu_item(rotateMenu, _(L"Around Y axis…"), _("Rotate the selected object by an arbitrary angle around Y axis."), [this](wxCommandEvent& e) { this->rotate(Y); }, wxID_ANY, "bullet_green.png");
|
||||
|
||||
append_menu_item(rotateMenu, _(L"Around Z axis…"), _("Rotate the selected object by an arbitrary angle around Z axis."), [this](wxCommandEvent& e) { this->rotate(Z); }, wxID_ANY, "bullet_blue.png");
|
||||
|
||||
append_submenu(menu, _("Rotate"), _("Rotate the selected object by an arbitrary angle"), rotateMenu, wxID_ANY, "textfield.png");
|
||||
}
|
||||
/*
|
||||
|
||||
{
|
||||
my $mirrorMenu = Wx::Menu->new;
|
||||
wxTheApp->append_menu_item($mirrorMenu, "Along X axis…", 'Mirror the selected object along the X axis', sub {
|
||||
$self->mirror(X);
|
||||
}, undef, 'bullet_red.png');
|
||||
wxTheApp->append_menu_item($mirrorMenu, "Along Y axis…", 'Mirror the selected object along the Y axis', sub {
|
||||
$self->mirror(Y);
|
||||
}, undef, 'bullet_green.png');
|
||||
wxTheApp->append_menu_item($mirrorMenu, "Along Z axis…", 'Mirror the selected object along the Z axis', sub {
|
||||
$self->mirror(Z);
|
||||
}, undef, 'bullet_blue.png');
|
||||
wxTheApp->append_submenu($menu, "Mirror", 'Mirror the selected object', $mirrorMenu, undef, 'shape_flip_horizontal.png');
|
||||
}
|
||||
|
||||
{
|
||||
my $scaleMenu = Wx::Menu->new;
|
||||
wxTheApp->append_menu_item($scaleMenu, "Uniformly…", 'Scale the selected object along the XYZ axes', sub {
|
||||
$self->changescale(undef);
|
||||
});
|
||||
wxTheApp->append_menu_item($scaleMenu, "Along X axis…", 'Scale the selected object along the X axis', sub {
|
||||
$self->changescale(X);
|
||||
}, undef, 'bullet_red.png');
|
||||
wxTheApp->append_menu_item($scaleMenu, "Along Y axis…", 'Scale the selected object along the Y axis', sub {
|
||||
$self->changescale(Y);
|
||||
}, undef, 'bullet_green.png');
|
||||
wxTheApp->append_menu_item($scaleMenu, "Along Z axis…", 'Scale the selected object along the Z axis', sub {
|
||||
$self->changescale(Z);
|
||||
}, undef, 'bullet_blue.png');
|
||||
wxTheApp->append_submenu($menu, "Scale", 'Scale the selected object by a given factor', $scaleMenu, undef, 'arrow_out.png');
|
||||
}
|
||||
|
||||
{
|
||||
my $scaleToSizeMenu = Wx::Menu->new;
|
||||
wxTheApp->append_menu_item($scaleToSizeMenu, "Uniformly…", 'Scale the selected object along the XYZ axes', sub {
|
||||
$self->changescale(undef, 1);
|
||||
});
|
||||
wxTheApp->append_menu_item($scaleToSizeMenu, "Along X axis…", 'Scale the selected object along the X axis', sub {
|
||||
$self->changescale(X, 1);
|
||||
}, undef, 'bullet_red.png');
|
||||
wxTheApp->append_menu_item($scaleToSizeMenu, "Along Y axis…", 'Scale the selected object along the Y axis', sub {
|
||||
$self->changescale(Y, 1);
|
||||
}, undef, 'bullet_green.png');
|
||||
wxTheApp->append_menu_item($scaleToSizeMenu, "Along Z axis…", 'Scale the selected object along the Z axis', sub {
|
||||
$self->changescale(Z, 1);
|
||||
}, undef, 'bullet_blue.png');
|
||||
wxTheApp->append_submenu($menu, "Scale to size", 'Scale the selected object to match a given size', $scaleToSizeMenu, undef, 'arrow_out.png');
|
||||
}
|
||||
|
||||
wxTheApp->append_menu_item($menu, "Split", 'Split the selected object into individual parts', sub {
|
||||
$self->split_object;
|
||||
}, undef, 'shape_ungroup.png');
|
||||
wxTheApp->append_menu_item($menu, "Cut…", 'Open the 3D cutting tool', sub {
|
||||
$self->object_cut_dialog;
|
||||
}, undef, 'package.png');
|
||||
wxTheApp->append_menu_item($menu, "Layer heights…", 'Open the dynamic layer height control', sub {
|
||||
$self->object_layers_dialog;
|
||||
}, undef, 'variable_layer_height.png');
|
||||
$menu->AppendSeparator();
|
||||
wxTheApp->append_menu_item($menu, "Settings…", 'Open the object editor dialog', sub {
|
||||
$self->object_settings_dialog;
|
||||
}, undef, 'cog.png');
|
||||
$menu->AppendSeparator();
|
||||
wxTheApp->append_menu_item($menu, "Reload from Disk", 'Reload the selected file from Disk', sub {
|
||||
$self->reload_from_disk;
|
||||
}, undef, 'arrow_refresh.png');
|
||||
wxTheApp->append_menu_item($menu, "Export object as STL…", 'Export this single object as STL file', sub {
|
||||
$self->export_object_stl;
|
||||
}, undef, 'brick_go.png');
|
||||
wxTheApp->append_menu_item($menu, "Export object and modifiers as AMF…", 'Export this single object and all associated modifiers as AMF file', sub {
|
||||
$self->export_object_amf;
|
||||
}, undef, 'brick_go.png');
|
||||
wxTheApp->append_menu_item($menu, "Export object and modifiers as 3MF…", 'Export this single object and all associated modifiers as 3MF file', sub {
|
||||
$self->export_object_tmf;
|
||||
}, undef, 'brick_go.png');
|
||||
|
||||
return $menu;
|
||||
}
|
||||
*/
|
||||
return menu;
|
||||
}
|
||||
|
||||
}} // Namespace Slic3r::GUI
|
||||
|
||||
|
@ -31,7 +31,7 @@ namespace Slic3r { namespace GUI {
|
||||
using UndoOperation = int;
|
||||
|
||||
enum class UndoCmd {
|
||||
Remove, Add, Reset, Increase, Decrease
|
||||
Remove, Add, Reset, Increase, Decrease, Rotate
|
||||
};
|
||||
|
||||
using ObjIdx = unsigned int;
|
||||
@ -70,6 +70,12 @@ public:
|
||||
/// Undo for increase/decrease
|
||||
void add_undo_operation(UndoCmd cmd, int obj_id, size_t copies);
|
||||
|
||||
/// Undo for increase/decrease
|
||||
void add_undo_operation(UndoCmd cmd, int obj_id, double angle, Axis axis);
|
||||
|
||||
/// Create menu for object.
|
||||
wxMenu* object_menu();
|
||||
|
||||
private:
|
||||
std::shared_ptr<Slic3r::Print> print {std::make_shared<Print>(Slic3r::Print())};
|
||||
std::shared_ptr<Slic3r::Model> model {std::make_shared<Model>(Slic3r::Model())};
|
||||
@ -158,8 +164,6 @@ private:
|
||||
void object_settings_dialog(ObjIdx obj_idx);
|
||||
void object_settings_dialog(ObjRef obj);
|
||||
|
||||
/// Create and launch menu for object.
|
||||
wxMenu* object_menu();
|
||||
|
||||
/// Instantiate the toolbar
|
||||
void build_toolbar();
|
||||
@ -173,6 +177,8 @@ private:
|
||||
/// Remove instances of the currently selected model.
|
||||
void decrease(size_t copies = 1, bool dont_push = false);
|
||||
|
||||
/// Rotate the currently selected model, triggering a user prompt.
|
||||
void rotate(Axis axis = Z, bool dont_push = false);
|
||||
/// Rotate the currently selected model.
|
||||
void rotate(double angle, Axis axis = Z, bool dont_push = false);
|
||||
|
||||
|
@ -64,6 +64,24 @@ void fatal_error(wxWindow* parent, const wxString& message) {
|
||||
throw std::runtime_error(message.ToStdString());
|
||||
}
|
||||
|
||||
wxMenuItem* append_submenu(wxMenu* menu, const wxString& name, const wxString& help, wxMenu* submenu, int id, const wxString& icon) {
|
||||
auto* item {new wxMenuItem(menu, id, name, help)};
|
||||
|
||||
set_menu_item_icon(item,icon);
|
||||
item->SetSubMenu(submenu);
|
||||
menu->Append(item);
|
||||
return item;
|
||||
}
|
||||
|
||||
void set_menu_item_icon(wxMenuItem* item, const wxString& icon) {
|
||||
if (!icon.IsEmpty()) {
|
||||
wxBitmap ico;
|
||||
if(ico.LoadFile(var(icon), wxBITMAP_TYPE_PNG))
|
||||
item->SetBitmap(ico);
|
||||
else
|
||||
std::cerr<< var(icon) << " failed to load \n";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
sub append_submenu {
|
||||
|
@ -107,6 +107,8 @@ void show_info(wxWindow* parent, const wxString& message, const wxString& title)
|
||||
/// Show an error messagebox and then throw an exception.
|
||||
void fatal_error(wxWindow* parent, const wxString& message);
|
||||
|
||||
/// Assign a menu item icon
|
||||
void set_menu_item_icon(wxMenuItem* item, const wxString& icon);
|
||||
|
||||
template <typename T>
|
||||
void append_menu_item(wxMenu* menu, const wxString& name,const wxString& help, T lambda, int id = wxID_ANY, const wxString& icon = "", const wxString& accel = "") {
|
||||
@ -117,18 +119,14 @@ void append_menu_item(wxMenu* menu, const wxString& name,const wxString& help, T
|
||||
tmp->SetAccel(a); // set the accelerator if and only if the accelerator is fine
|
||||
}
|
||||
tmp->SetHelp(help);
|
||||
if (!icon.IsEmpty()) {
|
||||
wxBitmap ico;
|
||||
if(ico.LoadFile(var(icon), wxBITMAP_TYPE_PNG))
|
||||
tmp->SetBitmap(ico);
|
||||
else
|
||||
std::cerr<< var(icon) << " failed to load \n";
|
||||
}
|
||||
set_menu_item_icon(tmp, icon);
|
||||
|
||||
if (typeid(lambda) != typeid(nullptr))
|
||||
menu->Bind(wxEVT_MENU, lambda, tmp->GetId(), tmp->GetId());
|
||||
}
|
||||
|
||||
wxMenuItem* append_submenu(wxMenu* menu, const wxString& name, const wxString& help, wxMenu* submenu, int id = wxID_ANY, const wxString& icon = "");
|
||||
|
||||
/*
|
||||
sub CallAfter {
|
||||
my ($self, $cb) = @_;
|
||||
|
Loading…
x
Reference in New Issue
Block a user