Added selection of custom bed texture to bed shape dialog

This commit is contained in:
Enrico Turri 2019-07-18 11:12:11 +02:00
parent 75c53a53b6
commit 08d37aad06
10 changed files with 198 additions and 70 deletions

View file

@ -51,6 +51,11 @@ void PrintConfigDef::init_common_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionPoints{ Vec2d(0, 0), Vec2d(200, 0), Vec2d(200, 200), Vec2d(0, 200) });
def = this->add("bed_custom_texture", coString);
def->label = L("Bed custom texture");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString(""));
def = this->add("layer_height", coFloat);
def->label = L("Layer height");
def->category = L("Layers and Perimeters");

View file

@ -18,10 +18,9 @@ wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), -
#ifdef __APPLE__
m_user_drawn_background = false;
#endif /*__APPLE__*/
Bind(wxEVT_PAINT, ([this](wxPaintEvent &/* e */) { repaint(); }));
Bind(wxEVT_SIZE, ([this](wxSizeEvent & /* e */) { Refresh(); }));
}
void Bed_2D::repaint()
void Bed_2D::repaint(const std::vector<Vec2d>& shape)
{
wxAutoBufferedPaintDC dc(this);
auto cw = GetSize().GetWidth();
@ -41,7 +40,7 @@ void Bed_2D::repaint()
dc.DrawRectangle(rect.GetLeft(), rect.GetTop(), rect.GetWidth(), rect.GetHeight());
}
if (m_bed_shape.empty())
if (shape.empty())
return;
// reduce size to have some space around the drawn shape
@ -52,10 +51,9 @@ void Bed_2D::repaint()
auto ccenter = cbb.center();
// get bounding box of bed shape in G - code coordinates
auto bed_shape = m_bed_shape;
auto bed_polygon = Polygon::new_scale(m_bed_shape);
auto bb = BoundingBoxf(m_bed_shape);
bb.merge(Vec2d(0, 0)); // origin needs to be in the visible area
auto bed_polygon = Polygon::new_scale(shape);
auto bb = BoundingBoxf(shape);
bb.merge(Vec2d(0, 0)); // origin needs to be in the visible area
auto bw = bb.size()(0);
auto bh = bb.size()(1);
auto bcenter = bb.center();
@ -73,8 +71,8 @@ void Bed_2D::repaint()
// draw bed fill
dc.SetBrush(wxBrush(wxColour(255, 255, 255), wxBRUSHSTYLE_SOLID));
wxPointList pt_list;
for (auto pt: m_bed_shape)
{
for (auto pt : shape)
{
Point pt_pix = to_pixels(pt, ch);
pt_list.push_back(new wxPoint(pt_pix(0), pt_pix(1)));
}
@ -155,13 +153,13 @@ void Bed_2D::repaint()
// convert G - code coordinates into pixels
Point Bed_2D::to_pixels(Vec2d point, int height)
Point Bed_2D::to_pixels(const Vec2d& point, int height)
{
auto p = point * m_scale_factor + m_shift;
return Point(p(0) + Border, height - p(1) + Border);
}
void Bed_2D::set_pos(Vec2d pos)
void Bed_2D::set_pos(const Vec2d& pos)
{
m_pos = pos;
Refresh();

View file

@ -17,16 +17,13 @@ class Bed_2D : public wxPanel
Vec2d m_shift = Vec2d::Zero();
Vec2d m_pos = Vec2d::Zero();
Point to_pixels(Vec2d point, int height);
void repaint();
void set_pos(Vec2d pos);
Point to_pixels(const Vec2d& point, int height);
void set_pos(const Vec2d& pos);
public:
Bed_2D(wxWindow* parent);
~Bed_2D() {}
explicit Bed_2D(wxWindow* parent);
std::vector<Vec2d> m_bed_shape;
void repaint(const std::vector<Vec2d>& shape);
};

View file

@ -10,17 +10,18 @@
#include "libslic3r/Polygon.hpp"
#include "boost/nowide/iostream.hpp"
#include <boost/algorithm/string/predicate.hpp>
#include <algorithm>
namespace Slic3r {
namespace GUI {
void BedShapeDialog::build_dialog(const ConfigOptionPoints& default_pt)
void BedShapeDialog::build_dialog(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture)
{
SetFont(wxGetApp().normal_font());
m_panel = new BedShapePanel(this);
m_panel->build_panel(default_pt);
m_panel->build_panel(default_pt, custom_texture);
auto main_sizer = new wxBoxSizer(wxVERTICAL);
main_sizer->Add(m_panel, 1, wxEXPAND);
@ -51,14 +52,19 @@ void BedShapeDialog::on_dpi_changed(const wxRect &suggested_rect)
Refresh();
}
void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt)
const std::string BedShapePanel::NONE = "None";
const std::string BedShapePanel::EMPTY_STRING = "";
void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture)
{
m_shape = default_pt.values;
m_custom_texture = custom_texture.value.empty() ? NONE : custom_texture.value;
auto sbsizer = new wxStaticBoxSizer(wxVERTICAL, this, _(L("Shape")));
sbsizer->GetStaticBox()->SetFont(wxGetApp().bold_font());
// shape options
m_shape_options_book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition,
wxSize(25*wxGetApp().em_unit(), -1), wxCHB_TOP);
m_shape_options_book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition, wxSize(25*wxGetApp().em_unit(), -1), wxCHB_TOP);
sbsizer->Add(m_shape_options_book);
auto optgroup = init_shape_options_page(_(L("Rectangular")));
@ -106,6 +112,8 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt)
};
optgroup->append_line(line);
wxPanel* texture_panel = init_texture_panel();
Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, ([this](wxCommandEvent& e)
{
update_shape();
@ -113,13 +121,20 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt)
// right pane with preview canvas
m_canvas = new Bed_2D(this);
m_canvas->m_bed_shape = default_pt.values;
// main sizer
auto top_sizer = new wxBoxSizer(wxHORIZONTAL);
top_sizer->Add(sbsizer, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 10);
if (m_canvas)
top_sizer->Add(m_canvas, 1, wxEXPAND | wxALL, 10) ;
if (m_canvas != nullptr)
{
m_canvas->Bind(wxEVT_PAINT, [this](wxPaintEvent& e) { m_canvas->repaint(m_shape); });
m_canvas->Bind(wxEVT_SIZE, [this](wxSizeEvent& e) { m_canvas->Refresh(); });
}
wxSizer* left_sizer = new wxBoxSizer(wxVERTICAL);
left_sizer->Add(sbsizer, 0, wxEXPAND);
left_sizer->Add(texture_panel, 1, wxEXPAND);
wxSizer* top_sizer = new wxBoxSizer(wxHORIZONTAL);
top_sizer->Add(left_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 10);
top_sizer->Add(m_canvas, 1, wxEXPAND | wxALL, 10);
SetSizerAndFit(top_sizer);
@ -150,6 +165,66 @@ ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(const wxString& tit
return optgroup;
}
wxPanel* BedShapePanel::init_texture_panel()
{
wxPanel* panel = new wxPanel(this);
ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Texture")));
optgroup->label_width = 10;
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
update_shape();
};
Line line{ "", "" };
line.full_width = 1;
line.widget = [this](wxWindow* parent) {
wxButton* load_btn = new wxButton(parent, wxID_ANY, _(L("Load...")));
wxSizer* load_sizer = new wxBoxSizer(wxHORIZONTAL);
load_sizer->Add(load_btn, 1, wxEXPAND);
wxStaticText* filename_lbl = new wxStaticText(parent, wxID_ANY, _(NONE));
wxSizer* filename_sizer = new wxBoxSizer(wxHORIZONTAL);
filename_sizer->Add(filename_lbl, 1, wxEXPAND);
wxButton* remove_btn = new wxButton(parent, wxID_ANY, _(L("Remove")));
wxSizer* remove_sizer = new wxBoxSizer(wxHORIZONTAL);
remove_sizer->Add(remove_btn, 1, wxEXPAND);
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(filename_sizer, 1, wxEXPAND);
sizer->Add(load_sizer, 1, wxEXPAND);
sizer->Add(remove_sizer, 1, wxEXPAND);
load_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
{
load_texture();
}));
remove_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
{
m_custom_texture = NONE;
update_shape();
}));
filename_lbl->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e)
{
e.SetText(_(boost::filesystem::path(m_custom_texture).filename().string()));
}));
remove_btn->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e)
{
e.Enable(m_custom_texture != NONE);
}));
return sizer;
};
optgroup->append_line(line);
panel->SetSizerAndFit(optgroup->sizer);
return panel;
}
// Called from the constructor.
// Set the initial bed shape from a list of points.
// Deduce the bed shape type(rect, circle, custom)
@ -232,7 +307,7 @@ void BedShapePanel::set_shape(const ConfigOptionPoints& points)
// This is a custom bed shape, use the polygon provided.
m_shape_options_book->SetSelection(SHAPE_CUSTOM);
// Copy the polygon to the canvas, make a copy of the array.
m_loaded_bed_shape = points.values;
m_loaded_shape = points.values;
update_shape();
}
@ -277,11 +352,11 @@ void BedShapePanel::update_shape()
x1 -= dx;
y0 -= dy;
y1 -= dy;
m_canvas->m_bed_shape = { Vec2d(x0, y0),
Vec2d(x1, y0),
Vec2d(x1, y1),
Vec2d(x0, y1)};
}
m_shape = { Vec2d(x0, y0),
Vec2d(x1, y0),
Vec2d(x1, y1),
Vec2d(x0, y1) };
}
else if(page_idx == SHAPE_CIRCULAR) {
double diameter;
try{
@ -293,16 +368,16 @@ void BedShapePanel::update_shape()
if (diameter == 0.0) return ;
auto r = diameter / 2;
auto twopi = 2 * PI;
auto edges = 60;
std::vector<Vec2d> points;
for (size_t i = 1; i <= 60; ++i) {
auto angle = i * twopi / edges;
auto edges = 72;
std::vector<Vec2d> points;
for (size_t i = 1; i <= edges; ++i) {
auto angle = i * twopi / edges;
points.push_back(Vec2d(r*cos(angle), r*sin(angle)));
}
m_canvas->m_bed_shape = points;
}
m_shape = points;
}
else if (page_idx == SHAPE_CUSTOM)
m_canvas->m_bed_shape = m_loaded_bed_shape;
m_shape = m_loaded_shape;
update_preview();
}
@ -310,19 +385,23 @@ void BedShapePanel::update_shape()
// Loads an stl file, projects it to the XY plane and calculates a polygon.
void BedShapePanel::load_stl()
{
wxFileDialog dialog(this, _(L("Choose a file to import bed shape from (STL/OBJ/AMF/3MF/PRUSA):")), "", "",
file_wildcards(FT_MODEL), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
wxFileDialog dialog(this, _(L("Choose an STL file to import bed shape from:")), "", "", file_wildcards(FT_STL), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dialog.ShowModal() != wxID_OK)
return;
wxArrayString input_file;
dialog.GetPaths(input_file);
std::string file_name = dialog.GetPath().ToUTF8().data();
std::string file_name = input_file[0].ToUTF8().data();
if (!boost::iequals(boost::filesystem::path(file_name).extension().string().c_str(), ".stl"))
{
show_error(this, _(L("Invalid file format.")));
return;
}
wxBusyCursor wait;
Model model;
try {
model = Model::read_from_file(file_name);
model = Model::read_from_file(file_name);
}
catch (std::exception &) {
show_error(this, _(L("Error! Invalid model")));
@ -346,7 +425,32 @@ void BedShapePanel::load_stl()
for (auto pt : polygon.points)
points.push_back(unscale(pt));
m_loaded_bed_shape = points;
m_loaded_shape = points;
update_shape();
}
void BedShapePanel::load_texture()
{
wxFileDialog dialog(this, _(L("Choose a file to import bed texture from (PNG/SVG):")), "", "",
file_wildcards(FT_TEX), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dialog.ShowModal() != wxID_OK)
return;
m_custom_texture = NONE;
std::string file_name = dialog.GetPath().ToUTF8().data();
std::string file_ext = boost::filesystem::path(file_name).extension().string();
if (!boost::iequals(file_ext.c_str(), ".png") && !boost::iequals(file_ext.c_str(), ".svg"))
{
show_error(this, _(L("Invalid file format.")));
return;
}
wxBusyCursor wait;
m_custom_texture = file_name;
update_shape();
}

View file

@ -16,25 +16,32 @@ namespace GUI {
using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>;
class BedShapePanel : public wxPanel
{
static const std::string NONE;
static const std::string EMPTY_STRING;
Bed_2D* m_canvas;
std::vector<Vec2d> m_loaded_bed_shape;
std::vector<Vec2d> m_shape;
std::vector<Vec2d> m_loaded_shape;
std::string m_custom_texture;
public:
BedShapePanel(wxWindow* parent) : wxPanel(parent, wxID_ANY) {}
~BedShapePanel() {}
void build_panel(const ConfigOptionPoints& default_pt);
BedShapePanel(wxWindow* parent) : wxPanel(parent, wxID_ANY), m_custom_texture(NONE) {}
~BedShapePanel() {}
void build_panel(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture);
// Returns the resulting bed shape polygon. This value will be stored to the ini file.
std::vector<Vec2d> get_bed_shape() { return m_canvas->m_bed_shape; }
const std::vector<Vec2d>& get_shape() const { return m_shape; }
const std::string& get_custom_texture() const { return (m_custom_texture != NONE) ? m_custom_texture : EMPTY_STRING; }
private:
ConfigOptionsGroupShp init_shape_options_page(const wxString& title);
wxPanel* init_texture_panel();
void set_shape(const ConfigOptionPoints& points);
void update_preview();
void update_shape();
void load_stl();
void load_texture();
wxChoicebook* m_shape_options_book;
std::vector <ConfigOptionsGroupShp> m_optgroups;
@ -49,8 +56,9 @@ public:
wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {}
~BedShapeDialog() {}
void build_dialog(const ConfigOptionPoints& default_pt);
std::vector<Vec2d> get_bed_shape() { return m_panel->get_bed_shape(); }
void build_dialog(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture);
const std::vector<Vec2d>& get_shape() const { return m_panel->get_shape(); }
const std::string& get_custom_texture() const { return m_panel->get_custom_texture(); }
protected:
void on_dpi_changed(const wxRect &suggested_rect) override;

View file

@ -532,14 +532,18 @@ PageBedShape::PageBedShape(ConfigWizard *parent)
{
append_text(_(L("Set the shape of your printer's bed.")));
shape_panel->build_panel(*wizard_p()->custom_config->option<ConfigOptionPoints>("bed_shape"));
shape_panel->build_panel(*wizard_p()->custom_config->option<ConfigOptionPoints>("bed_shape"),
*wizard_p()->custom_config->option<ConfigOptionString>("bed_custom_texture"));
append(shape_panel);
}
void PageBedShape::apply_custom_config(DynamicPrintConfig &config)
{
const auto points(shape_panel->get_bed_shape());
const std::vector<Vec2d>& points = shape_panel->get_shape();
const std::string& custom_texture = shape_panel->get_custom_texture();
config.set_key_value("bed_shape", new ConfigOptionPoints(points));
config.set_key_value("bed_custom_texture", new ConfigOptionString(custom_texture));
}
PageDiameters::PageDiameters(ConfigWizard *parent)
@ -1085,7 +1089,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason)
p->load_vendors();
p->custom_config.reset(DynamicPrintConfig::new_from_defaults_keys({
"gcode_flavor", "bed_shape", "nozzle_diameter", "filament_diameter", "temperature", "bed_temperature",
"gcode_flavor", "bed_shape", "bed_custom_texture", "nozzle_diameter", "filament_diameter", "temperature", "bed_temperature",
}));
p->index = new ConfigWizardIndex(this);

View file

@ -67,9 +67,12 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension)
/* FT_MODEL */ "Known files (*.stl, *.obj, *.amf, *.xml, *.3mf, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.3mf;*.3MF;*.prusa;*.PRUSA",
/* FT_PROJECT */ "Project files (*.3mf, *.amf)|*.3mf;*.3MF;*.amf;*.AMF",
/* FT_INI */ "INI files (*.ini)|*.ini;*.INI",
/* FT_SVG */ "SVG files (*.svg)|*.svg;*.SVG",
/* FT_PNGZIP */"Masked SLA files (*.sl1)|*.sl1;*.SL1",
/* FT_INI */ "INI files (*.ini)|*.ini;*.INI",
/* FT_SVG */ "SVG files (*.svg)|*.svg;*.SVG",
/* FT_TEX */ "Texture (*.png, *.svg)|*.png;*.PNG;*.svg;*.SVG",
/* FT_PNGZIP */ "Masked SLA files (*.sl1)|*.sl1;*.SL1",
};
std::string out = defaults[file_type];

View file

@ -44,6 +44,9 @@ enum FileType
FT_INI,
FT_SVG,
FT_TEX,
FT_PNGZIP,
FT_SIZE,

View file

@ -411,7 +411,7 @@ const std::vector<std::string>& Preset::printer_options()
if (s_opts.empty()) {
s_opts = {
"printer_technology",
"bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed",
"bed_shape", "bed_custom_texture", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed",
"use_firmware_retraction", "use_volumetric_e", "variable_layer_height",
"host_type", "print_host", "printhost_apikey", "printhost_cafile",
"single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",

View file

@ -1856,12 +1856,15 @@ void TabPrinter::build_fff()
btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e)
{
BedShapeDialog dlg(this);
dlg.build_dialog(*m_config->option<ConfigOptionPoints>("bed_shape"));
dlg.build_dialog(*m_config->option<ConfigOptionPoints>("bed_shape"),
*m_config->option<ConfigOptionString>("bed_custom_texture"));
if (dlg.ShowModal() == wxID_OK) {
std::vector<Vec2d> shape = dlg.get_bed_shape();
const std::vector<Vec2d>& shape = dlg.get_shape();
const std::string& custom_texture = dlg.get_custom_texture();
if (!shape.empty())
{
load_key_value("bed_shape", shape);
load_key_value("bed_custom_texture", custom_texture);
update_changed_ui();
}
}
@ -2062,12 +2065,15 @@ void TabPrinter::build_sla()
btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e)
{
BedShapeDialog dlg(this);
dlg.build_dialog(*m_config->option<ConfigOptionPoints>("bed_shape"));
dlg.build_dialog(*m_config->option<ConfigOptionPoints>("bed_shape"),
*m_config->option<ConfigOptionString>("bed_custom_texture"));
if (dlg.ShowModal() == wxID_OK) {
std::vector<Vec2d> shape = dlg.get_bed_shape();
const std::vector<Vec2d>& shape = dlg.get_shape();
const std::string& custom_texture = dlg.get_custom_texture();
if (!shape.empty())
{
load_key_value("bed_shape", shape);
load_key_value("bed_custom_texture", custom_texture);
update_changed_ui();
}
}