Merge remote-tracking branch 'origin/dev' into sla_base_pool

This commit is contained in:
tamasmeszaros 2018-08-27 09:53:52 +02:00
commit d749080261
388 changed files with 115166 additions and 3275 deletions

View file

@ -32,7 +32,7 @@ void Bed_2D::repaint()
cw--;
ch--;
auto cbb = BoundingBoxf(Pointf(0, 0),Pointf(cw, ch));
auto cbb = BoundingBoxf(Vec2d(0, 0),Vec2d(cw, ch));
// leave space for origin point
cbb.min(0) += 4;
cbb.max -= Vec2d(4., 4.);
@ -50,19 +50,19 @@ void Bed_2D::repaint()
auto bed_shape = m_bed_shape;
auto bed_polygon = Polygon::new_scale(m_bed_shape);
auto bb = BoundingBoxf(m_bed_shape);
bb.merge(Pointf(0, 0)); // origin needs to be in the visible area
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 = Pointf(
auto shift = Vec2d(
ccenter(0) - bcenter(0) * sfactor,
ccenter(1) - bcenter(1) * sfactor
);
m_scale_factor = sfactor;
m_shift = Pointf(shift(0) + cbb.min(0),
m_shift = Vec2d(shift(0) + cbb.min(0),
shift(1) - (cbb.max(1) - GetSize().GetHeight()));
// draw bed fill
@ -79,11 +79,10 @@ void Bed_2D::repaint()
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) {
Polyline pl = Polyline::new_scale({ Pointf(x, bb.min(1)), Pointf(x, bb.max(1)) });
polylines.push_back(pl);
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({ Pointf(bb.min(0), y), Pointf(bb.max(0), y) }));
polylines.push_back(Polyline::new_scale({ Vec2d(bb.min(0), y), Vec2d(bb.max(0), y) }));
}
polylines = intersection_pl(polylines, bed_polygon);
@ -91,8 +90,8 @@ void Bed_2D::repaint()
for (auto pl : polylines)
{
for (size_t i = 0; i < pl.points.size()-1; i++){
Point pt1 = to_pixels(Pointf::new_unscale(pl.points[i]));
Point pt2 = to_pixels(Pointf::new_unscale(pl.points[i+1]));
Point pt1 = to_pixels(unscale(pl.points[i]));
Point pt2 = to_pixels(unscale(pl.points[i+1]));
dc.DrawLine(pt1(0), pt1(1), pt2(0), pt2(1));
}
}
@ -102,29 +101,25 @@ void Bed_2D::repaint()
dc.SetBrush(wxBrush(wxColour(0, 0, 0), wxTRANSPARENT));
dc.DrawPolygon(&pt_list, 0, 0);
auto origin_px = to_pixels(Pointf(0, 0));
auto origin_px = to_pixels(Vec2d(0, 0));
// 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, wxSOLID)); // red
auto x_end = Pointf(origin_px(0) + axes_len, origin_px(1));
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 = x_end;
end(0) -= arrow_len;
end.rotate(angle, x_end);
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, wxSOLID)); // green
auto y_end = Pointf(origin_px(0), origin_px(1) - axes_len);
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 = y_end;
end(1) += arrow_len;
end.rotate(angle, y_end);
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)));
}
@ -142,7 +137,7 @@ void Bed_2D::repaint()
dc.DrawText(origin_label, origin_label_x, origin_label_y);
// draw current position
if (m_pos!= Pointf(0, 0)) {
if (m_pos!= Vec2d(0, 0)) {
auto pos_px = to_pixels(m_pos);
dc.SetPen(wxPen(wxColour(200, 0, 0), 2, wxSOLID));
dc.SetBrush(wxBrush(wxColour(200, 0, 0), wxTRANSPARENT));
@ -156,7 +151,7 @@ void Bed_2D::repaint()
}
// convert G - code coordinates into pixels
Point Bed_2D::to_pixels(Pointf point){
Point Bed_2D::to_pixels(Vec2d point){
auto p = point * m_scale_factor + m_shift;
return Point(p(0), GetSize().GetHeight() - p(1));
}
@ -175,11 +170,11 @@ void Bed_2D::mouse_event(wxMouseEvent event){
}
// convert pixels into G - code coordinates
Pointf Bed_2D::to_units(Point point){
return (Pointf(point(0), GetSize().GetHeight() - point(1)) - m_shift) * (1. / m_scale_factor);
Vec2d Bed_2D::to_units(Point point){
return (Vec2d(point(0), GetSize().GetHeight() - point(1)) - m_shift) * (1. / m_scale_factor);
}
void Bed_2D::set_pos(Pointf pos){
void Bed_2D::set_pos(Vec2d pos){
m_pos = pos;
Refresh();
}

View file

@ -14,15 +14,15 @@ class Bed_2D : public wxPanel
bool m_painted = false;
bool m_interactive = false;
double m_scale_factor;
Pointf m_shift;
Pointf m_pos;
std::function<void(Pointf)> m_on_move = nullptr;
Vec2d m_shift = Vec2d::Zero();
Vec2d m_pos = Vec2d::Zero();
std::function<void(Vec2d)> m_on_move = nullptr;
Point to_pixels(Pointf point);
Pointf to_units(Point point);
Point to_pixels(Vec2d point);
Vec2d to_units(Point point);
void repaint();
void mouse_event(wxMouseEvent event);
void set_pos(Pointf pos);
void set_pos(Vec2d pos);
public:
Bed_2D(wxWindow* parent)
@ -41,7 +41,7 @@ public:
}
~Bed_2D(){}
std::vector<Pointf> m_bed_shape;
std::vector<Vec2d> m_bed_shape;
};

View file

@ -39,7 +39,7 @@ void GLIndexedVertexArray::load_mesh_flat_shading(const TriangleMesh &mesh)
for (int i = 0; i < mesh.stl.stats.number_of_facets; ++ i) {
const stl_facet &facet = mesh.stl.facet_start[i];
for (int j = 0; j < 3; ++ j)
this->push_geometry(facet.vertex[j].x, facet.vertex[j].y, facet.vertex[j].z, facet.normal.x, facet.normal.y, facet.normal.z);
this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2));
}
}
@ -55,7 +55,7 @@ void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh &mesh)
for (int i = 0; i < mesh.stl.stats.number_of_facets; ++i) {
const stl_facet &facet = mesh.stl.facet_start[i];
for (int j = 0; j < 3; ++j)
this->push_geometry(facet.vertex[j].x, facet.vertex[j].y, facet.vertex[j].z, facet.normal.x, facet.normal.y, facet.normal.z);
this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2));
this->push_triangle(vertices_count, vertices_count + 1, vertices_count + 2);
vertices_count += 3;
@ -195,9 +195,12 @@ const float GLVolume::OUTSIDE_COLOR[4] = { 0.0f, 0.38f, 0.8f, 1.0f };
const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 0.19f, 0.58f, 1.0f, 1.0f };
GLVolume::GLVolume(float r, float g, float b, float a)
: m_angle_z(0.0f)
: m_origin(0, 0, 0)
, m_angle_z(0.0f)
, m_scale_factor(1.0f)
, m_dirty(true)
, m_transformed_bounding_box_dirty(true)
, m_transformed_convex_hull_bounding_box_dirty(true)
, m_convex_hull(nullptr)
, composite_id(-1)
, select_group_id(-1)
, drag_group_id(-1)
@ -214,8 +217,6 @@ GLVolume::GLVolume(float r, float g, float b, float a)
, tverts_range(0, size_t(-1))
, qverts_range(0, size_t(-1))
{
m_world_mat = Transform3f::Identity();
color[0] = r;
color[1] = g;
color[2] = b;
@ -252,51 +253,86 @@ void GLVolume::set_render_color()
set_render_color(color, 4);
}
const Pointf3& GLVolume::get_origin() const
const Vec3d& GLVolume::get_origin() const
{
return m_origin;
}
void GLVolume::set_origin(const Pointf3& origin)
float GLVolume::get_angle_z()
{
m_origin = origin;
m_dirty = true;
return m_angle_z;
}
void GLVolume::set_origin(const Vec3d& origin)
{
if (m_origin != origin)
{
m_origin = origin;
m_transformed_bounding_box_dirty = true;
m_transformed_convex_hull_bounding_box_dirty = true;
}
}
void GLVolume::set_angle_z(float angle_z)
{
m_angle_z = angle_z;
m_dirty = true;
if (m_angle_z != angle_z)
{
m_angle_z = angle_z;
m_transformed_bounding_box_dirty = true;
m_transformed_convex_hull_bounding_box_dirty = true;
}
}
void GLVolume::set_scale_factor(float scale_factor)
{
m_scale_factor = scale_factor;
m_dirty = true;
if (m_scale_factor != scale_factor)
{
m_scale_factor = scale_factor;
m_transformed_bounding_box_dirty = true;
m_transformed_convex_hull_bounding_box_dirty = true;
}
}
const Transform3f& GLVolume::world_matrix() const
void GLVolume::set_convex_hull(const TriangleMesh& convex_hull)
{
if (m_dirty)
{
m_world_mat = Transform3f::Identity();
m_world_mat.translate(Vec3f(m_origin(0), m_origin(1), 0));
m_world_mat.rotate(Eigen::AngleAxisf(m_angle_z, Vec3f::UnitZ()));
m_world_mat.scale(m_scale_factor);
m_dirty = false;
}
m_convex_hull = &convex_hull;
}
return m_world_mat;
Transform3f GLVolume::world_matrix() const
{
Transform3f matrix = Transform3f::Identity();
matrix.translate(Vec3f((float)m_origin(0), (float)m_origin(1), (float)m_origin(2)));
matrix.rotate(Eigen::AngleAxisf(m_angle_z, Vec3f::UnitZ()));
matrix.scale(m_scale_factor);
return matrix;
}
BoundingBoxf3 GLVolume::transformed_bounding_box() const
{
if (m_dirty)
m_transformed_bounding_box = bounding_box.transformed(world_matrix());
if (m_transformed_bounding_box_dirty)
{
m_transformed_bounding_box = bounding_box.transformed(world_matrix().cast<double>());
m_transformed_bounding_box_dirty = false;
}
return m_transformed_bounding_box;
}
BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box() const
{
if (m_transformed_convex_hull_bounding_box_dirty)
{
if ((m_convex_hull != nullptr) && (m_convex_hull->stl.stats.number_of_facets > 0))
m_transformed_convex_hull_bounding_box = m_convex_hull->transformed_bounding_box(world_matrix().cast<double>());
else
m_transformed_convex_hull_bounding_box = bounding_box.transformed(world_matrix().cast<double>());
m_transformed_convex_hull_bounding_box_dirty = false;
}
return m_transformed_convex_hull_bounding_box;
}
void GLVolume::set_range(double min_z, double max_z)
{
this->qverts_range.first = 0;
@ -623,13 +659,14 @@ std::vector<int> GLVolumeCollection::load_object(
if (!model_volume->modifier)
{
v.set_convex_hull(model_volume->get_convex_hull());
v.layer_height_texture = layer_height_texture;
if (extruder_id != -1)
v.extruder_id = extruder_id;
}
v.is_modifier = model_volume->modifier;
v.shader_outside_printer_detection_enabled = !model_volume->modifier;
v.set_origin(Pointf3(instance->offset(0), instance->offset(1), 0.0));
v.set_origin(Vec3d(instance->offset(0), instance->offset(1), 0.0));
v.set_angle_z(instance->rotation);
v.set_scale_factor(instance->scaling_factor);
}
@ -660,16 +697,16 @@ int GLVolumeCollection::load_wipe_tower_preview(
// We'll now create the box with jagged edge. y-coordinates of the pre-generated model are shifted so that the front
// edge has y=0 and centerline of the back edge has y=depth:
Pointf3s points;
std::vector<Point3> facets;
std::vector<Vec3crd> facets;
float out_points_idx[][3] = {{0, -depth, 0}, {0, 0, 0}, {38.453, 0, 0}, {61.547, 0, 0}, {100, 0, 0}, {100, -depth, 0}, {55.7735, -10, 0}, {44.2265, 10, 0},
{38.453, 0, 1}, {0, 0, 1}, {0, -depth, 1}, {100, -depth, 1}, {100, 0, 1}, {61.547, 0, 1}, {55.7735, -10, 1}, {44.2265, 10, 1}};
int out_facets_idx[][3] = {{0, 1, 2}, {3, 4, 5}, {6, 5, 0}, {3, 5, 6}, {6, 2, 7}, {6, 0, 2}, {8, 9, 10}, {11, 12, 13}, {10, 11, 14}, {14, 11, 13}, {15, 8, 14},
{8, 10, 14}, {3, 12, 4}, {3, 13, 12}, {6, 13, 3}, {6, 14, 13}, {7, 14, 6}, {7, 15, 14}, {2, 15, 7}, {2, 8, 15}, {1, 8, 2}, {1, 9, 8},
{0, 9, 1}, {0, 10, 9}, {5, 10, 0}, {5, 11, 10}, {4, 11, 5}, {4, 12, 11}};
for (int i=0;i<16;++i)
points.push_back(Pointf3(out_points_idx[i][0] / (100.f/min_width), out_points_idx[i][1] + depth, out_points_idx[i][2]));
points.push_back(Vec3d(out_points_idx[i][0] / (100.f/min_width), out_points_idx[i][1] + depth, out_points_idx[i][2]));
for (int i=0;i<28;++i)
facets.push_back(Point3(out_facets_idx[i][0], out_facets_idx[i][1], out_facets_idx[i][2]));
facets.push_back(Vec3crd(out_facets_idx[i][0], out_facets_idx[i][1], out_facets_idx[i][2]));
TriangleMesh tooth_mesh(points, facets);
// We have the mesh ready. It has one tooth and width of min_width. We will now append several of these together until we are close to
@ -680,7 +717,7 @@ int GLVolumeCollection::load_wipe_tower_preview(
tooth_mesh.translate(min_width, 0.f, 0.f);
}
mesh.scale(Pointf3(width/(n*min_width), 1.f, height)); // Scaling to proper width
mesh.scale(Vec3d(width/(n*min_width), 1.f, height)); // Scaling to proper width
}
else
mesh = make_cube(width, depth, height);
@ -700,7 +737,7 @@ int GLVolumeCollection::load_wipe_tower_preview(
else
v.indexed_vertex_array.load_mesh_flat_shading(mesh);
v.set_origin(Pointf3(pos_x, pos_y, 0.));
v.set_origin(Vec3d(pos_x, pos_y, 0.));
// finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().
v.bounding_box = v.indexed_vertex_array.bounding_box();
@ -786,7 +823,7 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
return false;
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min(0)), unscale(bed_box_2D.min(1)), 0.0), Pointf3(unscale(bed_box_2D.max(0)), unscale(bed_box_2D.max(1)), config->opt_float("max_print_height")));
BoundingBoxf3 print_volume(Vec3d(unscale<double>(bed_box_2D.min(0)), unscale<double>(bed_box_2D.min(1)), 0.0), Vec3d(unscale<double>(bed_box_2D.max(0)), unscale<double>(bed_box_2D.max(1)), config->opt_float("max_print_height")));
// Allow the objects to protrude below the print bed
print_volume.min(2) = -1e10;
@ -795,9 +832,9 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
for (GLVolume* volume : this->volumes)
{
if ((volume != nullptr) && !volume->is_modifier)
if ((volume != nullptr) && !volume->is_modifier && (!volume->is_wipe_tower || (volume->is_wipe_tower && volume->shader_outside_printer_detection_enabled)))
{
const BoundingBoxf3& bb = volume->transformed_bounding_box();
const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box();
bool contained = print_volume.contains(bb);
all_contained &= contained;
@ -951,8 +988,8 @@ static void thick_lines_to_indexed_vertex_array(
// right, left, top, bottom
int idx_prev[4] = { -1, -1, -1, -1 };
double bottom_z_prev = 0.;
Pointf b1_prev;
Vectorf v_prev;
Vec2d b1_prev(Vec2d::Zero());
Vec2d v_prev(Vec2d::Zero());
int idx_initial[4] = { -1, -1, -1, -1 };
double width_initial = 0.;
double bottom_z_initial = 0.0;
@ -962,7 +999,7 @@ static void thick_lines_to_indexed_vertex_array(
for (size_t ii = 0; ii < lines_end; ++ ii) {
size_t i = (ii == lines.size()) ? 0 : ii;
const Line &line = lines[i];
double len = unscale(line.length());
double len = unscale<double>(line.length());
double inv_len = 1.0 / len;
double bottom_z = top_z - heights[i];
double middle_z = 0.5 * (top_z + bottom_z);
@ -972,28 +1009,28 @@ static void thick_lines_to_indexed_vertex_array(
bool is_last = (ii == lines_end - 1);
bool is_closing = closed && is_last;
Vectorf v = Vectorf::new_unscale(line.vector());
Vec2d v = unscale(line.vector());
v *= inv_len;
Pointf a = Pointf::new_unscale(line.a);
Pointf b = Pointf::new_unscale(line.b);
Pointf a1 = a;
Pointf a2 = a;
Pointf b1 = b;
Pointf b2 = b;
Vec2d a = unscale(line.a);
Vec2d b = unscale(line.b);
Vec2d a1 = a;
Vec2d a2 = a;
Vec2d b1 = b;
Vec2d b2 = b;
{
double dist = 0.5 * width; // scaled
double dx = dist * v(0);
double dy = dist * v(1);
a1 += Vectorf(+dy, -dx);
a2 += Vectorf(-dy, +dx);
b1 += Vectorf(+dy, -dx);
b2 += Vectorf(-dy, +dx);
a1 += Vec2d(+dy, -dx);
a2 += Vec2d(-dy, +dx);
b1 += Vec2d(+dy, -dx);
b2 += Vec2d(-dy, +dx);
}
// calculate new XY normals
Vector n = line.normal();
Vectorf3 xy_right_normal = Vectorf3::new_unscale(n(0), n(1), 0);
Vec3d xy_right_normal = unscale(n(0), n(1), 0);
xy_right_normal *= inv_len;
int idx_a[4];
@ -1063,7 +1100,7 @@ static void thick_lines_to_indexed_vertex_array(
{
// Create a sharp corner with an overshot and average the left / right normals.
// At the crease angle of 45 degrees, the overshot at the corner will be less than (1-1/cos(PI/8)) = 8.2% over an arc.
Pointf intersection;
Vec2d intersection(Vec2d::Zero());
Geometry::ray_ray_intersection(b1_prev, v_prev, a1, v, intersection);
a1 = intersection;
a2 = 2. * a - intersection;
@ -1196,15 +1233,15 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines,
int idx_initial[4] = { -1, -1, -1, -1 };
int idx_prev[4] = { -1, -1, -1, -1 };
double z_prev = 0.0;
Vectorf3 n_right_prev;
Vectorf3 n_top_prev;
Vectorf3 unit_v_prev;
Vec3d n_right_prev = Vec3d::Zero();
Vec3d n_top_prev = Vec3d::Zero();
Vec3d unit_v_prev = Vec3d::Zero();
double width_initial = 0.0;
// new vertices around the line endpoints
// left, right, top, bottom
Pointf3 a[4];
Pointf3 b[4];
Vec3d a[4] = { Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() };
Vec3d b[4] = { Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() };
// loop once more in case of closed loops
size_t lines_end = closed ? (lines.size() + 1) : lines.size();
@ -1216,17 +1253,17 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines,
double height = heights[i];
double width = widths[i];
Vectorf3 unit_v = Vectorf3::new_unscale(line.vector()).normalized();
Vec3d unit_v = unscale(line.vector()).normalized();
Vectorf3 n_top;
Vectorf3 n_right;
Vectorf3 unit_positive_z(0.0, 0.0, 1.0);
Vec3d n_top = Vec3d::Zero();
Vec3d n_right = Vec3d::Zero();
Vec3d unit_positive_z(0.0, 0.0, 1.0);
if ((line.a(0) == line.b(0)) && (line.a(1) == line.b(1)))
{
// vertical segment
n_right = (line.a(2) < line.b(2)) ? Vectorf3(-1.0, 0.0, 0.0) : Vectorf3(1.0, 0.0, 0.0);
n_top = Vectorf3(0.0, 1.0, 0.0);
n_right = (line.a(2) < line.b(2)) ? Vec3d(-1.0, 0.0, 0.0) : Vec3d(1.0, 0.0, 0.0);
n_top = Vec3d(0.0, 1.0, 0.0);
}
else
{
@ -1235,10 +1272,10 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines,
n_top = n_right.cross(unit_v).normalized();
}
Vectorf3 rl_displacement = 0.5 * width * n_right;
Vectorf3 tb_displacement = 0.5 * height * n_top;
Pointf3 l_a = Pointf3::new_unscale(line.a);
Pointf3 l_b = Pointf3::new_unscale(line.b);
Vec3d rl_displacement = 0.5 * width * n_right;
Vec3d tb_displacement = 0.5 * height * n_top;
Vec3d l_a = unscale(line.a);
Vec3d l_b = unscale(line.b);
a[RIGHT] = l_a + rl_displacement;
a[LEFT] = l_a - rl_displacement;
@ -1249,8 +1286,8 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines,
b[TOP] = l_b + tb_displacement;
b[BOTTOM] = l_b - tb_displacement;
Vectorf3 n_bottom = -n_top;
Vectorf3 n_left = -n_right;
Vec3d n_bottom = -n_top;
Vec3d n_left = -n_right;
int idx_a[4];
int idx_b[4];
@ -1316,9 +1353,9 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines,
// At the crease angle of 45 degrees, the overshot at the corner will be less than (1-1/cos(PI/8)) = 8.2% over an arc.
// averages normals
Vectorf3 average_n_right = 0.5 * (n_right + n_right_prev).normalized();
Vectorf3 average_n_left = -average_n_right;
Vectorf3 average_rl_displacement = 0.5 * width * average_n_right;
Vec3d average_n_right = 0.5 * (n_right + n_right_prev).normalized();
Vec3d average_n_left = -average_n_right;
Vec3d average_rl_displacement = 0.5 * width * average_n_right;
// updates vertices around a
a[RIGHT] = l_a + average_rl_displacement;
@ -1441,14 +1478,14 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines,
#undef BOTTOM
}
static void point_to_indexed_vertex_array(const Point3& point,
static void point_to_indexed_vertex_array(const Vec3crd& point,
double width,
double height,
GLIndexedVertexArray& volume)
{
// builds a double piramid, with vertices on the local axes, around the point
Pointf3 center = Pointf3::new_unscale(point);
Vec3d center = unscale(point);
double scale_factor = 1.0;
double w = scale_factor * width;
@ -1462,13 +1499,13 @@ static void point_to_indexed_vertex_array(const Point3& point,
idxs[i] = idx_last + i;
}
Vectorf3 displacement_x(w, 0.0, 0.0);
Vectorf3 displacement_y(0.0, w, 0.0);
Vectorf3 displacement_z(0.0, 0.0, h);
Vec3d displacement_x(w, 0.0, 0.0);
Vec3d displacement_y(0.0, w, 0.0);
Vec3d displacement_z(0.0, 0.0, h);
Vectorf3 unit_x(1.0, 0.0, 0.0);
Vectorf3 unit_y(0.0, 1.0, 0.0);
Vectorf3 unit_z(0.0, 0.0, 1.0);
Vec3d unit_x(1.0, 0.0, 0.0);
Vec3d unit_y(0.0, 1.0, 0.0);
Vec3d unit_z(0.0, 0.0, 1.0);
// vertices
volume.push_geometry(center - displacement_x, -unit_x); // idxs[0]
@ -1511,7 +1548,7 @@ void _3DScene::thick_lines_to_verts(const Lines3& lines,
thick_lines_to_indexed_vertex_array(lines, widths, heights, closed, volume.indexed_vertex_array);
}
static void thick_point_to_verts(const Point3& point,
static void thick_point_to_verts(const Vec3crd& point,
double width,
double height,
GLVolume& volume)
@ -1617,7 +1654,7 @@ void _3DScene::polyline3_to_verts(const Polyline3& polyline, double width, doubl
thick_lines_to_verts(lines, widths, heights, false, volume);
}
void _3DScene::point3_to_verts(const Point3& point, double width, double height, GLVolume& volume)
void _3DScene::point3_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume)
{
thick_point_to_verts(point, width, height, volume);
}
@ -1814,6 +1851,11 @@ void _3DScene::enable_gizmos(wxGLCanvas* canvas, bool enable)
s_canvas_mgr.enable_gizmos(canvas, enable);
}
void _3DScene::enable_toolbar(wxGLCanvas* canvas, bool enable)
{
s_canvas_mgr.enable_toolbar(canvas, enable);
}
void _3DScene::enable_shader(wxGLCanvas* canvas, bool enable)
{
s_canvas_mgr.enable_shader(canvas, enable);
@ -1834,6 +1876,16 @@ void _3DScene::allow_multisample(wxGLCanvas* canvas, bool allow)
s_canvas_mgr.allow_multisample(canvas, allow);
}
void _3DScene::enable_toolbar_item(wxGLCanvas* canvas, const std::string& name, bool enable)
{
s_canvas_mgr.enable_toolbar_item(canvas, name, enable);
}
bool _3DScene::is_toolbar_item_pressed(wxGLCanvas* canvas, const std::string& name)
{
return s_canvas_mgr.is_toolbar_item_pressed(canvas, name);
}
void _3DScene::zoom_to_bed(wxGLCanvas* canvas)
{
s_canvas_mgr.zoom_to_bed(canvas);
@ -1969,6 +2021,56 @@ void _3DScene::register_on_update_geometry_info_callback(wxGLCanvas* canvas, voi
s_canvas_mgr.register_on_update_geometry_info_callback(canvas, callback);
}
void _3DScene::register_action_add_callback(wxGLCanvas* canvas, void* callback)
{
s_canvas_mgr.register_action_add_callback(canvas, callback);
}
void _3DScene::register_action_delete_callback(wxGLCanvas* canvas, void* callback)
{
s_canvas_mgr.register_action_delete_callback(canvas, callback);
}
void _3DScene::register_action_deleteall_callback(wxGLCanvas* canvas, void* callback)
{
s_canvas_mgr.register_action_deleteall_callback(canvas, callback);
}
void _3DScene::register_action_arrange_callback(wxGLCanvas* canvas, void* callback)
{
s_canvas_mgr.register_action_arrange_callback(canvas, callback);
}
void _3DScene::register_action_more_callback(wxGLCanvas* canvas, void* callback)
{
s_canvas_mgr.register_action_more_callback(canvas, callback);
}
void _3DScene::register_action_fewer_callback(wxGLCanvas* canvas, void* callback)
{
s_canvas_mgr.register_action_fewer_callback(canvas, callback);
}
void _3DScene::register_action_split_callback(wxGLCanvas* canvas, void* callback)
{
s_canvas_mgr.register_action_split_callback(canvas, callback);
}
void _3DScene::register_action_cut_callback(wxGLCanvas* canvas, void* callback)
{
s_canvas_mgr.register_action_cut_callback(canvas, callback);
}
void _3DScene::register_action_settings_callback(wxGLCanvas* canvas, void* callback)
{
s_canvas_mgr.register_action_settings_callback(canvas, callback);
}
void _3DScene::register_action_layersediting_callback(wxGLCanvas* canvas, void* callback)
{
s_canvas_mgr.register_action_layersediting_callback(canvas, callback);
}
static inline int hex_digit_to_int(const char c)
{
return

View file

@ -119,7 +119,7 @@ public:
push_geometry(float(x), float(y), float(z), float(nx), float(ny), float(nz));
}
inline void push_geometry(const Pointf3& p, const Vectorf3& n) {
inline void push_geometry(const Vec3d& p, const Vec3d& n) {
push_geometry(p(0), p(1), p(2), n(0), n(1), n(2));
}
@ -255,17 +255,21 @@ public:
private:
// Offset of the volume to be rendered.
Pointf3 m_origin;
Vec3d m_origin;
// Rotation around Z axis of the volume to be rendered.
float m_angle_z;
// Scale factor of the volume to be rendered.
float m_scale_factor;
// World matrix of the volume to be rendered.
mutable Transform3f m_world_mat;
// Bounding box of this volume, in unscaled coordinates.
mutable BoundingBoxf3 m_transformed_bounding_box;
// Whether or not is needed to recalculate the world matrix.
mutable bool m_dirty;
// Whether or not is needed to recalculate the transformed bounding box.
mutable bool m_transformed_bounding_box_dirty;
// Pointer to convex hull of the original mesh, if any.
const TriangleMesh* m_convex_hull;
// Bounding box of this volume, in unscaled coordinates.
mutable BoundingBoxf3 m_transformed_convex_hull_bounding_box;
// Whether or not is needed to recalculate the transformed convex hull bounding box.
mutable bool m_transformed_convex_hull_bounding_box_dirty;
public:
@ -319,17 +323,20 @@ public:
// Sets render color in dependence of current state
void set_render_color();
const Pointf3& get_origin() const;
void set_origin(const Pointf3& origin);
float get_angle_z();
const Vec3d& get_origin() const;
void set_origin(const Vec3d& origin);
void set_angle_z(float angle_z);
void set_scale_factor(float scale_factor);
void set_convex_hull(const TriangleMesh& convex_hull);
int object_idx() const { return this->composite_id / 1000000; }
int volume_idx() const { return (this->composite_id / 1000) % 1000; }
int instance_idx() const { return this->composite_id % 1000; }
const Transform3f& world_matrix() const;
Transform3f world_matrix() const;
BoundingBoxf3 transformed_bounding_box() const;
BoundingBoxf3 transformed_convex_hull_bounding_box() const;
bool empty() const { return this->indexed_vertex_array.empty(); }
bool indexed() const { return this->indexed_vertex_array.indexed(); }
@ -497,11 +504,15 @@ public:
static void enable_picking(wxGLCanvas* canvas, bool enable);
static void enable_moving(wxGLCanvas* canvas, bool enable);
static void enable_gizmos(wxGLCanvas* canvas, bool enable);
static void enable_toolbar(wxGLCanvas* canvas, bool enable);
static void enable_shader(wxGLCanvas* canvas, bool enable);
static void enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable);
static void enable_dynamic_background(wxGLCanvas* canvas, bool enable);
static void allow_multisample(wxGLCanvas* canvas, bool allow);
static void enable_toolbar_item(wxGLCanvas* canvas, const std::string& name, bool enable);
static bool is_toolbar_item_pressed(wxGLCanvas* canvas, const std::string& name);
static void zoom_to_bed(wxGLCanvas* canvas);
static void zoom_to_volumes(wxGLCanvas* canvas);
static void select_view(wxGLCanvas* canvas, const std::string& direction);
@ -534,6 +545,17 @@ public:
static void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback);
static void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback);
static void register_action_add_callback(wxGLCanvas* canvas, void* callback);
static void register_action_delete_callback(wxGLCanvas* canvas, void* callback);
static void register_action_deleteall_callback(wxGLCanvas* canvas, void* callback);
static void register_action_arrange_callback(wxGLCanvas* canvas, void* callback);
static void register_action_more_callback(wxGLCanvas* canvas, void* callback);
static void register_action_fewer_callback(wxGLCanvas* canvas, void* callback);
static void register_action_split_callback(wxGLCanvas* canvas, void* callback);
static void register_action_cut_callback(wxGLCanvas* canvas, void* callback);
static void register_action_settings_callback(wxGLCanvas* canvas, void* callback);
static void register_action_layersediting_callback(wxGLCanvas* canvas, void* callback);
static std::vector<int> load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector<int> instance_idxs);
static std::vector<int> load_object(wxGLCanvas* canvas, const Model* model, int obj_idx);
@ -553,7 +575,7 @@ public:
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 Point3& point, double width, double height, GLVolume& volume);
static void point3_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume);
};
}

View file

@ -233,6 +233,7 @@ void AppConfig::reset_selections()
if (it != m_storage.end()) {
it->second.erase("print");
it->second.erase("filament");
it->second.erase("sla_material");
it->second.erase("printer");
m_dirty = true;
}

View file

@ -48,14 +48,14 @@ void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
auto optgroup = init_shape_options_page(_(L("Rectangular")));
ConfigOptionDef def;
def.type = coPoints;
def.default_value = new ConfigOptionPoints{ Pointf(200, 200) };
def.default_value = new ConfigOptionPoints{ Vec2d(200, 200) };
def.label = L("Size");
def.tooltip = L("Size in X and Y of the rectangular plate.");
Option option(def, "rect_size");
optgroup->append_single_option_line(option);
def.type = coPoints;
def.default_value = new ConfigOptionPoints{ Pointf(0, 0) };
def.default_value = new ConfigOptionPoints{ Vec2d(0, 0) };
def.label = L("Origin");
def.tooltip = L("Distance of the 0,0 G-code coordinate from the front left corner of the rectangle.");
option = Option(def, "rect_origin");
@ -159,11 +159,11 @@ void BedShapePanel::set_shape(ConfigOptionPoints* points)
y_max = std::max(y_max, pt(1));
}
auto origin = new ConfigOptionPoints{ Pointf(-x_min, -y_min) };
auto origin = new ConfigOptionPoints{ Vec2d(-x_min, -y_min) };
m_shape_options_book->SetSelection(SHAPE_RECTANGULAR);
auto optgroup = m_optgroups[SHAPE_RECTANGULAR];
optgroup->set_value("rect_size", new ConfigOptionPoints{ Pointf(x_max - x_min, y_max - y_min) });//[x_max - x_min, y_max - y_min]);
optgroup->set_value("rect_size", new ConfigOptionPoints{ Vec2d(x_max - x_min, y_max - y_min) });//[x_max - x_min, y_max - y_min]);
optgroup->set_value("rect_origin", origin);
update_shape();
return;
@ -195,7 +195,7 @@ void BedShapePanel::set_shape(ConfigOptionPoints* points)
// all vertices are equidistant to center
m_shape_options_book->SetSelection(SHAPE_CIRCULAR);
auto optgroup = m_optgroups[SHAPE_CIRCULAR];
boost::any ret = wxNumberFormatter::ToString(unscale(avg_dist * 2), 0);
boost::any ret = wxNumberFormatter::ToString(unscale<double>(avg_dist * 2), 0);
optgroup->set_value("diameter", ret);
update_shape();
return;
@ -206,8 +206,8 @@ void BedShapePanel::set_shape(ConfigOptionPoints* points)
// Invalid polygon.Revert to default bed dimensions.
m_shape_options_book->SetSelection(SHAPE_RECTANGULAR);
auto optgroup = m_optgroups[SHAPE_RECTANGULAR];
optgroup->set_value("rect_size", new ConfigOptionPoints{ Pointf(200, 200) });
optgroup->set_value("rect_origin", new ConfigOptionPoints{ Pointf(0, 0) });
optgroup->set_value("rect_size", new ConfigOptionPoints{ Vec2d(200, 200) });
optgroup->set_value("rect_origin", new ConfigOptionPoints{ Vec2d(0, 0) });
update_shape();
return;
}
@ -230,13 +230,15 @@ void BedShapePanel::update_shape()
{
auto page_idx = m_shape_options_book->GetSelection();
if (page_idx == SHAPE_RECTANGULAR) {
Pointf rect_size, rect_origin;
Vec2d rect_size(Vec2d::Zero());
Vec2d rect_origin(Vec2d::Zero());
try{
rect_size = boost::any_cast<Pointf>(m_optgroups[SHAPE_RECTANGULAR]->get_value("rect_size")); }
rect_size = boost::any_cast<Vec2d>(m_optgroups[SHAPE_RECTANGULAR]->get_value("rect_size")); }
catch (const std::exception &e){
return;}
return;
}
try{
rect_origin = boost::any_cast<Pointf>(m_optgroups[SHAPE_RECTANGULAR]->get_value("rect_origin"));
rect_origin = boost::any_cast<Vec2d>(m_optgroups[SHAPE_RECTANGULAR]->get_value("rect_origin"));
}
catch (const std::exception &e){
return;}
@ -257,10 +259,10 @@ void BedShapePanel::update_shape()
x1 -= dx;
y0 -= dy;
y1 -= dy;
m_canvas->m_bed_shape = { Pointf(x0, y0),
Pointf(x1, y0),
Pointf(x1, y1),
Pointf(x0, y1)};
m_canvas->m_bed_shape = { Vec2d(x0, y0),
Vec2d(x1, y0),
Vec2d(x1, y1),
Vec2d(x0, y1)};
}
else if(page_idx == SHAPE_CIRCULAR) {
double diameter;
@ -274,10 +276,10 @@ void BedShapePanel::update_shape()
auto r = diameter / 2;
auto twopi = 2 * PI;
auto edges = 60;
std::vector<Pointf> points;
std::vector<Vec2d> points;
for (size_t i = 1; i <= 60; ++i){
auto angle = i * twopi / edges;
points.push_back(Pointf(r*cos(angle), r*sin(angle)));
points.push_back(Vec2d(r*cos(angle), r*sin(angle)));
}
m_canvas->m_bed_shape = points;
}
@ -330,9 +332,9 @@ void BedShapePanel::load_stl()
}
auto polygon = expolygons[0].contour;
std::vector<Pointf> points;
std::vector<Vec2d> points;
for (auto pt : polygon.points)
points.push_back(Pointf::new_unscale(pt));
points.push_back(unscale(pt));
m_canvas->m_bed_shape = points;
update_preview();
}

View file

@ -34,7 +34,7 @@ public:
void load_stl();
// Returns the resulting bed shape polygon. This value will be stored to the ini file.
std::vector<Pointf> GetValue() { return m_canvas->m_bed_shape; }
std::vector<Vec2d> GetValue() { return m_canvas->m_bed_shape; }
};
class BedShapeDialog : public wxDialog
@ -46,7 +46,7 @@ public:
~BedShapeDialog(){ }
void build_dialog(ConfigOptionPoints* default_pt);
std::vector<Pointf> GetValue() { return m_panel->GetValue(); }
std::vector<Vec2d> GetValue() { return m_panel->GetValue(); }
};
} // GUI

View file

@ -132,7 +132,11 @@ namespace Slic3r { namespace GUI {
break;
}
double val;
str.ToCDouble(&val);
if(!str.ToCDouble(&val))
{
show_error(m_parent, _(L("Input value contains incorrect symbol(s).\nUse, please, only digits")));
set_value(double_to_string(val), true);
}
if (m_opt.min > val || val > m_opt.max)
{
show_error(m_parent, _(L("Input value is out of range")));
@ -274,7 +278,7 @@ void CheckBox::BUILD() {
bool check_value = m_opt.type == coBool ?
m_opt.default_value->getBool() : m_opt.type == coBools ?
static_cast<ConfigOptionBools*>(m_opt.default_value)->get_at(m_opt_idx) :
static_cast<const ConfigOptionBools*>(m_opt.default_value)->get_at(m_opt_idx) :
false;
auto temp = new wxCheckBox(m_parent, wxID_ANY, wxString(""), wxDefaultPosition, size);
@ -330,9 +334,7 @@ void SpinCtrl::BUILD() {
break;
}
const int min_val = m_opt_id == "standby_temperature_delta" ?
-500 : m_opt.min > 0 ?
m_opt.min : 0;
const int min_val = m_opt.min == INT_MIN ? 0: m_opt.min;
const int max_val = m_opt.max < 2147483647 ? m_opt.max : 2147483647;
auto temp = new wxSpinCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size,
@ -558,8 +560,11 @@ boost::any& Choice::get_value()
// boost::any m_value;
wxString ret_str = static_cast<wxComboBox*>(window)->GetValue();
if (m_opt_id == "support")
return m_value = boost::any(ret_str);//ret_str;
// options from right panel
std::vector <std::string> right_panel_options{ "support", "scale_unit" };
for (auto rp_option: right_panel_options)
if (m_opt_id == rp_option)
return m_value = boost::any(ret_str);
if (m_opt.type != coEnum)
/*m_value = */get_value_by_opt_type(ret_str);
@ -586,6 +591,8 @@ boost::any& Choice::get_value()
m_value = static_cast<SupportMaterialPattern>(ret_enum);
else if (m_opt_id.compare("seam_position") == 0)
m_value = static_cast<SeamPosition>(ret_enum);
else if (m_opt_id.compare("host_type") == 0)
m_value = static_cast<PrintHostType>(ret_enum);
}
return m_value;
@ -597,7 +604,7 @@ void ColourPicker::BUILD()
if (m_opt.height >= 0) size.SetHeight(m_opt.height);
if (m_opt.width >= 0) size.SetWidth(m_opt.width);
wxString clr(static_cast<ConfigOptionStrings*>(m_opt.default_value)->get_at(m_opt_idx));
wxString clr(static_cast<const ConfigOptionStrings*>(m_opt.default_value)->get_at(m_opt_idx));
auto temp = new wxColourPickerCtrl(m_parent, wxID_ANY, clr, wxDefaultPosition, size);
// // recast as a wxWindow to fit the calling convention
@ -629,7 +636,7 @@ void PointCtrl::BUILD()
//
wxSize field_size(40, -1);
auto default_pt = static_cast<ConfigOptionPoints*>(m_opt.default_value)->values.at(0);
auto default_pt = static_cast<const ConfigOptionPoints*>(m_opt.default_value)->values.at(0);
double val = default_pt(0);
wxString X = val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None);
val = default_pt(1);
@ -653,7 +660,7 @@ void PointCtrl::BUILD()
y_textctrl->SetToolTip(get_tooltip_text(X+", "+Y));
}
void PointCtrl::set_value(const Pointf& value, bool change_event)
void PointCtrl::set_value(const Vec2d& value, bool change_event)
{
m_disable_change_event = !change_event;
@ -667,8 +674,8 @@ void PointCtrl::set_value(const Pointf& value, bool change_event)
void PointCtrl::set_value(const boost::any& value, bool change_event)
{
Pointf pt;
const Pointf *ptf = boost::any_cast<Pointf>(&value);
Vec2d pt(Vec2d::Zero());
const Vec2d *ptf = boost::any_cast<Vec2d>(&value);
if (!ptf)
{
ConfigOptionPoints* pts = boost::any_cast<ConfigOptionPoints*>(value);
@ -681,13 +688,10 @@ void PointCtrl::set_value(const boost::any& value, bool change_event)
boost::any& PointCtrl::get_value()
{
Pointf ret_point;
double val;
x_textctrl->GetValue().ToDouble(&val);
ret_point(0) = val;
y_textctrl->GetValue().ToDouble(&val);
ret_point(1) = val;
return m_value = ret_point;
double x, y;
x_textctrl->GetValue().ToDouble(&x);
y_textctrl->GetValue().ToDouble(&y);
return m_value = Vec2d(x, y);
}
void StaticText::BUILD()
@ -696,7 +700,7 @@ void StaticText::BUILD()
if (m_opt.height >= 0) size.SetHeight(m_opt.height);
if (m_opt.width >= 0) size.SetWidth(m_opt.width);
wxString legend(static_cast<ConfigOptionString*>(m_opt.default_value)->value);
wxString legend(static_cast<const ConfigOptionString*>(m_opt.default_value)->value);
auto temp = new wxStaticText(m_parent, wxID_ANY, legend, wxDefaultPosition, size);
temp->SetFont(bold_font());
@ -706,6 +710,69 @@ void StaticText::BUILD()
temp->SetToolTip(get_tooltip_text(legend));
}
void SliderCtrl::BUILD()
{
auto size = wxSize(wxDefaultSize);
if (m_opt.height >= 0) size.SetHeight(m_opt.height);
if (m_opt.width >= 0) size.SetWidth(m_opt.width);
auto temp = new wxBoxSizer(wxHORIZONTAL);
auto def_val = static_cast<const ConfigOptionInt*>(m_opt.default_value)->value;
auto min = m_opt.min == INT_MIN ? 0 : m_opt.min;
auto max = m_opt.max == INT_MAX ? 100 : m_opt.max;
m_slider = new wxSlider(m_parent, wxID_ANY, def_val * m_scale,
min * m_scale, max * m_scale,
wxDefaultPosition, size);
wxSize field_size(40, -1);
m_textctrl = new wxTextCtrl(m_parent, wxID_ANY, wxString::Format("%d", m_slider->GetValue()/m_scale),
wxDefaultPosition, field_size);
temp->Add(m_slider, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL, 0);
temp->Add(m_textctrl, 0, wxALIGN_CENTER_VERTICAL, 0);
m_slider->Bind(wxEVT_SLIDER, ([this](wxCommandEvent e) {
if (!m_disable_change_event){
int val = boost::any_cast<int>(get_value());
m_textctrl->SetLabel(wxString::Format("%d", val));
on_change_field();
}
}), m_slider->GetId());
m_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) {
std::string value = e.GetString().utf8_str().data();
if (is_matched(value, "^-?\\d+(\\.\\d*)?$")){
m_disable_change_event = true;
m_slider->SetValue(stoi(value)*m_scale);
m_disable_change_event = false;
on_change_field();
}
}), m_textctrl->GetId());
m_sizer = dynamic_cast<wxSizer*>(temp);
}
void SliderCtrl::set_value(const boost::any& value, bool change_event)
{
m_disable_change_event = !change_event;
m_slider->SetValue(boost::any_cast<int>(value)*m_scale);
int val = boost::any_cast<int>(get_value());
m_textctrl->SetLabel(wxString::Format("%d", val));
m_disable_change_event = false;
}
boost::any& SliderCtrl::get_value()
{
// int ret_val;
// x_textctrl->GetValue().ToDouble(&val);
return m_value = int(m_slider->GetValue()/m_scale);
}
} // GUI
} // Slic3r

View file

@ -38,6 +38,7 @@ wxString double_to_string(double const value);
class MyButton : public wxButton
{
bool hidden = false; // never show button if it's hidden ones
public:
MyButton() {}
MyButton(wxWindow* parent, wxWindowID id, const wxString& label = wxEmptyString,
@ -52,6 +53,12 @@ public:
// overridden from wxWindow base class
virtual bool
AcceptsFocusFromKeyboard() const { return false; }
virtual bool Show(bool show = true) override {
if (!show)
hidden = true;
return wxButton::Show(!hidden);
}
};
class Field {
@ -189,6 +196,10 @@ public:
return false;
}
void set_side_text_ptr(wxStaticText* side_text) {
m_side_text = side_text;
}
protected:
MyButton* m_Undo_btn = nullptr;
// 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.
@ -203,6 +214,8 @@ protected:
// 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;
wxStaticText* m_side_text = nullptr;
// current value
boost::any m_value;
@ -214,7 +227,7 @@ protected:
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; }
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; }
@ -372,7 +385,7 @@ public:
void BUILD() override;
void set_value(const Pointf& value, bool change_event = false);
void set_value(const Vec2d& value, bool change_event = false);
void set_value(const boost::any& value, bool change_event = false);
boost::any& get_value() override;
@ -408,11 +421,44 @@ public:
boost::any& get_value()override { return m_value; }
void enable() override { dynamic_cast<wxColourPickerCtrl*>(window)->Enable(); };
void disable() override{ dynamic_cast<wxColourPickerCtrl*>(window)->Disable(); };
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);
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

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@
#define slic3r_GLCanvas3D_hpp_
#include "../../slic3r/GUI/3DScene.hpp"
#include "../../slic3r/GUI/GLTexture.hpp"
#include "../../slic3r/GUI/GLToolbar.hpp"
class wxTimer;
class wxSizeEvent;
@ -120,7 +120,7 @@ public:
float zoom;
float phi;
// float distance;
Pointf3 target;
Vec3d target;
private:
float m_theta;
@ -185,7 +185,7 @@ public:
struct Axes
{
Pointf3 origin;
Vec3d origin;
float length;
Axes();
@ -299,11 +299,11 @@ public:
struct Drag
{
static const Point Invalid_2D_Point;
static const Pointf3 Invalid_3D_Point;
static const Vec3d Invalid_3D_Point;
Point start_position_2D;
Pointf3 start_position_3D;
Vectorf3 volume_center_offset;
Vec3d start_position_3D;
Vec3d volume_center_offset;
bool move_with_shift;
int move_volume_idx;
@ -314,7 +314,7 @@ public:
};
bool dragging;
Pointf position;
Vec2d position;
Drag drag;
Mouse();
@ -352,20 +352,20 @@ public:
Gizmos();
~Gizmos();
bool init();
bool init(GLCanvas3D& parent);
bool is_enabled() const;
void set_enabled(bool enable);
void update_hover_state(const GLCanvas3D& canvas, const Pointf& mouse_pos);
void update_on_off_state(const GLCanvas3D& canvas, const Pointf& mouse_pos);
void update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos);
void update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos);
void reset_all_states();
void set_hover_id(int id);
bool overlay_contains_mouse(const GLCanvas3D& canvas, const Pointf& mouse_pos) const;
bool overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const;
bool grabber_contains_mouse() const;
void update(const Pointf& mouse_pos);
void update(const Linef3& mouse_ray);
void refresh();
EType get_current_type() const;
@ -382,8 +382,9 @@ public:
float get_angle_z() const;
void set_angle_z(float angle_z);
void render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const;
void render_current_gizmo(const BoundingBoxf3& box) const;
void render_current_gizmo_for_picking_pass(const BoundingBoxf3& box) const;
void render_overlay(const GLCanvas3D& canvas) const;
private:
void _reset();
@ -400,8 +401,15 @@ public:
static const unsigned char Background_Color[3];
static const unsigned char Opacity;
int m_original_width;
int m_original_height;
public:
WarningTexture();
bool generate(const std::string& msg);
void render(const GLCanvas3D& canvas) const;
};
class LegendTexture : public GUI::GLTexture
@ -415,8 +423,15 @@ public:
static const unsigned char Background_Color[3];
static const unsigned char Opacity;
int m_original_width;
int m_original_height;
public:
LegendTexture();
bool generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
void render(const GLCanvas3D& canvas) const;
};
private:
@ -433,6 +448,7 @@ private:
Shader m_shader;
Mouse m_mouse;
mutable Gizmos m_gizmos;
mutable GLToolbar m_toolbar;
mutable GLVolumeCollection m_volumes;
DynamicPrintConfig* m_config;
@ -445,6 +461,7 @@ private:
bool m_force_zoom_to_bed_enabled;
bool m_apply_zoom_to_volumes_filter;
mutable int m_hover_volume_id;
bool m_toolbar_action_running;
bool m_warning_texture_enabled;
bool m_legend_texture_enabled;
bool m_picking_enabled;
@ -482,6 +499,17 @@ private:
PerlCallback m_on_gizmo_rotate_callback;
PerlCallback m_on_update_geometry_info_callback;
PerlCallback m_action_add_callback;
PerlCallback m_action_delete_callback;
PerlCallback m_action_deleteall_callback;
PerlCallback m_action_arrange_callback;
PerlCallback m_action_more_callback;
PerlCallback m_action_fewer_callback;
PerlCallback m_action_split_callback;
PerlCallback m_action_cut_callback;
PerlCallback m_action_settings_callback;
PerlCallback m_action_layersediting_callback;
public:
GLCanvas3D(wxGLCanvas* canvas);
~GLCanvas3D();
@ -539,11 +567,15 @@ public:
void enable_picking(bool enable);
void enable_moving(bool enable);
void enable_gizmos(bool enable);
void enable_toolbar(bool enable);
void enable_shader(bool enable);
void enable_force_zoom_to_bed(bool enable);
void enable_dynamic_background(bool enable);
void allow_multisample(bool allow);
void enable_toolbar_item(const std::string& name, bool enable);
bool is_toolbar_item_pressed(const std::string& name) const;
void zoom_to_bed();
void zoom_to_volumes();
void select_view(const std::string& direction);
@ -584,6 +616,17 @@ public:
void register_on_gizmo_rotate_callback(void* callback);
void register_on_update_geometry_info_callback(void* callback);
void register_action_add_callback(void* callback);
void register_action_delete_callback(void* callback);
void register_action_deleteall_callback(void* callback);
void register_action_arrange_callback(void* callback);
void register_action_more_callback(void* callback);
void register_action_fewer_callback(void* callback);
void register_action_split_callback(void* callback);
void register_action_cut_callback(void* callback);
void register_action_settings_callback(void* callback);
void register_action_layersediting_callback(void* callback);
void bind_event_handlers();
void unbind_event_handlers();
@ -601,10 +644,14 @@ public:
void reset_legend_texture();
void set_tooltip(const std::string& tooltip);
private:
bool _is_shown_on_screen() const;
void _force_zoom_to_bed();
bool _init_toolbar();
void _resize(unsigned int w, unsigned int h);
BoundingBoxf3 _max_bounding_box() const;
@ -629,17 +676,22 @@ private:
void _render_legend_texture() const;
void _render_layer_editing_overlay() const;
void _render_volumes(bool fake_colors) const;
void _render_gizmo() const;
void _render_current_gizmo() const;
void _render_gizmos_overlay() const;
void _render_toolbar() const;
float _get_layers_editing_cursor_z_relative() const;
void _perform_layer_editing_action(wxMouseEvent* evt = nullptr);
// Convert the screen space coordinate to an object space coordinate.
// If the Z screen space coordinate is not provided, a depth buffer value is substituted.
Pointf3 _mouse_to_3d(const Point& mouse_pos, float* z = nullptr);
Vec3d _mouse_to_3d(const Point& mouse_pos, float* z = nullptr);
// Convert the screen space coordinate to world coordinate on the bed.
Pointf3 _mouse_to_bed_3d(const Point& mouse_pos);
Vec3d _mouse_to_bed_3d(const Point& mouse_pos);
// Returns the view ray line, in world coordinate, at the given mouse position.
Linef3 mouse_ray(const Point& mouse_pos);
void _start_timer();
void _stop_timer();
@ -687,6 +739,8 @@ private:
bool _is_any_volume_outside() const;
void _resize_toolbar() const;
static std::vector<float> _parse_colors(const std::vector<std::string>& colors);
};

View file

@ -404,6 +404,13 @@ void GLCanvas3DManager::enable_gizmos(wxGLCanvas* canvas, bool enable)
it->second->enable_gizmos(enable);
}
void GLCanvas3DManager::enable_toolbar(wxGLCanvas* canvas, bool enable)
{
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
it->second->enable_toolbar(enable);
}
void GLCanvas3DManager::enable_shader(wxGLCanvas* canvas, bool enable)
{
CanvasesMap::iterator it = _get_canvas(canvas);
@ -432,6 +439,19 @@ void GLCanvas3DManager::allow_multisample(wxGLCanvas* canvas, bool allow)
it->second->allow_multisample(allow);
}
void GLCanvas3DManager::enable_toolbar_item(wxGLCanvas* canvas, const std::string& name, bool enable)
{
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
it->second->enable_toolbar_item(name, enable);
}
bool GLCanvas3DManager::is_toolbar_item_pressed(wxGLCanvas* canvas, const std::string& name) const
{
CanvasesMap::const_iterator it = _get_canvas(canvas);
return (it != m_canvases.end()) ? it->second->is_toolbar_item_pressed(name) : false;
}
void GLCanvas3DManager::zoom_to_bed(wxGLCanvas* canvas)
{
CanvasesMap::iterator it = _get_canvas(canvas);
@ -675,6 +695,76 @@ void GLCanvas3DManager::register_on_update_geometry_info_callback(wxGLCanvas* ca
it->second->register_on_update_geometry_info_callback(callback);
}
void GLCanvas3DManager::register_action_add_callback(wxGLCanvas* canvas, void* callback)
{
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
it->second->register_action_add_callback(callback);
}
void GLCanvas3DManager::register_action_delete_callback(wxGLCanvas* canvas, void* callback)
{
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
it->second->register_action_delete_callback(callback);
}
void GLCanvas3DManager::register_action_deleteall_callback(wxGLCanvas* canvas, void* callback)
{
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
it->second->register_action_deleteall_callback(callback);
}
void GLCanvas3DManager::register_action_arrange_callback(wxGLCanvas* canvas, void* callback)
{
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
it->second->register_action_arrange_callback(callback);
}
void GLCanvas3DManager::register_action_more_callback(wxGLCanvas* canvas, void* callback)
{
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
it->second->register_action_more_callback(callback);
}
void GLCanvas3DManager::register_action_fewer_callback(wxGLCanvas* canvas, void* callback)
{
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
it->second->register_action_fewer_callback(callback);
}
void GLCanvas3DManager::register_action_split_callback(wxGLCanvas* canvas, void* callback)
{
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
it->second->register_action_split_callback(callback);
}
void GLCanvas3DManager::register_action_cut_callback(wxGLCanvas* canvas, void* callback)
{
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
it->second->register_action_cut_callback(callback);
}
void GLCanvas3DManager::register_action_settings_callback(wxGLCanvas* canvas, void* callback)
{
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
it->second->register_action_settings_callback(callback);
}
void GLCanvas3DManager::register_action_layersediting_callback(wxGLCanvas* canvas, void* callback)
{
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
it->second->register_action_layersediting_callback(callback);
}
GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas)
{
return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas);

View file

@ -110,11 +110,15 @@ public:
void enable_picking(wxGLCanvas* canvas, bool enable);
void enable_moving(wxGLCanvas* canvas, bool enable);
void enable_gizmos(wxGLCanvas* canvas, bool enable);
void enable_toolbar(wxGLCanvas* canvas, bool enable);
void enable_shader(wxGLCanvas* canvas, bool enable);
void enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable);
void enable_dynamic_background(wxGLCanvas* canvas, bool enable);
void allow_multisample(wxGLCanvas* canvas, bool allow);
void enable_toolbar_item(wxGLCanvas* canvas, const std::string& name, bool enable);
bool is_toolbar_item_pressed(wxGLCanvas* canvas, const std::string& name) const;
void zoom_to_bed(wxGLCanvas* canvas);
void zoom_to_volumes(wxGLCanvas* canvas);
void select_view(wxGLCanvas* canvas, const std::string& direction);
@ -157,6 +161,17 @@ public:
void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback);
void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback);
void register_action_add_callback(wxGLCanvas* canvas, void* callback);
void register_action_delete_callback(wxGLCanvas* canvas, void* callback);
void register_action_deleteall_callback(wxGLCanvas* canvas, void* callback);
void register_action_arrange_callback(wxGLCanvas* canvas, void* callback);
void register_action_more_callback(wxGLCanvas* canvas, void* callback);
void register_action_fewer_callback(wxGLCanvas* canvas, void* callback);
void register_action_split_callback(wxGLCanvas* canvas, void* callback);
void register_action_cut_callback(wxGLCanvas* canvas, void* callback);
void register_action_settings_callback(wxGLCanvas* canvas, void* callback);
void register_action_layersediting_callback(wxGLCanvas* canvas, void* callback);
private:
CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas);
CanvasesMap::const_iterator _get_canvas(wxGLCanvas* canvas) const;

File diff suppressed because it is too large Load diff

View file

@ -3,33 +3,54 @@
#include "../../slic3r/GUI/GLTexture.hpp"
#include "../../libslic3r/Point.hpp"
#include "../../libslic3r/BoundingBox.hpp"
#include <vector>
#define ENABLE_GIZMOS_3D 1
namespace Slic3r {
class BoundingBoxf3;
class Pointf3;
class Linef3;
namespace GUI {
class GLCanvas3D;
class GLGizmoBase
{
protected:
static const float BaseColor[3];
static const float HighlightColor[3];
struct Grabber
{
static const float HalfSize;
static const float HoverOffset;
static const float DraggingScaleFactor;
Pointf center;
Vec3d center;
float angle_x;
float angle_y;
float angle_z;
float color[3];
bool dragging;
Grabber();
void render(bool hover) const;
#if ENABLE_GIZMOS_3D
void render_for_picking() const { render(color, false); }
#else
void render_for_picking() const { render(color); }
#endif // ENABLE_GIZMOS_3D
private:
#if ENABLE_GIZMOS_3D
void render(const float* render_color, bool use_lighting) const;
#else
void render(const float* render_color) const;
#endif // ENABLE_GIZMOS_3D
#if ENABLE_GIZMOS_3D
void render_face(float half_size) const;
#endif // ENABLE_GIZMOS_3D
};
public:
@ -42,46 +63,64 @@ public:
};
protected:
GLCanvas3D& m_parent;
int m_group_id;
EState m_state;
// textures are assumed to be square and all with the same size in pixels, no internal check is done
GLTexture m_textures[Num_States];
int m_hover_id;
float m_base_color[3];
float m_drag_color[3];
float m_highlight_color[3];
mutable std::vector<Grabber> m_grabbers;
bool m_is_container;
public:
GLGizmoBase();
virtual ~GLGizmoBase();
explicit GLGizmoBase(GLCanvas3D& parent);
virtual ~GLGizmoBase() {}
bool init();
bool init() { return on_init(); }
EState get_state() const;
void set_state(EState state);
int get_group_id() const { return m_group_id; }
void set_group_id(int id) { m_group_id = id; }
unsigned int get_texture_id() const;
int get_textures_size() const;
EState get_state() const { return m_state; }
void set_state(EState state) { m_state = state; on_set_state(); }
int get_hover_id() const;
unsigned int get_texture_id() const { return m_textures[m_state].get_id(); }
int get_textures_size() const { return m_textures[Off].get_width(); }
int get_hover_id() const { return m_hover_id; }
void set_hover_id(int id);
void set_highlight_color(const float* color);
void start_dragging();
void stop_dragging();
void update(const Pointf& mouse_pos);
void refresh();
void update(const Linef3& mouse_ray);
void refresh() { on_refresh(); }
void render(const BoundingBoxf3& box) const;
void render_for_picking(const BoundingBoxf3& box) const;
void render(const BoundingBoxf3& box) const { on_render(box); }
void render_for_picking(const BoundingBoxf3& box) const { on_render_for_picking(box); }
protected:
virtual bool on_init() = 0;
virtual void on_set_state();
virtual void on_start_dragging();
virtual void on_stop_dragging();
virtual void on_update(const Pointf& mouse_pos) = 0;
virtual void on_refresh();
virtual void on_set_state() {}
virtual void on_set_hover_id() {}
virtual void on_start_dragging() {}
virtual void on_stop_dragging() {}
virtual void on_update(const Linef3& mouse_ray) = 0;
virtual void on_refresh() {}
virtual void on_render(const BoundingBoxf3& box) const = 0;
virtual void on_render_for_picking(const BoundingBoxf3& box) const = 0;
float picking_color_component(unsigned int id) const;
void render_grabbers() const;
void render_grabbers_for_picking() const;
void set_tooltip(const std::string& tooltip) const;
std::string format(float value, unsigned int decimals) const;
};
class GLGizmoRotate : public GLGizmoBase
@ -97,33 +136,102 @@ class GLGizmoRotate : public GLGizmoBase
static const unsigned int SnapRegionsCount;
static const float GrabberOffset;
float m_angle_z;
public:
enum Axis : unsigned char
{
X,
Y,
Z
};
mutable Pointf m_center;
private:
Axis m_axis;
float m_angle;
mutable Vec3d m_center;
mutable float m_radius;
mutable bool m_keep_radius;
mutable bool m_keep_initial_values;
public:
GLGizmoRotate();
GLGizmoRotate(GLCanvas3D& parent, Axis axis);
float get_angle_z() const;
void set_angle_z(float angle_z);
float get_angle() const { return m_angle; }
void set_angle(float angle);
protected:
virtual bool on_init();
virtual void on_set_state();
virtual void on_update(const Pointf& mouse_pos);
virtual void on_refresh();
virtual void on_set_state() { m_keep_initial_values = (m_state == On) ? false : true; }
virtual void on_update(const Linef3& mouse_ray);
virtual void on_refresh() { m_keep_initial_values = false; }
virtual void on_render(const BoundingBoxf3& box) const;
virtual void on_render_for_picking(const BoundingBoxf3& box) const;
private:
void _render_circle() const;
void _render_scale() const;
void _render_snap_radii() const;
void _render_reference_radius() const;
void _render_angle_z() const;
void _render_grabber() const;
void render_circle() const;
void render_scale() const;
void render_snap_radii() const;
void render_reference_radius() const;
void render_angle() const;
void render_grabber() const;
void transform_to_local() const;
// returns the intersection of the mouse ray with the plane perpendicular to the gizmo axis, in local coordinate
Vec3d mouse_position_in_local_plane(const Linef3& mouse_ray) const;
};
class GLGizmoRotate3D : public GLGizmoBase
{
GLGizmoRotate m_x;
GLGizmoRotate m_y;
GLGizmoRotate m_z;
public:
explicit GLGizmoRotate3D(GLCanvas3D& parent);
float get_angle_x() const { return m_x.get_angle(); }
void set_angle_x(float angle) { m_x.set_angle(angle); }
float get_angle_y() const { return m_y.get_angle(); }
void set_angle_y(float angle) { m_y.set_angle(angle); }
float get_angle_z() const { return m_z.get_angle(); }
void set_angle_z(float angle) { m_z.set_angle(angle); }
protected:
virtual bool on_init();
virtual void on_set_state()
{
m_x.set_state(m_state);
m_y.set_state(m_state);
m_z.set_state(m_state);
}
virtual void on_set_hover_id()
{
m_x.set_hover_id(m_hover_id == 0 ? 0 : -1);
m_y.set_hover_id(m_hover_id == 1 ? 0 : -1);
m_z.set_hover_id(m_hover_id == 2 ? 0 : -1);
}
virtual void on_start_dragging();
virtual void on_stop_dragging();
virtual void on_update(const Linef3& mouse_ray)
{
m_x.update(mouse_ray);
m_y.update(mouse_ray);
m_z.update(mouse_ray);
}
virtual void on_refresh()
{
m_x.refresh();
m_y.refresh();
m_z.refresh();
}
virtual void on_render(const BoundingBoxf3& box) const;
virtual void on_render_for_picking(const BoundingBoxf3& box) const
{
m_x.render_for_picking(box);
m_y.render_for_picking(box);
m_z.render_for_picking(box);
}
};
class GLGizmoScale : public GLGizmoBase
@ -133,22 +241,77 @@ class GLGizmoScale : public GLGizmoBase
float m_scale;
float m_starting_scale;
Pointf m_starting_drag_position;
Vec2d m_starting_drag_position;
public:
GLGizmoScale();
explicit GLGizmoScale(GLCanvas3D& parent);
float get_scale() const;
void set_scale(float scale);
float get_scale() const { return m_scale; }
void set_scale(float scale) { m_starting_scale = scale; }
protected:
virtual bool on_init();
virtual void on_start_dragging();
virtual void on_update(const Pointf& mouse_pos);
virtual void on_update(const Linef3& mouse_ray);
virtual void on_render(const BoundingBoxf3& box) const;
virtual void on_render_for_picking(const BoundingBoxf3& box) const;
};
class GLGizmoScale3D : public GLGizmoBase
{
static const float Offset;
mutable BoundingBoxf3 m_box;
float m_scale_x;
float m_scale_y;
float m_scale_z;
float m_starting_scale_x;
float m_starting_scale_y;
float m_starting_scale_z;
Vec3d m_starting_drag_position;
Vec3d m_starting_center;
public:
explicit GLGizmoScale3D(GLCanvas3D& parent);
float get_scale_x() const { return m_scale_x; }
void set_scale_x(float scale) { m_starting_scale_x = scale; }
float get_scale_y() const { return m_scale_y; }
void set_scale_y(float scale) { m_starting_scale_y = scale; }
float get_scale_z() const { return m_scale_z; }
void set_scale_z(float scale) { m_starting_scale_z = scale; }
void set_scale(float scale)
{
m_starting_scale_x = scale;
m_starting_scale_y = scale;
m_starting_scale_z = scale;
}
protected:
virtual bool on_init();
virtual void on_start_dragging();
virtual void on_update(const Linef3& mouse_ray);
virtual void on_render(const BoundingBoxf3& box) const;
virtual void on_render_for_picking(const BoundingBoxf3& box) const;
private:
void render_box() const;
void render_grabbers_connection(unsigned int id_1, unsigned int id_2) const;
void do_scale_x(const Linef3& mouse_ray);
void do_scale_y(const Linef3& mouse_ray);
void do_scale_z(const Linef3& mouse_ray);
void do_scale_uniform(const Linef3& mouse_ray);
double calc_ratio(unsigned int preferred_plane_id, const Linef3& mouse_ray, const Vec3d& center) const;
};
} // namespace GUI
} // namespace Slic3r

View file

@ -12,6 +12,8 @@
namespace Slic3r {
namespace GUI {
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)
@ -128,6 +130,11 @@ const std::string& GLTexture::get_source() const
}
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)
{
::glEnable(GL_BLEND);
::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@ -138,10 +145,10 @@ void GLTexture::render_texture(unsigned int tex_id, float left, float right, flo
::glBindTexture(GL_TEXTURE_2D, (GLuint)tex_id);
::glBegin(GL_QUADS);
::glTexCoord2f(0.0f, 1.0f); ::glVertex2f(left, bottom);
::glTexCoord2f(1.0f, 1.0f); ::glVertex2f(right, bottom);
::glTexCoord2f(1.0f, 0.0f); ::glVertex2f(right, top);
::glTexCoord2f(0.0f, 0.0f); ::glVertex2f(left, top);
::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);
::glEnd();
::glBindTexture(GL_TEXTURE_2D, 0);

View file

@ -10,6 +10,23 @@ namespace GUI {
class GLTexture
{
public:
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;
@ -30,6 +47,7 @@ namespace GUI {
const std::string& get_source() const;
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);
protected:
unsigned int _generate_mipmaps(wxImage& image);

View file

@ -0,0 +1,722 @@
#include "../../libslic3r/Point.hpp"
#include "GLToolbar.hpp"
#include "../../libslic3r/libslic3r.h"
#include "../../slic3r/GUI/GLCanvas3D.hpp"
#include <GL/glew.h>
#include <wx/bitmap.h>
#include <wx/dcmemory.h>
#include <wx/settings.h>
namespace Slic3r {
namespace GUI {
GLToolbarItem::Data::Data()
: name("")
, tooltip("")
, sprite_id(-1)
, is_toggable(false)
, action_callback(nullptr)
{
}
GLToolbarItem::GLToolbarItem(GLToolbarItem::EType type, const GLToolbarItem::Data& data)
: m_type(type)
, m_state(Disabled)
, m_data(data)
{
}
GLToolbarItem::EState GLToolbarItem::get_state() const
{
return m_state;
}
void GLToolbarItem::set_state(GLToolbarItem::EState state)
{
m_state = state;
}
const std::string& GLToolbarItem::get_name() const
{
return m_data.name;
}
const std::string& GLToolbarItem::get_tooltip() const
{
return m_data.tooltip;
}
void GLToolbarItem::do_action()
{
if (m_data.action_callback != nullptr)
m_data.action_callback->call();
}
bool GLToolbarItem::is_enabled() const
{
return m_state != Disabled;
}
bool GLToolbarItem::is_hovered() const
{
return (m_state == Hover) || (m_state == HoverPressed);
}
bool GLToolbarItem::is_pressed() const
{
return (m_state == Pressed) || (m_state == HoverPressed);
}
bool GLToolbarItem::is_toggable() const
{
return m_data.is_toggable;
}
bool GLToolbarItem::is_separator() const
{
return m_type == Separator;
}
void GLToolbarItem::render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int texture_size, unsigned int border_size, unsigned int icon_size, unsigned int gap_size) const
{
GLTexture::render_sub_texture(tex_id, left, right, bottom, top, get_uvs(texture_size, border_size, icon_size, gap_size));
}
GLTexture::Quad_UVs GLToolbarItem::get_uvs(unsigned int texture_size, unsigned int border_size, unsigned int icon_size, unsigned int gap_size) const
{
GLTexture::Quad_UVs uvs;
float inv_texture_size = (texture_size != 0) ? 1.0f / (float)texture_size : 0.0f;
float scaled_icon_size = (float)icon_size * inv_texture_size;
float scaled_border_size = (float)border_size * inv_texture_size;
float scaled_gap_size = (float)gap_size * inv_texture_size;
float stride = scaled_icon_size + scaled_gap_size;
float left = scaled_border_size + (float)m_state * stride;
float right = left + scaled_icon_size;
float top = scaled_border_size + (float)m_data.sprite_id * stride;
float bottom = top + scaled_icon_size;
uvs.left_top = { left, top };
uvs.left_bottom = { left, bottom };
uvs.right_bottom = { right, bottom };
uvs.right_top = { right, top };
return uvs;
}
GLToolbar::ItemsIconsTexture::ItemsIconsTexture()
: items_icon_size(0)
, items_icon_border_size(0)
, items_icon_gap_size(0)
{
}
GLToolbar::Layout::Layout()
: type(Horizontal)
, top(0.0f)
, left(0.0f)
, separator_size(0.0f)
, gap_size(0.0f)
{
}
GLToolbar::GLToolbar(GLCanvas3D& parent)
: m_parent(parent)
, m_enabled(false)
{
}
bool GLToolbar::init(const std::string& icons_texture_filename, unsigned int items_icon_size, unsigned int items_icon_border_size, unsigned int items_icon_gap_size)
{
std::string path = resources_dir() + "/icons/";
bool res = !icons_texture_filename.empty() && m_icons_texture.texture.load_from_file(path + icons_texture_filename, false);
if (res)
{
m_icons_texture.items_icon_size = items_icon_size;
m_icons_texture.items_icon_border_size = items_icon_border_size;
m_icons_texture.items_icon_gap_size = items_icon_gap_size;
}
return res;
}
GLToolbar::Layout::Type GLToolbar::get_layout_type() const
{
return m_layout.type;
}
void GLToolbar::set_layout_type(GLToolbar::Layout::Type type)
{
m_layout.type = type;
}
void GLToolbar::set_position(float top, float left)
{
m_layout.top = top;
m_layout.left = left;
}
void GLToolbar::set_separator_size(float size)
{
m_layout.separator_size = size;
}
void GLToolbar::set_gap_size(float size)
{
m_layout.gap_size = size;
}
bool GLToolbar::is_enabled() const
{
return m_enabled;
}
void GLToolbar::set_enabled(bool enable)
{
m_enabled = true;
}
bool GLToolbar::add_item(const GLToolbarItem::Data& data)
{
GLToolbarItem* item = new GLToolbarItem(GLToolbarItem::Action, data);
if (item == nullptr)
return false;
m_items.push_back(item);
return true;
}
bool GLToolbar::add_separator()
{
GLToolbarItem::Data data;
GLToolbarItem* item = new GLToolbarItem(GLToolbarItem::Separator, data);
if (item == nullptr)
return false;
m_items.push_back(item);
return true;
}
float GLToolbar::get_width() const
{
switch (m_layout.type)
{
default:
case Layout::Horizontal:
{
return get_width_horizontal();
}
case Layout::Vertical:
{
return get_width_vertical();
}
}
}
float GLToolbar::get_height() const
{
switch (m_layout.type)
{
default:
case Layout::Horizontal:
{
return get_height_horizontal();
}
case Layout::Vertical:
{
return get_height_vertical();
}
}
}
void GLToolbar::enable_item(const std::string& name)
{
for (GLToolbarItem* item : m_items)
{
if ((item->get_name() == name) && (item->get_state() == GLToolbarItem::Disabled))
{
item->set_state(GLToolbarItem::Normal);
return;
}
}
}
void GLToolbar::disable_item(const std::string& name)
{
for (GLToolbarItem* item : m_items)
{
if (item->get_name() == name)
{
item->set_state(GLToolbarItem::Disabled);
return;
}
}
}
bool GLToolbar::is_item_pressed(const std::string& name) const
{
for (GLToolbarItem* item : m_items)
{
if (item->get_name() == name)
return item->is_pressed();
}
return false;
}
void GLToolbar::update_hover_state(const Vec2d& mouse_pos)
{
if (!m_enabled)
return;
switch (m_layout.type)
{
default:
case Layout::Horizontal:
{
update_hover_state_horizontal(mouse_pos);
break;
}
case Layout::Vertical:
{
update_hover_state_vertical(mouse_pos);
break;
}
}
}
int GLToolbar::contains_mouse(const Vec2d& mouse_pos) const
{
if (!m_enabled)
return -1;
switch (m_layout.type)
{
default:
case Layout::Horizontal:
{
return contains_mouse_horizontal(mouse_pos);
}
case Layout::Vertical:
{
return contains_mouse_vertical(mouse_pos);
}
}
}
void GLToolbar::do_action(unsigned int item_id)
{
if (item_id < (unsigned int)m_items.size())
{
GLToolbarItem* item = m_items[item_id];
if ((item != nullptr) && !item->is_separator() && item->is_hovered())
{
if (item->is_toggable())
{
GLToolbarItem::EState state = item->get_state();
if (state == GLToolbarItem::Hover)
item->set_state(GLToolbarItem::HoverPressed);
else if (state == GLToolbarItem::HoverPressed)
item->set_state(GLToolbarItem::Hover);
m_parent.render();
item->do_action();
}
else
{
item->set_state(GLToolbarItem::HoverPressed);
m_parent.render();
item->do_action();
if (item->get_state() != GLToolbarItem::Disabled)
{
// the item may get disabled during the action, if not, set it back to hover state
item->set_state(GLToolbarItem::Hover);
m_parent.render();
}
}
}
}
}
void GLToolbar::render() const
{
if (!m_enabled || m_items.empty())
return;
::glDisable(GL_DEPTH_TEST);
::glPushMatrix();
::glLoadIdentity();
switch (m_layout.type)
{
default:
case Layout::Horizontal:
{
render_horizontal();
break;
}
case Layout::Vertical:
{
render_vertical();
break;
}
}
::glPopMatrix();
}
float GLToolbar::get_width_horizontal() const
{
return get_main_size();
}
float GLToolbar::get_width_vertical() const
{
return m_icons_texture.items_icon_size;
}
float GLToolbar::get_height_horizontal() const
{
return m_icons_texture.items_icon_size;
}
float GLToolbar::get_height_vertical() const
{
return get_main_size();
}
float GLToolbar::get_main_size() const
{
float size = 0.0f;
for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i)
{
if (m_items[i]->is_separator())
size += m_layout.separator_size;
else
size += (float)m_icons_texture.items_icon_size;
}
if (m_items.size() > 1)
size += ((float)m_items.size() - 1.0f) * m_layout.gap_size;
return size;
}
void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos)
{
float zoom = m_parent.get_camera_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
Size cnv_size = m_parent.get_canvas_size();
Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom;
float scaled_separator_size = m_layout.separator_size * inv_zoom;
float scaled_gap_size = m_layout.gap_size * inv_zoom;
float separator_stride = scaled_separator_size + scaled_gap_size;
float icon_stride = scaled_icons_size + scaled_gap_size;
float left = m_layout.left;
float top = m_layout.top;
std::string tooltip = "";
for (GLToolbarItem* item : m_items)
{
if (item->is_separator())
left += separator_stride;
else
{
float right = left + scaled_icons_size;
float bottom = top - scaled_icons_size;
GLToolbarItem::EState state = item->get_state();
bool inside = (left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top);
switch (state)
{
case GLToolbarItem::Normal:
{
if (inside)
item->set_state(GLToolbarItem::Hover);
break;
}
case GLToolbarItem::Hover:
{
if (inside)
tooltip = item->get_tooltip();
else
item->set_state(GLToolbarItem::Normal);
break;
}
case GLToolbarItem::Pressed:
{
if (inside)
item->set_state(GLToolbarItem::HoverPressed);
break;
}
case GLToolbarItem::HoverPressed:
{
if (inside)
tooltip = item->get_tooltip();
else
item->set_state(GLToolbarItem::Pressed);
break;
}
default:
case GLToolbarItem::Disabled:
{
break;
}
}
left += icon_stride;
}
}
m_parent.set_tooltip(tooltip);
}
void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos)
{
float zoom = m_parent.get_camera_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
Size cnv_size = m_parent.get_canvas_size();
Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom;
float scaled_separator_size = m_layout.separator_size * inv_zoom;
float scaled_gap_size = m_layout.gap_size * inv_zoom;
float separator_stride = scaled_separator_size + scaled_gap_size;
float icon_stride = scaled_icons_size + scaled_gap_size;
float left = m_layout.left;
float top = m_layout.top;
std::string tooltip = "";
for (GLToolbarItem* item : m_items)
{
if (item->is_separator())
top -= separator_stride;
else
{
float right = left + scaled_icons_size;
float bottom = top - scaled_icons_size;
GLToolbarItem::EState state = item->get_state();
bool inside = (left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top);
switch (state)
{
case GLToolbarItem::Normal:
{
if (inside)
item->set_state(GLToolbarItem::Hover);
break;
}
case GLToolbarItem::Hover:
{
if (inside)
tooltip = item->get_tooltip();
else
item->set_state(GLToolbarItem::Normal);
break;
}
case GLToolbarItem::Pressed:
{
if (inside)
item->set_state(GLToolbarItem::HoverPressed);
break;
}
case GLToolbarItem::HoverPressed:
{
if (inside)
tooltip = item->get_tooltip();
else
item->set_state(GLToolbarItem::Pressed);
break;
}
default:
case GLToolbarItem::Disabled:
{
break;
}
}
top -= icon_stride;
}
}
m_parent.set_tooltip(tooltip);
}
int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos) const
{
float zoom = m_parent.get_camera_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
Size cnv_size = m_parent.get_canvas_size();
Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom;
float scaled_separator_size = m_layout.separator_size * inv_zoom;
float scaled_gap_size = m_layout.gap_size * inv_zoom;
float separator_stride = scaled_separator_size + scaled_gap_size;
float icon_stride = scaled_icons_size + scaled_gap_size;
float left = m_layout.left;
float top = m_layout.top;
int id = -1;
for (GLToolbarItem* item : m_items)
{
++id;
if (item->is_separator())
left += separator_stride;
else
{
float right = left + scaled_icons_size;
float bottom = top - scaled_icons_size;
if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top))
return id;
left += icon_stride;
}
}
return -1;
}
int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos) const
{
float zoom = m_parent.get_camera_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
Size cnv_size = m_parent.get_canvas_size();
Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom;
float scaled_separator_size = m_layout.separator_size * inv_zoom;
float scaled_gap_size = m_layout.gap_size * inv_zoom;
float separator_stride = scaled_separator_size + scaled_gap_size;
float icon_stride = scaled_icons_size + scaled_gap_size;
float left = m_layout.left;
float top = m_layout.top;
int id = -1;
for (GLToolbarItem* item : m_items)
{
++id;
if (item->is_separator())
top -= separator_stride;
else
{
float right = left + scaled_icons_size;
float bottom = top - scaled_icons_size;
if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top))
return id;
top -= icon_stride;
}
}
return -1;
}
void GLToolbar::render_horizontal() const
{
unsigned int tex_id = m_icons_texture.texture.get_id();
int tex_size = m_icons_texture.texture.get_width();
if ((tex_id == 0) || (tex_size <= 0))
return;
float zoom = m_parent.get_camera_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom;
float scaled_separator_size = m_layout.separator_size * inv_zoom;
float scaled_gap_size = m_layout.gap_size * inv_zoom;
float separator_stride = scaled_separator_size + scaled_gap_size;
float icon_stride = scaled_icons_size + scaled_gap_size;
float left = m_layout.left;
float top = m_layout.top;
// renders icons
for (const GLToolbarItem* item : m_items)
{
if (item->is_separator())
left += separator_stride;
else
{
item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, m_icons_texture.items_icon_border_size, m_icons_texture.items_icon_size, m_icons_texture.items_icon_gap_size);
left += icon_stride;
}
}
}
void GLToolbar::render_vertical() const
{
unsigned int tex_id = m_icons_texture.texture.get_id();
int tex_size = m_icons_texture.texture.get_width();
if ((tex_id == 0) || (tex_size <= 0))
return;
float zoom = m_parent.get_camera_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom;
float scaled_separator_size = m_layout.separator_size * inv_zoom;
float scaled_gap_size = m_layout.gap_size * inv_zoom;
float separator_stride = scaled_separator_size + scaled_gap_size;
float icon_stride = scaled_icons_size + scaled_gap_size;
float left = m_layout.left;
float top = m_layout.top;
// renders icons
for (const GLToolbarItem* item : m_items)
{
if (item->is_separator())
top -= separator_stride;
else
{
item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, m_icons_texture.items_icon_border_size, m_icons_texture.items_icon_size, m_icons_texture.items_icon_gap_size);
top -= icon_stride;
}
}
}
} // namespace GUI
} // namespace Slic3r

View file

@ -0,0 +1,175 @@
#ifndef slic3r_GLToolbar_hpp_
#define slic3r_GLToolbar_hpp_
#include "../../slic3r/GUI/GLTexture.hpp"
#include "../../libslic3r/Utils.hpp"
#include <string>
#include <vector>
namespace Slic3r {
namespace GUI {
class GLCanvas3D;
class GLToolbarItem
{
public:
enum EType : unsigned char
{
Action,
Separator,
Num_Types
};
enum EState : unsigned char
{
Normal,
Pressed,
Disabled,
Hover,
HoverPressed,
Num_States
};
struct Data
{
std::string name;
std::string tooltip;
unsigned int sprite_id;
bool is_toggable;
PerlCallback* action_callback;
Data();
};
private:
EType m_type;
EState m_state;
Data m_data;
public:
GLToolbarItem(EType type, const Data& data);
EState get_state() const;
void set_state(EState state);
const std::string& get_name() const;
const std::string& get_tooltip() const;
void do_action();
bool is_enabled() const;
bool is_hovered() const;
bool is_pressed() const;
bool is_toggable() const;
bool is_separator() const;
void render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int texture_size, unsigned int border_size, unsigned int icon_size, unsigned int gap_size) const;
private:
GLTexture::Quad_UVs get_uvs(unsigned int texture_size, unsigned int border_size, unsigned int icon_size, unsigned int gap_size) const;
};
class GLToolbar
{
public:
// items icon textures are assumed to be square and all with the same size in pixels, no internal check is done
// icons are layed-out into the texture starting from the top-left corner in the same order as enum GLToolbarItem::EState
// from left to right
struct ItemsIconsTexture
{
GLTexture texture;
// size of the square icons, in pixels
unsigned int items_icon_size;
// distance from the border, in pixels
unsigned int items_icon_border_size;
// distance between two adjacent icons (to avoid filtering artifacts), in pixels
unsigned int items_icon_gap_size;
ItemsIconsTexture();
};
struct Layout
{
enum Type : unsigned char
{
Horizontal,
Vertical,
Num_Types
};
Type type;
float top;
float left;
float separator_size;
float gap_size;
Layout();
};
private:
typedef std::vector<GLToolbarItem*> ItemsList;
GLCanvas3D& m_parent;
bool m_enabled;
ItemsIconsTexture m_icons_texture;
Layout m_layout;
ItemsList m_items;
public:
explicit GLToolbar(GLCanvas3D& parent);
bool init(const std::string& icons_texture_filename, unsigned int items_icon_size, unsigned int items_icon_border_size, unsigned int items_icon_gap_size);
Layout::Type get_layout_type() const;
void set_layout_type(Layout::Type type);
void set_position(float top, float left);
void set_separator_size(float size);
void set_gap_size(float size);
bool is_enabled() const;
void set_enabled(bool enable);
bool add_item(const GLToolbarItem::Data& data);
bool add_separator();
float get_width() const;
float get_height() const;
void enable_item(const std::string& name);
void disable_item(const std::string& name);
bool is_item_pressed(const std::string& name) const;
void update_hover_state(const Vec2d& mouse_pos);
// returns the id of the item under the given mouse position or -1 if none
int contains_mouse(const Vec2d& mouse_pos) const;
void do_action(unsigned int item_id);
void render() const;
private:
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 update_hover_state_horizontal(const Vec2d& mouse_pos);
void update_hover_state_vertical(const Vec2d& mouse_pos);
int contains_mouse_horizontal(const Vec2d& mouse_pos) const;
int contains_mouse_vertical(const Vec2d& mouse_pos) const;
void render_horizontal() const;
void render_vertical() const;
};
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_GLToolbar_hpp_

View file

@ -38,6 +38,8 @@
#include <wx/msgdlg.h>
#include <wx/settings.h>
#include <wx/display.h>
#include <wx/collpane.h>
#include <wx/wupdlock.h>
#include "wxExtensions.hpp"
@ -53,12 +55,15 @@
#include "PresetBundle.hpp"
#include "UpdateDialogs.hpp"
#include "FirmwareDialog.hpp"
#include "GUI_ObjectParts.hpp"
#include "../Utils/PresetUpdater.hpp"
#include "../Config/Snapshot.hpp"
#include "3DScene.hpp"
#include "libslic3r/I18N.hpp"
#include "Model.hpp"
#include "LambdaObjectDialog.hpp"
namespace Slic3r { namespace GUI {
@ -123,10 +128,26 @@ wxLocale* g_wxLocale;
wxFont g_small_font;
wxFont g_bold_font;
std::shared_ptr<ConfigOptionsGroup> m_optgroup;
double m_brim_width = 0.0;
std::vector <std::shared_ptr<ConfigOptionsGroup>> m_optgroups;
double m_brim_width = 0.0;
size_t m_label_width = 100;
wxButton* g_wiping_dialog_button = nullptr;
//showed/hided controls according to the view mode
wxWindow *g_right_panel = nullptr;
wxBoxSizer *g_frequently_changed_parameters_sizer = nullptr;
wxBoxSizer *g_expert_mode_part_sizer = nullptr;
wxBoxSizer *g_scrolled_window_sizer = nullptr;
wxBoxSizer *g_object_list_sizer = nullptr;
wxButton *g_btn_export_gcode = nullptr;
wxButton *g_btn_export_stl = nullptr;
wxButton *g_btn_reslice = nullptr;
wxButton *g_btn_print = nullptr;
wxButton *g_btn_send_gcode = nullptr;
wxStaticBitmap *g_manifold_warning_icon = nullptr;
bool g_show_print_info = false;
bool g_show_manifold_warning_icon = false;
static void init_label_colours()
{
auto luma = get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
@ -206,6 +227,39 @@ void set_3DScene(_3DScene *scene)
g_3DScene = scene;
}
void set_objects_from_perl( wxWindow* parent, wxBoxSizer *frequently_changed_parameters_sizer,
wxBoxSizer *expert_mode_part_sizer, wxBoxSizer *scrolled_window_sizer,
wxButton *btn_export_gcode,
wxButton *btn_export_stl, wxButton *btn_reslice,
wxButton *btn_print, wxButton *btn_send_gcode,
wxStaticBitmap *manifold_warning_icon)
{
g_right_panel = parent;
g_frequently_changed_parameters_sizer = frequently_changed_parameters_sizer;
g_expert_mode_part_sizer = expert_mode_part_sizer;
g_scrolled_window_sizer = scrolled_window_sizer;
g_btn_export_gcode = btn_export_gcode;
g_btn_export_stl = btn_export_stl;
g_btn_reslice = btn_reslice;
g_btn_print = btn_print;
g_btn_send_gcode = btn_send_gcode;
g_manifold_warning_icon = manifold_warning_icon;
}
void set_show_print_info(bool show)
{
g_show_print_info = show;
}
void set_show_manifold_warning_icon(bool show)
{
g_show_manifold_warning_icon = show;
}
void set_objects_list_sizer(wxBoxSizer *objects_list_sizer){
g_object_list_sizer = objects_list_sizer;
}
std::vector<Tab *>& get_tabs_list()
{
return g_tabs_list;
@ -332,10 +386,21 @@ enum ConfigMenuIDs {
ConfigMenuTakeSnapshot,
ConfigMenuUpdate,
ConfigMenuPreferences,
ConfigMenuModeSimple,
ConfigMenuModeExpert,
ConfigMenuLanguage,
ConfigMenuFlashFirmware,
ConfigMenuCnt,
};
ConfigMenuIDs get_view_mode()
{
if (!g_AppConfig->has("view_mode"))
return ConfigMenuModeSimple;
const auto mode = g_AppConfig->get("view_mode");
return mode == "expert" ? ConfigMenuModeExpert : ConfigMenuModeSimple;
}
static wxString dots("", wxConvUTF8);
@ -353,8 +418,15 @@ void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_l
// local_menu->Append(config_id_base + ConfigMenuUpdate, _(L("Check for updates")), _(L("Check for configuration updates")));
local_menu->AppendSeparator();
local_menu->Append(config_id_base + ConfigMenuPreferences, _(L("Preferences"))+dots+"\tCtrl+,", _(L("Application preferences")));
local_menu->Append(config_id_base + ConfigMenuLanguage, _(L("Change Application Language")));
local_menu->AppendSeparator();
auto mode_menu = new wxMenu();
mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeSimple, _(L("&Simple")), _(L("Simple View Mode")));
mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeExpert, _(L("&Expert")), _(L("Expert View Mode")));
mode_menu->Check(config_id_base + get_view_mode(), true);
local_menu->AppendSubMenu(mode_menu, _(L("&Mode")), _(L("Slic3r View Mode")));
local_menu->AppendSeparator();
local_menu->Append(config_id_base + ConfigMenuLanguage, _(L("Change Application Language")));
local_menu->AppendSeparator();
local_menu->Append(config_id_base + ConfigMenuFlashFirmware, _(L("Flash printer firmware")), _(L("Upload a firmware image into an Arduino based printer")));
// TODO: for when we're able to flash dictionaries
// local_menu->Append(config_id_base + FirmwareMenuDict, _(L("Flash language file")), _(L("Upload a language dictionary file into a Prusa printer")));
@ -421,6 +493,13 @@ void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_l
break;
}
});
mode_menu->Bind(wxEVT_MENU, [config_id_base](wxEvent& event) {
std::string mode = event.GetId() - config_id_base == ConfigMenuModeExpert ?
"expert" : "simple";
g_AppConfig->set("view_mode", mode);
g_AppConfig->save();
update_mode();
});
menu->Append(local_menu, _(L("&Configuration")));
}
@ -429,6 +508,26 @@ void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_languag
add_config_menu(menu, event_preferences_changed, event_language_change);
}
void open_model(wxWindow *parent, wxArrayString& input_files){
t_file_wild_card vec_FILE_WILDCARDS = get_file_wild_card();
std::vector<std::string> file_types = { "known", "stl", "obj", "amf", "3mf", "prusa" };
wxString MODEL_WILDCARD;
for (auto file_type : file_types)
MODEL_WILDCARD += vec_FILE_WILDCARDS.at(file_type) + "|";
auto dlg_title = _(L("Choose one or more files (STL/OBJ/AMF/3MF/PRUSA):"));
auto dialog = new wxFileDialog(parent /*? parent : GetTopWindow(g_wxMainFrame)*/, dlg_title,
g_AppConfig->get_last_dir(), "",
MODEL_WILDCARD, wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST);
if (dialog->ShowModal() != wxID_OK) {
dialog->Destroy();
return ;
}
dialog->GetPaths(input_files);
dialog->Destroy();
}
// This is called when closing the application, when loading a config file or when starting the config wizard
// to notify the user whether he is aware that some preset changes will be lost.
bool check_unsaved_changes()
@ -496,20 +595,33 @@ void open_preferences_dialog(int event_preferences)
void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed)
{
update_label_colours_from_appconfig();
add_created_tab(new TabPrint (g_wxTabPanel, no_controller));
add_created_tab(new TabFilament (g_wxTabPanel, no_controller));
add_created_tab(new TabPrinter (g_wxTabPanel, no_controller));
for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++ i) {
Tab *tab = dynamic_cast<Tab*>(g_wxTabPanel->GetPage(i));
if (! tab)
continue;
tab->set_event_value_change(wxEventType(event_value_change));
tab->set_event_presets_changed(wxEventType(event_presets_changed));
}
add_created_tab(new TabPrint (g_wxTabPanel, no_controller), event_value_change, event_presets_changed);
add_created_tab(new TabFilament (g_wxTabPanel, no_controller), event_value_change, event_presets_changed);
add_created_tab(new TabSLAMaterial (g_wxTabPanel, no_controller), event_value_change, event_presets_changed);
add_created_tab(new TabPrinter (g_wxTabPanel, no_controller), event_value_change, event_presets_changed);
}
std::vector<PresetTab> preset_tabs = {
{ "print", nullptr, ptFFF },
{ "filament", nullptr, ptFFF },
{ "sla_material", nullptr, ptSLA }
};
const std::vector<PresetTab>& get_preset_tabs() {
return preset_tabs;
}
Tab* get_tab(const std::string& name)
{
std::vector<PresetTab>::iterator it = std::find_if(preset_tabs.begin(), preset_tabs.end(),
[name](PresetTab& tab){ return name == tab.name; });
return it != preset_tabs.end() ? it->panel : nullptr;
}
TabIface* get_preset_tab_iface(char *name)
{
Tab* tab = get_tab(name);
if (tab) return new TabIface(tab);
for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++ i) {
Tab *tab = dynamic_cast<Tab*>(g_wxTabPanel->GetPage(i));
if (! tab)
@ -604,14 +716,16 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
config.set_key_value(opt_key, new ConfigOptionEnum<SupportMaterialPattern>(boost::any_cast<SupportMaterialPattern>(value)));
else if (opt_key.compare("seam_position") == 0)
config.set_key_value(opt_key, new ConfigOptionEnum<SeamPosition>(boost::any_cast<SeamPosition>(value)));
else if (opt_key.compare("host_type") == 0)
config.set_key_value(opt_key, new ConfigOptionEnum<PrintHostType>(boost::any_cast<PrintHostType>(value)));
}
break;
case coPoints:{
if (opt_key.compare("bed_shape") == 0){
config.option<ConfigOptionPoints>(opt_key)->values = boost::any_cast<std::vector<Pointf>>(value);
config.option<ConfigOptionPoints>(opt_key)->values = boost::any_cast<std::vector<Vec2d>>(value);
break;
}
ConfigOptionPoints* vec_new = new ConfigOptionPoints{ boost::any_cast<Pointf>(value) };
ConfigOptionPoints* vec_new = new ConfigOptionPoints{ boost::any_cast<Vec2d>(value) };
config.option<ConfigOptionPoints>(opt_key)->set_at(vec_new, opt_index, 0);
}
break;
@ -627,13 +741,28 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
}
}
void add_created_tab(Tab* panel)
void add_created_tab(Tab* panel, int event_value_change, int event_presets_changed)
{
panel->create_preset_tab(g_PresetBundle);
// Load the currently selected preset into the GUI, update the preset selection box.
panel->load_current_preset();
g_wxTabPanel->AddPage(panel, panel->title());
panel->set_event_value_change(wxEventType(event_value_change));
panel->set_event_presets_changed(wxEventType(event_presets_changed));
const wxString& tab_name = panel->GetName();
bool add_panel = true;
auto it = std::find_if( preset_tabs.begin(), preset_tabs.end(),
[tab_name](PresetTab& tab){return tab.name == tab_name; });
if (it != preset_tabs.end()) {
it->panel = panel;
add_panel = it->technology == g_PresetBundle->printers.get_edited_preset().printer_technology();
}
if (add_panel)
g_wxTabPanel->AddPage(panel, panel->title());
}
void load_current_presets()
@ -723,6 +852,22 @@ unsigned get_colour_approx_luma(const wxColour &colour)
));
}
wxWindow* get_right_panel(){
return g_right_panel;
}
wxFrame* get_main_frame() {
return g_wxMainFrame;
}
wxNotebook * get_tab_panel() {
return g_wxTabPanel;
}
const size_t& label_width(){
return m_label_width;
}
void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string items, bool initial_value)
{
if (comboCtrl == nullptr)
@ -791,14 +936,37 @@ wxString from_u8(const std::string &str)
return wxString::FromUTF8(str.c_str());
}
void add_expert_mode_part( wxWindow* parent, wxBoxSizer* sizer,
Model &model,
int event_object_selection_changed,
int event_object_settings_changed,
int event_remove_object,
int event_update_scene)
{
set_event_object_selection_changed(event_object_selection_changed);
set_event_object_settings_changed(event_object_settings_changed);
set_event_remove_object(event_remove_object);
set_event_update_scene(event_update_scene);
set_objects_from_model(model);
init_mesh_icons();
// wxWindowUpdateLocker noUpdates(parent);
// add_objects_list(parent, sizer);
// add_collapsible_panes(parent, sizer);
}
void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer)
{
DynamicPrintConfig* config = &g_PresetBundle->prints.get_edited_preset().config;
m_optgroup = std::make_shared<ConfigOptionsGroup>(parent, "", config);
std::shared_ptr<ConfigOptionsGroup> optgroup = std::make_shared<ConfigOptionsGroup>(parent, "", config);
const wxArrayInt& ar = preset_sizer->GetColWidths();
m_optgroup->label_width = ar.IsEmpty() ? 100 : ar.front()-4; // doesn't work
m_optgroup->m_on_change = [config](t_config_option_key opt_key, boost::any value){
m_label_width = ar.IsEmpty() ? 100 : ar.front()-4;
optgroup->label_width = m_label_width;
//Frequently changed parameters
optgroup->m_on_change = [config](t_config_option_key opt_key, boost::any value){
TabPrint* tab_print = nullptr;
for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++i) {
Tab *tab = dynamic_cast<Tab*>(g_wxTabPanel->GetPage(i));
@ -813,7 +981,7 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
return;
if (opt_key == "fill_density"){
value = m_optgroup->get_config_value(*config, opt_key);
value = m_optgroups[ogFrequentlyChangingParameters]->get_config_value(*config, opt_key);
tab_print->set_value(opt_key, value);
tab_print->update();
}
@ -836,14 +1004,14 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
}
else{ //(opt_key == "support")
const wxString& selection = boost::any_cast<wxString>(value);
auto support_material = selection == _("None") ? false : true;
new_conf.set_key_value("support_material", new ConfigOptionBool(support_material));
if (selection == _("Everywhere"))
new_conf.set_key_value("support_material_buildplate_only", new ConfigOptionBool(false));
else if (selection == _("Support on build plate only"))
new_conf.set_key_value("support_material_buildplate_only", new ConfigOptionBool(true));
new_conf.set_key_value("support_material_buildplate_only", new ConfigOptionBool(true));
}
tab_print->load_config(new_conf);
}
@ -851,10 +1019,10 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
tab_print->update_dirty();
};
Option option = m_optgroup->get_option("fill_density");
Option option = optgroup->get_option("fill_density");
option.opt.sidetext = "";
option.opt.full_width = true;
m_optgroup->append_single_option_line(option);
optgroup->append_single_option_line(option);
ConfigOptionDef def;
@ -873,7 +1041,7 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
def.default_value = new ConfigOptionStrings { selection };
option = Option(def, "support");
option.opt.full_width = true;
m_optgroup->append_single_option_line(option);
optgroup->append_single_option_line(option);
m_brim_width = config->opt_float("brim_width");
def.label = L("Brim");
@ -882,7 +1050,7 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
def.gui_type = "";
def.default_value = new ConfigOptionBool{ m_brim_width > 0.0 ? true : false };
option = Option(def, "brim");
m_optgroup->append_single_option_line(option);
optgroup->append_single_option_line(option);
Line line = { "", "" };
@ -908,16 +1076,95 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
}));
return sizer;
};
m_optgroup->append_line(line);
optgroup->append_line(line);
sizer->Add(optgroup->sizer, 0, wxEXPAND | wxBOTTOM, 2);
m_optgroups.push_back(optgroup);// ogFrequentlyChangingParameters
sizer->Add(m_optgroup->sizer, 1, wxEXPAND | wxBOTTOM, 2);
// Object List
add_objects_list(parent, sizer);
// Frequently Object Settings
add_object_settings(parent, sizer);
}
ConfigOptionsGroup* get_optgroup()
void show_frequently_changed_parameters(bool show)
{
return m_optgroup.get();
g_frequently_changed_parameters_sizer->Show(show);
if (!show) return;
for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++i) {
Tab *tab = dynamic_cast<Tab*>(g_wxTabPanel->GetPage(i));
if (!tab)
continue;
tab->update_wiping_button_visibility();
break;
}
}
void show_buttons(bool show)
{
g_btn_export_stl->Show(show);
g_btn_reslice->Show(show);
for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++i) {
TabPrinter *tab = dynamic_cast<TabPrinter*>(g_wxTabPanel->GetPage(i));
if (!tab)
continue;
if (g_PresetBundle->printers.get_selected_preset().printer_technology() == ptFFF) {
g_btn_print->Show(show && !tab->m_config->opt_string("serial_port").empty());
g_btn_send_gcode->Show(show && !tab->m_config->opt_string("print_host").empty());
}
break;
}
}
void show_info_sizer(bool show)
{
g_scrolled_window_sizer->Show(static_cast<size_t>(0), show);
g_scrolled_window_sizer->Show(1, show && g_show_print_info);
g_manifold_warning_icon->Show(show && g_show_manifold_warning_icon);
}
void update_mode()
{
wxWindowUpdateLocker noUpdates(g_right_panel);
// TODO There is a not the best place of it!
//*** Update style of the "Export G-code" button****
if (g_btn_export_gcode->GetFont() != bold_font()){
g_btn_export_gcode->SetBackgroundColour(wxColour(252, 77, 1));
g_btn_export_gcode->SetFont(bold_font());
}
// ***********************************
ConfigMenuIDs mode = get_view_mode();
// show_frequently_changed_parameters(mode >= ConfigMenuModeRegular);
// g_expert_mode_part_sizer->Show(mode == ConfigMenuModeExpert);
g_object_list_sizer->Show(mode == ConfigMenuModeExpert);
show_info_sizer(mode == ConfigMenuModeExpert);
show_buttons(mode == ConfigMenuModeExpert);
// TODO There is a not the best place of it!
// *** Update showing of the collpane_settings
// show_collpane_settings(mode == ConfigMenuModeExpert);
// *************************
g_right_panel->GetParent()->Layout();
g_right_panel->Layout();
}
bool is_expert_mode(){
return get_view_mode() == ConfigMenuModeExpert;
}
ConfigOptionsGroup* get_optgroup(size_t i)
{
return m_optgroups[i].get();
}
std::vector <std::shared_ptr<ConfigOptionsGroup>>& get_optgroups() {
return m_optgroups;
}
wxButton* get_wiping_dialog_button()

View file

@ -3,8 +3,9 @@
#include <string>
#include <vector>
#include "Config.hpp"
#include "PrintConfig.hpp"
#include "../../libslic3r/Utils.hpp"
#include "GUI_ObjectParts.hpp"
#include <wx/intl.h>
#include <wx/string.h>
@ -12,7 +13,6 @@
class wxApp;
class wxWindow;
class wxFrame;
class wxFont;
class wxMenuBar;
class wxNotebook;
class wxComboCtrl;
@ -24,6 +24,8 @@ class wxBoxSizer;
class wxFlexGridSizer;
class wxButton;
class wxFileDialog;
class wxStaticBitmap;
class wxFont;
namespace Slic3r {
@ -67,19 +69,26 @@ typedef std::map<std::string, std::string> t_file_wild_card;
inline t_file_wild_card& get_file_wild_card() {
static t_file_wild_card FILE_WILDCARDS;
if (FILE_WILDCARDS.empty()){
FILE_WILDCARDS["known"] = "Known files (*.stl, *.obj, *.amf, *.xml, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.prusa;*.PRUSA";
FILE_WILDCARDS["stl"] = "STL files (*.stl)|*.stl;*.STL";
FILE_WILDCARDS["obj"] = "OBJ files (*.obj)|*.obj;*.OBJ";
FILE_WILDCARDS["amf"] = "AMF files (*.amf)|*.zip.amf;*.amf;*.AMF;*.xml;*.XML";
FILE_WILDCARDS["3mf"] = "3MF files (*.3mf)|*.3mf;*.3MF;";
FILE_WILDCARDS["prusa"] = "Prusa Control files (*.prusa)|*.prusa;*.PRUSA";
FILE_WILDCARDS["ini"] = "INI files *.ini|*.ini;*.INI";
FILE_WILDCARDS["known"] = "Known files (*.stl, *.obj, *.amf, *.xml, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.prusa;*.PRUSA";
FILE_WILDCARDS["stl"] = "STL files (*.stl)|*.stl;*.STL";
FILE_WILDCARDS["obj"] = "OBJ files (*.obj)|*.obj;*.OBJ";
FILE_WILDCARDS["amf"] = "AMF files (*.amf)|*.zip.amf;*.amf;*.AMF;*.xml;*.XML";
FILE_WILDCARDS["3mf"] = "3MF files (*.3mf)|*.3mf;*.3MF;";
FILE_WILDCARDS["prusa"] = "Prusa Control files (*.prusa)|*.prusa;*.PRUSA";
FILE_WILDCARDS["ini"] = "INI files *.ini|*.ini;*.INI";
FILE_WILDCARDS["gcode"] = "G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC";
FILE_WILDCARDS["svg"] = "SVG files *.svg|*.svg;*.SVG";
FILE_WILDCARDS["svg"] = "SVG files *.svg|*.svg;*.SVG";
}
return FILE_WILDCARDS;
}
struct PresetTab {
std::string name;
Tab* panel;
PrinterTechnology technology;
};
void disable_screensaver();
void enable_screensaver();
bool debugged();
@ -93,10 +102,26 @@ void set_app_config(AppConfig *app_config);
void set_preset_bundle(PresetBundle *preset_bundle);
void set_preset_updater(PresetUpdater *updater);
void set_3DScene(_3DScene *scene);
void set_objects_from_perl( wxWindow* parent,
wxBoxSizer *frequently_changed_parameters_sizer,
wxBoxSizer *expert_mode_part_sizer,
wxBoxSizer *scrolled_window_sizer,
wxButton *btn_export_gcode,
wxButton *btn_export_stl,
wxButton *btn_reslice,
wxButton *btn_print,
wxButton *btn_send_gcode,
wxStaticBitmap *manifold_warning_icon);
void set_show_print_info(bool show);
void set_show_manifold_warning_icon(bool show);
void set_objects_list_sizer(wxBoxSizer *objects_list_sizer);
AppConfig* get_app_config();
wxApp* get_app();
PresetBundle* get_preset_bundle();
AppConfig* get_app_config();
wxApp* get_app();
PresetBundle* get_preset_bundle();
wxFrame* get_main_frame();
wxNotebook * get_tab_panel();
wxNotebook* get_tab_panel();
const wxColour& get_label_clr_modified();
const wxColour& get_label_clr_sys();
@ -108,6 +133,14 @@ void set_label_clr_sys(const wxColour& clr);
const wxFont& small_font();
const wxFont& bold_font();
void open_model(wxWindow *parent, wxArrayString& input_files);
wxWindow* get_right_panel();
const size_t& label_width();
Tab* get_tab(const std::string& name);
const std::vector<PresetTab>& get_preset_tabs();
extern void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_language_change);
// This is called when closing the application, when loading a config file or when starting the config wizard
@ -130,7 +163,7 @@ void create_preset_tabs(bool no_controller, int event_value_change, int event_pr
TabIface* get_preset_tab_iface(char *name);
// add it at the end of the tab panel.
void add_created_tab(Tab* panel);
void add_created_tab(Tab* panel, int event_value_change, int event_presets_changed);
// 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);
@ -150,6 +183,8 @@ void save_language();
void get_installed_languages(wxArrayString & names, wxArrayLong & identifiers);
// select language from the list of installed languages
bool select_language(wxArrayString & names, wxArrayLong & identifiers);
// update right panel of the Plater according to view mode
void update_mode();
std::vector<Tab *>& get_tabs_list();
bool checked_tab(Tab* tab);
@ -169,13 +204,22 @@ wxString L_str(const std::string &str);
// Return wxString from std::string in UTF8
wxString from_u8(const std::string &str);
void add_expert_mode_part( wxWindow* parent, wxBoxSizer* sizer,
Model &model,
int event_object_selection_changed,
int event_object_settings_changed,
int event_remove_object,
int event_update_scene);
void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer);
// Update view mode according to selected menu
void update_mode();
bool is_expert_mode();
// Callback to trigger a configuration update timer on the Plater.
static PerlCallback g_on_request_update_callback;
ConfigOptionsGroup* get_optgroup();
ConfigOptionsGroup* get_optgroup(size_t i);
std::vector <std::shared_ptr<ConfigOptionsGroup>>& get_optgroups();
wxButton* get_wiping_dialog_button();
void add_export_option(wxFileDialog* dlg, const std::string& format);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,126 @@
#ifndef slic3r_GUI_ObjectParts_hpp_
#define slic3r_GUI_ObjectParts_hpp_
class wxWindow;
class wxSizer;
class wxBoxSizer;
class wxString;
class wxArrayString;
class wxMenu;
class wxDataViewEvent;
class wxKeyEvent;
namespace Slic3r {
class ModelObject;
class Model;
namespace GUI {
enum ogGroup{
ogFrequentlyChangingParameters,
ogFrequentlyObjectSettings,
ogCurrentSettings
// ogObjectSettings,
// ogObjectMovers,
// ogPartSettings
};
enum LambdaTypeIDs{
LambdaTypeBox,
LambdaTypeCylinder,
LambdaTypeSphere,
LambdaTypeSlab
};
struct OBJECT_PARAMETERS
{
LambdaTypeIDs type = LambdaTypeBox;
double dim[3];// = { 1.0, 1.0, 1.0 };
int cyl_r = 1;
int cyl_h = 1;
double sph_rho = 1.0;
double slab_h = 1.0;
double slab_z = 0.0;
};
void add_collapsible_panes(wxWindow* parent, wxBoxSizer* sizer);
void add_objects_list(wxWindow* parent, wxBoxSizer* sizer);
void add_object_settings(wxWindow* parent, wxBoxSizer* sizer);
void show_collpane_settings(bool expert_mode);
wxMenu *create_add_settings_popupmenu(bool is_part);
wxMenu *create_add_part_popupmenu();
wxMenu *create_part_settings_popupmenu();
// Add object to the list
//void add_object(const std::string &name);
void add_object_to_list(const std::string &name, ModelObject* model_object);
// Delete object from the list
void delete_object_from_list();
// Delete all objects from the list
void delete_all_objects_from_list();
// Set count of object on c++ side
void set_object_count(int idx, int count);
// Set object scale on c++ side
void set_object_scale(int idx, int scale);
// Unselect all objects in the list on c++ side
void unselect_objects();
// Select current object in the list on c++ side
void select_current_object(int idx);
// Remove objects/sub-object from the list
void remove();
void object_ctrl_selection_changed();
void object_ctrl_context_menu();
void object_ctrl_key_event(wxKeyEvent& event);
void object_ctrl_item_value_change(wxDataViewEvent& event);
void show_context_menu();
void init_mesh_icons();
void set_event_object_selection_changed(const int& event);
void set_event_object_settings_changed(const int& event);
void set_event_remove_object(const int& event);
void set_event_update_scene(const int& event);
void set_objects_from_model(Model &model);
bool is_parts_changed();
bool is_part_settings_changed();
void load_part( wxWindow* parent, ModelObject* model_object,
wxArrayString& part_names, const bool is_modifier);
void load_lambda(wxWindow* parent, ModelObject* model_object,
wxArrayString& part_names, const bool is_modifier);
void on_btn_load(wxWindow* parent, bool is_modifier = false, bool is_lambda = false);
void on_btn_del();
void on_btn_split(const bool split_part);
void on_btn_move_up();
void on_btn_move_down();
void parts_changed(int obj_idx);
void part_selection_changed();
void update_settings_value();
// show/hide "Extruder" column for Objects List
void set_extruder_column_hidden(bool hide);
// update extruder in current config
void update_extruder_in_config(const wxString& selection);
// update scale values after scale unit changing or "gizmos"
void update_scale_values();
void update_scale_values(const Vec3d& size, float scale);
// update rotation values object selection changing
void update_rotation_values();
// update rotation value after "gizmos"
void update_rotation_value(const double angle, const std::string& axis);
void on_begin_drag(wxDataViewEvent &event);
void on_drop_possible(wxDataViewEvent &event);
void on_drop(wxDataViewEvent &event);
// update extruder column for objects_ctrl according to extruders count
void update_objects_list_extruder_column(int extruders_count);
} //namespace GUI
} //namespace Slic3r
#endif //slic3r_GUI_ObjectParts_hpp_

View file

@ -0,0 +1,176 @@
#include "LambdaObjectDialog.hpp"
#include <wx/window.h>
#include <wx/button.h>
#include "OptionsGroup.hpp"
namespace Slic3r
{
namespace GUI
{
static wxString dots("", wxConvUTF8);
LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent)
{
Create(parent, wxID_ANY, _(L("Lambda Object")),
wxDefaultPosition, wxDefaultSize,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
// instead of double dim[3] = { 1.0, 1.0, 1.0 };
object_parameters.dim[0] = 1.0;
object_parameters.dim[1] = 1.0;
object_parameters.dim[2] = 1.0;
sizer = new wxBoxSizer(wxVERTICAL);
// modificator options
m_modificator_options_book = new wxChoicebook( this, wxID_ANY, wxDefaultPosition,
wxDefaultSize, wxCHB_TOP);
sizer->Add(m_modificator_options_book, 1, wxEXPAND| wxALL, 10);
auto optgroup = init_modificator_options_page(_(L("Box")));
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
int opt_id = opt_key == "l" ? 0 :
opt_key == "w" ? 1 :
opt_key == "h" ? 2 : -1;
if (opt_id < 0) return;
object_parameters.dim[opt_id] = boost::any_cast<double>(value);
};
ConfigOptionDef def;
def.width = 70;
def.type = coFloat;
def.default_value = new ConfigOptionFloat{ 1.0 };
def.label = L("L");
Option option(def, "l");
optgroup->append_single_option_line(option);
def.label = L("W");
option = Option(def, "w");
optgroup->append_single_option_line(option);
def.label = L("H");
option = Option(def, "h");
optgroup->append_single_option_line(option);
optgroup = init_modificator_options_page(_(L("Cylinder")));
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
int val = boost::any_cast<int>(value);
if (opt_key == "cyl_r")
object_parameters.cyl_r = val;
else if (opt_key == "cyl_h")
object_parameters.cyl_h = val;
else return;
};
def.type = coInt;
def.default_value = new ConfigOptionInt{ 1 };
def.label = L("Radius");
option = Option(def, "cyl_r");
optgroup->append_single_option_line(option);
def.label = L("Height");
option = Option(def, "cyl_h");
optgroup->append_single_option_line(option);
optgroup = init_modificator_options_page(_(L("Sphere")));
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
if (opt_key == "sph_rho")
object_parameters.sph_rho = boost::any_cast<double>(value);
else return;
};
def.type = coFloat;
def.default_value = new ConfigOptionFloat{ 1.0 };
def.label = L("Rho");
option = Option(def, "sph_rho");
optgroup->append_single_option_line(option);
optgroup = init_modificator_options_page(_(L("Slab")));
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
double val = boost::any_cast<double>(value);
if (opt_key == "slab_z")
object_parameters.slab_z = val;
else if (opt_key == "slab_h")
object_parameters.slab_h = val;
else return;
};
def.label = L("H");
option = Option(def, "slab_h");
optgroup->append_single_option_line(option);
def.label = L("Initial Z");
option = Option(def, "slab_z");
optgroup->append_single_option_line(option);
Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, ([this](wxCommandEvent e)
{
auto page_idx = m_modificator_options_book->GetSelection();
if (page_idx < 0) return;
switch (page_idx)
{
case 0:
object_parameters.type = LambdaTypeBox;
break;
case 1:
object_parameters.type = LambdaTypeCylinder;
break;
case 2:
object_parameters.type = LambdaTypeSphere;
break;
case 3:
object_parameters.type = LambdaTypeSlab;
break;
default:
break;
}
}));
auto button_sizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL);
wxButton* btn_OK = static_cast<wxButton*>(FindWindowById(wxID_OK, this));
btn_OK->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
// validate user input
if (!CanClose())return;
EndModal(wxID_OK);
Destroy();
});
wxButton* btn_CANCEL = static_cast<wxButton*>(FindWindowById(wxID_CANCEL, this));
btn_CANCEL->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
// validate user input
if (!CanClose())return;
EndModal(wxID_CANCEL);
Destroy();
});
sizer->Add(button_sizer, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10);
SetSizer(sizer);
sizer->Fit(this);
sizer->SetSizeHints(this);
}
// Called from the constructor.
// Create a panel for a rectangular / circular / custom bed shape.
ConfigOptionsGroupShp LambdaObjectDialog::init_modificator_options_page(wxString title){
auto panel = new wxPanel(m_modificator_options_book);
ConfigOptionsGroupShp optgroup;
optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Add")) + " " +title + " " +dots);
optgroup->label_width = 100;
m_optgroups.push_back(optgroup);
panel->SetSizerAndFit(optgroup->sizer);
m_modificator_options_book->AddPage(panel, title);
return optgroup;
}
} //namespace GUI
} //namespace Slic3r

View file

@ -0,0 +1,35 @@
#ifndef slic3r_LambdaObjectDialog_hpp_
#define slic3r_LambdaObjectDialog_hpp_
#include "GUI.hpp"
#include <wx/dialog.h>
#include <wx/sizer.h>
#include <wx/choicebk.h>
namespace Slic3r
{
namespace GUI
{
using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>;
class LambdaObjectDialog : public wxDialog
{
wxChoicebook* m_modificator_options_book;
std::vector <ConfigOptionsGroupShp> m_optgroups;
public:
LambdaObjectDialog(wxWindow* parent);
~LambdaObjectDialog(){}
bool CanClose() { return true; } // ???
OBJECT_PARAMETERS& ObjectParameters(){ return object_parameters; }
ConfigOptionsGroupShp init_modificator_options_page(wxString title);
// Note whether the window was already closed, so a pending update is not executed.
bool m_already_closed = false;
OBJECT_PARAMETERS object_parameters;
wxBoxSizer* sizer = nullptr;
};
} //namespace GUI
} //namespace Slic3r
#endif //slic3r_LambdaObjectDialog_hpp_

View file

@ -30,6 +30,7 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
opt.gui_type.compare("i_enum_closed") == 0) {
m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(parent(), opt, id)));
} else if (opt.gui_type.compare("slider") == 0) {
m_fields.emplace(id, STDMOVE(SliderCtrl::Create<SliderCtrl>(parent(), opt, id)));
} else if (opt.gui_type.compare("i_spin") == 0) { // Spinctrl
} else if (opt.gui_type.compare("legend") == 0) { // StaticText
m_fields.emplace(id, STDMOVE(StaticText::Create<StaticText>(parent(), opt, id)));
@ -88,17 +89,23 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
if (!this->m_disabled)
this->back_to_sys_value(opt_id);
};
if (!m_show_modified_btns) {
field->m_Undo_btn->Hide();
field->m_Undo_to_sys_btn->Hide();
}
// if (nonsys_btn_icon != nullptr)
// field->set_nonsys_btn_icon(*nonsys_btn_icon);
// assign function objects for callbacks, etc.
return field;
}
void OptionsGroup::add_undo_buttuns_to_sizer(wxSizer* sizer, const t_field& field)
{
if (!m_show_modified_btns) {
field->m_Undo_btn->Hide();
field->m_Undo_to_sys_btn->Hide();
return;
}
sizer->Add(field->m_Undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL);
sizer->Add(field->m_Undo_btn, 0, wxALIGN_CENTER_VERTICAL);
}
void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* = nullptr*/) {
//! if (line.sizer != nullptr || (line.widget != nullptr && line.full_width > 0)){
if ( (line.sizer != nullptr || line.widget != nullptr) && line.full_width){
@ -133,8 +140,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/*
const auto& field = build_field(option);
auto btn_sizer = new wxBoxSizer(wxHORIZONTAL);
btn_sizer->Add(field->m_Undo_to_sys_btn);
btn_sizer->Add(field->m_Undo_btn);
add_undo_buttuns_to_sizer(btn_sizer, field);
tmp_sizer->Add(btn_sizer, 0, wxEXPAND | wxALL, 0);
if (is_window_field(field))
tmp_sizer->Add(field->getWindow(), 0, wxEXPAND | wxALL, wxOSX ? 0 : 5);
@ -149,6 +155,18 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/*
m_panel->Layout();
#endif /* __WXGTK__ */
// if we have an extra column, build it
if (extra_column) {
if (extra_column) {
grid_sizer->Add(extra_column(parent(), line), 0, wxALIGN_CENTER_VERTICAL, 0);
}
else {
// if the callback provides no sizer for the extra cell, put a spacer
grid_sizer->AddSpacer(1);
}
}
// Build a label if we have it
wxStaticText* label=nullptr;
if (label_width != 0) {
@ -163,7 +181,8 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/*
wxDefaultPosition, wxSize(label_width, -1), label_style);
label->SetFont(label_font);
label->Wrap(label_width); // avoid a Linux/GTK bug
grid_sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | wxALIGN_CENTER_VERTICAL, 5);
grid_sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) |
(m_flag == ogSIDE_OPTIONS_VERTICAL ? wxTOP : wxALIGN_CENTER_VERTICAL), 5);
if (line.label_tooltip.compare("") != 0)
label->SetToolTip(line.label_tooltip);
}
@ -177,28 +196,35 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/*
return;
}
// if we have a single option with no sidetext just add it directly to the grid sizer
auto sizer = new wxBoxSizer(wxHORIZONTAL);
grid_sizer->Add(sizer, 0, wxEXPAND | (staticbox ? wxALL : wxBOTTOM|wxTOP|wxLEFT), staticbox ? 0 : 1);
// If we're here, we have more than one option or a single option with sidetext
// so we need a horizontal sizer to arrange these things
auto sizer = new wxBoxSizer(m_flag == ogSIDE_OPTIONS_VERTICAL ? wxVERTICAL : wxHORIZONTAL);
grid_sizer->Add(sizer, 0, wxEXPAND | (staticbox ? wxALL : wxBOTTOM | wxTOP | wxLEFT), staticbox ? 0 : 1);
// If we have a single option with no sidetext just add it directly to the grid sizer
if (option_set.size() == 1 && option_set.front().opt.sidetext.size() == 0 &&
option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0) {
const auto& option = option_set.front();
const auto& field = build_field(option, label);
sizer->Add(field->m_Undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL);
sizer->Add(field->m_Undo_btn, 0, wxALIGN_CENTER_VERTICAL);
add_undo_buttuns_to_sizer(sizer, field);
if (is_window_field(field))
sizer->Add(field->getWindow(), option.opt.full_width ? 1 : 0, (option.opt.full_width ? wxEXPAND : 0) |
wxBOTTOM | wxTOP | wxALIGN_CENTER_VERTICAL, (wxOSX||!staticbox) ? 0 : 2);
if (is_sizer_field(field))
sizer->Add(field->getSizer(), 0, (option.opt.full_width ? wxEXPAND : 0) | wxALIGN_CENTER_VERTICAL, 0);
sizer->Add(field->getSizer(), 1, (option.opt.full_width ? wxEXPAND : 0) | wxALIGN_CENTER_VERTICAL, 0);
return;
}
// if we're here, we have more than one option or a single option with sidetext
// so we need a horizontal sizer to arrange these things
for (auto opt : option_set) {
for (auto opt : option_set) {
ConfigOptionDef option = opt.opt;
wxSizer* sizer_tmp;
if (m_flag == ogSIDE_OPTIONS_VERTICAL){
auto sz = new wxFlexGridSizer(1, 3, 2, 2);
sz->RemoveGrowableCol(2);
sizer_tmp = sz;
}
else
sizer_tmp = sizer;
// add label if any
if (option.label != "") {
wxString str_label = _(option.label);
@ -208,34 +234,38 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/*
// L_str(option.label);
label = new wxStaticText(parent(), wxID_ANY, str_label + ":", wxDefaultPosition, wxDefaultSize);
label->SetFont(label_font);
sizer->Add(label, 0, wxALIGN_CENTER_VERTICAL, 0);
sizer_tmp->Add(label, 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, 0);
}
// add field
const Option& opt_ref = opt;
auto& field = build_field(opt_ref, label);
sizer->Add(field->m_Undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL);
sizer->Add(field->m_Undo_btn, 0, wxALIGN_CENTER_VERTICAL, 0);
add_undo_buttuns_to_sizer(sizer_tmp, field);
is_sizer_field(field) ?
sizer->Add(field->getSizer(), 0, wxALIGN_CENTER_VERTICAL, 0) :
sizer->Add(field->getWindow(), 0, wxALIGN_CENTER_VERTICAL, 0);
sizer_tmp->Add(field->getSizer(), 0, wxALIGN_CENTER_VERTICAL, 0) :
sizer_tmp->Add(field->getWindow(), 0, wxALIGN_CENTER_VERTICAL, 0);
// add sidetext if any
if (option.sidetext != "") {
auto sidetext = new wxStaticText(parent(), wxID_ANY, _(option.sidetext), wxDefaultPosition, wxDefaultSize);
auto sidetext = new wxStaticText( parent(), wxID_ANY, _(option.sidetext), wxDefaultPosition,
wxSize(sidetext_width, -1)/*wxDefaultSize*/, wxALIGN_LEFT);
sidetext->SetFont(sidetext_font);
sizer->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4);
sizer_tmp->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, m_flag == ogSIDE_OPTIONS_VERTICAL ? 0 : 4);
field->set_side_text_ptr(sidetext);
}
// add side widget if any
if (opt.side_widget != nullptr) {
sizer->Add(opt.side_widget(parent())/*!.target<wxWindow>()*/, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 1); //! requires verification
sizer_tmp->Add(opt.side_widget(parent())/*!.target<wxWindow>()*/, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 1); //! requires verification
}
if (opt.opt_id != option_set.back().opt_id) //! istead of (opt != option_set.back())
if (opt.opt_id != option_set.back().opt_id && m_flag != ogSIDE_OPTIONS_VERTICAL) //! istead of (opt != option_set.back())
{
sizer->AddSpacer(6);
sizer_tmp->AddSpacer(6);
}
if (m_flag == ogSIDE_OPTIONS_VERTICAL)
sizer->Add(sizer_tmp, 0, wxALIGN_RIGHT|wxALL, 0);
}
// add extra sizers if any
for (auto extra_widget : line.get_extra_widgets()) {
@ -259,7 +289,7 @@ void OptionsGroup::on_change_OG(const t_config_option_key& opt_id, const boost::
Option ConfigOptionsGroup::get_option(const std::string& opt_key, int opt_index /*= -1*/)
{
if (!m_config->has(opt_key)) {
std::cerr << "No " << opt_key << " in ConfigOptionsGroup config.";
std::cerr << "No " << opt_key << " in ConfigOptionsGroup config.\n";
}
std::string opt_id = opt_index == -1 ? opt_key : opt_key + "#" + std::to_string(opt_index);
@ -459,8 +489,12 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
else if (opt_key.compare("support_material_pattern") == 0){
ret = static_cast<int>(config.option<ConfigOptionEnum<SupportMaterialPattern>>(opt_key)->value);
}
else if (opt_key.compare("seam_position") == 0)
else if (opt_key.compare("seam_position") == 0){
ret = static_cast<int>(config.option<ConfigOptionEnum<SeamPosition>>(opt_key)->value);
}
else if (opt_key.compare("host_type") == 0){
ret = static_cast<int>(config.option<ConfigOptionEnum<PrintHostType>>(opt_key)->value);
}
}
break;
case coPoints:

View file

@ -26,9 +26,13 @@
namespace Slic3r { namespace GUI {
enum ogDrawFlag{
ogDEFAULT,
ogSIDE_OPTIONS_VERTICAL
};
/// Widget type describes a function object that returns a wxWindow (our widget) and accepts a wxWidget (parent window).
using widget_t = std::function<wxSizer*(wxWindow*)>;//!std::function<wxWindow*(wxWindow*)>;
using column_t = std::function<wxSizer*(const Line&)>;
//auto default_label_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT); //GetSystemColour
//auto modified_label_clr = *new wxColour(254, 189, 101);
@ -71,10 +75,13 @@ private:
std::vector<widget_t> m_extra_widgets;//! {std::vector<widget_t>()};
};
using column_t = std::function<wxWindow*(wxWindow* parent, const Line&)>;//std::function<wxSizer*(const Line&)>;
using t_optionfield_map = std::map<t_config_option_key, t_field>;
using t_opt_map = std::map< std::string, std::pair<std::string, int> >;
class OptionsGroup {
wxStaticBox* stb;
public:
const bool staticbox {true};
const wxString title {wxString("")};
@ -88,8 +95,7 @@ public:
wxFont sidetext_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) };
wxFont label_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) };
// std::function<const wxBitmap&()> nonsys_btn_icon{ nullptr };
int sidetext_width{ -1 };
/// Returns a copy of the pointer of the parent wxWindow.
/// Accessor function is because users are not allowed to change the parent
@ -101,6 +107,11 @@ public:
return m_parent;
#endif /* __WXGTK__ */
}
#ifdef __WXGTK__
wxWindow* get_parent() const {
return m_parent;
}
#endif /* __WXGTK__ */
void append_line(const Line& line, wxStaticText** colored_Label = nullptr);
Line create_single_option_line(const Option& option) const;
@ -124,23 +135,39 @@ public:
return out;
}
bool set_side_text(const t_config_option_key& opt_key, const wxString& side_text) {
if (m_fields.find(opt_key) == m_fields.end()) return false;
auto st = m_fields.at(opt_key)->m_side_text;
if (!st) return false;
st->SetLabel(side_text);
return true;
}
void set_name(const wxString& new_name) {
stb->SetLabel(new_name);
}
inline void enable() { for (auto& field : m_fields) field.second->enable(); }
inline void disable() { for (auto& field : m_fields) field.second->disable(); }
void set_flag(ogDrawFlag flag) { m_flag = flag; }
void set_grid_vgap(int gap) { m_grid_sizer->SetVGap(gap); }
void set_show_modified_btns_val(bool show) {
m_show_modified_btns = show;
}
OptionsGroup(wxWindow* _parent, const wxString& title, bool is_tab_opt=false) :
m_parent(_parent), title(title), m_show_modified_btns(is_tab_opt), staticbox(title!="") {
auto stb = new wxStaticBox(_parent, wxID_ANY, title);
OptionsGroup( wxWindow* _parent, const wxString& title, bool is_tab_opt = false,
ogDrawFlag flag = ogDEFAULT, column_t extra_clmn = nullptr) :
m_parent(_parent), title(title), m_show_modified_btns(is_tab_opt),
staticbox(title!=""), m_flag(flag), extra_column(extra_clmn){
stb = new wxStaticBox(_parent, wxID_ANY, title);
stb->SetFont(bold_font());
sizer = (staticbox ? new wxStaticBoxSizer(stb/*new wxStaticBox(_parent, wxID_ANY, title)*/, wxVERTICAL) : new wxBoxSizer(wxVERTICAL));
sizer = (staticbox ? new wxStaticBoxSizer(stb, wxVERTICAL) : new wxBoxSizer(wxVERTICAL));
auto num_columns = 1U;
if (label_width != 0) num_columns++;
if (extra_column != nullptr) num_columns++;
m_grid_sizer = new wxFlexGridSizer(0, num_columns, 0,0);
static_cast<wxFlexGridSizer*>(m_grid_sizer)->SetFlexibleDirection(wxHORIZONTAL);
m_grid_sizer = new wxFlexGridSizer(0, num_columns, 1,0);
static_cast<wxFlexGridSizer*>(m_grid_sizer)->SetFlexibleDirection(wxBOTH/*wxHORIZONTAL*/);
static_cast<wxFlexGridSizer*>(m_grid_sizer)->AddGrowableCol(label_width != 0);
#ifdef __WXGTK__
m_panel = new wxPanel( _parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
@ -164,6 +191,8 @@ protected:
// "true" if option is created in preset tabs
bool m_show_modified_btns{ false };
ogDrawFlag m_flag{ ogDEFAULT };
// This panel is needed for correct showing of the ToolTips for Button, StaticText and CheckBox
// Tooltips on GTK doesn't work inside wxStaticBoxSizer unless you insert a panel
// inside it before you insert the other controls.
@ -177,6 +206,7 @@ protected:
const t_field& build_field(const t_config_option_key& id, const ConfigOptionDef& opt, wxStaticText* label = nullptr);
const t_field& build_field(const t_config_option_key& id, wxStaticText* label = nullptr);
const t_field& build_field(const Option& opt, wxStaticText* label = nullptr);
void add_undo_buttuns_to_sizer(wxSizer* sizer, const t_field& field);
virtual void on_kill_focus (){};
virtual void on_change_OG(const t_config_option_key& opt_id, const boost::any& value);
@ -186,8 +216,9 @@ protected:
class ConfigOptionsGroup: public OptionsGroup {
public:
ConfigOptionsGroup(wxWindow* parent, const wxString& title, DynamicPrintConfig* _config = nullptr, bool is_tab_opt = false) :
OptionsGroup(parent, title, is_tab_opt), m_config(_config) {}
ConfigOptionsGroup( wxWindow* parent, const wxString& title, DynamicPrintConfig* _config = nullptr,
bool is_tab_opt = false, ogDrawFlag flag = ogDEFAULT, column_t extra_clmn = nullptr) :
OptionsGroup(parent, title, is_tab_opt, flag, extra_clmn), m_config(_config) {}
/// reference to libslic3r config, non-owning pointer (?).
DynamicPrintConfig* m_config {nullptr};

View file

@ -120,6 +120,11 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem
VendorProfile::PrinterModel model;
model.id = section.first.substr(printer_model_key.size());
model.name = section.second.get<std::string>("name", model.id);
auto technology_field = section.second.get<std::string>("technology", "FFF");
if (! ConfigOptionEnum<PrinterTechnology>::from_string(technology_field, model.technology)) {
BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: Invalid printer technology field: `%2%`") % id % technology_field;
model.technology = ptFFF;
}
section.second.get<std::string>("variants", "");
const auto variants_field = section.second.get<std::string>("variants", "");
std::vector<std::string> variants;
@ -177,7 +182,7 @@ void Preset::normalize(DynamicPrintConfig &config)
{
auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(config.option("nozzle_diameter"));
if (nozzle_diameter != nullptr)
// Loaded the Printer settings. Verify, that all extruder dependent values have enough values.
// Loaded the FFF Printer settings. Verify, that all extruder dependent values have enough values.
set_num_extruders(config, (unsigned int)nozzle_diameter->values.size());
if (config.option("filament_diameter") != nullptr) {
// This config contains single or multiple filament presets.
@ -204,12 +209,9 @@ void Preset::normalize(DynamicPrintConfig &config)
}
}
// Load a config file, return a C++ class Slic3r::DynamicPrintConfig with $keys initialized from the config file.
// In case of a "default" config item, return the default values.
DynamicPrintConfig& Preset::load(const std::vector<std::string> &keys)
DynamicPrintConfig& Preset::load(const std::vector<std::string> &keys, const StaticPrintConfig &defaults)
{
// Set the configuration from the defaults.
Slic3r::FullPrintConfig defaults;
this->config.apply_only(defaults, keys.empty() ? defaults.keys() : keys);
if (! this->is_default) {
// Load the preset file, apply preset values on top of defaults.
@ -260,8 +262,9 @@ bool Preset::is_compatible_with_printer(const Preset &active_printer) const
{
DynamicPrintConfig config;
config.set_key_value("printer_preset", new ConfigOptionString(active_printer.name));
config.set_key_value("num_extruders", new ConfigOptionInt(
(int)static_cast<const ConfigOptionFloats*>(active_printer.config.option("nozzle_diameter"))->values.size()));
const ConfigOption *opt = active_printer.config.option("nozzle_diameter");
if (opt)
config.set_key_value("num_extruders", new ConfigOptionInt((int)static_cast<const ConfigOptionFloats*>(opt)->values.size()));
return this->is_compatible_with_printer(active_printer, &config);
}
@ -329,8 +332,10 @@ const std::vector<std::string>& Preset::printer_options()
static std::vector<std::string> s_opts;
if (s_opts.empty()) {
s_opts = {
"printer_technology",
"bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed",
"octoprint_host", "octoprint_apikey", "octoprint_cafile", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height",
"use_firmware_retraction", "use_volumetric_e", "variable_layer_height",
"host_type", "print_host", "printhost_apikey", "printhost_cafile",
"single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",
"between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "cooling_tube_retraction",
"cooling_tube_length", "parking_pos_retraction", "extra_loading_move", "max_print_height", "default_print_profile", "inherits",
@ -360,7 +365,39 @@ const std::vector<std::string>& Preset::nozzle_options()
return s_opts;
}
PresetCollection::PresetCollection(Preset::Type type, const std::vector<std::string> &keys) :
const std::vector<std::string>& Preset::sla_printer_options()
{
static std::vector<std::string> s_opts;
if (s_opts.empty()) {
s_opts = {
"printer_technology",
"bed_shape", "max_print_height",
"display_width", "display_height", "display_pixels_x", "display_pixels_y",
"printer_correction",
"printer_notes",
"inherits"
};
}
return s_opts;
}
const std::vector<std::string>& Preset::sla_material_options()
{
static std::vector<std::string> s_opts;
if (s_opts.empty()) {
s_opts = {
"layer_height", "initial_layer_height",
"exposure_time", "initial_exposure_time",
"material_correction_printing", "material_correction_curing",
"material_notes",
"compatible_printers",
"compatible_printers_condition", "inherits"
};
}
return s_opts;
}
PresetCollection::PresetCollection(Preset::Type type, const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name) :
m_type(type),
m_edited_preset(type, "", false),
m_idx_selected(0),
@ -368,8 +405,7 @@ PresetCollection::PresetCollection(Preset::Type type, const std::vector<std::str
m_bitmap_cache(new GUI::BitmapCache)
{
// Insert just the default preset.
m_presets.emplace_back(Preset(type, "- default -", true));
m_presets.front().load(keys);
this->add_default_preset(keys, defaults, default_name);
m_edited_preset.config.apply(m_presets.front().config);
}
@ -383,7 +419,7 @@ PresetCollection::~PresetCollection()
void PresetCollection::reset(bool delete_files)
{
if (m_presets.size() > 1) {
if (m_presets.size() > m_num_default_presets) {
if (delete_files) {
// Erase the preset files.
for (Preset &preset : m_presets)
@ -391,11 +427,19 @@ void PresetCollection::reset(bool delete_files)
boost::nowide::remove(preset.file.c_str());
}
// Don't use m_presets.resize() here as it requires a default constructor for Preset.
m_presets.erase(m_presets.begin() + 1, m_presets.end());
m_presets.erase(m_presets.begin() + m_num_default_presets, m_presets.end());
this->select_preset(0);
}
}
void PresetCollection::add_default_preset(const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &preset_name)
{
// Insert just the default preset.
m_presets.emplace_back(Preset(this->type(), preset_name, true));
m_presets.back().load(keys, defaults);
++ m_num_default_presets;
}
// Load all presets found in dir_path.
// Throws an exception on error.
void PresetCollection::load_presets(const std::string &dir_path, const std::string &subdir)
@ -418,14 +462,15 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri
try {
Preset preset(m_type, name, false);
preset.file = dir_entry.path().string();
preset.load(keys);
//FIXME One should initialize with SLAFullPrintConfig for the SLA profiles!
preset.load(keys, static_cast<const HostConfig&>(FullPrintConfig::defaults()));
m_presets.emplace_back(preset);
} catch (const std::runtime_error &err) {
errors_cummulative += err.what();
errors_cummulative += "\n";
}
}
std::sort(m_presets.begin() + 1, m_presets.end());
std::sort(m_presets.begin() + m_num_default_presets, m_presets.end());
this->select_preset(first_visible_idx());
if (! errors_cummulative.empty())
throw std::runtime_error(errors_cummulative);
@ -643,7 +688,7 @@ Preset* PresetCollection::find_preset(const std::string &name, bool first_visibl
// Return index of the first visible preset. Certainly at least the '- default -' preset shall be visible.
size_t PresetCollection::first_visible_idx() const
{
size_t idx = m_default_suppressed ? 1 : 0;
size_t idx = m_default_suppressed ? m_num_default_presets : 0;
for (; idx < this->m_presets.size(); ++ idx)
if (m_presets[idx].is_visible)
break;
@ -656,7 +701,7 @@ void PresetCollection::set_default_suppressed(bool default_suppressed)
{
if (m_default_suppressed != default_suppressed) {
m_default_suppressed = default_suppressed;
m_presets.front().is_visible = ! default_suppressed || (m_presets.size() > 1 && m_idx_selected > 0);
m_presets.front().is_visible = ! default_suppressed || (m_presets.size() > m_num_default_presets && m_idx_selected > 0);
}
}
@ -664,9 +709,10 @@ size_t PresetCollection::update_compatible_with_printer_internal(const Preset &a
{
DynamicPrintConfig config;
config.set_key_value("printer_preset", new ConfigOptionString(active_printer.name));
config.set_key_value("num_extruders", new ConfigOptionInt(
(int)static_cast<const ConfigOptionFloats*>(active_printer.config.option("nozzle_diameter"))->values.size()));
for (size_t idx_preset = 1; idx_preset < m_presets.size(); ++ idx_preset) {
const ConfigOption *opt = active_printer.config.option("nozzle_diameter");
if (opt)
config.set_key_value("num_extruders", new ConfigOptionInt((int)static_cast<const ConfigOptionFloats*>(opt)->values.size()));
for (size_t idx_preset = m_num_default_presets; idx_preset < m_presets.size(); ++ idx_preset) {
bool selected = idx_preset == m_idx_selected;
Preset &preset_selected = m_presets[idx_preset];
Preset &preset_edited = selected ? m_edited_preset : preset_selected;
@ -707,7 +753,7 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui)
wxString selected = "";
if (!this->m_presets.front().is_visible)
ui->Append("------- " +_(L("System presets")) + " -------", wxNullBitmap);
for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++i) {
for (size_t i = this->m_presets.front().is_visible ? 0 : m_num_default_presets; i < this->m_presets.size(); ++i) {
const Preset &preset = this->m_presets[i];
if (! preset.is_visible || (! preset.is_compatible && i != m_idx_selected))
continue;
@ -745,7 +791,7 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui)
if (i == m_idx_selected)
selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str());
}
if (preset.is_default)
if (i + 1 == m_num_default_presets)
ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap);
}
if (!nonsys_presets.empty())
@ -775,7 +821,7 @@ size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompati
wxString selected = "";
if (!this->m_presets.front().is_visible)
ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap);
for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++i) {
for (size_t i = this->m_presets.front().is_visible ? 0 : m_num_default_presets; i < this->m_presets.size(); ++i) {
const Preset &preset = this->m_presets[i];
if (! preset.is_visible || (! show_incompatible && ! preset.is_compatible && i != m_idx_selected))
continue;
@ -805,7 +851,7 @@ size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompati
if (i == m_idx_selected)
selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str());
}
if (preset.is_default)
if (i + 1 == m_num_default_presets)
ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap);
}
if (!nonsys_presets.empty())
@ -853,11 +899,11 @@ bool PresetCollection::update_dirty_ui(wxBitmapComboBox *ui)
return was_dirty != is_dirty;
}
std::vector<std::string> PresetCollection::dirty_options(const Preset *edited, const Preset *reference, const bool is_printer_type /*= false*/)
std::vector<std::string> PresetCollection::dirty_options(const Preset *edited, const Preset *reference, const bool deep_compare /*= false*/)
{
std::vector<std::string> changed;
if (edited != nullptr && reference != nullptr) {
changed = is_printer_type ?
changed = deep_compare ?
reference->config.deep_diff(edited->config) :
reference->config.diff(edited->config);
// The "compatible_printers" option key is handled differently from the others:
@ -897,7 +943,7 @@ bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, b
idx = it - m_presets.begin();
else {
// Find the first visible preset.
for (size_t i = m_default_suppressed ? 1 : 0; i < m_presets.size(); ++ i)
for (size_t i = m_default_suppressed ? m_num_default_presets : 0; i < m_presets.size(); ++ i)
if (m_presets[i].is_visible) {
idx = i;
break;
@ -939,7 +985,7 @@ std::vector<std::string> PresetCollection::merge_presets(PresetCollection &&othe
if (preset.is_default || preset.is_external)
continue;
Preset key(m_type, preset.name);
auto it = std::lower_bound(m_presets.begin() + 1, m_presets.end(), key);
auto it = std::lower_bound(m_presets.begin() + m_num_default_presets, m_presets.end(), key);
if (it == m_presets.end() || it->name != preset.name) {
if (preset.vendor != nullptr) {
// Re-assign a pointer to the vendor structure in the new PresetBundle.

View file

@ -52,6 +52,7 @@ public:
PrinterModel() {}
std::string id;
std::string name;
PrinterTechnology technology;
std::vector<PrinterVariant> variants;
PrinterVariant* variant(const std::string &name) {
for (auto &v : this->variants)
@ -83,6 +84,7 @@ public:
TYPE_INVALID,
TYPE_PRINT,
TYPE_FILAMENT,
TYPE_SLA_MATERIAL,
TYPE_PRINTER,
};
@ -123,8 +125,7 @@ public:
DynamicPrintConfig config;
// Load this profile for the following keys only.
// Throws std::runtime_error in case the file cannot be read.
DynamicPrintConfig& load(const std::vector<std::string> &keys);
DynamicPrintConfig& load(const std::vector<std::string> &keys, const StaticPrintConfig &defaults);
void save();
@ -149,6 +150,10 @@ public:
std::string& compatible_printers_condition() { return Preset::compatible_printers_condition(this->config); }
const std::string& compatible_printers_condition() const { return Preset::compatible_printers_condition(const_cast<Preset*>(this)->config); }
static PrinterTechnology& printer_technology(DynamicPrintConfig &cfg) { return cfg.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology", true)->value; }
PrinterTechnology& printer_technology() { return Preset::printer_technology(this->config); }
const PrinterTechnology& printer_technology() const { return Preset::printer_technology(const_cast<Preset*>(this)->config); }
// Mark this preset as compatible if it is compatible with active_printer.
bool update_compatible_with_printer(const Preset &active_printer, const DynamicPrintConfig *extra_config);
@ -167,6 +172,10 @@ public:
static const std::vector<std::string>& printer_options();
// Nozzle options of the printer options.
static const std::vector<std::string>& nozzle_options();
static const std::vector<std::string>& sla_printer_options();
static const std::vector<std::string>& sla_material_options();
static void update_suffix_modified();
protected:
@ -184,15 +193,15 @@ class PresetCollection
{
public:
// Initialize the PresetCollection with the "- default -" preset.
PresetCollection(Preset::Type type, const std::vector<std::string> &keys);
PresetCollection(Preset::Type type, const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name = "- default -");
~PresetCollection();
typedef std::deque<Preset>::iterator Iterator;
typedef std::deque<Preset>::const_iterator ConstIterator;
Iterator begin() { return m_presets.begin() + 1; }
ConstIterator begin() const { return m_presets.begin() + 1; }
Iterator end() { return m_presets.end(); }
ConstIterator end() const { return m_presets.end(); }
Iterator begin() { return m_presets.begin() + m_num_default_presets; }
ConstIterator begin() const { return m_presets.begin() + m_num_default_presets; }
Iterator end() { return m_presets.end(); }
ConstIterator end() const { return m_presets.end(); }
void reset(bool delete_files);
@ -200,6 +209,9 @@ public:
std::string name() const;
const std::deque<Preset>& operator()() const { return m_presets; }
// Add default preset at the start of the collection, increment the m_default_preset counter.
void add_default_preset(const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &preset_name);
// Load ini files of the particular type from the provided directory path.
void load_presets(const std::string &dir_path, const std::string &subdir);
@ -247,6 +259,8 @@ public:
Preset& get_selected_preset() { return m_presets[m_idx_selected]; }
const Preset& get_selected_preset() const { return m_presets[m_idx_selected]; }
int get_selected_idx() const { return m_idx_selected; }
// Returns the name of the selected preset, or an empty string if no preset is selected.
std::string get_selected_preset_name() const { return (m_idx_selected == -1) ? std::string() : this->get_selected_preset().name; }
// For the current edited preset, return the parent preset if there is one.
// If there is no parent preset, nullptr is returned.
// The parent preset may be a system preset or a user preset, which will be
@ -283,7 +297,7 @@ public:
template<typename PreferedCondition>
size_t first_compatible_idx(PreferedCondition prefered_condition) const
{
size_t i = m_default_suppressed ? 1 : 0;
size_t i = m_default_suppressed ? m_num_default_presets : 0;
size_t n = this->m_presets.size();
size_t i_compatible = n;
for (; i < n; ++ i)
@ -309,7 +323,8 @@ public:
const Preset& first_compatible() const { return this->preset(this->first_compatible_idx()); }
// Return number of presets including the "- default -" preset.
size_t size() const { return this->m_presets.size(); }
size_t size() const { return m_presets.size(); }
bool has_defaults_only() const { return m_presets.size() <= m_num_default_presets; }
// For Print / Filament presets, disable those, which are not compatible with the printer.
template<typename PreferedCondition>
@ -327,11 +342,11 @@ public:
// Compare the content of get_selected_preset() with get_edited_preset() configs, return true if they differ.
bool current_is_dirty() const { return ! this->current_dirty_options().empty(); }
// Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ.
std::vector<std::string> current_dirty_options(const bool is_printer_type = false) const
{ return dirty_options(&this->get_edited_preset(), &this->get_selected_preset(), is_printer_type); }
std::vector<std::string> current_dirty_options(const bool deep_compare = false) const
{ return dirty_options(&this->get_edited_preset(), &this->get_selected_preset(), deep_compare); }
// Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ.
std::vector<std::string> current_different_from_parent_options(const bool is_printer_type = false) const
{ return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), is_printer_type); }
std::vector<std::string> current_different_from_parent_options(const bool deep_compare = false) const
{ return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), deep_compare); }
// Update the choice UI from the list of presets.
// If show_incompatible, all presets are shown, otherwise only the compatible presets are shown.
@ -374,8 +389,16 @@ private:
std::deque<Preset>::iterator find_preset_internal(const std::string &name)
{
Preset key(m_type, name);
auto it = std::lower_bound(m_presets.begin() + 1, m_presets.end(), key);
return ((it == m_presets.end() || it->name != name) && m_presets.front().name == name) ? m_presets.begin() : it;
auto it = std::lower_bound(m_presets.begin() + m_num_default_presets, m_presets.end(), key);
if (it == m_presets.end() || it->name != name) {
// Preset has not been not found in the sorted list of non-default presets. Try the defaults.
for (size_t i = 0; i < m_num_default_presets; ++ i)
if (m_presets[i].name == name) {
it = m_presets.begin() + i;
break;
}
}
return it;
}
std::deque<Preset>::const_iterator find_preset_internal(const std::string &name) const
{ return const_cast<PresetCollection*>(this)->find_preset_internal(name); }
@ -395,7 +418,8 @@ private:
// Selected preset.
int m_idx_selected;
// Is the "- default -" preset suppressed?
bool m_default_suppressed = true;
bool m_default_suppressed = true;
size_t m_num_default_presets = 0;
// Compatible & incompatible marks, to be placed at the wxBitmapComboBox items of a Platter.
// These bitmaps are not owned by PresetCollection, but by a PresetBundle.
const wxBitmap *m_bitmap_compatible = nullptr;

View file

@ -40,9 +40,10 @@ static std::vector<std::string> s_project_options {
};
PresetBundle::PresetBundle() :
prints(Preset::TYPE_PRINT, Preset::print_options()),
filaments(Preset::TYPE_FILAMENT, Preset::filament_options()),
printers(Preset::TYPE_PRINTER, Preset::printer_options()),
prints(Preset::TYPE_PRINT, Preset::print_options(), static_cast<const HostConfig&>(FullPrintConfig::defaults())),
filaments(Preset::TYPE_FILAMENT, Preset::filament_options(), static_cast<const HostConfig&>(FullPrintConfig::defaults())),
sla_materials(Preset::TYPE_SLA_MATERIAL, Preset::sla_material_options(), static_cast<const SLAMaterialConfig&>(SLAFullPrintConfig::defaults())),
printers(Preset::TYPE_PRINTER, Preset::printer_options(), static_cast<const HostConfig&>(FullPrintConfig::defaults()), "- default FFF -"),
m_bitmapCompatible(new wxBitmap),
m_bitmapIncompatible(new wxBitmap),
m_bitmapLock(new wxBitmap),
@ -69,24 +70,35 @@ PresetBundle::PresetBundle() :
this->filaments.default_preset().compatible_printers_condition();
this->filaments.default_preset().inherits();
this->printers.default_preset().config.optptr("printer_settings_id", true);
this->printers.default_preset().config.optptr("printer_vendor", true);
this->printers.default_preset().config.optptr("printer_model", true);
this->printers.default_preset().config.optptr("printer_variant", true);
this->printers.default_preset().config.optptr("default_print_profile", true);
this->printers.default_preset().config.option<ConfigOptionStrings>("default_filament_profile", true)->values = { "" };
this->printers.default_preset().inherits();
this->sla_materials.default_preset().config.optptr("sla_material_settings_id", true);
this->sla_materials.default_preset().compatible_printers_condition();
this->sla_materials.default_preset().inherits();
this->printers.add_default_preset(Preset::sla_printer_options(), static_cast<const SLAMaterialConfig&>(SLAFullPrintConfig::defaults()), "- default SLA -");
this->printers.preset(1).printer_technology() = ptSLA;
for (size_t i = 0; i < 2; ++ i) {
Preset &preset = this->printers.preset(i);
preset.config.optptr("printer_settings_id", true);
preset.config.optptr("printer_vendor", true);
preset.config.optptr("printer_model", true);
preset.config.optptr("printer_variant", true);
preset.config.optptr("default_print_profile", true);
preset.config.option<ConfigOptionStrings>("default_filament_profile", true)->values = { "" };
preset.inherits();
}
// Load the default preset bitmaps.
this->prints .load_bitmap_default("cog.png");
this->filaments.load_bitmap_default("spool.png");
this->printers .load_bitmap_default("printer_empty.png");
this->prints .load_bitmap_default("cog.png");
this->filaments .load_bitmap_default("spool.png");
this->sla_materials.load_bitmap_default("package_green.png");
this->printers .load_bitmap_default("printer_empty.png");
this->load_compatible_bitmaps();
// Re-activate the default presets, so their "edited" preset copies will be updated with the additional configuration values above.
this->prints .select_preset(0);
this->filaments.select_preset(0);
this->printers .select_preset(0);
this->prints .select_preset(0);
this->filaments .select_preset(0);
this->sla_materials.select_preset(0);
this->printers .select_preset(0);
this->project_config.apply_only(FullPrintConfig::defaults(), s_project_options);
}
@ -113,13 +125,15 @@ void PresetBundle::reset(bool delete_files)
{
// Clear the existing presets, delete their respective files.
this->vendors.clear();
this->prints .reset(delete_files);
this->filaments.reset(delete_files);
this->printers .reset(delete_files);
this->prints .reset(delete_files);
this->filaments .reset(delete_files);
this->sla_materials.reset(delete_files);
this->printers .reset(delete_files);
this->filament_presets.clear();
this->filament_presets.emplace_back(this->filaments.get_selected_preset().name);
this->filament_presets.emplace_back(this->filaments.get_selected_preset_name());
this->obsolete_presets.prints.clear();
this->obsolete_presets.filaments.clear();
this->obsolete_presets.sla_materials.clear();
this->obsolete_presets.printers.clear();
}
@ -135,11 +149,13 @@ void PresetBundle::setup_directories()
data_dir / "presets",
data_dir / "presets" / "print",
data_dir / "presets" / "filament",
data_dir / "presets" / "sla_material",
data_dir / "presets" / "printer"
#else
// Store the print/filament/printer presets at the same location as the upstream Slic3r.
data_dir / "print",
data_dir / "filament",
data_dir / "sla_material",
data_dir / "printer"
#endif
};
@ -175,6 +191,11 @@ void PresetBundle::load_presets(const AppConfig &config)
} catch (const std::runtime_error &err) {
errors_cummulative += err.what();
}
try {
this->sla_materials.load_presets(dir_user_presets, "sla_material");
} catch (const std::runtime_error &err) {
errors_cummulative += err.what();
}
try {
this->printers.load_presets(dir_user_presets, "printer");
} catch (const std::runtime_error &err) {
@ -238,13 +259,16 @@ std::string PresetBundle::load_system_presets()
std::vector<std::string> PresetBundle::merge_presets(PresetBundle &&other)
{
this->vendors.insert(other.vendors.begin(), other.vendors.end());
std::vector<std::string> duplicate_prints = this->prints .merge_presets(std::move(other.prints), this->vendors);
std::vector<std::string> duplicate_filaments = this->filaments.merge_presets(std::move(other.filaments), this->vendors);
std::vector<std::string> duplicate_printers = this->printers .merge_presets(std::move(other.printers), this->vendors);
append(this->obsolete_presets.prints, std::move(other.obsolete_presets.prints));
append(this->obsolete_presets.filaments, std::move(other.obsolete_presets.filaments));
append(this->obsolete_presets.printers, std::move(other.obsolete_presets.printers));
std::vector<std::string> duplicate_prints = this->prints .merge_presets(std::move(other.prints), this->vendors);
std::vector<std::string> duplicate_filaments = this->filaments .merge_presets(std::move(other.filaments), this->vendors);
std::vector<std::string> duplicate_sla_materials = this->sla_materials.merge_presets(std::move(other.sla_materials), this->vendors);
std::vector<std::string> duplicate_printers = this->printers .merge_presets(std::move(other.printers), this->vendors);
append(this->obsolete_presets.prints, std::move(other.obsolete_presets.prints));
append(this->obsolete_presets.filaments, std::move(other.obsolete_presets.filaments));
append(this->obsolete_presets.sla_materials, std::move(other.obsolete_presets.sla_materials));
append(this->obsolete_presets.printers, std::move(other.obsolete_presets.printers));
append(duplicate_prints, std::move(duplicate_filaments));
append(duplicate_prints, std::move(duplicate_sla_materials));
append(duplicate_prints, std::move(duplicate_printers));
return duplicate_prints;
}
@ -275,9 +299,10 @@ void PresetBundle::load_selections(const AppConfig &config)
this->load_installed_printers(config);
// Parse the initial print / filament / printer profile names.
std::string initial_print_profile_name = remove_ini_suffix(config.get("presets", "print"));
std::string initial_filament_profile_name = remove_ini_suffix(config.get("presets", "filament"));
std::string initial_printer_profile_name = remove_ini_suffix(config.get("presets", "printer"));
std::string initial_print_profile_name = remove_ini_suffix(config.get("presets", "print"));
std::string initial_filament_profile_name = remove_ini_suffix(config.get("presets", "filament"));
std::string initial_sla_material_profile_name = remove_ini_suffix(config.get("presets", "sla_material"));
std::string initial_printer_profile_name = remove_ini_suffix(config.get("presets", "printer"));
// Activate print / filament / printer profiles from the config.
// If the printer profile enumerated by the config are not visible, select an alternate preset.
@ -285,21 +310,24 @@ void PresetBundle::load_selections(const AppConfig &config)
// will be selected by the following call of this->update_compatible_with_printer(true).
prints.select_preset_by_name_strict(initial_print_profile_name);
filaments.select_preset_by_name_strict(initial_filament_profile_name);
sla_materials.select_preset_by_name_strict(initial_sla_material_profile_name);
printers.select_preset_by_name(initial_printer_profile_name, true);
// Load the names of the other filament profiles selected for a multi-material printer.
auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(printers.get_selected_preset().config.option("nozzle_diameter"));
size_t num_extruders = nozzle_diameter->values.size();
this->filament_presets = { initial_filament_profile_name };
for (unsigned int i = 1; i < (unsigned int)num_extruders; ++ i) {
char name[64];
sprintf(name, "filament_%d", i);
if (! config.has("presets", name))
break;
this->filament_presets.emplace_back(remove_ini_suffix(config.get("presets", name)));
if (printers.get_selected_preset().printer_technology() == ptFFF){
// Load the names of the other filament profiles selected for a multi-material printer.
auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(printers.get_selected_preset().config.option("nozzle_diameter"));
size_t num_extruders = nozzle_diameter->values.size();
this->filament_presets = { initial_filament_profile_name };
for (unsigned int i = 1; i < (unsigned int)num_extruders; ++i) {
char name[64];
sprintf(name, "filament_%d", i);
if (!config.has("presets", name))
break;
this->filament_presets.emplace_back(remove_ini_suffix(config.get("presets", name)));
}
// Do not define the missing filaments, so that the update_compatible_with_printer() will use the preferred filaments.
this->filament_presets.resize(num_extruders, "");
}
// Do not define the missing filaments, so that the update_compatible_with_printer() will use the preferred filaments.
this->filament_presets.resize(num_extruders, "");
// Update visibility of presets based on their compatibility with the active printer.
// Always try to select a compatible print and filament preset to the current printer preset,
@ -313,25 +341,33 @@ void PresetBundle::load_selections(const AppConfig &config)
void PresetBundle::export_selections(AppConfig &config)
{
assert(filament_presets.size() >= 1);
assert(filament_presets.size() > 1 || filaments.get_selected_preset().name == filament_presets.front());
assert(filament_presets.size() > 1 || filaments.get_selected_preset_name() == filament_presets.front());
config.clear_section("presets");
config.set("presets", "print", prints.get_selected_preset().name);
config.set("presets", "filament", filament_presets.front());
config.set("presets", "print", prints.get_selected_preset_name());
config.set("presets", "filament", filament_presets.front());
for (int i = 1; i < filament_presets.size(); ++i) {
char name[64];
sprintf(name, "filament_%d", i);
config.set("presets", name, filament_presets[i]);
}
config.set("presets", "printer", printers.get_selected_preset().name);
config.set("presets", "sla_material", sla_materials.get_selected_preset_name());
config.set("presets", "printer", printers.get_selected_preset_name());
}
void PresetBundle::export_selections(PlaceholderParser &pp)
{
assert(filament_presets.size() >= 1);
assert(filament_presets.size() > 1 || filaments.get_selected_preset().name == filament_presets.front());
pp.set("print_preset", prints.get_selected_preset().name);
pp.set("filament_preset", filament_presets);
pp.set("printer_preset", printers.get_selected_preset().name);
assert(filament_presets.size() > 1 || filaments.get_selected_preset_name() == filament_presets.front());
switch (printers.get_edited_preset().printer_technology()) {
case ptFFF:
pp.set("print_preset", prints.get_selected_preset().name);
pp.set("filament_preset", filament_presets);
break;
case ptSLA:
pp.set("sla_material_preset", sla_materials.get_selected_preset().name);
break;
}
pp.set("printer_preset", printers.get_selected_preset().name);
}
bool PresetBundle::load_compatible_bitmaps()
@ -349,32 +385,43 @@ bool PresetBundle::load_compatible_bitmaps()
bool loaded_lock_open = m_bitmapLockOpen->LoadFile(
wxString::FromUTF8(Slic3r::var(path_bitmap_lock_open).c_str()), wxBITMAP_TYPE_PNG);
if (loaded_compatible) {
prints .set_bitmap_compatible(m_bitmapCompatible);
filaments.set_bitmap_compatible(m_bitmapCompatible);
prints .set_bitmap_compatible(m_bitmapCompatible);
filaments .set_bitmap_compatible(m_bitmapCompatible);
sla_materials.set_bitmap_compatible(m_bitmapCompatible);
// printers .set_bitmap_compatible(m_bitmapCompatible);
}
if (loaded_incompatible) {
prints .set_bitmap_incompatible(m_bitmapIncompatible);
filaments.set_bitmap_incompatible(m_bitmapIncompatible);
// printers .set_bitmap_incompatible(m_bitmapIncompatible);
prints .set_bitmap_incompatible(m_bitmapIncompatible);
filaments .set_bitmap_incompatible(m_bitmapIncompatible);
sla_materials.set_bitmap_incompatible(m_bitmapIncompatible);
// printers .set_bitmap_incompatible(m_bitmapIncompatible);
}
if (loaded_lock) {
prints .set_bitmap_lock(m_bitmapLock);
filaments.set_bitmap_lock(m_bitmapLock);
printers .set_bitmap_lock(m_bitmapLock);
prints .set_bitmap_lock(m_bitmapLock);
filaments .set_bitmap_lock(m_bitmapLock);
sla_materials.set_bitmap_lock(m_bitmapLock);
printers .set_bitmap_lock(m_bitmapLock);
}
if (loaded_lock_open) {
prints .set_bitmap_lock_open(m_bitmapLock);
filaments.set_bitmap_lock_open(m_bitmapLock);
printers .set_bitmap_lock_open(m_bitmapLock);
prints .set_bitmap_lock_open(m_bitmapLock);
filaments .set_bitmap_lock_open(m_bitmapLock);
sla_materials.set_bitmap_lock_open(m_bitmapLock);
printers .set_bitmap_lock_open(m_bitmapLock);
}
return loaded_compatible && loaded_incompatible && loaded_lock && loaded_lock_open;
}
DynamicPrintConfig PresetBundle::full_config() const
{
return (this->printers.get_edited_preset().printer_technology() == ptFFF) ?
this->full_fff_config() :
this->full_sla_config();
}
DynamicPrintConfig PresetBundle::full_fff_config() const
{
DynamicPrintConfig out;
out.apply(FullPrintConfig());
out.apply(FullPrintConfig::defaults());
out.apply(this->prints.get_edited_preset().config);
// Add the default filament preset to have the "filament_preset_id" defined.
out.apply(this->filaments.default_preset().config);
@ -466,6 +513,48 @@ DynamicPrintConfig PresetBundle::full_config() const
return out;
}
DynamicPrintConfig PresetBundle::full_sla_config() const
{
DynamicPrintConfig out;
out.apply(SLAFullPrintConfig::defaults());
out.apply(this->sla_materials.get_edited_preset().config);
out.apply(this->printers.get_edited_preset().config);
// There are no project configuration values as of now, the project_config is reserved for FFF printers.
// out.apply(this->project_config);
// Collect the "compatible_printers_condition" and "inherits" values over all presets (sla_materials, printers) into a single vector.
std::vector<std::string> compatible_printers_condition;
std::vector<std::string> inherits;
compatible_printers_condition.emplace_back(this->/*prints*/sla_materials.get_edited_preset().compatible_printers_condition());
inherits .emplace_back(this->/*prints*/sla_materials.get_edited_preset().inherits());
inherits .emplace_back(this->printers.get_edited_preset().inherits());
// These two value types clash between the print and filament profiles. They should be renamed.
out.erase("compatible_printers");
out.erase("compatible_printers_condition");
out.erase("inherits");
out.option<ConfigOptionString >("sla_material_settings_id", true)->value = this->sla_materials.get_selected_preset().name;
out.option<ConfigOptionString >("printer_settings_id", true)->value = this->printers.get_selected_preset().name;
// Serialize the collected "compatible_printers_condition" and "inherits" fields.
// There will be 1 + num_exturders fields for "inherits" and 2 + num_extruders for "compatible_printers_condition" stored.
// The vector will not be stored if all fields are empty strings.
auto add_if_some_non_empty = [&out](std::vector<std::string> &&values, const std::string &key) {
bool nonempty = false;
for (const std::string &v : values)
if (! v.empty()) {
nonempty = true;
break;
}
if (nonempty)
out.set_key_value(key, new ConfigOptionStrings(std::move(values)));
};
add_if_some_non_empty(std::move(compatible_printers_condition), "compatible_printers_condition_cummulative");
add_if_some_non_empty(std::move(inherits), "inherits_cummulative");
return out;
}
// Load an external config file containing the print, filament and printer presets.
// Instead of a config file, a G-code may be loaded containing the full set of parameters.
// In the future the configuration will likely be read from an AMF file as well.
@ -530,6 +619,8 @@ void PresetBundle::load_config_string(const char* str, const char* source_filena
// Load a config file from a boost property_tree. This is a private method called from load_config_file.
void PresetBundle::load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config)
{
PrinterTechnology printer_technology = Preset::printer_technology(config);
// The "compatible_printers" field should not have been exported into a config.ini or a G-code anyway,
// but some of the alpha versions of Slic3r did.
{
@ -541,8 +632,10 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
}
}
size_t num_extruders = std::min(config.option<ConfigOptionFloats>("nozzle_diameter" )->values.size(),
config.option<ConfigOptionFloats>("filament_diameter")->values.size());
size_t num_extruders = (printer_technology == ptFFF) ?
std::min(config.option<ConfigOptionFloats>("nozzle_diameter" )->values.size(),
config.option<ConfigOptionFloats>("filament_diameter")->values.size()) :
0;
// Make a copy of the "compatible_printers_condition_cummulative" and "inherits_cummulative" vectors, which
// accumulate values over all presets (print, filaments, printers).
// These values will be distributed into their particular presets when loading.
@ -553,7 +646,8 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
compatible_printers_condition_values.resize(num_extruders + 2, std::string());
inherits_values.resize(num_extruders + 2, std::string());
// The "default_filament_profile" will be later extracted into the printer profile.
config.option<ConfigOptionStrings>("default_filament_profile", true)->values.resize(num_extruders, std::string());
if (printer_technology == ptFFF)
config.option<ConfigOptionStrings>("default_filament_profile", true)->values.resize(num_extruders, std::string());
// 1) Create a name from the file name.
// Keep the suffix (.ini, .gcode, .amf, .3mf etc) to differentiate it from the normal profiles.
@ -562,81 +656,83 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
// 2) If the loading succeeded, split and load the config into print / filament / printer settings.
// First load the print and printer presets.
for (size_t i_group = 0; i_group < 2; ++ i_group) {
PresetCollection &presets = (i_group == 0) ? this->prints : this->printers;
PresetCollection &presets = (i_group == 0) ? ((printer_technology == ptFFF) ? this->prints : this->sla_materials) : this->printers;
// Split the "compatible_printers_condition" and "inherits" values one by one from a single vector to the print & printer profiles.
size_t idx = (i_group == 0) ? 0 : num_extruders + 1;
inherits = inherits_values[idx];
compatible_printers_condition = compatible_printers_condition_values[idx];
if (is_external)
presets.load_external_preset(name_or_path, name,
config.opt_string((i_group == 0) ? "print_settings_id" : "printer_settings_id", true),
config.opt_string((i_group == 0) ? ((printer_technology == ptFFF) ? "print_settings_id" : "sla_material_id") : "printer_settings_id", true),
config);
else
presets.load_preset(presets.path_from_name(name), name, config).save();
}
// 3) Now load the filaments. If there are multiple filament presets, split them and load them.
auto old_filament_profile_names = config.option<ConfigOptionStrings>("filament_settings_id", true);
old_filament_profile_names->values.resize(num_extruders, std::string());
if (Preset::printer_technology(config) == ptFFF) {
// 3) Now load the filaments. If there are multiple filament presets, split them and load them.
auto old_filament_profile_names = config.option<ConfigOptionStrings>("filament_settings_id", true);
old_filament_profile_names->values.resize(num_extruders, std::string());
if (num_extruders <= 1) {
// Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets.
inherits = inherits_values[1];
compatible_printers_condition = compatible_printers_condition_values[1];
if (is_external)
this->filaments.load_external_preset(name_or_path, name, old_filament_profile_names->values.front(), config);
else
this->filaments.load_preset(this->filaments.path_from_name(name), name, config).save();
this->filament_presets.clear();
this->filament_presets.emplace_back(name);
} else {
// Split the filament presets, load each of them separately.
std::vector<DynamicPrintConfig> configs(num_extruders, this->filaments.default_preset().config);
// loop through options and scatter them into configs.
for (const t_config_option_key &key : this->filaments.default_preset().config.keys()) {
const ConfigOption *other_opt = config.option(key);
if (other_opt == nullptr)
continue;
if (other_opt->is_scalar()) {
for (size_t i = 0; i < configs.size(); ++ i)
configs[i].option(key, false)->set(other_opt);
} else if (key != "compatible_printers") {
for (size_t i = 0; i < configs.size(); ++ i)
static_cast<ConfigOptionVectorBase*>(configs[i].option(key, false))->set_at(other_opt, 0, i);
}
}
// Load the configs into this->filaments and make them active.
this->filament_presets.clear();
for (size_t i = 0; i < configs.size(); ++ i) {
DynamicPrintConfig &cfg = configs[i];
if (num_extruders <= 1) {
// Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets.
cfg.opt_string("compatible_printers_condition", true) = compatible_printers_condition_values[i + 1];
cfg.opt_string("inherits", true) = inherits_values[i + 1];
// Load all filament presets, but only select the first one in the preset dialog.
Preset *loaded = nullptr;
inherits = inherits_values[1];
compatible_printers_condition = compatible_printers_condition_values[1];
if (is_external)
loaded = &this->filaments.load_external_preset(name_or_path, name,
(i < old_filament_profile_names->values.size()) ? old_filament_profile_names->values[i] : "",
std::move(cfg), i == 0);
else {
// Used by the config wizard when creating a custom setup.
// Therefore this block should only be called for a single extruder.
char suffix[64];
if (i == 0)
suffix[0] = 0;
else
sprintf(suffix, "%d", i);
std::string new_name = name + suffix;
loaded = &this->filaments.load_preset(this->filaments.path_from_name(new_name),
new_name, std::move(cfg), i == 0);
loaded->save();
this->filaments.load_external_preset(name_or_path, name, old_filament_profile_names->values.front(), config);
else
this->filaments.load_preset(this->filaments.path_from_name(name), name, config).save();
this->filament_presets.clear();
this->filament_presets.emplace_back(name);
} else {
// Split the filament presets, load each of them separately.
std::vector<DynamicPrintConfig> configs(num_extruders, this->filaments.default_preset().config);
// loop through options and scatter them into configs.
for (const t_config_option_key &key : this->filaments.default_preset().config.keys()) {
const ConfigOption *other_opt = config.option(key);
if (other_opt == nullptr)
continue;
if (other_opt->is_scalar()) {
for (size_t i = 0; i < configs.size(); ++ i)
configs[i].option(key, false)->set(other_opt);
} else if (key != "compatible_printers") {
for (size_t i = 0; i < configs.size(); ++ i)
static_cast<ConfigOptionVectorBase*>(configs[i].option(key, false))->set_at(other_opt, 0, i);
}
}
// Load the configs into this->filaments and make them active.
this->filament_presets.clear();
for (size_t i = 0; i < configs.size(); ++ i) {
DynamicPrintConfig &cfg = configs[i];
// Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets.
cfg.opt_string("compatible_printers_condition", true) = compatible_printers_condition_values[i + 1];
cfg.opt_string("inherits", true) = inherits_values[i + 1];
// Load all filament presets, but only select the first one in the preset dialog.
Preset *loaded = nullptr;
if (is_external)
loaded = &this->filaments.load_external_preset(name_or_path, name,
(i < old_filament_profile_names->values.size()) ? old_filament_profile_names->values[i] : "",
std::move(cfg), i == 0);
else {
// Used by the config wizard when creating a custom setup.
// Therefore this block should only be called for a single extruder.
char suffix[64];
if (i == 0)
suffix[0] = 0;
else
sprintf(suffix, "%d", i);
std::string new_name = name + suffix;
loaded = &this->filaments.load_preset(this->filaments.path_from_name(new_name),
new_name, std::move(cfg), i == 0);
loaded->save();
}
this->filament_presets.emplace_back(loaded->name);
}
this->filament_presets.emplace_back(loaded->name);
}
}
// 4) Load the project config values (the per extruder wipe matrix etc).
this->project_config.apply_only(config, s_project_options);
// 4) Load the project config values (the per extruder wipe matrix etc).
this->project_config.apply_only(config, s_project_options);
}
this->update_compatible_with_printer(false);
}
@ -691,9 +787,10 @@ void PresetBundle::load_config_file_config_bundle(const std::string &path, const
collection_dst.load_preset(path, preset_name_dst, std::move(preset_src->config), activate).is_external = true;
return preset_name_dst;
};
load_one(this->prints, tmp_bundle.prints, tmp_bundle.prints .get_selected_preset().name, true);
load_one(this->filaments, tmp_bundle.filaments, tmp_bundle.filaments.get_selected_preset().name, true);
load_one(this->printers, tmp_bundle.printers, tmp_bundle.printers .get_selected_preset().name, true);
load_one(this->prints, tmp_bundle.prints, tmp_bundle.prints .get_selected_preset().name, true);
load_one(this->filaments, tmp_bundle.filaments, tmp_bundle.filaments .get_selected_preset().name, true);
load_one(this->sla_materials, tmp_bundle.sla_materials, tmp_bundle.sla_materials.get_selected_preset().name, true);
load_one(this->printers, tmp_bundle.printers, tmp_bundle.printers .get_selected_preset().name, true);
this->update_multi_material_filament_presets();
for (size_t i = 1; i < std::min(tmp_bundle.filament_presets.size(), this->filament_presets.size()); ++ i)
this->filament_presets[i] = load_one(this->filaments, tmp_bundle.filaments, tmp_bundle.filament_presets[i], false);
@ -817,6 +914,7 @@ static void flatten_configbundle_hierarchy(boost::property_tree::ptree &tree)
{
flatten_configbundle_hierarchy(tree, "print");
flatten_configbundle_hierarchy(tree, "filament");
flatten_configbundle_hierarchy(tree, "sla_material");
flatten_configbundle_hierarchy(tree, "printer");
}
@ -853,9 +951,11 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
// Parse the obsolete preset names, to be deleted when upgrading from the old configuration structure.
std::vector<std::string> loaded_prints;
std::vector<std::string> loaded_filaments;
std::vector<std::string> loaded_sla_materials;
std::vector<std::string> loaded_printers;
std::string active_print;
std::vector<std::string> active_filaments;
std::string active_sla_material;
std::string active_printer;
size_t presets_loaded = 0;
for (const auto &section : tree) {
@ -870,6 +970,10 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
presets = &this->filaments;
loaded = &loaded_filaments;
preset_name = section.first.substr(9);
} else if (boost::starts_with(section.first, "sla_material:")) {
presets = &this->sla_materials;
loaded = &loaded_sla_materials;
preset_name = section.first.substr(9);
} else if (boost::starts_with(section.first, "printer:")) {
presets = &this->printers;
loaded = &loaded_printers;
@ -886,6 +990,8 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
active_filaments.resize(idx + 1, std::string());
active_filaments[idx] = kvp.second.data();
}
} else if (kvp.first == "sla_material") {
active_sla_material = kvp.second.data();
} else if (kvp.first == "printer") {
active_printer = kvp.second.data();
}
@ -899,6 +1005,8 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
dst = &this->obsolete_presets.prints;
else if (kvp.first == "filament")
dst = &this->obsolete_presets.filaments;
else if (kvp.first == "sla_material")
dst = &this->obsolete_presets.sla_materials;
else if (kvp.first == "printer")
dst = &this->obsolete_presets.printers;
if (dst)
@ -999,6 +1107,8 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
if ((flags & LOAD_CFGBNDLE_SYSTEM) == 0) {
if (! active_print.empty())
prints.select_preset_by_name(active_print, true);
if (! active_sla_material.empty())
sla_materials.select_preset_by_name(active_sla_material, true);
if (! active_printer.empty())
printers.select_preset_by_name(active_printer, true);
// Activate the first filament preset.
@ -1015,6 +1125,9 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
void PresetBundle::update_multi_material_filament_presets()
{
if (printers.get_edited_preset().printer_technology() != ptFFF)
return;
// Verify and select the filament presets.
auto *nozzle_diameter = static_cast<const ConfigOptionFloats*>(printers.get_edited_preset().config.option("nozzle_diameter"));
size_t num_extruders = nozzle_diameter->values.size();
@ -1055,36 +1168,51 @@ void PresetBundle::update_multi_material_filament_presets()
void PresetBundle::update_compatible_with_printer(bool select_other_if_incompatible)
{
const Preset &printer_preset = this->printers.get_edited_preset();
const std::string &prefered_print_profile = printer_preset.config.opt_string("default_print_profile");
const std::vector<std::string> &prefered_filament_profiles = printer_preset.config.option<ConfigOptionStrings>("default_filament_profile")->values;
prefered_print_profile.empty() ?
this->prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible) :
this->prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible,
[&prefered_print_profile](const std::string& profile_name){ return profile_name == prefered_print_profile; });
prefered_filament_profiles.empty() ?
this->filaments.update_compatible_with_printer(printer_preset, select_other_if_incompatible) :
this->filaments.update_compatible_with_printer(printer_preset, select_other_if_incompatible,
[&prefered_filament_profiles](const std::string& profile_name)
{ return std::find(prefered_filament_profiles.begin(), prefered_filament_profiles.end(), profile_name) != prefered_filament_profiles.end(); });
if (select_other_if_incompatible) {
// Verify validity of the current filament presets.
this->filament_presets.front() = this->filaments.get_edited_preset().name;
for (size_t idx = 1; idx < this->filament_presets.size(); ++ idx) {
std::string &filament_name = this->filament_presets[idx];
Preset *preset = this->filaments.find_preset(filament_name, false);
if (preset == nullptr || ! preset->is_compatible) {
// Pick a compatible profile. If there are prefered_filament_profiles, use them.
if (prefered_filament_profiles.empty())
filament_name = this->filaments.first_compatible().name;
else {
const std::string &preferred = (idx < prefered_filament_profiles.size()) ?
prefered_filament_profiles[idx] : prefered_filament_profiles.front();
filament_name = this->filaments.first_compatible(
[&preferred](const std::string& profile_name){ return profile_name == preferred; }).name;
switch (printers.get_edited_preset().printer_technology()) {
case ptFFF:
{
const std::string &prefered_print_profile = printer_preset.config.opt_string("default_print_profile");
const std::vector<std::string> &prefered_filament_profiles = printer_preset.config.option<ConfigOptionStrings>("default_filament_profile")->values;
prefered_print_profile.empty() ?
this->prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible) :
this->prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible,
[&prefered_print_profile](const std::string& profile_name){ return profile_name == prefered_print_profile; });
prefered_filament_profiles.empty() ?
this->filaments.update_compatible_with_printer(printer_preset, select_other_if_incompatible) :
this->filaments.update_compatible_with_printer(printer_preset, select_other_if_incompatible,
[&prefered_filament_profiles](const std::string& profile_name)
{ return std::find(prefered_filament_profiles.begin(), prefered_filament_profiles.end(), profile_name) != prefered_filament_profiles.end(); });
if (select_other_if_incompatible) {
// Verify validity of the current filament presets.
this->filament_presets.front() = this->filaments.get_edited_preset().name;
for (size_t idx = 1; idx < this->filament_presets.size(); ++ idx) {
std::string &filament_name = this->filament_presets[idx];
Preset *preset = this->filaments.find_preset(filament_name, false);
if (preset == nullptr || ! preset->is_compatible) {
// Pick a compatible profile. If there are prefered_filament_profiles, use them.
if (prefered_filament_profiles.empty())
filament_name = this->filaments.first_compatible().name;
else {
const std::string &preferred = (idx < prefered_filament_profiles.size()) ?
prefered_filament_profiles[idx] : prefered_filament_profiles.front();
filament_name = this->filaments.first_compatible(
[&preferred](const std::string& profile_name){ return profile_name == preferred; }).name;
}
}
}
}
}
case ptSLA:
{
const std::string &prefered_print_profile = printer_preset.config.opt_string("default_print_profile");
const std::vector<std::string> &prefered_filament_profiles = printer_preset.config.option<ConfigOptionStrings>("default_filament_profile")->values;
prefered_print_profile.empty() ?
this->sla_materials.update_compatible_with_printer(printer_preset, select_other_if_incompatible) :
this->sla_materials.update_compatible_with_printer(printer_preset, select_other_if_incompatible,
[&prefered_print_profile](const std::string& profile_name){ return profile_name == prefered_print_profile; });
}
}
}
void PresetBundle::export_configbundle(const std::string &path) //, const DynamicPrintConfig &settings
@ -1111,6 +1239,7 @@ void PresetBundle::export_configbundle(const std::string &path) //, const Dynami
// Export the names of the active presets.
c << std::endl << "[presets]" << std::endl;
c << "print = " << this->prints.get_selected_preset().name << std::endl;
c << "sla_material = " << this->sla_materials.get_selected_preset().name << std::endl;
c << "printer = " << this->printers.get_selected_preset().name << std::endl;
for (size_t i = 0; i < this->filament_presets.size(); ++ i) {
char suffix[64];
@ -1170,7 +1299,7 @@ bool PresetBundle::parse_color(const std::string &scolor, unsigned char *rgb_out
void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitmapComboBox *ui)
{
if (ui == nullptr)
if (ui == nullptr || this->printers.get_edited_preset().printer_technology() == ptSLA)
return;
unsigned char rgb[3];
@ -1265,6 +1394,7 @@ void PresetBundle::set_default_suppressed(bool default_suppressed)
{
prints.set_default_suppressed(default_suppressed);
filaments.set_default_suppressed(default_suppressed);
sla_materials.set_default_suppressed(default_suppressed);
printers.set_default_suppressed(default_suppressed);
}

View file

@ -40,6 +40,7 @@ public:
PresetCollection prints;
PresetCollection filaments;
PresetCollection sla_materials;
PresetCollection printers;
// Filament preset names for a multi-extruder or multi-material print.
// extruders.size() should be the same as printers.get_edited_preset().config.nozzle_diameter.size()
@ -57,12 +58,13 @@ public:
struct ObsoletePresets {
std::vector<std::string> prints;
std::vector<std::string> filaments;
std::vector<std::string> sla_materials;
std::vector<std::string> printers;
};
ObsoletePresets obsolete_presets;
bool has_defauls_only() const
{ return prints.size() <= 1 && filaments.size() <= 1 && printers.size() <= 1; }
{ return prints.has_defaults_only() && filaments.has_defaults_only() && printers.has_defaults_only(); }
DynamicPrintConfig full_config() const;
@ -146,6 +148,9 @@ private:
void load_config_file_config_bundle(const std::string &path, const boost::property_tree::ptree &tree);
bool load_compatible_bitmaps();
DynamicPrintConfig full_fff_config() const;
DynamicPrintConfig full_sla_config() const;
// Indicator, that the preset is compatible with the selected printer.
wxBitmap *m_bitmapCompatible;
// Indicator, that the preset is NOT compatible with the selected printer.

View file

@ -5,7 +5,7 @@
#include "../../libslic3r/Utils.hpp"
#include "slic3r/Utils/Http.hpp"
#include "slic3r/Utils/OctoPrint.hpp"
#include "slic3r/Utils/PrintHost.hpp"
#include "slic3r/Utils/Serial.hpp"
#include "BonjourDialog.hpp"
#include "WipeTowerDialog.hpp"
@ -214,7 +214,7 @@ void Tab::load_initial_data()
m_tt_non_system = m_presets->get_selected_preset_parent() ? &m_tt_value_unlock : &m_ttg_white_bullet_ns;
}
PageShp Tab::add_options_page(const wxString& title, const std::string& icon, bool is_extruder_pages/* = false*/)
Slic3r::GUI::PageShp Tab::add_options_page(const wxString& title, const std::string& icon, bool is_extruder_pages /*= false*/)
{
// Index of icon in an icon list $self->{icons}.
auto icon_idx = 0;
@ -238,7 +238,8 @@ PageShp Tab::add_options_page(const wxString& title, const std::string& icon, bo
page->SetScrollbars(1, 1, 1, 1);
page->Hide();
m_hsizer->Add(page.get(), 1, wxEXPAND | wxLEFT, 5);
if (!is_extruder_pages)
if (!is_extruder_pages)
m_pages.push_back(page);
page->set_config(m_config);
@ -313,10 +314,10 @@ void Tab::update_changed_ui()
if (m_postpone_update_ui)
return;
const bool is_printer_type = (name() == "printer");
auto dirty_options = m_presets->current_dirty_options(is_printer_type);
auto nonsys_options = m_presets->current_different_from_parent_options(is_printer_type);
if (is_printer_type){
const bool deep_compare = (m_name == "printer" || m_name == "sla_material");
auto dirty_options = m_presets->current_dirty_options(deep_compare);
auto nonsys_options = m_presets->current_different_from_parent_options(deep_compare);
if (name() == "printer"){
TabPrinter* tab = static_cast<TabPrinter*>(this);
if (tab->m_initial_extruders_count != tab->m_extruders_count)
dirty_options.emplace_back("extruders_count");
@ -397,7 +398,7 @@ void Tab::init_options_list()
}
template<class T>
void add_correct_opts_to_options_list(const std::string &opt_key, std::map<std::string, int>& map, TabPrinter *tab, const int& value)
void add_correct_opts_to_options_list(const std::string &opt_key, std::map<std::string, int>& map, Tab *tab, const int& value)
{
T *opt_cur = static_cast<T*>(tab->m_config->option(opt_key));
for (int i = 0; i < opt_cur->values.size(); i++)
@ -429,6 +430,30 @@ void TabPrinter::init_options_list()
m_options_list.emplace("extruders_count", m_opt_status_value);
}
void TabSLAMaterial::init_options_list()
{
if (!m_options_list.empty())
m_options_list.clear();
for (const auto opt_key : m_config->keys())
{
if (opt_key == "compatible_printers"){
m_options_list.emplace(opt_key, m_opt_status_value);
continue;
}
switch (m_config->option(opt_key)->type())
{
case coInts: add_correct_opts_to_options_list<ConfigOptionInts >(opt_key, m_options_list, this, m_opt_status_value); break;
case coBools: add_correct_opts_to_options_list<ConfigOptionBools >(opt_key, m_options_list, this, m_opt_status_value); break;
case coFloats: add_correct_opts_to_options_list<ConfigOptionFloats >(opt_key, m_options_list, this, m_opt_status_value); break;
case coStrings: add_correct_opts_to_options_list<ConfigOptionStrings >(opt_key, m_options_list, this, m_opt_status_value); break;
case coPercents:add_correct_opts_to_options_list<ConfigOptionPercents >(opt_key, m_options_list, this, m_opt_status_value); break;
case coPoints: add_correct_opts_to_options_list<ConfigOptionPoints >(opt_key, m_options_list, this, m_opt_status_value); break;
default: m_options_list.emplace(opt_key, m_opt_status_value); break;
}
}
}
void Tab::get_sys_and_mod_flags(const std::string& opt_key, bool& sys_page, bool& modified_page)
{
auto opt = m_options_list.find(opt_key);
@ -649,12 +674,21 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
int val = boost::any_cast<size_t>(value);
event.SetInt(val);
}
if (opt_key == "printer_technology")
{
int val = boost::any_cast<PrinterTechnology>(value);
event.SetInt(val);
g_wxMainFrame->ProcessWindowEvent(event);
return;
}
g_wxMainFrame->ProcessWindowEvent(event);
}
if (opt_key == "fill_density")
{
boost::any val = get_optgroup()->get_config_value(*m_config, opt_key);
get_optgroup()->set_value(opt_key, val);
boost::any val = get_optgroup(ogFrequentlyChangingParameters)->get_config_value(*m_config, opt_key);
get_optgroup(ogFrequentlyChangingParameters)->set_value(opt_key, val);
}
if (opt_key == "support_material" || opt_key == "support_material_buildplate_only")
{
@ -663,12 +697,12 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
m_config->opt_bool("support_material_buildplate_only") ?
_("Support on build plate only") :
_("Everywhere");
get_optgroup()->set_value("support", new_selection);
get_optgroup(ogFrequentlyChangingParameters)->set_value("support", new_selection);
}
if (opt_key == "brim_width")
{
bool val = m_config->opt_float("brim_width") > 0.0 ? true : false;
get_optgroup()->set_value("brim", val);
get_optgroup(ogFrequentlyChangingParameters)->set_value("brim", val);
}
if (opt_key == "wipe_tower" || opt_key == "single_extruder_multi_material" || opt_key == "extruders_count" )
@ -677,17 +711,16 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
update();
}
// Show/hide the 'purging volumes' button
void Tab::update_wiping_button_visibility() {
if (get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA)
return; // ys_FIXME
bool wipe_tower_enabled = dynamic_cast<ConfigOptionBool*>( (m_preset_bundle->prints.get_edited_preset().config ).option("wipe_tower"))->value;
bool multiple_extruders = dynamic_cast<ConfigOptionFloats*>((m_preset_bundle->printers.get_edited_preset().config).option("nozzle_diameter"))->values.size() > 1;
bool single_extruder_mm = dynamic_cast<ConfigOptionBool*>( (m_preset_bundle->printers.get_edited_preset().config).option("single_extruder_multi_material"))->value;
if (wipe_tower_enabled && multiple_extruders && single_extruder_mm)
get_wiping_dialog_button()->Show();
else get_wiping_dialog_button()->Hide();
get_wiping_dialog_button()->Show(wipe_tower_enabled && multiple_extruders && single_extruder_mm);
(get_wiping_dialog_button()->GetParent())->Layout();
}
@ -754,18 +787,18 @@ void Tab::update_preset_description_line()
void Tab::update_frequently_changed_parameters()
{
boost::any value = get_optgroup()->get_config_value(*m_config, "fill_density");
get_optgroup()->set_value("fill_density", value);
boost::any value = get_optgroup(ogFrequentlyChangingParameters)->get_config_value(*m_config, "fill_density");
get_optgroup(ogFrequentlyChangingParameters)->set_value("fill_density", value);
wxString new_selection = !m_config->opt_bool("support_material") ?
_("None") :
m_config->opt_bool("support_material_buildplate_only") ?
_("Support on build plate only") :
_("Everywhere");
get_optgroup()->set_value("support", new_selection);
get_optgroup(ogFrequentlyChangingParameters)->set_value("support", new_selection);
bool val = m_config->opt_float("brim_width") > 0.0 ? true : false;
get_optgroup()->set_value("brim", val);
get_optgroup(ogFrequentlyChangingParameters)->set_value("brim", val);
update_wiping_button_visibility();
}
@ -1007,6 +1040,9 @@ void TabPrint::reload_config(){
void TabPrint::update()
{
if (get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA)
return; // ys_FIXME
Freeze();
double fill_density = m_config->option<ConfigOptionPercent>("fill_density")->value;
@ -1366,6 +1402,9 @@ void TabFilament::reload_config(){
void TabFilament::update()
{
if (get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA)
return; // ys_FIXME
Freeze();
wxString text = from_u8(PresetHints::cooling_description(m_presets->get_edited_preset()));
m_cooling_description_line->SetText(text);
@ -1411,6 +1450,17 @@ void TabPrinter::build()
m_presets = &m_preset_bundle->printers;
load_initial_data();
m_printer_technology = m_presets->get_selected_preset().printer_technology();
m_presets->get_selected_preset().printer_technology() == ptSLA ? build_sla() : build_fff();
// on_value_change("printer_technology", m_printer_technology); // to update show/hide preset ComboBoxes
}
void TabPrinter::build_fff()
{
if (!m_pages.empty())
m_pages.resize(0);
// to avoid redundant memory allocation / deallocation during extruders count changing
m_pages.reserve(30);
@ -1426,7 +1476,7 @@ void TabPrinter::build()
Line line{ _(L("Bed shape")), "" };
line.widget = [this](wxWindow* parent){
auto btn = new wxButton(parent, wxID_ANY, _(L(" Set "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
// btn->SetFont(Slic3r::GUI::small_font);
btn->SetFont(Slic3r::GUI::small_font());
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("printer_empty.png")), wxBITMAP_TYPE_PNG));
auto sizer = new wxBoxSizer(wxHORIZONTAL);
@ -1494,7 +1544,7 @@ void TabPrinter::build()
auto serial_test = [this](wxWindow* parent){
auto btn = m_serial_test_btn = new wxButton(parent, wxID_ANY,
_(L("Test")), wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
// btn->SetFont($Slic3r::GUI::small_font);
btn->SetFont(Slic3r::GUI::small_font());
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG));
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn);
@ -1521,10 +1571,12 @@ void TabPrinter::build()
optgroup->append_line(line);
}
optgroup = page->new_optgroup(_(L("OctoPrint upload")));
optgroup = page->new_optgroup(_(L("Printer Host upload")));
auto octoprint_host_browse = [this, optgroup] (wxWindow* parent) {
auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
optgroup->append_single_option_line("host_type");
auto printhost_browse = [this, optgroup] (wxWindow* parent) {
auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG));
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn);
@ -1532,47 +1584,50 @@ void TabPrinter::build()
btn->Bind(wxEVT_BUTTON, [this, parent, optgroup](wxCommandEvent e) {
BonjourDialog dialog(parent);
if (dialog.show_and_lookup()) {
optgroup->set_value("octoprint_host", std::move(dialog.get_selected()), true);
optgroup->set_value("print_host", std::move(dialog.get_selected()), true);
}
});
return sizer;
};
auto octoprint_host_test = [this](wxWindow* parent) {
auto btn = m_octoprint_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")),
auto print_host_test = [this](wxWindow* parent) {
auto btn = m_print_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")),
wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG));
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn);
btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) {
OctoPrint octoprint(m_config);
wxString msg;
if (octoprint.test(msg)) {
show_info(this, _(L("Connection to OctoPrint works correctly.")), _(L("Success!")));
} else {
const auto text = wxString::Format("%s: %s\n\n%s",
_(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required."))
);
std::unique_ptr<PrintHost> host(PrintHost::get_print_host(m_config));
if (! host) {
const auto text = wxString::Format("%s",
_(L("Could not get a valid Printer Host reference")));
show_error(this, text);
return;
}
wxString msg;
if (host->test(msg)) {
show_info(this, host->get_test_ok_msg(), _(L("Success!")));
} else {
show_error(this, host->get_test_failed_msg(msg));
}
});
return sizer;
};
Line host_line = optgroup->create_single_option_line("octoprint_host");
host_line.append_widget(octoprint_host_browse);
host_line.append_widget(octoprint_host_test);
Line host_line = optgroup->create_single_option_line("print_host");
host_line.append_widget(printhost_browse);
host_line.append_widget(print_host_test);
optgroup->append_line(host_line);
optgroup->append_single_option_line("octoprint_apikey");
optgroup->append_single_option_line("printhost_apikey");
if (Http::ca_file_supported()) {
Line cafile_line = optgroup->create_single_option_line("octoprint_cafile");
Line cafile_line = optgroup->create_single_option_line("printhost_cafile");
auto octoprint_cafile_browse = [this, optgroup] (wxWindow* parent) {
auto printhost_cafile_browse = [this, optgroup] (wxWindow* parent) {
auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG));
auto sizer = new wxBoxSizer(wxHORIZONTAL);
@ -1582,17 +1637,17 @@ void TabPrinter::build()
static const auto filemasks = _(L("Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*"));
wxFileDialog openFileDialog(this, _(L("Open CA certificate file")), "", "", filemasks, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (openFileDialog.ShowModal() != wxID_CANCEL) {
optgroup->set_value("octoprint_cafile", std::move(openFileDialog.GetPath()), true);
optgroup->set_value("printhost_cafile", std::move(openFileDialog.GetPath()), true);
}
});
return sizer;
};
cafile_line.append_widget(octoprint_cafile_browse);
cafile_line.append_widget(printhost_cafile_browse);
optgroup->append_line(cafile_line);
auto octoprint_cafile_hint = [this, optgroup] (wxWindow* parent) {
auto printhost_cafile_hint = [this, optgroup] (wxWindow* parent) {
auto txt = new wxStaticText(parent, wxID_ANY,
_(L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate.")));
auto sizer = new wxBoxSizer(wxHORIZONTAL);
@ -1602,7 +1657,7 @@ void TabPrinter::build()
Line cafile_hint { "", "" };
cafile_hint.full_width = 1;
cafile_hint.widget = std::move(octoprint_cafile_hint);
cafile_hint.widget = std::move(printhost_cafile_hint);
optgroup->append_line(cafile_hint);
}
@ -1692,6 +1747,76 @@ void TabPrinter::build()
update_serial_ports();
}
void TabPrinter::build_sla()
{
if (!m_pages.empty())
m_pages.resize(0);
auto page = add_options_page(_(L("General")), "printer_empty.png");
auto optgroup = page->new_optgroup(_(L("Size and coordinates")));
Line line{ _(L("Bed shape")), "" };
line.widget = [this](wxWindow* parent){
auto btn = new wxButton(parent, wxID_ANY, _(L(" Set ")) + dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
// btn->SetFont(Slic3r::GUI::small_font);
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("printer_empty.png")), wxBITMAP_TYPE_PNG));
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn);
btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e)
{
auto dlg = new BedShapeDialog(this);
dlg->build_dialog(m_config->option<ConfigOptionPoints>("bed_shape"));
if (dlg->ShowModal() == wxID_OK){
load_key_value("bed_shape", dlg->GetValue());
update_changed_ui();
}
}));
return sizer;
};
optgroup->append_line(line, &m_colored_Label);
optgroup->append_single_option_line("max_print_height");
optgroup = page->new_optgroup(_(L("Display")));
optgroup->append_single_option_line("display_width");
optgroup->append_single_option_line("display_height");
auto option = optgroup->get_option("display_pixels_x");
line = { _(option.opt.full_label), "" };
line.append_option(option);
line.append_option(optgroup->get_option("display_pixels_y"));
optgroup->append_line(line);
optgroup = page->new_optgroup(_(L("Corrections")));
line = Line{ m_config->def()->get("printer_correction")->full_label, "" };
std::vector<std::string> axes{ "X", "Y", "Z" };
int id = 0;
for (auto& axis : axes) {
auto opt = optgroup->get_option("printer_correction", id);
opt.opt.label = axis;
line.append_option(opt);
++id;
}
optgroup->append_line(line);
page = add_options_page(_(L("Notes")), "note.png");
optgroup = page->new_optgroup(_(L("Notes")), 0);
option = optgroup->get_option("printer_notes");
option.opt.full_width = true;
option.opt.height = 250;
optgroup->append_single_option_line(option);
page = add_options_page(_(L("Dependencies")), "wrench.png");
optgroup = page->new_optgroup(_(L("Profile dependencies")));
line = Line{ "", "" };
line.full_width = 1;
line.widget = [this](wxWindow* parent) {
return description_line_widget(parent, &m_parent_preset_description_line);
};
optgroup->append_line(line);
}
void TabPrinter::update_serial_ports(){
Field *field = get_field("serial_port");
Choice *choice = static_cast<Choice *>(field);
@ -1705,6 +1830,7 @@ void TabPrinter::extruders_count_changed(size_t extruders_count){
build_extruder_pages();
reload_config();
on_value_change("extruders_count", extruders_count);
update_objects_list_extruder_column(extruders_count);
}
void TabPrinter::append_option_line(ConfigOptionsGroupShp optgroup, const std::string opt_key)
@ -1883,7 +2009,38 @@ void TabPrinter::on_preset_loaded()
extruders_count_changed(extruders_count);
}
void TabPrinter::update(){
void TabPrinter::update_pages()
{
// update m_pages ONLY if printer technology is changed
if (m_presets->get_edited_preset().printer_technology() == m_printer_technology)
return;
// hide all old pages
for (auto& el : m_pages)
el.get()->Hide();
// set m_pages to m_pages_(technology before changing)
m_printer_technology == ptFFF ? m_pages.swap(m_pages_fff) : m_pages.swap(m_pages_sla);
// build Tab according to the technology, if it's not exist jet OR
// set m_pages_(technology after changing) to m_pages
if (m_presets->get_edited_preset().printer_technology() == ptFFF)
m_pages_fff.empty() ? build_fff() : m_pages.swap(m_pages_fff);
else
m_pages_sla.empty() ? build_sla() : m_pages.swap(m_pages_sla);
rebuild_page_tree(true);
on_value_change("printer_technology", m_presets->get_edited_preset().printer_technology()); // to update show/hide preset ComboBoxes
}
void TabPrinter::update()
{
m_presets->get_edited_preset().printer_technology() == ptFFF ? update_fff() : update_sla();
}
void TabPrinter::update_fff()
{
Freeze();
bool en;
@ -1897,8 +2054,12 @@ void TabPrinter::update(){
m_serial_test_btn->Disable();
}
m_octoprint_host_test_btn->Enable(!m_config->opt_string("octoprint_host").empty());
{
std::unique_ptr<PrintHost> host(PrintHost::get_print_host(m_config));
m_print_host_test_btn->Enable(!m_config->opt_string("print_host").empty() && host->can_test());
m_printhost_browse_btn->Enable(host->has_auto_discovery());
}
bool have_multiple_extruders = m_extruders_count > 1;
get_field("toolchange_gcode")->toggle(have_multiple_extruders);
get_field("single_extruder_multi_material")->toggle(have_multiple_extruders);
@ -1982,20 +2143,25 @@ void TabPrinter::update(){
Thaw();
}
void TabPrinter::update_sla(){ ; }
// Initialize the UI from the current preset
void Tab::load_current_preset()
{
auto preset = m_presets->get_edited_preset();
(preset.is_default || preset.is_system) ? m_btn_delete_preset->Disable() : m_btn_delete_preset->Enable(true);
update();
// For the printer profile, generate the extruder pages.
on_preset_loaded();
// Reload preset pages with the new configuration values.
reload_config();
update();
// For the printer profile, generate the extruder pages.
if (preset.printer_technology() == ptFFF)
on_preset_loaded();
// Reload preset pages with the new configuration values.
reload_config();
m_bmp_non_system = m_presets->get_selected_preset_parent() ? &m_bmp_value_unlock : &m_bmp_white_bullet;
m_ttg_non_system = m_presets->get_selected_preset_parent() ? &m_ttg_value_unlock : &m_ttg_white_bullet_ns;
m_tt_non_system = m_presets->get_selected_preset_parent() ? &m_tt_value_unlock : &m_ttg_white_bullet_ns;
m_tt_non_system = m_presets->get_selected_preset_parent() ? &m_tt_value_unlock : &m_ttg_white_bullet_ns;
m_undo_to_sys_btn->Enable(!preset.is_default);
@ -2008,6 +2174,27 @@ void Tab::load_current_preset()
if (!checked_tab(this))
return;
update_tab_ui();
// update show/hide tabs
if (m_name == "printer"){
PrinterTechnology& printer_technology = m_presets->get_edited_preset().printer_technology();
if (printer_technology != static_cast<TabPrinter*>(this)->m_printer_technology)
{
for (auto& tab : get_preset_tabs()){
if (tab.technology != printer_technology)
{
int page_id = get_tab_panel()->FindPage(tab.panel);
get_tab_panel()->GetPage(page_id)->Show(false);
get_tab_panel()->RemovePage(page_id);
}
else
get_tab_panel()->InsertPage(get_tab_panel()->FindPage(this), tab.panel, tab.panel->title());
}
static_cast<TabPrinter*>(this)->m_printer_technology = printer_technology;
}
}
on_presets_changed();
if (name() == "print")
@ -2025,7 +2212,7 @@ void Tab::load_current_preset()
}
//Regerenerate content of the page tree.
void Tab::rebuild_page_tree()
void Tab::rebuild_page_tree(bool tree_sel_change_event /*= false*/)
{
Freeze();
// get label of the currently selected item
@ -2040,9 +2227,9 @@ void Tab::rebuild_page_tree()
m_treectrl->SetItemTextColour(itemId, p->get_item_colour());
if (p->title() == selected) {
if (!(p->title() == _(L("Machine limits")) || p->title() == _(L("Single extruder MM setup")))) // These Pages have to be updated inside OnTreeSelChange
m_disable_tree_sel_changed_event = 1;
m_disable_tree_sel_changed_event = !tree_sel_change_event;
m_treectrl->SelectItem(itemId);
m_disable_tree_sel_changed_event = 0;
m_disable_tree_sel_changed_event = false;
have_selection = 1;
}
}
@ -2057,48 +2244,53 @@ void Tab::rebuild_page_tree()
// Called by the UI combo box when the user switches profiles.
// Select a preset by a name.If !defined(name), then the default preset is selected.
// If the current profile is modified, user is asked to save the changes.
void Tab::select_preset(const std::string& preset_name /*= ""*/)
void Tab::select_preset(std::string preset_name /*= ""*/)
{
std::string name = preset_name;
auto force = false;
auto presets = m_presets;
// If no name is provided, select the "-- default --" preset.
if (name.empty())
name= presets->default_preset().name;
auto current_dirty = presets->current_is_dirty();
auto canceled = false;
auto printer_tab = presets->name().compare("printer")==0;
if (preset_name.empty())
preset_name = m_presets->default_preset().name;
auto current_dirty = m_presets->current_is_dirty();
auto printer_tab = m_presets->name() == "printer";
auto canceled = false;
m_reload_dependent_tabs = {};
if (!force && current_dirty && !may_discard_current_dirty_preset()) {
if (current_dirty && !may_discard_current_dirty_preset()) {
canceled = true;
} else if(printer_tab) {
} else if (printer_tab) {
// Before switching the printer to a new one, verify, whether the currently active print and filament
// are compatible with the new printer.
// If they are not compatible and the current print or filament are dirty, let user decide
// whether to discard the changes or keep the current printer selection.
auto new_printer_preset = presets->find_preset(name, true);
auto print_presets = &m_preset_bundle->prints;
bool print_preset_dirty = print_presets->current_is_dirty();
bool print_preset_compatible = print_presets->get_edited_preset().is_compatible_with_printer(*new_printer_preset);
canceled = !force && print_preset_dirty && !print_preset_compatible &&
!may_discard_current_dirty_preset(print_presets, name);
auto filament_presets = &m_preset_bundle->filaments;
bool filament_preset_dirty = filament_presets->current_is_dirty();
bool filament_preset_compatible = filament_presets->get_edited_preset().is_compatible_with_printer(*new_printer_preset);
if (!canceled && !force) {
canceled = filament_preset_dirty && !filament_preset_compatible &&
!may_discard_current_dirty_preset(filament_presets, name);
//
// With the introduction of the SLA printer types, we need to support switching between
// the FFF and SLA printers.
const Preset &new_printer_preset = *m_presets->find_preset(preset_name, true);
PrinterTechnology old_printer_technology = m_presets->get_edited_preset().printer_technology();
PrinterTechnology new_printer_technology = new_printer_preset.printer_technology();
struct PresetUpdate {
std::string name;
PresetCollection *presets;
PrinterTechnology technology;
bool old_preset_dirty;
bool new_preset_compatible;
};
std::vector<PresetUpdate> updates = {
{ "print", &m_preset_bundle->prints, ptFFF },
{ "filament", &m_preset_bundle->filaments, ptFFF },
{ "sla_material", &m_preset_bundle->sla_materials, ptSLA }
};
for (PresetUpdate &pu : updates) {
pu.old_preset_dirty = (old_printer_technology == pu.technology) && pu.presets->current_is_dirty();
pu.new_preset_compatible = (new_printer_technology == pu.technology) && pu.presets->get_edited_preset().is_compatible_with_printer(new_printer_preset);
if (! canceled)
canceled = pu.old_preset_dirty && ! pu.new_preset_compatible && ! may_discard_current_dirty_preset(pu.presets, preset_name);
}
if (!canceled) {
if (!print_preset_compatible) {
if (! canceled) {
for (PresetUpdate &pu : updates) {
// The preset will be switched to a different, compatible preset, or the '-- default --'.
m_reload_dependent_tabs.push_back("print");
if (print_preset_dirty) print_presets->discard_current_changes();
}
if (!filament_preset_compatible) {
// The preset will be switched to a different, compatible preset, or the '-- default --'.
m_reload_dependent_tabs.push_back("filament");
if (filament_preset_dirty) filament_presets->discard_current_changes();
if (pu.technology == new_printer_technology)
m_reload_dependent_tabs.emplace_back(pu.name);
if (pu.old_preset_dirty)
pu.presets->discard_current_changes();
}
}
}
@ -2107,19 +2299,20 @@ void Tab::select_preset(const std::string& preset_name /*= ""*/)
// Trigger the on_presets_changed event so that we also restore the previous value in the plater selector,
// if this action was initiated from the platter.
on_presets_changed();
}
else {
if (current_dirty) presets->discard_current_changes() ;
presets->select_preset_by_name(name, force);
} else {
if (current_dirty)
m_presets->discard_current_changes() ;
m_presets->select_preset_by_name(preset_name, false);
// Mark the print & filament enabled if they are compatible with the currently selected preset.
// The following method should not discard changes of current print or filament presets on change of a printer profile,
// if they are compatible with the current printer.
if (current_dirty || printer_tab)
m_preset_bundle->update_compatible_with_printer(true);
// Initialize the UI from the current preset.
if (printer_tab)
static_cast<TabPrinter*>(this)->update_pages();
load_current_preset();
}
}
// If the current preset is dirty, the user is asked whether the changes may be discarded.
@ -2761,5 +2954,71 @@ void SavePresetWindow::accept()
}
}
void TabSLAMaterial::build()
{
m_presets = &m_preset_bundle->sla_materials;
load_initial_data();
auto page = add_options_page(_(L("Material")), "package_green.png");
auto optgroup = page->new_optgroup(_(L("Layers")));
optgroup->append_single_option_line("layer_height");
optgroup->append_single_option_line("initial_layer_height");
optgroup = page->new_optgroup(_(L("Exposure")));
optgroup->append_single_option_line("exposure_time");
optgroup->append_single_option_line("initial_exposure_time");
optgroup = page->new_optgroup(_(L("Corrections")));
optgroup->label_width = 190;
std::vector<std::string> corrections = { "material_correction_printing", "material_correction_curing" };
std::vector<std::string> axes{ "X", "Y", "Z" };
for (auto& opt_key : corrections){
auto line = Line{ m_config->def()->get(opt_key)->full_label, "" };
int id = 0;
for (auto& axis : axes) {
auto opt = optgroup->get_option(opt_key, id);
opt.opt.label = axis;
opt.opt.width = 60;
line.append_option(opt);
++id;
}
optgroup->append_line(line);
}
page = add_options_page(_(L("Notes")), "note.png");
optgroup = page->new_optgroup(_(L("Notes")), 0);
optgroup->label_width = 0;
Option option = optgroup->get_option("material_notes");
option.opt.full_width = true;
option.opt.height = 250;
optgroup->append_single_option_line(option);
page = add_options_page(_(L("Dependencies")), "wrench.png");
optgroup = page->new_optgroup(_(L("Profile dependencies")));
auto line = Line { _(L("Compatible printers")), "" };
line.widget = [this](wxWindow* parent){
return compatible_printers_widget(parent, &m_compatible_printers_checkbox, &m_compatible_printers_btn);
};
optgroup->append_line(line, &m_colored_Label);
option = optgroup->get_option("compatible_printers_condition");
option.opt.full_width = true;
optgroup->append_single_option_line(option);
line = Line{ "", "" };
line.full_width = 1;
line.widget = [this](wxWindow* parent) {
return description_line_widget(parent, &m_parent_preset_description_line);
};
optgroup->append_line(line);
}
void TabSLAMaterial::update()
{
if (get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptFFF)
return; // ys_FIXME
}
} // GUI
} // Slic3r

View file

@ -218,8 +218,8 @@ public:
void create_preset_tab(PresetBundle *preset_bundle);
void load_current_preset();
void rebuild_page_tree();
void select_preset(const std::string& preset_name = "");
void rebuild_page_tree(bool tree_sel_change_event = false);
void select_preset(std::string preset_name = "");
bool may_discard_current_dirty_preset(PresetCollection* presets = nullptr, const std::string& new_printer_name = "");
wxSizer* compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox, wxButton** btn);
@ -267,11 +267,11 @@ public:
void on_value_change(const std::string& opt_key, const boost::any& value);
void update_wiping_button_visibility();
protected:
void on_presets_changed();
void update_preset_description_line();
void update_frequently_changed_parameters();
void update_wiping_button_visibility();
void update_tab_presets(wxComboCtrl* ui, bool show_incompatible);
void fill_icon_descriptions();
void set_tooltips_text();
@ -319,21 +319,32 @@ class TabPrinter : public Tab
bool m_use_silent_mode = false;
void append_option_line(ConfigOptionsGroupShp optgroup, const std::string opt_key);
bool m_rebuild_kinematics_page = false;
std::vector<PageShp> m_pages_fff;
std::vector<PageShp> m_pages_sla;
public:
wxButton* m_serial_test_btn;
wxButton* m_octoprint_host_test_btn;
wxButton* m_print_host_test_btn;
wxButton* m_printhost_browse_btn;
size_t m_extruders_count;
size_t m_extruders_count_old = 0;
size_t m_initial_extruders_count;
size_t m_sys_extruders_count;
PrinterTechnology m_printer_technology = ptFFF;
TabPrinter() {}
TabPrinter(wxNotebook* parent, bool no_controller) : Tab(parent, _(L("Printer Settings")), "printer", no_controller) {}
~TabPrinter(){}
void build() override;
void update() override;
void build_fff();
void build_sla();
void update() override;
void update_fff();
void update_sla();
void update_pages(); // update m_pages according to printer technology
void update_serial_ports();
void extruders_count_changed(size_t extruders_count);
PageShp build_kinematics_page();
@ -342,6 +353,19 @@ public:
void init_options_list() override;
};
class TabSLAMaterial : public Tab
{
public:
TabSLAMaterial() {}
TabSLAMaterial(wxNotebook* parent, bool no_controller) :
Tab(parent, _(L("SLA Material Settings")), "sla_material", no_controller) {}
~TabSLAMaterial(){}
void build() override;
void update() override;
void init_options_list() override;
};
class SavePresetWindow :public wxDialog
{
public:

File diff suppressed because it is too large Load diff

View file

@ -4,6 +4,14 @@
#include <wx/checklst.h>
#include <wx/combo.h>
#include <wx/dataview.h>
#include <wx/dc.h>
#include <wx/collpane.h>
#include <wx/wupdlock.h>
#include <wx/button.h>
#include <wx/slider.h>
#include <vector>
#include <set>
class wxCheckListBoxComboPopup : public wxCheckListBox, public wxComboPopup
{
@ -65,4 +73,568 @@ public:
void SetItemsCnt(int cnt) { m_cnt_open_items = cnt; }
};
// *** PrusaCollapsiblePane ***
// ----------------------------------------------------------------------------
class PrusaCollapsiblePane : public wxCollapsiblePane
{
public:
PrusaCollapsiblePane() {}
PrusaCollapsiblePane(wxWindow *parent,
wxWindowID winid,
const wxString& label,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = wxCP_DEFAULT_STYLE,
const wxValidator& val = wxDefaultValidator,
const wxString& name = wxCollapsiblePaneNameStr)
{
Create(parent, winid, label, pos, size, style, val, name);
}
~PrusaCollapsiblePane() {}
void OnStateChange(const wxSize& sz); //override/hide of OnStateChange from wxCollapsiblePane
virtual bool Show(bool show = true) override {
wxCollapsiblePane::Show(show);
OnStateChange(GetBestSize());
return true;
}
};
// *** PrusaCollapsiblePaneMSW *** used only #ifdef __WXMSW__
// ----------------------------------------------------------------------------
#ifdef __WXMSW__
class PrusaCollapsiblePaneMSW : public PrusaCollapsiblePane//wxCollapsiblePane
{
wxButton* m_pDisclosureTriangleButton = nullptr;
wxBitmap m_bmp_close;
wxBitmap m_bmp_open;
public:
PrusaCollapsiblePaneMSW() {}
PrusaCollapsiblePaneMSW( wxWindow *parent,
wxWindowID winid,
const wxString& label,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = wxCP_DEFAULT_STYLE,
const wxValidator& val = wxDefaultValidator,
const wxString& name = wxCollapsiblePaneNameStr)
{
Create(parent, winid, label, pos, size, style, val, name);
}
~PrusaCollapsiblePaneMSW() {}
bool Create(wxWindow *parent,
wxWindowID id,
const wxString& label,
const wxPoint& pos,
const wxSize& size,
long style,
const wxValidator& val,
const wxString& name);
void UpdateBtnBmp();
void SetLabel(const wxString &label) override;
bool Layout() override;
void Collapse(bool collapse) override;
};
#endif //__WXMSW__
// *****************************************************************************
// ----------------------------------------------------------------------------
// PrusaObjectDataViewModelNode: a node inside PrusaObjectDataViewModel
// ----------------------------------------------------------------------------
class PrusaObjectDataViewModelNode;
WX_DEFINE_ARRAY_PTR(PrusaObjectDataViewModelNode*, MyObjectTreeModelNodePtrArray);
class PrusaObjectDataViewModelNode
{
PrusaObjectDataViewModelNode* m_parent;
MyObjectTreeModelNodePtrArray m_children;
wxIcon m_empty_icon;
public:
PrusaObjectDataViewModelNode(const wxString &name, int instances_count=1, int scale=100) {
m_parent = NULL;
m_name = name;
m_copy = wxString::Format("%d", instances_count);
m_scale = wxString::Format("%d%%", scale);
m_type = "object";
m_volume_id = -1;
#ifdef __WXGTK__
// it's necessary on GTK because of control have to know if this item will be container
// in another case you couldn't to add subitem for this item
// it will be produce "segmentation fault"
m_container = true;
#endif //__WXGTK__
set_object_action_icon();
}
PrusaObjectDataViewModelNode( PrusaObjectDataViewModelNode* parent,
const wxString& sub_obj_name,
const wxIcon& icon,
const wxString& extruder,
const int volume_id=-1) {
m_parent = parent;
m_name = sub_obj_name;
m_copy = wxEmptyString;
m_scale = wxEmptyString;
m_icon = icon;
m_type = "volume";
m_volume_id = volume_id;
m_extruder = extruder;
set_part_action_icon();
}
~PrusaObjectDataViewModelNode()
{
// free all our children nodes
size_t count = m_children.GetCount();
for (size_t i = 0; i < count; i++)
{
PrusaObjectDataViewModelNode *child = m_children[i];
delete child;
}
}
wxString m_name;
wxIcon& m_icon = m_empty_icon;
wxString m_copy;
wxString m_scale;
std::string m_type;
int m_volume_id;
bool m_container = false;
wxString m_extruder = "default";
wxBitmap m_action_icon;
bool IsContainer() const
{
return m_container;
}
PrusaObjectDataViewModelNode* GetParent()
{
return m_parent;
}
MyObjectTreeModelNodePtrArray& GetChildren()
{
return m_children;
}
PrusaObjectDataViewModelNode* GetNthChild(unsigned int n)
{
return m_children.Item(n);
}
void Insert(PrusaObjectDataViewModelNode* child, unsigned int n)
{
m_children.Insert(child, n);
}
void Append(PrusaObjectDataViewModelNode* child)
{
if (!m_container)
m_container = true;
m_children.Add(child);
}
void RemoveAllChildren()
{
if (GetChildCount() == 0)
return;
for (size_t id = GetChildCount() - 1; id >= 0; --id)
{
if (m_children.Item(id)->GetChildCount() > 0)
m_children[id]->RemoveAllChildren();
auto node = m_children[id];
m_children.RemoveAt(id);
delete node;
}
}
size_t GetChildCount() const
{
return m_children.GetCount();
}
bool SetValue(const wxVariant &variant, unsigned int col)
{
switch (col)
{
case 0:{
wxDataViewIconText data;
data << variant;
m_icon = data.GetIcon();
m_name = data.GetText();
return true;}
case 1:
m_copy = variant.GetString();
return true;
case 2:
m_scale = variant.GetString();
return true;
case 3:
m_extruder = variant.GetString();
return true;
case 4:
m_action_icon << variant;
return true;
default:
printf("MyObjectTreeModel::SetValue: wrong column");
}
return false;
}
void SetIcon(const wxIcon &icon)
{
m_icon = icon;
}
void SetType(const std::string& type){
m_type = type;
}
const std::string& GetType(){
return m_type;
}
void SetVolumeId(const int& volume_id){
m_volume_id = volume_id;
}
const int& GetVolumeId(){
return m_volume_id;
}
// use this function only for childrens
void AssignAllVal(PrusaObjectDataViewModelNode& from_node)
{
// ! Don't overwrite other values because of equality of this values for all children --
m_name = from_node.m_name;
m_icon = from_node.m_icon;
m_volume_id = from_node.m_volume_id;
m_extruder = from_node.m_extruder;
}
bool SwapChildrens(int frst_id, int scnd_id) {
if (GetChildCount() < 2 ||
frst_id < 0 || frst_id >= GetChildCount() ||
scnd_id < 0 || scnd_id >= GetChildCount())
return false;
PrusaObjectDataViewModelNode new_scnd = *GetNthChild(frst_id);
PrusaObjectDataViewModelNode new_frst = *GetNthChild(scnd_id);
new_scnd.m_volume_id = m_children.Item(scnd_id)->m_volume_id;
new_frst.m_volume_id = m_children.Item(frst_id)->m_volume_id;
m_children.Item(frst_id)->AssignAllVal(new_frst);
m_children.Item(scnd_id)->AssignAllVal(new_scnd);
return true;
}
// Set action icons for node
void set_object_action_icon();
void set_part_action_icon();
};
// ----------------------------------------------------------------------------
// PrusaObjectDataViewModel
// ----------------------------------------------------------------------------
class PrusaObjectDataViewModel :public wxDataViewModel
{
std::vector<PrusaObjectDataViewModelNode*> m_objects;
public:
PrusaObjectDataViewModel(){}
~PrusaObjectDataViewModel()
{
for (auto object : m_objects)
delete object;
}
wxDataViewItem Add(wxString &name);
wxDataViewItem Add(wxString &name, int instances_count, int scale);
wxDataViewItem AddChild(const wxDataViewItem &parent_item,
const wxString &name,
const wxIcon& icon,
const int = 0,
const bool create_frst_child = true);
wxDataViewItem Delete(const wxDataViewItem &item);
void DeleteAll();
void DeleteChildren(wxDataViewItem& parent);
wxDataViewItem GetItemById(int obj_idx);
int GetIdByItem(wxDataViewItem& item);
int GetVolumeIdByItem(wxDataViewItem& item);
bool IsEmpty() { return m_objects.empty(); }
// helper method for wxLog
wxString GetName(const wxDataViewItem &item) const;
wxString GetCopy(const wxDataViewItem &item) const;
wxString GetScale(const wxDataViewItem &item) const;
wxIcon& GetIcon(const wxDataViewItem &item) const;
// helper methods to change the model
virtual unsigned int GetColumnCount() const override { return 3;}
virtual wxString GetColumnType(unsigned int col) const override{ return wxT("string"); }
virtual void GetValue(wxVariant &variant,
const wxDataViewItem &item, unsigned int col) const override;
virtual bool SetValue(const wxVariant &variant,
const wxDataViewItem &item, unsigned int col) override;
bool SetValue(const wxVariant &variant, const int item_idx, unsigned int col);
wxDataViewItem MoveChildUp(const wxDataViewItem &item);
wxDataViewItem MoveChildDown(const wxDataViewItem &item);
// For parent move child from cur_volume_id place to new_volume_id
// Remaining items will moved up/down accordingly
wxDataViewItem ReorganizeChildren(int cur_volume_id,
int new_volume_id,
const wxDataViewItem &parent);
// virtual bool IsEnabled(const wxDataViewItem &item,
// unsigned int col) const override;
virtual wxDataViewItem GetParent(const wxDataViewItem &item) const override;
virtual bool IsContainer(const wxDataViewItem &item) const override;
virtual unsigned int GetChildren(const wxDataViewItem &parent,
wxDataViewItemArray &array) const override;
// Is the container just a header or an item with all columns
// In our case it is an item with all columns
virtual bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; }
};
// ----------------------------------------------------------------------------
// MyCustomRenderer
// ----------------------------------------------------------------------------
class MyCustomRenderer : public wxDataViewCustomRenderer
{
public:
// This renderer can be either activatable or editable, for demonstration
// purposes. In real programs, you should select whether the user should be
// able to activate or edit the cell and it doesn't make sense to switch
// between the two -- but this is just an example, so it doesn't stop us.
explicit MyCustomRenderer(wxDataViewCellMode mode)
: wxDataViewCustomRenderer("string", mode, wxALIGN_CENTER)
{ }
virtual bool Render(wxRect rect, wxDC *dc, int state) override/*wxOVERRIDE*/
{
dc->SetBrush(*wxLIGHT_GREY_BRUSH);
dc->SetPen(*wxTRANSPARENT_PEN);
rect.Deflate(2);
dc->DrawRoundedRectangle(rect, 5);
RenderText(m_value,
0, // no offset
wxRect(dc->GetTextExtent(m_value)).CentreIn(rect),
dc,
state);
return true;
}
virtual bool ActivateCell(const wxRect& WXUNUSED(cell),
wxDataViewModel *WXUNUSED(model),
const wxDataViewItem &WXUNUSED(item),
unsigned int WXUNUSED(col),
const wxMouseEvent *mouseEvent) override/*wxOVERRIDE*/
{
wxString position;
if (mouseEvent)
position = wxString::Format("via mouse at %d, %d", mouseEvent->m_x, mouseEvent->m_y);
else
position = "from keyboard";
// wxLogMessage("MyCustomRenderer ActivateCell() %s", position);
return false;
}
virtual wxSize GetSize() const override/*wxOVERRIDE*/
{
return wxSize(60, 20);
}
virtual bool SetValue(const wxVariant &value) override/*wxOVERRIDE*/
{
m_value = value.GetString();
return true;
}
virtual bool GetValue(wxVariant &WXUNUSED(value)) const override/*wxOVERRIDE*/{ return true; }
virtual bool HasEditorCtrl() const override/*wxOVERRIDE*/{ return true; }
virtual wxWindow*
CreateEditorCtrl(wxWindow* parent,
wxRect labelRect,
const wxVariant& value) override/*wxOVERRIDE*/
{
wxTextCtrl* text = new wxTextCtrl(parent, wxID_ANY, value,
labelRect.GetPosition(),
labelRect.GetSize(),
wxTE_PROCESS_ENTER);
text->SetInsertionPointEnd();
return text;
}
virtual bool
GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) override/*wxOVERRIDE*/
{
wxTextCtrl* text = wxDynamicCast(ctrl, wxTextCtrl);
if (!text)
return false;
value = text->GetValue();
return true;
}
private:
wxString m_value;
};
// ******************************* EXPERIMENTS **********************************************
enum SelectedSlider {
ssUndef,
ssLower,
ssHigher
};
enum TicksAction{
taOnIcon,
taAdd,
taDel
};
class PrusaDoubleSlider : public wxControl
{
public:
PrusaDoubleSlider(
wxWindow *parent,
wxWindowID id,
int lowerValue,
int higherValue,
int minValue,
int maxValue,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = wxSL_HORIZONTAL,
const wxValidator& val = wxDefaultValidator,
const wxString& name = wxEmptyString);
int GetLowerValue() const {
return m_lower_value;
}
int GetHigherValue() const {
return m_higher_value;
}
int GetActiveValue() const;
wxSize DoGetBestSize() const override;
void SetLowerValue(const int lower_val);
void SetHigherValue(const int higher_val);
void SetMaxValue(const int max_value);
void SetKoefForLabels(const double koef) {
m_label_koef = koef;
}
void SetSliderValues(const std::vector<double>& values) {
m_values = values;
}
void OnPaint(wxPaintEvent& ){ render();}
void OnLeftDown(wxMouseEvent& event);
void OnMotion(wxMouseEvent& event);
void OnLeftUp(wxMouseEvent& event);
void OnEnterWin(wxMouseEvent& event){ enter_window(event, true); }
void OnLeaveWin(wxMouseEvent& event){ enter_window(event, false); }
void OnWheel(wxMouseEvent& event);
void OnKeyDown(wxKeyEvent &event);
void OnKeyUp(wxKeyEvent &event);
void OnRightDown(wxMouseEvent& event);
void OnRightUp(wxMouseEvent& event);
protected:
void render();
void draw_focus_rect();
void draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end);
void draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos);
void draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection);
void draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos);
void draw_ticks(wxDC& dc);
void draw_one_layer_icon(wxDC& dc);
void draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection);
void draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, SelectedSlider selection);
void draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const;
void update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection);
void detect_selected_slider(const wxPoint& pt, const bool is_mouse_wheel = false);
void correct_lower_value();
void correct_higher_value();
void move_current_thumb(const bool condition);
void action_tick(const TicksAction action);
void enter_window(wxMouseEvent& event, const bool enter);
bool is_point_in_rect(const wxPoint& pt, const wxRect& rect);
bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; }
double get_scroll_step();
wxString get_label(const SelectedSlider& selection) const;
void get_lower_and_higher_position(int& lower_pos, int& higher_pos);
int get_value_from_position(const wxCoord x, const wxCoord y);
wxCoord get_position_from_value(const int value);
wxSize get_size();
void get_size(int *w, int *h);
private:
int m_min_value;
int m_max_value;
int m_lower_value;
int m_higher_value;
wxBitmap m_bmp_thumb_higher;
wxBitmap m_bmp_thumb_lower;
wxBitmap m_bmp_add_tick_on;
wxBitmap m_bmp_add_tick_off;
wxBitmap m_bmp_del_tick_on;
wxBitmap m_bmp_del_tick_off;
wxBitmap m_bmp_one_layer_lock_on;
wxBitmap m_bmp_one_layer_lock_off;
wxBitmap m_bmp_one_layer_unlock_on;
wxBitmap m_bmp_one_layer_unlock_off;
SelectedSlider m_selection;
bool m_is_left_down = false;
bool m_is_right_down = false;
bool m_is_one_layer = false;
bool m_is_focused = false;
bool m_is_action_icon_focesed = false;
bool m_is_one_layer_icon_focesed = false;
wxRect m_rect_lower_thumb;
wxRect m_rect_higher_thumb;
wxRect m_rect_tick_action;
wxRect m_rect_one_layer_icon;
wxSize m_thumb_size;
int m_tick_icon_dim;
int m_lock_icon_dim = 16;
long m_style;
float m_label_koef = 1.0;
// control's view variables
wxCoord SLIDER_MARGIN; // margin around slider
wxPen DARK_ORANGE_PEN;
wxPen ORANGE_PEN;
wxPen LIGHT_ORANGE_PEN;
wxPen DARK_GREY_PEN;
wxPen GREY_PEN;
wxPen LIGHT_GREY_PEN;
std::vector<wxPen*> line_pens;
std::vector<wxPen*> segm_pens;
std::set<int> m_ticks;
std::vector<double> m_values;
};
// ******************************************************************************************
#endif // slic3r_GUI_wxExtensions_hpp_

View file

@ -0,0 +1,279 @@
#include "Duet.hpp"
#include "PrintHostSendDialog.hpp"
#include <algorithm>
#include <ctime>
#include <boost/filesystem/path.hpp>
#include <boost/format.hpp>
#include <boost/log/trivial.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <wx/frame.h>
#include <wx/event.h>
#include <wx/progdlg.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include <wx/checkbox.h>
#include "libslic3r/PrintConfig.hpp"
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/MsgDialog.hpp"
#include "Http.hpp"
namespace fs = boost::filesystem;
namespace pt = boost::property_tree;
namespace Slic3r {
Duet::Duet(DynamicPrintConfig *config) :
host(config->opt_string("print_host")),
password(config->opt_string("printhost_apikey"))
{}
Duet::~Duet() {}
bool Duet::test(wxString &msg) const
{
bool connected = connect(msg);
if (connected) {
disconnect();
}
return connected;
}
wxString Duet::get_test_ok_msg () const
{
return wxString::Format("%s", _(L("Connection to Duet works correctly.")));
}
wxString Duet::get_test_failed_msg (wxString &msg) const
{
return wxString::Format("%s: %s", _(L("Could not connect to Duet")), msg);
}
bool Duet::send_gcode(const std::string &filename) const
{
enum { PROGRESS_RANGE = 1000 };
const auto errortitle = _(L("Error while uploading to the Duet"));
fs::path filepath(filename);
PrintHostSendDialog send_dialog(filepath.filename(), true);
if (send_dialog.ShowModal() != wxID_OK) { return false; }
const bool print = send_dialog.print();
const auto upload_filepath = send_dialog.filename();
const auto upload_filename = upload_filepath.filename();
const auto upload_parent_path = upload_filepath.parent_path();
wxProgressDialog progress_dialog(
_(L("Duet upload")),
_(L("Sending G-code file to Duet...")),
PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
progress_dialog.Pulse();
wxString connect_msg;
if (!connect(connect_msg)) {
auto errormsg = wxString::Format("%s: %s", errortitle, connect_msg);
GUI::show_error(&progress_dialog, std::move(errormsg));
return false;
}
bool res = true;
auto upload_cmd = get_upload_url(upload_filepath.string());
BOOST_LOG_TRIVIAL(info) << boost::format("Duet: Uploading file %1%, filename: %2%, path: %3%, print: %4%, command: %5%")
% filepath.string()
% upload_filename.string()
% upload_parent_path.string()
% print
% upload_cmd;
auto http = Http::post(std::move(upload_cmd));
http.set_post_body(filename)
.on_complete([&](std::string body, unsigned status) {
BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: File uploaded: HTTP %1%: %2%") % status % body;
progress_dialog.Update(PROGRESS_RANGE);
int err_code = get_err_code_from_body(body);
if (err_code != 0) {
auto msg = format_error(body, L("Unknown error occured"), 0);
GUI::show_error(&progress_dialog, std::move(msg));
res = false;
} else if (print) {
wxString errormsg;
res = start_print(errormsg, upload_filepath.string());
if (!res) {
GUI::show_error(&progress_dialog, std::move(errormsg));
}
}
})
.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body;
auto errormsg = wxString::Format("%s: %s", errortitle, format_error(body, error, status));
GUI::show_error(&progress_dialog, std::move(errormsg));
res = false;
})
.on_progress([&](Http::Progress progress, bool &cancel) {
if (cancel) {
// Upload was canceled
res = false;
} else if (progress.ultotal > 0) {
int value = PROGRESS_RANGE * progress.ulnow / progress.ultotal;
cancel = !progress_dialog.Update(std::min(value, PROGRESS_RANGE - 1)); // Cap the value to prevent premature dialog closing
} else {
cancel = !progress_dialog.Pulse();
}
})
.perform_sync();
disconnect();
return res;
}
bool Duet::has_auto_discovery() const
{
return false;
}
bool Duet::can_test() const
{
return true;
}
bool Duet::connect(wxString &msg) const
{
bool res = false;
auto url = get_connect_url();
auto http = Http::get(std::move(url));
http.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error connecting: %1%, HTTP %2%, body: `%3%`") % error % status % body;
msg = format_error(body, error, status);
})
.on_complete([&](std::string body, unsigned) {
BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: Got: %1%") % body;
int err_code = get_err_code_from_body(body);
switch (err_code) {
case 0:
res = true;
break;
case 1:
msg = format_error(body, L("Wrong password"), 0);
break;
case 2:
msg = format_error(body, L("Could not get resources to create a new connection"), 0);
break;
default:
msg = format_error(body, L("Unknown error occured"), 0);
break;
}
})
.perform_sync();
return res;
}
void Duet::disconnect() const
{
auto url = (boost::format("%1%rr_disconnect")
% get_base_url()).str();
auto http = Http::get(std::move(url));
http.on_error([&](std::string body, std::string error, unsigned status) {
// we don't care about it, if disconnect is not working Duet will disconnect automatically after some time
BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error disconnecting: %1%, HTTP %2%, body: `%3%`") % error % status % body;
})
.perform_sync();
}
std::string Duet::get_upload_url(const std::string &filename) const
{
return (boost::format("%1%rr_upload?name=0:/gcodes/%2%&%3%")
% get_base_url()
% filename
% timestamp_str()).str();
}
std::string Duet::get_connect_url() const
{
return (boost::format("%1%rr_connect?password=%2%&%3%")
% get_base_url()
% (password.empty() ? "reprap" : password)
% timestamp_str()).str();
}
std::string Duet::get_base_url() const
{
if (host.find("http://") == 0 || host.find("https://") == 0) {
if (host.back() == '/') {
return host;
} else {
return (boost::format("%1%/") % host).str();
}
} else {
return (boost::format("http://%1%/") % host).str();
}
}
std::string Duet::timestamp_str() const
{
enum { BUFFER_SIZE = 32 };
auto t = std::time(nullptr);
auto tm = *std::localtime(&t);
char buffer[BUFFER_SIZE];
std::strftime(buffer, BUFFER_SIZE, "time=%Y-%d-%mT%H:%M:%S", &tm);
return std::string(buffer);
}
wxString Duet::format_error(const std::string &body, const std::string &error, unsigned status)
{
if (status != 0) {
auto wxbody = wxString::FromUTF8(body.data());
return wxString::Format("HTTP %u: %s", status, wxbody);
} else {
return wxString::FromUTF8(error.data());
}
}
bool Duet::start_print(wxString &msg, const std::string &filename) const
{
bool res = false;
auto url = (boost::format("%1%rr_gcode?gcode=M32%%20\"%2%\"")
% get_base_url()
% filename).str();
auto http = Http::get(std::move(url));
http.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error starting print: %1%, HTTP %2%, body: `%3%`") % error % status % body;
msg = format_error(body, error, status);
})
.on_complete([&](std::string body, unsigned) {
BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: Got: %1%") % body;
res = true;
})
.perform_sync();
return res;
}
int Duet::get_err_code_from_body(const std::string &body) const
{
pt::ptree root;
std::istringstream iss (body); // wrap returned json to istringstream
pt::read_json(iss, root);
return root.get<int>("err", 0);
}
}

View file

@ -0,0 +1,47 @@
#ifndef slic3r_Duet_hpp_
#define slic3r_Duet_hpp_
#include <string>
#include <wx/string.h>
#include "PrintHost.hpp"
namespace Slic3r {
class DynamicPrintConfig;
class Http;
class Duet : public PrintHost
{
public:
Duet(DynamicPrintConfig *config);
virtual ~Duet();
bool test(wxString &curl_msg) const;
wxString get_test_ok_msg () const;
wxString get_test_failed_msg (wxString &msg) const;
// Send gcode file to duet, filename is expected to be in UTF-8
bool send_gcode(const std::string &filename) const;
bool has_auto_discovery() const;
bool can_test() const;
private:
std::string host;
std::string password;
std::string get_upload_url(const std::string &filename) const;
std::string get_connect_url() const;
std::string get_base_url() const;
std::string timestamp_str() const;
bool connect(wxString &msg) const;
void disconnect() const;
bool start_print(wxString &msg, const std::string &filename) const;
int get_err_code_from_body(const std::string &body) const;
static wxString format_error(const std::string &body, const std::string &error, unsigned status);
};
}
#endif

View file

@ -4,6 +4,7 @@
#include <functional>
#include <thread>
#include <deque>
#include <sstream>
#include <boost/filesystem/fstream.hpp>
#include <boost/format.hpp>
@ -42,6 +43,7 @@ struct Http::priv
// Used for storing file streams added as multipart form parts
// Using a deque here because unlike vector it doesn't ivalidate pointers on insertion
std::deque<fs::ifstream> form_files;
std::string postfields;
size_t limit;
bool cancel;
@ -60,6 +62,7 @@ struct Http::priv
static size_t form_file_read_cb(char *buffer, size_t size, size_t nitems, void *userp);
void form_add_file(const char *name, const fs::path &path, const char* filename);
void set_post_body(const fs::path &path);
std::string curl_error(CURLcode curlcode);
std::string body_size_error();
@ -187,6 +190,13 @@ void Http::priv::form_add_file(const char *name, const fs::path &path, const cha
}
}
void Http::priv::set_post_body(const fs::path &path)
{
std::ifstream file(path.string());
std::string file_content { std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>() };
postfields = file_content;
}
std::string Http::priv::curl_error(CURLcode curlcode)
{
return (boost::format("%1% (%2%)")
@ -229,6 +239,11 @@ void Http::priv::http_perform()
::curl_easy_setopt(curl, CURLOPT_HTTPPOST, form);
}
if (!postfields.empty()) {
::curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postfields.c_str());
::curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, postfields.size());
}
CURLcode res = ::curl_easy_perform(curl);
if (res != CURLE_OK) {
@ -338,6 +353,12 @@ Http& Http::form_add_file(const std::string &name, const fs::path &path, const s
return *this;
}
Http& Http::set_post_body(const fs::path &path)
{
if (p) { p->set_post_body(path);}
return *this;
}
Http& Http::on_complete(CompleteFn fn)
{
if (p) { p->completefn = std::move(fn); }

View file

@ -73,6 +73,11 @@ public:
// Same as above except also override the file's filename with a custom one
Http& form_add_file(const std::string &name, const boost::filesystem::path &path, const std::string &filename);
// Set the file contents as a POST request body.
// The data is used verbatim, it is not additionally encoded in any way.
// This can be used for hosts which do not support multipart requests.
Http& set_post_body(const boost::filesystem::path &path);
// Callback called on HTTP request complete
Http& on_complete(CompleteFn fn);
// Callback called on an error occuring at any stage of the requests: Url parsing, DNS lookup,

View file

@ -1,21 +1,11 @@
#include "OctoPrint.hpp"
#include "PrintHostSendDialog.hpp"
#include <algorithm>
#include <boost/filesystem/path.hpp>
#include <boost/format.hpp>
#include <boost/log/trivial.hpp>
#include <wx/frame.h>
#include <wx/event.h>
#include <wx/progdlg.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include <wx/checkbox.h>
#include "libslic3r/PrintConfig.hpp"
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/MsgDialog.hpp"
#include "Http.hpp"
namespace fs = boost::filesystem;
@ -23,49 +13,14 @@ namespace fs = boost::filesystem;
namespace Slic3r {
struct SendDialog : public GUI::MsgDialog
{
wxTextCtrl *txt_filename;
wxCheckBox *box_print;
SendDialog(const fs::path &path) :
MsgDialog(nullptr, _(L("Send G-Code to printer")), _(L("Upload to OctoPrint with the following filename:")), wxID_NONE),
txt_filename(new wxTextCtrl(this, wxID_ANY, path.filename().wstring())),
box_print(new wxCheckBox(this, wxID_ANY, _(L("Start printing after upload"))))
{
auto *label_dir_hint = new wxStaticText(this, wxID_ANY, _(L("Use forward slashes ( / ) as a directory separator if needed.")));
label_dir_hint->Wrap(CONTENT_WIDTH);
content_sizer->Add(txt_filename, 0, wxEXPAND);
content_sizer->Add(label_dir_hint);
content_sizer->AddSpacer(VERT_SPACING);
content_sizer->Add(box_print, 0, wxBOTTOM, 2*VERT_SPACING);
btn_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL));
txt_filename->SetFocus();
wxString stem(path.stem().wstring());
txt_filename->SetSelection(0, stem.Length());
Fit();
}
fs::path filename() const {
return fs::path(txt_filename->GetValue().wx_str());
}
bool print() const { return box_print->GetValue(); }
};
OctoPrint::OctoPrint(DynamicPrintConfig *config) :
host(config->opt_string("octoprint_host")),
apikey(config->opt_string("octoprint_apikey")),
cafile(config->opt_string("octoprint_cafile"))
host(config->opt_string("print_host")),
apikey(config->opt_string("printhost_apikey")),
cafile(config->opt_string("printhost_cafile"))
{}
OctoPrint::~OctoPrint() {}
bool OctoPrint::test(wxString &msg) const
{
// Since the request is performed synchronously here,
@ -91,6 +46,17 @@ bool OctoPrint::test(wxString &msg) const
return res;
}
wxString OctoPrint::get_test_ok_msg () const
{
return wxString::Format("%s", _(L("Connection to OctoPrint works correctly.")));
}
wxString OctoPrint::get_test_failed_msg (wxString &msg) const
{
return wxString::Format("%s: %s\n\n%s",
_(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required.")));
}
bool OctoPrint::send_gcode(const std::string &filename) const
{
enum { PROGRESS_RANGE = 1000 };
@ -98,7 +64,7 @@ bool OctoPrint::send_gcode(const std::string &filename) const
const auto errortitle = _(L("Error while uploading to the OctoPrint server"));
fs::path filepath(filename);
SendDialog send_dialog(filepath.filename());
PrintHostSendDialog send_dialog(filepath.filename(), true);
if (send_dialog.ShowModal() != wxID_OK) { return false; }
const bool print = send_dialog.print();
@ -161,6 +127,16 @@ bool OctoPrint::send_gcode(const std::string &filename) const
return res;
}
bool OctoPrint::has_auto_discovery() const
{
return true;
}
bool OctoPrint::can_test() const
{
return true;
}
void OctoPrint::set_auth(Http &http) const
{
http.header("X-Api-Key", apikey);

View file

@ -4,6 +4,8 @@
#include <string>
#include <wx/string.h>
#include "PrintHost.hpp"
namespace Slic3r {
@ -11,14 +13,19 @@ namespace Slic3r {
class DynamicPrintConfig;
class Http;
class OctoPrint
class OctoPrint : public PrintHost
{
public:
OctoPrint(DynamicPrintConfig *config);
virtual ~OctoPrint();
bool test(wxString &curl_msg) const;
wxString get_test_ok_msg () const;
wxString get_test_failed_msg (wxString &msg) const;
// Send gcode file to octoprint, filename is expected to be in UTF-8
bool send_gcode(const std::string &filename) const;
bool has_auto_discovery() const;
bool can_test() const;
private:
std::string host;
std::string apikey;

View file

@ -447,6 +447,7 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons
for (const auto &name : bundle.obsolete_presets.prints) { obsolete_remover("print", name); }
for (const auto &name : bundle.obsolete_presets.filaments) { obsolete_remover("filament", name); }
for (const auto &name : bundle.obsolete_presets.filaments) { obsolete_remover("sla_material", name); }
for (const auto &name : bundle.obsolete_presets.printers) { obsolete_remover("printer", name); }
}
}

View file

@ -0,0 +1,23 @@
#include "OctoPrint.hpp"
#include "Duet.hpp"
#include "libslic3r/PrintConfig.hpp"
namespace Slic3r {
PrintHost::~PrintHost() {}
PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config)
{
PrintHostType kind = config->option<ConfigOptionEnum<PrintHostType>>("host_type")->value;
if (kind == htOctoPrint) {
return new OctoPrint(config);
} else if (kind == htDuet) {
return new Duet(config);
}
return nullptr;
}
}

View file

@ -0,0 +1,35 @@
#ifndef slic3r_PrintHost_hpp_
#define slic3r_PrintHost_hpp_
#include <memory>
#include <string>
#include <wx/string.h>
namespace Slic3r {
class DynamicPrintConfig;
class PrintHost
{
public:
virtual ~PrintHost();
virtual bool test(wxString &curl_msg) const = 0;
virtual wxString get_test_ok_msg () const = 0;
virtual wxString get_test_failed_msg (wxString &msg) const = 0;
// Send gcode file to print host, filename is expected to be in UTF-8
virtual bool send_gcode(const std::string &filename) const = 0;
virtual bool has_auto_discovery() const = 0;
virtual bool can_test() const = 0;
static PrintHost* get_print_host(DynamicPrintConfig *config);
};
}
#endif

View file

@ -0,0 +1,52 @@
#include "PrintHostSendDialog.hpp"
#include <wx/frame.h>
#include <wx/event.h>
#include <wx/progdlg.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include <wx/checkbox.h>
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/MsgDialog.hpp"
namespace fs = boost::filesystem;
namespace Slic3r {
PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_print) :
MsgDialog(nullptr, _(L("Send G-Code to printer host")), _(L("Upload to Printer Host with the following filename:")), wxID_NONE),
txt_filename(new wxTextCtrl(this, wxID_ANY, path.filename().wstring())),
box_print(new wxCheckBox(this, wxID_ANY, _(L("Start printing after upload")))),
can_start_print(can_start_print)
{
auto *label_dir_hint = new wxStaticText(this, wxID_ANY, _(L("Use forward slashes ( / ) as a directory separator if needed.")));
label_dir_hint->Wrap(CONTENT_WIDTH);
content_sizer->Add(txt_filename, 0, wxEXPAND);
content_sizer->Add(label_dir_hint);
content_sizer->AddSpacer(VERT_SPACING);
content_sizer->Add(box_print, 0, wxBOTTOM, 2*VERT_SPACING);
btn_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL));
txt_filename->SetFocus();
wxString stem(path.stem().wstring());
txt_filename->SetSelection(0, stem.Length());
box_print->Enable(can_start_print);
Fit();
}
fs::path PrintHostSendDialog::filename() const
{
return fs::path(txt_filename->GetValue().wx_str());
}
bool PrintHostSendDialog::print() const
{
return box_print->GetValue(); }
}

View file

@ -0,0 +1,38 @@
#ifndef slic3r_PrintHostSendDialog_hpp_
#define slic3r_PrintHostSendDialog_hpp_
#include <string>
#include <boost/filesystem/path.hpp>
#include <wx/string.h>
#include <wx/frame.h>
#include <wx/event.h>
#include <wx/progdlg.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include <wx/checkbox.h>
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/MsgDialog.hpp"
namespace Slic3r {
class PrintHostSendDialog : public GUI::MsgDialog
{
private:
wxTextCtrl *txt_filename;
wxCheckBox *box_print;
bool can_start_print;
public:
PrintHostSendDialog(const boost::filesystem::path &path, bool can_start_print);
boost::filesystem::path filename() const;
bool print() const;
};
}
#endif