mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-08-03 03:54:04 -06:00
Merge remote-tracking branch 'origin/master' into tm_ui_jobs
This commit is contained in:
commit
d60ecb3788
199 changed files with 71967 additions and 51359 deletions
|
@ -19,8 +19,6 @@ wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), -
|
|||
m_user_drawn_background = false;
|
||||
#endif /*__APPLE__*/
|
||||
Bind(wxEVT_PAINT, ([this](wxPaintEvent &/* e */) { repaint(); }));
|
||||
Bind(wxEVT_LEFT_DOWN, ([this](wxMouseEvent &event) { mouse_event(event); }));
|
||||
Bind(wxEVT_MOTION, ([this](wxMouseEvent &event) { mouse_event(event); }));
|
||||
Bind(wxEVT_SIZE, ([this](wxSizeEvent & /* e */) { Refresh(); }));
|
||||
}
|
||||
void Bed_2D::repaint()
|
||||
|
@ -43,22 +41,14 @@ void Bed_2D::repaint()
|
|||
dc.DrawRectangle(rect.GetLeft(), rect.GetTop(), rect.GetWidth(), rect.GetHeight());
|
||||
}
|
||||
|
||||
// turn cw and ch from sizes to max coordinates
|
||||
cw--;
|
||||
ch--;
|
||||
if (m_bed_shape.empty())
|
||||
return;
|
||||
|
||||
// reduce size to have some space around the drawn shape
|
||||
cw -= (2 * Border);
|
||||
ch -= (2 * Border);
|
||||
|
||||
auto cbb = BoundingBoxf(Vec2d(0, 0),Vec2d(cw, ch));
|
||||
// leave space for origin point
|
||||
cbb.min(0) += 4;
|
||||
cbb.max -= Vec2d(4., 4.);
|
||||
|
||||
// leave space for origin label
|
||||
cbb.max(1) -= 13;
|
||||
|
||||
// read new size
|
||||
cw = cbb.size()(0);
|
||||
ch = cbb.size()(1);
|
||||
|
||||
auto ccenter = cbb.center();
|
||||
|
||||
// get bounding box of bed shape in G - code coordinates
|
||||
|
@ -76,17 +66,17 @@ void Bed_2D::repaint()
|
|||
ccenter(0) - bcenter(0) * sfactor,
|
||||
ccenter(1) - bcenter(1) * sfactor
|
||||
);
|
||||
|
||||
m_scale_factor = sfactor;
|
||||
m_shift = Vec2d(shift(0) + cbb.min(0),
|
||||
shift(1) - (cbb.max(1) - GetSize().GetHeight()));
|
||||
m_shift = Vec2d(shift(0) + cbb.min(0), shift(1) - (cbb.max(1) - ch));
|
||||
|
||||
// draw bed fill
|
||||
dc.SetBrush(wxBrush(wxColour(255, 255, 255), wxBRUSHSTYLE_SOLID));
|
||||
wxPointList pt_list;
|
||||
for (auto pt: m_bed_shape)
|
||||
{
|
||||
Point pt_pix = to_pixels(pt);
|
||||
pt_list.push_back(new wxPoint(pt_pix(0), pt_pix(1)));
|
||||
Point pt_pix = to_pixels(pt, ch);
|
||||
pt_list.push_back(new wxPoint(pt_pix(0), pt_pix(1)));
|
||||
}
|
||||
dc.DrawPolygon(&pt_list, 0, 0);
|
||||
|
||||
|
@ -105,9 +95,9 @@ void Bed_2D::repaint()
|
|||
for (auto pl : polylines)
|
||||
{
|
||||
for (size_t i = 0; i < pl.points.size()-1; i++) {
|
||||
Point pt1 = to_pixels(unscale(pl.points[i]));
|
||||
Point pt2 = to_pixels(unscale(pl.points[i+1]));
|
||||
dc.DrawLine(pt1(0), pt1(1), pt2(0), pt2(1));
|
||||
Point pt1 = to_pixels(unscale(pl.points[i]), ch);
|
||||
Point pt2 = to_pixels(unscale(pl.points[i + 1]), ch);
|
||||
dc.DrawLine(pt1(0), pt1(1), pt2(0), pt2(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,7 +106,7 @@ void Bed_2D::repaint()
|
|||
dc.SetBrush(wxBrush(wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT));
|
||||
dc.DrawPolygon(&pt_list, 0, 0);
|
||||
|
||||
auto origin_px = to_pixels(Vec2d(0, 0));
|
||||
auto origin_px = to_pixels(Vec2d(0, 0), ch);
|
||||
|
||||
// draw axes
|
||||
auto axes_len = 50;
|
||||
|
@ -153,7 +143,7 @@ void Bed_2D::repaint()
|
|||
|
||||
// draw current position
|
||||
if (m_pos!= Vec2d(0, 0)) {
|
||||
auto pos_px = to_pixels(m_pos);
|
||||
auto pos_px = to_pixels(m_pos, ch);
|
||||
dc.SetPen(wxPen(wxColour(200, 0, 0), 2, wxPENSTYLE_SOLID));
|
||||
dc.SetBrush(wxBrush(wxColour(200, 0, 0), wxBRUSHSTYLE_TRANSPARENT));
|
||||
dc.DrawCircle(pos_px(0), pos_px(1), 5);
|
||||
|
@ -161,35 +151,14 @@ void Bed_2D::repaint()
|
|||
dc.DrawLine(pos_px(0) - 15, pos_px(1), pos_px(0) + 15, pos_px(1));
|
||||
dc.DrawLine(pos_px(0), pos_px(1) - 15, pos_px(0), pos_px(1) + 15);
|
||||
}
|
||||
|
||||
m_painted = true;
|
||||
}
|
||||
|
||||
|
||||
// convert G - code coordinates into pixels
|
||||
Point Bed_2D::to_pixels(Vec2d point)
|
||||
Point Bed_2D::to_pixels(Vec2d point, int height)
|
||||
{
|
||||
auto p = point * m_scale_factor + m_shift;
|
||||
return Point(p(0), GetSize().GetHeight() - p(1));
|
||||
}
|
||||
|
||||
void Bed_2D::mouse_event(wxMouseEvent event)
|
||||
{
|
||||
if (!m_interactive) return;
|
||||
if (!m_painted) return;
|
||||
|
||||
auto pos = event.GetPosition();
|
||||
auto point = to_units(Point(pos.x, pos.y));
|
||||
if (event.LeftDown() || event.Dragging()) {
|
||||
if (m_on_move)
|
||||
m_on_move(point) ;
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
// convert pixels into G - code coordinates
|
||||
Vec2d Bed_2D::to_units(Point point)
|
||||
{
|
||||
return (Vec2d(point(0), GetSize().GetHeight() - point(1)) - m_shift) * (1. / m_scale_factor);
|
||||
return Point(p(0) + Border, height - p(1) + Border);
|
||||
}
|
||||
|
||||
void Bed_2D::set_pos(Vec2d pos)
|
||||
|
|
|
@ -9,20 +9,17 @@ namespace GUI {
|
|||
|
||||
class Bed_2D : public wxPanel
|
||||
{
|
||||
static const int Border = 10;
|
||||
|
||||
bool m_user_drawn_background = true;
|
||||
|
||||
bool m_painted = false;
|
||||
bool m_interactive = false;
|
||||
double m_scale_factor;
|
||||
double m_scale_factor;
|
||||
Vec2d m_shift = Vec2d::Zero();
|
||||
Vec2d m_pos = Vec2d::Zero();
|
||||
std::function<void(Vec2d)> m_on_move = nullptr;
|
||||
|
||||
Point to_pixels(Vec2d point);
|
||||
Vec2d to_units(Point point);
|
||||
void repaint();
|
||||
void mouse_event(wxMouseEvent event);
|
||||
void set_pos(Vec2d pos);
|
||||
Point to_pixels(Vec2d point, int height);
|
||||
void repaint();
|
||||
void set_pos(Vec2d pos);
|
||||
|
||||
public:
|
||||
Bed_2D(wxWindow* parent);
|
||||
|
|
|
@ -241,8 +241,6 @@ GLVolume::GLVolume(float r, float g, float b, float a)
|
|||
: m_transformed_bounding_box_dirty(true)
|
||||
, m_sla_shift_z(0.0)
|
||||
, m_transformed_convex_hull_bounding_box_dirty(true)
|
||||
, m_convex_hull(nullptr)
|
||||
, m_convex_hull_owned(false)
|
||||
// geometry_id == 0 -> invalid
|
||||
, geometry_id(std::pair<size_t, size_t>(0, 0))
|
||||
, extruder_id(0)
|
||||
|
@ -268,12 +266,6 @@ GLVolume::GLVolume(float r, float g, float b, float a)
|
|||
set_render_color(r, g, b, a);
|
||||
}
|
||||
|
||||
GLVolume::~GLVolume()
|
||||
{
|
||||
if (m_convex_hull_owned)
|
||||
delete m_convex_hull;
|
||||
}
|
||||
|
||||
void GLVolume::set_render_color(float r, float g, float b, float a)
|
||||
{
|
||||
render_color[0] = r;
|
||||
|
@ -335,12 +327,6 @@ void GLVolume::set_color_from_model_volume(const ModelVolume *model_volume)
|
|||
color[3] = model_volume->is_model_part() ? 1.f : 0.5f;
|
||||
}
|
||||
|
||||
void GLVolume::set_convex_hull(const TriangleMesh *convex_hull, bool owned)
|
||||
{
|
||||
m_convex_hull = convex_hull;
|
||||
m_convex_hull_owned = owned;
|
||||
}
|
||||
|
||||
Transform3d GLVolume::world_matrix() const
|
||||
{
|
||||
Transform3d m = m_instance_transformation.get_matrix() * m_volume_transformation.get_matrix();
|
||||
|
@ -377,7 +363,7 @@ const BoundingBoxf3& GLVolume::transformed_convex_hull_bounding_box() const
|
|||
|
||||
BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d &trafo) const
|
||||
{
|
||||
return (m_convex_hull != nullptr && m_convex_hull->stl.stats.number_of_facets > 0) ?
|
||||
return (m_convex_hull && m_convex_hull->stl.stats.number_of_facets > 0) ?
|
||||
m_convex_hull->transformed_bounding_box(trafo) :
|
||||
bounding_box.transformed(trafo);
|
||||
}
|
||||
|
@ -587,7 +573,7 @@ int GLVolumeCollection::load_object_volume(
|
|||
const ModelVolume *model_volume = model_object->volumes[volume_idx];
|
||||
const int extruder_id = model_volume->extruder_id();
|
||||
const ModelInstance *instance = model_object->instances[instance_idx];
|
||||
const TriangleMesh& mesh = model_volume->mesh;
|
||||
const TriangleMesh& mesh = model_volume->mesh();
|
||||
float color[4];
|
||||
memcpy(color, GLVolume::MODEL_COLOR[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3);
|
||||
/* if (model_volume->is_support_blocker()) {
|
||||
|
@ -613,7 +599,7 @@ int GLVolumeCollection::load_object_volume(
|
|||
if (model_volume->is_model_part())
|
||||
{
|
||||
// GLVolume will reference a convex hull from model_volume!
|
||||
v.set_convex_hull(&model_volume->get_convex_hull(), false);
|
||||
v.set_convex_hull(model_volume->get_convex_hull_shared_ptr());
|
||||
if (extruder_id != -1)
|
||||
v.extruder_id = extruder_id;
|
||||
}
|
||||
|
@ -656,7 +642,10 @@ void GLVolumeCollection::load_object_auxiliary(
|
|||
v.composite_id = GLVolume::CompositeID(obj_idx, - int(milestone), (int)instance_idx.first);
|
||||
v.geometry_id = std::pair<size_t, size_t>(timestamp, model_instance.id().id);
|
||||
// Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance.
|
||||
v.set_convex_hull((&instance_idx == &instances.back()) ? new TriangleMesh(std::move(convex_hull)) : new TriangleMesh(convex_hull), true);
|
||||
if (&instance_idx == &instances.back())
|
||||
v.set_convex_hull(std::move(convex_hull));
|
||||
else
|
||||
v.set_convex_hull(convex_hull);
|
||||
v.is_modifier = false;
|
||||
v.shader_outside_printer_detection_enabled = (milestone == slaposSupportTree);
|
||||
v.set_instance_transformation(model_instance.get_transformation());
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "slic3r/GUI/GLCanvas3DManager.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define HAS_GLSAFE
|
||||
|
@ -243,7 +244,6 @@ public:
|
|||
|
||||
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]) {}
|
||||
~GLVolume();
|
||||
|
||||
private:
|
||||
Geometry::Transformation m_instance_transformation;
|
||||
|
@ -255,10 +255,8 @@ private:
|
|||
mutable BoundingBoxf3 m_transformed_bounding_box;
|
||||
// Whether or not is needed to recalculate the transformed bounding box.
|
||||
mutable bool m_transformed_bounding_box_dirty;
|
||||
// Pointer to convex hull of the original mesh, if any.
|
||||
// This object may or may not own the convex hull instance based on m_convex_hull_owned
|
||||
const TriangleMesh* m_convex_hull;
|
||||
bool m_convex_hull_owned;
|
||||
// Convex hull of the volume, if any.
|
||||
std::shared_ptr<const TriangleMesh> m_convex_hull;
|
||||
// Bounding box of this volume, in unscaled coordinates.
|
||||
mutable BoundingBoxf3 m_transformed_convex_hull_bounding_box;
|
||||
// Whether or not is needed to recalculate the transformed convex hull bounding box.
|
||||
|
@ -395,7 +393,9 @@ public:
|
|||
double get_sla_shift_z() const { return m_sla_shift_z; }
|
||||
void set_sla_shift_z(double z) { m_sla_shift_z = z; }
|
||||
|
||||
void set_convex_hull(const TriangleMesh *convex_hull, bool owned);
|
||||
void set_convex_hull(std::shared_ptr<const TriangleMesh> convex_hull) { m_convex_hull = std::move(convex_hull); }
|
||||
void set_convex_hull(const TriangleMesh &convex_hull) { m_convex_hull = std::make_shared<const TriangleMesh>(convex_hull); }
|
||||
void set_convex_hull(TriangleMesh &&convex_hull) { m_convex_hull = std::make_shared<const TriangleMesh>(std::move(convex_hull)); }
|
||||
|
||||
int object_idx() const { return this->composite_id.object_id; }
|
||||
int volume_idx() const { return this->composite_id.volume_id; }
|
||||
|
|
|
@ -31,6 +31,165 @@ void AboutDialogLogo::onRepaint(wxEvent &event)
|
|||
event.Skip();
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------
|
||||
// CopyrightsDialog
|
||||
// -----------------------------------------
|
||||
CopyrightsDialog::CopyrightsDialog()
|
||||
: DPIDialog(NULL, wxID_ANY, wxString::Format("%s - %s", SLIC3R_APP_NAME, _(L("Portions copyright"))),
|
||||
wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||
{
|
||||
this->SetFont(wxGetApp().normal_font());
|
||||
this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
|
||||
auto sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
fill_entries();
|
||||
|
||||
m_html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition,
|
||||
wxSize(40 * em_unit(), 20 * em_unit()), wxHW_SCROLLBAR_AUTO);
|
||||
|
||||
wxFont font = GetFont();
|
||||
const int fs = font.GetPointSize();
|
||||
const int fs2 = static_cast<int>(1.2f*fs);
|
||||
int size[] = { fs, fs, fs, fs, fs2, fs2, fs2 };
|
||||
|
||||
m_html->SetFonts(font.GetFaceName(), font.GetFaceName(), size);
|
||||
m_html->SetBorders(2);
|
||||
m_html->SetPage(get_html_text());
|
||||
|
||||
sizer->Add(m_html, 1, wxEXPAND | wxALL, 15);
|
||||
m_html->Bind(wxEVT_HTML_LINK_CLICKED, &CopyrightsDialog::onLinkClicked, this);
|
||||
|
||||
wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCLOSE);
|
||||
|
||||
this->SetEscapeId(wxID_CLOSE);
|
||||
this->Bind(wxEVT_BUTTON, &CopyrightsDialog::onCloseDialog, this, wxID_CLOSE);
|
||||
sizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 3);
|
||||
|
||||
SetSizer(sizer);
|
||||
sizer->SetSizeHints(this);
|
||||
|
||||
}
|
||||
|
||||
void CopyrightsDialog::fill_entries()
|
||||
{
|
||||
m_entries = {
|
||||
{ "wxWidgets" , "2019 wxWidgets" , "https://www.wxwidgets.org/" },
|
||||
{ "OpenGL" , "1997-2019 The Khronos™ Group Inc" , "https://www.opengl.org/" },
|
||||
{ "GNU gettext" , "1998, 2019 Free Software Foundation, Inc." , "https://www.gnu.org/software/gettext/" },
|
||||
{ "PoEdit" , "2019 Václav Slavík" , "https://poedit.net/" },
|
||||
{ "ImGUI" , "2014-2019 Omar Cornut" , "https://github.com/ocornut/imgui" },
|
||||
{ "Eigen" , "" , "http://eigen.tuxfamily.org" },
|
||||
{ "ADMesh" , "1995, 1996 Anthony D. Martin; "
|
||||
"2015, ADMesh contributors" , "https://admesh.readthedocs.io/en/latest/" },
|
||||
{ "Anti-Grain Geometry"
|
||||
, "2002-2005 Maxim Shemanarev (McSeem)" , "http://antigrain.com" },
|
||||
{ "Boost" , "1998-2005 Beman Dawes, David Abrahams; "
|
||||
"2004 - 2007 Rene Rivera" , "https://www.boost.org/" },
|
||||
{ "Clipper" , "2010-2015 Angus Johnson " , "http://www.angusj.com " },
|
||||
{ "GLEW (The OpenGL Extension Wrangler Library)",
|
||||
"2002 - 2007, Milan Ikits; "
|
||||
"2002 - 2007, Marcelo E.Magallon; "
|
||||
"2002, Lev Povalahev" , "http://glew.sourceforge.net/" },
|
||||
{ "Libigl" , "2013 Alec Jacobson and others" , "https://libigl.github.io/" },
|
||||
{ "Poly2Tri" , "2009-2018, Poly2Tri Contributors" , "https://github.com/jhasse/poly2tri" },
|
||||
{ "PolyPartition" , "2011 Ivan Fratric" , "https://github.com/ivanfratric/polypartition" },
|
||||
{ "Qhull" , "1993-2015 C.B.Barber Arlington and "
|
||||
"University of Minnesota" , "http://qhull.org/" },
|
||||
{ "SemVer" , "2015-2017 Tomas Aparicio" , "https://semver.org/" },
|
||||
{ "Nanosvg" , "2013-14 Mikko Mononen" , "https://github.com/memononen/nanosvg" },
|
||||
{ "Miniz" , "2013-2014 RAD Game Tools and Valve Software; "
|
||||
"2010-2014 Rich Geldreich and Tenacious Software LLC"
|
||||
, "https://github.com/richgel999/miniz" },
|
||||
{ "Expat" , "1998-2000 Thai Open Source Software Center Ltd and Clark Cooper"
|
||||
"2001-2016 Expat maintainers" , "http://www.libexpat.org/" },
|
||||
{ "AVRDUDE" , "2018 Free Software Foundation, Inc." , "http://savannah.nongnu.org/projects/avrdude" },
|
||||
{ "Shinyprofiler" , "2007-2010 Aidin Abedi" , "http://code.google.com/p/shinyprofiler/" },
|
||||
{ "Icons for STL and GCODE files."
|
||||
, "Akira Yasuda" , "http://3dp0.com/icons-for-stl-and-gcode/" }
|
||||
};
|
||||
}
|
||||
|
||||
wxString CopyrightsDialog::get_html_text()
|
||||
{
|
||||
wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||
|
||||
const auto text_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
const auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue());
|
||||
const auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue());
|
||||
|
||||
const wxString copyright_str = _(L("Copyright")) + "© ";
|
||||
// TRN "Slic3r _is licensed under the_ License"
|
||||
const wxString header_str = _(L("License agreements of all following programs (libraries) are part of application license agreement"));
|
||||
|
||||
wxString text = wxString::Format(
|
||||
"<html>"
|
||||
"<body bgcolor= %s link= %s>"
|
||||
"<font color=%s>"
|
||||
"<font size=\"5\">%s.</font>"
|
||||
"<br /><br />"
|
||||
"<font size=\"3\">"
|
||||
, bgr_clr_str, text_clr_str
|
||||
, text_clr_str
|
||||
, header_str);
|
||||
|
||||
for (auto& entry : m_entries) {
|
||||
text += wxString::Format(
|
||||
"<a href=\"%s\">%s</a><br/>"
|
||||
, entry.link, entry.lib_name);
|
||||
|
||||
if (!entry.copyright.empty())
|
||||
text += wxString::Format(
|
||||
"%s %s"
|
||||
"<br/><br/>"
|
||||
, copyright_str, entry.copyright);
|
||||
}
|
||||
|
||||
text += wxString(
|
||||
"</font>"
|
||||
"</font>"
|
||||
"</body>"
|
||||
"</html>");
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
void CopyrightsDialog::on_dpi_changed(const wxRect &suggested_rect)
|
||||
{
|
||||
const wxFont& font = GetFont();
|
||||
const int fs = font.GetPointSize();
|
||||
const int fs2 = static_cast<int>(1.2f*fs);
|
||||
int font_size[] = { fs, fs, fs, fs, fs2, fs2, fs2 };
|
||||
|
||||
m_html->SetFonts(font.GetFaceName(), font.GetFaceName(), font_size);
|
||||
|
||||
const int& em = em_unit();
|
||||
|
||||
msw_buttons_rescale(this, em, { wxID_CLOSE });
|
||||
|
||||
const wxSize& size = wxSize(40 * em, 20 * em);
|
||||
|
||||
m_html->SetMinSize(size);
|
||||
m_html->Refresh();
|
||||
|
||||
SetMinSize(size);
|
||||
Fit();
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void CopyrightsDialog::onLinkClicked(wxHtmlLinkEvent &event)
|
||||
{
|
||||
wxLaunchDefaultBrowser(event.GetLinkInfo().GetHref());
|
||||
event.Skip(false);
|
||||
}
|
||||
|
||||
void CopyrightsDialog::onCloseDialog(wxEvent &)
|
||||
{
|
||||
this->EndModal(wxID_CLOSE);
|
||||
}
|
||||
|
||||
AboutDialog::AboutDialog()
|
||||
: DPIDialog(NULL, wxID_ANY, wxString::Format(_(L("About %s")), SLIC3R_APP_NAME), wxDefaultPosition,
|
||||
wxDefaultSize, /*wxCAPTION*/wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||
|
@ -93,6 +252,7 @@ AboutDialog::AboutDialog()
|
|||
// TRN "Slic3r _is licensed under the_ License"
|
||||
const wxString is_lecensed_str = _(L("is licensed under the"));
|
||||
const wxString license_str = _(L("GNU Affero General Public License, version 3"));
|
||||
const wxString based_on_str = _(L("PrusaSlicer is based on Slic3r by Alessandro Ranellucci and the RepRap community."));
|
||||
const wxString contributors_str = _(L("Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and numerous others."));
|
||||
const auto text = wxString::Format(
|
||||
"<html>"
|
||||
|
@ -104,19 +264,29 @@ AboutDialog::AboutDialog()
|
|||
"<a href=\"http://www.gnu.org/licenses/agpl-3.0.html\">%s</a>."
|
||||
"<br /><br />"
|
||||
"%s"
|
||||
"<br /><br />"
|
||||
"%s"
|
||||
"</font>"
|
||||
"</body>"
|
||||
"</html>", bgr_clr_str, text_clr_str, text_clr_str,
|
||||
copyright_str, copyright_str,
|
||||
is_lecensed_str,
|
||||
license_str,
|
||||
based_on_str,
|
||||
contributors_str);
|
||||
m_html->SetPage(text);
|
||||
vsizer->Add(m_html, 1, wxEXPAND | wxBOTTOM, 10);
|
||||
m_html->Bind(wxEVT_HTML_LINK_CLICKED, &AboutDialog::onLinkClicked, this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCLOSE);
|
||||
|
||||
m_copy_rights_btn_id = NewControlId();
|
||||
auto copy_rights_btn = new wxButton(this, m_copy_rights_btn_id, _(L("Portions copyright"))+dots);
|
||||
buttons->Insert(0, copy_rights_btn, 0, wxLEFT, 5);
|
||||
copy_rights_btn->Bind(wxEVT_BUTTON, &AboutDialog::onCopyrightBtn, this);
|
||||
|
||||
this->SetEscapeId(wxID_CLOSE);
|
||||
this->Bind(wxEVT_BUTTON, &AboutDialog::onCloseDialog, this, wxID_CLOSE);
|
||||
vsizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 3);
|
||||
|
@ -137,7 +307,7 @@ void AboutDialog::on_dpi_changed(const wxRect &suggested_rect)
|
|||
|
||||
const int& em = em_unit();
|
||||
|
||||
msw_buttons_rescale(this, em, { wxID_CLOSE });
|
||||
msw_buttons_rescale(this, em, { wxID_CLOSE, m_copy_rights_btn_id });
|
||||
|
||||
m_html->SetMinSize(wxSize(-1, 16 * em));
|
||||
m_html->Refresh();
|
||||
|
@ -159,7 +329,12 @@ void AboutDialog::onLinkClicked(wxHtmlLinkEvent &event)
|
|||
void AboutDialog::onCloseDialog(wxEvent &)
|
||||
{
|
||||
this->EndModal(wxID_CLOSE);
|
||||
this->Close();
|
||||
}
|
||||
|
||||
void AboutDialog::onCopyrightBtn(wxEvent &)
|
||||
{
|
||||
CopyrightsDialog dlg;
|
||||
dlg.ShowModal();
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
|
|
|
@ -23,11 +23,45 @@ private:
|
|||
void onRepaint(wxEvent &event);
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CopyrightsDialog : public DPIDialog
|
||||
{
|
||||
public:
|
||||
CopyrightsDialog();
|
||||
~CopyrightsDialog() {}
|
||||
|
||||
struct Entry {
|
||||
Entry(const std::string &lib_name, const std::string ©right, const std::string &link) :
|
||||
lib_name(lib_name), copyright(copyright), link(link) {}
|
||||
|
||||
std::string lib_name;
|
||||
std::string copyright;
|
||||
std::string link;
|
||||
};
|
||||
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect &suggested_rect) override;
|
||||
|
||||
private:
|
||||
wxHtmlWindow* m_html;
|
||||
std::vector<Entry> m_entries;
|
||||
|
||||
void onLinkClicked(wxHtmlLinkEvent &event);
|
||||
void onCloseDialog(wxEvent &);
|
||||
|
||||
void fill_entries();
|
||||
wxString get_html_text();
|
||||
};
|
||||
|
||||
|
||||
|
||||
class AboutDialog : public DPIDialog
|
||||
{
|
||||
ScalableBitmap m_logo_bitmap;
|
||||
wxHtmlWindow* m_html;
|
||||
wxStaticBitmap* m_logo;
|
||||
int m_copy_rights_btn_id { wxID_ANY };
|
||||
public:
|
||||
AboutDialog();
|
||||
|
||||
|
@ -37,6 +71,7 @@ protected:
|
|||
private:
|
||||
void onLinkClicked(wxHtmlLinkEvent &event);
|
||||
void onCloseDialog(wxEvent &);
|
||||
void onCopyrightBtn(wxEvent &);
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace Slic3r {
|
|||
|
||||
static const std::string VENDOR_PREFIX = "vendor:";
|
||||
static const std::string MODEL_PREFIX = "model:";
|
||||
static const std::string VERSION_CHECK_URL = "https://raw.githubusercontent.com/prusa3d/PrusaSlicer-settings/master/live/PrusaSlicer.version";
|
||||
static const std::string VERSION_CHECK_URL = "http://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaSlicer.version";
|
||||
|
||||
void AppConfig::reset()
|
||||
{
|
||||
|
@ -55,7 +55,7 @@ void AppConfig::set_defaults()
|
|||
set("preset_update", "1");
|
||||
|
||||
// Use OpenGL 1.1 even if OpenGL 2.0 is available. This is mainly to support some buggy Intel HD Graphics drivers.
|
||||
// https://github.com/prusa3d/Slic3r/issues/233
|
||||
// github.com/prusa3d/PrusaSlicer/issues/233
|
||||
if (get("use_legacy_opengl").empty())
|
||||
set("use_legacy_opengl", "0");
|
||||
|
||||
|
@ -67,6 +67,12 @@ void AppConfig::set_defaults()
|
|||
if (get("remember_output_path").empty())
|
||||
set("remember_output_path", "1");
|
||||
|
||||
if (get("use_custom_toolbar_size").empty())
|
||||
set("use_custom_toolbar_size", "0");
|
||||
|
||||
if (get("custom_toolbar_size").empty())
|
||||
set("custom_toolbar_size", "100");
|
||||
|
||||
// Remove legacy window positions/sizes
|
||||
erase("", "main_frame_maximized");
|
||||
erase("", "main_frame_pos");
|
||||
|
|
|
@ -67,6 +67,14 @@ PrinterTechnology BackgroundSlicingProcess::current_printer_technology() const
|
|||
return m_print->technology();
|
||||
}
|
||||
|
||||
std::string BackgroundSlicingProcess::output_filepath_for_project(const boost::filesystem::path &project_path)
|
||||
{
|
||||
assert(m_print != nullptr);
|
||||
if (project_path.empty())
|
||||
return m_print->output_filepath("");
|
||||
return m_print->output_filepath(project_path.parent_path().string(), project_path.stem().string());
|
||||
}
|
||||
|
||||
// This function may one day be merged into the Print, but historically the print was separated
|
||||
// from the G-code generator.
|
||||
void BackgroundSlicingProcess::process_fff()
|
||||
|
@ -81,7 +89,7 @@ void BackgroundSlicingProcess::process_fff()
|
|||
// Perform the final post-processing of the export path by applying the print statistics over the file name.
|
||||
std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path);
|
||||
if (copy_file(m_temp_output_path, export_path) != 0)
|
||||
throw std::runtime_error(_utf8(L("Copying of the temporary G-code to the output G-code failed")));
|
||||
throw std::runtime_error(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?")));
|
||||
m_print->set_status(95, _utf8(L("Running post-processing scripts")));
|
||||
run_post_process_scripts(export_path, m_fff_print->config());
|
||||
m_print->set_status(100, (boost::format(_utf8(L("G-code file exported to %1%"))) % export_path).str());
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <wx/event.h>
|
||||
|
||||
#include "libslic3r/Print.hpp"
|
||||
|
@ -65,6 +67,9 @@ public:
|
|||
const PrintBase* current_print() const { return m_print; }
|
||||
const Print* fff_print() const { return m_fff_print; }
|
||||
const SLAPrint* sla_print() const { return m_sla_print; }
|
||||
// Take the project path (if provided), extract the name of the project, run it through the macro processor and save it next to the project file.
|
||||
// If the project_path is empty, just run output_filepath().
|
||||
std::string output_filepath_for_project(const boost::filesystem::path &project_path);
|
||||
|
||||
// Start the background processing. Returns false if the background processing was already running.
|
||||
bool start();
|
||||
|
|
|
@ -30,11 +30,9 @@ void BedShapeDialog::build_dialog(ConfigOptionPoints* default_pt)
|
|||
SetMinSize(GetSize());
|
||||
main_sizer->SetSizeHints(this);
|
||||
|
||||
// needed to actually free memory
|
||||
this->Bind(wxEVT_CLOSE_WINDOW, ([this](wxCloseEvent e) {
|
||||
EndModal(wxID_OK);
|
||||
Destroy();
|
||||
}));
|
||||
this->Bind(wxEVT_CLOSE_WINDOW, ([this](wxCloseEvent& evt) {
|
||||
EndModal(wxID_CANCEL);
|
||||
}));
|
||||
}
|
||||
|
||||
void BedShapeDialog::on_dpi_changed(const wxRect &suggested_rect)
|
||||
|
@ -135,7 +133,7 @@ void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
|
|||
|
||||
// Called from the constructor.
|
||||
// Create a panel for a rectangular / circular / custom bed shape.
|
||||
ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(wxString title)
|
||||
ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(const wxString& title)
|
||||
{
|
||||
|
||||
auto panel = new wxPanel(m_shape_options_book);
|
||||
|
@ -305,8 +303,9 @@ void BedShapePanel::update_shape()
|
|||
}
|
||||
m_canvas->m_bed_shape = points;
|
||||
}
|
||||
else if (page_idx == SHAPE_CUSTOM)
|
||||
m_canvas->m_bed_shape = m_loaded_bed_shape;
|
||||
|
||||
// $self->{on_change}->();
|
||||
update_preview();
|
||||
}
|
||||
|
||||
|
@ -351,8 +350,9 @@ void BedShapePanel::load_stl()
|
|||
std::vector<Vec2d> points;
|
||||
for (auto pt : polygon.points)
|
||||
points.push_back(unscale(pt));
|
||||
m_canvas->m_bed_shape = points;
|
||||
update_preview();
|
||||
|
||||
m_loaded_bed_shape = points;
|
||||
update_shape();
|
||||
}
|
||||
|
||||
} // GUI
|
||||
|
|
|
@ -16,7 +16,8 @@ namespace GUI {
|
|||
using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>;
|
||||
class BedShapePanel : public wxPanel
|
||||
{
|
||||
Bed_2D* m_canvas;
|
||||
Bed_2D* m_canvas;
|
||||
std::vector<Vec2d> m_loaded_bed_shape;
|
||||
|
||||
public:
|
||||
BedShapePanel(wxWindow* parent) : wxPanel(parent, wxID_ANY) {}
|
||||
|
@ -24,8 +25,8 @@ public:
|
|||
|
||||
void build_panel(ConfigOptionPoints* default_pt);
|
||||
|
||||
ConfigOptionsGroupShp init_shape_options_page(wxString title);
|
||||
void set_shape(ConfigOptionPoints* points);
|
||||
ConfigOptionsGroupShp init_shape_options_page(const wxString& title);
|
||||
void set_shape(ConfigOptionPoints* points);
|
||||
void update_preview();
|
||||
void update_shape();
|
||||
void load_stl();
|
||||
|
|
|
@ -189,7 +189,7 @@ wxBitmap* BitmapCache::insert_raw_rgba(const std::string &bitmap_key, unsigned w
|
|||
}
|
||||
|
||||
if (grayscale)
|
||||
image.ConvertToGreyscale(m_gs, m_gs, m_gs);
|
||||
image = image.ConvertToGreyscale(m_gs, m_gs, m_gs);
|
||||
|
||||
return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image), scale));
|
||||
}
|
||||
|
@ -220,7 +220,7 @@ wxBitmap* BitmapCache::load_png(const std::string &bitmap_name, unsigned int wid
|
|||
image.Rescale(width, height, wxIMAGE_QUALITY_BILINEAR);
|
||||
|
||||
if (grayscale)
|
||||
image.ConvertToGreyscale(m_gs, m_gs, m_gs);
|
||||
image = image.ConvertToGreyscale(m_gs, m_gs, m_gs);
|
||||
|
||||
return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image)));
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ struct LifetimeGuard
|
|||
};
|
||||
|
||||
BonjourDialog::BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology tech)
|
||||
: wxDialog(parent, wxID_ANY, _(L("Network lookup")), wxDefaultPosition, wxDefaultSize, wxRESIZE_BORDER)
|
||||
: wxDialog(parent, wxID_ANY, _(L("Network lookup")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
|
||||
, list(new wxListView(this, wxID_ANY))
|
||||
, replies(new ReplySet)
|
||||
, label(new wxStaticText(this, wxID_ANY, ""))
|
||||
|
|
|
@ -56,9 +56,9 @@ public:
|
|||
|
||||
Vec3d get_dir_right() const { return m_view_matrix.matrix().block(0, 0, 3, 3).row(0); }
|
||||
Vec3d get_dir_up() const { return m_view_matrix.matrix().block(0, 0, 3, 3).row(1); }
|
||||
Vec3d get_dir_forward() const { return m_view_matrix.matrix().block(0, 0, 3, 3).row(2); }
|
||||
Vec3d get_dir_forward() const { return -m_view_matrix.matrix().block(0, 0, 3, 3).row(2); }
|
||||
|
||||
Vec3d get_position() const { return m_view_matrix.matrix().block(0, 0, 3, 3).row(3); }
|
||||
Vec3d get_position() const { return m_view_matrix.matrix().inverse().block(0, 3, 3, 1); }
|
||||
|
||||
void apply_viewport(int x, int y, unsigned int w, unsigned int h) const;
|
||||
void apply_view_matrix() const;
|
||||
|
|
|
@ -159,13 +159,11 @@ void ConfigSnapshotDialog::onLinkClicked(wxHtmlLinkEvent &event)
|
|||
{
|
||||
m_snapshot_to_activate = event.GetLinkInfo().GetHref();
|
||||
this->EndModal(wxID_CLOSE);
|
||||
this->Close();
|
||||
}
|
||||
|
||||
void ConfigSnapshotDialog::onCloseDialog(wxEvent &)
|
||||
{
|
||||
this->EndModal(wxID_CLOSE);
|
||||
this->Close();
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
|
|
|
@ -590,6 +590,22 @@ void PageDiameters::apply_custom_config(DynamicPrintConfig &config)
|
|||
config.set_key_value("nozzle_diameter", opt_nozzle);
|
||||
auto *opt_filam = new ConfigOptionFloats(1, spin_filam->GetValue());
|
||||
config.set_key_value("filament_diameter", opt_filam);
|
||||
|
||||
auto set_extrusion_width = [&config, opt_nozzle](const char *key, double dmr) {
|
||||
char buf[64];
|
||||
sprintf(buf, "%.2lf", dmr * opt_nozzle->values.front() / 0.4);
|
||||
config.set_key_value(key, new ConfigOptionFloatOrPercent(atof(buf), false));
|
||||
};
|
||||
|
||||
set_extrusion_width("support_material_extrusion_width", 0.35);
|
||||
set_extrusion_width("top_infill_extrusion_width", 0.40);
|
||||
set_extrusion_width("first_layer_extrusion_width", 0.42);
|
||||
|
||||
set_extrusion_width("extrusion_width", 0.45);
|
||||
set_extrusion_width("perimeter_extrusion_width", 0.45);
|
||||
set_extrusion_width("external_perimeter_extrusion_width", 0.45);
|
||||
set_extrusion_width("infill_extrusion_width", 0.45);
|
||||
set_extrusion_width("solid_infill_extrusion_width", 0.45);
|
||||
}
|
||||
|
||||
PageTemperatures::PageTemperatures(ConfigWizard *parent)
|
||||
|
|
|
@ -434,7 +434,6 @@ void CheckBox::msw_rescale()
|
|||
field->SetMinSize(wxSize(-1, int(1.5f*field->GetFont().GetPixelSize().y +0.5f)));
|
||||
}
|
||||
|
||||
int undef_spin_val = -9999; //! Probably, It's not necessary
|
||||
|
||||
void SpinCtrl::BUILD() {
|
||||
auto size = wxSize(wxDefaultSize);
|
||||
|
@ -472,12 +471,14 @@ void SpinCtrl::BUILD() {
|
|||
temp->SetFont(Slic3r::GUI::wxGetApp().normal_font());
|
||||
temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||
|
||||
#ifndef __WXOSX__
|
||||
// #ys_FIXME_KILL_FOCUS
|
||||
// wxEVT_KILL_FOCUS doesn't handled on OSX now (wxWidgets 3.1.1)
|
||||
// So, we will update values on KILL_FOCUS & SPINCTRL events under MSW and GTK
|
||||
// and on TEXT event under OSX
|
||||
// XXX: On OS X the wxSpinCtrl widget is made up of two subwidgets, unfortunatelly
|
||||
// the kill focus event is not propagated to the encompassing widget,
|
||||
// so we need to bind it on the inner text widget instead. (Ugh.)
|
||||
#ifdef __WXOSX__
|
||||
temp->GetText()->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e)
|
||||
#else
|
||||
temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e)
|
||||
#endif
|
||||
{
|
||||
e.Skip();
|
||||
if (bEnterPressed) {
|
||||
|
@ -486,7 +487,7 @@ void SpinCtrl::BUILD() {
|
|||
}
|
||||
|
||||
propagate_value();
|
||||
}), temp->GetId());
|
||||
}));
|
||||
|
||||
temp->Bind(wxEVT_SPINCTRL, ([this](wxCommandEvent e) { propagate_value(); }), temp->GetId());
|
||||
|
||||
|
@ -496,7 +497,6 @@ void SpinCtrl::BUILD() {
|
|||
propagate_value();
|
||||
bEnterPressed = true;
|
||||
}), temp->GetId());
|
||||
#endif
|
||||
|
||||
temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e)
|
||||
{
|
||||
|
@ -504,24 +504,23 @@ void SpinCtrl::BUILD() {
|
|||
// # when it was changed from the text control, so the on_change callback
|
||||
// # gets the old one, and on_kill_focus resets the control to the old value.
|
||||
// # As a workaround, we get the new value from $event->GetString and store
|
||||
// # here temporarily so that we can return it from $self->get_value
|
||||
std::string value = e.GetString().utf8_str().data();
|
||||
if (is_matched(value, "^\\-?\\d+$")) {
|
||||
try {
|
||||
tmp_value = std::stoi(value);
|
||||
}
|
||||
catch (const std::exception & /* e */) {
|
||||
tmp_value = -9999;
|
||||
}
|
||||
}
|
||||
else tmp_value = -9999;
|
||||
#ifdef __WXOSX__
|
||||
propagate_value();
|
||||
// # here temporarily so that we can return it from get_value()
|
||||
|
||||
long value;
|
||||
const bool parsed = e.GetString().ToLong(&value);
|
||||
tmp_value = parsed && value >= INT_MIN && value <= INT_MAX ? (int)value : UNDEF_VALUE;
|
||||
|
||||
#ifdef __WXOSX__
|
||||
// Forcibly set the input value for SpinControl, since the value
|
||||
// inserted from the clipboard is not updated under OSX
|
||||
if (tmp_value > -9999)
|
||||
dynamic_cast<wxSpinCtrl*>(window)->SetValue(tmp_value);
|
||||
// inserted from the keyboard or clipboard is not updated under OSX
|
||||
if (tmp_value != UNDEF_VALUE) {
|
||||
wxSpinCtrl* spin = static_cast<wxSpinCtrl*>(window);
|
||||
spin->SetValue(tmp_value);
|
||||
|
||||
// But in SetValue() is executed m_text_ctrl->SelectAll(), so
|
||||
// discard this selection and set insertion point to the end of string
|
||||
spin->GetText()->SetInsertionPointEnd();
|
||||
}
|
||||
#endif
|
||||
}), temp->GetId());
|
||||
|
||||
|
@ -533,10 +532,11 @@ void SpinCtrl::BUILD() {
|
|||
|
||||
void SpinCtrl::propagate_value()
|
||||
{
|
||||
if (tmp_value == -9999)
|
||||
if (tmp_value == UNDEF_VALUE) {
|
||||
on_kill_focus();
|
||||
else if (boost::any_cast<int>(m_value) != tmp_value)
|
||||
} else {
|
||||
on_change_field();
|
||||
}
|
||||
}
|
||||
|
||||
void SpinCtrl::msw_rescale()
|
||||
|
@ -577,12 +577,15 @@ void Choice::BUILD() {
|
|||
// recast as a wxWindow to fit the calling convention
|
||||
window = dynamic_cast<wxWindow*>(temp);
|
||||
|
||||
if (m_opt.enum_labels.empty() && m_opt.enum_values.empty()) {
|
||||
}
|
||||
else{
|
||||
for (auto el : m_opt.enum_labels.empty() ? m_opt.enum_values : m_opt.enum_labels) {
|
||||
const wxString& str = _(el);//m_opt_id == "support" ? _(el) : el;
|
||||
temp->Append(str);
|
||||
if (! m_opt.enum_labels.empty() || ! m_opt.enum_values.empty()) {
|
||||
if (m_opt.enum_labels.empty()) {
|
||||
// Append non-localized enum_values
|
||||
for (auto el : m_opt.enum_values)
|
||||
temp->Append(el);
|
||||
} else {
|
||||
// Append localized enum_labels
|
||||
for (auto el : m_opt.enum_labels)
|
||||
temp->Append(_(el));
|
||||
}
|
||||
set_selection();
|
||||
}
|
||||
|
@ -608,7 +611,11 @@ void Choice::BUILD() {
|
|||
if (m_is_editable) {
|
||||
temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) {
|
||||
e.Skip();
|
||||
if (m_opt.type == coStrings) return;
|
||||
if (m_opt.type == coStrings) {
|
||||
on_change_field();
|
||||
return;
|
||||
}
|
||||
|
||||
double old_val = !m_value.empty() ? boost::any_cast<double>(m_value) : -99999;
|
||||
if (is_defined_input_value<wxBitmapComboBox>(window, m_opt.type)) {
|
||||
if (fabs(old_val - boost::any_cast<double>(get_value())) <= 0.0001)
|
||||
|
@ -846,7 +853,7 @@ boost::any& Choice::get_value()
|
|||
else if (m_opt.gui_type == "f_enum_open") {
|
||||
const int ret_enum = field->GetSelection();
|
||||
if (ret_enum < 0 || m_opt.enum_values.empty() || m_opt.type == coStrings ||
|
||||
ret_str != m_opt.enum_values[ret_enum] && ret_str != m_opt.enum_labels[ret_enum] )
|
||||
(ret_str != m_opt.enum_values[ret_enum] && ret_str != _(m_opt.enum_labels[ret_enum])))
|
||||
// modifies ret_string!
|
||||
get_value_by_opt_type(ret_str);
|
||||
else
|
||||
|
@ -882,15 +889,16 @@ void Choice::msw_rescale()
|
|||
// Set rescaled size
|
||||
field->SetSize(size);
|
||||
|
||||
size_t idx, counter = idx = 0;
|
||||
if (m_opt.enum_labels.empty() && m_opt.enum_values.empty()) {}
|
||||
else{
|
||||
for (auto el : m_opt.enum_labels.empty() ? m_opt.enum_values : m_opt.enum_labels) {
|
||||
const wxString& str = _(el);
|
||||
field->Append(str);
|
||||
if (el.compare(selection) == 0)
|
||||
size_t idx = 0;
|
||||
if (! m_opt.enum_labels.empty() || ! m_opt.enum_values.empty()) {
|
||||
size_t counter = 0;
|
||||
bool labels = ! m_opt.enum_labels.empty();
|
||||
for (const std::string &el : labels ? m_opt.enum_labels : m_opt.enum_values) {
|
||||
wxString text = labels ? _(el) : wxString::FromUTF8(el.c_str());
|
||||
field->Append(text);
|
||||
if (text == selection)
|
||||
idx = counter;
|
||||
++counter;
|
||||
++ counter;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#endif
|
||||
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <boost/any.hpp>
|
||||
|
||||
|
@ -331,9 +332,11 @@ public:
|
|||
|
||||
class SpinCtrl : public Field {
|
||||
using Field::Field;
|
||||
private:
|
||||
static const int UNDEF_VALUE = INT_MIN;
|
||||
public:
|
||||
SpinCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id), tmp_value(-9999) {}
|
||||
SpinCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id), tmp_value(-9999) {}
|
||||
SpinCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id), tmp_value(UNDEF_VALUE) {}
|
||||
SpinCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id), tmp_value(UNDEF_VALUE) {}
|
||||
~SpinCtrl() {}
|
||||
|
||||
int tmp_value;
|
||||
|
@ -355,9 +358,10 @@ public:
|
|||
dynamic_cast<wxSpinCtrl*>(window)->SetValue(tmp_value);
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
|
||||
boost::any& get_value() override {
|
||||
// return boost::any(tmp_value);
|
||||
return m_value = tmp_value;
|
||||
int value = static_cast<wxSpinCtrl*>(window)->GetValue();
|
||||
return m_value = value;
|
||||
}
|
||||
|
||||
void msw_rescale() override;
|
||||
|
|
|
@ -723,10 +723,10 @@ void FirmwareDialog::priv::ensure_joined()
|
|||
const char* FirmwareDialog::priv::avr109_dev_name(Avr109Pid usb_pid) {
|
||||
switch (usb_pid.boot) {
|
||||
case USB_PID_MMU_BOOT:
|
||||
return "Prusa MMU 2.0 Control";
|
||||
return "Original Prusa MMU 2.0 Control";
|
||||
break;
|
||||
case USB_PID_CW1_BOOT:
|
||||
return "Prusa CurWa";
|
||||
return "Original Prusa CW1";
|
||||
break;
|
||||
|
||||
default: throw std::runtime_error((boost::format("Invalid avr109 device USB PID: %1%") % usb_pid.boot).str());
|
||||
|
|
|
@ -63,11 +63,6 @@ static const float GROUND_Z = -0.02f;
|
|||
static const float GIZMO_RESET_BUTTON_HEIGHT = 22.0f;
|
||||
static const float GIZMO_RESET_BUTTON_WIDTH = 70.f;
|
||||
|
||||
static const float UNIT_MATRIX[] = { 1.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f };
|
||||
|
||||
static const float DEFAULT_BG_DARK_COLOR[3] = { 0.478f, 0.478f, 0.478f };
|
||||
static const float DEFAULT_BG_LIGHT_COLOR[3] = { 0.753f, 0.753f, 0.753f };
|
||||
static const float ERROR_BG_DARK_COLOR[3] = { 0.478f, 0.192f, 0.039f };
|
||||
|
@ -452,8 +447,7 @@ void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas
|
|||
m_shader.set_uniform("z_texture_row_to_normalized", 1.0f / (float)m_layers_texture.height);
|
||||
m_shader.set_uniform("z_cursor", m_object_max_z * this->get_cursor_z_relative(canvas));
|
||||
m_shader.set_uniform("z_cursor_band_width", band_width);
|
||||
// The shader requires the original model coordinates when rendering to the texture, so we pass it the unit matrix
|
||||
m_shader.set_uniform("volume_world_matrix", UNIT_MATRIX);
|
||||
m_shader.set_uniform("object_max_z", m_object_max_z);
|
||||
|
||||
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id));
|
||||
|
@ -466,10 +460,10 @@ void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas
|
|||
|
||||
::glBegin(GL_QUADS);
|
||||
::glNormal3f(0.0f, 0.0f, 1.0f);
|
||||
::glVertex3f(l, b, 0.0f);
|
||||
::glVertex3f(r, b, 0.0f);
|
||||
::glVertex3f(r, t, m_object_max_z);
|
||||
::glVertex3f(l, t, m_object_max_z);
|
||||
::glTexCoord2f(0.0f, 0.0f); ::glVertex2f(l, b);
|
||||
::glTexCoord2f(1.0f, 0.0f); ::glVertex2f(r, b);
|
||||
::glTexCoord2f(1.0f, 1.0f); ::glVertex2f(r, t);
|
||||
::glTexCoord2f(0.0f, 1.0f); ::glVertex2f(l, t);
|
||||
glsafe(::glEnd());
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
|
||||
|
@ -522,6 +516,7 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G
|
|||
GLint z_cursor_id = ::glGetUniformLocation(shader_id, "z_cursor");
|
||||
GLint z_cursor_band_width_id = ::glGetUniformLocation(shader_id, "z_cursor_band_width");
|
||||
GLint world_matrix_id = ::glGetUniformLocation(shader_id, "volume_world_matrix");
|
||||
GLint object_max_z_id = ::glGetUniformLocation(shader_id, "object_max_z");
|
||||
glcheck();
|
||||
|
||||
if (z_to_texture_row_id != -1 && z_texture_row_to_normalized_id != -1 && z_cursor_id != -1 && z_cursor_band_width_id != -1 && world_matrix_id != -1)
|
||||
|
@ -548,7 +543,10 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G
|
|||
// Render the object using the layer editing shader and texture.
|
||||
if (! glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier)
|
||||
continue;
|
||||
glsafe(::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)glvolume->world_matrix().cast<float>().data()));
|
||||
if (world_matrix_id != -1)
|
||||
glsafe(::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)glvolume->world_matrix().cast<float>().data()));
|
||||
if (object_max_z_id != -1)
|
||||
glsafe(::glUniform1f(object_max_z_id, GLfloat(0)));
|
||||
glvolume->render();
|
||||
}
|
||||
// Revert back to the previous shader.
|
||||
|
@ -1210,6 +1208,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent);
|
|||
wxDEFINE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent);
|
||||
|
||||
GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar)
|
||||
: m_canvas(canvas)
|
||||
|
@ -1252,6 +1251,8 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar
|
|||
m_timer.SetOwner(m_canvas);
|
||||
#if ENABLE_RETINA_GL
|
||||
m_retina_helper.reset(new RetinaHelper(canvas));
|
||||
// set default view_toolbar icons size equal to GLGizmosManager::Default_Icons_Size
|
||||
m_view_toolbar.set_icons_size(GLGizmosManager::Default_Icons_Size);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1577,7 +1578,13 @@ void GLCanvas3D::update_volumes_colors_by_extruder()
|
|||
|
||||
void GLCanvas3D::render()
|
||||
{
|
||||
wxCHECK_RET(!m_in_render, "GLCanvas3D::render() called recursively");
|
||||
if (m_in_render)
|
||||
{
|
||||
// if called recursively, return
|
||||
m_dirty = true;
|
||||
return;
|
||||
}
|
||||
|
||||
m_in_render = true;
|
||||
Slic3r::ScopeGuard in_render_guard([this]() { m_in_render = false; });
|
||||
(void)in_render_guard;
|
||||
|
@ -1715,6 +1722,16 @@ void GLCanvas3D::select_all()
|
|||
m_dirty = true;
|
||||
}
|
||||
|
||||
void GLCanvas3D::deselect_all()
|
||||
{
|
||||
m_selection.clear();
|
||||
m_selection.set_mode(Selection::Instance);
|
||||
wxGetApp().obj_manipul()->set_dirty();
|
||||
m_gizmos.reset_all_states();
|
||||
m_gizmos.update_data(*this);
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT));
|
||||
}
|
||||
|
||||
void GLCanvas3D::delete_selected()
|
||||
{
|
||||
m_selection.erase();
|
||||
|
@ -2010,7 +2027,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||
if (it->new_geometry())
|
||||
instances[istep].emplace_back(std::pair<size_t, size_t>(instance_idx, print_instance_idx));
|
||||
else
|
||||
// Recycling an old GLVolume. Update the Object/Instance indices into the current Model.
|
||||
// Recycling an old GLVolume. Update the Object/Instance indices into the current Model.
|
||||
m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx);
|
||||
}
|
||||
}
|
||||
|
@ -2282,6 +2299,9 @@ void GLCanvas3D::on_size(wxSizeEvent& evt)
|
|||
|
||||
void GLCanvas3D::on_idle(wxIdleEvent& evt)
|
||||
{
|
||||
if (!m_initialized)
|
||||
return;
|
||||
|
||||
m_dirty |= m_toolbar.update_items_state();
|
||||
m_dirty |= m_view_toolbar.update_items_state();
|
||||
|
||||
|
@ -2359,7 +2379,8 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
|||
post_event(SimpleEvent(EVT_GLTOOLBAR_DELETE));
|
||||
break;
|
||||
|
||||
case '0': { select_view("iso"); break; }
|
||||
case WXK_ESCAPE: { deselect_all(); break; }
|
||||
case '0': { select_view("iso"); break; }
|
||||
case '1': { select_view("top"); break; }
|
||||
case '2': { select_view("bottom"); break; }
|
||||
case '3': { select_view("front"); break; }
|
||||
|
@ -2379,11 +2400,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
|||
case 'o': { set_camera_zoom(-1.0f); break; }
|
||||
case 'Z':
|
||||
case 'z': { m_selection.is_empty() ? zoom_to_volumes() : zoom_to_selection(); break; }
|
||||
default:
|
||||
{
|
||||
evt.Skip();
|
||||
break;
|
||||
}
|
||||
default: { evt.Skip(); break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2451,6 +2468,20 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
|
|||
}
|
||||
else if (keyCode == WXK_CONTROL)
|
||||
m_dirty = true;
|
||||
// DoubleSlider navigation in Preview
|
||||
else if (keyCode == WXK_LEFT ||
|
||||
keyCode == WXK_RIGHT ||
|
||||
keyCode == WXK_UP ||
|
||||
keyCode == WXK_DOWN ||
|
||||
keyCode == '+' ||
|
||||
keyCode == WXK_NUMPAD_ADD ||
|
||||
keyCode == '-' ||
|
||||
keyCode == 390 ||
|
||||
keyCode == WXK_DELETE ||
|
||||
keyCode == WXK_BACK )
|
||||
{
|
||||
post_event(wxKeyEvent(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, evt));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2900,14 +2931,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
{
|
||||
// deselect and propagate event through callback
|
||||
if (!evt.ShiftDown() && m_picking_enabled)
|
||||
{
|
||||
m_selection.clear();
|
||||
m_selection.set_mode(Selection::Instance);
|
||||
wxGetApp().obj_manipul()->set_dirty();
|
||||
m_gizmos.reset_all_states();
|
||||
m_gizmos.update_data(*this);
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT));
|
||||
}
|
||||
deselect_all();
|
||||
}
|
||||
else if (evt.LeftUp() && m_mouse.dragging)
|
||||
// Flips X mouse deltas if bed is upside down
|
||||
|
@ -3415,9 +3439,6 @@ bool GLCanvas3D::_init_toolbar()
|
|||
return true;
|
||||
}
|
||||
|
||||
#if ENABLE_SVG_ICONS
|
||||
m_toolbar.set_icons_size(40);
|
||||
#endif // ENABLE_SVG_ICONS
|
||||
// m_toolbar.set_layout_type(GLToolbar::Layout::Vertical);
|
||||
m_toolbar.set_layout_type(GLToolbar::Layout::Horizontal);
|
||||
m_toolbar.set_layout_orientation(GLToolbar::Layout::Top);
|
||||
|
@ -4022,8 +4043,7 @@ void GLCanvas3D::_render_selection() const
|
|||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
void GLCanvas3D::_render_selection_center() const
|
||||
{
|
||||
if (!m_gizmos.is_running())
|
||||
m_selection.render_center();
|
||||
m_selection.render_center(m_gizmos.is_dragging());
|
||||
}
|
||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
|
||||
|
@ -4095,10 +4115,14 @@ void GLCanvas3D::_render_current_gizmo() const
|
|||
void GLCanvas3D::_render_gizmos_overlay() const
|
||||
{
|
||||
#if ENABLE_RETINA_GL
|
||||
m_gizmos.set_overlay_scale(m_retina_helper->get_scale_factor());
|
||||
// m_gizmos.set_overlay_scale(m_retina_helper->get_scale_factor());
|
||||
const float scale = m_retina_helper->get_scale_factor()*wxGetApp().toolbar_icon_scale();
|
||||
m_gizmos.set_overlay_scale(scale); //! #ys_FIXME_experiment
|
||||
#else
|
||||
// m_gizmos.set_overlay_scale(m_canvas->GetContentScaleFactor());
|
||||
m_gizmos.set_overlay_scale(wxGetApp().em_unit()*0.1f);//! #ys_FIXME_experiment
|
||||
// m_gizmos.set_overlay_scale(wxGetApp().em_unit()*0.1f);
|
||||
const float size = int(GLGizmosManager::Default_Icons_Size*wxGetApp().toolbar_icon_scale());
|
||||
m_gizmos.set_overlay_icon_size(size); //! #ys_FIXME_experiment
|
||||
#endif /* __WXMSW__ */
|
||||
|
||||
m_gizmos.render_overlay(*this, m_selection);
|
||||
|
@ -4108,10 +4132,14 @@ void GLCanvas3D::_render_toolbar() const
|
|||
{
|
||||
#if ENABLE_SVG_ICONS
|
||||
#if ENABLE_RETINA_GL
|
||||
m_toolbar.set_scale(m_retina_helper->get_scale_factor());
|
||||
// m_toolbar.set_scale(m_retina_helper->get_scale_factor());
|
||||
const float scale = m_retina_helper->get_scale_factor() * wxGetApp().toolbar_icon_scale(true);
|
||||
m_toolbar.set_scale(scale); //! #ys_FIXME_experiment
|
||||
#else
|
||||
// m_toolbar.set_scale(m_canvas->GetContentScaleFactor());
|
||||
m_toolbar.set_scale(wxGetApp().em_unit()*0.1f);//! #ys_FIXME_experiment
|
||||
// m_toolbar.set_scale(wxGetApp().em_unit()*0.1f);
|
||||
const float size = int(GLToolbar::Default_Icons_Size * wxGetApp().toolbar_icon_scale(true));
|
||||
m_toolbar.set_icons_size(size); //! #ys_FIXME_experiment
|
||||
#endif // ENABLE_RETINA_GL
|
||||
|
||||
Size cnv_size = get_canvas_size();
|
||||
|
@ -4172,10 +4200,14 @@ void GLCanvas3D::_render_view_toolbar() const
|
|||
{
|
||||
#if ENABLE_SVG_ICONS
|
||||
#if ENABLE_RETINA_GL
|
||||
m_view_toolbar.set_scale(m_retina_helper->get_scale_factor());
|
||||
// m_view_toolbar.set_scale(m_retina_helper->get_scale_factor());
|
||||
const float scale = m_retina_helper->get_scale_factor() * wxGetApp().toolbar_icon_scale();
|
||||
m_view_toolbar.set_scale(scale); //! #ys_FIXME_experiment
|
||||
#else
|
||||
// m_view_toolbar.set_scale(m_canvas->GetContentScaleFactor());
|
||||
m_view_toolbar.set_scale(wxGetApp().em_unit()*0.1f); //! #ys_FIXME_experiment
|
||||
// m_view_toolbar.set_scale(wxGetApp().em_unit()*0.1f);
|
||||
const float size = int(GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale());
|
||||
m_view_toolbar.set_icons_size(size); //! #ys_FIXME_experiment
|
||||
#endif // ENABLE_RETINA_GL
|
||||
|
||||
Size cnv_size = get_canvas_size();
|
||||
|
@ -5324,7 +5356,7 @@ bool GLCanvas3D::_travel_paths_by_tool(const GCodePreviewData& preview_data, con
|
|||
// creates a new volume for each tool
|
||||
for (Tool& tool : tools)
|
||||
{
|
||||
// tool.value could be invalid (as it was with https://github.com/prusa3d/Slic3r/issues/2179), we better check
|
||||
// tool.value could be invalid (as it was with https://github.com/prusa3d/PrusaSlicer/issues/2179), we better check
|
||||
if (tool.value >= tool_colors.size())
|
||||
continue;
|
||||
|
||||
|
@ -5485,6 +5517,7 @@ void GLCanvas3D::_load_sla_shells()
|
|||
v.set_instance_offset(unscale(instance.shift(0), instance.shift(1), 0));
|
||||
v.set_instance_rotation(Vec3d(0.0, 0.0, (double)instance.rotation));
|
||||
v.set_instance_mirror(X, object.is_left_handed() ? -1. : 1.);
|
||||
v.set_convex_hull(mesh.convex_hull_3d());
|
||||
};
|
||||
|
||||
// adds objects' volumes
|
||||
|
@ -5499,7 +5532,7 @@ void GLCanvas3D::_load_sla_shells()
|
|||
if (obj->is_step_done(slaposSupportTree) && obj->has_mesh(slaposSupportTree))
|
||||
add_volume(*obj, -int(slaposSupportTree), instance, obj->support_mesh(), GLVolume::SLA_SUPPORT_COLOR, true);
|
||||
if (obj->is_step_done(slaposBasePool) && obj->has_mesh(slaposBasePool))
|
||||
add_volume(*obj, -int(slaposBasePool), instance, obj->pad_mesh(), GLVolume::SLA_PAD_COLOR, true);
|
||||
add_volume(*obj, -int(slaposBasePool), instance, obj->pad_mesh(), GLVolume::SLA_PAD_COLOR, false);
|
||||
}
|
||||
double shift_z = obj->get_current_elevation();
|
||||
for (unsigned int i = initial_volumes_count; i < m_volumes.volumes.size(); ++ i) {
|
||||
|
@ -5622,7 +5655,7 @@ void GLCanvas3D::_update_sla_shells_outside_state()
|
|||
|
||||
for (GLVolume* volume : m_volumes.volumes)
|
||||
{
|
||||
volume->is_outside = ((print_volume.radius() > 0.0) && volume->is_sla_support()) ? !print_volume.contains(volume->transformed_convex_hull_bounding_box()) : false;
|
||||
volume->is_outside = ((print_volume.radius() > 0.0) && volume->shader_outside_printer_detection_enabled) ? !print_volume.contains(volume->transformed_convex_hull_bounding_box()) : false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -124,6 +124,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent);
|
|||
wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent);
|
||||
|
||||
class GLCanvas3D
|
||||
{
|
||||
|
@ -555,6 +556,7 @@ public:
|
|||
void render();
|
||||
|
||||
void select_all();
|
||||
void deselect_all();
|
||||
void delete_selected();
|
||||
void ensure_on_bed(unsigned int object_idx);
|
||||
|
||||
|
|
|
@ -123,7 +123,7 @@ BackgroundTexture::Metadata::Metadata()
|
|||
}
|
||||
|
||||
#if ENABLE_SVG_ICONS
|
||||
const float GLToolbar::Default_Icons_Size = 64.0f;
|
||||
const float GLToolbar::Default_Icons_Size = 40.0f;
|
||||
#endif // ENABLE_SVG_ICONS
|
||||
|
||||
GLToolbar::Layout::Layout()
|
||||
|
|
|
@ -58,13 +58,14 @@ namespace GUI {
|
|||
wxString file_wildcards(FileType file_type, const std::string &custom_extension)
|
||||
{
|
||||
static const std::string defaults[FT_SIZE] = {
|
||||
/* FT_STL */ "STL files (*.stl)|*.stl;*.STL",
|
||||
/* FT_OBJ */ "OBJ files (*.obj)|*.obj;*.OBJ",
|
||||
/* FT_AMF */ "AMF files (*.amf)|*.zip.amf;*.amf;*.AMF;*.xml;*.XML",
|
||||
/* FT_3MF */ "3MF files (*.3mf)|*.3mf;*.3MF;",
|
||||
/* FT_PRUSA */ "Prusa Control files (*.prusa)|*.prusa;*.PRUSA",
|
||||
/* FT_GCODE */ "G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC",
|
||||
/* FT_MODEL */ "Known files (*.stl, *.obj, *.amf, *.xml, *.3mf, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.3mf;*.3MF;*.prusa;*.PRUSA",
|
||||
/* FT_STL */ "STL files (*.stl)|*.stl;*.STL",
|
||||
/* FT_OBJ */ "OBJ files (*.obj)|*.obj;*.OBJ",
|
||||
/* FT_AMF */ "AMF files (*.amf)|*.zip.amf;*.amf;*.AMF;*.xml;*.XML",
|
||||
/* FT_3MF */ "3MF files (*.3mf)|*.3mf;*.3MF;",
|
||||
/* FT_PRUSA */ "Prusa Control files (*.prusa)|*.prusa;*.PRUSA",
|
||||
/* FT_GCODE */ "G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC",
|
||||
/* FT_MODEL */ "Known files (*.stl, *.obj, *.amf, *.xml, *.3mf, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.3mf;*.3MF;*.prusa;*.PRUSA",
|
||||
/* FT_PROJECT */ "Project files (*.3mf, *.amf)|*.3mf;*.3MF;*.amf;*.AMF",
|
||||
|
||||
/* FT_INI */ "INI files (*.ini)|*.ini;*.INI",
|
||||
/* FT_SVG */ "SVG files (*.svg)|*.svg;*.SVG",
|
||||
|
@ -184,8 +185,11 @@ bool GUI_App::on_init_inner()
|
|||
|
||||
app_conf_exists = app_config->exists();
|
||||
// load settings
|
||||
if (app_conf_exists)
|
||||
app_conf_exists = app_config->exists();
|
||||
if (app_conf_exists) {
|
||||
app_config->load();
|
||||
}
|
||||
|
||||
app_config->set("version", SLIC3R_VERSION);
|
||||
app_config->save();
|
||||
|
||||
|
@ -248,9 +252,13 @@ bool GUI_App::on_init_inner()
|
|||
if (once) {
|
||||
once = false;
|
||||
|
||||
PresetUpdater::UpdateResult updater_result;
|
||||
try {
|
||||
if (!preset_updater->config_update()) {
|
||||
updater_result = preset_updater->config_update();
|
||||
if (updater_result == PresetUpdater::R_INCOMPAT_EXIT) {
|
||||
mainframe->Close();
|
||||
} else if (updater_result == PresetUpdater::R_INCOMPAT_CONFIGURED) {
|
||||
app_conf_exists = true;
|
||||
}
|
||||
} catch (const std::exception &ex) {
|
||||
show_error(nullptr, from_u8(ex.what()));
|
||||
|
@ -382,6 +390,27 @@ void GUI_App::set_label_clr_sys(const wxColour& clr) {
|
|||
app_config->save();
|
||||
}
|
||||
|
||||
float GUI_App::toolbar_icon_scale(const bool is_limited/* = false*/) const
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
const float icon_sc = 1.0f; // for Retina display will be used its own scale
|
||||
#else
|
||||
const float icon_sc = m_em_unit*0.1f;
|
||||
#endif // __APPLE__
|
||||
|
||||
const std::string& use_val = app_config->get("use_custom_toolbar_size");
|
||||
const std::string& val = app_config->get("custom_toolbar_size");
|
||||
|
||||
if (val.empty() || use_val.empty() || use_val == "0")
|
||||
return icon_sc;
|
||||
|
||||
int int_val = atoi(val.c_str());
|
||||
if (is_limited && int_val < 50)
|
||||
int_val = 50;
|
||||
|
||||
return 0.01f * int_val * icon_sc;
|
||||
}
|
||||
|
||||
void GUI_App::recreate_GUI()
|
||||
{
|
||||
// Weird things happen as the Paint messages are floating around the windows being destructed.
|
||||
|
@ -441,14 +470,12 @@ void GUI_App::system_info()
|
|||
{
|
||||
SysInfoDialog dlg;
|
||||
dlg.ShowModal();
|
||||
dlg.Destroy();
|
||||
}
|
||||
|
||||
void GUI_App::keyboard_shortcuts()
|
||||
{
|
||||
KBShortcutsDialog dlg;
|
||||
dlg.ShowModal();
|
||||
dlg.Destroy();
|
||||
}
|
||||
|
||||
// static method accepting a wxWindow object as first parameter
|
||||
|
@ -501,9 +528,9 @@ void GUI_App::load_project(wxWindow *parent, wxString& input_file)
|
|||
{
|
||||
input_file.Clear();
|
||||
wxFileDialog dialog(parent ? parent : GetTopWindow(),
|
||||
_(L("Choose one file (3MF):")),
|
||||
_(L("Choose one file (3MF/AMF):")),
|
||||
app_config->get_last_dir(), "",
|
||||
file_wildcards(FT_3MF), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
file_wildcards(FT_PROJECT), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
|
||||
if (dialog.ShowModal() == wxID_OK)
|
||||
input_file = dialog.GetPath();
|
||||
|
@ -701,7 +728,7 @@ void GUI_App::update_mode()
|
|||
void GUI_App::add_config_menu(wxMenuBar *menu)
|
||||
{
|
||||
auto local_menu = new wxMenu();
|
||||
wxWindowID config_id_base = wxWindow::NewControlId((int)ConfigMenuCnt);
|
||||
wxWindowID config_id_base = wxWindow::NewControlId(int(ConfigMenuCnt));
|
||||
|
||||
const auto config_wizard_name = _(ConfigWizard::name(true).wx_str());
|
||||
const auto config_wizard_tooltip = wxString::Format(_(L("Run %s")), config_wizard_name);
|
||||
|
@ -723,9 +750,9 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
|
|||
mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeSimple, _(L("Simple")), _(L("Simple View Mode")));
|
||||
mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeAdvanced, _(L("Advanced")), _(L("Advanced View Mode")));
|
||||
mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeExpert, _(L("Expert")), _(L("Expert View Mode")));
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Check(get_mode() == comSimple); }, config_id_base + ConfigMenuModeSimple);
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Check(get_mode() == comAdvanced); }, config_id_base + ConfigMenuModeAdvanced);
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Check(get_mode() == comExpert); }, config_id_base + ConfigMenuModeExpert);
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { if(get_mode() == comSimple) evt.Check(true); }, config_id_base + ConfigMenuModeSimple);
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { if(get_mode() == comAdvanced) evt.Check(true); }, config_id_base + ConfigMenuModeAdvanced);
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { if(get_mode() == comExpert) evt.Check(true); }, config_id_base + ConfigMenuModeExpert);
|
||||
|
||||
local_menu->AppendSubMenu(mode_menu, _(L("Mode")), wxString::Format(_(L("%s View Mode")), SLIC3R_APP_NAME));
|
||||
local_menu->AppendSeparator();
|
||||
|
@ -804,10 +831,14 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
|
|||
break;
|
||||
}
|
||||
});
|
||||
mode_menu->Bind(wxEVT_MENU, [this, config_id_base](wxEvent& event) {
|
||||
int id_mode = event.GetId() - config_id_base;
|
||||
save_mode(id_mode - ConfigMenuModeSimple);
|
||||
});
|
||||
|
||||
using std::placeholders::_1;
|
||||
|
||||
auto modfn = [this](int mode, wxCommandEvent&) { if(get_mode() != mode) save_mode(mode); };
|
||||
mode_menu->Bind(wxEVT_MENU, std::bind(modfn, comSimple, _1), config_id_base + ConfigMenuModeSimple);
|
||||
mode_menu->Bind(wxEVT_MENU, std::bind(modfn, comAdvanced, _1), config_id_base + ConfigMenuModeAdvanced);
|
||||
mode_menu->Bind(wxEVT_MENU, std::bind(modfn, comExpert, _1), config_id_base + ConfigMenuModeExpert);
|
||||
|
||||
menu->Append(local_menu, _(L("&Configuration")));
|
||||
}
|
||||
|
||||
|
@ -908,6 +939,7 @@ wxNotebook* GUI_App::tab_panel() const
|
|||
return mainframe->m_tabpanel;
|
||||
}
|
||||
|
||||
// extruders count from selected printer preset
|
||||
int GUI_App::extruders_cnt() const
|
||||
{
|
||||
const Preset& preset = preset_bundle->printers.get_selected_preset();
|
||||
|
@ -915,6 +947,14 @@ int GUI_App::extruders_cnt() const
|
|||
preset.config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
|
||||
}
|
||||
|
||||
// extruders count from edited printer preset
|
||||
int GUI_App::extruders_edited_cnt() const
|
||||
{
|
||||
const Preset& preset = preset_bundle->printers.get_edited_preset();
|
||||
return preset.printer_technology() == ptSLA ? 1 :
|
||||
preset.config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
|
||||
}
|
||||
|
||||
void GUI_App::open_web_page_localized(const std::string &http_address)
|
||||
{
|
||||
wxLaunchDefaultBrowser(http_address + "&lng=" + this->current_language_code());
|
||||
|
@ -999,7 +1039,7 @@ void GUI_App::associate_3mf_files()
|
|||
{
|
||||
// see as reference: https://stackoverflow.com/questions/20245262/c-program-needs-an-file-association
|
||||
|
||||
auto reg_set = [](HKEY hkeyHive, const wchar_t* pszVar, const wchar_t* pszValue)
|
||||
auto reg_set = [](HKEY hkeyHive, const wchar_t* pszVar, const wchar_t* pszValue)->bool
|
||||
{
|
||||
wchar_t szValueCurrent[1000];
|
||||
DWORD dwType;
|
||||
|
@ -1011,26 +1051,32 @@ void GUI_App::associate_3mf_files()
|
|||
|
||||
if ((iRC != ERROR_SUCCESS) && !bDidntExist)
|
||||
// an error occurred
|
||||
return;
|
||||
return false;
|
||||
|
||||
if (!bDidntExist)
|
||||
{
|
||||
if (dwType != REG_SZ)
|
||||
// invalid type
|
||||
return;
|
||||
return false;
|
||||
|
||||
if (::wcscmp(szValueCurrent, pszValue) == 0)
|
||||
// value already set
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD dwDisposition;
|
||||
HKEY hkey;
|
||||
iRC = ::RegCreateKeyExW(hkeyHive, pszVar, 0, 0, 0, KEY_ALL_ACCESS, nullptr, &hkey, &dwDisposition);
|
||||
bool ret = false;
|
||||
if (iRC == ERROR_SUCCESS)
|
||||
{
|
||||
iRC = ::RegSetValueExW(hkey, L"", 0, REG_SZ, (BYTE*)pszValue, (::wcslen(pszValue) + 1) * sizeof(wchar_t));
|
||||
if (iRC == ERROR_SUCCESS)
|
||||
ret = true;
|
||||
}
|
||||
|
||||
RegCloseKey(hkey);
|
||||
return ret;
|
||||
};
|
||||
|
||||
wchar_t app_path[MAX_PATH];
|
||||
|
@ -1045,11 +1091,14 @@ void GUI_App::associate_3mf_files()
|
|||
std::wstring reg_prog_id = reg_base + L"\\" + prog_id;
|
||||
std::wstring reg_prog_id_command = reg_prog_id + L"\\Shell\\Open\\Command";
|
||||
|
||||
reg_set(HKEY_CURRENT_USER, reg_extension.c_str(), prog_id.c_str());
|
||||
reg_set(HKEY_CURRENT_USER, reg_prog_id.c_str(), prog_desc.c_str());
|
||||
reg_set(HKEY_CURRENT_USER, reg_prog_id_command.c_str(), prog_command.c_str());
|
||||
bool is_new = false;
|
||||
is_new |= reg_set(HKEY_CURRENT_USER, reg_extension.c_str(), prog_id.c_str());
|
||||
is_new |= reg_set(HKEY_CURRENT_USER, reg_prog_id.c_str(), prog_desc.c_str());
|
||||
is_new |= reg_set(HKEY_CURRENT_USER, reg_prog_id_command.c_str(), prog_command.c_str());
|
||||
|
||||
::SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);
|
||||
if (is_new)
|
||||
// notify Windows only when any of the values gets changed
|
||||
::SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);
|
||||
}
|
||||
#endif // __WXMSW__
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ enum FileType
|
|||
FT_PRUSA,
|
||||
FT_GCODE,
|
||||
FT_MODEL,
|
||||
FT_PROJECT,
|
||||
|
||||
FT_INI,
|
||||
FT_SVG,
|
||||
|
@ -114,6 +115,7 @@ public:
|
|||
const wxFont& normal_font() { return m_normal_font; }
|
||||
size_t em_unit() const { return m_em_unit; }
|
||||
void set_em_unit(const size_t em_unit) { m_em_unit = em_unit; }
|
||||
float toolbar_icon_scale(const bool is_limited = false) const;
|
||||
|
||||
void recreate_GUI();
|
||||
void system_info();
|
||||
|
@ -164,6 +166,7 @@ public:
|
|||
|
||||
wxNotebook* tab_panel() const ;
|
||||
int extruders_cnt() const;
|
||||
int extruders_edited_cnt() const;
|
||||
|
||||
std::vector<Tab *> tabs_list;
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include "libslic3r/libslic3r.h"
|
||||
#include "GUI_ObjectList.hpp"
|
||||
#include "GUI_ObjectManipulation.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
|
@ -129,7 +130,31 @@ ObjectList::ObjectList(wxWindow* parent) :
|
|||
#endif //__WXMSW__
|
||||
});
|
||||
|
||||
// Bind(wxEVT_CHAR, [this](wxKeyEvent& event) { key_event(event); }); // doesn't work on OSX
|
||||
#ifdef __WXOSX__
|
||||
// Key events are not correctly processed by the wxDataViewCtrl on OSX.
|
||||
// Our patched wxWidgets process the keyboard accelerators.
|
||||
// On the other hand, using accelerators will break in-place editing on Windows & Linux/GTK (there is no in-place editing working on OSX for wxDataViewCtrl for now).
|
||||
// Bind(wxEVT_KEY_DOWN, &ObjectList::OnChar, this);
|
||||
{
|
||||
// Accelerators
|
||||
wxAcceleratorEntry entries[6];
|
||||
entries[0].Set(wxACCEL_CTRL, (int) 'C', wxID_COPY);
|
||||
entries[1].Set(wxACCEL_CTRL, (int) 'X', wxID_CUT);
|
||||
entries[2].Set(wxACCEL_CTRL, (int) 'V', wxID_PASTE);
|
||||
entries[3].Set(wxACCEL_CTRL, (int) 'A', wxID_SELECTALL);
|
||||
entries[4].Set(wxACCEL_NORMAL, WXK_DELETE, wxID_DELETE);
|
||||
entries[5].Set(wxACCEL_NORMAL, WXK_BACK, wxID_DELETE);
|
||||
wxAcceleratorTable accel(6, entries);
|
||||
SetAcceleratorTable(accel);
|
||||
|
||||
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_COPY)); }, wxID_COPY);
|
||||
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_PASTE)); }, wxID_PASTE);
|
||||
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->select_item_all_children(); }, wxID_SELECTALL);
|
||||
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->remove(); }, wxID_DELETE);
|
||||
}
|
||||
#else __WXOSX__
|
||||
Bind(wxEVT_CHAR, [this](wxKeyEvent& event) { key_event(event); }); // doesn't work on OSX
|
||||
#endif
|
||||
|
||||
#ifdef __WXMSW__
|
||||
GetMainWindow()->Bind(wxEVT_MOTION, [this](wxMouseEvent& event) {
|
||||
|
@ -150,28 +175,6 @@ ObjectList::ObjectList(wxWindow* parent) :
|
|||
|
||||
Bind(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, [this](wxCommandEvent& e) { last_volume_is_deleted(e.GetInt()); });
|
||||
|
||||
#ifdef __WXOSX__
|
||||
// Bind(wxEVT_KEY_DOWN, &ObjectList::OnChar, this);
|
||||
#endif //__WXOSX__
|
||||
|
||||
{
|
||||
// Accelerators
|
||||
wxAcceleratorEntry entries[6];
|
||||
entries[0].Set(wxACCEL_CTRL, (int) 'C', wxID_COPY);
|
||||
entries[1].Set(wxACCEL_CTRL, (int) 'X', wxID_CUT);
|
||||
entries[2].Set(wxACCEL_CTRL, (int) 'V', wxID_PASTE);
|
||||
entries[3].Set(wxACCEL_CTRL, (int) 'A', wxID_SELECTALL);
|
||||
entries[4].Set(wxACCEL_NORMAL, WXK_DELETE, wxID_DELETE);
|
||||
entries[5].Set(wxACCEL_NORMAL, WXK_BACK, wxID_DELETE);
|
||||
wxAcceleratorTable accel(6, entries);
|
||||
SetAcceleratorTable(accel);
|
||||
|
||||
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_COPY)); }, wxID_COPY);
|
||||
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_PASTE)); }, wxID_PASTE);
|
||||
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->select_item_all_children(); }, wxID_SELECTALL);
|
||||
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->remove(); }, wxID_DELETE);
|
||||
}
|
||||
|
||||
Bind(wxEVT_SIZE, ([this](wxSizeEvent &e) { this->EnsureVisible(this->GetCurrentItem()); e.Skip(); }));
|
||||
}
|
||||
|
||||
|
@ -258,7 +261,7 @@ wxString ObjectList::get_mesh_errors_list(const int obj_idx, const int vol_idx /
|
|||
|
||||
const stl_stats& stats = vol_idx == -1 ?
|
||||
(*m_objects)[obj_idx]->get_object_stl_stats() :
|
||||
(*m_objects)[obj_idx]->volumes[vol_idx]->mesh.stl.stats;
|
||||
(*m_objects)[obj_idx]->volumes[vol_idx]->mesh().stl.stats;
|
||||
|
||||
std::map<std::string, int> error_msg = {
|
||||
{ L("degenerate facets"), stats.degenerate_facets },
|
||||
|
@ -295,13 +298,18 @@ void ObjectList::set_tooltip_for_item(const wxPoint& pt)
|
|||
wxDataViewItem item;
|
||||
wxDataViewColumn* col;
|
||||
HitTest(pt, item, col);
|
||||
if (!item) return;
|
||||
|
||||
/* GetMainWindow() return window, associated with wxDataViewCtrl.
|
||||
* And for this window we should to set tooltips.
|
||||
* Just this->SetToolTip(tooltip) => has no effect.
|
||||
*/
|
||||
|
||||
if (!item)
|
||||
{
|
||||
GetMainWindow()->SetToolTip(""); // hide tooltip
|
||||
return;
|
||||
}
|
||||
|
||||
if (col->GetTitle() == " " && GetSelectedItemsCount()<2)
|
||||
GetMainWindow()->SetToolTip(_(L("Right button click the icon to change the object settings")));
|
||||
else if (col->GetTitle() == _("Name"))
|
||||
|
@ -347,8 +355,8 @@ DynamicPrintConfig& ObjectList::get_item_config(const wxDataViewItem& item) cons
|
|||
const int vol_idx = type & itVolume ? m_objects_model->GetVolumeIdByItem(item) : -1;
|
||||
|
||||
assert(obj_idx >= 0 || ((type & itVolume) && vol_idx >=0));
|
||||
return type & itObject|itInstance ? (*m_objects)[obj_idx]->config :
|
||||
(*m_objects)[obj_idx]->volumes[vol_idx]->config;
|
||||
return type & itVolume ?(*m_objects)[obj_idx]->volumes[vol_idx]->config :
|
||||
(*m_objects)[obj_idx]->config;
|
||||
}
|
||||
|
||||
wxDataViewColumn* ObjectList::create_objects_list_extruder_column(int extruders_count)
|
||||
|
@ -579,7 +587,7 @@ void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& vol
|
|||
|
||||
for (const ModelVolume* volume : volumes)
|
||||
{
|
||||
const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(object_item, volume->name, volume->type(),
|
||||
const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(object_item, wxString::FromUTF8(volume->name.c_str()), volume->type(),
|
||||
volume->get_mesh_errors_count()>0 ,
|
||||
volume->config.has("extruder") ? volume->config.option<ConfigOptionInt>("extruder")->value : 0);
|
||||
auto opt_keys = volume->config.keys();
|
||||
|
@ -623,6 +631,8 @@ void ObjectList::paste_objects_into_list(const std::vector<size_t>& object_idxs)
|
|||
#endif //no __WXOSX__ //__WXMSW__
|
||||
}
|
||||
|
||||
#ifdef __WXOSX__
|
||||
/*
|
||||
void ObjectList::OnChar(wxKeyEvent& event)
|
||||
{
|
||||
if (event.GetKeyCode() == WXK_BACK){
|
||||
|
@ -633,6 +643,8 @@ void ObjectList::OnChar(wxKeyEvent& event)
|
|||
|
||||
event.Skip();
|
||||
}
|
||||
*/
|
||||
#endif /* __WXOSX__ */
|
||||
|
||||
void ObjectList::OnContextMenu(wxDataViewEvent&)
|
||||
{
|
||||
|
@ -701,7 +713,7 @@ void ObjectList::show_context_menu()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
#ifndef __WXOSX__
|
||||
void ObjectList::key_event(wxKeyEvent& event)
|
||||
{
|
||||
if (event.GetKeyCode() == WXK_TAB)
|
||||
|
@ -722,6 +734,7 @@ void ObjectList::key_event(wxKeyEvent& event)
|
|||
else
|
||||
event.Skip();
|
||||
}
|
||||
#endif /* __WXOSX__ */
|
||||
|
||||
void ObjectList::OnBeginDrag(wxDataViewEvent &event)
|
||||
{
|
||||
|
@ -1271,6 +1284,12 @@ void ObjectList::append_menu_item_delete(wxMenu* menu)
|
|||
[this](wxCommandEvent&) { remove(); }, "", menu);
|
||||
}
|
||||
|
||||
void ObjectList::append_menu_item_scale_selection_to_fit_print_volume(wxMenu* menu)
|
||||
{
|
||||
append_menu_item(menu, wxID_ANY, _(L("Scale to print volume")), _(L("Scale the selected object to fit the print volume")),
|
||||
[this](wxCommandEvent&) { wxGetApp().plater()->scale_selection_to_fit_print_volume(); }, "", menu);
|
||||
}
|
||||
|
||||
void ObjectList::create_object_popupmenu(wxMenu *menu)
|
||||
{
|
||||
#ifdef __WXOSX__
|
||||
|
@ -1279,6 +1298,7 @@ void ObjectList::create_object_popupmenu(wxMenu *menu)
|
|||
|
||||
append_menu_item_export_stl(menu);
|
||||
append_menu_item_fix_through_netfabb(menu);
|
||||
append_menu_item_scale_selection_to_fit_print_volume(menu);
|
||||
|
||||
// Split object to parts
|
||||
m_menu_item_split = append_menu_item_split(menu);
|
||||
|
@ -1395,13 +1415,18 @@ void ObjectList::update_opt_keys(t_config_option_keys& opt_keys)
|
|||
|
||||
void ObjectList::load_subobject(ModelVolumeType type)
|
||||
{
|
||||
auto item = GetSelection();
|
||||
if (!item || m_objects_model->GetParent(item) != wxDataViewItem(0))
|
||||
wxDataViewItem item = GetSelection();
|
||||
// we can add volumes for Object or Instance
|
||||
if (!item || !(m_objects_model->GetItemType(item)&(itObject|itInstance)))
|
||||
return;
|
||||
int obj_idx = m_objects_model->GetIdByItem(item);
|
||||
const int obj_idx = m_objects_model->GetObjectIdByItem(item);
|
||||
|
||||
if (obj_idx < 0) return;
|
||||
|
||||
// Get object item, if Instance is selected
|
||||
if (m_objects_model->GetItemType(item)&itInstance)
|
||||
item = m_objects_model->GetItemById(obj_idx);
|
||||
|
||||
std::vector<std::pair<wxString, bool>> volumes_info;
|
||||
load_part((*m_objects)[obj_idx], volumes_info, type);
|
||||
|
||||
|
@ -1445,9 +1470,6 @@ void ObjectList::load_part( ModelObject* model_object,
|
|||
delta = model_object->origin_translation - object->origin_translation;
|
||||
}
|
||||
for (auto volume : object->volumes) {
|
||||
#if !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
volume->center_geometry();
|
||||
#endif // !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
volume->translate(delta);
|
||||
auto new_volume = model_object->add_volume(*volume);
|
||||
new_volume->set_type(type);
|
||||
|
@ -1570,20 +1592,12 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
|
|||
ModelVolume *new_volume = model_object.add_volume(std::move(mesh));
|
||||
new_volume->set_type(type);
|
||||
|
||||
#if !ENABLE_GENERIC_SUBPARTS_PLACEMENT
|
||||
new_volume->set_offset(Vec3d(0.0, 0.0, model_object.origin_translation(2) - mesh.stl.stats.min(2)));
|
||||
#endif // !ENABLE_GENERIC_SUBPARTS_PLACEMENT
|
||||
#if !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
new_volume->center_geometry();
|
||||
#endif // !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
|
||||
#if ENABLE_GENERIC_SUBPARTS_PLACEMENT
|
||||
if (instance_idx != -1)
|
||||
{
|
||||
// First (any) GLVolume of the selected instance. They all share the same instance matrix.
|
||||
const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
// Transform the new modifier to be aligned with the print bed.
|
||||
const BoundingBoxf3 mesh_bb = new_volume->mesh.bounding_box();
|
||||
const BoundingBoxf3 mesh_bb = new_volume->mesh().bounding_box();
|
||||
new_volume->set_transformation(volume_to_bed_transformation(v->get_instance_transformation(), mesh_bb));
|
||||
// Set the modifier position.
|
||||
auto offset = (type_name == "Slab") ?
|
||||
|
@ -1593,7 +1607,6 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
|
|||
Vec3d(instance_bb.max(0), instance_bb.min(1), instance_bb.min(2)) + 0.5 * mesh_bb.size() - v->get_instance_offset();
|
||||
new_volume->set_offset(v->get_instance_transformation().get_matrix(true).inverse() * offset);
|
||||
}
|
||||
#endif // ENABLE_GENERIC_SUBPARTS_PLACEMENT
|
||||
|
||||
new_volume->name = into_u8(name);
|
||||
// set a default extruder value, since user can't add it manually
|
||||
|
@ -2040,7 +2053,10 @@ void ObjectList::delete_from_model_and_list(const std::vector<ItemForDelete>& it
|
|||
|
||||
void ObjectList::delete_all_objects_from_list()
|
||||
{
|
||||
m_prevent_list_events = true;
|
||||
this->UnselectAll();
|
||||
m_objects_model->DeleteAll();
|
||||
m_prevent_list_events = false;
|
||||
part_selection_changed();
|
||||
}
|
||||
|
||||
|
@ -2139,9 +2155,11 @@ void ObjectList::update_selections()
|
|||
if (GetSelectedItemsCount() == 1 && m_objects_model->GetItemType(GetSelection()) == itSettings )
|
||||
{
|
||||
const auto item = GetSelection();
|
||||
if (selection.is_single_full_object() &&
|
||||
m_objects_model->GetIdByItem(m_objects_model->GetParent(item)) == selection.get_object_idx())
|
||||
return;
|
||||
if (selection.is_single_full_object()) {
|
||||
if ( m_objects_model->GetIdByItem(m_objects_model->GetParent(item)) == selection.get_object_idx())
|
||||
return;
|
||||
sels.Add(m_objects_model->GetItemById(selection.get_object_idx()));
|
||||
}
|
||||
if (selection.is_single_volume() || selection.is_any_modifier()) {
|
||||
const auto gl_vol = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
if (m_objects_model->GetVolumeIdByItem(m_objects_model->GetParent(item)) == gl_vol->volume_idx())
|
||||
|
@ -2813,8 +2831,10 @@ void ObjectList::OnEditingDone(wxDataViewEvent &event)
|
|||
const auto renderer = dynamic_cast<BitmapTextRenderer*>(GetColumn(0)->GetRenderer());
|
||||
|
||||
if (renderer->WasCanceled())
|
||||
show_error(this, _(L("The supplied name is not valid;")) + "\n" +
|
||||
_(L("the following characters are not allowed:")) + " <>:/\\|?*\"");
|
||||
wxTheApp->CallAfter([this]{
|
||||
show_error(this, _(L("The supplied name is not valid;")) + "\n" +
|
||||
_(L("the following characters are not allowed:")) + " <>:/\\|?*\"");
|
||||
});
|
||||
}
|
||||
|
||||
void ObjectList::show_multi_selection_menu()
|
||||
|
|
|
@ -188,7 +188,9 @@ public:
|
|||
|
||||
void selection_changed();
|
||||
void show_context_menu();
|
||||
#ifndef __WXOSX__
|
||||
void key_event(wxKeyEvent& event);
|
||||
#endif /* __WXOSX__ */
|
||||
|
||||
void get_settings_choice(const wxString& category_name);
|
||||
void get_freq_settings_choice(const wxString& bundle_name);
|
||||
|
@ -205,6 +207,7 @@ public:
|
|||
void append_menu_item_export_stl(wxMenu* menu) const ;
|
||||
void append_menu_item_change_extruder(wxMenu* menu) const;
|
||||
void append_menu_item_delete(wxMenu* menu);
|
||||
void append_menu_item_scale_selection_to_fit_print_volume(wxMenu* menu);
|
||||
void create_object_popupmenu(wxMenu *menu);
|
||||
void create_sla_object_popupmenu(wxMenu*menu);
|
||||
void create_part_popupmenu(wxMenu*menu);
|
||||
|
@ -298,7 +301,9 @@ public:
|
|||
void msw_rescale();
|
||||
|
||||
private:
|
||||
void OnChar(wxKeyEvent& event);
|
||||
#ifdef __WXOSX__
|
||||
// void OnChar(wxKeyEvent& event);
|
||||
#endif /* __WXOSX__ */
|
||||
void OnContextMenu(wxDataViewEvent &event);
|
||||
|
||||
void OnBeginDrag(wxDataViewEvent &event);
|
||||
|
|
|
@ -92,6 +92,7 @@ void msw_rescale_word_local_combo(wxBitmapComboBox* combo)
|
|||
combo->SetValue(selection);
|
||||
}
|
||||
|
||||
|
||||
ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||
OG_Settings(parent, true)
|
||||
#ifndef __APPLE__
|
||||
|
@ -162,16 +163,71 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
|||
|
||||
const int field_width = 5;
|
||||
|
||||
// Mirror button size:
|
||||
const int mirror_btn_width = 3;
|
||||
|
||||
// Legend for object modification
|
||||
line = Line{ "", "" };
|
||||
def.label = "";
|
||||
def.type = coString;
|
||||
def.width = field_width/*50*/;
|
||||
def.width = field_width - mirror_btn_width;//field_width/*50*/;
|
||||
|
||||
// Load bitmaps to be used for the mirroring buttons:
|
||||
m_mirror_bitmap_on = ScalableBitmap(parent, "mirroring_on.png");
|
||||
m_mirror_bitmap_off = ScalableBitmap(parent, "mirroring_off.png");
|
||||
m_mirror_bitmap_hidden = ScalableBitmap(parent, "mirroring_transparent.png");
|
||||
|
||||
for (const std::string axis : { "x", "y", "z" }) {
|
||||
const std::string label = boost::algorithm::to_upper_copy(axis);
|
||||
def.set_default_value(new ConfigOptionString{ " " + label });
|
||||
Option option = Option(def, axis + "_axis_legend");
|
||||
|
||||
unsigned int axis_idx = (axis[0] - 'x'); // 0, 1 or 2
|
||||
|
||||
// We will add a button to toggle mirroring to each axis:
|
||||
auto mirror_button = [=](wxWindow* parent) {
|
||||
wxSize btn_size(em_unit(parent) * mirror_btn_width, em_unit(parent) * mirror_btn_width);
|
||||
auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_off.png", wxEmptyString, btn_size, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW);
|
||||
btn->SetToolTip(wxString::Format(_(L("Toggle %s axis mirroring")), label));
|
||||
|
||||
m_mirror_buttons[axis_idx].first = btn;
|
||||
m_mirror_buttons[axis_idx].second = mbShown;
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(btn);
|
||||
|
||||
btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) {
|
||||
Axis axis = (Axis)(axis_idx + X);
|
||||
if (m_mirror_buttons[axis_idx].second == mbHidden)
|
||||
return;
|
||||
|
||||
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||
Selection& selection = canvas->get_selection();
|
||||
|
||||
if (selection.is_single_volume() || selection.is_single_modifier()) {
|
||||
GLVolume* volume = const_cast<GLVolume*>(selection.get_volume(*selection.get_volume_idxs().begin()));
|
||||
volume->set_volume_mirror(axis, -volume->get_volume_mirror(axis));
|
||||
}
|
||||
else if (selection.is_single_full_instance()) {
|
||||
for (unsigned int idx : selection.get_volume_idxs()){
|
||||
GLVolume* volume = const_cast<GLVolume*>(selection.get_volume(idx));
|
||||
volume->set_instance_mirror(axis, -volume->get_instance_mirror(axis));
|
||||
}
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
// Update mirroring at the GLVolumes.
|
||||
selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL);
|
||||
selection.synchronize_unselected_volumes();
|
||||
// Copy mirroring values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing.
|
||||
canvas->do_mirror();
|
||||
canvas->set_as_dirty();
|
||||
UpdateAndShow(true);
|
||||
});
|
||||
return sizer;
|
||||
};
|
||||
|
||||
option.side_widget = mirror_button;
|
||||
line.append_option(option);
|
||||
}
|
||||
line.near_label_widget = [this](wxWindow* parent) {
|
||||
|
@ -190,8 +246,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
|||
def.set_default_value(new ConfigOptionFloat(0.0));
|
||||
def.width = field_width/*50*/;
|
||||
|
||||
// Add "uniform scaling" button in front of "Scale" option
|
||||
if (option_name == "Scale") {
|
||||
// Add "uniform scaling" button in front of "Scale" option
|
||||
line.near_label_widget = [this](wxWindow* parent) {
|
||||
auto btn = new LockButton(parent, wxID_ANY);
|
||||
btn->Bind(wxEVT_BUTTON, [btn, this](wxCommandEvent &event){
|
||||
|
@ -201,8 +257,59 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
|||
m_lock_bnt = btn;
|
||||
return btn;
|
||||
};
|
||||
// Add reset scale button
|
||||
auto reset_scale_button = [=](wxWindow* parent) {
|
||||
auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo"));
|
||||
btn->SetToolTip(_(L("Reset scale")));
|
||||
m_reset_scale_button = btn;
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(btn, wxBU_EXACTFIT);
|
||||
btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) {
|
||||
change_scale_value(0, 100.);
|
||||
change_scale_value(1, 100.);
|
||||
change_scale_value(2, 100.);
|
||||
});
|
||||
return sizer;
|
||||
};
|
||||
line.append_widget(reset_scale_button);
|
||||
}
|
||||
else if (option_name == "Rotation") {
|
||||
// Add reset rotation button
|
||||
auto reset_rotation_button = [=](wxWindow* parent) {
|
||||
auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo"));
|
||||
btn->SetToolTip(_(L("Reset rotation")));
|
||||
m_reset_rotation_button = btn;
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(btn, wxBU_EXACTFIT);
|
||||
btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) {
|
||||
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||
Selection& selection = canvas->get_selection();
|
||||
|
||||
if (selection.is_single_volume() || selection.is_single_modifier()) {
|
||||
GLVolume* volume = const_cast<GLVolume*>(selection.get_volume(*selection.get_volume_idxs().begin()));
|
||||
volume->set_volume_rotation(Vec3d::Zero());
|
||||
}
|
||||
else if (selection.is_single_full_instance()) {
|
||||
for (unsigned int idx : selection.get_volume_idxs()){
|
||||
GLVolume* volume = const_cast<GLVolume*>(selection.get_volume(idx));
|
||||
volume->set_instance_rotation(Vec3d::Zero());
|
||||
}
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
// Update rotation at the GLVolumes.
|
||||
selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL);
|
||||
selection.synchronize_unselected_volumes();
|
||||
// Copy rotation values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing.
|
||||
canvas->do_rotate();
|
||||
|
||||
UpdateAndShow(true);
|
||||
});
|
||||
return sizer;
|
||||
};
|
||||
line.append_widget(reset_rotation_button);
|
||||
}
|
||||
// Add empty bmp (Its size have to be equal to PrusaLockButton) in front of "Size" option to label alignment
|
||||
else if (option_name == "Size") {
|
||||
line.near_label_widget = [this](wxWindow* parent) {
|
||||
|
@ -224,8 +331,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
|||
return line;
|
||||
};
|
||||
|
||||
|
||||
// Settings table
|
||||
m_og->sidetext_width = 3;
|
||||
m_og->append_line(add_og_to_object_settings(L("Position"), L("mm")), &m_move_Label);
|
||||
m_og->append_line(add_og_to_object_settings(L("Rotation"), "°"), &m_rotate_Label);
|
||||
m_og->append_line(add_og_to_object_settings(L("Scale"), "%"), &m_scale_Label);
|
||||
|
@ -239,6 +346,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
|||
ctrl->msw_rescale();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ObjectManipulation::Show(const bool show)
|
||||
{
|
||||
|
@ -408,9 +517,95 @@ void ObjectManipulation::update_if_dirty()
|
|||
else
|
||||
m_og->disable();
|
||||
|
||||
update_reset_buttons_visibility();
|
||||
update_mirror_buttons_visibility();
|
||||
|
||||
m_dirty = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ObjectManipulation::update_reset_buttons_visibility()
|
||||
{
|
||||
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||
if (!canvas)
|
||||
return;
|
||||
const Selection& selection = canvas->get_selection();
|
||||
|
||||
bool show_rotation = false;
|
||||
bool show_scale = false;
|
||||
|
||||
if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) {
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
Vec3d rotation;
|
||||
Vec3d scale;
|
||||
|
||||
if (selection.is_single_full_instance()) {
|
||||
rotation = volume->get_instance_rotation();
|
||||
scale = volume->get_instance_scaling_factor();
|
||||
}
|
||||
else {
|
||||
rotation = volume->get_volume_rotation();
|
||||
scale = volume->get_volume_scaling_factor();
|
||||
}
|
||||
show_rotation = !rotation.isApprox(Vec3d::Zero());
|
||||
show_scale = !scale.isApprox(Vec3d::Ones());
|
||||
}
|
||||
|
||||
wxGetApp().CallAfter([this, show_rotation, show_scale]{
|
||||
m_reset_rotation_button->Show(show_rotation);
|
||||
m_reset_scale_button->Show(show_scale);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ObjectManipulation::update_mirror_buttons_visibility()
|
||||
{
|
||||
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||
Selection& selection = canvas->get_selection();
|
||||
std::array<MirrorButtonState, 3> new_states = {mbHidden, mbHidden, mbHidden};
|
||||
|
||||
if (!m_world_coordinates) {
|
||||
if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) {
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
Vec3d mirror;
|
||||
|
||||
if (selection.is_single_full_instance())
|
||||
mirror = volume->get_instance_mirror();
|
||||
else
|
||||
mirror = volume->get_volume_mirror();
|
||||
|
||||
for (unsigned char i=0; i<3; ++i)
|
||||
new_states[i] = (mirror[i] < 0. ? mbActive : mbShown);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// the mirroring buttons should be hidden in world coordinates,
|
||||
// unless we make it actually mirror in world coords.
|
||||
}
|
||||
|
||||
// Hiding the buttons through Hide() always messed up the sizers. As a workaround, the button
|
||||
// is assigned a transparent bitmap. We must of course remember the actual state.
|
||||
wxGetApp().CallAfter([this, new_states]{
|
||||
for (int i=0; i<3; ++i) {
|
||||
if (new_states[i] != m_mirror_buttons[i].second) {
|
||||
const wxBitmap* bmp;
|
||||
switch (new_states[i]) {
|
||||
case mbHidden : bmp = &m_mirror_bitmap_hidden.bmp(); m_mirror_buttons[i].first->Enable(false); break;
|
||||
case mbShown : bmp = &m_mirror_bitmap_off.bmp(); m_mirror_buttons[i].first->Enable(true); break;
|
||||
case mbActive : bmp = &m_mirror_bitmap_on.bmp(); m_mirror_buttons[i].first->Enable(true); break;
|
||||
}
|
||||
m_mirror_buttons[i].first->SetBitmap(*bmp);
|
||||
m_mirror_buttons[i].second = new_states[i];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef __APPLE__
|
||||
void ObjectManipulation::emulate_kill_focus()
|
||||
{
|
||||
|
@ -493,7 +688,7 @@ void ObjectManipulation::change_rotation_value(int axis, double value)
|
|||
|
||||
m_cache.rotation = rotation;
|
||||
m_cache.rotation_rounded(axis) = DBL_MAX;
|
||||
this->UpdateAndShow(true);
|
||||
this->UpdateAndShow(true);
|
||||
}
|
||||
|
||||
void ObjectManipulation::change_scale_value(int axis, double value)
|
||||
|
@ -511,6 +706,7 @@ void ObjectManipulation::change_scale_value(int axis, double value)
|
|||
this->UpdateAndShow(true);
|
||||
}
|
||||
|
||||
|
||||
void ObjectManipulation::change_size_value(int axis, double value)
|
||||
{
|
||||
if (std::abs(m_cache.size_rounded(axis) - value) < EPSILON)
|
||||
|
@ -666,6 +862,12 @@ void ObjectManipulation::msw_rescale()
|
|||
m_manifold_warning_bmp.msw_rescale();
|
||||
m_fix_throught_netfab_bitmap->SetBitmap(m_manifold_warning_bmp.bmp());
|
||||
|
||||
m_mirror_bitmap_on.msw_rescale();
|
||||
m_mirror_bitmap_off.msw_rescale();
|
||||
m_mirror_bitmap_hidden.msw_rescale();
|
||||
m_reset_scale_button->msw_rescale();
|
||||
m_reset_rotation_button->msw_rescale();
|
||||
|
||||
get_og()->msw_rescale();
|
||||
}
|
||||
|
||||
|
|
|
@ -53,6 +53,23 @@ class ObjectManipulation : public OG_Settings
|
|||
wxStaticText* m_scale_Label = nullptr;
|
||||
wxStaticText* m_rotate_Label = nullptr;
|
||||
|
||||
// Non-owning pointers to the reset buttons, so we can hide and show them.
|
||||
ScalableButton* m_reset_scale_button = nullptr;
|
||||
ScalableButton* m_reset_rotation_button = nullptr;
|
||||
|
||||
// Mirroring buttons and their current state
|
||||
enum MirrorButtonState {
|
||||
mbHidden,
|
||||
mbShown,
|
||||
mbActive
|
||||
};
|
||||
std::array<std::pair<ScalableButton*, MirrorButtonState>, 3> m_mirror_buttons;
|
||||
|
||||
// Bitmaps for the mirroring buttons.
|
||||
ScalableBitmap m_mirror_bitmap_on;
|
||||
ScalableBitmap m_mirror_bitmap_off;
|
||||
ScalableBitmap m_mirror_bitmap_hidden;
|
||||
|
||||
// Needs to be updated from OnIdle?
|
||||
bool m_dirty = false;
|
||||
// Cached labels for the delayed update, not localized!
|
||||
|
@ -111,10 +128,10 @@ private:
|
|||
void reset_settings_value();
|
||||
void update_settings_value(const Selection& selection);
|
||||
|
||||
// update size values after scale unit changing or "gizmos"
|
||||
void update_size_value(const Vec3d& size);
|
||||
// update rotation value after "gizmos"
|
||||
void update_rotation_value(const Vec3d& rotation);
|
||||
// Show or hide scale/rotation reset buttons if needed
|
||||
void update_reset_buttons_visibility();
|
||||
//Show or hide mirror buttons
|
||||
void update_mirror_buttons_visibility();
|
||||
|
||||
// change values
|
||||
void change_position_value(int axis, double value);
|
||||
|
|
|
@ -129,12 +129,15 @@ void ObjectSettings::update_settings_list()
|
|||
optgroup->m_on_change = [](const t_config_option_key& opt_id, const boost::any& value) {
|
||||
wxGetApp().obj_list()->changed_object(); };
|
||||
|
||||
const bool is_extriders_cat = cat.first == "Extruders";
|
||||
for (auto& opt : cat.second)
|
||||
{
|
||||
if (opt == "extruder")
|
||||
continue;
|
||||
Option option = optgroup->get_option(opt);
|
||||
option.opt.width = 12;
|
||||
if (is_extriders_cat)
|
||||
option.opt.max = wxGetApp().extruders_cnt();
|
||||
optgroup->append_single_option_line(option);
|
||||
}
|
||||
optgroup->reload_config();
|
||||
|
|
|
@ -99,6 +99,12 @@ void View3D::select_all()
|
|||
m_canvas->select_all();
|
||||
}
|
||||
|
||||
void View3D::deselect_all()
|
||||
{
|
||||
if (m_canvas != nullptr)
|
||||
m_canvas->deselect_all();
|
||||
}
|
||||
|
||||
void View3D::delete_selected()
|
||||
{
|
||||
if (m_canvas != nullptr)
|
||||
|
@ -408,6 +414,12 @@ void Preview::msw_rescale()
|
|||
refresh_print();
|
||||
}
|
||||
|
||||
void Preview::move_double_slider(wxKeyEvent& evt)
|
||||
{
|
||||
if (m_slider)
|
||||
m_slider->OnKeyDown(evt);
|
||||
}
|
||||
|
||||
void Preview::bind_event_handlers()
|
||||
{
|
||||
this->Bind(wxEVT_SIZE, &Preview::on_size, this);
|
||||
|
@ -527,6 +539,7 @@ void Preview::create_double_slider()
|
|||
|
||||
m_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_sliders_scroll_changed, this);
|
||||
|
||||
|
||||
Bind(wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) {
|
||||
auto& config = wxGetApp().preset_bundle->project_config;
|
||||
((config.option<ConfigOptionFloats>("colorprint_heights"))->values) = (m_slider->GetTicksValues());
|
||||
|
@ -817,7 +830,7 @@ void Preview::load_print_as_sla()
|
|||
}
|
||||
}
|
||||
|
||||
void Preview::on_sliders_scroll_changed(wxEvent& event)
|
||||
void Preview::on_sliders_scroll_changed(wxCommandEvent& event)
|
||||
{
|
||||
if (IsShown())
|
||||
{
|
||||
|
@ -825,7 +838,7 @@ void Preview::on_sliders_scroll_changed(wxEvent& event)
|
|||
if (tech == ptFFF)
|
||||
{
|
||||
m_canvas->set_toolpaths_range(m_slider->GetLowerValueD() - 1e-6, m_slider->GetHigherValueD() + 1e-6);
|
||||
m_canvas_widget->Refresh();
|
||||
m_canvas->render();
|
||||
m_canvas->set_use_clipping_planes(false);
|
||||
}
|
||||
else if (tech == ptSLA)
|
||||
|
@ -833,10 +846,11 @@ void Preview::on_sliders_scroll_changed(wxEvent& event)
|
|||
m_canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), -m_slider->GetLowerValueD()));
|
||||
m_canvas->set_clipping_plane(1, ClippingPlane(-Vec3d::UnitZ(), m_slider->GetHigherValueD()));
|
||||
m_canvas->set_use_clipping_planes(m_slider->GetHigherValue() != 0);
|
||||
m_canvas_widget->Refresh();
|
||||
m_canvas->render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -47,6 +47,7 @@ public:
|
|||
|
||||
void select_view(const std::string& direction);
|
||||
void select_all();
|
||||
void deselect_all();
|
||||
void delete_selected();
|
||||
void mirror_selection(Axis axis);
|
||||
|
||||
|
@ -121,6 +122,7 @@ public:
|
|||
void refresh_print();
|
||||
|
||||
void msw_rescale();
|
||||
void move_double_slider(wxKeyEvent& evt);
|
||||
|
||||
private:
|
||||
bool init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model);
|
||||
|
@ -153,7 +155,7 @@ private:
|
|||
void load_print_as_fff(bool keep_z_range = false);
|
||||
void load_print_as_sla();
|
||||
|
||||
void on_sliders_scroll_changed(wxEvent& event);
|
||||
void on_sliders_scroll_changed(wxCommandEvent& event);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include <wx/sizer.h>
|
||||
#include <wx/checkbox.h>
|
||||
#include <wx/dcclient.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/fontutil.h>
|
||||
|
||||
#include "libslic3r/Config.hpp"
|
||||
|
||||
|
@ -113,6 +115,32 @@ int get_dpi_for_window(wxWindow *window)
|
|||
#endif
|
||||
}
|
||||
|
||||
wxFont get_default_font_for_dpi(int dpi)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// First try to load the font with the Windows 10 specific way.
|
||||
struct SystemParametersInfoForDpi_t { typedef BOOL (WINAPI *FN)(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni, UINT dpi); };
|
||||
static auto SystemParametersInfoForDpi_fn = winapi_get_function<SystemParametersInfoForDpi_t>(L"User32.dll", "SystemParametersInfoForDpi");
|
||||
if (SystemParametersInfoForDpi_fn != nullptr) {
|
||||
NONCLIENTMETRICS nm;
|
||||
memset(&nm, 0, sizeof(NONCLIENTMETRICS));
|
||||
nm.cbSize = sizeof(NONCLIENTMETRICS);
|
||||
if (SystemParametersInfoForDpi_fn(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &nm, 0, dpi)) {
|
||||
wxNativeFontInfo info;
|
||||
info.lf = nm.lfMessageFont;
|
||||
return wxFont(info);
|
||||
}
|
||||
}
|
||||
// Then try to guesstimate the font DPI scaling on Windows 8.
|
||||
// Let's hope that the font returned by the SystemParametersInfo(), which is used by wxWidgets internally, makes sense.
|
||||
int dpi_primary = get_dpi_for_window(nullptr);
|
||||
if (dpi_primary != dpi) {
|
||||
// Rescale the font.
|
||||
return wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Scaled(float(dpi) / float(dpi_primary));
|
||||
}
|
||||
#endif
|
||||
return wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
}
|
||||
|
||||
CheckboxFileDialog::ExtraPanel::ExtraPanel(wxWindow *parent)
|
||||
: wxPanel(parent, wxID_ANY)
|
||||
|
|
|
@ -34,6 +34,7 @@ void on_window_geometry(wxTopLevelWindow *tlw, std::function<void()> callback);
|
|||
enum { DPI_DEFAULT = 96 };
|
||||
|
||||
int get_dpi_for_window(wxWindow *window);
|
||||
wxFont get_default_font_for_dpi(int dpi);
|
||||
|
||||
struct DpiChangedEvent : public wxEvent {
|
||||
int dpi;
|
||||
|
@ -58,15 +59,13 @@ public:
|
|||
const wxSize &size=wxDefaultSize, long style=wxDEFAULT_FRAME_STYLE, const wxString &name=wxFrameNameStr)
|
||||
: P(parent, id, title, pos, size, style, name)
|
||||
{
|
||||
m_scale_factor = (float)get_dpi_for_window(this) / (float)DPI_DEFAULT;
|
||||
int dpi = get_dpi_for_window(this);
|
||||
m_scale_factor = (float)dpi / (float)DPI_DEFAULT;
|
||||
m_prev_scale_factor = m_scale_factor;
|
||||
float scale_primary_display = (float)get_dpi_for_window(nullptr) / (float)DPI_DEFAULT;
|
||||
m_normal_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
if (std::abs(m_scale_factor - scale_primary_display) > 1e-6)
|
||||
m_normal_font = m_normal_font.Scale(m_scale_factor / scale_primary_display);
|
||||
m_normal_font = get_default_font_for_dpi(dpi);
|
||||
|
||||
// An analog of em_unit value from GUI_App.
|
||||
m_em_unit = std::max<size_t>(10, 10 * m_scale_factor);
|
||||
// initialize default width_unit according to the width of the one symbol ("m") of the currently active font of this window.
|
||||
m_em_unit = std::max<size_t>(10, this->GetTextExtent("m").x - 1);
|
||||
|
||||
// recalc_font();
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
const float GLGizmoBase::Grabber::SizeFactor = 0.05f;
|
||||
const float GLGizmoBase::Grabber::SizeFactor = 0.05f;
|
||||
const float GLGizmoBase::Grabber::MinHalfSize = 1.5f;
|
||||
const float GLGizmoBase::Grabber::DraggingScaleFactor = 1.25f;
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ static const float DEFAULT_BASE_COLOR[3] = { 0.625f, 0.625f, 0.625f };
|
|||
static const float DEFAULT_DRAG_COLOR[3] = { 1.0f, 1.0f, 1.0f };
|
||||
static const float DEFAULT_HIGHLIGHT_COLOR[3] = { 1.0f, 0.38f, 0.0f };
|
||||
static const float AXES_COLOR[3][3] = { { 0.75f, 0.0f, 0.0f }, { 0.0f, 0.75f, 0.0f }, { 0.0f, 0.0f, 0.75f } };
|
||||
static const float CONSTRAINED_COLOR[3] = { 0.5f, 0.5f, 0.5f };
|
||||
|
||||
|
||||
|
||||
|
@ -76,10 +77,9 @@ public:
|
|||
{
|
||||
const Linef3 mouse_ray;
|
||||
const Point* mouse_pos;
|
||||
bool shift_down;
|
||||
|
||||
UpdateData(const Linef3& mouse_ray, const Point* mouse_pos = nullptr, bool shift_down = false)
|
||||
: mouse_ray(mouse_ray), mouse_pos(mouse_pos), shift_down(shift_down)
|
||||
UpdateData(const Linef3& mouse_ray, const Point* mouse_pos = nullptr)
|
||||
: mouse_ray(mouse_ray), mouse_pos(mouse_pos)
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -141,6 +141,7 @@ public:
|
|||
|
||||
void start_dragging(const Selection& selection);
|
||||
void stop_dragging();
|
||||
|
||||
bool is_dragging() const { return m_dragging; }
|
||||
|
||||
void update(const UpdateData& data, const Selection& selection);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include <wx/utils.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
@ -206,7 +207,7 @@ double GLGizmoMove3D::calc_projection(const UpdateData& data) const
|
|||
projection = inters_vec.dot(starting_vec.normalized());
|
||||
}
|
||||
|
||||
if (data.shift_down)
|
||||
if (wxGetKeyState(WXK_SHIFT))
|
||||
projection = m_snap_step * (double)std::round(projection / m_snap_step);
|
||||
|
||||
return projection;
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include <wx/utils.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
|
@ -19,8 +21,8 @@ GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, unsigned int sprite_id)
|
|||
: GLGizmoBase(parent, sprite_id)
|
||||
#endif // ENABLE_SVG_ICONS
|
||||
, m_scale(Vec3d::Ones())
|
||||
, m_offset(Vec3d::Zero())
|
||||
, m_snap_step(0.05)
|
||||
, m_starting_scale(Vec3d::Ones())
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -55,19 +57,28 @@ void GLGizmoScale3D::on_start_dragging(const Selection& selection)
|
|||
{
|
||||
if (m_hover_id != -1)
|
||||
{
|
||||
m_starting_drag_position = m_grabbers[m_hover_id].center;
|
||||
m_starting_box = selection.get_bounding_box();
|
||||
m_starting.drag_position = m_grabbers[m_hover_id].center;
|
||||
m_starting.ctrl_down = wxGetKeyState(WXK_CONTROL);
|
||||
m_starting.box = (m_starting.ctrl_down && (m_hover_id < 6)) ? m_box : selection.get_bounding_box();
|
||||
|
||||
const Vec3d& center = m_starting.box.center();
|
||||
m_starting.pivots[0] = m_transform * Vec3d(m_starting.box.max(0), center(1), center(2));
|
||||
m_starting.pivots[1] = m_transform * Vec3d(m_starting.box.min(0), center(1), center(2));
|
||||
m_starting.pivots[2] = m_transform * Vec3d(center(0), m_starting.box.max(1), center(2));
|
||||
m_starting.pivots[3] = m_transform * Vec3d(center(0), m_starting.box.min(1), center(2));
|
||||
m_starting.pivots[4] = m_transform * Vec3d(center(0), center(1), m_starting.box.max(2));
|
||||
m_starting.pivots[5] = m_transform * Vec3d(center(0), center(1), m_starting.box.min(2));
|
||||
}
|
||||
}
|
||||
|
||||
void GLGizmoScale3D::on_update(const UpdateData& data, const Selection& selection)
|
||||
{
|
||||
if ((m_hover_id == 0) || (m_hover_id == 1))
|
||||
do_scale_x(data);
|
||||
do_scale_along_axis(X, data);
|
||||
else if ((m_hover_id == 2) || (m_hover_id == 3))
|
||||
do_scale_y(data);
|
||||
do_scale_along_axis(Y, data);
|
||||
else if ((m_hover_id == 4) || (m_hover_id == 5))
|
||||
do_scale_z(data);
|
||||
do_scale_along_axis(Z, data);
|
||||
else if (m_hover_id >= 6)
|
||||
do_scale_uniform(data);
|
||||
}
|
||||
|
@ -111,10 +122,12 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
|
|||
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
|
||||
BoundingBoxf3 box;
|
||||
Transform3d transform = Transform3d::Identity();
|
||||
Vec3d angles = Vec3d::Zero();
|
||||
m_box.reset();
|
||||
m_transform = Transform3d::Identity();
|
||||
// Transforms grabbers' offsets to world refefence system
|
||||
Transform3d offsets_transform = Transform3d::Identity();
|
||||
m_offsets_transform = Transform3d::Identity();
|
||||
Vec3d angles = Vec3d::Zero();
|
||||
|
||||
if (single_instance)
|
||||
{
|
||||
|
@ -123,59 +136,61 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
|
|||
for (unsigned int idx : idxs)
|
||||
{
|
||||
const GLVolume* vol = selection.get_volume(idx);
|
||||
box.merge(vol->bounding_box.transformed(vol->get_volume_transformation().get_matrix()));
|
||||
m_box.merge(vol->bounding_box.transformed(vol->get_volume_transformation().get_matrix()));
|
||||
}
|
||||
|
||||
// gets transform from first selected volume
|
||||
const GLVolume* v = selection.get_volume(*idxs.begin());
|
||||
transform = v->get_instance_transformation().get_matrix();
|
||||
m_transform = v->get_instance_transformation().get_matrix();
|
||||
// gets angles from first selected volume
|
||||
angles = v->get_instance_rotation();
|
||||
// consider rotation+mirror only components of the transform for offsets
|
||||
offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror());
|
||||
m_offsets_transform = offsets_transform;
|
||||
}
|
||||
else if (single_volume)
|
||||
{
|
||||
const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
box = v->bounding_box;
|
||||
transform = v->world_matrix();
|
||||
angles = Geometry::extract_euler_angles(transform);
|
||||
m_box = v->bounding_box;
|
||||
m_transform = v->world_matrix();
|
||||
angles = Geometry::extract_euler_angles(m_transform);
|
||||
// consider rotation+mirror only components of the transform for offsets
|
||||
offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror());
|
||||
m_offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), v->get_volume_rotation(), Vec3d::Ones(), v->get_volume_mirror());
|
||||
}
|
||||
else
|
||||
box = selection.get_bounding_box();
|
||||
|
||||
m_box = box;
|
||||
m_box = selection.get_bounding_box();
|
||||
|
||||
const Vec3d& center = m_box.center();
|
||||
Vec3d offset_x = offsets_transform * Vec3d((double)Offset, 0.0, 0.0);
|
||||
Vec3d offset_y = offsets_transform * Vec3d(0.0, (double)Offset, 0.0);
|
||||
Vec3d offset_z = offsets_transform * Vec3d(0.0, 0.0, (double)Offset);
|
||||
|
||||
bool ctrl_down = (m_dragging && m_starting.ctrl_down) || (!m_dragging && wxGetKeyState(WXK_CONTROL));
|
||||
|
||||
// x axis
|
||||
m_grabbers[0].center = transform * Vec3d(m_box.min(0), center(1), center(2)) - offset_x;
|
||||
m_grabbers[1].center = transform * Vec3d(m_box.max(0), center(1), center(2)) + offset_x;
|
||||
::memcpy((void*)m_grabbers[0].color, (const void*)&AXES_COLOR[0], 3 * sizeof(float));
|
||||
::memcpy((void*)m_grabbers[1].color, (const void*)&AXES_COLOR[0], 3 * sizeof(float));
|
||||
m_grabbers[0].center = m_transform * Vec3d(m_box.min(0), center(1), center(2)) - offset_x;
|
||||
m_grabbers[1].center = m_transform * Vec3d(m_box.max(0), center(1), center(2)) + offset_x;
|
||||
::memcpy((void*)m_grabbers[0].color, (ctrl_down && (m_hover_id == 1)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[0], 3 * sizeof(float));
|
||||
::memcpy((void*)m_grabbers[1].color, (ctrl_down && (m_hover_id == 0)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[0], 3 * sizeof(float));
|
||||
|
||||
// y axis
|
||||
m_grabbers[2].center = transform * Vec3d(center(0), m_box.min(1), center(2)) - offset_y;
|
||||
m_grabbers[3].center = transform * Vec3d(center(0), m_box.max(1), center(2)) + offset_y;
|
||||
::memcpy((void*)m_grabbers[2].color, (const void*)&AXES_COLOR[1], 3 * sizeof(float));
|
||||
::memcpy((void*)m_grabbers[3].color, (const void*)&AXES_COLOR[1], 3 * sizeof(float));
|
||||
m_grabbers[2].center = m_transform * Vec3d(center(0), m_box.min(1), center(2)) - offset_y;
|
||||
m_grabbers[3].center = m_transform * Vec3d(center(0), m_box.max(1), center(2)) + offset_y;
|
||||
::memcpy((void*)m_grabbers[2].color, (ctrl_down && (m_hover_id == 3)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[1], 3 * sizeof(float));
|
||||
::memcpy((void*)m_grabbers[3].color, (ctrl_down && (m_hover_id == 2)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[1], 3 * sizeof(float));
|
||||
|
||||
// z axis
|
||||
m_grabbers[4].center = transform * Vec3d(center(0), center(1), m_box.min(2)) - offset_z;
|
||||
m_grabbers[5].center = transform * Vec3d(center(0), center(1), m_box.max(2)) + offset_z;
|
||||
::memcpy((void*)m_grabbers[4].color, (const void*)&AXES_COLOR[2], 3 * sizeof(float));
|
||||
::memcpy((void*)m_grabbers[5].color, (const void*)&AXES_COLOR[2], 3 * sizeof(float));
|
||||
m_grabbers[4].center = m_transform * Vec3d(center(0), center(1), m_box.min(2)) - offset_z;
|
||||
m_grabbers[5].center = m_transform * Vec3d(center(0), center(1), m_box.max(2)) + offset_z;
|
||||
::memcpy((void*)m_grabbers[4].color, (ctrl_down && (m_hover_id == 5)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[2], 3 * sizeof(float));
|
||||
::memcpy((void*)m_grabbers[5].color, (ctrl_down && (m_hover_id == 4)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[2], 3 * sizeof(float));
|
||||
|
||||
// uniform
|
||||
m_grabbers[6].center = transform * Vec3d(m_box.min(0), m_box.min(1), center(2)) - offset_x - offset_y;
|
||||
m_grabbers[7].center = transform * Vec3d(m_box.max(0), m_box.min(1), center(2)) + offset_x - offset_y;
|
||||
m_grabbers[8].center = transform * Vec3d(m_box.max(0), m_box.max(1), center(2)) + offset_x + offset_y;
|
||||
m_grabbers[9].center = transform * Vec3d(m_box.min(0), m_box.max(1), center(2)) - offset_x + offset_y;
|
||||
m_grabbers[6].center = m_transform * Vec3d(m_box.min(0), m_box.min(1), center(2)) - offset_x - offset_y;
|
||||
m_grabbers[7].center = m_transform * Vec3d(m_box.max(0), m_box.min(1), center(2)) + offset_x - offset_y;
|
||||
m_grabbers[8].center = m_transform * Vec3d(m_box.max(0), m_box.max(1), center(2)) + offset_x + offset_y;
|
||||
m_grabbers[9].center = m_transform * Vec3d(m_box.min(0), m_box.max(1), center(2)) - offset_x + offset_y;
|
||||
for (int i = 6; i < 10; ++i)
|
||||
{
|
||||
::memcpy((void*)m_grabbers[i].color, (const void*)m_highlight_color, 3 * sizeof(float));
|
||||
|
@ -295,40 +310,50 @@ void GLGizmoScale3D::render_grabbers_connection(unsigned int id_1, unsigned int
|
|||
}
|
||||
}
|
||||
|
||||
void GLGizmoScale3D::do_scale_x(const UpdateData& data)
|
||||
void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data)
|
||||
{
|
||||
double ratio = calc_ratio(data);
|
||||
if (ratio > 0.0)
|
||||
m_scale(0) = m_starting_scale(0) * ratio;
|
||||
}
|
||||
{
|
||||
m_scale(axis) = m_starting.scale(axis) * ratio;
|
||||
if (m_starting.ctrl_down)
|
||||
{
|
||||
double local_offset = 0.5 * (m_scale(axis) - m_starting.scale(axis)) * m_starting.box.size()(axis);
|
||||
if (m_hover_id == 2 * axis)
|
||||
local_offset *= -1.0;
|
||||
|
||||
void GLGizmoScale3D::do_scale_y(const UpdateData& data)
|
||||
{
|
||||
double ratio = calc_ratio(data);
|
||||
if (ratio > 0.0)
|
||||
m_scale(1) = m_starting_scale(1) * ratio;
|
||||
}
|
||||
Vec3d local_offset_vec;
|
||||
switch (axis)
|
||||
{
|
||||
case X: { local_offset_vec = local_offset * Vec3d::UnitX(); break; }
|
||||
case Y: { local_offset_vec = local_offset * Vec3d::UnitY(); break; }
|
||||
case Z: { local_offset_vec = local_offset * Vec3d::UnitZ(); break; }
|
||||
}
|
||||
|
||||
void GLGizmoScale3D::do_scale_z(const UpdateData& data)
|
||||
{
|
||||
double ratio = calc_ratio(data);
|
||||
if (ratio > 0.0)
|
||||
m_scale(2) = m_starting_scale(2) * ratio;
|
||||
m_offset = m_offsets_transform * local_offset_vec;
|
||||
}
|
||||
else
|
||||
m_offset = Vec3d::Zero();
|
||||
}
|
||||
}
|
||||
|
||||
void GLGizmoScale3D::do_scale_uniform(const UpdateData& data)
|
||||
{
|
||||
double ratio = calc_ratio(data);
|
||||
if (ratio > 0.0)
|
||||
m_scale = m_starting_scale * ratio;
|
||||
{
|
||||
m_scale = m_starting.scale * ratio;
|
||||
m_offset = Vec3d::Zero();
|
||||
}
|
||||
}
|
||||
|
||||
double GLGizmoScale3D::calc_ratio(const UpdateData& data) const
|
||||
{
|
||||
double ratio = 0.0;
|
||||
|
||||
// vector from the center to the starting position
|
||||
Vec3d starting_vec = m_starting_drag_position - m_starting_box.center();
|
||||
Vec3d pivot = (m_starting.ctrl_down && (m_hover_id < 6)) ? m_starting.pivots[m_hover_id] : m_starting.box.center();
|
||||
|
||||
Vec3d starting_vec = m_starting.drag_position - pivot;
|
||||
double len_starting_vec = starting_vec.norm();
|
||||
if (len_starting_vec != 0.0)
|
||||
{
|
||||
|
@ -337,9 +362,9 @@ double GLGizmoScale3D::calc_ratio(const UpdateData& data) const
|
|||
// use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
|
||||
// in our case plane normal and ray direction are the same (orthogonal view)
|
||||
// when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
|
||||
Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir;
|
||||
Vec3d inters = data.mouse_ray.a + (m_starting.drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir;
|
||||
// vector from the starting position to the found intersection
|
||||
Vec3d inters_vec = inters - m_starting_drag_position;
|
||||
Vec3d inters_vec = inters - m_starting.drag_position;
|
||||
|
||||
// finds projection of the vector along the staring direction
|
||||
double proj = inters_vec.dot(starting_vec.normalized());
|
||||
|
@ -347,7 +372,7 @@ double GLGizmoScale3D::calc_ratio(const UpdateData& data) const
|
|||
ratio = (len_starting_vec + proj) / len_starting_vec;
|
||||
}
|
||||
|
||||
if (data.shift_down)
|
||||
if (wxGetKeyState(WXK_SHIFT))
|
||||
ratio = m_snap_step * (double)std::round(ratio / m_snap_step);
|
||||
|
||||
return ratio;
|
||||
|
|
|
@ -11,15 +11,25 @@ class GLGizmoScale3D : public GLGizmoBase
|
|||
{
|
||||
static const float Offset;
|
||||
|
||||
struct StartingData
|
||||
{
|
||||
Vec3d scale;
|
||||
Vec3d drag_position;
|
||||
BoundingBoxf3 box;
|
||||
Vec3d pivots[6];
|
||||
bool ctrl_down;
|
||||
|
||||
StartingData() : scale(Vec3d::Ones()), drag_position(Vec3d::Zero()), ctrl_down(false) { for (int i = 0; i < 5; ++i) { pivots[i] = Vec3d::Zero(); } }
|
||||
};
|
||||
|
||||
mutable BoundingBoxf3 m_box;
|
||||
|
||||
mutable Transform3d m_transform;
|
||||
// Transforms grabbers offsets to the proper reference system (world for instances, instance for volumes)
|
||||
mutable Transform3d m_offsets_transform;
|
||||
Vec3d m_scale;
|
||||
|
||||
Vec3d m_offset;
|
||||
double m_snap_step;
|
||||
|
||||
Vec3d m_starting_scale;
|
||||
Vec3d m_starting_drag_position;
|
||||
BoundingBoxf3 m_starting_box;
|
||||
StartingData m_starting;
|
||||
|
||||
public:
|
||||
#if ENABLE_SVG_ICONS
|
||||
|
@ -32,7 +42,9 @@ public:
|
|||
void set_snap_step(double step) { m_snap_step = step; }
|
||||
|
||||
const Vec3d& get_scale() const { return m_scale; }
|
||||
void set_scale(const Vec3d& scale) { m_starting_scale = scale; m_scale = scale; }
|
||||
void set_scale(const Vec3d& scale) { m_starting.scale = scale; m_scale = scale; }
|
||||
|
||||
const Vec3d& get_offset() const { return m_offset; }
|
||||
|
||||
protected:
|
||||
virtual bool on_init();
|
||||
|
@ -47,9 +59,7 @@ protected:
|
|||
private:
|
||||
void render_grabbers_connection(unsigned int id_1, unsigned int id_2) const;
|
||||
|
||||
void do_scale_x(const UpdateData& data);
|
||||
void do_scale_y(const UpdateData& data);
|
||||
void do_scale_z(const UpdateData& data);
|
||||
void do_scale_along_axis(Axis axis, const UpdateData& data);
|
||||
void do_scale_uniform(const UpdateData& data);
|
||||
|
||||
double calc_ratio(const UpdateData& data) const;
|
||||
|
|
|
@ -27,6 +27,7 @@ GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, unsigned int sprite_i
|
|||
: GLGizmoBase(parent, sprite_id)
|
||||
#endif // ENABLE_SVG_ICONS
|
||||
, m_quadric(nullptr)
|
||||
, m_its(nullptr)
|
||||
{
|
||||
m_quadric = ::gluNewQuadric();
|
||||
if (m_quadric != nullptr)
|
||||
|
@ -44,6 +45,20 @@ GLGizmoSlaSupports::~GLGizmoSlaSupports()
|
|||
bool GLGizmoSlaSupports::on_init()
|
||||
{
|
||||
m_shortcut_key = WXK_CONTROL_L;
|
||||
|
||||
m_desc["head_diameter"] = _(L("Head diameter")) + ": ";
|
||||
m_desc["lock_supports"] = _(L("Lock supports under new islands"));
|
||||
m_desc["remove_selected"] = _(L("Remove selected points"));
|
||||
m_desc["remove_all"] = _(L("Remove all points"));
|
||||
m_desc["apply_changes"] = _(L("Apply changes"));
|
||||
m_desc["discard_changes"] = _(L("Discard changes"));
|
||||
m_desc["minimal_distance"] = _(L("Minimal points distance")) + ": ";
|
||||
m_desc["points_density"] = _(L("Support points density")) + ": ";
|
||||
m_desc["auto_generate"] = _(L("Auto-generate points"));
|
||||
m_desc["manual_editing"] = _(L("Manual editing"));
|
||||
m_desc["clipping_of_view"] = _(L("Clipping of view"))+ ": ";
|
||||
m_desc["reset_direction"] = _(L("Reset direction"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -303,6 +318,9 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||
glsafe(::glTranslated(support_point.pos(0), support_point.pos(1), support_point.pos(2)));
|
||||
glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data()));
|
||||
|
||||
if (vol->is_left_handed())
|
||||
glFrontFace(GL_CW);
|
||||
|
||||
// Matrices set, we can render the point mark now.
|
||||
// If in editing mode, we'll also render a cone pointing to the sphere.
|
||||
if (m_editing_mode) {
|
||||
|
@ -324,6 +342,9 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||
glsafe(::glPopMatrix());
|
||||
}
|
||||
::gluSphere(m_quadric, m_editing_mode_cache[i].support_point.head_front_radius * RenderPointScale, 24, 12);
|
||||
if (vol->is_left_handed())
|
||||
glFrontFace(GL_CCW);
|
||||
|
||||
glsafe(::glPopMatrix());
|
||||
}
|
||||
|
||||
|
@ -359,36 +380,23 @@ bool GLGizmoSlaSupports::is_point_clipped(const Vec3d& point) const
|
|||
bool GLGizmoSlaSupports::is_mesh_update_necessary() const
|
||||
{
|
||||
return ((m_state == On) && (m_model_object != nullptr) && !m_model_object->instances.empty())
|
||||
&& ((m_model_object->id() != m_current_mesh_model_id) || m_V.size()==0);
|
||||
&& ((m_model_object->id() != m_current_mesh_model_id) || m_its == nullptr);
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::update_mesh()
|
||||
{
|
||||
wxBusyCursor wait;
|
||||
Eigen::MatrixXf& V = m_V;
|
||||
Eigen::MatrixXi& F = m_F;
|
||||
// We rely on SLA model object having a single volume,
|
||||
// this way we can use that mesh directly.
|
||||
// This mesh does not account for the possible Z up SLA offset.
|
||||
m_mesh = &m_model_object->volumes.front()->mesh;
|
||||
const_cast<TriangleMesh*>(m_mesh)->require_shared_vertices(); // TriangleMeshSlicer needs this
|
||||
const stl_file& stl = m_mesh->stl;
|
||||
V.resize(3 * stl.stats.number_of_facets, 3);
|
||||
F.resize(stl.stats.number_of_facets, 3);
|
||||
for (unsigned int i=0; i<stl.stats.number_of_facets; ++i) {
|
||||
const stl_facet* facet = stl.facet_start+i;
|
||||
V(3*i+0, 0) = facet->vertex[0](0); V(3*i+0, 1) = facet->vertex[0](1); V(3*i+0, 2) = facet->vertex[0](2);
|
||||
V(3*i+1, 0) = facet->vertex[1](0); V(3*i+1, 1) = facet->vertex[1](1); V(3*i+1, 2) = facet->vertex[1](2);
|
||||
V(3*i+2, 0) = facet->vertex[2](0); V(3*i+2, 1) = facet->vertex[2](1); V(3*i+2, 2) = facet->vertex[2](2);
|
||||
F(i, 0) = 3*i+0;
|
||||
F(i, 1) = 3*i+1;
|
||||
F(i, 2) = 3*i+2;
|
||||
}
|
||||
m_mesh = &m_model_object->volumes.front()->mesh();
|
||||
m_its = &m_mesh->its;
|
||||
m_current_mesh_model_id = m_model_object->id();
|
||||
m_editing_mode = false;
|
||||
|
||||
m_AABB = igl::AABB<Eigen::MatrixXf,3>();
|
||||
m_AABB.init(m_V, m_F);
|
||||
m_AABB.deinit();
|
||||
m_AABB.init(
|
||||
MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3),
|
||||
MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3));
|
||||
}
|
||||
|
||||
// Unprojects the mouse position on the mesh and return the hit point and normal of the facet.
|
||||
|
@ -396,7 +404,7 @@ void GLGizmoSlaSupports::update_mesh()
|
|||
std::pair<Vec3f, Vec3f> GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos)
|
||||
{
|
||||
// if the gizmo doesn't have the V, F structures for igl, calculate them first:
|
||||
if (m_V.size() == 0)
|
||||
if (m_its == nullptr)
|
||||
update_mesh();
|
||||
|
||||
const Camera& camera = m_parent.get_camera();
|
||||
|
@ -422,7 +430,10 @@ std::pair<Vec3f, Vec3f> GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse
|
|||
point1 = inv * point1;
|
||||
point2 = inv * point2;
|
||||
|
||||
if (!m_AABB.intersect_ray(m_V, m_F, point1.cast<float>(), (point2-point1).cast<float>(), hits))
|
||||
if (!m_AABB.intersect_ray(
|
||||
MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3),
|
||||
MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3),
|
||||
point1.cast<float>(), (point2-point1).cast<float>(), hits))
|
||||
throw std::invalid_argument("unproject_on_mesh(): No intersection found.");
|
||||
|
||||
std::sort(hits.begin(), hits.end(), [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; });
|
||||
|
@ -437,9 +448,9 @@ std::pair<Vec3f, Vec3f> GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse
|
|||
igl::Hit& hit = hits[i];
|
||||
int fid = hit.id; // facet id
|
||||
bc = Vec3f(1-hit.u-hit.v, hit.u, hit.v); // barycentric coordinates of the hit
|
||||
a = (m_V.row(m_F(fid, 1)) - m_V.row(m_F(fid, 0)));
|
||||
b = (m_V.row(m_F(fid, 2)) - m_V.row(m_F(fid, 0)));
|
||||
result = bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2));
|
||||
a = (m_its->vertices[m_its->indices[fid](1)] - m_its->vertices[m_its->indices[fid](0)]);
|
||||
b = (m_its->vertices[m_its->indices[fid](2)] - m_its->vertices[m_its->indices[fid](0)]);
|
||||
result = bc(0) * m_its->vertices[m_its->indices[fid](0)] + bc(1) * m_its->vertices[m_its->indices[fid](1)] + bc(2)*m_its->vertices[m_its->indices[fid](2)];
|
||||
if (m_clipping_plane_distance == 0.f || !is_point_clipped(result.cast<double>()))
|
||||
break;
|
||||
}
|
||||
|
@ -530,7 +541,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
const Selection& selection = m_parent.get_selection();
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
const Transform3d& instance_matrix_no_translation_no_scaling = volume->get_instance_transformation().get_matrix(true,false,true);
|
||||
Vec3f direction_to_camera = camera.get_dir_forward().cast<float>();
|
||||
Vec3f direction_to_camera = -camera.get_dir_forward().cast<float>();
|
||||
Vec3f direction_to_camera_mesh = (instance_matrix_no_translation_no_scaling.inverse().cast<float>() * direction_to_camera).normalized().eval();
|
||||
Vec3f scaling = volume->get_instance_scaling_factor().cast<float>();
|
||||
direction_to_camera_mesh = Vec3f(direction_to_camera_mesh(0)*scaling(0), direction_to_camera_mesh(1)*scaling(1), direction_to_camera_mesh(2)*scaling(2));
|
||||
|
@ -544,15 +555,18 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
// Cast a ray in the direction of the camera and look for intersection with the mesh:
|
||||
std::vector<igl::Hit> hits;
|
||||
// Offset the start of the ray to the front of the ball + EPSILON to account for numerical inaccuracies.
|
||||
if (m_AABB.intersect_ray(m_V, m_F, support_point.pos + direction_to_camera_mesh * (support_point.head_front_radius + EPSILON), direction_to_camera_mesh, hits)) {
|
||||
if (m_AABB.intersect_ray(
|
||||
MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3),
|
||||
MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3),
|
||||
support_point.pos + direction_to_camera_mesh * (support_point.head_front_radius + EPSILON), direction_to_camera_mesh, hits)) {
|
||||
std::sort(hits.begin(), hits.end(), [](const igl::Hit& h1, const igl::Hit& h2) { return h1.t < h2.t; });
|
||||
|
||||
if (m_clipping_plane_distance != 0.f) {
|
||||
// If the closest hit facet normal points in the same direction as the ray,
|
||||
// we are looking through the mesh and should therefore discard the point:
|
||||
int fid = hits.front().id; // facet id
|
||||
Vec3f a = (m_V.row(m_F(fid, 1)) - m_V.row(m_F(fid, 0)));
|
||||
Vec3f b = (m_V.row(m_F(fid, 2)) - m_V.row(m_F(fid, 0)));
|
||||
Vec3f a = (m_its->vertices[m_its->indices[fid](1)] - m_its->vertices[m_its->indices[fid](0)]);
|
||||
Vec3f b = (m_its->vertices[m_its->indices[fid](2)] - m_its->vertices[m_its->indices[fid](0)]);
|
||||
if ((a.cross(b)).dot(direction_to_camera_mesh) > 0.f)
|
||||
is_obscured = true;
|
||||
|
||||
|
@ -562,7 +576,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
int fid = hit.id; // facet id
|
||||
|
||||
Vec3f bc = Vec3f(1-hit.u-hit.v, hit.u, hit.v); // barycentric coordinates of the hit
|
||||
Vec3f hit_pos = bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2));
|
||||
Vec3f hit_pos = bc(0) * m_its->vertices[m_its->indices[fid](0)] + bc(1) * m_its->vertices[m_its->indices[fid](1)] + bc(2)*m_its->vertices[m_its->indices[fid](2)];
|
||||
if (is_point_clipped(hit_pos.cast<double>())) {
|
||||
hits.erase(hits.begin()+j);
|
||||
--j;
|
||||
|
@ -739,9 +753,12 @@ void GLGizmoSlaSupports::update_cache_entry_normal(unsigned int i) const
|
|||
int idx = 0;
|
||||
Eigen::Matrix<float, 1, 3> pp = m_editing_mode_cache[i].support_point.pos;
|
||||
Eigen::Matrix<float, 1, 3> cc;
|
||||
m_AABB.squared_distance(m_V, m_F, pp, idx, cc);
|
||||
Vec3f a = (m_V.row(m_F(idx, 1)) - m_V.row(m_F(idx, 0)));
|
||||
Vec3f b = (m_V.row(m_F(idx, 2)) - m_V.row(m_F(idx, 0)));
|
||||
m_AABB.squared_distance(
|
||||
MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3),
|
||||
MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3),
|
||||
pp, idx, cc);
|
||||
Vec3f a = (m_its->vertices[m_its->indices[idx](1)] - m_its->vertices[m_its->indices[idx](0)]);
|
||||
Vec3f b = (m_its->vertices[m_its->indices[idx](2)] - m_its->vertices[m_its->indices[idx](0)]);
|
||||
m_editing_mode_cache[i].normal = a.cross(b);
|
||||
}
|
||||
|
||||
|
@ -825,7 +842,18 @@ RENDER_AGAIN:
|
|||
m_imgui->set_next_window_bg_alpha(0.5f);
|
||||
m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
|
||||
|
||||
ImGui::PushItemWidth(m_imgui->scaled(5.55f));
|
||||
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
|
||||
|
||||
const float settings_sliders_left = std::max(m_imgui->calc_text_size(m_desc.at("minimal_distance")).x, m_imgui->calc_text_size(m_desc.at("points_density")).x) + m_imgui->scaled(1.f);
|
||||
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f);
|
||||
const float diameter_slider_left = m_imgui->calc_text_size(m_desc.at("head_diameter")).x + m_imgui->scaled(1.f);
|
||||
const float minimal_slider_width = m_imgui->scaled(4.f);
|
||||
const float buttons_width_approx = m_imgui->calc_text_size(m_desc.at("apply_changes")).x + m_imgui->calc_text_size(m_desc.at("discard_changes")).x + m_imgui->scaled(1.5f);
|
||||
const float lock_supports_width_approx = m_imgui->calc_text_size(m_desc.at("lock_supports")).x + m_imgui->scaled(2.f);
|
||||
|
||||
float window_width = minimal_slider_width + std::max(std::max(settings_sliders_left, clipping_slider_left), diameter_slider_left);
|
||||
window_width = std::max(std::max(window_width, buttons_width_approx), lock_supports_width_approx);
|
||||
|
||||
|
||||
bool force_refresh = false;
|
||||
bool remove_selected = false;
|
||||
|
@ -836,10 +864,10 @@ RENDER_AGAIN:
|
|||
float diameter_upper_cap = static_cast<ConfigOptionFloat*>(wxGetApp().preset_bundle->sla_prints.get_edited_preset().config.option("support_pillar_diameter"))->value;
|
||||
if (m_new_point_head_diameter > diameter_upper_cap)
|
||||
m_new_point_head_diameter = diameter_upper_cap;
|
||||
m_imgui->text(m_desc.at("head_diameter"));
|
||||
ImGui::SameLine(diameter_slider_left);
|
||||
ImGui::PushItemWidth(window_width - diameter_slider_left);
|
||||
|
||||
m_imgui->text(_(L("Head diameter")) + ": ");
|
||||
ImGui::SameLine(m_imgui->scaled(6.66f));
|
||||
ImGui::PushItemWidth(m_imgui->scaled(8.33f));
|
||||
if (ImGui::SliderFloat("", &m_new_point_head_diameter, 0.1f, diameter_upper_cap, "%.1f")) {
|
||||
// value was changed
|
||||
for (auto& cache_entry : m_editing_mode_cache)
|
||||
|
@ -850,34 +878,34 @@ RENDER_AGAIN:
|
|||
}
|
||||
|
||||
bool changed = m_lock_unique_islands;
|
||||
m_imgui->checkbox(_(L("Lock supports under new islands")), m_lock_unique_islands);
|
||||
m_imgui->checkbox(m_desc.at("lock_supports"), m_lock_unique_islands);
|
||||
force_refresh |= changed != m_lock_unique_islands;
|
||||
|
||||
m_imgui->disabled_begin(m_selection_empty);
|
||||
remove_selected = m_imgui->button(_(L("Remove selected points")));
|
||||
remove_selected = m_imgui->button(m_desc.at("remove_selected"));
|
||||
m_imgui->disabled_end();
|
||||
|
||||
m_imgui->disabled_begin(m_editing_mode_cache.empty());
|
||||
remove_all = m_imgui->button(_(L("Remove all points")));
|
||||
remove_all = m_imgui->button(m_desc.at("remove_all"));
|
||||
m_imgui->disabled_end();
|
||||
|
||||
m_imgui->text(" "); // vertical gap
|
||||
|
||||
if (m_imgui->button(_(L("Apply changes")))) {
|
||||
if (m_imgui->button(m_desc.at("apply_changes"))) {
|
||||
editing_mode_apply_changes();
|
||||
force_refresh = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
bool discard_changes = m_imgui->button(_(L("Discard changes")));
|
||||
bool discard_changes = m_imgui->button(m_desc.at("discard_changes"));
|
||||
if (discard_changes) {
|
||||
editing_mode_discard_changes();
|
||||
force_refresh = true;
|
||||
}
|
||||
}
|
||||
else { // not in editing mode:
|
||||
ImGui::PushItemWidth(m_imgui->scaled(5.55f));
|
||||
m_imgui->text(_(L("Minimal points distance")) + ": ");
|
||||
ImGui::SameLine(m_imgui->scaled(9.44f));
|
||||
m_imgui->text(m_desc.at("minimal_distance"));
|
||||
ImGui::SameLine(settings_sliders_left);
|
||||
ImGui::PushItemWidth(window_width - settings_sliders_left);
|
||||
|
||||
std::vector<const ConfigOption*> opts = get_config_options({"support_points_density_relative", "support_points_minimal_distance"});
|
||||
float density = static_cast<const ConfigOptionInt*>(opts[0])->value;
|
||||
|
@ -887,8 +915,9 @@ RENDER_AGAIN:
|
|||
if (value_changed)
|
||||
m_model_object->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = minimal_point_distance;
|
||||
|
||||
m_imgui->text(_(L("Support points density")) + ": ");
|
||||
ImGui::SameLine(m_imgui->scaled(9.44f));
|
||||
m_imgui->text(m_desc.at("points_density"));
|
||||
ImGui::SameLine(settings_sliders_left);
|
||||
|
||||
if (ImGui::SliderFloat(" ", &density, 0.f, 200.f, "%.f %%")) {
|
||||
value_changed = true;
|
||||
m_model_object->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)density;
|
||||
|
@ -901,17 +930,17 @@ RENDER_AGAIN:
|
|||
});
|
||||
}
|
||||
|
||||
bool generate = m_imgui->button(_(L("Auto-generate points")));
|
||||
bool generate = m_imgui->button(m_desc.at("auto_generate"));
|
||||
|
||||
if (generate)
|
||||
auto_generate();
|
||||
|
||||
m_imgui->text("");
|
||||
if (m_imgui->button(_(L("Manual editing"))))
|
||||
if (m_imgui->button(m_desc.at("manual_editing")))
|
||||
switch_to_editing_mode();
|
||||
|
||||
m_imgui->disabled_begin(m_editing_mode_cache.empty());
|
||||
remove_all = m_imgui->button(_(L("Remove all points")));
|
||||
remove_all = m_imgui->button(m_desc.at("remove_all"));
|
||||
m_imgui->disabled_end();
|
||||
|
||||
// m_imgui->text("");
|
||||
|
@ -925,17 +954,17 @@ RENDER_AGAIN:
|
|||
// Following is rendered in both editing and non-editing mode:
|
||||
m_imgui->text("");
|
||||
if (m_clipping_plane_distance == 0.f)
|
||||
m_imgui->text(_(L("Clipping of view"))+ ": ");
|
||||
m_imgui->text(m_desc.at("clipping_of_view"));
|
||||
else {
|
||||
if (m_imgui->button(_(L("Reset direction")))) {
|
||||
if (m_imgui->button(m_desc.at("reset_direction"))) {
|
||||
wxGetApp().CallAfter([this](){
|
||||
reset_clipping_plane_normal();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine(m_imgui->scaled(6.66f));
|
||||
ImGui::PushItemWidth(m_imgui->scaled(8.33f));
|
||||
ImGui::SameLine(clipping_slider_left);
|
||||
ImGui::PushItemWidth(window_width - clipping_slider_left);
|
||||
ImGui::SliderFloat(" ", &m_clipping_plane_distance, 0.f, 1.f, "%.2f");
|
||||
|
||||
|
||||
|
@ -1035,8 +1064,7 @@ void GLGizmoSlaSupports::on_set_state()
|
|||
m_clipping_plane_distance = 0.f;
|
||||
// Release triangle mesh slicer and the AABB spatial search structure.
|
||||
m_AABB.deinit();
|
||||
m_V = Eigen::MatrixXf();
|
||||
m_F = Eigen::MatrixXi();
|
||||
m_its = nullptr;
|
||||
m_tms.reset();
|
||||
m_supports_tms.reset();
|
||||
});
|
||||
|
@ -1260,4 +1288,4 @@ SlaGizmoHelpDialog::SlaGizmoHelpDialog()
|
|||
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
} // namespace Slic3r
|
|
@ -35,10 +35,11 @@ private:
|
|||
const float RenderPointScale = 1.f;
|
||||
|
||||
GLUquadricObj* m_quadric;
|
||||
Eigen::MatrixXf m_V; // vertices
|
||||
Eigen::MatrixXi m_F; // facets indices
|
||||
igl::AABB<Eigen::MatrixXf,3> m_AABB;
|
||||
typedef Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>> MapMatrixXfUnaligned;
|
||||
typedef Eigen::Map<const Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>> MapMatrixXiUnaligned;
|
||||
igl::AABB<MapMatrixXfUnaligned, 3> m_AABB;
|
||||
const TriangleMesh* m_mesh;
|
||||
const indexed_triangle_set* m_its;
|
||||
mutable const TriangleMesh* m_supports_mesh;
|
||||
mutable std::vector<Vec2f> m_triangles;
|
||||
mutable std::vector<Vec2f> m_supports_triangles;
|
||||
|
@ -95,6 +96,10 @@ private:
|
|||
mutable Vec3d m_old_clipping_plane_normal;
|
||||
mutable Vec3d m_clipping_plane_normal = Vec3d::Zero();
|
||||
|
||||
// This map holds all translated description texts, so they can be easily referenced during layout calculations
|
||||
// etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
|
||||
std::map<std::string, wxString> m_desc;
|
||||
|
||||
GLSelectionRectangle m_selection_rectangle;
|
||||
|
||||
bool m_wait_for_up_event = false;
|
||||
|
@ -127,6 +132,11 @@ private:
|
|||
|
||||
protected:
|
||||
void on_set_state() override;
|
||||
virtual void on_set_hover_id()
|
||||
{
|
||||
if ((int)m_editing_mode_cache.size() <= m_hover_id)
|
||||
m_hover_id = -1;
|
||||
}
|
||||
void on_start_dragging(const Selection& selection) override;
|
||||
virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection) override;
|
||||
|
||||
|
|
|
@ -248,14 +248,14 @@ void GLGizmosManager::enable_grabber(EType type, unsigned int id, bool enable)
|
|||
}
|
||||
}
|
||||
|
||||
void GLGizmosManager::update(const Linef3& mouse_ray, const Selection& selection, bool shift_down, const Point* mouse_pos)
|
||||
void GLGizmosManager::update(const Linef3& mouse_ray, const Selection& selection, const Point* mouse_pos)
|
||||
{
|
||||
if (!m_enabled)
|
||||
return;
|
||||
|
||||
GLGizmoBase* curr = get_current();
|
||||
if (curr != nullptr)
|
||||
curr->update(GLGizmoBase::UpdateData(mouse_ray, mouse_pos, shift_down), selection);
|
||||
curr->update(GLGizmoBase::UpdateData(mouse_ray, mouse_pos), selection);
|
||||
}
|
||||
|
||||
void GLGizmosManager::update_data(GLCanvas3D& canvas)
|
||||
|
@ -418,6 +418,15 @@ void GLGizmosManager::set_scale(const Vec3d& scale)
|
|||
reinterpret_cast<GLGizmoScale3D*>(it->second)->set_scale(scale);
|
||||
}
|
||||
|
||||
Vec3d GLGizmosManager::get_scale_offset() const
|
||||
{
|
||||
if (!m_enabled)
|
||||
return Vec3d::Zero();
|
||||
|
||||
GizmosMap::const_iterator it = m_gizmos.find(Scale);
|
||||
return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoScale3D*>(it->second)->get_offset() : Vec3d::Zero();
|
||||
}
|
||||
|
||||
Vec3d GLGizmosManager::get_rotation() const
|
||||
{
|
||||
if (!m_enabled)
|
||||
|
@ -627,7 +636,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas)
|
|||
canvas.get_wxglcanvas()->CaptureMouse();
|
||||
|
||||
canvas.set_mouse_as_dragging();
|
||||
update(canvas.mouse_ray(pos), selection, evt.ShiftDown(), &pos);
|
||||
update(canvas.mouse_ray(pos), selection, &pos);
|
||||
|
||||
switch (m_current)
|
||||
{
|
||||
|
@ -645,6 +654,8 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas)
|
|||
if (evt.AltDown())
|
||||
transformation_type.set_independent();
|
||||
selection.scale(get_scale(), transformation_type);
|
||||
if (evt.ControlDown())
|
||||
selection.translate(get_scale_offset(), true);
|
||||
wxGetApp().obj_manipul()->set_dirty();
|
||||
break;
|
||||
}
|
||||
|
@ -780,10 +791,13 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt, GLCanvas3D& canvas)
|
|||
// key ESC
|
||||
case WXK_ESCAPE:
|
||||
{
|
||||
if ((m_current != SlaSupports) || !gizmo_event(SLAGizmoEventType::DiscardChanges))
|
||||
reset_all_states();
|
||||
if (m_current != Undefined)
|
||||
{
|
||||
if ((m_current != SlaSupports) || !gizmo_event(SLAGizmoEventType::DiscardChanges))
|
||||
reset_all_states();
|
||||
|
||||
processed = true;
|
||||
processed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WXK_RETURN:
|
||||
|
@ -833,6 +847,19 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt, GLCanvas3D& canvas)
|
|||
|
||||
break;
|
||||
}
|
||||
case 'F':
|
||||
case 'f':
|
||||
{
|
||||
if (m_current == Scale)
|
||||
{
|
||||
if (!is_dragging())
|
||||
wxGetApp().plater()->scale_selection_to_fit_print_volume();
|
||||
|
||||
processed = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@ public:
|
|||
void set_hover_id(int id);
|
||||
void enable_grabber(EType type, unsigned int id, bool enable);
|
||||
|
||||
void update(const Linef3& mouse_ray, const Selection& selection, bool shift_down, const Point* mouse_pos = nullptr);
|
||||
void update(const Linef3& mouse_ray, const Selection& selection, const Point* mouse_pos = nullptr);
|
||||
void update_data(GLCanvas3D& canvas);
|
||||
|
||||
Rect get_reset_rect_viewport(const GLCanvas3D& canvas) const;
|
||||
|
@ -138,6 +138,8 @@ public:
|
|||
Vec3d get_scale() const;
|
||||
void set_scale(const Vec3d& scale);
|
||||
|
||||
Vec3d get_scale_offset() const;
|
||||
|
||||
Vec3d get_rotation() const;
|
||||
void set_rotation(const Vec3d& rotation);
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ namespace GUI {
|
|||
|
||||
ImGuiWrapper::ImGuiWrapper()
|
||||
: m_glyph_ranges(nullptr)
|
||||
, m_font_cjk(false)
|
||||
, m_font_size(18.0)
|
||||
, m_font_texture(0)
|
||||
, m_style_scaling(1.0)
|
||||
|
@ -68,16 +69,52 @@ void ImGuiWrapper::set_language(const std::string &language)
|
|||
0x0100, 0x017F, // Latin Extended-A
|
||||
0,
|
||||
};
|
||||
static const ImWchar ranges_turkish[] = {
|
||||
0x0020, 0x01FF, // Basic Latin + Latin Supplement
|
||||
0x0100, 0x017F, // Latin Extended-A
|
||||
0x0180, 0x01FF, // Turkish
|
||||
0,
|
||||
};
|
||||
static const ImWchar ranges_vietnamese[] =
|
||||
{
|
||||
0x0020, 0x00FF, // Basic Latin
|
||||
0x0102, 0x0103,
|
||||
0x0110, 0x0111,
|
||||
0x0128, 0x0129,
|
||||
0x0168, 0x0169,
|
||||
0x01A0, 0x01A1,
|
||||
0x01AF, 0x01B0,
|
||||
0x1EA0, 0x1EF9,
|
||||
0,
|
||||
};
|
||||
m_font_cjk = false;
|
||||
if (lang == "cs" || lang == "pl") {
|
||||
ranges = ranges_latin2;
|
||||
} else if (lang == "ru" || lang == "uk") {
|
||||
ranges = ImGui::GetIO().Fonts->GetGlyphRangesCyrillic();
|
||||
ranges = ImGui::GetIO().Fonts->GetGlyphRangesCyrillic(); // Default + about 400 Cyrillic characters
|
||||
} else if (lang == "tr") {
|
||||
ranges = ranges_turkish;
|
||||
} else if (lang == "vi") {
|
||||
ranges = ranges_vietnamese;
|
||||
} else if (lang == "jp") {
|
||||
ranges = ImGui::GetIO().Fonts->GetGlyphRangesJapanese();
|
||||
} else if (lang == "kr") {
|
||||
ranges = ImGui::GetIO().Fonts->GetGlyphRangesKorean();
|
||||
ranges = ImGui::GetIO().Fonts->GetGlyphRangesJapanese(); // Default + Hiragana, Katakana, Half-Width, Selection of 1946 Ideographs
|
||||
m_font_cjk = true;
|
||||
} else if (lang == "ko") {
|
||||
ranges = ImGui::GetIO().Fonts->GetGlyphRangesKorean(); // Default + Korean characters
|
||||
m_font_cjk = true;
|
||||
} else if (lang == "zh") {
|
||||
ranges = ImGui::GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon();
|
||||
ranges = (language == "zh_TW") ?
|
||||
// Traditional Chinese
|
||||
// Default + Half-Width + Japanese Hiragana/Katakana + full set of about 21000 CJK Unified Ideographs
|
||||
ImGui::GetIO().Fonts->GetGlyphRangesChineseFull() :
|
||||
// Simplified Chinese
|
||||
// Default + Half-Width + Japanese Hiragana/Katakana + set of 2500 CJK Unified Ideographs for common simplified Chinese
|
||||
ImGui::GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon();
|
||||
m_font_cjk = true;
|
||||
} else if (lang == "th") {
|
||||
ranges = ImGui::GetIO().Fonts->GetGlyphRangesThai(); // Default + Thai characters
|
||||
} else {
|
||||
ranges = ImGui::GetIO().Fonts->GetGlyphRangesDefault(); // Basic Latin, Extended Latin
|
||||
}
|
||||
|
||||
if (ranges != m_glyph_ranges) {
|
||||
|
@ -186,7 +223,14 @@ void ImGuiWrapper::render()
|
|||
ImVec2 ImGuiWrapper::calc_text_size(const wxString &text)
|
||||
{
|
||||
auto text_utf8 = into_u8(text);
|
||||
return ImGui::CalcTextSize(text_utf8.c_str());
|
||||
ImVec2 size = ImGui::CalcTextSize(text_utf8.c_str());
|
||||
|
||||
/*#ifdef __linux__
|
||||
size.x *= m_style_scaling;
|
||||
size.y *= m_style_scaling;
|
||||
#endif*/
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void ImGuiWrapper::set_next_window_pos(float x, float y, int flag)
|
||||
|
@ -345,7 +389,9 @@ void ImGuiWrapper::init_font()
|
|||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.Fonts->Clear();
|
||||
ImFont* font = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/NotoSans-Regular.ttf").c_str(), m_font_size, nullptr, m_glyph_ranges);
|
||||
//FIXME replace with io.Fonts->AddFontFromMemoryTTF(buf_decompressed_data, (int)buf_decompressed_size, m_font_size, nullptr, m_glyph_ranges);
|
||||
//https://github.com/ocornut/imgui/issues/220
|
||||
ImFont* font = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/" + (m_font_cjk ? "NotoSansCJK-Regular.ttc" : "NotoSans-Regular.ttf")).c_str(), m_font_size, nullptr, m_glyph_ranges);
|
||||
if (font == nullptr) {
|
||||
font = io.Fonts->AddFontDefault();
|
||||
if (font == nullptr) {
|
||||
|
|
|
@ -19,6 +19,8 @@ namespace GUI {
|
|||
class ImGuiWrapper
|
||||
{
|
||||
const ImWchar *m_glyph_ranges;
|
||||
// Chinese, Japanese, Korean
|
||||
bool m_font_cjk;
|
||||
float m_font_size;
|
||||
unsigned m_font_texture;
|
||||
float m_style_scaling;
|
||||
|
|
|
@ -46,7 +46,6 @@ KBShortcutsDialog::KBShortcutsDialog()
|
|||
main_grid_sizer->Add(r_sizer, 0);
|
||||
|
||||
m_head_bitmaps.reserve(m_full_shortcuts.size());
|
||||
const wxSize topic_size = wxSize(10 * wxGetApp().em_unit(), -1);
|
||||
|
||||
for (auto& shortcut : m_full_shortcuts)
|
||||
{
|
||||
|
@ -59,7 +58,7 @@ KBShortcutsDialog::KBShortcutsDialog()
|
|||
hsizer->Add(m_head_bitmaps.back(), 0, wxEXPAND | wxLEFT | wxRIGHT, 15);
|
||||
|
||||
// head
|
||||
wxStaticText* head = new wxStaticText(panel, wxID_ANY, shortcut.first, wxDefaultPosition, topic_size);
|
||||
wxStaticText* head = new wxStaticText(panel, wxID_ANY, shortcut.first);
|
||||
head->SetFont(head_font);
|
||||
hsizer->Add(head, 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
|
@ -144,14 +143,16 @@ void KBShortcutsDialog::fill_shortcuts()
|
|||
plater_shortcuts.push_back(Shortcut("C", L("Gizmo cut")));
|
||||
plater_shortcuts.push_back(Shortcut("F", L("Gizmo Place face on bed")));
|
||||
plater_shortcuts.push_back(Shortcut("L", L("Gizmo SLA support points")));
|
||||
plater_shortcuts.push_back(Shortcut("Shift+", L("Press to snap by 5% in Gizmo scale\nor by 1mm in Gizmo move")));
|
||||
plater_shortcuts.push_back(Shortcut(alt, L("Press to scale or rotate selected objects\naround their own center")));
|
||||
plater_shortcuts.push_back(Shortcut("Shift+", L("Press to activate selection rectangle\nor to snap by 5% in Gizmo scale\nor to snap by 1mm in Gizmo move")));
|
||||
plater_shortcuts.push_back(Shortcut("F", L("Press to scale selection to fit print volume\nin Gizmo scale")));
|
||||
plater_shortcuts.push_back(Shortcut(alt, L("Press to activate deselection rectangle\nor to scale or rotate selected objects\naround their own center")));
|
||||
plater_shortcuts.push_back(Shortcut(ctrl, L("Press to activate one direction scaling in Gizmo scale")));
|
||||
plater_shortcuts.push_back(Shortcut("B", L("Zoom to Bed")));
|
||||
plater_shortcuts.push_back(Shortcut("Z", L("Zoom to all objects in scene, if none selected")));
|
||||
plater_shortcuts.push_back(Shortcut("Z", L("Zoom to selected object")));
|
||||
plater_shortcuts.push_back(Shortcut("I", L("Zoom in")));
|
||||
plater_shortcuts.push_back(Shortcut("O", L("Zoom out")));
|
||||
plater_shortcuts.push_back(Shortcut("ESC", L("Unselect gizmo, keep object selection")));
|
||||
plater_shortcuts.push_back(Shortcut("ESC", L("Unselect gizmo / Clear selection")));
|
||||
|
||||
m_full_shortcuts.push_back(std::make_pair(_(L("Plater Shortcuts")), std::make_pair(plater_shortcuts, szRight)));
|
||||
|
||||
|
@ -211,7 +212,6 @@ void KBShortcutsDialog::on_dpi_changed(const wxRect &suggested_rect)
|
|||
void KBShortcutsDialog::onCloseDialog(wxEvent &)
|
||||
{
|
||||
this->EndModal(wxID_CLOSE);
|
||||
this->Close();
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
|
|
|
@ -33,14 +33,14 @@ namespace Slic3r {
|
|||
namespace GUI {
|
||||
|
||||
MainFrame::MainFrame() :
|
||||
DPIFrame(NULL, wxID_ANY, wxString(SLIC3R_BUILD_ID) + " " + _(L("based on Slic3r")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "mainframe"),
|
||||
m_printhost_queue_dlg(new PrintHostQueueDialog(this))
|
||||
DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "mainframe"),
|
||||
m_printhost_queue_dlg(new PrintHostQueueDialog(this))
|
||||
{
|
||||
// Fonts were created by the DPIFrame constructor for the monitor, on which the window opened.
|
||||
wxGetApp().update_fonts(this);
|
||||
#ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList
|
||||
this->SetFont(this->normal_font());
|
||||
// initialize default width_unit according to the width of the one symbol ("m") of the currently active font of this window.
|
||||
wxGetApp().set_em_unit(std::max<size_t>(10, GetTextExtent("m").x - 1));
|
||||
#endif
|
||||
|
||||
// Load the icon either from the exe, or from the ico file.
|
||||
#if _WIN32
|
||||
|
@ -95,6 +95,8 @@ DPIFrame(NULL, wxID_ANY, wxString(SLIC3R_BUILD_ID) + " " + _(L("based on Slic3r"
|
|||
#endif
|
||||
Layout();
|
||||
|
||||
update_title();
|
||||
|
||||
// declare events
|
||||
Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& event) {
|
||||
if (event.CanVeto() && !wxGetApp().check_unsaved_changes()) {
|
||||
|
@ -138,15 +140,32 @@ DPIFrame(NULL, wxID_ANY, wxString(SLIC3R_BUILD_ID) + " " + _(L("based on Slic3r"
|
|||
update_ui_from_settings(); // FIXME (?)
|
||||
}
|
||||
|
||||
MainFrame::~MainFrame() {}
|
||||
MainFrame::~MainFrame() = default;
|
||||
|
||||
void MainFrame::update_title()
|
||||
{
|
||||
wxString title = wxEmptyString;
|
||||
if (m_plater != nullptr)
|
||||
{
|
||||
// m_plater->get_project_filename() produces file name including path, but excluding extension.
|
||||
// Don't try to remove the extension, it would remove part of the file name after the last dot!
|
||||
wxString project = from_path(into_path(m_plater->get_project_filename()).filename());
|
||||
if (!project.empty())
|
||||
title += (project + " - ");
|
||||
}
|
||||
title += (wxString(SLIC3R_BUILD_ID) + " " + _(L("based on Slic3r")));
|
||||
|
||||
SetTitle(title);
|
||||
}
|
||||
|
||||
void MainFrame::init_tabpanel()
|
||||
{
|
||||
// wxNB_NOPAGETHEME: Disable Windows Vista theme for the Notebook background. The theme performance is terrible on Windows 10
|
||||
// with multiple high resolution displays connected.
|
||||
m_tabpanel = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME);
|
||||
#ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList
|
||||
m_tabpanel->SetFont(Slic3r::GUI::wxGetApp().normal_font());
|
||||
#endif
|
||||
|
||||
m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, [this](wxEvent&) {
|
||||
auto panel = m_tabpanel->GetCurrentPage();
|
||||
|
@ -210,6 +229,11 @@ void MainFrame::add_created_tab(Tab* panel)
|
|||
m_tabpanel->AddPage(panel, panel->title());
|
||||
}
|
||||
|
||||
bool MainFrame::can_start_new_project() const
|
||||
{
|
||||
return (m_plater != nullptr) && !m_plater->model().objects.empty();
|
||||
}
|
||||
|
||||
bool MainFrame::can_save() const
|
||||
{
|
||||
return (m_plater != nullptr) && !m_plater->model().objects.empty();
|
||||
|
@ -271,6 +295,11 @@ bool MainFrame::can_select() const
|
|||
return (m_plater != nullptr) && !m_plater->model().objects.empty();
|
||||
}
|
||||
|
||||
bool MainFrame::can_deselect() const
|
||||
{
|
||||
return (m_plater != nullptr) && !m_plater->is_selection_empty();
|
||||
}
|
||||
|
||||
bool MainFrame::can_delete() const
|
||||
{
|
||||
return (m_plater != nullptr) && !m_plater->is_selection_empty();
|
||||
|
@ -281,12 +310,15 @@ bool MainFrame::can_delete_all() const
|
|||
return (m_plater != nullptr) && !m_plater->model().objects.empty();
|
||||
}
|
||||
|
||||
bool MainFrame::can_reslice() const
|
||||
{
|
||||
return (m_plater != nullptr) && !m_plater->model().objects.empty();
|
||||
}
|
||||
|
||||
void MainFrame::on_dpi_changed(const wxRect &suggested_rect)
|
||||
{
|
||||
wxGetApp().update_fonts();
|
||||
this->SetFont(this->normal_font());
|
||||
// initialize default width_unit according to the width of the one symbol ("m") of the currently active font of this window.
|
||||
wxGetApp().set_em_unit(std::max<size_t>(10, GetTextExtent("m").x - 1));
|
||||
|
||||
/* Load default preset bitmaps before a tabpanel initialization,
|
||||
* but after filling of an em_unit value
|
||||
|
@ -345,13 +377,20 @@ void MainFrame::init_menubar()
|
|||
// File menu
|
||||
wxMenu* fileMenu = new wxMenu;
|
||||
{
|
||||
append_menu_item(fileMenu, wxID_ANY, _(L("&New Project")) + "\tCtrl+N", _(L("Start a new project")),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->new_project(); }, "", nullptr,
|
||||
[this](){return m_plater != nullptr && can_start_new_project(); }, this);
|
||||
append_menu_item(fileMenu, wxID_ANY, _(L("&Open Project")) + dots + "\tCtrl+O", _(L("Open a project file")),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, menu_icon("open"), nullptr,
|
||||
[this](){return m_plater != nullptr; }, this);
|
||||
append_menu_item(fileMenu, wxID_ANY, _(L("&Save Project")) + "\tCtrl+S", _(L("Save current project file")),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(into_path(m_plater->get_project_filename())); }, menu_icon("save"), nullptr,
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(into_path(m_plater->get_project_filename(".3mf"))); }, menu_icon("save"), nullptr,
|
||||
[this](){return m_plater != nullptr && can_save(); }, this);
|
||||
#ifdef __APPLE__
|
||||
append_menu_item(fileMenu, wxID_ANY, _(L("Save Project &as")) + dots + "\tCtrl+Shift+S", _(L("Save current project file as")),
|
||||
#else
|
||||
append_menu_item(fileMenu, wxID_ANY, _(L("Save Project &as")) + dots + "\tCtrl+Alt+S", _(L("Save current project file as")),
|
||||
#endif // __APPLE__
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(); }, menu_icon("save"), nullptr,
|
||||
[this](){return m_plater != nullptr && can_save(); }, this);
|
||||
|
||||
|
@ -417,8 +456,9 @@ void MainFrame::init_menubar()
|
|||
m_menu_item_repeat->Enable(false);
|
||||
fileMenu->AppendSeparator();
|
||||
#endif
|
||||
m_menu_item_reslice_now = append_menu_item(fileMenu, wxID_ANY, _(L("(Re)Slice &Now")) + "\tCtrl+R", _(L("Start new slicing process")),
|
||||
[this](wxCommandEvent&) { reslice_now(); }, menu_icon("re_slice"));
|
||||
m_menu_item_reslice_now = append_menu_item(fileMenu, wxID_ANY, _(L("(Re)Slice No&w")) + "\tCtrl+R", _(L("Start new slicing process")),
|
||||
[this](wxCommandEvent&) { reslice_now(); }, menu_icon("re_slice"), nullptr,
|
||||
[this](){return m_plater != nullptr && can_reslice(); }, this);
|
||||
fileMenu->AppendSeparator();
|
||||
append_menu_item(fileMenu, wxID_ANY, _(L("&Repair STL file")) + dots, _(L("Automatically repair an STL file")),
|
||||
[this](wxCommandEvent&) { repair_stl(); }, menu_icon("wrench"));
|
||||
|
@ -451,6 +491,9 @@ void MainFrame::init_menubar()
|
|||
append_menu_item(editMenu, wxID_ANY, _(L("&Select all")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "A",
|
||||
_(L("Selects all objects")), [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->select_all(); },
|
||||
"", nullptr, [this](){return can_select(); }, this);
|
||||
append_menu_item(editMenu, wxID_ANY, _(L("D&eselect all")) + sep + "Esc",
|
||||
_(L("Deselects all objects")), [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->deselect_all(); },
|
||||
"", nullptr, [this](){return can_deselect(); }, this);
|
||||
editMenu->AppendSeparator();
|
||||
append_menu_item(editMenu, wxID_ANY, _(L("&Delete selected")) + sep + hotkey_delete,
|
||||
_(L("Deletes the current selection")),[this](wxCommandEvent&) { m_plater->remove_selected(); },
|
||||
|
@ -460,7 +503,6 @@ void MainFrame::init_menubar()
|
|||
menu_icon("delete_all_menu"), nullptr, [this](){return can_delete_all(); }, this);
|
||||
|
||||
editMenu->AppendSeparator();
|
||||
|
||||
append_menu_item(editMenu, wxID_ANY, _(L("&Copy")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "C",
|
||||
_(L("Copy selection to clipboard")), [this](wxCommandEvent&) { m_plater->copy_selection_to_clipboard(); },
|
||||
menu_icon("copy_menu"), nullptr, [this](){return m_plater->can_copy(); }, this);
|
||||
|
@ -938,7 +980,8 @@ void MainFrame::load_config(const DynamicPrintConfig& config)
|
|||
if (! boost::algorithm::ends_with(opt_key, "_settings_id"))
|
||||
tab->get_config()->option(opt_key)->set(config.option(opt_key));
|
||||
}
|
||||
wxGetApp().load_current_presets();
|
||||
|
||||
wxGetApp().load_current_presets();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1031,6 +1074,5 @@ std::string MainFrame::get_dir_name(const wxString &full_name) const
|
|||
return boost::filesystem::path(full_name.wx_str()).parent_path().string();
|
||||
}
|
||||
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
|
|
|
@ -61,6 +61,7 @@ class MainFrame : public DPIFrame
|
|||
void on_presets_changed(SimpleEvent&);
|
||||
void on_value_changed(wxCommandEvent&);
|
||||
|
||||
bool can_start_new_project() const;
|
||||
bool can_save() const;
|
||||
bool can_export_model() const;
|
||||
bool can_export_supports() const;
|
||||
|
@ -68,8 +69,10 @@ class MainFrame : public DPIFrame
|
|||
bool can_slice() const;
|
||||
bool can_change_view() const;
|
||||
bool can_select() const;
|
||||
bool can_deselect() const;
|
||||
bool can_delete() const;
|
||||
bool can_delete_all() const;
|
||||
bool can_reslice() const;
|
||||
|
||||
// MenuBar items changeable in respect to printer technology
|
||||
enum MenuItems
|
||||
|
@ -90,6 +93,8 @@ public:
|
|||
|
||||
Plater* plater() { return m_plater; }
|
||||
|
||||
void update_title();
|
||||
|
||||
void init_tabpanel();
|
||||
void create_preset_tabs();
|
||||
void add_created_tab(Tab* panel);
|
||||
|
|
|
@ -276,7 +276,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
|
|||
// add sidetext if any
|
||||
if (option.sidetext != "") {
|
||||
auto sidetext = new wxStaticText( this->ctrl_parent(), wxID_ANY, _(option.sidetext), wxDefaultPosition,
|
||||
/*wxSize(sidetext_width*wxGetApp().em_unit(), -1)*/wxDefaultSize, wxALIGN_LEFT);
|
||||
wxSize(sidetext_width != -1 ? sidetext_width*wxGetApp().em_unit() : -1, -1) /*wxDefaultSize*/, wxALIGN_LEFT);
|
||||
sidetext->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||
sidetext->SetFont(wxGetApp().normal_font());
|
||||
sizer_tmp->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4);
|
||||
|
@ -410,18 +410,18 @@ void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config,
|
|||
auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(config.option("nozzle_diameter"));
|
||||
value = int(nozzle_diameter->values.size());
|
||||
}
|
||||
else if (m_opt_map.find(opt_key) != m_opt_map.end())
|
||||
else if (m_opt_map.find(opt_key) == m_opt_map.end() || opt_key == "bed_shape") {
|
||||
value = get_config_value(config, opt_key);
|
||||
change_opt_value(*m_config, opt_key, value);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto opt_id = m_opt_map.find(opt_key)->first;
|
||||
std::string opt_short_key = m_opt_map.at(opt_id).first;
|
||||
int opt_index = m_opt_map.at(opt_id).second;
|
||||
value = get_config_value(config, opt_short_key, opt_index);
|
||||
}
|
||||
else{
|
||||
value = get_config_value(config, opt_key);
|
||||
change_opt_value(*m_config, opt_key, value);
|
||||
return;
|
||||
}
|
||||
|
||||
set_value(opt_key, value);
|
||||
on_change_OG(opt_key, get_value(opt_key));
|
||||
|
@ -524,8 +524,7 @@ void ConfigOptionsGroup::msw_rescale()
|
|||
{
|
||||
auto label = dynamic_cast<wxStaticText*>(label_item->GetWindow());
|
||||
if (label != nullptr) {
|
||||
const int label_height = int(1.5f*label->GetFont().GetPixelSize().y + 0.5f);
|
||||
label->SetMinSize(wxSize(label_width*em, /*-1*/label_height));
|
||||
label->SetMinSize(wxSize(label_width*em, -1));
|
||||
}
|
||||
}
|
||||
else if (label_item->IsSizer()) // case when we have near_label_widget
|
||||
|
@ -535,8 +534,7 @@ void ConfigOptionsGroup::msw_rescale()
|
|||
{
|
||||
auto label = dynamic_cast<wxStaticText*>(l_item->GetWindow());
|
||||
if (label != nullptr) {
|
||||
const int label_height = int(1.5f*label->GetFont().GetPixelSize().y + 0.5f);
|
||||
label->SetMinSize(wxSize(label_width*em, /*-1*/label_height));
|
||||
label->SetMinSize(wxSize(label_width*em, -1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,9 +6,7 @@
|
|||
#include <string>
|
||||
#include <regex>
|
||||
#include <future>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
|
@ -262,29 +260,45 @@ wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(15 *
|
|||
if (preset_type == Slic3r::Preset::TYPE_FILAMENT)
|
||||
{
|
||||
Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &event) {
|
||||
if (extruder_idx < 0 || event.GetLogicalPosition(wxClientDC(this)).x > 24) {
|
||||
int shifl_Left = 0;
|
||||
float scale = m_em_unit*0.1f;
|
||||
#if defined(wxBITMAPCOMBOBOX_OWNERDRAWN_BASED)
|
||||
shifl_Left = int(scale * 4 + 0.5f); // IMAGE_SPACING_RIGHT = 4 for wxBitmapComboBox -> Space left of image
|
||||
#endif
|
||||
int icon_right_pos = int(scale * (24+4) + 0.5);
|
||||
int mouse_pos = event.GetLogicalPosition(wxClientDC(this)).x;
|
||||
// if (extruder_idx < 0 || event.GetLogicalPosition(wxClientDC(this)).x > 24) {
|
||||
if ( extruder_idx < 0 || mouse_pos < shifl_Left || mouse_pos > icon_right_pos ) {
|
||||
// Let the combo box process the mouse click.
|
||||
event.Skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Swallow the mouse click and open the color picker.
|
||||
|
||||
// get current color
|
||||
DynamicPrintConfig* cfg = wxGetApp().get_tab(Preset::TYPE_PRINTER)->get_config();
|
||||
auto colors = static_cast<ConfigOptionStrings*>(cfg->option("extruder_colour")->clone());
|
||||
wxColour clr(colors->values[extruder_idx]);
|
||||
if (!clr.IsOk())
|
||||
clr = wxTransparentColour;
|
||||
|
||||
auto data = new wxColourData();
|
||||
data->SetChooseFull(1);
|
||||
auto dialog = new wxColourDialog(/* wxGetApp().mainframe */this, data);
|
||||
dialog->CenterOnParent();
|
||||
if (dialog->ShowModal() == wxID_OK) {
|
||||
DynamicPrintConfig cfg = *wxGetApp().get_tab(Preset::TYPE_PRINTER)->get_config();
|
||||
data->SetColour(clr);
|
||||
|
||||
//FIXME this is too expensive to call full_config to get just the extruder color!
|
||||
auto colors = static_cast<ConfigOptionStrings*>(wxGetApp().preset_bundle->full_config().option("extruder_colour")->clone());
|
||||
auto dialog = new wxColourDialog(this, data);
|
||||
dialog->CenterOnParent();
|
||||
if (dialog->ShowModal() == wxID_OK)
|
||||
{
|
||||
colors->values[extruder_idx] = dialog->GetColourData().GetColour().GetAsString(wxC2S_HTML_SYNTAX);
|
||||
|
||||
cfg.set_key_value("extruder_colour", colors);
|
||||
DynamicPrintConfig cfg_new = *cfg;
|
||||
cfg_new.set_key_value("extruder_colour", colors);
|
||||
|
||||
wxGetApp().get_tab(Preset::TYPE_PRINTER)->load_config(cfg);
|
||||
wxGetApp().get_tab(Preset::TYPE_PRINTER)->load_config(cfg_new);
|
||||
wxGetApp().preset_bundle->update_platter_filament_ui(extruder_idx, this);
|
||||
wxGetApp().plater()->on_config_change(cfg);
|
||||
wxGetApp().plater()->on_config_change(cfg_new);
|
||||
}
|
||||
dialog->Destroy();
|
||||
});
|
||||
|
@ -308,7 +322,7 @@ wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(15 *
|
|||
/* In a case of a multi-material printing, for editing another Filament Preset
|
||||
* it's needed to select this preset for the "Filament settings" Tab
|
||||
*/
|
||||
if (preset_type == Preset::TYPE_FILAMENT && wxGetApp().extruders_cnt() > 1)
|
||||
if (preset_type == Preset::TYPE_FILAMENT && wxGetApp().extruders_edited_cnt() > 1)
|
||||
{
|
||||
const std::string& selected_preset = GetString(GetSelection()).ToUTF8().data();
|
||||
|
||||
|
@ -672,7 +686,8 @@ Sidebar::Sidebar(Plater *parent)
|
|||
auto combo_and_btn_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
combo_and_btn_sizer->Add(*combo, 1, wxEXPAND);
|
||||
if ((*combo)->edit_btn)
|
||||
combo_and_btn_sizer->Add((*combo)->edit_btn, 0, wxLEFT|wxRIGHT, int(0.3*wxGetApp().em_unit()));
|
||||
combo_and_btn_sizer->Add((*combo)->edit_btn, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT,
|
||||
int(0.3*wxGetApp().em_unit()));
|
||||
|
||||
auto *sizer_presets = this->p->sizer_presets;
|
||||
auto *sizer_filaments = this->p->sizer_filaments;
|
||||
|
@ -778,7 +793,8 @@ void Sidebar::init_filament_combo(PresetComboBox **combo, const int extr_idx) {
|
|||
|
||||
auto combo_and_btn_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
combo_and_btn_sizer->Add(*combo, 1, wxEXPAND);
|
||||
combo_and_btn_sizer->Add((*combo)->edit_btn, 0, wxLEFT | wxRIGHT, int(0.3*wxGetApp().em_unit()));
|
||||
combo_and_btn_sizer->Add((*combo)->edit_btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT,
|
||||
int(0.3*wxGetApp().em_unit()));
|
||||
|
||||
auto /***/sizer_filaments = this->p->sizer_filaments;
|
||||
sizer_filaments->Add(combo_and_btn_sizer, 1, wxEXPAND | wxBOTTOM, 1);
|
||||
|
@ -833,7 +849,7 @@ void Sidebar::update_presets(Preset::Type preset_type)
|
|||
|
||||
if (filament_cnt == 1) {
|
||||
// Single filament printer, synchronize the filament presets.
|
||||
const std::string &name = preset_bundle.filaments.get_selected_preset().name;
|
||||
const std::string &name = preset_bundle.filaments.get_selected_preset_name();
|
||||
preset_bundle.set_filament_preset(0, name);
|
||||
}
|
||||
|
||||
|
@ -1172,7 +1188,26 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME: when drag and drop is done on a .3mf or a .amf file we should clear the plater for consistence with the open project command
|
||||
// (the following call to plater->load_files() will load the config data, if present)
|
||||
|
||||
plater->load_files(paths);
|
||||
|
||||
// because right now the plater is not cleared, we set the project file (from the latest imported .3mf or .amf file)
|
||||
// only if not set yet
|
||||
if (plater->get_project_filename().empty())
|
||||
{
|
||||
for (std::vector<fs::path>::const_reverse_iterator it = paths.rbegin(); it != paths.rend(); ++it)
|
||||
{
|
||||
std::string filename = (*it).filename().string();
|
||||
if (boost::algorithm::iends_with(filename, ".3mf") || boost::algorithm::iends_with(filename, ".amf"))
|
||||
{
|
||||
plater->set_project_filename(from_path(*it));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1218,8 +1253,6 @@ struct Plater::priv
|
|||
GLToolbar view_toolbar;
|
||||
Preview *preview;
|
||||
|
||||
wxString project_filename;
|
||||
|
||||
BackgroundSlicingProcess background_process;
|
||||
|
||||
// A class to handle UI jobs like arranging and optimizing rotation.
|
||||
|
@ -1368,9 +1401,7 @@ struct Plater::priv
|
|||
static const std::regex pattern_3mf;
|
||||
static const std::regex pattern_zip_amf;
|
||||
static const std::regex pattern_any_amf;
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
static const std::regex pattern_prusa;
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
|
||||
priv(Plater *q, MainFrame *main_frame);
|
||||
|
||||
|
@ -1396,6 +1427,7 @@ struct Plater::priv
|
|||
void object_list_changed();
|
||||
|
||||
void select_all();
|
||||
void deselect_all();
|
||||
void remove(size_t obj_idx);
|
||||
void delete_object_from_model(size_t obj_idx);
|
||||
void reset();
|
||||
|
@ -1404,7 +1436,8 @@ struct Plater::priv
|
|||
void sla_optimize_rotation();
|
||||
void split_object();
|
||||
void split_volume();
|
||||
bool background_processing_enabled() const { return this->get_config("background_processing") == "1"; }
|
||||
void scale_selection_to_fit_print_volume();
|
||||
bool background_processing_enabled() const { return this->get_config("background_processing") == "1"; }
|
||||
void update_print_volume_state();
|
||||
void schedule_background_process();
|
||||
// Update background processing thread from the current config and Model.
|
||||
|
@ -1476,6 +1509,11 @@ struct Plater::priv
|
|||
|
||||
void msw_rescale_object_menu();
|
||||
|
||||
// returns the path to project file with the given extension (none if extension == wxEmptyString)
|
||||
// extension should contain the leading dot, i.e.: ".3mf"
|
||||
wxString get_project_filename(const wxString& extension = wxEmptyString) const;
|
||||
void set_project_filename(const wxString& filename);
|
||||
|
||||
private:
|
||||
bool init_object_menu();
|
||||
bool init_common_menu(wxMenu* menu, const bool is_part = false);
|
||||
|
@ -1489,15 +1527,16 @@ private:
|
|||
|
||||
void update_fff_scene();
|
||||
void update_sla_scene();
|
||||
|
||||
// path to project file stored with no extension
|
||||
wxString m_project_filename;
|
||||
};
|
||||
|
||||
const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf|prusa)", std::regex::icase);
|
||||
const std::regex Plater::priv::pattern_3mf(".*3mf", std::regex::icase);
|
||||
const std::regex Plater::priv::pattern_zip_amf(".*[.]zip[.]amf", std::regex::icase);
|
||||
const std::regex Plater::priv::pattern_any_amf(".*[.](amf|amf[.]xml|zip[.]amf)", std::regex::icase);
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
const std::regex Plater::priv::pattern_prusa(".*prusa", std::regex::icase);
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
|
||||
Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
: q(q)
|
||||
|
@ -1515,12 +1554,12 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
}))
|
||||
, sidebar(new Sidebar(q))
|
||||
, delayed_scene_refresh(false)
|
||||
, project_filename(wxEmptyString)
|
||||
#if ENABLE_SVG_ICONS
|
||||
, view_toolbar(GLToolbar::Radio, "View")
|
||||
#else
|
||||
, view_toolbar(GLToolbar::Radio)
|
||||
#endif // ENABLE_SVG_ICONS
|
||||
, m_project_filename(wxEmptyString)
|
||||
{
|
||||
this->q->SetFont(Slic3r::GUI::wxGetApp().normal_font());
|
||||
|
||||
|
@ -1610,6 +1649,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); });
|
||||
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&) { set_bed_shape(config->option<ConfigOptionPoints>("bed_shape")->values); });
|
||||
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); });
|
||||
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, [this](wxKeyEvent& evt) { preview->move_double_slider(evt); });
|
||||
|
||||
q->Bind(EVT_SLICING_COMPLETED, &priv::on_slicing_completed, this);
|
||||
q->Bind(EVT_PROCESS_COMPLETED, &priv::on_process_completed, this);
|
||||
|
@ -1750,11 +1790,10 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
const bool type_3mf = std::regex_match(path.string(), pattern_3mf);
|
||||
const bool type_zip_amf = !type_3mf && std::regex_match(path.string(), pattern_zip_amf);
|
||||
const bool type_any_amf = !type_3mf && std::regex_match(path.string(), pattern_any_amf);
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
const bool type_prusa = std::regex_match(path.string(), pattern_prusa);
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
|
||||
Slic3r::Model model;
|
||||
bool is_project_file = type_prusa;
|
||||
try {
|
||||
if (type_3mf || type_zip_amf) {
|
||||
DynamicPrintConfig config;
|
||||
|
@ -1764,6 +1803,22 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
if (load_config && !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);
|
||||
|
||||
// We can't to load SLA project if there is at least one multi-part object on the bed
|
||||
if (printer_technology == ptSLA)
|
||||
{
|
||||
const ModelObjectPtrs& objects = q->model().objects;
|
||||
for (auto object : objects)
|
||||
if (object->volumes.size() > 1)
|
||||
{
|
||||
Slic3r::GUI::show_info(nullptr,
|
||||
_(L("You can't to load SLA project if there is at least one multi-part object on the bed")) + "\n\n" +
|
||||
_(L("Please check your object list before preset changing.")),
|
||||
_(L("Attention!")));
|
||||
return obj_idxs;
|
||||
}
|
||||
}
|
||||
|
||||
config.apply(printer_technology == ptFFF ?
|
||||
static_cast<const ConfigBase&>(FullPrintConfig::defaults()) :
|
||||
static_cast<const ConfigBase&>(SLAFullPrintConfig::defaults()));
|
||||
|
@ -1778,6 +1833,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
Preset::normalize(config);
|
||||
wxGetApp().preset_bundle->load_config_model(filename.string(), std::move(config));
|
||||
wxGetApp().load_current_presets();
|
||||
is_project_file = true;
|
||||
}
|
||||
wxGetApp().app_config->update_config_dir(path.parent_path().string());
|
||||
}
|
||||
|
@ -1797,9 +1853,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
{
|
||||
// The model should now be initialized
|
||||
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
if (!type_3mf && !type_any_amf && !type_prusa) {
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
if (! is_project_file) {
|
||||
if (model.looks_like_multipart_object()) {
|
||||
wxMessageDialog dlg(q, _(L(
|
||||
"This file contains several objects positioned at multiple heights. "
|
||||
|
@ -1810,7 +1864,6 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
model.convert_multipart_object(nozzle_dmrs->values.size());
|
||||
}
|
||||
}
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
}
|
||||
else if ((wxGetApp().get_mode() == comSimple) && (type_3mf || type_any_amf))
|
||||
{
|
||||
|
@ -1867,22 +1920,11 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
return obj_idxs;
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
|
||||
#if !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
if (type_3mf || type_any_amf) {
|
||||
#endif // !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
for (ModelObject* model_object : model.objects) {
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
model_object->center_around_origin(false);
|
||||
#else
|
||||
model_object->center_around_origin();
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
model_object->ensure_on_bed();
|
||||
}
|
||||
#if !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
}
|
||||
#endif // !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
|
||||
// check multi-part object adding for the SLA-printing
|
||||
if (printer_technology == ptSLA)
|
||||
|
@ -2062,13 +2104,20 @@ wxString Plater::priv::get_export_file(GUI::FileType file_type)
|
|||
int obj_idx = selection.get_object_idx();
|
||||
|
||||
fs::path output_file;
|
||||
// first try to get the file name from the current selection
|
||||
if ((0 <= obj_idx) && (obj_idx < (int)this->model.objects.size()))
|
||||
output_file = this->model.objects[obj_idx]->get_export_filename();
|
||||
if (file_type == FT_3MF)
|
||||
// for 3mf take the path from the project filename, if any
|
||||
output_file = into_path(get_project_filename(".3mf"));
|
||||
|
||||
if (output_file.empty())
|
||||
// Find the file name of the first printable object.
|
||||
output_file = this->model.propose_export_file_name_and_path();
|
||||
{
|
||||
// first try to get the file name from the current selection
|
||||
if ((0 <= obj_idx) && (obj_idx < (int)this->model.objects.size()))
|
||||
output_file = this->model.objects[obj_idx]->get_export_filename();
|
||||
|
||||
if (output_file.empty())
|
||||
// Find the file name of the first printable object.
|
||||
output_file = this->model.propose_export_file_name_and_path();
|
||||
}
|
||||
|
||||
wxString dlg_title;
|
||||
switch (file_type) {
|
||||
|
@ -2165,6 +2214,11 @@ void Plater::priv::select_all()
|
|||
this->sidebar->obj_list()->update_selections();
|
||||
}
|
||||
|
||||
void Plater::priv::deselect_all()
|
||||
{
|
||||
view3D->deselect_all();
|
||||
}
|
||||
|
||||
void Plater::priv::remove(size_t obj_idx)
|
||||
{
|
||||
// Prevent toolpaths preview from rendering while we modify the Print object
|
||||
|
@ -2174,10 +2228,9 @@ void Plater::priv::remove(size_t obj_idx)
|
|||
view3D->enable_layers_editing(false);
|
||||
|
||||
model.delete_object(obj_idx);
|
||||
// Delete object from Sidebar list
|
||||
sidebar->obj_list()->delete_object_from_list(obj_idx);
|
||||
|
||||
update();
|
||||
// Delete object from Sidebar list. Do it after update, so that the GLScene selection is updated with the modified model.
|
||||
sidebar->obj_list()->delete_object_from_list(obj_idx);
|
||||
object_list_changed();
|
||||
}
|
||||
|
||||
|
@ -2191,7 +2244,7 @@ void Plater::priv::delete_object_from_model(size_t obj_idx)
|
|||
|
||||
void Plater::priv::reset()
|
||||
{
|
||||
project_filename.Clear();
|
||||
set_project_filename(wxEmptyString);
|
||||
|
||||
// Prevent toolpaths preview from rendering while we modify the Print object
|
||||
preview->set_enabled(false);
|
||||
|
@ -2202,10 +2255,9 @@ void Plater::priv::reset()
|
|||
// Stop and reset the Print content.
|
||||
this->background_process.reset();
|
||||
model.clear_objects();
|
||||
|
||||
// Delete all objects from list on c++ side
|
||||
sidebar->obj_list()->delete_all_objects_from_list();
|
||||
update();
|
||||
// Delete object from Sidebar list. Do it after update, so that the GLScene selection is updated with the modified model.
|
||||
sidebar->obj_list()->delete_all_objects_from_list();
|
||||
object_list_changed();
|
||||
|
||||
// The hiding of the slicing results, if shown, is not taken care by the background process, so we do it here
|
||||
|
@ -2419,6 +2471,11 @@ void Plater::priv::split_volume()
|
|||
wxGetApp().obj_list()->split();
|
||||
}
|
||||
|
||||
void Plater::priv::scale_selection_to_fit_print_volume()
|
||||
{
|
||||
this->view3D->get_canvas3d()->get_selection().scale_to_fit_print_volume(*config);
|
||||
}
|
||||
|
||||
void Plater::priv::schedule_background_process()
|
||||
{
|
||||
delayed_error_message.clear();
|
||||
|
@ -3016,6 +3073,31 @@ void Plater::priv::msw_rescale_object_menu()
|
|||
msw_rescale_menu(dynamic_cast<wxMenu*>(menu));
|
||||
}
|
||||
|
||||
wxString Plater::priv::get_project_filename(const wxString& extension) const
|
||||
{
|
||||
return m_project_filename.empty() ? "" : m_project_filename + extension;
|
||||
}
|
||||
|
||||
void Plater::priv::set_project_filename(const wxString& filename)
|
||||
{
|
||||
boost::filesystem::path full_path = into_path(filename);
|
||||
boost::filesystem::path ext = full_path.extension();
|
||||
if (boost::iequals(ext.string(), ".amf")) {
|
||||
// Remove the first extension.
|
||||
full_path.replace_extension("");
|
||||
// It may be ".zip.amf".
|
||||
if (boost::iequals(full_path.extension().string(), ".zip"))
|
||||
// Remove the 2nd extension.
|
||||
full_path.replace_extension("");
|
||||
} else {
|
||||
// Remove just one extension.
|
||||
full_path.replace_extension("");
|
||||
}
|
||||
|
||||
m_project_filename = from_path(full_path);
|
||||
wxGetApp().mainframe->update_title();
|
||||
}
|
||||
|
||||
bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/)
|
||||
{
|
||||
if (is_part) {
|
||||
|
@ -3056,6 +3138,8 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
|
|||
|
||||
sidebar->obj_list()->append_menu_item_fix_through_netfabb(menu);
|
||||
|
||||
sidebar->obj_list()->append_menu_item_scale_selection_to_fit_print_volume(menu);
|
||||
|
||||
wxMenu* mirror_menu = new wxMenu();
|
||||
if (mirror_menu == nullptr)
|
||||
return false;
|
||||
|
@ -3325,6 +3409,11 @@ Print& Plater::fff_print() { return p->fff_print; }
|
|||
const SLAPrint& Plater::sla_print() const { return p->sla_print; }
|
||||
SLAPrint& Plater::sla_print() { return p->sla_print; }
|
||||
|
||||
void Plater::new_project()
|
||||
{
|
||||
wxPostEvent(p->view3D->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL));
|
||||
}
|
||||
|
||||
void Plater::load_project()
|
||||
{
|
||||
wxString input_file;
|
||||
|
@ -3334,7 +3423,7 @@ void Plater::load_project()
|
|||
return;
|
||||
|
||||
p->reset();
|
||||
p->project_filename = input_file;
|
||||
p->set_project_filename(input_file);
|
||||
|
||||
std::vector<fs::path> input_paths;
|
||||
input_paths.push_back(into_path(input_file));
|
||||
|
@ -3404,6 +3493,7 @@ void Plater::select_view(const std::string& direction) { p->select_view(directio
|
|||
void Plater::select_view_3D(const std::string& name) { p->select_view_3D(name); }
|
||||
|
||||
void Plater::select_all() { p->select_all(); }
|
||||
void Plater::deselect_all() { p->deselect_all(); }
|
||||
|
||||
void Plater::remove(size_t obj_idx) { p->remove(obj_idx); }
|
||||
void Plater::reset() { p->reset(); }
|
||||
|
@ -3464,14 +3554,14 @@ void Plater::decrease_instances(size_t num)
|
|||
if (model_object->instances.size() > num) {
|
||||
for (size_t i = 0; i < num; ++ i)
|
||||
model_object->delete_last_instance();
|
||||
p->update();
|
||||
// Delete object from Sidebar list. Do it after update, so that the GLScene selection is updated with the modified model.
|
||||
sidebar().obj_list()->decrease_object_instances(obj_idx, num);
|
||||
}
|
||||
else {
|
||||
remove(obj_idx);
|
||||
}
|
||||
|
||||
p->update();
|
||||
|
||||
if (!model_object->instances.empty())
|
||||
p->get_selection().add_instance(obj_idx, (int)model_object->instances.size() - 1);
|
||||
|
||||
|
@ -3504,6 +3594,11 @@ bool Plater::is_selection_empty() const
|
|||
return p->get_selection().is_empty() || p->get_selection().is_wipe_tower();
|
||||
}
|
||||
|
||||
void Plater::scale_selection_to_fit_print_volume()
|
||||
{
|
||||
p->scale_selection_to_fit_print_volume();
|
||||
}
|
||||
|
||||
void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper, bool keep_lower, bool rotate_lower)
|
||||
{
|
||||
wxCHECK_RET(obj_idx < p->model.objects.size(), "obj_idx out of bounds");
|
||||
|
@ -3536,8 +3631,9 @@ void Plater::export_gcode()
|
|||
unsigned int state = this->p->update_restart_background_process(false, false);
|
||||
if (state & priv::UPDATE_BACKGROUND_PROCESS_INVALID)
|
||||
return;
|
||||
default_output_file = this->p->background_process.current_print()->output_filepath("");
|
||||
} catch (const std::exception &ex) {
|
||||
default_output_file = this->p->background_process.output_filepath_for_project(into_path(get_project_filename(".3mf")));
|
||||
}
|
||||
catch (const std::exception &ex) {
|
||||
show_error(this, ex.what());
|
||||
return;
|
||||
}
|
||||
|
@ -3590,7 +3686,7 @@ void Plater::export_stl(bool extended, bool selection_only)
|
|||
else
|
||||
{
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
mesh = model_object->volumes[volume->volume_idx()]->mesh;
|
||||
mesh = model_object->volumes[volume->volume_idx()]->mesh();
|
||||
mesh.transform(volume->get_volume_transformation().get_matrix());
|
||||
mesh.translate(-model_object->origin_translation.cast<float>());
|
||||
}
|
||||
|
@ -3702,7 +3798,9 @@ void Plater::export_3mf(const boost::filesystem::path& output_path)
|
|||
if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr)) {
|
||||
// Success
|
||||
p->statusbar()->set_status_text(wxString::Format(_(L("3MF file exported to %s")), path));
|
||||
} else {
|
||||
p->set_project_filename(path);
|
||||
}
|
||||
else {
|
||||
// Failure
|
||||
p->statusbar()->set_status_text(wxString::Format(_(L("Error exporting 3MF file %s")), path));
|
||||
}
|
||||
|
@ -3781,8 +3879,9 @@ void Plater::send_gcode()
|
|||
unsigned int state = this->p->update_restart_background_process(false, false);
|
||||
if (state & priv::UPDATE_BACKGROUND_PROCESS_INVALID)
|
||||
return;
|
||||
default_output_file = this->p->background_process.current_print()->output_filepath("");
|
||||
} catch (const std::exception &ex) {
|
||||
default_output_file = this->p->background_process.output_filepath_for_project(into_path(get_project_filename(".3mf")));
|
||||
}
|
||||
catch (const std::exception &ex) {
|
||||
show_error(this, ex.what());
|
||||
return;
|
||||
}
|
||||
|
@ -3896,9 +3995,14 @@ void Plater::on_activate()
|
|||
}
|
||||
}
|
||||
|
||||
const wxString& Plater::get_project_filename() const
|
||||
wxString Plater::get_project_filename(const wxString& extension) const
|
||||
{
|
||||
return p->project_filename;
|
||||
return p->get_project_filename(extension);
|
||||
}
|
||||
|
||||
void Plater::set_project_filename(const wxString& filename)
|
||||
{
|
||||
return p->set_project_filename(filename);
|
||||
}
|
||||
|
||||
bool Plater::is_export_gcode_scheduled() const
|
||||
|
|
|
@ -134,6 +134,7 @@ public:
|
|||
const SLAPrint& sla_print() const;
|
||||
SLAPrint& sla_print();
|
||||
|
||||
void new_project();
|
||||
void load_project();
|
||||
void add_model();
|
||||
void extract_config_from_project();
|
||||
|
@ -152,6 +153,7 @@ public:
|
|||
void update_ui_from_settings();
|
||||
|
||||
void select_all();
|
||||
void deselect_all();
|
||||
void remove(size_t obj_idx);
|
||||
void reset();
|
||||
void reset_with_confirm();
|
||||
|
@ -161,6 +163,7 @@ public:
|
|||
void decrease_instances(size_t num = 1);
|
||||
void set_number_of_copies(/*size_t num*/);
|
||||
bool is_selection_empty() const;
|
||||
void scale_selection_to_fit_print_volume();
|
||||
|
||||
void cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false);
|
||||
|
||||
|
@ -183,7 +186,9 @@ public:
|
|||
|
||||
void update_object_menu();
|
||||
|
||||
const wxString& get_project_filename() const;
|
||||
wxString get_project_filename(const wxString& extension = wxEmptyString) const;
|
||||
void set_project_filename(const wxString& filename);
|
||||
|
||||
bool is_export_gcode_scheduled() const;
|
||||
|
||||
int get_selected_object_idx();
|
||||
|
|
|
@ -10,6 +10,9 @@ PreferencesDialog::PreferencesDialog(wxWindow* parent) :
|
|||
DPIDialog(parent, wxID_ANY, _(L("Preferences")), wxDefaultPosition,
|
||||
wxDefaultSize, wxDEFAULT_DIALOG_STYLE)
|
||||
{
|
||||
#ifdef __WXOSX__
|
||||
isOSX = true;
|
||||
#endif
|
||||
build();
|
||||
}
|
||||
|
||||
|
@ -18,8 +21,13 @@ void PreferencesDialog::build()
|
|||
auto app_config = get_app_config();
|
||||
m_optgroup = std::make_shared<ConfigOptionsGroup>(this, _(L("General")));
|
||||
m_optgroup->label_width = 40;
|
||||
m_optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
|
||||
m_optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
|
||||
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
|
||||
|
||||
if (opt_key == "use_custom_toolbar_size") {
|
||||
m_icon_size_sizer->ShowItems(boost::any_cast<bool>(value));
|
||||
this->layout();
|
||||
}
|
||||
};
|
||||
|
||||
// TODO
|
||||
|
@ -109,6 +117,16 @@ void PreferencesDialog::build()
|
|||
m_optgroup->append_single_option_line(option);
|
||||
#endif
|
||||
|
||||
def.label = L("Use custom size for toolbar icons");
|
||||
def.type = coBool;
|
||||
def.tooltip = L("If enabled, you can change size of toolbar icons manually.");
|
||||
def.set_default_value(new ConfigOptionBool{ app_config->get("use_custom_toolbar_size") == "1" });
|
||||
option = Option (def,"use_custom_toolbar_size");
|
||||
m_optgroup->append_single_option_line(option);
|
||||
|
||||
create_icon_size_slider();
|
||||
m_icon_size_sizer->ShowItems(app_config->get("use_custom_toolbar_size") == "1");
|
||||
|
||||
auto sizer = new wxBoxSizer(wxVERTICAL);
|
||||
sizer->Add(m_optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||
|
||||
|
@ -136,7 +154,6 @@ void PreferencesDialog::accept()
|
|||
}
|
||||
|
||||
EndModal(wxID_OK);
|
||||
Close(); // needed on Linux
|
||||
|
||||
// Nothify the UI to update itself from the ini file.
|
||||
wxGetApp().update_ui_from_settings();
|
||||
|
@ -146,17 +163,79 @@ void PreferencesDialog::on_dpi_changed(const wxRect &suggested_rect)
|
|||
{
|
||||
m_optgroup->msw_rescale();
|
||||
|
||||
msw_buttons_rescale(this, em_unit(), { wxID_OK, wxID_CANCEL });
|
||||
|
||||
layout();
|
||||
}
|
||||
|
||||
void PreferencesDialog::layout()
|
||||
{
|
||||
const int em = em_unit();
|
||||
|
||||
msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL });
|
||||
|
||||
const wxSize& size = wxSize(47 * em, 28 * em);
|
||||
|
||||
SetMinSize(size);
|
||||
SetMinSize(wxSize(47 * em, 28 * em));
|
||||
Fit();
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void PreferencesDialog::create_icon_size_slider()
|
||||
{
|
||||
const auto app_config = get_app_config();
|
||||
|
||||
const int em = em_unit();
|
||||
|
||||
m_icon_size_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
wxWindow* parent = m_optgroup->ctrl_parent();
|
||||
|
||||
if (isOSX)
|
||||
// For correct rendering of the slider and value label under OSX
|
||||
// we should use system default background
|
||||
parent->SetBackgroundStyle(wxBG_STYLE_ERASE);
|
||||
|
||||
auto label = new wxStaticText(parent, wxID_ANY, _(L("Icon size in a respect to the default size")) + " (%) :");
|
||||
|
||||
m_icon_size_sizer->Add(label, 0, wxALIGN_CENTER_VERTICAL| wxRIGHT | (isOSX ? 0 : wxLEFT), em);
|
||||
|
||||
const int def_val = atoi(app_config->get("custom_toolbar_size").c_str());
|
||||
|
||||
long style = wxSL_HORIZONTAL;
|
||||
if (!isOSX)
|
||||
style |= wxSL_LABELS | wxSL_AUTOTICKS;
|
||||
|
||||
auto slider = new wxSlider(parent, wxID_ANY, def_val, 30, 100,
|
||||
wxDefaultPosition, wxDefaultSize, style);
|
||||
|
||||
slider->SetTickFreq(10);
|
||||
slider->SetPageSize(10);
|
||||
slider->SetToolTip(_(L("Select toolbar icon size in respect to the default one.")));
|
||||
|
||||
m_icon_size_sizer->Add(slider, 1, wxEXPAND);
|
||||
|
||||
wxStaticText* val_label{ nullptr };
|
||||
if (isOSX) {
|
||||
val_label = new wxStaticText(parent, wxID_ANY, wxString::Format("%d", def_val));
|
||||
m_icon_size_sizer->Add(val_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, em);
|
||||
}
|
||||
|
||||
slider->Bind(wxEVT_SLIDER, ([this, slider, val_label](wxCommandEvent e) {
|
||||
auto val = slider->GetValue();
|
||||
m_values["custom_toolbar_size"] = (boost::format("%d") % val).str();
|
||||
|
||||
if (val_label)
|
||||
val_label->SetLabelText(wxString::Format("%d", val));
|
||||
}), slider->GetId());
|
||||
|
||||
for (wxWindow* win : std::vector<wxWindow*>{ slider, label, val_label }) {
|
||||
if (!win) continue;
|
||||
win->SetFont(wxGetApp().normal_font());
|
||||
|
||||
if (isOSX) continue; // under OSX we use wxBG_STYLE_ERASE
|
||||
win->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||
}
|
||||
|
||||
m_optgroup->sizer->Add(m_icon_size_sizer, 0, wxEXPAND | wxALL, em);
|
||||
}
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
|
@ -16,6 +16,8 @@ class PreferencesDialog : public DPIDialog
|
|||
{
|
||||
std::map<std::string, std::string> m_values;
|
||||
std::shared_ptr<ConfigOptionsGroup> m_optgroup;
|
||||
wxSizer* m_icon_size_sizer;
|
||||
bool isOSX {false};
|
||||
public:
|
||||
PreferencesDialog(wxWindow* parent);
|
||||
~PreferencesDialog() {}
|
||||
|
@ -25,6 +27,8 @@ public:
|
|||
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect &suggested_rect) override;
|
||||
void layout();
|
||||
void create_icon_size_slider();
|
||||
};
|
||||
|
||||
} // GUI
|
||||
|
|
|
@ -127,11 +127,16 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem
|
|||
res.config_version = std::move(*config_version);
|
||||
}
|
||||
|
||||
auto config_update_url = vendor_section.find("config_update_url");
|
||||
const auto config_update_url = vendor_section.find("config_update_url");
|
||||
if (config_update_url != vendor_section.not_found()) {
|
||||
res.config_update_url = config_update_url->second.data();
|
||||
}
|
||||
|
||||
const auto changelog_url = vendor_section.find("changelog_url");
|
||||
if (changelog_url != vendor_section.not_found()) {
|
||||
res.changelog_url = changelog_url->second.data();
|
||||
}
|
||||
|
||||
if (! load_all) {
|
||||
return res;
|
||||
}
|
||||
|
@ -815,6 +820,9 @@ void PresetCollection::load_bitmap_add(wxWindow *window, const std::string &file
|
|||
|
||||
const Preset* PresetCollection::get_selected_preset_parent() const
|
||||
{
|
||||
if (this->get_selected_idx() == -1)
|
||||
// This preset collection has no preset activated yet. Only the get_edited_preset() is valid.
|
||||
return nullptr;
|
||||
const std::string &inherits = this->get_edited_preset().inherits();
|
||||
if (inherits.empty())
|
||||
return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr;
|
||||
|
|
|
@ -44,6 +44,7 @@ public:
|
|||
std::string id;
|
||||
Semver config_version;
|
||||
std::string config_update_url;
|
||||
std::string changelog_url;
|
||||
|
||||
struct PrinterVariant {
|
||||
PrinterVariant() {}
|
||||
|
|
|
@ -78,6 +78,7 @@ PresetBundle::PresetBundle() :
|
|||
this->sla_materials.default_preset().inherits();
|
||||
|
||||
this->sla_prints.default_preset().config.optptr("sla_print_settings_id", true);
|
||||
this->sla_prints.default_preset().config.opt_string("output_filename_format", true) = "[input_filename_base].sl1";
|
||||
this->sla_prints.default_preset().compatible_printers_condition();
|
||||
this->sla_prints.default_preset().inherits();
|
||||
|
||||
|
@ -344,40 +345,29 @@ void PresetBundle::load_selections(const AppConfig &config, const std::string &p
|
|||
|
||||
const Preset *initial_printer = printers.find_preset(initial_printer_profile_name);
|
||||
const Preset *preferred_printer = printers.find_by_model_id(preferred_model_id);
|
||||
printers.select_preset_by_name(
|
||||
(preferred_printer != nullptr && (initial_printer == nullptr || !initial_printer->is_visible)) ?
|
||||
preferred_printer->name :
|
||||
initial_printer_profile_name,
|
||||
true);
|
||||
|
||||
if (preferred_printer != nullptr && (initial_printer == nullptr || !initial_printer->is_visible)) {
|
||||
printers.select_preset_by_name(preferred_printer->name, true);
|
||||
} else {
|
||||
printers.select_preset_by_name(initial_printer_profile_name, true);
|
||||
}
|
||||
// Selects the profile, leaves it to -1 if the initial profile name is empty or if it was not found.
|
||||
prints.select_preset_by_name_strict(initial_print_profile_name);
|
||||
filaments.select_preset_by_name_strict(initial_filament_profile_name);
|
||||
sla_prints.select_preset_by_name_strict(initial_sla_print_profile_name);
|
||||
sla_materials.select_preset_by_name_strict(initial_sla_material_profile_name);
|
||||
|
||||
PrinterTechnology printer_technology = printers.get_selected_preset().printer_technology();
|
||||
if (printer_technology == ptFFF) {
|
||||
prints.select_preset_by_name_strict(initial_print_profile_name);
|
||||
filaments.select_preset_by_name_strict(initial_filament_profile_name);
|
||||
sla_prints.select_preset_by_name(initial_sla_material_profile_name, true);
|
||||
sla_materials.select_preset_by_name(initial_sla_material_profile_name, true);
|
||||
} else {
|
||||
prints.select_preset_by_name(initial_print_profile_name, true);
|
||||
filaments.select_preset_by_name(initial_filament_profile_name, true);
|
||||
sla_prints.select_preset_by_name_strict(initial_sla_material_profile_name);
|
||||
sla_materials.select_preset_by_name_strict(initial_sla_material_profile_name);
|
||||
}
|
||||
|
||||
if (printers.get_selected_preset().printer_technology() == ptFFF) {
|
||||
// Load the names of the other filament profiles selected for a multi-material printer.
|
||||
auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(printers.get_selected_preset().config.option("nozzle_diameter"));
|
||||
size_t num_extruders = nozzle_diameter->values.size();
|
||||
this->filament_presets = { initial_filament_profile_name };
|
||||
for (unsigned int i = 1; i < (unsigned int)num_extruders; ++i) {
|
||||
char name[64];
|
||||
sprintf(name, "filament_%d", i);
|
||||
if (!config.has("presets", name))
|
||||
break;
|
||||
this->filament_presets.emplace_back(remove_ini_suffix(config.get("presets", name)));
|
||||
}
|
||||
// Do not define the missing filaments, so that the update_compatible() will use the preferred filaments.
|
||||
this->filament_presets.resize(num_extruders, "");
|
||||
// Load the names of the other filament profiles selected for a multi-material printer.
|
||||
// Load it even if the current printer technology is SLA.
|
||||
// The possibly excessive filament names will be later removed with this->update_multi_material_filament_presets()
|
||||
// once the FFF technology gets selected.
|
||||
this->filament_presets = { filaments.get_selected_preset_name() };
|
||||
for (unsigned int i = 1; i < 1000; ++ i) {
|
||||
char name[64];
|
||||
sprintf(name, "filament_%d", i);
|
||||
if (! config.has("presets", name))
|
||||
break;
|
||||
this->filament_presets.emplace_back(remove_ini_suffix(config.get("presets", name)));
|
||||
}
|
||||
|
||||
// Update visibility of presets based on their compatibility with the active printer.
|
||||
|
@ -536,9 +526,9 @@ DynamicPrintConfig PresetBundle::full_fff_config() const
|
|||
opt->value = boost::algorithm::clamp<int>(opt->value, 0, int(num_extruders));
|
||||
}
|
||||
|
||||
out.option<ConfigOptionString >("print_settings_id", true)->value = this->prints.get_selected_preset().name;
|
||||
out.option<ConfigOptionString >("print_settings_id", true)->value = this->prints.get_selected_preset_name();
|
||||
out.option<ConfigOptionStrings>("filament_settings_id", true)->values = this->filament_presets;
|
||||
out.option<ConfigOptionString >("printer_settings_id", true)->value = this->printers.get_selected_preset().name;
|
||||
out.option<ConfigOptionString >("printer_settings_id", true)->value = this->printers.get_selected_preset_name();
|
||||
|
||||
// Serialize the collected "compatible_printers_condition" and "inherits" fields.
|
||||
// There will be 1 + num_exturders fields for "inherits" and 2 + num_extruders for "compatible_printers_condition" stored.
|
||||
|
@ -587,9 +577,9 @@ DynamicPrintConfig PresetBundle::full_sla_config() const
|
|||
out.erase("compatible_printers_condition");
|
||||
out.erase("inherits");
|
||||
|
||||
out.option<ConfigOptionString >("sla_print_settings_id", true)->value = this->sla_prints.get_selected_preset().name;
|
||||
out.option<ConfigOptionString >("sla_material_settings_id", true)->value = this->sla_materials.get_selected_preset().name;
|
||||
out.option<ConfigOptionString >("printer_settings_id", true)->value = this->printers.get_selected_preset().name;
|
||||
out.option<ConfigOptionString >("sla_print_settings_id", true)->value = this->sla_prints.get_selected_preset_name();
|
||||
out.option<ConfigOptionString >("sla_material_settings_id", true)->value = this->sla_materials.get_selected_preset_name();
|
||||
out.option<ConfigOptionString >("printer_settings_id", true)->value = this->printers.get_selected_preset_name();
|
||||
|
||||
// Serialize the collected "compatible_printers_condition" and "inherits" fields.
|
||||
// There will be 1 + num_exturders fields for "inherits" and 2 + num_extruders for "compatible_printers_condition" stored.
|
||||
|
@ -791,7 +781,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
|
|||
if (i == 0)
|
||||
suffix[0] = 0;
|
||||
else
|
||||
sprintf(suffix, "%d", i);
|
||||
sprintf(suffix, "%d", (int)i);
|
||||
std::string new_name = name + suffix;
|
||||
loaded = &this->filaments.load_preset(this->filaments.path_from_name(new_name),
|
||||
new_name, std::move(cfg), i == 0);
|
||||
|
@ -847,7 +837,7 @@ void PresetBundle::load_config_file_config_bundle(const std::string &path, const
|
|||
return preset_name_dst;
|
||||
// Try to generate another name.
|
||||
char buf[64];
|
||||
sprintf(buf, " (%d)", i);
|
||||
sprintf(buf, " (%d)", (int)i);
|
||||
preset_name_dst = preset_name_src + buf + bundle_name;
|
||||
}
|
||||
}
|
||||
|
@ -864,11 +854,11 @@ void PresetBundle::load_config_file_config_bundle(const std::string &path, const
|
|||
collection_dst.load_preset(path, preset_name_dst, std::move(preset_src->config), activate).is_external = true;
|
||||
return preset_name_dst;
|
||||
};
|
||||
load_one(this->prints, tmp_bundle.prints, tmp_bundle.prints .get_selected_preset().name, true);
|
||||
load_one(this->sla_prints, tmp_bundle.sla_prints, tmp_bundle.sla_prints .get_selected_preset().name, true);
|
||||
load_one(this->filaments, tmp_bundle.filaments, tmp_bundle.filaments .get_selected_preset().name, true);
|
||||
load_one(this->sla_materials, tmp_bundle.sla_materials, tmp_bundle.sla_materials.get_selected_preset().name, true);
|
||||
load_one(this->printers, tmp_bundle.printers, tmp_bundle.printers .get_selected_preset().name, true);
|
||||
load_one(this->prints, tmp_bundle.prints, tmp_bundle.prints .get_selected_preset_name(), true);
|
||||
load_one(this->sla_prints, tmp_bundle.sla_prints, tmp_bundle.sla_prints .get_selected_preset_name(), true);
|
||||
load_one(this->filaments, tmp_bundle.filaments, tmp_bundle.filaments .get_selected_preset_name(), true);
|
||||
load_one(this->sla_materials, tmp_bundle.sla_materials, tmp_bundle.sla_materials.get_selected_preset_name(), true);
|
||||
load_one(this->printers, tmp_bundle.printers, tmp_bundle.printers .get_selected_preset_name(), true);
|
||||
this->update_multi_material_filament_presets();
|
||||
for (size_t i = 1; i < std::min(tmp_bundle.filament_presets.size(), this->filament_presets.size()); ++ i)
|
||||
this->filament_presets[i] = load_one(this->filaments, tmp_bundle.filaments, tmp_bundle.filament_presets[i], false);
|
||||
|
@ -1382,14 +1372,14 @@ void PresetBundle::export_configbundle(const std::string &path, bool export_syst
|
|||
|
||||
// Export the names of the active presets.
|
||||
c << std::endl << "[presets]" << std::endl;
|
||||
c << "print = " << this->prints.get_selected_preset().name << std::endl;
|
||||
c << "sla_print = " << this->sla_prints.get_selected_preset().name << std::endl;
|
||||
c << "sla_material = " << this->sla_materials.get_selected_preset().name << std::endl;
|
||||
c << "printer = " << this->printers.get_selected_preset().name << std::endl;
|
||||
c << "print = " << this->prints.get_selected_preset_name() << std::endl;
|
||||
c << "sla_print = " << this->sla_prints.get_selected_preset_name() << std::endl;
|
||||
c << "sla_material = " << this->sla_materials.get_selected_preset_name() << std::endl;
|
||||
c << "printer = " << this->printers.get_selected_preset_name() << std::endl;
|
||||
for (size_t i = 0; i < this->filament_presets.size(); ++ i) {
|
||||
char suffix[64];
|
||||
if (i > 0)
|
||||
sprintf(suffix, "_%d", i);
|
||||
sprintf(suffix, "_%d", (int)i);
|
||||
else
|
||||
suffix[0] = 0;
|
||||
c << "filament" << suffix << " = " << this->filament_presets[i] << std::endl;
|
||||
|
|
|
@ -67,7 +67,7 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle
|
|||
int idx_extruder = 0;
|
||||
int num_extruders = (int)preset_bundle.filament_presets.size();
|
||||
for (; idx_extruder < num_extruders; ++ idx_extruder)
|
||||
if (preset_bundle.filament_presets[idx_extruder] == preset_bundle.filaments.get_selected_preset().name)
|
||||
if (preset_bundle.filament_presets[idx_extruder] == preset_bundle.filaments.get_selected_preset_name())
|
||||
break;
|
||||
if (idx_extruder == num_extruders)
|
||||
// The current filament preset is not active for any extruder.
|
||||
|
|
|
@ -92,12 +92,13 @@ void PrintHostSendDialog::EndModal(int ret)
|
|||
// Persist path and print settings
|
||||
wxString path = txt_filename->GetValue();
|
||||
int last_slash = path.Find('/', true);
|
||||
if (last_slash != wxNOT_FOUND) {
|
||||
if (last_slash == wxNOT_FOUND)
|
||||
path.clear();
|
||||
else
|
||||
path = path.SubString(0, last_slash);
|
||||
wxGetApp().app_config->set("recent", CONFIG_KEY_PATH, into_u8(path));
|
||||
}
|
||||
|
||||
GUI::get_app_config()->set("recent", CONFIG_KEY_PRINT, start_print() ? "1" : "0");
|
||||
AppConfig *app_config = wxGetApp().app_config;
|
||||
app_config->set("recent", CONFIG_KEY_PATH, into_u8(path));
|
||||
app_config->set("recent", CONFIG_KEY_PRINT, start_print() ? "1" : "0");
|
||||
}
|
||||
|
||||
MsgDialog::EndModal(ret);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
class wxTimer;
|
||||
class wxGauge;
|
||||
|
|
|
@ -296,6 +296,9 @@ void Selection::clear()
|
|||
if (!m_valid)
|
||||
return;
|
||||
|
||||
if (m_list.empty())
|
||||
return;
|
||||
|
||||
for (unsigned int i : m_list)
|
||||
{
|
||||
(*m_volumes)[i]->selected = false;
|
||||
|
@ -522,6 +525,10 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_
|
|||
//FIXME this does not work for absolute rotations (transformation_type.absolute() is true)
|
||||
rotation.cwiseAbs().maxCoeff(&rot_axis_max);
|
||||
|
||||
// if ( single instance or single volume )
|
||||
// Rotate around center , if only a single object or volume
|
||||
// transformation_type.set_independent();
|
||||
|
||||
// For generic rotation, we want to rotate the first volume in selection, and then to synchronize the other volumes with it.
|
||||
std::vector<int> object_instance_first(m_model->objects.size(), -1);
|
||||
auto rotate_instance = [this, &rotation, &object_instance_first, rot_axis_max, transformation_type](GLVolume &volume, int i) {
|
||||
|
@ -542,8 +549,8 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_
|
|||
transformation_type.absolute() ? rotation : rotation + m_cache.volumes_data[i].get_instance_rotation();
|
||||
if (rot_axis_max == 2 && transformation_type.joint()) {
|
||||
// Only allow rotation of multiple instances as a single rigid body when rotating around the Z axis.
|
||||
Vec3d offset = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0.0, 0.0, new_rotation(2) - m_cache.volumes_data[i].get_instance_rotation()(2))) * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center);
|
||||
volume.set_instance_offset(m_cache.dragging_center + offset);
|
||||
double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), new_rotation);
|
||||
volume.set_instance_offset(m_cache.dragging_center + Eigen::AngleAxisd(z_diff, Vec3d::UnitZ()) * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center));
|
||||
}
|
||||
volume.set_instance_rotation(new_rotation);
|
||||
object_instance_first[volume.object_idx()] = i;
|
||||
|
@ -658,14 +665,28 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type
|
|||
{
|
||||
GLVolume &volume = *(*m_volumes)[i];
|
||||
if (is_single_full_instance()) {
|
||||
assert(transformation_type.absolute());
|
||||
if (transformation_type.world() && (std::abs(scale.x() - scale.y()) > EPSILON || std::abs(scale.x() - scale.z()) > EPSILON)) {
|
||||
// Non-uniform scaling. Transform the scaling factors into the local coordinate system.
|
||||
// This is only possible, if the instance rotation is mulitples of ninety degrees.
|
||||
assert(Geometry::is_rotation_ninety_degrees(volume.get_instance_rotation()));
|
||||
volume.set_instance_scaling_factor((volume.get_instance_transformation().get_matrix(true, false, true, true).matrix().block<3, 3>(0, 0).transpose() * scale).cwiseAbs());
|
||||
} else
|
||||
volume.set_instance_scaling_factor(scale);
|
||||
if (transformation_type.relative())
|
||||
{
|
||||
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale);
|
||||
Eigen::Matrix<double, 3, 3, Eigen::DontAlign> new_matrix = (m * m_cache.volumes_data[i].get_instance_scale_matrix()).matrix().block(0, 0, 3, 3);
|
||||
// extracts scaling factors from the composed transformation
|
||||
Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm());
|
||||
if (transformation_type.joint())
|
||||
volume.set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center));
|
||||
|
||||
volume.set_instance_scaling_factor(new_scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (transformation_type.world() && (std::abs(scale.x() - scale.y()) > EPSILON || std::abs(scale.x() - scale.z()) > EPSILON)) {
|
||||
// Non-uniform scaling. Transform the scaling factors into the local coordinate system.
|
||||
// This is only possible, if the instance rotation is mulitples of ninety degrees.
|
||||
assert(Geometry::is_rotation_ninety_degrees(volume.get_instance_rotation()));
|
||||
volume.set_instance_scaling_factor((volume.get_instance_transformation().get_matrix(true, false, true, true).matrix().block<3, 3>(0, 0).transpose() * scale).cwiseAbs());
|
||||
}
|
||||
else
|
||||
volume.set_instance_scaling_factor(scale);
|
||||
}
|
||||
}
|
||||
else if (is_single_volume() || is_single_modifier())
|
||||
volume.set_volume_scaling_factor(scale);
|
||||
|
@ -709,6 +730,49 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type
|
|||
this->set_bounding_boxes_dirty();
|
||||
}
|
||||
|
||||
void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config)
|
||||
{
|
||||
if (is_empty() || (m_mode == Volume))
|
||||
return;
|
||||
|
||||
// adds 1/100th of a mm on all sides to avoid false out of print volume detections due to floating-point roundings
|
||||
Vec3d box_size = get_bounding_box().size() + 0.01 * Vec3d::Ones();
|
||||
|
||||
const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(config.option("bed_shape"));
|
||||
if (opt != nullptr)
|
||||
{
|
||||
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
|
||||
BoundingBoxf3 print_volume(Vec3d(unscale<double>(bed_box_2D.min(0)), unscale<double>(bed_box_2D.min(1)), 0.0), Vec3d(unscale<double>(bed_box_2D.max(0)), unscale<double>(bed_box_2D.max(1)), config.opt_float("max_print_height")));
|
||||
Vec3d print_volume_size = print_volume.size();
|
||||
double sx = (box_size(0) != 0.0) ? print_volume_size(0) / box_size(0) : 0.0;
|
||||
double sy = (box_size(1) != 0.0) ? print_volume_size(1) / box_size(1) : 0.0;
|
||||
double sz = (box_size(2) != 0.0) ? print_volume_size(2) / box_size(2) : 0.0;
|
||||
if ((sx != 0.0) && (sy != 0.0) && (sz != 0.0))
|
||||
{
|
||||
double s = std::min(sx, std::min(sy, sz));
|
||||
if (s != 1.0)
|
||||
{
|
||||
TransformationType type;
|
||||
type.set_world();
|
||||
type.set_relative();
|
||||
type.set_joint();
|
||||
|
||||
// apply scale
|
||||
start_dragging();
|
||||
scale(s * Vec3d::Ones(), type);
|
||||
wxGetApp().plater()->canvas3D()->do_scale();
|
||||
|
||||
// center selection on print bed
|
||||
start_dragging();
|
||||
translate(print_volume.center() - get_bounding_box().center());
|
||||
wxGetApp().plater()->canvas3D()->do_move();
|
||||
|
||||
wxGetApp().obj_manipul()->set_dirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Selection::mirror(Axis axis)
|
||||
{
|
||||
if (!m_valid)
|
||||
|
@ -951,14 +1015,14 @@ void Selection::render(float scale_factor) const
|
|||
}
|
||||
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
void Selection::render_center() const
|
||||
void Selection::render_center(bool gizmo_is_dragging) const
|
||||
{
|
||||
if (!m_valid || is_empty() || (m_quadric == nullptr))
|
||||
return;
|
||||
|
||||
const Vec3d& center = get_bounding_box().center();
|
||||
Vec3d center = gizmo_is_dragging ? m_cache.dragging_center : get_bounding_box().center();
|
||||
|
||||
glsafe(::glDisable(GL_DEPTH_TEST)));
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
|
||||
glsafe(::glEnable(GL_LIGHTING));
|
||||
|
||||
|
@ -1859,7 +1923,12 @@ void Selection::paste_objects_from_clipboard()
|
|||
{
|
||||
ModelObject* dst_object = m_model->add_object(*src_object);
|
||||
double offset = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.05);
|
||||
dst_object->translate(offset, offset, 0.0);
|
||||
Vec3d displacement(offset, offset, 0.0);
|
||||
for (ModelInstance* inst : dst_object->instances)
|
||||
{
|
||||
inst->set_offset(inst->get_offset() + displacement);
|
||||
}
|
||||
|
||||
object_idxs.push_back(m_model->objects.size() - 1);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
#include "libslic3r/Geometry.hpp"
|
||||
#include "3DScene.hpp"
|
||||
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
class GLUquadric;
|
||||
typedef class GLUquadric GLUquadricObj;
|
||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
|
@ -282,6 +287,7 @@ public:
|
|||
void rotate(const Vec3d& rotation, TransformationType transformation_type);
|
||||
void flattening_rotate(const Vec3d& normal);
|
||||
void scale(const Vec3d& scale, TransformationType transformation_type);
|
||||
void scale_to_fit_print_volume(const DynamicPrintConfig& config);
|
||||
void mirror(Axis axis);
|
||||
|
||||
void translate(unsigned int object_idx, const Vec3d& displacement);
|
||||
|
@ -291,7 +297,7 @@ public:
|
|||
|
||||
void render(float scale_factor = 1.0) const;
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
void render_center() const;
|
||||
void render_center(bool gizmo_is_dragging) const;
|
||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
void render_sidebar_hints(const std::string& sidebar_field) const;
|
||||
|
||||
|
@ -327,6 +333,8 @@ private:
|
|||
void render_sidebar_rotation_hint(Axis axis) const;
|
||||
void render_sidebar_scale_hint(Axis axis) const;
|
||||
void render_sidebar_size_hint(Axis axis, double length) const;
|
||||
|
||||
public:
|
||||
enum SyncRotationType {
|
||||
// Do not synchronize rotation. Either not rotating at all, or rotating by world Z axis.
|
||||
SYNC_ROTATION_NONE = 0,
|
||||
|
@ -337,6 +345,8 @@ private:
|
|||
};
|
||||
void synchronize_unselected_instances(SyncRotationType sync_rotation_type);
|
||||
void synchronize_unselected_volumes();
|
||||
|
||||
private:
|
||||
void ensure_on_bed();
|
||||
bool is_from_fully_selected_instance(unsigned int volume_idx) const;
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ SysInfoDialog::SysInfoDialog()
|
|||
}
|
||||
|
||||
wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxOK);
|
||||
m_btn_copy_to_clipboard = new wxButton(this, wxID_ANY, "Copy to Clipboard", wxDefaultPosition, wxDefaultSize);
|
||||
m_btn_copy_to_clipboard = new wxButton(this, wxID_ANY, _(L("Copy to Clipboard")), wxDefaultPosition, wxDefaultSize);
|
||||
|
||||
buttons->Insert(0, m_btn_copy_to_clipboard, 0, wxLEFT, 5);
|
||||
m_btn_copy_to_clipboard->Bind(wxEVT_BUTTON, &SysInfoDialog::onCopyToClipboard, this);
|
||||
|
@ -172,7 +172,6 @@ void SysInfoDialog::onCopyToClipboard(wxEvent &)
|
|||
void SysInfoDialog::onCloseDialog(wxEvent &)
|
||||
{
|
||||
this->EndModal(wxID_CLOSE);
|
||||
this->Close();
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
|
|
|
@ -178,7 +178,7 @@ void Tab::create_preset_tab()
|
|||
// Sizer with buttons for mode changing
|
||||
m_mode_sizer = new ModeSizer(panel);
|
||||
|
||||
const float scale_factor = wxGetApp().em_unit()*0.1;// GetContentScaleFactor();
|
||||
const float scale_factor = /*wxGetApp().*/em_unit(this)*0.1;// GetContentScaleFactor();
|
||||
m_hsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(m_hsizer, 0, wxEXPAND | wxBOTTOM, 3);
|
||||
m_hsizer->Add(m_presets_choice, 0, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, 3);
|
||||
|
@ -212,7 +212,8 @@ void Tab::create_preset_tab()
|
|||
m_treectrl = new wxTreeCtrl(panel, wxID_ANY, wxDefaultPosition, wxSize(20 * m_em_unit, -1),
|
||||
wxTR_NO_BUTTONS | wxTR_HIDE_ROOT | wxTR_SINGLE | wxTR_NO_LINES | wxBORDER_SUNKEN | wxWANTS_CHARS);
|
||||
m_left_sizer->Add(m_treectrl, 1, wxEXPAND);
|
||||
m_icons = new wxImageList(int(16 * scale_factor), int(16 * scale_factor), true, 1);
|
||||
const int img_sz = int(16 * scale_factor + 0.5f);
|
||||
m_icons = new wxImageList(img_sz, img_sz, true, 1);
|
||||
// Index of the last icon inserted into $self->{icons}.
|
||||
m_icon_count = -1;
|
||||
m_treectrl->AssignImageList(m_icons);
|
||||
|
@ -285,9 +286,10 @@ void Tab::add_scaled_bitmap(wxWindow* parent,
|
|||
void Tab::load_initial_data()
|
||||
{
|
||||
m_config = &m_presets->get_edited_preset().config;
|
||||
m_bmp_non_system = m_presets->get_selected_preset_parent() ? &m_bmp_value_unlock : &m_bmp_white_bullet;
|
||||
m_ttg_non_system = m_presets->get_selected_preset_parent() ? &m_ttg_value_unlock : &m_ttg_white_bullet_ns;
|
||||
m_tt_non_system = m_presets->get_selected_preset_parent() ? &m_tt_value_unlock : &m_ttg_white_bullet_ns;
|
||||
bool has_parent = m_presets->get_selected_preset_parent() != nullptr;
|
||||
m_bmp_non_system = has_parent ? &m_bmp_value_unlock : &m_bmp_white_bullet;
|
||||
m_ttg_non_system = has_parent ? &m_ttg_value_unlock : &m_ttg_white_bullet_ns;
|
||||
m_tt_non_system = has_parent ? &m_tt_value_unlock : &m_ttg_white_bullet_ns;
|
||||
}
|
||||
|
||||
Slic3r::GUI::PageShp Tab::add_options_page(const wxString& title, const std::string& icon, bool is_extruder_pages /*= false*/)
|
||||
|
@ -1233,16 +1235,41 @@ void TabPrint::update()
|
|||
return; // ys_FIXME
|
||||
|
||||
// #ys_FIXME_to_delete
|
||||
//! Temporary workaround for the correct updates of the SpinCtrl (like "perimeters"):
|
||||
//! Temporary workaround for the correct updates of the TextCtrl (like "layer_height"):
|
||||
// KillFocus() for the wxSpinCtrl use CallAfter function. So,
|
||||
// to except the duplicate call of the update() after dialog->ShowModal(),
|
||||
// let check if this process is already started.
|
||||
// if (is_msg_dlg_already_exist) // ! It looks like a fixed problem after start to using of a m_dirty_options
|
||||
// return; // ! TODO Let delete this part of code after a common aplication testing
|
||||
if (is_msg_dlg_already_exist)
|
||||
return;
|
||||
|
||||
m_update_cnt++;
|
||||
// Freeze();
|
||||
|
||||
// layer_height shouldn't be equal to zero
|
||||
if (m_config->opt_float("layer_height") < EPSILON)
|
||||
{
|
||||
const wxString msg_text = _(L("Zero layer height is not valid.\n\nThe layer height will be reset to 0.01."));
|
||||
auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Layer height")), wxICON_WARNING | wxOK);
|
||||
DynamicPrintConfig new_conf = *m_config;
|
||||
is_msg_dlg_already_exist = true;
|
||||
dialog->ShowModal();
|
||||
new_conf.set_key_value("layer_height", new ConfigOptionFloat(0.01));
|
||||
load_config(new_conf);
|
||||
is_msg_dlg_already_exist = false;
|
||||
}
|
||||
|
||||
if (fabs(m_config->option<ConfigOptionFloatOrPercent>("first_layer_height")->value - 0) < EPSILON)
|
||||
{
|
||||
const wxString msg_text = _(L("Zero first layer height is not valid.\n\nThe first layer height will be reset to 0.01."));
|
||||
auto dialog = new wxMessageDialog(parent(), msg_text, _(L("First layer height")), wxICON_WARNING | wxOK);
|
||||
DynamicPrintConfig new_conf = *m_config;
|
||||
is_msg_dlg_already_exist = true;
|
||||
dialog->ShowModal();
|
||||
new_conf.set_key_value("first_layer_height", new ConfigOptionFloatOrPercent(0.01, false));
|
||||
load_config(new_conf);
|
||||
is_msg_dlg_already_exist = false;
|
||||
}
|
||||
|
||||
double fill_density = m_config->option<ConfigOptionPercent>("fill_density")->value;
|
||||
|
||||
if (m_config->opt_bool("spiral_vase") &&
|
||||
|
@ -1256,7 +1283,6 @@ void TabPrint::update()
|
|||
"- no ensure_vertical_shell_thickness\n"
|
||||
"\nShall I adjust those settings in order to enable Spiral Vase?"));
|
||||
auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Spiral Vase")), wxICON_WARNING | wxYES | wxNO);
|
||||
// is_msg_dlg_already_exist = true;
|
||||
DynamicPrintConfig new_conf = *m_config;
|
||||
if (dialog->ShowModal() == wxID_YES) {
|
||||
new_conf.set_key_value("perimeters", new ConfigOptionInt(1));
|
||||
|
@ -1272,7 +1298,6 @@ void TabPrint::update()
|
|||
}
|
||||
load_config(new_conf);
|
||||
on_value_change("fill_density", fill_density);
|
||||
// is_msg_dlg_already_exist = false;
|
||||
}
|
||||
|
||||
if (m_config->opt_bool("wipe_tower") && m_config->opt_bool("support_material") &&
|
||||
|
@ -1829,13 +1854,17 @@ void TabPrinter::build_fff()
|
|||
|
||||
btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e)
|
||||
{
|
||||
auto dlg = new BedShapeDialog(this);
|
||||
dlg->build_dialog(m_config->option<ConfigOptionPoints>("bed_shape"));
|
||||
if (dlg->ShowModal() == wxID_OK) {
|
||||
load_key_value("bed_shape", dlg->GetValue());
|
||||
update_changed_ui();
|
||||
}
|
||||
}));
|
||||
BedShapeDialog dlg(this);
|
||||
dlg.build_dialog(m_config->option<ConfigOptionPoints>("bed_shape"));
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
std::vector<Vec2d> shape = dlg.GetValue();
|
||||
if (!shape.empty())
|
||||
{
|
||||
load_key_value("bed_shape", shape);
|
||||
update_changed_ui();
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
return sizer;
|
||||
};
|
||||
|
@ -2031,11 +2060,15 @@ void TabPrinter::build_sla()
|
|||
|
||||
btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e)
|
||||
{
|
||||
auto dlg = new BedShapeDialog(this);
|
||||
dlg->build_dialog(m_config->option<ConfigOptionPoints>("bed_shape"));
|
||||
if (dlg->ShowModal() == wxID_OK) {
|
||||
load_key_value("bed_shape", dlg->GetValue());
|
||||
update_changed_ui();
|
||||
BedShapeDialog dlg(this);
|
||||
dlg.build_dialog(m_config->option<ConfigOptionPoints>("bed_shape"));
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
std::vector<Vec2d> shape = dlg.GetValue();
|
||||
if (!shape.empty())
|
||||
{
|
||||
load_key_value("bed_shape", shape);
|
||||
update_changed_ui();
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
|
@ -2209,6 +2242,18 @@ void TabPrinter::build_unregular_pages()
|
|||
* */
|
||||
Freeze();
|
||||
|
||||
#ifdef __WXMSW__
|
||||
/* Workaround for correct layout of controls inside the created page:
|
||||
* In some _strange_ way we should we should imitate page resizing.
|
||||
*/
|
||||
auto layout_page = [this](PageShp page)
|
||||
{
|
||||
const wxSize& sz = page->GetSize();
|
||||
page->SetSize(sz.x + 1, sz.y + 1);
|
||||
page->SetSize(sz);
|
||||
};
|
||||
#endif //__WXMSW__
|
||||
|
||||
// Add/delete Kinematics page according to is_marlin_flavor
|
||||
size_t existed_page = 0;
|
||||
for (int i = n_before_extruders; i < m_pages.size(); ++i) // first make sure it's not there already
|
||||
|
@ -2222,6 +2267,9 @@ void TabPrinter::build_unregular_pages()
|
|||
|
||||
if (existed_page < n_before_extruders && is_marlin_flavor) {
|
||||
auto page = build_kinematics_page();
|
||||
#ifdef __WXMSW__
|
||||
layout_page(page);
|
||||
#endif
|
||||
m_pages.insert(m_pages.begin() + n_before_extruders, page);
|
||||
}
|
||||
|
||||
|
@ -2293,6 +2341,10 @@ void TabPrinter::build_unregular_pages()
|
|||
|
||||
optgroup = page->new_optgroup(_(L("Preview")));
|
||||
optgroup->append_single_option_line("extruder_colour", extruder_idx);
|
||||
|
||||
#ifdef __WXMSW__
|
||||
layout_page(page);
|
||||
#endif
|
||||
}
|
||||
|
||||
// # remove extra pages
|
||||
|
@ -2826,7 +2878,7 @@ void Tab::OnTreeSelChange(wxTreeEvent& event)
|
|||
if (m_disable_tree_sel_changed_event)
|
||||
return;
|
||||
|
||||
// There is a bug related to Ubuntu overlay scrollbars, see https://github.com/prusa3d/Slic3r/issues/898 and https://github.com/prusa3d/Slic3r/issues/952.
|
||||
// There is a bug related to Ubuntu overlay scrollbars, see https://github.com/prusa3d/PrusaSlicer/issues/898 and https://github.com/prusa3d/PrusaSlicer/issues/952.
|
||||
// The issue apparently manifests when Show()ing a window with overlay scrollbars while the UI is frozen. For this reason,
|
||||
// we will Thaw the UI prematurely on Linux. This means destroing the no_updates object prematurely.
|
||||
#ifdef __linux__
|
||||
|
@ -3287,27 +3339,27 @@ void SavePresetWindow::accept()
|
|||
{
|
||||
m_chosen_name = normalize_utf8_nfc(m_combo->GetValue().ToUTF8());
|
||||
if (!m_chosen_name.empty()) {
|
||||
const char* unusable_symbols = "<>:/\\|?*\"";
|
||||
const char* unusable_symbols = "<>[]:/\\|?*\"";
|
||||
bool is_unusable_symbol = false;
|
||||
bool is_unusable_postfix = false;
|
||||
const std::string unusable_postfix = PresetCollection::get_suffix_modified();//"(modified)";
|
||||
bool is_unusable_suffix = false;
|
||||
const std::string unusable_suffix = PresetCollection::get_suffix_modified();//"(modified)";
|
||||
for (size_t i = 0; i < std::strlen(unusable_symbols); i++) {
|
||||
if (m_chosen_name.find_first_of(unusable_symbols[i]) != std::string::npos) {
|
||||
is_unusable_symbol = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (m_chosen_name.find(unusable_postfix) != std::string::npos)
|
||||
is_unusable_postfix = true;
|
||||
if (m_chosen_name.find(unusable_suffix) != std::string::npos)
|
||||
is_unusable_suffix = true;
|
||||
|
||||
if (is_unusable_symbol) {
|
||||
show_error(this,_(L("The supplied name is not valid;")) + "\n" +
|
||||
_(L("the following characters are not allowed:")) + " <>:/\\|?*\"");
|
||||
_(L("the following characters are not allowed:")) + " " + unusable_symbols);
|
||||
}
|
||||
else if (is_unusable_postfix) {
|
||||
else if (is_unusable_suffix) {
|
||||
show_error(this,_(L("The supplied name is not valid;")) + "\n" +
|
||||
_(L("the following postfix are not allowed:")) + "\n\t" + //unusable_postfix);
|
||||
wxString::FromUTF8(unusable_postfix.c_str()));
|
||||
_(L("the following suffix is not allowed:")) + "\n\t" +
|
||||
wxString::FromUTF8(unusable_suffix.c_str()));
|
||||
}
|
||||
else if (m_chosen_name == "- default -") {
|
||||
show_error(this, _(L("The supplied name is not available.")));
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
#include "UpdateDialogs.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <wx/settings.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/event.h>
|
||||
|
@ -21,7 +25,11 @@ namespace Slic3r {
|
|||
namespace GUI {
|
||||
|
||||
|
||||
static const std::string CONFIG_UPDATE_WIKI_URL("https://github.com/prusa3d/Slic3r/wiki/Slic3r-PE-1.40-configuration-update");
|
||||
static const char* URL_CHANGELOG = "http://files.prusa3d.com/?latest=slicer-stable&lng=%1%";
|
||||
static const char* URL_DOWNLOAD = "https://www.prusa3d.com/downloads&lng=%1%";
|
||||
static const char* URL_DEV = "https://github.com/prusa3d/PrusaSlicer/releases/tag/version_%1%";
|
||||
|
||||
static const std::string CONFIG_UPDATE_WIKI_URL("https://github.com/prusa3d/PrusaSlicer/wiki/Slic3r-PE-1.40-configuration-update");
|
||||
|
||||
|
||||
// MsgUpdateSlic3r
|
||||
|
@ -31,15 +39,7 @@ MsgUpdateSlic3r::MsgUpdateSlic3r(const Semver &ver_current, const Semver &ver_on
|
|||
ver_current(ver_current),
|
||||
ver_online(ver_online)
|
||||
{
|
||||
const auto url = wxString::Format("https://github.com/prusa3d/Slic3r/releases/tag/version_%s", ver_online.to_string());
|
||||
auto *link = new wxHyperlinkCtrl(this, wxID_ANY, url, url);
|
||||
|
||||
auto *text = new wxStaticText(this, wxID_ANY, _(L("To download, follow the link below.")));
|
||||
const auto link_width = link->GetSize().GetWidth();
|
||||
const int content_width = CONTENT_WIDTH * wxGetApp().em_unit();
|
||||
text->Wrap(content_width > link_width ? content_width : link_width);
|
||||
content_sizer->Add(text);
|
||||
content_sizer->AddSpacer(VERT_SPACING);
|
||||
const bool dev_version = ver_online.prerelease() != nullptr;
|
||||
|
||||
auto *versions = new wxFlexGridSizer(2, 0, VERT_SPACING);
|
||||
versions->Add(new wxStaticText(this, wxID_ANY, _(L("Current version:"))));
|
||||
|
@ -49,7 +49,25 @@ MsgUpdateSlic3r::MsgUpdateSlic3r(const Semver &ver_current, const Semver &ver_on
|
|||
content_sizer->Add(versions);
|
||||
content_sizer->AddSpacer(VERT_SPACING);
|
||||
|
||||
content_sizer->Add(link);
|
||||
if (dev_version) {
|
||||
const std::string url = (boost::format(URL_DEV) % ver_online.to_string()).str();
|
||||
const wxString url_wx = from_u8(url);
|
||||
auto *link = new wxHyperlinkCtrl(this, wxID_ANY, _(L("Changelog && Download")), url_wx);
|
||||
content_sizer->Add(link);
|
||||
} else {
|
||||
const auto lang_code = wxGetApp().current_language_code().ToStdString();
|
||||
|
||||
const std::string url_log = (boost::format(URL_CHANGELOG) % lang_code).str();
|
||||
const wxString url_log_wx = from_u8(url_log);
|
||||
auto *link_log = new wxHyperlinkCtrl(this, wxID_ANY, _(L("Open changelog page")), url_log_wx);
|
||||
content_sizer->Add(link_log);
|
||||
|
||||
const std::string url_dw = (boost::format(URL_DOWNLOAD) % lang_code).str();
|
||||
const wxString url_dw_wx = from_u8(url_dw);
|
||||
auto *link_dw = new wxHyperlinkCtrl(this, wxID_ANY, _(L("Open download page")), url_dw_wx);
|
||||
content_sizer->Add(link_dw);
|
||||
}
|
||||
|
||||
content_sizer->AddSpacer(2*VERT_SPACING);
|
||||
|
||||
cbox = new wxCheckBox(this, wxID_ANY, _(L("Don't notify about new releases any more")));
|
||||
|
@ -69,7 +87,7 @@ bool MsgUpdateSlic3r::disable_version_check() const
|
|||
|
||||
// MsgUpdateConfig
|
||||
|
||||
MsgUpdateConfig::MsgUpdateConfig(const std::unordered_map<std::string, std::string> &updates) :
|
||||
MsgUpdateConfig::MsgUpdateConfig(const std::vector<Update> &updates) :
|
||||
MsgDialog(nullptr, _(L("Configuration update")), _(L("Configuration update is available")), wxID_NONE)
|
||||
{
|
||||
auto *text = new wxStaticText(this, wxID_ANY, _(L(
|
||||
|
@ -82,12 +100,31 @@ MsgUpdateConfig::MsgUpdateConfig(const std::unordered_map<std::string, std::stri
|
|||
content_sizer->Add(text);
|
||||
content_sizer->AddSpacer(VERT_SPACING);
|
||||
|
||||
auto *versions = new wxFlexGridSizer(2, 0, VERT_SPACING);
|
||||
const auto lang_code = wxGetApp().current_language_code().ToStdString();
|
||||
|
||||
auto *versions = new wxBoxSizer(wxVERTICAL);
|
||||
for (const auto &update : updates) {
|
||||
auto *text_vendor = new wxStaticText(this, wxID_ANY, update.first);
|
||||
auto *flex = new wxFlexGridSizer(2, 0, VERT_SPACING);
|
||||
|
||||
auto *text_vendor = new wxStaticText(this, wxID_ANY, update.vendor);
|
||||
text_vendor->SetFont(boldfont);
|
||||
versions->Add(text_vendor);
|
||||
versions->Add(new wxStaticText(this, wxID_ANY, update.second));
|
||||
flex->Add(text_vendor);
|
||||
flex->Add(new wxStaticText(this, wxID_ANY, update.version.to_string()));
|
||||
|
||||
if (! update.comment.empty()) {
|
||||
flex->Add(new wxStaticText(this, wxID_ANY, _(L("Comment:"))), 0, wxALIGN_RIGHT);
|
||||
flex->Add(new wxStaticText(this, wxID_ANY, from_u8(update.comment)));
|
||||
}
|
||||
|
||||
versions->Add(flex);
|
||||
|
||||
if (! update.changelog_url.empty() && update.version.prerelease() == nullptr) {
|
||||
auto *line = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto changelog_url = (boost::format(update.changelog_url) % lang_code).str();
|
||||
line->AddSpacer(3*VERT_SPACING);
|
||||
line->Add(new wxHyperlinkCtrl(this, wxID_ANY, _(L("Open changelog page")), changelog_url));
|
||||
versions->Add(line);
|
||||
}
|
||||
}
|
||||
|
||||
content_sizer->Add(versions);
|
||||
|
@ -186,7 +223,7 @@ MsgDataLegacy::MsgDataLegacy() :
|
|||
content_sizer->AddSpacer(VERT_SPACING);
|
||||
|
||||
auto *text2 = new wxStaticText(this, wxID_ANY, _(L("For more information please visit our wiki page:")));
|
||||
static const wxString url("https://github.com/prusa3d/Slic3r/wiki/Slic3r-PE-1.40-configuration-update");
|
||||
static const wxString url("https://github.com/prusa3d/PrusaSlicer/wiki/Slic3r-PE-1.40-configuration-update");
|
||||
// The wiki page name is intentionally not localized:
|
||||
auto *link = new wxHyperlinkCtrl(this, wxID_ANY, wxString::Format("%s 1.40 configuration update", SLIC3R_APP_NAME), CONFIG_UPDATE_WIKI_URL);
|
||||
content_sizer->Add(text2);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "slic3r/Utils/Semver.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
|
@ -40,8 +41,22 @@ private:
|
|||
class MsgUpdateConfig : public MsgDialog
|
||||
{
|
||||
public:
|
||||
// updates is a map of "vendor name" -> "version (comment)"
|
||||
MsgUpdateConfig(const std::unordered_map<std::string, std::string> &updates);
|
||||
struct Update
|
||||
{
|
||||
std::string vendor;
|
||||
Semver version;
|
||||
std::string comment;
|
||||
std::string changelog_url;
|
||||
|
||||
Update(std::string vendor, Semver version, std::string comment, std::string changelog_url)
|
||||
: vendor(std::move(vendor))
|
||||
, version(std::move(version))
|
||||
, comment(std::move(comment))
|
||||
, changelog_url(std::move(changelog_url))
|
||||
{}
|
||||
};
|
||||
|
||||
MsgUpdateConfig(const std::vector<Update> &updates);
|
||||
MsgUpdateConfig(MsgUpdateConfig &&) = delete;
|
||||
MsgUpdateConfig(const MsgUpdateConfig &) = delete;
|
||||
MsgUpdateConfig &operator=(MsgUpdateConfig &&) = delete;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "wxExtensions.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <cmath>
|
||||
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
|
@ -19,6 +20,7 @@
|
|||
#include "libslic3r/GCode/PreviewData.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "../Utils/MacDarkMode.hpp"
|
||||
|
||||
using Slic3r::GUI::from_u8;
|
||||
|
||||
|
@ -389,7 +391,15 @@ wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name_in,
|
|||
static Slic3r::GUI::BitmapCache cache;
|
||||
|
||||
#ifdef __APPLE__
|
||||
const float scale_factor = win != nullptr ? win->GetContentScaleFactor() : 1.0f;
|
||||
// Note: win->GetContentScaleFactor() is not used anymore here because it tends to
|
||||
// return bogus results quite often (such as 1.0 on Retina or even 0.0).
|
||||
// We're using the max scaling factor across all screens because it's very likely to be good enough.
|
||||
|
||||
static float max_scaling_factor = NAN;
|
||||
if (std::isnan(max_scaling_factor)) {
|
||||
max_scaling_factor = Slic3r::GUI::mac_max_scaling_factor();
|
||||
}
|
||||
const float scale_factor = win != nullptr ? max_scaling_factor : 1.0f;
|
||||
#else
|
||||
(void)(win);
|
||||
const float scale_factor = 1.0f;
|
||||
|
@ -576,7 +586,7 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent
|
|||
ItemAdded(parent_item, child);
|
||||
|
||||
root->m_volumes_cnt++;
|
||||
if (insert_position > 0) insert_position++;
|
||||
if (insert_position >= 0) insert_position++;
|
||||
}
|
||||
|
||||
const auto node = new ObjectDataViewModelNode(root, name, GetVolumeIcon(volume_type, has_errors), extruder_str, root->m_volumes_cnt);
|
||||
|
@ -1871,9 +1881,9 @@ void DoubleSlider::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoin
|
|||
{
|
||||
const int tick = m_selection == ssLower ? m_lower_value : m_higher_value;
|
||||
|
||||
wxBitmap& icon = m_is_action_icon_focesed ? m_bmp_add_tick_off.bmp() : m_bmp_add_tick_on.bmp();
|
||||
wxBitmap* icon = m_is_action_icon_focesed ? &m_bmp_add_tick_off.bmp() : &m_bmp_add_tick_on.bmp();
|
||||
if (m_ticks.find(tick) != m_ticks.end())
|
||||
icon = m_is_action_icon_focesed ? m_bmp_del_tick_off.bmp() : m_bmp_del_tick_on.bmp();
|
||||
icon = m_is_action_icon_focesed ? &m_bmp_del_tick_off.bmp() : &m_bmp_del_tick_on.bmp();
|
||||
|
||||
wxCoord x_draw, y_draw;
|
||||
is_horizontal() ? x_draw = pt_beg.x - 0.5*m_tick_icon_dim : y_draw = pt_beg.y - 0.5*m_tick_icon_dim;
|
||||
|
@ -1882,7 +1892,7 @@ void DoubleSlider::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoin
|
|||
else
|
||||
is_horizontal() ? y_draw = pt_beg.y - m_tick_icon_dim-2 : x_draw = pt_end.x + 3;
|
||||
|
||||
dc.DrawBitmap(icon, x_draw, y_draw);
|
||||
dc.DrawBitmap(*icon, x_draw, y_draw);
|
||||
|
||||
//update rect of the tick action icon
|
||||
m_rect_tick_action = wxRect(x_draw, y_draw, m_tick_icon_dim, m_tick_icon_dim);
|
||||
|
@ -2235,14 +2245,16 @@ void DoubleSlider::OnMotion(wxMouseEvent& event)
|
|||
}
|
||||
else if (m_is_left_down || m_is_right_down) {
|
||||
if (m_selection == ssLower) {
|
||||
int current_value = m_lower_value;
|
||||
m_lower_value = get_value_from_position(pos.x, pos.y);
|
||||
correct_lower_value();
|
||||
action = true;
|
||||
action = (current_value != m_lower_value);
|
||||
}
|
||||
else if (m_selection == ssHigher) {
|
||||
int current_value = m_higher_value;
|
||||
m_higher_value = get_value_from_position(pos.x, pos.y);
|
||||
correct_higher_value();
|
||||
action = true;
|
||||
action = (current_value != m_higher_value);
|
||||
}
|
||||
}
|
||||
Refresh();
|
||||
|
@ -2253,6 +2265,7 @@ void DoubleSlider::OnMotion(wxMouseEvent& event)
|
|||
{
|
||||
wxCommandEvent e(wxEVT_SCROLL_CHANGED);
|
||||
e.SetEventObject(this);
|
||||
e.SetString("moving");
|
||||
ProcessWindowEvent(e);
|
||||
}
|
||||
}
|
||||
|
@ -2544,6 +2557,11 @@ ModeSizer::ModeSizer(wxWindow *parent, int hgap/* = 10*/) :
|
|||
{_(L("Expert")), "mode_expert_sq.png"}
|
||||
};
|
||||
|
||||
auto modebtnfn = [](wxCommandEvent &event, int mode_id) {
|
||||
Slic3r::GUI::wxGetApp().save_mode(mode_id);
|
||||
event.Skip();
|
||||
};
|
||||
|
||||
m_mode_btns.reserve(3);
|
||||
for (const auto& button : buttons) {
|
||||
#ifdef __WXOSX__
|
||||
|
@ -2554,37 +2572,22 @@ ModeSizer::ModeSizer(wxWindow *parent, int hgap/* = 10*/) :
|
|||
#else
|
||||
m_mode_btns.push_back(new ModeButton(parent, wxID_ANY, button.second, button.first));;
|
||||
#endif // __WXOSX__
|
||||
|
||||
m_mode_btns.back()->Bind(wxEVT_BUTTON, std::bind(modebtnfn, std::placeholders::_1, m_mode_btns.size() - 1));
|
||||
Add(m_mode_btns.back());
|
||||
}
|
||||
|
||||
for (auto btn : m_mode_btns)
|
||||
{
|
||||
btn->Bind(wxEVT_BUTTON, [btn, this](wxCommandEvent &event) {
|
||||
event.Skip();
|
||||
int mode_id = 0;
|
||||
for (auto cur_btn : m_mode_btns) {
|
||||
if (cur_btn == btn)
|
||||
break;
|
||||
else
|
||||
mode_id++;
|
||||
}
|
||||
Slic3r::GUI::wxGetApp().save_mode(mode_id);
|
||||
});
|
||||
|
||||
Add(btn);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ModeSizer::SetMode(const int mode)
|
||||
{
|
||||
for (int m = 0; m < m_mode_btns.size(); m++)
|
||||
m_mode_btns[m]->SetState(m == mode);
|
||||
for (size_t m = 0; m < m_mode_btns.size(); m++)
|
||||
m_mode_btns[m]->SetState(int(m) == mode);
|
||||
}
|
||||
|
||||
|
||||
void ModeSizer::msw_rescale()
|
||||
{
|
||||
for (int m = 0; m < m_mode_btns.size(); m++)
|
||||
for (size_t m = 0; m < m_mode_btns.size(); m++)
|
||||
m_mode_btns[m]->msw_rescale();
|
||||
}
|
||||
|
||||
|
@ -2653,7 +2656,7 @@ ScalableButton::ScalableButton( wxWindow * parent,
|
|||
if (style & wxNO_BORDER)
|
||||
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
#endif // __WXMSW__
|
||||
|
||||
|
||||
SetBitmap(create_scaled_bitmap(parent, icon_name));
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue