mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-28 11:11:16 -06:00
Add the full source of BambuStudio
using version 1.0.10
This commit is contained in:
parent
30bcadab3e
commit
1555904bef
3771 changed files with 1251328 additions and 0 deletions
174
src/slic3r/GUI/2DBed.cpp
Normal file
174
src/slic3r/GUI/2DBed.cpp
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
#include "2DBed.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
|
||||
#include <wx/dcbuffer.h>
|
||||
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
|
||||
Bed_2D::Bed_2D(wxWindow* parent) :
|
||||
wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), -1), wxTAB_TRAVERSAL)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
m_user_drawn_background = false;
|
||||
#else
|
||||
SetBackgroundStyle(wxBG_STYLE_PAINT); // to avoid assert message after wxAutoBufferedPaintDC
|
||||
#endif /*__APPLE__*/
|
||||
}
|
||||
|
||||
void Bed_2D::repaint(const std::vector<Vec2d>& shape)
|
||||
{
|
||||
wxAutoBufferedPaintDC dc(this);
|
||||
auto cw = GetSize().GetWidth();
|
||||
auto ch = GetSize().GetHeight();
|
||||
// when canvas is not rendered yet, size is 0, 0
|
||||
if (cw == 0) return ;
|
||||
|
||||
if (m_user_drawn_background) {
|
||||
// On all systems the AutoBufferedPaintDC() achieves double buffering.
|
||||
// On MacOS the background is erased, on Windows the background is not erased
|
||||
// and on Linux / GTK the background is erased to gray color.
|
||||
// Fill DC with the background on Windows & Linux / GTK.
|
||||
#ifdef _WIN32
|
||||
auto color = wxGetApp().get_highlight_default_clr();
|
||||
#else
|
||||
auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT); //GetSystemColour
|
||||
#endif
|
||||
dc.SetPen(*new wxPen(color, 1, wxPENSTYLE_SOLID));
|
||||
dc.SetBrush(*new wxBrush(color, wxBRUSHSTYLE_SOLID));
|
||||
auto rect = GetUpdateRegion().GetBox();
|
||||
dc.DrawRectangle(rect.GetLeft(), rect.GetTop(), rect.GetWidth(), rect.GetHeight());
|
||||
}
|
||||
|
||||
if (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));
|
||||
auto ccenter = cbb.center();
|
||||
|
||||
// get bounding box of bed shape in G - code coordinates
|
||||
auto bed_polygon = Polygon::new_scale(shape);
|
||||
auto bb = BoundingBoxf(shape);
|
||||
bb.merge(Vec2d(0, 0)); // origin needs to be in the visible area
|
||||
auto bw = bb.size()(0);
|
||||
auto bh = bb.size()(1);
|
||||
auto bcenter = bb.center();
|
||||
|
||||
// calculate the scaling factor for fitting bed shape in canvas area
|
||||
auto sfactor = std::min(cw/bw, ch/bh);
|
||||
auto shift = Vec2d(
|
||||
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) - ch));
|
||||
|
||||
// draw bed fill
|
||||
dc.SetBrush(wxBrush(wxColour(255, 255, 255), wxBRUSHSTYLE_SOLID));
|
||||
wxPointList pt_list;
|
||||
for (auto pt : shape)
|
||||
{
|
||||
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);
|
||||
|
||||
// draw grid
|
||||
auto step = 10; // 1cm grid
|
||||
Polylines polylines;
|
||||
for (auto x = bb.min(0) - fmod(bb.min(0), step) + step; x < bb.max(0); x += step) {
|
||||
polylines.push_back(Polyline::new_scale({ Vec2d(x, bb.min(1)), Vec2d(x, bb.max(1)) }));
|
||||
}
|
||||
for (auto y = bb.min(1) - fmod(bb.min(1), step) + step; y < bb.max(1); y += step) {
|
||||
polylines.push_back(Polyline::new_scale({ Vec2d(bb.min(0), y), Vec2d(bb.max(0), y) }));
|
||||
}
|
||||
polylines = intersection_pl(polylines, bed_polygon);
|
||||
|
||||
dc.SetPen(wxPen(wxColour(230, 230, 230), 1, wxPENSTYLE_SOLID));
|
||||
for (auto pl : polylines)
|
||||
{
|
||||
for (size_t i = 0; i < pl.points.size()-1; i++) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
// draw bed contour
|
||||
dc.SetPen(wxPen(wxColour(0, 0, 0), 1, wxPENSTYLE_SOLID));
|
||||
dc.SetBrush(wxBrush(wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT));
|
||||
dc.DrawPolygon(&pt_list, 0, 0);
|
||||
|
||||
auto origin_px = to_pixels(Vec2d(0, 0), ch);
|
||||
|
||||
// draw axes
|
||||
auto axes_len = 50;
|
||||
auto arrow_len = 6;
|
||||
auto arrow_angle = Geometry::deg2rad(45.0);
|
||||
dc.SetPen(wxPen(wxColour(255, 0, 0), 2, wxPENSTYLE_SOLID)); // red
|
||||
auto x_end = Vec2d(origin_px(0) + axes_len, origin_px(1));
|
||||
dc.DrawLine(wxPoint(origin_px(0), origin_px(1)), wxPoint(x_end(0), x_end(1)));
|
||||
for (auto angle : { -arrow_angle, arrow_angle }) {
|
||||
auto end = Eigen::Translation2d(x_end) * Eigen::Rotation2Dd(angle) * Eigen::Translation2d(- x_end) * Eigen::Vector2d(x_end(0) - arrow_len, x_end(1));
|
||||
dc.DrawLine(wxPoint(x_end(0), x_end(1)), wxPoint(end(0), end(1)));
|
||||
}
|
||||
|
||||
dc.SetPen(wxPen(wxColour(0, 255, 0), 2, wxPENSTYLE_SOLID)); // green
|
||||
auto y_end = Vec2d(origin_px(0), origin_px(1) - axes_len);
|
||||
dc.DrawLine(wxPoint(origin_px(0), origin_px(1)), wxPoint(y_end(0), y_end(1)));
|
||||
for (auto angle : { -arrow_angle, arrow_angle }) {
|
||||
auto end = Eigen::Translation2d(y_end) * Eigen::Rotation2Dd(angle) * Eigen::Translation2d(- y_end) * Eigen::Vector2d(y_end(0), y_end(1) + arrow_len);
|
||||
dc.DrawLine(wxPoint(y_end(0), y_end(1)), wxPoint(end(0), end(1)));
|
||||
}
|
||||
|
||||
// draw origin
|
||||
dc.SetPen(wxPen(wxColour(0, 0, 0), 1, wxPENSTYLE_SOLID));
|
||||
dc.SetBrush(wxBrush(wxColour(0, 0, 0), wxBRUSHSTYLE_SOLID));
|
||||
dc.DrawCircle(origin_px(0), origin_px(1), 3);
|
||||
|
||||
static const auto origin_label = wxString("(0,0)");
|
||||
dc.SetTextForeground(wxColour(0, 0, 0));
|
||||
dc.SetFont(wxFont(10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
|
||||
auto extent = dc.GetTextExtent(origin_label);
|
||||
const auto origin_label_x = origin_px(0) <= cw / 2 ? origin_px(0) + 1 : origin_px(0) - 1 - extent.GetWidth();
|
||||
const auto origin_label_y = origin_px(1) <= ch / 2 ? origin_px(1) + 1 : origin_px(1) - 1 - extent.GetHeight();
|
||||
dc.DrawText(origin_label, origin_label_x, origin_label_y);
|
||||
|
||||
// draw current position
|
||||
if (m_pos!= Vec2d(0, 0)) {
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// convert G - code coordinates into pixels
|
||||
Point Bed_2D::to_pixels(const Vec2d& point, int height)
|
||||
{
|
||||
auto p = point * m_scale_factor + m_shift;
|
||||
return Point(p(0) + Border, height - p(1) + Border);
|
||||
}
|
||||
|
||||
void Bed_2D::set_pos(const Vec2d& pos)
|
||||
{
|
||||
m_pos = pos;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
33
src/slic3r/GUI/2DBed.hpp
Normal file
33
src/slic3r/GUI/2DBed.hpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef slic3r_2DBed_hpp_
|
||||
#define slic3r_2DBed_hpp_
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include "libslic3r/Config.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
class Bed_2D : public wxPanel
|
||||
{
|
||||
static const int Border = 10;
|
||||
|
||||
bool m_user_drawn_background = true;
|
||||
|
||||
double m_scale_factor;
|
||||
Vec2d m_shift = Vec2d::Zero();
|
||||
Vec2d m_pos = Vec2d::Zero();
|
||||
|
||||
Point to_pixels(const Vec2d& point, int height);
|
||||
void set_pos(const Vec2d& pos);
|
||||
|
||||
public:
|
||||
explicit Bed_2D(wxWindow* parent);
|
||||
|
||||
void repaint(const std::vector<Vec2d>& shape);
|
||||
};
|
||||
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
|
||||
#endif /* slic3r_2DBed_hpp_ */
|
||||
705
src/slic3r/GUI/3DBed.cpp
Normal file
705
src/slic3r/GUI/3DBed.cpp
Normal file
|
|
@ -0,0 +1,705 @@
|
|||
#include "libslic3r/libslic3r.h"
|
||||
|
||||
#include "3DBed.hpp"
|
||||
|
||||
#include "libslic3r/Polygon.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "libslic3r/Geometry/Circle.hpp"
|
||||
#include "libslic3r/Tesselate.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI_Colors.hpp"
|
||||
#include "GLCanvas3D.hpp"
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/timer.hpp>
|
||||
|
||||
static const float GROUND_Z = -0.04f;
|
||||
static const std::array<float, 4> DEFAULT_MODEL_COLOR = { 0.3255f, 0.337f, 0.337f, 1.0f };
|
||||
static const std::array<float, 4> PICKING_MODEL_COLOR = { 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
bool GeometryBuffer::set_from_triangles(const std::vector<Vec2f> &triangles, float z)
|
||||
{
|
||||
if (triangles.empty()) {
|
||||
m_vertices.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_vertices.clear();
|
||||
assert(triangles.size() % 3 == 0);
|
||||
m_vertices = std::vector<Vertex>(triangles.size(), Vertex());
|
||||
|
||||
Vec2f min = triangles.front();
|
||||
Vec2f max = min;
|
||||
|
||||
for (size_t v_count = 0; v_count < triangles.size(); ++ v_count) {
|
||||
const Vec2f &p = triangles[v_count];
|
||||
Vertex &v = m_vertices[v_count];
|
||||
v.position = Vec3f(p.x(), p.y(), z);
|
||||
v.tex_coords = p;
|
||||
min = min.cwiseMin(p).eval();
|
||||
max = max.cwiseMax(p).eval();
|
||||
}
|
||||
|
||||
Vec2f size = max - min;
|
||||
if (size.x() != 0.f && size.y() != 0.f) {
|
||||
Vec2f inv_size = size.cwiseInverse();
|
||||
inv_size.y() *= -1;
|
||||
for (Vertex& v : m_vertices) {
|
||||
v.tex_coords -= min;
|
||||
v.tex_coords.x() *= inv_size.x();
|
||||
v.tex_coords.y() *= inv_size.y();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GeometryBuffer::set_from_lines(const Lines& lines, float z)
|
||||
{
|
||||
m_vertices.clear();
|
||||
|
||||
unsigned int v_size = 2 * (unsigned int)lines.size();
|
||||
if (v_size == 0)
|
||||
return false;
|
||||
|
||||
m_vertices = std::vector<Vertex>(v_size, Vertex());
|
||||
|
||||
unsigned int v_count = 0;
|
||||
for (const Line& l : lines) {
|
||||
Vertex& v1 = m_vertices[v_count];
|
||||
v1.position[0] = unscale<float>(l.a(0));
|
||||
v1.position[1] = unscale<float>(l.a(1));
|
||||
v1.position[2] = z;
|
||||
++v_count;
|
||||
|
||||
Vertex& v2 = m_vertices[v_count];
|
||||
v2.position[0] = unscale<float>(l.b(0));
|
||||
v2.position[1] = unscale<float>(l.b(1));
|
||||
v2.position[2] = z;
|
||||
++v_count;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//BBS: set from 3d lines
|
||||
bool GeometryBuffer::set_from_3d_Lines(const Lines3& lines)
|
||||
{
|
||||
m_vertices.clear();
|
||||
|
||||
unsigned int v_size = 2 * (unsigned int)lines.size();
|
||||
if (v_size == 0)
|
||||
return false;
|
||||
|
||||
m_vertices = std::vector<Vertex>(v_size, Vertex());
|
||||
|
||||
unsigned int v_count = 0;
|
||||
for (const Line3& l : lines) {
|
||||
Vertex& v1 = m_vertices[v_count];
|
||||
v1.position[0] = unscale<float>(l.a(0));
|
||||
v1.position[1] = unscale<float>(l.a(1));
|
||||
v1.position[2] = unscale<float>(l.a(2));
|
||||
++v_count;
|
||||
|
||||
Vertex& v2 = m_vertices[v_count];
|
||||
v2.position[0] = unscale<float>(l.b(0));
|
||||
v2.position[1] = unscale<float>(l.b(1));
|
||||
v2.position[2] = unscale<float>(l.b(2));
|
||||
++v_count;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const float* GeometryBuffer::get_vertices_data() const
|
||||
{
|
||||
return (m_vertices.size() > 0) ? (const float*)m_vertices.data() : nullptr;
|
||||
}
|
||||
|
||||
const float Bed3D::Axes::DefaultStemRadius = 0.5f;
|
||||
const float Bed3D::Axes::DefaultStemLength = 25.0f;
|
||||
const float Bed3D::Axes::DefaultTipRadius = 2.5f * Bed3D::Axes::DefaultStemRadius;
|
||||
const float Bed3D::Axes::DefaultTipLength = 5.0f;
|
||||
|
||||
std::array<float, 4> Bed3D::AXIS_X_COLOR = decode_color_to_float_array("#FF0000");
|
||||
std::array<float, 4> Bed3D::AXIS_Y_COLOR = decode_color_to_float_array("#00FF00");
|
||||
std::array<float, 4> Bed3D::AXIS_Z_COLOR = decode_color_to_float_array("#0000FF");
|
||||
|
||||
void Bed3D::update_render_colors()
|
||||
{
|
||||
Bed3D::AXIS_X_COLOR = GLColor(RenderColor::colors[RenderCol_Axis_X]);
|
||||
Bed3D::AXIS_Y_COLOR = GLColor(RenderColor::colors[RenderCol_Axis_Y]);
|
||||
Bed3D::AXIS_Z_COLOR = GLColor(RenderColor::colors[RenderCol_Axis_Z]);
|
||||
}
|
||||
|
||||
void Bed3D::load_render_colors()
|
||||
{
|
||||
RenderColor::colors[RenderCol_Axis_X] = IMColor(Bed3D::AXIS_X_COLOR);
|
||||
RenderColor::colors[RenderCol_Axis_Y] = IMColor(Bed3D::AXIS_Y_COLOR);
|
||||
RenderColor::colors[RenderCol_Axis_Z] = IMColor(Bed3D::AXIS_Z_COLOR);
|
||||
}
|
||||
|
||||
void Bed3D::Axes::render() const
|
||||
{
|
||||
auto render_axis = [this](const Transform3f& transform) {
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glMultMatrixf(transform.data()));
|
||||
m_arrow.render();
|
||||
glsafe(::glPopMatrix());
|
||||
};
|
||||
|
||||
if (!m_arrow.is_initialized())
|
||||
const_cast<GLModel*>(&m_arrow)->init_from(stilized_arrow(16, DefaultTipRadius, DefaultTipLength, DefaultStemRadius, m_stem_length));
|
||||
|
||||
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
|
||||
if (shader == nullptr)
|
||||
return;
|
||||
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
|
||||
shader->start_using();
|
||||
shader->set_uniform("emission_factor", 0.0f);
|
||||
|
||||
// x axis
|
||||
const_cast<GLModel*>(&m_arrow)->set_color(-1, AXIS_X_COLOR);
|
||||
render_axis(Geometry::assemble_transform(m_origin, { 0.0, 0.5 * M_PI, 0.0 }).cast<float>());
|
||||
|
||||
// y axis
|
||||
const_cast<GLModel*>(&m_arrow)->set_color(-1, AXIS_Y_COLOR);
|
||||
render_axis(Geometry::assemble_transform(m_origin, { -0.5 * M_PI, 0.0, 0.0 }).cast<float>());
|
||||
|
||||
// z axis
|
||||
const_cast<GLModel*>(&m_arrow)->set_color(-1, AXIS_Z_COLOR);
|
||||
render_axis(Geometry::assemble_transform(m_origin).cast<float>());
|
||||
|
||||
shader->stop_using();
|
||||
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
}
|
||||
|
||||
//BBS: add part plate logic
|
||||
bool Bed3D::set_shape(const Pointfs& printable_area, const double printable_height, const std::string& custom_model, bool force_as_custom,
|
||||
const Vec2d position, bool with_reset)
|
||||
{
|
||||
/*auto check_texture = [](const std::string& texture) {
|
||||
boost::system::error_code ec; // so the exists call does not throw (e.g. after a permission problem)
|
||||
return !texture.empty() && (boost::algorithm::iends_with(texture, ".png") || boost::algorithm::iends_with(texture, ".svg")) && boost::filesystem::exists(texture, ec);
|
||||
};*/
|
||||
|
||||
auto check_model = [](const std::string& model) {
|
||||
boost::system::error_code ec;
|
||||
return !model.empty() && boost::algorithm::iends_with(model, ".stl") && boost::filesystem::exists(model, ec);
|
||||
};
|
||||
|
||||
Type type;
|
||||
std::string model;
|
||||
std::string texture;
|
||||
if (force_as_custom)
|
||||
type = Type::Custom;
|
||||
else {
|
||||
auto [new_type, system_model, system_texture] = detect_type(printable_area);
|
||||
type = new_type;
|
||||
model = system_model;
|
||||
texture = system_texture;
|
||||
}
|
||||
|
||||
/*std::string texture_filename = custom_texture.empty() ? texture : custom_texture;
|
||||
if (! texture_filename.empty() && ! check_texture(texture_filename)) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Unable to load bed texture: " << texture_filename;
|
||||
texture_filename.clear();
|
||||
}*/
|
||||
|
||||
std::string model_filename = custom_model.empty() ? model : custom_model;
|
||||
if (! model_filename.empty() && ! check_model(model_filename)) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Unable to load bed model: " << model_filename;
|
||||
model_filename.clear();
|
||||
}
|
||||
|
||||
//BBS: add position related logic
|
||||
if (m_bed_shape == printable_area && m_build_volume.printable_height() == printable_height && m_type == type && m_model_filename == model_filename && position == m_position)
|
||||
// No change, no need to update the UI.
|
||||
return false;
|
||||
|
||||
//BBS: add part plate logic, apply position to bed shape
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(":current position {%1%,%2%}, new position {%3%, %4%}") % m_position.x() % m_position.y() % position.x() % position.y();
|
||||
m_position = position;
|
||||
m_bed_shape = printable_area;
|
||||
if ((position(0) != 0) || (position(1) != 0)) {
|
||||
Pointfs new_bed_shape;
|
||||
for (const Vec2d& p : m_bed_shape) {
|
||||
Vec2d point(p(0) + m_position.x(), p(1) + m_position.y());
|
||||
new_bed_shape.push_back(point);
|
||||
}
|
||||
m_build_volume = BuildVolume { new_bed_shape, printable_height };
|
||||
}
|
||||
else
|
||||
m_build_volume = BuildVolume { printable_area, printable_height };
|
||||
m_type = type;
|
||||
//m_texture_filename = texture_filename;
|
||||
m_model_filename = model_filename;
|
||||
//BBS: add part plate logic
|
||||
m_extended_bounding_box = this->calc_extended_bounding_box(false);
|
||||
|
||||
//BBS: add part plate logic
|
||||
/*#if 0
|
||||
ExPolygon poly{ Polygon::new_scale(printable_area) };
|
||||
#else
|
||||
ExPolygon poly;
|
||||
for (const Vec2d& p : printable_area) {
|
||||
poly.contour.append(Point(scale_(p(0) + m_position.x()), scale_(p(1) + m_position.y())));
|
||||
}
|
||||
#endif
|
||||
|
||||
calc_triangles(poly);
|
||||
|
||||
const BoundingBox& bed_bbox = poly.contour.bounding_box();
|
||||
calc_gridlines(poly, bed_bbox);
|
||||
|
||||
m_polygon = offset(poly.contour, (float)bed_bbox.radius() * 1.7f, jtRound, scale_(0.5))[0];*/
|
||||
|
||||
if (with_reset) {
|
||||
this->release_VBOs();
|
||||
//m_texture.reset();
|
||||
m_model.reset();
|
||||
}
|
||||
//BBS: add part plate logic, always update model offset
|
||||
//else {
|
||||
update_model_offset();
|
||||
//}
|
||||
|
||||
// Set the origin and size for rendering the coordinate system axes.
|
||||
m_axes.set_origin({ 0.0, 0.0, static_cast<double>(GROUND_Z) });
|
||||
m_axes.set_stem_length(0.1f * static_cast<float>(m_build_volume.bounding_volume().max_size()));
|
||||
|
||||
// Let the calee to update the UI.
|
||||
return true;
|
||||
}
|
||||
|
||||
//BBS: add api to set position for partplate related bed
|
||||
void Bed3D::set_position(Vec2d& position)
|
||||
{
|
||||
set_shape(m_bed_shape, m_build_volume.printable_height(), m_model_filename, false, position, false);
|
||||
}
|
||||
|
||||
void Bed3D::set_axes_mode(bool origin)
|
||||
{
|
||||
if (origin) {
|
||||
m_axes.set_origin({ 0.0, 0.0, static_cast<double>(GROUND_Z) });
|
||||
}
|
||||
else {
|
||||
m_axes.set_origin({ m_position.x(), m_position.y(), static_cast<double>(GROUND_Z) });
|
||||
}
|
||||
}
|
||||
|
||||
/*bool Bed3D::contains(const Point& point) const
|
||||
{
|
||||
return m_polygon.contains(point);
|
||||
}
|
||||
|
||||
Point Bed3D::point_projection(const Point& point) const
|
||||
{
|
||||
return m_polygon.point_projection(point);
|
||||
}*/
|
||||
|
||||
void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes)
|
||||
{
|
||||
render_internal(canvas, bottom, scale_factor, show_axes);
|
||||
}
|
||||
|
||||
/*void Bed3D::render_for_picking(GLCanvas3D& canvas, bool bottom, float scale_factor)
|
||||
{
|
||||
render_internal(canvas, bottom, scale_factor, false, false, true);
|
||||
}*/
|
||||
|
||||
void Bed3D::render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
|
||||
bool show_axes)
|
||||
{
|
||||
float* factor = const_cast<float*>(&m_scale_factor);
|
||||
*factor = scale_factor;
|
||||
|
||||
if (show_axes)
|
||||
render_axes();
|
||||
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
|
||||
m_model.set_color(-1, DEFAULT_MODEL_COLOR);
|
||||
|
||||
switch (m_type)
|
||||
{
|
||||
case Type::System: { render_system(canvas, bottom); break; }
|
||||
default:
|
||||
case Type::Custom: { render_custom(canvas, bottom); break; }
|
||||
}
|
||||
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
}
|
||||
|
||||
//BBS: add partplate related logic
|
||||
// Calculate an extended bounding box from axes and current model for visualization purposes.
|
||||
BoundingBoxf3 Bed3D::calc_extended_bounding_box(bool consider_model_offset) const
|
||||
{
|
||||
BoundingBoxf3 out { m_build_volume.bounding_volume() };
|
||||
|
||||
const Vec3d size = out.size();
|
||||
// ensures that the bounding box is set as defined or the following calls to merge() will not work as intented
|
||||
if (size.x() > 0.0 && size.y() > 0.0 && !out.defined)
|
||||
out.defined = true;
|
||||
// Reset the build volume Z, we don't want to zoom to the top of the build volume if it is empty.
|
||||
out.min.z() = 0.0;
|
||||
out.max.z() = 0.0;
|
||||
// extend to contain axes
|
||||
//BBS: add part plate related logic.
|
||||
Vec3d offset{ m_position.x(), m_position.y(), 0.f };
|
||||
//out.merge(m_axes.get_origin() + offset + m_axes.get_total_length() * Vec3d::Ones());
|
||||
out.merge(Vec3d(0.f, 0.f, GROUND_Z) + offset + m_axes.get_total_length() * Vec3d::Ones());
|
||||
out.merge(out.min + Vec3d(-Axes::DefaultTipRadius, -Axes::DefaultTipRadius, out.max.z()));
|
||||
//BBS: add part plate related logic.
|
||||
if (consider_model_offset) {
|
||||
// extend to contain model, if any
|
||||
BoundingBoxf3 model_bb = m_model.get_bounding_box();
|
||||
if (model_bb.defined) {
|
||||
model_bb.translate(m_model_offset);
|
||||
out.merge(model_bb);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/*void Bed3D::calc_triangles(const ExPolygon& poly)
|
||||
{
|
||||
if (! m_triangles.set_from_triangles(triangulate_expolygon_2f(poly, NORMALS_UP), GROUND_Z))
|
||||
BOOST_LOG_TRIVIAL(error) << "Unable to create bed triangles";
|
||||
}
|
||||
|
||||
void Bed3D::calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox)
|
||||
{
|
||||
Polylines axes_lines;
|
||||
for (coord_t x = bed_bbox.min.x(); x <= bed_bbox.max.x(); x += scale_(10.0)) {
|
||||
Polyline line;
|
||||
line.append(Point(x, bed_bbox.min.y()));
|
||||
line.append(Point(x, bed_bbox.max.y()));
|
||||
axes_lines.push_back(line);
|
||||
}
|
||||
for (coord_t y = bed_bbox.min.y(); y <= bed_bbox.max.y(); y += scale_(10.0)) {
|
||||
Polyline line;
|
||||
line.append(Point(bed_bbox.min.x(), y));
|
||||
line.append(Point(bed_bbox.max.x(), y));
|
||||
axes_lines.push_back(line);
|
||||
}
|
||||
|
||||
// clip with a slightly grown expolygon because our lines lay on the contours and may get erroneously clipped
|
||||
Lines gridlines = to_lines(intersection_pl(axes_lines, offset(poly, (float)SCALED_EPSILON)));
|
||||
|
||||
// append bed contours
|
||||
Lines contour_lines = to_lines(poly);
|
||||
std::copy(contour_lines.begin(), contour_lines.end(), std::back_inserter(gridlines));
|
||||
|
||||
if (!m_gridlines.set_from_lines(gridlines, GROUND_Z))
|
||||
BOOST_LOG_TRIVIAL(error) << "Unable to create bed grid lines\n";
|
||||
}*/
|
||||
|
||||
// Try to match the print bed shape with the shape of an active profile. If such a match exists,
|
||||
// return the print bed model.
|
||||
std::tuple<Bed3D::Type, std::string, std::string> Bed3D::detect_type(const Pointfs& shape)
|
||||
{
|
||||
auto bundle = wxGetApp().preset_bundle;
|
||||
if (bundle != nullptr) {
|
||||
const Preset* curr = &bundle->printers.get_selected_preset();
|
||||
while (curr != nullptr) {
|
||||
if (curr->config.has("printable_area")) {
|
||||
std::string texture_filename, model_filename;
|
||||
if (shape == dynamic_cast<const ConfigOptionPoints*>(curr->config.option("printable_area"))->values) {
|
||||
if (curr->is_system)
|
||||
model_filename = PresetUtils::system_printer_bed_model(*curr);
|
||||
else {
|
||||
auto *printer_model = curr->config.opt<ConfigOptionString>("printer_model");
|
||||
if (printer_model != nullptr && ! printer_model->value.empty()) {
|
||||
model_filename = bundle->get_stl_model_for_printer_model(printer_model->value);
|
||||
}
|
||||
}
|
||||
//std::string model_filename = PresetUtils::system_printer_bed_model(*curr);
|
||||
//std::string texture_filename = PresetUtils::system_printer_bed_texture(*curr);
|
||||
if (!model_filename.empty())
|
||||
return { Type::System, model_filename, texture_filename };
|
||||
}
|
||||
}
|
||||
|
||||
curr = bundle->printers.get_preset_parent(*curr);
|
||||
}
|
||||
}
|
||||
|
||||
return { Type::Custom, {}, {} };
|
||||
}
|
||||
|
||||
void Bed3D::render_axes() const
|
||||
{
|
||||
if (m_build_volume.valid())
|
||||
m_axes.render();
|
||||
}
|
||||
|
||||
void Bed3D::render_system(GLCanvas3D& canvas, bool bottom) const
|
||||
{
|
||||
if (!bottom)
|
||||
render_model();
|
||||
|
||||
/*if (show_texture)
|
||||
render_texture(bottom, canvas);*/
|
||||
}
|
||||
|
||||
/*void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
|
||||
{
|
||||
GLTexture* texture = const_cast<GLTexture*>(&m_texture);
|
||||
GLTexture* temp_texture = const_cast<GLTexture*>(&m_temp_texture);
|
||||
|
||||
if (m_texture_filename.empty()) {
|
||||
texture->reset();
|
||||
render_default(bottom, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (texture->get_id() == 0 || texture->get_source() != m_texture_filename) {
|
||||
texture->reset();
|
||||
|
||||
if (boost::algorithm::iends_with(m_texture_filename, ".svg")) {
|
||||
// use higher resolution images if graphic card and opengl version allow
|
||||
GLint max_tex_size = OpenGLManager::get_gl_info().get_max_tex_size();
|
||||
if (temp_texture->get_id() == 0 || temp_texture->get_source() != m_texture_filename) {
|
||||
// generate a temporary lower resolution texture to show while no main texture levels have been compressed
|
||||
if (!temp_texture->load_from_svg_file(m_texture_filename, false, false, false, max_tex_size / 8)) {
|
||||
render_default(bottom, false);
|
||||
return;
|
||||
}
|
||||
canvas.request_extra_frame();
|
||||
}
|
||||
|
||||
// starts generating the main texture, compression will run asynchronously
|
||||
if (!texture->load_from_svg_file(m_texture_filename, true, true, true, max_tex_size)) {
|
||||
render_default(bottom, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (boost::algorithm::iends_with(m_texture_filename, ".png")) {
|
||||
// generate a temporary lower resolution texture to show while no main texture levels have been compressed
|
||||
if (temp_texture->get_id() == 0 || temp_texture->get_source() != m_texture_filename) {
|
||||
if (!temp_texture->load_from_file(m_texture_filename, false, GLTexture::None, false)) {
|
||||
render_default(bottom, false);
|
||||
return;
|
||||
}
|
||||
canvas.request_extra_frame();
|
||||
}
|
||||
|
||||
// starts generating the main texture, compression will run asynchronously
|
||||
if (!texture->load_from_file(m_texture_filename, true, GLTexture::MultiThreaded, true)) {
|
||||
render_default(bottom, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
render_default(bottom, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (texture->unsent_compressed_data_available()) {
|
||||
// sends to gpu the already available compressed levels of the main texture
|
||||
texture->send_compressed_data_to_gpu();
|
||||
|
||||
// the temporary texture is not needed anymore, reset it
|
||||
if (temp_texture->get_id() != 0)
|
||||
temp_texture->reset();
|
||||
|
||||
canvas.request_extra_frame();
|
||||
}
|
||||
|
||||
if (m_triangles.get_vertices_count() > 0) {
|
||||
GLShaderProgram* shader = wxGetApp().get_shader("printbed");
|
||||
if (shader != nullptr) {
|
||||
shader->start_using();
|
||||
shader->set_uniform("transparent_background", bottom);
|
||||
shader->set_uniform("svg_source", boost::algorithm::iends_with(m_texture.get_source(), ".svg"));
|
||||
|
||||
unsigned int* vbo_id = const_cast<unsigned int*>(&m_vbo_id);
|
||||
|
||||
if (*vbo_id == 0) {
|
||||
glsafe(::glGenBuffers(1, vbo_id));
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, *vbo_id));
|
||||
glsafe(::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)m_triangles.get_vertices_data_size(), (const GLvoid*)m_triangles.get_vertices_data(), GL_STATIC_DRAW));
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||
}
|
||||
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
if (bottom)
|
||||
glsafe(::glDepthMask(GL_FALSE));
|
||||
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
if (bottom)
|
||||
glsafe(::glFrontFace(GL_CW));
|
||||
|
||||
unsigned int stride = m_triangles.get_vertex_data_size();
|
||||
|
||||
GLint position_id = shader->get_attrib_location("v_position");
|
||||
GLint tex_coords_id = shader->get_attrib_location("v_tex_coords");
|
||||
|
||||
// show the temporary texture while no compressed data is available
|
||||
GLuint tex_id = (GLuint)temp_texture->get_id();
|
||||
if (tex_id == 0)
|
||||
tex_id = (GLuint)texture->get_id();
|
||||
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, tex_id));
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, *vbo_id));
|
||||
|
||||
if (position_id != -1) {
|
||||
glsafe(::glEnableVertexAttribArray(position_id));
|
||||
glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_triangles.get_position_offset()));
|
||||
}
|
||||
if (tex_coords_id != -1) {
|
||||
glsafe(::glEnableVertexAttribArray(tex_coords_id));
|
||||
glsafe(::glVertexAttribPointer(tex_coords_id, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_triangles.get_tex_coords_offset()));
|
||||
}
|
||||
|
||||
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)m_triangles.get_vertices_count()));
|
||||
|
||||
if (tex_coords_id != -1)
|
||||
glsafe(::glDisableVertexAttribArray(tex_coords_id));
|
||||
|
||||
if (position_id != -1)
|
||||
glsafe(::glDisableVertexAttribArray(position_id));
|
||||
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
|
||||
if (bottom)
|
||||
glsafe(::glFrontFace(GL_CCW));
|
||||
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
if (bottom)
|
||||
glsafe(::glDepthMask(GL_TRUE));
|
||||
|
||||
shader->stop_using();
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
//BBS: add part plate related logic
|
||||
void Bed3D::update_model_offset() const
|
||||
{
|
||||
// move the model so that its origin (0.0, 0.0, 0.0) goes into the bed shape center and a bit down to avoid z-fighting with the texture quad
|
||||
Vec3d shift = m_extended_bounding_box.center();
|
||||
shift(2) = -0.03;
|
||||
Vec3d* model_offset_ptr = const_cast<Vec3d*>(&m_model_offset);
|
||||
*model_offset_ptr = shift;
|
||||
//BBS: TODO: hack for current stl for BBL printer
|
||||
if (std::string::npos != m_model_filename.find("bbl-3dp-"))
|
||||
{
|
||||
(*model_offset_ptr)(0) -= 128.f;
|
||||
(*model_offset_ptr)(1) -= 128.f;
|
||||
(*model_offset_ptr)(2) = -0.41 + GROUND_Z;
|
||||
}
|
||||
|
||||
// update extended bounding box
|
||||
const_cast<BoundingBoxf3&>(m_extended_bounding_box) = calc_extended_bounding_box();
|
||||
}
|
||||
|
||||
void Bed3D::render_model() const
|
||||
{
|
||||
if (m_model_filename.empty())
|
||||
return;
|
||||
|
||||
GLModel* model = const_cast<GLModel*>(&m_model);
|
||||
|
||||
if (model->get_filename() != m_model_filename && model->init_from_file(m_model_filename)) {
|
||||
model->set_color(-1, DEFAULT_MODEL_COLOR);
|
||||
|
||||
update_model_offset();
|
||||
}
|
||||
|
||||
if (!model->get_filename().empty()) {
|
||||
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
|
||||
if (shader != nullptr) {
|
||||
shader->start_using();
|
||||
shader->set_uniform("emission_factor", 0.0f);
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glTranslated(m_model_offset.x(), m_model_offset.y(), m_model_offset.z()));
|
||||
model->render();
|
||||
glsafe(::glPopMatrix());
|
||||
shader->stop_using();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom) const
|
||||
{
|
||||
if (m_model_filename.empty()) {
|
||||
render_default(bottom);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bottom)
|
||||
render_model();
|
||||
|
||||
/*if (show_texture)
|
||||
render_texture(bottom, canvas);*/
|
||||
}
|
||||
|
||||
void Bed3D::render_default(bool bottom) const
|
||||
{
|
||||
/*const_cast<GLTexture*>(&m_texture)->reset();
|
||||
|
||||
unsigned int triangles_vcount = m_triangles.get_vertices_count();
|
||||
if (triangles_vcount > 0) {
|
||||
bool has_model = !m_model.get_filename().empty();
|
||||
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
|
||||
|
||||
if (!has_model && !bottom) {
|
||||
// draw background
|
||||
glsafe(::glDepthMask(GL_FALSE));
|
||||
glsafe(::glColor4fv(picking ? PICKING_MODEL_COLOR.data() : DEFAULT_MODEL_COLOR.data()));
|
||||
glsafe(::glNormal3d(0.0f, 0.0f, 1.0f));
|
||||
glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_vertices_data()));
|
||||
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount));
|
||||
glsafe(::glDepthMask(GL_TRUE));
|
||||
}
|
||||
|
||||
if (!picking) {
|
||||
// draw grid
|
||||
glsafe(::glLineWidth(1.5f * m_scale_factor));
|
||||
if (has_model && !bottom)
|
||||
glsafe(::glColor4f(0.9f, 0.9f, 0.9f, 1.0f));
|
||||
else
|
||||
glsafe(::glColor4f(0.9f, 0.9f, 0.9f, 0.6f));
|
||||
glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_gridlines.get_vertices_data()));
|
||||
glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)m_gridlines.get_vertices_count()));
|
||||
}
|
||||
|
||||
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
|
||||
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
}*/
|
||||
}
|
||||
|
||||
void Bed3D::release_VBOs()
|
||||
{
|
||||
if (m_vbo_id > 0) {
|
||||
glsafe(::glDeleteBuffers(1, &m_vbo_id));
|
||||
m_vbo_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
166
src/slic3r/GUI/3DBed.hpp
Normal file
166
src/slic3r/GUI/3DBed.hpp
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
#ifndef slic3r_3DBed_hpp_
|
||||
#define slic3r_3DBed_hpp_
|
||||
|
||||
//#include "GLTexture.hpp"
|
||||
#include "3DScene.hpp"
|
||||
#include "GLModel.hpp"
|
||||
|
||||
#include <libslic3r/BuildVolume.hpp>
|
||||
|
||||
#include <tuple>
|
||||
#include <array>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
class GLCanvas3D;
|
||||
|
||||
class GeometryBuffer
|
||||
{
|
||||
struct Vertex
|
||||
{
|
||||
Vec3f position{ Vec3f::Zero() };
|
||||
Vec2f tex_coords{ Vec2f::Zero() };
|
||||
};
|
||||
|
||||
std::vector<Vertex> m_vertices;
|
||||
|
||||
public:
|
||||
bool set_from_triangles(const std::vector<Vec2f> &triangles, float z);
|
||||
bool set_from_lines(const Lines& lines, float z);
|
||||
//BBS: add APi to set from 3d lines
|
||||
bool set_from_3d_Lines(const Lines3& lines);
|
||||
|
||||
const float* get_vertices_data() const;
|
||||
unsigned int get_vertices_data_size() const { return (unsigned int)m_vertices.size() * get_vertex_data_size(); }
|
||||
unsigned int get_vertex_data_size() const { return (unsigned int)(5 * sizeof(float)); }
|
||||
size_t get_position_offset() const { return 0; }
|
||||
size_t get_tex_coords_offset() const { return (size_t)(3 * sizeof(float)); }
|
||||
unsigned int get_vertices_count() const { return (unsigned int)m_vertices.size(); }
|
||||
};
|
||||
|
||||
class Bed3D
|
||||
{
|
||||
public:
|
||||
static std::array<float, 4> AXIS_X_COLOR;
|
||||
static std::array<float, 4> AXIS_Y_COLOR;
|
||||
static std::array<float, 4> AXIS_Z_COLOR;
|
||||
|
||||
static void update_render_colors();
|
||||
static void load_render_colors();
|
||||
|
||||
class Axes
|
||||
{
|
||||
public:
|
||||
static const float DefaultStemRadius;
|
||||
static const float DefaultStemLength;
|
||||
static const float DefaultTipRadius;
|
||||
static const float DefaultTipLength;
|
||||
|
||||
private:
|
||||
Vec3d m_origin{ Vec3d::Zero() };
|
||||
float m_stem_length{ DefaultStemLength };
|
||||
GLModel m_arrow;
|
||||
|
||||
public:
|
||||
const Vec3d& get_origin() const { return m_origin; }
|
||||
void set_origin(const Vec3d& origin) { m_origin = origin; }
|
||||
void set_stem_length(float length) {
|
||||
m_stem_length = length;
|
||||
m_arrow.reset();
|
||||
}
|
||||
float get_total_length() const { return m_stem_length + DefaultTipLength; }
|
||||
void render() const;
|
||||
};
|
||||
|
||||
public:
|
||||
enum class Type : unsigned char
|
||||
{
|
||||
// The print bed model and texture are available from some printer preset.
|
||||
System,
|
||||
// The print bed model is unknown, thus it is rendered procedurally.
|
||||
Custom
|
||||
};
|
||||
|
||||
private:
|
||||
BuildVolume m_build_volume;
|
||||
Type m_type{ Type::System };
|
||||
//std::string m_texture_filename;
|
||||
std::string m_model_filename;
|
||||
// Print volume bounding box exteded with axes and model.
|
||||
BoundingBoxf3 m_extended_bounding_box;
|
||||
// Slightly expanded print bed polygon, for collision detection.
|
||||
//Polygon m_polygon;
|
||||
//GeometryBuffer m_triangles;
|
||||
//GeometryBuffer m_gridlines;
|
||||
//GLTexture m_texture;
|
||||
// temporary texture shown until the main texture has still no levels compressed
|
||||
//GLTexture m_temp_texture;
|
||||
GLModel m_model;
|
||||
Vec3d m_model_offset{ Vec3d::Zero() };
|
||||
unsigned int m_vbo_id{ 0 };
|
||||
Axes m_axes;
|
||||
|
||||
float m_scale_factor{ 1.0f };
|
||||
//BBS: add part plate related logic
|
||||
Vec2d m_position{ Vec2d::Zero() };
|
||||
std::vector<Vec2d> m_bed_shape;
|
||||
|
||||
public:
|
||||
Bed3D() = default;
|
||||
~Bed3D() { release_VBOs(); }
|
||||
|
||||
// Update print bed model from configuration.
|
||||
// Return true if the bed shape changed, so the calee will update the UI.
|
||||
//FIXME if the build volume max print height is updated, this function still returns zero
|
||||
// as this class does not use it, thus there is no need to update the UI.
|
||||
// BBS
|
||||
bool set_shape(const Pointfs& printable_area, const double printable_height, const std::string& custom_model, bool force_as_custom = false,
|
||||
const Vec2d position = Vec2d::Zero(), bool with_reset = true);
|
||||
|
||||
void set_position(Vec2d& position);
|
||||
void set_axes_mode(bool origin);
|
||||
const Vec2d& get_position() const { return m_position; }
|
||||
|
||||
// Build volume geometry for various collision detection tasks.
|
||||
const BuildVolume& build_volume() const { return m_build_volume; }
|
||||
|
||||
// Was the model provided, or was it generated procedurally?
|
||||
Type get_type() const { return m_type; }
|
||||
// Was the model generated procedurally?
|
||||
bool is_custom() const { return m_type == Type::Custom; }
|
||||
|
||||
// Bounding box around the print bed, axes and model, for rendering.
|
||||
const BoundingBoxf3& extended_bounding_box() const { return m_extended_bounding_box; }
|
||||
|
||||
// Check against an expanded 2d bounding box.
|
||||
//FIXME shall one check against the real build volume?
|
||||
bool contains(const Point& point) const;
|
||||
Point point_projection(const Point& point) const;
|
||||
|
||||
void render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes);
|
||||
//void render_for_picking(GLCanvas3D& canvas, bool bottom, float scale_factor);
|
||||
|
||||
private:
|
||||
//BBS: add partplate related logic
|
||||
// Calculate an extended bounding box from axes and current model for visualization purposes.
|
||||
BoundingBoxf3 calc_extended_bounding_box(bool consider_model_offset = true) const;
|
||||
void calc_triangles(const ExPolygon& poly);
|
||||
void calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox);
|
||||
void update_model_offset() const;
|
||||
static std::tuple<Type, std::string, std::string> detect_type(const Pointfs& shape);
|
||||
void render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
|
||||
bool show_axes);
|
||||
void render_axes() const;
|
||||
void render_system(GLCanvas3D& canvas, bool bottom) const;
|
||||
//void render_texture(bool bottom, GLCanvas3D& canvas) const;
|
||||
void render_model() const;
|
||||
void render_custom(GLCanvas3D& canvas, bool bottom) const;
|
||||
void render_default(bool bottom) const;
|
||||
void release_VBOs();
|
||||
};
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
|
||||
#endif // slic3r_3DBed_hpp_
|
||||
2195
src/slic3r/GUI/3DScene.cpp
Normal file
2195
src/slic3r/GUI/3DScene.cpp
Normal file
File diff suppressed because it is too large
Load diff
724
src/slic3r/GUI/3DScene.hpp
Normal file
724
src/slic3r/GUI/3DScene.hpp
Normal file
|
|
@ -0,0 +1,724 @@
|
|||
#ifndef slic3r_3DScene_hpp_
|
||||
#define slic3r_3DScene_hpp_
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include "libslic3r/Line.hpp"
|
||||
#include "libslic3r/TriangleMesh.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
// BBS
|
||||
#include "libslic3r/ObjectID.hpp"
|
||||
|
||||
#include "GLModel.hpp"
|
||||
#include "GLShader.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define HAS_GLSAFE
|
||||
#endif // NDEBUG
|
||||
|
||||
#ifdef HAS_GLSAFE
|
||||
extern void glAssertRecentCallImpl(const char *file_name, unsigned int line, const char *function_name);
|
||||
inline void glAssertRecentCall() { glAssertRecentCallImpl(__FILE__, __LINE__, __FUNCTION__); }
|
||||
#define glsafe(cmd) do { cmd; glAssertRecentCallImpl(__FILE__, __LINE__, __FUNCTION__); } while (false)
|
||||
#define glcheck() do { glAssertRecentCallImpl(__FILE__, __LINE__, __FUNCTION__); } while (false)
|
||||
#else // HAS_GLSAFE
|
||||
inline void glAssertRecentCall() { }
|
||||
#define glsafe(cmd) cmd
|
||||
#define glcheck()
|
||||
#endif // HAS_GLSAFE
|
||||
extern std::vector<std::array<float, 4>> get_extruders_colors();
|
||||
|
||||
namespace Slic3r {
|
||||
class SLAPrintObject;
|
||||
enum SLAPrintObjectStep : unsigned int;
|
||||
class BuildVolume;
|
||||
class DynamicPrintConfig;
|
||||
class ExtrusionPath;
|
||||
class ExtrusionMultiPath;
|
||||
class ExtrusionLoop;
|
||||
class ExtrusionEntity;
|
||||
class ExtrusionEntityCollection;
|
||||
class ModelObject;
|
||||
class ModelVolume;
|
||||
class GLShaderProgram;
|
||||
enum ModelInstanceEPrintVolumeState : unsigned char;
|
||||
|
||||
using ModelObjectPtrs = std::vector<ModelObject*>;
|
||||
|
||||
// Return appropriate color based on the ModelVolume.
|
||||
std::array<float, 4> color_from_model_volume(const ModelVolume& model_volume);
|
||||
|
||||
// A container for interleaved arrays of 3D vertices and normals,
|
||||
// possibly indexed by triangles and / or quads.
|
||||
class GLIndexedVertexArray {
|
||||
public:
|
||||
// Only Eigen types of Nx16 size are vectorized. This bounding box will not be vectorized.
|
||||
static_assert(sizeof(Eigen::AlignedBox<float, 3>) == 24, "Eigen::AlignedBox<float, 3> is not being vectorized, thus it does not need to be aligned");
|
||||
using BoundingBox = Eigen::AlignedBox<float, 3>;
|
||||
|
||||
GLIndexedVertexArray() { m_bounding_box.setEmpty(); }
|
||||
GLIndexedVertexArray(const GLIndexedVertexArray &rhs) :
|
||||
vertices_and_normals_interleaved(rhs.vertices_and_normals_interleaved),
|
||||
triangle_indices(rhs.triangle_indices),
|
||||
quad_indices(rhs.quad_indices),
|
||||
m_bounding_box(rhs.m_bounding_box)
|
||||
{ assert(!rhs.has_VBOs()); m_bounding_box.setEmpty(); }
|
||||
GLIndexedVertexArray(GLIndexedVertexArray &&rhs) :
|
||||
vertices_and_normals_interleaved(std::move(rhs.vertices_and_normals_interleaved)),
|
||||
triangle_indices(std::move(rhs.triangle_indices)),
|
||||
quad_indices(std::move(rhs.quad_indices)),
|
||||
m_bounding_box(rhs.m_bounding_box)
|
||||
{ assert(! rhs.has_VBOs()); }
|
||||
|
||||
~GLIndexedVertexArray() { release_geometry(); }
|
||||
|
||||
GLIndexedVertexArray& operator=(const GLIndexedVertexArray &rhs)
|
||||
{
|
||||
assert(vertices_and_normals_interleaved_VBO_id == 0);
|
||||
assert(triangle_indices_VBO_id == 0);
|
||||
assert(quad_indices_VBO_id == 0);
|
||||
assert(rhs.vertices_and_normals_interleaved_VBO_id == 0);
|
||||
assert(rhs.triangle_indices_VBO_id == 0);
|
||||
assert(rhs.quad_indices_VBO_id == 0);
|
||||
this->vertices_and_normals_interleaved = rhs.vertices_and_normals_interleaved;
|
||||
this->triangle_indices = rhs.triangle_indices;
|
||||
this->quad_indices = rhs.quad_indices;
|
||||
this->m_bounding_box = rhs.m_bounding_box;
|
||||
this->vertices_and_normals_interleaved_size = rhs.vertices_and_normals_interleaved_size;
|
||||
this->triangle_indices_size = rhs.triangle_indices_size;
|
||||
this->quad_indices_size = rhs.quad_indices_size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
GLIndexedVertexArray& operator=(GLIndexedVertexArray &&rhs)
|
||||
{
|
||||
assert(vertices_and_normals_interleaved_VBO_id == 0);
|
||||
assert(triangle_indices_VBO_id == 0);
|
||||
assert(quad_indices_VBO_id == 0);
|
||||
assert(rhs.vertices_and_normals_interleaved_VBO_id == 0);
|
||||
assert(rhs.triangle_indices_VBO_id == 0);
|
||||
assert(rhs.quad_indices_VBO_id == 0);
|
||||
this->vertices_and_normals_interleaved = std::move(rhs.vertices_and_normals_interleaved);
|
||||
this->triangle_indices = std::move(rhs.triangle_indices);
|
||||
this->quad_indices = std::move(rhs.quad_indices);
|
||||
this->m_bounding_box = rhs.m_bounding_box;
|
||||
this->vertices_and_normals_interleaved_size = rhs.vertices_and_normals_interleaved_size;
|
||||
this->triangle_indices_size = rhs.triangle_indices_size;
|
||||
this->quad_indices_size = rhs.quad_indices_size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Vertices and their normals, interleaved to be used by void glInterleavedArrays(GL_N3F_V3F, 0, x)
|
||||
std::vector<float> vertices_and_normals_interleaved;
|
||||
std::vector<int> triangle_indices;
|
||||
std::vector<int> quad_indices;
|
||||
|
||||
// When the geometry data is loaded into the graphics card as Vertex Buffer Objects,
|
||||
// the above mentioned std::vectors are cleared and the following variables keep their original length.
|
||||
size_t vertices_and_normals_interleaved_size{ 0 };
|
||||
size_t triangle_indices_size{ 0 };
|
||||
size_t quad_indices_size{ 0 };
|
||||
|
||||
// IDs of the Vertex Array Objects, into which the geometry has been loaded.
|
||||
// Zero if the VBOs are not sent to GPU yet.
|
||||
unsigned int vertices_and_normals_interleaved_VBO_id{ 0 };
|
||||
unsigned int triangle_indices_VBO_id{ 0 };
|
||||
unsigned int quad_indices_VBO_id{ 0 };
|
||||
|
||||
#if ENABLE_SMOOTH_NORMALS
|
||||
void load_mesh_full_shading(const TriangleMesh& mesh, bool smooth_normals = false);
|
||||
void load_mesh(const TriangleMesh& mesh, bool smooth_normals = false) { this->load_mesh_full_shading(mesh, smooth_normals); }
|
||||
#else
|
||||
void load_mesh_full_shading(const TriangleMesh& mesh);
|
||||
void load_mesh(const TriangleMesh& mesh) { this->load_mesh_full_shading(mesh); }
|
||||
#endif // ENABLE_SMOOTH_NORMALS
|
||||
|
||||
void load_its_flat_shading(const indexed_triangle_set &its);
|
||||
|
||||
inline bool has_VBOs() const { return vertices_and_normals_interleaved_VBO_id != 0; }
|
||||
|
||||
inline void reserve(size_t sz) {
|
||||
this->vertices_and_normals_interleaved.reserve(sz * 6);
|
||||
this->triangle_indices.reserve(sz * 3);
|
||||
this->quad_indices.reserve(sz * 4);
|
||||
}
|
||||
|
||||
inline void push_geometry(float x, float y, float z, float nx, float ny, float nz) {
|
||||
assert(this->vertices_and_normals_interleaved_VBO_id == 0);
|
||||
if (this->vertices_and_normals_interleaved_VBO_id != 0)
|
||||
return;
|
||||
|
||||
if (this->vertices_and_normals_interleaved.size() + 6 > this->vertices_and_normals_interleaved.capacity())
|
||||
this->vertices_and_normals_interleaved.reserve(next_highest_power_of_2(this->vertices_and_normals_interleaved.size() + 6));
|
||||
this->vertices_and_normals_interleaved.emplace_back(nx);
|
||||
this->vertices_and_normals_interleaved.emplace_back(ny);
|
||||
this->vertices_and_normals_interleaved.emplace_back(nz);
|
||||
this->vertices_and_normals_interleaved.emplace_back(x);
|
||||
this->vertices_and_normals_interleaved.emplace_back(y);
|
||||
this->vertices_and_normals_interleaved.emplace_back(z);
|
||||
|
||||
this->vertices_and_normals_interleaved_size = this->vertices_and_normals_interleaved.size();
|
||||
m_bounding_box.extend(Vec3f(x, y, z));
|
||||
};
|
||||
|
||||
inline void push_geometry(double x, double y, double z, double nx, double ny, double nz) {
|
||||
push_geometry(float(x), float(y), float(z), float(nx), float(ny), float(nz));
|
||||
}
|
||||
|
||||
template<typename Derived, typename Derived2>
|
||||
inline void push_geometry(const Eigen::MatrixBase<Derived>& p, const Eigen::MatrixBase<Derived2>& n) {
|
||||
push_geometry(float(p(0)), float(p(1)), float(p(2)), float(n(0)), float(n(1)), float(n(2)));
|
||||
}
|
||||
|
||||
inline void push_triangle(int idx1, int idx2, int idx3) {
|
||||
assert(this->vertices_and_normals_interleaved_VBO_id == 0);
|
||||
if (this->vertices_and_normals_interleaved_VBO_id != 0)
|
||||
return;
|
||||
|
||||
if (this->triangle_indices.size() + 3 > this->vertices_and_normals_interleaved.capacity())
|
||||
this->triangle_indices.reserve(next_highest_power_of_2(this->triangle_indices.size() + 3));
|
||||
this->triangle_indices.emplace_back(idx1);
|
||||
this->triangle_indices.emplace_back(idx2);
|
||||
this->triangle_indices.emplace_back(idx3);
|
||||
this->triangle_indices_size = this->triangle_indices.size();
|
||||
};
|
||||
|
||||
inline void push_quad(int idx1, int idx2, int idx3, int idx4) {
|
||||
assert(this->vertices_and_normals_interleaved_VBO_id == 0);
|
||||
if (this->vertices_and_normals_interleaved_VBO_id != 0)
|
||||
return;
|
||||
|
||||
if (this->quad_indices.size() + 4 > this->vertices_and_normals_interleaved.capacity())
|
||||
this->quad_indices.reserve(next_highest_power_of_2(this->quad_indices.size() + 4));
|
||||
this->quad_indices.emplace_back(idx1);
|
||||
this->quad_indices.emplace_back(idx2);
|
||||
this->quad_indices.emplace_back(idx3);
|
||||
this->quad_indices.emplace_back(idx4);
|
||||
this->quad_indices_size = this->quad_indices.size();
|
||||
};
|
||||
|
||||
// Finalize the initialization of the geometry & indices,
|
||||
// upload the geometry and indices to OpenGL VBO objects
|
||||
// and shrink the allocated data, possibly relasing it if it has been loaded into the VBOs.
|
||||
void finalize_geometry(bool opengl_initialized);
|
||||
// Release the geometry data, release OpenGL VBOs.
|
||||
void release_geometry();
|
||||
|
||||
void render() const;
|
||||
void render(const std::pair<size_t, size_t>& tverts_range, const std::pair<size_t, size_t>& qverts_range) const;
|
||||
|
||||
// Is there any geometry data stored?
|
||||
bool empty() const { return vertices_and_normals_interleaved_size == 0; }
|
||||
|
||||
void clear() {
|
||||
this->vertices_and_normals_interleaved.clear();
|
||||
this->triangle_indices.clear();
|
||||
this->quad_indices.clear();
|
||||
vertices_and_normals_interleaved_size = 0;
|
||||
triangle_indices_size = 0;
|
||||
quad_indices_size = 0;
|
||||
m_bounding_box.setEmpty();
|
||||
}
|
||||
|
||||
// Shrink the internal storage to tighly fit the data stored.
|
||||
void shrink_to_fit() {
|
||||
this->vertices_and_normals_interleaved.shrink_to_fit();
|
||||
this->triangle_indices.shrink_to_fit();
|
||||
this->quad_indices.shrink_to_fit();
|
||||
}
|
||||
|
||||
const BoundingBox& bounding_box() const { return m_bounding_box; }
|
||||
|
||||
// Return an estimate of the memory consumed by this class.
|
||||
size_t cpu_memory_used() const { return sizeof(*this) + vertices_and_normals_interleaved.capacity() * sizeof(float) + triangle_indices.capacity() * sizeof(int) + quad_indices.capacity() * sizeof(int); }
|
||||
// Return an estimate of the memory held by GPU vertex buffers.
|
||||
size_t gpu_memory_used() const
|
||||
{
|
||||
size_t memsize = 0;
|
||||
if (this->vertices_and_normals_interleaved_VBO_id != 0)
|
||||
memsize += this->vertices_and_normals_interleaved_size * 4;
|
||||
if (this->triangle_indices_VBO_id != 0)
|
||||
memsize += this->triangle_indices_size * 4;
|
||||
if (this->quad_indices_VBO_id != 0)
|
||||
memsize += this->quad_indices_size * 4;
|
||||
return memsize;
|
||||
}
|
||||
size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); }
|
||||
|
||||
private:
|
||||
BoundingBox m_bounding_box;
|
||||
};
|
||||
|
||||
class GLVolume {
|
||||
public:
|
||||
std::string name;
|
||||
|
||||
static std::array<float, 4> DISABLED_COLOR;
|
||||
static std::array<float, 4> SLA_SUPPORT_COLOR;
|
||||
static std::array<float, 4> SLA_PAD_COLOR;
|
||||
static std::array<float, 4> NEUTRAL_COLOR;
|
||||
static std::array<float, 4> UNPRINTABLE_COLOR;
|
||||
static std::array<std::array<float, 4>, 5> MODEL_COLOR;
|
||||
static std::array<float, 4> MODEL_MIDIFIER_COL;
|
||||
static std::array<float, 4> MODEL_NEGTIVE_COL;
|
||||
static std::array<float, 4> SUPPORT_ENFORCER_COL;
|
||||
static std::array<float, 4> SUPPORT_BLOCKER_COL;
|
||||
|
||||
static void update_render_colors();
|
||||
static void load_render_colors();
|
||||
|
||||
static float explosion_ratio;
|
||||
static float last_explosion_ratio;
|
||||
|
||||
enum EHoverState : unsigned char
|
||||
{
|
||||
HS_None,
|
||||
HS_Hover,
|
||||
HS_Select,
|
||||
HS_Deselect
|
||||
};
|
||||
|
||||
GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f);
|
||||
GLVolume(const std::array<float, 4>& rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {}
|
||||
|
||||
// BBS
|
||||
protected:
|
||||
Geometry::Transformation m_instance_transformation;
|
||||
Geometry::Transformation m_volume_transformation;
|
||||
// BBS
|
||||
Vec3d m_offset_to_assembly{ 0.0, 0.0, 0.0 };
|
||||
|
||||
// Shift in z required by sla supports+pad
|
||||
double m_sla_shift_z;
|
||||
// Bounding box of this volume, in unscaled coordinates.
|
||||
std::optional<BoundingBoxf3> m_transformed_bounding_box;
|
||||
// Convex hull of the volume, if any.
|
||||
std::shared_ptr<const TriangleMesh> m_convex_hull;
|
||||
// Bounding box of this volume, in unscaled coordinates.
|
||||
std::optional<BoundingBoxf3> m_transformed_convex_hull_bounding_box;
|
||||
// Bounding box of the non sinking part of this volume, in unscaled coordinates.
|
||||
std::optional<BoundingBoxf3> m_transformed_non_sinking_bounding_box;
|
||||
|
||||
class SinkingContours
|
||||
{
|
||||
static const float HalfWidth;
|
||||
GLVolume& m_parent;
|
||||
GUI::GLModel m_model;
|
||||
BoundingBoxf3 m_old_box;
|
||||
Vec3d m_shift{ Vec3d::Zero() };
|
||||
|
||||
public:
|
||||
SinkingContours(GLVolume& volume) : m_parent(volume) {}
|
||||
void render();
|
||||
|
||||
private:
|
||||
void update();
|
||||
};
|
||||
|
||||
SinkingContours m_sinking_contours;
|
||||
|
||||
public:
|
||||
// Color of the triangles / quads held by this volume.
|
||||
std::array<float, 4> color;
|
||||
// Color used to render this volume.
|
||||
std::array<float, 4> render_color;
|
||||
|
||||
struct CompositeID {
|
||||
CompositeID(int object_id, int volume_id, int instance_id) : object_id(object_id), volume_id(volume_id), instance_id(instance_id) {}
|
||||
CompositeID() : object_id(-1), volume_id(-1), instance_id(-1) {}
|
||||
// Object ID, which is equal to the index of the respective ModelObject in Model.objects array.
|
||||
int object_id;
|
||||
// Volume ID, which is equal to the index of the respective ModelVolume in ModelObject.volumes array.
|
||||
// If negative, it is an index of a geometry produced by the PrintObject for the respective ModelObject,
|
||||
// and which has no associated ModelVolume in ModelObject.volumes. For example, SLA supports.
|
||||
// Volume with a negative volume_id cannot be picked independently, it will pick the associated instance.
|
||||
int volume_id;
|
||||
// Instance ID, which is equal to the index of the respective ModelInstance in ModelObject.instances array.
|
||||
int instance_id;
|
||||
bool operator==(const CompositeID &rhs) const { return object_id == rhs.object_id && volume_id == rhs.volume_id && instance_id == rhs.instance_id; }
|
||||
bool operator!=(const CompositeID &rhs) const { return ! (*this == rhs); }
|
||||
bool operator< (const CompositeID &rhs) const
|
||||
{ return object_id < rhs.object_id || (object_id == rhs.object_id && (volume_id < rhs.volume_id || (volume_id == rhs.volume_id && instance_id < rhs.instance_id))); }
|
||||
};
|
||||
CompositeID composite_id;
|
||||
// Fingerprint of the source geometry. For ModelVolumes, it is the ModelVolume::ID and ModelInstanceID,
|
||||
// for generated volumes it is the timestamp generated by PrintState::invalidate() or PrintState::set_done(),
|
||||
// and the associated ModelInstanceID.
|
||||
// Valid geometry_id should always be positive.
|
||||
std::pair<size_t, size_t> geometry_id;
|
||||
// An ID containing the extruder ID (used to select color).
|
||||
int extruder_id;
|
||||
|
||||
// Various boolean flags.
|
||||
struct {
|
||||
// Is this object selected?
|
||||
bool selected : 1;
|
||||
// Is this object disabled from selection?
|
||||
bool disabled : 1;
|
||||
// Is this object printable?
|
||||
bool printable : 1;
|
||||
// Whether or not this volume is active for rendering
|
||||
bool is_active : 1;
|
||||
// Whether or not to use this volume when applying zoom_to_volumes()
|
||||
bool zoom_to_volumes : 1;
|
||||
// Wheter or not this volume is enabled for outside print volume detection in shader.
|
||||
bool shader_outside_printer_detection_enabled : 1;
|
||||
// Wheter or not this volume is outside print volume.
|
||||
bool is_outside : 1;
|
||||
// Wheter or not this volume has been generated from a modifier
|
||||
bool is_modifier : 1;
|
||||
// Wheter or not this volume has been generated from the wipe tower
|
||||
bool is_wipe_tower : 1;
|
||||
bool is_timelapse_wipe_tower : 1;
|
||||
// Wheter or not this volume has been generated from an extrusion path
|
||||
bool is_extrusion_path : 1;
|
||||
// Wheter or not to always render this volume using its own alpha
|
||||
bool force_transparent : 1;
|
||||
// Whether or not always use the volume's own color (not using SELECTED/HOVER/DISABLED/OUTSIDE)
|
||||
bool force_native_color : 1;
|
||||
// Whether or not render this volume in neutral
|
||||
bool force_neutral_color : 1;
|
||||
// Whether or not to force rendering of sinking contours
|
||||
bool force_sinking_contours : 1;
|
||||
};
|
||||
|
||||
// Is mouse or rectangle selection over this object to select/deselect it ?
|
||||
EHoverState hover;
|
||||
|
||||
// Interleaved triangles & normals with indexed triangles & quads.
|
||||
GLIndexedVertexArray indexed_vertex_array;
|
||||
// BBS
|
||||
mutable std::vector<GLIndexedVertexArray> mmuseg_ivas;
|
||||
mutable ObjectBase::Timestamp mmuseg_ts;
|
||||
|
||||
// Ranges of triangle and quad indices to be rendered.
|
||||
std::pair<size_t, size_t> tverts_range;
|
||||
std::pair<size_t, size_t> qverts_range;
|
||||
|
||||
// If the qverts or tverts contain thick extrusions, then offsets keeps pointers of the starts
|
||||
// of the extrusions per layer.
|
||||
std::vector<coordf_t> print_zs;
|
||||
// Offset into qverts & tverts, or offsets into indices stored into an OpenGL name_index_buffer.
|
||||
std::vector<size_t> offsets;
|
||||
|
||||
// Bounding box of this volume, in unscaled coordinates.
|
||||
BoundingBoxf3 bounding_box() const {
|
||||
BoundingBoxf3 out;
|
||||
if (! this->indexed_vertex_array.bounding_box().isEmpty()) {
|
||||
out.min = this->indexed_vertex_array.bounding_box().min().cast<double>();
|
||||
out.max = this->indexed_vertex_array.bounding_box().max().cast<double>();
|
||||
out.defined = true;
|
||||
};
|
||||
return out;
|
||||
}
|
||||
|
||||
void set_color(const std::array<float, 4>& rgba);
|
||||
void set_render_color(float r, float g, float b, float a);
|
||||
void set_render_color(const std::array<float, 4>& rgba);
|
||||
// Sets render color in dependence of current state
|
||||
void set_render_color();
|
||||
// set color according to model volume
|
||||
void set_color_from_model_volume(const ModelVolume& model_volume);
|
||||
|
||||
const Geometry::Transformation& get_instance_transformation() const { return m_instance_transformation; }
|
||||
void set_instance_transformation(const Geometry::Transformation& transformation) { m_instance_transformation = transformation; set_bounding_boxes_as_dirty(); }
|
||||
|
||||
const Vec3d& get_instance_offset() const { return m_instance_transformation.get_offset(); }
|
||||
double get_instance_offset(Axis axis) const { return m_instance_transformation.get_offset(axis); }
|
||||
|
||||
void set_instance_offset(const Vec3d& offset) { m_instance_transformation.set_offset(offset); set_bounding_boxes_as_dirty(); }
|
||||
void set_instance_offset(Axis axis, double offset) { m_instance_transformation.set_offset(axis, offset); set_bounding_boxes_as_dirty(); }
|
||||
|
||||
const Vec3d& get_instance_rotation() const { return m_instance_transformation.get_rotation(); }
|
||||
double get_instance_rotation(Axis axis) const { return m_instance_transformation.get_rotation(axis); }
|
||||
|
||||
void set_instance_rotation(const Vec3d& rotation) { m_instance_transformation.set_rotation(rotation); set_bounding_boxes_as_dirty(); }
|
||||
void set_instance_rotation(Axis axis, double rotation) { m_instance_transformation.set_rotation(axis, rotation); set_bounding_boxes_as_dirty(); }
|
||||
|
||||
Vec3d get_instance_scaling_factor() const { return m_instance_transformation.get_scaling_factor(); }
|
||||
double get_instance_scaling_factor(Axis axis) const { return m_instance_transformation.get_scaling_factor(axis); }
|
||||
|
||||
void set_instance_scaling_factor(const Vec3d& scaling_factor) { m_instance_transformation.set_scaling_factor(scaling_factor); set_bounding_boxes_as_dirty(); }
|
||||
void set_instance_scaling_factor(Axis axis, double scaling_factor) { m_instance_transformation.set_scaling_factor(axis, scaling_factor); set_bounding_boxes_as_dirty(); }
|
||||
|
||||
const Vec3d& get_instance_mirror() const { return m_instance_transformation.get_mirror(); }
|
||||
double get_instance_mirror(Axis axis) const { return m_instance_transformation.get_mirror(axis); }
|
||||
|
||||
void set_instance_mirror(const Vec3d& mirror) { m_instance_transformation.set_mirror(mirror); set_bounding_boxes_as_dirty(); }
|
||||
void set_instance_mirror(Axis axis, double mirror) { m_instance_transformation.set_mirror(axis, mirror); set_bounding_boxes_as_dirty(); }
|
||||
|
||||
const Geometry::Transformation& get_volume_transformation() const { return m_volume_transformation; }
|
||||
void set_volume_transformation(const Geometry::Transformation& transformation) { m_volume_transformation = transformation; set_bounding_boxes_as_dirty(); }
|
||||
|
||||
const Vec3d& get_volume_offset() const { return m_volume_transformation.get_offset(); }
|
||||
double get_volume_offset(Axis axis) const { return m_volume_transformation.get_offset(axis); }
|
||||
|
||||
void set_volume_offset(const Vec3d& offset) { m_volume_transformation.set_offset(offset); set_bounding_boxes_as_dirty(); }
|
||||
void set_volume_offset(Axis axis, double offset) { m_volume_transformation.set_offset(axis, offset); set_bounding_boxes_as_dirty(); }
|
||||
|
||||
const Vec3d& get_volume_rotation() const { return m_volume_transformation.get_rotation(); }
|
||||
double get_volume_rotation(Axis axis) const { return m_volume_transformation.get_rotation(axis); }
|
||||
|
||||
void set_volume_rotation(const Vec3d& rotation) { m_volume_transformation.set_rotation(rotation); set_bounding_boxes_as_dirty(); }
|
||||
void set_volume_rotation(Axis axis, double rotation) { m_volume_transformation.set_rotation(axis, rotation); set_bounding_boxes_as_dirty(); }
|
||||
|
||||
const Vec3d& get_volume_scaling_factor() const { return m_volume_transformation.get_scaling_factor(); }
|
||||
double get_volume_scaling_factor(Axis axis) const { return m_volume_transformation.get_scaling_factor(axis); }
|
||||
|
||||
void set_volume_scaling_factor(const Vec3d& scaling_factor) { m_volume_transformation.set_scaling_factor(scaling_factor); set_bounding_boxes_as_dirty(); }
|
||||
void set_volume_scaling_factor(Axis axis, double scaling_factor) { m_volume_transformation.set_scaling_factor(axis, scaling_factor); set_bounding_boxes_as_dirty(); }
|
||||
|
||||
const Vec3d& get_volume_mirror() const { return m_volume_transformation.get_mirror(); }
|
||||
double get_volume_mirror(Axis axis) const { return m_volume_transformation.get_mirror(axis); }
|
||||
|
||||
void set_volume_mirror(const Vec3d& mirror) { m_volume_transformation.set_mirror(mirror); set_bounding_boxes_as_dirty(); }
|
||||
void set_volume_mirror(Axis axis, double mirror) { m_volume_transformation.set_mirror(axis, mirror); set_bounding_boxes_as_dirty(); }
|
||||
|
||||
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(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)); }
|
||||
|
||||
void set_offset_to_assembly(const Vec3d& offset) { m_offset_to_assembly = offset; set_bounding_boxes_as_dirty(); }
|
||||
Vec3d get_offset_to_assembly() { return m_offset_to_assembly; }
|
||||
|
||||
int object_idx() const { return this->composite_id.object_id; }
|
||||
int volume_idx() const { return this->composite_id.volume_id; }
|
||||
int instance_idx() const { return this->composite_id.instance_id; }
|
||||
|
||||
Transform3d world_matrix() const;
|
||||
bool is_left_handed() const;
|
||||
|
||||
//BBS: world_matrix with scale factor
|
||||
Transform3d world_matrix(float scale_factor) const;
|
||||
|
||||
const BoundingBoxf3& transformed_bounding_box() const;
|
||||
// non-caching variant
|
||||
BoundingBoxf3 transformed_convex_hull_bounding_box(const Transform3d &trafo) const;
|
||||
// caching variant
|
||||
const BoundingBoxf3& transformed_convex_hull_bounding_box() const;
|
||||
// non-caching variant
|
||||
BoundingBoxf3 transformed_non_sinking_bounding_box(const Transform3d& trafo) const;
|
||||
// caching variant
|
||||
const BoundingBoxf3& transformed_non_sinking_bounding_box() const;
|
||||
// convex hull
|
||||
const TriangleMesh* convex_hull() const { return m_convex_hull.get(); }
|
||||
|
||||
bool empty() const { return this->indexed_vertex_array.empty(); }
|
||||
|
||||
void set_range(double low, double high);
|
||||
|
||||
//BBS: add outline related logic and add virtual specifier
|
||||
virtual void render(bool with_outline = false) const;
|
||||
|
||||
//BBS: add simple render function for thumbnail
|
||||
void simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_objects, std::vector<std::array<float, 4>>& extruder_colors) const;
|
||||
|
||||
void finalize_geometry(bool opengl_initialized) { this->indexed_vertex_array.finalize_geometry(opengl_initialized); }
|
||||
void release_geometry() { this->indexed_vertex_array.release_geometry(); }
|
||||
|
||||
void set_bounding_boxes_as_dirty() {
|
||||
m_transformed_bounding_box.reset();
|
||||
m_transformed_convex_hull_bounding_box.reset();
|
||||
m_transformed_non_sinking_bounding_box.reset();
|
||||
}
|
||||
|
||||
bool is_sla_support() const;
|
||||
bool is_sla_pad() const;
|
||||
|
||||
bool is_sinking() const;
|
||||
bool is_below_printbed() const;
|
||||
void render_sinking_contours();
|
||||
|
||||
// Return an estimate of the memory consumed by this class.
|
||||
size_t cpu_memory_used() const {
|
||||
//FIXME what to do wih m_convex_hull?
|
||||
return sizeof(*this) - sizeof(this->indexed_vertex_array) + this->indexed_vertex_array.cpu_memory_used() + this->print_zs.capacity() * sizeof(coordf_t) + this->offsets.capacity() * sizeof(size_t);
|
||||
}
|
||||
// Return an estimate of the memory held by GPU vertex buffers.
|
||||
size_t gpu_memory_used() const { return this->indexed_vertex_array.gpu_memory_used(); }
|
||||
size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); }
|
||||
};
|
||||
|
||||
// BBS
|
||||
class GLWipeTowerVolume : public GLVolume {
|
||||
public:
|
||||
GLWipeTowerVolume(const std::vector<std::array<float, 4>>& colors);
|
||||
virtual void render(bool with_outline = false) const;
|
||||
|
||||
std::vector<GLIndexedVertexArray> iva_per_colors;
|
||||
|
||||
private:
|
||||
std::vector<std::array<float, 4>> m_colors;
|
||||
};
|
||||
|
||||
typedef std::vector<GLVolume*> GLVolumePtrs;
|
||||
typedef std::pair<GLVolume*, std::pair<unsigned int, double>> GLVolumeWithIdAndZ;
|
||||
typedef std::vector<GLVolumeWithIdAndZ> GLVolumeWithIdAndZList;
|
||||
|
||||
class GLVolumeCollection
|
||||
{
|
||||
public:
|
||||
enum class ERenderType : unsigned char
|
||||
{
|
||||
Opaque,
|
||||
Transparent,
|
||||
All
|
||||
};
|
||||
|
||||
struct PrintVolume
|
||||
{
|
||||
// see: Bed3D::EShapeType
|
||||
int type{ 0 };
|
||||
// data contains:
|
||||
// Rectangle:
|
||||
// [0] = min.x, [1] = min.y, [2] = max.x, [3] = max.y
|
||||
// Circle:
|
||||
// [0] = center.x, [1] = center.y, [3] = radius
|
||||
std::array<float, 4> data;
|
||||
// [0] = min z, [1] = max z
|
||||
std::array<float, 2> zs;
|
||||
};
|
||||
|
||||
private:
|
||||
PrintVolume m_print_volume;
|
||||
PrintVolume m_render_volume;
|
||||
|
||||
// z range for clipping in shaders
|
||||
float m_z_range[2];
|
||||
|
||||
// plane coeffs for clipping in shaders
|
||||
float m_clipping_plane[4];
|
||||
|
||||
struct Slope
|
||||
{
|
||||
// toggle for slope rendering
|
||||
bool active{ false };
|
||||
float normal_z;
|
||||
};
|
||||
|
||||
Slope m_slope;
|
||||
bool m_show_sinking_contours = false;
|
||||
|
||||
public:
|
||||
GLVolumePtrs volumes;
|
||||
|
||||
GLVolumeCollection() {
|
||||
set_default_slope_normal_z();
|
||||
|
||||
//BBS init render volume
|
||||
m_render_volume.type = -1;
|
||||
}
|
||||
~GLVolumeCollection() { clear(); }
|
||||
|
||||
std::vector<int> load_object(
|
||||
const ModelObject *model_object,
|
||||
int obj_idx,
|
||||
const std::vector<int> &instance_idxs,
|
||||
const std::string &color_by,
|
||||
bool opengl_initialized);
|
||||
|
||||
int load_object_volume(
|
||||
const ModelObject *model_object,
|
||||
int obj_idx,
|
||||
int volume_idx,
|
||||
int instance_idx,
|
||||
const std::string &color_by,
|
||||
bool opengl_initialized);
|
||||
|
||||
// Load SLA auxiliary GLVolumes (for support trees or pad).
|
||||
void load_object_auxiliary(
|
||||
const SLAPrintObject *print_object,
|
||||
int obj_idx,
|
||||
// pairs of <instance_idx, print_instance_idx>
|
||||
const std::vector<std::pair<size_t, size_t>>& instances,
|
||||
SLAPrintObjectStep milestone,
|
||||
// Timestamp of the last change of the milestone
|
||||
size_t timestamp,
|
||||
bool opengl_initialized);
|
||||
|
||||
int load_wipe_tower_preview(
|
||||
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width, bool opengl_initialized);
|
||||
|
||||
GLVolume* new_toolpath_volume(const std::array<float, 4>& rgba, size_t reserve_vbo_floats = 0);
|
||||
GLVolume* new_nontoolpath_volume(const std::array<float, 4>& rgba, size_t reserve_vbo_floats = 0);
|
||||
|
||||
// Render the volumes by OpenGL.
|
||||
//BBS: add outline drawing logic
|
||||
void render(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func = std::function<bool(const GLVolume&)>(), bool with_outline = true) const;
|
||||
|
||||
// Finalize the initialization of the geometry & indices,
|
||||
// upload the geometry and indices to OpenGL VBO objects
|
||||
// and shrink the allocated data, possibly relasing it if it has been loaded into the VBOs.
|
||||
void finalize_geometry(bool opengl_initialized) { for (auto* v : volumes) v->finalize_geometry(opengl_initialized); }
|
||||
// Release the geometry data assigned to the volumes.
|
||||
// If OpenGL VBOs were allocated, an OpenGL context has to be active to release them.
|
||||
void release_geometry() { for (auto *v : volumes) v->release_geometry(); }
|
||||
// Clear the geometry
|
||||
void clear() { for (auto *v : volumes) delete v; volumes.clear(); }
|
||||
|
||||
bool empty() const { return volumes.empty(); }
|
||||
void set_range(double low, double high) { for (GLVolume *vol : this->volumes) vol->set_range(low, high); }
|
||||
|
||||
void set_print_volume(const PrintVolume& print_volume) { m_print_volume = print_volume; }
|
||||
|
||||
void set_z_range(float min_z, float max_z) { m_z_range[0] = min_z; m_z_range[1] = max_z; }
|
||||
void set_clipping_plane(const double* coeffs) { m_clipping_plane[0] = coeffs[0]; m_clipping_plane[1] = coeffs[1]; m_clipping_plane[2] = coeffs[2]; m_clipping_plane[3] = coeffs[3]; }
|
||||
|
||||
bool is_slope_active() const { return m_slope.active; }
|
||||
void set_slope_active(bool active) { m_slope.active = active; }
|
||||
|
||||
float get_slope_normal_z() const { return m_slope.normal_z; }
|
||||
void set_slope_normal_z(float normal_z) { m_slope.normal_z = normal_z; }
|
||||
void set_default_slope_normal_z() { m_slope.normal_z = -::cos(Geometry::deg2rad(90.0f - 45.0f)); }
|
||||
void set_show_sinking_contours(bool show) { m_show_sinking_contours = show; }
|
||||
|
||||
// returns true if all the volumes are completely contained in the print volume
|
||||
// returns the containment state in the given out_state, if non-null
|
||||
bool check_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state) const;
|
||||
void reset_outside_state();
|
||||
|
||||
void update_colors_by_extruder(const DynamicPrintConfig* config);
|
||||
|
||||
// Returns a vector containing the sorted list of all the print_zs of the volumes contained in this collection
|
||||
std::vector<double> get_current_print_zs(bool active_only) const;
|
||||
|
||||
// Return an estimate of the memory consumed by this class.
|
||||
size_t cpu_memory_used() const;
|
||||
// Return an estimate of the memory held by GPU vertex buffers.
|
||||
size_t gpu_memory_used() const;
|
||||
size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); }
|
||||
// Return CPU, GPU and total memory log line.
|
||||
std::string log_memory_info() const;
|
||||
|
||||
private:
|
||||
GLVolumeCollection(const GLVolumeCollection &other);
|
||||
GLVolumeCollection& operator=(const GLVolumeCollection &);
|
||||
};
|
||||
|
||||
GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func = nullptr);
|
||||
|
||||
struct _3DScene
|
||||
{
|
||||
static void thick_lines_to_verts(const Lines& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, double top_z, GLVolume& volume);
|
||||
static void thick_lines_to_verts(const Lines3& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, GLVolume& volume);
|
||||
static void extrusionentity_to_verts(const Polyline &polyline, float width, float height, float print_z, GLVolume& volume);
|
||||
static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, GLVolume& volume);
|
||||
static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GLVolume& volume);
|
||||
static void extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GLVolume& volume);
|
||||
static void extrusionentity_to_verts(const ExtrusionMultiPath& extrusion_multi_path, float print_z, const Point& copy, GLVolume& volume);
|
||||
static void extrusionentity_to_verts(const ExtrusionEntityCollection& extrusion_entity_collection, float print_z, const Point& copy, GLVolume& volume);
|
||||
static void extrusionentity_to_verts(const ExtrusionEntity* extrusion_entity, float print_z, const Point& copy, GLVolume& volume);
|
||||
static void polyline3_to_verts(const Polyline3& polyline, double width, double height, GLVolume& volume);
|
||||
static void point3_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
508
src/slic3r/GUI/AMSMaterialsSetting.cpp
Normal file
508
src/slic3r/GUI/AMSMaterialsSetting.cpp
Normal file
|
|
@ -0,0 +1,508 @@
|
|||
#include "AMSMaterialsSetting.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "libslic3r/Preset.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
AMSMaterialsSetting::AMSMaterialsSetting(wxWindow *parent, wxWindowID id): wxPopupTransientWindow(parent, id)
|
||||
{
|
||||
create();
|
||||
}
|
||||
|
||||
void AMSMaterialsSetting::create()
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
wxBoxSizer *m_sizer_main = new wxBoxSizer(wxVERTICAL);
|
||||
wxBoxSizer *m_sizer_body = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_panel_body = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(AMS_MATERIALS_SETTING_BODY_WIDTH, -1), wxTAB_TRAVERSAL);
|
||||
m_panel_body->SetBackgroundColour(*wxWHITE);
|
||||
wxBoxSizer *m_sizer_filament = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
m_title_filament = new wxStaticText(m_panel_body, wxID_ANY, _L("Filament"), wxDefaultPosition, wxSize(AMS_MATERIALS_SETTING_LABEL_WIDTH, -1), 0);
|
||||
m_title_filament->SetFont(::Label::Body_13);
|
||||
m_title_filament->SetForegroundColour(AMS_MATERIALS_SETTING_GREY800);
|
||||
m_title_filament->Wrap(-1);
|
||||
m_sizer_filament->Add(m_title_filament, 0, wxALIGN_CENTER, 0);
|
||||
|
||||
m_sizer_filament->Add(0, 0, 0, wxEXPAND, 0);
|
||||
|
||||
m_comboBox_filament = new ::ComboBox(m_panel_body, wxID_ANY, wxEmptyString, wxDefaultPosition, AMS_MATERIALS_SETTING_COMBOX_WIDTH, 0, nullptr, wxCB_READONLY);
|
||||
m_sizer_filament->Add(m_comboBox_filament, 0, wxALIGN_CENTER, 0);
|
||||
|
||||
wxBoxSizer *m_sizer_colour = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
m_title_colour = new wxStaticText(m_panel_body, wxID_ANY, _L("Colour"), wxDefaultPosition, wxSize(AMS_MATERIALS_SETTING_LABEL_WIDTH, -1), 0);
|
||||
m_title_colour->SetFont(::Label::Body_13);
|
||||
m_title_colour->SetForegroundColour(AMS_MATERIALS_SETTING_GREY800);
|
||||
m_title_colour->Wrap(-1);
|
||||
m_sizer_colour->Add(m_title_colour, 0, wxALIGN_CENTER, 0);
|
||||
|
||||
m_sizer_colour->Add(0, 0, 0, wxEXPAND, 0);
|
||||
|
||||
m_clrData = new wxColourData();
|
||||
m_clrData->SetChooseFull(true);
|
||||
m_clrData->SetChooseAlpha(false);
|
||||
m_clr_picker = new wxButton(m_panel_body, wxID_ANY, "", wxDefaultPosition, wxSize(FromDIP(20), FromDIP(20)), wxBU_EXACTFIT | wxBORDER_NONE);
|
||||
m_clr_picker->Bind(wxEVT_BUTTON, &AMSMaterialsSetting::on_clr_picker, this);
|
||||
m_sizer_colour->Add(m_clr_picker, 0, 0, 0);
|
||||
|
||||
|
||||
m_panel_SN = new wxPanel(m_panel_body, wxID_ANY);
|
||||
wxBoxSizer *m_sizer_SN = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
auto m_title_SN = new wxStaticText(m_panel_SN, wxID_ANY, _L("SN"), wxDefaultPosition, wxSize(AMS_MATERIALS_SETTING_LABEL_WIDTH, -1), 0);
|
||||
m_title_SN->SetFont(::Label::Body_13);
|
||||
m_title_SN->SetForegroundColour(AMS_MATERIALS_SETTING_GREY800);
|
||||
m_title_SN->Wrap(-1);
|
||||
m_sizer_SN->Add(m_title_SN, 0, wxALIGN_CENTER, 0);
|
||||
|
||||
m_sizer_SN->Add(0, 0, 0, wxEXPAND, 0);
|
||||
|
||||
m_sn_number = new wxStaticText(m_panel_SN, wxID_ANY, wxEmptyString, wxDefaultPosition, AMS_MATERIALS_SETTING_COMBOX_WIDTH);
|
||||
m_sizer_SN->Add(m_sn_number, 0, wxALIGN_CENTER, 0);
|
||||
m_panel_SN->SetSizer(m_sizer_SN);
|
||||
m_panel_SN->Layout();
|
||||
m_panel_SN->Fit();
|
||||
|
||||
wxBoxSizer *m_sizer_temperature = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_title_temperature = new wxStaticText(m_panel_body, wxID_ANY, _L("Nozzle\nTemperature"), wxDefaultPosition, wxSize(AMS_MATERIALS_SETTING_LABEL_WIDTH, -1), 0);
|
||||
m_title_temperature->SetFont(::Label::Body_13);
|
||||
m_title_temperature->SetForegroundColour(AMS_MATERIALS_SETTING_GREY800);
|
||||
m_title_temperature->Wrap(-1);
|
||||
m_sizer_temperature->Add(m_title_temperature, 0, wxALIGN_CENTER, 0);
|
||||
|
||||
m_sizer_temperature->Add(0, 0, 0, wxEXPAND, 0);
|
||||
|
||||
wxBoxSizer *sizer_other = new wxBoxSizer(wxVERTICAL);
|
||||
wxBoxSizer *sizer_tempinput = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
|
||||
m_input_nozzle_max = new ::TextInput(m_panel_body, wxEmptyString, wxEmptyString, wxEmptyString, wxDefaultPosition, AMS_MATERIALS_SETTING_INPUT_SIZE,
|
||||
wxTE_CENTRE | wxTE_PROCESS_ENTER);
|
||||
m_input_nozzle_max->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
m_input_nozzle_max->GetTextCtrl()->SetSize(wxSize(-1, FromDIP(20)));
|
||||
auto bitmap_max_degree = new wxStaticBitmap(m_panel_body, -1, create_scaled_bitmap("degree", nullptr, 16), wxDefaultPosition, wxDefaultSize);
|
||||
|
||||
m_input_nozzle_min = new ::TextInput(m_panel_body, wxEmptyString, wxEmptyString, wxEmptyString, wxDefaultPosition, AMS_MATERIALS_SETTING_INPUT_SIZE,
|
||||
wxTE_CENTRE | wxTE_PROCESS_ENTER);
|
||||
m_input_nozzle_min->GetTextCtrl()->SetSize(wxSize(-1, FromDIP(20)));
|
||||
m_input_nozzle_min->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
auto bitmap_min_degree = new wxStaticBitmap(m_panel_body, -1, create_scaled_bitmap("degree", nullptr, 16), wxDefaultPosition, wxDefaultSize);
|
||||
|
||||
sizer_tempinput->Add(m_input_nozzle_max, 1, wxALIGN_CENTER, 0);
|
||||
sizer_tempinput->Add(bitmap_min_degree, 0, wxALIGN_CENTER, 0);
|
||||
sizer_tempinput->Add(FromDIP(10), 0, wxEXPAND, 0);
|
||||
sizer_tempinput->Add(m_input_nozzle_min, 1, wxALIGN_CENTER, 0);
|
||||
sizer_tempinput->Add(bitmap_max_degree, 0, wxALIGN_CENTER, 0);
|
||||
|
||||
wxBoxSizer *sizer_temp_txt = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto m_title_max = new wxStaticText(m_panel_body, wxID_ANY, _L("max"), wxDefaultPosition, AMS_MATERIALS_SETTING_INPUT_SIZE);
|
||||
m_title_max->SetForegroundColour(AMS_MATERIALS_SETTING_GREY800);
|
||||
m_title_max->SetFont(::Label::Body_13);
|
||||
auto m_title_min = new wxStaticText(m_panel_body, wxID_ANY, _L("min"), wxDefaultPosition, AMS_MATERIALS_SETTING_INPUT_SIZE);
|
||||
m_title_min->SetForegroundColour(AMS_MATERIALS_SETTING_GREY800);
|
||||
m_title_min->SetFont(::Label::Body_13);
|
||||
sizer_temp_txt->Add(m_title_max, 1, wxALIGN_CENTER, 0);
|
||||
sizer_temp_txt->Add(FromDIP(10), 0, wxEXPAND, 0);
|
||||
sizer_temp_txt->Add(m_title_min, 1, wxALIGN_CENTER|wxRIGHT, FromDIP(16));
|
||||
|
||||
|
||||
sizer_other->Add(sizer_temp_txt, 0, wxALIGN_CENTER, 0);
|
||||
sizer_other->Add(sizer_tempinput, 0, wxALIGN_CENTER, 0);
|
||||
|
||||
m_sizer_temperature->Add(sizer_other, 0, wxALL | wxALIGN_CENTER, 0);
|
||||
m_sizer_temperature->AddStretchSpacer();
|
||||
|
||||
wxString warning_string = wxString::FromUTF8(
|
||||
(boost::format(_u8L("The input value should be greater than %1% and less than %2%")) % FILAMENT_MIN_TEMP % FILAMENT_MAX_TEMP).str());
|
||||
warning_text = new wxStaticText(m_panel_body, wxID_ANY, warning_string, wxDefaultPosition, wxDefaultSize, 0);
|
||||
warning_text->SetFont(::Label::Body_13);
|
||||
warning_text->SetForegroundColour(wxColour(255, 111, 0));
|
||||
|
||||
warning_text->Wrap(AMS_MATERIALS_SETTING_BODY_WIDTH);
|
||||
warning_text->SetMinSize(wxSize(AMS_MATERIALS_SETTING_BODY_WIDTH, -1));
|
||||
warning_text->Hide();
|
||||
|
||||
m_input_nozzle_min->GetTextCtrl()->Bind(wxEVT_SET_FOCUS, [this](wxFocusEvent &e) {
|
||||
warning_text->Hide();
|
||||
Layout();
|
||||
Fit();
|
||||
e.Skip();
|
||||
});
|
||||
m_input_nozzle_min->GetTextCtrl()->Bind(wxEVT_TEXT_ENTER, [this](wxCommandEvent &e) {
|
||||
input_min_finish();
|
||||
e.Skip();
|
||||
});
|
||||
m_input_nozzle_min->GetTextCtrl()->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent &e) {
|
||||
input_min_finish();
|
||||
e.Skip();
|
||||
});
|
||||
|
||||
m_input_nozzle_max->GetTextCtrl()->Bind(wxEVT_SET_FOCUS, [this](wxFocusEvent& e) {
|
||||
warning_text->Hide();
|
||||
Layout();
|
||||
Fit();
|
||||
e.Skip();
|
||||
});
|
||||
m_input_nozzle_max->GetTextCtrl()->Bind(wxEVT_TEXT_ENTER, [this](wxCommandEvent& e) {
|
||||
input_max_finish();
|
||||
e.Skip();
|
||||
});
|
||||
m_input_nozzle_max->GetTextCtrl()->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent& e) {
|
||||
input_max_finish();
|
||||
e.Skip();
|
||||
});
|
||||
|
||||
wxBoxSizer *m_sizer_button = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_sizer_button->Add(0, 0, 1, wxEXPAND, 0);
|
||||
|
||||
m_button_confirm = new Button(m_panel_body, _L("Confirm"));
|
||||
m_btn_bg_green = StateColor(std::pair<wxColour, int>(wxColour(27, 136, 68), StateColor::Pressed), std::pair<wxColour, int>(wxColour(61, 203, 115), StateColor::Hovered),
|
||||
std::pair<wxColour, int>(wxColour(0, 174, 66), StateColor::Normal));
|
||||
|
||||
m_button_confirm->SetBackgroundColor(m_btn_bg_green);
|
||||
m_button_confirm->SetBorderColor(wxColour(0, 174, 66));
|
||||
m_button_confirm->SetTextColor(AMS_MATERIALS_SETTING_GREY200);
|
||||
m_button_confirm->SetMinSize(AMS_MATERIALS_SETTING_BUTTON_SIZE);
|
||||
m_button_confirm->SetCornerRadius(12);
|
||||
m_button_confirm->Bind(wxEVT_LEFT_DOWN, &AMSMaterialsSetting::on_select_ok, this);
|
||||
m_sizer_button->Add(m_button_confirm, 0, wxALIGN_CENTER, 0);
|
||||
|
||||
|
||||
|
||||
StateColor btn_bg_white(std::pair<wxColour, int>(wxColour(206, 206, 206), StateColor::Pressed),
|
||||
std::pair<wxColour, int>(wxColour(238, 238, 238), StateColor::Hovered),
|
||||
std::pair<wxColour, int>(*wxWHITE, StateColor::Normal));
|
||||
StateColor btn_bd_white(std::pair<wxColour, int>(*wxWHITE, StateColor::Disabled), std::pair<wxColour, int>(wxColour(38, 46, 48), StateColor::Enabled));
|
||||
|
||||
m_sizer_body->Add(m_sizer_filament, 0, wxEXPAND, 0);
|
||||
m_sizer_body->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(16));
|
||||
m_sizer_body->Add(m_sizer_colour, 0, wxEXPAND, 0);
|
||||
m_sizer_body->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(16));
|
||||
m_sizer_body->Add(m_panel_SN, 0, wxEXPAND, 0);
|
||||
m_sizer_body->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(10));
|
||||
m_sizer_body->Add(m_sizer_temperature, 0, wxEXPAND, 0);
|
||||
m_sizer_body->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(5));
|
||||
m_sizer_body->Add(warning_text, 0, wxEXPAND, 0);
|
||||
m_sizer_body->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(24));
|
||||
m_sizer_body->Add(m_sizer_button, 0, wxEXPAND, 0);
|
||||
|
||||
m_panel_body->SetSizer(m_sizer_body);
|
||||
m_panel_body->Layout();
|
||||
m_sizer_main->Add(m_panel_body, 0, wxALL | wxEXPAND, 24);
|
||||
|
||||
this->SetSizer(m_sizer_main);
|
||||
this->Layout();
|
||||
m_sizer_main->Fit(this);
|
||||
|
||||
this->Centre(wxBOTH);
|
||||
|
||||
Bind(wxEVT_PAINT, &AMSMaterialsSetting::paintEvent, this);
|
||||
m_comboBox_filament->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(AMSMaterialsSetting::on_select_filament), NULL, this);
|
||||
}
|
||||
|
||||
void AMSMaterialsSetting::paintEvent(wxPaintEvent &evt)
|
||||
{
|
||||
auto size = GetSize();
|
||||
wxPaintDC dc(this);
|
||||
dc.SetPen(wxPen(wxColour(38, 46, 48), 1, wxSOLID));
|
||||
dc.SetBrush(wxBrush(*wxTRANSPARENT_BRUSH));
|
||||
dc.DrawRectangle(0, 0, size.x, size.y);
|
||||
}
|
||||
|
||||
AMSMaterialsSetting::~AMSMaterialsSetting()
|
||||
{
|
||||
m_comboBox_filament->Disconnect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(AMSMaterialsSetting::on_select_filament), NULL, this);
|
||||
}
|
||||
|
||||
|
||||
void AMSMaterialsSetting::input_min_finish()
|
||||
{
|
||||
if (m_input_nozzle_min->GetTextCtrl()->GetValue().empty())return;
|
||||
|
||||
auto val = std::atoi(m_input_nozzle_min->GetTextCtrl()->GetValue().c_str());
|
||||
|
||||
if (val < FILAMENT_MIN_TEMP || val > FILAMENT_MAX_TEMP) {
|
||||
warning_text->Show();
|
||||
} else {
|
||||
warning_text->Hide();
|
||||
}
|
||||
Layout();
|
||||
Fit();
|
||||
}
|
||||
|
||||
void AMSMaterialsSetting::input_max_finish()
|
||||
{
|
||||
if (m_input_nozzle_max->GetTextCtrl()->GetValue().empty())return;
|
||||
|
||||
auto val = std::atoi(m_input_nozzle_max->GetTextCtrl()->GetValue().c_str());
|
||||
|
||||
if (val < FILAMENT_MIN_TEMP || val > FILAMENT_MAX_TEMP) {
|
||||
warning_text->Show();
|
||||
}
|
||||
else {
|
||||
warning_text->Hide();
|
||||
}
|
||||
Layout();
|
||||
Fit();
|
||||
}
|
||||
|
||||
void AMSMaterialsSetting::update()
|
||||
{
|
||||
if (obj) {
|
||||
if (obj->is_in_printing() || obj->can_resume()) {
|
||||
enable_confirm_button(false);
|
||||
} else {
|
||||
enable_confirm_button(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AMSMaterialsSetting::enable_confirm_button(bool en)
|
||||
{
|
||||
if (!en) {
|
||||
if (m_button_confirm->IsEnabled()) {
|
||||
m_button_confirm->Disable();
|
||||
m_button_confirm->SetBackgroundColor(wxColour(0x90, 0x90, 0x90));
|
||||
m_button_confirm->SetBorderColor(wxColour(0x90, 0x90, 0x90));
|
||||
}
|
||||
} else {
|
||||
if (!m_button_confirm->IsEnabled()) {
|
||||
m_button_confirm->Enable();
|
||||
m_button_confirm->SetBackgroundColor(m_btn_bg_green);
|
||||
m_button_confirm->SetBorderColor(m_btn_bg_green);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AMSMaterialsSetting::on_select_ok(wxMouseEvent &event)
|
||||
{
|
||||
wxString nozzle_temp_min = m_input_nozzle_min->GetTextCtrl()->GetValue();
|
||||
auto filament = m_comboBox_filament->GetValue();
|
||||
|
||||
wxString nozzle_temp_max = m_input_nozzle_max->GetTextCtrl()->GetValue();
|
||||
|
||||
long nozzle_temp_min_int, nozzle_temp_max_int;
|
||||
nozzle_temp_min.ToLong(&nozzle_temp_min_int);
|
||||
nozzle_temp_max.ToLong(&nozzle_temp_max_int);
|
||||
wxColour color = m_clrData->GetColour();
|
||||
char col_buf[10];
|
||||
sprintf(col_buf, "%02X%02X%02XFF", (int) color.Red(), (int) color.Green(), (int) color.Blue());
|
||||
ams_filament_id = "";
|
||||
|
||||
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
|
||||
if (preset_bundle) {
|
||||
for (auto it = preset_bundle->filaments.begin(); it != preset_bundle->filaments.end(); it++) {
|
||||
if (it->alias.compare(m_comboBox_filament->GetValue().ToStdString()) == 0) {
|
||||
ams_filament_id = it->filament_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ams_filament_id.empty() || nozzle_temp_min.empty() || nozzle_temp_max.empty() || m_filament_type.empty()) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Invalid Setting id";
|
||||
} else {
|
||||
if (obj) {
|
||||
obj->command_ams_filament_settings(ams_id, tray_id, ams_filament_id, std::string(col_buf), m_filament_type, nozzle_temp_min_int, nozzle_temp_max_int);
|
||||
}
|
||||
}
|
||||
wxPopupTransientWindow::Dismiss();
|
||||
}
|
||||
|
||||
void AMSMaterialsSetting::set_color(wxColour color)
|
||||
{
|
||||
m_clrData = new wxColourData();
|
||||
m_clrData->SetColour(color);
|
||||
}
|
||||
|
||||
static bool show_flag;
|
||||
void AMSMaterialsSetting::on_clr_picker(wxCommandEvent & event) {
|
||||
|
||||
auto clr_dialog = new wxColourDialog(this, m_clrData);
|
||||
|
||||
clr_dialog->Bind(wxEVT_ACTIVATE, [this](wxActivateEvent &e) {
|
||||
int a ;
|
||||
});
|
||||
|
||||
show_flag = true;
|
||||
if (clr_dialog->ShowModal() == wxID_OK) {
|
||||
m_clrData = &(clr_dialog->GetColourData());
|
||||
m_clr_picker->SetBackgroundColour(m_clrData->GetColour());
|
||||
}
|
||||
}
|
||||
|
||||
void AMSMaterialsSetting::Dismiss()
|
||||
{
|
||||
if (show_flag)
|
||||
{
|
||||
show_flag = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
wxPopupTransientWindow::Dismiss();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool AMSMaterialsSetting::Show(bool show)
|
||||
{
|
||||
if (show) {
|
||||
m_button_confirm->SetMinSize(AMS_MATERIALS_SETTING_BUTTON_SIZE);
|
||||
m_input_nozzle_max->GetTextCtrl()->SetSize(wxSize(-1, FromDIP(20)));
|
||||
m_input_nozzle_min->GetTextCtrl()->SetSize(wxSize(-1, FromDIP(20)));
|
||||
}
|
||||
return wxPopupTransientWindow::Show(show);
|
||||
}
|
||||
|
||||
void AMSMaterialsSetting::Popup(bool show, bool third, wxString filament, wxColour colour, wxString sn, wxString tep)
|
||||
{
|
||||
if (!m_is_third) {
|
||||
m_panel_SN->Show();
|
||||
m_comboBox_filament->SetValue(filament);
|
||||
m_sn_number->SetLabelText(sn);
|
||||
m_input_nozzle_min->GetTextCtrl()->SetValue(tep);
|
||||
m_clrData->SetColour(colour);
|
||||
m_comboBox_filament->Disable();
|
||||
m_input_nozzle_min->Disable();
|
||||
wxPopupTransientWindow::Popup();
|
||||
Layout();
|
||||
return;
|
||||
}
|
||||
m_panel_SN->Hide();
|
||||
Layout();
|
||||
Fit();
|
||||
|
||||
m_clr_picker->SetBackgroundColour(m_clrData->GetColour());
|
||||
|
||||
int selection_idx = -1, idx = 0;
|
||||
wxArrayString filament_items;
|
||||
std::set<std::string> filament_id_set;
|
||||
|
||||
if (show) {
|
||||
PresetBundle* preset_bundle = wxGetApp().preset_bundle;
|
||||
if (preset_bundle) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "system_preset_bundle filament number=" << preset_bundle->filaments.size();
|
||||
for (auto filament_it = preset_bundle->filaments.begin(); filament_it != preset_bundle->filaments.end(); filament_it++) {
|
||||
// filter by system preset
|
||||
if (!filament_it->is_system) continue;
|
||||
|
||||
for (auto printer_it = preset_bundle->printers.begin(); printer_it != preset_bundle->printers.end(); printer_it++) {
|
||||
// filter by system preset
|
||||
if (!printer_it->is_system) continue;
|
||||
// get printer_model
|
||||
ConfigOption* printer_model_opt = printer_it->config.option("printer_model");
|
||||
ConfigOptionString* printer_model_str = dynamic_cast<ConfigOptionString*>(printer_model_opt);
|
||||
if (!printer_model_str || !obj)
|
||||
continue;
|
||||
|
||||
// use printer_model as printer type
|
||||
if (printer_model_str->value != MachineObject::get_preset_printer_model_name(obj->printer_type))
|
||||
continue;
|
||||
ConfigOption* printer_opt = filament_it->config.option("compatible_printers");
|
||||
ConfigOptionStrings* printer_strs = dynamic_cast<ConfigOptionStrings*>(printer_opt);
|
||||
for (auto printer_str : printer_strs->values) {
|
||||
if (printer_it->name == printer_str) {
|
||||
if (filament_id_set.find(filament_it->filament_id) != filament_id_set.end()) {
|
||||
continue;
|
||||
} else {
|
||||
filament_id_set.insert(filament_it->filament_id);
|
||||
// name matched
|
||||
filament_items.push_back(filament_it->alias);
|
||||
if (filament_it->filament_id == ams_filament_id) {
|
||||
selection_idx = idx;
|
||||
|
||||
// update if nozzle_temperature_range is found
|
||||
ConfigOption* opt_min = filament_it->config.option("nozzle_temperature_range_low");
|
||||
if(opt_min) {
|
||||
ConfigOptionInts* opt_min_ints = dynamic_cast<ConfigOptionInts*>(opt_min);
|
||||
if (opt_min_ints) {
|
||||
wxString text_nozzle_temp_min = wxString::Format("%d", opt_min_ints->get_at(0));
|
||||
m_input_nozzle_min->GetTextCtrl()->SetValue(text_nozzle_temp_min);
|
||||
}
|
||||
}
|
||||
ConfigOption* opt_max = filament_it->config.option("nozzle_temperature_range_high");
|
||||
if (opt_max) {
|
||||
ConfigOptionInts* opt_max_ints = dynamic_cast<ConfigOptionInts*>(opt_max);
|
||||
if (opt_max_ints) {
|
||||
wxString text_nozzle_temp_max = wxString::Format("%d", opt_max_ints->get_at(0));
|
||||
m_input_nozzle_max->GetTextCtrl()->SetValue(text_nozzle_temp_max);
|
||||
}
|
||||
}
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_comboBox_filament->Set(filament_items);
|
||||
if (selection_idx >= 0 && selection_idx < filament_items.size()) {
|
||||
m_comboBox_filament->SetSelection(selection_idx);
|
||||
post_select_event();
|
||||
}
|
||||
else {
|
||||
m_comboBox_filament->SetSelection(selection_idx);
|
||||
post_select_event();
|
||||
}
|
||||
}
|
||||
wxPopupTransientWindow::Popup();
|
||||
}
|
||||
|
||||
void AMSMaterialsSetting::post_select_event() {
|
||||
wxCommandEvent event(wxEVT_COMBOBOX);
|
||||
event.SetEventObject(m_comboBox_filament);
|
||||
wxPostEvent(m_comboBox_filament, event);
|
||||
}
|
||||
|
||||
void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt)
|
||||
{
|
||||
m_filament_type = "";
|
||||
PresetBundle* preset_bundle = wxGetApp().preset_bundle;
|
||||
if (preset_bundle) {
|
||||
for (auto it = preset_bundle->filaments.begin(); it != preset_bundle->filaments.end(); it++) {
|
||||
auto a = it->alias;
|
||||
if (it->alias.compare(m_comboBox_filament->GetValue().ToStdString()) == 0) {
|
||||
// ) if nozzle_temperature_range is found
|
||||
ConfigOption* opt_min = it->config.option("nozzle_temperature_range_low");
|
||||
if (opt_min) {
|
||||
ConfigOptionInts* opt_min_ints = dynamic_cast<ConfigOptionInts*>(opt_min);
|
||||
if (opt_min_ints) {
|
||||
wxString text_nozzle_temp_min = wxString::Format("%d", opt_min_ints->get_at(0));
|
||||
m_input_nozzle_min->GetTextCtrl()->SetValue(text_nozzle_temp_min);
|
||||
}
|
||||
}
|
||||
ConfigOption* opt_max = it->config.option("nozzle_temperature_range_high");
|
||||
if (opt_max) {
|
||||
ConfigOptionInts* opt_max_ints = dynamic_cast<ConfigOptionInts*>(opt_max);
|
||||
if (opt_max_ints) {
|
||||
wxString text_nozzle_temp_max = wxString::Format("%d", opt_max_ints->get_at(0));
|
||||
m_input_nozzle_max->GetTextCtrl()->SetValue(text_nozzle_temp_max);
|
||||
}
|
||||
}
|
||||
ConfigOption* opt_type = it->config.option("filament_type");
|
||||
bool found_filament_type = false;
|
||||
if (opt_type) {
|
||||
ConfigOptionStrings* opt_type_strs = dynamic_cast<ConfigOptionStrings*>(opt_type);
|
||||
if (opt_type_strs) {
|
||||
found_filament_type = true;
|
||||
//m_filament_type = opt_type_strs->get_at(0);
|
||||
m_filament_type = it->config.get_filament_type();
|
||||
}
|
||||
}
|
||||
if (!found_filament_type)
|
||||
m_filament_type = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_input_nozzle_min->GetTextCtrl()->GetValue().IsEmpty()) {
|
||||
m_input_nozzle_min->GetTextCtrl()->SetValue("220");
|
||||
}
|
||||
if (m_input_nozzle_max->GetTextCtrl()->GetValue().IsEmpty()) {
|
||||
m_input_nozzle_max->GetTextCtrl()->SetValue("220");
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
91
src/slic3r/GUI/AMSMaterialsSetting.hpp
Normal file
91
src/slic3r/GUI/AMSMaterialsSetting.hpp
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
#ifndef slic3r_AMSMaterialsSetting_hpp_
|
||||
#define slic3r_AMSMaterialsSetting_hpp_
|
||||
|
||||
#include "libslic3r/Preset.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "DeviceManager.hpp"
|
||||
#include "wx/clrpicker.h"
|
||||
#include "Widgets/RadioBox.hpp"
|
||||
#include "Widgets/Button.hpp"
|
||||
#include "Widgets/RoundedRectangle.hpp"
|
||||
#include "Widgets/Label.hpp"
|
||||
#include "Widgets/CheckBox.hpp"
|
||||
#include "Widgets/ComboBox.hpp"
|
||||
#include "Widgets/TextInput.hpp"
|
||||
|
||||
#define AMS_MATERIALS_SETTING_DEF_COLOUR wxColour(255, 255, 255)
|
||||
#define AMS_MATERIALS_SETTING_GREY800 wxColour(50, 58, 61)
|
||||
#define AMS_MATERIALS_SETTING_GREY700 wxColour(107, 107, 107)
|
||||
#define AMS_MATERIALS_SETTING_GREY300 wxColour(174,174,174)
|
||||
#define AMS_MATERIALS_SETTING_GREY200 wxColour(248, 248, 248)
|
||||
#define AMS_MATERIALS_SETTING_BODY_WIDTH FromDIP(340)
|
||||
#define AMS_MATERIALS_SETTING_LABEL_WIDTH FromDIP(80)
|
||||
#define AMS_MATERIALS_SETTING_COMBOX_WIDTH wxSize(FromDIP(250), FromDIP(30))
|
||||
#define AMS_MATERIALS_SETTING_BUTTON_SIZE wxSize(FromDIP(90), FromDIP(24))
|
||||
#define AMS_MATERIALS_SETTING_INPUT_SIZE wxSize(FromDIP(90), FromDIP(24))
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
class AMSMaterialsSetting : public wxPopupTransientWindow
|
||||
{
|
||||
public:
|
||||
AMSMaterialsSetting(wxWindow *parent, wxWindowID id);
|
||||
~AMSMaterialsSetting();
|
||||
void create();
|
||||
|
||||
void paintEvent(wxPaintEvent &evt);
|
||||
void input_min_finish();
|
||||
void input_max_finish();
|
||||
void update();
|
||||
void enable_confirm_button(bool en);
|
||||
void on_select_cancel(wxMouseEvent &event);
|
||||
void Dismiss() override;
|
||||
bool Show(bool show) override;
|
||||
void Popup(bool show, bool third = true, wxString filament = wxEmptyString, wxColour colour = *wxWHITE, wxString sn = wxEmptyString, wxString tep = wxEmptyString);
|
||||
|
||||
void post_select_event();
|
||||
void on_select_ok(wxMouseEvent &event);
|
||||
void set_color(wxColour color);
|
||||
|
||||
void on_clr_picker(wxCommandEvent &event);
|
||||
|
||||
MachineObject *obj{nullptr};
|
||||
int ams_id { 0 }; /* 0 ~ 3 */
|
||||
int tray_id { 0 }; /* 0 ~ 3 */
|
||||
|
||||
std::string ams_filament_id;
|
||||
|
||||
bool m_is_third;
|
||||
wxString m_brand_filament;
|
||||
wxString m_brand_sn;
|
||||
wxString m_brand_tmp;
|
||||
wxColour m_brand_colour;
|
||||
std::string m_filament_type;
|
||||
|
||||
protected:
|
||||
//void on_dpi_changed(const wxRect &suggested_rect) override;
|
||||
void on_select_filament(wxCommandEvent& evt);
|
||||
|
||||
protected:
|
||||
StateColor m_btn_bg_green;
|
||||
wxPanel * m_panel_SN;
|
||||
wxStaticText * m_sn_number;
|
||||
wxStaticText * warning_text;
|
||||
wxPanel * m_panel_body;
|
||||
wxStaticText * m_title_filament;
|
||||
ComboBox * m_comboBox_filament;
|
||||
wxStaticText * m_title_colour;
|
||||
wxButton * m_clr_picker;
|
||||
wxStaticText * m_title_temperature;
|
||||
wxStaticText * m_label_other;
|
||||
TextInput * m_input_nozzle_min;
|
||||
TextInput* m_input_nozzle_max;
|
||||
Button * m_button_confirm;
|
||||
wxColourData * m_clrData;
|
||||
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
#endif
|
||||
309
src/slic3r/GUI/AMSSetting.cpp
Normal file
309
src/slic3r/GUI/AMSSetting.cpp
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
#include "AMSSetting.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
AMSSetting::AMSSetting(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style)
|
||||
: DPIDialog(parent, id, wxEmptyString, pos, size, style)
|
||||
{
|
||||
create();
|
||||
}
|
||||
AMSSetting::~AMSSetting() {}
|
||||
|
||||
void AMSSetting::create()
|
||||
{
|
||||
wxBoxSizer *m_sizer_main;
|
||||
m_sizer_main = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto m_static_ams_settings = new wxStaticText(this, wxID_ANY, _L("AMS Settings"), wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_static_ams_settings->SetFont(::Label::Head_14);
|
||||
m_static_ams_settings->SetForegroundColour(AMS_SETTING_GREY800);
|
||||
m_sizer_main->Add(m_static_ams_settings, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(24));
|
||||
|
||||
m_panel_body = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, -1), wxTAB_TRAVERSAL);
|
||||
wxBoxSizer *m_sizerl_body;
|
||||
m_sizerl_body = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
|
||||
// checkbox area 1
|
||||
wxBoxSizer *m_sizer_Insert_material = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_checkbox_Insert_material_auto_read = new ::CheckBox(m_panel_body);
|
||||
m_checkbox_Insert_material_auto_read->Bind(wxEVT_TOGGLEBUTTON, &AMSSetting::on_insert_material_read, this);
|
||||
m_sizer_Insert_material->Add(m_checkbox_Insert_material_auto_read, 0, wxTOP, 1);
|
||||
|
||||
m_sizer_Insert_material->Add(0, 0, 0, wxLEFT, 12);
|
||||
|
||||
m_title_Insert_material_auto_read = new wxStaticText(m_panel_body, wxID_ANY, _L("Insertion update"),
|
||||
wxDefaultPosition, wxDefaultSize, 0);
|
||||
|
||||
m_title_Insert_material_auto_read->SetFont(::Label::Head_13);
|
||||
m_title_Insert_material_auto_read->SetForegroundColour(AMS_SETTING_GREY800);
|
||||
m_title_Insert_material_auto_read->Wrap(AMS_SETTING_BODY_WIDTH);
|
||||
m_sizer_Insert_material->Add(m_title_Insert_material_auto_read, 1, wxALL | wxEXPAND, 0);
|
||||
|
||||
m_sizerl_body->Add(m_sizer_Insert_material, 0, wxEXPAND, 0);
|
||||
m_sizerl_body->Add(0, 0, 0, wxTOP, 8);
|
||||
|
||||
wxBoxSizer *m_sizer_Insert_material_tip = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_sizer_Insert_material_tip_inline = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_sizer_Insert_material_tip->Add(0, 0, 0, wxLEFT, 10);
|
||||
|
||||
// tip line1
|
||||
m_tip_Insert_material_line1 = new wxStaticText(m_panel_body, wxID_ANY,
|
||||
_L("The AMS will automatically read the filament information when inserting a new Bambu Lab filament. This takes about 20 seconds."),
|
||||
wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_tip_Insert_material_line1->SetFont(::Label::Body_13);
|
||||
m_tip_Insert_material_line1->SetForegroundColour(AMS_SETTING_GREY700);
|
||||
m_tip_Insert_material_line1->SetSize(wxSize(AMS_SETTING_BODY_WIDTH, -1));
|
||||
m_tip_Insert_material_line1->Wrap(AMS_SETTING_BODY_WIDTH);
|
||||
m_tip_Insert_material_line1->Hide();
|
||||
m_sizer_Insert_material_tip_inline->Add(m_tip_Insert_material_line1, 0, wxEXPAND, 0);
|
||||
|
||||
// tip line2
|
||||
m_tip_Insert_material_line2 = new wxStaticText(m_panel_body, wxID_ANY,
|
||||
_L("Note: if new filament is inserted during printing, the AMS will not automatically read any information until printing is completed."),
|
||||
wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_tip_Insert_material_line2->SetFont(::Label::Body_13);
|
||||
m_tip_Insert_material_line2->SetForegroundColour(AMS_SETTING_GREY700);
|
||||
m_tip_Insert_material_line2->SetSize(wxSize(AMS_SETTING_BODY_WIDTH, -1));
|
||||
m_tip_Insert_material_line2->Wrap(AMS_SETTING_BODY_WIDTH);
|
||||
m_tip_Insert_material_line2->Hide();
|
||||
m_sizer_Insert_material_tip_inline->Add(m_tip_Insert_material_line2, 0, wxEXPAND | wxTOP, 8);
|
||||
|
||||
// tip line2
|
||||
m_tip_Insert_material_line3 =
|
||||
new wxStaticText(m_panel_body, wxID_ANY,
|
||||
_L("When inserting a new filament, the AMS will not automatically read its information, leaving it blank for you to enter manually."),
|
||||
wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_tip_Insert_material_line3->SetFont(::Label::Body_13);
|
||||
m_tip_Insert_material_line3->SetForegroundColour(AMS_SETTING_GREY700);
|
||||
m_tip_Insert_material_line3->SetSize(wxSize(AMS_SETTING_BODY_WIDTH, -1));
|
||||
m_tip_Insert_material_line3->Wrap(AMS_SETTING_BODY_WIDTH);
|
||||
m_tip_Insert_material_line3->Hide();
|
||||
m_sizer_Insert_material_tip_inline->Add(m_tip_Insert_material_line3, 0, wxEXPAND, 0);
|
||||
|
||||
m_sizer_Insert_material_tip->Add(m_sizer_Insert_material_tip_inline, 1, wxALIGN_CENTER, 0);
|
||||
m_sizerl_body->Add(m_sizer_Insert_material_tip, 0, wxEXPAND | wxLEFT, 18);
|
||||
|
||||
// checkbox area 2
|
||||
m_sizerl_body->Add(0, 0, 0, wxTOP, 15);
|
||||
wxBoxSizer *m_sizer_starting = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
m_checkbox_starting_auto_read = new ::CheckBox(m_panel_body);
|
||||
m_checkbox_starting_auto_read->Bind(wxEVT_TOGGLEBUTTON, &AMSSetting::on_starting_read, this);
|
||||
m_sizer_starting->Add(m_checkbox_starting_auto_read, 0, wxTOP, 1);
|
||||
|
||||
m_sizer_starting->Add(0, 0, 0, wxLEFT, 12);
|
||||
|
||||
m_title_starting_auto_read = new wxStaticText(m_panel_body, wxID_ANY, _L("Power on update"), wxDefaultPosition,
|
||||
wxDefaultSize, 0);
|
||||
m_title_starting_auto_read->SetFont(::Label::Head_13);
|
||||
m_title_starting_auto_read->SetForegroundColour(AMS_SETTING_GREY800);
|
||||
m_title_starting_auto_read->Wrap(AMS_SETTING_BODY_WIDTH);
|
||||
m_sizer_starting->Add(m_title_starting_auto_read, 1, wxEXPAND, 0);
|
||||
|
||||
m_sizerl_body->Add(m_sizer_starting, 0, wxEXPAND|wxTOP, FromDIP(8));
|
||||
|
||||
m_sizerl_body->Add(0, 0, 0, wxTOP, 8);
|
||||
|
||||
wxBoxSizer *m_sizer_starting_tip = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
m_sizer_starting_tip->Add(0, 0, 0, wxLEFT, 10);
|
||||
|
||||
// tip line
|
||||
m_sizer_starting_tip_inline = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_tip_starting_line1 = new wxStaticText(m_panel_body, wxID_ANY,
|
||||
_L("The AMS will automatically read the information of inserted filament on start-up. It will take about 1 minute.The reading process will roll filament spools."),
|
||||
wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_tip_starting_line1->SetFont(::Label::Body_13);
|
||||
m_tip_starting_line1->SetForegroundColour(AMS_SETTING_GREY700);
|
||||
m_tip_starting_line1->SetSize(wxSize(AMS_SETTING_BODY_WIDTH, -1));
|
||||
m_tip_starting_line1->Wrap(AMS_SETTING_BODY_WIDTH);
|
||||
m_sizer_starting_tip_inline->Add(m_tip_starting_line1, 0, wxEXPAND, 0);
|
||||
|
||||
m_tip_starting_line2 = new wxStaticText(m_panel_body, wxID_ANY,
|
||||
_L("The AMS will not automatically read information from inserted filament during startup and will continue to use the information recorded before the last shutdown."),
|
||||
wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_tip_starting_line2->SetFont(::Label::Body_13);
|
||||
m_tip_starting_line2->SetForegroundColour(AMS_SETTING_GREY700);
|
||||
m_tip_starting_line2->SetSize(wxSize(AMS_SETTING_BODY_WIDTH, -1));
|
||||
m_tip_starting_line2->Wrap(AMS_SETTING_BODY_WIDTH);
|
||||
m_sizer_starting_tip_inline->Add(m_tip_starting_line2, 0, wxEXPAND,0);
|
||||
|
||||
m_sizer_starting_tip->Add(m_sizer_starting_tip_inline, 1, wxALIGN_CENTER, 0);
|
||||
|
||||
m_sizerl_body->Add(m_sizer_starting_tip, 0, wxLEFT, 18);
|
||||
|
||||
m_sizerl_body->Add(0, 0, 0, wxTOP, 6);
|
||||
|
||||
// panel img
|
||||
m_panel_img = new wxPanel(m_panel_body, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
m_panel_img->SetBackgroundColour(AMS_SETTING_GREY200);
|
||||
|
||||
wxBoxSizer *m_sizer_img = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto img = new wxStaticBitmap(m_panel_img, wxID_ANY, create_scaled_bitmap("ams_icon", nullptr, 126), wxDefaultPosition, wxDefaultSize);
|
||||
m_sizer_img->Add(img, 0, wxALIGN_CENTER | wxTOP, 26);
|
||||
|
||||
m_sizer_img->Add(0, 0, 0, wxTOP, 18);
|
||||
|
||||
/* wxBoxSizer *m_sizer_ams_img_tip = new wxBoxSizer(wxVERTICAL);
|
||||
m_tip_ams_img = new wxStaticText(m_panel_img, wxID_ANY, _L("Click the automatic calibration button to enter the AMS initialization setup program"), wxDefaultPosition,
|
||||
wxDefaultSize, 0);
|
||||
m_tip_ams_img->SetFont(::Label::Body_13);
|
||||
m_tip_ams_img->SetForegroundColour(AMS_SETTING_GREY700);
|
||||
m_tip_ams_img->Wrap(AMS_SETTING_BODY_WIDTH);
|
||||
m_sizer_ams_img_tip->Add(m_tip_ams_img, 0, wxALIGN_CENTER, 0);
|
||||
|
||||
m_button_auto_demarcate = new Button(m_panel_img, _L("Auto Calibration"));
|
||||
StateColor btn_bg_green(std::pair<wxColour, int>(wxColour(27, 136, 68), StateColor::Pressed), std::pair<wxColour, int>(wxColour(61, 203, 115), StateColor::Hovered),
|
||||
std::pair<wxColour, int>(wxColour(0, 174, 66), StateColor::Normal));
|
||||
m_button_auto_demarcate->SetBackgroundColor(btn_bg_green);
|
||||
m_button_auto_demarcate->SetBorderColor(wxColour(0, 174, 66));
|
||||
m_button_auto_demarcate->SetTextColor(AMS_SETTING_GREY200);
|
||||
m_button_auto_demarcate->SetMinSize(AMS_SETTING_BUTTON_SIZE);
|
||||
m_button_auto_demarcate->SetCornerRadius(12);
|
||||
m_button_auto_demarcate->Bind(wxEVT_LEFT_DOWN, &AMSSetting::on_select_ok, this);
|
||||
|
||||
m_sizer_img->Add(m_sizer_ams_img_tip, 1, wxALIGN_CENTER, 0);
|
||||
m_sizer_img->Add(0, 0, 0, wxTOP, 12);
|
||||
m_sizer_img->Add(m_button_auto_demarcate, 0, wxALIGN_CENTER, 0);
|
||||
m_sizer_img->Add(0, 0, 0, wxBOTTOM, 17);*/
|
||||
|
||||
m_panel_img->SetSizer(m_sizer_img);
|
||||
m_panel_img->Layout();
|
||||
m_sizer_img->Fit(m_panel_img);
|
||||
m_sizerl_body->Add(0,0,0,wxTOP, FromDIP(5));
|
||||
m_sizerl_body->Add(m_panel_img, 1, wxEXPAND | wxALL, FromDIP(5));
|
||||
|
||||
m_panel_body->SetSizer(m_sizerl_body);
|
||||
m_panel_body->Layout();
|
||||
m_sizerl_body->Fit(m_panel_body);
|
||||
m_sizer_main->Add(m_panel_body, 1, wxALL | wxEXPAND, FromDIP(24));
|
||||
|
||||
this->SetSizer(m_sizer_main);
|
||||
this->Layout();
|
||||
m_sizer_main->Fit(this);
|
||||
|
||||
this->Centre(wxBOTH);
|
||||
|
||||
// set mode
|
||||
update_insert_material_read_mode(true);
|
||||
update_starting_read_mode(false);
|
||||
}
|
||||
|
||||
void AMSSetting::update_insert_material_read_mode(bool selected)
|
||||
{
|
||||
m_checkbox_Insert_material_auto_read->SetValue(selected);
|
||||
if (selected) {
|
||||
m_tip_Insert_material_line1->Show();
|
||||
m_tip_Insert_material_line2->Show();
|
||||
m_tip_Insert_material_line3->Hide();
|
||||
} else {
|
||||
m_tip_Insert_material_line1->Hide();
|
||||
m_tip_Insert_material_line2->Hide();
|
||||
m_tip_Insert_material_line3->Show();
|
||||
}
|
||||
m_sizer_Insert_material_tip_inline->Layout();
|
||||
Layout();
|
||||
Fit();
|
||||
}
|
||||
|
||||
void AMSSetting::update_starting_read_mode(bool selected)
|
||||
{
|
||||
m_checkbox_starting_auto_read->SetValue(selected);
|
||||
if (selected) { // selected
|
||||
m_tip_starting_line1->Show();
|
||||
m_tip_starting_line2->Hide();
|
||||
} else { // unselected
|
||||
m_tip_starting_line1->Hide();
|
||||
m_tip_starting_line2->Show();
|
||||
}
|
||||
m_sizer_starting_tip_inline->Layout();
|
||||
Layout();
|
||||
Fit();
|
||||
}
|
||||
|
||||
void AMSSetting::on_select_ok(wxMouseEvent &event)
|
||||
{
|
||||
if (obj) {
|
||||
obj->command_ams_calibrate(ams_id);
|
||||
}
|
||||
}
|
||||
|
||||
void AMSSetting::on_insert_material_read(wxCommandEvent &event)
|
||||
{
|
||||
// send command
|
||||
if (m_checkbox_Insert_material_auto_read->GetValue()) {
|
||||
// checked
|
||||
m_tip_Insert_material_line1->Show();
|
||||
m_tip_Insert_material_line2->Show();
|
||||
m_tip_Insert_material_line3->Hide();
|
||||
} else {
|
||||
// unchecked
|
||||
m_tip_Insert_material_line1->Hide();
|
||||
m_tip_Insert_material_line2->Hide();
|
||||
m_tip_Insert_material_line3->Show();
|
||||
}
|
||||
m_checkbox_Insert_material_auto_read->SetValue(event.GetInt());
|
||||
|
||||
bool start_read_opt = m_checkbox_starting_auto_read->GetValue();
|
||||
bool tray_read_opt = m_checkbox_Insert_material_auto_read->GetValue();
|
||||
|
||||
obj->command_ams_user_settings(ams_id, start_read_opt, tray_read_opt);
|
||||
|
||||
m_sizer_Insert_material_tip_inline->Layout();
|
||||
Layout();
|
||||
Fit();
|
||||
}
|
||||
|
||||
void AMSSetting::on_starting_read(wxCommandEvent &event)
|
||||
{
|
||||
if (m_checkbox_starting_auto_read->GetValue()) {
|
||||
// checked
|
||||
m_tip_starting_line1->Show();
|
||||
m_tip_starting_line2->Hide();
|
||||
} else {
|
||||
// unchecked
|
||||
m_tip_starting_line1->Hide();
|
||||
m_tip_starting_line2->Show();
|
||||
}
|
||||
m_checkbox_starting_auto_read->SetValue(event.GetInt());
|
||||
|
||||
bool start_read_opt = m_checkbox_starting_auto_read->GetValue();
|
||||
bool tray_read_opt = m_checkbox_Insert_material_auto_read->GetValue();
|
||||
|
||||
obj->command_ams_user_settings(ams_id, start_read_opt, tray_read_opt);
|
||||
|
||||
m_sizer_starting_tip_inline->Layout();
|
||||
Layout();
|
||||
Fit();
|
||||
}
|
||||
|
||||
wxString AMSSetting::append_title(wxString text)
|
||||
{
|
||||
wxString lab;
|
||||
auto * widget = new wxStaticText(m_panel_body, wxID_ANY, text, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
|
||||
widget->Wrap(AMS_SETTING_BODY_WIDTH);
|
||||
widget->SetMinSize(wxSize(AMS_SETTING_BODY_WIDTH, -1));
|
||||
lab = widget->GetLabel();
|
||||
widget->Destroy();
|
||||
return lab;
|
||||
}
|
||||
|
||||
wxStaticText *AMSSetting::append_text(wxString text)
|
||||
{
|
||||
auto *widget = new wxStaticText(m_panel_body, wxID_ANY, text, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
|
||||
widget->Wrap(250);
|
||||
widget->SetMinSize(wxSize(250, -1));
|
||||
return widget;
|
||||
}
|
||||
|
||||
void AMSSetting::on_dpi_changed(const wxRect &suggested_rect)
|
||||
{
|
||||
m_button_auto_demarcate->SetMinSize(AMS_SETTING_BUTTON_SIZE);
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
64
src/slic3r/GUI/AMSSetting.hpp
Normal file
64
src/slic3r/GUI/AMSSetting.hpp
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
#ifndef slic3r_AMSSettingDialog_hpp_
|
||||
#define slic3r_AMSSettingDialog_hpp_
|
||||
|
||||
#include "libslic3r/Preset.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "DeviceManager.hpp"
|
||||
#include "Widgets/RadioBox.hpp"
|
||||
#include "Widgets/Button.hpp"
|
||||
#include "Widgets/RoundedRectangle.hpp"
|
||||
#include "Widgets/Label.hpp"
|
||||
#include "Widgets/CheckBox.hpp"
|
||||
|
||||
#define AMS_SETTING_DEF_COLOUR wxColour(255, 255, 255)
|
||||
#define AMS_SETTING_GREY800 wxColour(50, 58, 61)
|
||||
#define AMS_SETTING_GREY700 wxColour(107, 107, 107)
|
||||
#define AMS_SETTING_GREY200 wxColour(248, 248, 248)
|
||||
#define AMS_SETTING_BODY_WIDTH FromDIP(380)
|
||||
#define AMS_SETTING_BUTTON_SIZE wxSize(FromDIP(150), FromDIP(24))
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
class AMSSetting : public DPIDialog
|
||||
{
|
||||
public:
|
||||
AMSSetting(wxWindow *parent, wxWindowID id, const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE);
|
||||
~AMSSetting();
|
||||
void create();
|
||||
|
||||
void update_insert_material_read_mode(bool selected);
|
||||
void update_starting_read_mode(bool selected);
|
||||
void on_select_ok(wxMouseEvent &event);
|
||||
void on_insert_material_read(wxCommandEvent &event);
|
||||
void on_starting_read(wxCommandEvent &event);
|
||||
wxString append_title(wxString text);
|
||||
wxStaticText *append_text(wxString text);
|
||||
MachineObject *obj{nullptr};
|
||||
int ams_id { 0 };
|
||||
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect &suggested_rect) override;
|
||||
|
||||
protected:
|
||||
wxPanel * m_panel_body;
|
||||
CheckBox * m_checkbox_Insert_material_auto_read;
|
||||
wxStaticText *m_title_Insert_material_auto_read;
|
||||
wxStaticText *m_tip_Insert_material_line1;
|
||||
wxStaticText *m_tip_Insert_material_line2;
|
||||
wxStaticText *m_tip_Insert_material_line3;
|
||||
CheckBox * m_checkbox_starting_auto_read;
|
||||
wxStaticText *m_title_starting_auto_read;
|
||||
wxStaticText *m_tip_starting_line1;
|
||||
wxStaticText *m_tip_starting_line2;
|
||||
wxPanel * m_panel_img;
|
||||
wxStaticText *m_tip_ams_img;
|
||||
Button * m_button_auto_demarcate;
|
||||
|
||||
wxBoxSizer *m_sizer_Insert_material_tip_inline;
|
||||
wxBoxSizer *m_sizer_starting_tip_inline;
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
#endif
|
||||
361
src/slic3r/GUI/AboutDialog.cpp
Normal file
361
src/slic3r/GUI/AboutDialog.cpp
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
#include "AboutDialog.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "format.hpp"
|
||||
#include "Widgets/Button.hpp"
|
||||
|
||||
#include <wx/clipbrd.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
AboutDialogLogo::AboutDialogLogo(wxWindow* parent)
|
||||
: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize)
|
||||
{
|
||||
this->SetBackgroundColour(*wxWHITE);
|
||||
this->logo = wxBitmap(from_u8(Slic3r::var("BambuStudio_192px.png")), wxBITMAP_TYPE_PNG);
|
||||
this->SetMinSize(this->logo.GetSize());
|
||||
|
||||
this->Bind(wxEVT_PAINT, &AboutDialogLogo::onRepaint, this);
|
||||
}
|
||||
|
||||
void AboutDialogLogo::onRepaint(wxEvent &event)
|
||||
{
|
||||
wxPaintDC dc(this);
|
||||
dc.SetBackgroundMode(wxTRANSPARENT);
|
||||
|
||||
wxSize size = this->GetSize();
|
||||
int logo_w = this->logo.GetWidth();
|
||||
int logo_h = this->logo.GetHeight();
|
||||
dc.DrawBitmap(this->logo, (size.GetWidth() - logo_w)/2, (size.GetHeight() - logo_h)/2, true);
|
||||
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------
|
||||
// CopyrightsDialog
|
||||
// -----------------------------------------
|
||||
CopyrightsDialog::CopyrightsDialog()
|
||||
: DPIDialog(static_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY, from_u8((boost::format("%1% - %2%")
|
||||
% (wxGetApp().is_editor() ? SLIC3R_APP_FULL_NAME : GCODEVIEWER_APP_NAME)
|
||||
% _utf8(L("Portions copyright"))).str()),
|
||||
wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||
{
|
||||
this->SetFont(wxGetApp().normal_font());
|
||||
#ifdef _WIN32
|
||||
wxGetApp().UpdateDarkUI(this);
|
||||
#else
|
||||
this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
#endif
|
||||
|
||||
std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico") % resources_dir()).str();
|
||||
SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO));
|
||||
|
||||
wxStaticLine *staticline1 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
|
||||
|
||||
auto sizer = new wxBoxSizer(wxVERTICAL);
|
||||
sizer->Add( staticline1, 0, wxEXPAND | wxALL, 5 );
|
||||
|
||||
fill_entries();
|
||||
|
||||
m_html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition,
|
||||
wxSize(40 * em_unit(), 20 * em_unit()), wxHW_SCROLLBAR_AUTO);
|
||||
m_html->SetMinSize(wxSize(FromDIP(870),FromDIP(520)));
|
||||
wxFont font = get_default_font(this);
|
||||
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);
|
||||
|
||||
SetSizer(sizer);
|
||||
sizer->SetSizeHints(this);
|
||||
|
||||
}
|
||||
|
||||
void CopyrightsDialog::fill_entries()
|
||||
{
|
||||
m_entries = {
|
||||
{ "Admesh", "", "https://admesh.readthedocs.io/" },
|
||||
{ "Anti-Grain Geometry", "", "http://antigrain.com" },
|
||||
{ "Boost", "", "http://www.boost.org" },
|
||||
{ "Cereal", "", "http://uscilab.github.io/cereal" },
|
||||
{ "CGAL", "", "https://www.cgal.org" },
|
||||
{ "Clipper", "", "http://www.angusj.co" },
|
||||
{ "libcurl", "", "https://curl.se/libcurl" },
|
||||
{ "Eigen3", "", "http://eigen.tuxfamily.org" },
|
||||
{ "Expat", "", "http://www.libexpat.org" },
|
||||
{ "fast_float", "", "https://github.com/fastfloat/fast_float" },
|
||||
{ "GLEW (The OpenGL Extension Wrangler Library)", "", "http://glew.sourceforge.net" },
|
||||
{ "GLFW", "", "https://www.glfw.org" },
|
||||
{ "GNU gettext", "", "https://www.gnu.org/software/gettext" },
|
||||
{ "ImGUI", "", "https://github.com/ocornut/imgui" },
|
||||
{ "Libigl", "", "https://libigl.github.io" },
|
||||
{ "libnest2d", "", "https://github.com/tamasmeszaros/libnest2d" },
|
||||
{ "lib_fts", "", "https://www.forrestthewoods.com" },
|
||||
{ "Mesa 3D", "", "https://mesa3d.org" },
|
||||
{ "Miniz", "", "https://github.com/richgel999/miniz" },
|
||||
{ "Nanosvg", "", "https://github.com/memononen/nanosvg" },
|
||||
{ "nlohmann/json", "", "https://json.nlohmann.me" },
|
||||
{ "Qhull", "", "http://qhull.org" },
|
||||
{ "Open Cascade", "", "https://www.opencascade.com" },
|
||||
{ "OpenGL", "", "https://www.opengl.org" },
|
||||
{ "PoEdit", "", "https://poedit.net" },
|
||||
{ "PrusaSlicer", "", "https://www.prusa3d.com" },
|
||||
{ "Real-Time DXT1/DXT5 C compression library", "", "https://github.com/Cyan4973/RygsDXTc" },
|
||||
{ "SemVer", "", "https://semver.org" },
|
||||
{ "Shinyprofiler", "", "https://code.google.com/p/shinyprofiler" },
|
||||
{ "TBB", "", "https://www.intel.cn/content/www/cn/zh/developer/tools/oneapi/onetbb.html" },
|
||||
{ "wxWidgets", "", "https://www.wxwidgets.org" },
|
||||
{ "zlib", "", "http://zlib.net" },
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
wxString CopyrightsDialog::get_html_text()
|
||||
{
|
||||
wxColour bgr_clr = wxGetApp().get_window_default_clr();//wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||
|
||||
const auto text_clr = wxGetApp().get_label_clr_default();// 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")) + "© ";
|
||||
|
||||
wxString text = wxString::Format(
|
||||
"<html>"
|
||||
"<body bgcolor= %s link= %s>"
|
||||
"<font color=%s>"
|
||||
"<font size=\"5\">%s</font><br/>"
|
||||
"<font size=\"5\">%s</font>"
|
||||
"<a href=\"%s\">%s.</a><br/>"
|
||||
"<font size=\"5\">%s.</font><br/>"
|
||||
"<br /><br />"
|
||||
"<font size=\"5\">%s</font><br/>"
|
||||
"<font size=\"5\">%s:</font><br/>"
|
||||
"<br />"
|
||||
"<font size=\"3\">",
|
||||
bgr_clr_str, text_clr_str, text_clr_str,
|
||||
_L("License"),
|
||||
_L("Bambu Studio is licensed under "),
|
||||
"https://www.gnu.org/licenses/agpl-3.0.html",_L("GNU Affero General Public License, version 3"),
|
||||
_L("Bambu Studio is based on PrusaSlicer by Prusa Research, which is from Slic3r by Alessandro Ranellucci and the RepRap community"),
|
||||
_L("Libraries"),
|
||||
_L("This software uses open source components whose copyright and other proprietary rights belong to their respective owners"));
|
||||
|
||||
for (auto& entry : m_entries) {
|
||||
text += format_wxstr(
|
||||
"%s<br/>"
|
||||
, entry.lib_name);
|
||||
|
||||
text += wxString::Format(
|
||||
"<a href=\"%s\">%s</a><br/><br/>"
|
||||
, entry.link, entry.link);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
wxGetApp().open_browser_with_warning_dialog(event.GetLinkInfo().GetHref());
|
||||
event.Skip(false);
|
||||
}
|
||||
|
||||
void CopyrightsDialog::onCloseDialog(wxEvent &)
|
||||
{
|
||||
this->EndModal(wxID_CLOSE);
|
||||
}
|
||||
|
||||
AboutDialog::AboutDialog()
|
||||
: DPIDialog(static_cast<wxWindow *>(wxGetApp().mainframe),wxID_ANY,from_u8((boost::format(_utf8(L("About %s"))) % (wxGetApp().is_editor() ? SLIC3R_APP_FULL_NAME : GCODEVIEWER_APP_NAME)).str()),wxDefaultPosition,
|
||||
wxDefaultSize, /*wxCAPTION*/wxDEFAULT_DIALOG_STYLE)
|
||||
{
|
||||
SetFont(wxGetApp().normal_font());
|
||||
|
||||
wxColour bgr_clr = wxGetApp().get_window_default_clr();//wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||
SetBackgroundColour(bgr_clr);
|
||||
|
||||
std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico") % resources_dir()).str();
|
||||
SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO));
|
||||
|
||||
wxPanel *m_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(430), FromDIP(237)), wxTAB_TRAVERSAL);
|
||||
|
||||
wxBoxSizer *panel_versizer = new wxBoxSizer(wxVERTICAL);
|
||||
wxBoxSizer *vesizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_panel->SetSizer(panel_versizer);
|
||||
|
||||
wxBoxSizer *ver_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
main_sizer->Add(m_panel, 1, wxEXPAND | wxALL, 0);
|
||||
main_sizer->Add(ver_sizer, 0, wxEXPAND | wxALL, 0);
|
||||
|
||||
// logo
|
||||
m_logo_bitmap = ScalableBitmap(this, "BambuStudio_about", 240);
|
||||
m_logo = new wxStaticBitmap(this, wxID_ANY, m_logo_bitmap.bmp(), wxDefaultPosition,wxDefaultSize, 0);
|
||||
m_logo->SetSizer(vesizer);
|
||||
|
||||
panel_versizer->Add(m_logo, 1, wxALL | wxEXPAND, 0);
|
||||
|
||||
// version
|
||||
{
|
||||
vesizer->Add(0, FromDIP(165), 1, wxEXPAND, FromDIP(5));
|
||||
auto version_string = _L("Version") + " " + std::string(SLIC3R_VERSION);
|
||||
wxStaticText* version = new wxStaticText(this, wxID_ANY, version_string.c_str(), wxDefaultPosition, wxDefaultSize);
|
||||
wxFont version_font = GetFont();
|
||||
#ifdef __WXMSW__
|
||||
version_font.SetPointSize(version_font.GetPointSize()-1);
|
||||
#else
|
||||
version_font.SetPointSize(11);
|
||||
#endif
|
||||
version_font.SetPointSize(12);
|
||||
version->SetFont(version_font);
|
||||
version->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
|
||||
version->SetBackgroundColour(wxColour(0, 174, 66));
|
||||
vesizer->Add(version, 0, wxALL | wxALIGN_CENTER_HORIZONTAL, FromDIP(5));
|
||||
vesizer->Add(0, 0, 1, wxEXPAND, FromDIP(5));
|
||||
}
|
||||
|
||||
wxStaticText *html_text = new wxStaticText(this, wxID_ANY, "Copyright(C) 2021-2022 Bambu Lab", wxDefaultPosition, wxDefaultSize);
|
||||
ver_sizer->Add(0, 0, 0, wxTOP, FromDIP(38));
|
||||
html_text->SetForegroundColour(wxColour(107, 107, 107));
|
||||
ver_sizer->Add(html_text, 0, wxALL | wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
|
||||
// text
|
||||
m_html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_NEVER /*NEVER*/);
|
||||
{
|
||||
wxFont font = get_default_font(this);
|
||||
const int fs = font.GetPointSize()-1;
|
||||
int size[] = {fs,fs,fs,fs,fs,fs,fs};
|
||||
m_html->SetFonts(font.GetFaceName(), font.GetFaceName(), size);
|
||||
m_html->SetMinSize(wxSize(FromDIP(-1), FromDIP(16)));
|
||||
m_html->SetBorders(2);
|
||||
const auto text = from_u8(
|
||||
(boost::format(
|
||||
"<html>"
|
||||
"<body>"
|
||||
"<p style=\"text-align:center\"><a href=\"www.bambulab.com\">www.bambulab.com</ a></p>"
|
||||
"</body>"
|
||||
"</html>")
|
||||
).str());
|
||||
m_html->SetPage(text);
|
||||
ver_sizer->Add(m_html, 0, wxEXPAND, 0);
|
||||
m_html->Bind(wxEVT_HTML_LINK_CLICKED, &AboutDialog::onLinkClicked, this);
|
||||
}
|
||||
//Add "Portions copyright" button
|
||||
Button* button_portions = new Button(this,_L("Portions copyright"));
|
||||
StateColor report_bg(std::pair<wxColour, int>(wxColour(255, 255, 255), StateColor::Disabled), std::pair<wxColour, int>(wxColour(206, 206, 206), StateColor::Pressed),
|
||||
std::pair<wxColour, int>(wxColour(238, 238, 238), StateColor::Hovered), std::pair<wxColour, int>(wxColour(255, 255, 255), StateColor::Enabled),
|
||||
std::pair<wxColour, int>(wxColour(255, 255, 255), StateColor::Normal));
|
||||
button_portions->SetBackgroundColor(report_bg);
|
||||
StateColor report_bd(std::pair<wxColour, int>(wxColour(144, 144, 144), StateColor::Disabled), std::pair<wxColour, int>(wxColour(38, 46, 48), StateColor::Enabled));
|
||||
button_portions->SetBorderColor(report_bd);
|
||||
StateColor report_text(std::pair<wxColour, int>(wxColour(144, 144, 144), StateColor::Disabled), std::pair<wxColour, int>(wxColour(38, 46, 48), StateColor::Enabled));
|
||||
button_portions->SetTextColor(report_text);
|
||||
button_portions->SetFont(Label::Body_12);
|
||||
button_portions->SetCornerRadius(FromDIP(12));
|
||||
button_portions->SetMinSize(wxSize(FromDIP(120), FromDIP(24)));
|
||||
|
||||
ver_sizer->Add( 0, 0, 0, wxTOP, FromDIP(22));
|
||||
ver_sizer->Add(button_portions, 0, wxALIGN_CENTER_HORIZONTAL|wxALL,0);
|
||||
ver_sizer->Add( 0, 0, 0, wxTOP, FromDIP(38));
|
||||
button_portions->Bind(wxEVT_BUTTON, &AboutDialog::onCopyrightBtn, this);
|
||||
|
||||
m_panel->Layout();
|
||||
SetSizer(main_sizer);
|
||||
main_sizer->SetSizeHints(this);
|
||||
}
|
||||
|
||||
void AboutDialog::on_dpi_changed(const wxRect &suggested_rect)
|
||||
{
|
||||
m_logo_bitmap.msw_rescale();
|
||||
m_logo->SetBitmap(m_logo_bitmap.bmp());
|
||||
|
||||
const wxFont& font = GetFont();
|
||||
const int fs = font.GetPointSize() - 1;
|
||||
int font_size[] = { fs, fs, fs, fs, fs, fs, fs };
|
||||
m_html->SetFonts(font.GetFaceName(), font.GetFaceName(), font_size);
|
||||
|
||||
const int& em = em_unit();
|
||||
|
||||
msw_buttons_rescale(this, em, { wxID_CLOSE, m_copy_rights_btn_id });
|
||||
|
||||
m_html->SetMinSize(wxSize(-1, 16 * em));
|
||||
m_html->Refresh();
|
||||
|
||||
const wxSize& size = wxSize(65 * em, 30 * em);
|
||||
|
||||
SetMinSize(size);
|
||||
Fit();
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void AboutDialog::onLinkClicked(wxHtmlLinkEvent &event)
|
||||
{
|
||||
wxGetApp().open_browser_with_warning_dialog(event.GetLinkInfo().GetHref());
|
||||
event.Skip(false);
|
||||
}
|
||||
|
||||
void AboutDialog::onCloseDialog(wxEvent &)
|
||||
{
|
||||
this->EndModal(wxID_CLOSE);
|
||||
}
|
||||
|
||||
void AboutDialog::onCopyrightBtn(wxEvent &)
|
||||
{
|
||||
CopyrightsDialog dlg;
|
||||
dlg.ShowModal();
|
||||
}
|
||||
|
||||
void AboutDialog::onCopyToClipboard(wxEvent&)
|
||||
{
|
||||
wxTheClipboard->Open();
|
||||
wxTheClipboard->SetData(new wxTextDataObject(_L("Version") + " " + std::string(SLIC3R_VERSION)));
|
||||
wxTheClipboard->Close();
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
80
src/slic3r/GUI/AboutDialog.hpp
Normal file
80
src/slic3r/GUI/AboutDialog.hpp
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#ifndef slic3r_GUI_AboutDialog_hpp_
|
||||
#define slic3r_GUI_AboutDialog_hpp_
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/intl.h>
|
||||
#include <wx/html/htmlwin.h>
|
||||
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
class AboutDialogLogo : public wxPanel
|
||||
{
|
||||
public:
|
||||
AboutDialogLogo(wxWindow* parent);
|
||||
|
||||
private:
|
||||
wxBitmap logo;
|
||||
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 };
|
||||
int m_copy_version_btn_id { wxID_ANY };
|
||||
public:
|
||||
AboutDialog();
|
||||
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect &suggested_rect) override;
|
||||
|
||||
private:
|
||||
void onLinkClicked(wxHtmlLinkEvent &event);
|
||||
void onCloseDialog(wxEvent &);
|
||||
void onCopyrightBtn(wxEvent &);
|
||||
void onCopyToClipboard(wxEvent&);
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif
|
||||
371
src/slic3r/GUI/AmsMappingPopup.cpp
Normal file
371
src/slic3r/GUI/AmsMappingPopup.cpp
Normal file
|
|
@ -0,0 +1,371 @@
|
|||
#include "AmsMappingPopup.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Thread.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI_Preview.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "format.hpp"
|
||||
#include "Widgets/ProgressDialog.hpp"
|
||||
#include "Widgets/RoundedRectangle.hpp"
|
||||
#include "Widgets/StaticBox.hpp"
|
||||
|
||||
#include <wx/progdlg.h>
|
||||
#include <wx/clipbrd.h>
|
||||
#include <wx/dcgraph.h>
|
||||
#include <miniz.h>
|
||||
#include <algorithm>
|
||||
#include "Plater.hpp"
|
||||
#include "BitmapCache.hpp"
|
||||
#include "BindDialog.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
wxDEFINE_EVENT(EVT_SET_FINISH_MAPPING, wxCommandEvent);
|
||||
|
||||
MaterialItem::MaterialItem(wxWindow *parent, wxColour mcolour, wxString mname)
|
||||
: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize)
|
||||
{
|
||||
m_material_coloul = mcolour;
|
||||
m_material_name = mname;
|
||||
m_ams_coloul = wxColour(0xEE,0xEE,0xEE);
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
SetDoubleBuffered(true);
|
||||
#endif //__WINDOWS__
|
||||
|
||||
SetSize(MATERIAL_ITEM_SIZE);
|
||||
SetMinSize(MATERIAL_ITEM_SIZE);
|
||||
SetMaxSize(MATERIAL_ITEM_SIZE);
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
|
||||
Bind(wxEVT_PAINT, &MaterialItem::paintEvent, this);
|
||||
}
|
||||
|
||||
MaterialItem::~MaterialItem() {}
|
||||
|
||||
void MaterialItem::msw_rescale() {}
|
||||
|
||||
void MaterialItem::set_ams_info(wxColour col, wxString txt)
|
||||
{
|
||||
m_ams_coloul = col;
|
||||
m_ams_name = txt;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void MaterialItem::on_selected()
|
||||
{
|
||||
if (!m_selected) {
|
||||
m_selected = true;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MaterialItem::on_warning()
|
||||
{
|
||||
if (!m_warning) {
|
||||
m_warning = true;
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialItem::on_normal()
|
||||
{
|
||||
m_selected = false;
|
||||
m_warning = false;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void MaterialItem::paintEvent(wxPaintEvent &evt)
|
||||
{
|
||||
wxPaintDC dc(this);
|
||||
render(dc);
|
||||
}
|
||||
|
||||
void MaterialItem::render(wxDC &dc)
|
||||
{
|
||||
#ifdef __WXMSW__
|
||||
wxSize size = GetSize();
|
||||
wxMemoryDC memdc;
|
||||
wxBitmap bmp(size.x, size.y);
|
||||
memdc.SelectObject(bmp);
|
||||
memdc.Blit({0, 0}, size, &dc, {0, 0});
|
||||
|
||||
{
|
||||
wxGCDC dc2(memdc);
|
||||
doRender(dc2);
|
||||
}
|
||||
|
||||
memdc.SelectObject(wxNullBitmap);
|
||||
dc.DrawBitmap(bmp, 0, 0);
|
||||
#else
|
||||
doRender(dc);
|
||||
#endif
|
||||
|
||||
//materials name
|
||||
dc.SetFont(::Label::Body_13);
|
||||
|
||||
auto material_name_colour = m_material_coloul.GetLuminance() < 0.5 ? *wxWHITE : wxColour(0x26,0x2E,0x30);
|
||||
dc.SetTextForeground(material_name_colour);
|
||||
|
||||
auto material_txt_size = dc.GetTextExtent(m_material_name);
|
||||
dc.DrawText(m_material_name, wxPoint((MATERIAL_ITEM_SIZE.x - material_txt_size.x) / 2, FromDIP(3)));
|
||||
|
||||
//mapping num
|
||||
dc.SetFont(::Label::Body_10);
|
||||
dc.SetTextForeground(m_ams_coloul.GetLuminance() < 0.5 ? *wxWHITE : wxColour(0x26,0x2E,0x30));
|
||||
|
||||
|
||||
wxString mapping_txt = wxEmptyString;
|
||||
if (m_ams_name.empty()) {
|
||||
mapping_txt = "-";
|
||||
}else{
|
||||
mapping_txt = m_ams_name;
|
||||
}
|
||||
|
||||
auto mapping_txt_size = dc.GetTextExtent(mapping_txt);
|
||||
dc.DrawText(mapping_txt, wxPoint((MATERIAL_ITEM_SIZE.x - mapping_txt_size.x) / 2, FromDIP(2) + material_txt_size.y ));
|
||||
}
|
||||
|
||||
void MaterialItem::doRender(wxDC &dc)
|
||||
{
|
||||
//top
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.SetBrush(wxBrush(m_material_coloul));
|
||||
dc.DrawRoundedRectangle(FromDIP(3), FromDIP(3), MATERIAL_ITEM_SIZE.x - FromDIP(6), MATERIAL_ITEM_SIZE.y / 2, 5);
|
||||
|
||||
//bottom
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.SetBrush(wxBrush(wxColour(m_ams_coloul)));
|
||||
dc.DrawRoundedRectangle(FromDIP(3), MATERIAL_ITEM_SIZE.y / 2 - FromDIP(2), MATERIAL_ITEM_SIZE.x - FromDIP(6), MATERIAL_ITEM_SIZE.y / 2, 5);
|
||||
|
||||
//middle
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.SetBrush(wxBrush(m_material_coloul));
|
||||
dc.DrawRectangle(FromDIP(3), FromDIP(10), MATERIAL_ITEM_SIZE.x - FromDIP(6), FromDIP(8));
|
||||
|
||||
//border
|
||||
if (m_material_coloul == *wxWHITE) {
|
||||
dc.SetPen(wxColour(0xAC, 0xAC, 0xAC));
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.DrawRoundedRectangle(3, 3, MATERIAL_ITEM_SIZE.x -6, MATERIAL_ITEM_SIZE.y - 6, 5);
|
||||
}
|
||||
|
||||
|
||||
if (m_selected) {
|
||||
dc.SetPen(wxColour(0x00, 0xAE, 0x42));
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.DrawRoundedRectangle(1, 1, MATERIAL_ITEM_SIZE.x - 2, MATERIAL_ITEM_SIZE.y - 2, 5);
|
||||
}
|
||||
|
||||
if (m_warning) {
|
||||
dc.SetPen(wxColour(0xFF, 0x6F, 0x00));
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.DrawRoundedRectangle(1, 1, MATERIAL_ITEM_SIZE.x - 2, MATERIAL_ITEM_SIZE.y - 2, 5);
|
||||
}
|
||||
}
|
||||
|
||||
AmsMapingPopup::AmsMapingPopup(wxWindow *parent)
|
||||
:wxPopupTransientWindow(parent, wxBORDER_NONE)
|
||||
{
|
||||
SetSize(wxSize(FromDIP(220), -1));
|
||||
SetMinSize(wxSize(FromDIP(220), -1));
|
||||
SetMaxSize(wxSize(FromDIP(220), -1));
|
||||
Bind(wxEVT_PAINT, &AmsMapingPopup::paintEvent, this);
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
|
||||
m_sizer_main = new wxBoxSizer(wxVERTICAL);
|
||||
//m_sizer_main->Add(0, 0, 1, wxEXPAND, 0);
|
||||
|
||||
SetSizer(m_sizer_main);
|
||||
Layout();
|
||||
}
|
||||
|
||||
void AmsMapingPopup::Popup(wxWindow *focus /*= NULL*/)
|
||||
{
|
||||
wxPopupTransientWindow::Popup();
|
||||
}
|
||||
|
||||
void AmsMapingPopup::update_materials_list(std::vector<std::string> list)
|
||||
{
|
||||
m_materials_list = list;
|
||||
}
|
||||
|
||||
void AmsMapingPopup::set_tag_texture(std::string texture)
|
||||
{
|
||||
m_tag_material = texture;
|
||||
}
|
||||
|
||||
|
||||
bool AmsMapingPopup::is_match_material(int id, std::string material)
|
||||
{
|
||||
return m_tag_material == material ? true : false;
|
||||
}
|
||||
|
||||
|
||||
void AmsMapingPopup::update_ams_data(std::map<std::string, Ams*> amsList)
|
||||
{
|
||||
std::map<std::string, Ams *>::iterator ams_iter;
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "ams_mapping total count " << amsList.size();
|
||||
|
||||
for (ams_iter = amsList.begin(); ams_iter != amsList.end(); ams_iter++) {
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "ams_mapping ams id " << ams_iter->first.c_str();
|
||||
|
||||
auto ams_indx = atoi(ams_iter->first.c_str());
|
||||
Ams *ams_group = ams_iter->second;
|
||||
std::vector<TrayData> tray_datas;
|
||||
std::map<std::string, AmsTray *>::iterator tray_iter;
|
||||
|
||||
for (tray_iter = ams_group->trayList.begin(); tray_iter != ams_group->trayList.end(); tray_iter++) {
|
||||
AmsTray *tray_data = tray_iter->second;
|
||||
TrayData td;
|
||||
|
||||
td.id = ams_indx * AMS_TOTAL_COUNT + atoi(tray_data->id.c_str());
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "ams_mapping ams data ==type==" << tray_data->type << "==colour=="<<tray_data->color << "==trayid=="<<tray_data->id.c_str() << "==ftrayid=="<<td.id;
|
||||
|
||||
if (!tray_data->is_exists) {
|
||||
td.type = EMPTY;
|
||||
} else {
|
||||
if (!tray_data->is_tray_info_ready()) {
|
||||
td.type = THIRD;
|
||||
} else {
|
||||
td.type = NORMAL;
|
||||
td.colour = AmsTray::decode_color(tray_data->color);
|
||||
td.name = tray_data->type;
|
||||
}
|
||||
}
|
||||
|
||||
tray_datas.push_back(td);
|
||||
}
|
||||
add_ams_mapping(tray_datas);
|
||||
}
|
||||
|
||||
Layout();
|
||||
Fit();
|
||||
}
|
||||
|
||||
void AmsMapingPopup::add_ams_mapping(std::vector<TrayData> tray_data)
|
||||
{
|
||||
wxBoxSizer *sizer_mapping_list = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
|
||||
for (auto i = 0; i < tray_data.size(); i++) {
|
||||
wxBoxSizer *sizer_mapping_item = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// set number
|
||||
auto number = new wxStaticText(this, wxID_ANY, wxString::Format("%02d",tray_data[i].id + 1), wxDefaultPosition, wxDefaultSize, 0);
|
||||
number->SetFont(::Label::Body_13);
|
||||
number->SetForegroundColour(wxColour(0X6B, 0X6B, 0X6B));
|
||||
number->Wrap(-1);
|
||||
|
||||
|
||||
// set button
|
||||
Button *m_filament_name = new Button(this, "", wxEmptyString);
|
||||
m_filament_name->SetSize(wxSize(FromDIP(38), FromDIP(20)));
|
||||
m_filament_name->SetMinSize(wxSize(FromDIP(38), FromDIP(20)));
|
||||
m_filament_name->SetMaxSize(wxSize(FromDIP(38), FromDIP(20)));
|
||||
m_filament_name->SetCornerRadius(5);
|
||||
m_filament_name->SetFont(::Label::Body_12);
|
||||
|
||||
if (tray_data[i].type == NORMAL) {
|
||||
|
||||
if (m_filament_name->GetTextExtent(tray_data[i].name).x > FromDIP(38)) {
|
||||
m_filament_name->SetFont(::Label::Body_10);
|
||||
auto name = tray_data[i].name.substr(0, 3) + "." + tray_data[i].name.substr(tray_data[i].name.length() - 1);
|
||||
m_filament_name->SetLabel(name);
|
||||
} else {
|
||||
m_filament_name->SetLabel(tray_data[i].name);
|
||||
}
|
||||
|
||||
auto material_name_colour = tray_data[i].colour.GetLuminance() < 0.5 ? *wxWHITE : wxColour(0x26, 0x2E, 0x30);
|
||||
m_filament_name->SetTextColor(material_name_colour);
|
||||
m_filament_name->SetBackgroundColor(tray_data[i].colour);
|
||||
|
||||
if (tray_data[i].colour == *wxWHITE) {
|
||||
m_filament_name->SetBorderColor(wxColour(0xAC, 0xAC, 0xAC));
|
||||
} else {
|
||||
m_filament_name->SetBorderColor(tray_data[i].colour);
|
||||
}
|
||||
|
||||
m_filament_name->Bind(wxEVT_BUTTON, [this, tray_data, i](wxCommandEvent &e) {
|
||||
if (!is_match_material(tray_data[i].id, tray_data[i].name)) return;
|
||||
wxCommandEvent event(EVT_SET_FINISH_MAPPING);
|
||||
event.SetInt(tray_data[i].id);
|
||||
wxString param = wxString::Format("%d|%d|%d|%02d", tray_data[i].colour.Red(), tray_data[i].colour.Green(), tray_data[i].colour.Blue(), tray_data[i].id + 1);
|
||||
event.SetString(param);
|
||||
event.SetEventObject(this->GetParent());
|
||||
wxPostEvent(this->GetParent(), event);
|
||||
Dismiss();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// temp
|
||||
if (tray_data[i].type == EMPTY) {
|
||||
m_filament_name->SetLabel("-");
|
||||
m_filament_name->SetTextColor(*wxWHITE);
|
||||
m_filament_name->SetBackgroundColor(wxColour(0x6B, 0x6B, 0x6B));
|
||||
m_filament_name->SetBorderColor(wxColour(0x6B, 0x6B, 0x6B));
|
||||
m_filament_name->Bind(wxEVT_BUTTON, [this, tray_data, i](wxCommandEvent &e) {
|
||||
wxCommandEvent event(EVT_SET_FINISH_MAPPING);
|
||||
event.SetInt(tray_data[i].id);
|
||||
wxString param = wxString::Format("%d|%d|%d|%02d", 0x6B, 0x6B, 0x6B, tray_data[i].id + 1);
|
||||
event.SetString(param);
|
||||
event.SetEventObject(this->GetParent());
|
||||
wxPostEvent(this->GetParent(), event);
|
||||
Dismiss();
|
||||
});
|
||||
}
|
||||
|
||||
// third party
|
||||
if (tray_data[i].type == THIRD) {
|
||||
m_filament_name->SetLabel("?");
|
||||
m_filament_name->SetTextColor(*wxWHITE);
|
||||
m_filament_name->SetBackgroundColor(wxColour(0x6B, 0x6B, 0x6B));
|
||||
m_filament_name->SetBorderColor(wxColour(0x6B, 0x6B, 0x6B));
|
||||
m_filament_name->Bind(wxEVT_BUTTON, [this, tray_data, i](wxCommandEvent &e) {
|
||||
wxCommandEvent event(EVT_SET_FINISH_MAPPING);
|
||||
event.SetInt(tray_data[i].id);
|
||||
wxString param = wxString::Format("%d|%d|%d|%02d", 0x6B, 0x6B, 0x6B, tray_data[i].id + 1);
|
||||
event.SetString(param);
|
||||
event.SetEventObject(this->GetParent());
|
||||
wxPostEvent(this->GetParent(), event);
|
||||
Dismiss();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
sizer_mapping_item->Add(number, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
sizer_mapping_item->Add(m_filament_name, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
sizer_mapping_list->Add(sizer_mapping_item, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, FromDIP(5));
|
||||
}
|
||||
m_sizer_main->Add(sizer_mapping_list, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
}
|
||||
|
||||
void AmsMapingPopup::OnDismiss()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
bool AmsMapingPopup::ProcessLeftDown(wxMouseEvent &event)
|
||||
{
|
||||
return wxPopupTransientWindow::ProcessLeftDown(event);
|
||||
}
|
||||
|
||||
void AmsMapingPopup::paintEvent(wxPaintEvent &evt)
|
||||
{
|
||||
wxPaintDC dc(this);
|
||||
dc.SetPen(wxColour(0xAC, 0xAC, 0xAC));
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.DrawRoundedRectangle(0, 0, GetSize().x, GetSize().y, 0);
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
113
src/slic3r/GUI/AmsMappingPopup.hpp
Normal file
113
src/slic3r/GUI/AmsMappingPopup.hpp
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
#ifndef slic3r_GUI_AmsMappingPopup_hpp_
|
||||
#define slic3r_GUI_AmsMappingPopup_hpp_
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/intl.h>
|
||||
#include <wx/collpane.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/hyperlink.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/popupwin.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/wrapsizer.h>
|
||||
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "DeviceManager.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "BBLStatusBar.hpp"
|
||||
#include "BBLStatusBarSend.hpp"
|
||||
#include "Widgets/Label.hpp"
|
||||
#include "Widgets/Button.hpp"
|
||||
#include "Widgets/CheckBox.hpp"
|
||||
#include "Widgets/ComboBox.hpp"
|
||||
#include "Widgets/ScrolledWindow.hpp"
|
||||
#include <wx/simplebook.h>
|
||||
#include <wx/hashmap.h>
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
#define MATERIAL_ITEM_SIZE wxSize(FromDIP(42), FromDIP(31))
|
||||
#define AMS_TOTAL_COUNT 4
|
||||
|
||||
enum TrayType {
|
||||
NORMAL,
|
||||
THIRD,
|
||||
EMPTY
|
||||
};
|
||||
|
||||
struct TrayData
|
||||
{
|
||||
TrayType type;
|
||||
int id;
|
||||
std::string name;
|
||||
wxColour colour;
|
||||
};
|
||||
|
||||
class MaterialItem: public wxPanel
|
||||
{
|
||||
public:
|
||||
MaterialItem(wxWindow *parent,wxColour mcolour, wxString mname);
|
||||
~MaterialItem();
|
||||
|
||||
wxColour m_material_coloul;
|
||||
wxString m_material_name;
|
||||
|
||||
wxColour m_ams_coloul;
|
||||
wxString m_ams_name;
|
||||
|
||||
bool m_selected {false};
|
||||
bool m_warning{false};
|
||||
|
||||
void msw_rescale();
|
||||
void set_ams_info(wxColour col, wxString txt);
|
||||
|
||||
void on_normal();
|
||||
void on_selected();
|
||||
void on_warning();
|
||||
|
||||
void on_left_down(wxMouseEvent &evt);
|
||||
void paintEvent(wxPaintEvent &evt);
|
||||
void render(wxDC &dc);
|
||||
void doRender(wxDC &dc);
|
||||
};
|
||||
|
||||
|
||||
class AmsMapingPopup : public wxPopupTransientWindow
|
||||
{
|
||||
public:
|
||||
AmsMapingPopup(wxWindow *parent);
|
||||
~AmsMapingPopup() {};
|
||||
|
||||
std::vector<std::string> m_materials_list;
|
||||
std::string m_tag_material;
|
||||
wxBoxSizer *m_sizer_main;
|
||||
|
||||
virtual void Popup(wxWindow *focus = NULL) wxOVERRIDE;
|
||||
void update_materials_list(std::vector<std::string> list);
|
||||
void set_tag_texture(std::string texture);
|
||||
void update_ams_data(std::map<std::string, Ams *> amsList);
|
||||
void add_ams_mapping(std::vector<TrayData> tray_data);
|
||||
bool is_match_material(int id, std::string material);
|
||||
virtual void OnDismiss() wxOVERRIDE;
|
||||
virtual bool ProcessLeftDown(wxMouseEvent &event) wxOVERRIDE;
|
||||
void paintEvent(wxPaintEvent &evt);
|
||||
};
|
||||
|
||||
wxDECLARE_EVENT(EVT_SET_FINISH_MAPPING, wxCommandEvent);
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
#endif
|
||||
199
src/slic3r/GUI/AmsWidgets.cpp
Normal file
199
src/slic3r/GUI/AmsWidgets.cpp
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
#include "AmsWidgets.hpp"
|
||||
#include <wx/button.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/dataview.h>
|
||||
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI_ObjectList.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "Widgets/Label.hpp"
|
||||
#include "format.hpp"
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
TrayListModel::TrayListModel() :
|
||||
wxDataViewVirtualListModel(0)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void TrayListModel::GetValueByRow(wxVariant& variant,
|
||||
unsigned int row, unsigned int col) const
|
||||
{
|
||||
switch (col) {
|
||||
case Col_TrayTitle:
|
||||
if (row >= m_titleColValues.GetCount())
|
||||
variant = wxString::Format("N/A", row);
|
||||
else
|
||||
variant = m_titleColValues[row];
|
||||
break;
|
||||
case Col_TrayColor:
|
||||
if (row >= m_colorColValues.GetCount())
|
||||
variant = wxString::Format("N/A", row);
|
||||
else
|
||||
variant = m_colorColValues[row];
|
||||
break;
|
||||
case Col_TrayMeterial:
|
||||
if (row >= m_meterialColValues.GetCount())
|
||||
variant = wxString::Format("N/A", row);
|
||||
else
|
||||
variant = m_meterialColValues[row];
|
||||
break;
|
||||
case Col_TrayWeight:
|
||||
if (row >= m_weightColValues.GetCount())
|
||||
variant = wxString::Format("N/A", row);
|
||||
else
|
||||
variant = m_weightColValues[row];
|
||||
break;
|
||||
case Col_TrayDiameter:
|
||||
if (row >= m_diameterColValues.GetCount())
|
||||
variant = wxString::Format("N/A", row);
|
||||
else
|
||||
variant = m_diameterColValues[row];
|
||||
break;
|
||||
case Col_TrayTime:
|
||||
if (row >= m_timeColValues.GetCount())
|
||||
variant = wxString::Format("N/A", row);
|
||||
else
|
||||
variant = m_timeColValues[row];
|
||||
break;
|
||||
case Col_TraySN:
|
||||
if (row >= m_snColValues.GetCount())
|
||||
variant = wxString::Format("N/A", row);
|
||||
else
|
||||
variant = m_snColValues[row];
|
||||
break;
|
||||
case Col_TrayManufacturer:
|
||||
if (row >= m_manufacturerColValues.GetCount())
|
||||
variant = wxString::Format("N/A", row);
|
||||
else
|
||||
variant = m_manufacturerColValues[row];
|
||||
break;
|
||||
case Col_TraySaturability:
|
||||
if (row >= m_saturabilityColValues.GetCount())
|
||||
variant = wxString::Format("N/A", row);
|
||||
else
|
||||
variant = m_saturabilityColValues[row];
|
||||
break;
|
||||
case Col_TrayTransmittance:
|
||||
if (row >= m_transmittanceColValues.GetCount())
|
||||
variant = wxString::Format("N/A", row);
|
||||
else
|
||||
variant = m_transmittanceColValues[row];
|
||||
break;
|
||||
case Col_TraySmooth:
|
||||
if (row >= m_smoothColValues.GetCount())
|
||||
variant = wxString::Format("N/A", row);
|
||||
else
|
||||
variant = m_smoothColValues[row];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool TrayListModel::GetAttrByRow(unsigned int row, unsigned int col,
|
||||
wxDataViewItemAttr& attr) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TrayListModel::SetValueByRow(const wxVariant& variant,
|
||||
unsigned int row, unsigned int col)
|
||||
{
|
||||
switch (col)
|
||||
{
|
||||
case Col_TrayTitle:
|
||||
case Col_TrayColor:
|
||||
case Col_TrayMeterial:
|
||||
case Col_TrayWeight:
|
||||
case Col_TrayDiameter:
|
||||
case Col_TrayTime:
|
||||
case Col_TraySN:
|
||||
case Col_TrayManufacturer:
|
||||
case Col_TraySaturability:
|
||||
case Col_TrayTransmittance:
|
||||
case Col_TraySmooth:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TrayListModel::update(MachineObject* obj)
|
||||
{
|
||||
if (!obj) return;
|
||||
|
||||
m_titleColValues.clear();
|
||||
m_colorColValues.clear();
|
||||
m_meterialColValues.clear();
|
||||
m_weightColValues.clear();
|
||||
m_diameterColValues.clear();
|
||||
m_timeColValues.clear();
|
||||
m_snColValues.clear();
|
||||
m_manufacturerColValues.clear();
|
||||
m_saturabilityColValues.clear();
|
||||
m_transmittanceColValues.clear();
|
||||
|
||||
std::map<std::string, Ams*>::iterator ams_it;
|
||||
std::map<std::string, AmsTray*>::iterator tray_it;
|
||||
int tray_index = 0;
|
||||
for (ams_it = obj->amsList.begin(); ams_it != obj->amsList.end(); ams_it++) {
|
||||
if (ams_it->second) {
|
||||
for (tray_it = ams_it->second->trayList.begin(); tray_it != ams_it->second->trayList.end(); tray_it++) {
|
||||
AmsTray* tray = tray_it->second;
|
||||
if (tray) {
|
||||
tray_index++;
|
||||
wxString title_text = wxString::Format("tray %s(ams %s)", tray->id, ams_it->second->id);
|
||||
m_titleColValues.push_back(title_text);
|
||||
wxString color_text = wxString::Format("%s", tray->wx_color.GetAsString());
|
||||
m_colorColValues.push_back(color_text);
|
||||
wxString meterial_text = wxString::Format("%s", tray->type);
|
||||
m_meterialColValues.push_back(meterial_text);
|
||||
wxString weight_text = wxString::Format("%sg", tray->weight);
|
||||
m_weightColValues.push_back(weight_text);
|
||||
wxString diameter_text = wxString::Format("%0.2f", tray->diameter);
|
||||
m_diameterColValues.push_back(diameter_text);
|
||||
wxString time_text = wxString::Format("%s", tray->time);
|
||||
m_timeColValues.push_back(time_text);
|
||||
wxString sn_text = wxString::Format("%s", tray->uuid);
|
||||
m_snColValues.push_back(sn_text);
|
||||
wxString manufacturer_text = wxString::Format("%s", tray->sub_brands);
|
||||
m_manufacturerColValues.push_back(manufacturer_text);
|
||||
// TODO:
|
||||
//wxString saturability_text = wxString::Format("%s", tray->saturability);
|
||||
//m_saturabilityColValues.push_back(saturability_text);
|
||||
//wxString transmittance_text = wxString::Format("%s", tray->transmittance);
|
||||
//m_transmittanceColValues.push_back(transmittance_text);
|
||||
//wxString smooth_text = wxString::Format("%s", tray->smooth);
|
||||
//m_smoothColValues.push_back(smooth_text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reset(m_titleColValues.GetCount());
|
||||
}
|
||||
void TrayListModel::clear_data()
|
||||
{
|
||||
m_titleColValues.clear();
|
||||
m_colorColValues.clear();
|
||||
m_meterialColValues.clear();
|
||||
m_weightColValues.clear();
|
||||
m_diameterColValues.clear();
|
||||
m_timeColValues.clear();
|
||||
m_snColValues.clear();
|
||||
m_manufacturerColValues.clear();
|
||||
m_saturabilityColValues.clear();
|
||||
m_transmittanceColValues.clear();
|
||||
m_smoothColValues.clear();
|
||||
|
||||
Reset(0);
|
||||
}
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
106
src/slic3r/GUI/AmsWidgets.hpp
Normal file
106
src/slic3r/GUI/AmsWidgets.hpp
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
#ifndef slic3r_AmsWidgets_hpp_
|
||||
#define slic3r_AmsWidgets_hpp_
|
||||
|
||||
|
||||
#include <wx/notebook.h>
|
||||
#include <wx/scrolwin.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/bmpcbox.h>
|
||||
#include <wx/bmpbuttn.h>
|
||||
#include <wx/treectrl.h>
|
||||
#include <wx/imaglist.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/grid.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/icon.h>
|
||||
#include <wx/bmpbuttn.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/statbox.h>
|
||||
#include <wx/tglbtn.h>
|
||||
#include <wx/popupwin.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/artprov.h>
|
||||
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "Event.hpp"
|
||||
#include "libslic3r/ProjectTask.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
class TrayListModel : public wxDataViewVirtualListModel
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
Col_TrayTitle,
|
||||
Col_TrayColor,
|
||||
Col_TrayMeterial,
|
||||
Col_TrayWeight,
|
||||
Col_TrayDiameter,
|
||||
Col_TrayTime,
|
||||
Col_TraySN,
|
||||
Col_TrayManufacturer,
|
||||
Col_TraySaturability,
|
||||
Col_TrayTransmittance,
|
||||
Col_TraySmooth,
|
||||
Col_Max,
|
||||
};
|
||||
|
||||
TrayListModel();
|
||||
|
||||
virtual unsigned int GetColumnCount() const wxOVERRIDE
|
||||
{
|
||||
return Col_Max;
|
||||
}
|
||||
|
||||
virtual wxString GetColumnType(unsigned int col) const wxOVERRIDE
|
||||
{
|
||||
return "string";
|
||||
}
|
||||
|
||||
virtual void GetValueByRow(wxVariant& variant,
|
||||
unsigned int row, unsigned int col) const wxOVERRIDE;
|
||||
virtual bool GetAttrByRow(unsigned int row, unsigned int col,
|
||||
wxDataViewItemAttr& attr) const wxOVERRIDE;
|
||||
virtual bool SetValueByRow(const wxVariant& variant,
|
||||
unsigned int row, unsigned int col) wxOVERRIDE;
|
||||
|
||||
void update(MachineObject* obj);
|
||||
void clear_data();
|
||||
|
||||
private:
|
||||
wxArrayString m_titleColValues;
|
||||
wxArrayString m_colorColValues;
|
||||
wxArrayString m_meterialColValues;
|
||||
wxArrayString m_weightColValues;
|
||||
wxArrayString m_diameterColValues;
|
||||
wxArrayString m_timeColValues;
|
||||
wxArrayString m_snColValues;
|
||||
wxArrayString m_manufacturerColValues;
|
||||
wxArrayString m_saturabilityColValues;
|
||||
wxArrayString m_transmittanceColValues;
|
||||
wxArrayString m_smoothColValues;
|
||||
|
||||
};
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
|
||||
#endif /* slic3r_Tab_hpp_ */
|
||||
1121
src/slic3r/GUI/Auxiliary.cpp
Normal file
1121
src/slic3r/GUI/Auxiliary.cpp
Normal file
File diff suppressed because it is too large
Load diff
232
src/slic3r/GUI/Auxiliary.hpp
Normal file
232
src/slic3r/GUI/Auxiliary.hpp
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
#ifndef slic3r_Auxiliary_hpp_
|
||||
#define slic3r_Auxiliary_hpp_
|
||||
|
||||
#include "Tabbook.hpp"
|
||||
#include <wx/notebook.h>
|
||||
#include <wx/scrolwin.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/bmpcbox.h>
|
||||
#include <wx/bmpbuttn.h>
|
||||
#include <wx/treectrl.h>
|
||||
#include <wx/imaglist.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/grid.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/statline.h>
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/icon.h>
|
||||
#include <wx/bmpbuttn.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/gbsizer.h>
|
||||
#include <wx/statbox.h>
|
||||
#include <wx/tglbtn.h>
|
||||
#include <wx/popupwin.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/webrequest.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "Event.hpp"
|
||||
#include "libslic3r/ProjectTask.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
#include "slic3r/GUI/MonitorBasePanel.h"
|
||||
#include "slic3r/GUI/StatusPanel.hpp"
|
||||
#include "slic3r/GUI/UpgradePanel.hpp"
|
||||
#include "slic3r/GUI/AmsWidgets.hpp"
|
||||
#include "Widgets/SideTools.hpp"
|
||||
|
||||
#define AUFILE_GREY700 wxColour(107, 107, 107)
|
||||
#define AUFILE_GREY300 wxColour(238, 238, 238)
|
||||
#define AUFILE_GREY200 wxColour(248, 248, 248)
|
||||
|
||||
enum AuxiliaryFolderType {
|
||||
MODEL_PICTURE,
|
||||
BILL_OF_MATERIALS,
|
||||
ASSEMBLY_GUIDE,
|
||||
OTHERS,
|
||||
THUMBNAILS,
|
||||
DESIGNER,
|
||||
};
|
||||
|
||||
const static std::array<wxString, 5> s_default_folders = {("Model Pictures"), ("Bill of Materials"), ("Assembly Guide"), ("Others"), (".thumbnails")};
|
||||
|
||||
|
||||
enum ValidationType { Valid, NoValid, Warning };
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
class AuFile : public wxPanel
|
||||
{
|
||||
public:
|
||||
AuxiliaryFolderType m_type;
|
||||
bool m_hover{false};
|
||||
bool m_cover{false};
|
||||
wxStaticText* m_text_name {nullptr};
|
||||
::TextInput* m_input_name {nullptr};
|
||||
fs::path m_file_path;
|
||||
wxString m_file_name;
|
||||
wxString cover_text_left;
|
||||
wxString cover_text_right;
|
||||
wxString cover_text_cover;
|
||||
wxBitmap m_file_bitmap;
|
||||
wxBitmap m_file_cover;
|
||||
wxBitmap m_file_edit_mask;
|
||||
wxBitmap m_file_delete;
|
||||
|
||||
wxBitmap m_bitmap_excel;
|
||||
wxBitmap m_bitmap_pdf;
|
||||
wxBitmap m_bitmap_txt;
|
||||
|
||||
public:
|
||||
AuFile(wxWindow *parent, fs::path file_path, wxString file_name, AuxiliaryFolderType type, wxWindowID id = wxID_ANY, const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize, long style = wxTAB_TRAVERSAL);
|
||||
void enter_rename_mode();
|
||||
void exit_rename_mode();
|
||||
void OnPaint(wxPaintEvent &evt);
|
||||
void PaintBackground(wxDC &dc);
|
||||
void OnEraseBackground(wxEraseEvent &evt);
|
||||
void PaintForeground(wxDC &dc);
|
||||
void on_mouse_enter(wxMouseEvent &evt);
|
||||
void on_mouse_leave(wxMouseEvent &evt);
|
||||
void on_input_enter(wxCommandEvent &evt);
|
||||
void on_dclick(wxMouseEvent &evt);
|
||||
void on_mouse_left_up(wxMouseEvent &evt);
|
||||
|
||||
void on_set_cover();
|
||||
void on_set_delete();
|
||||
void on_set_rename();
|
||||
void on_set_open();
|
||||
|
||||
void set_cover(bool cover);
|
||||
void msw_rescale();
|
||||
~AuFile();
|
||||
};
|
||||
|
||||
class AuFiles
|
||||
{
|
||||
public:
|
||||
wxString path;
|
||||
AuFile * file;
|
||||
};
|
||||
|
||||
WX_DEFINE_ARRAY(AuFiles *, AuFilesHash);
|
||||
|
||||
class AuFolderPanel : public wxPanel
|
||||
{
|
||||
public:
|
||||
AuFolderPanel(wxWindow * parent,
|
||||
AuxiliaryFolderType type,
|
||||
wxWindowID id = wxID_ANY,
|
||||
const wxPoint & pos = wxDefaultPosition,
|
||||
const wxSize & size = wxDefaultSize,
|
||||
long style = wxTAB_TRAVERSAL);
|
||||
~AuFolderPanel();
|
||||
|
||||
|
||||
|
||||
|
||||
void clear();
|
||||
void update_cover();
|
||||
void update(std::vector<fs::path> paths);
|
||||
void msw_rescale();
|
||||
|
||||
public:
|
||||
AuxiliaryFolderType m_type;
|
||||
wxScrolledWindow * m_scrolledWindow{nullptr};
|
||||
wxGridSizer * m_gsizer_content{nullptr};
|
||||
Button * m_button_add{nullptr};
|
||||
Button * m_button_del{nullptr};
|
||||
AuFilesHash m_aufiles_list;
|
||||
|
||||
void on_add(wxCommandEvent &event);
|
||||
void on_delete(wxCommandEvent &event);
|
||||
};
|
||||
|
||||
class DesignerPanel : public wxPanel
|
||||
{
|
||||
public:
|
||||
DesignerPanel(wxWindow * parent,
|
||||
AuxiliaryFolderType type,
|
||||
wxWindowID id = wxID_ANY,
|
||||
const wxPoint & pos = wxDefaultPosition,
|
||||
const wxSize & size = wxDefaultSize,
|
||||
long style = wxTAB_TRAVERSAL);
|
||||
~DesignerPanel();
|
||||
|
||||
::TextInput* m_input_designer {nullptr};
|
||||
::TextInput* m_imput_model_name {nullptr};
|
||||
//wxComboBox* m_combo_license {nullptr};
|
||||
bool Show(bool show) override;
|
||||
void init_license_list();
|
||||
void on_input_enter_designer(wxCommandEvent &evt);
|
||||
void on_input_enter_model(wxCommandEvent &evt);
|
||||
void on_select_license(wxCommandEvent& evt);
|
||||
void update_info();
|
||||
void msw_rescale();
|
||||
};
|
||||
|
||||
|
||||
class AuxiliaryPanel : public wxPanel
|
||||
{
|
||||
private:
|
||||
Tabbook *m_tabpanel = {nullptr};
|
||||
wxSizer *m_main_sizer = {nullptr};
|
||||
|
||||
AuFolderPanel *m_pictures_panel= {nullptr};
|
||||
AuFolderPanel *m_bill_of_materials_panel= {nullptr};
|
||||
AuFolderPanel *m_assembly_panel= {nullptr};
|
||||
AuFolderPanel *m_others_panel= {nullptr};
|
||||
DesignerPanel * m_designer_panel= {nullptr};
|
||||
|
||||
/* images */
|
||||
wxBitmap m_signal_strong_img;
|
||||
wxBitmap m_signal_middle_img;
|
||||
wxBitmap m_signal_weak_img;
|
||||
wxBitmap m_signal_no_img;
|
||||
wxBitmap m_printer_img;
|
||||
wxBitmap m_arrow_img;
|
||||
wxWindow *create_side_tools();
|
||||
|
||||
public:
|
||||
AuxiliaryPanel(wxWindow *parent, wxWindowID id = wxID_ANY, const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize, long style = wxTAB_TRAVERSAL);
|
||||
~AuxiliaryPanel();
|
||||
void init_bitmap();
|
||||
void init_tabpanel();
|
||||
|
||||
void Split(const std::string &src, const std::string &separator, std::vector<std::string> &dest);
|
||||
|
||||
void msw_rescale();
|
||||
void on_size(wxSizeEvent &event);
|
||||
bool Show(bool show);
|
||||
|
||||
// core logic
|
||||
std::map<std::string, std::vector<fs::path>> m_paths_list;
|
||||
wxString m_root_dir;
|
||||
void init_auxiliary();
|
||||
void create_folder(wxString name = wxEmptyString);
|
||||
std::string replaceSpace(std::string s, std::string ts, std::string ns);
|
||||
void on_import_file(wxCommandEvent &event);
|
||||
void Reload(wxString aux_path);
|
||||
|
||||
void update_all_panel();
|
||||
void update_all_cover();
|
||||
};
|
||||
|
||||
wxDECLARE_EVENT(EVT_AUXILIARY_IMPORT, wxCommandEvent);
|
||||
wxDECLARE_EVENT(EVT_AUXILIARY_UPDATE_COVER, wxCommandEvent);
|
||||
wxDECLARE_EVENT(EVT_AUXILIARY_UPDATE_DELETE, wxCommandEvent);
|
||||
wxDECLARE_EVENT(EVT_AUXILIARY_UPDATE_RENAME, wxCommandEvent);
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
#endif
|
||||
489
src/slic3r/GUI/AuxiliaryDataViewModel.cpp
Normal file
489
src/slic3r/GUI/AuxiliaryDataViewModel.cpp
Normal file
|
|
@ -0,0 +1,489 @@
|
|||
#include "AuxiliaryDataViewModel.hpp"
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/Format/bbs_3mf.hpp"
|
||||
|
||||
const static std::array<wxString, 4> s_default_folders = {
|
||||
_L("Model Pictures"),
|
||||
_L("Bill of Materials"),
|
||||
_L("Assembly Guide"),
|
||||
_L("Others")
|
||||
};
|
||||
|
||||
AuxiliaryModel::AuxiliaryModel()
|
||||
{
|
||||
m_root = nullptr;
|
||||
}
|
||||
|
||||
void AuxiliaryModel::Init(wxString aux_path)
|
||||
{
|
||||
m_root = new AuxiliaryModelNode();
|
||||
m_root_dir = aux_path;
|
||||
|
||||
if (wxDirExists(m_root_dir)) {
|
||||
fs::path path_to_del(m_root_dir.ToStdWstring());
|
||||
try {
|
||||
fs::remove_all(path_to_del);
|
||||
}
|
||||
catch (...) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Failed removing the auxiliary directory " << m_root_dir.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
fs::path top_dir_path(m_root_dir.ToStdWstring());
|
||||
fs::create_directory(top_dir_path);
|
||||
|
||||
for (auto folder : s_default_folders)
|
||||
CreateFolder(folder);
|
||||
}
|
||||
|
||||
AuxiliaryModel::~AuxiliaryModel()
|
||||
{
|
||||
if (wxDirExists(m_root_dir)) {
|
||||
fs::path path_to_del(m_root_dir.ToStdWstring());
|
||||
try {
|
||||
fs::remove_all(path_to_del);
|
||||
}
|
||||
catch (...) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Failed removing the auxiliary directory " << m_root_dir.c_str();
|
||||
}
|
||||
m_root_dir = "";
|
||||
}
|
||||
delete m_root;
|
||||
m_root = nullptr;
|
||||
}
|
||||
|
||||
void AuxiliaryModel::Reload(wxString aux_path)
|
||||
{
|
||||
fs::path new_aux_path(aux_path.ToStdWstring());
|
||||
|
||||
// Clean
|
||||
try {
|
||||
fs::remove_all(fs::path(m_root_dir.ToStdWstring()));
|
||||
}
|
||||
catch (...) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Failed removing the auxiliary directory " << m_root_dir.c_str();
|
||||
}
|
||||
|
||||
if (m_root) {
|
||||
delete m_root;
|
||||
m_root = nullptr;
|
||||
}
|
||||
Cleared();
|
||||
|
||||
// Create new root.
|
||||
m_root = new AuxiliaryModelNode();
|
||||
m_root_dir = aux_path;
|
||||
|
||||
// Check new path. If not exist, create a new one.
|
||||
if (!fs::exists(new_aux_path)) {
|
||||
fs::create_directory(new_aux_path);
|
||||
// Create default folders if they are not loaded
|
||||
wxDataViewItemArray default_items;
|
||||
for (auto folder : s_default_folders) {
|
||||
wxString folder_path = aux_path + "\\" + folder;
|
||||
if (fs::exists(folder_path.ToStdWstring())) continue;
|
||||
|
||||
fs::create_directory(folder_path.ToStdWstring());
|
||||
AuxiliaryModelNode *node = new AuxiliaryModelNode(m_root,
|
||||
folder_path,
|
||||
true);
|
||||
default_items.Add(wxDataViewItem(node));
|
||||
}
|
||||
ItemsAdded(wxDataViewItem(nullptr), default_items);
|
||||
return;
|
||||
}
|
||||
|
||||
// Load from new path
|
||||
std::map<fs::path, AuxiliaryModelNode *> dir_cache;
|
||||
fs::directory_iterator iter_end;
|
||||
wxDataViewItemArray items;
|
||||
for (fs::directory_iterator iter(new_aux_path); iter != iter_end; iter++) {
|
||||
wxString path = iter->path().generic_wstring();
|
||||
AuxiliaryModelNode* node = new AuxiliaryModelNode(m_root, path, fs::is_directory(iter->path()));
|
||||
items.Add(wxDataViewItem(node));
|
||||
|
||||
if (node->IsContainer()) {
|
||||
dir_cache.insert({ iter->path(), node });
|
||||
}
|
||||
}
|
||||
ItemsAdded(wxDataViewItem(nullptr), items);
|
||||
|
||||
items.Clear();
|
||||
for (auto dir : dir_cache) {
|
||||
for (fs::directory_iterator iter(dir.first); iter != iter_end; iter++) {
|
||||
if (fs::is_directory(iter->path()))
|
||||
continue;
|
||||
|
||||
wxString file_path = iter->path().generic_wstring();
|
||||
AuxiliaryModelNode* file = new AuxiliaryModelNode(dir.second, file_path, false);
|
||||
items.Add(wxDataViewItem(file));
|
||||
}
|
||||
ItemsAdded(wxDataViewItem(dir.second), items);
|
||||
}
|
||||
|
||||
// Create default folders if they are not loaded
|
||||
wxDataViewItemArray default_items;
|
||||
for (auto folder : s_default_folders) {
|
||||
wxString folder_path = aux_path + "\\" + folder;
|
||||
if (fs::exists(folder_path.ToStdWstring()))
|
||||
continue;
|
||||
|
||||
fs::create_directory(folder_path.ToStdWstring());
|
||||
AuxiliaryModelNode* node = new AuxiliaryModelNode(m_root, folder_path, true);
|
||||
default_items.Add(wxDataViewItem(node));
|
||||
}
|
||||
ItemsAdded(wxDataViewItem(nullptr), default_items);
|
||||
}
|
||||
|
||||
int AuxiliaryModel::Compare(const wxDataViewItem& item1, const wxDataViewItem& item2,
|
||||
unsigned int column, bool ascending) const
|
||||
{
|
||||
wxASSERT(item1.IsOk() && item2.IsOk());
|
||||
// should never happen
|
||||
|
||||
if (IsContainer(item1) && IsContainer(item2))
|
||||
{
|
||||
wxVariant value1, value2;
|
||||
GetValue(value1, item1, 0);
|
||||
GetValue(value2, item2, 0);
|
||||
|
||||
wxString str1 = value1.GetString();
|
||||
wxString str2 = value2.GetString();
|
||||
int res = str1.Cmp(str2);
|
||||
if (res) return res;
|
||||
|
||||
// items must be different
|
||||
wxUIntPtr litem1 = (wxUIntPtr)item1.GetID();
|
||||
wxUIntPtr litem2 = (wxUIntPtr)item2.GetID();
|
||||
|
||||
return litem1 - litem2;
|
||||
}
|
||||
|
||||
return wxDataViewModel::Compare(item1, item2, column, ascending);
|
||||
}
|
||||
|
||||
void AuxiliaryModel::GetValue(wxVariant& variant,
|
||||
const wxDataViewItem& item, unsigned int col) const
|
||||
{
|
||||
wxASSERT(item.IsOk());
|
||||
|
||||
AuxiliaryModelNode* node = (AuxiliaryModelNode*)item.GetID();
|
||||
switch (col)
|
||||
{
|
||||
case 0:
|
||||
variant = node->name;
|
||||
break;
|
||||
|
||||
default:
|
||||
wxLogError("AuxiliaryModel::GetValue: wrong column %d", col);
|
||||
}
|
||||
}
|
||||
|
||||
bool AuxiliaryModel::SetValue(const wxVariant& variant,
|
||||
const wxDataViewItem& item, unsigned int col)
|
||||
{
|
||||
wxASSERT(item.IsOk());
|
||||
|
||||
AuxiliaryModelNode* node = (AuxiliaryModelNode*)item.GetID();
|
||||
switch (col)
|
||||
{
|
||||
case 0:
|
||||
node->name = variant.GetString();
|
||||
return true;
|
||||
|
||||
default:
|
||||
wxLogError("AuxiliaryModel::SetValue: wrong column");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AuxiliaryModel::IsEnabled(const wxDataViewItem& item,
|
||||
unsigned int col) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
wxDataViewItem AuxiliaryModel::GetParent(const wxDataViewItem& item) const
|
||||
{
|
||||
AuxiliaryModelNode* node = (AuxiliaryModelNode*)item.GetID();
|
||||
|
||||
return wxDataViewItem(GetParent(node));
|
||||
}
|
||||
|
||||
bool AuxiliaryModel::IsContainer(const wxDataViewItem& item) const
|
||||
{
|
||||
// the invisible root node can have children
|
||||
// (in our model always "MyMusic")
|
||||
if (!item.IsOk())
|
||||
return true;
|
||||
|
||||
AuxiliaryModelNode* node = (AuxiliaryModelNode*)item.GetID();
|
||||
return node->IsContainer();
|
||||
}
|
||||
|
||||
static unsigned int count = 0;
|
||||
|
||||
unsigned int AuxiliaryModel::GetChildren(const wxDataViewItem& parent,
|
||||
wxDataViewItemArray& array) const
|
||||
{
|
||||
if (m_root == nullptr)
|
||||
return 0;
|
||||
|
||||
AuxiliaryModelNode* node = (AuxiliaryModelNode*)parent.GetID();
|
||||
if (!node)
|
||||
{
|
||||
node = m_root;
|
||||
}
|
||||
|
||||
count = node->GetChildren().GetCount();
|
||||
for (unsigned int pos = 0; pos < count; pos++)
|
||||
{
|
||||
AuxiliaryModelNode* child = node->GetChildren().Item(pos);
|
||||
array.Add(wxDataViewItem((void*)child));
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
wxDataViewItem AuxiliaryModel::CreateFolder(wxString name)
|
||||
{
|
||||
wxString folder_name = name;
|
||||
if (folder_name == wxEmptyString) {
|
||||
folder_name = _L("New Folder");
|
||||
for (int i = 1; i <= 1000; i++) {
|
||||
bool exist = false;
|
||||
for (AuxiliaryModelNode* node : m_root->GetChildren()) {
|
||||
if (!node->IsContainer())
|
||||
continue;
|
||||
|
||||
if (node->name == folder_name) {
|
||||
exist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!exist)
|
||||
break;
|
||||
|
||||
folder_name = _L("New Folder");
|
||||
folder_name << "(" << i << ")";
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (AuxiliaryModelNode* node : m_root->GetChildren()) {
|
||||
if (!node->IsContainer())
|
||||
continue;
|
||||
|
||||
if (node->name == folder_name) {
|
||||
return wxDataViewItem(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create folder in file system
|
||||
fs::path bfs_path((m_root_dir + "\\" + folder_name).ToStdWstring());
|
||||
if (fs::exists(bfs_path)) {
|
||||
try {
|
||||
bool is_done = fs::remove_all(bfs_path);
|
||||
if (!is_done)
|
||||
return wxDataViewItem(nullptr);
|
||||
}
|
||||
catch (...) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Failed removing the auxiliary directory " << m_root_dir.c_str();
|
||||
}
|
||||
}
|
||||
fs::create_directory(bfs_path);
|
||||
|
||||
// Create model node
|
||||
AuxiliaryModelNode* folder = new AuxiliaryModelNode(m_root, bfs_path.generic_wstring(), true);
|
||||
|
||||
// Notify wxDataViewCtrl to update ui
|
||||
wxDataViewItem folder_item(folder);
|
||||
ItemAdded(wxDataViewItem(NULL), folder_item);
|
||||
return folder_item;
|
||||
}
|
||||
|
||||
wxDataViewItemArray AuxiliaryModel::ImportFile(AuxiliaryModelNode* sel, wxArrayString file_paths)
|
||||
{
|
||||
if (sel == nullptr) {
|
||||
sel = m_root;
|
||||
}
|
||||
|
||||
wxDataViewItemArray added_items;
|
||||
AuxiliaryModelNode* parent = sel->IsContainer() ? sel : sel->GetParent();
|
||||
for (wxString file_path : file_paths) {
|
||||
bool exists = false;
|
||||
for (AuxiliaryModelNode* node : parent->GetChildren()) {
|
||||
if (node->path == file_path) {
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (exists)
|
||||
continue;
|
||||
|
||||
// Copy imported file to project temp directory
|
||||
fs::path src_bfs_path(file_path.ToStdWstring());
|
||||
wxString dir_path = m_root_dir;
|
||||
if (sel != m_root)
|
||||
dir_path += "\\" + sel->name;
|
||||
dir_path += "\\" + src_bfs_path.filename().generic_wstring();
|
||||
|
||||
boost::system::error_code ec;
|
||||
if (!fs::copy_file(src_bfs_path, fs::path(dir_path.ToStdWstring()), fs::copy_option::overwrite_if_exists, ec))
|
||||
continue;
|
||||
|
||||
// Update model data
|
||||
AuxiliaryModelNode* file = new AuxiliaryModelNode(parent, dir_path, false);
|
||||
|
||||
// Notify wxDataViewCtrl to update ui
|
||||
wxDataViewItem file_item(file);
|
||||
if (parent == m_root)
|
||||
parent = nullptr;
|
||||
Slic3r::put_other_changes();
|
||||
wxDataViewItem parent_item(parent);
|
||||
ItemAdded(parent_item, file_item);
|
||||
added_items.push_back(file_item);
|
||||
}
|
||||
|
||||
return added_items;
|
||||
}
|
||||
|
||||
void AuxiliaryModel::Delete(const wxDataViewItem& item)
|
||||
{
|
||||
AuxiliaryModelNode* node = (AuxiliaryModelNode*)item.GetID();
|
||||
if (!node) // happens if item.IsOk()==false
|
||||
return;
|
||||
|
||||
bool is_done = false;
|
||||
if (node->IsContainer()) {
|
||||
fs::path bfs_path((m_root_dir + "\\" + node->name).ToStdWstring());
|
||||
try {
|
||||
is_done = fs::remove_all(bfs_path);
|
||||
}
|
||||
catch (...) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Failed removing the auxiliary directory " << m_root_dir.c_str();
|
||||
}
|
||||
}
|
||||
else {
|
||||
fs::path bfs_path(node->path.ToStdWstring());
|
||||
is_done = fs::remove(bfs_path);
|
||||
}
|
||||
|
||||
if (!is_done)
|
||||
return;
|
||||
|
||||
node->GetParent()->GetChildren().Remove(node);
|
||||
|
||||
Slic3r::put_other_changes();
|
||||
wxDataViewItem parent_item = GetParent(item);
|
||||
ItemDeleted(parent_item, item);
|
||||
delete node;
|
||||
}
|
||||
|
||||
void AuxiliaryModel::MoveItem(const wxDataViewItem& dropped_item, const wxDataViewItem& dragged_item)
|
||||
{
|
||||
AuxiliaryModelNode* dropped = (AuxiliaryModelNode*)dropped_item.GetID();
|
||||
AuxiliaryModelNode* dragged = (AuxiliaryModelNode*)dragged_item.GetID();
|
||||
|
||||
if (dragged == nullptr || dragged->IsContainer())
|
||||
return;
|
||||
|
||||
AuxiliaryModelNode* target_folder = nullptr;
|
||||
if (dropped == nullptr) {
|
||||
target_folder = m_root;
|
||||
}
|
||||
else if (dropped->IsContainer()) {
|
||||
target_folder = dropped;
|
||||
}
|
||||
else {
|
||||
target_folder = dropped->GetParent();
|
||||
}
|
||||
|
||||
if (dragged->GetParent() == target_folder)
|
||||
return;
|
||||
|
||||
for (AuxiliaryModelNode* node : target_folder->GetChildren()) {
|
||||
if (node->path == dragged->path)
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate new path
|
||||
wxString new_path = m_root_dir;
|
||||
if (target_folder != m_root)
|
||||
new_path += "\\" + target_folder->name;
|
||||
new_path += "\\" + dragged->name;
|
||||
|
||||
// Perform file movement in file system
|
||||
fs::path bfs_new_path(new_path.ToStdWstring());
|
||||
fs::path bfs_old_path(dragged->path.ToStdWstring());
|
||||
boost::system::error_code err;
|
||||
fs::rename(bfs_old_path, bfs_new_path, err);
|
||||
if (err.failed())
|
||||
return;
|
||||
|
||||
// Reparent dragged node
|
||||
wxDataViewItem old_parent_item = this->GetParent(dragged_item);
|
||||
this->Reparent(dragged, target_folder);
|
||||
dragged->path = new_path;
|
||||
|
||||
// Notify wxDataViewCtrl to update ui
|
||||
Slic3r::put_other_changes();
|
||||
ItemDeleted(old_parent_item, wxDataViewItem(dragged));
|
||||
ItemAdded(wxDataViewItem(target_folder == m_root ? nullptr : target_folder), wxDataViewItem(dragged));
|
||||
}
|
||||
|
||||
bool AuxiliaryModel::IsOrphan(const wxDataViewItem& item)
|
||||
{
|
||||
AuxiliaryModelNode* node = (AuxiliaryModelNode*)item.GetID();
|
||||
return node->GetParent() != m_root;
|
||||
}
|
||||
|
||||
bool AuxiliaryModel::Rename(const wxDataViewItem& item, const wxString& name)
|
||||
{
|
||||
AuxiliaryModelNode* node = (AuxiliaryModelNode*)item.GetID();
|
||||
AuxiliaryModelNode* parent = node->GetParent();
|
||||
|
||||
if (node->IsContainer())
|
||||
return false;
|
||||
|
||||
if (!parent->IsContainer())
|
||||
return false;
|
||||
|
||||
for (AuxiliaryModelNode* cur_node : parent->GetChildren()) {
|
||||
if (cur_node->name == name)
|
||||
return false;
|
||||
}
|
||||
|
||||
boost::system::error_code err;
|
||||
fs::path old_path((m_root_dir + "\\" + parent->name + "\\" + node->name).ToStdWstring());
|
||||
fs::path new_path((m_root_dir + "\\" + parent->name + "\\" + name).ToStdWstring());
|
||||
fs::rename(old_path, new_path, err);
|
||||
if (err.failed())
|
||||
return false;
|
||||
|
||||
Slic3r::put_other_changes();
|
||||
node->name = name;
|
||||
node->path = m_root_dir + "\\" + parent->name + "\\" + name;
|
||||
return true;
|
||||
}
|
||||
|
||||
AuxiliaryModelNode* AuxiliaryModel::GetParent(AuxiliaryModelNode* node) const
|
||||
{
|
||||
if (node == m_root || node->GetParent() == m_root)
|
||||
return nullptr;
|
||||
|
||||
return node->GetParent();
|
||||
}
|
||||
|
||||
void AuxiliaryModel::Reparent(AuxiliaryModelNode* node, AuxiliaryModelNode* new_parent)
|
||||
{
|
||||
if (node->IsContainer())
|
||||
return;
|
||||
|
||||
node->GetParent()->GetChildren().Remove(node);
|
||||
node->SetParent(new_parent);
|
||||
new_parent->Append(node);
|
||||
}
|
||||
155
src/slic3r/GUI/AuxiliaryDataViewModel.hpp
Normal file
155
src/slic3r/GUI/AuxiliaryDataViewModel.hpp
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
#ifndef slic3r_GUI_AuxiliaryDataViewModel_hpp_
|
||||
#define slic3r_GUI_AuxiliaryDataViewModel_hpp_
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
#include "wx/dataview.h"
|
||||
#include "wx/hashmap.h"
|
||||
#include "wx/vector.h"
|
||||
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
class AuxiliaryModelNode;
|
||||
WX_DEFINE_ARRAY_PTR(AuxiliaryModelNode*, AuxiliaryModelNodePtrArray);
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
class AuxiliaryModelNode
|
||||
{
|
||||
public:
|
||||
AuxiliaryModelNode()
|
||||
{
|
||||
m_parent = NULL;
|
||||
name = "";
|
||||
m_container = true;
|
||||
m_root = true;
|
||||
}
|
||||
|
||||
AuxiliaryModelNode(AuxiliaryModelNode* parent, const wxString& abs_path, bool is_container)
|
||||
{
|
||||
m_parent = parent;
|
||||
m_container = is_container;
|
||||
m_root = false;
|
||||
path = abs_path;
|
||||
fs::path path_obj(path.c_str());
|
||||
name = path_obj.filename().generic_wstring();
|
||||
|
||||
parent->Append(this);
|
||||
}
|
||||
|
||||
~AuxiliaryModelNode()
|
||||
{
|
||||
// free all our children nodes
|
||||
size_t count = m_children.GetCount();
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
AuxiliaryModelNode* child = m_children[i];
|
||||
delete child;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsContainer() const
|
||||
{
|
||||
return m_container;
|
||||
}
|
||||
|
||||
AuxiliaryModelNode* GetParent()
|
||||
{
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
void SetParent(AuxiliaryModelNode* parent)
|
||||
{
|
||||
m_parent = parent;
|
||||
}
|
||||
|
||||
AuxiliaryModelNodePtrArray& GetChildren()
|
||||
{
|
||||
return m_children;
|
||||
}
|
||||
AuxiliaryModelNode* GetNthChild(unsigned int n)
|
||||
{
|
||||
return m_children.Item(n);
|
||||
}
|
||||
void Insert(AuxiliaryModelNode* child, unsigned int n)
|
||||
{
|
||||
m_children.Insert(child, n);
|
||||
}
|
||||
void Append(AuxiliaryModelNode* child)
|
||||
{
|
||||
m_children.Add(child);
|
||||
}
|
||||
unsigned int GetChildCount() const
|
||||
{
|
||||
return m_children.GetCount();
|
||||
}
|
||||
|
||||
public:
|
||||
wxString name;
|
||||
wxString path;
|
||||
|
||||
private:
|
||||
AuxiliaryModelNode* m_parent;
|
||||
AuxiliaryModelNodePtrArray m_children;
|
||||
bool m_container;
|
||||
bool m_root;
|
||||
|
||||
};
|
||||
|
||||
class AuxiliaryModel : public wxDataViewModel
|
||||
{
|
||||
public:
|
||||
AuxiliaryModel();
|
||||
~AuxiliaryModel();
|
||||
|
||||
// helper methods to change the model
|
||||
wxDataViewItem CreateFolder(wxString name = wxEmptyString);
|
||||
wxDataViewItemArray ImportFile(AuxiliaryModelNode* sel, wxArrayString file_paths);
|
||||
void Delete(const wxDataViewItem& item);
|
||||
void MoveItem(const wxDataViewItem& dropped_item, const wxDataViewItem& dragged_item);
|
||||
bool IsOrphan(const wxDataViewItem& item);
|
||||
bool Rename(const wxDataViewItem& item, const wxString& name);
|
||||
AuxiliaryModelNode* GetParent(AuxiliaryModelNode* node) const;
|
||||
void Reparent(AuxiliaryModelNode* node, AuxiliaryModelNode* new_parent);
|
||||
|
||||
void Init(wxString aux_path);
|
||||
void Reload(wxString aux_path);
|
||||
|
||||
// override sorting to always sort branches ascendingly
|
||||
|
||||
int Compare(const wxDataViewItem& item1, const wxDataViewItem& item2,
|
||||
unsigned int column, bool ascending) const wxOVERRIDE;
|
||||
|
||||
// implementation of base class virtuals to define model
|
||||
|
||||
virtual unsigned int GetColumnCount() const wxOVERRIDE
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
virtual wxString GetColumnType(unsigned int col) const wxOVERRIDE
|
||||
{
|
||||
return "string";
|
||||
}
|
||||
|
||||
virtual void GetValue(wxVariant& variant,
|
||||
const wxDataViewItem& item, unsigned int col) const wxOVERRIDE;
|
||||
virtual bool SetValue(const wxVariant& variant,
|
||||
const wxDataViewItem& item, unsigned int col) wxOVERRIDE;
|
||||
|
||||
virtual bool IsEnabled(const wxDataViewItem& item,
|
||||
unsigned int col) const wxOVERRIDE;
|
||||
|
||||
virtual wxDataViewItem GetParent(const wxDataViewItem& item) const wxOVERRIDE;
|
||||
virtual bool IsContainer(const wxDataViewItem& item) const wxOVERRIDE;
|
||||
virtual unsigned int GetChildren(const wxDataViewItem& parent,
|
||||
wxDataViewItemArray& array) const wxOVERRIDE;
|
||||
|
||||
private:
|
||||
AuxiliaryModelNode* m_root;
|
||||
wxString m_root_dir;
|
||||
};
|
||||
|
||||
#endif // slic3r_GUI_AuxiliaryDataViewModel_hpp_
|
||||
|
||||
36
src/slic3r/GUI/AuxiliaryDialog.cpp
Normal file
36
src/slic3r/GUI/AuxiliaryDialog.cpp
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#include "AuxiliaryDialog.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "GUI_AuxiliaryList.hpp"
|
||||
|
||||
#include "libslic3r/Utils.hpp"
|
||||
|
||||
namespace pt = boost::property_tree;
|
||||
typedef pt::ptree JSON;
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
|
||||
AuxiliaryDialog::AuxiliaryDialog(wxWindow * parent)
|
||||
: DPIDialog(parent, wxID_ANY, _L("Auxiliaryies"), wxDefaultPosition,
|
||||
wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||
{
|
||||
m_aux_list = new AuxiliaryList(this);
|
||||
|
||||
SetSizerAndFit(m_aux_list->get_top_sizer());
|
||||
SetSize({80 * em_unit(), 50 * em_unit()});
|
||||
|
||||
Layout();
|
||||
Center();
|
||||
}
|
||||
|
||||
void AuxiliaryDialog::on_dpi_changed(const wxRect& suggested_rect)
|
||||
{
|
||||
Fit();
|
||||
SetSize({80 * em_unit(), 50 * em_unit()});
|
||||
//m_aux_list->msw_rescale();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
33
src/slic3r/GUI/AuxiliaryDialog.hpp
Normal file
33
src/slic3r/GUI/AuxiliaryDialog.hpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef slic3r_GUI_AuxiliaryDialog_hpp_
|
||||
#define slic3r_GUI_AuxiliaryDialog_hpp_
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/intl.h>
|
||||
#include <wx/collpane.h>
|
||||
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
class AuxiliaryList;
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
class AuxiliaryDialog : public DPIDialog
|
||||
{
|
||||
public:
|
||||
AuxiliaryDialog(wxWindow * parent);
|
||||
|
||||
AuxiliaryList * aux_list() { return m_aux_list; }
|
||||
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect& suggested_rect) override;
|
||||
|
||||
private:
|
||||
AuxiliaryList * m_aux_list;
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif
|
||||
247
src/slic3r/GUI/BBLStatusBar.cpp
Normal file
247
src/slic3r/GUI/BBLStatusBar.cpp
Normal file
|
|
@ -0,0 +1,247 @@
|
|||
#include "BBLStatusBar.hpp"
|
||||
|
||||
#include <wx/timer.h>
|
||||
#include <wx/gauge.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/statusbr.h>
|
||||
#include <wx/frame.h>
|
||||
|
||||
#include "GUI_App.hpp"
|
||||
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
BBLStatusBar::BBLStatusBar(wxWindow *parent, int id)
|
||||
: m_self{new wxPanel(parent, id == -1 ? wxID_ANY : id)}
|
||||
, m_prog{new wxGauge(m_self,
|
||||
wxGA_HORIZONTAL,
|
||||
100,
|
||||
wxDefaultPosition,
|
||||
wxSize(120, -1))}
|
||||
, m_cancelbutton{new wxButton(m_self,
|
||||
-1,
|
||||
_(L("Cancel")),
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize)}
|
||||
, m_sizer(new wxBoxSizer(wxHORIZONTAL))
|
||||
, m_slice_info_sizer(new wxBoxSizer(wxHORIZONTAL))
|
||||
, m_object_info_sizer(new wxBoxSizer(wxHORIZONTAL))
|
||||
{
|
||||
m_status_text = new wxStaticText(m_self, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
|
||||
m_object_info = new wxStaticText(m_self, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
|
||||
m_slice_info = new wxStaticText(m_self, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
|
||||
wxStaticLine* seperator_1 = new wxStaticLine(m_self, wxID_ANY, wxDefaultPosition, wxSize(3, -1), wxLI_VERTICAL);
|
||||
wxStaticLine* seperator_2 = new wxStaticLine(m_self, wxID_ANY, wxDefaultPosition, wxSize(3, -1), wxLI_VERTICAL);
|
||||
|
||||
m_object_info_sizer->Add(m_object_info, 1, wxEXPAND | wxALL, 0);
|
||||
m_object_info_sizer->Add(seperator_1, 0, wxEXPAND | wxLEFT | wxRIGHT, 5);
|
||||
|
||||
m_slice_info_sizer->Add(m_slice_info, 1, wxEXPAND | wxALL, 0);
|
||||
m_slice_info_sizer->Add(seperator_2, 0, wxEXPAND | wxLEFT | wxRIGHT, 5);
|
||||
|
||||
m_cancelbutton->Bind(wxEVT_BUTTON, [this](const wxCommandEvent&) {
|
||||
if (m_cancel_cb)
|
||||
m_cancel_cb();
|
||||
m_cancelbutton->Hide();
|
||||
});
|
||||
|
||||
m_sizer->Add(m_object_info_sizer, 1, wxEXPAND | wxALL | wxALIGN_LEFT, 5);
|
||||
m_sizer->Add(m_slice_info_sizer, 1, wxEXPAND | wxALL | wxALIGN_LEFT, 5);
|
||||
m_sizer->Add(m_status_text, 1, wxEXPAND | wxALL | wxALIGN_LEFT, 5);
|
||||
m_sizer->Add(m_prog, 0, wxEXPAND | wxLEFT | wxALL | wxALIGN_RIGHT, 5);
|
||||
m_sizer->Add(m_cancelbutton, 0, wxEXPAND | wxALL | wxALIGN_RIGHT, 5);
|
||||
m_sizer->SetSizeHints(m_self);
|
||||
m_self->SetSizer(m_sizer);
|
||||
|
||||
m_sizer->Hide(m_object_info_sizer);
|
||||
m_sizer->Hide(m_slice_info_sizer);
|
||||
m_sizer->Hide(m_prog);
|
||||
m_sizer->Hide(m_cancelbutton);
|
||||
m_sizer->Layout();
|
||||
}
|
||||
|
||||
int BBLStatusBar::get_progress() const
|
||||
{
|
||||
return m_prog->GetValue();
|
||||
}
|
||||
|
||||
void BBLStatusBar::set_progress(int val)
|
||||
{
|
||||
if(val < 0)
|
||||
return;
|
||||
|
||||
bool need_layout = false;
|
||||
//add the logic for arrange/orient jobs, which don't call stop_busy
|
||||
if(val == m_prog->GetRange()) {
|
||||
m_prog->SetValue(0);
|
||||
m_sizer->Hide(m_prog);
|
||||
need_layout = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_sizer->IsShown(m_object_info_sizer)) {
|
||||
m_sizer->Hide(m_object_info_sizer);
|
||||
need_layout = true;
|
||||
}
|
||||
|
||||
if (m_sizer->IsShown(m_slice_info_sizer)) {
|
||||
m_sizer->Hide(m_slice_info_sizer);
|
||||
need_layout = true;
|
||||
}
|
||||
|
||||
if (!m_sizer->IsShown(m_prog)) {
|
||||
m_sizer->Show(m_prog);
|
||||
m_sizer->Show(m_cancelbutton);
|
||||
need_layout = true;
|
||||
}
|
||||
m_prog->SetValue(val);
|
||||
}
|
||||
|
||||
if (need_layout) {
|
||||
m_sizer->Layout();
|
||||
}
|
||||
}
|
||||
|
||||
int BBLStatusBar::get_range() const
|
||||
{
|
||||
return m_prog->GetRange();
|
||||
}
|
||||
|
||||
void BBLStatusBar::set_range(int val)
|
||||
{
|
||||
if(val != m_prog->GetRange()) {
|
||||
m_prog->SetRange(val);
|
||||
}
|
||||
}
|
||||
|
||||
void BBLStatusBar::show_progress(bool show)
|
||||
{
|
||||
if (show) {
|
||||
m_sizer->Hide(m_object_info);
|
||||
m_sizer->Hide(m_slice_info);
|
||||
|
||||
m_sizer->Show(m_prog);
|
||||
m_sizer->Layout();
|
||||
}
|
||||
else {
|
||||
m_sizer->Hide(m_prog);
|
||||
m_sizer->Layout();
|
||||
}
|
||||
}
|
||||
|
||||
void BBLStatusBar::start_busy(int rate)
|
||||
{
|
||||
m_busy = true;
|
||||
show_progress(true);
|
||||
show_cancel_button();
|
||||
}
|
||||
|
||||
void BBLStatusBar::stop_busy()
|
||||
{
|
||||
show_progress(false);
|
||||
hide_cancel_button();
|
||||
m_prog->SetValue(0);
|
||||
m_sizer->Show(m_slice_info_sizer);
|
||||
m_sizer->Layout();
|
||||
m_busy = false;
|
||||
}
|
||||
|
||||
void BBLStatusBar::set_cancel_callback(BBLStatusBar::CancelFn ccb) {
|
||||
m_cancel_cb = ccb;
|
||||
if (ccb) {
|
||||
m_sizer->Show(m_cancelbutton);
|
||||
}
|
||||
else {
|
||||
m_sizer->Hide(m_cancelbutton);
|
||||
}
|
||||
}
|
||||
|
||||
wxPanel* BBLStatusBar::get_panel()
|
||||
{
|
||||
return m_self;
|
||||
}
|
||||
|
||||
void BBLStatusBar::set_status_text(const wxString& txt)
|
||||
{
|
||||
m_status_text->SetLabelText(txt);
|
||||
}
|
||||
|
||||
void BBLStatusBar::set_status_text(const std::string& txt)
|
||||
{
|
||||
this->set_status_text(txt.c_str());
|
||||
}
|
||||
|
||||
void BBLStatusBar::set_status_text(const char *txt)
|
||||
{
|
||||
this->set_status_text(wxString::FromUTF8(txt));
|
||||
}
|
||||
|
||||
wxString BBLStatusBar::get_status_text() const
|
||||
{
|
||||
return m_status_text->GetLabelText();
|
||||
}
|
||||
|
||||
void BBLStatusBar::set_object_info(const wxString& txt)
|
||||
{
|
||||
if (txt == "") {
|
||||
m_object_info->SetLabelText("");
|
||||
m_sizer->Hide(m_object_info_sizer);
|
||||
}
|
||||
else {
|
||||
if (!m_sizer->IsShown(m_object_info_sizer)) {
|
||||
m_sizer->Show(m_object_info_sizer);
|
||||
}
|
||||
m_object_info->SetLabelText(txt);
|
||||
}
|
||||
m_sizer->Layout();
|
||||
}
|
||||
|
||||
void BBLStatusBar::set_slice_info(const wxString& txt)
|
||||
{
|
||||
if (!txt.empty()) {
|
||||
if (!m_sizer->IsShown(m_slice_info_sizer)) {
|
||||
m_sizer->Show(m_slice_info_sizer);
|
||||
}
|
||||
m_slice_info->SetLabelText(txt);
|
||||
m_sizer->Layout();
|
||||
}
|
||||
}
|
||||
|
||||
void BBLStatusBar::show_slice_info(bool show)
|
||||
{
|
||||
if (show) {
|
||||
m_sizer->Show(m_slice_info_sizer);
|
||||
m_sizer->Layout();
|
||||
}
|
||||
else {
|
||||
m_sizer->Hide(m_slice_info_sizer);
|
||||
m_sizer->Layout();
|
||||
}
|
||||
}
|
||||
|
||||
bool BBLStatusBar::is_slice_info_shown()
|
||||
{
|
||||
return m_sizer->IsShown(m_slice_info_sizer);
|
||||
}
|
||||
|
||||
void BBLStatusBar::set_font(const wxFont &font)
|
||||
{
|
||||
m_self->SetFont(font);
|
||||
}
|
||||
|
||||
void BBLStatusBar::show_cancel_button()
|
||||
{
|
||||
m_sizer->Show(m_cancelbutton);
|
||||
m_sizer->Layout();
|
||||
}
|
||||
|
||||
void BBLStatusBar::hide_cancel_button()
|
||||
{
|
||||
m_sizer->Hide(m_cancelbutton);
|
||||
m_sizer->Layout();
|
||||
}
|
||||
|
||||
}
|
||||
79
src/slic3r/GUI/BBLStatusBar.hpp
Normal file
79
src/slic3r/GUI/BBLStatusBar.hpp
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#ifndef BBLSTATUSBAR_HPP
|
||||
#define BBLSTATUSBAR_HPP
|
||||
|
||||
#include <wx/panel.h>
|
||||
#include <wx/stattext.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include "Jobs/ProgressIndicator.hpp"
|
||||
|
||||
class wxTimer;
|
||||
class wxGauge;
|
||||
class wxButton;
|
||||
class wxTimerEvent;
|
||||
class wxStatusBar;
|
||||
class wxWindow;
|
||||
class wxFrame;
|
||||
class wxString;
|
||||
class wxFont;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class BBLStatusBar : public ProgressIndicator
|
||||
{
|
||||
wxPanel* m_self; // we cheat! It should be the base class but: perl!
|
||||
wxGauge *m_prog;
|
||||
wxButton *m_cancelbutton;
|
||||
wxStaticText *m_status_text;
|
||||
wxStaticText* m_object_info;
|
||||
wxStaticText* m_slice_info;
|
||||
wxBoxSizer *m_slice_info_sizer;
|
||||
wxBoxSizer *m_object_info_sizer;
|
||||
wxBoxSizer *m_sizer;
|
||||
public:
|
||||
BBLStatusBar(wxWindow *parent = nullptr, int id = -1);
|
||||
~BBLStatusBar() = default;
|
||||
|
||||
int get_progress() const;
|
||||
// if the argument is less than 0 it shows the last state or
|
||||
// pulses if no state was set before.
|
||||
void set_progress(int) override;
|
||||
int get_range() const override;
|
||||
void set_range(int = 100) override;
|
||||
void show_progress(bool);
|
||||
void start_busy(int = 100);
|
||||
void stop_busy();
|
||||
inline bool is_busy() const { return m_busy; }
|
||||
void set_cancel_callback(CancelFn = CancelFn()) override;
|
||||
inline void reset_cancel_callback() { set_cancel_callback(); }
|
||||
wxPanel* get_panel();
|
||||
void set_status_text(const wxString& txt);
|
||||
void set_status_text(const std::string& txt);
|
||||
void set_status_text(const char *txt) override;
|
||||
wxString get_status_text() const;
|
||||
void set_font(const wxFont &font);
|
||||
void set_object_info(const wxString& txt);
|
||||
void set_slice_info(const wxString& txt);
|
||||
void show_slice_info(bool show);
|
||||
bool is_slice_info_shown();
|
||||
|
||||
// Temporary methods to satisfy Perl side
|
||||
void show_cancel_button();
|
||||
void hide_cancel_button();
|
||||
|
||||
private:
|
||||
bool m_busy = false;
|
||||
CancelFn m_cancel_cb;
|
||||
};
|
||||
|
||||
namespace GUI {
|
||||
using Slic3r::BBLStatusBar;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // BBLSTATUSBAR_HPP
|
||||
241
src/slic3r/GUI/BBLStatusBarBind.cpp
Normal file
241
src/slic3r/GUI/BBLStatusBarBind.cpp
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
#include "BBLStatusBarBind.hpp"
|
||||
|
||||
#include <wx/timer.h>
|
||||
#include <wx/gauge.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/statusbr.h>
|
||||
#include <wx/frame.h>
|
||||
#include "wx/evtloop.h"
|
||||
#include <wx/gdicmn.h>
|
||||
#include "GUI_App.hpp"
|
||||
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
BBLStatusBarBind::BBLStatusBarBind(wxWindow *parent, int id)
|
||||
: m_self{new wxPanel(parent, id == -1 ? wxID_ANY : id)}
|
||||
, m_sizer(new wxBoxSizer(wxHORIZONTAL))
|
||||
{
|
||||
m_self->SetBackgroundColour(wxColour(255,255,255));
|
||||
m_self->SetMinSize(wxSize(m_self->FromDIP(450), m_self->FromDIP(30)));
|
||||
|
||||
|
||||
//wxBoxSizer *m_sizer_bottom = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
/* m_status_text = new wxStaticText(m_self, wxID_ANY, L(""), wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_status_text->SetForegroundColour(wxColour(107, 107, 107));
|
||||
m_status_text->SetFont(::Label::Body_13);
|
||||
m_status_text->Wrap(-1);
|
||||
m_sizer_body->Add(m_status_text, 0, 0, 0);*/
|
||||
|
||||
m_prog = new wxGauge(m_self, wxID_ANY, 100, wxDefaultPosition, wxSize(m_self->FromDIP(400), m_self->FromDIP(6)), wxGA_HORIZONTAL);
|
||||
m_prog->SetValue(0);
|
||||
|
||||
block_left = new wxWindow(m_prog, wxID_ANY, wxPoint(0, 0), wxSize(2, m_prog->GetSize().GetHeight() * 2));
|
||||
block_left->SetBackgroundColour(wxColour(255, 255, 255));
|
||||
block_right = new wxWindow(m_prog, wxID_ANY, wxPoint(m_prog->GetSize().GetWidth() - 2, 0), wxSize(2, m_prog->GetSize().GetHeight() * 2));
|
||||
block_right->SetBackgroundColour(wxColour(255, 255, 255));
|
||||
|
||||
m_stext_percent = new wxStaticText(m_self, wxID_ANY, _L(""), wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_stext_percent->SetForegroundColour(wxColour(107, 107, 107));
|
||||
m_stext_percent->SetFont(::Label::Body_13);
|
||||
m_stext_percent->Wrap(-1);
|
||||
|
||||
m_sizer->Add(m_prog, 1, wxALIGN_CENTER, 0);
|
||||
m_sizer->Add(0, 0, 1, wxEXPAND, 0);
|
||||
m_sizer->Add(m_stext_percent, 1, wxALIGN_CENTER, 0);
|
||||
|
||||
|
||||
//m_sizer->Add(m_sizer_bottom, 1, wxALIGN_CENTER, 0);
|
||||
|
||||
m_self->SetSizer(m_sizer);
|
||||
m_self->Layout();
|
||||
m_sizer->Fit(m_self);
|
||||
//set_prog_block();
|
||||
}
|
||||
|
||||
void BBLStatusBarBind::set_prog_block()
|
||||
{
|
||||
block_left->SetPosition(wxPoint(0, 0));
|
||||
block_right->SetPosition(wxPoint(m_prog->GetSize().GetWidth() - 2, 0));
|
||||
}
|
||||
|
||||
int BBLStatusBarBind::get_progress() const
|
||||
{
|
||||
return m_prog->GetValue();
|
||||
}
|
||||
|
||||
void BBLStatusBarBind::set_progress(int val)
|
||||
{
|
||||
set_prog_block();
|
||||
|
||||
if(val < 0)
|
||||
return;
|
||||
|
||||
bool need_layout = false;
|
||||
//add the logic for arrange/orient jobs, which don't call stop_busy
|
||||
if(val == m_prog->GetRange()) {
|
||||
m_prog->SetValue(0);
|
||||
set_percent_text("0%");
|
||||
//m_sizer->Hide(m_prog);
|
||||
need_layout = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!m_sizer->IsShown(m_prog)) {
|
||||
m_sizer->Show(m_prog);
|
||||
m_sizer->Show(m_cancelbutton);
|
||||
need_layout = true;
|
||||
}
|
||||
m_prog->SetValue(val);
|
||||
set_percent_text(wxString::Format("%d%%", val));
|
||||
}
|
||||
|
||||
if (need_layout) {
|
||||
m_sizer->Layout();
|
||||
}
|
||||
}
|
||||
|
||||
int BBLStatusBarBind::get_range() const
|
||||
{
|
||||
return m_prog->GetRange();
|
||||
}
|
||||
|
||||
void BBLStatusBarBind::set_range(int val)
|
||||
{
|
||||
if(val != m_prog->GetRange()) {
|
||||
m_prog->SetRange(val);
|
||||
}
|
||||
}
|
||||
|
||||
void BBLStatusBarBind::show_progress(bool show)
|
||||
{
|
||||
if (show) {
|
||||
m_sizer->Show(m_prog);
|
||||
m_sizer->Layout();
|
||||
}
|
||||
else {
|
||||
//m_sizer->Hide(m_prog);
|
||||
m_sizer->Layout();
|
||||
}
|
||||
}
|
||||
|
||||
void BBLStatusBarBind::start_busy(int rate)
|
||||
{
|
||||
m_busy = true;
|
||||
show_progress(true);
|
||||
show_cancel_button();
|
||||
}
|
||||
|
||||
void BBLStatusBarBind::stop_busy()
|
||||
{
|
||||
show_progress(false);
|
||||
hide_cancel_button();
|
||||
m_prog->SetValue(0);
|
||||
m_sizer->Layout();
|
||||
m_busy = false;
|
||||
}
|
||||
|
||||
void BBLStatusBarBind::set_cancel_callback_fina(BBLStatusBarBind::CancelFn ccb)
|
||||
{
|
||||
m_cancel_cb_fina = ccb;
|
||||
if (ccb) {
|
||||
m_sizer->Show(m_cancelbutton);
|
||||
} else {
|
||||
m_sizer->Hide(m_cancelbutton);
|
||||
}
|
||||
}
|
||||
|
||||
void BBLStatusBarBind::set_cancel_callback(BBLStatusBarBind::CancelFn ccb) {
|
||||
/* m_cancel_cb = ccb;
|
||||
if (ccb) {
|
||||
m_sizer->Show(m_cancelbutton);
|
||||
}
|
||||
else {
|
||||
m_sizer->Hide(m_cancelbutton);
|
||||
}
|
||||
m_sizer->Layout();*/
|
||||
}
|
||||
|
||||
wxPanel* BBLStatusBarBind::get_panel()
|
||||
{
|
||||
return m_self;
|
||||
}
|
||||
|
||||
void BBLStatusBarBind::set_status_text(const wxString& txt)
|
||||
{
|
||||
//auto txtss = "Sending the printing task has timed out.\nPlease try again!";
|
||||
//auto txtss = "The printing project is being uploaded... 25%%";
|
||||
//m_status_text->SetLabelText(txtss);
|
||||
//m_status_text->SetLabelText(txt);
|
||||
}
|
||||
|
||||
void BBLStatusBarBind::set_percent_text(const wxString &txt)
|
||||
{
|
||||
m_stext_percent->SetLabelText(txt);
|
||||
}
|
||||
|
||||
void BBLStatusBarBind::set_status_text(const std::string& txt)
|
||||
{
|
||||
this->set_status_text(txt.c_str());
|
||||
}
|
||||
|
||||
void BBLStatusBarBind::set_status_text(const char *txt)
|
||||
{
|
||||
this->set_status_text(wxString::FromUTF8(txt));
|
||||
}
|
||||
|
||||
void BBLStatusBarBind::msw_rescale() {
|
||||
set_prog_block();
|
||||
m_cancelbutton->SetMinSize(wxSize(m_self->FromDIP(56), m_self->FromDIP(24)));
|
||||
}
|
||||
|
||||
wxString BBLStatusBarBind::get_status_text() const
|
||||
{
|
||||
return m_status_text->GetLabelText();
|
||||
}
|
||||
|
||||
bool BBLStatusBarBind::update_status(wxString &msg, bool &was_cancel, int percent, bool yield)
|
||||
{
|
||||
set_status_text(msg);
|
||||
|
||||
if (percent >= 0)
|
||||
this->set_progress(percent);
|
||||
|
||||
if (yield)
|
||||
wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI | wxEVT_CATEGORY_USER_INPUT);
|
||||
was_cancel = m_was_cancelled;
|
||||
return true;
|
||||
}
|
||||
|
||||
void BBLStatusBarBind::reset()
|
||||
{
|
||||
set_status_text("");
|
||||
m_was_cancelled = false;
|
||||
set_progress(0);
|
||||
}
|
||||
|
||||
|
||||
void BBLStatusBarBind::set_font(const wxFont &font)
|
||||
{
|
||||
m_self->SetFont(font);
|
||||
}
|
||||
|
||||
void BBLStatusBarBind::show_cancel_button()
|
||||
{
|
||||
m_sizer->Show(m_cancelbutton);
|
||||
m_sizer->Layout();
|
||||
}
|
||||
|
||||
void BBLStatusBarBind::hide_cancel_button()
|
||||
{
|
||||
m_sizer->Hide(m_cancelbutton);
|
||||
m_sizer->Layout();
|
||||
}
|
||||
|
||||
}
|
||||
91
src/slic3r/GUI/BBLStatusBarBind.hpp
Normal file
91
src/slic3r/GUI/BBLStatusBarBind.hpp
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
#ifndef BBLStatusBarBind_HPP
|
||||
#define BBLStatusBarBind_HPP
|
||||
|
||||
#include <wx/panel.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/simplebook.h>
|
||||
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include "Jobs/ProgressIndicator.hpp"
|
||||
#include "Widgets/Label.hpp"
|
||||
#include "Widgets/Button.hpp"
|
||||
|
||||
class wxTimer;
|
||||
class wxGauge;
|
||||
class wxButton;
|
||||
class wxTimerEvent;
|
||||
class wxStatusBar;
|
||||
class wxWindow;
|
||||
class wxFrame;
|
||||
class wxString;
|
||||
class wxFont;
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
class BBLStatusBarBind : public ProgressIndicator
|
||||
{
|
||||
wxPanel * m_self; // we cheat! It should be the base class but: perl!
|
||||
wxGauge * m_prog;
|
||||
Button * m_cancelbutton;
|
||||
wxStaticText *m_status_text;
|
||||
wxStaticText *m_stext_percent;
|
||||
wxBoxSizer * m_sizer;
|
||||
wxBoxSizer * m_sizer_eline;
|
||||
wxWindow * block_left;
|
||||
wxWindow * block_right;
|
||||
|
||||
public:
|
||||
BBLStatusBarBind(wxWindow *parent = nullptr, int id = -1);
|
||||
~BBLStatusBarBind() = default;
|
||||
|
||||
int get_progress() const;
|
||||
// if the argument is less than 0 it shows the last state or
|
||||
// pulses if no state was set before.
|
||||
void set_prog_block();
|
||||
void set_progress(int) override;
|
||||
int get_range() const override;
|
||||
void set_range(int = 100) override;
|
||||
void show_progress(bool);
|
||||
void start_busy(int = 100);
|
||||
void stop_busy();
|
||||
void set_cancel_callback_fina(BBLStatusBarBind::CancelFn ccb);
|
||||
inline bool is_busy() const { return m_busy; }
|
||||
void set_cancel_callback(CancelFn = CancelFn()) override;
|
||||
inline void reset_cancel_callback() { set_cancel_callback(); }
|
||||
wxPanel * get_panel();
|
||||
void set_status_text(const wxString &txt);
|
||||
void set_percent_text(const wxString &txt);
|
||||
void msw_rescale();
|
||||
void set_status_text(const std::string &txt);
|
||||
void set_status_text(const char *txt) override;
|
||||
wxString get_status_text() const;
|
||||
void set_font(const wxFont &font);
|
||||
void set_object_info(const wxString &txt);
|
||||
void set_slice_info(const wxString &txt);
|
||||
void show_slice_info(bool show);
|
||||
bool is_slice_info_shown();
|
||||
bool update_status(wxString &msg, bool &was_cancel, int percent = -1, bool yield = true);
|
||||
void reset();
|
||||
|
||||
// Temporary methods to satisfy Perl side
|
||||
void show_cancel_button();
|
||||
void hide_cancel_button();
|
||||
|
||||
private:
|
||||
bool m_busy = false;
|
||||
bool m_was_cancelled = false;
|
||||
CancelFn m_cancel_cb;
|
||||
CancelFn m_cancel_cb_fina;
|
||||
};
|
||||
|
||||
namespace GUI {
|
||||
using Slic3r::BBLStatusBarBind;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // BBLSTATUSBAR_HPP
|
||||
253
src/slic3r/GUI/BBLStatusBarSend.cpp
Normal file
253
src/slic3r/GUI/BBLStatusBarSend.cpp
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
#include "BBLStatusBarSend.hpp"
|
||||
|
||||
#include <wx/timer.h>
|
||||
#include <wx/gauge.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/statusbr.h>
|
||||
#include <wx/frame.h>
|
||||
#include "wx/evtloop.h"
|
||||
#include <wx/gdicmn.h>
|
||||
#include "GUI_App.hpp"
|
||||
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
BBLStatusBarSend::BBLStatusBarSend(wxWindow *parent, int id)
|
||||
: m_self{new wxPanel(parent, id == -1 ? wxID_ANY : id)}
|
||||
, m_sizer(new wxBoxSizer(wxHORIZONTAL))
|
||||
{
|
||||
m_self->SetBackgroundColour(wxColour(255,255,255));
|
||||
|
||||
wxBoxSizer *m_sizer_body = new wxBoxSizer(wxVERTICAL);
|
||||
wxBoxSizer *m_sizer_bottom = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
m_status_text = new wxStaticText(m_self, wxID_ANY, L(""), wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_status_text->SetForegroundColour(wxColour(107, 107, 107));
|
||||
m_status_text->SetFont(::Label::Body_13);
|
||||
m_status_text->Wrap(-1);
|
||||
m_sizer_body->Add(m_status_text, 0, 0, 0);
|
||||
|
||||
m_prog = new wxGauge(m_self, wxID_ANY, 100, wxDefaultPosition, wxSize(-1, m_self->FromDIP(6)), wxGA_HORIZONTAL);
|
||||
m_prog->SetValue(0);
|
||||
|
||||
block_left = new wxWindow(m_prog, wxID_ANY, wxPoint(0, 0), wxSize(2, m_prog->GetSize().GetHeight() * 2));
|
||||
block_left->SetBackgroundColour(wxColour(255, 255, 255));
|
||||
block_right = new wxWindow(m_prog, wxID_ANY, wxPoint(m_prog->GetSize().GetWidth() - 2, 0), wxSize(2, m_prog->GetSize().GetHeight() * 2));
|
||||
block_right->SetBackgroundColour(wxColour(255, 255, 255));
|
||||
|
||||
m_sizer_bottom->Add(m_prog, 1, wxALIGN_CENTER, 0);
|
||||
|
||||
m_cancelbutton = new Button(m_self, _L("Cancel"));
|
||||
m_cancelbutton->SetMinSize(wxSize(m_self->FromDIP(64), m_self->FromDIP(24)));
|
||||
m_cancelbutton->SetTextColor(wxColour(107, 107, 107));
|
||||
m_cancelbutton->SetBackgroundColor(wxColour(255, 255, 255));
|
||||
m_cancelbutton->SetCornerRadius(12);
|
||||
m_cancelbutton->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) {
|
||||
m_was_cancelled = true;
|
||||
if (m_cancel_cb_fina)
|
||||
m_cancel_cb_fina();
|
||||
});
|
||||
|
||||
m_stext_percent = new wxStaticText(m_self, wxID_ANY, L(""), wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_stext_percent->SetForegroundColour(wxColour(107, 107, 107));
|
||||
m_stext_percent->SetFont(::Label::Body_13);
|
||||
m_stext_percent->Wrap(-1);
|
||||
m_sizer_bottom->Add(m_stext_percent, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, 10);
|
||||
|
||||
m_sizer_bottom->Add(m_cancelbutton, 0, wxALIGN_CENTER, 0);
|
||||
m_sizer_body->Add(0, 0, 0, wxTOP, 5);
|
||||
m_sizer_body->Add(m_sizer_bottom, 1, wxEXPAND, 0);
|
||||
|
||||
m_sizer->Add(m_sizer_body, 1, wxALIGN_CENTER, 0);
|
||||
|
||||
m_self->SetSizer(m_sizer);
|
||||
m_self->Layout();
|
||||
m_sizer->Fit(m_self);
|
||||
//set_prog_block();
|
||||
}
|
||||
|
||||
void BBLStatusBarSend::set_prog_block()
|
||||
{
|
||||
block_left->SetPosition(wxPoint(0, 0));
|
||||
block_right->SetPosition(wxPoint(m_prog->GetSize().GetWidth() - 2, 0));
|
||||
}
|
||||
|
||||
int BBLStatusBarSend::get_progress() const
|
||||
{
|
||||
return m_prog->GetValue();
|
||||
}
|
||||
|
||||
void BBLStatusBarSend::set_progress(int val)
|
||||
{
|
||||
set_prog_block();
|
||||
|
||||
if(val < 0)
|
||||
return;
|
||||
|
||||
bool need_layout = false;
|
||||
//add the logic for arrange/orient jobs, which don't call stop_busy
|
||||
if(val == m_prog->GetRange()) {
|
||||
m_prog->SetValue(0);
|
||||
set_percent_text("0%");
|
||||
m_sizer->Hide(m_prog);
|
||||
need_layout = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!m_sizer->IsShown(m_prog)) {
|
||||
m_sizer->Show(m_prog);
|
||||
m_sizer->Show(m_cancelbutton);
|
||||
need_layout = true;
|
||||
}
|
||||
m_prog->SetValue(val);
|
||||
set_percent_text(wxString::Format("%d%%", val));
|
||||
}
|
||||
|
||||
if (need_layout) {
|
||||
m_sizer->Layout();
|
||||
}
|
||||
}
|
||||
|
||||
int BBLStatusBarSend::get_range() const
|
||||
{
|
||||
return m_prog->GetRange();
|
||||
}
|
||||
|
||||
void BBLStatusBarSend::set_range(int val)
|
||||
{
|
||||
if(val != m_prog->GetRange()) {
|
||||
m_prog->SetRange(val);
|
||||
}
|
||||
}
|
||||
|
||||
void BBLStatusBarSend::show_progress(bool show)
|
||||
{
|
||||
if (show) {
|
||||
m_sizer->Show(m_prog);
|
||||
m_sizer->Layout();
|
||||
}
|
||||
else {
|
||||
m_sizer->Hide(m_prog);
|
||||
m_sizer->Layout();
|
||||
}
|
||||
}
|
||||
|
||||
void BBLStatusBarSend::start_busy(int rate)
|
||||
{
|
||||
m_busy = true;
|
||||
show_progress(true);
|
||||
show_cancel_button();
|
||||
}
|
||||
|
||||
void BBLStatusBarSend::stop_busy()
|
||||
{
|
||||
show_progress(false);
|
||||
hide_cancel_button();
|
||||
m_prog->SetValue(0);
|
||||
m_sizer->Layout();
|
||||
m_busy = false;
|
||||
}
|
||||
|
||||
void BBLStatusBarSend::set_cancel_callback_fina(BBLStatusBarSend::CancelFn ccb)
|
||||
{
|
||||
m_cancel_cb_fina = ccb;
|
||||
if (ccb) {
|
||||
m_sizer->Show(m_cancelbutton);
|
||||
} else {
|
||||
m_sizer->Hide(m_cancelbutton);
|
||||
}
|
||||
}
|
||||
|
||||
void BBLStatusBarSend::set_cancel_callback(BBLStatusBarSend::CancelFn ccb) {
|
||||
/* m_cancel_cb = ccb;
|
||||
if (ccb) {
|
||||
m_sizer->Show(m_cancelbutton);
|
||||
}
|
||||
else {
|
||||
m_sizer->Hide(m_cancelbutton);
|
||||
}
|
||||
m_sizer->Layout();*/
|
||||
}
|
||||
|
||||
wxPanel* BBLStatusBarSend::get_panel()
|
||||
{
|
||||
return m_self;
|
||||
}
|
||||
|
||||
void BBLStatusBarSend::set_status_text(const wxString& txt)
|
||||
{
|
||||
//auto txtss = "Sending the printing task has timed out.\nPlease try again!";
|
||||
//auto txtss = "The printing project is being uploaded... 25%%";
|
||||
//m_status_text->SetLabelText(txtss);
|
||||
m_status_text->SetLabelText(txt);
|
||||
}
|
||||
|
||||
void BBLStatusBarSend::set_percent_text(const wxString &txt)
|
||||
{
|
||||
m_stext_percent->SetLabelText(txt);
|
||||
}
|
||||
|
||||
void BBLStatusBarSend::set_status_text(const std::string& txt)
|
||||
{
|
||||
this->set_status_text(txt.c_str());
|
||||
}
|
||||
|
||||
void BBLStatusBarSend::set_status_text(const char *txt)
|
||||
{
|
||||
this->set_status_text(wxString::FromUTF8(txt));
|
||||
}
|
||||
|
||||
void BBLStatusBarSend::msw_rescale() {
|
||||
set_prog_block();
|
||||
m_cancelbutton->SetMinSize(wxSize(m_self->FromDIP(56), m_self->FromDIP(24)));
|
||||
}
|
||||
|
||||
wxString BBLStatusBarSend::get_status_text() const
|
||||
{
|
||||
return m_status_text->GetLabelText();
|
||||
}
|
||||
|
||||
bool BBLStatusBarSend::update_status(wxString &msg, bool &was_cancel, int percent, bool yield)
|
||||
{
|
||||
set_status_text(msg);
|
||||
|
||||
if (percent >= 0)
|
||||
this->set_progress(percent);
|
||||
|
||||
if (yield)
|
||||
wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI | wxEVT_CATEGORY_USER_INPUT);
|
||||
was_cancel = m_was_cancelled;
|
||||
return true;
|
||||
}
|
||||
|
||||
void BBLStatusBarSend::reset()
|
||||
{
|
||||
set_status_text("");
|
||||
m_was_cancelled = false;
|
||||
set_progress(0);
|
||||
}
|
||||
|
||||
|
||||
void BBLStatusBarSend::set_font(const wxFont &font)
|
||||
{
|
||||
m_self->SetFont(font);
|
||||
}
|
||||
|
||||
void BBLStatusBarSend::show_cancel_button()
|
||||
{
|
||||
m_sizer->Show(m_cancelbutton);
|
||||
m_sizer->Layout();
|
||||
}
|
||||
|
||||
void BBLStatusBarSend::hide_cancel_button()
|
||||
{
|
||||
m_sizer->Hide(m_cancelbutton);
|
||||
m_sizer->Layout();
|
||||
}
|
||||
|
||||
}
|
||||
90
src/slic3r/GUI/BBLStatusBarSend.hpp
Normal file
90
src/slic3r/GUI/BBLStatusBarSend.hpp
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
#ifndef BBLSTATUSBARSEND_HPP
|
||||
#define BBLSTATUSBARSEND_HPP
|
||||
|
||||
#include <wx/panel.h>
|
||||
#include <wx/stattext.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include "Jobs/ProgressIndicator.hpp"
|
||||
#include "Widgets/Label.hpp"
|
||||
#include "Widgets/Button.hpp"
|
||||
|
||||
class wxTimer;
|
||||
class wxGauge;
|
||||
class wxButton;
|
||||
class wxTimerEvent;
|
||||
class wxStatusBar;
|
||||
class wxWindow;
|
||||
class wxFrame;
|
||||
class wxString;
|
||||
class wxFont;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class BBLStatusBarSend : public ProgressIndicator
|
||||
{
|
||||
wxPanel * m_self; // we cheat! It should be the base class but: perl!
|
||||
wxGauge * m_prog;
|
||||
Button * m_cancelbutton;
|
||||
wxStaticText *m_status_text;
|
||||
wxStaticText *m_stext_percent;
|
||||
wxBoxSizer * m_sizer;
|
||||
wxBoxSizer * m_sizer_eline;
|
||||
wxWindow * block_left;
|
||||
wxWindow * block_right;
|
||||
|
||||
public:
|
||||
BBLStatusBarSend(wxWindow *parent = nullptr, int id = -1);
|
||||
~BBLStatusBarSend() = default;
|
||||
|
||||
int get_progress() const;
|
||||
// if the argument is less than 0 it shows the last state or
|
||||
// pulses if no state was set before.
|
||||
void set_prog_block();
|
||||
void set_progress(int) override;
|
||||
int get_range() const override;
|
||||
void set_range(int = 100) override;
|
||||
void show_progress(bool);
|
||||
void start_busy(int = 100);
|
||||
void stop_busy();
|
||||
void set_cancel_callback_fina(BBLStatusBarSend::CancelFn ccb);
|
||||
inline bool is_busy() const { return m_busy; }
|
||||
void set_cancel_callback(CancelFn = CancelFn()) override;
|
||||
inline void reset_cancel_callback() { set_cancel_callback(); }
|
||||
wxPanel * get_panel();
|
||||
void set_status_text(const wxString &txt);
|
||||
void set_percent_text(const wxString &txt);
|
||||
void msw_rescale();
|
||||
void set_status_text(const std::string &txt);
|
||||
void set_status_text(const char *txt) override;
|
||||
wxString get_status_text() const;
|
||||
void set_font(const wxFont &font);
|
||||
void set_object_info(const wxString &txt);
|
||||
void set_slice_info(const wxString &txt);
|
||||
void show_slice_info(bool show);
|
||||
bool is_slice_info_shown();
|
||||
bool update_status(wxString &msg, bool &was_cancel, int percent = -1, bool yield = true);
|
||||
void reset();
|
||||
|
||||
// Temporary methods to satisfy Perl side
|
||||
void show_cancel_button();
|
||||
void hide_cancel_button();
|
||||
|
||||
private:
|
||||
bool m_busy = false;
|
||||
bool m_was_cancelled = false;
|
||||
CancelFn m_cancel_cb;
|
||||
CancelFn m_cancel_cb_fina;
|
||||
};
|
||||
|
||||
namespace GUI {
|
||||
using Slic3r::BBLStatusBarSend;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // BBLSTATUSBAR_HPP
|
||||
627
src/slic3r/GUI/BBLTopbar.cpp
Normal file
627
src/slic3r/GUI/BBLTopbar.cpp
Normal file
|
|
@ -0,0 +1,627 @@
|
|||
#include "BBLTopbar.hpp"
|
||||
#include "wx/artprov.h"
|
||||
#include "wx/aui/framemanager.h"
|
||||
#include "I18N.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "WebViewDialog.hpp"
|
||||
#include "PartPlate.hpp"
|
||||
|
||||
#define TOPBAR_ICON_SIZE 18
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
enum CUSTOM_ID
|
||||
{
|
||||
ID_TOP_MENU_TOOL = 3100,
|
||||
ID_LOGO,
|
||||
ID_TOP_FILE_MENU,
|
||||
ID_TOP_DROPDOWN_MENU,
|
||||
ID_TITLE,
|
||||
ID_MODEL_STORE,
|
||||
ID_PUBLISH,
|
||||
ID_TOOL_BAR = 3200,
|
||||
ID_AMS_NOTEBOOK,
|
||||
};
|
||||
|
||||
class BBLTopbarArt : public wxAuiDefaultToolBarArt
|
||||
{
|
||||
public:
|
||||
virtual void DrawLabel(wxDC& dc, wxWindow* wnd, const wxAuiToolBarItem& item, const wxRect& rect) wxOVERRIDE;
|
||||
virtual void DrawBackground(wxDC& dc, wxWindow* wnd, const wxRect& rect) wxOVERRIDE;
|
||||
virtual void DrawButton(wxDC& dc, wxWindow* wnd, const wxAuiToolBarItem& item, const wxRect& rect) wxOVERRIDE;
|
||||
};
|
||||
|
||||
void BBLTopbarArt::DrawLabel(wxDC& dc, wxWindow* wnd, const wxAuiToolBarItem& item, const wxRect& rect)
|
||||
{
|
||||
dc.SetFont(m_font);
|
||||
#ifdef __WINDOWS__
|
||||
dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
|
||||
#else
|
||||
dc.SetTextForeground(*wxWHITE);
|
||||
#endif
|
||||
|
||||
int textWidth = 0, textHeight = 0;
|
||||
dc.GetTextExtent(item.GetLabel(), &textWidth, &textHeight);
|
||||
|
||||
wxRect clipRect = rect;
|
||||
clipRect.width -= 1;
|
||||
dc.SetClippingRegion(clipRect);
|
||||
|
||||
int textX, textY;
|
||||
if (textWidth < rect.GetWidth()) {
|
||||
textX = rect.x + 1 + (rect.width - textWidth) / 2;
|
||||
}
|
||||
else {
|
||||
textX = rect.x + 1;
|
||||
}
|
||||
textY = rect.y + (rect.height - textHeight) / 2;
|
||||
dc.DrawText(item.GetLabel(), textX, textY);
|
||||
dc.DestroyClippingRegion();
|
||||
}
|
||||
|
||||
void BBLTopbarArt::DrawBackground(wxDC& dc, wxWindow* wnd, const wxRect& rect)
|
||||
{
|
||||
dc.SetBrush(wxBrush(wxColour(38, 46, 48)));
|
||||
wxRect clipRect = rect;
|
||||
clipRect.y -= 8;
|
||||
clipRect.height += 8;
|
||||
dc.SetClippingRegion(clipRect);
|
||||
dc.DrawRectangle(rect);
|
||||
dc.DestroyClippingRegion();
|
||||
}
|
||||
|
||||
void BBLTopbarArt::DrawButton(wxDC& dc, wxWindow* wnd, const wxAuiToolBarItem& item, const wxRect& rect)
|
||||
{
|
||||
int textWidth = 0, textHeight = 0;
|
||||
|
||||
if (m_flags & wxAUI_TB_TEXT)
|
||||
{
|
||||
dc.SetFont(m_font);
|
||||
int tx, ty;
|
||||
|
||||
dc.GetTextExtent(wxT("ABCDHgj"), &tx, &textHeight);
|
||||
textWidth = 0;
|
||||
dc.GetTextExtent(item.GetLabel(), &textWidth, &ty);
|
||||
}
|
||||
|
||||
int bmpX = 0, bmpY = 0;
|
||||
int textX = 0, textY = 0;
|
||||
|
||||
const wxBitmap& bmp = item.GetState() & wxAUI_BUTTON_STATE_DISABLED
|
||||
? item.GetDisabledBitmap()
|
||||
: item.GetBitmap();
|
||||
|
||||
const wxSize bmpSize = bmp.IsOk() ? bmp.GetScaledSize() : wxSize(0, 0);
|
||||
|
||||
if (m_textOrientation == wxAUI_TBTOOL_TEXT_BOTTOM)
|
||||
{
|
||||
bmpX = rect.x +
|
||||
(rect.width / 2) -
|
||||
(bmpSize.x / 2);
|
||||
|
||||
bmpY = rect.y +
|
||||
((rect.height - textHeight) / 2) -
|
||||
(bmpSize.y / 2);
|
||||
|
||||
textX = rect.x + (rect.width / 2) - (textWidth / 2) + 1;
|
||||
textY = rect.y + rect.height - textHeight - 1;
|
||||
}
|
||||
else if (m_textOrientation == wxAUI_TBTOOL_TEXT_RIGHT)
|
||||
{
|
||||
bmpX = rect.x + wnd->FromDIP(3);
|
||||
|
||||
bmpY = rect.y +
|
||||
(rect.height / 2) -
|
||||
(bmpSize.y / 2);
|
||||
|
||||
textX = bmpX + wnd->FromDIP(3) + bmpSize.x;
|
||||
textY = rect.y +
|
||||
(rect.height / 2) -
|
||||
(textHeight / 2);
|
||||
}
|
||||
|
||||
|
||||
if (!(item.GetState() & wxAUI_BUTTON_STATE_DISABLED))
|
||||
{
|
||||
if (item.GetState() & wxAUI_BUTTON_STATE_PRESSED)
|
||||
{
|
||||
dc.SetPen(wxPen(m_highlightColour));
|
||||
dc.SetBrush(wxBrush(m_highlightColour.ChangeLightness(20)));
|
||||
dc.DrawRectangle(rect);
|
||||
}
|
||||
else if ((item.GetState() & wxAUI_BUTTON_STATE_HOVER) || item.IsSticky())
|
||||
{
|
||||
dc.SetPen(wxPen(m_highlightColour));
|
||||
dc.SetBrush(wxBrush(m_highlightColour.ChangeLightness(40)));
|
||||
|
||||
// draw an even lighter background for checked item hovers (since
|
||||
// the hover background is the same color as the check background)
|
||||
if (item.GetState() & wxAUI_BUTTON_STATE_CHECKED)
|
||||
dc.SetBrush(wxBrush(m_highlightColour.ChangeLightness(50)));
|
||||
|
||||
dc.DrawRectangle(rect);
|
||||
}
|
||||
else if (item.GetState() & wxAUI_BUTTON_STATE_CHECKED)
|
||||
{
|
||||
// it's important to put this code in an else statement after the
|
||||
// hover, otherwise hovers won't draw properly for checked items
|
||||
dc.SetPen(wxPen(m_highlightColour));
|
||||
dc.SetBrush(wxBrush(m_highlightColour.ChangeLightness(40)));
|
||||
dc.DrawRectangle(rect);
|
||||
}
|
||||
}
|
||||
|
||||
if (bmp.IsOk())
|
||||
dc.DrawBitmap(bmp, bmpX, bmpY, true);
|
||||
|
||||
// set the item's text color based on if it is disabled
|
||||
#ifdef __WINDOWS__
|
||||
dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
|
||||
#else
|
||||
dc.SetTextForeground(*wxWHITE);
|
||||
#endif
|
||||
if (item.GetState() & wxAUI_BUTTON_STATE_DISABLED)
|
||||
{
|
||||
dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
|
||||
}
|
||||
|
||||
if ((m_flags & wxAUI_TB_TEXT) && !item.GetLabel().empty())
|
||||
{
|
||||
dc.DrawText(item.GetLabel(), textX, textY);
|
||||
}
|
||||
}
|
||||
|
||||
BBLTopbar::BBLTopbar(wxFrame* parent)
|
||||
: wxAuiToolBar(parent, ID_TOOL_BAR, wxDefaultPosition, wxDefaultSize, wxAUI_TB_TEXT | wxAUI_TB_HORZ_TEXT)
|
||||
{
|
||||
Init(parent);
|
||||
}
|
||||
|
||||
BBLTopbar::BBLTopbar(wxWindow* pwin, wxFrame* parent)
|
||||
: wxAuiToolBar(pwin, ID_TOOL_BAR, wxDefaultPosition, wxDefaultSize, wxAUI_TB_TEXT | wxAUI_TB_HORZ_TEXT)
|
||||
{
|
||||
Init(parent);
|
||||
}
|
||||
|
||||
void BBLTopbar::Init(wxFrame* parent)
|
||||
{
|
||||
SetArtProvider(new BBLTopbarArt());
|
||||
m_frame = parent;
|
||||
m_skip_popup_file_menu = false;
|
||||
m_skip_popup_dropdown_menu = false;
|
||||
|
||||
wxInitAllImageHandlers();
|
||||
|
||||
this->AddSpacer(5);
|
||||
|
||||
/*wxBitmap logo_bitmap = create_scaled_bitmap("topbar_logo", nullptr, TOPBAR_ICON_SIZE);
|
||||
wxAuiToolBarItem* logo_item = this->AddTool(ID_LOGO, "", logo_bitmap);
|
||||
logo_item->SetHoverBitmap(logo_bitmap);
|
||||
logo_item->SetActive(false);*/
|
||||
|
||||
wxBitmap file_bitmap = create_scaled_bitmap("topbar_file", nullptr, TOPBAR_ICON_SIZE);
|
||||
m_file_menu_item = this->AddTool(ID_TOP_FILE_MENU, _L("File"), file_bitmap, wxEmptyString, wxITEM_NORMAL);
|
||||
|
||||
this->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
|
||||
|
||||
this->AddSpacer(FromDIP(5));
|
||||
|
||||
wxBitmap dropdown_bitmap = create_scaled_bitmap("topbar_dropdown", nullptr, TOPBAR_ICON_SIZE);
|
||||
m_dropdown_menu_item = this->AddTool(ID_TOP_DROPDOWN_MENU, "",
|
||||
dropdown_bitmap, wxEmptyString);
|
||||
|
||||
this->AddSpacer(FromDIP(5));
|
||||
this->AddSeparator();
|
||||
this->AddSpacer(FromDIP(5));
|
||||
|
||||
//wxBitmap open_bitmap = create_scaled_bitmap("topbar_open", nullptr, TOPBAR_ICON_SIZE);
|
||||
//wxAuiToolBarItem* tool_item = this->AddTool(wxID_OPEN, "", open_bitmap);
|
||||
|
||||
this->AddSpacer(FromDIP(10));
|
||||
|
||||
wxBitmap save_bitmap = create_scaled_bitmap("topbar_save", nullptr, TOPBAR_ICON_SIZE);
|
||||
wxAuiToolBarItem* save_btn = this->AddTool(wxID_SAVE, "", save_bitmap);
|
||||
|
||||
this->AddSpacer(FromDIP(10));
|
||||
|
||||
wxBitmap undo_bitmap = create_scaled_bitmap("topbar_undo", nullptr, TOPBAR_ICON_SIZE);
|
||||
m_undo_item = this->AddTool(wxID_UNDO, "", undo_bitmap);
|
||||
wxBitmap undo_inactive_bitmap = create_scaled_bitmap("topbar_undo_inactive", nullptr, TOPBAR_ICON_SIZE);
|
||||
m_undo_item->SetDisabledBitmap(undo_inactive_bitmap);
|
||||
|
||||
this->AddSpacer(FromDIP(10));
|
||||
|
||||
wxBitmap redo_bitmap = create_scaled_bitmap("topbar_redo", nullptr, TOPBAR_ICON_SIZE);
|
||||
m_redo_item = this->AddTool(wxID_REDO, "", redo_bitmap);
|
||||
wxBitmap redo_inactive_bitmap = create_scaled_bitmap("topbar_redo_inactive", nullptr, TOPBAR_ICON_SIZE);
|
||||
m_redo_item->SetDisabledBitmap(redo_inactive_bitmap);
|
||||
|
||||
this->AddSpacer(FromDIP(10));
|
||||
this->AddStretchSpacer(1);
|
||||
|
||||
m_title_item = this->AddLabel(ID_TITLE, "", FromDIP(300));
|
||||
m_title_item->SetAlignment(wxCENTER);
|
||||
|
||||
this->AddSpacer(FromDIP(10));
|
||||
this->AddStretchSpacer(1);
|
||||
|
||||
#if !BBL_RELEASE_TO_PUBLIC
|
||||
/*wxBitmap m_publish_bitmap = create_scaled_bitmap("topbar_publish", nullptr, TOPBAR_ICON_SIZE);
|
||||
m_publish_item = this->AddTool(ID_PUBLISH, "", m_publish_bitmap);
|
||||
wxBitmap m_publish_disable_bitmap = create_scaled_bitmap("topbar_publish_disable", nullptr, TOPBAR_ICON_SIZE);
|
||||
m_publish_item->SetDisabledBitmap(m_publish_disable_bitmap);
|
||||
this->AddSpacer(FromDIP(12));*/
|
||||
#endif
|
||||
|
||||
/*wxBitmap model_store_bitmap = create_scaled_bitmap("topbar_store", nullptr, TOPBAR_ICON_SIZE);
|
||||
m_model_store_item = this->AddTool(ID_MODEL_STORE, "", model_store_bitmap);
|
||||
this->AddSpacer(12);
|
||||
*/
|
||||
|
||||
//this->AddSeparator();
|
||||
this->AddSpacer(FromDIP(6));
|
||||
|
||||
wxBitmap iconize_bitmap = create_scaled_bitmap("topbar_min", nullptr, TOPBAR_ICON_SIZE);
|
||||
wxAuiToolBarItem* iconize_btn = this->AddTool(wxID_ICONIZE_FRAME, "", iconize_bitmap);
|
||||
|
||||
this->AddSpacer(FromDIP(6));
|
||||
|
||||
maximize_bitmap = create_scaled_bitmap("topbar_max", nullptr, TOPBAR_ICON_SIZE);
|
||||
window_bitmap = create_scaled_bitmap("topbar_win", nullptr, TOPBAR_ICON_SIZE);
|
||||
if (m_frame->IsMaximized()) {
|
||||
maximize_btn = this->AddTool(wxID_MAXIMIZE_FRAME, "", window_bitmap);
|
||||
}
|
||||
else {
|
||||
maximize_btn = this->AddTool(wxID_MAXIMIZE_FRAME, "", maximize_bitmap);
|
||||
}
|
||||
|
||||
this->AddSpacer(FromDIP(6));
|
||||
|
||||
wxBitmap close_bitmap = create_scaled_bitmap("topbar_close", nullptr, TOPBAR_ICON_SIZE);
|
||||
wxAuiToolBarItem* close_btn = this->AddTool(wxID_CLOSE_FRAME, "", close_bitmap);
|
||||
|
||||
this->AddSpacer(FromDIP(6));
|
||||
|
||||
Realize();
|
||||
// m_toolbar_h = this->GetSize().GetHeight();
|
||||
m_toolbar_h = FromDIP(30);
|
||||
|
||||
int client_w = parent->GetClientSize().GetWidth();
|
||||
this->SetSize(client_w, m_toolbar_h);
|
||||
|
||||
this->Bind(wxEVT_MOTION, &BBLTopbar::OnMouseMotion, this);
|
||||
this->Bind(wxEVT_MOUSE_CAPTURE_LOST, &BBLTopbar::OnMouseCaptureLost, this);
|
||||
this->Bind(wxEVT_MENU_CLOSE, &BBLTopbar::OnMenuClose, this);
|
||||
this->Bind(wxEVT_AUITOOLBAR_TOOL_DROPDOWN, &BBLTopbar::OnFileToolItem, this, ID_TOP_FILE_MENU);
|
||||
this->Bind(wxEVT_AUITOOLBAR_TOOL_DROPDOWN, &BBLTopbar::OnDropdownToolItem, this, ID_TOP_DROPDOWN_MENU);
|
||||
this->Bind(wxEVT_AUITOOLBAR_TOOL_DROPDOWN, &BBLTopbar::OnIconize, this, wxID_ICONIZE_FRAME);
|
||||
this->Bind(wxEVT_AUITOOLBAR_TOOL_DROPDOWN, &BBLTopbar::OnFullScreen, this, wxID_MAXIMIZE_FRAME);
|
||||
this->Bind(wxEVT_AUITOOLBAR_TOOL_DROPDOWN, &BBLTopbar::OnCloseFrame, this, wxID_CLOSE_FRAME);
|
||||
this->Bind(wxEVT_LEFT_DCLICK, &BBLTopbar::OnMouseLeftDClock, this);
|
||||
this->Bind(wxEVT_LEFT_DOWN, &BBLTopbar::OnMouseLeftDown, this);
|
||||
this->Bind(wxEVT_LEFT_UP, &BBLTopbar::OnMouseLeftUp, this);
|
||||
this->Bind(wxEVT_AUITOOLBAR_TOOL_DROPDOWN, &BBLTopbar::OnOpenProject, this, wxID_OPEN);
|
||||
this->Bind(wxEVT_AUITOOLBAR_TOOL_DROPDOWN, &BBLTopbar::OnSaveProject, this, wxID_SAVE);
|
||||
this->Bind(wxEVT_AUITOOLBAR_TOOL_DROPDOWN, &BBLTopbar::OnRedo, this, wxID_REDO);
|
||||
this->Bind(wxEVT_AUITOOLBAR_TOOL_DROPDOWN, &BBLTopbar::OnUndo, this, wxID_UNDO);
|
||||
//this->Bind(wxEVT_AUITOOLBAR_TOOL_DROPDOWN, &BBLTopbar::OnModelStoreClicked, this, ID_MODEL_STORE);
|
||||
//this->Bind(wxEVT_AUITOOLBAR_TOOL_DROPDOWN, &BBLTopbar::OnPublishClicked, this, ID_PUBLISH);
|
||||
}
|
||||
|
||||
BBLTopbar::~BBLTopbar()
|
||||
{
|
||||
m_file_menu_item = nullptr;
|
||||
m_dropdown_menu_item = nullptr;
|
||||
m_file_menu = nullptr;
|
||||
}
|
||||
|
||||
void BBLTopbar::OnOpenProject(wxAuiToolBarEvent& event)
|
||||
{
|
||||
MainFrame* main_frame = dynamic_cast<MainFrame*>(m_frame);
|
||||
Plater* plater = main_frame->plater();
|
||||
plater->load_project();
|
||||
}
|
||||
|
||||
void BBLTopbar::OnSaveProject(wxAuiToolBarEvent& event)
|
||||
{
|
||||
MainFrame* main_frame = dynamic_cast<MainFrame*>(m_frame);
|
||||
Plater* plater = main_frame->plater();
|
||||
plater->save_project();
|
||||
}
|
||||
|
||||
void BBLTopbar::OnUndo(wxAuiToolBarEvent& event)
|
||||
{
|
||||
MainFrame* main_frame = dynamic_cast<MainFrame*>(m_frame);
|
||||
Plater* plater = main_frame->plater();
|
||||
plater->undo();
|
||||
}
|
||||
|
||||
void BBLTopbar::OnRedo(wxAuiToolBarEvent& event)
|
||||
{
|
||||
MainFrame* main_frame = dynamic_cast<MainFrame*>(m_frame);
|
||||
Plater* plater = main_frame->plater();
|
||||
plater->redo();
|
||||
}
|
||||
|
||||
void BBLTopbar::EnableUndoRedoItems()
|
||||
{
|
||||
this->EnableTool(m_undo_item->GetId(), true);
|
||||
this->EnableTool(m_redo_item->GetId(), true);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void BBLTopbar::DisableUndoRedoItems()
|
||||
{
|
||||
this->EnableTool(m_undo_item->GetId(), false);
|
||||
this->EnableTool(m_redo_item->GetId(), false);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void BBLTopbar::SaveNormalRect()
|
||||
{
|
||||
m_normalRect = m_frame->GetRect();
|
||||
}
|
||||
|
||||
void BBLTopbar::OnModelStoreClicked(wxAuiToolBarEvent& event)
|
||||
{
|
||||
//GUI::wxGetApp().load_url(wxString(wxGetApp().app_config->get_web_host_url() + MODEL_STORE_URL));
|
||||
}
|
||||
|
||||
void BBLTopbar::OnPublishClicked(wxAuiToolBarEvent& event)
|
||||
{
|
||||
if (GUI::wxGetApp().plater()->model().objects.empty()) return;
|
||||
|
||||
if (!wxGetApp().is_user_login()) return;
|
||||
|
||||
wxGetApp().plater()->show_publish_dialog();
|
||||
}
|
||||
|
||||
void BBLTopbar::SetFileMenu(wxMenu* file_menu)
|
||||
{
|
||||
m_file_menu = file_menu;
|
||||
}
|
||||
|
||||
void BBLTopbar::AddDropDownSubMenu(wxMenu* sub_menu, const wxString& title)
|
||||
{
|
||||
m_top_menu.AppendSubMenu(sub_menu, title);
|
||||
}
|
||||
|
||||
void BBLTopbar::AddDropDownMenuItem(wxMenuItem* menu_item)
|
||||
{
|
||||
m_top_menu.Append(menu_item);
|
||||
}
|
||||
|
||||
wxMenu* BBLTopbar::GetTopMenu()
|
||||
{
|
||||
return &m_top_menu;
|
||||
}
|
||||
|
||||
void BBLTopbar::SetTitle(wxString title)
|
||||
{
|
||||
m_title_item->SetLabel(title);
|
||||
m_title_item->SetAlignment(wxALIGN_CENTRE_HORIZONTAL);
|
||||
this->Refresh();
|
||||
}
|
||||
|
||||
void BBLTopbar::SetMaximizedSize()
|
||||
{
|
||||
maximize_btn->SetBitmap(maximize_bitmap);
|
||||
}
|
||||
|
||||
void BBLTopbar::SetWindowSize()
|
||||
{
|
||||
maximize_btn->SetBitmap(window_bitmap);
|
||||
}
|
||||
|
||||
void BBLTopbar::UpdateToolbarWidth(int width)
|
||||
{
|
||||
this->SetSize(width, m_toolbar_h);
|
||||
}
|
||||
|
||||
void BBLTopbar::Rescale() {
|
||||
int em = em_unit(this);
|
||||
wxAuiToolBarItem* item;
|
||||
|
||||
/*item = this->FindTool(ID_LOGO);
|
||||
item->SetBitmap(create_scaled_bitmap("topbar_logo", nullptr, TOPBAR_ICON_SIZE));*/
|
||||
|
||||
item = this->FindTool(ID_TOP_FILE_MENU);
|
||||
item->SetBitmap(create_scaled_bitmap("topbar_file", this, TOPBAR_ICON_SIZE));
|
||||
|
||||
item = this->FindTool(ID_TOP_DROPDOWN_MENU);
|
||||
item->SetBitmap(create_scaled_bitmap("topbar_dropdown", this, TOPBAR_ICON_SIZE));
|
||||
|
||||
//item = this->FindTool(wxID_OPEN);
|
||||
//item->SetBitmap(create_scaled_bitmap("topbar_open", nullptr, TOPBAR_ICON_SIZE));
|
||||
|
||||
item = this->FindTool(wxID_SAVE);
|
||||
item->SetBitmap(create_scaled_bitmap("topbar_save", this, TOPBAR_ICON_SIZE));
|
||||
|
||||
item = this->FindTool(wxID_UNDO);
|
||||
item->SetBitmap(create_scaled_bitmap("topbar_undo", this, TOPBAR_ICON_SIZE));
|
||||
item->SetDisabledBitmap(create_scaled_bitmap("topbar_undo_inactive", nullptr, TOPBAR_ICON_SIZE));
|
||||
|
||||
item = this->FindTool(wxID_REDO);
|
||||
item->SetBitmap(create_scaled_bitmap("topbar_redo", this, TOPBAR_ICON_SIZE));
|
||||
item->SetDisabledBitmap(create_scaled_bitmap("topbar_redo_inactive", nullptr, TOPBAR_ICON_SIZE));
|
||||
|
||||
item = this->FindTool(ID_TITLE);
|
||||
|
||||
/*item = this->FindTool(ID_PUBLISH);
|
||||
item->SetBitmap(create_scaled_bitmap("topbar_publish", this, TOPBAR_ICON_SIZE));
|
||||
item->SetDisabledBitmap(create_scaled_bitmap("topbar_publish_disable", nullptr, TOPBAR_ICON_SIZE));*/
|
||||
|
||||
/*item = this->FindTool(ID_MODEL_STORE);
|
||||
item->SetBitmap(create_scaled_bitmap("topbar_store", this, TOPBAR_ICON_SIZE));
|
||||
*/
|
||||
|
||||
item = this->FindTool(wxID_ICONIZE_FRAME);
|
||||
item->SetBitmap(create_scaled_bitmap("topbar_min", this, TOPBAR_ICON_SIZE));
|
||||
|
||||
item = this->FindTool(wxID_MAXIMIZE_FRAME);
|
||||
maximize_bitmap = create_scaled_bitmap("topbar_max", this, TOPBAR_ICON_SIZE);
|
||||
window_bitmap = create_scaled_bitmap("topbar_win", this, TOPBAR_ICON_SIZE);
|
||||
if (m_frame->IsMaximized()) {
|
||||
item->SetBitmap(window_bitmap);
|
||||
}
|
||||
else {
|
||||
item->SetBitmap(maximize_bitmap);
|
||||
}
|
||||
|
||||
item = this->FindTool(wxID_CLOSE_FRAME);
|
||||
item->SetBitmap(create_scaled_bitmap("topbar_close", this, TOPBAR_ICON_SIZE));
|
||||
|
||||
Realize();
|
||||
}
|
||||
|
||||
void BBLTopbar::OnIconize(wxAuiToolBarEvent& event)
|
||||
{
|
||||
m_frame->Iconize();
|
||||
}
|
||||
|
||||
void BBLTopbar::OnFullScreen(wxAuiToolBarEvent& event)
|
||||
{
|
||||
if (m_frame->IsMaximized()) {
|
||||
m_frame->Restore();
|
||||
}
|
||||
else {
|
||||
m_normalRect = m_frame->GetRect();
|
||||
m_frame->Maximize();
|
||||
}
|
||||
}
|
||||
|
||||
void BBLTopbar::OnCloseFrame(wxAuiToolBarEvent& event)
|
||||
{
|
||||
m_frame->Close();
|
||||
}
|
||||
|
||||
void BBLTopbar::OnMouseLeftDClock(wxMouseEvent& mouse)
|
||||
{
|
||||
// check whether mouse is not on any tool item
|
||||
if (this->FindToolByCurrentPosition() != NULL &&
|
||||
this->FindToolByCurrentPosition() != m_title_item) {
|
||||
mouse.Skip();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_frame->IsMaximized()) {
|
||||
m_frame->Restore();
|
||||
}
|
||||
else {
|
||||
m_normalRect = m_frame->GetRect();
|
||||
m_frame->Maximize();
|
||||
}
|
||||
}
|
||||
|
||||
void BBLTopbar::OnFileToolItem(wxAuiToolBarEvent& evt)
|
||||
{
|
||||
wxAuiToolBar* tb = static_cast<wxAuiToolBar*>(evt.GetEventObject());
|
||||
|
||||
tb->SetToolSticky(evt.GetId(), true);
|
||||
|
||||
if (!m_skip_popup_file_menu) {
|
||||
this->PopupMenu(m_file_menu, wxPoint(0, this->GetSize().GetHeight() - 2));
|
||||
}
|
||||
else {
|
||||
m_skip_popup_file_menu = false;
|
||||
}
|
||||
|
||||
// make sure the button is "un-stuck"
|
||||
tb->SetToolSticky(evt.GetId(), false);
|
||||
}
|
||||
|
||||
void BBLTopbar::OnDropdownToolItem(wxAuiToolBarEvent& evt)
|
||||
{
|
||||
wxAuiToolBar* tb = static_cast<wxAuiToolBar*>(evt.GetEventObject());
|
||||
|
||||
tb->SetToolSticky(evt.GetId(), true);
|
||||
|
||||
if (!m_skip_popup_dropdown_menu) {
|
||||
PopupMenu(&m_top_menu, wxPoint(0, this->GetSize().GetHeight() - 2));
|
||||
}
|
||||
else {
|
||||
m_skip_popup_dropdown_menu = false;
|
||||
}
|
||||
|
||||
// make sure the button is "un-stuck"
|
||||
tb->SetToolSticky(evt.GetId(), false);
|
||||
}
|
||||
|
||||
void BBLTopbar::OnMouseLeftDown(wxMouseEvent& event)
|
||||
{
|
||||
wxPoint mouse_pos = ::wxGetMousePosition();
|
||||
wxPoint frame_pos = m_frame->GetScreenPosition();
|
||||
m_delta = mouse_pos - frame_pos;
|
||||
|
||||
if (FindToolByCurrentPosition() == NULL)
|
||||
{
|
||||
CaptureMouse();
|
||||
}
|
||||
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
void BBLTopbar::OnMouseLeftUp(wxMouseEvent& event)
|
||||
{
|
||||
if (HasCapture())
|
||||
{
|
||||
ReleaseMouse();
|
||||
}
|
||||
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
void BBLTopbar::OnMouseMotion(wxMouseEvent& event)
|
||||
{
|
||||
wxPoint mouse_pos = event.GetPosition();
|
||||
if (!HasCapture()) {
|
||||
//m_frame->OnMouseMotion(event);
|
||||
|
||||
event.Skip();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.Dragging() && event.LeftIsDown())
|
||||
{
|
||||
wxPoint mouse_pos = ::wxGetMousePosition();
|
||||
// leave max state and adjust position
|
||||
if (m_frame->IsMaximized()) {
|
||||
wxRect rect = m_frame->GetRect();
|
||||
// Filter unexcept mouse move
|
||||
if (m_delta + rect.GetLeftTop() != mouse_pos) {
|
||||
m_delta = mouse_pos - rect.GetLeftTop();
|
||||
m_delta.x = m_delta.x * m_normalRect.width / rect.width;
|
||||
m_delta.y = m_delta.y * m_normalRect.height / rect.height;
|
||||
m_frame->Restore();
|
||||
}
|
||||
}
|
||||
m_frame->Move(mouse_pos - m_delta);
|
||||
}
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
void BBLTopbar::OnMouseCaptureLost(wxMouseCaptureLostEvent& event)
|
||||
{
|
||||
}
|
||||
|
||||
void BBLTopbar::OnMenuClose(wxMenuEvent& event)
|
||||
{
|
||||
wxAuiToolBarItem* item = this->FindToolByCurrentPosition();
|
||||
if (item == m_file_menu_item) {
|
||||
m_skip_popup_file_menu = true;
|
||||
}
|
||||
else if (item == m_dropdown_menu_item) {
|
||||
m_skip_popup_dropdown_menu = true;
|
||||
}
|
||||
}
|
||||
|
||||
wxAuiToolBarItem* BBLTopbar::FindToolByCurrentPosition()
|
||||
{
|
||||
wxPoint mouse_pos = ::wxGetMousePosition();
|
||||
wxPoint client_pos = this->ScreenToClient(mouse_pos);
|
||||
return this->FindToolByPosition(client_pos.x, client_pos.y);
|
||||
}
|
||||
77
src/slic3r/GUI/BBLTopbar.hpp
Normal file
77
src/slic3r/GUI/BBLTopbar.hpp
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
#pragma once
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
#include "wx/aui/auibar.h"
|
||||
|
||||
#include "SelectMachine.hpp"
|
||||
#include "DeviceManager.hpp"
|
||||
|
||||
|
||||
using namespace Slic3r::GUI;
|
||||
|
||||
class BBLTopbar : public wxAuiToolBar
|
||||
{
|
||||
public:
|
||||
BBLTopbar(wxWindow* pwin, wxFrame* parent);
|
||||
BBLTopbar(wxFrame* parent);
|
||||
void Init(wxFrame *parent);
|
||||
~BBLTopbar();
|
||||
void UpdateToolbarWidth(int width);
|
||||
void Rescale();
|
||||
void OnIconize(wxAuiToolBarEvent& event);
|
||||
void OnFullScreen(wxAuiToolBarEvent& event);
|
||||
void OnCloseFrame(wxAuiToolBarEvent& event);
|
||||
void OnFileToolItem(wxAuiToolBarEvent& evt);
|
||||
void OnDropdownToolItem(wxAuiToolBarEvent& evt);
|
||||
void OnMouseLeftDClock(wxMouseEvent& mouse);
|
||||
void OnMouseLeftDown(wxMouseEvent& event);
|
||||
void OnMouseLeftUp(wxMouseEvent& event);
|
||||
void OnMouseMotion(wxMouseEvent& event);
|
||||
void OnMouseCaptureLost(wxMouseCaptureLostEvent& event);
|
||||
void OnMenuClose(wxMenuEvent& event);
|
||||
void OnOpenProject(wxAuiToolBarEvent& event);
|
||||
void OnSaveProject(wxAuiToolBarEvent& event);
|
||||
void OnUndo(wxAuiToolBarEvent& event);
|
||||
void OnRedo(wxAuiToolBarEvent& event);
|
||||
void OnModelStoreClicked(wxAuiToolBarEvent& event);
|
||||
void OnPublishClicked(wxAuiToolBarEvent &event);
|
||||
|
||||
wxAuiToolBarItem* FindToolByCurrentPosition();
|
||||
|
||||
void SetFileMenu(wxMenu* file_menu);
|
||||
void AddDropDownSubMenu(wxMenu* sub_menu, const wxString& title);
|
||||
void AddDropDownMenuItem(wxMenuItem* menu_item);
|
||||
wxMenu *GetTopMenu();
|
||||
void SetTitle(wxString title);
|
||||
void SetMaximizedSize();
|
||||
void SetWindowSize();
|
||||
|
||||
void EnableUndoRedoItems();
|
||||
void DisableUndoRedoItems();
|
||||
|
||||
void SaveNormalRect();
|
||||
|
||||
private:
|
||||
wxFrame* m_frame;
|
||||
wxAuiToolBarItem* m_file_menu_item;
|
||||
wxAuiToolBarItem* m_dropdown_menu_item;
|
||||
wxRect m_normalRect;
|
||||
wxPoint m_delta;
|
||||
wxMenu m_top_menu;
|
||||
wxMenu* m_file_menu;
|
||||
wxAuiToolBarItem* m_title_item;
|
||||
wxAuiToolBarItem* m_account_item;
|
||||
wxAuiToolBarItem* m_model_store_item;
|
||||
|
||||
//wxAuiToolBarItem *m_publish_item;
|
||||
wxAuiToolBarItem* m_undo_item;
|
||||
wxAuiToolBarItem* m_redo_item;
|
||||
wxAuiToolBarItem* maximize_btn;
|
||||
|
||||
wxBitmap maximize_bitmap;
|
||||
wxBitmap window_bitmap;
|
||||
|
||||
int m_toolbar_h;
|
||||
bool m_skip_popup_file_menu;
|
||||
bool m_skip_popup_dropdown_menu;
|
||||
};
|
||||
812
src/slic3r/GUI/BackgroundSlicingProcess.cpp
Normal file
812
src/slic3r/GUI/BackgroundSlicingProcess.cpp
Normal file
|
|
@ -0,0 +1,812 @@
|
|||
#include "BackgroundSlicingProcess.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "format.hpp"
|
||||
|
||||
#include <wx/app.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/stdpaths.h>
|
||||
|
||||
// For zipped archive creation
|
||||
#include <wx/stdstream.h>
|
||||
#include <wx/wfstream.h>
|
||||
#include <wx/zipstrm.h>
|
||||
|
||||
#include <miniz.h>
|
||||
|
||||
// Print now includes tbb, and tbb includes Windows. This breaks compilation of wxWidgets if included before wx.
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/SLAPrint.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/GCode/PostProcessor.hpp"
|
||||
#include "libslic3r/Format/SL1.hpp"
|
||||
#include "libslic3r/Thread.hpp"
|
||||
#include "libslic3r/libslic3r.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
#include <cctype>
|
||||
|
||||
#include <boost/format/format_fwd.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#include "I18N.hpp"
|
||||
//#include "RemovableDriveManager.hpp"
|
||||
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
bool SlicingProcessCompletedEvent::critical_error() const
|
||||
{
|
||||
try {
|
||||
this->rethrow_exception();
|
||||
} catch (const Slic3r::SlicingError &) {
|
||||
// Exception derived from SlicingError is non-critical.
|
||||
return false;
|
||||
} catch (...) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SlicingProcessCompletedEvent::invalidate_plater() const
|
||||
{
|
||||
if (critical_error())
|
||||
{
|
||||
try {
|
||||
this->rethrow_exception();
|
||||
}
|
||||
catch (const Slic3r::ExportError&) {
|
||||
// Exception thrown by copying file does not ivalidate plater
|
||||
return false;
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::pair<std::string, bool> SlicingProcessCompletedEvent::format_error_message() const
|
||||
{
|
||||
std::string error;
|
||||
bool monospace = false;
|
||||
try {
|
||||
this->rethrow_exception();
|
||||
} catch (const std::bad_alloc &ex) {
|
||||
wxString errmsg = GUI::from_u8(boost::format(_utf8(L("A error occurred. Maybe memory of system is not enough or it's a bug "
|
||||
"of the program"))).str());
|
||||
error = std::string(errmsg.ToUTF8()) + "\n" + std::string(ex.what());
|
||||
} catch (const HardCrash &ex) {
|
||||
error = GUI::format("A fatal error occurred: \"%1%\"", ex.what()) + "\n" +
|
||||
_u8L("Please save project and restart the program. ");
|
||||
} catch (PlaceholderParserError &ex) {
|
||||
error = ex.what();
|
||||
monospace = true;
|
||||
} catch (std::exception &ex) {
|
||||
error = ex.what();
|
||||
} catch (...) {
|
||||
error = "Unknown C++ exception.";
|
||||
}
|
||||
return std::make_pair(std::move(error), monospace);
|
||||
}
|
||||
|
||||
BackgroundSlicingProcess::BackgroundSlicingProcess()
|
||||
{
|
||||
//BBS: move this logic to part plate
|
||||
#if 0
|
||||
boost::filesystem::path temp_path(wxStandardPaths::Get().GetTempDir().utf8_str().data());
|
||||
temp_path /= (boost::format(".%1%.gcode") % get_current_pid()).str();
|
||||
m_temp_output_path = temp_path.string();
|
||||
#endif
|
||||
}
|
||||
|
||||
BackgroundSlicingProcess::~BackgroundSlicingProcess()
|
||||
{
|
||||
this->stop();
|
||||
this->join_background_thread();
|
||||
//BBS: move this logic to part plate
|
||||
//boost::nowide::remove(m_temp_output_path.c_str());
|
||||
}
|
||||
|
||||
//BBS: switch the print in background slicing process
|
||||
bool BackgroundSlicingProcess::switch_print_preprocess()
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
/*switch (m_printer_tech) {
|
||||
case ptFFF: m_print = m_fff_print; break;
|
||||
case ptSLA: m_print = m_sla_print; break;
|
||||
default: assert(false); break;
|
||||
}*/
|
||||
return result;
|
||||
}
|
||||
|
||||
//BBS: judge whether can switch the print
|
||||
bool BackgroundSlicingProcess::can_switch_print()
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
if (m_state == STATE_RUNNING)
|
||||
{
|
||||
//currently it is on slicing, judge whether the slice result is valid or not
|
||||
//if (m_current_plate->is_slice_result_valid())
|
||||
{
|
||||
result = false;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": slicing plate's plate_id %1%, on slicing, can not switch print") % m_current_plate->get_index();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//BBS: select the printer technology
|
||||
bool BackgroundSlicingProcess::select_technology(PrinterTechnology tech)
|
||||
{
|
||||
bool changed = false;
|
||||
if (m_printer_tech != tech) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": change the printer technology from %1% to %2%") % m_printer_tech % tech;
|
||||
m_printer_tech = tech;
|
||||
if (m_print != nullptr)
|
||||
this->reset();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
switch (tech) {
|
||||
case ptFFF: m_print = m_fff_print; break;
|
||||
case ptSLA: m_print = m_sla_print; break;
|
||||
default: assert(false); break;
|
||||
}
|
||||
assert(m_print != nullptr);
|
||||
return changed;
|
||||
}
|
||||
|
||||
PrinterTechnology BackgroundSlicingProcess::current_printer_technology() const
|
||||
{
|
||||
//BBS: as the m_printer is changed frequently when switch plates, use m_printer_tech directly
|
||||
return m_printer_tech;
|
||||
//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()
|
||||
{
|
||||
assert(m_print == m_fff_print);
|
||||
//BBS: add the logic to process from an existed gcode file
|
||||
if (m_print->finished()) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%: skip slicing, to process previous gcode file")%__LINE__;
|
||||
m_fff_print->set_status(80, _utf8(L("Processing G-Code from Previous file...")));
|
||||
wxCommandEvent evt(m_event_slicing_completed_id);
|
||||
// Post the Slicing Finished message for the G-code viewer to update.
|
||||
// Passing the timestamp
|
||||
evt.SetInt((int)(m_fff_print->step_state_with_timestamp(PrintStep::psSlicingFinished).timestamp));
|
||||
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone());
|
||||
|
||||
m_temp_output_path = this->get_current_plate()->get_tmp_gcode_path();
|
||||
if (! m_export_path.empty()) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%: export gcode from %2% directly to %3%")%__LINE__%m_temp_output_path %m_export_path;
|
||||
}
|
||||
else {
|
||||
m_fff_print->export_gcode_from_previous_file(m_temp_output_path, m_gcode_result, [this](const ThumbnailsParams& params) { return this->render_thumbnails(params); });
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%: export_gcode_from_previous_file from %2% finished")%__LINE__ % m_temp_output_path;
|
||||
}
|
||||
}
|
||||
else {
|
||||
//BBS: reset the gcode before reload_print in slicing_completed event processing
|
||||
//FIX the gcode rename failed issue
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%: will start slicing, reset gcode_result %2% firstly")%__LINE__%m_gcode_result;
|
||||
m_gcode_result->reset();
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%: gcode_result reseted, will start print::process")%__LINE__;
|
||||
m_print->process();
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%: after print::process, send slicing complete event to gui...")%__LINE__;
|
||||
|
||||
wxCommandEvent evt(m_event_slicing_completed_id);
|
||||
// Post the Slicing Finished message for the G-code viewer to update.
|
||||
// Passing the timestamp
|
||||
evt.SetInt((int)(m_fff_print->step_state_with_timestamp(PrintStep::psSlicingFinished).timestamp));
|
||||
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone());
|
||||
|
||||
//BBS: add plate index into render params
|
||||
m_temp_output_path = this->get_current_plate()->get_tmp_gcode_path();
|
||||
m_fff_print->export_gcode(m_temp_output_path, m_gcode_result, [this](const ThumbnailsParams& params) { return this->render_thumbnails(params); });
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": export gcode finished");
|
||||
}
|
||||
if (this->set_step_started(bspsGCodeFinalize)) {
|
||||
if (! m_export_path.empty()) {
|
||||
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_export_began_id));
|
||||
finalize_gcode();
|
||||
} else {
|
||||
m_print->set_status(100, _utf8(L("Slicing complete")));
|
||||
}
|
||||
this->set_step_done(bspsGCodeFinalize);
|
||||
}
|
||||
}
|
||||
|
||||
static void write_thumbnail(Zipper& zipper, const ThumbnailData& data)
|
||||
{
|
||||
size_t png_size = 0;
|
||||
void* png_data = tdefl_write_image_to_png_file_in_memory_ex((const void*)data.pixels.data(), data.width, data.height, 4, &png_size, MZ_DEFAULT_LEVEL, 1);
|
||||
if (png_data != nullptr)
|
||||
{
|
||||
zipper.add_entry("thumbnail/thumbnail" + std::to_string(data.width) + "x" + std::to_string(data.height) + ".png", (const std::uint8_t*)png_data, png_size);
|
||||
mz_free(png_data);
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundSlicingProcess::process_sla()
|
||||
{
|
||||
assert(m_print == m_sla_print);
|
||||
m_print->process();
|
||||
if (this->set_step_started(bspsGCodeFinalize)) {
|
||||
if (! m_export_path.empty()) {
|
||||
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_export_began_id));
|
||||
|
||||
const std::string export_path = m_sla_print->print_statistics().finalize_output_path(m_export_path);
|
||||
|
||||
//BBS: add plate id for thumbnail generation
|
||||
ThumbnailsList thumbnails = this->render_thumbnails(
|
||||
ThumbnailsParams{ THUMBNAIL_SIZE, true, true, true, true, 0 });
|
||||
|
||||
Zipper zipper(export_path);
|
||||
m_sla_archive.export_print(zipper, *m_sla_print); // true, false, true, true); // renders also supports and pad
|
||||
for (const ThumbnailData& data : thumbnails)
|
||||
if (data.is_valid())
|
||||
write_thumbnail(zipper, data);
|
||||
zipper.finalize();
|
||||
|
||||
//m_print->set_status(100, (boost::format(_utf8(L("Masked SLA file exported to %1%"))) % export_path).str());
|
||||
m_print->set_status(100, (boost::format(_utf8("Masked SLA file exported to %1%")) % export_path).str());
|
||||
} else {
|
||||
//m_print->set_status(100, _utf8(L("Slicing complete")));
|
||||
m_print->set_status(100, _utf8("Slicing complete"));
|
||||
}
|
||||
this->set_step_done(bspsGCodeFinalize);
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundSlicingProcess::thread_proc()
|
||||
{
|
||||
//BBS: thread name
|
||||
set_current_thread_name("bbl_BgSlcPcs");
|
||||
name_tbb_thread_pool_threads_set_locale();
|
||||
|
||||
assert(m_print != nullptr);
|
||||
assert(m_print == m_fff_print || m_print == m_sla_print);
|
||||
std::unique_lock<std::mutex> lck(m_mutex);
|
||||
// Let the caller know we are ready to run the background processing task.
|
||||
m_state = STATE_IDLE;
|
||||
lck.unlock();
|
||||
m_condition.notify_one();
|
||||
for (;;) {
|
||||
//BBS: sometimes the state has already been set in the start function
|
||||
//assert(m_state == STATE_IDLE || m_state == STATE_CANCELED || m_state == STATE_FINISHED || m_state == STATE_STARTED);
|
||||
// Wait until a new task is ready to be executed, or this thread should be finished.
|
||||
lck.lock();
|
||||
m_condition.wait(lck, [this](){ return m_state == STATE_STARTED || m_state == STATE_EXIT; });
|
||||
if (m_state == STATE_EXIT)
|
||||
// Exiting this thread.
|
||||
break;
|
||||
// Process the background slicing task.
|
||||
m_state = STATE_RUNNING;
|
||||
lck.unlock();
|
||||
std::exception_ptr exception;
|
||||
#ifdef _WIN32
|
||||
this->call_process_seh_throw(exception);
|
||||
#else
|
||||
this->call_process(exception);
|
||||
#endif
|
||||
m_print->finalize();
|
||||
lck.lock();
|
||||
m_state = m_print->canceled() ? STATE_CANCELED : STATE_FINISHED;
|
||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": process finished, state %1%, print cancel_status %2%")%m_state %m_print->cancel_status();
|
||||
if (m_print->cancel_status() != Print::CANCELED_INTERNAL) {
|
||||
// Only post the canceled event, if canceled by user.
|
||||
// Don't post the canceled event, if canceled from Print::apply().
|
||||
SlicingProcessCompletedEvent evt(m_event_finished_id, 0,
|
||||
(m_state == STATE_CANCELED) ? SlicingProcessCompletedEvent::Cancelled :
|
||||
exception ? SlicingProcessCompletedEvent::Error : SlicingProcessCompletedEvent::Finished, exception);
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": send SlicingProcessCompletedEvent to main, status %1%")%evt.status();
|
||||
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone());
|
||||
}
|
||||
m_print->restart();
|
||||
lck.unlock();
|
||||
// Let the UI thread wake up if it is waiting for the background task to finish.
|
||||
m_condition.notify_one();
|
||||
// Let the UI thread see the result.
|
||||
}
|
||||
m_state = STATE_EXITED;
|
||||
lck.unlock();
|
||||
// End of the background processing thread. The UI thread should join m_thread now.
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Only these SEH exceptions will be catched and turned into Slic3r::HardCrash C++ exceptions.
|
||||
static bool is_win32_seh_harware_exception(unsigned long ex) throw() {
|
||||
return
|
||||
ex == STATUS_ACCESS_VIOLATION ||
|
||||
ex == STATUS_DATATYPE_MISALIGNMENT ||
|
||||
ex == STATUS_FLOAT_DIVIDE_BY_ZERO ||
|
||||
ex == STATUS_FLOAT_OVERFLOW ||
|
||||
ex == STATUS_FLOAT_UNDERFLOW ||
|
||||
#ifdef STATUS_FLOATING_RESEVERED_OPERAND
|
||||
ex == STATUS_FLOATING_RESEVERED_OPERAND ||
|
||||
#endif // STATUS_FLOATING_RESEVERED_OPERAND
|
||||
ex == STATUS_ILLEGAL_INSTRUCTION ||
|
||||
ex == STATUS_PRIVILEGED_INSTRUCTION ||
|
||||
ex == STATUS_INTEGER_DIVIDE_BY_ZERO ||
|
||||
ex == STATUS_INTEGER_OVERFLOW ||
|
||||
ex == STATUS_STACK_OVERFLOW;
|
||||
}
|
||||
|
||||
// Rethrow some SEH exceptions as Slic3r::HardCrash C++ exceptions.
|
||||
static void rethrow_seh_exception(unsigned long win32_seh_catched)
|
||||
{
|
||||
if (win32_seh_catched) {
|
||||
// Rethrow SEH exception as Slicer::HardCrash.
|
||||
if (win32_seh_catched == STATUS_ACCESS_VIOLATION || win32_seh_catched == STATUS_DATATYPE_MISALIGNMENT)
|
||||
throw Slic3r::HardCrash(_u8L("Access violation"));
|
||||
if (win32_seh_catched == STATUS_ILLEGAL_INSTRUCTION || win32_seh_catched == STATUS_PRIVILEGED_INSTRUCTION)
|
||||
throw Slic3r::HardCrash(_u8L("Illegal instruction"));
|
||||
if (win32_seh_catched == STATUS_FLOAT_DIVIDE_BY_ZERO || win32_seh_catched == STATUS_INTEGER_DIVIDE_BY_ZERO)
|
||||
throw Slic3r::HardCrash(_u8L("Divide by zero"));
|
||||
if (win32_seh_catched == STATUS_FLOAT_OVERFLOW || win32_seh_catched == STATUS_INTEGER_OVERFLOW)
|
||||
throw Slic3r::HardCrash(_u8L("Overflow"));
|
||||
if (win32_seh_catched == STATUS_FLOAT_UNDERFLOW)
|
||||
throw Slic3r::HardCrash(_u8L("Underflow"));
|
||||
#ifdef STATUS_FLOATING_RESEVERED_OPERAND
|
||||
if (win32_seh_catched == STATUS_FLOATING_RESEVERED_OPERAND)
|
||||
throw Slic3r::HardCrash(_u8L("Floating reserved operand"));
|
||||
#endif // STATUS_FLOATING_RESEVERED_OPERAND
|
||||
if (win32_seh_catched == STATUS_STACK_OVERFLOW)
|
||||
throw Slic3r::HardCrash(_u8L("Stack overflow"));
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapper for Win32 structured exceptions. Win32 structured exception blocks and C++ exception blocks cannot be mixed in the same function.
|
||||
unsigned long BackgroundSlicingProcess::call_process_seh(std::exception_ptr &ex) throw()
|
||||
{
|
||||
unsigned long win32_seh_catched = 0;
|
||||
__try {
|
||||
this->call_process(ex);
|
||||
} __except (is_win32_seh_harware_exception(GetExceptionCode())) {
|
||||
win32_seh_catched = GetExceptionCode();
|
||||
}
|
||||
return win32_seh_catched;
|
||||
}
|
||||
void BackgroundSlicingProcess::call_process_seh_throw(std::exception_ptr &ex) throw()
|
||||
{
|
||||
unsigned long win32_seh_catched = this->call_process_seh(ex);
|
||||
if (win32_seh_catched) {
|
||||
// Rethrow SEH exception as Slicer::HardCrash.
|
||||
try {
|
||||
rethrow_seh_exception(win32_seh_catched);
|
||||
} catch (...) {
|
||||
ex = std::current_exception();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
void BackgroundSlicingProcess::call_process(std::exception_ptr &ex) throw()
|
||||
{
|
||||
try {
|
||||
assert(m_print != nullptr);
|
||||
switch (m_print->technology()) {
|
||||
case ptFFF: this->process_fff(); break;
|
||||
case ptSLA: this->process_sla(); break;
|
||||
default: m_print->process(); break;
|
||||
}
|
||||
} catch (CanceledException& /* ex */) {
|
||||
// Canceled, this is all right.
|
||||
assert(m_print->canceled());
|
||||
ex = std::current_exception();
|
||||
BOOST_LOG_TRIVIAL(error) <<__FUNCTION__ << ":got cancelled exception" << std::endl;
|
||||
} catch (...) {
|
||||
ex = std::current_exception();
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":got other exception" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
unsigned long BackgroundSlicingProcess::thread_proc_safe_seh() throw()
|
||||
{
|
||||
unsigned long win32_seh_catched = 0;
|
||||
__try {
|
||||
this->thread_proc_safe();
|
||||
} __except (is_win32_seh_harware_exception(GetExceptionCode())) {
|
||||
win32_seh_catched = GetExceptionCode();
|
||||
}
|
||||
return win32_seh_catched;
|
||||
}
|
||||
void BackgroundSlicingProcess::thread_proc_safe_seh_throw() throw()
|
||||
{
|
||||
unsigned long win32_seh_catched = this->thread_proc_safe_seh();
|
||||
if (win32_seh_catched) {
|
||||
// Rethrow SEH exception as Slicer::HardCrash.
|
||||
try {
|
||||
rethrow_seh_exception(win32_seh_catched);
|
||||
} catch (...) {
|
||||
wxTheApp->OnUnhandledException();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
void BackgroundSlicingProcess::thread_proc_safe() throw()
|
||||
{
|
||||
try {
|
||||
this->thread_proc();
|
||||
} catch (...) {
|
||||
wxTheApp->OnUnhandledException();
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundSlicingProcess::join_background_thread()
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(m_mutex);
|
||||
if (m_state == STATE_INITIAL) {
|
||||
// Worker thread has not been started yet.
|
||||
assert(! m_thread.joinable());
|
||||
} else {
|
||||
assert(m_state == STATE_IDLE);
|
||||
assert(m_thread.joinable());
|
||||
// Notify the worker thread to exit.
|
||||
m_state = STATE_EXIT;
|
||||
lck.unlock();
|
||||
m_condition.notify_one();
|
||||
// Wait until the worker thread exits.
|
||||
m_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
bool BackgroundSlicingProcess::start()
|
||||
{
|
||||
if (m_print->empty()) {
|
||||
if (!m_current_plate || !m_current_plate->is_slice_result_valid())
|
||||
// The print is empty (no object in Model, or all objects are out of the print bed).
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lck(m_mutex);
|
||||
if (m_state == STATE_INITIAL) {
|
||||
// The worker thread is not running yet. Start it.
|
||||
assert(! m_thread.joinable());
|
||||
m_thread = create_thread([this]{
|
||||
#ifdef _WIN32
|
||||
this->thread_proc_safe_seh_throw();
|
||||
#else // _WIN32
|
||||
this->thread_proc_safe();
|
||||
#endif // _WIN32
|
||||
});
|
||||
// Wait until the worker thread is ready to execute the background processing task.
|
||||
m_condition.wait(lck, [this](){ return m_state == STATE_IDLE; });
|
||||
}
|
||||
assert(m_state == STATE_IDLE || this->running());
|
||||
if (this->running())
|
||||
// The background processing thread is already running.
|
||||
return false;
|
||||
if (! this->idle())
|
||||
throw Slic3r::RuntimeError("Cannot start a background task, the worker thread is not idle.");
|
||||
m_state = STATE_STARTED;
|
||||
m_print->set_cancel_callback([this](){ this->stop_internal(); });
|
||||
lck.unlock();
|
||||
m_condition.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
// To be called on the UI thread.
|
||||
bool BackgroundSlicingProcess::stop()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< ", enter"<<std::endl;
|
||||
// m_print->state_mutex() shall NOT be held. Unfortunately there is no interface to test for it.
|
||||
std::unique_lock<std::mutex> lck(m_mutex);
|
||||
if (m_state == STATE_INITIAL) {
|
||||
// m_export_path.clear();
|
||||
return false;
|
||||
}
|
||||
// assert(this->running());
|
||||
if (m_state == STATE_STARTED || m_state == STATE_RUNNING) {
|
||||
// Cancel any task planned by the background thread on UI thread.
|
||||
cancel_ui_task(m_ui_task);
|
||||
m_print->cancel();
|
||||
// Wait until the background processing stops by being canceled.
|
||||
m_condition.wait(lck, [this](){ return m_state == STATE_CANCELED; });
|
||||
// In the "Canceled" state. Reset the state to "Idle".
|
||||
m_state = STATE_IDLE;
|
||||
m_print->set_cancel_callback([](){});
|
||||
} else if (m_state == STATE_FINISHED || m_state == STATE_CANCELED) {
|
||||
// In the "Finished" or "Canceled" state. Reset the state to "Idle".
|
||||
m_state = STATE_IDLE;
|
||||
m_print->set_cancel_callback([](){});
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< ", exit"<<std::endl;
|
||||
// m_export_path.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BackgroundSlicingProcess::reset()
|
||||
{
|
||||
bool stopped = this->stop();
|
||||
this->reset_export();
|
||||
//BBS: don't clear print for print is not owned by background slicing process anymore
|
||||
//do it in the part_plate
|
||||
//m_print->clear();
|
||||
this->invalidate_all_steps();
|
||||
return stopped;
|
||||
}
|
||||
|
||||
// To be called by Print::apply() on the UI thread through the Print::m_cancel_callback to stop the background
|
||||
// processing before changing any data of running or finalized milestones.
|
||||
// This function shall not trigger any UI update through the wxWidgets event.
|
||||
void BackgroundSlicingProcess::stop_internal()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< ", enter"<<std::endl;
|
||||
// m_print->state_mutex() shall be held. Unfortunately there is no interface to test for it.
|
||||
if (m_state == STATE_IDLE)
|
||||
// The worker thread is waiting on m_mutex/m_condition for wake up. The following lock of the mutex would block.
|
||||
return;
|
||||
std::unique_lock<std::mutex> lck(m_mutex);
|
||||
assert(m_state == STATE_STARTED || m_state == STATE_RUNNING || m_state == STATE_FINISHED || m_state == STATE_CANCELED);
|
||||
if (m_state == STATE_STARTED || m_state == STATE_RUNNING) {
|
||||
// Cancel any task planned by the background thread on UI thread.
|
||||
cancel_ui_task(m_ui_task);
|
||||
// At this point of time the worker thread may be blocking on m_print->state_mutex().
|
||||
// Set the print state to canceled before unlocking the state_mutex(), so when the worker thread wakes up,
|
||||
// it throws the CanceledException().
|
||||
m_print->cancel_internal();
|
||||
// Allow the worker thread to wake up if blocking on a milestone.
|
||||
m_print->state_mutex().unlock();
|
||||
// Wait until the background processing stops by being canceled.
|
||||
m_condition.wait(lck, [this](){ return m_state == STATE_CANCELED; });
|
||||
// Lock it back to be in a consistent state.
|
||||
m_print->state_mutex().lock();
|
||||
}
|
||||
// In the "Canceled" state. Reset the state to "Idle".
|
||||
m_state = STATE_IDLE;
|
||||
m_print->set_cancel_callback([](){});
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< ", exit"<<std::endl;
|
||||
}
|
||||
|
||||
// Execute task from background thread on the UI thread. Returns true if processed, false if cancelled.
|
||||
bool BackgroundSlicingProcess::execute_ui_task(std::function<void()> task)
|
||||
{
|
||||
bool running = false;
|
||||
if (m_mutex.try_lock()) {
|
||||
// Cancellation is either not in process, or already canceled and waiting for us to finish.
|
||||
// There must be no UI task planned.
|
||||
assert(! m_ui_task);
|
||||
if (! m_print->canceled()) {
|
||||
running = true;
|
||||
m_ui_task = std::make_shared<UITask>();
|
||||
}
|
||||
m_mutex.unlock();
|
||||
} else {
|
||||
// Cancellation is in process.
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
if (running) {
|
||||
std::shared_ptr<UITask> ctx = m_ui_task;
|
||||
GUI::wxGetApp().mainframe->m_plater->CallAfter([task, ctx]() {
|
||||
// Running on the UI thread, thus ctx->state does not need to be guarded with mutex against ::cancel_ui_task().
|
||||
assert(ctx->state == UITask::Planned || ctx->state == UITask::Canceled);
|
||||
if (ctx->state == UITask::Planned) {
|
||||
task();
|
||||
std::unique_lock<std::mutex> lck(ctx->mutex);
|
||||
ctx->state = UITask::Finished;
|
||||
}
|
||||
// Wake up the worker thread from the UI thread.
|
||||
ctx->condition.notify_all();
|
||||
});
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(ctx->mutex);
|
||||
ctx->condition.wait(lock, [&ctx]{ return ctx->state == UITask::Finished || ctx->state == UITask::Canceled; });
|
||||
}
|
||||
result = ctx->state == UITask::Finished;
|
||||
m_ui_task.reset();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// To be called on the UI thread from ::stop() and ::stop_internal().
|
||||
void BackgroundSlicingProcess::cancel_ui_task(std::shared_ptr<UITask> task)
|
||||
{
|
||||
if (task) {
|
||||
std::unique_lock<std::mutex> lck(task->mutex);
|
||||
task->state = UITask::Canceled;
|
||||
lck.unlock();
|
||||
task->condition.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
bool BackgroundSlicingProcess::empty() const
|
||||
{
|
||||
assert(m_print != nullptr);
|
||||
return m_print->empty();
|
||||
}
|
||||
|
||||
StringObjectException BackgroundSlicingProcess::validate(StringObjectException *warning, Polygons* collison_polygons, std::vector<std::pair<Polygon, float>>* height_polygons)
|
||||
{
|
||||
assert(m_print != nullptr);
|
||||
return m_print->validate(warning, collison_polygons, height_polygons);
|
||||
}
|
||||
|
||||
// Apply config over the print. Returns false, if the new config values caused any of the already
|
||||
// processed steps to be invalidated, therefore the task will need to be restarted.
|
||||
Print::ApplyStatus BackgroundSlicingProcess::apply(const Model &model, const DynamicPrintConfig &config)
|
||||
{
|
||||
assert(m_print != nullptr);
|
||||
assert(config.opt_enum<PrinterTechnology>("printer_technology") == m_print->technology());
|
||||
Print::ApplyStatus invalidated = m_print->apply(model, config);
|
||||
if ((invalidated & PrintBase::APPLY_STATUS_INVALIDATED) != 0 && m_print->technology() == ptFFF &&
|
||||
!m_fff_print->is_step_done(psGCodeExport)) {
|
||||
// Some FFF status was invalidated, and the G-code was not exported yet.
|
||||
// Let the G-code preview UI know that the final G-code preview is not valid.
|
||||
// In addition, this early memory deallocation reduces memory footprint.
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": invalide gcode result %1%, will reset soon")%m_gcode_result;
|
||||
if (m_gcode_result != nullptr)
|
||||
m_gcode_result->reset();
|
||||
}
|
||||
return invalidated;
|
||||
}
|
||||
|
||||
void BackgroundSlicingProcess::set_task(const PrintBase::TaskParams ¶ms)
|
||||
{
|
||||
assert(m_print != nullptr);
|
||||
m_print->set_task(params);
|
||||
}
|
||||
|
||||
// Set the output path of the G-code.
|
||||
void BackgroundSlicingProcess::schedule_export(const std::string &path, bool export_path_on_removable_media)
|
||||
{
|
||||
assert(m_export_path.empty());
|
||||
if (! m_export_path.empty())
|
||||
return;
|
||||
|
||||
// Guard against entering the export step before changing the export path.
|
||||
std::scoped_lock<std::mutex> lock(m_print->state_mutex());
|
||||
this->invalidate_step(bspsGCodeFinalize);
|
||||
m_export_path = path;
|
||||
m_export_path_on_removable_media = export_path_on_removable_media;
|
||||
}
|
||||
|
||||
void BackgroundSlicingProcess::reset_export()
|
||||
{
|
||||
assert(! this->running());
|
||||
if (! this->running()) {
|
||||
m_export_path.clear();
|
||||
m_export_path_on_removable_media = false;
|
||||
// invalidate_step expects the mutex to be locked.
|
||||
std::scoped_lock<std::mutex> lock(m_print->state_mutex());
|
||||
this->invalidate_step(bspsGCodeFinalize);
|
||||
}
|
||||
}
|
||||
|
||||
bool BackgroundSlicingProcess::set_step_started(BackgroundSlicingProcessStep step)
|
||||
{
|
||||
return m_step_state.set_started(step, m_print->state_mutex(), [this](){ this->throw_if_canceled(); });
|
||||
}
|
||||
|
||||
void BackgroundSlicingProcess::set_step_done(BackgroundSlicingProcessStep step)
|
||||
{
|
||||
m_step_state.set_done(step, m_print->state_mutex(), [this](){ this->throw_if_canceled(); });
|
||||
}
|
||||
|
||||
bool BackgroundSlicingProcess::is_step_done(BackgroundSlicingProcessStep step) const
|
||||
{
|
||||
return m_step_state.is_done(step, m_print->state_mutex());
|
||||
}
|
||||
|
||||
bool BackgroundSlicingProcess::invalidate_step(BackgroundSlicingProcessStep step)
|
||||
{
|
||||
bool invalidated = m_step_state.invalidate(step, [this](){ this->stop_internal(); });
|
||||
return invalidated;
|
||||
}
|
||||
|
||||
bool BackgroundSlicingProcess::invalidate_all_steps()
|
||||
{
|
||||
return m_step_state.invalidate_all([this](){ this->stop_internal(); });
|
||||
}
|
||||
|
||||
// G-code is generated in m_temp_output_path.
|
||||
// Optionally run a post-processing script on a copy of m_temp_output_path.
|
||||
// Copy the final G-code to target location (possibly a SD card, if it is a removable media, then verify that the file was written without an error).
|
||||
void BackgroundSlicingProcess::finalize_gcode()
|
||||
{
|
||||
//BBS: don't support running user-defined post-processing scripts
|
||||
//m_print->set_status(95, _utf8(L("Running post-processing scripts")));
|
||||
|
||||
// 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);
|
||||
std::string output_path = m_temp_output_path;
|
||||
|
||||
// Both output_path and export_path ar in-out parameters.
|
||||
// If post processed, output_path will differ from m_temp_output_path as run_post_process_scripts() will make a copy of the G-code to not
|
||||
// collide with the G-code viewer memory mapping of the unprocessed G-code. G-code viewer maps unprocessed G-code, because m_gcode_result
|
||||
// is calculated for the unprocessed G-code and it references lines in the memory mapped G-code file by line numbers.
|
||||
// export_path may be changed by the post-processing script as well if the post processing script decides so, see GH #6042.
|
||||
//BBS: don't support running post process scripts
|
||||
//bool post_processed = run_post_process_scripts(output_path, true, "File", export_path, m_fff_print->full_print_config());
|
||||
bool post_processed = false;
|
||||
auto remove_post_processed_temp_file = [post_processed, &output_path]() {
|
||||
if (post_processed)
|
||||
try {
|
||||
boost::filesystem::remove(output_path);
|
||||
} catch (const std::exception &ex) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Failed to remove temp file " << output_path << ": " << ex.what();
|
||||
}
|
||||
};
|
||||
|
||||
//FIXME localize the messages
|
||||
std::string error_message;
|
||||
int copy_ret_val = CopyFileResult::SUCCESS;
|
||||
try
|
||||
{
|
||||
copy_ret_val = copy_file(output_path, export_path, error_message, m_export_path_on_removable_media);
|
||||
remove_post_processed_temp_file();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
remove_post_processed_temp_file();
|
||||
throw Slic3r::ExportError(_utf8(L("Unknown error when export G-code.")));
|
||||
}
|
||||
switch (copy_ret_val) {
|
||||
case CopyFileResult::SUCCESS: break; // no error
|
||||
case CopyFileResult::FAIL_COPY_FILE:
|
||||
//throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?\nError message: %1%"))) % error_message).str());
|
||||
//break;
|
||||
case CopyFileResult::FAIL_FILES_DIFFERENT:
|
||||
//throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. There might be problem with target device, please try exporting again or using different device. The corrupted output G-code is at %1%.tmp."))) % export_path).str());
|
||||
//break;
|
||||
case CopyFileResult::FAIL_RENAMING:
|
||||
//throw Slic3r::ExportError((boost::format(_utf8(L("Renaming of the G-code after copying to the selected destination folder has failed. Current path is %1%.tmp. Please try exporting again."))) % export_path).str());
|
||||
//break;
|
||||
case CopyFileResult::FAIL_CHECK_ORIGIN_NOT_OPENED:
|
||||
//throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the original code at %1% couldn't be opened during copy check. The output G-code is at %2%.tmp."))) % output_path % export_path).str());
|
||||
//break;
|
||||
case CopyFileResult::FAIL_CHECK_TARGET_NOT_OPENED:
|
||||
//throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the exported code couldn't be opened during copy check. The output G-code is at %1%.tmp."))) % export_path).str());
|
||||
//break;
|
||||
default:
|
||||
BOOST_LOG_TRIVIAL(error) << "Fail code(" << (int)copy_ret_val << ") when copy "<<output_path<<" to " << export_path << ".";
|
||||
throw Slic3r::ExportError((boost::format(_utf8(L("Failed to save gcode file.\nError message: %1%.\nSource file %2%."))) % error_message % output_path).str());
|
||||
//throw Slic3r::ExportError(_utf8(L("Unknown error when export G-code.")));
|
||||
break;
|
||||
}
|
||||
|
||||
// BBS
|
||||
auto evt = new wxCommandEvent(m_event_export_finished_id, GUI::wxGetApp().mainframe->m_plater->GetId());
|
||||
wxString output_gcode_str = wxString::FromUTF8(export_path.c_str(), export_path.length());
|
||||
evt->SetString(output_gcode_str);
|
||||
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt);
|
||||
|
||||
// BBS: to be checked. Whether use export_path or output_path.
|
||||
gcode_add_line_number(export_path, m_fff_print->full_print_config());
|
||||
|
||||
m_print->set_status(100, (boost::format(_utf8(L("Succeed to export G-code to %1%"))) % export_path).str());
|
||||
}
|
||||
|
||||
// Executed by the background thread, to start a task on the UI thread.
|
||||
ThumbnailsList BackgroundSlicingProcess::render_thumbnails(const ThumbnailsParams ¶ms)
|
||||
{
|
||||
ThumbnailsList thumbnails;
|
||||
if (m_thumbnail_cb)
|
||||
this->execute_ui_task([this, ¶ms, &thumbnails](){ thumbnails = m_thumbnail_cb(params); });
|
||||
return thumbnails;
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
||||
299
src/slic3r/GUI/BackgroundSlicingProcess.hpp
Normal file
299
src/slic3r/GUI/BackgroundSlicingProcess.hpp
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
#ifndef slic3r_GUI_BackgroundSlicingProcess_hpp_
|
||||
#define slic3r_GUI_BackgroundSlicingProcess_hpp_
|
||||
|
||||
#include <string>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#include <wx/event.h>
|
||||
|
||||
#include "libslic3r/PrintBase.hpp"
|
||||
#include "libslic3r/GCode/ThumbnailData.hpp"
|
||||
#include "libslic3r/Format/SL1.hpp"
|
||||
#include "libslic3r/GCode/GCodeProcessor.hpp"
|
||||
#include "PartPlate.hpp"
|
||||
|
||||
namespace boost { namespace filesystem { class path; } }
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class DynamicPrintConfig;
|
||||
class Model;
|
||||
class SLAPrint;
|
||||
|
||||
class SlicingStatusEvent : public wxEvent
|
||||
{
|
||||
public:
|
||||
SlicingStatusEvent(wxEventType eventType, int winid, const PrintBase::SlicingStatus &status) :
|
||||
wxEvent(winid, eventType), status(std::move(status)) {}
|
||||
virtual wxEvent *Clone() const { return new SlicingStatusEvent(*this); }
|
||||
|
||||
PrintBase::SlicingStatus status;
|
||||
};
|
||||
|
||||
class SlicingProcessCompletedEvent : public wxEvent
|
||||
{
|
||||
public:
|
||||
enum StatusType {
|
||||
Finished,
|
||||
Cancelled,
|
||||
Error
|
||||
};
|
||||
|
||||
SlicingProcessCompletedEvent(wxEventType eventType, int winid, StatusType status, std::exception_ptr exception) :
|
||||
wxEvent(winid, eventType), m_status(status), m_exception(exception) {}
|
||||
virtual wxEvent* Clone() const { return new SlicingProcessCompletedEvent(*this); }
|
||||
|
||||
StatusType status() const { return m_status; }
|
||||
bool finished() const { return m_status == Finished; }
|
||||
bool success() const { return m_status == Finished; }
|
||||
bool cancelled() const { return m_status == Cancelled; }
|
||||
bool error() const { return m_status == Error; }
|
||||
// Unhandled error produced by stdlib or a Win32 structured exception, or unhandled Slic3r's own critical exception.
|
||||
bool critical_error() const;
|
||||
// Critical errors does invalidate plater except CopyFileError.
|
||||
bool invalidate_plater() const;
|
||||
// Only valid if error()
|
||||
void rethrow_exception() const { assert(this->error()); assert(m_exception); std::rethrow_exception(m_exception); }
|
||||
// Produce a human readable message to be displayed by a notification or a message box.
|
||||
// 2nd parameter defines whether the output should be displayed with a monospace font.
|
||||
std::pair<std::string, bool> format_error_message() const;
|
||||
|
||||
private:
|
||||
StatusType m_status;
|
||||
std::exception_ptr m_exception;
|
||||
};
|
||||
|
||||
//BBS: move it to plater.hpp
|
||||
//wxDEFINE_EVENT(EVT_SLICING_UPDATE, SlicingStatusEvent);
|
||||
|
||||
// Print step IDs for keeping track of the print state.
|
||||
enum BackgroundSlicingProcessStep {
|
||||
bspsGCodeFinalize, bspsCount,
|
||||
};
|
||||
|
||||
// Support for the GUI background processing (Slicing and G-code generation).
|
||||
// As of now this class is not declared in Slic3r::GUI due to the Perl bindings limits.
|
||||
class BackgroundSlicingProcess
|
||||
{
|
||||
public:
|
||||
BackgroundSlicingProcess();
|
||||
// Stop the background processing and finalize the bacgkround processing thread, remove temp files.
|
||||
~BackgroundSlicingProcess();
|
||||
|
||||
void set_fff_print(Print *print) { m_fff_print = print; }
|
||||
void set_sla_print(SLAPrint *print) { m_sla_print = print; m_sla_print->set_printer(&m_sla_archive); }
|
||||
void set_thumbnail_cb(ThumbnailsGeneratorCallback cb) { m_thumbnail_cb = cb; }
|
||||
void set_gcode_result(GCodeProcessorResult* result) { m_gcode_result = result; }
|
||||
|
||||
//BBS: add partplate related logic
|
||||
bool switch_print_preprocess();
|
||||
bool can_switch_print();
|
||||
void set_current_plate(GUI::PartPlate* plate) { m_current_plate = plate; }
|
||||
GUI::PartPlate* get_current_plate() { return m_current_plate; }
|
||||
GCodeProcessorResult* get_current_gcode_result() { return m_gcode_result;}
|
||||
|
||||
// The following wxCommandEvent will be sent to the UI thread / Plater window, when the slicing is finished
|
||||
// and the background processing will transition into G-code export.
|
||||
// The wxCommandEvent is sent to the UI thread asynchronously without waiting for the event to be processed.
|
||||
void set_slicing_completed_event(int event_id) { m_event_slicing_completed_id = event_id; }
|
||||
// The following wxCommandEvent will be sent to the UI thread / Plater window, when the G-code export is finished.
|
||||
// The wxCommandEvent is sent to the UI thread asynchronously without waiting for the event to be processed.
|
||||
void set_finished_event(int event_id) { m_event_finished_id = event_id; }
|
||||
// The following wxCommandEvent will be sent to the UI thread / Plater window, when the G-code is being exported to
|
||||
// specified path or uploaded.
|
||||
// The wxCommandEvent is sent to the UI thread asynchronously without waiting for the event to be processed.
|
||||
void set_export_began_event(int event_id) { m_event_export_began_id = event_id; }
|
||||
|
||||
// BBS
|
||||
void set_export_finished_event(int event_id) { m_event_export_finished_id = event_id; }
|
||||
|
||||
// Activate either m_fff_print or m_sla_print.
|
||||
// Return true if changed.
|
||||
bool select_technology(PrinterTechnology tech);
|
||||
|
||||
// Get the currently active printer technology.
|
||||
PrinterTechnology current_printer_technology() const;
|
||||
// Get the current print. It is either m_fff_print or m_sla_print.
|
||||
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();
|
||||
// Cancel the background processing. Returns false if the background processing was not running.
|
||||
// A stopped background processing may be restarted with start().
|
||||
bool stop();
|
||||
// Cancel the background processing and reset the print. Returns false if the background processing was not running.
|
||||
// Useful when the Model or configuration is being changed drastically.
|
||||
bool reset();
|
||||
|
||||
// Apply config over the print. Returns false, if the new config values caused any of the already
|
||||
// processed steps to be invalidated, therefore the task will need to be restarted.
|
||||
PrintBase::ApplyStatus apply(const Model &model, const DynamicPrintConfig &config);
|
||||
// After calling the apply() function, set_task() may be called to limit the task to be processed by process().
|
||||
// This is useful for calculating SLA supports for a single object only.
|
||||
void set_task(const PrintBase::TaskParams ¶ms);
|
||||
// After calling apply, the empty() call will report whether there is anything to slice.
|
||||
bool empty() const;
|
||||
// Validate the print. Returns an empty string if valid, returns an error message if invalid.
|
||||
// Call validate before calling start().
|
||||
StringObjectException validate(StringObjectException *warning = nullptr, Polygons* collison_polygons = nullptr, std::vector<std::pair<Polygon, float>>* height_polygons = nullptr);
|
||||
|
||||
// Set the export path of the G-code.
|
||||
// Once the path is set, the G-code
|
||||
void schedule_export(const std::string &path, bool export_path_on_removable_media);
|
||||
// Clear m_export_path.
|
||||
void reset_export();
|
||||
// Once the G-code export is scheduled, the apply() methods will do nothing.
|
||||
bool is_export_scheduled() const { return ! m_export_path.empty(); }
|
||||
|
||||
enum State {
|
||||
// m_thread is not running yet, or it did not reach the STATE_IDLE yet (it does not wait on the condition yet).
|
||||
STATE_INITIAL = 0,
|
||||
// m_thread is waiting for the task to execute.
|
||||
STATE_IDLE,
|
||||
STATE_STARTED,
|
||||
// m_thread is executing a task.
|
||||
STATE_RUNNING,
|
||||
// m_thread finished executing a task, and it is waiting until the UI thread picks up the results.
|
||||
STATE_FINISHED,
|
||||
// m_thread finished executing a task, the task has been canceled by the UI thread, therefore the UI thread will not be notified.
|
||||
STATE_CANCELED,
|
||||
// m_thread exited the loop and it is going to finish. The UI thread should join on m_thread.
|
||||
STATE_EXIT,
|
||||
STATE_EXITED,
|
||||
};
|
||||
State state() const { return m_state; }
|
||||
bool idle() const { return m_state == STATE_IDLE; }
|
||||
bool running() const { return m_state == STATE_STARTED || m_state == STATE_RUNNING || m_state == STATE_FINISHED || m_state == STATE_CANCELED; }
|
||||
// Returns true if the last step of the active print was finished with success.
|
||||
// The "finished" flag is reset by the apply() method, if it changes the state of the print.
|
||||
// This "finished" flag does not account for the final export of the output file (.gcode or zipped PNGs),
|
||||
// and it does not account for the OctoPrint scheduling.
|
||||
//BBS: improve the finished logic, also judge the m_gcode_result
|
||||
//bool finished() const { return m_print->finished(); }
|
||||
bool finished() const { return m_print->finished() && !m_gcode_result->moves.empty(); }
|
||||
|
||||
//BBS: add Plater to friend class
|
||||
//need to call stop_internal in ui thread
|
||||
friend class GUI::Plater;
|
||||
|
||||
private:
|
||||
void thread_proc();
|
||||
// Calls thread_proc(), catches all C++ exceptions and shows them using wxApp::OnUnhandledException().
|
||||
void thread_proc_safe() throw();
|
||||
#ifdef _WIN32
|
||||
// Wrapper for Win32 structured exceptions. Win32 structured exception blocks and C++ exception blocks cannot be mixed in the same function.
|
||||
// Catch a SEH exception and return its ID or zero if no SEH exception has been catched.
|
||||
unsigned long thread_proc_safe_seh() throw();
|
||||
// Calls thread_proc_safe_seh(), rethrows a Slic3r::HardCrash exception based on SEH exception
|
||||
// returned by thread_proc_safe_seh() and lets wxApp::OnUnhandledException() display it.
|
||||
void thread_proc_safe_seh_throw() throw();
|
||||
#endif // _WIN32
|
||||
void join_background_thread();
|
||||
// To be called by Print::apply() through the Print::m_cancel_callback to stop the background
|
||||
// processing before changing any data of running or finalized milestones.
|
||||
// This function shall not trigger any UI update through the wxWidgets event.
|
||||
void stop_internal();
|
||||
|
||||
// Helper to wrap the FFF slicing & G-code generation.
|
||||
void process_fff();
|
||||
|
||||
// Temporary: for mimicking the fff file export behavior with the raster output
|
||||
void process_sla();
|
||||
|
||||
// Call Print::process() and catch all exceptions into ex, thus no exception could be thrown
|
||||
// by this method. This exception behavior is required to combine C++ exceptions with Win32 SEH exceptions
|
||||
// on the same thread.
|
||||
void call_process(std::exception_ptr &ex) throw();
|
||||
|
||||
#ifdef _WIN32
|
||||
// Wrapper for Win32 structured exceptions. Win32 structured exception blocks and C++ exception blocks cannot be mixed in the same function.
|
||||
// Catch a SEH exception and return its ID or zero if no SEH exception has been catched.
|
||||
unsigned long call_process_seh(std::exception_ptr &ex) throw();
|
||||
// Calls call_process_seh(), rethrows a Slic3r::HardCrash exception based on SEH exception
|
||||
// returned by call_process_seh().
|
||||
void call_process_seh_throw(std::exception_ptr &ex) throw();
|
||||
#endif // _WIN32
|
||||
|
||||
// Currently active print. It is one of m_fff_print and m_sla_print.
|
||||
PrintBase *m_print = nullptr;
|
||||
// Non-owned pointers to Print instances.
|
||||
Print *m_fff_print = nullptr;
|
||||
SLAPrint *m_sla_print = nullptr;
|
||||
// Data structure, to which the G-code export writes its annotations.
|
||||
GCodeProcessorResult *m_gcode_result = nullptr;
|
||||
// Callback function, used to write thumbnails into gcode.
|
||||
ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr;
|
||||
SL1Archive m_sla_archive;
|
||||
// Temporary G-code, there is one defined for the BackgroundSlicingProcess, differentiated from the other processes by a process ID.
|
||||
std::string m_temp_output_path;
|
||||
// Output path provided by the user. The output path may be set even if the slicing is running,
|
||||
// but once set, it cannot be re-set.
|
||||
std::string m_export_path;
|
||||
bool m_export_path_on_removable_media = false;
|
||||
// Thread, on which the background processing is executed. The thread will always be present
|
||||
// and ready to execute the slicing process.
|
||||
boost::thread m_thread;
|
||||
// Mutex and condition variable to synchronize m_thread with the UI thread.
|
||||
std::mutex m_mutex;
|
||||
std::condition_variable m_condition;
|
||||
State m_state = STATE_INITIAL;
|
||||
|
||||
// For executing tasks from the background thread on UI thread synchronously (waiting for result) using wxWidgets CallAfter().
|
||||
// When the background proces is canceled, the UITask has to be invalidated as well, so that it will not be
|
||||
// executed on the UI thread referencing invalid data.
|
||||
struct UITask {
|
||||
enum State {
|
||||
Planned,
|
||||
Finished,
|
||||
Canceled,
|
||||
};
|
||||
State state = Planned;
|
||||
std::mutex mutex;
|
||||
std::condition_variable condition;
|
||||
};
|
||||
// Only one UI task may be planned by the background thread to be executed on the UI thread, as the background
|
||||
// thread is blocking until the UI thread calculation finishes.
|
||||
std::shared_ptr<UITask> m_ui_task;
|
||||
|
||||
//BBS: partplate related
|
||||
GUI::PartPlate* m_current_plate;
|
||||
PrinterTechnology m_printer_tech = ptUnknown;
|
||||
|
||||
PrintState<BackgroundSlicingProcessStep, bspsCount> m_step_state;
|
||||
bool set_step_started(BackgroundSlicingProcessStep step);
|
||||
void set_step_done(BackgroundSlicingProcessStep step);
|
||||
bool is_step_done(BackgroundSlicingProcessStep step) const;
|
||||
bool invalidate_step(BackgroundSlicingProcessStep step);
|
||||
bool invalidate_all_steps();
|
||||
// If the background processing stop was requested, throw CanceledException.
|
||||
void throw_if_canceled() const { if (m_print->canceled()) throw CanceledException(); }
|
||||
void finalize_gcode();
|
||||
// To be executed at the background thread.
|
||||
ThumbnailsList render_thumbnails(const ThumbnailsParams ¶ms);
|
||||
// Execute task from background thread on the UI thread synchronously. Returns true if processed, false if cancelled before executing the task.
|
||||
bool execute_ui_task(std::function<void()> task);
|
||||
// To be called from inside m_mutex to cancel a planned UI task.
|
||||
static void cancel_ui_task(std::shared_ptr<BackgroundSlicingProcess::UITask> task);
|
||||
|
||||
// wxWidgets command ID to be sent to the plater to inform that the slicing is finished, and the G-code export will continue.
|
||||
int m_event_slicing_completed_id = 0;
|
||||
// wxWidgets command ID to be sent to the plater to inform that the task finished.
|
||||
int m_event_finished_id = 0;
|
||||
// wxWidgets command ID to be sent to the plater to inform that the G-code is being exported.
|
||||
int m_event_export_began_id = 0;
|
||||
// wxWidgets command ID to be sent to the plater to inform that the G-code is exported end.
|
||||
int m_event_export_finished_id = 0;
|
||||
|
||||
};
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_GUI_BackgroundSlicingProcess_hpp_ */
|
||||
30
src/slic3r/GUI/BambuPlayer/BambuPlayer.h
Normal file
30
src/slic3r/GUI/BambuPlayer/BambuPlayer.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// BambuPlayer.h
|
||||
// BambuPlayer
|
||||
//
|
||||
// Created by cmguo on 2021/12/6.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AVFoundation/AVSampleBufferDisplayLayer.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface BambuPlayer : NSObject
|
||||
|
||||
+ (void) initialize;
|
||||
|
||||
- (instancetype) initWithDisplayLayer: (AVSampleBufferDisplayLayer*) layer;
|
||||
- (instancetype) initWithImageView: (NSView*) view;
|
||||
- (int) open: (char const *) url;
|
||||
- (NSSize) videoSize;
|
||||
- (int) play;
|
||||
- (void) stop;
|
||||
- (void) close;
|
||||
|
||||
- (void) setLogger: (void (*)(void const * context, int level, char const * msg)) logger withContext: (void const *) context;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
595
src/slic3r/GUI/BedShapeDialog.cpp
Normal file
595
src/slic3r/GUI/BedShapeDialog.cpp
Normal file
|
|
@ -0,0 +1,595 @@
|
|||
#include "BedShapeDialog.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "OptionsGroup.hpp"
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/numformatter.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/statbox.h>
|
||||
#include <wx/tooltip.h>
|
||||
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/Polygon.hpp"
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
BedShape::BedShape(const ConfigOptionPoints& points)
|
||||
{
|
||||
m_build_volume = { points.values, 0. };
|
||||
}
|
||||
|
||||
static std::string get_option_label(BedShape::Parameter param)
|
||||
{
|
||||
switch (param) {
|
||||
case BedShape::Parameter::RectSize : return L("Size");
|
||||
case BedShape::Parameter::RectOrigin: return L("Origin");
|
||||
case BedShape::Parameter::Diameter : return L("Diameter");
|
||||
default: assert(false); return {};
|
||||
}
|
||||
}
|
||||
|
||||
void BedShape::append_option_line(ConfigOptionsGroupShp optgroup, Parameter param)
|
||||
{
|
||||
ConfigOptionDef def;
|
||||
t_config_option_key key;
|
||||
switch (param) {
|
||||
case Parameter::RectSize:
|
||||
def.type = coPoints;
|
||||
def.set_default_value(new ConfigOptionPoints{ Vec2d(200, 200) });
|
||||
def.min = 0;
|
||||
def.max = 1200;
|
||||
def.label = get_option_label(param);
|
||||
def.tooltip = L("Size in X and Y of the rectangular plate.");
|
||||
key = "rect_size";
|
||||
break;
|
||||
case Parameter::RectOrigin:
|
||||
def.type = coPoints;
|
||||
def.set_default_value(new ConfigOptionPoints{ Vec2d(0, 0) });
|
||||
def.min = -600;
|
||||
def.max = 600;
|
||||
def.label = get_option_label(param);
|
||||
def.tooltip = L("Distance of the 0,0 G-code coordinate from the front left corner of the rectangle.");
|
||||
key = "rect_origin";
|
||||
break;
|
||||
case Parameter::Diameter:
|
||||
def.type = coFloat;
|
||||
def.set_default_value(new ConfigOptionFloat(200));
|
||||
def.sidetext = L("mm");
|
||||
def.label = get_option_label(param);
|
||||
def.tooltip = L("Diameter of the print bed. It is assumed that origin (0,0) is located in the center.");
|
||||
key = "diameter";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
optgroup->append_single_option_line({ def, std::move(key) });
|
||||
}
|
||||
|
||||
wxString BedShape::get_name(PageType type)
|
||||
{
|
||||
switch (type) {
|
||||
case PageType::Rectangle: return _L("Rectangular");
|
||||
case PageType::Circle: return _L("Circular");
|
||||
case PageType::Custom: return _L("Custom");
|
||||
}
|
||||
// make visual studio happy
|
||||
assert(false);
|
||||
return {};
|
||||
}
|
||||
|
||||
BedShape::PageType BedShape::get_page_type()
|
||||
{
|
||||
switch (m_build_volume.type()) {
|
||||
case BuildVolume::Type::Rectangle:
|
||||
case BuildVolume::Type::Invalid: return PageType::Rectangle;
|
||||
case BuildVolume::Type::Circle: return PageType::Circle;
|
||||
case BuildVolume::Type::Convex:
|
||||
case BuildVolume::Type::Custom: return PageType::Custom;
|
||||
}
|
||||
// make visual studio happy
|
||||
assert(false);
|
||||
return PageType::Rectangle;
|
||||
}
|
||||
|
||||
wxString BedShape::get_full_name_with_params()
|
||||
{
|
||||
wxString out = _L("Shape") + ": " + get_name(this->get_page_type());
|
||||
switch (m_build_volume.type()) {
|
||||
case BuildVolume::Type::Circle:
|
||||
out += "\n" + _L(get_option_label(Parameter::Diameter)) + ": [" + double_to_string(2. * unscaled<double>(m_build_volume.circle().radius)) + "]";
|
||||
break;
|
||||
default:
|
||||
// rectangle, convex, concave...
|
||||
out += "\n" + _(get_option_label(Parameter::RectSize)) + ": [" + ConfigOptionPoint(to_2d(m_build_volume.bounding_volume().size())).serialize() + "]";
|
||||
out += "\n" + _(get_option_label(Parameter::RectOrigin)) + ": [" + ConfigOptionPoint(- to_2d(m_build_volume.bounding_volume().min)).serialize() + "]";
|
||||
break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void BedShape::apply_optgroup_values(ConfigOptionsGroupShp optgroup)
|
||||
{
|
||||
switch (m_build_volume.type()) {
|
||||
case BuildVolume::Type::Circle:
|
||||
optgroup->set_value("diameter", double_to_string(2. * unscaled<double>(m_build_volume.circle().radius)));
|
||||
break;
|
||||
default:
|
||||
// rectangle, convex, concave...
|
||||
optgroup->set_value("rect_size" , new ConfigOptionPoints{ to_2d(m_build_volume.bounding_volume().size()) });
|
||||
optgroup->set_value("rect_origin" , new ConfigOptionPoints{ - to_2d(m_build_volume.bounding_volume().min) });
|
||||
}
|
||||
}
|
||||
|
||||
void BedShapeDialog::build_dialog(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model)
|
||||
{
|
||||
SetFont(wxGetApp().normal_font());
|
||||
|
||||
m_panel = new BedShapePanel(this);
|
||||
m_panel->build_panel(default_pt, custom_texture, custom_model);
|
||||
|
||||
auto main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
main_sizer->Add(m_panel, 1, wxEXPAND);
|
||||
main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10);
|
||||
|
||||
wxGetApp().UpdateDlgDarkUI(this, true);
|
||||
|
||||
SetSizer(main_sizer);
|
||||
SetMinSize(GetSize());
|
||||
main_sizer->SetSizeHints(this);
|
||||
|
||||
this->Bind(wxEVT_CLOSE_WINDOW, ([this](wxCloseEvent& evt) {
|
||||
EndModal(wxID_CANCEL);
|
||||
}));
|
||||
}
|
||||
|
||||
void BedShapeDialog::on_dpi_changed(const wxRect &suggested_rect)
|
||||
{
|
||||
const int& em = em_unit();
|
||||
m_panel->m_shape_options_book->SetMinSize(wxSize(25 * em, -1));
|
||||
|
||||
for (auto og : m_panel->m_optgroups)
|
||||
og->msw_rescale();
|
||||
|
||||
const wxSize& size = wxSize(50 * em, -1);
|
||||
|
||||
SetMinSize(size);
|
||||
SetSize(size);
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
const std::string BedShapePanel::NONE = "None";
|
||||
const std::string BedShapePanel::EMPTY_STRING = "";
|
||||
|
||||
void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model)
|
||||
{
|
||||
wxGetApp().UpdateDarkUI(this);
|
||||
m_shape = default_pt.values;
|
||||
m_custom_texture = custom_texture.value.empty() ? NONE : custom_texture.value;
|
||||
m_custom_model = custom_model.value.empty() ? NONE : custom_model.value;
|
||||
|
||||
auto sbsizer = new wxStaticBoxSizer(wxVERTICAL, this, _L("Shape"));
|
||||
sbsizer->GetStaticBox()->SetFont(wxGetApp().bold_font());
|
||||
wxGetApp().UpdateDarkUI(sbsizer->GetStaticBox());
|
||||
|
||||
// shape options
|
||||
m_shape_options_book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition, wxSize(25*wxGetApp().em_unit(), -1), wxCHB_TOP);
|
||||
wxGetApp().UpdateDarkUI(m_shape_options_book->GetChoiceCtrl());
|
||||
|
||||
sbsizer->Add(m_shape_options_book);
|
||||
|
||||
auto optgroup = init_shape_options_page(BedShape::get_name(BedShape::PageType::Rectangle));
|
||||
BedShape::append_option_line(optgroup, BedShape::Parameter::RectSize);
|
||||
BedShape::append_option_line(optgroup, BedShape::Parameter::RectOrigin);
|
||||
activate_options_page(optgroup);
|
||||
|
||||
optgroup = init_shape_options_page(BedShape::get_name(BedShape::PageType::Circle));
|
||||
BedShape::append_option_line(optgroup, BedShape::Parameter::Diameter);
|
||||
activate_options_page(optgroup);
|
||||
|
||||
optgroup = init_shape_options_page(BedShape::get_name(BedShape::PageType::Custom));
|
||||
|
||||
Line line{ "", "" };
|
||||
line.full_width = 1;
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
wxButton* shape_btn = new wxButton(parent, wxID_ANY, _L("Load shape from STL..."));
|
||||
wxSizer* shape_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
shape_sizer->Add(shape_btn, 1, wxEXPAND);
|
||||
|
||||
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
sizer->Add(shape_sizer, 1, wxEXPAND);
|
||||
|
||||
shape_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) {
|
||||
load_stl();
|
||||
});
|
||||
|
||||
return sizer;
|
||||
};
|
||||
optgroup->append_line(line);
|
||||
activate_options_page(optgroup);
|
||||
|
||||
wxPanel* texture_panel = init_texture_panel();
|
||||
wxPanel* model_panel = init_model_panel();
|
||||
|
||||
Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, ([this](wxCommandEvent& e) { update_shape(); }));
|
||||
|
||||
// right pane with preview canvas
|
||||
m_canvas = new Bed_2D(this);
|
||||
m_canvas->Bind(wxEVT_PAINT, [this](wxPaintEvent& e) { m_canvas->repaint(m_shape); });
|
||||
m_canvas->Bind(wxEVT_SIZE, [this](wxSizeEvent& e) { m_canvas->Refresh(); });
|
||||
|
||||
wxSizer* left_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
left_sizer->Add(sbsizer, 0, wxEXPAND);
|
||||
left_sizer->Add(texture_panel, 1, wxEXPAND);
|
||||
left_sizer->Add(model_panel, 1, wxEXPAND);
|
||||
|
||||
wxSizer* top_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
top_sizer->Add(left_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 10);
|
||||
top_sizer->Add(m_canvas, 1, wxEXPAND | wxALL, 10);
|
||||
|
||||
SetSizerAndFit(top_sizer);
|
||||
|
||||
set_shape(default_pt);
|
||||
update_preview();
|
||||
}
|
||||
|
||||
// Called from the constructor.
|
||||
// Create a panel for a rectangular / circular / custom bed shape.
|
||||
ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(const wxString& title)
|
||||
{
|
||||
wxPanel* panel = new wxPanel(m_shape_options_book);
|
||||
ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(panel, _L("Settings"));
|
||||
|
||||
optgroup->label_width = 10;
|
||||
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
|
||||
update_shape();
|
||||
};
|
||||
|
||||
m_optgroups.push_back(optgroup);
|
||||
// panel->SetSizerAndFit(optgroup->sizer);
|
||||
m_shape_options_book->AddPage(panel, title);
|
||||
|
||||
return optgroup;
|
||||
}
|
||||
|
||||
void BedShapePanel::activate_options_page(ConfigOptionsGroupShp options_group)
|
||||
{
|
||||
options_group->activate();
|
||||
options_group->parent()->SetSizerAndFit(options_group->sizer);
|
||||
}
|
||||
|
||||
wxPanel* BedShapePanel::init_texture_panel()
|
||||
{
|
||||
wxPanel* panel = new wxPanel(this);
|
||||
wxGetApp().UpdateDarkUI(panel, true);
|
||||
ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(panel, _L("Texture"));
|
||||
|
||||
optgroup->label_width = 10;
|
||||
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
|
||||
update_shape();
|
||||
};
|
||||
|
||||
Line line{ "", "" };
|
||||
line.full_width = 1;
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
wxButton* load_btn = new wxButton(parent, wxID_ANY, _L("Load..."));
|
||||
wxSizer* load_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
load_sizer->Add(load_btn, 1, wxEXPAND);
|
||||
|
||||
wxStaticText* filename_lbl = new wxStaticText(parent, wxID_ANY, _(NONE));
|
||||
|
||||
wxSizer* filename_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
filename_sizer->Add(filename_lbl, 1, wxEXPAND);
|
||||
|
||||
wxButton* remove_btn = new wxButton(parent, wxID_ANY, _L("Remove"));
|
||||
wxSizer* remove_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
remove_sizer->Add(remove_btn, 1, wxEXPAND);
|
||||
|
||||
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
sizer->Add(filename_sizer, 1, wxEXPAND);
|
||||
sizer->Add(load_sizer, 1, wxEXPAND);
|
||||
sizer->Add(remove_sizer, 1, wxEXPAND | wxTOP, 2);
|
||||
|
||||
load_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) { load_texture(); }));
|
||||
remove_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) {
|
||||
m_custom_texture = NONE;
|
||||
update_shape();
|
||||
}));
|
||||
|
||||
filename_lbl->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e) {
|
||||
e.SetText(_(boost::filesystem::path(m_custom_texture).filename().string()));
|
||||
wxStaticText* lbl = dynamic_cast<wxStaticText*>(e.GetEventObject());
|
||||
if (lbl != nullptr) {
|
||||
bool exists = (m_custom_texture == NONE) || boost::filesystem::exists(m_custom_texture);
|
||||
lbl->SetForegroundColour(exists ? /*wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)*/wxGetApp().get_label_clr_default() : wxColor(*wxRED));
|
||||
|
||||
wxString tooltip_text = "";
|
||||
if (m_custom_texture != NONE) {
|
||||
if (!exists)
|
||||
tooltip_text += _L("Not found:") + " ";
|
||||
|
||||
tooltip_text += _(m_custom_texture);
|
||||
}
|
||||
|
||||
wxToolTip* tooltip = lbl->GetToolTip();
|
||||
if ((tooltip == nullptr) || (tooltip->GetTip() != tooltip_text))
|
||||
lbl->SetToolTip(tooltip_text);
|
||||
}
|
||||
}));
|
||||
|
||||
remove_btn->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e) { e.Enable(m_custom_texture != NONE); }));
|
||||
|
||||
return sizer;
|
||||
};
|
||||
optgroup->append_line(line);
|
||||
optgroup->activate();
|
||||
|
||||
panel->SetSizerAndFit(optgroup->sizer);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
wxPanel* BedShapePanel::init_model_panel()
|
||||
{
|
||||
wxPanel* panel = new wxPanel(this);
|
||||
wxGetApp().UpdateDarkUI(panel, true);
|
||||
ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(panel, _L("Model"));
|
||||
|
||||
optgroup->label_width = 10;
|
||||
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
|
||||
update_shape();
|
||||
};
|
||||
|
||||
Line line{ "", "" };
|
||||
line.full_width = 1;
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
wxButton* load_btn = new wxButton(parent, wxID_ANY, _L("Load..."));
|
||||
wxSizer* load_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
load_sizer->Add(load_btn, 1, wxEXPAND);
|
||||
|
||||
wxStaticText* filename_lbl = new wxStaticText(parent, wxID_ANY, _(NONE));
|
||||
wxSizer* filename_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
filename_sizer->Add(filename_lbl, 1, wxEXPAND);
|
||||
|
||||
wxButton* remove_btn = new wxButton(parent, wxID_ANY, _L("Remove"));
|
||||
wxSizer* remove_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
remove_sizer->Add(remove_btn, 1, wxEXPAND);
|
||||
|
||||
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
sizer->Add(filename_sizer, 1, wxEXPAND);
|
||||
sizer->Add(load_sizer, 1, wxEXPAND);
|
||||
sizer->Add(remove_sizer, 1, wxEXPAND | wxTOP, 2);
|
||||
|
||||
load_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) { load_model(); }));
|
||||
|
||||
remove_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) {
|
||||
m_custom_model = NONE;
|
||||
update_shape();
|
||||
}));
|
||||
|
||||
filename_lbl->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e) {
|
||||
e.SetText(_(boost::filesystem::path(m_custom_model).filename().string()));
|
||||
wxStaticText* lbl = dynamic_cast<wxStaticText*>(e.GetEventObject());
|
||||
if (lbl != nullptr) {
|
||||
bool exists = (m_custom_model == NONE) || boost::filesystem::exists(m_custom_model);
|
||||
lbl->SetForegroundColour(exists ? /*wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)*/wxGetApp().get_label_clr_default() : wxColor(*wxRED));
|
||||
|
||||
wxString tooltip_text = "";
|
||||
if (m_custom_model != NONE) {
|
||||
if (!exists)
|
||||
tooltip_text += _L("Not found:") + " ";
|
||||
|
||||
tooltip_text += _(m_custom_model);
|
||||
}
|
||||
|
||||
wxToolTip* tooltip = lbl->GetToolTip();
|
||||
if ((tooltip == nullptr) || (tooltip->GetTip() != tooltip_text))
|
||||
lbl->SetToolTip(tooltip_text);
|
||||
}
|
||||
}));
|
||||
|
||||
remove_btn->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e) { e.Enable(m_custom_model != NONE); }));
|
||||
|
||||
return sizer;
|
||||
};
|
||||
optgroup->append_line(line);
|
||||
optgroup->activate();
|
||||
|
||||
panel->SetSizerAndFit(optgroup->sizer);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
// Called from the constructor.
|
||||
// Set the initial bed shape from a list of points.
|
||||
// Deduce the bed shape type(rect, circle, custom)
|
||||
// This routine shall be smart enough if the user messes up
|
||||
// with the list of points in the ini file directly.
|
||||
void BedShapePanel::set_shape(const ConfigOptionPoints& points)
|
||||
{
|
||||
BedShape shape(points);
|
||||
|
||||
m_shape_options_book->SetSelection(int(shape.get_page_type()));
|
||||
shape.apply_optgroup_values(m_optgroups[int(shape.get_page_type())]);
|
||||
|
||||
// Copy the polygon to the canvas, make a copy of the array, if custom shape is selected
|
||||
if (shape.is_custom())
|
||||
m_loaded_shape = points.values;
|
||||
|
||||
update_shape();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void BedShapePanel::update_preview()
|
||||
{
|
||||
if (m_canvas) m_canvas->Refresh();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
// Update the bed shape from the dialog fields.
|
||||
void BedShapePanel::update_shape()
|
||||
{
|
||||
auto page_idx = m_shape_options_book->GetSelection();
|
||||
auto opt_group = m_optgroups[page_idx];
|
||||
|
||||
switch (static_cast<BedShape::PageType>(page_idx)) {
|
||||
case BedShape::PageType::Rectangle:
|
||||
{
|
||||
Vec2d rect_size(Vec2d::Zero());
|
||||
Vec2d rect_origin(Vec2d::Zero());
|
||||
|
||||
try { rect_size = boost::any_cast<Vec2d>(opt_group->get_value("rect_size")); }
|
||||
catch (const std::exception& /* e */) { return; }
|
||||
|
||||
try { rect_origin = boost::any_cast<Vec2d>(opt_group->get_value("rect_origin")); }
|
||||
catch (const std::exception & /* e */) { return; }
|
||||
|
||||
auto x = rect_size(0);
|
||||
auto y = rect_size(1);
|
||||
// empty strings or '-' or other things
|
||||
if (x == 0 || y == 0) return;
|
||||
double x0 = 0.0;
|
||||
double y0 = 0.0;
|
||||
double x1 = x;
|
||||
double y1 = y;
|
||||
|
||||
auto dx = rect_origin(0);
|
||||
auto dy = rect_origin(1);
|
||||
|
||||
x0 -= dx;
|
||||
x1 -= dx;
|
||||
y0 -= dy;
|
||||
y1 -= dy;
|
||||
m_shape = { Vec2d(x0, y0),
|
||||
Vec2d(x1, y0),
|
||||
Vec2d(x1, y1),
|
||||
Vec2d(x0, y1) };
|
||||
break;
|
||||
}
|
||||
case BedShape::PageType::Circle:
|
||||
{
|
||||
double diameter;
|
||||
try { diameter = boost::any_cast<double>(opt_group->get_value("diameter")); }
|
||||
catch (const std::exception & /* e */) { return; }
|
||||
|
||||
if (diameter == 0.0) return ;
|
||||
auto r = diameter / 2;
|
||||
auto twopi = 2 * PI;
|
||||
// Don't change this value without adjusting BuildVolume constructor detecting circle diameter!
|
||||
auto edges = 72;
|
||||
std::vector<Vec2d> points;
|
||||
for (int i = 1; i <= edges; ++i) {
|
||||
auto angle = i * twopi / edges;
|
||||
points.push_back(Vec2d(r*cos(angle), r*sin(angle)));
|
||||
}
|
||||
m_shape = points;
|
||||
break;
|
||||
}
|
||||
case BedShape::PageType::Custom:
|
||||
m_shape = m_loaded_shape;
|
||||
break;
|
||||
}
|
||||
|
||||
update_preview();
|
||||
}
|
||||
|
||||
// Loads an stl file, projects it to the XY plane and calculates a polygon.
|
||||
void BedShapePanel::load_stl()
|
||||
{
|
||||
wxFileDialog dialog(this, _L("Choose an STL file to import bed shape from:"), "", "", file_wildcards(FT_STL), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
if (dialog.ShowModal() != wxID_OK)
|
||||
return;
|
||||
|
||||
std::string file_name = dialog.GetPath().ToUTF8().data();
|
||||
if (!boost::algorithm::iends_with(file_name, ".stl")) {
|
||||
show_error(this, _L("Invalid file format."));
|
||||
return;
|
||||
}
|
||||
|
||||
wxBusyCursor wait;
|
||||
|
||||
Model model;
|
||||
try {
|
||||
model = Model::read_from_file(file_name);
|
||||
}
|
||||
catch (std::exception &) {
|
||||
show_error(this, _L("Error! Invalid model"));
|
||||
return;
|
||||
}
|
||||
|
||||
auto mesh = model.mesh();
|
||||
auto expolygons = mesh.horizontal_projection();
|
||||
|
||||
if (expolygons.size() == 0) {
|
||||
show_error(this, _L("The selected file contains no geometry."));
|
||||
return;
|
||||
}
|
||||
if (expolygons.size() > 1) {
|
||||
show_error(this, _L("The selected file contains several disjoint areas. This is not supported."));
|
||||
return;
|
||||
}
|
||||
|
||||
auto polygon = expolygons[0].contour;
|
||||
std::vector<Vec2d> points;
|
||||
for (auto pt : polygon.points)
|
||||
points.push_back(unscale(pt));
|
||||
|
||||
m_loaded_shape = points;
|
||||
update_shape();
|
||||
}
|
||||
|
||||
void BedShapePanel::load_texture()
|
||||
{
|
||||
wxFileDialog dialog(this, _L("Choose a file to import bed texture from (PNG/SVG):"), "", "",
|
||||
file_wildcards(FT_TEX), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
|
||||
if (dialog.ShowModal() != wxID_OK)
|
||||
return;
|
||||
|
||||
m_custom_texture = NONE;
|
||||
|
||||
std::string file_name = dialog.GetPath().ToUTF8().data();
|
||||
if (!boost::algorithm::iends_with(file_name, ".png") && !boost::algorithm::iends_with(file_name, ".svg")) {
|
||||
show_error(this, _L("Invalid file format."));
|
||||
return;
|
||||
}
|
||||
|
||||
wxBusyCursor wait;
|
||||
|
||||
m_custom_texture = file_name;
|
||||
update_shape();
|
||||
}
|
||||
|
||||
void BedShapePanel::load_model()
|
||||
{
|
||||
wxFileDialog dialog(this, _L("Choose an STL file to import bed model from:"), "", "",
|
||||
file_wildcards(FT_STL), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
|
||||
if (dialog.ShowModal() != wxID_OK)
|
||||
return;
|
||||
|
||||
m_custom_model = NONE;
|
||||
|
||||
std::string file_name = dialog.GetPath().ToUTF8().data();
|
||||
if (!boost::algorithm::iends_with(file_name, ".stl")) {
|
||||
show_error(this, _L("Invalid file format."));
|
||||
return;
|
||||
}
|
||||
|
||||
wxBusyCursor wait;
|
||||
|
||||
m_custom_model = file_name;
|
||||
update_shape();
|
||||
}
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
112
src/slic3r/GUI/BedShapeDialog.hpp
Normal file
112
src/slic3r/GUI/BedShapeDialog.hpp
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
#ifndef slic3r_BedShapeDialog_hpp_
|
||||
#define slic3r_BedShapeDialog_hpp_
|
||||
// The bed shape dialog.
|
||||
// The dialog opens from Print Settins tab->Bed Shape : Set...
|
||||
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "2DBed.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include <libslic3r/BuildVolume.hpp>
|
||||
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/choicebk.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
class ConfigOptionsGroup;
|
||||
|
||||
using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>;
|
||||
|
||||
struct BedShape
|
||||
{
|
||||
enum class PageType {
|
||||
Rectangle,
|
||||
Circle,
|
||||
Custom
|
||||
};
|
||||
|
||||
enum class Parameter {
|
||||
RectSize,
|
||||
RectOrigin,
|
||||
Diameter
|
||||
};
|
||||
|
||||
BedShape(const ConfigOptionPoints& points);
|
||||
|
||||
bool is_custom() { return m_build_volume.type() == BuildVolume::Type::Convex || m_build_volume.type() == BuildVolume::Type::Custom; }
|
||||
|
||||
static void append_option_line(ConfigOptionsGroupShp optgroup, Parameter param);
|
||||
static wxString get_name(PageType type);
|
||||
|
||||
PageType get_page_type();
|
||||
|
||||
wxString get_full_name_with_params();
|
||||
void apply_optgroup_values(ConfigOptionsGroupShp optgroup);
|
||||
|
||||
private:
|
||||
BuildVolume m_build_volume;
|
||||
};
|
||||
|
||||
class BedShapePanel : public wxPanel
|
||||
{
|
||||
static const std::string NONE;
|
||||
static const std::string EMPTY_STRING;
|
||||
|
||||
Bed_2D* m_canvas;
|
||||
std::vector<Vec2d> m_shape;
|
||||
std::vector<Vec2d> m_loaded_shape;
|
||||
std::string m_custom_texture;
|
||||
std::string m_custom_model;
|
||||
|
||||
public:
|
||||
BedShapePanel(wxWindow* parent) : wxPanel(parent, wxID_ANY), m_custom_texture(NONE), m_custom_model(NONE) {}
|
||||
|
||||
void build_panel(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model);
|
||||
|
||||
// Returns the resulting bed shape polygon. This value will be stored to the ini file.
|
||||
const std::vector<Vec2d>& get_shape() const { return m_shape; }
|
||||
const std::string& get_custom_texture() const { return (m_custom_texture != NONE) ? m_custom_texture : EMPTY_STRING; }
|
||||
const std::string& get_custom_model() const { return (m_custom_model != NONE) ? m_custom_model : EMPTY_STRING; }
|
||||
|
||||
private:
|
||||
ConfigOptionsGroupShp init_shape_options_page(const wxString& title);
|
||||
void activate_options_page(ConfigOptionsGroupShp options_group);
|
||||
wxPanel* init_texture_panel();
|
||||
wxPanel* init_model_panel();
|
||||
void set_shape(const ConfigOptionPoints& points);
|
||||
void update_preview();
|
||||
void update_shape();
|
||||
void load_stl();
|
||||
void load_texture();
|
||||
void load_model();
|
||||
|
||||
wxChoicebook* m_shape_options_book;
|
||||
std::vector <ConfigOptionsGroupShp> m_optgroups;
|
||||
|
||||
friend class BedShapeDialog;
|
||||
};
|
||||
|
||||
class BedShapeDialog : public DPIDialog
|
||||
{
|
||||
BedShapePanel* m_panel;
|
||||
public:
|
||||
BedShapeDialog(wxWindow* parent) : DPIDialog(parent, wxID_ANY, _(L("Bed Shape")),
|
||||
wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {}
|
||||
|
||||
void build_dialog(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model);
|
||||
|
||||
const std::vector<Vec2d>& get_shape() const { return m_panel->get_shape(); }
|
||||
const std::string& get_custom_texture() const { return m_panel->get_custom_texture(); }
|
||||
const std::string& get_custom_model() const { return m_panel->get_custom_model(); }
|
||||
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect &suggested_rect) override;
|
||||
};
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
|
||||
|
||||
#endif /* slic3r_BedShapeDialog_hpp_ */
|
||||
465
src/slic3r/GUI/BindDialog.cpp
Normal file
465
src/slic3r/GUI/BindDialog.cpp
Normal file
|
|
@ -0,0 +1,465 @@
|
|||
#include "BindDialog.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/statbox.h>
|
||||
#include "wx/evtloop.h"
|
||||
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/Polygon.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "Plater.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
BindMachineDilaog::BindMachineDilaog(Plater *plater /*= nullptr*/)
|
||||
: DPIDialog(static_cast<wxWindow *>(wxGetApp().mainframe), wxID_ANY, _L("Log in printer"), wxDefaultPosition, wxDefaultSize, wxCAPTION)
|
||||
{
|
||||
std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico") % resources_dir()).str();
|
||||
SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO));
|
||||
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
wxBoxSizer *m_sizer_main = new wxBoxSizer(wxVERTICAL);
|
||||
auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL);
|
||||
m_line_top->SetBackgroundColour(wxColour(166, 169, 170));
|
||||
m_sizer_main->Add(m_line_top, 0, wxEXPAND, 0);
|
||||
m_sizer_main->Add(0, 0, 0, wxTOP, FromDIP(38));
|
||||
|
||||
wxBoxSizer *m_sizer_body = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
m_panel_left = new StaticBox(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(201), FromDIP(212)), wxBORDER_NONE);
|
||||
m_panel_left->SetMinSize(wxSize(FromDIP(201), FromDIP(212)));
|
||||
m_panel_left->SetCornerRadius(8);
|
||||
m_panel_left->SetBackgroundColor(BIND_DIALOG_GREY200);
|
||||
wxBoxSizer *m_sizere_left_h = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxBoxSizer *m_sizere_left_v= new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto m_printer_img = new wxStaticBitmap(m_panel_left, wxID_ANY, create_scaled_bitmap("printer_thumbnail", nullptr, 96), wxDefaultPosition, wxSize(FromDIP(100), FromDIP(96)), 0);
|
||||
m_printer_name = new wxStaticText(m_panel_left, wxID_ANY, wxEmptyString);
|
||||
m_printer_name->SetBackgroundColour(BIND_DIALOG_GREY200);
|
||||
m_printer_name->SetFont(::Label::Head_14);
|
||||
m_sizere_left_v->Add(m_printer_img, 0, wxALIGN_CENTER, 0);
|
||||
m_sizere_left_v->Add(0, 0, 0, wxTOP, 5);
|
||||
m_sizere_left_v->Add(m_printer_name, 0, wxALIGN_CENTER, 0);
|
||||
m_sizere_left_h->Add(m_sizere_left_v, 1, wxALIGN_CENTER, 0);
|
||||
|
||||
m_panel_left->SetSizer(m_sizere_left_h);
|
||||
m_panel_left->Layout();
|
||||
m_sizer_body->Add(m_panel_left, 0, wxEXPAND, 0);
|
||||
|
||||
auto m_bind_icon = create_scaled_bitmap("bind_machine", nullptr, 14);
|
||||
m_sizer_body->Add(new wxStaticBitmap(this, wxID_ANY, m_bind_icon, wxDefaultPosition, wxSize(FromDIP(34), FromDIP(14)), 0), 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, FromDIP(20));
|
||||
|
||||
m_panel_right = new StaticBox(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(201), FromDIP(212)), wxBORDER_NONE);
|
||||
m_panel_right->SetMinSize(wxSize(FromDIP(201), FromDIP(212)));
|
||||
m_panel_right->SetCornerRadius(8);
|
||||
m_panel_right->SetBackgroundColor(BIND_DIALOG_GREY200);
|
||||
|
||||
m_user_name = new wxStaticText(m_panel_right, wxID_ANY, wxEmptyString);
|
||||
m_user_name->SetBackgroundColour(BIND_DIALOG_GREY200);
|
||||
m_user_name->SetFont(::Label::Head_14);
|
||||
wxBoxSizer *m_sizer_right_h = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxBoxSizer *m_sizer_right_v = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
|
||||
Bind(wxEVT_WEBREQUEST_STATE, [this](wxWebRequestEvent& evt) {
|
||||
switch (evt.GetState()) {
|
||||
// Request completed
|
||||
case wxWebRequest::State_Completed: {
|
||||
wxImage avatar_stream = *evt.GetResponse().GetStream();
|
||||
if (avatar_stream.IsOk()) {
|
||||
avatar_stream.Rescale(FromDIP(60), FromDIP(60));
|
||||
auto bitmap = new wxBitmap(avatar_stream);
|
||||
//bitmap->SetSize(wxSize(FromDIP(60), FromDIP(60)));
|
||||
m_avatar->SetBitmap(*bitmap);
|
||||
Layout();
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Request failed
|
||||
case wxWebRequest::State_Failed: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (wxGetApp().is_user_login()) {
|
||||
wxString username_text = from_u8(wxGetApp().getAgent()->get_user_nickanme());
|
||||
m_user_name->SetLabelText(username_text);
|
||||
|
||||
m_avatar = new wxStaticBitmap(m_panel_right, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize(FromDIP(60), FromDIP(60)), 0);
|
||||
|
||||
wxWebRequest request = wxWebSession::GetDefault().CreateRequest(this, wxGetApp().getAgent()->get_user_avatar());
|
||||
if (!request.IsOk()) {
|
||||
// todo request fail
|
||||
}
|
||||
// Start the request
|
||||
request.Start();
|
||||
}
|
||||
|
||||
m_sizer_right_v->Add(m_avatar, 0, wxALIGN_CENTER, 0);
|
||||
m_sizer_right_v->Add(0, 0, 0, wxTOP, 7);
|
||||
m_sizer_right_v->Add(m_user_name, 0, wxALIGN_CENTER, 0);
|
||||
m_sizer_right_h->Add(m_sizer_right_v, 1, wxALIGN_CENTER, 0);
|
||||
|
||||
m_panel_right->SetSizer(m_sizer_right_h);
|
||||
m_panel_right->Layout();
|
||||
m_sizer_body->Add(m_panel_right, 0, wxEXPAND, 0);
|
||||
|
||||
m_sizer_main->Add(m_sizer_body, 1, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(30));
|
||||
|
||||
m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(20));
|
||||
|
||||
m_status_text = new wxStaticText(this, wxID_ANY, _L("Would you like to log in this printer with current account?"), wxDefaultPosition,
|
||||
wxSize(BIND_DIALOG_BUTTON_PANEL_SIZE.x, -1), wxST_ELLIPSIZE_END);
|
||||
m_status_text->SetForegroundColour(wxColour(107, 107, 107));
|
||||
m_status_text->SetFont(::Label::Body_13);
|
||||
m_status_text->Wrap(-1);
|
||||
|
||||
m_simplebook = new wxSimplebook(this, wxID_ANY, wxDefaultPosition,BIND_DIALOG_BUTTON_PANEL_SIZE, 0);
|
||||
m_simplebook->SetBackgroundColour(*wxWHITE);
|
||||
|
||||
m_status_bar = std::make_shared<BBLStatusBarBind>(m_simplebook);
|
||||
|
||||
auto button_panel = new wxPanel(m_simplebook, wxID_ANY, wxDefaultPosition, BIND_DIALOG_BUTTON_PANEL_SIZE);
|
||||
button_panel->SetBackgroundColour(*wxWHITE);
|
||||
wxBoxSizer *m_sizer_button = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_sizer_button->Add(0, 0, 1, wxEXPAND, 5);
|
||||
m_button_bind = new Button(button_panel, _L("Confirm"));
|
||||
StateColor btn_bg_green(std::pair<wxColour, int>(wxColour(61, 203, 115), StateColor::Hovered),
|
||||
std::pair<wxColour, int>(wxColour(0, 174, 66), StateColor::Normal));
|
||||
m_button_bind->SetBackgroundColor(btn_bg_green);
|
||||
m_button_bind->SetBorderColor(wxColour(0, 174, 66));
|
||||
m_button_bind->SetTextColor(*wxWHITE);
|
||||
m_button_bind->SetSize(BIND_DIALOG_BUTTON_SIZE);
|
||||
m_button_bind->SetMinSize(BIND_DIALOG_BUTTON_SIZE);
|
||||
m_button_bind->SetCornerRadius(10);
|
||||
|
||||
|
||||
StateColor btn_bg_white(std::pair<wxColour, int>(wxColour(206, 206, 206), StateColor::Hovered),
|
||||
std::pair<wxColour, int>(*wxWHITE, StateColor::Normal));
|
||||
|
||||
m_button_cancel = new Button(button_panel, _L("Cancel"));
|
||||
m_button_cancel->SetBackgroundColor(btn_bg_white);
|
||||
m_button_cancel->SetBorderColor(BIND_DIALOG_GREY900);
|
||||
m_button_cancel->SetSize(BIND_DIALOG_BUTTON_SIZE);
|
||||
m_button_cancel->SetMinSize(BIND_DIALOG_BUTTON_SIZE);
|
||||
m_button_cancel->SetTextColor(BIND_DIALOG_GREY900);
|
||||
m_button_cancel->SetCornerRadius(10);
|
||||
|
||||
m_sizer_button->Add(m_button_bind, 0, wxALIGN_CENTER, 0);
|
||||
m_sizer_button->Add(0, 0, 0, wxLEFT, FromDIP(13));
|
||||
m_sizer_button->Add(m_button_cancel, 0, wxALIGN_CENTER, 0);
|
||||
button_panel->SetSizer(m_sizer_button);
|
||||
button_panel->Layout();
|
||||
m_sizer_button->Fit(button_panel);
|
||||
|
||||
m_simplebook->AddPage(m_status_bar->get_panel(), wxEmptyString, false);
|
||||
m_simplebook->AddPage(button_panel, wxEmptyString, false);
|
||||
|
||||
//m_sizer_main->Add(m_sizer_button, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(30));
|
||||
|
||||
m_sizer_main->Add(m_status_text, 0, wxALIGN_CENTER, 0);
|
||||
m_sizer_main->Add(0, 0, 0, wxTOP, FromDIP(10));
|
||||
m_sizer_main->Add(m_simplebook, 0, wxALIGN_CENTER, 0);
|
||||
m_sizer_main->Add(0, 0, 0, wxTOP, FromDIP(20));
|
||||
|
||||
SetSizer(m_sizer_main);
|
||||
Layout();
|
||||
Fit();
|
||||
Centre(wxBOTH);
|
||||
|
||||
Bind(wxEVT_SHOW, &BindMachineDilaog::on_show, this);
|
||||
|
||||
m_button_bind->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(BindMachineDilaog::on_bind_printer), NULL, this);
|
||||
m_button_cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(BindMachineDilaog::on_cancel), NULL, this);
|
||||
this->Connect(EVT_BIND_MACHINE_FAIL, wxCommandEventHandler(BindMachineDilaog::on_bind_fail), NULL, this);
|
||||
this->Connect(EVT_BIND_MACHINE_SUCCESS, wxCommandEventHandler(BindMachineDilaog::on_bind_success), NULL, this);
|
||||
this->Connect(EVT_BIND_UPDATE_MESSAGE, wxCommandEventHandler(BindMachineDilaog::on_update_message), NULL, this);
|
||||
m_simplebook->SetSelection(1);
|
||||
}
|
||||
|
||||
BindMachineDilaog::~BindMachineDilaog()
|
||||
{
|
||||
m_button_bind->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(BindMachineDilaog::on_bind_printer), NULL, this);
|
||||
m_button_cancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(BindMachineDilaog::on_cancel), NULL, this);
|
||||
this->Disconnect(EVT_BIND_MACHINE_FAIL, wxCommandEventHandler(BindMachineDilaog::on_bind_fail), NULL, this);
|
||||
this->Disconnect(EVT_BIND_MACHINE_SUCCESS, wxCommandEventHandler(BindMachineDilaog::on_bind_success), NULL, this);
|
||||
this->Disconnect(EVT_BIND_UPDATE_MESSAGE, wxCommandEventHandler(BindMachineDilaog::on_update_message), NULL, this);
|
||||
}
|
||||
|
||||
//static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
|
||||
//{
|
||||
// register int realsize = size * nmemb;
|
||||
// struct MemoryStruct *mem = (struct MemoryStruct *) userp;
|
||||
// mem->memory = (char *) realloc(mem->memory, mem->size + realsize + 1);
|
||||
// if (mem->memory) {
|
||||
// memcpy(&(mem->memory[mem->size]), contents, realsize);
|
||||
// mem->size += realsize;
|
||||
// mem->memory[mem->size] = 0;
|
||||
// }
|
||||
// return realsize;
|
||||
//}
|
||||
|
||||
|
||||
void BindMachineDilaog::on_cancel(wxCommandEvent &event)
|
||||
{
|
||||
EndModal(wxID_CANCEL);
|
||||
}
|
||||
|
||||
void BindMachineDilaog::on_bind_fail(wxCommandEvent &event)
|
||||
{
|
||||
//m_status_text->SetLabel(_L("Would you like to log in this printer with current account?"));
|
||||
m_simplebook->SetSelection(1);
|
||||
}
|
||||
|
||||
void BindMachineDilaog::on_update_message(wxCommandEvent &event)
|
||||
{
|
||||
m_status_text->SetLabelText(event.GetString());
|
||||
}
|
||||
|
||||
void BindMachineDilaog::on_bind_success(wxCommandEvent &event)
|
||||
{
|
||||
EndModal(wxID_OK);
|
||||
MessageDialog msg_wingow(nullptr, _L("Log in successful."), "", wxAPPLY | wxOK);
|
||||
if (msg_wingow.ShowModal() == wxOK) { return; }
|
||||
}
|
||||
|
||||
void BindMachineDilaog::on_bind_printer(wxCommandEvent &event)
|
||||
{
|
||||
//check isset info
|
||||
if (m_machine_info == nullptr || m_machine_info == NULL) return;
|
||||
|
||||
//check dev_id
|
||||
if (m_machine_info->dev_id.empty()) return;
|
||||
|
||||
m_simplebook->SetSelection(0);
|
||||
m_bind_job = std::make_shared<BindJob>(m_status_bar, wxGetApp().plater(), m_machine_info->dev_id, m_machine_info->dev_ip);
|
||||
m_bind_job->set_event_handle(this);
|
||||
m_bind_job->start();
|
||||
}
|
||||
|
||||
void BindMachineDilaog::on_dpi_changed(const wxRect &suggested_rect)
|
||||
{
|
||||
m_button_bind->SetMinSize(BIND_DIALOG_BUTTON_SIZE);
|
||||
m_button_cancel->SetMinSize(BIND_DIALOG_BUTTON_SIZE);
|
||||
}
|
||||
|
||||
void BindMachineDilaog::on_show(wxShowEvent &event)
|
||||
{
|
||||
//m_printer_name->SetLabelText(m_machine_info->get_printer_type_string());
|
||||
m_printer_name->SetLabelText(from_u8(m_machine_info->dev_name));
|
||||
Layout();
|
||||
}
|
||||
|
||||
|
||||
UnBindMachineDilaog::UnBindMachineDilaog(Plater *plater /*= nullptr*/)
|
||||
: DPIDialog(static_cast<wxWindow *>(wxGetApp().mainframe), wxID_ANY, _L("Log out printer"), wxDefaultPosition, wxDefaultSize, wxCAPTION)
|
||||
{
|
||||
std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico") % resources_dir()).str();
|
||||
SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO));
|
||||
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
wxBoxSizer *m_sizer_main = new wxBoxSizer(wxVERTICAL);
|
||||
auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL);
|
||||
m_line_top->SetBackgroundColour(wxColour(166, 169, 170));
|
||||
m_sizer_main->Add(m_line_top, 0, wxEXPAND, 0);
|
||||
m_sizer_main->Add(0, 0, 0, wxTOP, FromDIP(38));
|
||||
|
||||
wxBoxSizer *m_sizer_body = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
auto m_panel_left = new StaticBox(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(201), FromDIP(212)), wxBORDER_NONE);
|
||||
m_panel_left->SetMinSize(wxSize(FromDIP(201), FromDIP(212)));
|
||||
m_panel_left->SetCornerRadius(8);
|
||||
m_panel_left->SetBackgroundColor(BIND_DIALOG_GREY200);
|
||||
wxBoxSizer *m_sizere_left_h = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxBoxSizer *m_sizere_left_v= new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto m_printer_img = new wxStaticBitmap(m_panel_left, wxID_ANY, create_scaled_bitmap("printer_thumbnail", nullptr, 96), wxDefaultPosition, wxSize(FromDIP(100), FromDIP(96)),
|
||||
0);
|
||||
m_printer_name = new wxStaticText(m_panel_left, wxID_ANY, wxEmptyString);
|
||||
m_printer_name->SetFont(::Label::Head_14);
|
||||
m_printer_name->SetBackgroundColour(BIND_DIALOG_GREY200);
|
||||
m_sizere_left_v->Add(m_printer_img, 0, wxALIGN_CENTER, 0);
|
||||
m_sizere_left_v->Add(0, 0, 0, wxTOP, 5);
|
||||
m_sizere_left_v->Add(m_printer_name, 0, wxALIGN_CENTER, 0);
|
||||
m_sizere_left_h->Add(m_sizere_left_v, 1, wxALIGN_CENTER, 0);
|
||||
|
||||
m_panel_left->SetSizer(m_sizere_left_h);
|
||||
m_panel_left->Layout();
|
||||
m_sizer_body->Add(m_panel_left, 0, wxEXPAND, 0);
|
||||
|
||||
auto m_bind_icon = create_scaled_bitmap("unbind_machine", nullptr, 28);
|
||||
m_sizer_body->Add(new wxStaticBitmap(this, wxID_ANY, m_bind_icon, wxDefaultPosition, wxSize(FromDIP(36), FromDIP(28)), 0), 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, FromDIP(20));
|
||||
|
||||
auto m_panel_right = new StaticBox(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(201), FromDIP(212)), wxBORDER_NONE);
|
||||
m_panel_right->SetMinSize(wxSize(FromDIP(201), FromDIP(212)));
|
||||
m_panel_right->SetCornerRadius(8);
|
||||
m_panel_right->SetBackgroundColor(BIND_DIALOG_GREY200);
|
||||
m_user_name = new wxStaticText(m_panel_right, wxID_ANY, wxEmptyString);
|
||||
m_user_name->SetBackgroundColour(BIND_DIALOG_GREY200);
|
||||
m_user_name->SetFont(::Label::Head_14);
|
||||
wxBoxSizer *m_sizer_right_h = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxBoxSizer *m_sizer_right_v = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_avatar = new wxStaticBitmap(m_panel_right, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize(FromDIP(60), FromDIP(60)), 0);
|
||||
|
||||
if (wxGetApp().is_user_login()) {
|
||||
wxString username_text = from_u8(wxGetApp().getAgent()->get_user_name());
|
||||
m_user_name->SetLabelText(username_text);
|
||||
wxString avatar_url = wxGetApp().getAgent()->get_user_avatar();
|
||||
wxWebRequest request = wxWebSession::GetDefault().CreateRequest(this, avatar_url);
|
||||
if (!request.IsOk()) {
|
||||
// todo request fail
|
||||
}
|
||||
request.Start();
|
||||
}
|
||||
|
||||
|
||||
Bind(wxEVT_WEBREQUEST_STATE, [this](wxWebRequestEvent &evt) {
|
||||
switch (evt.GetState()) {
|
||||
// Request completed
|
||||
case wxWebRequest::State_Completed: {
|
||||
wxImage avatar_stream = *evt.GetResponse().GetStream();
|
||||
if (avatar_stream.IsOk()) {
|
||||
avatar_stream.Rescale(FromDIP(60), FromDIP(60));
|
||||
auto bitmap = new wxBitmap(avatar_stream);
|
||||
//bitmap->SetSize(wxSize(FromDIP(60), FromDIP(60)));
|
||||
m_avatar->SetBitmap(*bitmap);
|
||||
Layout();
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Request failed
|
||||
case wxWebRequest::State_Failed: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
m_sizer_right_v->Add(m_avatar, 0, wxALIGN_CENTER, 0);
|
||||
m_sizer_right_v->Add(0, 0, 0, wxTOP, 7);
|
||||
m_sizer_right_v->Add(m_user_name, 0, wxALIGN_CENTER, 0);
|
||||
m_sizer_right_h->Add(m_sizer_right_v, 1, wxALIGN_CENTER, 0);
|
||||
|
||||
m_panel_right->SetSizer(m_sizer_right_h);
|
||||
m_panel_right->Layout();
|
||||
m_sizer_body->Add(m_panel_right, 0, wxEXPAND, 0);
|
||||
|
||||
m_sizer_main->Add(m_sizer_body, 1, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(30));
|
||||
|
||||
m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(20));
|
||||
|
||||
m_status_text = new wxStaticText(this, wxID_ANY, _L("Would you like to log out the printer?"), wxDefaultPosition,
|
||||
wxSize(BIND_DIALOG_BUTTON_PANEL_SIZE.x, -1), wxST_ELLIPSIZE_END);
|
||||
m_status_text->SetForegroundColour(wxColour(107, 107, 107));
|
||||
m_status_text->SetFont(::Label::Body_13);
|
||||
m_status_text->Wrap(-1);
|
||||
|
||||
|
||||
|
||||
wxBoxSizer *m_sizer_button = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
m_sizer_button->Add(0, 0, 1, wxEXPAND, 5);
|
||||
m_button_unbind = new Button(this, _L("Confirm"));
|
||||
StateColor btn_bg_green(std::pair<wxColour, int>(wxColour(61, 203, 115), StateColor::Hovered),
|
||||
std::pair<wxColour, int>(wxColour(0, 174, 66), StateColor::Normal));
|
||||
m_button_unbind->SetBackgroundColor(btn_bg_green);
|
||||
m_button_unbind->SetBorderColor(wxColour(0, 174, 66));
|
||||
m_button_unbind->SetTextColor(*wxWHITE);
|
||||
m_button_unbind->SetSize(BIND_DIALOG_BUTTON_SIZE);
|
||||
m_button_unbind->SetMinSize(BIND_DIALOG_BUTTON_SIZE);
|
||||
m_button_unbind->SetCornerRadius(10);
|
||||
|
||||
|
||||
StateColor btn_bg_white(std::pair<wxColour, int>(wxColour(206, 206, 206), StateColor::Hovered),
|
||||
std::pair<wxColour, int>(*wxWHITE, StateColor::Normal));
|
||||
|
||||
m_button_cancel = new Button(this, _L("Cancel"));
|
||||
m_button_cancel->SetBackgroundColor(btn_bg_white);
|
||||
m_button_cancel->SetBorderColor(BIND_DIALOG_GREY900);
|
||||
m_button_cancel->SetSize(BIND_DIALOG_BUTTON_SIZE);
|
||||
m_button_cancel->SetMinSize(BIND_DIALOG_BUTTON_SIZE);
|
||||
m_button_cancel->SetTextColor(BIND_DIALOG_GREY900);
|
||||
m_button_cancel->SetCornerRadius(10);
|
||||
|
||||
m_sizer_button->Add(m_button_unbind, 0, wxALIGN_CENTER, 0);
|
||||
m_sizer_button->Add(0, 0, 0, wxLEFT, FromDIP(13));
|
||||
m_sizer_button->Add(m_button_cancel, 0, wxALIGN_CENTER, 0);
|
||||
|
||||
m_sizer_main->Add(m_status_text, 0, wxALIGN_CENTER, 0);
|
||||
m_sizer_main->Add(0, 0, 0, wxTOP, FromDIP(10));
|
||||
m_sizer_main->Add(m_sizer_button, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(30));
|
||||
m_sizer_main->Add(0, 0, 0, wxTOP, FromDIP(20));
|
||||
|
||||
SetSizer(m_sizer_main);
|
||||
Layout();
|
||||
Fit();
|
||||
Centre(wxBOTH);
|
||||
|
||||
Bind(wxEVT_SHOW, &UnBindMachineDilaog::on_show, this);
|
||||
m_button_unbind->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(UnBindMachineDilaog::on_unbind_printer), NULL, this);
|
||||
m_button_cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(UnBindMachineDilaog::on_cancel), NULL, this);
|
||||
}
|
||||
|
||||
UnBindMachineDilaog::~UnBindMachineDilaog()
|
||||
{
|
||||
m_button_unbind->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(UnBindMachineDilaog::on_unbind_printer), NULL, this);
|
||||
m_button_cancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(UnBindMachineDilaog::on_cancel), NULL, this);
|
||||
}
|
||||
|
||||
|
||||
void UnBindMachineDilaog::on_cancel(wxCommandEvent &event)
|
||||
{
|
||||
EndModal(wxID_CANCEL);
|
||||
}
|
||||
|
||||
void UnBindMachineDilaog::on_unbind_printer(wxCommandEvent &event)
|
||||
{
|
||||
if (!wxGetApp().is_user_login()) {
|
||||
m_status_text->SetLabelText(_L("Please log in first."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_machine_info) {
|
||||
m_status_text->SetLabelText(_L("There was a problem connecting to the printer. Please try again."));
|
||||
return;
|
||||
}
|
||||
|
||||
m_machine_info->set_access_code("");
|
||||
int result = wxGetApp().request_user_unbind(m_machine_info->dev_id);
|
||||
if (result == 0) {
|
||||
DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager();
|
||||
if (!dev) return;
|
||||
dev->update_user_machine_list_info();
|
||||
|
||||
m_status_text->SetLabelText(_L("Log out successful."));
|
||||
m_button_cancel->SetLabel(_L("Close"));
|
||||
m_button_unbind->Hide();
|
||||
}
|
||||
else {
|
||||
m_status_text->SetLabelText(_L("Failed to log out."));
|
||||
return;
|
||||
}
|
||||
EndModal(wxID_OK);
|
||||
}
|
||||
|
||||
void UnBindMachineDilaog::on_dpi_changed(const wxRect &suggested_rect)
|
||||
{
|
||||
m_button_unbind->SetMinSize(BIND_DIALOG_BUTTON_SIZE);
|
||||
m_button_cancel->SetMinSize(BIND_DIALOG_BUTTON_SIZE);
|
||||
}
|
||||
|
||||
void UnBindMachineDilaog::on_show(wxShowEvent &event)
|
||||
{
|
||||
//m_printer_name->SetLabelText(m_machine_info->get_printer_type_string());
|
||||
m_printer_name->SetLabelText(from_u8(m_machine_info->dev_name));
|
||||
Layout();
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
99
src/slic3r/GUI/BindDialog.hpp
Normal file
99
src/slic3r/GUI/BindDialog.hpp
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
#ifndef slic3r_BindDialog_hpp_
|
||||
#define slic3r_BindDialog_hpp_
|
||||
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/gauge.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/icon.h>
|
||||
#include <wx/dialog.h>
|
||||
#include <curl/curl.h>
|
||||
#include "wxExtensions.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "Widgets/StepCtrl.hpp"
|
||||
#include "Widgets/ProgressDialog.hpp"
|
||||
#include "Widgets/Button.hpp"
|
||||
#include "Widgets/ProgressBar.hpp"
|
||||
#include "Widgets/RoundedRectangle.hpp"
|
||||
#include "Jobs/BindJob.hpp"
|
||||
#include "BBLStatusBar.hpp"
|
||||
#include "BBLStatusBarBind.hpp"
|
||||
|
||||
#define BIND_DIALOG_GREY200 wxColour(248, 248, 248)
|
||||
#define BIND_DIALOG_GREY800 wxColour(50, 58, 61)
|
||||
#define BIND_DIALOG_GREY900 wxColour(38, 46, 48)
|
||||
#define BIND_DIALOG_BUTTON_SIZE wxSize(FromDIP(68), FromDIP(24))
|
||||
#define BIND_DIALOG_BUTTON_PANEL_SIZE wxSize(FromDIP(450), FromDIP(30))
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
struct MemoryStruct
|
||||
{
|
||||
char * memory;
|
||||
size_t read_pos;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
class BindMachineDilaog : public DPIDialog
|
||||
{
|
||||
private:
|
||||
wxStaticText * m_printer_name;
|
||||
wxStaticText * m_user_name;
|
||||
StaticBox * m_panel_left;
|
||||
StaticBox * m_panel_right;
|
||||
wxStaticText *m_status_text;
|
||||
Button * m_button_bind;
|
||||
Button * m_button_cancel;
|
||||
wxSimplebook *m_simplebook;
|
||||
wxStaticBitmap *m_avatar;
|
||||
|
||||
MachineObject * m_machine_info{nullptr};
|
||||
std::shared_ptr<BindJob> m_bind_job;
|
||||
std::shared_ptr<BBLStatusBarBind> m_status_bar;
|
||||
|
||||
public:
|
||||
BindMachineDilaog(Plater *plater = nullptr);
|
||||
~BindMachineDilaog();
|
||||
void on_cancel(wxCommandEvent &event);
|
||||
void on_bind_fail(wxCommandEvent &event);
|
||||
void on_update_message(wxCommandEvent &event);
|
||||
void on_bind_success(wxCommandEvent &event);
|
||||
void on_bind_printer(wxCommandEvent &event);
|
||||
void on_dpi_changed(const wxRect &suggested_rect) override;
|
||||
void update_machine_info(MachineObject *info) { m_machine_info = info; };
|
||||
void on_show(wxShowEvent &event);
|
||||
};
|
||||
|
||||
class UnBindMachineDilaog : public DPIDialog
|
||||
{
|
||||
protected:
|
||||
wxStaticText * m_printer_name;
|
||||
wxStaticText * m_user_name;
|
||||
wxStaticText *m_status_text;
|
||||
Button * m_button_unbind;
|
||||
Button * m_button_cancel;
|
||||
MachineObject *m_machine_info{nullptr};
|
||||
wxStaticBitmap *m_avatar;
|
||||
|
||||
public:
|
||||
UnBindMachineDilaog(Plater *plater = nullptr);
|
||||
~UnBindMachineDilaog();
|
||||
|
||||
void on_cancel(wxCommandEvent &event);
|
||||
void on_unbind_printer(wxCommandEvent &event);
|
||||
void on_dpi_changed(const wxRect &suggested_rect) override;
|
||||
void update_machine_info(MachineObject *info) { m_machine_info = info; };
|
||||
void on_show(wxShowEvent &event);
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
#endif /* slic3r_BindDialog_hpp_ */
|
||||
432
src/slic3r/GUI/BitmapCache.cpp
Normal file
432
src/slic3r/GUI/BitmapCache.cpp
Normal file
|
|
@ -0,0 +1,432 @@
|
|||
#include "BitmapCache.hpp"
|
||||
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "../Utils/MacDarkMode.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_Utils.hpp"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#ifdef __WXGTK2__
|
||||
// Broken alpha workaround
|
||||
#include <wx/mstream.h>
|
||||
#include <wx/rawbmp.h>
|
||||
#endif /* __WXGTK2__ */
|
||||
|
||||
#define NANOSVG_IMPLEMENTATION
|
||||
#include "nanosvg/nanosvg.h"
|
||||
#define NANOSVGRAST_IMPLEMENTATION
|
||||
#include "nanosvg/nanosvgrast.h"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
BitmapCache::BitmapCache()
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
// 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.
|
||||
m_scale = mac_max_scaling_factor();
|
||||
#endif
|
||||
}
|
||||
|
||||
void BitmapCache::clear()
|
||||
{
|
||||
for (std::pair<const std::string, wxBitmap*> &bitmap : m_map)
|
||||
delete bitmap.second;
|
||||
|
||||
m_map.clear();
|
||||
}
|
||||
|
||||
static wxBitmap wxImage_to_wxBitmap_with_alpha(wxImage &&image, float scale = 1.0f)
|
||||
{
|
||||
#ifdef __WXGTK2__
|
||||
// Broken alpha workaround
|
||||
wxMemoryOutputStream stream;
|
||||
image.SaveFile(stream, wxBITMAP_TYPE_PNG);
|
||||
wxStreamBuffer *buf = stream.GetOutputStreamBuffer();
|
||||
return wxBitmap::NewFromPNGData(buf->GetBufferStart(), buf->GetBufferSize());
|
||||
#else
|
||||
#ifdef __APPLE__
|
||||
// This is a c-tor native to Mac OS. We need to let the Mac OS wxBitmap implementation
|
||||
// know that the image may already be scaled appropriately for Retina,
|
||||
// and thereby that it's not supposed to upscale it.
|
||||
// Contrary to intuition, the `scale` argument isn't "please scale this to such and such"
|
||||
// but rather "the wxImage is sized for backing scale such and such".
|
||||
return wxBitmap(std::move(image), -1, scale);
|
||||
#else
|
||||
return wxBitmap(std::move(image));
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
wxBitmap* BitmapCache::insert(const std::string &bitmap_key, size_t width, size_t height)
|
||||
{
|
||||
wxBitmap *bitmap = nullptr;
|
||||
auto it = m_map.find(bitmap_key);
|
||||
if (it == m_map.end()) {
|
||||
bitmap = new wxBitmap(width, height
|
||||
#ifdef __WXGTK3__
|
||||
, 32
|
||||
#endif
|
||||
);
|
||||
#ifdef __APPLE__
|
||||
// Contrary to intuition, the `scale` argument isn't "please scale this to such and such"
|
||||
// but rather "the wxImage is sized for backing scale such and such".
|
||||
// So, We need to let the Mac OS wxBitmap implementation
|
||||
// know that the image may already be scaled appropriately for Retina,
|
||||
// and thereby that it's not supposed to upscale it.
|
||||
bitmap->CreateScaled(width, height, -1, m_scale);
|
||||
#endif
|
||||
m_map[bitmap_key] = bitmap;
|
||||
} else {
|
||||
bitmap = it->second;
|
||||
if (size_t(bitmap->GetWidth()) != width || size_t(bitmap->GetHeight()) != height)
|
||||
bitmap->Create(width, height);
|
||||
}
|
||||
#if defined(WIN32) || defined(__APPLE__)
|
||||
// Not needed or harmful for GTK2 and GTK3.
|
||||
bitmap->UseAlpha();
|
||||
#endif
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap &bmp)
|
||||
{
|
||||
wxBitmap *bitmap = nullptr;
|
||||
auto it = m_map.find(bitmap_key);
|
||||
if (it == m_map.end()) {
|
||||
bitmap = new wxBitmap(bmp);
|
||||
m_map[bitmap_key] = bitmap;
|
||||
} else {
|
||||
bitmap = it->second;
|
||||
*bitmap = bmp;
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap &bmp, const wxBitmap &bmp2)
|
||||
{
|
||||
// Copying the wxBitmaps is cheap as the bitmap's content is reference counted.
|
||||
const wxBitmap bmps[2] = { bmp, bmp2 };
|
||||
return this->insert(bitmap_key, bmps, bmps + 2);
|
||||
}
|
||||
|
||||
wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3)
|
||||
{
|
||||
// Copying the wxBitmaps is cheap as the bitmap's content is reference counted.
|
||||
const wxBitmap bmps[3] = { bmp, bmp2, bmp3 };
|
||||
return this->insert(bitmap_key, bmps, bmps + 3);
|
||||
}
|
||||
|
||||
wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap *begin, const wxBitmap *end)
|
||||
{
|
||||
size_t width = 0;
|
||||
size_t height = 0;
|
||||
for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) {
|
||||
#ifdef __APPLE__
|
||||
width += bmp->GetScaledWidth();
|
||||
height = std::max<size_t>(height, bmp->GetScaledHeight());
|
||||
#else
|
||||
width += bmp->GetWidth();
|
||||
height = std::max<size_t>(height, bmp->GetHeight());
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __WXGTK2__
|
||||
// Broken alpha workaround
|
||||
wxImage image(width, height);
|
||||
image.InitAlpha();
|
||||
// Fill in with a white color.
|
||||
memset(image.GetData(), 0x0ff, width * height * 3);
|
||||
// Fill in with full transparency.
|
||||
memset(image.GetAlpha(), 0, width * height);
|
||||
size_t x = 0;
|
||||
for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) {
|
||||
if (bmp->GetWidth() > 0) {
|
||||
if (bmp->GetDepth() == 32) {
|
||||
wxAlphaPixelData data(*const_cast<wxBitmap*>(bmp));
|
||||
//FIXME The following method is missing from wxWidgets 3.1.1.
|
||||
// It looks like the wxWidgets 3.0.3 called the wrapped bitmap's UseAlpha().
|
||||
//data.UseAlpha();
|
||||
if (data) {
|
||||
for (int r = 0; r < bmp->GetHeight(); ++ r) {
|
||||
wxAlphaPixelData::Iterator src(data);
|
||||
src.Offset(data, 0, r);
|
||||
unsigned char *dst_pixels = image.GetData() + (x + r * width) * 3;
|
||||
unsigned char *dst_alpha = image.GetAlpha() + x + r * width;
|
||||
for (int c = 0; c < bmp->GetWidth(); ++ c, ++ src) {
|
||||
*dst_pixels ++ = src.Red();
|
||||
*dst_pixels ++ = src.Green();
|
||||
*dst_pixels ++ = src.Blue();
|
||||
*dst_alpha ++ = src.Alpha();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (bmp->GetDepth() == 24) {
|
||||
wxNativePixelData data(*const_cast<wxBitmap*>(bmp));
|
||||
if (data) {
|
||||
for (int r = 0; r < bmp->GetHeight(); ++ r) {
|
||||
wxNativePixelData::Iterator src(data);
|
||||
src.Offset(data, 0, r);
|
||||
unsigned char *dst_pixels = image.GetData() + (x + r * width) * 3;
|
||||
unsigned char *dst_alpha = image.GetAlpha() + x + r * width;
|
||||
for (int c = 0; c < bmp->GetWidth(); ++ c, ++ src) {
|
||||
*dst_pixels ++ = src.Red();
|
||||
*dst_pixels ++ = src.Green();
|
||||
*dst_pixels ++ = src.Blue();
|
||||
*dst_alpha ++ = wxALPHA_OPAQUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
x += bmp->GetWidth();
|
||||
}
|
||||
return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image)));
|
||||
|
||||
#else
|
||||
|
||||
wxBitmap *bitmap = this->insert(bitmap_key, width, height);
|
||||
wxMemoryDC memDC;
|
||||
memDC.SelectObject(*bitmap);
|
||||
memDC.SetBackground(*wxTRANSPARENT_BRUSH);
|
||||
memDC.Clear();
|
||||
size_t x = 0;
|
||||
for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) {
|
||||
if (bmp->GetWidth() > 0)
|
||||
memDC.DrawBitmap(*bmp, x, 0, true);
|
||||
#ifdef __APPLE__
|
||||
// we should "move" with step equal to non-scaled width
|
||||
x += bmp->GetScaledWidth();
|
||||
#else
|
||||
x += bmp->GetWidth();
|
||||
#endif
|
||||
}
|
||||
memDC.SelectObject(wxNullBitmap);
|
||||
return bitmap;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
wxBitmap* BitmapCache::insert_raw_rgba(const std::string &bitmap_key, unsigned width, unsigned height, const unsigned char *raw_data, const bool grayscale/* = false*/)
|
||||
{
|
||||
wxImage image(width, height);
|
||||
image.InitAlpha();
|
||||
unsigned char *rgb = image.GetData();
|
||||
unsigned char *alpha = image.GetAlpha();
|
||||
unsigned int pixels = width * height;
|
||||
for (unsigned int i = 0; i < pixels; ++ i) {
|
||||
*rgb ++ = *raw_data ++;
|
||||
*rgb ++ = *raw_data ++;
|
||||
*rgb ++ = *raw_data ++;
|
||||
*alpha ++ = *raw_data ++;
|
||||
}
|
||||
|
||||
if (grayscale)
|
||||
image = image.ConvertToGreyscale(m_gs, m_gs, m_gs);
|
||||
|
||||
return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image), m_scale));
|
||||
}
|
||||
|
||||
wxBitmap* BitmapCache::load_png(const std::string &bitmap_name, unsigned width, unsigned height,
|
||||
const bool grayscale/* = false*/, const float scale_in_center/* = 0*/) // BBS: support resize by fill border
|
||||
{
|
||||
std::string bitmap_key = bitmap_name + ( height !=0 ?
|
||||
"-h" + std::to_string(height) :
|
||||
"-w" + std::to_string(width))
|
||||
+ (grayscale ? "-gs" : "");
|
||||
|
||||
auto it = m_map.find(bitmap_key);
|
||||
if (it != m_map.end())
|
||||
return it->second;
|
||||
|
||||
wxImage image;
|
||||
if (! image.LoadFile(Slic3r::GUI::from_u8(Slic3r::var(bitmap_name + ".png")), wxBITMAP_TYPE_PNG) ||
|
||||
image.GetWidth() == 0 || image.GetHeight() == 0)
|
||||
return nullptr;
|
||||
|
||||
if (height == 0 && width == 0)
|
||||
height = image.GetHeight();
|
||||
|
||||
if (height != 0 && unsigned(image.GetHeight()) != height)
|
||||
width = unsigned(0.5f + float(image.GetWidth()) * height / image.GetHeight());
|
||||
else if (width != 0 && unsigned(image.GetWidth()) != width)
|
||||
height = unsigned(0.5f + float(image.GetHeight()) * width / image.GetWidth());
|
||||
|
||||
if (height != 0 && width != 0) {
|
||||
// BBS: support resize by fill border
|
||||
if (scale_in_center > 0)
|
||||
image.Resize({ (int)width, (int)height }, { (int)(width - image.GetWidth()) / 2, (int)(height - image.GetHeight()) / 2 });
|
||||
else
|
||||
image.Rescale(width, height, wxIMAGE_QUALITY_BILINEAR);
|
||||
}
|
||||
|
||||
if (grayscale)
|
||||
image = image.ConvertToGreyscale(m_gs, m_gs, m_gs);
|
||||
|
||||
return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image)));
|
||||
}
|
||||
|
||||
NSVGimage* BitmapCache::nsvgParseFromFileWithReplace(const char* filename, const char* units, float dpi, const std::map<std::string, std::string>& replaces)
|
||||
{
|
||||
std::string str;
|
||||
FILE* fp = NULL;
|
||||
size_t size;
|
||||
char* data = NULL;
|
||||
NSVGimage* image = NULL;
|
||||
|
||||
fp = boost::nowide::fopen(filename, "rb");
|
||||
if (!fp) goto error;
|
||||
fseek(fp, 0, SEEK_END);
|
||||
size = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
data = (char*)malloc(size + 1);
|
||||
if (data == NULL) goto error;
|
||||
if (fread(data, 1, size, fp) != size) goto error;
|
||||
data[size] = '\0'; // Must be null terminated.
|
||||
fclose(fp);
|
||||
|
||||
if (replaces.empty())
|
||||
image = nsvgParse(data, units, dpi);
|
||||
else {
|
||||
str.assign(data);
|
||||
for (auto val : replaces)
|
||||
boost::replace_all(str, val.first, val.second);
|
||||
image = nsvgParse(str.data(), units, dpi);
|
||||
}
|
||||
free(data);
|
||||
return image;
|
||||
|
||||
error:
|
||||
if (fp) fclose(fp);
|
||||
if (data) free(data);
|
||||
if (image) nsvgDelete(image);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_width, unsigned target_height,
|
||||
const bool grayscale/* = false*/, const bool dark_mode/* = false*/, const std::string& new_color /*= ""*/, const float scale_in_center/* = 0*/)
|
||||
{
|
||||
std::string bitmap_key = bitmap_name + ( target_height !=0 ?
|
||||
"-h" + std::to_string(target_height) :
|
||||
"-w" + std::to_string(target_width))
|
||||
+ (m_scale != 1.0f ? "-s" + float_to_string_decimal_point(m_scale) : "")
|
||||
+ (dark_mode ? "-dm" : "")
|
||||
+ (grayscale ? "-gs" : "")
|
||||
+ new_color;
|
||||
|
||||
auto it = m_map.find(bitmap_key);
|
||||
if (it != m_map.end())
|
||||
return it->second;
|
||||
|
||||
// map of color replaces
|
||||
std::map<std::string, std::string> replaces;
|
||||
if (dark_mode)
|
||||
replaces["\"#808080\""] = "\"#FFFFFF\"";
|
||||
if (!new_color.empty())
|
||||
replaces["\"#ED6B21\""] = "\"" + new_color + "\"";
|
||||
|
||||
NSVGimage *image = nsvgParseFromFileWithReplace(Slic3r::var(bitmap_name + ".svg").c_str(), "px", 96.0f, replaces);
|
||||
if (image == nullptr)
|
||||
return nullptr;
|
||||
|
||||
if (target_height == 0 && target_width == 0)
|
||||
target_height = image->height;
|
||||
|
||||
target_height != 0 ? target_height *= m_scale : target_width *= m_scale;
|
||||
|
||||
float svg_scale = target_height != 0 ?
|
||||
(float)target_height / image->height : target_width != 0 ?
|
||||
(float)target_width / image->width : 1;
|
||||
|
||||
int width = (int)(svg_scale * image->width + 0.5f);
|
||||
int height = (int)(svg_scale * image->height + 0.5f);
|
||||
int n_pixels = width * height;
|
||||
if (n_pixels <= 0) {
|
||||
::nsvgDelete(image);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NSVGrasterizer *rast = ::nsvgCreateRasterizer();
|
||||
if (rast == nullptr) {
|
||||
::nsvgDelete(image);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> data(n_pixels * 4, 0);
|
||||
// BBS: support resize by fill border
|
||||
if (scale_in_center > 0) {
|
||||
int w = (int)(image->width * scale_in_center);
|
||||
int h = (int)(image->height * scale_in_center);
|
||||
::nsvgRasterize(rast, image, 0, 0, scale_in_center, data.data() + int(height - h) / 2 * width * 4 + int(width - w) / 2 * 4, w, h, width * 4);
|
||||
} else
|
||||
::nsvgRasterize(rast, image, 0, 0, svg_scale, data.data(), width, height, width * 4);
|
||||
::nsvgDeleteRasterizer(rast);
|
||||
::nsvgDelete(image);
|
||||
|
||||
return this->insert_raw_rgba(bitmap_key, width, height, data.data(), grayscale);
|
||||
}
|
||||
|
||||
//we make scaled solid bitmaps only for the cases, when its will be used with scaled SVG icon in one output bitmap
|
||||
wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, bool suppress_scaling/* = false*/, size_t border_width /*= 0*/, bool dark_mode/* = false*/)
|
||||
{
|
||||
double scale = suppress_scaling ? 1.0f : m_scale;
|
||||
width *= scale;
|
||||
height *= scale;
|
||||
|
||||
wxImage image(width, height);
|
||||
image.InitAlpha();
|
||||
unsigned char* imgdata = image.GetData();
|
||||
unsigned char* imgalpha = image.GetAlpha();
|
||||
for (size_t i = 0; i < width * height; ++ i) {
|
||||
*imgdata ++ = r;
|
||||
*imgdata ++ = g;
|
||||
*imgdata ++ = b;
|
||||
*imgalpha ++ = transparency;
|
||||
}
|
||||
|
||||
// Add border, make white/light spools easier to see
|
||||
if (border_width > 0) {
|
||||
|
||||
// Restrict to width of image
|
||||
if (border_width > height) border_width = height - 1;
|
||||
if (border_width > width) border_width = width - 1;
|
||||
|
||||
auto px_data = (uint8_t*)image.GetData();
|
||||
auto a_data = (uint8_t*)image.GetAlpha();
|
||||
|
||||
for (size_t x = 0; x < width; ++x) {
|
||||
for (size_t y = 0; y < height; ++y) {
|
||||
if (x < border_width || y < border_width ||
|
||||
x >= (width - border_width) || y >= (height - border_width)) {
|
||||
const size_t idx = (x + y * width);
|
||||
const size_t idx_rgb = (x + y * width) * 3;
|
||||
px_data[idx_rgb] = px_data[idx_rgb + 1] = px_data[idx_rgb + 2] = dark_mode ? 245u : 110u;
|
||||
a_data[idx] = 255u;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return wxImage_to_wxBitmap_with_alpha(std::move(image), scale);
|
||||
}
|
||||
|
||||
bool BitmapCache::parse_color(const std::string& scolor, unsigned char* rgb_out)
|
||||
{
|
||||
rgb_out[0] = rgb_out[1] = rgb_out[2] = 0;
|
||||
if (scolor.size() != 7 || scolor.front() != '#')
|
||||
return false;
|
||||
const char* c = scolor.data() + 1;
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
int digit1 = hex_digit_to_int(*c++);
|
||||
int digit2 = hex_digit_to_int(*c++);
|
||||
if (digit1 == -1 || digit2 == -1)
|
||||
return false;
|
||||
rgb_out[i] = (unsigned char)(digit1 * 16 + digit2);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
61
src/slic3r/GUI/BitmapCache.hpp
Normal file
61
src/slic3r/GUI/BitmapCache.hpp
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
#ifndef SLIC3R_GUI_BITMAP_CACHE_HPP
|
||||
#define SLIC3R_GUI_BITMAP_CACHE_HPP
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <wx/wxprec.h>
|
||||
#ifndef WX_PRECOMP
|
||||
#include <wx/wx.h>
|
||||
#endif
|
||||
|
||||
struct NSVGimage;
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
class BitmapCache
|
||||
{
|
||||
public:
|
||||
BitmapCache();
|
||||
~BitmapCache() { clear(); }
|
||||
void clear();
|
||||
double scale() { return m_scale; }
|
||||
|
||||
wxBitmap* find(const std::string &name) { auto it = m_map.find(name); return (it == m_map.end()) ? nullptr : it->second; }
|
||||
const wxBitmap* find(const std::string &name) const { return const_cast<BitmapCache*>(this)->find(name); }
|
||||
|
||||
wxBitmap* insert(const std::string &name, size_t width, size_t height);
|
||||
wxBitmap* insert(const std::string &name, const wxBitmap &bmp);
|
||||
wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2);
|
||||
wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3);
|
||||
wxBitmap* insert(const std::string &name, const std::vector<wxBitmap> &bmps) { return this->insert(name, &bmps.front(), &bmps.front() + bmps.size()); }
|
||||
wxBitmap* insert(const std::string &name, const wxBitmap *begin, const wxBitmap *end);
|
||||
wxBitmap* insert_raw_rgba(const std::string &bitmap_key, unsigned width, unsigned height, const unsigned char *raw_data, const bool grayscale = false);
|
||||
|
||||
// BBS: support resize by fill border (scale_in_center)
|
||||
// Load png from resources/icons. bitmap_key is given without the .png suffix. Bitmap will be rescaled to provided height/width if nonzero.
|
||||
wxBitmap* load_png(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false, const float scale_in_center = 0.f);
|
||||
|
||||
// Parses SVG file from a file, returns SVG image as paths.
|
||||
// And makes replases befor parsing
|
||||
// replace_map containes old_value->new_value
|
||||
static NSVGimage* nsvgParseFromFileWithReplace(const char* filename, const char* units, float dpi, const std::map<std::string, std::string>& replaces);
|
||||
// Load svg from resources/icons. bitmap_key is given without the .svg suffix. SVG will be rasterized to provided height/width.
|
||||
wxBitmap* load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false, const bool dark_mode = false, const std::string& new_color = "", const float scale_in_center = 0.f);
|
||||
|
||||
wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, bool suppress_scaling = false, size_t border_width = 0, bool dark_mode = false);
|
||||
wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3], bool suppress_scaling = false, size_t border_width = 0, bool dark_mode = false) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE, suppress_scaling, border_width, dark_mode); }
|
||||
wxBitmap mkclear(size_t width, size_t height) { return mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT); }
|
||||
|
||||
static bool parse_color(const std::string& scolor, unsigned char* rgb_out);
|
||||
|
||||
private:
|
||||
std::map<std::string, wxBitmap*> m_map;
|
||||
double m_gs = 0.2; // value, used for image.ConvertToGreyscale(m_gs, m_gs, m_gs)
|
||||
double m_scale = 1.0; // value, used for correct scaling of SVG icons on Retina display
|
||||
};
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
|
||||
#endif /* SLIC3R_GUI_BITMAP_CACHE_HPP */
|
||||
286
src/slic3r/GUI/BitmapComboBox.cpp
Normal file
286
src/slic3r/GUI/BitmapComboBox.cpp
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
#include "BitmapComboBox.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/textctrl.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/statbox.h>
|
||||
#include <wx/colordlg.h>
|
||||
#include <wx/wupdlock.h>
|
||||
#include <wx/menu.h>
|
||||
#include <wx/odcombo.h>
|
||||
#include <wx/listbook.h>
|
||||
#include <wx/window.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <wx/msw/dcclient.h>
|
||||
#include <wx/msw/private.h>
|
||||
#ifdef _MSW_DARK_MODE
|
||||
#include <wx/msw/dark_mode.h>
|
||||
#endif //_MSW_DARK_MODE
|
||||
#endif
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "format.hpp"
|
||||
|
||||
// A workaround for a set of issues related to text fitting into gtk widgets:
|
||||
// See e.g.: https://github.com/prusa3d/PrusaSlicer/issues/4584
|
||||
#if defined(__WXGTK20__) || defined(__WXGTK3__)
|
||||
#include <glib-2.0/glib-object.h>
|
||||
#include <pango-1.0/pango/pango-layout.h>
|
||||
#include <gtk/gtk.h>
|
||||
#endif
|
||||
|
||||
using Slic3r::GUI::format_wxstr;
|
||||
|
||||
#define BORDER_W 10
|
||||
|
||||
// ---------------------------------
|
||||
// *** BitmapComboBox ***
|
||||
// ---------------------------------
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
/* For PresetComboBox we use bitmaps that are created from images that are already scaled appropriately for Retina
|
||||
* (Contrary to the intuition, the `scale` argument for Bitmap's constructor doesn't mean
|
||||
* "please scale this to such and such" but rather
|
||||
* "the wxImage is already sized for backing scale such and such". )
|
||||
* Unfortunately, the constructor changes the size of wxBitmap too.
|
||||
* Thus We need to use unscaled size value for bitmaps that we use
|
||||
* to avoid scaled size of control items.
|
||||
* For this purpose control drawing methods and
|
||||
* control size calculation methods (virtual) are overridden.
|
||||
**/
|
||||
|
||||
BitmapComboBox::BitmapComboBox(wxWindow* parent,
|
||||
wxWindowID id/* = wxID_ANY*/,
|
||||
const wxString& value/* = wxEmptyString*/,
|
||||
const wxPoint& pos/* = wxDefaultPosition*/,
|
||||
const wxSize& size/* = wxDefaultSize*/,
|
||||
int n/* = 0*/,
|
||||
const wxString choices[]/* = NULL*/,
|
||||
long style/* = 0*/) :
|
||||
wxBitmapComboBox(parent, id, value, pos, size, n, choices, style)
|
||||
{
|
||||
SetFont(Slic3r::GUI::wxGetApp().normal_font());
|
||||
#ifdef _WIN32
|
||||
// Workaround for ignoring CBN_EDITCHANGE events, which are processed after the content of the combo box changes, so that
|
||||
// the index of the item inside CBN_EDITCHANGE may no more be valid.
|
||||
EnableTextChangedEvents(false);
|
||||
wxGetApp().UpdateDarkUI(this);
|
||||
if (!HasFlag(wxCB_READONLY))
|
||||
wxTextEntry::SetMargins(0,0);
|
||||
#endif /* _WIN32 */
|
||||
}
|
||||
|
||||
BitmapComboBox::~BitmapComboBox()
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
bool BitmapComboBox::OnAddBitmap(const wxBitmap& bitmap)
|
||||
{
|
||||
if (bitmap.IsOk())
|
||||
{
|
||||
// we should use scaled! size values of bitmap
|
||||
int width = (int)bitmap.GetScaledWidth();
|
||||
int height = (int)bitmap.GetScaledHeight();
|
||||
|
||||
if (m_usedImgSize.x < 0)
|
||||
{
|
||||
// If size not yet determined, get it from this image.
|
||||
m_usedImgSize.x = width;
|
||||
m_usedImgSize.y = height;
|
||||
|
||||
// Adjust control size to vertically fit the bitmap
|
||||
wxWindow* ctrl = GetControl();
|
||||
ctrl->InvalidateBestSize();
|
||||
wxSize newSz = ctrl->GetBestSize();
|
||||
wxSize sz = ctrl->GetSize();
|
||||
if (newSz.y > sz.y)
|
||||
ctrl->SetSize(sz.x, newSz.y);
|
||||
else
|
||||
DetermineIndent();
|
||||
}
|
||||
|
||||
wxCHECK_MSG(width == m_usedImgSize.x && height == m_usedImgSize.y,
|
||||
false,
|
||||
"you can only add images of same size");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void BitmapComboBox::OnDrawItem(wxDC& dc,
|
||||
const wxRect& rect,
|
||||
int item,
|
||||
int flags) const
|
||||
{
|
||||
const wxBitmap& bmp = *(static_cast<wxBitmap*>(m_bitmaps[item]));
|
||||
if (bmp.IsOk())
|
||||
{
|
||||
// we should use scaled! size values of bitmap
|
||||
wxCoord w = bmp.GetScaledWidth();
|
||||
wxCoord h = bmp.GetScaledHeight();
|
||||
|
||||
const int imgSpacingLeft = 4;
|
||||
|
||||
// Draw the image centered
|
||||
dc.DrawBitmap(bmp,
|
||||
rect.x + (m_usedImgSize.x - w) / 2 + imgSpacingLeft,
|
||||
rect.y + (rect.height - h) / 2,
|
||||
true);
|
||||
}
|
||||
|
||||
wxString text = GetString(item);
|
||||
if (!text.empty())
|
||||
dc.DrawText(text,
|
||||
rect.x + m_imgAreaWidth + 1,
|
||||
rect.y + (rect.height - dc.GetCharHeight()) / 2);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
int BitmapComboBox::Append(const wxString& item)
|
||||
{
|
||||
// Workaround for a correct rendering of the control without Bitmap (under MSW):
|
||||
//1. We should create small Bitmap to fill Bitmaps RefData,
|
||||
// ! in this case wxBitmap.IsOK() return true.
|
||||
//2. But then set width to 0 value for no using of bitmap left and right spacing
|
||||
//3. Set this empty bitmap to the at list one item and BitmapCombobox will be recreated correct
|
||||
|
||||
wxBitmap bitmap(1, int(1.6 * wxGetApp().em_unit() + 1));
|
||||
{
|
||||
// bitmap.SetWidth(0); is depricated now
|
||||
// so, use next code
|
||||
bitmap.UnShare();// AllocExclusive();
|
||||
bitmap.GetGDIImageData()->m_width = 0;
|
||||
}
|
||||
|
||||
OnAddBitmap(bitmap);
|
||||
const int n = wxComboBox::Append(item);
|
||||
if (n != wxNOT_FOUND)
|
||||
DoSetItemBitmap(n, bitmap);
|
||||
return n;
|
||||
}
|
||||
|
||||
enum OwnerDrawnComboBoxPaintingFlags
|
||||
{
|
||||
ODCB_PAINTING_DISABLED = 0x0004,
|
||||
};
|
||||
|
||||
bool BitmapComboBox::MSWOnDraw(WXDRAWITEMSTRUCT* item)
|
||||
{
|
||||
LPDRAWITEMSTRUCT lpDrawItem = (LPDRAWITEMSTRUCT)item;
|
||||
int pos = lpDrawItem->itemID;
|
||||
|
||||
// Draw default for item -1, which means 'focus rect only'
|
||||
if (pos == -1)
|
||||
return false;
|
||||
|
||||
int flags = 0;
|
||||
if (lpDrawItem->itemState & ODS_COMBOBOXEDIT)
|
||||
flags |= wxODCB_PAINTING_CONTROL;
|
||||
if (lpDrawItem->itemState & ODS_SELECTED)
|
||||
flags |= wxODCB_PAINTING_SELECTED;
|
||||
if (lpDrawItem->itemState & ODS_DISABLED)
|
||||
flags |= ODCB_PAINTING_DISABLED;
|
||||
|
||||
wxPaintDCEx dc(this, lpDrawItem->hDC);
|
||||
wxRect rect = wxRectFromRECT(lpDrawItem->rcItem);
|
||||
|
||||
DrawBackground_(dc, rect, pos, flags);
|
||||
|
||||
wxString text;
|
||||
|
||||
if (flags & wxODCB_PAINTING_CONTROL)
|
||||
{
|
||||
// Don't draw anything in the editable selection field.
|
||||
//if (!HasFlag(wxCB_READONLY))
|
||||
// return true;
|
||||
|
||||
pos = GetSelection();
|
||||
// Skip drawing if there is nothing selected.
|
||||
if (pos < 0)
|
||||
return true;
|
||||
|
||||
text = GetValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
text = GetString(pos);
|
||||
}
|
||||
|
||||
wxBitmapComboBoxBase::DrawItem(dc, rect, pos, text, flags);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BitmapComboBox::DrawBackground_(wxDC& dc, const wxRect& rect, int WXUNUSED(item), int flags) const
|
||||
{
|
||||
if (flags & wxODCB_PAINTING_SELECTED)
|
||||
{
|
||||
const int vSizeDec = 0; // Vertical size reduction of selection rectangle edges
|
||||
|
||||
dc.SetTextForeground(wxGetApp().get_label_highlight_clr());
|
||||
|
||||
wxColour selCol = wxGetApp().get_highlight_default_clr();
|
||||
dc.SetPen(selCol);
|
||||
dc.SetBrush(selCol);
|
||||
dc.DrawRectangle(rect.x,
|
||||
rect.y + vSizeDec,
|
||||
rect.width,
|
||||
rect.height - (vSizeDec * 2));
|
||||
}
|
||||
else
|
||||
{
|
||||
dc.SetTextForeground(flags & ODCB_PAINTING_DISABLED ? wxColour(108,108,108) : wxGetApp().get_label_clr_default());
|
||||
|
||||
wxColour selCol = flags & ODCB_PAINTING_DISABLED ?
|
||||
#ifdef _MSW_DARK_MODE
|
||||
wxRGBToColour(NppDarkMode::GetSofterBackgroundColor()) :
|
||||
#else
|
||||
wxGetApp().get_highlight_default_clr() :
|
||||
#endif
|
||||
wxGetApp().get_window_default_clr();
|
||||
dc.SetPen(selCol);
|
||||
dc.SetBrush(selCol);
|
||||
dc.DrawRectangle(rect);
|
||||
}
|
||||
}
|
||||
|
||||
void BitmapComboBox::Rescale()
|
||||
{
|
||||
// Next workaround: To correct scaling of a BitmapCombobox
|
||||
// we need to refill control with new bitmaps
|
||||
const wxString selection = this->GetValue();
|
||||
std::vector<wxString> items;
|
||||
for (size_t i = 0; i < GetCount(); i++)
|
||||
items.push_back(GetString(i));
|
||||
|
||||
this->Clear();
|
||||
for (const wxString& item : items)
|
||||
Append(item);
|
||||
this->SetValue(selection);
|
||||
}
|
||||
#endif
|
||||
|
||||
}}
|
||||
|
||||
64
src/slic3r/GUI/BitmapComboBox.hpp
Normal file
64
src/slic3r/GUI/BitmapComboBox.hpp
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
#ifndef slic3r_BitmapComboBox_hpp_
|
||||
#define slic3r_BitmapComboBox_hpp_
|
||||
|
||||
#include <wx/bmpcbox.h>
|
||||
#include <wx/gdicmn.h>
|
||||
|
||||
#include "GUI_Utils.hpp"
|
||||
|
||||
// ---------------------------------
|
||||
// *** BitmapComboBox ***
|
||||
// ---------------------------------
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
// BitmapComboBox used to presets list on Sidebar and Tabs
|
||||
class BitmapComboBox : public wxBitmapComboBox
|
||||
{
|
||||
public:
|
||||
BitmapComboBox(wxWindow* parent,
|
||||
wxWindowID id = wxID_ANY,
|
||||
const wxString& value = wxEmptyString,
|
||||
const wxPoint& pos = wxDefaultPosition,
|
||||
const wxSize& size = wxDefaultSize,
|
||||
int n = 0,
|
||||
const wxString choices[] = NULL,
|
||||
long style = 0);
|
||||
~BitmapComboBox();
|
||||
|
||||
#ifdef _WIN32
|
||||
int Append(const wxString& item);
|
||||
#endif
|
||||
int Append(const wxString& item, const wxBitmap& bitmap)
|
||||
{
|
||||
return wxBitmapComboBox::Append(item, bitmap);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
#ifdef __APPLE__
|
||||
/* For PresetComboBox we use bitmaps that are created from images that are already scaled appropriately for Retina
|
||||
* (Contrary to the intuition, the `scale` argument for Bitmap's constructor doesn't mean
|
||||
* "please scale this to such and such" but rather
|
||||
* "the wxImage is already sized for backing scale such and such". )
|
||||
* Unfortunately, the constructor changes the size of wxBitmap too.
|
||||
* Thus We need to use unscaled size value for bitmaps that we use
|
||||
* to avoid scaled size of control items.
|
||||
* For this purpose control drawing methods and
|
||||
* control size calculation methods (virtual) are overridden.
|
||||
**/
|
||||
bool OnAddBitmap(const wxBitmap& bitmap) override;
|
||||
void OnDrawItem(wxDC& dc, const wxRect& rect, int item, int flags) const override;
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
bool MSWOnDraw(WXDRAWITEMSTRUCT* item) override;
|
||||
void DrawBackground_(wxDC& dc, const wxRect& rect, int WXUNUSED(item), int flags) const;
|
||||
public:
|
||||
void Rescale();
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
}}
|
||||
#endif
|
||||
203
src/slic3r/GUI/Calibration.cpp
Normal file
203
src/slic3r/GUI/Calibration.cpp
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
#include "Calibration.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Thread.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI_Preview.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "format.hpp"
|
||||
#include "Widgets/RoundedRectangle.hpp"
|
||||
#include "Widgets/StaticBox.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
CalibrationDialog::CalibrationDialog(Plater *plater)
|
||||
: DPIDialog(static_cast<wxWindow *>(wxGetApp().mainframe), wxID_ANY, _L("Calibration"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX)
|
||||
{
|
||||
this->SetDoubleBuffered(true);
|
||||
std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico") % resources_dir()).str();
|
||||
SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO));
|
||||
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
wxBoxSizer *m_sizer_main = new wxBoxSizer(wxVERTICAL);
|
||||
auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL);
|
||||
m_line_top->SetBackgroundColour(wxColour(166, 169, 170));
|
||||
m_sizer_main->Add(m_line_top, 0, wxEXPAND, 0);
|
||||
|
||||
wxBoxSizer *sizer_body = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto body_panel = new wxPanel(this, wxID_ANY);
|
||||
|
||||
body_panel->SetBackgroundColour(*wxWHITE);
|
||||
auto cali_left_panel = new StaticBox(body_panel, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(303), FromDIP(243)));
|
||||
cali_left_panel->SetBackgroundColor(wxColour(0xF8, 0xF8, 0xF8));
|
||||
cali_left_panel->SetBorderColor(wxColour(0xF8, 0xF8, 0xF8));
|
||||
|
||||
wxBoxSizer *cali_left_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
cali_left_sizer->Add(0, 0, 0, wxTOP, FromDIP(25));
|
||||
|
||||
auto cali_left_text_top = new wxStaticText(cali_left_panel, wxID_ANY, _L("Calibration program"), wxDefaultPosition, wxDefaultSize, 0);
|
||||
cali_left_text_top->SetFont(::Label::Head_14);
|
||||
cali_left_text_top->Wrap(-1);
|
||||
cali_left_text_top->SetForegroundColour(wxColour(0x32, 0x3A, 0x3D));
|
||||
cali_left_text_top->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8));
|
||||
|
||||
cali_left_sizer->Add(cali_left_text_top, 0, wxLEFT, FromDIP(15));
|
||||
|
||||
cali_left_sizer->Add(0, 0, 0, wxTOP, FromDIP(5));
|
||||
|
||||
auto cali_left_text_body =
|
||||
new wxStaticText(cali_left_panel, wxID_ANY,
|
||||
_L("The calibration program detects the status of your device automatically to minimize deviation.\nIt keeps the device performing optimally."),
|
||||
wxDefaultPosition, wxSize(FromDIP(260), -1), 0);
|
||||
cali_left_text_body->Wrap(FromDIP(260));
|
||||
cali_left_text_body->SetForegroundColour(wxColour(0x6B, 0x6B, 0x6B));
|
||||
cali_left_text_body->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8));
|
||||
cali_left_text_body->SetFont(::Label::Body_13);
|
||||
cali_left_sizer->Add(cali_left_text_body, 0, wxLEFT, FromDIP(15));
|
||||
|
||||
cali_left_sizer->Add(0, 0, 0, wxTOP, FromDIP(20));
|
||||
|
||||
/* auto cali_left_text_top_prepar = new wxStaticText(cali_left_panel, wxID_ANY, _L("Preparation before calibration"), wxDefaultPosition, wxDefaultSize, 0);
|
||||
cali_left_text_top_prepar->SetFont(::Label::Head_14);
|
||||
cali_left_text_top_prepar->SetForegroundColour(wxColour(0x32, 0x3A, 0x3D));
|
||||
cali_left_text_top_prepar->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8));
|
||||
cali_left_text_top_prepar->Wrap(-1);
|
||||
cali_left_sizer->Add(cali_left_text_top_prepar, 0, wxLEFT, FromDIP(15));
|
||||
|
||||
cali_left_sizer->Add(0, 0, 0, wxTOP, FromDIP(5));
|
||||
|
||||
auto cali_left_text_body_prepar =
|
||||
new wxStaticText(cali_left_panel, wxID_ANY,
|
||||
_L("Before calibration, please make sure a filament is loaded and its nozzle temperature and bed temperature is set in Feeding lab."),
|
||||
wxDefaultPosition, wxSize(FromDIP(260), -1), 0); cali_left_text_body_prepar->Wrap(FromDIP(260)); cali_left_text_body_prepar->SetFont(::Label::Body_13);
|
||||
cali_left_text_body_prepar->SetForegroundColour(wxColour(0x6B, 0x6B, 0x6B));
|
||||
cali_left_text_body_prepar->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8));
|
||||
cali_left_sizer->Add(cali_left_text_body_prepar, 0, wxLEFT, FromDIP(15));*/
|
||||
|
||||
cali_left_panel->SetSizer(cali_left_sizer);
|
||||
cali_left_panel->Layout();
|
||||
sizer_body->Add(cali_left_panel, 0, wxALIGN_CENTER, 0);
|
||||
|
||||
sizer_body->Add(0, 0, 0, wxLEFT, FromDIP(8));
|
||||
|
||||
wxBoxSizer *cali_right_sizer_h = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxBoxSizer *cali_right_sizer_v = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto cali_right_panel = new StaticBox(body_panel, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(182), FromDIP(160)));
|
||||
cali_right_panel->SetBackgroundColor(wxColour(0xF8, 0xF8, 0xF8));
|
||||
cali_right_panel->SetBorderColor(wxColour(0xF8, 0xF8, 0xF8));
|
||||
|
||||
auto cali_text_right_top = new wxStaticText(cali_right_panel, wxID_ANY, _L("Calibration Flow"), wxDefaultPosition, wxDefaultSize, 0);
|
||||
cali_text_right_top->Wrap(-1);
|
||||
cali_text_right_top->SetFont(::Label::Head_14);
|
||||
cali_text_right_top->SetForegroundColour(wxColour(0x00, 0xAE, 0x42));
|
||||
cali_text_right_top->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8));
|
||||
|
||||
auto staticline = new ::StaticLine(cali_right_panel);
|
||||
staticline->SetLineColour(wxColour(0x00, 0xAE, 0x42));
|
||||
|
||||
m_calibration_flow = new StepIndicator(cali_right_panel, wxID_ANY);
|
||||
StateColor bg_color(std::pair<wxColour, int>(wxColour(248, 248, 248), StateColor::Normal));
|
||||
m_calibration_flow->SetBackgroundColor(bg_color);
|
||||
m_calibration_flow->SetFont(Label::Body_12);
|
||||
m_calibration_flow->SetMinSize(wxSize(FromDIP(170), FromDIP(160)));
|
||||
m_calibration_flow->SetSize(wxSize(FromDIP(170), FromDIP(160)));
|
||||
|
||||
StateColor btn_bg_green(std::pair<wxColour, int>(AMS_CONTROL_DISABLE_COLOUR, StateColor::Disabled), std::pair<wxColour, int>(wxColour(27, 136, 68), StateColor::Pressed),
|
||||
std::pair<wxColour, int>(wxColour(61, 203, 115), StateColor::Hovered), std::pair<wxColour, int>(AMS_CONTROL_BRAND_COLOUR, StateColor::Normal));
|
||||
StateColor btn_bd_green(std::pair<wxColour, int>(AMS_CONTROL_WHITE_COLOUR, StateColor::Disabled), std::pair<wxColour, int>(AMS_CONTROL_BRAND_COLOUR, StateColor::Enabled));
|
||||
|
||||
m_calibration_btn = new Button(cali_right_panel, _L("Start Calibration"));
|
||||
m_calibration_btn->SetBackgroundColor(btn_bg_green);
|
||||
m_calibration_btn->SetBorderColor(btn_bd_green);
|
||||
m_calibration_btn->SetTextColor(*wxWHITE);
|
||||
m_calibration_btn->SetSize(wxSize(FromDIP(128), FromDIP(26)));
|
||||
m_calibration_btn->SetMinSize(wxSize(FromDIP(128), FromDIP(26)));
|
||||
|
||||
cali_right_sizer_v->Add(cali_text_right_top, 0, wxALIGN_CENTER, 0);
|
||||
cali_right_sizer_v->Add(0, 0, 0, wxTOP, FromDIP(7));
|
||||
cali_right_sizer_v->Add(staticline, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(10));
|
||||
cali_right_sizer_v->Add(0, 0, 0, wxTOP, FromDIP(9));
|
||||
cali_right_sizer_v->Add(m_calibration_flow, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, FromDIP(6));
|
||||
cali_right_sizer_v->Add(0, 0, 0, wxTOP, FromDIP(10));
|
||||
cali_right_sizer_v->Add(m_calibration_btn, 0, wxALIGN_CENTER, 0);
|
||||
|
||||
cali_right_sizer_h->Add(cali_right_sizer_v, 0, wxALIGN_CENTER, 0);
|
||||
cali_right_panel->SetSizer(cali_right_sizer_h);
|
||||
cali_right_panel->Layout();
|
||||
|
||||
sizer_body->Add(cali_right_panel, 0, wxALIGN_CENTER_HORIZONTAL | wxEXPAND, 0);
|
||||
|
||||
body_panel->SetSizer(sizer_body);
|
||||
body_panel->Layout();
|
||||
|
||||
m_sizer_main->Add(body_panel, 0, wxEXPAND | wxALL, FromDIP(25));
|
||||
SetSizer(m_sizer_main);
|
||||
Layout();
|
||||
Fit();
|
||||
Centre(wxBOTH);
|
||||
|
||||
Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent &evt) { Hide(); });
|
||||
|
||||
m_calibration_btn->Bind(wxEVT_LEFT_DOWN, &CalibrationDialog::on_start_calibration, this);
|
||||
}
|
||||
|
||||
CalibrationDialog::~CalibrationDialog() {}
|
||||
|
||||
void CalibrationDialog::on_dpi_changed(const wxRect &suggested_rect) {}
|
||||
|
||||
void CalibrationDialog::update_cali(MachineObject *obj)
|
||||
{
|
||||
if (!obj) return;
|
||||
|
||||
// in printing
|
||||
if (obj->is_in_printing()) {
|
||||
m_calibration_flow->DeleteAllItems();
|
||||
m_calibration_btn->Disable();
|
||||
return;
|
||||
} else {
|
||||
m_calibration_btn->Enable();
|
||||
if (!obj->is_in_calibration()) {
|
||||
m_calibration_flow->DeleteAllItems();
|
||||
m_calibration_btn->SetLabel(_L("Start Calibration"));
|
||||
} else {
|
||||
m_calibration_btn->Disable();
|
||||
m_calibration_btn->SetLabel(_L("Calibrating"));
|
||||
|
||||
if (is_stage_list_info_changed(obj)) {
|
||||
// change items if stage_list_info changed
|
||||
m_calibration_flow->DeleteAllItems();
|
||||
for (int i = 0; i < obj->stage_list_info.size(); i++) { m_calibration_flow->AppendItem(get_stage_string(obj->stage_list_info[i])); }
|
||||
}
|
||||
int index = obj->get_curr_stage_idx();
|
||||
m_calibration_flow->SelectItem(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CalibrationDialog::is_stage_list_info_changed(MachineObject *obj)
|
||||
{
|
||||
if (!obj) return true;
|
||||
|
||||
if (last_stage_list_info.size() != obj->stage_list_info.size()) return true;
|
||||
|
||||
for (int i = 0; i < last_stage_list_info.size(); i++) {
|
||||
if (last_stage_list_info[i] != obj->stage_list_info[i]) return true;
|
||||
}
|
||||
last_stage_list_info = obj->stage_list_info;
|
||||
return false;
|
||||
}
|
||||
|
||||
void CalibrationDialog::on_start_calibration(wxMouseEvent &event)
|
||||
{
|
||||
if (m_obj) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "on_start_calibration";
|
||||
m_obj->command_start_calibration();
|
||||
}
|
||||
}
|
||||
|
||||
void CalibrationDialog::update_machine_obj(MachineObject *obj) { m_obj = obj; }
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
57
src/slic3r/GUI/Calibration.hpp
Normal file
57
src/slic3r/GUI/Calibration.hpp
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
#ifndef slic3r_GUI_Calibration_hpp_
|
||||
#define slic3r_GUI_Calibration_hpp_
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/intl.h>
|
||||
#include <wx/collpane.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/hyperlink.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/popupwin.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/wrapsizer.h>
|
||||
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "DeviceManager.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "Widgets/Label.hpp"
|
||||
#include "Widgets/Button.hpp"
|
||||
#include "Widgets/StepCtrl.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
class CalibrationDialog : public DPIDialog
|
||||
{
|
||||
public:
|
||||
CalibrationDialog(Plater *plater = nullptr);
|
||||
~CalibrationDialog();
|
||||
void on_dpi_changed(const wxRect &suggested_rect) override;
|
||||
|
||||
StepIndicator *m_calibration_flow;
|
||||
Button * m_calibration_btn;
|
||||
MachineObject *m_obj;
|
||||
|
||||
std::vector<int> last_stage_list_info;
|
||||
int m_state{0};
|
||||
void update_cali(MachineObject *obj);
|
||||
bool is_stage_list_info_changed(MachineObject *obj);
|
||||
void on_start_calibration(wxMouseEvent &event);
|
||||
void update_machine_obj(MachineObject *obj);
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
#endif
|
||||
589
src/slic3r/GUI/Camera.cpp
Normal file
589
src/slic3r/GUI/Camera.cpp
Normal file
|
|
@ -0,0 +1,589 @@
|
|||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
|
||||
#include "Camera.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#if ENABLE_CAMERA_STATISTICS
|
||||
#include "Mouse3DController.hpp"
|
||||
#include "Plater.hpp"
|
||||
#endif // ENABLE_CAMERA_STATISTICS
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
const double Camera::DefaultDistance = 1000.0;
|
||||
const double Camera::DefaultZoomToBoxMarginFactor = 1.025;
|
||||
const double Camera::DefaultZoomToVolumesMarginFactor = 1.025;
|
||||
double Camera::FrustrumMinZRange = 50.0;
|
||||
double Camera::FrustrumMinNearZ = 100.0;
|
||||
double Camera::FrustrumZMargin = 10.0;
|
||||
double Camera::MaxFovDeg = 60.0;
|
||||
|
||||
std::string Camera::get_type_as_string() const
|
||||
{
|
||||
switch (m_type)
|
||||
{
|
||||
case EType::Unknown: return "unknown";
|
||||
case EType::Perspective: return "perspective";
|
||||
default:
|
||||
case EType::Ortho: return "orthographic";
|
||||
};
|
||||
}
|
||||
|
||||
void Camera::set_type(EType type)
|
||||
{
|
||||
if (m_type != type && (type == EType::Ortho || type == EType::Perspective)) {
|
||||
m_type = type;
|
||||
if (m_update_config_on_type_change_enabled) {
|
||||
wxGetApp().app_config->set_bool("use_perspective_camera", m_type == EType::Perspective);
|
||||
wxGetApp().app_config->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Camera::select_next_type()
|
||||
{
|
||||
unsigned char next = (unsigned char)m_type + 1;
|
||||
if (next == (unsigned char)EType::Num_types)
|
||||
next = 1;
|
||||
|
||||
set_type((EType)next);
|
||||
}
|
||||
|
||||
void Camera::set_target(const Vec3d& target)
|
||||
{
|
||||
//BBS do not check validation
|
||||
//const Vec3d new_target = validate_target(target);
|
||||
const Vec3d new_target = target;
|
||||
const Vec3d new_displacement = new_target - m_target;
|
||||
if (!new_displacement.isApprox(Vec3d::Zero())) {
|
||||
m_target = new_target;
|
||||
m_view_matrix.translate(-new_displacement);
|
||||
}
|
||||
}
|
||||
|
||||
void Camera::set_zoom(double zoom)
|
||||
{
|
||||
// Don't allow to zoom too far outside the scene.
|
||||
const double zoom_min = min_zoom();
|
||||
if (zoom_min > 0.0)
|
||||
zoom = std::max(zoom, zoom_min);
|
||||
|
||||
// Don't allow to zoom too close to the scene.
|
||||
m_zoom = std::min(zoom, max_zoom());
|
||||
}
|
||||
|
||||
void Camera::select_view(const std::string& direction)
|
||||
{
|
||||
if (direction == "iso")
|
||||
set_default_orientation();
|
||||
else if (direction == "left")
|
||||
look_at(m_target - m_distance * Vec3d::UnitX(), m_target, Vec3d::UnitZ());
|
||||
else if (direction == "right")
|
||||
look_at(m_target + m_distance * Vec3d::UnitX(), m_target, Vec3d::UnitZ());
|
||||
else if (direction == "top")
|
||||
look_at(m_target + m_distance * Vec3d::UnitZ(), m_target, Vec3d::UnitY());
|
||||
else if (direction == "bottom")
|
||||
look_at(m_target - m_distance * Vec3d::UnitZ(), m_target, -Vec3d::UnitY());
|
||||
else if (direction == "front")
|
||||
look_at(m_target - m_distance * Vec3d::UnitY(), m_target, Vec3d::UnitZ());
|
||||
else if (direction == "rear")
|
||||
look_at(m_target + m_distance * Vec3d::UnitY(), m_target, Vec3d::UnitZ());
|
||||
else if (direction == "topfront")
|
||||
look_at(m_target - 0.707 * m_distance * Vec3d::UnitY() + 0.707 * m_distance * Vec3d::UnitZ(), m_target, Vec3d::UnitY() + Vec3d::UnitZ());
|
||||
}
|
||||
|
||||
double Camera::get_fov() const
|
||||
{
|
||||
switch (m_type)
|
||||
{
|
||||
case EType::Perspective:
|
||||
return 2.0 * Geometry::rad2deg(std::atan(1.0 / m_projection_matrix.matrix()(1, 1)));
|
||||
default:
|
||||
case EType::Ortho:
|
||||
return 0.0;
|
||||
};
|
||||
}
|
||||
|
||||
void Camera::apply_viewport(int x, int y, unsigned int w, unsigned int h)
|
||||
{
|
||||
glsafe(::glViewport(0, 0, w, h));
|
||||
glsafe(::glGetIntegerv(GL_VIEWPORT, m_viewport.data()));
|
||||
}
|
||||
|
||||
void Camera::apply_view_matrix()
|
||||
{
|
||||
glsafe(::glMatrixMode(GL_MODELVIEW));
|
||||
glsafe(::glLoadIdentity());
|
||||
glsafe(::glMultMatrixd(m_view_matrix.data()));
|
||||
}
|
||||
|
||||
void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double far_z)
|
||||
{
|
||||
double w = 0.0;
|
||||
double h = 0.0;
|
||||
|
||||
const double old_distance = m_distance;
|
||||
m_frustrum_zs = calc_tight_frustrum_zs_around(box);
|
||||
if (m_distance != old_distance)
|
||||
// the camera has been moved re-apply view matrix
|
||||
apply_view_matrix();
|
||||
|
||||
if (near_z > 0.0)
|
||||
m_frustrum_zs.first = std::max(std::min(m_frustrum_zs.first, near_z), FrustrumMinNearZ);
|
||||
|
||||
if (far_z > 0.0)
|
||||
m_frustrum_zs.second = std::max(m_frustrum_zs.second, far_z);
|
||||
|
||||
w = 0.5 * (double)m_viewport[2];
|
||||
h = 0.5 * (double)m_viewport[3];
|
||||
|
||||
const double inv_zoom = get_inv_zoom();
|
||||
w *= inv_zoom;
|
||||
h *= inv_zoom;
|
||||
|
||||
switch (m_type)
|
||||
{
|
||||
default:
|
||||
case EType::Ortho:
|
||||
{
|
||||
m_gui_scale = 1.0;
|
||||
break;
|
||||
}
|
||||
case EType::Perspective:
|
||||
{
|
||||
// scale near plane to keep w and h constant on the plane at z = m_distance
|
||||
const double scale = m_frustrum_zs.first / m_distance;
|
||||
w *= scale;
|
||||
h *= scale;
|
||||
m_gui_scale = scale;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
glsafe(::glMatrixMode(GL_PROJECTION));
|
||||
glsafe(::glLoadIdentity());
|
||||
|
||||
switch (m_type)
|
||||
{
|
||||
default:
|
||||
case EType::Ortho:
|
||||
{
|
||||
glsafe(::glOrtho(-w, w, -h, h, m_frustrum_zs.first, m_frustrum_zs.second));
|
||||
break;
|
||||
}
|
||||
case EType::Perspective:
|
||||
{
|
||||
glsafe(::glFrustum(-w, w, -h, h, m_frustrum_zs.first, m_frustrum_zs.second));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
glsafe(::glGetDoublev(GL_PROJECTION_MATRIX, m_projection_matrix.data()));
|
||||
glsafe(::glMatrixMode(GL_MODELVIEW));
|
||||
}
|
||||
|
||||
void Camera::zoom_to_box(const BoundingBoxf3& box, double margin_factor)
|
||||
{
|
||||
// Calculate the zoom factor needed to adjust the view around the given box.
|
||||
const double zoom = calc_zoom_to_bounding_box_factor(box, margin_factor);
|
||||
if (zoom > 0.0) {
|
||||
m_zoom = zoom;
|
||||
// center view around box center
|
||||
set_target(box.center());
|
||||
}
|
||||
}
|
||||
|
||||
void Camera::zoom_to_volumes(const GLVolumePtrs& volumes, double margin_factor)
|
||||
{
|
||||
Vec3d center;
|
||||
const double zoom = calc_zoom_to_volumes_factor(volumes, center, margin_factor);
|
||||
if (zoom > 0.0) {
|
||||
m_zoom = zoom;
|
||||
// center view around the calculated center
|
||||
set_target(center);
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_CAMERA_STATISTICS
|
||||
void Camera::debug_render() const
|
||||
{
|
||||
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||
imgui.begin(std::string("Camera statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
|
||||
|
||||
std::string type = get_type_as_string();
|
||||
if (wxGetApp().plater()->get_mouse3d_controller().connected()
|
||||
#ifdef SUPPORT_FREE_CAMERA
|
||||
|| (wxGetApp().app_config->get("use_free_camera") == "1")
|
||||
#endif
|
||||
)
|
||||
type += "/free";
|
||||
else
|
||||
type += "/constrained";
|
||||
|
||||
Vec3f position = get_position().cast<float>();
|
||||
Vec3f target = m_target.cast<float>();
|
||||
float distance = (float)get_distance();
|
||||
float zenit = (float)m_zenit;
|
||||
Vec3f forward = get_dir_forward().cast<float>();
|
||||
Vec3f right = get_dir_right().cast<float>();
|
||||
Vec3f up = get_dir_up().cast<float>();
|
||||
float nearZ = (float)m_frustrum_zs.first;
|
||||
float farZ = (float)m_frustrum_zs.second;
|
||||
float deltaZ = farZ - nearZ;
|
||||
float zoom = (float)m_zoom;
|
||||
float fov = (float)get_fov();
|
||||
std::array<int, 4>viewport = get_viewport();
|
||||
float gui_scale = (float)get_gui_scale();
|
||||
|
||||
ImGui::InputText("Type", type.data(), type.length(), ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::Separator();
|
||||
ImGui::InputFloat3("Position", position.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::InputFloat3("Target", target.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::InputFloat("Distance", &distance, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::Separator();
|
||||
ImGui::InputFloat("Zenit", &zenit, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::Separator();
|
||||
ImGui::InputFloat3("Forward", forward.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::InputFloat3("Right", right.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::InputFloat3("Up", up.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::Separator();
|
||||
ImGui::InputFloat("Near Z", &nearZ, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::InputFloat("Far Z", &farZ, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::InputFloat("Delta Z", &deltaZ, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::Separator();
|
||||
ImGui::InputFloat("Zoom", &zoom, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::InputFloat("Fov", &fov, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::Separator();
|
||||
ImGui::InputInt4("Viewport", viewport.data(), ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::Separator();
|
||||
ImGui::InputFloat("GUI scale", &gui_scale, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly);
|
||||
imgui.end();
|
||||
}
|
||||
#endif // ENABLE_CAMERA_STATISTICS
|
||||
|
||||
void Camera::rotate_on_sphere_with_target(double delta_azimut_rad, double delta_zenit_rad, bool apply_limits, Vec3d target)
|
||||
{
|
||||
m_zenit += Geometry::rad2deg(delta_zenit_rad);
|
||||
if (apply_limits) {
|
||||
if (m_zenit > 90.0f) {
|
||||
delta_zenit_rad -= Geometry::deg2rad(m_zenit - 90.0f);
|
||||
m_zenit = 90.0f;
|
||||
}
|
||||
else if (m_zenit < -90.0f) {
|
||||
delta_zenit_rad -= Geometry::deg2rad(m_zenit + 90.0f);
|
||||
m_zenit = -90.0f;
|
||||
}
|
||||
}
|
||||
|
||||
Vec3d translation = m_view_matrix.translation() + m_view_rotation * target;
|
||||
auto rot_z = Eigen::AngleAxisd(delta_azimut_rad, Vec3d::UnitZ());
|
||||
m_view_rotation *= rot_z * Eigen::AngleAxisd(delta_zenit_rad, rot_z.inverse() * get_dir_right());
|
||||
m_view_rotation.normalize();
|
||||
m_view_matrix.fromPositionOrientationScale(m_view_rotation * (-target) + translation, m_view_rotation, Vec3d(1., 1., 1.));
|
||||
}
|
||||
|
||||
|
||||
void Camera::rotate_on_sphere(double delta_azimut_rad, double delta_zenit_rad, bool apply_limits)
|
||||
{
|
||||
m_zenit += Geometry::rad2deg(delta_zenit_rad);
|
||||
if (apply_limits) {
|
||||
if (m_zenit > 90.0f) {
|
||||
delta_zenit_rad -= Geometry::deg2rad(m_zenit - 90.0f);
|
||||
m_zenit = 90.0f;
|
||||
}
|
||||
else if (m_zenit < -90.0f) {
|
||||
delta_zenit_rad -= Geometry::deg2rad(m_zenit + 90.0f);
|
||||
m_zenit = -90.0f;
|
||||
}
|
||||
}
|
||||
|
||||
const Vec3d translation = m_view_matrix.translation() + m_view_rotation * m_target;
|
||||
const auto rot_z = Eigen::AngleAxisd(delta_azimut_rad, Vec3d::UnitZ());
|
||||
m_view_rotation *= rot_z * Eigen::AngleAxisd(delta_zenit_rad, rot_z.inverse() * get_dir_right());
|
||||
m_view_rotation.normalize();
|
||||
m_view_matrix.fromPositionOrientationScale(m_view_rotation * (- m_target) + translation, m_view_rotation, Vec3d(1., 1., 1.));
|
||||
}
|
||||
|
||||
//BBS rotate with target
|
||||
void Camera::rotate_local_with_target(const Vec3d& rotation_rad, Vec3d target)
|
||||
{
|
||||
double angle = rotation_rad.norm();
|
||||
if (std::abs(angle) > EPSILON) {
|
||||
Vec3d translation = m_view_matrix.translation() + m_view_rotation * target;
|
||||
Vec3d axis = m_view_rotation.conjugate() * rotation_rad.normalized();
|
||||
m_view_rotation *= Eigen::Quaterniond(Eigen::AngleAxisd(angle, axis));
|
||||
m_view_rotation.normalize();
|
||||
m_view_matrix.fromPositionOrientationScale(m_view_rotation * (-target) + translation, m_view_rotation, Vec3d(1., 1., 1.));
|
||||
update_zenit();
|
||||
}
|
||||
}
|
||||
|
||||
// Virtual trackball, rotate around an axis, where the eucledian norm of the axis gives the rotation angle in radians.
|
||||
void Camera::rotate_local_around_target(const Vec3d& rotation_rad)
|
||||
{
|
||||
const double angle = rotation_rad.norm();
|
||||
if (std::abs(angle) > EPSILON) {
|
||||
const Vec3d translation = m_view_matrix.translation() + m_view_rotation * m_target;
|
||||
const Vec3d axis = m_view_rotation.conjugate() * rotation_rad.normalized();
|
||||
m_view_rotation *= Eigen::Quaterniond(Eigen::AngleAxisd(angle, axis));
|
||||
m_view_rotation.normalize();
|
||||
m_view_matrix.fromPositionOrientationScale(m_view_rotation * (-m_target) + translation, m_view_rotation, Vec3d(1., 1., 1.));
|
||||
update_zenit();
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<double, double> Camera::calc_tight_frustrum_zs_around(const BoundingBoxf3& box)
|
||||
{
|
||||
std::pair<double, double> ret;
|
||||
auto& [near_z, far_z] = ret;
|
||||
|
||||
// box in eye space
|
||||
const BoundingBoxf3 eye_box = box.transformed(m_view_matrix);
|
||||
near_z = -eye_box.max(2);
|
||||
far_z = -eye_box.min(2);
|
||||
|
||||
// apply margin
|
||||
near_z -= FrustrumZMargin;
|
||||
far_z += FrustrumZMargin;
|
||||
|
||||
// ensure min size
|
||||
if (far_z - near_z < FrustrumMinZRange) {
|
||||
const double mid_z = 0.5 * (near_z + far_z);
|
||||
const double half_size = 0.5 * FrustrumMinZRange;
|
||||
near_z = mid_z - half_size;
|
||||
far_z = mid_z + half_size;
|
||||
}
|
||||
|
||||
if (near_z < FrustrumMinNearZ) {
|
||||
const double delta = FrustrumMinNearZ - near_z;
|
||||
set_distance(m_distance + delta);
|
||||
near_z += delta;
|
||||
far_z += delta;
|
||||
}
|
||||
// The following is commented out because it causes flickering of the 3D scene GUI
|
||||
// when the bounding box of the scene gets large enough
|
||||
// We need to introduce some smarter code to move the camera back and forth in such case
|
||||
// else if (near_z > 2.0 * FrustrumMinNearZ && m_distance > DefaultDistance) {
|
||||
// float delta = m_distance - DefaultDistance;
|
||||
// set_distance(DefaultDistance);
|
||||
// near_z -= delta;
|
||||
// far_z -= delta;
|
||||
// }
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, double margin_factor) const
|
||||
{
|
||||
const double max_bb_size = box.max_size();
|
||||
if (max_bb_size == 0.0)
|
||||
return -1.0;
|
||||
|
||||
// project the box vertices on a plane perpendicular to the camera forward axis
|
||||
// then calculates the vertices coordinate on this plane along the camera xy axes
|
||||
|
||||
const Vec3d right = get_dir_right();
|
||||
const Vec3d up = get_dir_up();
|
||||
const Vec3d forward = get_dir_forward();
|
||||
const Vec3d bb_center = box.center();
|
||||
|
||||
// box vertices in world space
|
||||
const std::vector<Vec3d> vertices = {
|
||||
box.min,
|
||||
{ box.max(0), box.min(1), box.min(2) },
|
||||
{ box.max(0), box.max(1), box.min(2) },
|
||||
{ box.min(0), box.max(1), box.min(2) },
|
||||
{ box.min(0), box.min(1), box.max(2) },
|
||||
{ box.max(0), box.min(1), box.max(2) },
|
||||
box.max,
|
||||
{ box.min(0), box.max(1), box.max(2) }
|
||||
};
|
||||
|
||||
double min_x = DBL_MAX;
|
||||
double min_y = DBL_MAX;
|
||||
double max_x = -DBL_MAX;
|
||||
double max_y = -DBL_MAX;
|
||||
|
||||
for (const Vec3d& v : vertices) {
|
||||
// project vertex on the plane perpendicular to camera forward axis
|
||||
const Vec3d pos = v - bb_center;
|
||||
const Vec3d proj_on_plane = pos - pos.dot(forward) * forward;
|
||||
|
||||
// calculates vertex coordinate along camera xy axes
|
||||
const double x_on_plane = proj_on_plane.dot(right);
|
||||
const double y_on_plane = proj_on_plane.dot(up);
|
||||
|
||||
min_x = std::min(min_x, x_on_plane);
|
||||
min_y = std::min(min_y, y_on_plane);
|
||||
max_x = std::max(max_x, x_on_plane);
|
||||
max_y = std::max(max_y, y_on_plane);
|
||||
}
|
||||
|
||||
double dx = max_x - min_x;
|
||||
double dy = max_y - min_y;
|
||||
if (dx <= 0.0 || dy <= 0.0)
|
||||
return -1.0f;
|
||||
|
||||
dx *= margin_factor;
|
||||
dy *= margin_factor;
|
||||
|
||||
return std::min((double)m_viewport[2] / dx, (double)m_viewport[3] / dy);
|
||||
}
|
||||
|
||||
double Camera::calc_zoom_to_volumes_factor(const GLVolumePtrs& volumes, Vec3d& center, double margin_factor) const
|
||||
{
|
||||
if (volumes.empty())
|
||||
return -1.0;
|
||||
|
||||
// project the volumes vertices on a plane perpendicular to the camera forward axis
|
||||
// then calculates the vertices coordinate on this plane along the camera xy axes
|
||||
|
||||
const Vec3d right = get_dir_right();
|
||||
const Vec3d up = get_dir_up();
|
||||
const Vec3d forward = get_dir_forward();
|
||||
|
||||
BoundingBoxf3 box;
|
||||
for (const GLVolume* volume : volumes) {
|
||||
box.merge(volume->transformed_bounding_box());
|
||||
}
|
||||
center = box.center();
|
||||
|
||||
double min_x = DBL_MAX;
|
||||
double min_y = DBL_MAX;
|
||||
double max_x = -DBL_MAX;
|
||||
double max_y = -DBL_MAX;
|
||||
|
||||
for (const GLVolume* volume : volumes) {
|
||||
const Transform3d& transform = volume->world_matrix();
|
||||
const TriangleMesh* hull = volume->convex_hull();
|
||||
if (hull == nullptr)
|
||||
continue;
|
||||
|
||||
for (const Vec3f& vertex : hull->its.vertices) {
|
||||
const Vec3d v = transform * vertex.cast<double>();
|
||||
|
||||
// project vertex on the plane perpendicular to camera forward axis
|
||||
const Vec3d pos = v - center;
|
||||
const Vec3d proj_on_plane = pos - pos.dot(forward) * forward;
|
||||
|
||||
// calculates vertex coordinate along camera xy axes
|
||||
const double x_on_plane = proj_on_plane.dot(right);
|
||||
const double y_on_plane = proj_on_plane.dot(up);
|
||||
|
||||
min_x = std::min(min_x, x_on_plane);
|
||||
min_y = std::min(min_y, y_on_plane);
|
||||
max_x = std::max(max_x, x_on_plane);
|
||||
max_y = std::max(max_y, y_on_plane);
|
||||
}
|
||||
}
|
||||
|
||||
center += 0.5 * (max_x + min_x) * right + 0.5 * (max_y + min_y) * up;
|
||||
|
||||
const double dx = margin_factor * (max_x - min_x);
|
||||
const double dy = margin_factor * (max_y - min_y);
|
||||
|
||||
if (dx <= 0.0 || dy <= 0.0)
|
||||
return -1.0f;
|
||||
|
||||
return std::min((double)m_viewport[2] / dx, (double)m_viewport[3] / dy);
|
||||
}
|
||||
|
||||
void Camera::set_distance(double distance)
|
||||
{
|
||||
if (m_distance != distance) {
|
||||
m_view_matrix.translate((distance - m_distance) * get_dir_forward());
|
||||
m_distance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
void Camera::load_camera_view(Camera& cam)
|
||||
{
|
||||
m_target = cam.get_target();
|
||||
m_zoom = cam.get_zoom();
|
||||
m_scene_box = cam.get_scene_box();
|
||||
m_viewport = cam.get_viewport();
|
||||
m_view_matrix = cam.get_view_matrix();
|
||||
m_projection_matrix = cam.get_projection_matrix();
|
||||
m_view_rotation = cam.get_view_rotation();
|
||||
m_frustrum_zs = cam.get_z_range();
|
||||
m_zenit = cam.get_zenit();
|
||||
}
|
||||
|
||||
void Camera::look_at(const Vec3d& position, const Vec3d& target, const Vec3d& up)
|
||||
{
|
||||
const Vec3d unit_z = (position - target).normalized();
|
||||
const Vec3d unit_x = up.cross(unit_z).normalized();
|
||||
const Vec3d unit_y = unit_z.cross(unit_x).normalized();
|
||||
|
||||
m_target = target;
|
||||
m_distance = (position - target).norm();
|
||||
const Vec3d new_position = m_target + m_distance * unit_z;
|
||||
|
||||
m_view_matrix(0, 0) = unit_x(0);
|
||||
m_view_matrix(0, 1) = unit_x(1);
|
||||
m_view_matrix(0, 2) = unit_x(2);
|
||||
m_view_matrix(0, 3) = -unit_x.dot(new_position);
|
||||
|
||||
m_view_matrix(1, 0) = unit_y(0);
|
||||
m_view_matrix(1, 1) = unit_y(1);
|
||||
m_view_matrix(1, 2) = unit_y(2);
|
||||
m_view_matrix(1, 3) = -unit_y.dot(new_position);
|
||||
|
||||
m_view_matrix(2, 0) = unit_z(0);
|
||||
m_view_matrix(2, 1) = unit_z(1);
|
||||
m_view_matrix(2, 2) = unit_z(2);
|
||||
m_view_matrix(2, 3) = -unit_z.dot(new_position);
|
||||
|
||||
m_view_matrix(3, 0) = 0.0;
|
||||
m_view_matrix(3, 1) = 0.0;
|
||||
m_view_matrix(3, 2) = 0.0;
|
||||
m_view_matrix(3, 3) = 1.0;
|
||||
|
||||
// Initialize the rotation quaternion from the rotation submatrix of of m_view_matrix.
|
||||
m_view_rotation = Eigen::Quaterniond(m_view_matrix.matrix().template block<3, 3>(0, 0));
|
||||
m_view_rotation.normalize();
|
||||
|
||||
update_zenit();
|
||||
}
|
||||
|
||||
void Camera::set_default_orientation()
|
||||
{
|
||||
// BBS modify default orientation
|
||||
look_at(m_target - 0.707 * m_distance * Vec3d::UnitY() + 0.707 * m_distance * Vec3d::UnitZ(), m_target, Vec3d::UnitY() + Vec3d::UnitZ());
|
||||
|
||||
/*m_zenit = 45.0f;
|
||||
const double theta_rad = Geometry::deg2rad(-(double)m_zenit);
|
||||
const double phi_rad = Geometry::deg2rad(45.0);
|
||||
const double sin_theta = ::sin(theta_rad);
|
||||
const Vec3d camera_pos = m_target + m_distance * Vec3d(sin_theta * ::sin(phi_rad), sin_theta * ::cos(phi_rad), ::cos(theta_rad));
|
||||
m_view_rotation = Eigen::AngleAxisd(theta_rad, Vec3d::UnitX()) * Eigen::AngleAxisd(phi_rad, Vec3d::UnitZ());
|
||||
m_view_rotation.normalize();
|
||||
m_view_matrix.fromPositionOrientationScale(m_view_rotation * (-camera_pos), m_view_rotation, Vec3d::Ones());*/
|
||||
}
|
||||
|
||||
Vec3d Camera::validate_target(const Vec3d& target) const
|
||||
{
|
||||
BoundingBoxf3 test_box = m_scene_box;
|
||||
test_box.translate(-m_scene_box.center());
|
||||
// We may let this factor be customizable
|
||||
//BBS enlarge scene box factor
|
||||
static const double ScaleFactor = 3.0;
|
||||
test_box.scale(ScaleFactor);
|
||||
test_box.translate(m_scene_box.center());
|
||||
|
||||
return { std::clamp(target(0), test_box.min(0), test_box.max(0)),
|
||||
std::clamp(target(1), test_box.min(1), test_box.max(1)),
|
||||
std::clamp(target(2), test_box.min(2), test_box.max(2)) };
|
||||
}
|
||||
|
||||
void Camera::update_zenit()
|
||||
{
|
||||
m_zenit = Geometry::rad2deg(0.5 * M_PI - std::acos(std::clamp(-get_dir_forward().dot(Vec3d::UnitZ()), -1.0, 1.0)));
|
||||
}
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
|
||||
170
src/slic3r/GUI/Camera.hpp
Normal file
170
src/slic3r/GUI/Camera.hpp
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
#ifndef slic3r_Camera_hpp_
|
||||
#define slic3r_Camera_hpp_
|
||||
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "3DScene.hpp"
|
||||
#include <array>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
#define REQUIRES_ZOOM_TO_CUR_PLATE -1
|
||||
#define REQUIRES_ZOOM_TO_ALL_PLATE -2
|
||||
#define REQUIRES_ZOOM_TO_PLATE_IDLE -100
|
||||
|
||||
|
||||
struct Camera
|
||||
{
|
||||
static const double DefaultDistance;
|
||||
static const double DefaultZoomToBoxMarginFactor;
|
||||
static const double DefaultZoomToVolumesMarginFactor;
|
||||
static double FrustrumMinZRange;
|
||||
static double FrustrumMinNearZ;
|
||||
static double FrustrumZMargin;
|
||||
static double MaxFovDeg;
|
||||
|
||||
enum class EType : unsigned char
|
||||
{
|
||||
Unknown,
|
||||
Ortho,
|
||||
Perspective,
|
||||
Num_types
|
||||
};
|
||||
|
||||
bool requires_zoom_to_bed{ false };
|
||||
//BBS
|
||||
bool requires_zoom_to_volumes{ false };
|
||||
int requires_zoom_to_plate{ REQUIRES_ZOOM_TO_PLATE_IDLE };
|
||||
|
||||
private:
|
||||
EType m_type{ EType::Perspective };
|
||||
bool m_update_config_on_type_change_enabled{ false };
|
||||
Vec3d m_target{ Vec3d::Zero() };
|
||||
float m_zenit{ 45.0f };
|
||||
double m_zoom{ 1.0 };
|
||||
// Distance between camera position and camera target measured along the camera Z axis
|
||||
double m_distance{ DefaultDistance };
|
||||
double m_gui_scale{ 1.0 };
|
||||
|
||||
std::array<int, 4> m_viewport;
|
||||
Transform3d m_view_matrix{ Transform3d::Identity() };
|
||||
// We are calculating the rotation part of the m_view_matrix from m_view_rotation.
|
||||
Eigen::Quaterniond m_view_rotation{ 1.0, 0.0, 0.0, 0.0 };
|
||||
Transform3d m_projection_matrix{ Transform3d::Identity() };
|
||||
std::pair<double, double> m_frustrum_zs;
|
||||
|
||||
BoundingBoxf3 m_scene_box;
|
||||
|
||||
public:
|
||||
Camera() { set_default_orientation(); }
|
||||
|
||||
EType get_type() const { return m_type; }
|
||||
std::string get_type_as_string() const;
|
||||
void set_type(EType type);
|
||||
// valid values for type: "false" -> ortho, "true" -> perspective
|
||||
void set_type(const std::string& type) { set_type((type == "true") ? EType::Perspective : EType::Ortho); }
|
||||
void select_next_type();
|
||||
|
||||
void enable_update_config_on_type_change(bool enable) { m_update_config_on_type_change_enabled = enable; }
|
||||
|
||||
const Vec3d& get_target() const { return m_target; }
|
||||
void set_target(const Vec3d& target);
|
||||
|
||||
double get_distance() const { return (get_position() - m_target).norm(); }
|
||||
double get_gui_scale() const { return m_gui_scale; }
|
||||
float get_zenit() const { return m_zenit; }
|
||||
|
||||
double get_zoom() const { return m_zoom; }
|
||||
double get_inv_zoom() const { assert(m_zoom != 0.0); return 1.0 / m_zoom; }
|
||||
void update_zoom(double delta_zoom) { set_zoom(m_zoom / (1.0 - std::max(std::min(delta_zoom, 4.0), -4.0) * 0.1)); }
|
||||
void set_zoom(double zoom);
|
||||
|
||||
const BoundingBoxf3& get_scene_box() const { return m_scene_box; }
|
||||
void set_scene_box(const BoundingBoxf3& box) { m_scene_box = box; }
|
||||
|
||||
|
||||
void select_view(const std::string& direction);
|
||||
|
||||
const std::array<int, 4>& get_viewport() const { return m_viewport; }
|
||||
const Transform3d& get_view_matrix() const { return m_view_matrix; }
|
||||
const Transform3d& get_projection_matrix() const { return m_projection_matrix; }
|
||||
|
||||
//BBS
|
||||
const Eigen::Quaterniond& get_view_rotation() const {return m_view_rotation; }
|
||||
|
||||
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_position() const { return m_view_matrix.matrix().inverse().block(0, 3, 3, 1); }
|
||||
|
||||
double get_near_z() const { return m_frustrum_zs.first; }
|
||||
double get_far_z() const { return m_frustrum_zs.second; }
|
||||
const std::pair<double, double>& get_z_range() const { return m_frustrum_zs; }
|
||||
|
||||
double get_fov() const;
|
||||
|
||||
void apply_viewport(int x, int y, unsigned int w, unsigned int h);
|
||||
void apply_view_matrix();
|
||||
// Calculates and applies the projection matrix tighting the frustrum z range around the given box.
|
||||
// If larger z span is needed, pass the desired values of near and far z (negative values are ignored)
|
||||
void apply_projection(const BoundingBoxf3& box, double near_z = -1.0, double far_z = -1.0);
|
||||
|
||||
void zoom_to_box(const BoundingBoxf3& box, double margin_factor = DefaultZoomToBoxMarginFactor);
|
||||
void zoom_to_volumes(const GLVolumePtrs& volumes, double margin_factor = DefaultZoomToVolumesMarginFactor);
|
||||
|
||||
#if ENABLE_CAMERA_STATISTICS
|
||||
void debug_render() const;
|
||||
#endif // ENABLE_CAMERA_STATISTICS
|
||||
|
||||
// translate the camera in world space
|
||||
void translate_world(const Vec3d& displacement) { set_target(m_target + displacement); }
|
||||
|
||||
// BBS rotate the camera on a sphere having center == target
|
||||
void rotate_on_sphere_with_target(double delta_azimut_rad, double delta_zenit_rad, bool apply_limits, Vec3d target);
|
||||
void rotate_local_with_target(const Vec3d& rotation_rad, Vec3d target);
|
||||
|
||||
// rotate the camera on a sphere having center == m_target and radius == m_distance
|
||||
// using the given variations of spherical coordinates
|
||||
// if apply_limits == true the camera stops rotating when its forward vector is parallel to the world Z axis
|
||||
void rotate_on_sphere(double delta_azimut_rad, double delta_zenit_rad, bool apply_limits);
|
||||
|
||||
// rotate the camera around three axes parallel to the camera local axes and passing through m_target
|
||||
void rotate_local_around_target(const Vec3d& rotation_rad);
|
||||
|
||||
// returns true if the camera z axis (forward) is pointing in the negative direction of the world z axis
|
||||
bool is_looking_downward() const { return get_dir_forward().dot(Vec3d::UnitZ()) < 0.0; }
|
||||
|
||||
// forces camera right vector to be parallel to XY plane
|
||||
void recover_from_free_camera() {
|
||||
if (std::abs(get_dir_right()(2)) > EPSILON)
|
||||
look_at(get_position(), m_target, Vec3d::UnitZ());
|
||||
}
|
||||
|
||||
//BBS store and load camera view
|
||||
void load_camera_view(Camera& cam);
|
||||
|
||||
void look_at(const Vec3d& position, const Vec3d& target, const Vec3d& up);
|
||||
|
||||
double max_zoom() const { return 250.0; }
|
||||
double min_zoom() const { return 0.2 * calc_zoom_to_bounding_box_factor(m_scene_box); }
|
||||
|
||||
private:
|
||||
// returns tight values for nearZ and farZ plane around the given bounding box
|
||||
// the camera MUST be outside of the bounding box in eye coordinate of the given box
|
||||
std::pair<double, double> calc_tight_frustrum_zs_around(const BoundingBoxf3& box);
|
||||
double calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, double margin_factor = DefaultZoomToBoxMarginFactor) const;
|
||||
double calc_zoom_to_volumes_factor(const GLVolumePtrs& volumes, Vec3d& center, double margin_factor = DefaultZoomToVolumesMarginFactor) const;
|
||||
void set_distance(double distance);
|
||||
|
||||
void set_default_orientation();
|
||||
Vec3d validate_target(const Vec3d& target) const;
|
||||
void update_zenit();
|
||||
};
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
|
||||
#endif // slic3r_Camera_hpp_
|
||||
|
||||
132
src/slic3r/GUI/CameraPopup.cpp
Normal file
132
src/slic3r/GUI/CameraPopup.cpp
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
#include "CameraPopup.hpp"
|
||||
|
||||
#include "I18N.hpp"
|
||||
#include "Widgets/Label.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
wxIMPLEMENT_CLASS(CameraPopup, wxPopupTransientWindow);
|
||||
|
||||
wxBEGIN_EVENT_TABLE(CameraPopup, wxPopupTransientWindow)
|
||||
EVT_MOUSE_EVENTS(CameraPopup::OnMouse )
|
||||
EVT_SIZE(CameraPopup::OnSize)
|
||||
EVT_SET_FOCUS(CameraPopup::OnSetFocus )
|
||||
EVT_KILL_FOCUS(CameraPopup::OnKillFocus )
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
|
||||
static const wxFont TEXT_FONT = Label::Body_14;
|
||||
static wxColour TEXT_COL = wxColour(43, 52, 54);
|
||||
|
||||
CameraPopup::CameraPopup(wxWindow *parent, MachineObject* obj)
|
||||
: wxPopupTransientWindow(parent, wxBORDER_NONE | wxPU_CONTAINS_CONTROLS),
|
||||
m_obj(obj)
|
||||
{
|
||||
#ifdef __WINDOWS__
|
||||
SetDoubleBuffered(true);
|
||||
#endif
|
||||
m_panel = new wxScrolledWindow(this, wxID_ANY);
|
||||
m_panel->SetBackgroundColour(*wxWHITE);
|
||||
|
||||
m_panel->Bind(wxEVT_MOTION, &CameraPopup::OnMouse, this);
|
||||
|
||||
wxBoxSizer * main_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxFlexGridSizer* top_sizer = new wxFlexGridSizer(0, 2, 0, 0);
|
||||
top_sizer->AddGrowableCol(0);
|
||||
top_sizer->SetFlexibleDirection(wxBOTH);
|
||||
top_sizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
|
||||
m_text_timelapse = new wxStaticText(m_panel, wxID_ANY, _L("Timelapse"));
|
||||
m_text_timelapse->Wrap(-1);
|
||||
m_text_timelapse->SetFont(TEXT_FONT);
|
||||
m_text_timelapse->SetForegroundColour(TEXT_COL);
|
||||
m_switch_timelapse = new SwitchButton(m_panel);
|
||||
if (obj)
|
||||
m_switch_timelapse->SetValue(obj->camera_timelapse);
|
||||
m_text_recording = new wxStaticText(m_panel, wxID_ANY, _L("Monitoring Recording"));
|
||||
m_text_recording->Wrap(-1);
|
||||
m_text_recording->SetFont(TEXT_FONT);
|
||||
m_text_recording->SetForegroundColour(TEXT_COL);
|
||||
m_switch_recording = new SwitchButton(m_panel);
|
||||
if (obj)
|
||||
m_switch_recording->SetValue(obj->camera_recording);
|
||||
|
||||
top_sizer->Add(m_text_timelapse, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, FromDIP(5));
|
||||
top_sizer->Add(m_switch_timelapse, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxALL, FromDIP(5));
|
||||
top_sizer->Add(m_text_recording, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, FromDIP(5));
|
||||
top_sizer->Add(m_switch_recording, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxALL, FromDIP(5));
|
||||
main_sizer->Add(top_sizer, 0, wxALL, FromDIP(10));
|
||||
m_panel->SetSizer(main_sizer);
|
||||
m_panel->Layout();
|
||||
|
||||
main_sizer->Fit(m_panel);
|
||||
|
||||
SetClientSize(m_panel->GetSize());
|
||||
m_switch_timelapse->Connect(wxEVT_LEFT_DOWN, wxCommandEventHandler(CameraPopup::on_switch_timelapse), NULL, this);
|
||||
m_switch_recording->Connect(wxEVT_LEFT_DOWN, wxCommandEventHandler(CameraPopup::on_switch_recording), NULL, this);
|
||||
}
|
||||
|
||||
void CameraPopup::on_switch_timelapse(wxCommandEvent& event)
|
||||
{
|
||||
if (!m_obj) return;
|
||||
|
||||
bool value = m_switch_timelapse->GetValue();
|
||||
m_switch_timelapse->SetValue(!value);
|
||||
m_obj->command_ipcam_timelapse(!value);
|
||||
}
|
||||
|
||||
void CameraPopup::on_switch_recording(wxCommandEvent& event)
|
||||
{
|
||||
if (!m_obj) return;
|
||||
|
||||
bool value = m_switch_recording->GetValue();
|
||||
m_switch_recording->SetValue(!value);
|
||||
m_obj->command_ipcam_record(!value);
|
||||
}
|
||||
|
||||
void CameraPopup::Popup(wxWindow *WXUNUSED(focus))
|
||||
{
|
||||
wxPoint curr_position = this->GetPosition();
|
||||
wxSize win_size = this->GetSize();
|
||||
curr_position.x -= win_size.x;
|
||||
this->SetPosition(curr_position);
|
||||
wxPopupTransientWindow::Popup();
|
||||
}
|
||||
|
||||
void CameraPopup::OnDismiss() {
|
||||
wxPopupTransientWindow::OnDismiss();
|
||||
}
|
||||
|
||||
bool CameraPopup::ProcessLeftDown(wxMouseEvent &event)
|
||||
{
|
||||
return wxPopupTransientWindow::ProcessLeftDown(event);
|
||||
}
|
||||
bool CameraPopup::Show(bool show)
|
||||
{
|
||||
return wxPopupTransientWindow::Show(show);
|
||||
}
|
||||
|
||||
void CameraPopup::OnSize(wxSizeEvent &event)
|
||||
{
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
void CameraPopup::OnSetFocus(wxFocusEvent &event)
|
||||
{
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
void CameraPopup::OnKillFocus(wxFocusEvent &event)
|
||||
{
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
void CameraPopup::OnMouse(wxMouseEvent &event)
|
||||
{
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
49
src/slic3r/GUI/CameraPopup.hpp
Normal file
49
src/slic3r/GUI/CameraPopup.hpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#ifndef slic3r_CameraPopup_hpp_
|
||||
#define slic3r_CameraPopup_hpp_
|
||||
|
||||
#include "slic3r/GUI/MonitorBasePanel.h"
|
||||
#include "DeviceManager.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include <wx/panel.h>
|
||||
#include "Widgets/SwitchButton.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
class CameraPopup : public wxPopupTransientWindow
|
||||
{
|
||||
public:
|
||||
CameraPopup(wxWindow *parent, MachineObject* obj = nullptr);
|
||||
virtual ~CameraPopup() {}
|
||||
|
||||
// wxPopupTransientWindow virtual methods are all overridden to log them
|
||||
virtual void Popup(wxWindow *focus = NULL) wxOVERRIDE;
|
||||
virtual void OnDismiss() wxOVERRIDE;
|
||||
virtual bool ProcessLeftDown(wxMouseEvent &event) wxOVERRIDE;
|
||||
virtual bool Show(bool show = true) wxOVERRIDE;
|
||||
|
||||
protected:
|
||||
void on_switch_timelapse(wxCommandEvent& event);
|
||||
void on_switch_recording(wxCommandEvent& event);
|
||||
|
||||
private:
|
||||
MachineObject* m_obj { nullptr };
|
||||
wxStaticText* m_text_recording;
|
||||
SwitchButton* m_switch_recording;
|
||||
wxStaticText* m_text_timelapse;
|
||||
SwitchButton* m_switch_timelapse;
|
||||
wxScrolledWindow *m_panel;
|
||||
|
||||
void OnMouse(wxMouseEvent &event);
|
||||
void OnSize(wxSizeEvent &event);
|
||||
void OnSetFocus(wxFocusEvent &event);
|
||||
void OnKillFocus(wxFocusEvent &event);
|
||||
|
||||
private:
|
||||
wxDECLARE_ABSTRACT_CLASS(CameraPopup);
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
15
src/slic3r/GUI/ConfigExceptions.hpp
Normal file
15
src/slic3r/GUI/ConfigExceptions.hpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#include <exception>
|
||||
namespace Slic3r {
|
||||
|
||||
class ConfigError : public Slic3r::RuntimeError {
|
||||
using Slic3r::RuntimeError::RuntimeError;
|
||||
};
|
||||
|
||||
namespace GUI {
|
||||
|
||||
class ConfigGUITypeError : public ConfigError {
|
||||
using ConfigError::ConfigError;
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
665
src/slic3r/GUI/ConfigManipulation.cpp
Normal file
665
src/slic3r/GUI/ConfigManipulation.cpp
Normal file
|
|
@ -0,0 +1,665 @@
|
|||
// #include "libslic3r/GCodeSender.hpp"
|
||||
#include "ConfigManipulation.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "format.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
|
||||
#include <wx/msgdlg.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
void ConfigManipulation::apply(DynamicPrintConfig* config, DynamicPrintConfig* new_config)
|
||||
{
|
||||
bool modified = false;
|
||||
m_applying_keys = config->diff(*new_config);
|
||||
for (auto opt_key : m_applying_keys) {
|
||||
config->set_key_value(opt_key, new_config->option(opt_key)->clone());
|
||||
modified = true;
|
||||
}
|
||||
if (modified && load_config != nullptr)
|
||||
load_config();
|
||||
m_applying_keys.clear();
|
||||
}
|
||||
|
||||
bool ConfigManipulation::is_applying() const { return is_msg_dlg_already_exist; }
|
||||
|
||||
t_config_option_keys const &ConfigManipulation::applying_keys() const
|
||||
{
|
||||
return m_applying_keys;
|
||||
}
|
||||
|
||||
void ConfigManipulation::toggle_field(const std::string &opt_key, const bool toggle, int opt_index /* = -1*/)
|
||||
{
|
||||
if (local_config) {
|
||||
if (local_config->option(opt_key) == nullptr) return;
|
||||
}
|
||||
cb_toggle_field(opt_key, toggle, opt_index);
|
||||
}
|
||||
|
||||
void ConfigManipulation::toggle_line(const std::string& opt_key, const bool toggle)
|
||||
{
|
||||
if (local_config) {
|
||||
if (local_config->option(opt_key) == nullptr)
|
||||
return;
|
||||
}
|
||||
if (cb_toggle_line)
|
||||
cb_toggle_line(opt_key, toggle);
|
||||
}
|
||||
|
||||
void ConfigManipulation::check_nozzle_temperature_range(DynamicPrintConfig *config)
|
||||
{
|
||||
if (is_msg_dlg_already_exist)
|
||||
return;
|
||||
|
||||
int temperature_range_low = config->has("nozzle_temperature_range_low") ?
|
||||
config->opt_int("nozzle_temperature_range_low", (unsigned int)0) :
|
||||
0;
|
||||
int temperature_range_high = config->has("nozzle_temperature_range_high") ?
|
||||
config->opt_int("nozzle_temperature_range_high", (unsigned int)0) :
|
||||
0;
|
||||
|
||||
if (temperature_range_low != 0 &&
|
||||
temperature_range_high != 0 &&
|
||||
config->has("nozzle_temperature")) {
|
||||
if (config->opt_int("nozzle_temperature", 0) < temperature_range_low ||
|
||||
config->opt_int("nozzle_temperature", 0) > temperature_range_high)
|
||||
{
|
||||
wxString msg_text = _(L("Nozzle may be blocked when the temperature is out of recommended range.\n"
|
||||
"Please make sure whether to use the temperature to print.\n\n"));
|
||||
msg_text += wxString::Format(_L("Recommended nozzle temperature of this filament type is [%d, %d] degree centigrade"), temperature_range_low, temperature_range_high);
|
||||
MessageDialog dialog(m_msg_dlg_parent, msg_text, "", wxICON_WARNING | wxOK);
|
||||
is_msg_dlg_already_exist = true;
|
||||
dialog.ShowModal();
|
||||
is_msg_dlg_already_exist = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigManipulation::check_nozzle_temperature_initial_layer_range(DynamicPrintConfig* config)
|
||||
{
|
||||
if (is_msg_dlg_already_exist)
|
||||
return;
|
||||
|
||||
int temperature_range_low = config->has("nozzle_temperature_range_low") ?
|
||||
config->opt_int("nozzle_temperature_range_low", (unsigned int)0) :
|
||||
0;
|
||||
int temperature_range_high = config->has("nozzle_temperature_range_high") ?
|
||||
config->opt_int("nozzle_temperature_range_high", (unsigned int)0) :
|
||||
0;
|
||||
|
||||
if (temperature_range_low != 0 &&
|
||||
temperature_range_high != 0 &&
|
||||
config->has("nozzle_temperature_initial_layer")) {
|
||||
if (config->opt_int("nozzle_temperature_initial_layer", 0) < temperature_range_low ||
|
||||
config->opt_int("nozzle_temperature_initial_layer", 0) > temperature_range_high)
|
||||
{
|
||||
wxString msg_text = _(L("Nozzle may be blocked when the temperature is out of recommended range.\n"
|
||||
"Please make sure whether to use the temperature to print.\n\n"));
|
||||
msg_text += wxString::Format(_L("Recommended nozzle temperature of this filament type is [%d, %d] degree centigrade"), temperature_range_low, temperature_range_high);
|
||||
MessageDialog dialog(m_msg_dlg_parent, msg_text, "", wxICON_WARNING | wxOK);
|
||||
is_msg_dlg_already_exist = true;
|
||||
dialog.ShowModal();
|
||||
is_msg_dlg_already_exist = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigManipulation::check_bed_temperature_difference(int bed_type, DynamicPrintConfig* config)
|
||||
{
|
||||
if (is_msg_dlg_already_exist)
|
||||
return;
|
||||
|
||||
if (config->has("bed_temperature_difference") && config->has("temperature_vitrification")) {
|
||||
int bed_temp_difference = config->opt_int("bed_temperature_difference", 0);
|
||||
int vitrification = config->opt_int("temperature_vitrification", 0);
|
||||
const ConfigOptionInts* bed_temp_1st_layer_opt = config->option<ConfigOptionInts>(get_bed_temp_1st_layer_key((BedType)bed_type));
|
||||
const ConfigOptionInts* bed_temp_opt = config->option<ConfigOptionInts>(get_bed_temp_key((BedType)bed_type));
|
||||
|
||||
if (bed_temp_1st_layer_opt != nullptr && bed_temp_opt != nullptr) {
|
||||
int first_layer_bed_temp = bed_temp_1st_layer_opt->get_at(0);
|
||||
int bed_temp = bed_temp_opt->get_at(0);
|
||||
if (first_layer_bed_temp - bed_temp > bed_temp_difference) {
|
||||
const wxString msg_text = wxString::Format(_L("Bed temperature of other layer is lower than bed temperature of initial layer for more than %d degree centigrade.\nThis may cause model broken free from build plate during printing"), bed_temp_difference);
|
||||
MessageDialog dialog(m_msg_dlg_parent, msg_text, "", wxICON_WARNING | wxOK);
|
||||
is_msg_dlg_already_exist = true;
|
||||
dialog.ShowModal();
|
||||
is_msg_dlg_already_exist = false;
|
||||
}
|
||||
|
||||
if (first_layer_bed_temp > vitrification || bed_temp > vitrification) {
|
||||
const wxString msg_text = wxString::Format(_L("Bed temperature is higher than vitrification temperature of this filament.\nThis may cause nozzle blocked and printing failure"));
|
||||
MessageDialog dialog(m_msg_dlg_parent, msg_text, "", wxICON_WARNING | wxOK);
|
||||
is_msg_dlg_already_exist = true;
|
||||
dialog.ShowModal();
|
||||
is_msg_dlg_already_exist = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, const bool is_global_config)
|
||||
{
|
||||
// #ys_FIXME_to_delete
|
||||
//! 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)
|
||||
return;
|
||||
|
||||
// layer_height shouldn't be equal to zero
|
||||
if (config->opt_float("layer_height") < EPSILON)
|
||||
{
|
||||
const wxString msg_text = _(L("Too small layer height.\nReset to 0.2"));
|
||||
MessageDialog dialog(m_msg_dlg_parent, msg_text,"", wxICON_WARNING | wxOK);
|
||||
DynamicPrintConfig new_conf = *config;
|
||||
is_msg_dlg_already_exist = true;
|
||||
dialog.ShowModal();
|
||||
new_conf.set_key_value("layer_height", new ConfigOptionFloat(0.2));
|
||||
apply(config, &new_conf);
|
||||
is_msg_dlg_already_exist = false;
|
||||
}
|
||||
|
||||
//BBS: limite the max layer_herght
|
||||
if (config->opt_float("layer_height") > 0.6 + EPSILON)
|
||||
{
|
||||
const wxString msg_text = _(L("Too large layer height.\nReset to 0.2"));
|
||||
MessageDialog dialog(nullptr, msg_text, "", wxICON_WARNING | wxOK);
|
||||
DynamicPrintConfig new_conf = *config;
|
||||
is_msg_dlg_already_exist = true;
|
||||
dialog.ShowModal();
|
||||
new_conf.set_key_value("layer_height", new ConfigOptionFloat(0.2));
|
||||
apply(config, &new_conf);
|
||||
is_msg_dlg_already_exist = false;
|
||||
}
|
||||
|
||||
//BBS: ironing_spacing shouldn't be too small or equal to zero
|
||||
if (config->opt_float("ironing_spacing") < 0.05)
|
||||
{
|
||||
const wxString msg_text = _(L("Too small ironing spacing.\nReset to 0.1"));
|
||||
MessageDialog dialog(nullptr, msg_text, "", wxICON_WARNING | wxOK);
|
||||
DynamicPrintConfig new_conf = *config;
|
||||
is_msg_dlg_already_exist = true;
|
||||
dialog.ShowModal();
|
||||
new_conf.set_key_value("ironing_spacing", new ConfigOptionFloat(0.1));
|
||||
apply(config, &new_conf);
|
||||
is_msg_dlg_already_exist = false;
|
||||
}
|
||||
|
||||
if (config->option<ConfigOptionFloat>("initial_layer_print_height")->value < EPSILON)
|
||||
{
|
||||
const wxString msg_text = _(L("Zero initial layer height is invalid.\n\nThe first layer height will be reset to 0.2."));
|
||||
MessageDialog dialog(m_msg_dlg_parent, msg_text, "", wxICON_WARNING | wxOK);
|
||||
DynamicPrintConfig new_conf = *config;
|
||||
is_msg_dlg_already_exist = true;
|
||||
dialog.ShowModal();
|
||||
new_conf.set_key_value("initial_layer_print_height", new ConfigOptionFloat(0.2));
|
||||
apply(config, &new_conf);
|
||||
is_msg_dlg_already_exist = false;
|
||||
}
|
||||
|
||||
if (abs(config->option<ConfigOptionFloat>("xy_hole_compensation")->value) > 2)
|
||||
{
|
||||
const wxString msg_text = _(L("This setting is only used for model size tunning with small value in some cases.\n"
|
||||
"For example, when model size has small error and hard to be assembled.\n"
|
||||
"For large size tuning, please use model scale function.\n\n"
|
||||
"The value will be reset to 0."));
|
||||
MessageDialog dialog(m_msg_dlg_parent, msg_text, "", wxICON_WARNING | wxOK);
|
||||
DynamicPrintConfig new_conf = *config;
|
||||
is_msg_dlg_already_exist = true;
|
||||
dialog.ShowModal();
|
||||
new_conf.set_key_value("xy_hole_compensation", new ConfigOptionFloat(0));
|
||||
apply(config, &new_conf);
|
||||
is_msg_dlg_already_exist = false;
|
||||
}
|
||||
|
||||
if (abs(config->option<ConfigOptionFloat>("xy_contour_compensation")->value) > 2)
|
||||
{
|
||||
const wxString msg_text = _(L("This setting is only used for model size tunning with small value in some cases.\n"
|
||||
"For example, when model size has small error and hard to be assembled.\n"
|
||||
"For large size tuning, please use model scale function.\n\n"
|
||||
"The value will be reset to 0."));
|
||||
MessageDialog dialog(m_msg_dlg_parent, msg_text, "", wxICON_WARNING | wxOK);
|
||||
DynamicPrintConfig new_conf = *config;
|
||||
is_msg_dlg_already_exist = true;
|
||||
dialog.ShowModal();
|
||||
new_conf.set_key_value("xy_contour_compensation", new ConfigOptionFloat(0));
|
||||
apply(config, &new_conf);
|
||||
is_msg_dlg_already_exist = false;
|
||||
}
|
||||
|
||||
if (config->option<ConfigOptionFloat>("elefant_foot_compensation")->value > 1)
|
||||
{
|
||||
const wxString msg_text = _(L("Too large elefant foot compensation is unreasonable.\n"
|
||||
"If really have serious elephant foot effect, please check other settings.\n"
|
||||
"For example, whether bed temperature is too high.\n\n"
|
||||
"The value will be reset to 0."));
|
||||
MessageDialog dialog(m_msg_dlg_parent, msg_text, "", wxICON_WARNING | wxOK);
|
||||
DynamicPrintConfig new_conf = *config;
|
||||
is_msg_dlg_already_exist = true;
|
||||
dialog.ShowModal();
|
||||
new_conf.set_key_value("elefant_foot_compensation", new ConfigOptionFloat(0));
|
||||
apply(config, &new_conf);
|
||||
is_msg_dlg_already_exist = false;
|
||||
}
|
||||
|
||||
double sparse_infill_density = config->option<ConfigOptionPercent>("sparse_infill_density")->value;
|
||||
|
||||
if (config->opt_bool("spiral_mode") &&
|
||||
! (config->opt_int("wall_loops") == 1 &&
|
||||
config->opt_int("top_shell_layers") == 0 &&
|
||||
sparse_infill_density == 0 &&
|
||||
! config->opt_bool("enable_support") &&
|
||||
config->opt_int("enforce_support_layers") == 0 &&
|
||||
! config->opt_bool("detect_thin_wall")))
|
||||
{
|
||||
wxString msg_text = _(L("Spiral mode only works when wall loops is 1, \n"
|
||||
"support is disabled, top shell layers is 0 and sparse infill density is 0\n"));
|
||||
if (is_global_config)
|
||||
msg_text += "\n" + _(L("Change these settings automatically? \n"
|
||||
"Yes - Change these settings and enable spiral mode automatically\n"
|
||||
"No - Give up using spiral mode this time"));
|
||||
MessageDialog dialog(m_msg_dlg_parent, msg_text, "",
|
||||
wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK));
|
||||
DynamicPrintConfig new_conf = *config;
|
||||
is_msg_dlg_already_exist = true;
|
||||
auto answer = dialog.ShowModal();
|
||||
bool support = true;
|
||||
if (!is_global_config || answer == wxID_YES) {
|
||||
new_conf.set_key_value("wall_loops", new ConfigOptionInt(1));
|
||||
new_conf.set_key_value("top_shell_layers", new ConfigOptionInt(0));
|
||||
new_conf.set_key_value("sparse_infill_density", new ConfigOptionPercent(0));
|
||||
new_conf.set_key_value("enable_support", new ConfigOptionBool(false));
|
||||
new_conf.set_key_value("enforce_support_layers", new ConfigOptionInt(0));
|
||||
new_conf.set_key_value("detect_thin_wall", new ConfigOptionBool(false));
|
||||
sparse_infill_density = 0;
|
||||
support = false;
|
||||
}
|
||||
else {
|
||||
new_conf.set_key_value("spiral_mode", new ConfigOptionBool(false));
|
||||
}
|
||||
apply(config, &new_conf);
|
||||
if (cb_value_change) {
|
||||
cb_value_change("sparse_infill_density", sparse_infill_density);
|
||||
if (!support)
|
||||
cb_value_change("enable_support", false);
|
||||
}
|
||||
is_msg_dlg_already_exist = false;
|
||||
}
|
||||
|
||||
//BBS
|
||||
if (config->opt_bool("timelapse_no_toolhead") && !is_timelapse_wipe_tower_already_prompted) {
|
||||
wxString msg_text = _(L("When recording timelapse without toolhead, it is recommended to add a \"Timelapse Wipe Tower\" \n"
|
||||
"by right-click the empty position of build plate and choose \"Add Primitive\"->\"Timelapse Wipe Tower\".\n"));
|
||||
MessageDialog dialog(m_msg_dlg_parent, msg_text, "", wxICON_WARNING | wxOK);
|
||||
is_msg_dlg_already_exist = true;
|
||||
dialog.ShowModal();
|
||||
is_msg_dlg_already_exist = false;
|
||||
is_timelapse_wipe_tower_already_prompted = true;
|
||||
}
|
||||
|
||||
// BBS
|
||||
int filament_cnt = wxGetApp().preset_bundle->filament_presets.size();
|
||||
#if 0
|
||||
bool has_wipe_tower = filament_cnt > 1 && config->opt_bool("enable_prime_tower");
|
||||
if (has_wipe_tower && (config->opt_bool("adaptive_layer_height") || config->opt_bool("independent_support_layer_height"))) {
|
||||
wxString msg_text;
|
||||
if (config->opt_bool("adaptive_layer_height") && config->opt_bool("independent_support_layer_height")) {
|
||||
msg_text = _(L("Prime tower does not work when Adaptive Layer Height or Independent Support Layer Height is on.\n"
|
||||
"Which do you want to keep?\n"
|
||||
"YES - Keep Prime Tower\n"
|
||||
"NO - Keep Adaptive Layer Height and Independent Support Layer Height"));
|
||||
}
|
||||
else if (config->opt_bool("adaptive_layer_height")) {
|
||||
msg_text = _(L("Prime tower does not work when Adaptive Layer Height is on.\n"
|
||||
"Which do you want to keep?\n"
|
||||
"YES - Keep Prime Tower\n"
|
||||
"NO - Keep Adaptive Layer Height"));
|
||||
}
|
||||
else {
|
||||
msg_text = _(L("Prime tower does not work when Independent Support Layer Height is on.\n"
|
||||
"Which do you want to keep?\n"
|
||||
"YES - Keep Prime Tower\n"
|
||||
"NO - Keep Independent Support Layer Height"));
|
||||
}
|
||||
|
||||
MessageDialog dialog(m_msg_dlg_parent, msg_text, "", wxICON_WARNING | wxYES | wxNO);
|
||||
is_msg_dlg_already_exist = true;
|
||||
auto answer = dialog.ShowModal();
|
||||
|
||||
DynamicPrintConfig new_conf = *config;
|
||||
if (answer == wxID_YES) {
|
||||
if (config->opt_bool("adaptive_layer_height"))
|
||||
new_conf.set_key_value("adaptive_layer_height", new ConfigOptionBool(false));
|
||||
|
||||
if (config->opt_bool("independent_support_layer_height"))
|
||||
new_conf.set_key_value("independent_support_layer_height", new ConfigOptionBool(false));
|
||||
}
|
||||
else
|
||||
new_conf.set_key_value("enable_prime_tower", new ConfigOptionBool(false));
|
||||
|
||||
apply(config, &new_conf);
|
||||
is_msg_dlg_already_exist = false;
|
||||
}
|
||||
|
||||
// BBS
|
||||
if (has_wipe_tower && config->opt_bool("enable_support") && !config->opt_bool("independent_support_layer_height")) {
|
||||
double layer_height = config->opt_float("layer_height");
|
||||
double top_gap_raw = config->opt_float("support_top_z_distance");
|
||||
//double bottom_gap_raw = config->opt_float("support_bottom_z_distance");
|
||||
double top_gap = std::round(top_gap_raw / layer_height) * layer_height;
|
||||
//double bottom_gap = std::round(bottom_gap_raw / layer_height) * layer_height;
|
||||
if (top_gap != top_gap_raw /* || bottom_gap != bottom_gap_raw*/) {
|
||||
DynamicPrintConfig new_conf = *config;
|
||||
new_conf.set_key_value("support_top_z_distance", new ConfigOptionFloat(top_gap));
|
||||
//new_conf.set_key_value("support_bottom_z_distance", new ConfigOptionFloat(bottom_gap));
|
||||
apply(config, &new_conf);
|
||||
|
||||
//wxMessageBox(_L("Support top/bottom Z distance is automatically changed to multiple of layer height."));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check "enable_support" and "overhangs" relations only on global settings level
|
||||
if (is_global_config && config->opt_bool("enable_support")) {
|
||||
// Ask only once.
|
||||
if (!m_support_material_overhangs_queried) {
|
||||
m_support_material_overhangs_queried = true;
|
||||
if (!config->opt_bool("detect_overhang_wall")/* != 1*/) {
|
||||
//BBS: detect_overhang_wall is setting in develop mode. Enable it directly.
|
||||
DynamicPrintConfig new_conf = *config;
|
||||
new_conf.set_key_value("detect_overhang_wall", new ConfigOptionBool(true));
|
||||
apply(config, &new_conf);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_support_material_overhangs_queried = false;
|
||||
}
|
||||
|
||||
if (config->option<ConfigOptionPercent>("sparse_infill_density")->value == 100) {
|
||||
std::string sparse_infill_pattern = config->option<ConfigOptionEnum<InfillPattern>>("sparse_infill_pattern")->serialize();
|
||||
const auto &top_fill_pattern_values = config->def()->get("top_surface_pattern")->enum_values;
|
||||
bool correct_100p_fill = std::find(top_fill_pattern_values.begin(), top_fill_pattern_values.end(), sparse_infill_pattern) != top_fill_pattern_values.end();
|
||||
if (!correct_100p_fill) {
|
||||
// get sparse_infill_pattern name from enum_labels for using this one at dialog_msg
|
||||
const ConfigOptionDef *fill_pattern_def = config->def()->get("sparse_infill_pattern");
|
||||
assert(fill_pattern_def != nullptr);
|
||||
auto it_pattern = std::find(fill_pattern_def->enum_values.begin(), fill_pattern_def->enum_values.end(), sparse_infill_pattern);
|
||||
assert(it_pattern != fill_pattern_def->enum_values.end());
|
||||
if (it_pattern != fill_pattern_def->enum_values.end()) {
|
||||
wxString msg_text = GUI::format_wxstr(_L("%1% infill pattern doesn't support 100%% density."),
|
||||
_(fill_pattern_def->enum_labels[it_pattern - fill_pattern_def->enum_values.begin()]));
|
||||
if (is_global_config)
|
||||
msg_text += "\n" + _L("Switch to zig-zag pattern?\n"
|
||||
"Yes - switch to zig-zag pattern automaticlly\n"
|
||||
"No - reset density to default non 100% value automaticlly\n");
|
||||
MessageDialog dialog(m_msg_dlg_parent, msg_text, "",
|
||||
wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK) );
|
||||
DynamicPrintConfig new_conf = *config;
|
||||
is_msg_dlg_already_exist = true;
|
||||
auto answer = dialog.ShowModal();
|
||||
if (!is_global_config || answer == wxID_YES) {
|
||||
new_conf.set_key_value("sparse_infill_pattern", new ConfigOptionEnum<InfillPattern>(ipRectilinear));
|
||||
sparse_infill_density = 100;
|
||||
}
|
||||
else
|
||||
sparse_infill_density = wxGetApp().preset_bundle->prints.get_selected_preset().config.option<ConfigOptionPercent>("sparse_infill_density")->value;
|
||||
new_conf.set_key_value("sparse_infill_density", new ConfigOptionPercent(sparse_infill_density));
|
||||
apply(config, &new_conf);
|
||||
if (cb_value_change)
|
||||
cb_value_change("sparse_infill_density", sparse_infill_density);
|
||||
is_msg_dlg_already_exist = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BBS
|
||||
static const char* keys[] = { "support_filament", "support_interface_filament"};
|
||||
for (int i = 0; i < sizeof(keys) / sizeof(keys[0]); i++) {
|
||||
std::string key = std::string(keys[i]);
|
||||
auto* opt = dynamic_cast<ConfigOptionInt*>(config->option(key, false));
|
||||
if (opt != nullptr) {
|
||||
if (opt->getInt() > filament_cnt) {
|
||||
DynamicPrintConfig new_conf = *config;
|
||||
new_conf.set_key_value(key, new ConfigOptionInt(filament_cnt));
|
||||
apply(config, &new_conf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigManipulation::apply_null_fff_config(DynamicPrintConfig *config, std::vector<std::string> const &keys, std::map<ObjectBase *, ModelConfig *> const &configs)
|
||||
{
|
||||
for (auto &k : keys) {
|
||||
if (k == "adaptive_layer_height" || k == "independent_support_layer_height" || k == "enable_support" || k == "detect_thin_wall")
|
||||
config->set_key_value(k, new ConfigOptionBool(true));
|
||||
else if (k == "wall_loops")
|
||||
config->set_key_value(k, new ConfigOptionInt(0));
|
||||
else if (k == "top_shell_layers" || k == "enforce_support_layers")
|
||||
config->set_key_value(k, new ConfigOptionInt(1));
|
||||
else if (k == "sparse_infill_density") {
|
||||
double v = config->option<ConfigOptionPercent>(k)->value;
|
||||
for (auto &c : configs) {
|
||||
auto o = c.second->get().option<ConfigOptionPercent>(k);
|
||||
if (o && o->value > v) v = o->value;
|
||||
}
|
||||
config->set_key_value(k, new ConfigOptionPercent(v)); // sparse_infill_pattern
|
||||
}
|
||||
else if (k == "detect_overhang_wall")
|
||||
config->set_key_value(k, new ConfigOptionBool(false));
|
||||
else if (k == "sparse_infill_pattern")
|
||||
config->set_key_value(k, new ConfigOptionEnum<InfillPattern>(ipGrid));
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, const bool is_global_config)
|
||||
{
|
||||
bool have_perimeters = config->opt_int("wall_loops") > 0;
|
||||
for (auto el : { "detect_thin_wall", "detect_overhang_wall",
|
||||
"seam_position", "wall_infill_order", "outer_wall_line_width",
|
||||
"inner_wall_speed", "outer_wall_speed" })
|
||||
toggle_field(el, have_perimeters);
|
||||
|
||||
bool have_infill = config->option<ConfigOptionPercent>("sparse_infill_density")->value > 0;
|
||||
// sparse_infill_filament uses the same logic as in Print::extruders()
|
||||
for (auto el : { "sparse_infill_pattern", "infill_combination",
|
||||
"minimum_sparse_infill_area", "sparse_infill_filament"})
|
||||
toggle_line(el, have_infill);
|
||||
|
||||
bool has_spiral_vase = config->opt_bool("spiral_mode");
|
||||
bool has_top_solid_infill = config->opt_int("top_shell_layers") > 0;
|
||||
bool has_bottom_solid_infill = config->opt_int("bottom_shell_layers") > 0;
|
||||
bool has_solid_infill = has_top_solid_infill || has_bottom_solid_infill;
|
||||
// solid_infill_filament uses the same logic as in Print::extruders()
|
||||
for (auto el : { "top_surface_pattern", "bottom_surface_pattern", "solid_infill_filament"})
|
||||
toggle_field(el, has_solid_infill);
|
||||
|
||||
for (auto el : { "infill_direction", "sparse_infill_line_width",
|
||||
"sparse_infill_speed", "bridge_speed" })
|
||||
toggle_field(el, have_infill || has_solid_infill);
|
||||
|
||||
toggle_field("top_shell_thickness", ! has_spiral_vase && has_top_solid_infill);
|
||||
toggle_field("bottom_shell_thickness", ! has_spiral_vase && has_bottom_solid_infill);
|
||||
|
||||
// Gap fill is newly allowed in between perimeter lines even for empty infill (see GH #1476).
|
||||
toggle_field("gap_infill_speed", have_perimeters);
|
||||
|
||||
for (auto el : { "top_surface_line_width", "top_surface_speed" })
|
||||
toggle_field(el, has_top_solid_infill || (has_spiral_vase && has_bottom_solid_infill));
|
||||
|
||||
bool have_default_acceleration = config->opt_float("default_acceleration") > 0;
|
||||
//BBS
|
||||
for (auto el : { "initial_layer_acceleration", "top_surface_acceleration" })
|
||||
toggle_field(el, have_default_acceleration);
|
||||
|
||||
bool have_skirt = config->opt_int("skirt_loops") > 0;
|
||||
toggle_field("skirt_height", have_skirt && config->opt_enum<DraftShield>("draft_shield") != dsEnabled);
|
||||
for (auto el : { "skirt_distance", "draft_shield"})
|
||||
toggle_field(el, have_skirt);
|
||||
|
||||
bool have_brim = (config->opt_enum<BrimType>("brim_type") != btNoBrim);
|
||||
toggle_field("brim_object_gap", have_brim);
|
||||
bool have_brim_width = (config->opt_enum<BrimType>("brim_type") != btNoBrim) && config->opt_enum<BrimType>("brim_type") != btAutoBrim;
|
||||
toggle_field("brim_width", have_brim_width);
|
||||
// wall_filament uses the same logic as in Print::extruders()
|
||||
toggle_field("wall_filament", have_perimeters || have_brim);
|
||||
|
||||
bool have_raft = config->opt_int("raft_layers") > 0;
|
||||
bool have_support_material = config->opt_bool("enable_support") || have_raft;
|
||||
// BBS
|
||||
SupportType support_type = config->opt_enum<SupportType>("support_type");
|
||||
bool have_support_interface = config->opt_int("support_interface_top_layers") > 0 || config->opt_int("support_interface_bottom_layers") > 0;
|
||||
bool have_support_soluble = have_support_material && config->opt_float("support_top_z_distance") == 0;
|
||||
auto support_style = config->opt_enum<SupportMaterialStyle>("support_style");
|
||||
for (auto el : { "support_style", "support_base_pattern",
|
||||
"support_base_pattern_spacing", "support_angle",
|
||||
"support_interface_pattern", "support_interface_top_layers", "support_interface_bottom_layers",
|
||||
"bridge_no_support","max_bridge_length" "support_top_z_distance",
|
||||
//BBS: add more support params to dependent of enable_support
|
||||
"support_type","support_on_build_plate_only",
|
||||
"support_object_xy_distance", "independent_support_layer_height"})
|
||||
toggle_field(el, have_support_material);
|
||||
toggle_field("support_threshold_angle", have_support_material && (support_type == stNormalAuto || support_type == stTreeAuto || support_type==stHybridAuto));
|
||||
//toggle_field("support_closing_radius", have_support_material && support_style == smsSnug);
|
||||
|
||||
for (auto el : {"tree_support_branch_angle", "tree_support_wall_count", "tree_support_with_infill", "tree_support_branch_distance", "tree_support_branch_diameter"})
|
||||
toggle_field(el, config->opt_bool("enable_support") && (support_type == stTreeAuto || support_type == stTree || support_type == stHybridAuto));
|
||||
|
||||
// hide tree support settings when normal is selected
|
||||
bool support_is_tree = std::set<SupportType>{stTreeAuto, stHybridAuto, stTree}.count(support_type) != 0;
|
||||
toggle_line("tree_support_branch_distance", support_is_tree);
|
||||
toggle_line("tree_support_branch_diameter", support_is_tree);
|
||||
toggle_line("tree_support_branch_angle", support_is_tree);
|
||||
toggle_line("tree_support_wall_count", support_is_tree);
|
||||
toggle_line("tree_support_with_infill", support_is_tree);
|
||||
|
||||
for (auto el : { "support_interface_spacing", "support_interface_filament",
|
||||
"support_interface_loop_pattern", "support_bottom_interface_spacing" })
|
||||
toggle_field(el, have_support_material && have_support_interface);
|
||||
|
||||
//BBS
|
||||
bool have_skirt_height = have_skirt &&
|
||||
(config->opt_int("skirt_height") > 1 || config->opt_enum<DraftShield>("draft_shield") != dsEnabled);
|
||||
toggle_line("support_speed", have_support_material || have_skirt_height);
|
||||
toggle_line("support_interface_speed", have_support_material && have_support_interface);
|
||||
|
||||
// BBS
|
||||
//toggle_field("support_material_synchronize_layers", have_support_soluble);
|
||||
|
||||
toggle_field("inner_wall_line_width", have_perimeters || have_skirt || have_brim);
|
||||
toggle_field("support_filament", have_support_material || have_skirt);
|
||||
|
||||
toggle_field("raft_contact_distance", have_raft && !have_support_soluble);
|
||||
for (auto el : { "raft_expansion" })
|
||||
toggle_field(el, have_raft);
|
||||
|
||||
bool has_ironing = (config->opt_enum<IroningType>("ironing_type") != IroningType::NoIroning);
|
||||
for (auto el : { "ironing_flow", "ironing_spacing", "ironing_speed" })
|
||||
toggle_field(el, has_ironing);
|
||||
|
||||
bool have_sequential_printing = (config->opt_enum<PrintSequence>("print_sequence") == PrintSequence::ByObject);
|
||||
for (auto el : { "extruder_clearance_radius", "extruder_clearance_height_to_rod", "extruder_clearance_height_to_lid" })
|
||||
toggle_field(el, have_sequential_printing);
|
||||
|
||||
bool have_ooze_prevention = config->opt_bool("ooze_prevention");
|
||||
toggle_field("standby_temperature_delta", have_ooze_prevention);
|
||||
|
||||
bool have_prime_tower = config->opt_bool("enable_prime_tower");
|
||||
for (auto el : { "prime_tower_width", "prime_volume"})
|
||||
toggle_field(el, have_prime_tower);
|
||||
|
||||
bool have_avoid_crossing_perimeters = config->opt_bool("reduce_crossing_wall");
|
||||
toggle_field("max_travel_detour_distance", have_avoid_crossing_perimeters);
|
||||
|
||||
bool has_overhang_speed = config->opt_bool("enable_overhang_speed");
|
||||
toggle_line("overhang_1_4_speed", has_overhang_speed);
|
||||
toggle_line("overhang_2_4_speed", has_overhang_speed);
|
||||
toggle_line("overhang_3_4_speed", has_overhang_speed);
|
||||
toggle_line("overhang_4_4_speed", has_overhang_speed);
|
||||
|
||||
toggle_line("flush_into_objects", !is_global_config);
|
||||
}
|
||||
|
||||
void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, const bool is_global_config/* = false*/)
|
||||
{
|
||||
double head_penetration = config->opt_float("support_head_penetration");
|
||||
double head_width = config->opt_float("support_head_width");
|
||||
if (head_penetration > head_width) {
|
||||
//wxString msg_text = _(L("Head penetration should not be greater than the head width."));
|
||||
wxString msg_text = "Head penetration should not be greater than the head width.";
|
||||
|
||||
//MessageDialog dialog(m_msg_dlg_parent, msg_text, _(L("Invalid Head penetration")), wxICON_WARNING | wxOK);
|
||||
MessageDialog dialog(m_msg_dlg_parent, msg_text, "Invalid Head penetration", wxICON_WARNING | wxOK);
|
||||
DynamicPrintConfig new_conf = *config;
|
||||
if (dialog.ShowModal() == wxID_OK) {
|
||||
new_conf.set_key_value("support_head_penetration", new ConfigOptionFloat(head_width));
|
||||
apply(config, &new_conf);
|
||||
}
|
||||
}
|
||||
|
||||
double pinhead_d = config->opt_float("support_head_front_diameter");
|
||||
double pillar_d = config->opt_float("support_pillar_diameter");
|
||||
if (pinhead_d > pillar_d) {
|
||||
//wxString msg_text = _(L("Pinhead diameter should be smaller than the pillar diameter."));
|
||||
wxString msg_text = "Pinhead diameter should be smaller than the pillar diameter.";
|
||||
|
||||
//MessageDialog dialog(m_msg_dlg_parent, msg_text, _(L("Invalid pinhead diameter")), wxICON_WARNING | wxOK);
|
||||
MessageDialog dialog(m_msg_dlg_parent, msg_text, "Invalid pinhead diameter", wxICON_WARNING | wxOK);
|
||||
|
||||
DynamicPrintConfig new_conf = *config;
|
||||
if (dialog.ShowModal() == wxID_OK) {
|
||||
new_conf.set_key_value("support_head_front_diameter", new ConfigOptionFloat(pillar_d / 2.0));
|
||||
apply(config, &new_conf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigManipulation::toggle_print_sla_options(DynamicPrintConfig* config)
|
||||
{
|
||||
bool supports_en = config->opt_bool("supports_enable");
|
||||
|
||||
toggle_field("support_head_front_diameter", supports_en);
|
||||
toggle_field("support_head_penetration", supports_en);
|
||||
toggle_field("support_head_width", supports_en);
|
||||
toggle_field("support_pillar_diameter", supports_en);
|
||||
toggle_field("support_small_pillar_diameter_percent", supports_en);
|
||||
toggle_field("support_max_bridges_on_pillar", supports_en);
|
||||
toggle_field("support_pillar_connection_mode", supports_en);
|
||||
toggle_field("support_buildplate_only", supports_en);
|
||||
toggle_field("support_base_diameter", supports_en);
|
||||
toggle_field("support_base_height", supports_en);
|
||||
toggle_field("support_base_safety_distance", supports_en);
|
||||
toggle_field("support_critical_angle", supports_en);
|
||||
toggle_field("support_max_bridge_length", supports_en);
|
||||
toggle_field("support_max_pillar_link_distance", supports_en);
|
||||
toggle_field("support_points_density_relative", supports_en);
|
||||
toggle_field("support_points_minimal_distance", supports_en);
|
||||
|
||||
bool pad_en = config->opt_bool("pad_enable");
|
||||
|
||||
toggle_field("pad_wall_thickness", pad_en);
|
||||
toggle_field("pad_wall_height", pad_en);
|
||||
toggle_field("pad_brim_size", pad_en);
|
||||
toggle_field("pad_max_merge_distance", pad_en);
|
||||
// toggle_field("pad_edge_radius", supports_en);
|
||||
toggle_field("pad_wall_slope", pad_en);
|
||||
toggle_field("pad_around_object", pad_en);
|
||||
toggle_field("pad_around_object_everywhere", pad_en);
|
||||
|
||||
bool zero_elev = config->opt_bool("pad_around_object") && pad_en;
|
||||
|
||||
toggle_field("support_object_elevation", supports_en && !zero_elev);
|
||||
toggle_field("pad_object_gap", zero_elev);
|
||||
toggle_field("pad_around_object_everywhere", zero_elev);
|
||||
toggle_field("pad_object_connector_stride", zero_elev);
|
||||
toggle_field("pad_object_connector_width", zero_elev);
|
||||
toggle_field("pad_object_connector_penetration", zero_elev);
|
||||
}
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
100
src/slic3r/GUI/ConfigManipulation.hpp
Normal file
100
src/slic3r/GUI/ConfigManipulation.hpp
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
#ifndef slic3r_ConfigManipulation_hpp_
|
||||
#define slic3r_ConfigManipulation_hpp_
|
||||
|
||||
/* Class for validation config options
|
||||
* and update (enable/disable) IU components
|
||||
*
|
||||
* Used for config validation for global config (Print Settings Tab)
|
||||
* and local config (overrides options on sidebar)
|
||||
* */
|
||||
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "Field.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class ModelConfig;
|
||||
class ObjectBase;
|
||||
|
||||
namespace GUI {
|
||||
|
||||
class ConfigManipulation
|
||||
{
|
||||
bool is_msg_dlg_already_exist{ false };
|
||||
bool is_timelapse_wipe_tower_already_prompted{false}; // BBS
|
||||
bool m_is_initialized_support_material_overhangs_queried{ false };
|
||||
bool m_support_material_overhangs_queried{ false };
|
||||
|
||||
// function to loading of changed configuration
|
||||
std::function<void()> load_config = nullptr;
|
||||
std::function<void (const std::string&, bool toggle, int opt_index)> cb_toggle_field = nullptr;
|
||||
std::function<void (const std::string&, bool toggle)> cb_toggle_line = nullptr;
|
||||
// callback to propagation of changed value, if needed
|
||||
std::function<void(const std::string&, const boost::any&)> cb_value_change = nullptr;
|
||||
//BBS: change local config to const DynamicPrintConfig
|
||||
const DynamicPrintConfig* local_config = nullptr;
|
||||
//ModelConfig* local_config = nullptr;
|
||||
wxWindow* m_msg_dlg_parent {nullptr};
|
||||
|
||||
t_config_option_keys m_applying_keys;
|
||||
|
||||
public:
|
||||
ConfigManipulation(std::function<void()> load_config,
|
||||
std::function<void(const std::string&, bool toggle, int opt_index)> cb_toggle_field,
|
||||
std::function<void(const std::string&, bool toggle)> cb_toggle_line,
|
||||
std::function<void(const std::string&, const boost::any&)> cb_value_change,
|
||||
//BBS: change local config to DynamicPrintConfig
|
||||
const DynamicPrintConfig* local_config = nullptr,
|
||||
wxWindow* msg_dlg_parent = nullptr) :
|
||||
load_config(load_config),
|
||||
cb_toggle_field(cb_toggle_field),
|
||||
cb_toggle_line(cb_toggle_line),
|
||||
cb_value_change(cb_value_change),
|
||||
m_msg_dlg_parent(msg_dlg_parent),
|
||||
local_config(local_config) {}
|
||||
ConfigManipulation() {}
|
||||
|
||||
~ConfigManipulation() {
|
||||
load_config = nullptr;
|
||||
cb_toggle_field = nullptr;
|
||||
cb_toggle_line = nullptr;
|
||||
cb_value_change = nullptr;
|
||||
}
|
||||
|
||||
bool is_applying() const;
|
||||
|
||||
void apply(DynamicPrintConfig* config, DynamicPrintConfig* new_config);
|
||||
t_config_option_keys const &applying_keys() const;
|
||||
void toggle_field(const std::string& field_key, const bool toggle, int opt_index = -1);
|
||||
void toggle_line(const std::string& field_key, const bool toggle);
|
||||
|
||||
// FFF print
|
||||
void update_print_fff_config(DynamicPrintConfig* config, const bool is_global_config = false);
|
||||
void toggle_print_fff_options(DynamicPrintConfig* config, const bool is_global_config = false);
|
||||
void apply_null_fff_config(DynamicPrintConfig *config, std::vector<std::string> const &keys, std::map<ObjectBase*, ModelConfig*> const & configs);
|
||||
|
||||
//BBS: FFF filament nozzle temperature range
|
||||
void check_nozzle_temperature_range(DynamicPrintConfig* config);
|
||||
void check_nozzle_temperature_initial_layer_range(DynamicPrintConfig* config);
|
||||
void check_bed_temperature_difference(int bed_type, DynamicPrintConfig* config);
|
||||
|
||||
// SLA print
|
||||
void update_print_sla_config(DynamicPrintConfig* config, const bool is_global_config = false);
|
||||
void toggle_print_sla_options(DynamicPrintConfig* config);
|
||||
|
||||
bool is_initialized_support_material_overhangs_queried() { return m_is_initialized_support_material_overhangs_queried; }
|
||||
void initialize_support_material_overhangs_queried(bool queried)
|
||||
{
|
||||
m_is_initialized_support_material_overhangs_queried = true;
|
||||
m_support_material_overhangs_queried = queried;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<int> get_temperature_range_by_filament_type(const std::string &filament_type);
|
||||
|
||||
};
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
|
||||
#endif /* slic3r_ConfigManipulation_hpp_ */
|
||||
2580
src/slic3r/GUI/ConfigWizard.cpp
Normal file
2580
src/slic3r/GUI/ConfigWizard.cpp
Normal file
File diff suppressed because it is too large
Load diff
64
src/slic3r/GUI/ConfigWizard.hpp
Normal file
64
src/slic3r/GUI/ConfigWizard.hpp
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
#ifndef slic3r_ConfigWizard_hpp_
|
||||
#define slic3r_ConfigWizard_hpp_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <wx/dialog.h>
|
||||
|
||||
#include "GUI_Utils.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class PresetBundle;
|
||||
class PresetUpdater;
|
||||
|
||||
namespace GUI {
|
||||
|
||||
|
||||
class ConfigWizard: public DPIDialog
|
||||
{
|
||||
public:
|
||||
// Why is the Wizard run
|
||||
enum RunReason {
|
||||
RR_DATA_EMPTY, // No or empty datadir
|
||||
RR_DATA_LEGACY, // Pre-updating datadir
|
||||
RR_DATA_INCOMPAT, // Incompatible datadir - Slic3r downgrade situation
|
||||
RR_USER, // User requested the Wizard from the menus
|
||||
};
|
||||
|
||||
// What page should wizard start on
|
||||
enum StartPage {
|
||||
SP_WELCOME,
|
||||
SP_PRINTERS,
|
||||
SP_FILAMENTS,
|
||||
SP_MATERIALS,
|
||||
};
|
||||
|
||||
ConfigWizard(wxWindow *parent);
|
||||
ConfigWizard(ConfigWizard &&) = delete;
|
||||
ConfigWizard(const ConfigWizard &) = delete;
|
||||
ConfigWizard &operator=(ConfigWizard &&) = delete;
|
||||
ConfigWizard &operator=(const ConfigWizard &) = delete;
|
||||
~ConfigWizard();
|
||||
|
||||
// Run the Wizard. Return whether it was completed.
|
||||
bool run(RunReason reason, StartPage start_page = SP_WELCOME);
|
||||
|
||||
static const wxString& name(const bool from_menu = false);
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect &suggested_rect) override ;
|
||||
void on_sys_color_changed() override;
|
||||
|
||||
private:
|
||||
struct priv;
|
||||
std::unique_ptr<priv> p;
|
||||
|
||||
friend struct ConfigWizardPage;
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
552
src/slic3r/GUI/ConfigWizard_private.hpp
Normal file
552
src/slic3r/GUI/ConfigWizard_private.hpp
Normal file
|
|
@ -0,0 +1,552 @@
|
|||
#ifndef slic3r_ConfigWizard_private_hpp_
|
||||
#define slic3r_ConfigWizard_private_hpp_
|
||||
|
||||
#include "ConfigWizard.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/choice.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/textctrl.h>
|
||||
#include <wx/listbox.h>
|
||||
#include <wx/checklst.h>
|
||||
#include <wx/radiobut.h>
|
||||
#include <wx/html/htmlwin.h>
|
||||
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "slic3r/Utils/PresetUpdater.hpp"
|
||||
//#include "BedShapeDialog.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
enum {
|
||||
WRAP_WIDTH = 500,
|
||||
MODEL_MIN_WRAP = 150,
|
||||
|
||||
DIALOG_MARGIN = 15,
|
||||
INDEX_MARGIN = 40,
|
||||
BTN_SPACING = 10,
|
||||
INDENT_SPACING = 30,
|
||||
VERTICAL_SPACING = 10,
|
||||
|
||||
MAX_COLS = 4,
|
||||
ROW_SPACING = 75,
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Configuration data structures extensions needed for the wizard
|
||||
|
||||
enum Technology {
|
||||
// Bitflag equivalent of PrinterTechnology
|
||||
T_FFF = 0x1,
|
||||
T_SLA = 0x2,
|
||||
T_ANY = ~0,
|
||||
};
|
||||
|
||||
struct Bundle
|
||||
{
|
||||
std::unique_ptr<PresetBundle> preset_bundle;
|
||||
VendorProfile* vendor_profile{ nullptr };
|
||||
bool is_in_resources{ false };
|
||||
//BBS: set BBL as default
|
||||
bool is_bbl_bundle{ false };
|
||||
|
||||
Bundle() = default;
|
||||
Bundle(Bundle&& other);
|
||||
|
||||
// Returns false if not loaded. Reason for that is logged as boost::log error.
|
||||
//BBS: set BBL as default
|
||||
bool load(fs::path source_path, bool is_in_resources, bool is_bbl_bundle = false);
|
||||
|
||||
const std::string& vendor_id() const { return vendor_profile->id; }
|
||||
};
|
||||
|
||||
struct BundleMap : std::unordered_map<std::string /* = vendor ID */, Bundle>
|
||||
{
|
||||
static BundleMap load();
|
||||
|
||||
//BBS: add BBL as default
|
||||
Bundle& bbl_bundle();
|
||||
const Bundle& bbl_bundle() const;
|
||||
};
|
||||
|
||||
struct Materials
|
||||
{
|
||||
Technology technology;
|
||||
// use vector for the presets to purpose of save of presets sorting in the bundle
|
||||
std::vector<const Preset*> presets;
|
||||
// String is alias of material, size_t number of compatible counters
|
||||
std::vector<std::pair<std::string, size_t>> compatibility_counter;
|
||||
std::set<std::string> types;
|
||||
std::set<const Preset*> printers;
|
||||
|
||||
Materials(Technology technology) : technology(technology) {}
|
||||
|
||||
void push(const Preset *preset);
|
||||
void add_printer(const Preset* preset);
|
||||
void clear();
|
||||
bool containts(const Preset *preset) const {
|
||||
//return std::find(presets.begin(), presets.end(), preset) != presets.end();
|
||||
return std::find_if(presets.begin(), presets.end(),
|
||||
[preset](const Preset* element) { return element == preset; }) != presets.end();
|
||||
|
||||
}
|
||||
|
||||
bool get_omnipresent(const Preset* preset) {
|
||||
return get_printer_counter(preset) == printers.size();
|
||||
}
|
||||
|
||||
const std::vector<const Preset*> get_presets_by_alias(const std::string name) {
|
||||
std::vector<const Preset*> ret_vec;
|
||||
for (auto it = presets.begin(); it != presets.end(); ++it) {
|
||||
if ((*it)->alias == name)
|
||||
ret_vec.push_back((*it));
|
||||
}
|
||||
return ret_vec;
|
||||
}
|
||||
|
||||
|
||||
|
||||
size_t get_printer_counter(const Preset* preset) {
|
||||
for (auto it : compatibility_counter) {
|
||||
if (it.first == preset->alias)
|
||||
return it.second;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const std::string& appconfig_section() const;
|
||||
const std::string& get_type(const Preset *preset) const;
|
||||
const std::string& get_vendor(const Preset *preset) const;
|
||||
|
||||
template<class F> void filter_presets(const Preset* printer, const std::string& type, const std::string& vendor, F cb) {
|
||||
for (auto preset : presets) {
|
||||
const Preset& prst = *(preset);
|
||||
const Preset& prntr = *printer;
|
||||
if ((printer == nullptr || is_compatible_with_printer(PresetWithVendorProfile(prst, prst.vendor), PresetWithVendorProfile(prntr, prntr.vendor))) &&
|
||||
(type.empty() || get_type(preset) == type) &&
|
||||
(vendor.empty() || get_vendor(preset) == vendor)) {
|
||||
|
||||
cb(preset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const std::string UNKNOWN;
|
||||
static const std::string& get_filament_type(const Preset *preset);
|
||||
static const std::string& get_filament_vendor(const Preset *preset);
|
||||
static const std::string& get_material_type(const Preset *preset);
|
||||
static const std::string& get_material_vendor(const Preset *preset);
|
||||
};
|
||||
|
||||
|
||||
struct PrinterPickerEvent;
|
||||
|
||||
// GUI elements
|
||||
|
||||
typedef std::function<bool(const VendorProfile::PrinterModel&)> ModelFilter;
|
||||
|
||||
struct PrinterPicker: wxPanel
|
||||
{
|
||||
struct Checkbox : wxCheckBox
|
||||
{
|
||||
Checkbox(wxWindow *parent, const wxString &label, const std::string &model, const std::string &variant) :
|
||||
wxCheckBox(parent, wxID_ANY, label),
|
||||
model(model),
|
||||
variant(variant)
|
||||
{}
|
||||
|
||||
std::string model;
|
||||
std::string variant;
|
||||
};
|
||||
|
||||
const std::string vendor_id;
|
||||
std::vector<Checkbox*> cboxes;
|
||||
std::vector<Checkbox*> cboxes_alt;
|
||||
|
||||
PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig, const ModelFilter &filter);
|
||||
PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig);
|
||||
|
||||
void select_all(bool select, bool alternates = false);
|
||||
void select_one(size_t i, bool select);
|
||||
bool any_selected() const;
|
||||
std::set<std::string> get_selected_models() const ;
|
||||
|
||||
int get_width() const { return width; }
|
||||
const std::vector<int>& get_button_indexes() { return m_button_indexes; }
|
||||
|
||||
static const std::string PRINTER_PLACEHOLDER;
|
||||
private:
|
||||
int width;
|
||||
std::vector<int> m_button_indexes;
|
||||
|
||||
void on_checkbox(const Checkbox *cbox, bool checked);
|
||||
};
|
||||
|
||||
struct ConfigWizardPage: wxPanel
|
||||
{
|
||||
ConfigWizard *parent;
|
||||
const wxString shortname;
|
||||
wxBoxSizer *content;
|
||||
const unsigned indent;
|
||||
|
||||
ConfigWizardPage(ConfigWizard *parent, wxString title, wxString shortname, unsigned indent = 0);
|
||||
virtual ~ConfigWizardPage();
|
||||
|
||||
template<class T>
|
||||
T* append(T *thing, int proportion = 0, int flag = wxEXPAND|wxTOP|wxBOTTOM, int border = 10)
|
||||
{
|
||||
content->Add(thing, proportion, flag, border);
|
||||
return thing;
|
||||
}
|
||||
|
||||
wxStaticText* append_text(wxString text);
|
||||
void append_spacer(int space);
|
||||
|
||||
ConfigWizard::priv *wizard_p() const { return parent->p.get(); }
|
||||
|
||||
virtual void apply_custom_config(DynamicPrintConfig &config) {}
|
||||
virtual void set_run_reason(ConfigWizard::RunReason run_reason) {}
|
||||
virtual void on_activate() {}
|
||||
};
|
||||
|
||||
struct PageWelcome: ConfigWizardPage
|
||||
{
|
||||
wxStaticText *welcome_text;
|
||||
wxCheckBox *cbox_reset;
|
||||
wxCheckBox *cbox_integrate;
|
||||
|
||||
PageWelcome(ConfigWizard *parent);
|
||||
|
||||
bool reset_user_profile() const { return cbox_reset != nullptr ? cbox_reset->GetValue() : false; }
|
||||
bool integrate_desktop() const { return cbox_integrate != nullptr ? cbox_integrate->GetValue() : false; }
|
||||
|
||||
virtual void set_run_reason(ConfigWizard::RunReason run_reason) override;
|
||||
};
|
||||
|
||||
struct PagePrinters: ConfigWizardPage
|
||||
{
|
||||
std::vector<PrinterPicker *> printer_pickers;
|
||||
Technology technology;
|
||||
bool install;
|
||||
|
||||
PagePrinters(ConfigWizard *parent,
|
||||
wxString title,
|
||||
wxString shortname,
|
||||
const VendorProfile &vendor,
|
||||
unsigned indent, Technology technology);
|
||||
|
||||
void select_all(bool select, bool alternates = false);
|
||||
int get_width() const;
|
||||
bool any_selected() const;
|
||||
std::set<std::string> get_selected_models();
|
||||
|
||||
std::string get_vendor_id() const { return printer_pickers.empty() ? "" : printer_pickers[0]->vendor_id; }
|
||||
|
||||
virtual void set_run_reason(ConfigWizard::RunReason run_reason) override;
|
||||
|
||||
bool has_printers { false };
|
||||
bool is_primary_printer_page { false };
|
||||
};
|
||||
|
||||
// Here we extend wxListBox and wxCheckListBox
|
||||
// to make the client data API much easier to use.
|
||||
template<class T, class D> struct DataList : public T
|
||||
{
|
||||
DataList(wxWindow *parent) : T(parent, wxID_ANY) {}
|
||||
DataList(wxWindow* parent, int style) : T(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, style) {}
|
||||
|
||||
// Note: We're _not_ using wxLB_SORT here because it doesn't do the right thing,
|
||||
// eg. "ABS" is sorted before "(All)"
|
||||
|
||||
int append(const std::string &label, const D *data) {
|
||||
void *ptr = reinterpret_cast<void*>(const_cast<D*>(data));
|
||||
return this->Append(from_u8(label), ptr);
|
||||
}
|
||||
|
||||
int append(const wxString &label, const D *data) {
|
||||
void *ptr = reinterpret_cast<void*>(const_cast<D*>(data));
|
||||
return this->Append(label, ptr);
|
||||
}
|
||||
|
||||
const D& get_data(int n) {
|
||||
return *reinterpret_cast<const D*>(this->GetClientData(n));
|
||||
}
|
||||
|
||||
int find(const D &data) {
|
||||
for (unsigned i = 0; i < this->GetCount(); i++) {
|
||||
if (get_data(i) == data) { return i; }
|
||||
}
|
||||
|
||||
return wxNOT_FOUND;
|
||||
}
|
||||
|
||||
int size() { return this->GetCount(); }
|
||||
|
||||
void on_mouse_move(const wxPoint& position) {
|
||||
int item = T::HitTest(position);
|
||||
|
||||
if(item == wxHitTest::wxHT_WINDOW_INSIDE)
|
||||
BOOST_LOG_TRIVIAL(error) << "hit test wxHT_WINDOW_INSIDE";
|
||||
else if (item == wxHitTest::wxHT_WINDOW_OUTSIDE)
|
||||
BOOST_LOG_TRIVIAL(error) << "hit test wxHT_WINDOW_OUTSIDE";
|
||||
else if(item == wxHitTest::wxHT_WINDOW_CORNER)
|
||||
BOOST_LOG_TRIVIAL(error) << "hit test wxHT_WINDOW_CORNER";
|
||||
else if (item == wxHitTest::wxHT_WINDOW_VERT_SCROLLBAR)
|
||||
BOOST_LOG_TRIVIAL(error) << "hit test wxHT_WINDOW_VERT_SCROLLBAR";
|
||||
else if (item == wxHitTest::wxHT_NOWHERE)
|
||||
BOOST_LOG_TRIVIAL(error) << "hit test wxHT_NOWHERE";
|
||||
else if (item == wxHitTest::wxHT_MAX)
|
||||
BOOST_LOG_TRIVIAL(error) << "hit test wxHT_MAX";
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(error) << "hit test: " << item;
|
||||
}
|
||||
};
|
||||
|
||||
typedef DataList<wxListBox, std::string> StringList;
|
||||
typedef DataList<wxCheckListBox, std::string> PresetList;
|
||||
|
||||
struct ProfilePrintData
|
||||
{
|
||||
std::reference_wrapper<const std::string> name;
|
||||
bool omnipresent;
|
||||
bool checked;
|
||||
ProfilePrintData(const std::string& n, bool o, bool c) : name(n), omnipresent(o), checked(c) {}
|
||||
};
|
||||
|
||||
struct PageMaterials: ConfigWizardPage
|
||||
{
|
||||
Materials *materials;
|
||||
StringList *list_printer, *list_type, *list_vendor;
|
||||
PresetList *list_profile;
|
||||
wxArrayInt sel_printers_prev;
|
||||
int sel_type_prev, sel_vendor_prev;
|
||||
bool presets_loaded;
|
||||
|
||||
wxFlexGridSizer *grid;
|
||||
wxHtmlWindow* html_window;
|
||||
|
||||
int compatible_printers_width = { 100 };
|
||||
std::string empty_printers_label;
|
||||
bool first_paint = { false };
|
||||
static const std::string EMPTY;
|
||||
int last_hovered_item = { -1 } ;
|
||||
|
||||
PageMaterials(ConfigWizard *parent, Materials *materials, wxString title, wxString shortname, wxString list1name);
|
||||
|
||||
void reload_presets();
|
||||
void update_lists(int sel_type, int sel_vendor, int last_selected_printer = -1);
|
||||
void on_material_highlighted(int sel_material);
|
||||
void on_material_hovered(int sel_material);
|
||||
void select_material(int i);
|
||||
void select_all(bool select);
|
||||
void clear();
|
||||
void set_compatible_printers_html_window(const std::vector<std::string>& printer_names, bool all_printers = false);
|
||||
void clear_compatible_printers_label();
|
||||
|
||||
void sort_list_data(StringList* list, bool add_All_item, bool material_type_ordering);
|
||||
void sort_list_data(PresetList* list, const std::vector<ProfilePrintData>& data);
|
||||
|
||||
void on_paint();
|
||||
void on_mouse_move_on_profiles(wxMouseEvent& evt);
|
||||
void on_mouse_enter_profiles(wxMouseEvent& evt);
|
||||
void on_mouse_leave_profiles(wxMouseEvent& evt);
|
||||
virtual void on_activate() override;
|
||||
};
|
||||
|
||||
struct PageCustom: ConfigWizardPage
|
||||
{
|
||||
PageCustom(ConfigWizard *parent);
|
||||
|
||||
bool custom_wanted() const { return cb_custom->GetValue(); }
|
||||
std::string profile_name() const { return into_u8(tc_profile_name->GetValue()); }
|
||||
|
||||
private:
|
||||
static const char* default_profile_name;
|
||||
|
||||
wxCheckBox *cb_custom;
|
||||
wxTextCtrl *tc_profile_name;
|
||||
wxString profile_name_prev;
|
||||
|
||||
};
|
||||
|
||||
struct PageUpdate: ConfigWizardPage
|
||||
{
|
||||
bool version_check;
|
||||
bool preset_update;
|
||||
|
||||
PageUpdate(ConfigWizard *parent);
|
||||
};
|
||||
|
||||
struct PageReloadFromDisk : ConfigWizardPage
|
||||
{
|
||||
bool full_pathnames;
|
||||
|
||||
PageReloadFromDisk(ConfigWizard* parent);
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
struct PageFilesAssociation : ConfigWizardPage
|
||||
{
|
||||
private:
|
||||
wxCheckBox* cb_3mf{ nullptr };
|
||||
wxCheckBox* cb_stl{ nullptr };
|
||||
// wxCheckBox* cb_gcode;
|
||||
|
||||
public:
|
||||
PageFilesAssociation(ConfigWizard* parent);
|
||||
|
||||
bool associate_3mf() const { return cb_3mf->IsChecked(); }
|
||||
bool associate_stl() const { return cb_stl->IsChecked(); }
|
||||
// bool associate_gcode() const { return cb_gcode->IsChecked(); }
|
||||
};
|
||||
#endif // _WIN32
|
||||
|
||||
struct PageVendors: ConfigWizardPage
|
||||
{
|
||||
PageVendors(ConfigWizard *parent);
|
||||
};
|
||||
|
||||
/*struct PageBedShape: ConfigWizardPage
|
||||
{
|
||||
BedShapePanel *shape_panel;
|
||||
|
||||
PageBedShape(ConfigWizard *parent);
|
||||
virtual void apply_custom_config(DynamicPrintConfig &config);
|
||||
};*/
|
||||
|
||||
struct PageDiameters: ConfigWizardPage
|
||||
{
|
||||
wxTextCtrl *diam_nozzle;
|
||||
wxTextCtrl *diam_filam;
|
||||
|
||||
PageDiameters(ConfigWizard *parent);
|
||||
virtual void apply_custom_config(DynamicPrintConfig &config);
|
||||
};
|
||||
|
||||
struct PageTemperatures: ConfigWizardPage
|
||||
{
|
||||
wxSpinCtrlDouble *spin_extr;
|
||||
wxSpinCtrlDouble *spin_bed;
|
||||
|
||||
PageTemperatures(ConfigWizard *parent);
|
||||
virtual void apply_custom_config(DynamicPrintConfig &config);
|
||||
};
|
||||
|
||||
// hypothetically, each vendor can has printers both of technologies (FFF and SLA)
|
||||
typedef std::map<std::string /* = vendor ID */,
|
||||
std::pair<PagePrinters* /* = FFF page */,
|
||||
PagePrinters* /* = SLA page */>> Pages3rdparty;
|
||||
|
||||
// ConfigWizard private data
|
||||
|
||||
typedef std::map<std::string, std::set<std::string>> PresetAliases;
|
||||
|
||||
struct ConfigWizard::priv
|
||||
{
|
||||
ConfigWizard *q;
|
||||
ConfigWizard::RunReason run_reason = RR_USER;
|
||||
AppConfig appconfig_new; // Backing for vendor/model/variant and material selections in the GUI
|
||||
BundleMap bundles; // Holds all loaded config bundles, the key is the vendor names.
|
||||
// Materials refers to Presets in those bundles by pointers.
|
||||
// Also we update the is_visible flag in printer Presets according to the
|
||||
// PrinterPickers state.
|
||||
Materials filaments; // Holds available filament presets and their types & vendors
|
||||
Materials sla_materials; // Ditto for SLA materials
|
||||
PresetAliases aliases_fff; // Map of aliase to preset names
|
||||
PresetAliases aliases_sla; // Map of aliase to preset names
|
||||
std::unique_ptr<DynamicPrintConfig> custom_config; // Backing for custom printer definition
|
||||
bool any_fff_selected; // Used to decide whether to display Filaments page
|
||||
bool any_sla_selected; // Used to decide whether to display SLA Materials page
|
||||
bool custom_printer_selected { false };
|
||||
// Set to true if there are none FFF printers on the main FFF page. If true, only SLA printers are shown (not even custum printers)
|
||||
bool only_sla_mode { false };
|
||||
|
||||
wxScrolledWindow *hscroll = nullptr;
|
||||
wxBoxSizer *hscroll_sizer = nullptr;
|
||||
wxBoxSizer *btnsizer = nullptr;
|
||||
ConfigWizardPage *page_current = nullptr;
|
||||
wxButton *btn_sel_all = nullptr;
|
||||
wxButton *btn_prev = nullptr;
|
||||
wxButton *btn_next = nullptr;
|
||||
wxButton *btn_finish = nullptr;
|
||||
wxButton *btn_cancel = nullptr;
|
||||
|
||||
PagePrinters *page_fff = nullptr;
|
||||
PagePrinters *page_msla = nullptr;
|
||||
PageMaterials *page_filaments = nullptr;
|
||||
PageMaterials *page_sla_materials = nullptr;
|
||||
PageCustom *page_custom = nullptr;
|
||||
PageReloadFromDisk *page_reload_from_disk = nullptr;
|
||||
#ifdef _WIN32
|
||||
PageFilesAssociation* page_files_association = nullptr;
|
||||
#endif // _WIN32
|
||||
PageVendors *page_vendors = nullptr;
|
||||
Pages3rdparty pages_3rdparty;
|
||||
|
||||
// Custom setup pages
|
||||
//PageBedShape *page_bed = nullptr;
|
||||
PageDiameters *page_diams = nullptr;
|
||||
PageTemperatures *page_temps = nullptr;
|
||||
|
||||
// Pointers to all pages (regardless or whether currently part of the ConfigWizardIndex)
|
||||
std::vector<ConfigWizardPage*> all_pages;
|
||||
|
||||
priv(ConfigWizard *q)
|
||||
: q(q)
|
||||
, appconfig_new()
|
||||
, filaments(T_FFF)
|
||||
, sla_materials(T_SLA)
|
||||
{}
|
||||
|
||||
void load_pages();
|
||||
void init_dialog_size();
|
||||
|
||||
void load_vendors();
|
||||
void add_page(ConfigWizardPage *page);
|
||||
void enable_next(bool enable);
|
||||
void set_start_page(ConfigWizard::StartPage start_page);
|
||||
void create_3rdparty_pages();
|
||||
void set_run_reason(RunReason run_reason);
|
||||
void update_materials(Technology technology);
|
||||
|
||||
void on_custom_setup(const bool custom_wanted);
|
||||
void on_printer_pick(PagePrinters *page, const PrinterPickerEvent &evt);
|
||||
void select_default_materials_for_printer_model(const VendorProfile::PrinterModel &printer_model, Technology technology);
|
||||
void select_default_materials_for_printer_models(Technology technology, const std::set<const VendorProfile::PrinterModel*> &printer_models);
|
||||
void on_3rdparty_install(const VendorProfile *vendor, bool install);
|
||||
|
||||
bool on_bnt_finish();
|
||||
bool check_and_install_missing_materials(Technology technology, const std::string &only_for_model_id = std::string());
|
||||
bool apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater, bool& apply_keeped_changes);
|
||||
// #ys_FIXME_alise
|
||||
void update_presets_in_config(const std::string& section, const std::string& alias_key, bool add);
|
||||
#ifdef __linux__
|
||||
void perform_desktop_integration() const;
|
||||
#endif
|
||||
bool check_fff_selected(); // Used to decide whether to display Filaments page
|
||||
bool check_sla_selected(); // Used to decide whether to display SLA Materials page
|
||||
|
||||
int em() const { return 10; }
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
154
src/slic3r/GUI/ConnectPrinter.cpp
Normal file
154
src/slic3r/GUI/ConnectPrinter.cpp
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
#include "ConnectPrinter.hpp"
|
||||
#include <slic3r/GUI/I18N.hpp>
|
||||
#include <slic3r/GUI/Widgets/Label.hpp>
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
ConnectPrinterDialog::ConnectPrinterDialog(wxWindow *parent, wxWindowID id, const wxString &title, const wxPoint &pos, const wxSize &size, long style)
|
||||
: DPIDialog(parent, id, _L("ConnectPrinter(LAN)"), pos, size, style)
|
||||
{
|
||||
init_bitmap();
|
||||
|
||||
this->SetSizeHints(wxDefaultSize, wxDefaultSize);
|
||||
|
||||
wxBoxSizer *main_sizer;
|
||||
main_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
main_sizer->Add(FromDIP(40), 0);
|
||||
|
||||
wxBoxSizer *sizer_top;
|
||||
sizer_top = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
sizer_top->Add(0, FromDIP(40));
|
||||
|
||||
m_staticText_connection_code = new wxStaticText(this, wxID_ANY, _L("Please input the printer access code:"), wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_staticText_connection_code->SetFont(Label::Body_15);
|
||||
m_staticText_connection_code->SetForegroundColour(wxColour(50, 58, 61));
|
||||
m_staticText_connection_code->Wrap(-1);
|
||||
sizer_top->Add(m_staticText_connection_code, 0, wxALL, 0);
|
||||
|
||||
sizer_top->Add(0, FromDIP(10));
|
||||
|
||||
wxBoxSizer *sizer_connect;
|
||||
sizer_connect = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
m_textCtrl_code = new TextInput(this, wxEmptyString);
|
||||
m_textCtrl_code->SetFont(Label::Body_14);
|
||||
m_textCtrl_code->SetCornerRadius(FromDIP(5));
|
||||
m_textCtrl_code->SetSize(wxSize(FromDIP(330), FromDIP(40)));
|
||||
m_textCtrl_code->SetMinSize(wxSize(FromDIP(330), FromDIP(40)));
|
||||
m_textCtrl_code->GetTextCtrl()->SetSize(wxSize(-1, FromDIP(22)));
|
||||
m_textCtrl_code->GetTextCtrl()->SetMinSize(wxSize(-1, FromDIP(22)));
|
||||
m_textCtrl_code->SetBackgroundColour(*wxWHITE);
|
||||
m_textCtrl_code->GetTextCtrl()->SetForegroundColour(wxColour(107, 107, 107));
|
||||
sizer_connect->Add(m_textCtrl_code, 0, wxALL | wxALIGN_CENTER_VERTICAL, 0);
|
||||
|
||||
sizer_connect->Add(FromDIP(20), 0);
|
||||
|
||||
m_button_confirm = new Button(this, _L("Confirm"));
|
||||
m_button_confirm->SetFont(Label::Body_12);
|
||||
m_button_confirm->SetMinSize(wxSize(-1, FromDIP(24)));
|
||||
m_button_confirm->SetCornerRadius(FromDIP(12));
|
||||
|
||||
StateColor btn_bg(
|
||||
std::pair<wxColour, int>(wxColour(27, 136, 68), StateColor::Pressed),
|
||||
std::pair<wxColour, int>(wxColour(61, 203, 115), StateColor::Hovered),
|
||||
std::pair<wxColour, int>(wxColour(0, 174, 66), StateColor::Normal)
|
||||
);
|
||||
|
||||
StateColor btn_bd(std::pair<wxColour, int>(wxColour(0, 174, 66), StateColor::Normal));
|
||||
|
||||
StateColor btn_text(std::pair<wxColour, int>(wxColour(255, 255, 255), StateColor::Normal));
|
||||
|
||||
m_button_confirm->SetBackgroundColor(btn_bg);
|
||||
m_button_confirm->SetBorderColor(btn_bd);
|
||||
m_button_confirm->SetTextColor(btn_text);
|
||||
|
||||
sizer_connect->Add(m_button_confirm, 0, wxALL | wxALIGN_CENTER_VERTICAL, 0);
|
||||
|
||||
sizer_connect->Add(FromDIP(60), 0);
|
||||
|
||||
sizer_top->Add(sizer_connect);
|
||||
|
||||
sizer_top->Add(0, FromDIP(35));
|
||||
|
||||
m_staticText_hints = new wxStaticText(this, wxID_ANY, _L("You can find it in \"Settings > Network > Connection code\"\non the printer, as shown in the figure:"), wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_staticText_hints->SetFont(Label::Body_15);
|
||||
m_staticText_hints->SetForegroundColour(wxColour(50, 58, 61));
|
||||
m_staticText_hints->Wrap(-1);
|
||||
sizer_top->Add(m_staticText_hints, 0, wxALL, 0);
|
||||
|
||||
sizer_top->Add(0, FromDIP(25));
|
||||
|
||||
wxBoxSizer *sizer_diagram;
|
||||
sizer_diagram = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
m_bitmap_diagram = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize(FromDIP(340), FromDIP(190)), 0);
|
||||
m_bitmap_diagram->SetBitmap(m_diagram_img);
|
||||
sizer_diagram->Add(m_bitmap_diagram);
|
||||
|
||||
sizer_top->Add(sizer_diagram);
|
||||
|
||||
sizer_top->Add(0, FromDIP(40), 0, wxEXPAND, 0);
|
||||
|
||||
main_sizer->Add(sizer_top);
|
||||
|
||||
this->SetSizer(main_sizer);
|
||||
this->Layout();
|
||||
this->Fit();
|
||||
this->Centre(wxBOTH);
|
||||
|
||||
m_textCtrl_code->Bind(wxEVT_TEXT, &ConnectPrinterDialog::on_input_enter, this);
|
||||
m_button_confirm->Bind(wxEVT_BUTTON, &ConnectPrinterDialog::on_button_confirm, this);
|
||||
}
|
||||
|
||||
ConnectPrinterDialog::~ConnectPrinterDialog() {}
|
||||
|
||||
void ConnectPrinterDialog::init_bitmap()
|
||||
{
|
||||
AppConfig *config = get_app_config();
|
||||
std::string language = config->get("language");
|
||||
if (language == "zh_CN") {
|
||||
m_diagram_bmp = create_scaled_bitmap("input_access_code_cn", nullptr, 190);
|
||||
}
|
||||
else{
|
||||
m_diagram_bmp = create_scaled_bitmap("input_access_code_en", nullptr, 190);
|
||||
}
|
||||
m_diagram_img = m_diagram_bmp.ConvertToImage();
|
||||
m_diagram_img.Rescale(FromDIP(340), FromDIP(190));
|
||||
}
|
||||
|
||||
void ConnectPrinterDialog::set_machine_object(MachineObject* obj)
|
||||
{
|
||||
m_obj = obj;
|
||||
}
|
||||
|
||||
void ConnectPrinterDialog::on_input_enter(wxCommandEvent& evt)
|
||||
{
|
||||
m_input_access_code = evt.GetString();
|
||||
}
|
||||
|
||||
|
||||
void ConnectPrinterDialog::on_button_confirm(wxCommandEvent &event)
|
||||
{
|
||||
wxString code = m_textCtrl_code->GetTextCtrl()->GetValue();
|
||||
if (m_obj) {
|
||||
m_obj->set_access_code(code.ToStdString());
|
||||
}
|
||||
EndModal(wxID_OK);
|
||||
}
|
||||
|
||||
void ConnectPrinterDialog::on_dpi_changed(const wxRect &suggested_rect)
|
||||
{
|
||||
init_bitmap();
|
||||
m_bitmap_diagram->SetBitmap(m_diagram_img);
|
||||
m_textCtrl_code->GetTextCtrl()->SetSize(wxSize(-1, FromDIP(22)));
|
||||
m_textCtrl_code->GetTextCtrl()->SetMinSize(wxSize(-1, FromDIP(22)));
|
||||
|
||||
m_button_confirm->SetCornerRadius(FromDIP(12));
|
||||
m_button_confirm->Rescale();
|
||||
|
||||
Layout();
|
||||
this->Refresh();
|
||||
}
|
||||
}} // namespace Slic3r::GUI
|
||||
56
src/slic3r/GUI/ConnectPrinter.hpp
Normal file
56
src/slic3r/GUI/ConnectPrinter.hpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#ifndef slic3r_GUI_ConnectPrinter_hpp_
|
||||
#define slic3r_GUI_ConnectPrinter_hpp_
|
||||
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_Utils.hpp"
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/icon.h>
|
||||
#include <wx/statbmp.h>
|
||||
#include "Widgets/Button.hpp"
|
||||
#include "Widgets/TextInput.hpp"
|
||||
#include "DeviceManager.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
class ConnectPrinterDialog : public DPIDialog
|
||||
{
|
||||
private:
|
||||
protected:
|
||||
wxStaticText * m_staticText_connection_code;
|
||||
TextInput * m_textCtrl_code;
|
||||
Button * m_button_confirm;
|
||||
wxStaticText* m_staticText_hints;
|
||||
wxStaticBitmap* m_bitmap_diagram;
|
||||
wxBitmap m_diagram_bmp;
|
||||
wxImage m_diagram_img;
|
||||
|
||||
MachineObject* m_obj;
|
||||
wxString m_input_access_code;
|
||||
public:
|
||||
ConnectPrinterDialog(wxWindow * parent,
|
||||
wxWindowID id = wxID_ANY,
|
||||
const wxString &title = wxEmptyString,
|
||||
const wxPoint & pos = wxDefaultPosition,
|
||||
const wxSize & size = wxDefaultSize,
|
||||
long style = wxCLOSE_BOX | wxCAPTION);
|
||||
|
||||
~ConnectPrinterDialog();
|
||||
|
||||
void init_bitmap();
|
||||
void set_machine_object(MachineObject* obj);
|
||||
void on_input_enter(wxCommandEvent& evt);
|
||||
void on_button_confirm(wxCommandEvent &event);
|
||||
void on_dpi_changed(const wxRect &suggested_rect) override;
|
||||
};
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
#endif
|
||||
506
src/slic3r/GUI/DesktopIntegrationDialog.cpp
Normal file
506
src/slic3r/GUI/DesktopIntegrationDialog.cpp
Normal file
|
|
@ -0,0 +1,506 @@
|
|||
#ifdef __linux__
|
||||
#include "DesktopIntegrationDialog.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "format.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "NotificationManager.hpp"
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Platform.hpp"
|
||||
#include "libslic3r/Config.hpp"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/dll/runtime_symbol_info.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
#include <wx/filename.h>
|
||||
#include <wx/stattext.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
namespace {
|
||||
|
||||
// escaping of path string according to
|
||||
// https://cgit.freedesktop.org/xdg/xdg-specs/tree/desktop-entry/desktop-entry-spec.xml
|
||||
std::string escape_string(const std::string& str)
|
||||
{
|
||||
// The buffer needs to be bigger if escaping <,>,&
|
||||
std::vector<char> out(str.size() * 4, 0);
|
||||
char *outptr = out.data();
|
||||
for (size_t i = 0; i < str.size(); ++ i) {
|
||||
char c = str[i];
|
||||
// must be escaped
|
||||
if (c == '\"') { //double quote
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '\"';
|
||||
} else if (c == '`') { // backtick character
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '`';
|
||||
} else if (c == '$') { // dollar sign
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '$';
|
||||
} else if (c == '\\') { // backslash character
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '\\';
|
||||
// Reserved characters
|
||||
// At Ubuntu, all these characters must NOT be escaped for desktop integration to work
|
||||
/*
|
||||
} else if (c == ' ') { // space
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = ' ';
|
||||
} else if (c == '\t') { // tab
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '\t';
|
||||
} else if (c == '\n') { // newline
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '\n';
|
||||
} else if (c == '\'') { // single quote
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '\'';
|
||||
} else if (c == '>') { // greater-than sign
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '&';
|
||||
(*outptr ++) = 'g';
|
||||
(*outptr ++) = 't';
|
||||
(*outptr ++) = ';';
|
||||
} else if (c == '<') { //less-than sign
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '&';
|
||||
(*outptr ++) = 'l';
|
||||
(*outptr ++) = 't';
|
||||
(*outptr ++) = ';';
|
||||
} else if (c == '~') { // tilde
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '~';
|
||||
} else if (c == '|') { // vertical bar
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '|';
|
||||
} else if (c == '&') { // ampersand
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '&';
|
||||
(*outptr ++) = 'a';
|
||||
(*outptr ++) = 'm';
|
||||
(*outptr ++) = 'p';
|
||||
(*outptr ++) = ';';
|
||||
} else if (c == ';') { // semicolon
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = ';';
|
||||
} else if (c == '*') { //asterisk
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '*';
|
||||
} else if (c == '?') { // question mark
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '?';
|
||||
} else if (c == '#') { // hash mark
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '#';
|
||||
} else if (c == '(') { // parenthesis
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '(';
|
||||
} else if (c == ')') {
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = ')';
|
||||
*/
|
||||
} else
|
||||
(*outptr ++) = c;
|
||||
}
|
||||
return std::string(out.data(), outptr - out.data());
|
||||
}
|
||||
// Disects path strings stored in system variable divided by ':' and adds into vector
|
||||
void resolve_path_from_var(const std::string& var, std::vector<std::string>& paths)
|
||||
{
|
||||
wxString wxdirs;
|
||||
if (! wxGetEnv(boost::nowide::widen(var), &wxdirs) || wxdirs.empty() )
|
||||
return;
|
||||
std::string dirs = boost::nowide::narrow(wxdirs);
|
||||
for (size_t i = dirs.find(':'); i != std::string::npos; i = dirs.find(':'))
|
||||
{
|
||||
paths.push_back(dirs.substr(0, i));
|
||||
if (dirs.size() > i+1)
|
||||
dirs = dirs.substr(i+1);
|
||||
}
|
||||
paths.push_back(dirs);
|
||||
}
|
||||
// Return true if directory in path p+dir_name exists
|
||||
bool contains_path_dir(const std::string& p, const std::string& dir_name)
|
||||
{
|
||||
if (p.empty() || dir_name.empty())
|
||||
return false;
|
||||
boost::filesystem::path path(p + (p[p.size()-1] == '/' ? "" : "/") + dir_name);
|
||||
if (boost::filesystem::exists(path) && boost::filesystem::is_directory(path)) {
|
||||
//BOOST_LOG_TRIVIAL(debug) << path.string() << " " << std::oct << boost::filesystem::status(path).permissions();
|
||||
return true; //boost::filesystem::status(path).permissions() & boost::filesystem::owner_write;
|
||||
} else
|
||||
BOOST_LOG_TRIVIAL(debug) << path.string() << " doesnt exists";
|
||||
return false;
|
||||
}
|
||||
// Creates directory in path if not exists yet
|
||||
void create_dir(const boost::filesystem::path& path)
|
||||
{
|
||||
if (boost::filesystem::exists(path))
|
||||
return;
|
||||
BOOST_LOG_TRIVIAL(debug)<< "creating " << path.string();
|
||||
boost::system::error_code ec;
|
||||
boost::filesystem::create_directory(path, ec);
|
||||
if (ec)
|
||||
BOOST_LOG_TRIVIAL(error)<< "create directory failed: " << ec.message();
|
||||
}
|
||||
// Starts at basic_path (excluded) and creates all directories in dir_path
|
||||
void create_path(const std::string& basic_path, const std::string& dir_path)
|
||||
{
|
||||
if (basic_path.empty() || dir_path.empty())
|
||||
return;
|
||||
|
||||
boost::filesystem::path path(basic_path);
|
||||
std::string dirs = dir_path;
|
||||
for (size_t i = dirs.find('/'); i != std::string::npos; i = dirs.find('/'))
|
||||
{
|
||||
std::string dir = dirs.substr(0, i);
|
||||
path = boost::filesystem::path(path.string() +"/"+ dir);
|
||||
create_dir(path);
|
||||
dirs = dirs.substr(i+1);
|
||||
}
|
||||
path = boost::filesystem::path(path.string() +"/"+ dirs);
|
||||
create_dir(path);
|
||||
}
|
||||
// Calls our internal copy_file function to copy file at icon_path to dest_path
|
||||
bool copy_icon(const std::string& icon_path, const std::string& dest_path)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) <<"icon from "<< icon_path;
|
||||
BOOST_LOG_TRIVIAL(debug) <<"icon to "<< dest_path;
|
||||
std::string error_message;
|
||||
auto cfr = copy_file(icon_path, dest_path, error_message, false);
|
||||
if (cfr) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "Copy icon fail(" << cfr << "): " << error_message;
|
||||
return false;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(debug) << "Copy icon success.";
|
||||
return true;
|
||||
}
|
||||
// Creates new file filled with data.
|
||||
bool create_desktop_file(const std::string& path, const std::string& data)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) <<".desktop to "<< path;
|
||||
std::ofstream output(path);
|
||||
output << data;
|
||||
struct stat buffer;
|
||||
if (stat(path.c_str(), &buffer) == 0)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << "Desktop file created.";
|
||||
return true;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(debug) << "Desktop file NOT created.";
|
||||
return false;
|
||||
}
|
||||
} // namespace integratec_desktop_internal
|
||||
|
||||
// methods that actually do / undo desktop integration. Static to be accesible from anywhere.
|
||||
bool DesktopIntegrationDialog::is_integrated()
|
||||
{
|
||||
const AppConfig *app_config = wxGetApp().app_config;
|
||||
std::string path(app_config->get("desktop_integration_app_path"));
|
||||
BOOST_LOG_TRIVIAL(debug) << "Desktop integration desktop file path: " << path;
|
||||
|
||||
if (path.empty())
|
||||
return false;
|
||||
|
||||
// confirmation that BambuStudio.desktop exists
|
||||
struct stat buffer;
|
||||
return (stat (path.c_str(), &buffer) == 0);
|
||||
}
|
||||
bool DesktopIntegrationDialog::integration_possible()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
void DesktopIntegrationDialog::perform_desktop_integration()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << "performing desktop integration";
|
||||
|
||||
// Path to appimage
|
||||
const char *appimage_env = std::getenv("APPIMAGE");
|
||||
std::string excutable_path;
|
||||
if (appimage_env) {
|
||||
try {
|
||||
excutable_path = boost::filesystem::canonical(boost::filesystem::path(appimage_env)).string();
|
||||
} catch (std::exception &) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - boost::filesystem::canonical did not return appimage path.";
|
||||
show_error(nullptr, _L("Performing desktop integration failed - boost::filesystem::canonical did not return appimage path."));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// not appimage - find executable
|
||||
excutable_path = boost::dll::program_location().string();
|
||||
//excutable_path = wxStandardPaths::Get().GetExecutablePath().string();
|
||||
BOOST_LOG_TRIVIAL(debug) << "non-appimage path to executable: " << excutable_path;
|
||||
if (excutable_path.empty())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - no executable found.";
|
||||
show_error(nullptr, _L("Performing desktop integration failed - Could not find executable."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Escape ' characters in appimage, other special symbols will be esacaped in desktop file by 'excutable_path'
|
||||
//boost::replace_all(excutable_path, "'", "'\\''");
|
||||
excutable_path = escape_string(excutable_path);
|
||||
|
||||
// Find directories icons and applications
|
||||
// $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored.
|
||||
// If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used.
|
||||
// $XDG_DATA_DIRS defines the preference-ordered set of base directories to search for data files in addition to the $XDG_DATA_HOME base directory.
|
||||
// The directories in $XDG_DATA_DIRS should be seperated with a colon ':'.
|
||||
// If $XDG_DATA_DIRS is either not set or empty, a value equal to /usr/local/share/:/usr/share/ should be used.
|
||||
std::vector<std::string>target_candidates;
|
||||
resolve_path_from_var("XDG_DATA_HOME", target_candidates);
|
||||
resolve_path_from_var("XDG_DATA_DIRS", target_candidates);
|
||||
|
||||
AppConfig *app_config = wxGetApp().app_config;
|
||||
// suffix string to create different desktop file for alpha, beta.
|
||||
|
||||
std::string version_suffix;
|
||||
std::string name_suffix;
|
||||
std::string version(SLIC3R_VERSION);
|
||||
if (version.find("alpha") != std::string::npos)
|
||||
{
|
||||
version_suffix = "-alpha";
|
||||
name_suffix = " - alpha";
|
||||
}else if (version.find("beta") != std::string::npos)
|
||||
{
|
||||
version_suffix = "-beta";
|
||||
name_suffix = " - beta";
|
||||
}
|
||||
|
||||
// theme path to icon destination
|
||||
std::string icon_theme_path;
|
||||
std::string icon_theme_dirs;
|
||||
|
||||
if (platform_flavor() == PlatformFlavor::LinuxOnChromium) {
|
||||
icon_theme_path = "hicolor/96x96/apps/";
|
||||
icon_theme_dirs = "/hicolor/96x96/apps";
|
||||
}
|
||||
|
||||
std::string target_dir_icons;
|
||||
std::string target_dir_desktop;
|
||||
|
||||
// slicer icon
|
||||
// iterate thru target_candidates to find icons folder
|
||||
for (size_t i = 0; i < target_candidates.size(); ++i) {
|
||||
// Copy icon BambuStudio.png from resources_dir()/icons to target_dir_icons/icons/
|
||||
if (contains_path_dir(target_candidates[i], "images")) {
|
||||
target_dir_icons = target_candidates[i];
|
||||
std::string icon_path = GUI::format("%1%/images/BambuStudio.png",resources_dir());
|
||||
std::string dest_path = GUI::format("%1%/images/%2%BambuStudio%3%.png", target_dir_icons, icon_theme_path, version_suffix);
|
||||
if (copy_icon(icon_path, dest_path))
|
||||
break; // success
|
||||
else
|
||||
target_dir_icons.clear(); // copying failed
|
||||
// if all failed - try creating default home folder
|
||||
if (i == target_candidates.size() - 1) {
|
||||
// create $HOME/.local/share
|
||||
create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/icons" + icon_theme_dirs);
|
||||
// copy icon
|
||||
target_dir_icons = GUI::format("%1%/.local/share",wxFileName::GetHomeDir());
|
||||
std::string icon_path = GUI::format("%1%/images/BambuStudio.png",resources_dir());
|
||||
std::string dest_path = GUI::format("%1%/images/%2%BambuStudio%3%.png", target_dir_icons, icon_theme_path, version_suffix);
|
||||
if (!contains_path_dir(target_dir_icons, "images")
|
||||
|| !copy_icon(icon_path, dest_path)) {
|
||||
// every attempt failed - icon wont be present
|
||||
target_dir_icons.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(target_dir_icons.empty()) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Copying BambuStudio icon to icons directory failed.";
|
||||
} else
|
||||
// save path to icon
|
||||
app_config->set("desktop_integration_icon_slicer_path", GUI::format("%1%/images/%2%BambuStudio%3%.png", target_dir_icons, icon_theme_path, version_suffix));
|
||||
|
||||
// desktop file
|
||||
// iterate thru target_candidates to find applications folder
|
||||
for (size_t i = 0; i < target_candidates.size(); ++i)
|
||||
{
|
||||
if (contains_path_dir(target_candidates[i], "applications")) {
|
||||
target_dir_desktop = target_candidates[i];
|
||||
// Write slicer desktop file
|
||||
std::string desktop_file = GUI::format(
|
||||
"[Desktop Entry]\n"
|
||||
"Name=BambuStudio%1%\n"
|
||||
"GenericName=3D Printing Software\n"
|
||||
"Icon=BambuStudio%2%\n"
|
||||
"Exec=\"%3%\" %%F\n"
|
||||
"Terminal=false\n"
|
||||
"Type=Application\n"
|
||||
"MimeType=model/stl;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;application/x-amf;\n"
|
||||
"Categories=Graphics;3DGraphics;Engineering;\n"
|
||||
"Keywords=3D;Printing;Slicer;slice;3D;printer;convert;gcode;stl;obj;amf;SLA\n"
|
||||
"StartupNotify=false\n"
|
||||
"StartupWMClass=bambu-studio\n", name_suffix, version_suffix, excutable_path);
|
||||
|
||||
std::string path = GUI::format("%1%/applications/BambuStudio%2%.desktop", target_dir_desktop, version_suffix);
|
||||
if (create_desktop_file(path, desktop_file)){
|
||||
BOOST_LOG_TRIVIAL(debug) << "BambuStudio.desktop file installation success.";
|
||||
break;
|
||||
} else {
|
||||
// write failed - try another path
|
||||
BOOST_LOG_TRIVIAL(debug) << "Attempt to BambuStudio.desktop file installation failed. failed path: " << target_candidates[i];
|
||||
target_dir_desktop.clear();
|
||||
}
|
||||
// if all failed - try creating default home folder
|
||||
if (i == target_candidates.size() - 1) {
|
||||
// create $HOME/.local/share
|
||||
create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/applications");
|
||||
// create desktop file
|
||||
target_dir_desktop = GUI::format("%1%/.local/share",wxFileName::GetHomeDir());
|
||||
std::string path = GUI::format("%1%/applications/BambuStudio%2%.desktop", target_dir_desktop, version_suffix);
|
||||
if (contains_path_dir(target_dir_desktop, "applications")) {
|
||||
if (!create_desktop_file(path, desktop_file)) {
|
||||
// Desktop file not written - end desktop integration
|
||||
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create desktop file";
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Desktop file not written - end desktop integration
|
||||
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed because the application directory was not found.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(target_dir_desktop.empty()) {
|
||||
// Desktop file not written - end desktop integration
|
||||
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed because the application directory was not found.";
|
||||
show_error(nullptr, _L("Performing desktop integration failed because the application directory was not found."));
|
||||
return;
|
||||
}
|
||||
// save path to desktop file
|
||||
app_config->set("desktop_integration_app_path", GUI::format("%1%/applications/BambuStudio%2%.desktop", target_dir_desktop, version_suffix));
|
||||
|
||||
// Repeat for Gcode viewer - use same paths as for slicer files
|
||||
// Do NOT add gcode viewer desktop file on ChromeOS
|
||||
if (platform_flavor() != PlatformFlavor::LinuxOnChromium) {
|
||||
// Icon
|
||||
if (!target_dir_icons.empty())
|
||||
{
|
||||
std::string icon_path = GUI::format("%1%/images/BambuStudio-gcodeviewer_192px.png",resources_dir());
|
||||
std::string dest_path = GUI::format("%1%/images/%2%BambuStudio-gcodeviewer%3%.png", target_dir_icons, icon_theme_path, version_suffix);
|
||||
if (copy_icon(icon_path, dest_path))
|
||||
// save path to icon
|
||||
app_config->set("desktop_integration_icon_viewer_path", dest_path);
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(error) << "Copying Gcode Viewer icon to icons directory failed.";
|
||||
}
|
||||
|
||||
// Desktop file
|
||||
std::string desktop_file = GUI::format(
|
||||
"[Desktop Entry]\n"
|
||||
"Name=Bambu Gcode Viewer%1%\n"
|
||||
"GenericName=3D Printing Software\n"
|
||||
"Icon=BambuStudio-gcodeviewer%2%\n"
|
||||
"Exec=\"%3%\" --gcodeviewer %%F\n"
|
||||
"Terminal=false\n"
|
||||
"Type=Application\n"
|
||||
"MimeType=text/x.gcode;\n"
|
||||
"Categories=Graphics;3DGraphics;\n"
|
||||
"Keywords=3D;Printing;Slicer;\n"
|
||||
"StartupNotify=false\n", name_suffix, version_suffix, excutable_path);
|
||||
|
||||
std::string desktop_path = GUI::format("%1%/applications/BambuStudioGcodeViewer%2%.desktop", target_dir_desktop, version_suffix);
|
||||
if (create_desktop_file(desktop_path, desktop_file))
|
||||
// save path to desktop file
|
||||
app_config->set("desktop_integration_app_viewer_path", desktop_path);
|
||||
else {
|
||||
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create Gcodeviewer desktop file";
|
||||
show_error(nullptr, _L("Performing desktop integration failed - could not create Gcodeviewer desktop file. BambuStudio desktop file was probably created successfully."));
|
||||
}
|
||||
}
|
||||
|
||||
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationSuccess);
|
||||
}
|
||||
void DesktopIntegrationDialog::undo_desktop_intgration()
|
||||
{
|
||||
const AppConfig *app_config = wxGetApp().app_config;
|
||||
// slicer .desktop
|
||||
std::string path = std::string(app_config->get("desktop_integration_app_path"));
|
||||
if (!path.empty()) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
|
||||
std::remove(path.c_str());
|
||||
}
|
||||
// slicer icon
|
||||
path = std::string(app_config->get("desktop_integration_icon_slicer_path"));
|
||||
if (!path.empty()) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
|
||||
std::remove(path.c_str());
|
||||
}
|
||||
// No gcode viewer at ChromeOS
|
||||
if (platform_flavor() != PlatformFlavor::LinuxOnChromium) {
|
||||
// gcode viewer .desktop
|
||||
path = std::string(app_config->get("desktop_integration_app_viewer_path"));
|
||||
if (!path.empty()) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
|
||||
std::remove(path.c_str());
|
||||
}
|
||||
// gcode viewer icon
|
||||
path = std::string(app_config->get("desktop_integration_icon_viewer_path"));
|
||||
if (!path.empty()) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
|
||||
std::remove(path.c_str());
|
||||
}
|
||||
}
|
||||
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::UndoDesktopIntegrationSuccess);
|
||||
}
|
||||
|
||||
DesktopIntegrationDialog::DesktopIntegrationDialog(wxWindow *parent)
|
||||
: wxDialog(parent, wxID_ANY, _(L("Desktop Integration")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
|
||||
{
|
||||
bool can_undo = DesktopIntegrationDialog::is_integrated();
|
||||
|
||||
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
|
||||
wxString text = _L("Desktop Integration sets this binary to be searchable by the system.\n\nPress \"Perform\" to proceed.");
|
||||
if (can_undo)
|
||||
text += "\nPress \"Undo\" to remove previous integration.";
|
||||
|
||||
vbox->Add(
|
||||
new wxStaticText( this, wxID_ANY, text),
|
||||
// , wxDefaultPosition, wxSize(100,50), wxTE_MULTILINE),
|
||||
1, // make vertically stretchable
|
||||
wxEXPAND | // make horizontally stretchable
|
||||
wxALL, // and make border all around
|
||||
10 ); // set border width to 10
|
||||
|
||||
|
||||
wxBoxSizer *btn_szr = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxButton *btn_perform = new wxButton(this, wxID_ANY, _L("Perform"));
|
||||
btn_szr->Add(btn_perform, 0, wxALL, 10);
|
||||
|
||||
btn_perform->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { DesktopIntegrationDialog::perform_desktop_integration(); EndModal(wxID_ANY); });
|
||||
|
||||
if (can_undo){
|
||||
wxButton *btn_undo = new wxButton(this, wxID_ANY, _L("Undo"));
|
||||
btn_szr->Add(btn_undo, 0, wxALL, 10);
|
||||
btn_undo->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { DesktopIntegrationDialog::undo_desktop_intgration(); EndModal(wxID_ANY); });
|
||||
}
|
||||
wxButton *btn_cancel = new wxButton(this, wxID_ANY, _L("Cancel"));
|
||||
btn_szr->Add(btn_cancel, 0, wxALL, 10);
|
||||
btn_cancel->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { EndModal(wxID_ANY); });
|
||||
|
||||
vbox->Add(btn_szr, 0, wxALIGN_CENTER);
|
||||
|
||||
SetSizerAndFit(vbox);
|
||||
}
|
||||
|
||||
DesktopIntegrationDialog::~DesktopIntegrationDialog()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
#endif // __linux__
|
||||
39
src/slic3r/GUI/DesktopIntegrationDialog.hpp
Normal file
39
src/slic3r/GUI/DesktopIntegrationDialog.hpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#ifdef __linux__
|
||||
#ifndef slic3r_DesktopIntegrationDialog_hpp_
|
||||
#define slic3r_DesktopIntegrationDialog_hpp_
|
||||
|
||||
#include <wx/dialog.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
class DesktopIntegrationDialog : public wxDialog
|
||||
{
|
||||
public:
|
||||
DesktopIntegrationDialog(wxWindow *parent);
|
||||
DesktopIntegrationDialog(DesktopIntegrationDialog &&) = delete;
|
||||
DesktopIntegrationDialog(const DesktopIntegrationDialog &) = delete;
|
||||
DesktopIntegrationDialog &operator=(DesktopIntegrationDialog &&) = delete;
|
||||
DesktopIntegrationDialog &operator=(const DesktopIntegrationDialog &) = delete;
|
||||
~DesktopIntegrationDialog();
|
||||
|
||||
// methods that actually do / undo desktop integration. Static to be accesible from anywhere.
|
||||
|
||||
// returns true if path to BambuStudio.desktop is stored in App Config and existence of desktop file.
|
||||
// Does not check if desktop file leads to this binary or existence of icons and viewer desktop file.
|
||||
static bool is_integrated();
|
||||
// true if appimage
|
||||
static bool integration_possible();
|
||||
// Creates Desktop files and icons for both PrusaSlicer and GcodeViewer.
|
||||
// Stores paths into App Config.
|
||||
// Rewrites if files already existed.
|
||||
static void perform_desktop_integration();
|
||||
// Deletes Desktop files and icons for both PrusaSlicer and GcodeViewer at paths stored in App Config.
|
||||
static void undo_desktop_intgration();
|
||||
private:
|
||||
|
||||
};
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_DesktopIntegrationDialog_hpp_
|
||||
#endif // __linux__
|
||||
2669
src/slic3r/GUI/DeviceManager.cpp
Normal file
2669
src/slic3r/GUI/DeviceManager.cpp
Normal file
File diff suppressed because it is too large
Load diff
597
src/slic3r/GUI/DeviceManager.hpp
Normal file
597
src/slic3r/GUI/DeviceManager.hpp
Normal file
|
|
@ -0,0 +1,597 @@
|
|||
#ifndef slic3r_DeviceManager_hpp_
|
||||
#define slic3r_DeviceManager_hpp_
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
#include <boost/thread.hpp>
|
||||
#include "libslic3r/ProjectTask.hpp"
|
||||
#include "slic3r/Utils/json_diff.hpp"
|
||||
#include "slic3r/Utils/NetworkAgent.hpp"
|
||||
|
||||
#define USE_LOCAL_SOCKET_BIND 0
|
||||
|
||||
#define DISCONNECT_TIMEOUT 10000.f // milliseconds
|
||||
#define PUSHINFO_TIMEOUT 15000.f // milliseconds
|
||||
#define REQUEST_PUSH_MIN_TIME 3000.f // milliseconds
|
||||
|
||||
#define FILAMENT_MAX_TEMP 300
|
||||
#define FILAMENT_DEF_TEMP 220
|
||||
#define FILAMENT_MIN_TEMP 120
|
||||
|
||||
inline int correct_filament_temperature(int filament_temp)
|
||||
{
|
||||
int temp = std::min(filament_temp, FILAMENT_MAX_TEMP);
|
||||
temp = std::max(temp, FILAMENT_MIN_TEMP);
|
||||
return temp;
|
||||
}
|
||||
|
||||
wxString get_stage_string(int stage);
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
enum PRINTER_TYPE {
|
||||
PRINTER_3DPrinter_UKNOWN,
|
||||
PRINTER_3DPrinter_NONE,
|
||||
PRINTER_3DPrinter_X1_Carbon, // BL-P001
|
||||
PRINTER_3DPrinter_X1, // BL-P002
|
||||
PRINTER_3DPrinter_P1, // BL-P003
|
||||
};
|
||||
|
||||
enum PRINTING_STAGE {
|
||||
PRINTING_STAGE_PRINTING = 0,
|
||||
PRINTING_STAGE_BED_LEVELING,
|
||||
PRINTING_STAGE_HEADBED,
|
||||
PRINTING_STAGE_XY_MECH_MODE,
|
||||
PRINTING_STAGE_CHANGE_MATERIAL,
|
||||
PRINTING_STAGE_M400_PAUSE,
|
||||
PRINTING_STAGE_FILAMENT_RUNOUT_PAUSE,
|
||||
PRINTING_STAGE_HOTEND_HEATING,
|
||||
PRINTING_STAGE_EXTRUDER_SCAN,
|
||||
PRINTING_STAGE_BED_SCAN,
|
||||
PRINTING_STAGE_FIRST_LAYER_SCAN,
|
||||
PRINTING_STAGE_SURFACE_TYPE_IDENT,
|
||||
PRINTING_STAGE_SCANNER_PARAM_CALI,
|
||||
PRINTING_STAGE_TOOHEAD_HOMING,
|
||||
PRINTING_STAGE_NOZZLE_TIP_CLEANING,
|
||||
PRINTING_STAGE_COUNT
|
||||
};
|
||||
|
||||
|
||||
enum PrintingSpeedLevel {
|
||||
SPEED_LEVEL_INVALID = 0,
|
||||
SPEED_LEVEL_SILENCE = 1,
|
||||
SPEED_LEVEL_NORMAL = 2,
|
||||
SPEED_LEVEL_RAPID = 3,
|
||||
SPEED_LEVEL_RAMPAGE = 4,
|
||||
SPEED_LEVEL_COUNT
|
||||
};
|
||||
|
||||
class NetworkAgent;
|
||||
|
||||
|
||||
enum AmsRfidState {
|
||||
AMS_RFID_INIT,
|
||||
AMS_RFID_LOADING,
|
||||
AMS_REID_DONE,
|
||||
};
|
||||
|
||||
enum AmsStep {
|
||||
AMS_STEP_INIT,
|
||||
AMS_STEP_HEAT_EXTRUDER,
|
||||
AMS_STEP_LOADING,
|
||||
AMS_STEP_COMPLETED,
|
||||
};
|
||||
|
||||
enum AmsRoadPosition {
|
||||
AMS_ROAD_POSITION_TRAY, // filament at tray
|
||||
AMS_ROAD_POSITION_TUBE, // filament at tube
|
||||
AMS_ROAD_POSITION_HOTEND, // filament at hotend
|
||||
};
|
||||
|
||||
enum AmsStatusMain {
|
||||
AMS_STATUS_MAIN_IDLE = 0x00,
|
||||
AMS_STATUS_MAIN_FILAMENT_CHANGE = 0x01,
|
||||
AMS_STATUS_MAIN_RFID_IDENTIFYING = 0x02,
|
||||
AMS_STATUS_MAIN_ASSIST = 0x03,
|
||||
AMS_STATUS_MAIN_CALIBRATION = 0x04,
|
||||
AMS_STATUS_MAIN_SELF_CHECK = 0x10,
|
||||
AMS_STATUS_MAIN_DEBUG = 0x20,
|
||||
AMS_STATUS_MAIN_UNKNOWN = 0xFF,
|
||||
};
|
||||
|
||||
class AmsTray {
|
||||
public:
|
||||
AmsTray(std::string tray_id) {
|
||||
is_bbl = false;
|
||||
id = tray_id;
|
||||
road_position = AMS_ROAD_POSITION_TRAY;
|
||||
step_state = AMS_STEP_INIT;
|
||||
rfid_state = AMS_RFID_INIT;
|
||||
}
|
||||
|
||||
static int hex_digit_to_int(const char c)
|
||||
{
|
||||
return (c >= '0' && c <= '9') ? int(c - '0') : (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1;
|
||||
}
|
||||
|
||||
static wxColour decode_color(const std::string &color)
|
||||
{
|
||||
std::array<int, 3> ret = {0, 0, 0};
|
||||
const char * c = color.data();
|
||||
if (color.size() == 8) {
|
||||
for (size_t j = 0; j < 3; ++j) {
|
||||
int digit1 = hex_digit_to_int(*c++);
|
||||
int digit2 = hex_digit_to_int(*c++);
|
||||
if (digit1 == -1 || digit2 == -1) break;
|
||||
ret[j] = float(digit1 * 16 + digit2);
|
||||
}
|
||||
}
|
||||
return wxColour(ret[0], ret[1], ret[2]);
|
||||
}
|
||||
|
||||
std::string id;
|
||||
std::string tag_uid; // tag_uid
|
||||
std::string setting_id; // tray_info_idx
|
||||
std::string type;
|
||||
std::string sub_brands;
|
||||
std::string color;
|
||||
std::string weight;
|
||||
std::string diameter;
|
||||
std::string temp;
|
||||
std::string time;
|
||||
std::string bed_temp_type;
|
||||
std::string bed_temp;
|
||||
std::string nozzle_temp_max;
|
||||
std::string nozzle_temp_min;
|
||||
std::string xcam_info;
|
||||
std::string uuid;
|
||||
|
||||
wxColour wx_color;
|
||||
bool is_bbl;
|
||||
bool is_exists = false;
|
||||
|
||||
AmsRoadPosition road_position;
|
||||
AmsStep step_state;
|
||||
AmsRfidState rfid_state;
|
||||
|
||||
void update_color_from_str(std::string color);
|
||||
wxColour get_color();
|
||||
|
||||
bool is_tray_info_ready();
|
||||
};
|
||||
|
||||
|
||||
class Ams {
|
||||
public:
|
||||
Ams(std::string ams_id) {
|
||||
id = ams_id;
|
||||
}
|
||||
std::string id;
|
||||
bool startup_read_opt{true};
|
||||
bool tray_read_opt{false};
|
||||
bool is_exists{false};
|
||||
std::map<std::string, AmsTray*> trayList;
|
||||
};
|
||||
|
||||
enum PrinterFirmwareType {
|
||||
FIRMWARE_TYPE_ENGINEER = 0,
|
||||
FIRMWARE_TYPE_PRODUCTION,
|
||||
FIRMEARE_TYPE_UKNOWN,
|
||||
};
|
||||
|
||||
|
||||
class FirmwareInfo
|
||||
{
|
||||
public:
|
||||
std::string module_type; // ota or ams
|
||||
std::string version;
|
||||
std::string url;
|
||||
std::string name;
|
||||
std::string description;
|
||||
};
|
||||
|
||||
enum ModuleID {
|
||||
MODULE_UKNOWN = 0x00,
|
||||
MODULE_01 = 0x01,
|
||||
MODULE_02 = 0x02,
|
||||
MODULE_MC = 0x03,
|
||||
MODULE_04 = 0x04,
|
||||
MODULE_MAINBOARD = 0x05,
|
||||
MODULE_06 = 0x06,
|
||||
MODULE_AMS = 0x07,
|
||||
MODULE_TH = 0x08,
|
||||
MODULE_09 = 0x09,
|
||||
MODULE_10 = 0x0A,
|
||||
MODULE_11 = 0x0B,
|
||||
MODULE_XCAM = 0x0C,
|
||||
MODULE_13 = 0x0D,
|
||||
MODULE_14 = 0x0E,
|
||||
MODULE_15 = 0x0F,
|
||||
MODULE_MAX = 0x10
|
||||
};
|
||||
|
||||
enum HMSMessageLevel {
|
||||
HMS_UNKNOWN = 0,
|
||||
HMS_FATAL = 1,
|
||||
HMS_SERIOUS = 2,
|
||||
HMS_COMMON = 3,
|
||||
HMS_INFO = 4,
|
||||
HMS_MSG_LEVEL_MAX,
|
||||
};
|
||||
|
||||
class HMSItem
|
||||
{
|
||||
public:
|
||||
ModuleID module_id;
|
||||
unsigned module_num;
|
||||
unsigned part_id;
|
||||
unsigned reserved;
|
||||
HMSMessageLevel msg_level = HMS_UNKNOWN;
|
||||
int msg_code = 0;
|
||||
bool parse_hms_info(unsigned attr, unsigned code);
|
||||
static wxString get_module_name(ModuleID module_id);
|
||||
static wxString get_hms_msg_level_str(HMSMessageLevel level);
|
||||
};
|
||||
|
||||
|
||||
#define UpgradeNoError 0
|
||||
#define UpgradeDownloadFailed -1
|
||||
#define UpgradeVerfifyFailed -2
|
||||
#define UpgradeFlashFailed -3
|
||||
#define UpgradePrinting -4
|
||||
|
||||
|
||||
|
||||
class MachineObject
|
||||
{
|
||||
private:
|
||||
NetworkAgent* m_agent { nullptr };
|
||||
|
||||
bool check_valid_ip();
|
||||
public:
|
||||
|
||||
enum LIGHT_EFFECT {
|
||||
LIGHT_EFFECT_ON,
|
||||
LIGHT_EFFECT_OFF,
|
||||
LIGHT_EFFECT_FLASHING,
|
||||
LIGHT_EFFECT_UNKOWN,
|
||||
};
|
||||
|
||||
enum FanType {
|
||||
COOLING_FAN = 1,
|
||||
BIG_COOLING_FAN = 2,
|
||||
CHAMBER_FAN = 3,
|
||||
};
|
||||
|
||||
enum UpgradingDisplayState {
|
||||
UpgradingUnavaliable = 0,
|
||||
UpgradingAvaliable = 1,
|
||||
UpgradingInProgress = 2,
|
||||
UpgradingFinished = 3
|
||||
};
|
||||
|
||||
class ModuleVersionInfo
|
||||
{
|
||||
public:
|
||||
std::string name;
|
||||
std::string sn;
|
||||
std::string hw_ver;
|
||||
std::string sw_ver;
|
||||
};
|
||||
|
||||
/* static members and functions */
|
||||
static inline int m_sequence_id = 20000;
|
||||
static PRINTER_TYPE parse_printer_type(std::string type_str);
|
||||
static PRINTER_TYPE parse_iot_printer_type(std::string type_str);
|
||||
static PRINTER_TYPE parse_preset_printer_type(std::string type_str);
|
||||
static std::string get_preset_printer_model_name(PRINTER_TYPE printer_type);
|
||||
static bool is_bbl_filament(std::string tag_uid);
|
||||
|
||||
typedef std::function<void()> UploadedFn;
|
||||
typedef std::function<void(int progress)> UploadProgressFn;
|
||||
typedef std::function<void(std::string error)> ErrorFn;
|
||||
typedef std::function<void(int result, std::string info)> ResultFn;
|
||||
|
||||
/* properties */
|
||||
std::string dev_name;
|
||||
std::string dev_ip;
|
||||
std::string dev_id;
|
||||
std::string access_code;
|
||||
std::string dev_connection_type; /* lan | cloud */
|
||||
std::string connection_type() { return dev_connection_type; }
|
||||
bool has_access_right() { return !access_code.empty(); }
|
||||
void set_access_code(std::string code);
|
||||
bool is_lan_mode_printer();
|
||||
PRINTER_TYPE printer_type = PRINTER_3DPrinter_UKNOWN;
|
||||
std::string get_printer_type_string();
|
||||
wxString get_printer_type_display_str();
|
||||
|
||||
std::string product_name; // set by iot service, get /user/print
|
||||
|
||||
std::string bind_user_name;
|
||||
std::string bind_user_id;
|
||||
std::string bind_state; /* free | occupied */
|
||||
bool is_avaliable() { return bind_state == "free"; }
|
||||
time_t last_alive;
|
||||
bool m_is_online;
|
||||
int parse_msg_count = 0;
|
||||
std::chrono::system_clock::time_point last_update_time; /* last received print data from machine */
|
||||
std::chrono::system_clock::time_point last_push_time; /* last received print push from machine */
|
||||
std::chrono::system_clock::time_point last_request_push; /* last received print push from machine */
|
||||
|
||||
/* ams properties */
|
||||
std::map<std::string, Ams*> amsList; // key: ams[id], start with 0
|
||||
long ams_exist_bits = 0;
|
||||
long tray_exist_bits = 0;
|
||||
long tray_is_bbl_bits = 0;
|
||||
long tray_read_done_bits = 0;
|
||||
AmsStatusMain ams_status_main;
|
||||
int ams_status_sub;
|
||||
int ams_version = 0;
|
||||
std::string m_ams_id; // local ams : "0" ~ "3"
|
||||
std::string m_tray_id; // local tray id : "0" ~ "3"
|
||||
std::string m_tray_now; // tray_now : "0" ~ "15" or "255"
|
||||
std::string m_tray_tar; // tray_tar : "0" ~ "15" or "255"
|
||||
void _parse_tray_now(std::string tray_now);
|
||||
bool is_filament_move() { return atoi(m_tray_now.c_str()) == 255 ? false : true; };
|
||||
bool is_ams_need_update;
|
||||
|
||||
inline bool is_ams_unload() { return m_tray_tar.compare("255") == 0; }
|
||||
Ams* get_curr_Ams();
|
||||
AmsTray* get_curr_tray();
|
||||
AmsTray *get_ams_tray(std::string ams_id, std::string tray_id);
|
||||
// parse amsStatusMain and ams_status_sub
|
||||
void _parse_ams_status(int ams_status);
|
||||
bool has_ams() { return ams_exist_bits != 0; }
|
||||
bool is_need_upgrade_for_ams();
|
||||
bool is_only_support_cloud_print();
|
||||
|
||||
|
||||
int ams_filament_mapping(std::vector<FilamentInfo> filaments, std::vector<FilamentInfo> &result, std::vector<int> exclude_id = std::vector<int>());
|
||||
bool is_valid_mapping_result(std::vector<FilamentInfo>& result);
|
||||
void reset_mapping_result(std::vector<FilamentInfo>& result);
|
||||
|
||||
|
||||
/* temperature */
|
||||
float nozzle_temp;
|
||||
float nozzle_temp_target;
|
||||
float bed_temp;
|
||||
float bed_temp_target;
|
||||
float chamber_temp;
|
||||
float frame_temp;
|
||||
|
||||
/* cooling */
|
||||
int heatbreak_fan_speed = 0;
|
||||
int cooling_fan_speed = 0;
|
||||
int big_fan1_speed = 0;
|
||||
int big_fan2_speed = 0;
|
||||
|
||||
/* signals */
|
||||
std::string wifi_signal;
|
||||
std::string link_th;
|
||||
std::string link_ams;
|
||||
|
||||
/* lights */
|
||||
LIGHT_EFFECT chamber_light;
|
||||
LIGHT_EFFECT work_light;
|
||||
std::string light_effect_str(LIGHT_EFFECT effect);
|
||||
LIGHT_EFFECT light_effect_parse(std::string effect_str);
|
||||
|
||||
/* upgrade */
|
||||
bool upgrade_force_upgrade { false };
|
||||
bool upgrade_new_version { false };
|
||||
bool upgrade_consistency_request;
|
||||
int upgrade_display_state = 0; // 0 : upgrade unavailable, 1: upgrade idle, 2: upgrading, 3: upgrade_finished
|
||||
PrinterFirmwareType firmware_type; // engineer|production
|
||||
std::string upgrade_progress;
|
||||
std::string upgrade_message;
|
||||
std::string upgrade_status;
|
||||
std::string upgrade_module;
|
||||
std::string ams_new_version_number;
|
||||
std::string ota_new_version_number;
|
||||
std::string ahb_new_version_number;
|
||||
std::map<std::string, ModuleVersionInfo> module_vers;
|
||||
int upgrade_err_code = 0;
|
||||
std::vector<FirmwareInfo> firmware_list;
|
||||
|
||||
std::string get_firmware_type_str();
|
||||
bool is_in_upgrading();
|
||||
bool is_upgrading_avalable();
|
||||
int get_upgrade_percent();
|
||||
std::string get_ota_version();
|
||||
wxString get_upgrade_result_str(int upgrade_err_code);
|
||||
// key: ams_id start as 0,1,2,3
|
||||
std::map<int, ModuleVersionInfo> get_ams_version();
|
||||
|
||||
/* printing */
|
||||
std::string print_type;
|
||||
int mc_print_stage;
|
||||
int mc_print_sub_stage;
|
||||
int mc_print_error_code;
|
||||
int mc_print_line_number;
|
||||
int mc_print_percent; /* left print progess in percent */
|
||||
int mc_left_time; /* left time in seconds */
|
||||
bool is_system_printing();
|
||||
|
||||
std::vector<int> stage_list_info;
|
||||
int stage_curr = 0;
|
||||
int m_push_count = 0;
|
||||
|
||||
wxString get_curr_stage();
|
||||
// return curr stage index of stage list
|
||||
int get_curr_stage_idx();
|
||||
bool is_in_calibration();
|
||||
|
||||
/* printing status */
|
||||
std::string print_status; /* enum string: FINISH, RUNNING, PAUSE, INIT, FAILED */
|
||||
std::string iot_print_status; /* iot */
|
||||
PrintingSpeedLevel printing_speed_lvl;
|
||||
int printing_speed_mag = 100;
|
||||
PrintingSpeedLevel _parse_printing_speed_lvl(int lvl);
|
||||
|
||||
/* camera */
|
||||
bool camera_recording { false };
|
||||
bool camera_timelapse { false };
|
||||
bool camera_has_sdcard { false };
|
||||
|
||||
/* HMS */
|
||||
std::vector<HMSItem> hms_list;
|
||||
|
||||
/* machine mqtt apis */
|
||||
int connect(bool is_anonymous = false);
|
||||
int disconnect();
|
||||
|
||||
json_diff print_json;
|
||||
|
||||
/* Project Task and Sub Task */
|
||||
std::string project_id_;
|
||||
std::string profile_id_;
|
||||
std::string task_id_;
|
||||
std::string subtask_id_;
|
||||
BBLSliceInfo* slice_info {nullptr};
|
||||
int plate_index { -1 };
|
||||
std::string m_gcode_file;
|
||||
BBLSubTask* subtask_;
|
||||
std::string obj_subtask_id; // subtask_id == 0 for sdcard
|
||||
std::string subtask_name;
|
||||
bool is_sdcard_printing();
|
||||
bool has_sdcard();
|
||||
|
||||
|
||||
MachineObject(NetworkAgent* agent, std::string name, std::string id, std::string ip);
|
||||
~MachineObject();
|
||||
/* command commands */
|
||||
int command_get_version();
|
||||
int command_request_push_all();
|
||||
|
||||
/* command upgrade */
|
||||
int command_upgrade_confirm();
|
||||
int command_upgrade_firmware(FirmwareInfo info);
|
||||
|
||||
/* control apis */
|
||||
int command_xyz_abs();
|
||||
int command_auto_leveling();
|
||||
int command_go_home();
|
||||
int command_control_fan(FanType fan_type, bool on_off);
|
||||
int command_task_abort();
|
||||
int command_task_pause();
|
||||
int command_task_resume();
|
||||
int command_set_bed(int temp);
|
||||
int command_set_nozzle(int temp);
|
||||
// ams controls
|
||||
int command_ams_switch(int tray_index, int old_temp = 210, int new_temp = 210);
|
||||
int command_ams_change_filament(int tray_id, int old_temp = 210, int new_temp = 210);
|
||||
int command_ams_user_settings(int ams_id, bool start_read_opt, bool tray_read_opt);
|
||||
int command_ams_calibrate(int ams_id);
|
||||
int command_ams_filament_settings(int ams_id, int tray_id, std::string setting_id, std::string tray_color, std::string tray_type, int nozzle_temp_min, int nozzle_temp_max);
|
||||
int command_ams_select_tray(std::string tray_id);
|
||||
int command_ams_refresh_rfid(std::string tray_id);
|
||||
int command_set_chamber_light(LIGHT_EFFECT effect, int on_time = 500, int off_time = 500, int loops = 1, int interval = 1000);
|
||||
int command_set_work_light(LIGHT_EFFECT effect, int on_time = 500, int off_time = 500, int loops = 1, int interval = 1000);
|
||||
|
||||
// set printing speed
|
||||
int command_set_printing_speed(PrintingSpeedLevel lvl);
|
||||
|
||||
// axis string is X, Y, Z, E
|
||||
int command_axis_control(std::string axis, double unit = 1.0f, double value = 1.0f, int speed = 3000);
|
||||
|
||||
// calibration printer
|
||||
int command_start_calibration();
|
||||
|
||||
int command_unload_filament();
|
||||
|
||||
// camera control
|
||||
int command_ipcam_record(bool on_off);
|
||||
int command_ipcam_timelapse(bool on_off);
|
||||
|
||||
/* common apis */
|
||||
inline bool is_local() { return !dev_ip.empty(); }
|
||||
void set_bind_status(std::string status);
|
||||
std::string get_bind_str();
|
||||
bool can_print();
|
||||
bool can_resume();
|
||||
bool can_pause();
|
||||
bool can_abort();
|
||||
bool is_in_printing();
|
||||
bool is_printing_finished();
|
||||
void reset_update_time();
|
||||
void reset();
|
||||
static bool is_in_printing_status(std::string status);
|
||||
|
||||
void set_print_state(std::string status);
|
||||
|
||||
bool is_connected();
|
||||
void set_online_state(bool on_off);
|
||||
bool is_online() { return m_is_online; }
|
||||
bool is_info_ready();
|
||||
|
||||
|
||||
/* Msg for display MsgFn */
|
||||
typedef std::function<void(std::string topic, std::string payload)> MsgFn;
|
||||
int publish_json(std::string json_str, int qos = 0);
|
||||
int cloud_publish_json(std::string json_str, int qos = 0);
|
||||
int local_publish_json(std::string json_str, int qos = 0);
|
||||
int parse_json(std::string payload);
|
||||
int publish_gcode(std::string gcode_str);
|
||||
|
||||
BBLSubTask* get_subtask();
|
||||
void update_slice_info(std::string project_id, std::string profile_id, std::string subtask_id, int plate_idx);
|
||||
|
||||
bool m_firmware_valid { false };
|
||||
void get_firmware_info();
|
||||
bool is_firmware_info_valid();
|
||||
};
|
||||
|
||||
class DeviceManager
|
||||
{
|
||||
private:
|
||||
NetworkAgent* m_agent { nullptr };
|
||||
|
||||
public:
|
||||
DeviceManager(NetworkAgent* agent = nullptr);
|
||||
~DeviceManager();
|
||||
|
||||
std::mutex listMutex;
|
||||
std::string selected_machine; /* dev_id */
|
||||
std::string local_selected_machine; /* dev_id */
|
||||
std::map<std::string, MachineObject*> localMachineList; /* dev_id -> MachineObject*, localMachine SSDP */
|
||||
std::map<std::string, MachineObject*> userMachineList; /* dev_id -> MachineObject* cloudMachine of User */
|
||||
|
||||
MachineObject* get_default_machine();
|
||||
MachineObject* get_local_selected_machine();
|
||||
MachineObject* get_local_machine(std::string dev_id);
|
||||
MachineObject* get_user_machine(std::string dev_id);
|
||||
MachineObject* get_my_machine(std::string dev_id);
|
||||
void clean_user_info();
|
||||
|
||||
bool set_selected_machine(std::string dev_id);
|
||||
MachineObject* get_selected_machine();
|
||||
|
||||
/* return machine has access code and user machine if login*/
|
||||
std::map<std::string, MachineObject*> get_my_machine_list();
|
||||
std::string get_first_online_user_machine();
|
||||
void modify_device_name(std::string dev_id, std::string dev_name);
|
||||
void update_user_machine_list_info();
|
||||
void parse_user_print_info(std::string body);
|
||||
|
||||
/* create machine or update machine properties */
|
||||
void on_machine_alive(std::string json_str);
|
||||
|
||||
/* disconnect all machine connections */
|
||||
void disconnect_all();
|
||||
int query_bind_status(std::string &msg);
|
||||
|
||||
// get alive machine
|
||||
std::map<std::string, MachineObject*> get_local_machine_list();
|
||||
void load_last_machine();
|
||||
|
||||
void check_alive();
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_DeviceManager_hpp_
|
||||
92
src/slic3r/GUI/Event.hpp
Normal file
92
src/slic3r/GUI/Event.hpp
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
#ifndef slic3r_Events_hpp_
|
||||
#define slic3r_Events_hpp_
|
||||
|
||||
#include <array>
|
||||
#include <wx/event.h>
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace GUI {
|
||||
|
||||
|
||||
struct SimpleEvent : public wxEvent
|
||||
{
|
||||
SimpleEvent(wxEventType type, wxObject* origin = nullptr) : wxEvent(0, type)
|
||||
{
|
||||
m_propagationLevel = wxEVENT_PROPAGATE_MAX;
|
||||
SetEventObject(origin);
|
||||
}
|
||||
|
||||
virtual wxEvent* Clone() const
|
||||
{
|
||||
return new SimpleEvent(GetEventType(), GetEventObject());
|
||||
}
|
||||
};
|
||||
|
||||
struct IntEvent : public wxEvent
|
||||
{
|
||||
public:
|
||||
IntEvent(wxEventType type, int data, wxObject* origin = nullptr) : wxEvent(0, type)
|
||||
{
|
||||
m_propagationLevel = wxEVENT_PROPAGATE_MAX;
|
||||
SetEventObject(origin);
|
||||
m_data = data;
|
||||
}
|
||||
|
||||
virtual wxEvent* Clone() const
|
||||
{
|
||||
return new IntEvent(GetEventType(), m_data, GetEventObject());
|
||||
}
|
||||
int get_data() { return m_data; }
|
||||
|
||||
private:
|
||||
int m_data;
|
||||
|
||||
};
|
||||
|
||||
template<class T, size_t N> struct ArrayEvent : public wxEvent
|
||||
{
|
||||
std::array<T, N> data;
|
||||
|
||||
ArrayEvent(wxEventType type, std::array<T, N> data, wxObject* origin = nullptr)
|
||||
: wxEvent(0, type), data(std::move(data))
|
||||
{
|
||||
m_propagationLevel = wxEVENT_PROPAGATE_MAX;
|
||||
SetEventObject(origin);
|
||||
}
|
||||
|
||||
virtual wxEvent* Clone() const
|
||||
{
|
||||
return new ArrayEvent<T, N>(GetEventType(), data, GetEventObject());
|
||||
}
|
||||
};
|
||||
|
||||
template<class T> struct Event : public wxEvent
|
||||
{
|
||||
T data;
|
||||
|
||||
Event(wxEventType type, const T &data, wxObject* origin = nullptr)
|
||||
: wxEvent(0, type), data(std::move(data))
|
||||
{
|
||||
m_propagationLevel = wxEVENT_PROPAGATE_MAX;
|
||||
SetEventObject(origin);
|
||||
}
|
||||
|
||||
Event(wxEventType type, T&& data, wxObject* origin = nullptr)
|
||||
: wxEvent(0, type), data(std::move(data))
|
||||
{
|
||||
m_propagationLevel = wxEVENT_PROPAGATE_MAX;
|
||||
SetEventObject(origin);
|
||||
}
|
||||
|
||||
virtual wxEvent* Clone() const
|
||||
{
|
||||
return new Event<T>(GetEventType(), data, GetEventObject());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // slic3r_Events_hpp_
|
||||
395
src/slic3r/GUI/ExtraRenderers.cpp
Normal file
395
src/slic3r/GUI/ExtraRenderers.cpp
Normal file
|
|
@ -0,0 +1,395 @@
|
|||
#include "ExtraRenderers.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "BitmapComboBox.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "Widgets/ComboBox.hpp"
|
||||
|
||||
#include <wx/dc.h>
|
||||
#ifdef wxHAS_GENERIC_DATAVIEWCTRL
|
||||
#include "wx/generic/private/markuptext.h"
|
||||
#include "wx/generic/private/rowheightcache.h"
|
||||
#include "wx/generic/private/widthcalc.h"
|
||||
#endif
|
||||
/*
|
||||
#ifdef __WXGTK__
|
||||
#include "wx/gtk/private.h"
|
||||
#include "wx/gtk/private/value.h"
|
||||
#endif
|
||||
*/
|
||||
#if wxUSE_ACCESSIBILITY
|
||||
#include "wx/private/markupparser.h"
|
||||
#endif // wxUSE_ACCESSIBILITY
|
||||
|
||||
using Slic3r::GUI::from_u8;
|
||||
using Slic3r::GUI::into_u8;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// DataViewBitmapText
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
wxIMPLEMENT_DYNAMIC_CLASS(DataViewBitmapText, wxObject)
|
||||
|
||||
IMPLEMENT_VARIANT_OBJECT(DataViewBitmapText)
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// BitmapTextRenderer
|
||||
// ---------------------------------------------------------
|
||||
|
||||
#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
|
||||
BitmapTextRenderer::BitmapTextRenderer(wxDataViewCellMode mode /*= wxDATAVIEW_CELL_EDITABLE*/,
|
||||
int align /*= wxDVR_DEFAULT_ALIGNMENT*/):
|
||||
wxDataViewRenderer(wxT("PrusaDataViewBitmapText"), mode, align)
|
||||
{
|
||||
SetMode(mode);
|
||||
SetAlignment(align);
|
||||
}
|
||||
#endif // ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
|
||||
|
||||
BitmapTextRenderer::~BitmapTextRenderer()
|
||||
{
|
||||
#ifdef SUPPORTS_MARKUP
|
||||
#ifdef wxHAS_GENERIC_DATAVIEWCTRL
|
||||
delete m_markupText;
|
||||
#endif //wxHAS_GENERIC_DATAVIEWCTRL
|
||||
#endif // SUPPORTS_MARKUP
|
||||
}
|
||||
|
||||
void BitmapTextRenderer::EnableMarkup(bool enable)
|
||||
{
|
||||
#ifdef SUPPORTS_MARKUP
|
||||
#ifdef wxHAS_GENERIC_DATAVIEWCTRL
|
||||
if (enable) {
|
||||
if (!m_markupText)
|
||||
m_markupText = new wxItemMarkupText(wxString());
|
||||
}
|
||||
else {
|
||||
if (m_markupText) {
|
||||
delete m_markupText;
|
||||
m_markupText = nullptr;
|
||||
}
|
||||
}
|
||||
#else
|
||||
is_markupText = enable;
|
||||
#endif //wxHAS_GENERIC_DATAVIEWCTRL
|
||||
#endif // SUPPORTS_MARKUP
|
||||
}
|
||||
|
||||
bool BitmapTextRenderer::SetValue(const wxVariant &value)
|
||||
{
|
||||
m_value << value;
|
||||
|
||||
#ifdef SUPPORTS_MARKUP
|
||||
#ifdef wxHAS_GENERIC_DATAVIEWCTRL
|
||||
if (m_markupText)
|
||||
m_markupText->SetMarkup(m_value.GetText());
|
||||
/*
|
||||
#else
|
||||
#if defined(__WXGTK__)
|
||||
GValue gvalue = G_VALUE_INIT;
|
||||
g_value_init(&gvalue, G_TYPE_STRING);
|
||||
g_value_set_string(&gvalue, wxGTK_CONV_FONT(str.GetText(), GetOwner()->GetOwner()->GetFont()));
|
||||
g_object_set_property(G_OBJECT(m_renderer/ *.GetText()* /), is_markupText ? "markup" : "text", &gvalue);
|
||||
g_value_unset(&gvalue);
|
||||
#endif // __WXGTK__
|
||||
*/
|
||||
#endif // wxHAS_GENERIC_DATAVIEWCTRL
|
||||
#endif // SUPPORTS_MARKUP
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitmapTextRenderer::GetValue(wxVariant& WXUNUSED(value)) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY
|
||||
wxString BitmapTextRenderer::GetAccessibleDescription() const
|
||||
{
|
||||
#ifdef SUPPORTS_MARKUP
|
||||
if (m_markupText)
|
||||
return wxMarkupParser::Strip(m_text);
|
||||
#endif // SUPPORTS_MARKUP
|
||||
|
||||
return m_value.GetText();
|
||||
}
|
||||
#endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
|
||||
|
||||
bool BitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state)
|
||||
{
|
||||
int xoffset = 0;
|
||||
|
||||
const wxBitmap& icon = m_value.GetBitmap();
|
||||
if (icon.IsOk())
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
wxSize icon_sz = icon.GetScaledSize();
|
||||
#else
|
||||
wxSize icon_sz = icon.GetSize();
|
||||
#endif
|
||||
dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon_sz.y) / 2);
|
||||
xoffset = icon_sz.x + 4;
|
||||
}
|
||||
|
||||
#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL)
|
||||
if (m_markupText)
|
||||
{
|
||||
rect.x += xoffset;
|
||||
m_markupText->Render(GetView(), *dc, rect, 0, GetEllipsizeMode());
|
||||
}
|
||||
else
|
||||
#endif // SUPPORTS_MARKUP && wxHAS_GENERIC_DATAVIEWCTRL
|
||||
#ifdef _WIN32
|
||||
// workaround for Windows DarkMode : Don't respect to the state & wxDATAVIEW_CELL_SELECTED to avoid update of the text color
|
||||
RenderText(m_value.GetText(), xoffset, rect, dc, state & wxDATAVIEW_CELL_SELECTED ? 0 :state);
|
||||
#else
|
||||
RenderText(m_value.GetText(), xoffset, rect, dc, state);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
wxSize BitmapTextRenderer::GetSize() const
|
||||
{
|
||||
if (!m_value.GetText().empty())
|
||||
{
|
||||
wxSize size;
|
||||
#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL)
|
||||
if (m_markupText)
|
||||
{
|
||||
wxDataViewCtrl* const view = GetView();
|
||||
wxClientDC dc(view);
|
||||
if (GetAttr().HasFont())
|
||||
dc.SetFont(GetAttr().GetEffectiveFont(view->GetFont()));
|
||||
|
||||
size = m_markupText->Measure(dc);
|
||||
|
||||
int lines = m_value.GetText().Freq('\n') + 1;
|
||||
size.SetHeight(size.GetHeight() * lines);
|
||||
}
|
||||
else
|
||||
#endif // SUPPORTS_MARKUP && wxHAS_GENERIC_DATAVIEWCTRL
|
||||
size = GetTextExtent(m_value.GetText());
|
||||
|
||||
if (m_value.GetBitmap().IsOk())
|
||||
size.x += m_value.GetBitmap().GetWidth() + 4;
|
||||
return size;
|
||||
}
|
||||
return wxSize(80, 20);
|
||||
}
|
||||
|
||||
|
||||
wxWindow* BitmapTextRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value)
|
||||
{
|
||||
if (can_create_editor_ctrl && !can_create_editor_ctrl())
|
||||
return nullptr;
|
||||
|
||||
DataViewBitmapText data;
|
||||
data << value;
|
||||
|
||||
m_was_unusable_symbol = false;
|
||||
|
||||
wxPoint position = labelRect.GetPosition();
|
||||
if (data.GetBitmap().IsOk()) {
|
||||
const int bmp_width = data.GetBitmap().GetWidth();
|
||||
position.x += bmp_width;
|
||||
labelRect.SetWidth(labelRect.GetWidth() - bmp_width);
|
||||
}
|
||||
|
||||
#ifdef __WXMSW__
|
||||
// Case when from some reason we try to create next EditorCtrl till old one was not deleted
|
||||
if (auto children = parent->GetChildren(); children.GetCount() > 0)
|
||||
for (auto child : children)
|
||||
if (dynamic_cast<wxTextCtrl*>(child)) {
|
||||
parent->RemoveChild(child);
|
||||
child->Destroy();
|
||||
break;
|
||||
}
|
||||
#endif // __WXMSW__
|
||||
|
||||
wxTextCtrl* text_editor = new wxTextCtrl(parent, wxID_ANY, data.GetText(),
|
||||
position, labelRect.GetSize(), wxTE_PROCESS_ENTER);
|
||||
text_editor->SetInsertionPointEnd();
|
||||
text_editor->SelectAll();
|
||||
|
||||
return text_editor;
|
||||
}
|
||||
|
||||
bool BitmapTextRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value)
|
||||
{
|
||||
wxTextCtrl* text_editor = wxDynamicCast(ctrl, wxTextCtrl);
|
||||
if (!text_editor || text_editor->GetValue().IsEmpty())
|
||||
return false;
|
||||
|
||||
m_was_unusable_symbol = Slic3r::GUI::Plater::has_illegal_filename_characters(text_editor->GetValue());
|
||||
if (m_was_unusable_symbol)
|
||||
return false;
|
||||
|
||||
// The icon can't be edited so get its old value and reuse it.
|
||||
wxVariant valueOld;
|
||||
GetView()->GetModel()->GetValue(valueOld, m_item, /*colName*/0);
|
||||
|
||||
DataViewBitmapText bmpText;
|
||||
bmpText << valueOld;
|
||||
|
||||
// But replace the text with the value entered by user.
|
||||
bmpText.SetText(text_editor->GetValue());
|
||||
|
||||
value << bmpText;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// BitmapChoiceRenderer
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
bool BitmapChoiceRenderer::SetValue(const wxVariant& value)
|
||||
{
|
||||
m_value << value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitmapChoiceRenderer::GetValue(wxVariant& value) const
|
||||
{
|
||||
value << m_value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitmapChoiceRenderer::Render(wxRect rect, wxDC* dc, int state)
|
||||
{
|
||||
// int xoffset = 0;
|
||||
|
||||
const wxBitmap& icon = m_value.GetBitmap();
|
||||
if (icon.IsOk())
|
||||
{
|
||||
dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon.GetHeight()) / 2);
|
||||
// xoffset = icon.GetWidth() + 4;
|
||||
|
||||
if (rect.height == 0)
|
||||
rect.height = icon.GetHeight();
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// workaround for Windows DarkMode : Don't respect to the state & wxDATAVIEW_CELL_SELECTED to avoid update of the text color
|
||||
// RenderText(m_value.GetText(), xoffset, rect, dc, state & wxDATAVIEW_CELL_SELECTED ? 0 : state);
|
||||
#else
|
||||
// RenderText(m_value.GetText(), xoffset, rect, dc, state);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
wxSize BitmapChoiceRenderer::GetSize() const
|
||||
{
|
||||
wxSize sz;// = GetTextExtent(m_value.GetText());
|
||||
|
||||
if (m_value.GetBitmap().IsOk()) {
|
||||
sz.x += m_value.GetBitmap().GetWidth() + 4;
|
||||
sz.y = m_value.GetBitmap().GetHeight() + 4;
|
||||
}
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
|
||||
wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value)
|
||||
{
|
||||
if (can_create_editor_ctrl && !can_create_editor_ctrl())
|
||||
return nullptr;
|
||||
|
||||
std::vector<wxBitmap*> icons = get_extruder_color_icons();
|
||||
if (icons.empty())
|
||||
return nullptr;
|
||||
|
||||
DataViewBitmapText data;
|
||||
data << value;
|
||||
|
||||
::ComboBox *c_editor = new ::ComboBox(parent, wxID_ANY, wxEmptyString,
|
||||
labelRect.GetTopLeft(), wxSize(labelRect.GetWidth(), -1),
|
||||
0, nullptr, wxCB_READONLY | CB_NO_DROP_ICON | CB_NO_TEXT);
|
||||
c_editor->GetDropDown().SetUseContentWidth(true);
|
||||
// BBS
|
||||
for (size_t i = 0; i < icons.size(); i++)
|
||||
c_editor->Append(wxString::Format("%d", i+1), *icons[i]);
|
||||
|
||||
c_editor->SetSelection(atoi(data.GetText().c_str()) - 1);
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
c_editor->Bind(wxEVT_COMBOBOX, [this, c_editor](wxCommandEvent& evt) {
|
||||
// to avoid event propagation to other sidebar items
|
||||
evt.StopPropagation();
|
||||
// FinishEditing grabs new selection and triggers config update. We better call
|
||||
// it explicitly, automatic update on KILL_FOCUS didn't work on Linux.
|
||||
c_editor->SetClientData(this);
|
||||
this->FinishEditing();
|
||||
});
|
||||
c_editor->Bind(wxEVT_COMBOBOX_DROPDOWN, [this, c_editor](wxCommandEvent& evt) {
|
||||
c_editor->SetClientData(this);
|
||||
this->FinishEditing();
|
||||
});
|
||||
c_editor->Bind(wxEVT_KILL_FOCUS, [this, c_editor](wxFocusEvent& evt) {
|
||||
if (!c_editor->GetDropDown().IsShown() && c_editor->GetClientData() == nullptr) { // TODO: Fix called twice
|
||||
c_editor->SetClientData(this);
|
||||
this->FinishEditing();
|
||||
}
|
||||
}, c_editor->GetId());
|
||||
#else
|
||||
// to avoid event propagation to other sidebar items
|
||||
c_editor->Bind(wxEVT_COMBOBOX, [](wxCommandEvent& evt) { evt.StopPropagation(); });
|
||||
#endif
|
||||
|
||||
return c_editor;
|
||||
}
|
||||
|
||||
bool BitmapChoiceRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value)
|
||||
{
|
||||
::ComboBox*c = static_cast<::ComboBox *>(ctrl);
|
||||
int selection = c->GetSelection();
|
||||
if (selection < 0)
|
||||
return false;
|
||||
|
||||
DataViewBitmapText bmpText;
|
||||
|
||||
bmpText.SetText(c->GetString(selection));
|
||||
bmpText.SetBitmap(c->GetItemBitmap(selection));
|
||||
|
||||
value << bmpText;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// TextRenderer
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
bool TextRenderer::SetValue(const wxVariant& value)
|
||||
{
|
||||
m_value = value.GetString();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextRenderer::GetValue(wxVariant& value) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TextRenderer::Render(wxRect rect, wxDC* dc, int state)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// workaround for Windows DarkMode : Don't respect to the state & wxDATAVIEW_CELL_SELECTED to avoid update of the text color
|
||||
RenderText(m_value, 0, rect, dc, state & wxDATAVIEW_CELL_SELECTED ? 0 : state);
|
||||
#else
|
||||
RenderText(m_value, 0, rect, dc, state);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
wxSize TextRenderer::GetSize() const
|
||||
{
|
||||
return GetTextExtent(m_value);
|
||||
}
|
||||
|
||||
|
||||
189
src/slic3r/GUI/ExtraRenderers.hpp
Normal file
189
src/slic3r/GUI/ExtraRenderers.hpp
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
#ifndef slic3r_GUI_ExtraRenderers_hpp_
|
||||
#define slic3r_GUI_ExtraRenderers_hpp_
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <wx/dataview.h>
|
||||
|
||||
#if wxUSE_MARKUP && wxCHECK_VERSION(3, 1, 1)
|
||||
#define SUPPORTS_MARKUP
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DataViewBitmapText: helper class used by BitmapTextRenderer
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class DataViewBitmapText : public wxObject
|
||||
{
|
||||
public:
|
||||
DataViewBitmapText( const wxString &text = wxEmptyString,
|
||||
const wxBitmap& bmp = wxNullBitmap) :
|
||||
m_text(text),
|
||||
m_bmp(bmp)
|
||||
{ }
|
||||
|
||||
DataViewBitmapText(const DataViewBitmapText &other)
|
||||
: wxObject(),
|
||||
m_text(other.m_text),
|
||||
m_bmp(other.m_bmp)
|
||||
{ }
|
||||
|
||||
void SetText(const wxString &text) { m_text = text; }
|
||||
wxString GetText() const { return m_text; }
|
||||
void SetBitmap(const wxBitmap &bmp) { m_bmp = bmp; }
|
||||
const wxBitmap &GetBitmap() const { return m_bmp; }
|
||||
|
||||
bool IsSameAs(const DataViewBitmapText& other) const {
|
||||
return m_text == other.m_text && m_bmp.IsSameAs(other.m_bmp);
|
||||
}
|
||||
|
||||
bool operator==(const DataViewBitmapText& other) const {
|
||||
return IsSameAs(other);
|
||||
}
|
||||
|
||||
bool operator!=(const DataViewBitmapText& other) const {
|
||||
return !IsSameAs(other);
|
||||
}
|
||||
|
||||
private:
|
||||
wxString m_text;
|
||||
wxBitmap m_bmp;
|
||||
|
||||
wxDECLARE_DYNAMIC_CLASS(DataViewBitmapText);
|
||||
};
|
||||
DECLARE_VARIANT_OBJECT(DataViewBitmapText)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// BitmapTextRenderer
|
||||
// ----------------------------------------------------------------------------
|
||||
#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
|
||||
class BitmapTextRenderer : public wxDataViewRenderer
|
||||
#else
|
||||
class BitmapTextRenderer : public wxDataViewCustomRenderer
|
||||
#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
|
||||
{
|
||||
public:
|
||||
BitmapTextRenderer(bool use_markup = false,
|
||||
wxDataViewCellMode mode =
|
||||
//#ifdef __WXOSX__
|
||||
// wxDATAVIEW_CELL_INERT
|
||||
//#else
|
||||
wxDATAVIEW_CELL_EDITABLE
|
||||
//#endif
|
||||
|
||||
, int align = wxDVR_DEFAULT_ALIGNMENT
|
||||
#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
|
||||
);
|
||||
#else
|
||||
) :
|
||||
wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align)
|
||||
{
|
||||
EnableMarkup(use_markup);
|
||||
}
|
||||
#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
|
||||
|
||||
~BitmapTextRenderer();
|
||||
|
||||
void EnableMarkup(bool enable = true);
|
||||
|
||||
bool SetValue(const wxVariant& value) override;
|
||||
bool GetValue(wxVariant& value) const override;
|
||||
#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY
|
||||
virtual wxString GetAccessibleDescription() const override;
|
||||
#endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
|
||||
|
||||
virtual bool Render(wxRect cell, wxDC* dc, int state) override;
|
||||
virtual wxSize GetSize() const override;
|
||||
|
||||
bool HasEditorCtrl() const override
|
||||
{
|
||||
//#ifdef __WXOSX__
|
||||
// return false;
|
||||
//#else
|
||||
return true;
|
||||
//#endif
|
||||
}
|
||||
wxWindow* CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) override;
|
||||
bool GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) override;
|
||||
bool WasCanceled() const { return m_was_unusable_symbol; }
|
||||
|
||||
void set_can_create_editor_ctrl_function(std::function<bool()> can_create_fn) { can_create_editor_ctrl = can_create_fn; }
|
||||
|
||||
private:
|
||||
DataViewBitmapText m_value;
|
||||
bool m_was_unusable_symbol{ false };
|
||||
|
||||
std::function<bool()> can_create_editor_ctrl { nullptr };
|
||||
|
||||
#ifdef SUPPORTS_MARKUP
|
||||
#ifdef wxHAS_GENERIC_DATAVIEWCTRL
|
||||
class wxItemMarkupText* m_markupText { nullptr };;
|
||||
#else
|
||||
bool is_markupText {false};
|
||||
#endif
|
||||
#endif // SUPPORTS_MARKUP
|
||||
};
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// BitmapChoiceRenderer
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class BitmapChoiceRenderer : public wxDataViewCustomRenderer
|
||||
{
|
||||
public:
|
||||
BitmapChoiceRenderer(wxDataViewCellMode mode =
|
||||
//#ifdef __WXOSX__
|
||||
// wxDATAVIEW_CELL_INERT
|
||||
//#else
|
||||
wxDATAVIEW_CELL_EDITABLE
|
||||
//#endif
|
||||
, int align = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL
|
||||
) : wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align) {}
|
||||
|
||||
bool SetValue(const wxVariant& value) override;
|
||||
bool GetValue(wxVariant& value) const override;
|
||||
|
||||
virtual bool Render(wxRect cell, wxDC* dc, int state) override;
|
||||
virtual wxSize GetSize() const override;
|
||||
|
||||
bool HasEditorCtrl() const override { return true; }
|
||||
wxWindow* CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) override;
|
||||
bool GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) override;
|
||||
|
||||
void set_can_create_editor_ctrl_function(std::function<bool()> can_create_fn) { can_create_editor_ctrl = can_create_fn; }
|
||||
void set_default_extruder_idx(std::function<int()> default_extruder_idx_fn) { get_default_extruder_idx = default_extruder_idx_fn; }
|
||||
|
||||
private:
|
||||
DataViewBitmapText m_value;
|
||||
std::function<bool()> can_create_editor_ctrl { nullptr };
|
||||
std::function<int()> get_default_extruder_idx{ nullptr };
|
||||
};
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// TextRenderer
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class TextRenderer : public wxDataViewCustomRenderer
|
||||
{
|
||||
public:
|
||||
TextRenderer(wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT
|
||||
, int align = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL
|
||||
) : wxDataViewCustomRenderer(wxT("string"), mode, align) {}
|
||||
|
||||
bool SetValue(const wxVariant& value) override;
|
||||
bool GetValue(wxVariant& value) const override;
|
||||
|
||||
virtual bool Render(wxRect cell, wxDC* dc, int state) override;
|
||||
virtual wxSize GetSize() const override;
|
||||
|
||||
bool HasEditorCtrl() const override { return false; }
|
||||
|
||||
private:
|
||||
wxString m_value;
|
||||
};
|
||||
|
||||
|
||||
#endif // slic3r_GUI_ExtraRenderers_hpp_
|
||||
1748
src/slic3r/GUI/Field.cpp
Normal file
1748
src/slic3r/GUI/Field.cpp
Normal file
File diff suppressed because it is too large
Load diff
500
src/slic3r/GUI/Field.hpp
Normal file
500
src/slic3r/GUI/Field.hpp
Normal file
|
|
@ -0,0 +1,500 @@
|
|||
#ifndef SLIC3R_GUI_FIELD_HPP
|
||||
#define SLIC3R_GUI_FIELD_HPP
|
||||
|
||||
#include <wx/wxprec.h>
|
||||
#ifndef WX_PRECOMP
|
||||
#include <wx/wx.h>
|
||||
#endif
|
||||
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <boost/any.hpp>
|
||||
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/bmpcbox.h>
|
||||
#include <wx/clrpicker.h>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
|
||||
#include "GUI.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "Widgets/SpinInput.hpp"
|
||||
|
||||
#ifdef __WXMSW__
|
||||
#define wxMSW true
|
||||
#else
|
||||
#define wxMSW false
|
||||
#endif
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
class Field;
|
||||
using t_field = std::unique_ptr<Field>;
|
||||
using t_kill_focus = std::function<void(const std::string&)>;
|
||||
using t_change = std::function<void(const t_config_option_key&, const boost::any&)>;
|
||||
using t_back_to_init = std::function<void(const std::string&)>;
|
||||
|
||||
wxString double_to_string(double const value, const int max_precision = 4);
|
||||
wxString get_thumbnails_string(const std::vector<Vec2d>& values);
|
||||
|
||||
class Field {
|
||||
protected:
|
||||
// factory function to defer and enforce creation of derived type.
|
||||
virtual void PostInitialize();
|
||||
|
||||
/// Finish constructing the Field's wxWidget-related properties, including setting its own sizer, etc.
|
||||
virtual void BUILD() = 0;
|
||||
|
||||
/// Call the attached on_kill_focus method.
|
||||
//! It's important to use wxEvent instead of wxFocusEvent,
|
||||
//! in another case we can't unfocused control at all
|
||||
void on_kill_focus();
|
||||
/// Call the attached on_change method.
|
||||
void on_change_field();
|
||||
|
||||
class EnterPressed {
|
||||
public:
|
||||
EnterPressed(Field* field) :
|
||||
m_parent(field){ m_parent->set_enter_pressed(true); }
|
||||
~EnterPressed() { m_parent->set_enter_pressed(false); }
|
||||
private:
|
||||
Field* m_parent;
|
||||
};
|
||||
|
||||
public:
|
||||
/// Call the attached m_back_to_initial_value method.
|
||||
void on_back_to_initial_value();
|
||||
/// Call the attached m_back_to_sys_value method.
|
||||
void on_back_to_sys_value();
|
||||
|
||||
public:
|
||||
/// parent wx item, opportunity to refactor (probably not necessary - data duplication)
|
||||
wxWindow* m_parent {nullptr};
|
||||
|
||||
/// Function object to store callback passed in from owning object.
|
||||
t_kill_focus m_on_kill_focus {nullptr};
|
||||
|
||||
/// Function object to store callback passed in from owning object.
|
||||
t_change m_on_change {nullptr};
|
||||
|
||||
/// Function object to store callback passed in from owning object.
|
||||
t_back_to_init m_back_to_initial_value{ nullptr };
|
||||
t_back_to_init m_back_to_sys_value{ nullptr };
|
||||
|
||||
// This is used to avoid recursive invocation of the field change/update by wxWidgets.
|
||||
bool m_disable_change_event {false};
|
||||
bool m_is_modified_value {false};
|
||||
bool m_is_nonsys_value {true};
|
||||
|
||||
/// Copy of ConfigOption for deduction purposes
|
||||
const ConfigOptionDef m_opt {ConfigOptionDef()};
|
||||
const t_config_option_key m_opt_id;//! {""};
|
||||
int m_opt_idx = 0;
|
||||
|
||||
double opt_height{ 0.0 };
|
||||
bool parent_is_custom_ctrl{ false };
|
||||
|
||||
/// Sets a value for this control.
|
||||
/// subclasses should overload with a specific version
|
||||
/// Postcondition: Method does not fire the on_change event.
|
||||
virtual void set_value(const boost::any& value, bool change_event) = 0;
|
||||
virtual void set_last_meaningful_value() {}
|
||||
virtual void set_na_value() {}
|
||||
|
||||
/// Gets a boost::any representing this control.
|
||||
/// subclasses should overload with a specific version
|
||||
virtual boost::any& get_value() = 0;
|
||||
|
||||
virtual void enable() = 0;
|
||||
virtual void disable() = 0;
|
||||
|
||||
/// Fires the enable or disable function, based on the input.
|
||||
void toggle(bool en);
|
||||
|
||||
virtual wxString get_tooltip_text(const wxString& default_string);
|
||||
|
||||
void field_changed() { on_change_field(); }
|
||||
|
||||
Field(const ConfigOptionDef& opt, const t_config_option_key& id) : m_opt(opt), m_opt_id(id) {}
|
||||
Field(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : m_parent(parent), m_opt(opt), m_opt_id(id) {}
|
||||
virtual ~Field();
|
||||
|
||||
/// If you don't know what you are getting back, check both methods for nullptr.
|
||||
virtual wxSizer* getSizer() { return nullptr; }
|
||||
virtual wxWindow* getWindow() { return nullptr; }
|
||||
|
||||
bool is_matched(const std::string& string, const std::string& pattern);
|
||||
void get_value_by_opt_type(wxString& str, const bool check_value = true);
|
||||
|
||||
/// Factory method for generating new derived classes.
|
||||
template<class T>
|
||||
static t_field Create(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id)// interface for creating shared objects
|
||||
{
|
||||
auto p = Slic3r::make_unique<T>(parent, opt, id);
|
||||
p->PostInitialize();
|
||||
return std::move(p); //!p;
|
||||
}
|
||||
|
||||
bool set_undo_bitmap(const ScalableBitmap *bmp) {
|
||||
if (m_undo_bitmap != bmp) {
|
||||
m_undo_bitmap = bmp;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set_undo_to_sys_bitmap(const ScalableBitmap *bmp) {
|
||||
if (m_undo_to_sys_bitmap != bmp) {
|
||||
m_undo_to_sys_bitmap = bmp;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set_label_colour(const wxColour *clr) {
|
||||
if (m_label_color != clr) {
|
||||
m_label_color = clr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set_undo_tooltip(const wxString *tip) {
|
||||
if (m_undo_tooltip != tip) {
|
||||
m_undo_tooltip = tip;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set_undo_to_sys_tooltip(const wxString *tip) {
|
||||
if (m_undo_to_sys_tooltip != tip) {
|
||||
m_undo_to_sys_tooltip = tip;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool* get_blink_ptr() {
|
||||
return &m_blink;
|
||||
}
|
||||
|
||||
virtual void msw_rescale();
|
||||
virtual void sys_color_changed();
|
||||
|
||||
bool get_enter_pressed() const { return bEnterPressed; }
|
||||
void set_enter_pressed(bool pressed) { bEnterPressed = pressed; }
|
||||
|
||||
// Values of width to alignments of fields
|
||||
static int def_width() ;
|
||||
static int def_width_wider() ;
|
||||
static int def_width_thinner() ;
|
||||
|
||||
const ScalableBitmap* undo_bitmap() { return m_undo_bitmap; }
|
||||
const wxString* undo_tooltip() { return m_undo_tooltip; }
|
||||
const ScalableBitmap* undo_to_sys_bitmap() { return m_undo_to_sys_bitmap; }
|
||||
const wxString* undo_to_sys_tooltip() { return m_undo_to_sys_tooltip; }
|
||||
const wxColour* label_color() { return m_label_color; }
|
||||
const bool blink() { return m_blink; }
|
||||
const bool combine_side_text() { return m_combine_side_text; } // BBS: new param style
|
||||
|
||||
protected:
|
||||
// Bitmap and Tooltip text for m_Undo_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one.
|
||||
const ScalableBitmap* m_undo_bitmap = nullptr;
|
||||
const wxString* m_undo_tooltip = nullptr;
|
||||
// Bitmap and Tooltip text for m_Undo_to_sys_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one.
|
||||
const ScalableBitmap* m_undo_to_sys_bitmap = nullptr;
|
||||
const wxString* m_undo_to_sys_tooltip = nullptr;
|
||||
|
||||
bool m_blink{ false };
|
||||
|
||||
// Color for Label. The wxColour will be updated only if the new wxColour pointer differs from the currently rendered one.
|
||||
const wxColour* m_label_color = nullptr;
|
||||
|
||||
// current value
|
||||
boost::any m_value;
|
||||
// last maeningful value
|
||||
boost::any m_last_meaningful_value;
|
||||
|
||||
int m_em_unit;
|
||||
bool m_combine_side_text = false;
|
||||
|
||||
bool bEnterPressed = false;
|
||||
|
||||
|
||||
friend class OptionsGroup;
|
||||
};
|
||||
|
||||
/// Convenience function, accepts a const reference to t_field and checks to see whether
|
||||
/// or not both wx pointers are null.
|
||||
inline bool is_bad_field(const t_field& obj) { return obj->getSizer() == nullptr && obj->getWindow() == nullptr; }
|
||||
|
||||
/// Covenience function to determine whether this field is a valid window field.
|
||||
inline bool is_window_field(const t_field& obj) { return !is_bad_field(obj) && obj->getWindow() != nullptr && obj->getSizer() == nullptr; }
|
||||
|
||||
/// Covenience function to determine whether this field is a valid sizer field.
|
||||
inline bool is_sizer_field(const t_field& obj) { return !is_bad_field(obj) && obj->getSizer() != nullptr; }
|
||||
|
||||
class TextCtrl : public Field {
|
||||
using Field::Field;
|
||||
#ifdef __WXGTK__
|
||||
bool bChangedValueEvent = true;
|
||||
void change_field_value(wxEvent& event);
|
||||
#endif //__WXGTK__
|
||||
|
||||
public:
|
||||
TextCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
|
||||
TextCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
|
||||
~TextCtrl() {}
|
||||
|
||||
void BUILD() override;
|
||||
bool value_was_changed();
|
||||
// Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER
|
||||
void propagate_value();
|
||||
wxWindow* window {nullptr};
|
||||
|
||||
void set_value(const std::string& value, bool change_event = false) {
|
||||
m_disable_change_event = !change_event;
|
||||
dynamic_cast<wxTextCtrl*>(window)->SetValue(wxString(value));
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
void set_value(const boost::any& value, bool change_event = false) override;
|
||||
void set_last_meaningful_value() override;
|
||||
void set_na_value() override;
|
||||
|
||||
boost::any& get_value() override;
|
||||
|
||||
void msw_rescale() override;
|
||||
|
||||
void enable() override;
|
||||
void disable() override;
|
||||
wxWindow* getWindow() override { return window; }
|
||||
wxTextCtrl * text_ctrl();
|
||||
};
|
||||
|
||||
class CheckBox : public Field {
|
||||
using Field::Field;
|
||||
bool m_is_na_val {false};
|
||||
public:
|
||||
CheckBox(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
|
||||
CheckBox(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
|
||||
~CheckBox() {}
|
||||
|
||||
wxWindow* window{ nullptr };
|
||||
void BUILD() override;
|
||||
|
||||
void set_value(const bool value, bool change_event = false);
|
||||
void set_value(const boost::any& value, bool change_event = false) override;
|
||||
void set_last_meaningful_value() override;
|
||||
void set_na_value() override;
|
||||
boost::any& get_value() override;
|
||||
|
||||
void msw_rescale() override;
|
||||
|
||||
void enable() override { window->Enable(); }
|
||||
void disable() override { window->Disable(); }
|
||||
wxWindow* getWindow() override { return window; }
|
||||
};
|
||||
|
||||
class SpinCtrl : public Field {
|
||||
using Field::Field;
|
||||
private:
|
||||
static const int UNDEF_VALUE = INT_MIN;
|
||||
|
||||
bool suppress_propagation {false};
|
||||
public:
|
||||
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;
|
||||
|
||||
wxWindow* window{ nullptr };
|
||||
void BUILD() override;
|
||||
/// Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER
|
||||
void propagate_value() ;
|
||||
|
||||
void set_value(const std::string& value, bool change_event = false) {
|
||||
m_disable_change_event = !change_event;
|
||||
dynamic_cast<SpinInput*>(window)->SetValue(value);
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
void set_value(const boost::any& value, bool change_event = false) override;
|
||||
|
||||
boost::any& get_value() override {
|
||||
int value = static_cast<SpinInput*>(window)->GetValue();
|
||||
return m_value = value;
|
||||
}
|
||||
|
||||
void msw_rescale() override;
|
||||
|
||||
void enable() override { dynamic_cast<SpinInput*>(window)->Enable(); }
|
||||
void disable() override { dynamic_cast<SpinInput*>(window)->Disable(); }
|
||||
wxWindow* getWindow() override { return window; }
|
||||
};
|
||||
|
||||
class Choice : public Field {
|
||||
using Field::Field;
|
||||
|
||||
public:
|
||||
Choice(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
|
||||
Choice(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
|
||||
~Choice() {}
|
||||
|
||||
wxWindow* window{ nullptr };
|
||||
void BUILD() override;
|
||||
// Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER
|
||||
void propagate_value();
|
||||
|
||||
/* Under OSX: wxBitmapComboBox->GetWindowStyle() returns some weard value,
|
||||
* so let use a flag, which has TRUE value for a control without wxCB_READONLY style
|
||||
*/
|
||||
bool m_is_editable { false };
|
||||
bool m_is_dropped { false };
|
||||
bool m_suppress_scroll { false };
|
||||
int m_last_selected { wxNOT_FOUND };
|
||||
|
||||
void set_selection();
|
||||
void set_value(const std::string& value, bool change_event = false);
|
||||
void set_value(const boost::any& value, bool change_event = false) override;
|
||||
void set_values(const std::vector<std::string> &values);
|
||||
void set_values(const wxArrayString &values);
|
||||
boost::any& get_value() override;
|
||||
|
||||
void msw_rescale() override;
|
||||
|
||||
void enable() override ;//{ dynamic_cast<wxBitmapComboBox*>(window)->Enable(); };
|
||||
void disable() override;//{ dynamic_cast<wxBitmapComboBox*>(window)->Disable(); };
|
||||
wxWindow* getWindow() override { return window; }
|
||||
|
||||
void suppress_scroll();
|
||||
};
|
||||
|
||||
class ColourPicker : public Field {
|
||||
using Field::Field;
|
||||
|
||||
void set_undef_value(wxColourPickerCtrl* field);
|
||||
public:
|
||||
ColourPicker(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
|
||||
ColourPicker(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
|
||||
~ColourPicker() {}
|
||||
|
||||
wxWindow* window{ nullptr };
|
||||
void BUILD() override;
|
||||
|
||||
void set_value(const std::string& value, bool change_event = false) {
|
||||
m_disable_change_event = !change_event;
|
||||
dynamic_cast<wxColourPickerCtrl*>(window)->SetColour(value);
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
void set_value(const boost::any& value, bool change_event = false) override;
|
||||
boost::any& get_value() override;
|
||||
void msw_rescale() override;
|
||||
void sys_color_changed() override;
|
||||
|
||||
void enable() override { dynamic_cast<wxColourPickerCtrl*>(window)->Enable(); }
|
||||
void disable() override{ dynamic_cast<wxColourPickerCtrl*>(window)->Disable(); }
|
||||
wxWindow* getWindow() override { return window; }
|
||||
};
|
||||
|
||||
class PointCtrl : public Field {
|
||||
using Field::Field;
|
||||
public:
|
||||
PointCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
|
||||
PointCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
|
||||
~PointCtrl() {}
|
||||
|
||||
wxSizer* sizer{ nullptr };
|
||||
wxTextCtrl* x_textctrl{ nullptr };
|
||||
wxTextCtrl* y_textctrl{ nullptr };
|
||||
|
||||
void BUILD() override;
|
||||
bool value_was_changed(wxTextCtrl* win);
|
||||
// Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER
|
||||
void propagate_value(wxTextCtrl* win);
|
||||
void set_value(const Vec2d& value, bool change_event = false);
|
||||
void set_value(const boost::any& value, bool change_event = false) override;
|
||||
boost::any& get_value() override;
|
||||
|
||||
void msw_rescale() override;
|
||||
void sys_color_changed() override;
|
||||
|
||||
void enable() override {
|
||||
x_textctrl->Enable();
|
||||
y_textctrl->Enable(); }
|
||||
void disable() override{
|
||||
x_textctrl->Disable();
|
||||
y_textctrl->Disable(); }
|
||||
wxSizer* getSizer() override { return sizer; }
|
||||
wxWindow* getWindow() override { return dynamic_cast<wxWindow*>(x_textctrl); }
|
||||
};
|
||||
|
||||
class StaticText : public Field {
|
||||
using Field::Field;
|
||||
public:
|
||||
StaticText(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
|
||||
StaticText(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
|
||||
~StaticText() {}
|
||||
|
||||
wxWindow* window{ nullptr };
|
||||
void BUILD() override;
|
||||
|
||||
void set_value(const std::string& value, bool change_event = false) {
|
||||
m_disable_change_event = !change_event;
|
||||
dynamic_cast<wxStaticText*>(window)->SetLabel(wxString::FromUTF8(value.data()));
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
void set_value(const boost::any& value, bool change_event = false) override {
|
||||
m_disable_change_event = !change_event;
|
||||
dynamic_cast<wxStaticText*>(window)->SetLabel(boost::any_cast<wxString>(value));
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
|
||||
boost::any& get_value()override { return m_value; }
|
||||
|
||||
void msw_rescale() override;
|
||||
|
||||
void enable() override { dynamic_cast<wxStaticText*>(window)->Enable(); }
|
||||
void disable() override{ dynamic_cast<wxStaticText*>(window)->Disable(); }
|
||||
wxWindow* getWindow() override { return window; }
|
||||
};
|
||||
|
||||
class SliderCtrl : public Field {
|
||||
using Field::Field;
|
||||
public:
|
||||
SliderCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
|
||||
SliderCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
|
||||
~SliderCtrl() {}
|
||||
|
||||
wxSizer* m_sizer{ nullptr };
|
||||
wxTextCtrl* m_textctrl{ nullptr };
|
||||
wxSlider* m_slider{ nullptr };
|
||||
|
||||
int m_scale = 10;
|
||||
|
||||
void BUILD() override;
|
||||
|
||||
void set_value(const int value, bool change_event = false);
|
||||
void set_value(const boost::any& value, bool change_event = false) override;
|
||||
boost::any& get_value() override;
|
||||
|
||||
void enable() override {
|
||||
m_slider->Enable();
|
||||
m_textctrl->Enable();
|
||||
m_textctrl->SetEditable(true);
|
||||
}
|
||||
void disable() override{
|
||||
m_slider->Disable();
|
||||
m_textctrl->Disable();
|
||||
m_textctrl->SetEditable(false);
|
||||
}
|
||||
wxSizer* getSizer() override { return m_sizer; }
|
||||
wxWindow* getWindow() override { return dynamic_cast<wxWindow*>(m_slider); }
|
||||
};
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
|
||||
#endif /* SLIC3R_GUI_FIELD_HPP */
|
||||
5211
src/slic3r/GUI/GCodeViewer.cpp
Normal file
5211
src/slic3r/GUI/GCodeViewer.cpp
Normal file
File diff suppressed because it is too large
Load diff
889
src/slic3r/GUI/GCodeViewer.hpp
Normal file
889
src/slic3r/GUI/GCodeViewer.hpp
Normal file
|
|
@ -0,0 +1,889 @@
|
|||
#ifndef slic3r_GCodeViewer_hpp_
|
||||
#define slic3r_GCodeViewer_hpp_
|
||||
|
||||
#include "3DScene.hpp"
|
||||
#include "libslic3r/GCode/GCodeProcessor.hpp"
|
||||
#include "libslic3r/GCode/ThumbnailData.hpp"
|
||||
#include "IMSlider.hpp"
|
||||
#include "GLModel.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include <boost/iostreams/device/mapped_file.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <float.h>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Print;
|
||||
class TriangleMesh;
|
||||
class PresetBundle;
|
||||
|
||||
namespace GUI {
|
||||
|
||||
class PartPlateList;
|
||||
class OpenGLManager;
|
||||
|
||||
class GCodeViewer
|
||||
{
|
||||
using IBufferType = unsigned short;
|
||||
using Color = std::array<float, 4>;
|
||||
using VertexBuffer = std::vector<float>;
|
||||
using MultiVertexBuffer = std::vector<VertexBuffer>;
|
||||
using IndexBuffer = std::vector<IBufferType>;
|
||||
using MultiIndexBuffer = std::vector<IndexBuffer>;
|
||||
using InstanceBuffer = std::vector<float>;
|
||||
using InstanceIdBuffer = std::vector<size_t>;
|
||||
using InstancesOffsets = std::vector<Vec3f>;
|
||||
|
||||
static const std::vector<Color> Extrusion_Role_Colors;
|
||||
static const std::vector<Color> Options_Colors;
|
||||
static const std::vector<Color> Travel_Colors;
|
||||
static const std::vector<Color> Range_Colors;
|
||||
static const Color Wipe_Color;
|
||||
static const Color Neutral_Color;
|
||||
|
||||
enum class EOptionsColors : unsigned char
|
||||
{
|
||||
Retractions,
|
||||
Unretractions,
|
||||
Seams,
|
||||
ToolChanges,
|
||||
ColorChanges,
|
||||
PausePrints,
|
||||
CustomGCodes
|
||||
};
|
||||
|
||||
// vbo buffer containing vertices data used to render a specific toolpath type
|
||||
struct VBuffer
|
||||
{
|
||||
enum class EFormat : unsigned char
|
||||
{
|
||||
// vertex format: 3 floats -> position.x|position.y|position.z
|
||||
Position,
|
||||
// vertex format: 4 floats -> position.x|position.y|position.z|normal.x
|
||||
PositionNormal1,
|
||||
// vertex format: 6 floats -> position.x|position.y|position.z|normal.x|normal.y|normal.z
|
||||
PositionNormal3
|
||||
};
|
||||
|
||||
EFormat format{ EFormat::Position };
|
||||
// vbos id
|
||||
std::vector<unsigned int> vbos;
|
||||
// sizes of the buffers, in bytes, used in export to obj
|
||||
std::vector<size_t> sizes;
|
||||
// count of vertices, updated after data are sent to gpu
|
||||
size_t count{ 0 };
|
||||
|
||||
size_t data_size_bytes() const { return count * vertex_size_bytes(); }
|
||||
// We set 65536 as max count of vertices inside a vertex buffer to allow
|
||||
// to use unsigned short in place of unsigned int for indices in the index buffer, to save memory
|
||||
size_t max_size_bytes() const { return 65536 * vertex_size_bytes(); }
|
||||
|
||||
size_t vertex_size_floats() const { return position_size_floats() + normal_size_floats(); }
|
||||
size_t vertex_size_bytes() const { return vertex_size_floats() * sizeof(float); }
|
||||
|
||||
size_t position_offset_floats() const { return 0; }
|
||||
size_t position_offset_bytes() const { return position_offset_floats() * sizeof(float); }
|
||||
|
||||
size_t position_size_floats() const { return 3; }
|
||||
size_t position_size_bytes() const { return position_size_floats() * sizeof(float); }
|
||||
|
||||
size_t normal_offset_floats() const {
|
||||
assert(format == EFormat::PositionNormal1 || format == EFormat::PositionNormal3);
|
||||
return position_size_floats();
|
||||
}
|
||||
size_t normal_offset_bytes() const { return normal_offset_floats() * sizeof(float); }
|
||||
|
||||
size_t normal_size_floats() const {
|
||||
switch (format)
|
||||
{
|
||||
case EFormat::PositionNormal1: { return 1; }
|
||||
case EFormat::PositionNormal3: { return 3; }
|
||||
default: { return 0; }
|
||||
}
|
||||
}
|
||||
size_t normal_size_bytes() const { return normal_size_floats() * sizeof(float); }
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
// buffer containing instances data used to render a toolpaths using instanced or batched models
|
||||
// instance record format:
|
||||
// instanced models: 5 floats -> position.x|position.y|position.z|width|height (which are sent to the shader as -> vec3 (offset) + vec2 (scales) in GLModel::render_instanced())
|
||||
// batched models: 3 floats -> position.x|position.y|position.z
|
||||
struct InstanceVBuffer
|
||||
{
|
||||
// ranges used to render only subparts of the intances
|
||||
struct Ranges
|
||||
{
|
||||
struct Range
|
||||
{
|
||||
// offset in bytes of the 1st instance to render
|
||||
unsigned int offset;
|
||||
// count of instances to render
|
||||
unsigned int count;
|
||||
// vbo id
|
||||
unsigned int vbo{ 0 };
|
||||
// Color to apply to the instances
|
||||
Color color;
|
||||
};
|
||||
|
||||
std::vector<Range> ranges;
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
enum class EFormat : unsigned char
|
||||
{
|
||||
InstancedModel,
|
||||
BatchedModel
|
||||
};
|
||||
|
||||
EFormat format;
|
||||
|
||||
// cpu-side buffer containing all instances data
|
||||
InstanceBuffer buffer;
|
||||
// indices of the moves for all instances
|
||||
std::vector<size_t> s_ids;
|
||||
// position offsets, used to show the correct value of the tool position
|
||||
InstancesOffsets offsets;
|
||||
Ranges render_ranges;
|
||||
|
||||
size_t data_size_bytes() const { return s_ids.size() * instance_size_bytes(); }
|
||||
|
||||
size_t instance_size_floats() const {
|
||||
switch (format)
|
||||
{
|
||||
case EFormat::InstancedModel: { return 5; }
|
||||
case EFormat::BatchedModel: { return 3; }
|
||||
default: { return 0; }
|
||||
}
|
||||
}
|
||||
size_t instance_size_bytes() const { return instance_size_floats() * sizeof(float); }
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
// ibo buffer containing indices data (for lines/triangles) used to render a specific toolpath type
|
||||
struct IBuffer
|
||||
{
|
||||
// id of the associated vertex buffer
|
||||
unsigned int vbo{ 0 };
|
||||
// ibo id
|
||||
unsigned int ibo{ 0 };
|
||||
// count of indices, updated after data are sent to gpu
|
||||
size_t count{ 0 };
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
// Used to identify different toolpath sub-types inside a IBuffer
|
||||
struct Path
|
||||
{
|
||||
struct Endpoint
|
||||
{
|
||||
// index of the buffer in the multibuffer vector
|
||||
// the buffer type may change:
|
||||
// it is the vertex buffer while extracting vertices data,
|
||||
// the index buffer while extracting indices data
|
||||
unsigned int b_id{ 0 };
|
||||
// index into the buffer
|
||||
size_t i_id{ 0 };
|
||||
// move id
|
||||
size_t s_id{ 0 };
|
||||
Vec3f position{ Vec3f::Zero() };
|
||||
};
|
||||
|
||||
struct Sub_Path
|
||||
{
|
||||
Endpoint first;
|
||||
Endpoint last;
|
||||
|
||||
bool contains(size_t s_id) const {
|
||||
return first.s_id <= s_id && s_id <= last.s_id;
|
||||
}
|
||||
};
|
||||
|
||||
EMoveType type{ EMoveType::Noop };
|
||||
ExtrusionRole role{ erNone };
|
||||
float delta_extruder{ 0.0f };
|
||||
float height{ 0.0f };
|
||||
float width{ 0.0f };
|
||||
float feedrate{ 0.0f };
|
||||
float fan_speed{ 0.0f };
|
||||
float temperature{ 0.0f };
|
||||
float volumetric_rate{ 0.0f };
|
||||
unsigned char extruder_id{ 0 };
|
||||
unsigned char cp_color_id{ 0 };
|
||||
std::vector<Sub_Path> sub_paths;
|
||||
|
||||
bool matches(const GCodeProcessorResult::MoveVertex& move) const;
|
||||
size_t vertices_count() const {
|
||||
return sub_paths.empty() ? 0 : sub_paths.back().last.s_id - sub_paths.front().first.s_id + 1;
|
||||
}
|
||||
bool contains(size_t s_id) const {
|
||||
return sub_paths.empty() ? false : sub_paths.front().first.s_id <= s_id && s_id <= sub_paths.back().last.s_id;
|
||||
}
|
||||
int get_id_of_sub_path_containing(size_t s_id) const {
|
||||
if (sub_paths.empty())
|
||||
return -1;
|
||||
else {
|
||||
for (int i = 0; i < static_cast<int>(sub_paths.size()); ++i) {
|
||||
if (sub_paths[i].contains(s_id))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
void add_sub_path(const GCodeProcessorResult::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id) {
|
||||
Endpoint endpoint = { b_id, i_id, s_id, move.position };
|
||||
sub_paths.push_back({ endpoint , endpoint });
|
||||
}
|
||||
};
|
||||
|
||||
// Used to batch the indices needed to render the paths
|
||||
struct RenderPath
|
||||
{
|
||||
// Index of the parent tbuffer
|
||||
unsigned char tbuffer_id;
|
||||
// Render path property
|
||||
Color color;
|
||||
// Index of the buffer in TBuffer::indices
|
||||
unsigned int ibuffer_id;
|
||||
// Render path content
|
||||
// Index of the path in TBuffer::paths
|
||||
unsigned int path_id;
|
||||
std::vector<unsigned int> sizes;
|
||||
std::vector<size_t> offsets; // use size_t because we need an unsigned integer whose size matches pointer's size (used in the call glMultiDrawElements())
|
||||
bool contains(size_t offset) const {
|
||||
for (size_t i = 0; i < offsets.size(); ++i) {
|
||||
if (offsets[i] <= offset && offset <= offsets[i] + static_cast<size_t>(sizes[i] * sizeof(IBufferType)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
struct RenderPathPropertyLower {
|
||||
bool operator() (const RenderPath &l, const RenderPath &r) const {
|
||||
if (l.tbuffer_id < r.tbuffer_id)
|
||||
return true;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (l.color[i] < r.color[i])
|
||||
return true;
|
||||
else if (l.color[i] > r.color[i])
|
||||
return false;
|
||||
}
|
||||
return l.ibuffer_id < r.ibuffer_id;
|
||||
}
|
||||
};
|
||||
struct RenderPathPropertyEqual {
|
||||
bool operator() (const RenderPath &l, const RenderPath &r) const {
|
||||
return l.tbuffer_id == r.tbuffer_id && l.ibuffer_id == r.ibuffer_id && l.color == r.color;
|
||||
}
|
||||
};
|
||||
|
||||
// buffer containing data for rendering a specific toolpath type
|
||||
struct TBuffer
|
||||
{
|
||||
enum class ERenderPrimitiveType : unsigned char
|
||||
{
|
||||
Point,
|
||||
Line,
|
||||
Triangle,
|
||||
InstancedModel,
|
||||
BatchedModel
|
||||
};
|
||||
|
||||
ERenderPrimitiveType render_primitive_type;
|
||||
|
||||
// buffers for point, line and triangle primitive types
|
||||
VBuffer vertices;
|
||||
std::vector<IBuffer> indices;
|
||||
|
||||
struct Model
|
||||
{
|
||||
GLModel model;
|
||||
Color color;
|
||||
InstanceVBuffer instances;
|
||||
GLModel::InitializationData data;
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
// contain the buffer for model primitive types
|
||||
Model model;
|
||||
|
||||
std::string shader;
|
||||
std::vector<Path> paths;
|
||||
std::vector<RenderPath> render_paths;
|
||||
bool visible{ false };
|
||||
|
||||
void reset();
|
||||
|
||||
// b_id index of buffer contained in this->indices
|
||||
// i_id index of first index contained in this->indices[b_id]
|
||||
// s_id index of first vertex contained in this->vertices
|
||||
void add_path(const GCodeProcessorResult::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id);
|
||||
|
||||
unsigned int max_vertices_per_segment() const {
|
||||
switch (render_primitive_type)
|
||||
{
|
||||
case ERenderPrimitiveType::Point: { return 1; }
|
||||
case ERenderPrimitiveType::Line: { return 2; }
|
||||
case ERenderPrimitiveType::Triangle: { return 8; }
|
||||
default: { return 0; }
|
||||
}
|
||||
}
|
||||
|
||||
size_t max_vertices_per_segment_size_floats() const { return vertices.vertex_size_floats() * static_cast<size_t>(max_vertices_per_segment()); }
|
||||
size_t max_vertices_per_segment_size_bytes() const { return max_vertices_per_segment_size_floats() * sizeof(float); }
|
||||
unsigned int indices_per_segment() const {
|
||||
switch (render_primitive_type)
|
||||
{
|
||||
case ERenderPrimitiveType::Point: { return 1; }
|
||||
case ERenderPrimitiveType::Line: { return 2; }
|
||||
case ERenderPrimitiveType::Triangle: { return 30; } // 3 indices x 10 triangles
|
||||
default: { return 0; }
|
||||
}
|
||||
}
|
||||
size_t indices_per_segment_size_bytes() const { return static_cast<size_t>(indices_per_segment() * sizeof(IBufferType)); }
|
||||
unsigned int max_indices_per_segment() const {
|
||||
switch (render_primitive_type)
|
||||
{
|
||||
case ERenderPrimitiveType::Point: { return 1; }
|
||||
case ERenderPrimitiveType::Line: { return 2; }
|
||||
case ERenderPrimitiveType::Triangle: { return 36; } // 3 indices x 12 triangles
|
||||
default: { return 0; }
|
||||
}
|
||||
}
|
||||
size_t max_indices_per_segment_size_bytes() const { return max_indices_per_segment() * sizeof(IBufferType); }
|
||||
|
||||
bool has_data() const {
|
||||
switch (render_primitive_type)
|
||||
{
|
||||
case ERenderPrimitiveType::Point:
|
||||
case ERenderPrimitiveType::Line:
|
||||
case ERenderPrimitiveType::Triangle: {
|
||||
return !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0;
|
||||
}
|
||||
case ERenderPrimitiveType::InstancedModel: { return model.model.is_initialized() && !model.instances.buffer.empty(); }
|
||||
case ERenderPrimitiveType::BatchedModel: {
|
||||
return model.data.vertices_count() > 0 && model.data.indices_count() &&
|
||||
!vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0;
|
||||
}
|
||||
default: { return false; }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// helper to render shells
|
||||
struct Shells
|
||||
{
|
||||
GLVolumeCollection volumes;
|
||||
bool visible{ false };
|
||||
//BBS: always load shell when preview
|
||||
int print_id{ -1 };
|
||||
int print_modify_count { -1 };
|
||||
bool previewing{ false };
|
||||
};
|
||||
|
||||
// helper to render extrusion paths
|
||||
struct Extrusions
|
||||
{
|
||||
struct Range
|
||||
{
|
||||
float min;
|
||||
float max;
|
||||
unsigned int count;
|
||||
|
||||
Range() { reset(); }
|
||||
|
||||
void update_from(const float value) {
|
||||
if (value != max && value != min)
|
||||
++count;
|
||||
min = std::min(min, value);
|
||||
max = std::max(max, value);
|
||||
}
|
||||
void reset() { min = FLT_MAX; max = -FLT_MAX; count = 0; }
|
||||
|
||||
float step_size() const { return (max - min) / (static_cast<float>(Range_Colors.size()) - 1.0f); }
|
||||
Color get_color_at(float value) const;
|
||||
};
|
||||
|
||||
struct Ranges
|
||||
{
|
||||
// Color mapping by layer height.
|
||||
Range height;
|
||||
// Color mapping by extrusion width.
|
||||
Range width;
|
||||
// Color mapping by feedrate.
|
||||
Range feedrate;
|
||||
// Color mapping by fan speed.
|
||||
Range fan_speed;
|
||||
// Color mapping by volumetric extrusion rate.
|
||||
Range volumetric_rate;
|
||||
// Color mapping by extrusion temperature.
|
||||
Range temperature;
|
||||
|
||||
void reset() {
|
||||
height.reset();
|
||||
width.reset();
|
||||
feedrate.reset();
|
||||
fan_speed.reset();
|
||||
volumetric_rate.reset();
|
||||
temperature.reset();
|
||||
}
|
||||
};
|
||||
|
||||
unsigned int role_visibility_flags{ 0 };
|
||||
Ranges ranges;
|
||||
|
||||
void reset_role_visibility_flags() {
|
||||
role_visibility_flags = 0;
|
||||
for (unsigned int i = 0; i < erCount; ++i) {
|
||||
role_visibility_flags |= 1 << i;
|
||||
}
|
||||
}
|
||||
|
||||
void reset_ranges() { ranges.reset(); }
|
||||
};
|
||||
|
||||
class Layers
|
||||
{
|
||||
public:
|
||||
struct Endpoints
|
||||
{
|
||||
size_t first{ 0 };
|
||||
size_t last{ 0 };
|
||||
|
||||
bool operator == (const Endpoints& other) const {
|
||||
return first == other.first && last == other.last;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<double> m_zs;
|
||||
std::vector<Endpoints> m_endpoints;
|
||||
|
||||
public:
|
||||
void append(double z, Endpoints endpoints) {
|
||||
m_zs.emplace_back(z);
|
||||
m_endpoints.emplace_back(endpoints);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
m_zs = std::vector<double>();
|
||||
m_endpoints = std::vector<Endpoints>();
|
||||
}
|
||||
|
||||
size_t size() const { return m_zs.size(); }
|
||||
bool empty() const { return m_zs.empty(); }
|
||||
const std::vector<double>& get_zs() const { return m_zs; }
|
||||
const std::vector<Endpoints>& get_endpoints() const { return m_endpoints; }
|
||||
std::vector<Endpoints>& get_endpoints() { return m_endpoints; }
|
||||
double get_z_at(unsigned int id) const { return (id < m_zs.size()) ? m_zs[id] : 0.0; }
|
||||
Endpoints get_endpoints_at(unsigned int id) const { return (id < m_endpoints.size()) ? m_endpoints[id] : Endpoints(); }
|
||||
|
||||
bool operator != (const Layers& other) const {
|
||||
if (m_zs != other.m_zs)
|
||||
return true;
|
||||
if (!(m_endpoints == other.m_endpoints))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// used to render the toolpath caps of the current sequential range
|
||||
// (i.e. when sliding on the horizontal slider)
|
||||
struct SequentialRangeCap
|
||||
{
|
||||
TBuffer* buffer{ nullptr };
|
||||
unsigned int ibo{ 0 };
|
||||
unsigned int vbo{ 0 };
|
||||
Color color;
|
||||
|
||||
~SequentialRangeCap();
|
||||
bool is_renderable() const { return buffer != nullptr; }
|
||||
void reset();
|
||||
size_t indices_count() const { return 6; }
|
||||
};
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
struct Statistics
|
||||
{
|
||||
// time
|
||||
int64_t results_time{ 0 };
|
||||
int64_t load_time{ 0 };
|
||||
int64_t load_vertices{ 0 };
|
||||
int64_t smooth_vertices{ 0 };
|
||||
int64_t load_indices{ 0 };
|
||||
int64_t refresh_time{ 0 };
|
||||
int64_t refresh_paths_time{ 0 };
|
||||
// opengl calls
|
||||
int64_t gl_multi_points_calls_count{ 0 };
|
||||
int64_t gl_multi_lines_calls_count{ 0 };
|
||||
int64_t gl_multi_triangles_calls_count{ 0 };
|
||||
int64_t gl_triangles_calls_count{ 0 };
|
||||
int64_t gl_instanced_models_calls_count{ 0 };
|
||||
int64_t gl_batched_models_calls_count{ 0 };
|
||||
// memory
|
||||
int64_t results_size{ 0 };
|
||||
int64_t total_vertices_gpu_size{ 0 };
|
||||
int64_t total_indices_gpu_size{ 0 };
|
||||
int64_t total_instances_gpu_size{ 0 };
|
||||
int64_t max_vbuffer_gpu_size{ 0 };
|
||||
int64_t max_ibuffer_gpu_size{ 0 };
|
||||
int64_t paths_size{ 0 };
|
||||
int64_t render_paths_size{ 0 };
|
||||
int64_t models_instances_size{ 0 };
|
||||
// other
|
||||
int64_t travel_segments_count{ 0 };
|
||||
int64_t wipe_segments_count{ 0 };
|
||||
int64_t extrude_segments_count{ 0 };
|
||||
int64_t instances_count{ 0 };
|
||||
int64_t batched_count{ 0 };
|
||||
int64_t vbuffers_count{ 0 };
|
||||
int64_t ibuffers_count{ 0 };
|
||||
|
||||
void reset_all() {
|
||||
reset_times();
|
||||
reset_opengl();
|
||||
reset_sizes();
|
||||
reset_others();
|
||||
}
|
||||
|
||||
void reset_times() {
|
||||
results_time = 0;
|
||||
load_time = 0;
|
||||
load_vertices = 0;
|
||||
smooth_vertices = 0;
|
||||
load_indices = 0;
|
||||
refresh_time = 0;
|
||||
refresh_paths_time = 0;
|
||||
}
|
||||
|
||||
void reset_opengl() {
|
||||
gl_multi_points_calls_count = 0;
|
||||
gl_multi_lines_calls_count = 0;
|
||||
gl_multi_triangles_calls_count = 0;
|
||||
gl_triangles_calls_count = 0;
|
||||
gl_instanced_models_calls_count = 0;
|
||||
gl_batched_models_calls_count = 0;
|
||||
}
|
||||
|
||||
void reset_sizes() {
|
||||
results_size = 0;
|
||||
total_vertices_gpu_size = 0;
|
||||
total_indices_gpu_size = 0;
|
||||
total_instances_gpu_size = 0;
|
||||
max_vbuffer_gpu_size = 0;
|
||||
max_ibuffer_gpu_size = 0;
|
||||
paths_size = 0;
|
||||
render_paths_size = 0;
|
||||
models_instances_size = 0;
|
||||
}
|
||||
|
||||
void reset_others() {
|
||||
travel_segments_count = 0;
|
||||
wipe_segments_count = 0;
|
||||
extrude_segments_count = 0;
|
||||
instances_count = 0;
|
||||
batched_count = 0;
|
||||
vbuffers_count = 0;
|
||||
ibuffers_count = 0;
|
||||
}
|
||||
};
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
|
||||
public:
|
||||
struct SequentialView
|
||||
{
|
||||
class Marker
|
||||
{
|
||||
GLModel m_model;
|
||||
Vec3f m_world_position;
|
||||
Transform3f m_world_transform;
|
||||
// for seams, the position of the marker is on the last endpoint of the toolpath containing it
|
||||
// the offset is used to show the correct value of tool position in the "ToolPosition" window
|
||||
// see implementation of render() method
|
||||
Vec3f m_world_offset;
|
||||
float m_z_offset{ 0.5f };
|
||||
bool m_visible{ true };
|
||||
|
||||
public:
|
||||
void init(std::string filename);
|
||||
|
||||
const BoundingBoxf3& get_bounding_box() const { return m_model.get_bounding_box(); }
|
||||
|
||||
void set_world_position(const Vec3f& position);
|
||||
void set_world_offset(const Vec3f& offset) { m_world_offset = offset; }
|
||||
|
||||
bool is_visible() const { return m_visible; }
|
||||
void set_visible(bool visible) { m_visible = visible; }
|
||||
|
||||
//BBS: GUI refactor: add canvas size
|
||||
void render(int canvas_width, int canvas_height) const;
|
||||
};
|
||||
|
||||
class GCodeWindow
|
||||
{
|
||||
struct Line
|
||||
{
|
||||
std::string command;
|
||||
std::string parameters;
|
||||
std::string comment;
|
||||
};
|
||||
bool m_visible{ true };
|
||||
uint64_t m_selected_line_id{ 0 };
|
||||
size_t m_last_lines_size{ 0 };
|
||||
std::string m_filename;
|
||||
boost::iostreams::mapped_file_source m_file;
|
||||
// map for accessing data in file by line number
|
||||
std::vector<size_t> m_lines_ends;
|
||||
// current visible lines
|
||||
std::vector<Line> m_lines;
|
||||
|
||||
public:
|
||||
GCodeWindow() = default;
|
||||
~GCodeWindow() { stop_mapping_file(); }
|
||||
void load_gcode(const std::string& filename, std::vector<size_t> &&lines_ends);
|
||||
void reset() {
|
||||
stop_mapping_file();
|
||||
m_lines_ends.clear();
|
||||
m_lines.clear();
|
||||
m_filename.clear();
|
||||
}
|
||||
|
||||
void toggle_visibility() { m_visible = !m_visible; }
|
||||
|
||||
//BBS: GUI refactor: add canvas size
|
||||
//void render(float top, float bottom, uint64_t curr_line_id) const;
|
||||
void render(float top, float bottom, float right, uint64_t curr_line_id) const;
|
||||
|
||||
void stop_mapping_file();
|
||||
};
|
||||
|
||||
struct Endpoints
|
||||
{
|
||||
size_t first{ 0 };
|
||||
size_t last{ 0 };
|
||||
};
|
||||
|
||||
bool skip_invisible_moves{ false };
|
||||
Endpoints endpoints;
|
||||
Endpoints current;
|
||||
Endpoints last_current;
|
||||
Endpoints global;
|
||||
Vec3f current_position{ Vec3f::Zero() };
|
||||
Vec3f current_offset{ Vec3f::Zero() };
|
||||
Marker marker;
|
||||
GCodeWindow gcode_window;
|
||||
std::vector<unsigned int> gcode_ids;
|
||||
|
||||
//BBS: GUI refactor: add canvas size
|
||||
void render(float legend_height, int canvas_width, int canvas_height) const;
|
||||
};
|
||||
|
||||
struct ETools
|
||||
{
|
||||
std::vector<Color> m_tool_colors;
|
||||
std::vector<bool> m_tool_visibles;
|
||||
};
|
||||
|
||||
enum class EViewType : unsigned char
|
||||
{
|
||||
FeatureType = 0,
|
||||
Height,
|
||||
Width,
|
||||
Feedrate,
|
||||
FanSpeed,
|
||||
Temperature,
|
||||
VolumetricRate,
|
||||
Tool,
|
||||
ColorPrint,
|
||||
FilamentId,
|
||||
Count
|
||||
};
|
||||
|
||||
private:
|
||||
bool m_gl_data_initialized{ false };
|
||||
unsigned int m_last_result_id{ 0 };
|
||||
size_t m_moves_count{ 0 };
|
||||
//BBS: save m_gcode_result as well
|
||||
const GCodeProcessorResult* m_gcode_result;
|
||||
//BBS: add only gcode mode
|
||||
bool m_only_gcode_in_preview {false};
|
||||
std::vector<size_t> m_ssid_to_moveid_map;
|
||||
|
||||
std::vector<TBuffer> m_buffers{ static_cast<size_t>(EMoveType::Extrude) };
|
||||
// bounding box of toolpaths
|
||||
BoundingBoxf3 m_paths_bounding_box;
|
||||
// bounding box of toolpaths + marker tools
|
||||
BoundingBoxf3 m_max_bounding_box;
|
||||
//BBS: add shell bounding box
|
||||
BoundingBoxf3 m_shell_bounding_box;
|
||||
float m_max_print_height{ 0.0f };
|
||||
|
||||
//BBS save m_tools_color and m_tools_visible
|
||||
ETools m_tools;
|
||||
ConfigOptionMode m_user_mode;
|
||||
bool m_fold = {false};
|
||||
|
||||
Layers m_layers;
|
||||
std::array<unsigned int, 2> m_layers_z_range;
|
||||
std::vector<ExtrusionRole> m_roles;
|
||||
size_t m_extruders_count;
|
||||
std::vector<unsigned char> m_extruder_ids;
|
||||
std::vector<float> m_filament_diameters;
|
||||
std::vector<float> m_filament_densities;
|
||||
Extrusions m_extrusions;
|
||||
SequentialView m_sequential_view;
|
||||
IMSlider* m_moves_slider;
|
||||
IMSlider* m_layers_slider;
|
||||
Shells m_shells;
|
||||
/*BBS GUI refactor, store displayed items in color scheme combobox */
|
||||
std::vector<EViewType> view_type_items;
|
||||
std::vector<std::string> view_type_items_str;
|
||||
int m_view_type_sel = 0;
|
||||
EViewType m_view_type{ EViewType::FeatureType };
|
||||
std::vector<EMoveType> options_items;
|
||||
|
||||
bool m_legend_enabled{ true };
|
||||
PrintEstimatedStatistics m_print_statistics;
|
||||
PrintEstimatedStatistics::ETimeMode m_time_estimate_mode{ PrintEstimatedStatistics::ETimeMode::Normal };
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
Statistics m_statistics;
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
std::array<float, 2> m_detected_point_sizes = { 0.0f, 0.0f };
|
||||
GCodeProcessorResult::SettingsIds m_settings_ids;
|
||||
std::array<SequentialRangeCap, 2> m_sequential_range_caps;
|
||||
|
||||
std::vector<CustomGCode::Item> m_custom_gcode_per_print_z;
|
||||
|
||||
bool m_contained_in_bed{ true };
|
||||
mutable bool m_no_render_path { false };
|
||||
|
||||
public:
|
||||
GCodeViewer();
|
||||
~GCodeViewer();
|
||||
|
||||
float m_scale = 1.0;
|
||||
void set_scale(float scale = 1.0);
|
||||
void init(ConfigOptionMode mode, Slic3r::PresetBundle* preset_bundle);
|
||||
void update_by_mode(ConfigOptionMode mode);
|
||||
|
||||
// extract rendering data from the given parameters
|
||||
//BBS: add only gcode mode
|
||||
void load(const GCodeProcessorResult& gcode_result, const Print& print, const BuildVolume& build_volume,
|
||||
const std::vector<BoundingBoxf3>& exclude_bounding_box, bool initialized, ConfigOptionMode mode, bool only_gcode = false);
|
||||
// recalculate ranges in dependence of what is visible and sets tool/print colors
|
||||
void refresh(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors);
|
||||
void refresh_render_paths();
|
||||
void update_shells_color_by_extruder(const DynamicPrintConfig* config);
|
||||
|
||||
void reset();
|
||||
//BBS: always load shell at preview
|
||||
void reset_shell();
|
||||
void load_shells(const Print& print, bool initialized, bool force_previewing = false);
|
||||
void set_shells_on_preview(bool is_previewing) { m_shells.previewing = is_previewing; }
|
||||
//BBS: GUI refactor: add canvas width and height
|
||||
void render(int canvas_width, int canvas_height, int right_margin);
|
||||
//BBS
|
||||
void _render_calibration_thumbnail_internal(ThumbnailData& thumbnail_data, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
||||
void _render_calibration_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
||||
void render_calibration_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
||||
|
||||
bool has_data() const { return !m_roles.empty(); }
|
||||
bool can_export_toolpaths() const;
|
||||
|
||||
const BoundingBoxf3& get_paths_bounding_box() const { return m_paths_bounding_box; }
|
||||
const BoundingBoxf3& get_max_bounding_box() const { return m_max_bounding_box; }
|
||||
const BoundingBoxf3& get_shell_bounding_box() const { return m_shell_bounding_box; }
|
||||
const std::vector<double>& get_layers_zs() const { return m_layers.get_zs(); }
|
||||
|
||||
const SequentialView& get_sequential_view() const { return m_sequential_view; }
|
||||
void update_sequential_view_current(unsigned int first, unsigned int last);
|
||||
|
||||
/* BBS IMSlider */
|
||||
IMSlider *get_moves_slider() { return m_moves_slider; }
|
||||
IMSlider *get_layers_slider() { return m_layers_slider; }
|
||||
void enable_moves_slider(bool enable) const;
|
||||
void update_moves_slider(bool set_to_max = false);
|
||||
void update_layers_slider_mode();
|
||||
|
||||
bool is_contained_in_bed() const { return m_contained_in_bed; }
|
||||
//BBS: add only gcode mode
|
||||
bool is_only_gcode_in_preview() const { return m_only_gcode_in_preview; }
|
||||
|
||||
EViewType get_view_type() const { return m_view_type; }
|
||||
void set_view_type(EViewType type, bool reset_feature_type_visible = true) {
|
||||
if (type == EViewType::Count)
|
||||
type = EViewType::FeatureType;
|
||||
m_view_type = (EViewType)type;
|
||||
if (reset_feature_type_visible && type == EViewType::ColorPrint) {
|
||||
reset_visible(EViewType::FeatureType);
|
||||
}
|
||||
}
|
||||
void reset_visible(EViewType type) {
|
||||
if (type == EViewType::FeatureType) {
|
||||
for (size_t i = 0; i < m_roles.size(); ++i) {
|
||||
ExtrusionRole role = m_roles[i];
|
||||
m_extrusions.role_visibility_flags = m_extrusions.role_visibility_flags | (1 << role);
|
||||
}
|
||||
} else if (type == EViewType::ColorPrint){
|
||||
for(auto item: m_tools.m_tool_visibles) item = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_toolpath_move_type_visible(EMoveType type) const;
|
||||
void set_toolpath_move_type_visible(EMoveType type, bool visible);
|
||||
unsigned int get_toolpath_role_visibility_flags() const { return m_extrusions.role_visibility_flags; }
|
||||
void set_toolpath_role_visibility_flags(unsigned int flags) { m_extrusions.role_visibility_flags = flags; }
|
||||
unsigned int get_options_visibility_flags() const;
|
||||
void set_options_visibility_from_flags(unsigned int flags);
|
||||
void set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range);
|
||||
|
||||
bool is_legend_enabled() const { return m_legend_enabled; }
|
||||
void enable_legend(bool enable) { m_legend_enabled = enable; }
|
||||
|
||||
void export_toolpaths_to_obj(const char* filename) const;
|
||||
|
||||
void toggle_gcode_window_visibility() { m_sequential_view.gcode_window.toggle_visibility(); }
|
||||
|
||||
std::vector<CustomGCode::Item>& get_custom_gcode_per_print_z() { return m_custom_gcode_per_print_z; }
|
||||
size_t get_extruders_count() { return m_extruders_count; }
|
||||
void push_combo_style();
|
||||
void pop_combo_style();
|
||||
|
||||
private:
|
||||
void load_toolpaths(const GCodeProcessorResult& gcode_result, const BuildVolume& build_volume, const std::vector<BoundingBoxf3>& exclude_bounding_box);
|
||||
//BBS: always load shell at preview
|
||||
//void load_shells(const Print& print, bool initialized);
|
||||
void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const;
|
||||
void render_toolpaths();
|
||||
void render_shells();
|
||||
|
||||
//BBS: GUI refactor: add canvas size
|
||||
void render_legend(float &legend_height, int canvas_width, int canvas_height, int right_margin);
|
||||
void render_slider(int canvas_width, int canvas_height);
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
void render_statistics();
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
bool is_visible(ExtrusionRole role) const {
|
||||
return role < erCount && (m_extrusions.role_visibility_flags & (1 << role)) != 0;
|
||||
}
|
||||
bool is_visible(const Path& path) const { return is_visible(path.role); }
|
||||
void log_memory_used(const std::string& label, int64_t additional = 0) const;
|
||||
Color option_color(EMoveType move_type) const;
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_GCodeViewer_hpp_
|
||||
|
||||
7788
src/slic3r/GUI/GLCanvas3D.cpp
Normal file
7788
src/slic3r/GUI/GLCanvas3D.cpp
Normal file
File diff suppressed because it is too large
Load diff
1035
src/slic3r/GUI/GLCanvas3D.hpp
Normal file
1035
src/slic3r/GUI/GLCanvas3D.hpp
Normal file
File diff suppressed because it is too large
Load diff
748
src/slic3r/GUI/GLModel.cpp
Normal file
748
src/slic3r/GUI/GLModel.cpp
Normal file
|
|
@ -0,0 +1,748 @@
|
|||
#include "libslic3r/libslic3r.h"
|
||||
#include "GLModel.hpp"
|
||||
|
||||
#include "3DScene.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "GLShader.hpp"
|
||||
|
||||
#include "libslic3r/TriangleMesh.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/Polygon.hpp"
|
||||
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
size_t GLModel::InitializationData::vertices_count() const
|
||||
{
|
||||
size_t ret = 0;
|
||||
for (const Entity& entity : entities) {
|
||||
ret += entity.positions.size();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t GLModel::InitializationData::indices_count() const
|
||||
{
|
||||
size_t ret = 0;
|
||||
for (const Entity& entity : entities) {
|
||||
ret += entity.indices.size();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GLModel::init_from(const InitializationData& data)
|
||||
{
|
||||
if (!m_render_data.empty()) // call reset() if you want to reuse this model
|
||||
return;
|
||||
|
||||
for (const InitializationData::Entity& entity : data.entities) {
|
||||
if (entity.positions.empty() || entity.indices.empty())
|
||||
continue;
|
||||
|
||||
assert(entity.normals.empty() || entity.normals.size() == entity.positions.size());
|
||||
|
||||
RenderData rdata;
|
||||
rdata.type = entity.type;
|
||||
rdata.color = entity.color;
|
||||
|
||||
// vertices/normals data
|
||||
std::vector<float> vertices(6 * entity.positions.size());
|
||||
for (size_t i = 0; i < entity.positions.size(); ++i) {
|
||||
const size_t offset = i * 6;
|
||||
::memcpy(static_cast<void*>(&vertices[offset]), static_cast<const void*>(entity.positions[i].data()), 3 * sizeof(float));
|
||||
if (!entity.normals.empty())
|
||||
::memcpy(static_cast<void*>(&vertices[3 + offset]), static_cast<const void*>(entity.normals[i].data()), 3 * sizeof(float));
|
||||
}
|
||||
|
||||
// indices data
|
||||
std::vector<unsigned int> indices = entity.indices;
|
||||
|
||||
rdata.indices_count = static_cast<unsigned int>(indices.size());
|
||||
|
||||
// update bounding box
|
||||
for (size_t i = 0; i < entity.positions.size(); ++i) {
|
||||
m_bounding_box.merge(entity.positions[i].cast<double>());
|
||||
}
|
||||
|
||||
send_to_gpu(rdata, vertices, indices);
|
||||
m_render_data.emplace_back(rdata);
|
||||
}
|
||||
}
|
||||
|
||||
void GLModel::init_from(const indexed_triangle_set& its, const BoundingBoxf3 &bbox)
|
||||
{
|
||||
if (!m_render_data.empty()) // call reset() if you want to reuse this model
|
||||
return;
|
||||
|
||||
RenderData data;
|
||||
data.type = PrimitiveType::Triangles;
|
||||
|
||||
std::vector<float> vertices = std::vector<float>(18 * its.indices.size());
|
||||
std::vector<unsigned int> indices = std::vector<unsigned int>(3 * its.indices.size());
|
||||
|
||||
unsigned int vertices_count = 0;
|
||||
for (uint32_t i = 0; i < its.indices.size(); ++i) {
|
||||
stl_triangle_vertex_indices face = its.indices[i];
|
||||
stl_vertex vertex[3] = { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] };
|
||||
stl_vertex n = face_normal_normalized(vertex);
|
||||
for (size_t j = 0; j < 3; ++ j) {
|
||||
size_t offset = i * 18 + j * 6;
|
||||
::memcpy(static_cast<void*>(&vertices[offset]), static_cast<const void*>(vertex[j].data()), 3 * sizeof(float));
|
||||
::memcpy(static_cast<void*>(&vertices[3 + offset]), static_cast<const void*>(n.data()), 3 * sizeof(float));
|
||||
}
|
||||
for (size_t j = 0; j < 3; ++j)
|
||||
indices[i * 3 + j] = vertices_count + j;
|
||||
vertices_count += 3;
|
||||
}
|
||||
|
||||
data.indices_count = static_cast<unsigned int>(indices.size());
|
||||
m_bounding_box = bbox;
|
||||
|
||||
send_to_gpu(data, vertices, indices);
|
||||
m_render_data.emplace_back(data);
|
||||
}
|
||||
|
||||
void GLModel::init_from(const indexed_triangle_set& its)
|
||||
{
|
||||
this->init_from(its, bounding_box(its));
|
||||
}
|
||||
|
||||
void GLModel::init_from(const Polygons& polygons, float z)
|
||||
{
|
||||
auto append_polygon = [](const Polygon& polygon, float z, GUI::GLModel::InitializationData& data) {
|
||||
if (!polygon.empty()) {
|
||||
GUI::GLModel::InitializationData::Entity entity;
|
||||
entity.type = GUI::GLModel::PrimitiveType::LineLoop;
|
||||
// contour
|
||||
entity.positions.reserve(polygon.size() + 1);
|
||||
entity.indices.reserve(polygon.size() + 1);
|
||||
unsigned int id = 0;
|
||||
for (const Point& p : polygon) {
|
||||
Vec3f position = unscale(p.x(), p.y(), 0.0).cast<float>();
|
||||
position.z() = z;
|
||||
entity.positions.emplace_back(position);
|
||||
entity.indices.emplace_back(id++);
|
||||
}
|
||||
data.entities.emplace_back(entity);
|
||||
}
|
||||
};
|
||||
|
||||
InitializationData init_data;
|
||||
for (const Polygon& polygon : polygons) {
|
||||
append_polygon(polygon, z, init_data);
|
||||
}
|
||||
init_from(init_data);
|
||||
}
|
||||
|
||||
bool GLModel::init_from_file(const std::string& filename)
|
||||
{
|
||||
if (!boost::filesystem::exists(filename))
|
||||
return false;
|
||||
|
||||
if (!boost::algorithm::iends_with(filename, ".stl"))
|
||||
return false;
|
||||
|
||||
Model model;
|
||||
try
|
||||
{
|
||||
model = Model::read_from_file(filename);
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TriangleMesh mesh = model.mesh();
|
||||
init_from(mesh.its, mesh.bounding_box());
|
||||
|
||||
m_filename = filename;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLModel::set_color(int entity_id, const std::array<float, 4>& color)
|
||||
{
|
||||
for (size_t i = 0; i < m_render_data.size(); ++i) {
|
||||
if (entity_id == -1 || static_cast<int>(i) == entity_id)
|
||||
m_render_data[i].color = color;
|
||||
}
|
||||
}
|
||||
|
||||
void GLModel::reset()
|
||||
{
|
||||
for (RenderData& data : m_render_data) {
|
||||
// release gpu memory
|
||||
if (data.ibo_id > 0)
|
||||
glsafe(::glDeleteBuffers(1, &data.ibo_id));
|
||||
if (data.vbo_id > 0)
|
||||
glsafe(::glDeleteBuffers(1, &data.vbo_id));
|
||||
}
|
||||
|
||||
m_render_data.clear();
|
||||
m_bounding_box = BoundingBoxf3();
|
||||
m_filename = std::string();
|
||||
}
|
||||
|
||||
void GLModel::render() const
|
||||
{
|
||||
GLShaderProgram* shader = wxGetApp().get_current_shader();
|
||||
|
||||
for (const RenderData& data : m_render_data) {
|
||||
if (data.vbo_id == 0 || data.ibo_id == 0)
|
||||
continue;
|
||||
|
||||
GLenum mode;
|
||||
switch (data.type)
|
||||
{
|
||||
default:
|
||||
case PrimitiveType::Triangles: { mode = GL_TRIANGLES; break; }
|
||||
case PrimitiveType::Lines: { mode = GL_LINES; break; }
|
||||
case PrimitiveType::LineStrip: { mode = GL_LINE_STRIP; break; }
|
||||
case PrimitiveType::LineLoop: { mode = GL_LINE_LOOP; break; }
|
||||
}
|
||||
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, data.vbo_id));
|
||||
glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)0));
|
||||
glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))));
|
||||
|
||||
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
|
||||
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
|
||||
|
||||
if (shader != nullptr)
|
||||
shader->set_uniform("uniform_color", data.color);
|
||||
else
|
||||
glsafe(::glColor4fv(data.color.data()));
|
||||
|
||||
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ibo_id));
|
||||
glsafe(::glDrawElements(mode, static_cast<GLsizei>(data.indices_count), GL_UNSIGNED_INT, (const void*)0));
|
||||
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
|
||||
|
||||
glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
|
||||
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
|
||||
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void GLModel::render_instanced(unsigned int instances_vbo, unsigned int instances_count) const
|
||||
{
|
||||
if (instances_vbo == 0)
|
||||
return;
|
||||
|
||||
GLShaderProgram* shader = wxGetApp().get_current_shader();
|
||||
assert(shader == nullptr || boost::algorithm::iends_with(shader->get_name(), "_instanced"));
|
||||
|
||||
// vertex attributes
|
||||
GLint position_id = (shader != nullptr) ? shader->get_attrib_location("v_position") : -1;
|
||||
GLint normal_id = (shader != nullptr) ? shader->get_attrib_location("v_normal") : -1;
|
||||
assert(position_id != -1 && normal_id != -1);
|
||||
|
||||
// instance attributes
|
||||
GLint offset_id = (shader != nullptr) ? shader->get_attrib_location("i_offset") : -1;
|
||||
GLint scales_id = (shader != nullptr) ? shader->get_attrib_location("i_scales") : -1;
|
||||
assert(offset_id != -1 && scales_id != -1);
|
||||
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, instances_vbo));
|
||||
if (offset_id != -1) {
|
||||
glsafe(::glVertexAttribPointer(offset_id, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (GLvoid*)0));
|
||||
glsafe(::glEnableVertexAttribArray(offset_id));
|
||||
glsafe(::glVertexAttribDivisor(offset_id, 1));
|
||||
}
|
||||
if (scales_id != -1) {
|
||||
glsafe(::glVertexAttribPointer(scales_id, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (GLvoid*)(3 * sizeof(float))));
|
||||
glsafe(::glEnableVertexAttribArray(scales_id));
|
||||
glsafe(::glVertexAttribDivisor(scales_id, 1));
|
||||
}
|
||||
|
||||
for (const RenderData& data : m_render_data) {
|
||||
if (data.vbo_id == 0 || data.ibo_id == 0)
|
||||
continue;
|
||||
|
||||
GLenum mode;
|
||||
switch (data.type)
|
||||
{
|
||||
default:
|
||||
case PrimitiveType::Triangles: { mode = GL_TRIANGLES; break; }
|
||||
case PrimitiveType::Lines: { mode = GL_LINES; break; }
|
||||
case PrimitiveType::LineStrip: { mode = GL_LINE_STRIP; break; }
|
||||
case PrimitiveType::LineLoop: { mode = GL_LINE_LOOP; break; }
|
||||
}
|
||||
|
||||
if (shader != nullptr)
|
||||
shader->set_uniform("uniform_color", data.color);
|
||||
else
|
||||
glsafe(::glColor4fv(data.color.data()));
|
||||
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, data.vbo_id));
|
||||
if (position_id != -1) {
|
||||
glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (GLvoid*)0));
|
||||
glsafe(::glEnableVertexAttribArray(position_id));
|
||||
}
|
||||
if (normal_id != -1) {
|
||||
glsafe(::glVertexAttribPointer(normal_id, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (GLvoid*)(3 * sizeof(float))));
|
||||
glsafe(::glEnableVertexAttribArray(normal_id));
|
||||
}
|
||||
|
||||
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ibo_id));
|
||||
glsafe(::glDrawElementsInstanced(mode, static_cast<GLsizei>(data.indices_count), GL_UNSIGNED_INT, (const void*)0, instances_count));
|
||||
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
|
||||
|
||||
if (normal_id != -1)
|
||||
glsafe(::glDisableVertexAttribArray(normal_id));
|
||||
if (position_id != -1)
|
||||
glsafe(::glDisableVertexAttribArray(position_id));
|
||||
}
|
||||
|
||||
if (scales_id != -1)
|
||||
glsafe(::glDisableVertexAttribArray(scales_id));
|
||||
if (offset_id != -1)
|
||||
glsafe(::glDisableVertexAttribArray(offset_id));
|
||||
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||
}
|
||||
|
||||
void GLModel::send_to_gpu(RenderData& data, const std::vector<float>& vertices, const std::vector<unsigned int>& indices)
|
||||
{
|
||||
assert(data.vbo_id == 0);
|
||||
assert(data.ibo_id == 0);
|
||||
|
||||
// vertex data -> send to gpu
|
||||
glsafe(::glGenBuffers(1, &data.vbo_id));
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, data.vbo_id));
|
||||
glsafe(::glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW));
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||
|
||||
// indices data -> send to gpu
|
||||
glsafe(::glGenBuffers(1, &data.ibo_id));
|
||||
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ibo_id));
|
||||
glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW));
|
||||
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
|
||||
}
|
||||
|
||||
GLModel::InitializationData stilized_arrow(int resolution, float tip_radius, float tip_height, float stem_radius, float stem_height)
|
||||
{
|
||||
auto append_vertex = [](GLModel::InitializationData::Entity& entity, const Vec3f& position, const Vec3f& normal) {
|
||||
entity.positions.emplace_back(position);
|
||||
entity.normals.emplace_back(normal);
|
||||
};
|
||||
auto append_indices = [](GLModel::InitializationData::Entity& entity, unsigned int v1, unsigned int v2, unsigned int v3) {
|
||||
entity.indices.emplace_back(v1);
|
||||
entity.indices.emplace_back(v2);
|
||||
entity.indices.emplace_back(v3);
|
||||
};
|
||||
|
||||
resolution = std::max(4, resolution);
|
||||
|
||||
GLModel::InitializationData data;
|
||||
GLModel::InitializationData::Entity entity;
|
||||
entity.type = GLModel::PrimitiveType::Triangles;
|
||||
|
||||
const float angle_step = 2.0f * M_PI / static_cast<float>(resolution);
|
||||
std::vector<float> cosines(resolution);
|
||||
std::vector<float> sines(resolution);
|
||||
|
||||
for (int i = 0; i < resolution; ++i) {
|
||||
const float angle = angle_step * static_cast<float>(i);
|
||||
cosines[i] = ::cos(angle);
|
||||
sines[i] = -::sin(angle);
|
||||
}
|
||||
|
||||
const float total_height = tip_height + stem_height;
|
||||
|
||||
// tip vertices/normals
|
||||
append_vertex(entity, { 0.0f, 0.0f, total_height }, Vec3f::UnitZ());
|
||||
for (int i = 0; i < resolution; ++i) {
|
||||
append_vertex(entity, { tip_radius * sines[i], tip_radius * cosines[i], stem_height }, { sines[i], cosines[i], 0.0f });
|
||||
}
|
||||
|
||||
// tip triangles
|
||||
for (int i = 0; i < resolution; ++i) {
|
||||
const int v3 = (i < resolution - 1) ? i + 2 : 1;
|
||||
append_indices(entity, 0, i + 1, v3);
|
||||
}
|
||||
|
||||
// tip cap outer perimeter vertices
|
||||
for (int i = 0; i < resolution; ++i) {
|
||||
append_vertex(entity, { tip_radius * sines[i], tip_radius * cosines[i], stem_height }, -Vec3f::UnitZ());
|
||||
}
|
||||
|
||||
// tip cap inner perimeter vertices
|
||||
for (int i = 0; i < resolution; ++i) {
|
||||
append_vertex(entity, { stem_radius * sines[i], stem_radius * cosines[i], stem_height }, -Vec3f::UnitZ());
|
||||
}
|
||||
|
||||
// tip cap triangles
|
||||
for (int i = 0; i < resolution; ++i) {
|
||||
const int v2 = (i < resolution - 1) ? i + resolution + 2 : resolution + 1;
|
||||
const int v3 = (i < resolution - 1) ? i + 2 * resolution + 2 : 2 * resolution + 1;
|
||||
append_indices(entity, i + resolution + 1, v3, v2);
|
||||
append_indices(entity, i + resolution + 1, i + 2 * resolution + 1, v3);
|
||||
}
|
||||
|
||||
// stem bottom vertices
|
||||
for (int i = 0; i < resolution; ++i) {
|
||||
append_vertex(entity, { stem_radius * sines[i], stem_radius * cosines[i], stem_height }, { sines[i], cosines[i], 0.0f });
|
||||
}
|
||||
|
||||
// stem top vertices
|
||||
for (int i = 0; i < resolution; ++i) {
|
||||
append_vertex(entity, { stem_radius * sines[i], stem_radius * cosines[i], 0.0f }, { sines[i], cosines[i], 0.0f });
|
||||
}
|
||||
|
||||
// stem triangles
|
||||
for (int i = 0; i < resolution; ++i) {
|
||||
const int v2 = (i < resolution - 1) ? i + 3 * resolution + 2 : 3 * resolution + 1;
|
||||
const int v3 = (i < resolution - 1) ? i + 4 * resolution + 2 : 4 * resolution + 1;
|
||||
append_indices(entity, i + 3 * resolution + 1, v3, v2);
|
||||
append_indices(entity, i + 3 * resolution + 1, i + 4 * resolution + 1, v3);
|
||||
}
|
||||
|
||||
// stem cap vertices
|
||||
append_vertex(entity, Vec3f::Zero(), -Vec3f::UnitZ());
|
||||
for (int i = 0; i < resolution; ++i) {
|
||||
append_vertex(entity, { stem_radius * sines[i], stem_radius * cosines[i], 0.0f }, -Vec3f::UnitZ());
|
||||
}
|
||||
|
||||
// stem cap triangles
|
||||
for (int i = 0; i < resolution; ++i) {
|
||||
const int v3 = (i < resolution - 1) ? i + 5 * resolution + 3 : 5 * resolution + 2;
|
||||
append_indices(entity, 5 * resolution + 1, v3, i + 5 * resolution + 2);
|
||||
}
|
||||
|
||||
data.entities.emplace_back(entity);
|
||||
return data;
|
||||
}
|
||||
|
||||
GLModel::InitializationData circular_arrow(int resolution, float radius, float tip_height, float tip_width, float stem_width, float thickness)
|
||||
{
|
||||
auto append_vertex = [](GLModel::InitializationData::Entity& entity, const Vec3f& position, const Vec3f& normal) {
|
||||
entity.positions.emplace_back(position);
|
||||
entity.normals.emplace_back(normal);
|
||||
};
|
||||
auto append_indices = [](GLModel::InitializationData::Entity& entity, unsigned int v1, unsigned int v2, unsigned int v3) {
|
||||
entity.indices.emplace_back(v1);
|
||||
entity.indices.emplace_back(v2);
|
||||
entity.indices.emplace_back(v3);
|
||||
};
|
||||
|
||||
resolution = std::max(2, resolution);
|
||||
|
||||
GLModel::InitializationData data;
|
||||
GLModel::InitializationData::Entity entity;
|
||||
entity.type = GLModel::PrimitiveType::Triangles;
|
||||
|
||||
const float half_thickness = 0.5f * thickness;
|
||||
const float half_stem_width = 0.5f * stem_width;
|
||||
const float half_tip_width = 0.5f * tip_width;
|
||||
|
||||
const float outer_radius = radius + half_stem_width;
|
||||
const float inner_radius = radius - half_stem_width;
|
||||
const float step_angle = 0.5f * PI / static_cast<float>(resolution);
|
||||
|
||||
// tip
|
||||
// top face vertices
|
||||
append_vertex(entity, { 0.0f, outer_radius, half_thickness }, Vec3f::UnitZ());
|
||||
append_vertex(entity, { 0.0f, radius + half_tip_width, half_thickness }, Vec3f::UnitZ());
|
||||
append_vertex(entity, { -tip_height, radius, half_thickness }, Vec3f::UnitZ());
|
||||
append_vertex(entity, { 0.0f, radius - half_tip_width, half_thickness }, Vec3f::UnitZ());
|
||||
append_vertex(entity, { 0.0f, inner_radius, half_thickness }, Vec3f::UnitZ());
|
||||
|
||||
// top face triangles
|
||||
append_indices(entity, 0, 1, 2);
|
||||
append_indices(entity, 0, 2, 4);
|
||||
append_indices(entity, 4, 2, 3);
|
||||
|
||||
// bottom face vertices
|
||||
append_vertex(entity, { 0.0f, outer_radius, -half_thickness }, -Vec3f::UnitZ());
|
||||
append_vertex(entity, { 0.0f, radius + half_tip_width, -half_thickness }, -Vec3f::UnitZ());
|
||||
append_vertex(entity, { -tip_height, radius, -half_thickness }, -Vec3f::UnitZ());
|
||||
append_vertex(entity, { 0.0f, radius - half_tip_width, -half_thickness }, -Vec3f::UnitZ());
|
||||
append_vertex(entity, { 0.0f, inner_radius, -half_thickness }, -Vec3f::UnitZ());
|
||||
|
||||
// bottom face triangles
|
||||
append_indices(entity, 5, 7, 6);
|
||||
append_indices(entity, 5, 9, 7);
|
||||
append_indices(entity, 9, 8, 7);
|
||||
|
||||
// side faces vertices
|
||||
append_vertex(entity, { 0.0f, outer_radius, -half_thickness }, Vec3f::UnitX());
|
||||
append_vertex(entity, { 0.0f, radius + half_tip_width, -half_thickness }, Vec3f::UnitX());
|
||||
append_vertex(entity, { 0.0f, outer_radius, half_thickness }, Vec3f::UnitX());
|
||||
append_vertex(entity, { 0.0f, radius + half_tip_width, half_thickness }, Vec3f::UnitX());
|
||||
|
||||
Vec3f normal(-half_tip_width, tip_height, 0.0f);
|
||||
normal.normalize();
|
||||
append_vertex(entity, { 0.0f, radius + half_tip_width, -half_thickness }, normal);
|
||||
append_vertex(entity, { -tip_height, radius, -half_thickness }, normal);
|
||||
append_vertex(entity, { 0.0f, radius + half_tip_width, half_thickness }, normal);
|
||||
append_vertex(entity, { -tip_height, radius, half_thickness }, normal);
|
||||
|
||||
normal = Vec3f(-half_tip_width, -tip_height, 0.0f);
|
||||
normal.normalize();
|
||||
append_vertex(entity, { -tip_height, radius, -half_thickness }, normal);
|
||||
append_vertex(entity, { 0.0f, radius - half_tip_width, -half_thickness }, normal);
|
||||
append_vertex(entity, { -tip_height, radius, half_thickness }, normal);
|
||||
append_vertex(entity, { 0.0f, radius - half_tip_width, half_thickness }, normal);
|
||||
|
||||
append_vertex(entity, { 0.0f, radius - half_tip_width, -half_thickness }, Vec3f::UnitX());
|
||||
append_vertex(entity, { 0.0f, inner_radius, -half_thickness }, Vec3f::UnitX());
|
||||
append_vertex(entity, { 0.0f, radius - half_tip_width, half_thickness }, Vec3f::UnitX());
|
||||
append_vertex(entity, { 0.0f, inner_radius, half_thickness }, Vec3f::UnitX());
|
||||
|
||||
// side face triangles
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
const int ii = i * 4;
|
||||
append_indices(entity, 10 + ii, 11 + ii, 13 + ii);
|
||||
append_indices(entity, 10 + ii, 13 + ii, 12 + ii);
|
||||
}
|
||||
|
||||
// stem
|
||||
// top face vertices
|
||||
for (int i = 0; i <= resolution; ++i) {
|
||||
const float angle = static_cast<float>(i) * step_angle;
|
||||
append_vertex(entity, { inner_radius * ::sin(angle), inner_radius * ::cos(angle), half_thickness }, Vec3f::UnitZ());
|
||||
}
|
||||
|
||||
for (int i = 0; i <= resolution; ++i) {
|
||||
const float angle = static_cast<float>(i) * step_angle;
|
||||
append_vertex(entity, { outer_radius * ::sin(angle), outer_radius * ::cos(angle), half_thickness }, Vec3f::UnitZ());
|
||||
}
|
||||
|
||||
// top face triangles
|
||||
for (int i = 0; i < resolution; ++i) {
|
||||
append_indices(entity, 26 + i, 27 + i, 27 + resolution + i);
|
||||
append_indices(entity, 27 + i, 28 + resolution + i, 27 + resolution + i);
|
||||
}
|
||||
|
||||
// bottom face vertices
|
||||
for (int i = 0; i <= resolution; ++i) {
|
||||
const float angle = static_cast<float>(i) * step_angle;
|
||||
append_vertex(entity, { inner_radius * ::sin(angle), inner_radius * ::cos(angle), -half_thickness }, -Vec3f::UnitZ());
|
||||
}
|
||||
|
||||
for (int i = 0; i <= resolution; ++i) {
|
||||
const float angle = static_cast<float>(i) * step_angle;
|
||||
append_vertex(entity, { outer_radius * ::sin(angle), outer_radius * ::cos(angle), -half_thickness }, -Vec3f::UnitZ());
|
||||
}
|
||||
|
||||
// bottom face triangles
|
||||
for (int i = 0; i < resolution; ++i) {
|
||||
append_indices(entity, 28 + 2 * resolution + i, 29 + 3 * resolution + i, 29 + 2 * resolution + i);
|
||||
append_indices(entity, 29 + 2 * resolution + i, 29 + 3 * resolution + i, 30 + 3 * resolution + i);
|
||||
}
|
||||
|
||||
// side faces vertices and triangles
|
||||
for (int i = 0; i <= resolution; ++i) {
|
||||
const float angle = static_cast<float>(i) * step_angle;
|
||||
const float c = ::cos(angle);
|
||||
const float s = ::sin(angle);
|
||||
append_vertex(entity, { inner_radius * s, inner_radius * c, -half_thickness }, { -s, -c, 0.0f });
|
||||
}
|
||||
|
||||
for (int i = 0; i <= resolution; ++i) {
|
||||
const float angle = static_cast<float>(i) * step_angle;
|
||||
const float c = ::cos(angle);
|
||||
const float s = ::sin(angle);
|
||||
append_vertex(entity, { inner_radius * s, inner_radius * c, half_thickness }, { -s, -c, 0.0f });
|
||||
}
|
||||
|
||||
int first_id = 26 + 4 * (resolution + 1);
|
||||
for (int i = 0; i < resolution; ++i) {
|
||||
const int ii = first_id + i;
|
||||
append_indices(entity, ii, ii + 1, ii + resolution + 2);
|
||||
append_indices(entity, ii, ii + resolution + 2, ii + resolution + 1);
|
||||
}
|
||||
|
||||
append_vertex(entity, { inner_radius, 0.0f, -half_thickness }, -Vec3f::UnitY());
|
||||
append_vertex(entity, { outer_radius, 0.0f, -half_thickness }, -Vec3f::UnitY());
|
||||
append_vertex(entity, { inner_radius, 0.0f, half_thickness }, -Vec3f::UnitY());
|
||||
append_vertex(entity, { outer_radius, 0.0f, half_thickness }, -Vec3f::UnitY());
|
||||
|
||||
first_id = 26 + 6 * (resolution + 1);
|
||||
append_indices(entity, first_id, first_id + 1, first_id + 3);
|
||||
append_indices(entity, first_id, first_id + 3, first_id + 2);
|
||||
|
||||
for (int i = resolution; i >= 0; --i) {
|
||||
const float angle = static_cast<float>(i) * step_angle;
|
||||
const float c = ::cos(angle);
|
||||
const float s = ::sin(angle);
|
||||
append_vertex(entity, { outer_radius * s, outer_radius * c, -half_thickness }, { s, c, 0.0f });
|
||||
}
|
||||
|
||||
for (int i = resolution; i >= 0; --i) {
|
||||
const float angle = static_cast<float>(i) * step_angle;
|
||||
const float c = ::cos(angle);
|
||||
const float s = ::sin(angle);
|
||||
append_vertex(entity, { outer_radius * s, outer_radius * c, +half_thickness }, { s, c, 0.0f });
|
||||
}
|
||||
|
||||
first_id = 30 + 6 * (resolution + 1);
|
||||
for (int i = 0; i < resolution; ++i) {
|
||||
const int ii = first_id + i;
|
||||
append_indices(entity, ii, ii + 1, ii + resolution + 2);
|
||||
append_indices(entity, ii, ii + resolution + 2, ii + resolution + 1);
|
||||
}
|
||||
|
||||
data.entities.emplace_back(entity);
|
||||
return data;
|
||||
}
|
||||
|
||||
GLModel::InitializationData straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness)
|
||||
{
|
||||
auto append_vertex = [](GLModel::InitializationData::Entity& entity, const Vec3f& position, const Vec3f& normal) {
|
||||
entity.positions.emplace_back(position);
|
||||
entity.normals.emplace_back(normal);
|
||||
};
|
||||
auto append_indices = [](GLModel::InitializationData::Entity& entity, unsigned int v1, unsigned int v2, unsigned int v3) {
|
||||
entity.indices.emplace_back(v1);
|
||||
entity.indices.emplace_back(v2);
|
||||
entity.indices.emplace_back(v3);
|
||||
};
|
||||
|
||||
GLModel::InitializationData data;
|
||||
GLModel::InitializationData::Entity entity;
|
||||
entity.type = GLModel::PrimitiveType::Triangles;
|
||||
|
||||
const float half_thickness = 0.5f * thickness;
|
||||
const float half_stem_width = 0.5f * stem_width;
|
||||
const float half_tip_width = 0.5f * tip_width;
|
||||
const float total_height = tip_height + stem_height;
|
||||
|
||||
// top face vertices
|
||||
append_vertex(entity, { half_stem_width, 0.0, half_thickness }, Vec3f::UnitZ());
|
||||
append_vertex(entity, { half_stem_width, stem_height, half_thickness }, Vec3f::UnitZ());
|
||||
append_vertex(entity, { half_tip_width, stem_height, half_thickness }, Vec3f::UnitZ());
|
||||
append_vertex(entity, { 0.0, total_height, half_thickness }, Vec3f::UnitZ());
|
||||
append_vertex(entity, { -half_tip_width, stem_height, half_thickness }, Vec3f::UnitZ());
|
||||
append_vertex(entity, { -half_stem_width, stem_height, half_thickness }, Vec3f::UnitZ());
|
||||
append_vertex(entity, { -half_stem_width, 0.0, half_thickness }, Vec3f::UnitZ());
|
||||
|
||||
// top face triangles
|
||||
append_indices(entity, 0, 1, 6);
|
||||
append_indices(entity, 6, 1, 5);
|
||||
append_indices(entity, 4, 5, 3);
|
||||
append_indices(entity, 5, 1, 3);
|
||||
append_indices(entity, 1, 2, 3);
|
||||
|
||||
// bottom face vertices
|
||||
append_vertex(entity, { half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitZ());
|
||||
append_vertex(entity, { half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitZ());
|
||||
append_vertex(entity, { half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitZ());
|
||||
append_vertex(entity, { 0.0, total_height, -half_thickness }, -Vec3f::UnitZ());
|
||||
append_vertex(entity, { -half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitZ());
|
||||
append_vertex(entity, { -half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitZ());
|
||||
append_vertex(entity, { -half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitZ());
|
||||
|
||||
// bottom face triangles
|
||||
append_indices(entity, 7, 13, 8);
|
||||
append_indices(entity, 13, 12, 8);
|
||||
append_indices(entity, 12, 11, 10);
|
||||
append_indices(entity, 8, 12, 10);
|
||||
append_indices(entity, 9, 8, 10);
|
||||
|
||||
// side faces vertices
|
||||
append_vertex(entity, { half_stem_width, 0.0, -half_thickness }, Vec3f::UnitX());
|
||||
append_vertex(entity, { half_stem_width, stem_height, -half_thickness }, Vec3f::UnitX());
|
||||
append_vertex(entity, { half_stem_width, 0.0, half_thickness }, Vec3f::UnitX());
|
||||
append_vertex(entity, { half_stem_width, stem_height, half_thickness }, Vec3f::UnitX());
|
||||
|
||||
append_vertex(entity, { half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitY());
|
||||
append_vertex(entity, { half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitY());
|
||||
append_vertex(entity, { half_stem_width, stem_height, half_thickness }, -Vec3f::UnitY());
|
||||
append_vertex(entity, { half_tip_width, stem_height, half_thickness }, -Vec3f::UnitY());
|
||||
|
||||
Vec3f normal(tip_height, half_tip_width, 0.0f);
|
||||
normal.normalize();
|
||||
append_vertex(entity, { half_tip_width, stem_height, -half_thickness }, normal);
|
||||
append_vertex(entity, { 0.0, total_height, -half_thickness }, normal);
|
||||
append_vertex(entity, { half_tip_width, stem_height, half_thickness }, normal);
|
||||
append_vertex(entity, { 0.0, total_height, half_thickness }, normal);
|
||||
|
||||
normal = Vec3f(-tip_height, half_tip_width, 0.0f);
|
||||
normal.normalize();
|
||||
append_vertex(entity, { 0.0, total_height, -half_thickness }, normal);
|
||||
append_vertex(entity, { -half_tip_width, stem_height, -half_thickness }, normal);
|
||||
append_vertex(entity, { 0.0, total_height, half_thickness }, normal);
|
||||
append_vertex(entity, { -half_tip_width, stem_height, half_thickness }, normal);
|
||||
|
||||
append_vertex(entity, { -half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitY());
|
||||
append_vertex(entity, { -half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitY());
|
||||
append_vertex(entity, { -half_tip_width, stem_height, half_thickness }, -Vec3f::UnitY());
|
||||
append_vertex(entity, { -half_stem_width, stem_height, half_thickness }, -Vec3f::UnitY());
|
||||
|
||||
append_vertex(entity, { -half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitX());
|
||||
append_vertex(entity, { -half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitX());
|
||||
append_vertex(entity, { -half_stem_width, stem_height, half_thickness }, -Vec3f::UnitX());
|
||||
append_vertex(entity, { -half_stem_width, 0.0, half_thickness }, -Vec3f::UnitX());
|
||||
|
||||
append_vertex(entity, { -half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitY());
|
||||
append_vertex(entity, { half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitY());
|
||||
append_vertex(entity, { -half_stem_width, 0.0, half_thickness }, -Vec3f::UnitY());
|
||||
append_vertex(entity, { half_stem_width, 0.0, half_thickness }, -Vec3f::UnitY());
|
||||
|
||||
// side face triangles
|
||||
for (int i = 0; i < 7; ++i) {
|
||||
const int ii = i * 4;
|
||||
append_indices(entity, 14 + ii, 15 + ii, 17 + ii);
|
||||
append_indices(entity, 14 + ii, 17 + ii, 16 + ii);
|
||||
}
|
||||
|
||||
data.entities.emplace_back(entity);
|
||||
return data;
|
||||
}
|
||||
|
||||
GLModel::InitializationData diamond(int resolution)
|
||||
{
|
||||
resolution = std::max(4, resolution);
|
||||
|
||||
GLModel::InitializationData data;
|
||||
GLModel::InitializationData::Entity entity;
|
||||
entity.type = GLModel::PrimitiveType::Triangles;
|
||||
|
||||
const float step = 2.0f * float(PI) / float(resolution);
|
||||
|
||||
// positions
|
||||
for (int i = 0; i < resolution; ++i) {
|
||||
float ii = float(i) * step;
|
||||
entity.positions.emplace_back(0.5f * ::cos(ii), 0.5f * ::sin(ii), 0.0f);
|
||||
}
|
||||
entity.positions.emplace_back(0.0f, 0.0f, 0.5f);
|
||||
entity.positions.emplace_back(0.0f, 0.0f, -0.5f);
|
||||
|
||||
// normals
|
||||
for (const Vec3f& v : entity.positions) {
|
||||
entity.normals.emplace_back(v.normalized());
|
||||
}
|
||||
|
||||
// triangles
|
||||
// top
|
||||
for (int i = 0; i < resolution; ++i) {
|
||||
entity.indices.push_back(i + 0);
|
||||
entity.indices.push_back(i + 1);
|
||||
entity.indices.push_back(resolution);
|
||||
}
|
||||
entity.indices.push_back(resolution - 1);
|
||||
entity.indices.push_back(0);
|
||||
entity.indices.push_back(resolution);
|
||||
|
||||
// bottom
|
||||
for (int i = 0; i < resolution; ++i) {
|
||||
entity.indices.push_back(i + 0);
|
||||
entity.indices.push_back(resolution + 1);
|
||||
entity.indices.push_back(i + 1);
|
||||
}
|
||||
entity.indices.push_back(resolution - 1);
|
||||
entity.indices.push_back(resolution + 1);
|
||||
entity.indices.push_back(0);
|
||||
|
||||
data.entities.emplace_back(entity);
|
||||
return data;
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
119
src/slic3r/GUI/GLModel.hpp
Normal file
119
src/slic3r/GUI/GLModel.hpp
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
#ifndef slic3r_GLModel_hpp_
|
||||
#define slic3r_GLModel_hpp_
|
||||
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
struct indexed_triangle_set;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class TriangleMesh;
|
||||
class Polygon;
|
||||
using Polygons = std::vector<Polygon>;
|
||||
|
||||
namespace GUI {
|
||||
|
||||
class GLModel
|
||||
{
|
||||
public:
|
||||
enum class PrimitiveType : unsigned char
|
||||
{
|
||||
Triangles,
|
||||
Lines,
|
||||
LineStrip,
|
||||
LineLoop
|
||||
};
|
||||
|
||||
struct RenderData
|
||||
{
|
||||
PrimitiveType type;
|
||||
unsigned int vbo_id{ 0 };
|
||||
unsigned int ibo_id{ 0 };
|
||||
size_t indices_count{ 0 };
|
||||
std::array<float, 4> color{ 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
};
|
||||
|
||||
struct InitializationData
|
||||
{
|
||||
struct Entity
|
||||
{
|
||||
PrimitiveType type;
|
||||
std::vector<Vec3f> positions;
|
||||
std::vector<Vec3f> normals;
|
||||
std::vector<unsigned int> indices;
|
||||
std::array<float, 4> color{ 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
};
|
||||
|
||||
std::vector<Entity> entities;
|
||||
|
||||
size_t vertices_count() const;
|
||||
size_t vertices_size_floats() const { return vertices_count() * 6; }
|
||||
size_t vertices_size_bytes() const { return vertices_size_floats() * sizeof(float); }
|
||||
|
||||
size_t indices_count() const;
|
||||
size_t indices_size_bytes() const { return indices_count() * sizeof(unsigned int); }
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<RenderData> m_render_data;
|
||||
|
||||
BoundingBoxf3 m_bounding_box;
|
||||
std::string m_filename;
|
||||
|
||||
public:
|
||||
GLModel() = default;
|
||||
virtual ~GLModel() { reset(); }
|
||||
|
||||
void init_from(const InitializationData& data);
|
||||
void init_from(const indexed_triangle_set& its, const BoundingBoxf3& bbox);
|
||||
void init_from(const indexed_triangle_set& its);
|
||||
void init_from(const Polygons& polygons, float z);
|
||||
bool init_from_file(const std::string& filename);
|
||||
|
||||
// if entity_id == -1 set the color of all entities
|
||||
void set_color(int entity_id, const std::array<float, 4>& color);
|
||||
|
||||
void reset();
|
||||
void render() const;
|
||||
void render_instanced(unsigned int instances_vbo, unsigned int instances_count) const;
|
||||
|
||||
bool is_initialized() const { return !m_render_data.empty(); }
|
||||
|
||||
const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; }
|
||||
const std::string& get_filename() const { return m_filename; }
|
||||
|
||||
private:
|
||||
void send_to_gpu(RenderData& data, const std::vector<float>& vertices, const std::vector<unsigned int>& indices);
|
||||
};
|
||||
|
||||
// create an arrow with cylindrical stem and conical tip, with the given dimensions and resolution
|
||||
// the origin of the arrow is in the center of the stem cap
|
||||
// the arrow has its axis of symmetry along the Z axis and is pointing upward
|
||||
// used to render bed axes and sequential marker
|
||||
GLModel::InitializationData stilized_arrow(int resolution, float tip_radius, float tip_height, float stem_radius, float stem_height);
|
||||
|
||||
// create an arrow whose stem is a quarter of circle, with the given dimensions and resolution
|
||||
// the origin of the arrow is in the center of the circle
|
||||
// the arrow is contained in the 1st quadrant of the XY plane and is pointing counterclockwise
|
||||
// used to render sidebar hints for rotations
|
||||
GLModel::InitializationData circular_arrow(int resolution, float radius, float tip_height, float tip_width, float stem_width, float thickness);
|
||||
|
||||
// create an arrow with the given dimensions
|
||||
// the origin of the arrow is in the center of the stem cap
|
||||
// the arrow is contained in XY plane and has its main axis along the Y axis
|
||||
// used to render sidebar hints for position and scale
|
||||
GLModel::InitializationData straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness);
|
||||
|
||||
// create a diamond with the given resolution
|
||||
// the origin of the diamond is in its center
|
||||
// the diamond is contained into a box with size [1, 1, 1]
|
||||
GLModel::InitializationData diamond(int resolution);
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_GLModel_hpp_
|
||||
|
||||
128
src/slic3r/GUI/GLSelectionRectangle.cpp
Normal file
128
src/slic3r/GUI/GLSelectionRectangle.cpp
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
#include "GLSelectionRectangle.hpp"
|
||||
#include "Camera.hpp"
|
||||
#include "3DScene.hpp"
|
||||
#include "GLCanvas3D.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include <igl/project.h>
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
void GLSelectionRectangle::start_dragging(const Vec2d& mouse_position, EState state)
|
||||
{
|
||||
if (is_dragging() || (state == Off))
|
||||
return;
|
||||
|
||||
m_state = state;
|
||||
m_start_corner = mouse_position;
|
||||
m_end_corner = mouse_position;
|
||||
}
|
||||
|
||||
void GLSelectionRectangle::dragging(const Vec2d& mouse_position)
|
||||
{
|
||||
if (!is_dragging())
|
||||
return;
|
||||
|
||||
m_end_corner = mouse_position;
|
||||
}
|
||||
|
||||
std::vector<unsigned int> GLSelectionRectangle::stop_dragging(const GLCanvas3D& canvas, const std::vector<Vec3d>& points)
|
||||
{
|
||||
std::vector<unsigned int> out;
|
||||
|
||||
if (!is_dragging())
|
||||
return out;
|
||||
|
||||
m_state = Off;
|
||||
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
Matrix4d modelview = camera.get_view_matrix().matrix();
|
||||
Matrix4d projection= camera.get_projection_matrix().matrix();
|
||||
Vec4i viewport(camera.get_viewport().data());
|
||||
|
||||
// Convert our std::vector to Eigen dynamic matrix.
|
||||
Eigen::Matrix<double, Eigen::Dynamic, 3, Eigen::DontAlign> pts(points.size(), 3);
|
||||
for (size_t i=0; i<points.size(); ++i)
|
||||
pts.block<1, 3>(i, 0) = points[i];
|
||||
|
||||
// Get the projections.
|
||||
Eigen::Matrix<double, Eigen::Dynamic, 3, Eigen::DontAlign> projections;
|
||||
igl::project(pts, modelview, projection, viewport, projections);
|
||||
|
||||
// bounding box created from the rectangle corners - will take care of order of the corners
|
||||
BoundingBox rectangle(Points{ Point(m_start_corner.cast<coord_t>()), Point(m_end_corner.cast<coord_t>()) });
|
||||
|
||||
// Iterate over all points and determine whether they're in the rectangle.
|
||||
for (int i = 0; i<projections.rows(); ++i)
|
||||
if (rectangle.contains(Point(projections(i, 0), canvas.get_canvas_size().get_height() - projections(i, 1))))
|
||||
out.push_back(i);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void GLSelectionRectangle::stop_dragging()
|
||||
{
|
||||
if (is_dragging())
|
||||
m_state = Off;
|
||||
}
|
||||
|
||||
void GLSelectionRectangle::render(const GLCanvas3D& canvas) const
|
||||
{
|
||||
if (!is_dragging())
|
||||
return;
|
||||
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
float inv_zoom = (float)camera.get_inv_zoom();
|
||||
|
||||
Size cnv_size = canvas.get_canvas_size();
|
||||
float cnv_half_width = 0.5f * (float)cnv_size.get_width();
|
||||
float cnv_half_height = 0.5f * (float)cnv_size.get_height();
|
||||
if ((cnv_half_width == 0.0f) || (cnv_half_height == 0.0f))
|
||||
return;
|
||||
|
||||
Vec2d start(m_start_corner(0) - cnv_half_width, cnv_half_height - m_start_corner(1));
|
||||
Vec2d end(m_end_corner(0) - cnv_half_width, cnv_half_height - m_end_corner(1));
|
||||
|
||||
float left = (float)std::min(start(0), end(0)) * inv_zoom;
|
||||
float top = (float)std::max(start(1), end(1)) * inv_zoom;
|
||||
float right = (float)std::max(start(0), end(0)) * inv_zoom;
|
||||
float bottom = (float)std::min(start(1), end(1)) * inv_zoom;
|
||||
|
||||
glsafe(::glLineWidth(1.5f));
|
||||
float color[3];
|
||||
color[0] = 0.00f;
|
||||
color[1] = 1.00f;
|
||||
color[2] = 0.38f;
|
||||
glsafe(::glColor3fv(color));
|
||||
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glLoadIdentity());
|
||||
// ensure that the rectangle is renderered inside the frustrum
|
||||
glsafe(::glTranslated(0.0, 0.0, -(camera.get_near_z() + 0.5)));
|
||||
// ensure that the overlay fits the frustrum near z plane
|
||||
double gui_scale = camera.get_gui_scale();
|
||||
glsafe(::glScaled(gui_scale, gui_scale, 1.0));
|
||||
|
||||
glsafe(::glPushAttrib(GL_ENABLE_BIT));
|
||||
glsafe(::glLineStipple(4, 0xAAAA));
|
||||
glsafe(::glEnable(GL_LINE_STIPPLE));
|
||||
|
||||
::glBegin(GL_LINE_LOOP);
|
||||
::glVertex2f((GLfloat)left, (GLfloat)bottom);
|
||||
::glVertex2f((GLfloat)right, (GLfloat)bottom);
|
||||
::glVertex2f((GLfloat)right, (GLfloat)top);
|
||||
::glVertex2f((GLfloat)left, (GLfloat)top);
|
||||
glsafe(::glEnd());
|
||||
|
||||
glsafe(::glPopAttrib());
|
||||
|
||||
glsafe(::glPopMatrix());
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
56
src/slic3r/GUI/GLSelectionRectangle.hpp
Normal file
56
src/slic3r/GUI/GLSelectionRectangle.hpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#ifndef slic3r_GLSelectionRectangle_hpp_
|
||||
#define slic3r_GLSelectionRectangle_hpp_
|
||||
|
||||
#include "libslic3r/Point.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
struct Camera;
|
||||
class GLCanvas3D;
|
||||
|
||||
class GLSelectionRectangle {
|
||||
public:
|
||||
enum EState {
|
||||
Off,
|
||||
Select,
|
||||
Deselect
|
||||
};
|
||||
|
||||
// Initiates the rectangle.
|
||||
void start_dragging(const Vec2d& mouse_position, EState state);
|
||||
|
||||
// To be called on mouse move.
|
||||
void dragging(const Vec2d& mouse_position);
|
||||
|
||||
// Given a vector of points in world coordinates, the function returns indices of those
|
||||
// that are in the rectangle. It then disables the rectangle.
|
||||
std::vector<unsigned int> stop_dragging(const GLCanvas3D& canvas, const std::vector<Vec3d>& points);
|
||||
|
||||
// Disables the rectangle.
|
||||
void stop_dragging();
|
||||
|
||||
void render(const GLCanvas3D& canvas) const;
|
||||
|
||||
bool is_dragging() const { return m_state != Off; }
|
||||
EState get_state() const { return m_state; }
|
||||
|
||||
float get_width() const { return std::abs(m_start_corner(0) - m_end_corner(0)); }
|
||||
float get_height() const { return std::abs(m_start_corner(1) - m_end_corner(1)); }
|
||||
float get_left() const { return std::min(m_start_corner(0), m_end_corner(0)); }
|
||||
float get_right() const { return std::max(m_start_corner(0), m_end_corner(0)); }
|
||||
float get_top() const { return std::max(m_start_corner(1), m_end_corner(1)); }
|
||||
float get_bottom() const { return std::min(m_start_corner(1), m_end_corner(1)); }
|
||||
|
||||
private:
|
||||
EState m_state = Off;
|
||||
Vec2d m_start_corner;
|
||||
Vec2d m_end_corner;
|
||||
};
|
||||
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
|
||||
#endif // slic3r_GLGizmoSlaSupports_hpp_
|
||||
395
src/slic3r/GUI/GLShader.cpp
Normal file
395
src/slic3r/GUI/GLShader.cpp
Normal file
|
|
@ -0,0 +1,395 @@
|
|||
#include "libslic3r/libslic3r.h"
|
||||
#include "GLShader.hpp"
|
||||
|
||||
#include "3DScene.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/format.hpp"
|
||||
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include <GL/glew.h>
|
||||
#include <cassert>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
GLShaderProgram::~GLShaderProgram()
|
||||
{
|
||||
if (m_id > 0)
|
||||
glsafe(::glDeleteProgram(m_id));
|
||||
}
|
||||
|
||||
bool GLShaderProgram::init_from_files(const std::string& name, const ShaderFilenames& filenames, const std::initializer_list<std::string_view> &defines)
|
||||
{
|
||||
// Load a shader program from file, prepend defs block.
|
||||
auto load_from_file = [](const std::string& filename, const std::string &defs) {
|
||||
std::string path = resources_dir() + "/shaders/" + filename;
|
||||
boost::nowide::ifstream s(path, boost::nowide::ifstream::binary);
|
||||
if (!s.good()) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Couldn't open file: '" << path << "'";
|
||||
return std::string();
|
||||
}
|
||||
|
||||
s.seekg(0, s.end);
|
||||
int file_length = static_cast<int>(s.tellg());
|
||||
s.seekg(0, s.beg);
|
||||
std::string source(defs.size() + file_length, '\0');
|
||||
memcpy(source.data(), defs.c_str(), defs.size());
|
||||
s.read(source.data() + defs.size(), file_length);
|
||||
if (!s.good()) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Error while loading file: '" << path << "'";
|
||||
return std::string();
|
||||
}
|
||||
s.close();
|
||||
|
||||
if (! defs.empty()) {
|
||||
// Extract the version and flip the order of "defines" and version in the source block.
|
||||
size_t idx = source.find("\n", defs.size());
|
||||
if (idx != std::string::npos && strncmp(source.c_str() + defs.size(), "#version", 8) == 0) {
|
||||
// Swap the version line with the defines.
|
||||
size_t len = idx - defs.size() + 1;
|
||||
memmove(source.data(), source.c_str() + defs.size(), len);
|
||||
memcpy(source.data() + len, defs.c_str(), defs.size());
|
||||
}
|
||||
}
|
||||
|
||||
return source;
|
||||
};
|
||||
|
||||
// Create a block of C "defines" from list of symbols.
|
||||
std::string defines_program;
|
||||
for (std::string_view def : defines)
|
||||
// Our shaders are stored with "\r\n", thus replicate the same here for consistency. Likely "\n" would suffice,
|
||||
// but we don't know all the OpenGL shader compilers around.
|
||||
defines_program += format("#define %s\r\n", def);
|
||||
|
||||
ShaderSources sources = {};
|
||||
for (size_t i = 0; i < static_cast<size_t>(EShaderType::Count); ++i) {
|
||||
sources[i] = filenames[i].empty() ? std::string() : load_from_file(filenames[i], defines_program);
|
||||
}
|
||||
|
||||
bool valid = !sources[static_cast<size_t>(EShaderType::Vertex)].empty() && !sources[static_cast<size_t>(EShaderType::Fragment)].empty() && sources[static_cast<size_t>(EShaderType::Compute)].empty();
|
||||
valid |= !sources[static_cast<size_t>(EShaderType::Compute)].empty() && sources[static_cast<size_t>(EShaderType::Vertex)].empty() && sources[static_cast<size_t>(EShaderType::Fragment)].empty() &&
|
||||
sources[static_cast<size_t>(EShaderType::Geometry)].empty() && sources[static_cast<size_t>(EShaderType::TessEvaluation)].empty() && sources[static_cast<size_t>(EShaderType::TessControl)].empty();
|
||||
|
||||
return valid ? init_from_texts(name, sources) : false;
|
||||
}
|
||||
|
||||
bool GLShaderProgram::init_from_texts(const std::string& name, const ShaderSources& sources)
|
||||
{
|
||||
auto shader_type_as_string = [](EShaderType type) {
|
||||
switch (type)
|
||||
{
|
||||
case EShaderType::Vertex: { return "vertex"; }
|
||||
case EShaderType::Fragment: { return "fragment"; }
|
||||
case EShaderType::Geometry: { return "geometry"; }
|
||||
case EShaderType::TessEvaluation: { return "tesselation evaluation"; }
|
||||
case EShaderType::TessControl: { return "tesselation control"; }
|
||||
case EShaderType::Compute: { return "compute"; }
|
||||
default: { return "unknown"; }
|
||||
}
|
||||
};
|
||||
|
||||
auto create_shader = [](EShaderType type) {
|
||||
GLuint id = 0;
|
||||
switch (type)
|
||||
{
|
||||
case EShaderType::Vertex: { id = ::glCreateShader(GL_VERTEX_SHADER); glcheck(); break; }
|
||||
case EShaderType::Fragment: { id = ::glCreateShader(GL_FRAGMENT_SHADER); glcheck(); break; }
|
||||
case EShaderType::Geometry: { id = ::glCreateShader(GL_GEOMETRY_SHADER); glcheck(); break; }
|
||||
case EShaderType::TessEvaluation: { id = ::glCreateShader(GL_TESS_EVALUATION_SHADER); glcheck(); break; }
|
||||
case EShaderType::TessControl: { id = ::glCreateShader(GL_TESS_CONTROL_SHADER); glcheck(); break; }
|
||||
case EShaderType::Compute: { id = ::glCreateShader(GL_COMPUTE_SHADER); glcheck(); break; }
|
||||
default: { break; }
|
||||
}
|
||||
|
||||
return (id == 0) ? std::make_pair(false, GLuint(0)) : std::make_pair(true, id);
|
||||
};
|
||||
|
||||
auto release_shaders = [](const std::array<GLuint, static_cast<size_t>(EShaderType::Count)>& shader_ids) {
|
||||
for (size_t i = 0; i < static_cast<size_t>(EShaderType::Count); ++i) {
|
||||
if (shader_ids[i] > 0)
|
||||
glsafe(::glDeleteShader(shader_ids[i]));
|
||||
}
|
||||
};
|
||||
|
||||
assert(m_id == 0);
|
||||
|
||||
m_name = name;
|
||||
|
||||
std::array<GLuint, static_cast<size_t>(EShaderType::Count)> shader_ids = { 0 };
|
||||
|
||||
for (size_t i = 0; i < static_cast<size_t>(EShaderType::Count); ++i) {
|
||||
const std::string& source = sources[i];
|
||||
if (!source.empty())
|
||||
{
|
||||
EShaderType type = static_cast<EShaderType>(i);
|
||||
auto [result, id] = create_shader(type);
|
||||
if (result)
|
||||
shader_ids[i] = id;
|
||||
else {
|
||||
BOOST_LOG_TRIVIAL(error) << "glCreateShader() failed for " << shader_type_as_string(type) << " shader of shader program '" << name << "'";
|
||||
|
||||
// release shaders
|
||||
release_shaders(shader_ids);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* source_ptr = source.c_str();
|
||||
glsafe(::glShaderSource(id, 1, &source_ptr, nullptr));
|
||||
glsafe(::glCompileShader(id));
|
||||
GLint params;
|
||||
glsafe(::glGetShaderiv(id, GL_COMPILE_STATUS, ¶ms));
|
||||
if (params == GL_FALSE) {
|
||||
// Compilation failed.
|
||||
glsafe(::glGetShaderiv(id, GL_INFO_LOG_LENGTH, ¶ms));
|
||||
std::vector<char> msg(params);
|
||||
glsafe(::glGetShaderInfoLog(id, params, ¶ms, msg.data()));
|
||||
BOOST_LOG_TRIVIAL(error) << "Unable to compile " << shader_type_as_string(type) << " shader of shader program '" << name << "':\n" << msg.data();
|
||||
|
||||
// release shaders
|
||||
release_shaders(shader_ids);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_id = ::glCreateProgram();
|
||||
glcheck();
|
||||
if (m_id == 0) {
|
||||
BOOST_LOG_TRIVIAL(error) << "glCreateProgram() failed for shader program '" << name << "'";
|
||||
|
||||
// release shaders
|
||||
release_shaders(shader_ids);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < static_cast<size_t>(EShaderType::Count); ++i) {
|
||||
if (shader_ids[i] > 0)
|
||||
glsafe(::glAttachShader(m_id, shader_ids[i]));
|
||||
}
|
||||
|
||||
glsafe(::glLinkProgram(m_id));
|
||||
GLint params;
|
||||
glsafe(::glGetProgramiv(m_id, GL_LINK_STATUS, ¶ms));
|
||||
if (params == GL_FALSE) {
|
||||
// Linking failed.
|
||||
glsafe(::glGetProgramiv(m_id, GL_INFO_LOG_LENGTH, ¶ms));
|
||||
std::vector<char> msg(params);
|
||||
glsafe(::glGetProgramInfoLog(m_id, params, ¶ms, msg.data()));
|
||||
BOOST_LOG_TRIVIAL(error) << "Unable to link shader program '" << name << "':\n" << msg.data();
|
||||
|
||||
// release shaders
|
||||
release_shaders(shader_ids);
|
||||
|
||||
// release shader program
|
||||
glsafe(::glDeleteProgram(m_id));
|
||||
m_id = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// release shaders, they are no more needed
|
||||
release_shaders(shader_ids);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLShaderProgram::start_using() const
|
||||
{
|
||||
assert(m_id > 0);
|
||||
glsafe(::glUseProgram(m_id));
|
||||
}
|
||||
|
||||
void GLShaderProgram::stop_using() const
|
||||
{
|
||||
glsafe(::glUseProgram(0));
|
||||
}
|
||||
|
||||
bool GLShaderProgram::set_uniform(const char* name, int value) const
|
||||
{
|
||||
int id = get_uniform_location(name);
|
||||
if (id >= 0) {
|
||||
glsafe(::glUniform1i(id, static_cast<GLint>(value)));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLShaderProgram::set_uniform(const char* name, bool value) const
|
||||
{
|
||||
return set_uniform(name, value ? 1 : 0);
|
||||
}
|
||||
|
||||
bool GLShaderProgram::set_uniform(const char* name, float value) const
|
||||
{
|
||||
int id = get_uniform_location(name);
|
||||
if (id >= 0) {
|
||||
glsafe(::glUniform1f(id, static_cast<GLfloat>(value)));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLShaderProgram::set_uniform(const char* name, double value) const
|
||||
{
|
||||
return set_uniform(name, static_cast<float>(value));
|
||||
}
|
||||
|
||||
bool GLShaderProgram::set_uniform(const char* name, const std::array<int, 2>& value) const
|
||||
{
|
||||
int id = get_uniform_location(name);
|
||||
if (id >= 0) {
|
||||
glsafe(::glUniform2iv(id, 1, static_cast<const GLint*>(value.data())));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLShaderProgram::set_uniform(const char* name, const std::array<int, 3>& value) const
|
||||
{
|
||||
int id = get_uniform_location(name);
|
||||
if (id >= 0) {
|
||||
glsafe(::glUniform3iv(id, 1, static_cast<const GLint*>(value.data())));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLShaderProgram::set_uniform(const char* name, const std::array<int, 4>& value) const
|
||||
{
|
||||
int id = get_uniform_location(name);
|
||||
if (id >= 0) {
|
||||
glsafe(::glUniform4iv(id, 1, static_cast<const GLint*>(value.data())));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLShaderProgram::set_uniform(const char* name, const std::array<float, 2>& value) const
|
||||
{
|
||||
int id = get_uniform_location(name);
|
||||
if (id >= 0) {
|
||||
glsafe(::glUniform2fv(id, 1, static_cast<const GLfloat*>(value.data())));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLShaderProgram::set_uniform(const char* name, const std::array<float, 3>& value) const
|
||||
{
|
||||
int id = get_uniform_location(name);
|
||||
if (id >= 0) {
|
||||
glsafe(::glUniform3fv(id, 1, static_cast<const GLfloat*>(value.data())));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLShaderProgram::set_uniform(const char* name, const std::array<float, 4>& value) const
|
||||
{
|
||||
int id = get_uniform_location(name);
|
||||
if (id >= 0) {
|
||||
glsafe(::glUniform4fv(id, 1, static_cast<const GLfloat*>(value.data())));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLShaderProgram::set_uniform(const char* name, const float* value, size_t size) const
|
||||
{
|
||||
if (size == 1)
|
||||
return set_uniform(name, value[0]);
|
||||
else if (size < 5) {
|
||||
int id = get_uniform_location(name);
|
||||
if (id >= 0) {
|
||||
if (size == 2)
|
||||
glsafe(::glUniform2fv(id, 1, static_cast<const GLfloat*>(value)));
|
||||
else if (size == 3)
|
||||
glsafe(::glUniform3fv(id, 1, static_cast<const GLfloat*>(value)));
|
||||
else
|
||||
glsafe(::glUniform4fv(id, 1, static_cast<const GLfloat*>(value)));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLShaderProgram::set_uniform(const char* name, const Transform3f& value) const
|
||||
{
|
||||
int id = get_uniform_location(name);
|
||||
if (id >= 0) {
|
||||
glsafe(::glUniformMatrix4fv(id, 1, GL_FALSE, static_cast<const GLfloat*>(value.matrix().data())));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLShaderProgram::set_uniform(const char* name, const Transform3d& value) const
|
||||
{
|
||||
return set_uniform(name, value.cast<float>());
|
||||
}
|
||||
|
||||
bool GLShaderProgram::set_uniform(const char* name, const Matrix3f& value) const
|
||||
{
|
||||
int id = get_uniform_location(name);
|
||||
if (id >= 0) {
|
||||
glsafe(::glUniformMatrix3fv(id, 1, GL_FALSE, static_cast<const GLfloat*>(value.data())));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLShaderProgram::set_uniform(const char* name, const Vec3f& value) const
|
||||
{
|
||||
int id = get_uniform_location(name);
|
||||
if (id >= 0) {
|
||||
glsafe(::glUniform3fv(id, 1, static_cast<const GLfloat*>(value.data())));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLShaderProgram::set_uniform(const char* name, const Vec3d& value) const
|
||||
{
|
||||
return set_uniform(name, static_cast<Vec3f>(value.cast<float>()));
|
||||
}
|
||||
|
||||
int GLShaderProgram::get_attrib_location(const char* name) const
|
||||
{
|
||||
assert(m_id > 0);
|
||||
|
||||
if (m_id <= 0)
|
||||
// Shader program not loaded. This should not happen.
|
||||
return -1;
|
||||
|
||||
auto it = std::find_if(m_attrib_location_cache.begin(), m_attrib_location_cache.end(), [name](const auto& p) { return p.first == name; });
|
||||
if (it != m_attrib_location_cache.end())
|
||||
// Attrib ID cached.
|
||||
return it->second;
|
||||
|
||||
int id = ::glGetAttribLocation(m_id, name);
|
||||
const_cast<GLShaderProgram*>(this)->m_attrib_location_cache.push_back({ name, id });
|
||||
return id;
|
||||
}
|
||||
|
||||
int GLShaderProgram::get_uniform_location(const char* name) const
|
||||
{
|
||||
assert(m_id > 0);
|
||||
|
||||
if (m_id <= 0)
|
||||
// Shader program not loaded. This should not happen.
|
||||
return -1;
|
||||
|
||||
auto it = std::find_if(m_uniform_location_cache.begin(), m_uniform_location_cache.end(), [name](const auto &p) { return p.first == name; });
|
||||
if (it != m_uniform_location_cache.end())
|
||||
// Uniform ID cached.
|
||||
return it->second;
|
||||
|
||||
int id = ::glGetUniformLocation(m_id, name);
|
||||
const_cast<GLShaderProgram*>(this)->m_uniform_location_cache.push_back({ name, id });
|
||||
return id;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
72
src/slic3r/GUI/GLShader.hpp
Normal file
72
src/slic3r/GUI/GLShader.hpp
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#ifndef slic3r_GLShader_hpp_
|
||||
#define slic3r_GLShader_hpp_
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "libslic3r/Point.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class GLShaderProgram
|
||||
{
|
||||
public:
|
||||
enum class EShaderType
|
||||
{
|
||||
Vertex,
|
||||
Fragment,
|
||||
Geometry,
|
||||
TessEvaluation,
|
||||
TessControl,
|
||||
Compute,
|
||||
Count
|
||||
};
|
||||
|
||||
typedef std::array<std::string, static_cast<size_t>(EShaderType::Count)> ShaderFilenames;
|
||||
typedef std::array<std::string, static_cast<size_t>(EShaderType::Count)> ShaderSources;
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
unsigned int m_id{ 0 };
|
||||
std::vector<std::pair<std::string, int>> m_attrib_location_cache;
|
||||
std::vector<std::pair<std::string, int>> m_uniform_location_cache;
|
||||
|
||||
public:
|
||||
~GLShaderProgram();
|
||||
|
||||
bool init_from_files(const std::string& name, const ShaderFilenames& filenames, const std::initializer_list<std::string_view> &defines = {});
|
||||
bool init_from_texts(const std::string& name, const ShaderSources& sources);
|
||||
|
||||
const std::string& get_name() const { return m_name; }
|
||||
unsigned int get_id() const { return m_id; }
|
||||
|
||||
void start_using() const;
|
||||
void stop_using() const;
|
||||
|
||||
bool set_uniform(const char* name, int value) const;
|
||||
bool set_uniform(const char* name, bool value) const;
|
||||
bool set_uniform(const char* name, float value) const;
|
||||
bool set_uniform(const char* name, double value) const;
|
||||
bool set_uniform(const char* name, const std::array<int, 2>& value) const;
|
||||
bool set_uniform(const char* name, const std::array<int, 3>& value) const;
|
||||
bool set_uniform(const char* name, const std::array<int, 4>& value) const;
|
||||
bool set_uniform(const char* name, const std::array<float, 2>& value) const;
|
||||
bool set_uniform(const char* name, const std::array<float, 3>& value) const;
|
||||
bool set_uniform(const char* name, const std::array<float, 4>& value) const;
|
||||
bool set_uniform(const char* name, const float* value, size_t size) const;
|
||||
bool set_uniform(const char* name, const Transform3f& value) const;
|
||||
bool set_uniform(const char* name, const Transform3d& value) const;
|
||||
bool set_uniform(const char* name, const Matrix3f& value) const;
|
||||
bool set_uniform(const char* name, const Vec3f& value) const;
|
||||
bool set_uniform(const char* name, const Vec3d& value) const;
|
||||
|
||||
// returns -1 if not found
|
||||
int get_attrib_location(const char* name) const;
|
||||
// returns -1 if not found
|
||||
int get_uniform_location(const char* name) const;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_GLShader_hpp_ */
|
||||
97
src/slic3r/GUI/GLShadersManager.cpp
Normal file
97
src/slic3r/GUI/GLShadersManager.cpp
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Platform.hpp"
|
||||
#include "GLShadersManager.hpp"
|
||||
#include "3DScene.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <string_view>
|
||||
using namespace std::literals;
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
std::pair<bool, std::string> GLShadersManager::init()
|
||||
{
|
||||
std::string error;
|
||||
|
||||
auto append_shader = [this, &error](const std::string& name, const GLShaderProgram::ShaderFilenames& filenames,
|
||||
const std::initializer_list<std::string_view> &defines = {}) {
|
||||
m_shaders.push_back(std::make_unique<GLShaderProgram>());
|
||||
if (!m_shaders.back()->init_from_files(name, filenames, defines)) {
|
||||
error += name + "\n";
|
||||
// if any error happens while initializating the shader, we remove it from the list
|
||||
m_shaders.pop_back();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
assert(m_shaders.empty());
|
||||
|
||||
bool valid = true;
|
||||
|
||||
// used to render bed axes and model, selection hints, gcode sequential view marker model, preview shells, options in gcode preview
|
||||
valid &= append_shader("gouraud_light", { "gouraud_light.vs", "gouraud_light.fs" });
|
||||
// used to render first layer for calibration
|
||||
valid &= append_shader("cali", { "cali.vs", "cali.fs"});
|
||||
// used to render printbed
|
||||
valid &= append_shader("printbed", { "printbed.vs", "printbed.fs" });
|
||||
// used to render options in gcode preview
|
||||
if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 3))
|
||||
valid &= append_shader("gouraud_light_instanced", { "gouraud_light_instanced.vs", "gouraud_light_instanced.fs" });
|
||||
// used to render extrusion and travel paths as lines in gcode preview
|
||||
valid &= append_shader("toolpaths_lines", { "toolpaths_lines.vs", "toolpaths_lines.fs" });
|
||||
// used to render objects in 3d editor
|
||||
valid &= append_shader("gouraud", { "gouraud.vs", "gouraud.fs" }
|
||||
#if ENABLE_ENVIRONMENT_MAP
|
||||
, { "ENABLE_ENVIRONMENT_MAP"sv }
|
||||
#endif // ENABLE_ENVIRONMENT_MAP
|
||||
);
|
||||
// used to render variable layers heights in 3d editor
|
||||
valid &= append_shader("variable_layer_height", { "variable_layer_height.vs", "variable_layer_height.fs" });
|
||||
// used to render highlight contour around selected triangles inside the multi-material gizmo
|
||||
valid &= append_shader("mm_contour", { "mm_contour.vs", "mm_contour.fs" });
|
||||
// Used to render painted triangles inside the multi-material gizmo. Triangle normals are computed inside fragment shader.
|
||||
// For Apple's on Arm CPU computed triangle normals inside fragment shader using dFdx and dFdy has the opposite direction.
|
||||
// Because of this, objects had darker colors inside the multi-material gizmo.
|
||||
// Based on https://stackoverflow.com/a/66206648, the similar behavior was also spotted on some other devices with Arm CPU.
|
||||
// Since macOS 12 (Monterey), this issue with the opposite direction on Apple's Arm CPU seems to be fixed, and computed
|
||||
// triangle normals inside fragment shader have the right direction.
|
||||
if (platform_flavor() == PlatformFlavor::OSXOnArm && wxPlatformInfo::Get().GetOSMajorVersion() < 12)
|
||||
valid &= append_shader("mm_gouraud", {"mm_gouraud.vs", "mm_gouraud.fs"}, {"FLIP_TRIANGLE_NORMALS"sv});
|
||||
else
|
||||
valid &= append_shader("mm_gouraud", {"mm_gouraud.vs", "mm_gouraud.fs"});
|
||||
|
||||
//BBS: add shader for outline
|
||||
valid &= append_shader("outline", { "outline.vs", "outline.fs" });
|
||||
|
||||
return { valid, error };
|
||||
}
|
||||
|
||||
void GLShadersManager::shutdown()
|
||||
{
|
||||
m_shaders.clear();
|
||||
}
|
||||
|
||||
GLShaderProgram* GLShadersManager::get_shader(const std::string& shader_name)
|
||||
{
|
||||
auto it = std::find_if(m_shaders.begin(), m_shaders.end(), [&shader_name](std::unique_ptr<GLShaderProgram>& p) { return p->get_name() == shader_name; });
|
||||
return (it != m_shaders.end()) ? it->get() : nullptr;
|
||||
}
|
||||
|
||||
GLShaderProgram* GLShadersManager::get_current_shader()
|
||||
{
|
||||
GLint id = 0;
|
||||
glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, &id));
|
||||
if (id == 0)
|
||||
return nullptr;
|
||||
|
||||
auto it = std::find_if(m_shaders.begin(), m_shaders.end(), [id](std::unique_ptr<GLShaderProgram>& p) { return static_cast<GLint>(p->get_id()) == id; });
|
||||
return (it != m_shaders.end()) ? it->get() : nullptr;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
30
src/slic3r/GUI/GLShadersManager.hpp
Normal file
30
src/slic3r/GUI/GLShadersManager.hpp
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef slic3r_GLShadersManager_hpp_
|
||||
#define slic3r_GLShadersManager_hpp_
|
||||
|
||||
#include "GLShader.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class GLShadersManager
|
||||
{
|
||||
std::vector<std::unique_ptr<GLShaderProgram>> m_shaders;
|
||||
|
||||
public:
|
||||
std::pair<bool, std::string> init();
|
||||
// call this method before to release the OpenGL context
|
||||
void shutdown();
|
||||
|
||||
// returns nullptr if not found
|
||||
GLShaderProgram* get_shader(const std::string& shader_name);
|
||||
|
||||
// returns currently active shader, nullptr if none
|
||||
GLShaderProgram* get_current_shader();
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_GLShadersManager_hpp_
|
||||
813
src/slic3r/GUI/GLTexture.cpp
Normal file
813
src/slic3r/GUI/GLTexture.cpp
Normal file
|
|
@ -0,0 +1,813 @@
|
|||
//BBS:add i18n
|
||||
#include "I18N.hpp"
|
||||
//BBS: add fstream for debug output
|
||||
//#include <fstream>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "GLTexture.hpp"
|
||||
|
||||
#include "3DScene.hpp"
|
||||
#include "OpenGLManager.hpp"
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include <wx/image.h>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
|
||||
#define STB_DXT_IMPLEMENTATION
|
||||
#include "stb_dxt/stb_dxt.h"
|
||||
|
||||
#include "nanosvg/nanosvg.h"
|
||||
#include "nanosvg/nanosvgrast.h"
|
||||
|
||||
#include "libslic3r/Utils.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
void GLTexture::Compressor::reset()
|
||||
{
|
||||
if (m_thread.joinable()) {
|
||||
m_abort_compressing = true;
|
||||
m_thread.join();
|
||||
m_levels.clear();
|
||||
m_num_levels_compressed = 0;
|
||||
m_abort_compressing = false;
|
||||
}
|
||||
assert(m_levels.empty());
|
||||
assert(m_abort_compressing == false);
|
||||
assert(m_num_levels_compressed == 0);
|
||||
}
|
||||
|
||||
void GLTexture::Compressor::start_compressing()
|
||||
{
|
||||
// The worker thread should be stopped already.
|
||||
assert(! m_thread.joinable());
|
||||
assert(! m_levels.empty());
|
||||
assert(m_abort_compressing == false);
|
||||
assert(m_num_levels_compressed == 0);
|
||||
if (! m_levels.empty()) {
|
||||
std::thread thrd(&GLTexture::Compressor::compress, this);
|
||||
m_thread = std::move(thrd);
|
||||
}
|
||||
}
|
||||
|
||||
bool GLTexture::Compressor::unsent_compressed_data_available() const
|
||||
{
|
||||
if (m_levels.empty())
|
||||
return false;
|
||||
// Querying the atomic m_num_levels_compressed value synchronizes processor caches, so that the data of m_levels modified by the worker thread are accessible to the calling thread.
|
||||
unsigned int num_compressed = m_num_levels_compressed;
|
||||
for (unsigned int i = 0; i < num_compressed; ++ i)
|
||||
if (! m_levels[i].sent_to_gpu && ! m_levels[i].compressed_data.empty())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void GLTexture::Compressor::send_compressed_data_to_gpu()
|
||||
{
|
||||
// this method should be called inside the main thread of Slicer or a new OpenGL context (sharing resources) would be needed
|
||||
if (m_levels.empty())
|
||||
return;
|
||||
|
||||
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_texture.m_id));
|
||||
// Querying the atomic m_num_levels_compressed value synchronizes processor caches, so that the dat of m_levels modified by the worker thread are accessible to the calling thread.
|
||||
int num_compressed = (int)m_num_levels_compressed;
|
||||
for (int i = 0; i < num_compressed; ++ i) {
|
||||
Level& level = m_levels[i];
|
||||
if (! level.sent_to_gpu && ! level.compressed_data.empty()) {
|
||||
glsafe(::glCompressedTexSubImage2D(GL_TEXTURE_2D, (GLint)i, 0, 0, (GLsizei)level.w, (GLsizei)level.h, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)level.compressed_data.size(), (const GLvoid*)level.compressed_data.data()));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, i));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (i > 0) ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR));
|
||||
level.sent_to_gpu = true;
|
||||
// we are done with the compressed data, we can discard it
|
||||
level.compressed_data.clear();
|
||||
}
|
||||
}
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
|
||||
if (num_compressed == (int)m_levels.size())
|
||||
// Finalize the worker thread, close it.
|
||||
this->reset();
|
||||
}
|
||||
|
||||
void GLTexture::Compressor::compress()
|
||||
{
|
||||
// reference: https://github.com/Cyan4973/RygsDXTc
|
||||
|
||||
assert(m_num_levels_compressed == 0);
|
||||
assert(m_abort_compressing == false);
|
||||
|
||||
for (Level& level : m_levels) {
|
||||
if (m_abort_compressing)
|
||||
break;
|
||||
|
||||
// stb_dxt library, despite claiming that the needed size of the destination buffer is equal to (source buffer size)/4,
|
||||
// crashes if doing so, requiring a minimum of 64 bytes and up to a third of the source buffer size, so we set the destination buffer initial size to be half the source buffer size
|
||||
level.compressed_data = std::vector<unsigned char>(std::max((unsigned int)64, (unsigned int)level.src_data.size() / 2), 0);
|
||||
int compressed_size = 0;
|
||||
rygCompress(level.compressed_data.data(), level.src_data.data(), level.w, level.h, 1, compressed_size);
|
||||
level.compressed_data.resize(compressed_size);
|
||||
|
||||
// we are done with the source data, we can discard it
|
||||
level.src_data.clear();
|
||||
++ m_num_levels_compressed;
|
||||
}
|
||||
}
|
||||
|
||||
GLTexture::Quad_UVs GLTexture::FullTextureUVs = { { 0.0f, 1.0f }, { 1.0f, 1.0f }, { 1.0f, 0.0f }, { 0.0f, 0.0f } };
|
||||
|
||||
GLTexture::GLTexture()
|
||||
: m_id(0)
|
||||
, m_width(0)
|
||||
, m_height(0)
|
||||
, m_source("")
|
||||
, m_compressor(*this)
|
||||
{
|
||||
}
|
||||
|
||||
GLTexture::~GLTexture()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy)
|
||||
{
|
||||
reset();
|
||||
|
||||
if (!boost::filesystem::exists(filename))
|
||||
return false;
|
||||
|
||||
if (boost::algorithm::iends_with(filename, ".png"))
|
||||
return load_from_png(filename, use_mipmaps, compression_type, apply_anisotropy);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLTexture::load_from_svg_file(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px)
|
||||
{
|
||||
reset();
|
||||
|
||||
if (!boost::filesystem::exists(filename))
|
||||
return false;
|
||||
|
||||
if (boost::algorithm::iends_with(filename, ".svg"))
|
||||
return load_from_svg(filename, use_mipmaps, compress, apply_anisotropy, max_size_px);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLTexture::load_from_raw_data(std::vector<unsigned char> data, unsigned int w, unsigned int h, bool apply_anisotropy)
|
||||
{
|
||||
m_width = w;
|
||||
m_height = h;
|
||||
int n_pixels = m_width * m_height;
|
||||
if (n_pixels <= 0) {
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
// sends data to gpu
|
||||
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||
glsafe(::glGenTextures(1, &m_id));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_id));
|
||||
|
||||
if (apply_anisotropy) {
|
||||
GLfloat max_anisotropy = OpenGLManager::get_gl_info().get_max_anisotropy();
|
||||
if (max_anisotropy > 1.0f)
|
||||
glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy));
|
||||
}
|
||||
|
||||
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
|
||||
bool use_mipmaps = true;
|
||||
if (use_mipmaps) {
|
||||
// we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
|
||||
int lod_w = m_width;
|
||||
int lod_h = m_height;
|
||||
GLint level = 0;
|
||||
while (lod_w > 1 || lod_h > 1) {
|
||||
++level;
|
||||
lod_w = std::max(lod_w / 2, 1);
|
||||
lod_h = std::max(lod_h / 2, 1);
|
||||
n_pixels = lod_w * lod_h;
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
}
|
||||
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
|
||||
}
|
||||
else {
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
|
||||
}
|
||||
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
|
||||
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
|
||||
#if 0
|
||||
// debug output
|
||||
static int pass = 0;
|
||||
++pass;
|
||||
|
||||
wxImage output(m_width, m_height);
|
||||
output.InitAlpha();
|
||||
|
||||
for (int h = 0; h < m_height; ++h) {
|
||||
int px_h = h * m_width;
|
||||
for (int w = 0; w < m_width; ++w) {
|
||||
int offset = (px_h + w) * 4;
|
||||
output.SetRGB(w, h, data.data()[offset + 0], data.data()[offset + 1], data.data()[offset + 2]);
|
||||
output.SetAlpha(w, h, data.data()[offset + 3]);
|
||||
}
|
||||
}
|
||||
|
||||
std::string out_filename = resources_dir() + "/images/test_" + std::to_string(pass) + ".png";
|
||||
output.SaveFile(out_filename, wxBITMAP_TYPE_PNG);
|
||||
#endif // 0
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::string>& filenames, const std::vector<std::pair<int, bool>>& states, unsigned int sprite_size_px, bool compress)
|
||||
{
|
||||
reset();
|
||||
|
||||
if (filenames.empty() || states.empty() || sprite_size_px == 0)
|
||||
return false;
|
||||
|
||||
// every tile needs to have a 1px border around it to avoid artifacts when linear sampling on its edges
|
||||
unsigned int sprite_size_px_ex = sprite_size_px + 1;
|
||||
|
||||
m_width = 1 + (int)(sprite_size_px_ex * states.size());
|
||||
m_height = 1 + (int)(sprite_size_px_ex * filenames.size());
|
||||
|
||||
int n_pixels = m_width * m_height;
|
||||
int sprite_n_pixels = sprite_size_px_ex * sprite_size_px_ex;
|
||||
int sprite_stride = sprite_size_px_ex * 4;
|
||||
int sprite_bytes = sprite_n_pixels * 4;
|
||||
|
||||
if (n_pixels <= 0) {
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> data(n_pixels * 4, 0);
|
||||
std::vector<unsigned char> sprite_data(sprite_bytes, 0);
|
||||
std::vector<unsigned char> sprite_white_only_data(sprite_bytes, 0);
|
||||
std::vector<unsigned char> sprite_gray_only_data(sprite_bytes, 0);
|
||||
std::vector<unsigned char> output_data(sprite_bytes, 0);
|
||||
|
||||
//BBS
|
||||
std::vector<unsigned char> pressed_data(sprite_bytes, 0);
|
||||
std::vector<unsigned char> disable_data(sprite_bytes, 0);
|
||||
std::vector<unsigned char> hover_data(sprite_bytes, 0);
|
||||
|
||||
NSVGrasterizer* rast = nsvgCreateRasterizer();
|
||||
if (rast == nullptr) {
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
int sprite_id = -1;
|
||||
for (const std::string& filename : filenames) {
|
||||
++sprite_id;
|
||||
|
||||
if (!boost::filesystem::exists(filename))
|
||||
continue;
|
||||
|
||||
if (!boost::algorithm::iends_with(filename, ".svg"))
|
||||
continue;
|
||||
|
||||
NSVGimage* image = nsvgParseFromFile(filename.c_str(), "px", 96.0f);
|
||||
if (image == nullptr)
|
||||
continue;
|
||||
|
||||
float scale = (float)sprite_size_px / std::max(image->width, image->height);
|
||||
|
||||
// offset by 1 to leave the first pixel empty (both in x and y)
|
||||
nsvgRasterize(rast, image, 1, 1, scale, sprite_data.data(), sprite_size_px, sprite_size_px, sprite_stride);
|
||||
|
||||
::memcpy((void*)pressed_data.data(), (const void*)sprite_data.data(), sprite_bytes);
|
||||
for (int i = 0; i < sprite_n_pixels; ++i) {
|
||||
int offset = i * 4;
|
||||
if (pressed_data.data()[offset] == 0) {
|
||||
::memset((void*)&pressed_data.data()[offset], 172, 3);
|
||||
pressed_data.data()[offset + 3] = (unsigned char)150;
|
||||
}
|
||||
}
|
||||
|
||||
::memcpy((void*)disable_data.data(), (const void*)sprite_data.data(), sprite_bytes);
|
||||
for (int i = 0; i < sprite_n_pixels; ++i) {
|
||||
int offset = i * 4;
|
||||
if (disable_data.data()[offset] != 0)
|
||||
::memset((void*)&disable_data.data()[offset], 200, 3);
|
||||
}
|
||||
|
||||
::memcpy((void*)hover_data.data(), (const void*)sprite_data.data(), sprite_bytes);
|
||||
for (int i = 0; i < sprite_n_pixels; ++i) {
|
||||
int offset = i * 4;
|
||||
if (hover_data.data()[offset] == 0) {
|
||||
::memset((void*)&hover_data.data()[offset], 172, 3);
|
||||
hover_data.data()[offset + 3] = (unsigned char)75;
|
||||
}
|
||||
}
|
||||
|
||||
::memcpy((void*)sprite_white_only_data.data(), (const void*)sprite_data.data(), sprite_bytes);
|
||||
|
||||
::memcpy((void*)sprite_gray_only_data.data(), (const void*)sprite_data.data(), sprite_bytes);
|
||||
for (int i = 0; i < sprite_n_pixels; ++i) {
|
||||
int offset = i * 4;
|
||||
if (sprite_gray_only_data.data()[offset + 0] == 0) {
|
||||
::memset((void*)&sprite_gray_only_data.data()[offset], 200, 3);
|
||||
}
|
||||
else {
|
||||
sprite_gray_only_data.data()[offset + 0] = (unsigned char)(sprite_data.data()[offset + 0] * 0.3 + 178);
|
||||
sprite_gray_only_data.data()[offset + 1] = (unsigned char)(sprite_data.data()[offset + 1] * 0.3 + 178);
|
||||
sprite_gray_only_data.data()[offset + 2] = (unsigned char)(sprite_data.data()[offset + 2] * 0.3 + 178);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int sprite_offset_px = sprite_id * (int)sprite_size_px_ex * m_width;
|
||||
int state_id = -1;
|
||||
for (const std::pair<int, bool>& state : states) {
|
||||
++state_id;
|
||||
|
||||
// select the sprite variant
|
||||
std::vector<unsigned char>* src = nullptr;
|
||||
switch (state.first)
|
||||
{
|
||||
case 1: { src = &sprite_white_only_data; break; }
|
||||
case 2: { src = &sprite_gray_only_data; break; }
|
||||
default: { src = &hover_data; break; }
|
||||
}
|
||||
|
||||
// applies background, if needed
|
||||
if (state.second) {
|
||||
src = &pressed_data;
|
||||
}
|
||||
|
||||
::memcpy((void*)output_data.data(), (const void*)src->data(), sprite_bytes);
|
||||
|
||||
//BBS use BBS pressed style
|
||||
//if (state.second) {
|
||||
// float inv_255 = 1.0f / 255.0f;
|
||||
// // offset by 1 to leave the first pixel empty (both in x and y)
|
||||
// for (unsigned int r = 1; r <= sprite_size_px; ++r) {
|
||||
// unsigned int offset_r = r * sprite_size_px_ex;
|
||||
// for (unsigned int c = 1; c <= sprite_size_px; ++c) {
|
||||
// unsigned int offset = (offset_r + c) * 4;
|
||||
// float alpha = (float)output_data.data()[offset + 3] * inv_255;
|
||||
// output_data.data()[offset + 0] = (unsigned char)(output_data.data()[offset + 0] * alpha);
|
||||
// output_data.data()[offset + 1] = (unsigned char)(output_data.data()[offset + 1] * alpha);
|
||||
// output_data.data()[offset + 2] = (unsigned char)(output_data.data()[offset + 2] * alpha);
|
||||
// output_data.data()[offset + 3] = (unsigned char)(128 * (1.0f - alpha) + output_data.data()[offset + 3] * alpha);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
int state_offset_px = sprite_offset_px + state_id * sprite_size_px_ex;
|
||||
for (int j = 0; j < (int)sprite_size_px_ex; ++j) {
|
||||
::memcpy((void*)&data.data()[(state_offset_px + j * m_width) * 4], (const void*)&output_data.data()[j * sprite_stride], sprite_stride);
|
||||
}
|
||||
}
|
||||
|
||||
nsvgDelete(image);
|
||||
}
|
||||
|
||||
nsvgDeleteRasterizer(rast);
|
||||
|
||||
// sends data to gpu
|
||||
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||
glsafe(::glGenTextures(1, &m_id));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_id));
|
||||
if (compress && GLEW_EXT_texture_compression_s3tc)
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
else
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
|
||||
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
|
||||
m_source = filenames.front();
|
||||
|
||||
#if 0
|
||||
// debug output
|
||||
static int pass = 0;
|
||||
++pass;
|
||||
|
||||
wxImage output(m_width, m_height);
|
||||
output.InitAlpha();
|
||||
|
||||
for (int h = 0; h < m_height; ++h) {
|
||||
int px_h = h * m_width;
|
||||
for (int w = 0; w < m_width; ++w) {
|
||||
int offset = (px_h + w) * 4;
|
||||
output.SetRGB(w, h, data.data()[offset + 0], data.data()[offset + 1], data.data()[offset + 2]);
|
||||
output.SetAlpha(w, h, data.data()[offset + 3]);
|
||||
}
|
||||
}
|
||||
|
||||
std::string out_filename = resources_dir() + "/images/test_" + std::to_string(pass) + ".png";
|
||||
output.SaveFile(out_filename, wxBITMAP_TYPE_PNG);
|
||||
#endif // 0
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLTexture::reset()
|
||||
{
|
||||
if (m_id != 0)
|
||||
glsafe(::glDeleteTextures(1, &m_id));
|
||||
|
||||
m_id = 0;
|
||||
m_width = 0;
|
||||
m_height = 0;
|
||||
m_source = "";
|
||||
m_compressor.reset();
|
||||
|
||||
//BBS: GUI refactor
|
||||
m_original_width = m_original_height = 0;
|
||||
}
|
||||
|
||||
bool GLTexture::generate_from_text_string(const std::string &text_str, wxFont &font, wxColor background, wxColor foreground)
|
||||
{
|
||||
if (text_str.empty())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":no text string, should not happen\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
wxString msg = _(text_str);
|
||||
wxMemoryDC memDC;
|
||||
|
||||
memDC.SetFont(font);
|
||||
|
||||
// calculates texture size
|
||||
wxCoord w, h;
|
||||
memDC.GetMultiLineTextExtent(msg, &w, &h);
|
||||
|
||||
m_original_width = (int)w;
|
||||
m_original_height = (int)h;
|
||||
m_width = (int)next_highest_power_of_2((uint32_t)w);
|
||||
m_height = (int)next_highest_power_of_2((uint32_t)h);
|
||||
|
||||
// generates bitmap
|
||||
wxBitmap bitmap(m_width, m_height);
|
||||
|
||||
memDC.SelectObject(bitmap);
|
||||
memDC.SetBackground(wxBrush(background));
|
||||
memDC.Clear();
|
||||
|
||||
// draw message
|
||||
memDC.SetTextForeground(*wxWHITE);
|
||||
memDC.DrawLabel(msg, wxRect(0,0, m_original_width, m_original_height), wxALIGN_CENTER);
|
||||
|
||||
memDC.SelectObject(wxNullBitmap);
|
||||
|
||||
// Convert the bitmap into a linear data ready to be loaded into the GPU.
|
||||
wxImage image = bitmap.ConvertToImage();
|
||||
|
||||
// prepare buffer
|
||||
std::vector<unsigned char> data(4 * m_width * m_height, 0);
|
||||
const unsigned char *src = image.GetData();
|
||||
/* for debug use
|
||||
std::ofstream fout;
|
||||
fout.open(text_str+std::to_string(m_width)+"_"+std::to_string(m_height)+".rgb", std::ios::out);
|
||||
fout.write((const char*)src, 3 * m_width * m_height);
|
||||
fout.close();*/
|
||||
for (int h = 0; h < m_height; ++h) {
|
||||
unsigned char* dst = data.data() + 4 * h * m_width;
|
||||
for (int w = 0; w < m_width; ++w) {
|
||||
*dst++ = foreground.Red();
|
||||
*dst++ = foreground.Green();
|
||||
*dst++ = foreground.Blue();
|
||||
*dst++ = (unsigned char)std::min<int>(255, *src);
|
||||
src += 3;
|
||||
}
|
||||
}
|
||||
|
||||
// sends buffer to gpu
|
||||
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||
glsafe(::glGenTextures(1, &m_id));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id));
|
||||
if (GLEW_EXT_texture_compression_s3tc)
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
else
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void GLTexture::render_texture(unsigned int tex_id, float left, float right, float bottom, float top)
|
||||
{
|
||||
render_sub_texture(tex_id, left, right, bottom, top, FullTextureUVs);
|
||||
}
|
||||
|
||||
void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right, float bottom, float top, const GLTexture::Quad_UVs& uvs)
|
||||
{
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
glsafe(::glEnable(GL_TEXTURE_2D));
|
||||
glsafe(::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE));
|
||||
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)tex_id));
|
||||
|
||||
::glBegin(GL_QUADS);
|
||||
::glTexCoord2f(uvs.left_bottom.u, uvs.left_bottom.v); ::glVertex2f(left, bottom);
|
||||
::glTexCoord2f(uvs.right_bottom.u, uvs.right_bottom.v); ::glVertex2f(right, bottom);
|
||||
::glTexCoord2f(uvs.right_top.u, uvs.right_top.v); ::glVertex2f(right, top);
|
||||
::glTexCoord2f(uvs.left_top.u, uvs.left_top.v); ::glVertex2f(left, top);
|
||||
glsafe(::glEnd());
|
||||
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
|
||||
glsafe(::glDisable(GL_TEXTURE_2D));
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
}
|
||||
|
||||
bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy)
|
||||
{
|
||||
bool compression_enabled = (compression_type != None) && GLEW_EXT_texture_compression_s3tc;
|
||||
|
||||
// Load a PNG with an alpha channel.
|
||||
wxImage image;
|
||||
if (!image.LoadFile(wxString::FromUTF8(filename.c_str()), wxBITMAP_TYPE_PNG)) {
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_width = image.GetWidth();
|
||||
m_height = image.GetHeight();
|
||||
|
||||
bool requires_rescale = false;
|
||||
|
||||
if (compression_enabled && compression_type == MultiThreaded) {
|
||||
// the stb_dxt compression library seems to like only texture sizes which are a multiple of 4
|
||||
int width_rem = m_width % 4;
|
||||
int height_rem = m_height % 4;
|
||||
|
||||
if (width_rem != 0) {
|
||||
m_width += (4 - width_rem);
|
||||
requires_rescale = true;
|
||||
}
|
||||
|
||||
if (height_rem != 0) {
|
||||
m_height += (4 - height_rem);
|
||||
requires_rescale = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (requires_rescale)
|
||||
image = image.ResampleBicubic(m_width, m_height);
|
||||
|
||||
int n_pixels = m_width * m_height;
|
||||
if (n_pixels <= 0) {
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get RGB & alpha raw data from wxImage, pack them into an array.
|
||||
unsigned char* img_rgb = image.GetData();
|
||||
if (img_rgb == nullptr) {
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char* img_alpha = image.GetAlpha();
|
||||
|
||||
std::vector<unsigned char> data(n_pixels * 4, 0);
|
||||
for (int i = 0; i < n_pixels; ++i) {
|
||||
int data_id = i * 4;
|
||||
int img_id = i * 3;
|
||||
data[data_id + 0] = img_rgb[img_id + 0];
|
||||
data[data_id + 1] = img_rgb[img_id + 1];
|
||||
data[data_id + 2] = img_rgb[img_id + 2];
|
||||
data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255;
|
||||
}
|
||||
|
||||
// sends data to gpu
|
||||
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||
glsafe(::glGenTextures(1, &m_id));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_id));
|
||||
|
||||
if (apply_anisotropy) {
|
||||
GLfloat max_anisotropy = OpenGLManager::get_gl_info().get_max_anisotropy();
|
||||
if (max_anisotropy > 1.0f)
|
||||
glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy));
|
||||
}
|
||||
|
||||
if (compression_enabled) {
|
||||
if (compression_type == SingleThreaded)
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
else {
|
||||
// initializes the texture on GPU
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
|
||||
// and send the uncompressed data to the compressor
|
||||
m_compressor.add_level((unsigned int)m_width, (unsigned int)m_height, data);
|
||||
}
|
||||
}
|
||||
else
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
|
||||
if (use_mipmaps) {
|
||||
// we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
|
||||
int lod_w = m_width;
|
||||
int lod_h = m_height;
|
||||
GLint level = 0;
|
||||
while (lod_w > 1 || lod_h > 1) {
|
||||
++level;
|
||||
|
||||
lod_w = std::max(lod_w / 2, 1);
|
||||
lod_h = std::max(lod_h / 2, 1);
|
||||
n_pixels = lod_w * lod_h;
|
||||
|
||||
image = image.ResampleBicubic(lod_w, lod_h);
|
||||
|
||||
data.resize(n_pixels * 4);
|
||||
|
||||
img_rgb = image.GetData();
|
||||
img_alpha = image.GetAlpha();
|
||||
|
||||
for (int i = 0; i < n_pixels; ++i) {
|
||||
int data_id = i * 4;
|
||||
int img_id = i * 3;
|
||||
data[data_id + 0] = img_rgb[img_id + 0];
|
||||
data[data_id + 1] = img_rgb[img_id + 1];
|
||||
data[data_id + 2] = img_rgb[img_id + 2];
|
||||
data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255;
|
||||
}
|
||||
|
||||
if (compression_enabled) {
|
||||
if (compression_type == SingleThreaded)
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
else {
|
||||
// initializes the texture on GPU
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
|
||||
// and send the uncompressed data to the compressor
|
||||
m_compressor.add_level((unsigned int)lod_w, (unsigned int)lod_h, data);
|
||||
}
|
||||
}
|
||||
else
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
}
|
||||
|
||||
if (!compression_enabled) {
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
|
||||
}
|
||||
}
|
||||
else {
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
|
||||
}
|
||||
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
|
||||
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
|
||||
m_source = filename;
|
||||
|
||||
if (compression_enabled && compression_type == MultiThreaded)
|
||||
// start asynchronous compression
|
||||
m_compressor.start_compressing();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px)
|
||||
{
|
||||
bool compression_enabled = compress && GLEW_EXT_texture_compression_s3tc;
|
||||
|
||||
NSVGimage* image = nsvgParseFromFile(filename.c_str(), "px", 96.0f);
|
||||
if (image == nullptr) {
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
float scale = (float)max_size_px / std::max(image->width, image->height);
|
||||
|
||||
m_width = (int)(scale * image->width);
|
||||
m_height = (int)(scale * image->height);
|
||||
|
||||
if (compression_enabled) {
|
||||
// the stb_dxt compression library seems to like only texture sizes which are a multiple of 4
|
||||
int width_rem = m_width % 4;
|
||||
int height_rem = m_height % 4;
|
||||
|
||||
if (width_rem != 0)
|
||||
m_width += (4 - width_rem);
|
||||
|
||||
if (height_rem != 0)
|
||||
m_height += (4 - height_rem);
|
||||
}
|
||||
|
||||
int n_pixels = m_width * m_height;
|
||||
|
||||
if (n_pixels <= 0) {
|
||||
reset();
|
||||
nsvgDelete(image);
|
||||
return false;
|
||||
}
|
||||
|
||||
NSVGrasterizer* rast = nsvgCreateRasterizer();
|
||||
if (rast == nullptr) {
|
||||
nsvgDelete(image);
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
// creates the temporary buffer only once, with max size, and reuse it for all the levels, if generating mipmaps
|
||||
std::vector<unsigned char> data(n_pixels * 4, 0);
|
||||
nsvgRasterize(rast, image, 0, 0, scale, data.data(), m_width, m_height, m_width * 4);
|
||||
|
||||
// sends data to gpu
|
||||
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||
glsafe(::glGenTextures(1, &m_id));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_id));
|
||||
|
||||
if (apply_anisotropy) {
|
||||
GLfloat max_anisotropy = OpenGLManager::get_gl_info().get_max_anisotropy();
|
||||
if (max_anisotropy > 1.0f)
|
||||
glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy));
|
||||
}
|
||||
|
||||
if (compression_enabled) {
|
||||
// initializes the texture on GPU
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
|
||||
// and send the uncompressed data to the compressor
|
||||
m_compressor.add_level((unsigned int)m_width, (unsigned int)m_height, data);
|
||||
}
|
||||
else
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
|
||||
if (use_mipmaps) {
|
||||
// we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
|
||||
int lod_w = m_width;
|
||||
int lod_h = m_height;
|
||||
GLint level = 0;
|
||||
while (lod_w > 1 || lod_h > 1) {
|
||||
++level;
|
||||
|
||||
lod_w = std::max(lod_w / 2, 1);
|
||||
lod_h = std::max(lod_h / 2, 1);
|
||||
scale /= 2.0f;
|
||||
|
||||
data.resize(lod_w * lod_h * 4);
|
||||
|
||||
nsvgRasterize(rast, image, 0, 0, scale, data.data(), lod_w, lod_h, lod_w * 4);
|
||||
if (compression_enabled) {
|
||||
// initializes the texture on GPU
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
|
||||
// and send the uncompressed data to the compressor
|
||||
m_compressor.add_level((unsigned int)lod_w, (unsigned int)lod_h, data);
|
||||
}
|
||||
else
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
}
|
||||
|
||||
if (!compression_enabled) {
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
|
||||
}
|
||||
}
|
||||
else {
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
|
||||
}
|
||||
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
|
||||
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
|
||||
m_source = filename;
|
||||
|
||||
if (compression_enabled)
|
||||
// start asynchronous compression
|
||||
m_compressor.start_compressing();
|
||||
|
||||
nsvgDeleteRasterizer(rast);
|
||||
nsvgDelete(image);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
135
src/slic3r/GUI/GLTexture.hpp
Normal file
135
src/slic3r/GUI/GLTexture.hpp
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
#ifndef slic3r_GLTexture_hpp_
|
||||
#define slic3r_GLTexture_hpp_
|
||||
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
|
||||
class wxImage;
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
class GLTexture
|
||||
{
|
||||
class Compressor
|
||||
{
|
||||
struct Level
|
||||
{
|
||||
unsigned int w;
|
||||
unsigned int h;
|
||||
std::vector<unsigned char> src_data;
|
||||
std::vector<unsigned char> compressed_data;
|
||||
bool sent_to_gpu;
|
||||
|
||||
Level(unsigned int w, unsigned int h, const std::vector<unsigned char>& data) : w(w), h(h), src_data(data), sent_to_gpu(false) {}
|
||||
};
|
||||
|
||||
GLTexture& m_texture;
|
||||
std::vector<Level> m_levels;
|
||||
std::thread m_thread;
|
||||
// Does the caller want the background thread to stop?
|
||||
// This atomic also works as a memory barrier for synchronizing the cancel event with the worker thread.
|
||||
std::atomic<bool> m_abort_compressing;
|
||||
// How many levels were compressed since the start of the background processing thread?
|
||||
// This atomic also works as a memory barrier for synchronizing results of the worker thread with the calling thread.
|
||||
std::atomic<unsigned int> m_num_levels_compressed;
|
||||
|
||||
public:
|
||||
explicit Compressor(GLTexture& texture) : m_texture(texture), m_abort_compressing(false), m_num_levels_compressed(0) {}
|
||||
~Compressor() { reset(); }
|
||||
|
||||
void reset();
|
||||
|
||||
void add_level(unsigned int w, unsigned int h, const std::vector<unsigned char>& data) { m_levels.emplace_back(w, h, data); }
|
||||
|
||||
void start_compressing();
|
||||
|
||||
bool unsent_compressed_data_available() const;
|
||||
void send_compressed_data_to_gpu();
|
||||
bool all_compressed_data_sent_to_gpu() const { return m_levels.empty(); }
|
||||
|
||||
private:
|
||||
void compress();
|
||||
};
|
||||
|
||||
public:
|
||||
enum ECompressionType : unsigned char
|
||||
{
|
||||
None,
|
||||
SingleThreaded,
|
||||
MultiThreaded
|
||||
};
|
||||
|
||||
struct UV
|
||||
{
|
||||
float u;
|
||||
float v;
|
||||
};
|
||||
|
||||
struct Quad_UVs
|
||||
{
|
||||
UV left_bottom;
|
||||
UV right_bottom;
|
||||
UV right_top;
|
||||
UV left_top;
|
||||
};
|
||||
|
||||
static Quad_UVs FullTextureUVs;
|
||||
|
||||
protected:
|
||||
unsigned int m_id;
|
||||
int m_width;
|
||||
int m_height;
|
||||
std::string m_source;
|
||||
Compressor m_compressor;
|
||||
|
||||
public:
|
||||
GLTexture();
|
||||
virtual ~GLTexture();
|
||||
|
||||
bool load_from_file(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy);
|
||||
bool load_from_svg_file(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px);
|
||||
//BBS load GLTexture from raw pixel data
|
||||
bool load_from_raw_data(std::vector<unsigned char> data, unsigned int w, unsigned int h, bool apply_anisotropy = false);
|
||||
// meanings of states: (std::pair<int, bool>)
|
||||
// first field (int):
|
||||
// 0 -> no changes
|
||||
// 1 -> use white only color variant
|
||||
// 2 -> use gray only color variant
|
||||
// second field (bool):
|
||||
// false -> no changes
|
||||
// true -> add background color
|
||||
bool load_from_svg_files_as_sprites_array(const std::vector<std::string>& filenames, const std::vector<std::pair<int, bool>>& states, unsigned int sprite_size_px, bool compress);
|
||||
void reset();
|
||||
//BBS: add generate logic for text strings
|
||||
int m_original_width;
|
||||
int m_original_height;
|
||||
bool generate_from_text_string(const std::string &text_str, wxFont &font, wxColor background = *wxBLACK, wxColor foreground = *wxWHITE);
|
||||
|
||||
unsigned int get_id() const { return m_id; }
|
||||
int get_width() const { return m_width; }
|
||||
int get_height() const { return m_height; }
|
||||
|
||||
const std::string& get_source() const { return m_source; }
|
||||
|
||||
bool unsent_compressed_data_available() const { return m_compressor.unsent_compressed_data_available(); }
|
||||
void send_compressed_data_to_gpu() { m_compressor.send_compressed_data_to_gpu(); }
|
||||
bool all_compressed_data_sent_to_gpu() const { return m_compressor.all_compressed_data_sent_to_gpu(); }
|
||||
|
||||
static void render_texture(unsigned int tex_id, float left, float right, float bottom, float top);
|
||||
static void render_sub_texture(unsigned int tex_id, float left, float right, float bottom, float top, const Quad_UVs& uvs);
|
||||
|
||||
private:
|
||||
bool load_from_png(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy);
|
||||
bool load_from_svg(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px);
|
||||
|
||||
friend class Compressor;
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_GLTexture_hpp_
|
||||
|
||||
1650
src/slic3r/GUI/GLToolbar.cpp
Normal file
1650
src/slic3r/GUI/GLToolbar.cpp
Normal file
File diff suppressed because it is too large
Load diff
445
src/slic3r/GUI/GLToolbar.hpp
Normal file
445
src/slic3r/GUI/GLToolbar.hpp
Normal file
|
|
@ -0,0 +1,445 @@
|
|||
#ifndef slic3r_GLToolbar_hpp_
|
||||
#define slic3r_GLToolbar_hpp_
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "GLTexture.hpp"
|
||||
#include "Event.hpp"
|
||||
#include "libslic3r/Point.hpp"
|
||||
|
||||
class wxEvtHandler;
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
class GLCanvas3D;
|
||||
|
||||
|
||||
//BBS: GUI refactor: GLToolbar
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_OPEN_PROJECT, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_SLICE_ALL, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_SLICE_PLATE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_PRINT_ALL, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_PRINT_PLATE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_EXPORT_GCODE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_EXPORT_SLICED_FILE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_PRINT_SELECT, SimpleEvent);
|
||||
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_ADD, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_DELETE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_DELETE_ALL, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_ADD_PLATE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_DEL_PLATE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_ORIENT, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_ARRANGE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_CUT, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_COPY, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_PASTE, SimpleEvent);
|
||||
//BBS: add clone event
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_CLONE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_MORE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_FEWER, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_SPLIT_OBJECTS, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_SPLIT_VOLUMES, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_FILLCOLOR, IntEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_SELECT_SLICED_PLATE, wxCommandEvent);
|
||||
|
||||
wxDECLARE_EVENT(EVT_GLVIEWTOOLBAR_3D, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLVIEWTOOLBAR_PREVIEW, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLVIEWTOOLBAR_ASSEMBLE, SimpleEvent);
|
||||
|
||||
|
||||
|
||||
class GLToolbarItem
|
||||
{
|
||||
public:
|
||||
typedef std::function<void()> ActionCallback;
|
||||
typedef std::function<bool()> VisibilityCallback;
|
||||
typedef std::function<bool()> EnablingCallback;
|
||||
typedef std::function<void(float, float, float, float)> RenderCallback;
|
||||
|
||||
enum EType : unsigned char
|
||||
{
|
||||
Action,
|
||||
Separator,
|
||||
//BBS: GUI refactor: GLToolbar
|
||||
ActionWithText,
|
||||
ActionWithTextImage,
|
||||
Num_Types
|
||||
};
|
||||
|
||||
enum EActionType : unsigned char
|
||||
{
|
||||
Undefined,
|
||||
Left,
|
||||
Right,
|
||||
Num_Action_Types
|
||||
};
|
||||
|
||||
enum EState : unsigned char
|
||||
{
|
||||
Normal,
|
||||
Pressed,
|
||||
Disabled,
|
||||
Hover,
|
||||
HoverPressed,
|
||||
HoverDisabled,
|
||||
Num_States
|
||||
};
|
||||
|
||||
enum EHighlightState : unsigned char
|
||||
{
|
||||
HighlightedShown,
|
||||
HighlightedHidden,
|
||||
Num_Rendered_Highlight_States,
|
||||
NotHighlighted
|
||||
};
|
||||
|
||||
struct Data
|
||||
{
|
||||
struct Option
|
||||
{
|
||||
bool toggable;
|
||||
ActionCallback action_callback;
|
||||
RenderCallback render_callback;
|
||||
|
||||
Option();
|
||||
|
||||
bool can_render() const { return toggable && (render_callback != nullptr); }
|
||||
};
|
||||
|
||||
std::string name;
|
||||
std::string icon_filename;
|
||||
std::string tooltip;
|
||||
std::string additional_tooltip;
|
||||
//BBS: GUI refactor: GLToolbar
|
||||
std::string button_text;
|
||||
float extra_size_ratio;
|
||||
GLTexture text_texture;
|
||||
GLTexture image_texture;
|
||||
std::vector<unsigned char> image_data;
|
||||
unsigned int image_width;
|
||||
unsigned int image_height;
|
||||
|
||||
unsigned int sprite_id;
|
||||
// mouse left click
|
||||
Option left;
|
||||
// mouse right click
|
||||
Option right;
|
||||
bool visible;
|
||||
VisibilityCallback visibility_callback;
|
||||
EnablingCallback enabling_callback;
|
||||
|
||||
Data();
|
||||
//BBS: GUI refactor: GLToolbar
|
||||
Data(const GLToolbarItem::Data& data)
|
||||
{
|
||||
name = data.name;
|
||||
icon_filename = data.icon_filename;
|
||||
tooltip = data.tooltip;
|
||||
additional_tooltip = data.additional_tooltip;
|
||||
button_text = data.button_text;
|
||||
extra_size_ratio = data.extra_size_ratio;
|
||||
sprite_id = data.sprite_id;
|
||||
left = data.left;
|
||||
right = data.right;
|
||||
visible = data.visible;
|
||||
visibility_callback = data.visibility_callback;
|
||||
enabling_callback = data.enabling_callback;
|
||||
image_data = data.image_data;
|
||||
image_width = data.image_width;
|
||||
image_height = data.image_height;
|
||||
}
|
||||
};
|
||||
|
||||
static const ActionCallback Default_Action_Callback;
|
||||
static const VisibilityCallback Default_Visibility_Callback;
|
||||
static const EnablingCallback Default_Enabling_Callback;
|
||||
static const RenderCallback Default_Render_Callback;
|
||||
|
||||
private:
|
||||
EType m_type;
|
||||
EState m_state;
|
||||
Data m_data;
|
||||
EActionType m_last_action_type;
|
||||
EHighlightState m_highlight_state;
|
||||
public:
|
||||
// remember left position for rendering menu
|
||||
mutable float render_left_pos;
|
||||
|
||||
GLToolbarItem(EType type, const Data& data);
|
||||
|
||||
EState get_state() const { return m_state; }
|
||||
void set_state(EState state) { m_state = state; }
|
||||
|
||||
EHighlightState get_highlight() const { return m_highlight_state; }
|
||||
void set_highlight(EHighlightState state) { m_highlight_state = state; }
|
||||
|
||||
const std::string& get_name() const { return m_data.name; }
|
||||
const std::string& get_icon_filename() const { return m_data.icon_filename; }
|
||||
const std::string& get_tooltip() const { return m_data.tooltip; }
|
||||
const std::string& get_additional_tooltip() const { return m_data.additional_tooltip; }
|
||||
void set_additional_tooltip(const std::string& text) { m_data.additional_tooltip = text; }
|
||||
void set_tooltip(const std::string& text) { m_data.tooltip = text; }
|
||||
|
||||
void do_left_action() { m_last_action_type = Left; m_data.left.action_callback(); }
|
||||
void do_right_action() { m_last_action_type = Right; m_data.right.action_callback(); }
|
||||
|
||||
bool is_enabled() const { return (m_state != Disabled) && (m_state != HoverDisabled); }
|
||||
bool is_disabled() const { return (m_state == Disabled) || (m_state == HoverDisabled); }
|
||||
bool is_hovered() const { return (m_state == Hover) || (m_state == HoverPressed) || (m_state == HoverDisabled); }
|
||||
bool is_pressed() const { return (m_state == Pressed) || (m_state == HoverPressed); }
|
||||
bool is_visible() const { return m_data.visible; }
|
||||
bool is_separator() const { return m_type == Separator; }
|
||||
|
||||
bool is_left_toggable() const { return m_data.left.toggable; }
|
||||
bool is_right_toggable() const { return m_data.right.toggable; }
|
||||
|
||||
bool has_left_render_callback() const { return m_data.left.render_callback != nullptr; }
|
||||
bool has_right_render_callback() const { return m_data.right.render_callback != nullptr; }
|
||||
|
||||
EActionType get_last_action_type() const { return m_last_action_type; }
|
||||
void reset_last_action_type() { m_last_action_type = Undefined; }
|
||||
|
||||
// returns true if the state changes
|
||||
bool update_visibility();
|
||||
// returns true if the state changes
|
||||
bool update_enabled_state();
|
||||
|
||||
//BBS: GUI refactor: GLToolbar
|
||||
bool is_action() const { return m_type == Action; }
|
||||
bool is_action_with_text() const { return m_type == ActionWithText; }
|
||||
bool is_action_with_text_image() const { return m_type == ActionWithTextImage; }
|
||||
const std::string& get_button_text() const { return m_data.button_text; }
|
||||
void set_button_text(const std::string& text) { m_data.button_text = text; }
|
||||
float get_extra_size_ratio() const { return m_data.extra_size_ratio; }
|
||||
void set_extra_size_ratio(const float ratio) { m_data.extra_size_ratio = ratio; }
|
||||
void render_text(float left, float right, float bottom, float top) const;
|
||||
int generate_texture(wxFont& font);
|
||||
int generate_image_texture();
|
||||
|
||||
void render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const;
|
||||
void render_image(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const;
|
||||
private:
|
||||
void set_visible(bool visible) { m_data.visible = visible; }
|
||||
|
||||
friend class GLToolbar;
|
||||
};
|
||||
|
||||
struct BackgroundTexture
|
||||
{
|
||||
struct Metadata
|
||||
{
|
||||
// path of the file containing the background texture
|
||||
std::string filename;
|
||||
// size of the left edge, in pixels
|
||||
unsigned int left;
|
||||
// size of the right edge, in pixels
|
||||
unsigned int right;
|
||||
// size of the top edge, in pixels
|
||||
unsigned int top;
|
||||
// size of the bottom edge, in pixels
|
||||
unsigned int bottom;
|
||||
|
||||
Metadata();
|
||||
};
|
||||
|
||||
GLTexture texture;
|
||||
Metadata metadata;
|
||||
};
|
||||
|
||||
class GLToolbar
|
||||
{
|
||||
public:
|
||||
static const float Default_Icons_Size;
|
||||
|
||||
enum EType : unsigned char
|
||||
{
|
||||
Normal,
|
||||
Radio,
|
||||
Num_Types
|
||||
};
|
||||
|
||||
struct Layout
|
||||
{
|
||||
enum EType : unsigned char
|
||||
{
|
||||
Horizontal,
|
||||
Vertical,
|
||||
Num_Types
|
||||
};
|
||||
|
||||
enum EHorizontalOrientation : unsigned char
|
||||
{
|
||||
HO_Left,
|
||||
HO_Center,
|
||||
HO_Right,
|
||||
Num_Horizontal_Orientations
|
||||
};
|
||||
|
||||
enum EVerticalOrientation : unsigned char
|
||||
{
|
||||
VO_Top,
|
||||
VO_Center,
|
||||
VO_Bottom,
|
||||
Num_Vertical_Orientations
|
||||
};
|
||||
|
||||
EType type;
|
||||
EHorizontalOrientation horizontal_orientation;
|
||||
EVerticalOrientation vertical_orientation;
|
||||
float top;
|
||||
float left;
|
||||
float border;
|
||||
float separator_size;
|
||||
float gap_size;
|
||||
float icons_size;
|
||||
float text_size;
|
||||
float image_width;
|
||||
float image_height;
|
||||
float scale;
|
||||
|
||||
float width;
|
||||
float height;
|
||||
bool dirty;
|
||||
|
||||
Layout();
|
||||
};
|
||||
|
||||
private:
|
||||
typedef std::vector<GLToolbarItem*> ItemsList;
|
||||
|
||||
EType m_type;
|
||||
std::string m_name;
|
||||
bool m_enabled;
|
||||
GLTexture m_icons_texture;
|
||||
bool m_icons_texture_dirty;
|
||||
mutable GLTexture m_images_texture;
|
||||
mutable bool m_images_texture_dirty;
|
||||
BackgroundTexture m_background_texture;
|
||||
BackgroundTexture m_arrow_texture;
|
||||
Layout m_layout;
|
||||
|
||||
ItemsList m_items;
|
||||
|
||||
struct MouseCapture
|
||||
{
|
||||
bool left;
|
||||
bool middle;
|
||||
bool right;
|
||||
GLCanvas3D* parent;
|
||||
|
||||
MouseCapture() { reset(); }
|
||||
|
||||
bool any() const { return left || middle || right; }
|
||||
void reset() { left = middle = right = false; parent = nullptr; }
|
||||
};
|
||||
|
||||
MouseCapture m_mouse_capture;
|
||||
int m_pressed_toggable_id;
|
||||
|
||||
public:
|
||||
GLToolbar(EType type, const std::string& name);
|
||||
~GLToolbar();
|
||||
|
||||
bool init(const BackgroundTexture::Metadata& background_texture);
|
||||
|
||||
bool init_arrow(const BackgroundTexture::Metadata& arrow_texture);
|
||||
|
||||
Layout::EType get_layout_type() const;
|
||||
void set_layout_type(Layout::EType type);
|
||||
Layout::EHorizontalOrientation get_horizontal_orientation() const { return m_layout.horizontal_orientation; }
|
||||
void set_horizontal_orientation(Layout::EHorizontalOrientation orientation) { m_layout.horizontal_orientation = orientation; }
|
||||
Layout::EVerticalOrientation get_vertical_orientation() const { return m_layout.vertical_orientation; }
|
||||
void set_vertical_orientation(Layout::EVerticalOrientation orientation) { m_layout.vertical_orientation = orientation; }
|
||||
|
||||
void set_position(float top, float left);
|
||||
void set_border(float border);
|
||||
void set_separator_size(float size);
|
||||
void set_gap_size(float size);
|
||||
void set_icons_size(float size);
|
||||
void set_text_size(float size);
|
||||
void set_scale(float scale);
|
||||
|
||||
bool is_enabled() const { return m_enabled; }
|
||||
void set_enabled(bool enable) { m_enabled = enable; }
|
||||
|
||||
//BBS: GUI refactor: GLToolbar
|
||||
bool add_item(const GLToolbarItem::Data& data, GLToolbarItem::EType type = GLToolbarItem::Action);
|
||||
bool add_separator();
|
||||
bool del_all_item();
|
||||
|
||||
float get_icons_size() { return m_layout.icons_size; }
|
||||
float get_width();
|
||||
float get_height();
|
||||
|
||||
void select_item(const std::string& name);
|
||||
|
||||
bool is_item_pressed(const std::string& name) const;
|
||||
bool is_item_disabled(const std::string& name) const;
|
||||
bool is_item_visible(const std::string& name) const;
|
||||
|
||||
bool is_any_item_pressed() const;
|
||||
|
||||
unsigned int get_items_count() const { return (unsigned int)m_items.size(); }
|
||||
int get_item_id(const std::string& name) const;
|
||||
|
||||
void force_left_action(int item_id, GLCanvas3D& parent) { do_action(GLToolbarItem::Left, item_id, parent, false); }
|
||||
void force_right_action(int item_id, GLCanvas3D& parent) { do_action(GLToolbarItem::Right, item_id, parent, false); }
|
||||
|
||||
std::string get_tooltip() const;
|
||||
|
||||
void get_additional_tooltip(int item_id, std::string& text);
|
||||
void set_additional_tooltip(int item_id, const std::string& text);
|
||||
void set_tooltip(int item_id, const std::string& text);
|
||||
int get_visible_items_cnt() const;
|
||||
|
||||
// returns true if any item changed its state
|
||||
bool update_items_state();
|
||||
|
||||
void render(const GLCanvas3D& parent);
|
||||
void render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighted_item);
|
||||
|
||||
bool on_mouse(wxMouseEvent& evt, GLCanvas3D& parent);
|
||||
// get item pointer for highlighter timer
|
||||
GLToolbarItem* get_item(const std::string& item_name);
|
||||
|
||||
//BBS: GUI refactor: GLToolbar
|
||||
int generate_button_text_textures(wxFont& font);
|
||||
int generate_image_textures();
|
||||
float get_scaled_icon_size();
|
||||
|
||||
private:
|
||||
void calc_layout();
|
||||
float get_width_horizontal() const;
|
||||
float get_width_vertical() const;
|
||||
float get_height_horizontal() const;
|
||||
float get_height_vertical() const;
|
||||
float get_main_size() const;
|
||||
void do_action(GLToolbarItem::EActionType type, int item_id, GLCanvas3D& parent, bool check_hover);
|
||||
void update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent);
|
||||
void update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent);
|
||||
void update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent);
|
||||
// returns the id of the item under the given mouse position or -1 if none
|
||||
int contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const;
|
||||
int contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3D& parent) const;
|
||||
int contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& parent) const;
|
||||
|
||||
void render_background(float left, float top, float right, float bottom, float border) const;
|
||||
void render_horizontal(const GLCanvas3D& parent);
|
||||
void render_vertical(const GLCanvas3D& parent);
|
||||
|
||||
bool generate_icons_texture();
|
||||
|
||||
// returns true if any item changed its state
|
||||
bool update_items_visibility();
|
||||
// returns true if any item changed its state
|
||||
bool update_items_enabled_state();
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_GLToolbar_hpp_
|
||||
541
src/slic3r/GUI/GUI.cpp
Normal file
541
src/slic3r/GUI/GUI.cpp
Normal file
|
|
@ -0,0 +1,541 @@
|
|||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "format.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include "libslic3r/LocalesUtils.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/any.hpp>
|
||||
|
||||
#if __APPLE__
|
||||
#import <IOKit/pwr_mgt/IOPMLib.h>
|
||||
#elif _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#include <Windows.h>
|
||||
#include "boost/nowide/convert.hpp"
|
||||
#endif
|
||||
|
||||
#include "AboutDialog.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "format.hpp"
|
||||
|
||||
#include "WebUserLoginDialog.hpp"
|
||||
|
||||
#include "libslic3r/Print.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class AppConfig;
|
||||
|
||||
namespace GUI {
|
||||
|
||||
#if __APPLE__
|
||||
IOPMAssertionID assertionID;
|
||||
#endif
|
||||
|
||||
void disable_screensaver()
|
||||
{
|
||||
#if __APPLE__
|
||||
CFStringRef reasonForActivity = CFSTR("Slic3r");
|
||||
[[maybe_unused]]IOReturn success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
|
||||
kIOPMAssertionLevelOn, reasonForActivity, &assertionID);
|
||||
// ignore result: success == kIOReturnSuccess
|
||||
#elif _WIN32
|
||||
SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_CONTINUOUS);
|
||||
#endif
|
||||
}
|
||||
|
||||
void enable_screensaver()
|
||||
{
|
||||
#if __APPLE__
|
||||
IOPMAssertionRelease(assertionID);
|
||||
#elif _WIN32
|
||||
SetThreadExecutionState(ES_CONTINUOUS);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool debugged()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return IsDebuggerPresent() == TRUE;
|
||||
#else
|
||||
return false;
|
||||
#endif /* _WIN32 */
|
||||
}
|
||||
|
||||
void break_to_debugger()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (IsDebuggerPresent())
|
||||
DebugBreak();
|
||||
#endif /* _WIN32 */
|
||||
}
|
||||
|
||||
const std::string& shortkey_ctrl_prefix()
|
||||
{
|
||||
static const std::string str =
|
||||
#ifdef __APPLE__
|
||||
"⌘+"
|
||||
#else
|
||||
"Ctrl+"
|
||||
#endif
|
||||
;
|
||||
return str;
|
||||
}
|
||||
|
||||
const std::string& shortkey_alt_prefix()
|
||||
{
|
||||
static const std::string str =
|
||||
#ifdef __APPLE__
|
||||
"⌥+"
|
||||
#else
|
||||
"Alt+"
|
||||
#endif
|
||||
;
|
||||
return str;
|
||||
}
|
||||
|
||||
// opt_index = 0, by the reason of zero-index in ConfigOptionVector by default (in case only one element)
|
||||
void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index /*= 0*/)
|
||||
{
|
||||
try{
|
||||
|
||||
if (config.def()->get(opt_key)->type == coBools && config.def()->get(opt_key)->nullable) {
|
||||
ConfigOptionBoolsNullable* vec_new = new ConfigOptionBoolsNullable{ boost::any_cast<unsigned char>(value) };
|
||||
config.option<ConfigOptionBoolsNullable>(opt_key)->set_at(vec_new, opt_index, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
const ConfigOptionDef *opt_def = config.def()->get(opt_key);
|
||||
switch (opt_def->type) {
|
||||
case coFloatOrPercent:{
|
||||
std::string str = boost::any_cast<std::string>(value);
|
||||
bool percent = false;
|
||||
if (str.back() == '%') {
|
||||
str.pop_back();
|
||||
percent = true;
|
||||
}
|
||||
double val = std::stod(str); // locale-dependent (on purpose - the input is the actual content of the field)
|
||||
config.set_key_value(opt_key, new ConfigOptionFloatOrPercent(val, percent));
|
||||
break;}
|
||||
case coPercent:
|
||||
config.set_key_value(opt_key, new ConfigOptionPercent(boost::any_cast<double>(value)));
|
||||
break;
|
||||
case coFloat:{
|
||||
double& val = config.opt_float(opt_key);
|
||||
val = boost::any_cast<double>(value);
|
||||
break;
|
||||
}
|
||||
case coPercents:{
|
||||
ConfigOptionPercents* vec_new = new ConfigOptionPercents{ boost::any_cast<double>(value) };
|
||||
config.option<ConfigOptionPercents>(opt_key)->set_at(vec_new, opt_index, opt_index);
|
||||
break;
|
||||
}
|
||||
case coFloats:{
|
||||
ConfigOptionFloats* vec_new = new ConfigOptionFloats{ boost::any_cast<double>(value) };
|
||||
config.option<ConfigOptionFloats>(opt_key)->set_at(vec_new, opt_index, opt_index);
|
||||
break;
|
||||
}
|
||||
case coString:
|
||||
config.set_key_value(opt_key, new ConfigOptionString(boost::any_cast<std::string>(value)));
|
||||
break;
|
||||
case coStrings:{
|
||||
if (opt_key == "compatible_prints" || opt_key == "compatible_printers") {
|
||||
config.option<ConfigOptionStrings>(opt_key)->values =
|
||||
boost::any_cast<std::vector<std::string>>(value);
|
||||
}
|
||||
else if (config.def()->get(opt_key)->gui_flags.compare("serialized") == 0) {
|
||||
std::string str = boost::any_cast<std::string>(value);
|
||||
std::vector<std::string> values {};
|
||||
if (!str.empty()) {
|
||||
if (str.back() == ';') str.pop_back();
|
||||
// Split a string to multiple strings by a semi - colon.This is the old way of storing multi - string values.
|
||||
// Currently used for the post_process config value only.
|
||||
boost::split(values, str, boost::is_any_of(";"));
|
||||
if (values.size() == 1 && values[0] == "")
|
||||
values.resize(0);
|
||||
}
|
||||
config.option<ConfigOptionStrings>(opt_key)->values = values;
|
||||
}
|
||||
else{
|
||||
ConfigOptionStrings* vec_new = new ConfigOptionStrings{ boost::any_cast<std::string>(value) };
|
||||
config.option<ConfigOptionStrings>(opt_key)->set_at(vec_new, opt_index, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case coBool:
|
||||
config.set_key_value(opt_key, new ConfigOptionBool(boost::any_cast<bool>(value)));
|
||||
break;
|
||||
case coBools:{
|
||||
ConfigOptionBools* vec_new = new ConfigOptionBools{ boost::any_cast<unsigned char>(value) != 0 };
|
||||
config.option<ConfigOptionBools>(opt_key)->set_at(vec_new, opt_index, 0);
|
||||
break;}
|
||||
case coInt:
|
||||
config.set_key_value(opt_key, new ConfigOptionInt(boost::any_cast<int>(value)));
|
||||
break;
|
||||
case coInts:{
|
||||
ConfigOptionInts* vec_new = new ConfigOptionInts{ boost::any_cast<int>(value) };
|
||||
config.option<ConfigOptionInts>(opt_key)->set_at(vec_new, opt_index, 0);
|
||||
}
|
||||
break;
|
||||
case coEnum:{
|
||||
auto *opt = opt_def->default_value.get()->clone();
|
||||
opt->setInt(boost::any_cast<int>(value));
|
||||
config.set_key_value(opt_key, opt);
|
||||
}
|
||||
break;
|
||||
// BBS
|
||||
case coEnums:{
|
||||
ConfigOptionEnumsGeneric* vec_new = new ConfigOptionEnumsGeneric{ boost::any_cast<int>(value) };
|
||||
if (config.has(opt_key))
|
||||
config.option<ConfigOptionEnumsGeneric>(opt_key)->set_at(vec_new, opt_index, 0);
|
||||
}
|
||||
break;
|
||||
case coPoints:{
|
||||
if (opt_key == "printable_area") {
|
||||
config.option<ConfigOptionPoints>(opt_key)->values = boost::any_cast<std::vector<Vec2d>>(value);
|
||||
break;
|
||||
}
|
||||
ConfigOptionPoints* vec_new = new ConfigOptionPoints{ boost::any_cast<Vec2d>(value) };
|
||||
config.option<ConfigOptionPoints>(opt_key)->set_at(vec_new, opt_index, 0);
|
||||
}
|
||||
break;
|
||||
case coNone:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
wxLogError(format_wxstr("Internal error when changing value for %1%: %2%", opt_key, e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void show_error(wxWindow* parent, const wxString& message, bool monospaced_font)
|
||||
{
|
||||
ErrorDialog msg(parent, message, monospaced_font);
|
||||
msg.ShowModal();
|
||||
}
|
||||
|
||||
void show_error(wxWindow* parent, const char* message, bool monospaced_font)
|
||||
{
|
||||
assert(message);
|
||||
show_error(parent, wxString::FromUTF8(message), monospaced_font);
|
||||
}
|
||||
|
||||
void show_error_id(int id, const std::string& message)
|
||||
{
|
||||
auto *parent = id != 0 ? wxWindow::FindWindowById(id) : nullptr;
|
||||
show_error(parent, message);
|
||||
}
|
||||
|
||||
void show_info(wxWindow* parent, const wxString& message, const wxString& title)
|
||||
{
|
||||
//wxMessageDialog msg_wingow(parent, message, wxString(SLIC3R_APP_NAME " - ") + (title.empty() ? _L("Notice") : title), wxOK | wxICON_INFORMATION);
|
||||
MessageDialog msg_wingow(parent, message, wxString(SLIC3R_APP_FULL_NAME " - ") + (title.empty() ? _L("Notice") : title), wxOK | wxICON_INFORMATION);
|
||||
msg_wingow.ShowModal();
|
||||
}
|
||||
|
||||
void show_info(wxWindow* parent, const char* message, const char* title)
|
||||
{
|
||||
assert(message);
|
||||
show_info(parent, wxString::FromUTF8(message), title ? wxString::FromUTF8(title) : wxString());
|
||||
}
|
||||
|
||||
void warning_catcher(wxWindow* parent, const wxString& message)
|
||||
{
|
||||
MessageDialog msg(parent, message, _L("Warning"), wxOK | wxICON_WARNING);
|
||||
msg.ShowModal();
|
||||
}
|
||||
|
||||
static wxString bold(const wxString& str)
|
||||
{
|
||||
return wxString::Format("<b>%s</b>", str);
|
||||
};
|
||||
|
||||
static wxString bold_string(const wxString& str)
|
||||
{
|
||||
return wxString::Format("<b>\"%s\"</b>", str);
|
||||
};
|
||||
|
||||
static void add_config_substitutions(const ConfigSubstitutions& conf_substitutions, wxString& changes)
|
||||
{
|
||||
changes += "<table>";
|
||||
for (const ConfigSubstitution& conf_substitution : conf_substitutions) {
|
||||
wxString new_val;
|
||||
const ConfigOptionDef* def = conf_substitution.opt_def;
|
||||
if (!def)
|
||||
continue;
|
||||
switch (def->type) {
|
||||
case coEnum:
|
||||
{
|
||||
const std::vector<std::string>& labels = def->enum_labels;
|
||||
const std::vector<std::string>& values = def->enum_values;
|
||||
int val = conf_substitution.new_value->getInt();
|
||||
|
||||
bool is_infill = def->opt_key == "top_surface_pattern" ||
|
||||
def->opt_key == "bottom_surface_pattern" ||
|
||||
def->opt_key == "sparse_infill_pattern";
|
||||
|
||||
// Each infill doesn't use all list of infill declared in PrintConfig.hpp.
|
||||
// So we should "convert" val to the correct one
|
||||
if (is_infill) {
|
||||
for (const auto& key_val : *def->enum_keys_map)
|
||||
if ((int)key_val.second == val) {
|
||||
auto it = std::find(values.begin(), values.end(), key_val.first);
|
||||
if (it == values.end())
|
||||
break;
|
||||
auto idx = it - values.begin();
|
||||
new_val = wxString("\"") + values[idx] + "\"" + " (" + from_u8(_utf8(labels[idx])) + ")";
|
||||
break;
|
||||
}
|
||||
if (new_val.IsEmpty()) {
|
||||
assert(false);
|
||||
new_val = _L("Undefined");
|
||||
}
|
||||
}
|
||||
else
|
||||
new_val = wxString("\"") + values[val] + "\"" + " (" + from_u8(_utf8(labels[val])) + ")";
|
||||
break;
|
||||
}
|
||||
case coBool:
|
||||
new_val = conf_substitution.new_value->getBool() ? "true" : "false";
|
||||
break;
|
||||
case coBools:
|
||||
if (conf_substitution.new_value->nullable())
|
||||
for (const char v : static_cast<const ConfigOptionBoolsNullable*>(conf_substitution.new_value.get())->values)
|
||||
new_val += std::string(v == ConfigOptionBoolsNullable::nil_value() ? "nil" : v ? "true" : "false") + ", ";
|
||||
else
|
||||
for (const char v : static_cast<const ConfigOptionBools*>(conf_substitution.new_value.get())->values)
|
||||
new_val += std::string(v ? "true" : "false") + ", ";
|
||||
if (! new_val.empty())
|
||||
new_val.erase(new_val.begin() + new_val.size() - 2, new_val.end());
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
changes += format_wxstr("<tr><td><b>\"%1%\" (%2%)</b></td><td>: ", def->opt_key, _(def->label)) +
|
||||
format_wxstr(_L("%1% was replaced with %2%"), bold_string(conf_substitution.old_value), bold(new_val)) +
|
||||
"</td></tr>";
|
||||
}
|
||||
changes += "</table>";
|
||||
}
|
||||
|
||||
static wxString substitution_message(const wxString& changes)
|
||||
{
|
||||
return
|
||||
_L("The configuration may be generated by a newer version of BambuStudio.") + " " +
|
||||
_L("Some values have been replaced. Please check them:") + "\n" + changes + "\n";
|
||||
}
|
||||
|
||||
void show_substitutions_info(const PresetsConfigSubstitutions& presets_config_substitutions)
|
||||
{
|
||||
wxString changes;
|
||||
|
||||
auto preset_type_name = [](Preset::Type type) {
|
||||
switch (type) {
|
||||
case Preset::TYPE_PRINT: return _L("Process");
|
||||
// BBS: remove TYPE_SLA_PRINT
|
||||
case Preset::TYPE_FILAMENT: return _L("Filament");
|
||||
// BBS: remove TYPE_SLA_MATERIAL
|
||||
case Preset::TYPE_PRINTER: return _L("Machine");
|
||||
// BBS: remove TYPE_PHYSICAL_PRINTER
|
||||
default: assert(false); return wxString();
|
||||
}
|
||||
};
|
||||
|
||||
for (const PresetConfigSubstitutions& substitution : presets_config_substitutions) {
|
||||
changes += "\n\n" + format_wxstr("%1% : %2%", preset_type_name(substitution.preset_type), bold_string(substitution.preset_name));
|
||||
if (!substitution.preset_file.empty())
|
||||
changes += format_wxstr(" (%1%)", substitution.preset_file);
|
||||
|
||||
add_config_substitutions(substitution.substitutions, changes);
|
||||
}
|
||||
|
||||
InfoDialog msg(nullptr, _L("Configuration package was loaded, but some values were not recognized."), substitution_message(changes), true);
|
||||
msg.ShowModal();
|
||||
}
|
||||
|
||||
void show_substitutions_info(const ConfigSubstitutions& config_substitutions, const std::string& filename)
|
||||
{
|
||||
wxString changes = "\n";
|
||||
add_config_substitutions(config_substitutions, changes);
|
||||
|
||||
InfoDialog msg(nullptr,
|
||||
format_wxstr(_L("Configuration file \"%1%\" was loaded, but some values were not recognized."), from_u8(filename)),
|
||||
substitution_message(changes), true);
|
||||
msg.ShowModal();
|
||||
}
|
||||
|
||||
void create_combochecklist(wxComboCtrl* comboCtrl, const std::string& text, const std::string& items)
|
||||
{
|
||||
if (comboCtrl == nullptr)
|
||||
return;
|
||||
wxGetApp().UpdateDarkUI(comboCtrl);
|
||||
|
||||
wxCheckListBoxComboPopup* popup = new wxCheckListBoxComboPopup;
|
||||
if (popup != nullptr) {
|
||||
// FIXME If the following line is removed, the combo box popup list will not react to mouse clicks.
|
||||
// On the other side, with this line the combo box popup cannot be closed by clicking on the combo button on Windows 10.
|
||||
comboCtrl->UseAltPopupWindow();
|
||||
|
||||
int max_width = 0;
|
||||
|
||||
// the following line messes up the popup size the first time it is shown on wxWidgets 3.1.3
|
||||
// comboCtrl->EnablePopupAnimation(false);
|
||||
#ifdef _WIN32
|
||||
popup->SetFont(comboCtrl->GetFont());
|
||||
#endif // _WIN32
|
||||
comboCtrl->SetPopupControl(popup);
|
||||
wxString title = from_u8(text);
|
||||
max_width = std::max(max_width, 60 + comboCtrl->GetTextExtent(title).x);
|
||||
popup->SetStringValue(title);
|
||||
popup->Bind(wxEVT_CHECKLISTBOX, [popup](wxCommandEvent& evt) { popup->OnCheckListBox(evt); });
|
||||
popup->Bind(wxEVT_LISTBOX, [popup](wxCommandEvent& evt) { popup->OnListBoxSelection(evt); });
|
||||
popup->Bind(wxEVT_KEY_DOWN, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); });
|
||||
popup->Bind(wxEVT_KEY_UP, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); });
|
||||
|
||||
std::vector<std::string> items_str;
|
||||
boost::split(items_str, items, boost::is_any_of("|"), boost::token_compress_off);
|
||||
|
||||
// each item must be composed by 2 parts
|
||||
assert(items_str.size() %2 == 0);
|
||||
|
||||
for (size_t i = 0; i < items_str.size(); i += 2) {
|
||||
wxString label = from_u8(items_str[i]);
|
||||
max_width = std::max(max_width, 60 + popup->GetTextExtent(label).x);
|
||||
popup->Append(label);
|
||||
popup->Check(i / 2, items_str[i + 1] == "1");
|
||||
}
|
||||
|
||||
comboCtrl->SetMinClientSize(wxSize(max_width, -1));
|
||||
wxGetApp().UpdateDarkUI(popup);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int combochecklist_get_flags(wxComboCtrl* comboCtrl)
|
||||
{
|
||||
unsigned int flags = 0;
|
||||
|
||||
wxCheckListBoxComboPopup* popup = wxDynamicCast(comboCtrl->GetPopupControl(), wxCheckListBoxComboPopup);
|
||||
if (popup != nullptr) {
|
||||
for (unsigned int i = 0; i < popup->GetCount(); ++i) {
|
||||
if (popup->IsChecked(i))
|
||||
flags |= 1 << i;
|
||||
}
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
void combochecklist_set_flags(wxComboCtrl* comboCtrl, unsigned int flags)
|
||||
{
|
||||
wxCheckListBoxComboPopup* popup = wxDynamicCast(comboCtrl->GetPopupControl(), wxCheckListBoxComboPopup);
|
||||
if (popup != nullptr) {
|
||||
for (unsigned int i = 0; i < popup->GetCount(); ++i) {
|
||||
popup->Check(i, (flags & (1 << i)) != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AppConfig* get_app_config()
|
||||
{
|
||||
return wxGetApp().app_config;
|
||||
}
|
||||
|
||||
wxString from_u8(const std::string &str)
|
||||
{
|
||||
return wxString::FromUTF8(str.c_str());
|
||||
}
|
||||
|
||||
std::string into_u8(const wxString &str)
|
||||
{
|
||||
auto buffer_utf8 = str.utf8_str();
|
||||
return std::string(buffer_utf8.data());
|
||||
}
|
||||
|
||||
wxString from_path(const boost::filesystem::path &path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return wxString(path.string<std::wstring>());
|
||||
#else
|
||||
return from_u8(path.string<std::string>());
|
||||
#endif
|
||||
}
|
||||
|
||||
boost::filesystem::path into_path(const wxString &str)
|
||||
{
|
||||
return boost::filesystem::path(str.wx_str());
|
||||
}
|
||||
|
||||
void about()
|
||||
{
|
||||
AboutDialog dlg;
|
||||
dlg.ShowModal();
|
||||
}
|
||||
|
||||
void login()
|
||||
{
|
||||
//LoginDialog dlg;
|
||||
//dlg.ShowModal();
|
||||
|
||||
ZUserLogin dlg;
|
||||
dlg.run();
|
||||
}
|
||||
|
||||
void desktop_open_datadir_folder()
|
||||
{
|
||||
// Execute command to open a file explorer, platform dependent.
|
||||
// FIXME: The const_casts aren't needed in wxWidgets 3.1, remove them when we upgrade.
|
||||
|
||||
const auto path = data_dir();
|
||||
#ifdef _WIN32
|
||||
const wxString widepath = from_u8(path);
|
||||
const wchar_t *argv[] = { L"explorer", widepath.GetData(), nullptr };
|
||||
::wxExecute(const_cast<wchar_t**>(argv), wxEXEC_ASYNC, nullptr);
|
||||
#elif __APPLE__
|
||||
const char *argv[] = { "open", path.data(), nullptr };
|
||||
::wxExecute(const_cast<char**>(argv), wxEXEC_ASYNC, nullptr);
|
||||
#else
|
||||
const char *argv[] = { "xdg-open", path.data(), nullptr };
|
||||
|
||||
// Check if we're running in an AppImage container, if so, we need to remove AppImage's env vars,
|
||||
// because they may mess up the environment expected by the file manager.
|
||||
// Mostly this is about LD_LIBRARY_PATH, but we remove a few more too for good measure.
|
||||
if (wxGetEnv("APPIMAGE", nullptr)) {
|
||||
// We're running from AppImage
|
||||
wxEnvVariableHashMap env_vars;
|
||||
wxGetEnvMap(&env_vars);
|
||||
|
||||
env_vars.erase("APPIMAGE");
|
||||
env_vars.erase("APPDIR");
|
||||
env_vars.erase("LD_LIBRARY_PATH");
|
||||
env_vars.erase("LD_PRELOAD");
|
||||
env_vars.erase("UNION_PRELOAD");
|
||||
|
||||
wxExecuteEnv exec_env;
|
||||
exec_env.env = std::move(env_vars);
|
||||
|
||||
wxString owd;
|
||||
if (wxGetEnv("OWD", &owd)) {
|
||||
// This is the original work directory from which the AppImage image was run,
|
||||
// set it as CWD for the child process:
|
||||
exec_env.cwd = std::move(owd);
|
||||
}
|
||||
|
||||
::wxExecute(const_cast<char**>(argv), wxEXEC_ASYNC, nullptr, &exec_env);
|
||||
} else {
|
||||
// Looks like we're NOT running from AppImage, we'll make no changes to the environment.
|
||||
::wxExecute(const_cast<char**>(argv), wxEXEC_ASYNC, nullptr, nullptr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} }
|
||||
89
src/slic3r/GUI/GUI.hpp
Normal file
89
src/slic3r/GUI/GUI.hpp
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
#ifndef slic3r_GUI_hpp_
|
||||
#define slic3r_GUI_hpp_
|
||||
|
||||
namespace boost { class any; }
|
||||
namespace boost::filesystem { class path; }
|
||||
|
||||
#include <wx/string.h>
|
||||
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/Preset.hpp"
|
||||
|
||||
class wxWindow;
|
||||
class wxMenuBar;
|
||||
class wxComboCtrl;
|
||||
class wxFileDialog;
|
||||
class wxTopLevelWindow;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class AppConfig;
|
||||
class DynamicPrintConfig;
|
||||
class Print;
|
||||
|
||||
namespace GUI {
|
||||
|
||||
void disable_screensaver();
|
||||
void enable_screensaver();
|
||||
bool debugged();
|
||||
void break_to_debugger();
|
||||
|
||||
// Platform specific Ctrl+/Alt+ (Windows, Linux) vs. ⌘/⌥ (OSX) prefixes
|
||||
extern const std::string& shortkey_ctrl_prefix();
|
||||
extern const std::string& shortkey_alt_prefix();
|
||||
|
||||
extern AppConfig* get_app_config();
|
||||
|
||||
extern void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_language_change);
|
||||
|
||||
// Change option value in config
|
||||
void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index = 0);
|
||||
|
||||
// If monospaced_font is true, the error message is displayed using html <code><pre></pre></code> tags,
|
||||
// so that the code formatting will be preserved. This is useful for reporting errors from the placeholder parser.
|
||||
void show_error(wxWindow* parent, const wxString& message, bool monospaced_font = false);
|
||||
void show_error(wxWindow* parent, const char* message, bool monospaced_font = false);
|
||||
inline void show_error(wxWindow* parent, const std::string& message, bool monospaced_font = false) { show_error(parent, message.c_str(), monospaced_font); }
|
||||
void show_error_id(int id, const std::string& message); // For Perl
|
||||
void show_info(wxWindow* parent, const wxString& message, const wxString& title = wxString());
|
||||
void show_info(wxWindow* parent, const char* message, const char* title = nullptr);
|
||||
inline void show_info(wxWindow* parent, const std::string& message,const std::string& title = std::string()) { show_info(parent, message.c_str(), title.c_str()); }
|
||||
void warning_catcher(wxWindow* parent, const wxString& message);
|
||||
void show_substitutions_info(const PresetsConfigSubstitutions& presets_config_substitutions);
|
||||
void show_substitutions_info(const ConfigSubstitutions& config_substitutions, const std::string& filename);
|
||||
|
||||
// Creates a wxCheckListBoxComboPopup inside the given wxComboCtrl, filled with the given text and items.
|
||||
// Items data must be separated by '|', and contain the item name to be shown followed by its initial value (0 for false, 1 for true).
|
||||
// For example "Item1|0|Item2|1|Item3|0", and so on.
|
||||
void create_combochecklist(wxComboCtrl* comboCtrl, const std::string& text, const std::string& items);
|
||||
|
||||
// Returns the current state of the items listed in the wxCheckListBoxComboPopup contained in the given wxComboCtrl,
|
||||
// encoded inside an unsigned int.
|
||||
unsigned int combochecklist_get_flags(wxComboCtrl* comboCtrl);
|
||||
|
||||
// Sets the current state of the items listed in the wxCheckListBoxComboPopup contained in the given wxComboCtrl,
|
||||
// with the flags encoded in the given unsigned int.
|
||||
void combochecklist_set_flags(wxComboCtrl* comboCtrl, unsigned int flags);
|
||||
|
||||
// wxString conversions:
|
||||
|
||||
// wxString from std::string in UTF8
|
||||
wxString from_u8(const std::string &str);
|
||||
// std::string in UTF8 from wxString
|
||||
std::string into_u8(const wxString &str);
|
||||
// wxString from boost path
|
||||
wxString from_path(const boost::filesystem::path &path);
|
||||
// boost path from wxString
|
||||
boost::filesystem::path into_path(const wxString &str);
|
||||
|
||||
// Display an About dialog
|
||||
extern void about();
|
||||
// Display a Login dialog
|
||||
extern void login();
|
||||
// Ask the destop to open the datadir using the default file explorer.
|
||||
extern void desktop_open_datadir_folder();
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif
|
||||
4325
src/slic3r/GUI/GUI_App.cpp
Normal file
4325
src/slic3r/GUI/GUI_App.cpp
Normal file
File diff suppressed because it is too large
Load diff
516
src/slic3r/GUI/GUI_App.hpp
Normal file
516
src/slic3r/GUI/GUI_App.hpp
Normal file
|
|
@ -0,0 +1,516 @@
|
|||
#ifndef slic3r_GUI_App_hpp_
|
||||
#define slic3r_GUI_App_hpp_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "ImGuiWrapper.hpp"
|
||||
#include "ConfigWizard.hpp"
|
||||
#include "OpenGLManager.hpp"
|
||||
#include "libslic3r/Preset.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
#include "slic3r/Utils/NetworkAgent.hpp"
|
||||
#include "slic3r/GUI/WebViewDialog.hpp"
|
||||
|
||||
#include <wx/app.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/snglinst.h>
|
||||
#include <wx/msgdlg.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <stack>
|
||||
|
||||
#define BBL_HAS_FIRST_PAGE 1
|
||||
|
||||
class wxMenuItem;
|
||||
class wxMenuBar;
|
||||
class wxTopLevelWindow;
|
||||
class wxDataViewCtrl;
|
||||
class wxBookCtrlBase;
|
||||
// BBS
|
||||
class Notebook;
|
||||
struct wxLanguageInfo;
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class AppConfig;
|
||||
class PresetBundle;
|
||||
class PresetUpdater;
|
||||
class ModelObject;
|
||||
class Model;
|
||||
class DeviceManager;
|
||||
class NetworkAgent;
|
||||
|
||||
namespace GUI{
|
||||
|
||||
//class RemovableDriveManager;
|
||||
class OtherInstanceMessageHandler;
|
||||
class MainFrame;
|
||||
class Sidebar;
|
||||
class ObjectSettings;
|
||||
class ObjectList;
|
||||
class Plater;
|
||||
class ParamsPanel;
|
||||
class NotificationManager;
|
||||
struct GUI_InitParams;
|
||||
class ParamsDialog;
|
||||
|
||||
|
||||
enum FileType
|
||||
{
|
||||
FT_STEP,
|
||||
FT_STL,
|
||||
FT_OBJ,
|
||||
FT_AMF,
|
||||
FT_3MF,
|
||||
FT_GCODE,
|
||||
FT_MODEL,
|
||||
FT_PROJECT,
|
||||
FT_GALLERY,
|
||||
|
||||
FT_INI,
|
||||
FT_SVG,
|
||||
|
||||
FT_TEX,
|
||||
|
||||
FT_SL1,
|
||||
|
||||
FT_SIZE,
|
||||
};
|
||||
|
||||
extern wxString file_wildcards(FileType file_type, const std::string &custom_extension = std::string{});
|
||||
|
||||
enum ConfigMenuIDs {
|
||||
//ConfigMenuWizard,
|
||||
//ConfigMenuSnapshots,
|
||||
//ConfigMenuTakeSnapshot,
|
||||
//ConfigMenuUpdate,
|
||||
//ConfigMenuDesktopIntegration,
|
||||
ConfigMenuPreferences,
|
||||
ConfigMenuPrinter,
|
||||
//ConfigMenuModeSimple,
|
||||
//ConfigMenuModeAdvanced,
|
||||
//ConfigMenuLanguage,
|
||||
//ConfigMenuFlashFirmware,
|
||||
ConfigMenuCnt,
|
||||
};
|
||||
|
||||
enum CameraMenuIDs {
|
||||
wxID_CAMERA_PERSPECTIVE,
|
||||
wxID_CAMERA_ORTHOGONAL,
|
||||
wxID_CAMERA_COUNT,
|
||||
};
|
||||
|
||||
class Tab;
|
||||
class ConfigWizard;
|
||||
|
||||
static wxString dots("...", wxConvUTF8);
|
||||
|
||||
// Does our wxWidgets version support markup?
|
||||
#if wxUSE_MARKUP && wxCHECK_VERSION(3, 1, 1)
|
||||
#define SUPPORTS_MARKUP
|
||||
#endif
|
||||
|
||||
|
||||
#define VERSION_LEN 4
|
||||
class VersionInfo
|
||||
{
|
||||
public:
|
||||
std::string version_str;
|
||||
std::string version_name;
|
||||
std::string description;
|
||||
std::string url;
|
||||
bool force_upgrade{ false };
|
||||
int ver_items[VERSION_LEN]; // AA.BB.CC.DD
|
||||
VersionInfo() {
|
||||
for (int i = 0; i < VERSION_LEN; i++) {
|
||||
ver_items[i] = 0;
|
||||
}
|
||||
force_upgrade = false;
|
||||
}
|
||||
|
||||
void parse_version_str(std::string str) {
|
||||
version_str = str;
|
||||
std::vector<std::string> items;
|
||||
boost::split(items, str, boost::is_any_of("."));
|
||||
if (items.size() == VERSION_LEN) {
|
||||
try {
|
||||
for (int i = 0; i < VERSION_LEN; i++) {
|
||||
ver_items[i] = stoi(items[i]);
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
static std::string convert_full_version(std::string short_version);
|
||||
static std::string convert_short_version(std::string full_version);
|
||||
static std::string get_full_version() {
|
||||
return convert_full_version(SLIC3R_VERSION);
|
||||
}
|
||||
|
||||
/* return > 0, need update */
|
||||
int compare(std::string ver_str) {
|
||||
if (version_str.empty()) return -1;
|
||||
|
||||
int ver_target[VERSION_LEN];
|
||||
std::vector<std::string> items;
|
||||
boost::split(items, ver_str, boost::is_any_of("."));
|
||||
if (items.size() == VERSION_LEN) {
|
||||
try {
|
||||
for (int i = 0; i < VERSION_LEN; i++) {
|
||||
ver_target[i] = stoi(items[i]);
|
||||
if (ver_target[i] < ver_items[i]) {
|
||||
return 1;
|
||||
}
|
||||
else if (ver_target[i] == ver_items[i]) {
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
class GUI_App : public wxApp
|
||||
{
|
||||
public:
|
||||
//BBS: remove GCodeViewer as seperate APP logic
|
||||
enum class EAppMode : unsigned char
|
||||
{
|
||||
Editor,
|
||||
GCodeViewer
|
||||
};
|
||||
|
||||
private:
|
||||
bool m_initialized { false };
|
||||
bool m_app_conf_exists{ false };
|
||||
EAppMode m_app_mode{ EAppMode::Editor };
|
||||
bool m_is_recreating_gui{ false };
|
||||
#ifdef __linux__
|
||||
bool m_opengl_initialized{ false };
|
||||
#endif
|
||||
|
||||
wxColour m_color_label_modified;
|
||||
wxColour m_color_label_sys;
|
||||
wxColour m_color_label_default;
|
||||
wxColour m_color_window_default;
|
||||
// BBS
|
||||
//#ifdef _WIN32
|
||||
wxColour m_color_highlight_label_default;
|
||||
wxColour m_color_hovered_btn_label;
|
||||
wxColour m_color_highlight_default;
|
||||
wxColour m_color_selected_btn_bg;
|
||||
bool m_force_colors_update { false };
|
||||
//#endif
|
||||
|
||||
wxFont m_small_font;
|
||||
wxFont m_bold_font;
|
||||
wxFont m_normal_font;
|
||||
wxFont m_code_font;
|
||||
wxFont m_link_font;
|
||||
|
||||
int m_em_unit; // width of a "m"-symbol in pixels for current system font
|
||||
// Note: for 100% Scale m_em_unit = 10 -> it's a good enough coefficient for a size setting of controls
|
||||
|
||||
std::unique_ptr<wxLocale> m_wxLocale;
|
||||
// System language, from locales, owned by wxWidgets.
|
||||
const wxLanguageInfo *m_language_info_system = nullptr;
|
||||
// Best translation language, provided by Windows or OSX, owned by wxWidgets.
|
||||
const wxLanguageInfo *m_language_info_best = nullptr;
|
||||
|
||||
OpenGLManager m_opengl_mgr;
|
||||
//std::unique_ptr<RemovableDriveManager> m_removable_drive_manager;
|
||||
|
||||
std::unique_ptr<ImGuiWrapper> m_imgui;
|
||||
//std::unique_ptr <OtherInstanceMessageHandler> m_other_instance_message_handler;
|
||||
//std::unique_ptr <wxSingleInstanceChecker> m_single_instance_checker;
|
||||
//std::string m_instance_hash_string;
|
||||
//size_t m_instance_hash_int;
|
||||
|
||||
//BBS
|
||||
bool m_is_closing {false};
|
||||
Slic3r::DeviceManager* m_device_manager;
|
||||
NetworkAgent* m_agent { nullptr };
|
||||
std::vector<std::string> need_delete_presets; // store setting ids of preset
|
||||
|
||||
VersionInfo version_info;
|
||||
|
||||
boost::thread m_sync_update_thread;
|
||||
bool enable_sync = false;
|
||||
|
||||
public:
|
||||
bool OnInit() override;
|
||||
bool initialized() const { return m_initialized; }
|
||||
|
||||
//BBS: remove GCodeViewer as seperate APP logic
|
||||
explicit GUI_App();
|
||||
//explicit GUI_App(EAppMode mode = EAppMode::Editor);
|
||||
~GUI_App() override;
|
||||
|
||||
void show_message_box(std::string msg) { wxMessageBox(msg); }
|
||||
EAppMode get_app_mode() const { return m_app_mode; }
|
||||
Slic3r::DeviceManager* getDeviceManager() { return m_device_manager; }
|
||||
NetworkAgent* getAgent() { return m_agent; }
|
||||
bool is_editor() const { return m_app_mode == EAppMode::Editor; }
|
||||
bool is_gcode_viewer() const { return m_app_mode == EAppMode::GCodeViewer; }
|
||||
bool is_recreating_gui() const { return m_is_recreating_gui; }
|
||||
std::string logo_name() const { return is_editor() ? "BambuStudio" : "BambuStudio-gcodeviewer"; }
|
||||
|
||||
// To be called after the GUI is fully built up.
|
||||
// Process command line parameters cached in this->init_params,
|
||||
// load configs, STLs etc.
|
||||
void post_init();
|
||||
// If formatted for github, plaintext with OpenGL extensions enclosed into <details>.
|
||||
// Otherwise HTML formatted for the system info dialog.
|
||||
static std::string get_gl_info(bool for_github);
|
||||
wxGLContext* init_glcontext(wxGLCanvas& canvas);
|
||||
bool init_opengl();
|
||||
|
||||
static unsigned get_colour_approx_luma(const wxColour &colour);
|
||||
static bool dark_mode();
|
||||
const wxColour get_label_default_clr_system();
|
||||
const wxColour get_label_default_clr_modified();
|
||||
void init_label_colours();
|
||||
void update_label_colours_from_appconfig();
|
||||
void update_label_colours();
|
||||
// update color mode for window
|
||||
void UpdateDarkUI(wxWindow *window, bool highlited = false, bool just_font = false);
|
||||
// update color mode for whole dialog including all children
|
||||
void UpdateDlgDarkUI(wxDialog* dlg, bool just_buttons_update = false);
|
||||
// update color mode for DataViewControl
|
||||
void UpdateDVCDarkUI(wxDataViewCtrl* dvc, bool highlited = false);
|
||||
// update color mode for panel including all static texts controls
|
||||
void UpdateAllStaticTextDarkUI(wxWindow* parent);
|
||||
void init_fonts();
|
||||
void update_fonts(const MainFrame *main_frame = nullptr);
|
||||
void set_label_clr_modified(const wxColour& clr);
|
||||
void set_label_clr_sys(const wxColour& clr);
|
||||
|
||||
const wxColour& get_label_clr_modified(){ return m_color_label_modified; }
|
||||
const wxColour& get_label_clr_sys() { return m_color_label_sys; }
|
||||
const wxColour& get_label_clr_default() { return m_color_label_default; }
|
||||
const wxColour& get_window_default_clr(){ return m_color_window_default; }
|
||||
|
||||
// BBS
|
||||
//#ifdef _WIN32
|
||||
const wxColour& get_label_highlight_clr() { return m_color_highlight_label_default; }
|
||||
const wxColour& get_highlight_default_clr() { return m_color_highlight_default; }
|
||||
const wxColour& get_color_hovered_btn_label() { return m_color_hovered_btn_label; }
|
||||
const wxColour& get_color_selected_btn_bg() { return m_color_selected_btn_bg; }
|
||||
void force_colors_update();
|
||||
#ifdef _MSW_DARK_MODE
|
||||
void force_menu_update();
|
||||
#endif //_MSW_DARK_MODE
|
||||
//#endif
|
||||
|
||||
const wxFont& small_font() { return m_small_font; }
|
||||
const wxFont& bold_font() { return m_bold_font; }
|
||||
const wxFont& normal_font() { return m_normal_font; }
|
||||
const wxFont& code_font() { return m_code_font; }
|
||||
const wxFont& link_font() { return m_link_font; }
|
||||
int em_unit() const { return m_em_unit; }
|
||||
bool tabs_as_menu() const;
|
||||
wxSize get_min_size() const;
|
||||
float toolbar_icon_scale(const bool is_limited = false) const;
|
||||
void set_auto_toolbar_icon_scale(float scale) const;
|
||||
void check_printer_presets();
|
||||
|
||||
void recreate_GUI(const wxString& message);
|
||||
void system_info();
|
||||
void keyboard_shortcuts();
|
||||
void load_project(wxWindow *parent, wxString& input_file) const;
|
||||
void import_model(wxWindow *parent, wxArrayString& input_files) const;
|
||||
void load_gcode(wxWindow* parent, wxString& input_file) const;
|
||||
|
||||
void ShowUserGuide();
|
||||
void ShowDailyTip();
|
||||
void ShowUserLogin();
|
||||
void ShowOnlyFilament();
|
||||
//BBS
|
||||
void request_login(bool show_user_info = false);
|
||||
bool check_login();
|
||||
void get_login_info();
|
||||
bool is_user_login();
|
||||
|
||||
void request_user_login(int online_login);
|
||||
void request_user_logout();
|
||||
int request_user_unbind(std::string dev_id);
|
||||
std::string handle_web_request(std::string cmd);
|
||||
void handle_script_message(std::string msg);
|
||||
void request_model_download(std::string import_json);
|
||||
void download_project(std::string project_id);
|
||||
void request_project_download(std::string project_id);
|
||||
void request_open_project(std::string project_id);
|
||||
|
||||
void handle_http_error(unsigned int status, std::string body);
|
||||
void on_http_error(wxCommandEvent &evt);
|
||||
void on_user_login(wxCommandEvent &evt);
|
||||
|
||||
void check_update(bool show_tips);
|
||||
void check_new_version(bool show_tips = false);
|
||||
void request_new_version();
|
||||
void enter_force_upgrade();
|
||||
void no_new_version();
|
||||
void reload_settings();
|
||||
void remove_user_presets();
|
||||
void sync_preset(Preset* preset);
|
||||
void start_sync_user_preset(bool with_progress_dlg = false);
|
||||
void stop_sync_user_preset();
|
||||
|
||||
static bool catch_error(std::function<void()> cb, const std::string& err);
|
||||
|
||||
void persist_window_geometry(wxTopLevelWindow *window, bool default_maximized = false);
|
||||
void update_ui_from_settings();
|
||||
|
||||
bool switch_language();
|
||||
bool load_language(wxString language, bool initial);
|
||||
|
||||
Tab* get_tab(Preset::Type type);
|
||||
Tab* get_model_tab(bool part = false);
|
||||
ConfigOptionMode get_mode();
|
||||
void save_mode(const /*ConfigOptionMode*/int mode) ;
|
||||
void update_mode();
|
||||
|
||||
// BBS
|
||||
//void add_config_menu(wxMenuBar *menu);
|
||||
//void add_config_menu(wxMenu* menu);
|
||||
bool has_unsaved_preset_changes() const;
|
||||
bool has_current_preset_changes() const;
|
||||
void update_saved_preset_from_current_preset();
|
||||
std::vector<std::pair<unsigned int, std::string>> get_selected_presets() const;
|
||||
bool check_and_save_current_preset_changes(const wxString& caption, const wxString& header, bool remember_choice = true, bool use_dont_save_insted_of_discard = false);
|
||||
void apply_keeped_preset_modifications();
|
||||
bool check_and_keep_current_preset_changes(const wxString& caption, const wxString& header, int action_buttons, bool* postponed_apply_of_keeped_changes = nullptr);
|
||||
bool can_load_project();
|
||||
bool checked_tab(Tab* tab);
|
||||
//BBS: add preset combox re-active logic
|
||||
void load_current_presets(bool active_preset_combox = false, bool check_printer_presets = true);
|
||||
std::vector<std::string>& get_delete_cache_presets();
|
||||
void delete_preset_from_cloud(std::string setting_id);
|
||||
|
||||
wxString current_language_code() const { return m_wxLocale->GetCanonicalName(); }
|
||||
// Translate the language code to a code, for which Prusa Research maintains translations. Defaults to "en_US".
|
||||
wxString current_language_code_safe() const;
|
||||
bool is_localized() const { return m_wxLocale->GetLocale() != "English"; }
|
||||
|
||||
void open_preferences(size_t open_on_tab = 0, const std::string& highlight_option = std::string());
|
||||
|
||||
virtual bool OnExceptionInMainLoop() override;
|
||||
// Calls wxLaunchDefaultBrowser if user confirms in dialog.
|
||||
bool open_browser_with_warning_dialog(const wxString& url, int flags = 0);
|
||||
#ifdef __APPLE__
|
||||
void OSXStoreOpenFiles(const wxArrayString &files);
|
||||
// wxWidgets override to get an event on open files.
|
||||
void MacOpenFiles(const wxArrayString &fileNames) override;
|
||||
#endif /* __APPLE */
|
||||
|
||||
Sidebar& sidebar();
|
||||
ObjectSettings* obj_settings();
|
||||
ObjectList* obj_list();
|
||||
Plater* plater();
|
||||
const Plater* plater() const;
|
||||
ParamsPanel* params_panel();
|
||||
ParamsDialog* params_dialog();
|
||||
Model& model();
|
||||
NotificationManager * notification_manager();
|
||||
//BBS
|
||||
void load_url(wxString url);
|
||||
void run_script(wxString js);
|
||||
|
||||
// Parameters extracted from the command line to be passed to GUI after initialization.
|
||||
GUI_InitParams* init_params { nullptr };
|
||||
|
||||
AppConfig* app_config{ nullptr };
|
||||
PresetBundle* preset_bundle{ nullptr };
|
||||
PresetUpdater* preset_updater{ nullptr };
|
||||
MainFrame* mainframe{ nullptr };
|
||||
Plater* plater_{ nullptr };
|
||||
|
||||
PresetUpdater* get_preset_updater() { return preset_updater; }
|
||||
|
||||
Notebook* tab_panel() const ;
|
||||
int extruders_cnt() const;
|
||||
int extruders_edited_cnt() const;
|
||||
|
||||
// BBS
|
||||
int filaments_cnt() const;
|
||||
|
||||
std::vector<Tab *> tabs_list;
|
||||
std::vector<Tab *> model_tabs_list;
|
||||
|
||||
//RemovableDriveManager* removable_drive_manager() { return m_removable_drive_manager.get(); }
|
||||
//OtherInstanceMessageHandler* other_instance_message_handler() { return m_other_instance_message_handler.get(); }
|
||||
//wxSingleInstanceChecker* single_instance_checker() {return m_single_instance_checker.get();}
|
||||
|
||||
//void init_single_instance_checker(const std::string &name, const std::string &path);
|
||||
//void set_instance_hash (const size_t hash) { m_instance_hash_int = hash; m_instance_hash_string = std::to_string(hash); }
|
||||
//std::string get_instance_hash_string () { return m_instance_hash_string; }
|
||||
//size_t get_instance_hash_int () { return m_instance_hash_int; }
|
||||
|
||||
ImGuiWrapper* imgui() { return m_imgui.get(); }
|
||||
|
||||
void open_web_page_localized(const std::string &http_address);
|
||||
bool may_switch_to_SLA_preset(const wxString& caption);
|
||||
bool run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage start_page = ConfigWizard::SP_WELCOME);
|
||||
void show_desktop_integration_dialog();
|
||||
|
||||
#if ENABLE_THUMBNAIL_GENERATOR_DEBUG
|
||||
// temporary and debug only -> extract thumbnails from selected gcode and save them as png files
|
||||
void gcode_thumbnails_debug();
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG
|
||||
|
||||
OpenGLManager& get_opengl_manager() { return m_opengl_mgr; }
|
||||
GLShaderProgram* get_shader(const std::string& shader_name) { return m_opengl_mgr.get_shader(shader_name); }
|
||||
GLShaderProgram* get_current_shader() { return m_opengl_mgr.get_current_shader(); }
|
||||
|
||||
bool is_gl_version_greater_or_equal_to(unsigned int major, unsigned int minor) const { return m_opengl_mgr.get_gl_info().is_version_greater_or_equal_to(major, minor); }
|
||||
bool is_glsl_version_greater_or_equal_to(unsigned int major, unsigned int minor) const { return m_opengl_mgr.get_gl_info().is_glsl_version_greater_or_equal_to(major, minor); }
|
||||
int GetSingleChoiceIndex(const wxString& message, const wxString& caption, const wxArrayString& choices, int initialSelection);
|
||||
|
||||
#ifdef __WXMSW__
|
||||
// extend is stl/3mf/gcode/step etc
|
||||
void associate_files(std::wstring extend);
|
||||
void disassociate_files(std::wstring extend);
|
||||
#endif // __WXMSW__
|
||||
|
||||
private:
|
||||
bool on_init_inner();
|
||||
void init_networking_callbacks();
|
||||
void init_app_config();
|
||||
//BBS set extra header for http request
|
||||
void init_http_extra_header();
|
||||
bool check_older_app_config(Semver current_version, bool backup);
|
||||
void copy_older_config();
|
||||
void window_pos_save(wxTopLevelWindow* window, const std::string &name);
|
||||
bool window_pos_restore(wxTopLevelWindow* window, const std::string &name, bool default_maximized = false);
|
||||
void window_pos_sanitize(wxTopLevelWindow* window);
|
||||
void window_pos_center(wxTopLevelWindow *window);
|
||||
bool select_language();
|
||||
|
||||
bool config_wizard_startup();
|
||||
void check_updates(const bool verbose);
|
||||
|
||||
bool m_init_app_config_from_older { false };
|
||||
bool m_datadir_redefined { false };
|
||||
std::string m_older_data_dir_path;
|
||||
boost::optional<Semver> m_last_config_version;
|
||||
};
|
||||
|
||||
DECLARE_APP(GUI_App)
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
|
||||
#endif // slic3r_GUI_App_hpp_
|
||||
299
src/slic3r/GUI/GUI_AuxiliaryList.cpp
Normal file
299
src/slic3r/GUI/GUI_AuxiliaryList.cpp
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
#include <wx/button.h>
|
||||
#include "GUI_AuxiliaryList.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include "GUI_App.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
|
||||
using namespace Slic3r::GUI;
|
||||
using namespace Slic3r;
|
||||
|
||||
AuxiliaryList::AuxiliaryList(wxWindow* parent)
|
||||
: wxDataViewCtrl(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDV_NO_HEADER)
|
||||
{
|
||||
wxDataViewTextRenderer* tr = new wxDataViewTextRenderer("string", wxDATAVIEW_CELL_INERT);
|
||||
wxDataViewColumn* column0 = new wxDataViewColumn("", tr, 0, 200, wxALIGN_LEFT,
|
||||
wxDATAVIEW_COL_SORTABLE | wxDATAVIEW_COL_RESIZABLE);
|
||||
this->AppendColumn(column0);
|
||||
|
||||
m_auxiliary_model = new AuxiliaryModel();
|
||||
this->AssociateModel(m_auxiliary_model);
|
||||
m_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_sizer->Add(this, 1, wxEXPAND | wxALL, 0);
|
||||
|
||||
wxPanel* panel = new wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(-1, FromDIP(21)));
|
||||
//panel->SetBackgroundColour(*wxLIGHT_GREY);
|
||||
|
||||
#if 0
|
||||
wxBitmap if_bitmap = create_scaled_bitmap("import_file.png", nullptr, FromDIP(21));
|
||||
wxBitmap nf_bitmap = create_scaled_bitmap("new_folder.png", nullptr, FromDIP(21));
|
||||
wxBitmap del_bitmap = create_scaled_bitmap("delete.png", nullptr, FromDIP(21));
|
||||
|
||||
wxBitmapButton* m_if_btn = new wxBitmapButton(panel, wxID_OPEN, if_bitmap);
|
||||
wxBitmapButton* m_nf_btn = new wxBitmapButton(panel, wxID_NEW, nf_bitmap);
|
||||
wxBitmapButton* m_del_btn = new wxBitmapButton(panel, wxID_DELETE, del_bitmap);
|
||||
#endif
|
||||
|
||||
//m_nf_btn = new wxButton(panel, wxID_NEW, _L("New Folder"));
|
||||
m_if_btn = new wxButton(panel, wxID_ADD, _L("Import File"));
|
||||
m_of_btn = new wxButton(panel, wxID_OPEN, _("Open File"));
|
||||
m_del_btn = new wxButton(panel, wxID_DELETE, _L("Delete"));
|
||||
|
||||
wxBoxSizer* hsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
//hsizer->Add(m_nf_btn, 0, wxRIGHT, 5);
|
||||
hsizer->Add(m_if_btn, 0, wxLEFT | wxRIGHT, 5);
|
||||
hsizer->Add(m_of_btn, 0, wxLEFT | wxRIGHT, 5);
|
||||
hsizer->Add(m_del_btn, 0, wxLEFT | wxRIGHT, 5);
|
||||
panel->SetSizer(hsizer);
|
||||
|
||||
m_sizer->Add(panel, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
EnableDragSource(wxDF_UNICODETEXT);
|
||||
EnableDropTarget(wxDF_UNICODETEXT);
|
||||
|
||||
// Keyboard events
|
||||
Bind(wxEVT_CHAR, [this](wxKeyEvent& event) { this->handle_key_event(event); });
|
||||
|
||||
// Button events
|
||||
//m_nf_btn->Bind(wxEVT_BUTTON, &AuxiliaryList::on_create_folder, this, wxID_NEW);
|
||||
m_if_btn->Bind(wxEVT_BUTTON, &AuxiliaryList::on_import_file, this, wxID_ADD);
|
||||
m_del_btn->Bind(wxEVT_BUTTON, &AuxiliaryList::on_delete, this, wxID_DELETE);
|
||||
m_of_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) {
|
||||
wxDataViewItem sel_item = this->GetSelection();
|
||||
AuxiliaryModelNode* sel = (AuxiliaryModelNode*)sel_item.GetID();
|
||||
if (sel != nullptr && !sel->IsContainer()) {
|
||||
wxLaunchDefaultApplication(sel->path, 0);
|
||||
}
|
||||
else {
|
||||
evt.Skip();
|
||||
}
|
||||
}, wxID_OPEN);
|
||||
|
||||
// Dataview events
|
||||
this->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &AuxiliaryList::on_context_menu, this);
|
||||
this->Bind(wxEVT_DATAVIEW_ITEM_BEGIN_DRAG, &AuxiliaryList::on_begin_drag, this);
|
||||
this->Bind(wxEVT_DATAVIEW_ITEM_DROP_POSSIBLE, &AuxiliaryList::on_drop_possible, this);
|
||||
this->Bind(wxEVT_DATAVIEW_ITEM_DROP, &AuxiliaryList::on_drop, this);
|
||||
this->Bind(wxEVT_DATAVIEW_ITEM_EDITING_STARTED, &AuxiliaryList::on_editing_started, this);
|
||||
this->Bind(wxEVT_DATAVIEW_ITEM_EDITING_DONE, &AuxiliaryList::on_editing_done, this);
|
||||
|
||||
// Mouse events
|
||||
wxWindow* win = this->GetMainWindow();
|
||||
win->Bind(wxEVT_LEFT_DCLICK, &AuxiliaryList::on_left_dclick, this);
|
||||
|
||||
Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxDataViewEvent& event) {
|
||||
wxDataViewItem sel_item = event.GetItem();
|
||||
AuxiliaryModelNode* sel_node = (AuxiliaryModelNode*)sel_item.GetID();
|
||||
if (sel_node == nullptr)
|
||||
return;
|
||||
|
||||
m_del_btn->Enable(!sel_node->IsContainer());
|
||||
});
|
||||
}
|
||||
|
||||
AuxiliaryList::~AuxiliaryList()
|
||||
{
|
||||
this->AssociateModel(nullptr);
|
||||
delete m_auxiliary_model;
|
||||
}
|
||||
|
||||
void AuxiliaryList::init_auxiliary()
|
||||
{
|
||||
Model& model = wxGetApp().plater()->model();
|
||||
std::string aux_path = encode_path(model.get_auxiliary_file_temp_path().c_str());
|
||||
m_auxiliary_model->Init(aux_path);
|
||||
}
|
||||
|
||||
void AuxiliaryList::reload(wxString aux_path)
|
||||
{
|
||||
m_auxiliary_model->Reload(aux_path);
|
||||
|
||||
wxDataViewItemArray items;
|
||||
m_auxiliary_model->GetChildren(wxDataViewItem(nullptr), items);
|
||||
for (wxDataViewItem item : items) {
|
||||
Expand(item);
|
||||
}
|
||||
}
|
||||
|
||||
void AuxiliaryList::create_new_folder()
|
||||
{
|
||||
wxDataViewItem folder_item = m_auxiliary_model->CreateFolder(wxEmptyString);
|
||||
AuxiliaryModelNode* folder = (AuxiliaryModelNode*)folder_item.GetID();
|
||||
if (folder == nullptr)
|
||||
return;
|
||||
|
||||
Select(folder_item);
|
||||
|
||||
wxDataViewColumn* col = GetColumn(0);
|
||||
wxDataViewCellMode mode = col->GetRenderer()->GetMode();
|
||||
col->GetRenderer()->SetMode(wxDATAVIEW_CELL_EDITABLE);
|
||||
EditItem(folder_item, col);
|
||||
col->GetRenderer()->SetMode(mode);
|
||||
}
|
||||
|
||||
void AuxiliaryList::do_import_file(AuxiliaryModelNode* folder)
|
||||
{
|
||||
if (folder == nullptr || !folder->IsContainer())
|
||||
return;
|
||||
|
||||
wxString src_path;
|
||||
wxString dst_path;
|
||||
wxFileDialog dialog(this, _L("Choose files"), wxEmptyString, wxEmptyString,
|
||||
wxFileSelectorDefaultWildcardStr, wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE);
|
||||
if (dialog.ShowModal() == wxID_OK) {
|
||||
wxArrayString sel_paths;
|
||||
dialog.GetPaths(sel_paths);
|
||||
wxDataViewItemArray file_items = m_auxiliary_model->ImportFile(folder, sel_paths);
|
||||
if (!file_items.empty()) {
|
||||
wxDataViewItem file_item = file_items[0];
|
||||
AuxiliaryModelNode* file_node = (AuxiliaryModelNode*)file_item.GetID();
|
||||
if (file_node != nullptr) {
|
||||
if (!m_auxiliary_model->IsOrphan(file_item)) {
|
||||
Expand(wxDataViewItem(file_node->GetParent()));
|
||||
}
|
||||
Select(file_item);
|
||||
m_del_btn->Enable(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AuxiliaryList::on_create_folder(wxCommandEvent& evt)
|
||||
{
|
||||
create_new_folder();
|
||||
}
|
||||
|
||||
void AuxiliaryList::on_import_file(wxCommandEvent& evt)
|
||||
{
|
||||
wxDataViewItem sel_item = this->GetSelection();
|
||||
AuxiliaryModelNode* sel_node = (AuxiliaryModelNode*)sel_item.GetID();
|
||||
if (sel_node == nullptr)
|
||||
return;
|
||||
|
||||
AuxiliaryModelNode* folder_node = sel_node;
|
||||
if (!folder_node->IsContainer()) {
|
||||
wxDataViewItem folder_item = m_auxiliary_model->GetParent(sel_item);
|
||||
folder_node = (AuxiliaryModelNode*)folder_item.GetID();
|
||||
|
||||
if (folder_node == nullptr)
|
||||
return;
|
||||
}
|
||||
|
||||
do_import_file(folder_node);
|
||||
}
|
||||
|
||||
void AuxiliaryList::on_delete(wxCommandEvent& evt)
|
||||
{
|
||||
m_auxiliary_model->Delete(this->GetSelection());
|
||||
}
|
||||
|
||||
void AuxiliaryList::on_context_menu(wxDataViewEvent& evt)
|
||||
{
|
||||
wxMenu* menu = new wxMenu();
|
||||
wxDataViewItem item = evt.GetItem();
|
||||
AuxiliaryModelNode* node = (AuxiliaryModelNode*)item.GetID();
|
||||
if (node == nullptr) {
|
||||
append_menu_item(menu, wxID_ANY, _L("New Folder"), wxEmptyString,
|
||||
[this](wxCommandEvent&)
|
||||
{
|
||||
create_new_folder();
|
||||
});
|
||||
}
|
||||
else if (node->IsContainer()) {
|
||||
append_menu_item(menu, wxID_ANY, _L("Import File"), wxEmptyString,
|
||||
[this, node](wxCommandEvent&)
|
||||
{
|
||||
do_import_file(node);
|
||||
});
|
||||
append_menu_item(menu, wxID_ANY, _L("Delete"), wxEmptyString,
|
||||
[this, item](wxCommandEvent&)
|
||||
{
|
||||
m_auxiliary_model->Delete(item);
|
||||
});
|
||||
}
|
||||
else {
|
||||
append_menu_item(menu, wxID_ANY, _L("Open"), wxEmptyString,
|
||||
[this, node](wxCommandEvent&)
|
||||
{
|
||||
wxLaunchDefaultApplication(node->path, 0);
|
||||
});
|
||||
append_menu_item(menu, wxID_ANY, _L("Delete"), wxEmptyString,
|
||||
[this, item](wxCommandEvent&)
|
||||
{
|
||||
m_auxiliary_model->Delete(item);
|
||||
});
|
||||
append_menu_item(menu, wxID_ANY, _L("Rename"), wxEmptyString,
|
||||
[this, item](wxCommandEvent&)
|
||||
{
|
||||
wxDataViewColumn* col = this->GetColumn(0);
|
||||
wxDataViewCellMode mode = col->GetRenderer()->GetMode();
|
||||
col->GetRenderer()->SetMode(wxDATAVIEW_CELL_EDITABLE);
|
||||
this->EditItem(item, col);
|
||||
col->GetRenderer()->SetMode(mode);
|
||||
});
|
||||
}
|
||||
|
||||
PopupMenu(menu);
|
||||
}
|
||||
|
||||
void AuxiliaryList::on_begin_drag(wxDataViewEvent& evt)
|
||||
{
|
||||
wxDataViewItem sel_item = evt.GetItem();
|
||||
AuxiliaryModelNode* sel = (AuxiliaryModelNode*)sel_item.GetID();
|
||||
if (sel == nullptr || sel->IsContainer())
|
||||
return;
|
||||
|
||||
m_dragged_item = sel_item;
|
||||
|
||||
wxTextDataObject* obj = new wxTextDataObject;
|
||||
obj->SetText("Some text");
|
||||
evt.SetDataObject(obj);
|
||||
evt.SetDragFlags(wxDrag_DefaultMove);
|
||||
}
|
||||
|
||||
void AuxiliaryList::on_drop_possible(wxDataViewEvent& evt)
|
||||
{
|
||||
evt.Allow();
|
||||
}
|
||||
|
||||
void AuxiliaryList::on_drop(wxDataViewEvent& evt)
|
||||
{
|
||||
m_auxiliary_model->MoveItem(evt.GetItem(), m_dragged_item);
|
||||
|
||||
Expand(evt.GetItem());
|
||||
Select(m_dragged_item);
|
||||
m_dragged_item = wxDataViewItem(nullptr);
|
||||
}
|
||||
|
||||
void AuxiliaryList::on_editing_started(wxDataViewEvent& evt)
|
||||
{
|
||||
}
|
||||
|
||||
void AuxiliaryList::on_editing_done(wxDataViewEvent& evt)
|
||||
{
|
||||
bool is_done = m_auxiliary_model->Rename(evt.GetItem(), evt.GetValue().GetString());
|
||||
if (!is_done)
|
||||
evt.Veto();
|
||||
}
|
||||
|
||||
void AuxiliaryList::on_left_dclick(wxMouseEvent& evt)
|
||||
{
|
||||
wxDataViewItem sel_item = this->GetSelection();
|
||||
AuxiliaryModelNode* sel = (AuxiliaryModelNode*)sel_item.GetID();
|
||||
if (sel != nullptr && !sel->IsContainer()) {
|
||||
wxLaunchDefaultApplication(sel->path, 0);
|
||||
}
|
||||
else {
|
||||
evt.Skip();
|
||||
}
|
||||
}
|
||||
|
||||
void AuxiliaryList::handle_key_event(wxKeyEvent& evt)
|
||||
{
|
||||
if (evt.GetKeyCode() == WXK_DELETE || evt.GetKeyCode() == WXK_BACK)
|
||||
m_auxiliary_model->Delete(this->GetSelection());
|
||||
}
|
||||
52
src/slic3r/GUI/GUI_AuxiliaryList.hpp
Normal file
52
src/slic3r/GUI/GUI_AuxiliaryList.hpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#ifndef slic3r_GUI_AuxiliaryList_hpp_
|
||||
#define slic3r_GUI_AuxiliaryList_hpp_
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/menu.h>
|
||||
#include <wx/file.h>
|
||||
#include <wx/dir.h>
|
||||
|
||||
#include "AuxiliaryDataViewModel.hpp"
|
||||
|
||||
class AuxiliaryList : public wxDataViewCtrl
|
||||
{
|
||||
public:
|
||||
AuxiliaryList(wxWindow* parent);
|
||||
~AuxiliaryList();
|
||||
wxSizer* get_top_sizer() { return m_sizer; }
|
||||
void init_auxiliary();
|
||||
void reload(wxString aux_path);
|
||||
|
||||
private:
|
||||
void do_import_file(AuxiliaryModelNode* folder);
|
||||
void on_create_folder(wxCommandEvent& evt);
|
||||
void on_import_file(wxCommandEvent& evt);
|
||||
void on_delete(wxCommandEvent& evt);
|
||||
void on_context_menu(wxDataViewEvent& evt);
|
||||
void on_begin_drag(wxDataViewEvent& evt);
|
||||
void on_drop_possible(wxDataViewEvent& evt);
|
||||
void on_drop(wxDataViewEvent& evt);
|
||||
void on_editing_started(wxDataViewEvent& evt);
|
||||
void on_editing_done(wxDataViewEvent& evt);
|
||||
void on_left_dclick(wxMouseEvent& evt);
|
||||
|
||||
void create_new_folder();
|
||||
void handle_key_event(wxKeyEvent& evt);
|
||||
|
||||
wxDataViewItem m_dragged_item;
|
||||
AuxiliaryModel* m_auxiliary_model;
|
||||
wxSizer* m_sizer;
|
||||
|
||||
//wxButton* m_nf_btn;
|
||||
wxButton* m_if_btn;
|
||||
wxButton* m_of_btn;
|
||||
wxButton* m_del_btn;
|
||||
};
|
||||
|
||||
#endif //slic3r_GUI_AuxiliaryList_hpp_
|
||||
|
||||
39
src/slic3r/GUI/GUI_Colors.cpp
Normal file
39
src/slic3r/GUI/GUI_Colors.cpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#include "GUI_Colors.hpp"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
|
||||
namespace Slic3r{
|
||||
|
||||
ImVec4 RenderColor::colors[RenderCol_Count] = { };
|
||||
|
||||
const char* GetRenderColName(RenderCol idx)
|
||||
{
|
||||
switch (idx)
|
||||
{
|
||||
case RenderCol_3D_Background: return "3D Background";
|
||||
case RenderCol_Plate_Unselected: return "Plate Unselected";
|
||||
case RenderCol_Plate_Selected: return "Plate Selected";
|
||||
case RenderCol_Plate_Default: return "Plate Default";
|
||||
case RenderCol_Plate_Line_Top: return "Plate Line Top";
|
||||
case RenderCol_Plate_Line_Bottom: return "Plate Line Bottom";
|
||||
case RenderCol_Model_Disable: return "Model Disable";
|
||||
case RenderCol_Model_Unprintable: return "Model Unprintable";
|
||||
case RenderCol_Model_Neutral: return "Model Neutral";
|
||||
case RenderCol_Part: return "Part";
|
||||
case RenderCol_Modifier: return "Modifier";
|
||||
case RenderCol_Negtive_Volume: return "Negtive Volume";
|
||||
case RenderCol_Support_Enforcer: return "Support Enforcer";
|
||||
case RenderCol_Support_Blocker: return "Support Blocker";
|
||||
case RenderCol_Axis_X: return "Axis X";
|
||||
case RenderCol_Axis_Y: return "Axis Y";
|
||||
case RenderCol_Axis_Z: return "Axis Z";
|
||||
case RenderCol_Grabber_X: return "Grabber X";
|
||||
case RenderCol_Grabber_Y: return "Grabber Y";
|
||||
case RenderCol_Grabber_Z: return "Grabber Z";
|
||||
case RenderCol_Flatten_Plane: return "Flatten Plane";
|
||||
case RenderCol_Flatten_Plane_Hover: return "Flatten Plane Hover";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
}
|
||||
51
src/slic3r/GUI/GUI_Colors.hpp
Normal file
51
src/slic3r/GUI/GUI_Colors.hpp
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#ifndef slic3r_GUI_Colors_hpp_
|
||||
#define slic3r_GUI_Colors_hpp_
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
enum RenderCol_ {
|
||||
RenderCol_3D_Background = 0,
|
||||
RenderCol_Plate_Unselected,
|
||||
RenderCol_Plate_Selected,
|
||||
RenderCol_Plate_Default,
|
||||
RenderCol_Plate_Line_Top,
|
||||
RenderCol_Plate_Line_Bottom,
|
||||
RenderCol_Model_Disable,
|
||||
RenderCol_Model_Unprintable,
|
||||
RenderCol_Model_Neutral,
|
||||
RenderCol_Part,
|
||||
RenderCol_Modifier,
|
||||
RenderCol_Negtive_Volume,
|
||||
RenderCol_Support_Enforcer,
|
||||
RenderCol_Support_Blocker,
|
||||
RenderCol_Axis_X,
|
||||
RenderCol_Axis_Y,
|
||||
RenderCol_Axis_Z,
|
||||
RenderCol_Grabber_X,
|
||||
RenderCol_Grabber_Y,
|
||||
RenderCol_Grabber_Z,
|
||||
RenderCol_Flatten_Plane,
|
||||
RenderCol_Flatten_Plane_Hover,
|
||||
RenderCol_Count,
|
||||
};
|
||||
|
||||
typedef int RenderCol;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class RenderColor {
|
||||
public:
|
||||
static ImVec4 colors[RenderCol_Count];
|
||||
};
|
||||
const char* GetRenderColName(RenderCol idx);
|
||||
inline std::array<float, 4> GLColor(ImVec4 color) {
|
||||
return {color.x, color.y, color.z, color.w };
|
||||
}
|
||||
|
||||
inline ImVec4 IMColor(std::array<float, 4> color) {
|
||||
return ImVec4(color[0], color[1], color[2], color[3]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
1477
src/slic3r/GUI/GUI_Factories.cpp
Normal file
1477
src/slic3r/GUI/GUI_Factories.cpp
Normal file
File diff suppressed because it is too large
Load diff
150
src/slic3r/GUI/GUI_Factories.hpp
Normal file
150
src/slic3r/GUI/GUI_Factories.hpp
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
#ifndef slic3r_GUI_Factories_hpp_
|
||||
#define slic3r_GUI_Factories_hpp_
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
#include <wx/bitmap.h>
|
||||
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
class wxMenu;
|
||||
class wxMenuItem;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
enum class ModelVolumeType : int;
|
||||
|
||||
namespace GUI {
|
||||
|
||||
struct SimpleSettingData
|
||||
{
|
||||
std::string name;
|
||||
std::string label;
|
||||
int priority;
|
||||
};
|
||||
|
||||
struct SettingsFactory
|
||||
{
|
||||
// category -> vector ( option )
|
||||
typedef std::map<std::string, std::vector<std::string>> Bundle;
|
||||
static std::map<std::string, std::string> CATEGORY_ICON;
|
||||
|
||||
//BBS: add setting data for table
|
||||
static std::map<std::string, std::vector<SimpleSettingData>> OBJECT_CATEGORY_SETTINGS;
|
||||
static std::map<std::string, std::vector<SimpleSettingData>> PART_CATEGORY_SETTINGS;
|
||||
|
||||
static wxBitmap get_category_bitmap(const std::string& category_name, bool menu_bmp = true);
|
||||
static Bundle get_bundle(const DynamicPrintConfig* config, bool is_object_settings);
|
||||
static std::vector<std::string> get_options(bool is_part);
|
||||
//BBS: add api to get options for catogary
|
||||
static std::vector<SimpleSettingData> get_visible_options(const std::string& category, const bool is_part);
|
||||
static std::map<std::string, std::vector<SimpleSettingData>> get_all_visible_options(const bool is_part);
|
||||
};
|
||||
|
||||
class MenuFactory
|
||||
{
|
||||
public:
|
||||
static const std::vector<std::pair<std::string, std::string>> ADD_VOLUME_MENU_ITEMS;
|
||||
static std::vector<wxBitmap> get_volume_bitmaps();
|
||||
|
||||
MenuFactory();
|
||||
~MenuFactory() = default;
|
||||
|
||||
void init(wxWindow* parent);
|
||||
void update();
|
||||
void update_object_menu();
|
||||
void update_default_menu();
|
||||
void msw_rescale();
|
||||
void sys_color_changed();
|
||||
|
||||
static void sys_color_changed(wxMenuBar* menu_bar);
|
||||
|
||||
wxMenu* default_menu();
|
||||
wxMenu* object_menu();
|
||||
wxMenu* sla_object_menu();
|
||||
wxMenu* part_menu();
|
||||
wxMenu* instance_menu();
|
||||
wxMenu* layer_menu();
|
||||
wxMenu* multi_selection_menu();
|
||||
//BBS: add part plate related logic
|
||||
wxMenu* plate_menu();
|
||||
wxMenu* assemble_object_menu();
|
||||
wxMenu* assemble_part_menu();
|
||||
wxMenu* assemble_multi_selection_menu();
|
||||
|
||||
private:
|
||||
enum MenuType {
|
||||
mtObjectFFF = 0,
|
||||
mtObjectSLA,
|
||||
mtCount
|
||||
};
|
||||
|
||||
wxWindow* m_parent {nullptr};
|
||||
|
||||
MenuWithSeparators m_object_menu;
|
||||
MenuWithSeparators m_part_menu;
|
||||
MenuWithSeparators m_sla_object_menu;
|
||||
MenuWithSeparators m_default_menu;
|
||||
MenuWithSeparators m_instance_menu;
|
||||
//BBS: add part plate related logic
|
||||
MenuWithSeparators m_plate_menu;
|
||||
MenuWithSeparators m_assemble_object_menu;
|
||||
MenuWithSeparators m_assemble_part_menu;
|
||||
|
||||
|
||||
// Removed/Prepended Items according to the view mode
|
||||
std::array<wxMenuItem*, mtCount> items_increase;
|
||||
std::array<wxMenuItem*, mtCount> items_decrease;
|
||||
std::array<wxMenuItem*, mtCount> items_set_number_of_copies;
|
||||
|
||||
void create_default_menu();
|
||||
void create_common_object_menu(wxMenu *menu);
|
||||
void create_object_menu();
|
||||
void create_sla_object_menu();
|
||||
void create_part_menu();
|
||||
//BBS: add part plate related logic
|
||||
void create_plate_menu();
|
||||
//BBS: add bbl object menu
|
||||
void create_bbl_object_menu();
|
||||
void create_bbl_part_menu();
|
||||
void create_bbl_assemble_object_menu();
|
||||
void create_bbl_assemble_part_menu();
|
||||
|
||||
wxMenu* append_submenu_add_generic(wxMenu* menu, ModelVolumeType type);
|
||||
void append_menu_items_add_volume(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_settings(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_change_type(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_instance_to_object(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_printable(wxMenu* menu);
|
||||
void append_menu_item_rename(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_fix_through_netfabb(wxMenu* menu);
|
||||
//wxMenuItem* append_menu_item_simplify(wxMenu* menu);
|
||||
void append_menu_item_export_stl(wxMenu* menu, bool is_mulity_menu = false);
|
||||
void append_menu_item_reload_from_disk(wxMenu* menu);
|
||||
void append_menu_item_replace_with_stl(wxMenu* menu);
|
||||
void append_menu_item_change_extruder(wxMenu* menu);
|
||||
void append_menu_item_delete(wxMenu* menu);
|
||||
void append_menu_item_scale_selection_to_fit_print_volume(wxMenu* menu);
|
||||
void append_menu_items_convert_unit(wxMenu* menu); // Add "Conver/Revert..." menu items (from/to inches/meters) after "Reload From Disk"
|
||||
void append_menu_item_merge_to_multipart_object(wxMenu *menu);
|
||||
void append_menu_item_merge_to_single_object(wxMenu* menu);
|
||||
void append_menu_item_merge_parts_to_single_part(wxMenu *menu);
|
||||
void append_menu_items_mirror(wxMenu *menu);
|
||||
//void append_menu_items_instance_manipulation(wxMenu *menu);
|
||||
//void update_menu_items_instance_manipulation(MenuType type);
|
||||
//BBS add bbl menu item
|
||||
void append_menu_item_clone(wxMenu* menu);
|
||||
void append_menu_item_simplify(wxMenu* menu);
|
||||
void append_menu_item_per_object_settings(wxMenu* menu);
|
||||
void append_menu_item_change_filament(wxMenu* menu);
|
||||
void append_menu_item_set_printable(wxMenu* menu);
|
||||
void append_menu_item_locked(wxMenu* menu);
|
||||
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif //slic3r_GUI_Factories_hpp_
|
||||
68
src/slic3r/GUI/GUI_Init.cpp
Normal file
68
src/slic3r/GUI/GUI_Init.cpp
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
#include "GUI_Init.hpp"
|
||||
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/3DScene.hpp"
|
||||
#include "slic3r/GUI/InstanceCheck.hpp"
|
||||
#include "slic3r/GUI/format.hpp"
|
||||
#include "slic3r/GUI/MainFrame.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
|
||||
// To show a message box if GUI initialization ends up with an exception thrown.
|
||||
#include <wx/msgdlg.h>
|
||||
|
||||
#include <boost/nowide/iostream.hpp>
|
||||
#include <boost/nowide/convert.hpp>
|
||||
|
||||
#if __APPLE__
|
||||
#include <signal.h>
|
||||
#endif // __APPLE__
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
int GUI_Run(GUI_InitParams ¶ms)
|
||||
{
|
||||
#if __APPLE__
|
||||
// On OSX, we use boost::process::spawn() to launch new instances of PrusaSlicer from another PrusaSlicer.
|
||||
// boost::process::spawn() sets SIGCHLD to SIGIGN for the child process, thus if a child PrusaSlicer spawns another
|
||||
// subprocess and the subrocess dies, the child PrusaSlicer will not receive information on end of subprocess
|
||||
// (posix waitpid() call will always fail).
|
||||
// https://jmmv.dev/2008/10/boostprocess-and-sigchld.html
|
||||
// The child instance of PrusaSlicer has to reset SIGCHLD to its default, so that posix waitpid() and similar continue to work.
|
||||
// See GH issue #5507
|
||||
signal(SIGCHLD, SIG_DFL);
|
||||
#endif // __APPLE__
|
||||
|
||||
//BBS: remove the try-catch and let exception goto above
|
||||
try {
|
||||
//GUI::GUI_App* gui = new GUI::GUI_App(params.start_as_gcodeviewer ? GUI::GUI_App::EAppMode::GCodeViewer : GUI::GUI_App::EAppMode::Editor);
|
||||
GUI::GUI_App* gui = new GUI::GUI_App();
|
||||
/*if (gui->get_app_mode() != GUI::GUI_App::EAppMode::GCodeViewer) {
|
||||
// G-code viewer is currently not performing instance check, a new G-code viewer is started every time.
|
||||
bool gui_single_instance_setting = gui->app_config->get("single_instance") == "1";
|
||||
if (Slic3r::instance_check(params.argc, params.argv, gui_single_instance_setting)) {
|
||||
//TODO: do we have delete gui and other stuff?
|
||||
return -1;
|
||||
}
|
||||
//}*/
|
||||
|
||||
// gui->autosave = m_config.opt_string("autosave");
|
||||
GUI::GUI_App::SetInstance(gui);
|
||||
gui->init_params = ¶ms;
|
||||
|
||||
return wxEntry(params.argc, params.argv);
|
||||
} catch (const Slic3r::Exception &ex) {
|
||||
BOOST_LOG_TRIVIAL(error) << ex.what() << std::endl;
|
||||
wxMessageBox(boost::nowide::widen(ex.what()), _L("Bambu Studio GUI initialization failed"), wxICON_STOP);
|
||||
} catch (const std::exception &ex) {
|
||||
BOOST_LOG_TRIVIAL(error) << ex.what() << std::endl;
|
||||
wxMessageBox(format_wxstr(_L("Fatal error, exception catched: %1%"), ex.what()), _L("Bambu Studio GUI initialization failed"), wxICON_STOP);
|
||||
}
|
||||
// error
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
src/slic3r/GUI/GUI_Init.hpp
Normal file
32
src/slic3r/GUI/GUI_Init.hpp
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef slic3r_GUI_Init_hpp_
|
||||
#define slic3r_GUI_Init_hpp_
|
||||
|
||||
#include <libslic3r/Preset.hpp>
|
||||
#include <libslic3r/PrintConfig.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace GUI {
|
||||
|
||||
struct GUI_InitParams
|
||||
{
|
||||
int argc;
|
||||
char **argv;
|
||||
|
||||
// Substitutions of unknown configuration values done during loading of user presets.
|
||||
PresetsConfigSubstitutions preset_substitutions;
|
||||
|
||||
std::vector<std::string> load_configs;
|
||||
DynamicPrintConfig extra_config;
|
||||
std::vector<std::string> input_files;
|
||||
|
||||
//BBS: remove start_as_gcodeviewer logic
|
||||
//bool start_as_gcodeviewer;
|
||||
};
|
||||
|
||||
int GUI_Run(GUI_InitParams ¶ms);
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_GUI_Init_hpp_
|
||||
443
src/slic3r/GUI/GUI_ObjectLayers.cpp
Normal file
443
src/slic3r/GUI/GUI_ObjectLayers.cpp
Normal file
|
|
@ -0,0 +1,443 @@
|
|||
#include "GUI_ObjectLayers.hpp"
|
||||
#include "GUI_ObjectList.hpp"
|
||||
|
||||
#include "OptionsGroup.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "GLCanvas3D.hpp"
|
||||
#include "Plater.hpp"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include <wx/wupdlock.h>
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
namespace GUI
|
||||
{
|
||||
|
||||
ObjectLayers::ObjectLayers(wxWindow* parent) :
|
||||
OG_Settings(parent, true)
|
||||
{
|
||||
m_grid_sizer = new wxFlexGridSizer(3, 5, wxGetApp().em_unit()); // "Min Z", "Max Z", "Layer height" & buttons sizer
|
||||
m_grid_sizer->SetFlexibleDirection(wxHORIZONTAL);
|
||||
|
||||
// Legend for object layers
|
||||
for (const std::string col : { L("From height"), L("To height"), L("Layer height") }) {
|
||||
auto temp = new wxStaticText(m_parent, wxID_ANY, _(col), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_MIDDLE);
|
||||
temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||
temp->SetFont(wxGetApp().bold_font());
|
||||
|
||||
m_grid_sizer->Add(temp);
|
||||
}
|
||||
|
||||
m_og->activate();
|
||||
m_og->sizer->Clear(true);
|
||||
m_og->sizer->Add(m_grid_sizer, 0, wxEXPAND | wxALL, wxOSX ? 0 : 5);
|
||||
|
||||
m_bmp_delete = ScalableBitmap(parent, "remove_copies"/*"cross"*/);
|
||||
m_bmp_add = ScalableBitmap(parent, "add_copies");
|
||||
}
|
||||
|
||||
void ObjectLayers::select_editor(LayerRangeEditor* editor, const bool is_last_edited_range)
|
||||
{
|
||||
if (is_last_edited_range && m_selection_type == editor->type()) {
|
||||
/* Workaround! Under OSX we should use CallAfter() for SetFocus() after LayerEditors "reorganizations",
|
||||
* because of selected control's strange behavior:
|
||||
* cursor is set to the control, but blue border - doesn't.
|
||||
* And as a result we couldn't edit this control.
|
||||
* */
|
||||
#ifdef __WXOSX__
|
||||
wxTheApp->CallAfter([editor]() {
|
||||
#endif
|
||||
editor->SetFocus();
|
||||
editor->SetInsertionPointEnd();
|
||||
#ifdef __WXOSX__
|
||||
});
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinusButton *delete_button, PlusMinusButton *add_button)
|
||||
{
|
||||
const bool is_last_edited_range = range == m_selectable_range;
|
||||
|
||||
auto set_focus_data = [range, this](const EditorType type)
|
||||
{
|
||||
m_selectable_range = range;
|
||||
m_selection_type = type;
|
||||
};
|
||||
|
||||
auto update_focus_data = [range, this](const t_layer_height_range& new_range, EditorType type, bool enter_pressed)
|
||||
{
|
||||
// change selectable range for new one, if enter was pressed or if same range was selected
|
||||
if (enter_pressed || m_selectable_range == range)
|
||||
m_selectable_range = new_range;
|
||||
if (enter_pressed)
|
||||
m_selection_type = type;
|
||||
};
|
||||
|
||||
// Add control for the "Min Z"
|
||||
|
||||
auto editor = new LayerRangeEditor(this, double_to_string(range.first), etMinZ, set_focus_data,
|
||||
[range, update_focus_data, this, delete_button, add_button](coordf_t min_z, bool enter_pressed, bool dont_update_ui)
|
||||
{
|
||||
if (fabs(min_z - range.first) < EPSILON) {
|
||||
m_selection_type = etUndef;
|
||||
return false;
|
||||
}
|
||||
|
||||
// data for next focusing
|
||||
coordf_t max_z = min_z < range.second ? range.second : min_z + 0.5;
|
||||
const t_layer_height_range new_range = { min_z, max_z };
|
||||
if (delete_button)
|
||||
delete_button->range = new_range;
|
||||
if (add_button)
|
||||
add_button->range = new_range;
|
||||
update_focus_data(new_range, etMinZ, enter_pressed);
|
||||
|
||||
return wxGetApp().obj_list()->edit_layer_range(range, new_range, dont_update_ui);
|
||||
});
|
||||
|
||||
select_editor(editor, is_last_edited_range);
|
||||
m_grid_sizer->Add(editor);
|
||||
|
||||
// Add control for the "Max Z"
|
||||
|
||||
editor = new LayerRangeEditor(this, double_to_string(range.second), etMaxZ, set_focus_data,
|
||||
[range, update_focus_data, this, delete_button, add_button](coordf_t max_z, bool enter_pressed, bool dont_update_ui)
|
||||
{
|
||||
if (fabs(max_z - range.second) < EPSILON || range.first > max_z) {
|
||||
m_selection_type = etUndef;
|
||||
return false; // LayersList would not be updated/recreated
|
||||
}
|
||||
|
||||
// data for next focusing
|
||||
const t_layer_height_range& new_range = { range.first, max_z };
|
||||
if (delete_button)
|
||||
delete_button->range = new_range;
|
||||
if (add_button)
|
||||
add_button->range = new_range;
|
||||
update_focus_data(new_range, etMaxZ, enter_pressed);
|
||||
|
||||
return wxGetApp().obj_list()->edit_layer_range(range, new_range, dont_update_ui);
|
||||
});
|
||||
|
||||
select_editor(editor, is_last_edited_range);
|
||||
m_grid_sizer->Add(editor);
|
||||
|
||||
// Add control for the "Layer height"
|
||||
|
||||
editor = new LayerRangeEditor(this, double_to_string(m_object->layer_config_ranges[range].option("layer_height")->getFloat()), etLayerHeight, set_focus_data,
|
||||
[range](coordf_t layer_height, bool, bool)
|
||||
{
|
||||
return wxGetApp().obj_list()->edit_layer_range(range, layer_height);
|
||||
});
|
||||
|
||||
select_editor(editor, is_last_edited_range);
|
||||
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(editor);
|
||||
|
||||
auto temp = new wxStaticText(m_parent, wxID_ANY, _L("mm"));
|
||||
temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||
temp->SetFont(wxGetApp().normal_font());
|
||||
sizer->Add(temp, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, wxGetApp().em_unit());
|
||||
|
||||
m_grid_sizer->Add(sizer);
|
||||
|
||||
return sizer;
|
||||
}
|
||||
|
||||
void ObjectLayers::create_layers_list()
|
||||
{
|
||||
for (const auto &layer : m_object->layer_config_ranges) {
|
||||
const t_layer_height_range& range = layer.first;
|
||||
auto del_btn = new PlusMinusButton(m_parent, m_bmp_delete, range);
|
||||
del_btn->SetToolTip(_L("Remove height range"));
|
||||
|
||||
auto add_btn = new PlusMinusButton(m_parent, m_bmp_add, range);
|
||||
wxString tooltip = wxGetApp().obj_list()->can_add_new_range_after_current(range);
|
||||
add_btn->SetToolTip(tooltip.IsEmpty() ? _L("Add height range") : tooltip);
|
||||
add_btn->Enable(tooltip.IsEmpty());
|
||||
|
||||
auto sizer = create_layer(range, del_btn, add_btn);
|
||||
sizer->Add(del_btn, 0, wxRIGHT | wxLEFT, em_unit(m_parent));
|
||||
sizer->Add(add_btn);
|
||||
|
||||
del_btn->Bind(wxEVT_BUTTON, [del_btn](wxEvent &) {
|
||||
wxGetApp().obj_list()->del_layer_range(del_btn->range);
|
||||
});
|
||||
|
||||
add_btn->Bind(wxEVT_BUTTON, [add_btn](wxEvent &) {
|
||||
wxGetApp().obj_list()->add_layer_range_after_current(add_btn->range);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectLayers::update_layers_list()
|
||||
{
|
||||
ObjectList* objects_ctrl = wxGetApp().obj_list();
|
||||
if (objects_ctrl->multiple_selection()) return;
|
||||
|
||||
const auto item = objects_ctrl->GetSelection();
|
||||
if (!item) return;
|
||||
|
||||
const int obj_idx = objects_ctrl->get_selected_obj_idx();
|
||||
if (obj_idx < 0) return;
|
||||
|
||||
const ItemType type = objects_ctrl->GetModel()->GetItemType(item);
|
||||
if (!(type & (itLayerRoot | itLayer))) return;
|
||||
|
||||
m_object = objects_ctrl->object(obj_idx);
|
||||
if (!m_object || m_object->layer_config_ranges.empty()) return;
|
||||
|
||||
// Delete all controls from options group except of the legends
|
||||
|
||||
const int cols = m_grid_sizer->GetEffectiveColsCount();
|
||||
const int rows = m_grid_sizer->GetEffectiveRowsCount();
|
||||
for (int idx = cols*rows-1; idx >= cols; idx--) {
|
||||
wxSizerItem* t = m_grid_sizer->GetItem(idx);
|
||||
if (t->IsSizer())
|
||||
t->GetSizer()->Clear(true);
|
||||
else
|
||||
t->DeleteWindows();
|
||||
m_grid_sizer->Remove(idx);
|
||||
}
|
||||
|
||||
// Add new control according to the selected item
|
||||
|
||||
if (type & itLayerRoot)
|
||||
create_layers_list();
|
||||
else
|
||||
create_layer(objects_ctrl->GetModel()->GetLayerRangeByItem(item), nullptr, nullptr);
|
||||
|
||||
m_parent->Layout();
|
||||
}
|
||||
|
||||
void ObjectLayers::update_scene_from_editor_selection() const
|
||||
{
|
||||
// needed to show the visual hints in 3D scene
|
||||
wxGetApp().plater()->canvas3D()->handle_layers_data_focus_event(m_selectable_range, m_selection_type);
|
||||
}
|
||||
|
||||
void ObjectLayers::UpdateAndShow(const bool show)
|
||||
{
|
||||
if (show)
|
||||
update_layers_list();
|
||||
|
||||
OG_Settings::UpdateAndShow(show);
|
||||
}
|
||||
|
||||
void ObjectLayers::msw_rescale()
|
||||
{
|
||||
m_bmp_delete.msw_rescale();
|
||||
m_bmp_add.msw_rescale();
|
||||
|
||||
m_grid_sizer->SetHGap(wxGetApp().em_unit());
|
||||
|
||||
// rescale edit-boxes
|
||||
const int cells_cnt = m_grid_sizer->GetCols() * m_grid_sizer->GetEffectiveRowsCount();
|
||||
for (int i = 0; i < cells_cnt; ++i) {
|
||||
const wxSizerItem* item = m_grid_sizer->GetItem(i);
|
||||
if (item->IsWindow()) {
|
||||
LayerRangeEditor* editor = dynamic_cast<LayerRangeEditor*>(item->GetWindow());
|
||||
if (editor != nullptr)
|
||||
editor->msw_rescale();
|
||||
}
|
||||
else if (item->IsSizer()) // case when we have editor with buttons
|
||||
{
|
||||
wxSizerItem* e_item = item->GetSizer()->GetItem(size_t(0)); // editor
|
||||
if (e_item->IsWindow()) {
|
||||
LayerRangeEditor* editor = dynamic_cast<LayerRangeEditor*>(e_item->GetWindow());
|
||||
if (editor != nullptr)
|
||||
editor->msw_rescale();
|
||||
}
|
||||
|
||||
if (item->GetSizer()->GetItemCount() > 2) // if there are Add/Del buttons
|
||||
for (size_t btn : {2, 3}) { // del_btn, add_btn
|
||||
wxSizerItem* b_item = item->GetSizer()->GetItem(btn);
|
||||
if (b_item->IsWindow()) {
|
||||
auto button = dynamic_cast<PlusMinusButton*>(b_item->GetWindow());
|
||||
if (button != nullptr)
|
||||
button->msw_rescale();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_grid_sizer->Layout();
|
||||
}
|
||||
|
||||
void ObjectLayers::sys_color_changed()
|
||||
{
|
||||
m_bmp_delete.msw_rescale();
|
||||
m_bmp_add.msw_rescale();
|
||||
|
||||
// rescale edit-boxes
|
||||
const int cells_cnt = m_grid_sizer->GetCols() * m_grid_sizer->GetEffectiveRowsCount();
|
||||
for (int i = 0; i < cells_cnt; ++i) {
|
||||
const wxSizerItem* item = m_grid_sizer->GetItem(i);
|
||||
if (item->IsSizer()) {// case when we have editor with buttons
|
||||
for (size_t btn : {2, 3}) { // del_btn, add_btn
|
||||
wxSizerItem* b_item = item->GetSizer()->GetItem(btn);
|
||||
if (b_item->IsWindow()) {
|
||||
auto button = dynamic_cast<PlusMinusButton*>(b_item->GetWindow());
|
||||
if (button != nullptr)
|
||||
button->msw_rescale();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
m_og->sys_color_changed();
|
||||
for (int i = 0; i < cells_cnt; ++i) {
|
||||
const wxSizerItem* item = m_grid_sizer->GetItem(i);
|
||||
if (item->IsWindow()) {
|
||||
if (LayerRangeEditor* editor = dynamic_cast<LayerRangeEditor*>(item->GetWindow()))
|
||||
wxGetApp().UpdateDarkUI(editor);
|
||||
}
|
||||
else if (item->IsSizer()) {// case when we have editor with buttons
|
||||
if (wxSizerItem* e_item = item->GetSizer()->GetItem(size_t(0)); e_item->IsWindow()) {
|
||||
if (LayerRangeEditor* editor = dynamic_cast<LayerRangeEditor*>(e_item->GetWindow()))
|
||||
wxGetApp().UpdateDarkUI(editor);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void ObjectLayers::reset_selection()
|
||||
{
|
||||
m_selectable_range = { 0.0, 0.0 };
|
||||
m_selection_type = etLayerHeight;
|
||||
}
|
||||
|
||||
LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent,
|
||||
const wxString& value,
|
||||
EditorType type,
|
||||
std::function<void(EditorType)> set_focus_data_fn,
|
||||
std::function<bool(coordf_t, bool, bool)> edit_fn
|
||||
) :
|
||||
m_valid_value(value),
|
||||
m_type(type),
|
||||
m_set_focus_data(set_focus_data_fn),
|
||||
wxTextCtrl(parent->m_parent, wxID_ANY, value, wxDefaultPosition,
|
||||
wxSize(8 * em_unit(parent->m_parent), wxDefaultCoord), wxTE_PROCESS_ENTER
|
||||
#ifdef _WIN32
|
||||
| wxBORDER_SIMPLE
|
||||
#endif
|
||||
)
|
||||
{
|
||||
this->SetFont(wxGetApp().normal_font());
|
||||
wxGetApp().UpdateDarkUI(this);
|
||||
|
||||
// Reset m_enter_pressed flag to _false_, when value is editing
|
||||
this->Bind(wxEVT_TEXT, [this](wxEvent&) { m_enter_pressed = false; }, this->GetId());
|
||||
|
||||
this->Bind(wxEVT_TEXT_ENTER, [this, edit_fn](wxEvent&)
|
||||
{
|
||||
m_enter_pressed = true;
|
||||
// If LayersList wasn't updated/recreated, we can call wxEVT_KILL_FOCUS.Skip()
|
||||
if (m_type&etLayerHeight) {
|
||||
if (!edit_fn(get_value(), true, false))
|
||||
SetValue(m_valid_value);
|
||||
else
|
||||
m_valid_value = double_to_string(get_value());
|
||||
m_call_kill_focus = true;
|
||||
}
|
||||
else if (!edit_fn(get_value(), true, false)) {
|
||||
SetValue(m_valid_value);
|
||||
m_call_kill_focus = true;
|
||||
}
|
||||
}, this->GetId());
|
||||
|
||||
this->Bind(wxEVT_KILL_FOCUS, [this, edit_fn](wxFocusEvent& e)
|
||||
{
|
||||
if (!m_enter_pressed) {
|
||||
#ifndef __WXGTK__
|
||||
/* Update data for next editor selection.
|
||||
* But under GTK it looks like there is no information about selected control at e.GetWindow(),
|
||||
* so we'll take it from wxEVT_LEFT_DOWN event
|
||||
* */
|
||||
LayerRangeEditor* new_editor = dynamic_cast<LayerRangeEditor*>(e.GetWindow());
|
||||
if (new_editor)
|
||||
new_editor->set_focus_data();
|
||||
#endif // not __WXGTK__
|
||||
// If LayersList wasn't updated/recreated, we should call e.Skip()
|
||||
if (m_type & etLayerHeight) {
|
||||
if (!edit_fn(get_value(), false, dynamic_cast<ObjectLayers::PlusMinusButton*>(e.GetWindow()) != nullptr))
|
||||
SetValue(m_valid_value);
|
||||
else
|
||||
m_valid_value = double_to_string(get_value());
|
||||
e.Skip();
|
||||
}
|
||||
else if (!edit_fn(get_value(), false, dynamic_cast<ObjectLayers::PlusMinusButton*>(e.GetWindow()) != nullptr)) {
|
||||
SetValue(m_valid_value);
|
||||
e.Skip();
|
||||
}
|
||||
}
|
||||
else if (m_call_kill_focus) {
|
||||
m_call_kill_focus = false;
|
||||
e.Skip();
|
||||
}
|
||||
}, this->GetId());
|
||||
|
||||
this->Bind(wxEVT_SET_FOCUS, [this, parent](wxFocusEvent& e)
|
||||
{
|
||||
set_focus_data();
|
||||
parent->update_scene_from_editor_selection();
|
||||
e.Skip();
|
||||
}, this->GetId());
|
||||
|
||||
#ifdef __WXGTK__ // Workaround! To take information about selectable range
|
||||
this->Bind(wxEVT_LEFT_DOWN, [this](wxEvent& e)
|
||||
{
|
||||
set_focus_data();
|
||||
e.Skip();
|
||||
}, this->GetId());
|
||||
#endif //__WXGTK__
|
||||
|
||||
this->Bind(wxEVT_CHAR, ([this](wxKeyEvent& event)
|
||||
{
|
||||
// select all text using Ctrl+A
|
||||
if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL))
|
||||
this->SetSelection(-1, -1); //select all
|
||||
event.Skip();
|
||||
}));
|
||||
}
|
||||
|
||||
coordf_t LayerRangeEditor::get_value()
|
||||
{
|
||||
wxString str = GetValue();
|
||||
|
||||
coordf_t layer_height;
|
||||
const char dec_sep = is_decimal_separator_point() ? '.' : ',';
|
||||
const char dec_sep_alt = dec_sep == '.' ? ',' : '.';
|
||||
// Replace the first incorrect separator in decimal number.
|
||||
if (str.Replace(dec_sep_alt, dec_sep, false) != 0)
|
||||
SetValue(str);
|
||||
|
||||
if (str == ".")
|
||||
layer_height = 0.0;
|
||||
else {
|
||||
if (!str.ToDouble(&layer_height) || layer_height < 0.0f) {
|
||||
show_error(m_parent, _L("Invalid numeric."));
|
||||
SetValue(double_to_string(layer_height));
|
||||
}
|
||||
}
|
||||
|
||||
return layer_height;
|
||||
}
|
||||
|
||||
void LayerRangeEditor::msw_rescale()
|
||||
{
|
||||
SetMinSize(wxSize(8 * wxGetApp().em_unit(), wxDefaultCoord));
|
||||
}
|
||||
|
||||
} //namespace GUI
|
||||
} //namespace Slic3r
|
||||
106
src/slic3r/GUI/GUI_ObjectLayers.hpp
Normal file
106
src/slic3r/GUI/GUI_ObjectLayers.hpp
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
#ifndef slic3r_GUI_ObjectLayers_hpp_
|
||||
#define slic3r_GUI_ObjectLayers_hpp_
|
||||
|
||||
#include "GUI_ObjectSettings.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
#ifdef __WXOSX__
|
||||
#include "../libslic3r/PrintConfig.hpp"
|
||||
#endif
|
||||
|
||||
class wxBoxSizer;
|
||||
|
||||
namespace Slic3r {
|
||||
class ModelObject;
|
||||
|
||||
namespace GUI {
|
||||
class ConfigOptionsGroup;
|
||||
|
||||
typedef double coordf_t;
|
||||
typedef std::pair<coordf_t, coordf_t> t_layer_height_range;
|
||||
|
||||
class ObjectLayers;
|
||||
|
||||
enum EditorType
|
||||
{
|
||||
etUndef = 0,
|
||||
etMinZ = 1,
|
||||
etMaxZ = 2,
|
||||
etLayerHeight = 4,
|
||||
};
|
||||
|
||||
class LayerRangeEditor : public wxTextCtrl
|
||||
{
|
||||
bool m_enter_pressed { false };
|
||||
bool m_call_kill_focus { false };
|
||||
wxString m_valid_value;
|
||||
EditorType m_type;
|
||||
|
||||
std::function<void(EditorType)> m_set_focus_data;
|
||||
|
||||
public:
|
||||
LayerRangeEditor( ObjectLayers* parent,
|
||||
const wxString& value = wxEmptyString,
|
||||
EditorType type = etUndef,
|
||||
std::function<void(EditorType)> set_focus_data_fn = [](EditorType) {;},
|
||||
// callback parameters: new value, from enter, dont't update panel UI (when called from edit field's kill focus handler for the PlusMinusButton)
|
||||
std::function<bool(coordf_t, bool, bool)> edit_fn = [](coordf_t, bool, bool) {return false; }
|
||||
);
|
||||
~LayerRangeEditor() {}
|
||||
|
||||
EditorType type() const {return m_type;}
|
||||
void set_focus_data() const { m_set_focus_data(m_type);}
|
||||
void msw_rescale();
|
||||
|
||||
private:
|
||||
coordf_t get_value();
|
||||
};
|
||||
|
||||
class ObjectLayers : public OG_Settings
|
||||
{
|
||||
ScalableBitmap m_bmp_delete;
|
||||
ScalableBitmap m_bmp_add;
|
||||
ModelObject* m_object {nullptr};
|
||||
|
||||
wxFlexGridSizer* m_grid_sizer;
|
||||
t_layer_height_range m_selectable_range;
|
||||
EditorType m_selection_type {etUndef};
|
||||
|
||||
public:
|
||||
ObjectLayers(wxWindow* parent);
|
||||
~ObjectLayers() {}
|
||||
|
||||
|
||||
// Button remembers the layer height range, for which it has been created.
|
||||
// The layer height range for this button is updated when the low or high boundary of the layer height range is updated
|
||||
// by the respective text edit field, so that this button emits an action for an up to date layer height range value.
|
||||
class PlusMinusButton : public ScalableButton
|
||||
{
|
||||
public:
|
||||
PlusMinusButton(wxWindow *parent, const ScalableBitmap &bitmap, std::pair<coordf_t, coordf_t> range) : ScalableButton(parent, wxID_ANY, bitmap), range(range) {}
|
||||
// updated when the text edit field loses focus for any PlusMinusButton.
|
||||
std::pair<coordf_t, coordf_t> range;
|
||||
};
|
||||
|
||||
void select_editor(LayerRangeEditor* editor, const bool is_last_edited_range);
|
||||
// Create sizer with layer height range and layer height text edit fields, without buttons.
|
||||
// If the delete and add buttons are provided, the respective text edit fields will modify the layer height ranges of thes buttons
|
||||
// on value change, so that these buttons work with up to date values.
|
||||
wxSizer* create_layer(const t_layer_height_range& range, PlusMinusButton *delete_button, PlusMinusButton *add_button);
|
||||
void create_layers_list();
|
||||
void update_layers_list();
|
||||
|
||||
void update_scene_from_editor_selection() const;
|
||||
|
||||
void UpdateAndShow(const bool show) override;
|
||||
void msw_rescale();
|
||||
void sys_color_changed();
|
||||
void reset_selection();
|
||||
void set_selectable_range(const t_layer_height_range& range) { m_selectable_range = range; }
|
||||
|
||||
friend class LayerRangeEditor;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif // slic3r_GUI_ObjectLayers_hpp_
|
||||
5011
src/slic3r/GUI/GUI_ObjectList.cpp
Normal file
5011
src/slic3r/GUI/GUI_ObjectList.cpp
Normal file
File diff suppressed because it is too large
Load diff
467
src/slic3r/GUI/GUI_ObjectList.hpp
Normal file
467
src/slic3r/GUI/GUI_ObjectList.hpp
Normal file
|
|
@ -0,0 +1,467 @@
|
|||
#ifndef slic3r_GUI_ObjectList_hpp_
|
||||
#define slic3r_GUI_ObjectList_hpp_
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/menu.h>
|
||||
|
||||
#include "Event.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "ObjectDataViewModel.hpp"
|
||||
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
|
||||
class wxBoxSizer;
|
||||
class wxBitmapComboBox;
|
||||
class wxMenuItem;
|
||||
class MenuWithSeparators;
|
||||
|
||||
namespace Slic3r {
|
||||
class ConfigOptionsGroup;
|
||||
class DynamicPrintConfig;
|
||||
class ModelConfig;
|
||||
class ModelObject;
|
||||
class ModelVolume;
|
||||
class TriangleMesh;
|
||||
enum class ModelVolumeType : int;
|
||||
|
||||
// FIXME: broken build on mac os because of this is missing:
|
||||
typedef std::vector<std::string> t_config_option_keys;
|
||||
typedef std::vector<ModelVolume*> ModelVolumePtrs;
|
||||
typedef double coordf_t;
|
||||
typedef std::pair<coordf_t, coordf_t> t_layer_height_range;
|
||||
typedef std::map<t_layer_height_range, ModelConfig> t_layer_config_ranges;
|
||||
|
||||
// Manifold mesh may contain self-intersections, so we want to always allow fixing the mesh.
|
||||
#define FIX_THROUGH_NETFABB_ALWAYS 1
|
||||
|
||||
namespace GUI {
|
||||
struct ObjectVolumeID {
|
||||
ModelObject* object{ nullptr };
|
||||
ModelVolume* volume{ nullptr };
|
||||
};
|
||||
|
||||
typedef Event<ObjectVolumeID> ObjectSettingEvent;
|
||||
|
||||
class PartPlate;
|
||||
|
||||
wxDECLARE_EVENT(EVT_OBJ_LIST_OBJECT_SELECT, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_PARTPLATE_LIST_PLATE_SELECT, IntEvent);
|
||||
class BitmapComboBox;
|
||||
|
||||
struct ItemForDelete
|
||||
{
|
||||
ItemType type;
|
||||
int obj_idx;
|
||||
int sub_obj_idx;
|
||||
|
||||
ItemForDelete(ItemType type, int obj_idx, int sub_obj_idx)
|
||||
: type(type), obj_idx(obj_idx), sub_obj_idx(sub_obj_idx)
|
||||
{}
|
||||
|
||||
bool operator==(const ItemForDelete& r) const
|
||||
{
|
||||
return (type == r.type && obj_idx == r.obj_idx && sub_obj_idx == r.sub_obj_idx);
|
||||
}
|
||||
|
||||
bool operator<(const ItemForDelete& r) const
|
||||
{
|
||||
if (obj_idx != r.obj_idx)
|
||||
return (obj_idx < r.obj_idx);
|
||||
return (sub_obj_idx < r.sub_obj_idx);
|
||||
}
|
||||
};
|
||||
|
||||
struct MeshErrorsInfo
|
||||
{
|
||||
wxString tooltip;
|
||||
std::string warning_icon_name;
|
||||
};
|
||||
|
||||
class ObjectList : public wxDataViewCtrl
|
||||
{
|
||||
public:
|
||||
enum SELECTION_MODE
|
||||
{
|
||||
smUndef = 0,
|
||||
smVolume = 1,
|
||||
smInstance = 2,
|
||||
smLayer = 4,
|
||||
smSettings = 8, // used for undo/redo
|
||||
smLayerRoot = 16, // used for undo/redo
|
||||
};
|
||||
|
||||
enum OBJECT_ORGANIZE_TYPE
|
||||
{
|
||||
ortByPlate = 0,
|
||||
ortByModule = 1,
|
||||
};
|
||||
|
||||
struct Clipboard
|
||||
{
|
||||
void reset() {
|
||||
m_type = itUndef;
|
||||
m_layer_config_ranges_cache .clear();
|
||||
m_config_cache.clear();
|
||||
}
|
||||
bool empty() const { return m_type == itUndef; }
|
||||
ItemType get_type() const { return m_type; }
|
||||
void set_type(ItemType type) { m_type = type; }
|
||||
|
||||
t_layer_config_ranges& get_ranges_cache() { return m_layer_config_ranges_cache; }
|
||||
DynamicPrintConfig& get_config_cache() { return m_config_cache; }
|
||||
|
||||
private:
|
||||
ItemType m_type {itUndef};
|
||||
t_layer_config_ranges m_layer_config_ranges_cache;
|
||||
DynamicPrintConfig m_config_cache;
|
||||
};
|
||||
|
||||
private:
|
||||
SELECTION_MODE m_selection_mode {smUndef};
|
||||
int m_selected_layers_range_idx {-1};
|
||||
|
||||
Clipboard m_clipboard;
|
||||
|
||||
struct dragged_item_data
|
||||
{
|
||||
void init(const int obj_idx, const int subobj_idx, const ItemType type) {
|
||||
m_obj_idx = obj_idx;
|
||||
m_type = type;
|
||||
if (m_type&itVolume)
|
||||
m_vol_idx = subobj_idx;
|
||||
else
|
||||
m_inst_idxs.insert(subobj_idx);
|
||||
}
|
||||
|
||||
void init(const int obj_idx, const ItemType type) {
|
||||
m_obj_idx = obj_idx;
|
||||
m_type = type;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
m_obj_idx = -1;
|
||||
m_vol_idx = -1;
|
||||
m_inst_idxs.clear();
|
||||
m_type = itUndef;
|
||||
}
|
||||
|
||||
int obj_idx() const { return m_obj_idx; }
|
||||
int sub_obj_idx() const { return m_vol_idx; }
|
||||
ItemType type() const { return m_type; }
|
||||
std::set<int>& inst_idxs() { return m_inst_idxs; }
|
||||
|
||||
private:
|
||||
int m_obj_idx = -1;
|
||||
int m_vol_idx = -1;
|
||||
std::set<int> m_inst_idxs{};
|
||||
ItemType m_type = itUndef;
|
||||
|
||||
} m_dragged_data;
|
||||
|
||||
wxBoxSizer *m_sizer {nullptr};
|
||||
|
||||
ObjectDataViewModel *m_objects_model{ nullptr };
|
||||
ModelConfig *m_config {nullptr};
|
||||
std::vector<ModelObject*> *m_objects{ nullptr };
|
||||
|
||||
BitmapComboBox *m_extruder_editor { nullptr };
|
||||
|
||||
std::vector<wxBitmap*> m_bmp_vector;
|
||||
|
||||
int m_selected_object_id = -1;
|
||||
bool m_prevent_list_events = false; // We use this flag to avoid circular event handling Select()
|
||||
// happens to fire a wxEVT_LIST_ITEM_SELECTED on OSX, whose event handler
|
||||
// calls this method again and again and again
|
||||
|
||||
bool m_prevent_update_filament_in_config = false; // We use this flag to avoid updating of the extruder value in config
|
||||
// during updating of the extruder count.
|
||||
|
||||
bool m_prevent_canvas_selection_update = false; // This flag prevents changing selection on the canvas. See function
|
||||
// update_settings_items - updating canvas selection is undesirable,
|
||||
// because it would turn off the gizmos (mainly a problem for the SLA gizmo)
|
||||
|
||||
wxDataViewItem m_last_selected_item {nullptr};
|
||||
#ifdef __WXMSW__
|
||||
// Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected.
|
||||
int m_last_selected_column = -1;
|
||||
#endif /* __MSW__ */
|
||||
|
||||
#if 0
|
||||
SettingsFactory::Bundle m_freq_settings_fff;
|
||||
SettingsFactory::Bundle m_freq_settings_sla;
|
||||
#endif
|
||||
|
||||
size_t m_items_count { size_t(-1) };
|
||||
|
||||
inline void ensure_current_item_visible()
|
||||
{
|
||||
if (const auto &item = this->GetCurrentItem())
|
||||
this->EnsureVisible(item);
|
||||
}
|
||||
|
||||
public:
|
||||
ObjectList(wxWindow* parent);
|
||||
~ObjectList();
|
||||
|
||||
void set_min_height();
|
||||
void update_min_height();
|
||||
|
||||
ObjectDataViewModel* GetModel() const { return m_objects_model; }
|
||||
ModelConfig* config() const { return m_config; }
|
||||
std::vector<ModelObject*>* objects() const { return m_objects; }
|
||||
|
||||
ModelObject* object(const int obj_idx) const ;
|
||||
|
||||
void create_objects_ctrl();
|
||||
// BBS
|
||||
void update_objects_list_filament_column(size_t filaments_count);
|
||||
void update_filament_colors();
|
||||
// show/hide "Extruder" column for Objects List
|
||||
void set_filament_column_hidden(const bool hide) const;
|
||||
// BBS
|
||||
void set_color_paint_hidden(const bool hide) const;
|
||||
void set_support_paint_hidden(const bool hide) const;
|
||||
|
||||
// update extruder in current config
|
||||
void update_filament_in_config(const wxDataViewItem& item);
|
||||
// update changed name in the object model
|
||||
void update_name_in_model(const wxDataViewItem& item) const;
|
||||
void update_name_in_list(int obj_idx, int vol_idx) const;
|
||||
void update_filament_values_for_items(const size_t filaments_count);
|
||||
|
||||
//BBS: update plate
|
||||
void update_plate_values_for_items();
|
||||
void update_name_for_items();
|
||||
|
||||
// Get obj_idx and vol_idx values for the selected (by default) or an adjusted item
|
||||
void get_selected_item_indexes(int& obj_idx, int& vol_idx, const wxDataViewItem& item = wxDataViewItem(0));
|
||||
void get_selection_indexes(std::vector<int>& obj_idxs, std::vector<int>& vol_idxs);
|
||||
// Get count of errors in the mesh
|
||||
int get_repaired_errors_count(const int obj_idx, const int vol_idx = -1) const;
|
||||
// Get list of errors in the mesh and name of the warning icon
|
||||
// Return value is a pair <Tooltip, warning_icon_name>, used for the tooltip and related warning icon
|
||||
// Function without parameters is for a call from Manipulation panel,
|
||||
// when we don't know parameters of selected item
|
||||
MeshErrorsInfo get_mesh_errors_info(const int obj_idx, const int vol_idx = -1, wxString* sidebar_info = nullptr, int* non_manifold_edges = nullptr) const;
|
||||
MeshErrorsInfo get_mesh_errors_info(wxString* sidebar_info = nullptr, int* non_manifold_edges = nullptr);
|
||||
void set_tooltip_for_item(const wxPoint& pt);
|
||||
|
||||
void selection_changed();
|
||||
void show_context_menu(const bool evt_context_menu);
|
||||
void extruder_editing();
|
||||
#ifndef __WXOSX__
|
||||
void key_event(wxKeyEvent& event);
|
||||
#endif /* __WXOSX__ */
|
||||
|
||||
void copy();
|
||||
void paste();
|
||||
void cut();
|
||||
//BBS
|
||||
void clone();
|
||||
bool cut_to_clipboard();
|
||||
bool copy_to_clipboard();
|
||||
bool paste_from_clipboard();
|
||||
void undo();
|
||||
void redo();
|
||||
void increase_instances();
|
||||
void decrease_instances();
|
||||
|
||||
void add_category_to_settings_from_selection(const std::vector< std::pair<std::string, bool> >& category_options, wxDataViewItem item);
|
||||
void add_category_to_settings_from_frequent(const std::vector<std::string>& category_options, wxDataViewItem item);
|
||||
void show_settings(const wxDataViewItem settings_item);
|
||||
bool is_instance_or_object_selected();
|
||||
|
||||
void load_subobject(ModelVolumeType type, bool from_galery = false);
|
||||
// ! ysFIXME - delete commented code after testing and rename "load_modifier" to something common
|
||||
//void load_part(ModelObject& model_object, std::vector<ModelVolume*>& added_volumes, ModelVolumeType type, bool from_galery = false);
|
||||
void load_modifier(const wxArrayString& input_files, ModelObject& model_object, std::vector<ModelVolume*>& added_volumes, ModelVolumeType type, bool from_galery = false);
|
||||
void load_generic_subobject(const std::string& type_name, const ModelVolumeType type);
|
||||
void load_shape_object(const std::string &type_name, bool is_timelapse_wt = false);
|
||||
void load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center = true, bool is_timelapse_wt = false);
|
||||
void del_object(const int obj_idx, bool refresh_immediately = true);
|
||||
void del_subobject_item(wxDataViewItem& item);
|
||||
void del_settings_from_config(const wxDataViewItem& parent_item);
|
||||
void del_instances_from_object(const int obj_idx);
|
||||
void del_layer_from_object(const int obj_idx, const t_layer_height_range& layer_range);
|
||||
void del_layers_from_object(const int obj_idx);
|
||||
bool del_subobject_from_object(const int obj_idx, const int idx, const int type);
|
||||
void del_info_item(const int obj_idx, InfoItemType type);
|
||||
void split();
|
||||
void merge(bool to_multipart_object);
|
||||
void merge_volumes(); // BBS: merge parts to single part
|
||||
void layers_editing();
|
||||
|
||||
wxDataViewItem add_layer_root_item(const wxDataViewItem obj_item);
|
||||
wxDataViewItem add_settings_item(wxDataViewItem parent_item, const DynamicPrintConfig* config);
|
||||
|
||||
DynamicPrintConfig get_default_layer_config(const int obj_idx);
|
||||
bool get_volume_by_item(const wxDataViewItem& item, ModelVolume*& volume);
|
||||
bool is_splittable(bool to_objects);
|
||||
bool selected_instances_of_same_object();
|
||||
bool can_split_instances();
|
||||
bool can_merge_to_multipart_object() const;
|
||||
bool can_merge_to_single_object() const;
|
||||
|
||||
wxPoint get_mouse_position_in_control() const { return wxGetMousePosition() - this->GetScreenPosition(); }
|
||||
wxBoxSizer* get_sizer() {return m_sizer;}
|
||||
int get_selected_obj_idx() const;
|
||||
ModelConfig& get_item_config(const wxDataViewItem& item) const;
|
||||
|
||||
void changed_object(const int obj_idx = -1) const;
|
||||
void part_selection_changed();
|
||||
|
||||
// Add object to the list
|
||||
void add_object_to_list(size_t obj_idx, bool call_selection_changed = true, bool notify_partplate = true);
|
||||
// Delete object from the list
|
||||
void delete_object_from_list();
|
||||
void delete_object_from_list(const size_t obj_idx);
|
||||
void delete_volume_from_list(const size_t obj_idx, const size_t vol_idx);
|
||||
void delete_instance_from_list(const size_t obj_idx, const size_t inst_idx);
|
||||
void delete_from_model_and_list(const ItemType type, const int obj_idx, const int sub_obj_idx);
|
||||
void delete_from_model_and_list(const std::vector<ItemForDelete>& items_for_delete);
|
||||
// Delete all objects from the list
|
||||
void delete_all_objects_from_list();
|
||||
// Increase instances count
|
||||
void increase_object_instances(const size_t obj_idx, const size_t num);
|
||||
// Decrease instances count
|
||||
void decrease_object_instances(const size_t obj_idx, const size_t num);
|
||||
|
||||
// #ys_FIXME_to_delete
|
||||
// Unselect all objects in the list on c++ side
|
||||
void unselect_objects();
|
||||
// Select object item in the ObjectList, when some gizmo is activated
|
||||
// "is_msr_gizmo" indicates if Move/Scale/Rotate gizmo was activated
|
||||
void select_object_item(bool is_msr_gizmo);
|
||||
|
||||
// Remove objects/sub-object from the list
|
||||
void remove();
|
||||
void del_layer_range(const t_layer_height_range& range);
|
||||
// Add a new layer height after the current range if possible.
|
||||
// The current range is shortened and the new range is entered after the shortened current range if it fits.
|
||||
// If no range fits after the current range, then no range is inserted.
|
||||
// The layer range panel is updated even if this function does not change the layer ranges, as the panel update
|
||||
// may have been postponed from the "kill focus" event of a text field, if the focus was lost for the "add layer" button.
|
||||
// Rather providing the range by a value than by a reference, so that the memory referenced cannot be invalidated.
|
||||
void add_layer_range_after_current(const t_layer_height_range current_range);
|
||||
wxString can_add_new_range_after_current( t_layer_height_range current_range);
|
||||
void add_layer_item (const t_layer_height_range& range,
|
||||
const wxDataViewItem layers_item,
|
||||
const int layer_idx = -1);
|
||||
bool edit_layer_range(const t_layer_height_range& range, coordf_t layer_height);
|
||||
// This function may be called when a text field loses focus for a "add layer" or "remove layer" button.
|
||||
// In that case we don't want to destroy the panel with that "add layer" or "remove layer" buttons, as some messages
|
||||
// are already planned for them and destroying these widgets leads to crashes at least on OSX.
|
||||
// In that case the "add layer" or "remove layer" button handlers are responsible for always rebuilding the panel
|
||||
// even if the "add layer" or "remove layer" buttons did not update the layer spans or layer heights.
|
||||
bool edit_layer_range(const t_layer_height_range& range,
|
||||
const t_layer_height_range& new_range,
|
||||
// Don't destroy the panel with the "add layer" or "remove layer" buttons.
|
||||
bool suppress_ui_update = false);
|
||||
|
||||
void init();
|
||||
bool multiple_selection() const ;
|
||||
bool is_selected(const ItemType type) const;
|
||||
int get_selected_layers_range_idx() const;
|
||||
void set_selected_layers_range_idx(const int range_idx) { m_selected_layers_range_idx = range_idx; }
|
||||
void set_selection_mode(SELECTION_MODE mode) { m_selection_mode = mode; }
|
||||
void update_selections();
|
||||
void update_selections_on_canvas();
|
||||
void select_item(const wxDataViewItem& item);
|
||||
void select_item(std::function<wxDataViewItem()> get_item);
|
||||
void select_items(const wxDataViewItemArray& sels);
|
||||
// BBS
|
||||
void select_item(const ObjectVolumeID& ov_id);
|
||||
void select_items(const std::vector<ObjectVolumeID>& ov_ids);
|
||||
void select_all();
|
||||
void select_item_all_children();
|
||||
void update_selection_mode();
|
||||
bool check_last_selection(wxString& msg_str);
|
||||
// correct current selections to avoid of the possible conflicts
|
||||
void fix_multiselection_conflicts();
|
||||
|
||||
ModelVolume* get_selected_model_volume();
|
||||
void change_part_type();
|
||||
|
||||
void last_volume_is_deleted(const int obj_idx);
|
||||
void update_and_show_object_settings_item();
|
||||
void update_settings_item_and_selection(wxDataViewItem item, wxDataViewItemArray& selections);
|
||||
void update_object_list_by_printer_technology();
|
||||
void update_info_items(size_t obj_idx, wxDataViewItemArray* selections = nullptr, bool added_object = false);
|
||||
|
||||
void instances_to_separated_object(const int obj_idx, const std::set<int>& inst_idx);
|
||||
void instances_to_separated_objects(const int obj_idx);
|
||||
void split_instances();
|
||||
void rename_item();
|
||||
void fix_through_netfabb();
|
||||
void simplify();
|
||||
void update_item_error_icon(const int obj_idx, int vol_idx) const ;
|
||||
|
||||
void copy_layers_to_clipboard();
|
||||
void paste_layers_into_list();
|
||||
void copy_settings_to_clipboard();
|
||||
void paste_settings_into_list();
|
||||
bool clipboard_is_empty() const { return m_clipboard.empty(); }
|
||||
void paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes);
|
||||
void paste_objects_into_list(const std::vector<size_t>& object_idxs);
|
||||
|
||||
void msw_rescale();
|
||||
void sys_color_changed();
|
||||
|
||||
void update_after_undo_redo();
|
||||
//update printable state for item from objects model
|
||||
void update_printable_state(int obj_idx, int instance_idx);
|
||||
void toggle_printable_state();
|
||||
|
||||
//BBS: remove const qualifier
|
||||
void set_extruder_for_selected_items(const int extruder);
|
||||
wxDataViewItemArray reorder_volumes_and_get_selection(int obj_idx, std::function<bool(const ModelVolume*)> add_to_selection = nullptr);
|
||||
void apply_volumes_order();
|
||||
bool has_paint_on_segmentation();
|
||||
|
||||
// BBS
|
||||
void on_plate_added(PartPlate* part_plate);
|
||||
void on_plate_deleted(int plate_index);
|
||||
void reload_all_plates(bool notify_partplate = false);
|
||||
void on_plate_selected(int plate_index);
|
||||
void notify_instance_updated(int obj_idx);
|
||||
void object_config_options_changed(const ObjectVolumeID& ov_id);
|
||||
void printable_state_changed(const std::vector<ObjectVolumeID>& ov_ids);
|
||||
|
||||
private:
|
||||
#ifdef __WXOSX__
|
||||
// void OnChar(wxKeyEvent& event);
|
||||
wxAcceleratorTable m_accel;
|
||||
#endif /* __WXOSX__ */
|
||||
void OnContextMenu(wxDataViewEvent &event);
|
||||
void list_manipulation(const wxPoint& mouse_pos, bool evt_context_menu = false);
|
||||
|
||||
// BBS
|
||||
void update_name_column_width() const;
|
||||
|
||||
void OnBeginDrag(wxDataViewEvent &event);
|
||||
void OnDropPossible(wxDataViewEvent &event);
|
||||
void OnDrop(wxDataViewEvent &event);
|
||||
bool can_drop(const wxDataViewItem& item) const ;
|
||||
|
||||
void ItemValueChanged(wxDataViewEvent &event);
|
||||
// Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected.
|
||||
void OnEditingStarted(wxDataViewEvent &event);
|
||||
void OnEditingDone(wxDataViewEvent &event);
|
||||
|
||||
// BBS
|
||||
void OnColumnHeadClicked(wxDataViewEvent& event);
|
||||
void OnOrganizeObjects(OBJECT_ORGANIZE_TYPE type);
|
||||
wxMenu m_object_org_menu;
|
||||
|
||||
std::vector<int> m_columns_width;
|
||||
};
|
||||
|
||||
|
||||
}}
|
||||
|
||||
#endif //slic3r_GUI_ObjectList_hpp_
|
||||
383
src/slic3r/GUI/GUI_ObjectSettings.cpp
Normal file
383
src/slic3r/GUI/GUI_ObjectSettings.cpp
Normal file
|
|
@ -0,0 +1,383 @@
|
|||
#include "GUI_ObjectSettings.hpp"
|
||||
#include "GUI_ObjectList.hpp"
|
||||
#include "GUI_Factories.hpp"
|
||||
#include "Tab.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
|
||||
#include "OptionsGroup.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include "I18N.hpp"
|
||||
#include "ConfigManipulation.hpp"
|
||||
|
||||
#include <wx/wupdlock.h>
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
namespace GUI
|
||||
{
|
||||
|
||||
OG_Settings::OG_Settings(wxWindow* parent, const bool staticbox) :
|
||||
m_parent(parent)
|
||||
{
|
||||
wxString title = staticbox ? " " : ""; // temporary workaround - #ys_FIXME
|
||||
m_og = std::make_shared<ConfigOptionsGroup>(parent, title);
|
||||
}
|
||||
|
||||
bool OG_Settings::IsShown()
|
||||
{
|
||||
return m_og->sizer->IsEmpty() ? false : m_og->sizer->IsShown(size_t(0));
|
||||
}
|
||||
|
||||
void OG_Settings::Show(const bool show)
|
||||
{
|
||||
m_og->Show(show);
|
||||
}
|
||||
|
||||
void OG_Settings::Hide()
|
||||
{
|
||||
Show(false);
|
||||
}
|
||||
|
||||
void OG_Settings::UpdateAndShow(const bool show)
|
||||
{
|
||||
Show(show);
|
||||
// m_parent->Layout();
|
||||
}
|
||||
|
||||
wxSizer* OG_Settings::get_sizer()
|
||||
{
|
||||
return m_og->sizer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ObjectSettings::ObjectSettings(wxWindow* parent) :
|
||||
#if !NEW_OBJECT_SETTING
|
||||
OG_Settings(parent, true)
|
||||
#else
|
||||
m_parent(parent)
|
||||
#endif
|
||||
{
|
||||
#if !NEW_OBJECT_SETTING
|
||||
m_og->activate();
|
||||
m_og->set_name(_(L("Additional process preset")));
|
||||
|
||||
m_settings_list_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_og->sizer->Add(m_settings_list_sizer, 1, wxEXPAND | wxLEFT, 5);
|
||||
|
||||
m_bmp_delete = ScalableBitmap(parent, "cross");
|
||||
m_bmp_delete_focus = ScalableBitmap(parent, "cross_focus");
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !NEW_OBJECT_SETTING // BBS: new object settings
|
||||
|
||||
bool ObjectSettings::update_settings_list()
|
||||
{
|
||||
m_settings_list_sizer->Clear(true);
|
||||
m_og_settings.resize(0);
|
||||
|
||||
auto objects_ctrl = wxGetApp().obj_list();
|
||||
auto objects_model = wxGetApp().obj_list()->GetModel();
|
||||
auto config = wxGetApp().obj_list()->config();
|
||||
|
||||
const auto item = objects_ctrl->GetSelection();
|
||||
|
||||
if (!item || !objects_model->IsSettingsItem(item) || !config || objects_ctrl->multiple_selection())
|
||||
return false;
|
||||
|
||||
const bool is_object_settings = objects_model->GetItemType(objects_model->GetParent(item)) == itObject;
|
||||
SettingsFactory::Bundle cat_options = SettingsFactory::get_bundle(&config->get(), is_object_settings);
|
||||
|
||||
if (!cat_options.empty())
|
||||
{
|
||||
std::vector<std::string> categories;
|
||||
categories.reserve(cat_options.size());
|
||||
|
||||
auto extra_column = [config, this](wxWindow* parent, const Line& line)
|
||||
{
|
||||
auto opt_key = (line.get_options())[0].opt_id; //we assume that we have one option per line
|
||||
|
||||
auto btn = new ScalableButton(parent, wxID_ANY, m_bmp_delete);
|
||||
btn->SetToolTip(_(L("Remove parameter")));
|
||||
|
||||
btn->SetBitmapFocus(m_bmp_delete_focus.bmp());
|
||||
btn->SetBitmapHover(m_bmp_delete_focus.bmp());
|
||||
|
||||
btn->Bind(wxEVT_BUTTON, [opt_key, config, this](wxEvent &event) {
|
||||
wxGetApp().plater()->take_snapshot(from_u8(boost::format(("Delete Option %s") % opt_key).str()));
|
||||
config->erase(opt_key);
|
||||
wxGetApp().obj_list()->changed_object();
|
||||
wxTheApp->CallAfter([this]() {
|
||||
wxWindowUpdateLocker noUpdates(m_parent);
|
||||
update_settings_list();
|
||||
m_parent->Layout();
|
||||
});
|
||||
});
|
||||
return btn;
|
||||
};
|
||||
|
||||
for (auto& cat : cat_options)
|
||||
{
|
||||
categories.push_back(cat.first);
|
||||
|
||||
auto optgroup = std::make_shared<ConfigOptionsGroup>(m_og->ctrl_parent(), _(cat.first), config, false, extra_column);
|
||||
optgroup->label_width = 15;
|
||||
optgroup->sidetext_width = 5;
|
||||
|
||||
optgroup->m_on_change = [this, config](const t_config_option_key& opt_id, const boost::any& value) {
|
||||
this->update_config_values(config);
|
||||
wxGetApp().obj_list()->changed_object(); };
|
||||
|
||||
// call back for rescaling of the extracolumn control
|
||||
optgroup->rescale_extra_column_item = [this](wxWindow* win) {
|
||||
auto *ctrl = dynamic_cast<ScalableButton*>(win);
|
||||
if (ctrl == nullptr)
|
||||
return;
|
||||
ctrl->SetBitmap_(m_bmp_delete);
|
||||
ctrl->SetBitmapFocus(m_bmp_delete_focus.bmp());
|
||||
ctrl->SetBitmapHover(m_bmp_delete_focus.bmp());
|
||||
};
|
||||
|
||||
const bool is_extruders_cat = cat.first == "Extruders";
|
||||
for (auto& opt : cat.second)
|
||||
{
|
||||
Option option = optgroup->get_option(opt);
|
||||
option.opt.width = 12;
|
||||
if (is_extruders_cat)
|
||||
option.opt.max = wxGetApp().extruders_edited_cnt();
|
||||
optgroup->append_single_option_line(option);
|
||||
}
|
||||
optgroup->activate();
|
||||
for (auto& opt : cat.second)
|
||||
optgroup->get_field(opt)->m_on_change = [optgroup](const std::string& opt_id, const boost::any& value) {
|
||||
// first of all take a snapshot and then change value in configuration
|
||||
wxGetApp().plater()->take_snapshot(from_u8((boost::format("Change Option %s")% opt_id).str()));
|
||||
optgroup->on_change_OG(opt_id, value);
|
||||
};
|
||||
|
||||
optgroup->reload_config();
|
||||
|
||||
m_settings_list_sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 0);
|
||||
m_og_settings.push_back(optgroup);
|
||||
}
|
||||
|
||||
if (!categories.empty()) {
|
||||
objects_model->UpdateSettingsDigest(item, categories);
|
||||
update_config_values(config);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
objects_ctrl->select_item(objects_model->Delete(item));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
bool ObjectSettings::update_settings_list()
|
||||
{
|
||||
if (!wxGetApp().is_editor())
|
||||
return false;
|
||||
if (!wxGetApp().mainframe->IsShown())
|
||||
return false;
|
||||
|
||||
auto objects_ctrl = wxGetApp().obj_list();
|
||||
auto objects_model = wxGetApp().obj_list()->GetModel();
|
||||
|
||||
wxDataViewItemArray items;
|
||||
objects_ctrl->GetSelections(items);
|
||||
|
||||
std::map<ObjectBase *, ModelConfig *> object_configs;
|
||||
bool is_object_settings = true;
|
||||
bool is_volume_settings = true;
|
||||
ModelObject * parent_object = nullptr;
|
||||
for (auto item : items) {
|
||||
auto type = objects_model->GetItemType(item);
|
||||
if (type != itObject && type != itVolume) {
|
||||
return false;
|
||||
}
|
||||
const int obj_idx = objects_model->GetObjectIdByItem(item);
|
||||
assert(obj_idx >= 0);
|
||||
auto object = wxGetApp().model().objects[obj_idx];
|
||||
if (type == itObject) {
|
||||
if (!is_object_settings)
|
||||
return false;
|
||||
is_volume_settings = false;
|
||||
object_configs.emplace(object, &object->config);
|
||||
} else {
|
||||
if (!is_volume_settings)
|
||||
return false;
|
||||
if (parent_object && parent_object != object)
|
||||
return false;
|
||||
parent_object = object;
|
||||
is_object_settings = false;
|
||||
const int vol_idx = objects_model->GetVolumeIdByItem(item);
|
||||
assert(vol_idx >= 0);
|
||||
auto volume = object->volumes[vol_idx];
|
||||
object_configs.emplace(volume, &volume->config);
|
||||
}
|
||||
}
|
||||
|
||||
auto tab_object = dynamic_cast<TabPrintModel*>(wxGetApp().get_model_tab());
|
||||
auto tab_volume = dynamic_cast<TabPrintModel*>(wxGetApp().get_model_tab(true));
|
||||
|
||||
if (is_volume_settings == is_object_settings) {
|
||||
tab_object->set_model_config({});
|
||||
tab_volume->set_model_config({});
|
||||
m_tab_active = nullptr;
|
||||
} else if (is_volume_settings) {
|
||||
tab_object->set_model_config({{parent_object, &parent_object->config}});
|
||||
tab_volume->set_model_config(object_configs);
|
||||
m_tab_active = tab_volume;
|
||||
} else if (is_object_settings) {
|
||||
tab_object->set_model_config(object_configs);
|
||||
tab_volume->set_model_config({});
|
||||
m_tab_active = tab_object;
|
||||
}
|
||||
((ParamsPanel*) tab_object->GetParent())->set_active_tab(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool ObjectSettings::add_missed_options(ModelConfig* config_to, const DynamicPrintConfig& config_from)
|
||||
{
|
||||
const DynamicPrintConfig& print_config = wxGetApp().plater()->printer_technology() == ptFFF ?
|
||||
wxGetApp().preset_bundle->prints.get_edited_preset().config :
|
||||
wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
|
||||
bool is_added = false;
|
||||
|
||||
for (auto opt_key : config_from.diff(print_config))
|
||||
if (!config_to->has(opt_key)) {
|
||||
config_to->set_key_value(opt_key, config_from.option(opt_key)->clone());
|
||||
is_added = true;
|
||||
}
|
||||
|
||||
return is_added;
|
||||
}
|
||||
|
||||
void ObjectSettings::update_config_values(ModelConfig* config)
|
||||
{
|
||||
const auto objects_model = wxGetApp().obj_list()->GetModel();
|
||||
const auto item = wxGetApp().obj_list()->GetSelection();
|
||||
const auto printer_technology = wxGetApp().plater()->printer_technology();
|
||||
const bool is_object_settings = objects_model->GetItemType(objects_model->GetParent(item)) == itObject;
|
||||
|
||||
if (!item || !objects_model->IsSettingsItem(item) || !config)
|
||||
return;
|
||||
|
||||
// update config values according to configuration hierarchy
|
||||
DynamicPrintConfig main_config = printer_technology == ptFFF ?
|
||||
wxGetApp().preset_bundle->prints.get_edited_preset().config :
|
||||
wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
|
||||
|
||||
auto load_config = [this, config, &main_config]()
|
||||
{
|
||||
/* Additional check for overrided options.
|
||||
* There is a case, when some options should to be added,
|
||||
* to avoid check loop in the next configuration update
|
||||
*/
|
||||
bool is_added = add_missed_options(config, main_config);
|
||||
|
||||
// load checked values from main_config to config
|
||||
config->apply_only(main_config, config->keys(), true);
|
||||
// Initialize UI components with the config values.
|
||||
#if !NEW_OBJECT_SETTING
|
||||
for (auto og : m_og_settings)
|
||||
og->reload_config();
|
||||
#else
|
||||
#endif
|
||||
// next config check
|
||||
update_config_values(config);
|
||||
|
||||
if (is_added) {
|
||||
// #ysFIXME - Delete after testing! Very likely this CallAfret is no needed
|
||||
// wxTheApp->CallAfter([this]() {
|
||||
wxWindowUpdateLocker noUpdates(m_parent);
|
||||
update_settings_list();
|
||||
m_parent->Layout();
|
||||
// });
|
||||
}
|
||||
};
|
||||
|
||||
#if !NEW_OBJECT_SETTING
|
||||
auto toggle_field = [this](const t_config_option_key & opt_key, bool toggle, int opt_index)
|
||||
{
|
||||
Field* field = nullptr;
|
||||
for (auto og : m_og_settings) {
|
||||
field = og->get_fieldc(opt_key, opt_index);
|
||||
if (field != nullptr)
|
||||
break;
|
||||
}
|
||||
if (field)
|
||||
field->toggle(toggle);
|
||||
};
|
||||
#else
|
||||
auto toggle_field = [this](const t_config_option_key & opt_key, bool toggle, int opt_index)
|
||||
{
|
||||
m_tab_active->toggle_option(opt_key, toggle, opt_index);
|
||||
};
|
||||
#endif
|
||||
|
||||
//BBS: change local config to DynamicPrintConfig
|
||||
ConfigManipulation config_manipulation(load_config, toggle_field, nullptr, nullptr, &(config->get()));
|
||||
|
||||
if (!is_object_settings)
|
||||
{
|
||||
const int obj_idx = objects_model->GetObjectIdByItem(item);
|
||||
assert(obj_idx >= 0);
|
||||
// for object's part first of all update konfiguration from object
|
||||
main_config.apply(wxGetApp().model().objects[obj_idx]->config.get(), true);
|
||||
// and then from its own config
|
||||
}
|
||||
|
||||
main_config.apply(config->get(), true);
|
||||
printer_technology == ptFFF ? config_manipulation.update_print_fff_config(&main_config) :
|
||||
config_manipulation.update_print_sla_config(&main_config) ;
|
||||
|
||||
printer_technology == ptFFF ? config_manipulation.toggle_print_fff_options(&main_config) :
|
||||
config_manipulation.toggle_print_sla_options(&main_config) ;
|
||||
}
|
||||
|
||||
void ObjectSettings::UpdateAndShow(const bool show)
|
||||
{
|
||||
#if !NEW_OBJECT_SETTING
|
||||
OG_Settings::UpdateAndShow(show ? update_settings_list() : false);
|
||||
#else
|
||||
update_settings_list();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ObjectSettings::msw_rescale()
|
||||
{
|
||||
#if !NEW_OBJECT_SETTING
|
||||
m_bmp_delete.msw_rescale();
|
||||
m_bmp_delete_focus.msw_rescale();
|
||||
|
||||
for (auto group : m_og_settings)
|
||||
group->msw_rescale();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ObjectSettings::sys_color_changed()
|
||||
{
|
||||
#if !NEW_OBJECT_SETTING
|
||||
m_og->sys_color_changed();
|
||||
|
||||
for (auto group : m_og_settings)
|
||||
group->sys_color_changed();
|
||||
#endif
|
||||
}
|
||||
|
||||
} //namespace GUI
|
||||
} //namespace Slic3r
|
||||
77
src/slic3r/GUI/GUI_ObjectSettings.hpp
Normal file
77
src/slic3r/GUI/GUI_ObjectSettings.hpp
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
#ifndef slic3r_GUI_ObjectSettings_hpp_
|
||||
#define slic3r_GUI_ObjectSettings_hpp_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <wx/panel.h>
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
#define NEW_OBJECT_SETTING 1
|
||||
|
||||
class wxBoxSizer;
|
||||
|
||||
namespace Slic3r {
|
||||
class DynamicPrintConfig;
|
||||
class ModelConfig;
|
||||
namespace GUI {
|
||||
class ConfigOptionsGroup;
|
||||
|
||||
class OG_Settings
|
||||
{
|
||||
protected:
|
||||
std::shared_ptr<ConfigOptionsGroup> m_og;
|
||||
wxWindow* m_parent;
|
||||
public:
|
||||
OG_Settings(wxWindow* parent, const bool staticbox);
|
||||
virtual ~OG_Settings() {}
|
||||
|
||||
virtual bool IsShown();
|
||||
virtual void Show(const bool show);
|
||||
virtual void Hide();
|
||||
virtual void UpdateAndShow(const bool show);
|
||||
|
||||
virtual wxSizer* get_sizer();
|
||||
ConfigOptionsGroup* get_og() { return m_og.get(); }
|
||||
wxWindow* parent() const {return m_parent; }
|
||||
};
|
||||
|
||||
class TabPrintModel;
|
||||
|
||||
#if !NEW_OBJECT_SETTING
|
||||
class ObjectSettings : public OG_Settings
|
||||
#else
|
||||
class ObjectSettings
|
||||
#endif
|
||||
{
|
||||
// sizer for extra Object/Part's settings
|
||||
#if !NEW_OBJECT_SETTING
|
||||
wxBoxSizer* m_settings_list_sizer{ nullptr };
|
||||
// option groups for settings
|
||||
std::vector <std::shared_ptr<ConfigOptionsGroup>> m_og_settings;
|
||||
|
||||
ScalableBitmap m_bmp_delete;
|
||||
ScalableBitmap m_bmp_delete_focus;
|
||||
#else
|
||||
wxWindow* m_parent;
|
||||
TabPrintModel * m_tab_active;
|
||||
#endif
|
||||
|
||||
public:
|
||||
ObjectSettings(wxWindow* parent);
|
||||
~ObjectSettings() {}
|
||||
|
||||
bool update_settings_list();
|
||||
/* Additional check for override options: Add options, if its needed.
|
||||
* Example: if Infill is set to 100%, and Fill Pattern is missed in config_to,
|
||||
* we should add sparse_infill_pattern to avoid endless loop in update
|
||||
*/
|
||||
bool add_missed_options(ModelConfig *config_to, const DynamicPrintConfig &config_from);
|
||||
void update_config_values(ModelConfig *config);
|
||||
void UpdateAndShow(const bool show);
|
||||
void msw_rescale();
|
||||
void sys_color_changed();
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif // slic3r_GUI_ObjectSettings_hpp_
|
||||
2985
src/slic3r/GUI/GUI_ObjectTable.cpp
Normal file
2985
src/slic3r/GUI/GUI_ObjectTable.cpp
Normal file
File diff suppressed because it is too large
Load diff
576
src/slic3r/GUI/GUI_ObjectTable.hpp
Normal file
576
src/slic3r/GUI/GUI_ObjectTable.hpp
Normal file
|
|
@ -0,0 +1,576 @@
|
|||
#ifndef slic3r_GUI_ObjectTable_hpp_
|
||||
#define slic3r_GUI_ObjectTable_hpp_
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/intl.h>
|
||||
#include <wx/generic/gridsel.h>
|
||||
#include <wx/grid.h>
|
||||
#include <wx/renderer.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/valnum.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/popupwin.h>
|
||||
|
||||
#include "Plater.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
//#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "OptionsGroup.hpp"
|
||||
#include "GUI_Factories.hpp"
|
||||
#include "GUI_ObjectTableSettings.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
namespace GUI {
|
||||
|
||||
class ObjectTablePanel;
|
||||
|
||||
class GridCellIconRenderer : public wxGridCellRenderer
|
||||
{
|
||||
public:
|
||||
virtual void Draw(wxGrid& grid,
|
||||
wxGridCellAttr& attr,
|
||||
wxDC& dc,
|
||||
const wxRect& rect,
|
||||
int row, int col,
|
||||
bool isSelected) wxOVERRIDE;
|
||||
|
||||
virtual wxSize GetBestSize(wxGrid& WXUNUSED(grid),
|
||||
wxGridCellAttr& attr,
|
||||
wxDC& dc,
|
||||
int WXUNUSED(row),
|
||||
int WXUNUSED(col)) wxOVERRIDE;
|
||||
|
||||
virtual GridCellIconRenderer *Clone() const wxOVERRIDE;
|
||||
};
|
||||
|
||||
// the editor for string data allowing to choose from the list of strings
|
||||
class GridCellFilamentsEditor : public wxGridCellChoiceEditor
|
||||
{
|
||||
public:
|
||||
// if !allowOthers, user can't type a string not in choices array
|
||||
GridCellFilamentsEditor(size_t count = 0,
|
||||
const wxString choices[] = NULL,
|
||||
bool allowOthers = false,
|
||||
std::vector<wxBitmap*>* bitmaps = NULL);
|
||||
GridCellFilamentsEditor(const wxArrayString& choices,
|
||||
bool allowOthers = false,
|
||||
std::vector<wxBitmap*>* bitmaps = NULL);
|
||||
|
||||
virtual void Create(wxWindow* parent,
|
||||
wxWindowID id,
|
||||
wxEvtHandler* evtHandler) wxOVERRIDE;
|
||||
virtual void SetSize(const wxRect& rect) wxOVERRIDE;
|
||||
|
||||
virtual wxGridCellEditor *Clone() const wxOVERRIDE;
|
||||
|
||||
virtual void BeginEdit(int row, int col, wxGrid* grid) wxOVERRIDE;
|
||||
|
||||
virtual wxGridActivationResult TryActivate(int row, int col, wxGrid* grid, const wxGridActivationSource& actSource) wxOVERRIDE;
|
||||
virtual void DoActivate(int row, int col, wxGrid* grid) wxOVERRIDE;
|
||||
|
||||
protected:
|
||||
wxBitmapComboBox *Combo() const { return (wxBitmapComboBox *)m_control; }
|
||||
void OnComboCloseUp(wxCommandEvent& evt);
|
||||
|
||||
std::vector<wxBitmap*>* m_icons;
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(GridCellFilamentsEditor);
|
||||
private:
|
||||
int m_cached_value {-1};
|
||||
};
|
||||
|
||||
|
||||
class GridCellFilamentsRenderer : public wxGridCellChoiceRenderer
|
||||
{
|
||||
public:
|
||||
virtual void Draw(wxGrid& grid,
|
||||
wxGridCellAttr& attr,
|
||||
wxDC& dc,
|
||||
const wxRect& rect,
|
||||
int row, int col,
|
||||
bool isSelected) wxOVERRIDE;
|
||||
|
||||
virtual wxSize GetBestSize(wxGrid& WXUNUSED(grid),
|
||||
wxGridCellAttr& attr,
|
||||
wxDC& dc,
|
||||
int WXUNUSED(row),
|
||||
int WXUNUSED(col)) wxOVERRIDE;
|
||||
|
||||
virtual GridCellFilamentsRenderer *Clone() const wxOVERRIDE;
|
||||
};
|
||||
|
||||
class GridCellSupportEditor : public wxGridCellBoolEditor
|
||||
{
|
||||
public:
|
||||
GridCellSupportEditor() { }
|
||||
virtual void DoActivate(int row, int col, wxGrid* grid) wxOVERRIDE;
|
||||
|
||||
private:
|
||||
// These functions modify or use m_value.
|
||||
void SetValueFromGrid(int row, int col, wxGrid* grid);
|
||||
void SetGridFromValue(int row, int col, wxGrid* grid) const;
|
||||
|
||||
wxString GetStringValue() const { return GetStringValue(m_value); }
|
||||
|
||||
static
|
||||
wxString GetStringValue(bool value) { return ms_stringValues[value]; }
|
||||
|
||||
bool m_value;
|
||||
|
||||
static wxString ms_stringValues[2];
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(GridCellSupportEditor);
|
||||
};
|
||||
|
||||
class GridCellSupportRenderer : public wxGridCellBoolRenderer
|
||||
{
|
||||
public:
|
||||
virtual void Draw(wxGrid& grid,
|
||||
wxGridCellAttr& attr,
|
||||
wxDC& dc,
|
||||
const wxRect& rect,
|
||||
int row, int col,
|
||||
bool isSelected) wxOVERRIDE;
|
||||
|
||||
virtual wxSize GetBestSize(wxGrid& WXUNUSED(grid),
|
||||
wxGridCellAttr& attr,
|
||||
wxDC& dc,
|
||||
int WXUNUSED(row),
|
||||
int WXUNUSED(col)) wxOVERRIDE;
|
||||
|
||||
virtual GridCellSupportRenderer *Clone() const wxOVERRIDE;
|
||||
};
|
||||
|
||||
//ObjectGrid for the param setting table
|
||||
class ObjectGrid : public wxGrid
|
||||
{
|
||||
public:
|
||||
ObjectGrid(wxWindow *parent,
|
||||
wxWindowID id,
|
||||
const wxPoint& pos = wxDefaultPosition,
|
||||
const wxSize& size = wxDefaultSize,
|
||||
long style = wxWANTS_CHARS,
|
||||
const wxString& name = wxASCII_STR(wxGridNameStr))
|
||||
:wxGrid(parent, id, pos, size, style, name)
|
||||
{
|
||||
}
|
||||
|
||||
~ObjectGrid() {}
|
||||
|
||||
/*virtual wxPen GetColGridLinePen(int col)
|
||||
{
|
||||
if (col % 2 == 1)
|
||||
return wxPen(*wxBLUE, 2, wxPENSTYLE_SOLID);
|
||||
else
|
||||
return *wxTRANSPARENT_PEN;
|
||||
}
|
||||
|
||||
virtual wxPen GetRowGridLinePen(int row)
|
||||
{
|
||||
return wxNullPen;
|
||||
}*/
|
||||
|
||||
bool OnCellLeftClick(wxGridEvent& event, int row, int col, ConfigOptionType type);
|
||||
void OnColHeadLeftClick(wxGridEvent& event);
|
||||
|
||||
virtual void DrawColLabels( wxDC& dc, const wxArrayInt& cols );
|
||||
virtual void DrawColLabel( wxDC& dc, int col );
|
||||
|
||||
//set ObjectGridTable and ObjectTablePanel as friend
|
||||
friend class ObjectGridTable;
|
||||
friend class ObjectTablePanel;
|
||||
protected:
|
||||
//void OnSize( wxSizeEvent& );
|
||||
void OnKeyDown( wxKeyEvent& );
|
||||
void OnKeyUp( wxKeyEvent& );
|
||||
void OnChar( wxKeyEvent& );
|
||||
|
||||
private:
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
|
||||
wxGridBlockCoords m_selected_block;
|
||||
void paste_data( wxTextDataObject& text_data );
|
||||
};
|
||||
|
||||
class ObjectGridTable : public wxGridTableBase
|
||||
{
|
||||
public:
|
||||
static std::string category_all;
|
||||
static std::string plate_outside;
|
||||
enum GridRowType
|
||||
{
|
||||
row_object = 0,
|
||||
row_volume = 1
|
||||
};
|
||||
enum GridColType
|
||||
{
|
||||
/* col_plate_index = 0,
|
||||
col_assemble_name = 1,
|
||||
col_name = col_assemble_name + 1,
|
||||
col_printable = col_name + 1,
|
||||
col_printable_reset = col_printable + 1,
|
||||
col_filaments = col_printable_reset + 1,
|
||||
col_filaments_reset = col_filaments + 1,
|
||||
col_layer_height = col_filaments_reset + 1,
|
||||
col_layer_height_reset = col_layer_height + 1,
|
||||
col_wall_loops = col_layer_height_reset + 1,
|
||||
col_wall_loops_reset = col_wall_loops + 1,
|
||||
col_fill_density = col_wall_loops_reset + 1,
|
||||
col_fill_density_reset = col_fill_density + 1,
|
||||
col_enable_support = col_fill_density_reset + 1,
|
||||
col_enable_support_reset = col_enable_support + 1,
|
||||
col_brim_type = col_enable_support_reset + 1,
|
||||
col_brim_type_reset = col_brim_type + 1,
|
||||
col_speed_perimeter = col_brim_type_reset + 1,
|
||||
col_speed_perimeter_reset = col_speed_perimeter + 1,
|
||||
col_max*/
|
||||
col_printable = 0,
|
||||
col_printable_reset = 1,
|
||||
col_plate_index = 2,
|
||||
//col_assemble_name = 3,
|
||||
col_name = 3,
|
||||
col_filaments = 4,
|
||||
col_filaments_reset = 5,
|
||||
col_layer_height = 6,
|
||||
col_layer_height_reset = 7,
|
||||
col_wall_loops = 8,
|
||||
col_wall_loops_reset = 9,
|
||||
col_fill_density = 10,
|
||||
col_fill_density_reset = 11,
|
||||
col_enable_support = 12,
|
||||
col_enable_support_reset = 13,
|
||||
col_brim_type = 14,
|
||||
col_brim_type_reset = 15,
|
||||
col_speed_perimeter = 16,
|
||||
col_speed_perimeter_reset = 17,
|
||||
col_max
|
||||
};
|
||||
|
||||
struct ObjectGridRow
|
||||
{
|
||||
int object_id;
|
||||
int volume_id;
|
||||
GridRowType row_type;
|
||||
ConfigOptionString plate_index;
|
||||
//ConfigOptionString assemble_name;
|
||||
//ConfigOptionString ori_assemble_name;
|
||||
ConfigOptionString name;
|
||||
ConfigOptionString ori_name;
|
||||
ConfigOptionBool printable;
|
||||
ConfigOptionBool ori_printable;
|
||||
ConfigOptionInt filaments;
|
||||
ConfigOptionInt ori_filaments;
|
||||
ConfigOptionFloat layer_height;
|
||||
ConfigOptionFloat ori_layer_height;
|
||||
ConfigOptionInt wall_loops;
|
||||
ConfigOptionInt ori_wall_loops;
|
||||
ConfigOptionPercent sparse_infill_density;
|
||||
ConfigOptionPercent ori_fill_density;
|
||||
ConfigOptionBool enable_support;
|
||||
ConfigOptionBool ori_enable_support;
|
||||
ConfigOptionEnum<BrimType> brim_type;
|
||||
ConfigOptionEnum<BrimType> ori_brim_type;
|
||||
ConfigOptionFloat speed_perimeter;
|
||||
ConfigOptionFloat ori_speed_perimeter;
|
||||
|
||||
ModelConfig* config;
|
||||
|
||||
ObjectGridRow(int obj_id, int vol_id, GridRowType type)
|
||||
: object_id(obj_id), volume_id(vol_id), row_type(type)
|
||||
{
|
||||
config = nullptr;
|
||||
}
|
||||
|
||||
ConfigOption& operator[](GridColType idx)
|
||||
{
|
||||
switch(idx)
|
||||
{
|
||||
case col_plate_index:
|
||||
return plate_index;
|
||||
/*case col_assemble_name:
|
||||
return assemble_name;*/
|
||||
case col_name:
|
||||
return name;
|
||||
case col_printable:
|
||||
return printable;
|
||||
case col_printable_reset:
|
||||
return ori_printable;
|
||||
case col_filaments:
|
||||
return filaments;
|
||||
case col_filaments_reset:
|
||||
return ori_filaments;
|
||||
case col_layer_height:
|
||||
return layer_height;
|
||||
case col_layer_height_reset:
|
||||
return ori_layer_height;
|
||||
case col_wall_loops:
|
||||
return wall_loops;
|
||||
case col_wall_loops_reset:
|
||||
return ori_wall_loops;
|
||||
case col_fill_density:
|
||||
return sparse_infill_density;
|
||||
case col_fill_density_reset:
|
||||
return ori_fill_density;
|
||||
case col_enable_support:
|
||||
return enable_support;
|
||||
case col_enable_support_reset:
|
||||
return ori_enable_support;
|
||||
case col_brim_type:
|
||||
return brim_type;
|
||||
case col_brim_type_reset:
|
||||
return ori_brim_type;
|
||||
case col_speed_perimeter:
|
||||
return speed_perimeter;
|
||||
case col_speed_perimeter_reset:
|
||||
return ori_speed_perimeter;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
};
|
||||
typedef std::function<bool(ObjectGridRow* row1, ObjectGridRow* row2)> compare_row_func;
|
||||
|
||||
struct ObjectGridCol
|
||||
{
|
||||
int size;
|
||||
ConfigOptionType type;
|
||||
std::string key;
|
||||
std::string category;
|
||||
bool b_for_object;
|
||||
bool b_icon;
|
||||
bool b_editable;
|
||||
bool b_from_config;
|
||||
wxString *choices;
|
||||
int choice_count;
|
||||
int horizontal_align;
|
||||
|
||||
ObjectGridCol(ConfigOptionType option_type, std::string key_str, std::string cat, bool only_object, bool icon, bool edit, bool config, int ho_align)
|
||||
: type(option_type), key(key_str), category(cat), b_for_object(only_object), b_icon(icon), b_editable(edit), b_from_config(config), horizontal_align(ho_align)
|
||||
{
|
||||
if (b_icon)
|
||||
size = 32;
|
||||
else
|
||||
size = -1;
|
||||
choices = nullptr;
|
||||
choice_count = 0;
|
||||
}
|
||||
|
||||
~ObjectGridCol()
|
||||
{
|
||||
choices = nullptr;
|
||||
}
|
||||
};
|
||||
ObjectGridTable(ObjectTablePanel* panel): m_panel(panel) { }
|
||||
~ObjectGridTable();
|
||||
|
||||
void release_object_configs();
|
||||
wxString convert_filament_string(int index, wxString& filament_str);
|
||||
|
||||
virtual int GetNumberRows() wxOVERRIDE;
|
||||
virtual int GetNumberCols() wxOVERRIDE;
|
||||
virtual bool IsEmptyCell( int row, int col ) wxOVERRIDE;
|
||||
|
||||
|
||||
//virtual wxString GetColLabelValue( int col ) wxOVERRIDE;
|
||||
|
||||
virtual wxString GetTypeName( int row, int col ) wxOVERRIDE;
|
||||
virtual bool CanGetValueAs( int row, int col, const wxString& typeName ) wxOVERRIDE;
|
||||
virtual bool CanSetValueAs( int row, int col, const wxString& typeName ) wxOVERRIDE;
|
||||
|
||||
virtual wxString GetValue( int row, int col ) wxOVERRIDE;
|
||||
virtual void SetValue( int row, int col, const wxString& value ) wxOVERRIDE;
|
||||
|
||||
virtual long GetValueAsLong( int row, int col ) wxOVERRIDE;
|
||||
virtual bool GetValueAsBool( int row, int col ) wxOVERRIDE;
|
||||
virtual double GetValueAsDouble (int row, int col) wxOVERRIDE;
|
||||
|
||||
virtual void SetValueAsLong( int row, int col, long value ) wxOVERRIDE;
|
||||
virtual void SetValueAsBool( int row, int col, bool value ) wxOVERRIDE;
|
||||
virtual void SetValueAsDouble (int row, int col, double value) wxOVERRIDE;
|
||||
|
||||
void SetColLabelValue( int col, const wxString& ) wxOVERRIDE;
|
||||
wxString GetColLabelValue( int col ) wxOVERRIDE;
|
||||
|
||||
template<typename TYPE> const TYPE* get_object_config_value(const DynamicPrintConfig& global_config, ModelConfig* obj_config, std::string& config_option)
|
||||
{
|
||||
if (obj_config->has(config_option))
|
||||
return static_cast<const TYPE*>(obj_config->option(config_option));
|
||||
else {
|
||||
const TYPE* ptr = global_config.option<TYPE>(config_option);
|
||||
//todo: how to deal with nullptr
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TYPE> const TYPE* get_volume_config_value(const DynamicPrintConfig& global_config, ModelConfig* obj_config, ModelConfig* volume_config, std::string& config_option)
|
||||
{
|
||||
if (volume_config->has(config_option))
|
||||
return static_cast<const TYPE*>(volume_config->option(config_option));
|
||||
else if (obj_config->has(config_option))
|
||||
return static_cast<const TYPE*>(obj_config->option(config_option));
|
||||
else {
|
||||
const TYPE* ptr = global_config.option<TYPE>(config_option);
|
||||
//todo: how to deal with nullptr
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
|
||||
int get_row_count() { return m_grid_data.size() + 1; }
|
||||
int get_col_count() { return m_col_data.size(); }
|
||||
ObjectGridCol* get_grid_col(int col) { return m_col_data[col]; }
|
||||
ObjectGridRow* get_grid_row(int row) { return m_grid_data[row]; }
|
||||
void construct_object_configs(ObjectGrid* object_grid);
|
||||
void update_value_to_config(ModelConfig* config, std::string& key, ConfigOption& new_value, ConfigOption& ori_value);
|
||||
void update_filament_to_config(ModelConfig* config, std::string& key, ConfigOption& new_value, ConfigOption& ori_value, bool is_object);
|
||||
void update_volume_values_from_object(int row, int col);
|
||||
void update_value_to_object(Model* model, ObjectGridRow* grid_row, int col);
|
||||
wxBitmap& get_undo_bitmap(bool selected = false);
|
||||
wxBitmap* get_color_bitmap(int color_index);
|
||||
bool OnCellLeftClick(int row, int col, ConfigOptionType &type);
|
||||
void OnSelectCell(int row, int col);
|
||||
void OnRangeSelected(int row, int col, int row_count, int col_count);
|
||||
//void OnRangeSelecting( wxGridRangeSelectEvent& );
|
||||
//void OnCellValueChanging( wxGridEvent& );
|
||||
void OnCellValueChanged(int row, int col);
|
||||
//set the selection by object id and volume id
|
||||
void SetSelection(int object_id, int volume_id);
|
||||
//sort the table row datas by default
|
||||
void sort_by_default();
|
||||
void sort_by_col(int col);
|
||||
|
||||
//reload data caused by settings in the side window
|
||||
void reload_object_data(ObjectGridRow* grid_row, const std::string& category, DynamicPrintConfig& global_config);
|
||||
void reload_part_data(ObjectGridRow* volume_row, ObjectGridRow* object_row, const std::string& category, DynamicPrintConfig& global_config);
|
||||
void reload_cell_data(int row, const std::string& category);
|
||||
void resetValuesInCurrentCell(wxEvent& WXUNUSED(event));
|
||||
void enable_reset_all_button(bool enable);
|
||||
|
||||
int m_icon_col_width{ 0 };
|
||||
int m_icon_row_height{ 0 };
|
||||
private:
|
||||
ObjectTablePanel* m_panel{nullptr};
|
||||
std::vector<ObjectGridRow*> m_grid_data;
|
||||
std::vector<ObjectGridCol*> m_col_data;
|
||||
bool m_data_valid{false};
|
||||
|
||||
std::list<wxGridCellCoords> m_selected_cells;
|
||||
|
||||
int m_sort_col{ -1 };
|
||||
|
||||
void init_cols(ObjectGrid *object_grid);
|
||||
//generic function for sort row datas
|
||||
void sort_row_data(compare_row_func sort_func);
|
||||
//update the row properties for the data has been sorted
|
||||
void update_row_properties();
|
||||
int m_current_row {-1};
|
||||
int m_current_col {-1};
|
||||
|
||||
wxArrayString m_colLabels;
|
||||
};
|
||||
|
||||
|
||||
//the main panel
|
||||
class ObjectTablePanel : public wxPanel
|
||||
{
|
||||
void OnCellLeftClick( wxGridEvent& );
|
||||
void OnRowSize( wxGridSizeEvent& );
|
||||
void OnColSize( wxGridSizeEvent& );
|
||||
void OnSelectCell( wxGridEvent& );
|
||||
void OnRangeSelected( wxGridRangeSelectEvent& );
|
||||
//void OnRangeSelecting( wxGridRangeSelectEvent& );
|
||||
//void OnCellValueChanging( wxGridEvent& );
|
||||
void OnCellValueChanged( wxGridEvent& );
|
||||
//void OnCellBeginDrag( wxGridEvent& );
|
||||
public:
|
||||
ObjectTablePanel( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name, Plater* platerObj, Model *modelObj );
|
||||
~ObjectTablePanel();
|
||||
|
||||
void load_data();
|
||||
void SetSelection(int object_id, int volume_id);
|
||||
void sort_by_default() { m_object_grid_table->sort_by_default(); }
|
||||
wxSize get_init_size();
|
||||
void resetAllValuesInSideWindow(int row, bool is_object, ModelObject* object, ModelConfig* config, const std::string& category);
|
||||
|
||||
//set ObjectGridTable as friend
|
||||
friend class ObjectGridTable;
|
||||
|
||||
std::vector<wxString> m_filaments_name;
|
||||
std::vector<wxColour> m_filaments_colors;
|
||||
int m_filaments_count{ 1 };
|
||||
void set_default_filaments_and_colors()
|
||||
{
|
||||
m_filaments_count = 1;
|
||||
m_filaments_colors.push_back(*wxGREEN);
|
||||
m_filaments_name.push_back("Generic PLA");
|
||||
}
|
||||
|
||||
private:
|
||||
wxColour m_bg_colour;
|
||||
wxColour m_hover_colour;
|
||||
wxBoxSizer* m_top_sizer{nullptr};
|
||||
wxBoxSizer* m_page_sizer{nullptr};
|
||||
wxBoxSizer* m_page_top_sizer{nullptr};
|
||||
wxTextCtrl* m_search_line{ nullptr };
|
||||
ObjectGrid* m_object_grid{nullptr};
|
||||
ObjectGridTable* m_object_grid_table{nullptr};
|
||||
wxStaticText* m_page_text{nullptr};
|
||||
ScalableButton* m_global_reset{nullptr};
|
||||
wxScrolledWindow* m_side_window{nullptr};
|
||||
ObjectTableSettings* m_object_settings{ nullptr };
|
||||
Model* m_model{nullptr};
|
||||
ModelConfig* m_config {nullptr};
|
||||
Plater* m_plater{nullptr};
|
||||
|
||||
int m_cur_row { -1 };
|
||||
int m_cur_col { -1 };
|
||||
|
||||
int init_bitmap();
|
||||
int init_filaments_and_colors();
|
||||
|
||||
wxFloatingPointValidator<float> m_float_validator;
|
||||
wxBitmap m_undo_bitmap;
|
||||
std::vector<wxBitmap*> m_color_bitmaps;
|
||||
ScalableBitmap m_bmp_reset;
|
||||
ScalableBitmap m_bmp_reset_disable;
|
||||
private:
|
||||
wxDECLARE_ABSTRACT_CLASS(ObjectGrid);
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
class ObjectTableDialog : public GUI::DPIDialog
|
||||
{
|
||||
const int POPUP_WIDTH = FromDIP(512);
|
||||
const int POPUP_HEIGHT = FromDIP(1024);
|
||||
|
||||
//wxPanel* m_panel{ nullptr };
|
||||
wxBoxSizer* m_top_sizer{ nullptr };
|
||||
wxStaticText* m_static_title{ nullptr };
|
||||
//wxTimer* m_refresh_timer;
|
||||
ObjectTablePanel* m_obj_panel{ nullptr };
|
||||
Model* m_model{ nullptr };
|
||||
Plater* m_plater{ nullptr };
|
||||
|
||||
|
||||
public:
|
||||
ObjectTableDialog(wxWindow* parent, Plater* platerObj, Model *modelObj, wxSize maxSize);
|
||||
~ObjectTableDialog();
|
||||
void Popup(int obj_idx = -1, int vol_idx = -1, wxPoint position = wxDefaultPosition);
|
||||
void OnClose(wxCloseEvent &evt);
|
||||
void OnSize(wxSizeEvent& event);
|
||||
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect& suggested_rect) override;
|
||||
void on_sys_color_changed() override;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
#endif //slic3r_GUI_ObjectTable_hpp_
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue