Implement generic object rotation in all 3 axes with a slider and box.

This commit is contained in:
Joseph Lenox 2018-05-13 21:18:50 -05:00
parent f3c652e322
commit ada744d718
6 changed files with 270 additions and 21 deletions

View 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

View File

@ -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();
{
}

View File

@ -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

View File

@ -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);

View File

@ -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 {

View File

@ -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) = @_;