#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libslic3r/libslic3r.h" #include "libslic3r/Polygon.hpp" #include "libslic3r/ClipperUtils.hpp" #include "libslic3r/BoundingBox.hpp" #include "libslic3r/Geometry.hpp" #include "libslic3r/Tesselate.hpp" #include "libslic3r/GCode/ThumbnailData.hpp" #include "libslic3r/Utils.hpp" #include "I18N.hpp" #include "GUI_App.hpp" #include "libslic3r/AppConfig.hpp" #include "libslic3r/PresetBundle.hpp" #include "BackgroundSlicingProcess.hpp" #include "Widgets/Label.hpp" #include "3DBed.hpp" #include "PartPlate.hpp" #include "Camera.hpp" #include "GUI_Colors.hpp" #include "GUI_ObjectList.hpp" #include "Tab.hpp" #include "format.hpp" #include #include using boost::optional; namespace fs = boost::filesystem; static const float GROUND_Z = -0.03f; static const float GRABBER_X_FACTOR = 0.20f; static const float GRABBER_Y_FACTOR = 0.03f; static const float GRABBER_Z_VALUE = 0.5f; static unsigned int GLOBAL_PLATE_INDEX = 0; static const double LOGICAL_PART_PLATE_GAP = 1. / 5.; static const int PARTPLATE_ICON_SIZE = 16; static const int PARTPLATE_EDIT_PLATE_NAME_ICON_SIZE = 12; static const int PARTPLATE_PLATE_NAME_FIX_HEIGHT_SIZE = 20; static const int PARTPLATE_ICON_GAP_TOP = 3; static const int PARTPLATE_ICON_GAP_LEFT = 3; static const int PARTPLATE_ICON_GAP_Y = 5; static const int PARTPLATE_TEXT_OFFSET_X1 = 3; static const int PARTPLATE_TEXT_OFFSET_X2 = 1; static const int PARTPLATE_TEXT_OFFSET_Y = 1; static const int PARTPLATE_PLATENAME_OFFSET_Y = 10; const float WIPE_TOWER_DEFAULT_X_POS = 165.; const float WIPE_TOWER_DEFAULT_Y_POS = 250.; // Max y const float I3_WIPE_TOWER_DEFAULT_X_POS = 0.; const float I3_WIPE_TOWER_DEFAULT_Y_POS = 250.; // Max y std::array PlateTextureForeground = {0x0, 0xae, 0x42, 0xff}; namespace Slic3r { namespace GUI { class Bed3D; std::array PartPlate::SELECT_COLOR = { 0.2666f, 0.2784f, 0.2784f, 1.0f }; //{ 0.4196f, 0.4235f, 0.4235f, 1.0f }; std::array PartPlate::UNSELECT_COLOR = { 0.82f, 0.82f, 0.82f, 1.0f }; std::array PartPlate::UNSELECT_DARK_COLOR = { 0.384f, 0.384f, 0.412f, 1.0f }; std::array PartPlate::DEFAULT_COLOR = { 0.5f, 0.5f, 0.5f, 1.0f }; std::array PartPlate::LINE_TOP_COLOR = { 0.89f, 0.89f, 0.89f, 1.0f }; std::array PartPlate::LINE_TOP_DARK_COLOR = { 0.431f, 0.431f, 0.463f, 1.0f }; std::array PartPlate::LINE_TOP_SEL_COLOR = { 0.5294f, 0.5451, 0.5333f, 1.0f}; std::array PartPlate::LINE_TOP_SEL_DARK_COLOR = { 0.298f, 0.298f, 0.3333f, 1.0f}; std::array PartPlate::LINE_BOTTOM_COLOR = { 0.8f, 0.8f, 0.8f, 0.4f }; std::array PartPlate::HEIGHT_LIMIT_TOP_COLOR = { 0.6f, 0.6f, 1.0f, 1.0f }; std::array PartPlate::HEIGHT_LIMIT_BOTTOM_COLOR = { 0.4f, 0.4f, 1.0f, 1.0f }; void PartPlate::update_render_colors() { PartPlate::SELECT_COLOR = GLColor(RenderColor::colors[RenderCol_Plate_Selected]); PartPlate::UNSELECT_COLOR = GLColor(RenderColor::colors[RenderCol_Plate_Unselected]); PartPlate::DEFAULT_COLOR = GLColor(RenderColor::colors[RenderCol_Plate_Default]); PartPlate::LINE_TOP_COLOR = GLColor(RenderColor::colors[RenderCol_Plate_Line_Top]); PartPlate::LINE_BOTTOM_COLOR = GLColor(RenderColor::colors[RenderCol_Plate_Line_Bottom]); } void PartPlate::load_render_colors() { RenderColor::colors[RenderCol_Plate_Selected] = IMColor(SELECT_COLOR); RenderColor::colors[RenderCol_Plate_Unselected] = IMColor(UNSELECT_COLOR); RenderColor::colors[RenderCol_Plate_Default] = IMColor(DEFAULT_COLOR); RenderColor::colors[RenderCol_Plate_Line_Top] = IMColor(LINE_TOP_COLOR); RenderColor::colors[RenderCol_Plate_Line_Bottom] = IMColor(LINE_BOTTOM_COLOR); } PartPlate::PartPlate() : ObjectBase(-1), m_plater(nullptr), m_model(nullptr), m_quadric(nullptr) { assert(this->id().invalid()); init(); } PartPlate::PartPlate(PartPlateList *partplate_list, Vec3d origin, int width, int depth, int height, Plater* platerObj, Model* modelObj, bool printable, PrinterTechnology tech) :m_partplate_list(partplate_list), m_plater(platerObj), m_model(modelObj), printer_technology(tech), m_origin(origin), m_width(width), m_depth(depth), m_height(height), m_printable(printable) { init(); } PartPlate::~PartPlate() { clear(); //if (m_quadric != nullptr) // ::gluDeleteQuadric(m_quadric); release_opengl_resource(); //boost::nowide::remove(m_tmp_gcode_path.c_str()); } void PartPlate::init() { m_locked = false; m_ready_for_slice = true; m_slice_result_valid = false; m_slice_percent = 0.0f; m_hover_id = -1; m_selected = false; //m_quadric = ::gluNewQuadric(); //if (m_quadric != nullptr) // ::gluQuadricDrawStyle(m_quadric, GLU_FILL); m_print_index = -1; m_print = nullptr; } BedType PartPlate::get_bed_type(bool load_from_project) const { std::string bed_type_key = "curr_bed_type"; if (m_config.has(bed_type_key)) { BedType bed_type = m_config.opt_enum(bed_type_key); return bed_type; } if (!load_from_project || !m_plater || !wxGetApp().preset_bundle) return btDefault; DynamicConfig& proj_cfg = wxGetApp().preset_bundle->project_config; if (proj_cfg.has(bed_type_key)) return proj_cfg.opt_enum(bed_type_key); return btDefault; } void PartPlate::set_bed_type(BedType bed_type) { std::string bed_type_key = "curr_bed_type"; // should be called in GUI context assert(m_plater != nullptr); // update slice state BedType old_real_bed_type = get_bed_type(); if (old_real_bed_type == btDefault) { DynamicConfig& proj_cfg = wxGetApp().preset_bundle->project_config; if (proj_cfg.has(bed_type_key)) old_real_bed_type = proj_cfg.opt_enum(bed_type_key); } BedType new_real_bed_type = bed_type; if (bed_type == BedType::btDefault) { DynamicConfig& proj_cfg = wxGetApp().preset_bundle->project_config; if (proj_cfg.has(bed_type_key)) new_real_bed_type = proj_cfg.opt_enum(bed_type_key); } if (old_real_bed_type != new_real_bed_type) { update_slice_result_valid_state(false); } if (bed_type == BedType::btDefault) m_config.erase(bed_type_key); else m_config.set_key_value("curr_bed_type", new ConfigOptionEnum(bed_type)); } void PartPlate::reset_bed_type() { m_config.erase("curr_bed_type"); } void PartPlate::set_print_seq(PrintSequence print_seq) { std::string print_seq_key = "print_sequence"; // should be called in GUI context assert(m_plater != nullptr); // update slice state PrintSequence old_real_print_seq = get_print_seq(); if (old_real_print_seq == PrintSequence::ByDefault) { auto curr_preset_config = wxGetApp().preset_bundle->prints.get_edited_preset().config; if (curr_preset_config.has(print_seq_key)) old_real_print_seq = curr_preset_config.option>(print_seq_key)->value; } PrintSequence new_real_print_seq = print_seq; if (print_seq == PrintSequence::ByDefault) { auto curr_preset_config = wxGetApp().preset_bundle->prints.get_edited_preset().config; if (curr_preset_config.has(print_seq_key)) new_real_print_seq = curr_preset_config.option>(print_seq_key)->value; } if (old_real_print_seq != new_real_print_seq) { update_slice_result_valid_state(false); } //print_seq_same_global = same_global; if (print_seq == PrintSequence::ByDefault) m_config.erase(print_seq_key); else m_config.set_key_value(print_seq_key, new ConfigOptionEnum(print_seq)); } PrintSequence PartPlate::get_print_seq() const { std::string print_seq_key = "print_sequence"; if (m_config.has(print_seq_key)) { PrintSequence print_seq = m_config.opt_enum(print_seq_key); return print_seq; } return PrintSequence::ByDefault; } PrintSequence PartPlate::get_real_print_seq(bool* plate_same_as_global) const { PrintSequence global_print_seq = wxGetApp().global_print_sequence(); PrintSequence curr_plate_seq = get_print_seq(); if (curr_plate_seq == PrintSequence::ByDefault) { curr_plate_seq = global_print_seq; } if(plate_same_as_global) *plate_same_as_global = (curr_plate_seq == global_print_seq); return curr_plate_seq; } bool PartPlate::has_spiral_mode_config() const { std::string key = "spiral_mode"; return m_config.has(key); } bool PartPlate::get_spiral_vase_mode() const { std::string key = "spiral_mode"; if (m_config.has(key)) { return m_config.opt_bool(key); } else { DynamicPrintConfig* global_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config; if (global_config->has(key)) return global_config->opt_bool(key); } return false; } void PartPlate::set_spiral_vase_mode(bool spiral_mode, bool as_global) { std::string key = "spiral_mode"; if (as_global) m_config.erase(key); else { if (spiral_mode) { if (get_spiral_vase_mode()) return; // Secondary confirmation auto answer = static_cast(wxGetApp().plate_tab)->show_spiral_mode_settings_dialog(false); if (answer == wxID_YES) { m_config.set_key_value(key, new ConfigOptionBool(true)); set_vase_mode_related_object_config(); } } else m_config.set_key_value(key, new ConfigOptionBool(false)); } } bool PartPlate::valid_instance(int obj_id, int instance_id) { if ((obj_id >= 0) && (obj_id < m_model->objects.size())) { ModelObject* object = m_model->objects[obj_id]; if ((instance_id >= 0) && (instance_id < object->instances.size())) return true; } return false; } void PartPlate::calc_bounding_boxes() const { BoundingBoxf3* bounding_box = const_cast(&m_bounding_box); *bounding_box = BoundingBoxf3(); for (const Vec2d& p : m_shape) { bounding_box->merge({ p(0), p(1), 0.0 }); } BoundingBoxf3* extended_bounding_box = const_cast(&m_extended_bounding_box); *extended_bounding_box = m_bounding_box; double half_x = bounding_box->size().x() * GRABBER_X_FACTOR; double half_y = bounding_box->size().y() * 1.0f * GRABBER_Y_FACTOR; double half_z = GRABBER_Z_VALUE; Vec3d center(bounding_box->center().x(), bounding_box->min(1) -half_y, GROUND_Z); m_grabber_box.min = Vec3d(center.x() - half_x, center.y() - half_y, center.z() - half_z); m_grabber_box.max = Vec3d(center.x() + half_x, center.y() + half_y, center.z() + half_z); m_grabber_box.defined = true; extended_bounding_box->merge(m_grabber_box); //calc exclude area bounding box m_exclude_bounding_box.clear(); BoundingBoxf3 exclude_bb; for (int index = 0; index < m_exclude_area.size(); index ++) { const Vec2d& p = m_exclude_area[index]; if (index % 4 == 0) exclude_bb = BoundingBoxf3(); exclude_bb.merge({ p(0), p(1), 0.0 }); if (index % 4 == 3) { exclude_bb.max(2) = m_depth; exclude_bb.min(2) = GROUND_Z; m_exclude_bounding_box.emplace_back(exclude_bb); } } } void PartPlate::calc_triangles(const ExPolygon& poly) { if (!m_triangles.set_from_triangles(triangulate_expolygon_2f(poly, NORMALS_UP), GROUND_Z)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":Unable to create plate triangles\n"; } void PartPlate::calc_exclude_triangles(const ExPolygon& poly) { if (!m_exclude_triangles.set_from_triangles(triangulate_expolygon_2f(poly, NORMALS_UP), GROUND_Z)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":Unable to create exclude triangles\n"; } void PartPlate::calc_gridlines(const ExPolygon& poly, const BoundingBox& pp_bbox) { Polylines axes_lines, axes_lines_bolder; int count = 0; for (coord_t x = pp_bbox.min(0); x <= pp_bbox.max(0); x += scale_(10.0)) { Polyline line; line.append(Point(x, pp_bbox.min(1))); line.append(Point(x, pp_bbox.max(1))); if ( (count % 5) == 0 ) axes_lines_bolder.push_back(line); else axes_lines.push_back(line); count ++; } count = 0; for (coord_t y = pp_bbox.min(1); y <= pp_bbox.max(1); y += scale_(10.0)) { Polyline line; line.append(Point(pp_bbox.min(0), y)); line.append(Point(pp_bbox.max(0), y)); axes_lines.push_back(line); if ( (count % 5) == 0 ) axes_lines_bolder.push_back(line); else axes_lines.push_back(line); count ++; } // clip with a slightly grown expolygon because our lines lay on the contours and may get erroneously clipped Lines gridlines = to_lines(intersection_pl(axes_lines, offset(poly, (float)SCALED_EPSILON))); Lines gridlines_bolder = to_lines(intersection_pl(axes_lines_bolder, offset(poly, (float)SCALED_EPSILON))); // append bed contours Lines contour_lines = to_lines(poly); std::copy(contour_lines.begin(), contour_lines.end(), std::back_inserter(gridlines)); if (!m_gridlines.set_from_lines(gridlines, GROUND_Z)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to create bed grid lines\n"; if (!m_gridlines_bolder.set_from_lines(gridlines_bolder, GROUND_Z)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to create bed grid lines\n"; } void PartPlate::calc_height_limit() { Lines3 bottom_h_lines, top_lines, top_h_lines, common_lines; int shape_count = m_shape.size(); float first_z = 0.02f; for (int i = 0; i < shape_count; i++) { auto &cur_p = m_shape[i]; Vec3crd p1(scale_(cur_p.x()), scale_(cur_p.y()), scale_(first_z)); Vec3crd p2(scale_(cur_p.x()), scale_(cur_p.y()), scale_(m_height_to_rod)); Vec3crd p3(scale_(cur_p.x()), scale_(cur_p.y()), scale_(m_height_to_lid)); common_lines.emplace_back(p1, p2); top_lines.emplace_back(p2, p3); Vec2d next_p; if (i < (shape_count - 1)) { next_p = m_shape[i+1]; } else { next_p = m_shape[0]; } Vec3crd p4(scale_(cur_p.x()), scale_(cur_p.y()), scale_(m_height_to_rod)); Vec3crd p5(scale_(next_p.x()), scale_(next_p.y()), scale_(m_height_to_rod)); bottom_h_lines.emplace_back(p4, p5); Vec3crd p6(scale_(cur_p.x()), scale_(cur_p.y()), scale_(m_height_to_lid)); Vec3crd p7(scale_(next_p.x()), scale_(next_p.y()), scale_(m_height_to_lid)); top_h_lines.emplace_back(p6, p7); } //std::copy(bottom_lines.begin(), bottom_lines.end(), std::back_inserter(bottom_h_lines)); std::copy(top_lines.begin(), top_lines.end(), std::back_inserter(top_h_lines)); if (!m_height_limit_common.set_from_3d_Lines(common_lines)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to create height limit bottom lines\n"; if (!m_height_limit_bottom.set_from_3d_Lines(bottom_h_lines)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to create height limit bottom lines\n"; if (!m_height_limit_top.set_from_3d_Lines(top_h_lines)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to create height limit top lines\n"; } void PartPlate::calc_vertex_for_number(int index, bool one_number, GeometryBuffer &buffer) { ExPolygon poly; #if 0 //in the up area Vec2d& p = m_shape[2]; float offset_x = one_number?PARTPLATE_TEXT_OFFSET_X1: PARTPLATE_TEXT_OFFSET_X2; poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP + offset_x), scale_(p(1) - index * (PARTPLATE_ICON_SIZE + PARTPLATE_ICON_GAP) - PARTPLATE_ICON_GAP - PARTPLATE_ICON_SIZE + PARTPLATE_TEXT_OFFSET_Y) }); poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP + PARTPLATE_ICON_SIZE - offset_x), scale_(p(1) - index * (PARTPLATE_ICON_SIZE + PARTPLATE_ICON_GAP)- PARTPLATE_ICON_GAP - PARTPLATE_ICON_SIZE + PARTPLATE_TEXT_OFFSET_Y) }); poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP + PARTPLATE_ICON_SIZE - offset_x), scale_(p(1) - index * (PARTPLATE_ICON_SIZE + PARTPLATE_ICON_GAP)- PARTPLATE_ICON_GAP - PARTPLATE_TEXT_OFFSET_Y)}); poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP + offset_x), scale_(p(1) - index * (PARTPLATE_ICON_SIZE + PARTPLATE_ICON_GAP)- PARTPLATE_ICON_GAP - PARTPLATE_TEXT_OFFSET_Y) }); #else //in the bottom Vec2d& p = m_shape[1]; float offset_x = one_number?PARTPLATE_TEXT_OFFSET_X1: PARTPLATE_TEXT_OFFSET_X2; poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + offset_x), scale_(p(1) + PARTPLATE_TEXT_OFFSET_Y) }); poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + PARTPLATE_ICON_SIZE - offset_x), scale_(p(1) + PARTPLATE_TEXT_OFFSET_Y) }); poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + PARTPLATE_ICON_SIZE - offset_x), scale_(p(1) + PARTPLATE_ICON_SIZE - PARTPLATE_TEXT_OFFSET_Y)}); poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + offset_x), scale_(p(1) + PARTPLATE_ICON_SIZE - PARTPLATE_TEXT_OFFSET_Y) }); #endif auto triangles = triangulate_expolygon_2f(poly, NORMALS_UP); if (!buffer.set_from_triangles(triangles, GROUND_Z)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to generate geometry buffers for icons\n"; } void PartPlate::calc_vertex_for_plate_name(GLTexture &texture, GeometryBuffer &buffer) { if (texture.get_width() > 0 && texture.get_height()) { wxCoord w, h; auto bed_ext = get_extents(m_shape); auto factor = bed_ext.size()(1) / 200.0; ExPolygon poly; float offset_x = 1; w = int(factor * (texture.get_width() * 16) / texture.get_height()); h = PARTPLATE_PLATE_NAME_FIX_HEIGHT_SIZE; Vec2d p = bed_ext[3] + Vec2d(0, PARTPLATE_PLATENAME_OFFSET_Y + h * texture.m_original_height / texture.get_height()); poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + offset_x), scale_(p(1) - h )}); poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + w - offset_x), scale_(p(1) - h )}); poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + w - offset_x), scale_(p(1) )}); poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + offset_x), scale_(p(1) )}); auto triangles = triangulate_expolygon_2f(poly, NORMALS_UP); if (!buffer.set_from_triangles(triangles, GROUND_Z)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to generate geometry buffers for icons\n"; } } void PartPlate::calc_vertex_for_plate_name_edit_icon(GLTexture *texture, int index, GeometryBuffer &buffer) { auto bed_ext = get_extents(m_shape); auto factor = bed_ext.size()(1) / 200.0; wxCoord w, h; h = int(factor * 16); ExPolygon poly; Vec2d p = bed_ext[3]; float offset_x = 1; h = PARTPLATE_EDIT_PLATE_NAME_ICON_SIZE; p += Vec2d(0, PARTPLATE_PLATENAME_OFFSET_Y + h); if (texture && texture->get_width() > 0 && texture->get_height()) { w = int(factor * (texture->get_original_width() * 16) / texture->get_height()) + 1; poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + w), scale_(p(1) - h )}); poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + w + PARTPLATE_EDIT_PLATE_NAME_ICON_SIZE), scale_(p(1) - h)}); poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + w + PARTPLATE_EDIT_PLATE_NAME_ICON_SIZE), scale_(p(1))}); poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + w), scale_(p(1) )}); auto triangles = triangulate_expolygon_2f(poly, NORMALS_UP); if (!buffer.set_from_triangles(triangles, GROUND_Z)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to generate geometry buffers for icons\n"; } else { poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + offset_x ), scale_(p(1) - h )}); poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + offset_x + PARTPLATE_EDIT_PLATE_NAME_ICON_SIZE), scale_(p(1) - h)}); poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + offset_x + PARTPLATE_EDIT_PLATE_NAME_ICON_SIZE), scale_(p(1))}); poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + offset_x), scale_(p(1) )}); auto triangles = triangulate_expolygon_2f(poly, NORMALS_UP); if (!buffer.set_from_triangles(triangles, GROUND_Z)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to generate geometry buffers for icons\n"; } } void PartPlate::calc_vertex_for_icons(int index, GeometryBuffer &buffer) { ExPolygon poly; Vec2d& p = m_shape[2]; poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP_LEFT), scale_(p(1) - index * (PARTPLATE_ICON_SIZE + PARTPLATE_ICON_GAP_Y) - PARTPLATE_ICON_GAP_TOP - PARTPLATE_ICON_SIZE) }); poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + PARTPLATE_ICON_SIZE), scale_(p(1) - index * (PARTPLATE_ICON_SIZE + PARTPLATE_ICON_GAP_Y)- PARTPLATE_ICON_GAP_TOP - PARTPLATE_ICON_SIZE) }); poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + PARTPLATE_ICON_SIZE), scale_(p(1) - index * (PARTPLATE_ICON_SIZE + PARTPLATE_ICON_GAP_Y)- PARTPLATE_ICON_GAP_TOP)}); poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP_LEFT), scale_(p(1) - index * (PARTPLATE_ICON_SIZE + PARTPLATE_ICON_GAP_Y)- PARTPLATE_ICON_GAP_TOP) }); auto triangles = triangulate_expolygon_2f(poly, NORMALS_UP); if (!buffer.set_from_triangles(triangles, GROUND_Z)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to generate geometry buffers for icons\n"; } void PartPlate::calc_vertex_for_icons_background(int icon_count, GeometryBuffer &buffer) { ExPolygon poly; Vec2d& p = m_shape[2]; poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP_LEFT), scale_(p(1) - icon_count * (PARTPLATE_ICON_SIZE + PARTPLATE_ICON_GAP_Y) - PARTPLATE_ICON_GAP_TOP) }); poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + PARTPLATE_ICON_SIZE), scale_(p(1) - icon_count * (PARTPLATE_ICON_SIZE + PARTPLATE_ICON_GAP_Y)- PARTPLATE_ICON_GAP_TOP) }); poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + PARTPLATE_ICON_SIZE), scale_(p(1) - PARTPLATE_ICON_GAP_TOP)}); poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP_LEFT), scale_(p(1) - PARTPLATE_ICON_GAP_TOP) }); auto triangles = triangulate_expolygon_2f(poly, NORMALS_UP); if (!buffer.set_from_triangles(triangles, GROUND_Z)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to generate geometry buffers for icons\n"; } void PartPlate::render_background(bool force_default_color) const { unsigned int triangles_vcount = m_triangles.get_vertices_count(); //return directly for current plate if (m_selected && !force_default_color) return; // draw background glsafe(::glDepthMask(GL_FALSE)); if (!force_default_color) { if (m_selected) { glsafe(::glColor4fv(PartPlate::SELECT_COLOR.data())); } else { glsafe(m_partplate_list->m_is_dark ? ::glColor4fv(PartPlate::UNSELECT_DARK_COLOR.data()) : ::glColor4fv(PartPlate::UNSELECT_COLOR.data())); } } else { glsafe(::glColor4fv(PartPlate::DEFAULT_COLOR.data())); } glsafe(::glNormal3d(0.0f, 0.0f, 1.0f)); glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_vertices_data())); glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount)); glsafe(::glDepthMask(GL_TRUE)); } void PartPlate::render_logo_texture(GLTexture &logo_texture, const GeometryBuffer& logo_buffer, bool bottom, unsigned int vbo_id) const { //check valid if (logo_texture.unsent_compressed_data_available()) { // sends to gpu the already available compressed levels of the main texture logo_texture.send_compressed_data_to_gpu(); } if (logo_buffer.get_vertices_count() > 0) { GLShaderProgram* shader = wxGetApp().get_shader("printbed"); if (shader != nullptr) { shader->start_using(); shader->set_uniform("transparent_background", 0); shader->set_uniform("svg_source", 0); //glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glDepthMask(GL_FALSE)); if (bottom) glsafe(::glFrontFace(GL_CW)); unsigned int stride = logo_buffer.get_vertex_data_size(); GLint position_id = shader->get_attrib_location("v_position"); GLint tex_coords_id = shader->get_attrib_location("v_tex_coords"); if (position_id != -1) { glsafe(::glEnableVertexAttribArray(position_id)); } if (tex_coords_id != -1) { glsafe(::glEnableVertexAttribArray(tex_coords_id)); } // show the temporary texture while no compressed data is available GLuint tex_id = (GLuint)logo_texture.get_id(); glsafe(::glBindTexture(GL_TEXTURE_2D, tex_id)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, vbo_id)); if (position_id != -1) glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)logo_buffer.get_position_offset())); if (tex_coords_id != -1) glsafe(::glVertexAttribPointer(tex_coords_id, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)logo_buffer.get_tex_coords_offset())); glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)logo_buffer.get_vertices_count())); if (tex_coords_id != -1) glsafe(::glDisableVertexAttribArray(tex_coords_id)); if (position_id != -1) glsafe(::glDisableVertexAttribArray(position_id)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); if (bottom) glsafe(::glFrontFace(GL_CCW)); glsafe(::glDepthMask(GL_TRUE)); shader->stop_using(); } } } void PartPlate::render_logo(bool bottom, bool render_cali) const { if (!m_partplate_list->render_bedtype_logo) { // render third-party printer texture logo if (m_partplate_list->m_logo_texture_filename.empty()) { m_partplate_list->m_logo_texture.reset(); return; } //GLTexture* temp_texture = const_cast(&m_temp_texture); if (m_partplate_list->m_logo_texture.get_id() == 0 || m_partplate_list->m_logo_texture.get_source() != m_partplate_list->m_logo_texture_filename) { m_partplate_list->m_logo_texture.reset(); if (boost::algorithm::iends_with(m_partplate_list->m_logo_texture_filename, ".svg")) { /*// use higher resolution images if graphic card and opengl version allow GLint max_tex_size = OpenGLManager::get_gl_info().get_max_tex_size(); if (temp_texture->get_id() == 0 || temp_texture->get_source() != m_texture_filename) { // generate a temporary lower resolution texture to show while no main texture levels have been compressed if (!temp_texture->load_from_svg_file(m_texture_filename, false, false, false, max_tex_size / 8)) { render_default(bottom, false); return; } canvas.request_extra_frame(); }*/ // starts generating the main texture, compression will run asynchronously GLint max_tex_size = OpenGLManager::get_gl_info().get_max_tex_size(); GLint logo_tex_size = (max_tex_size < 2048) ? max_tex_size : 2048; if (!m_partplate_list->m_logo_texture.load_from_svg_file(m_partplate_list->m_logo_texture_filename, true, false, false, logo_tex_size)) { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": load logo texture from %1% failed!") % m_partplate_list->m_logo_texture_filename; return; } } else if (boost::algorithm::iends_with(m_partplate_list->m_logo_texture_filename, ".png")) { // generate a temporary lower resolution texture to show while no main texture levels have been compressed /* if (temp_texture->get_id() == 0 || temp_texture->get_source() != m_logo_texture_filename) { if (!temp_texture->load_from_file(m_logo_texture_filename, false, GLTexture::None, false)) { render_default(bottom, false); return; } canvas.request_extra_frame(); }*/ // starts generating the main texture, compression will run asynchronously if (!m_partplate_list->m_logo_texture.load_from_file(m_partplate_list->m_logo_texture_filename, true, GLTexture::MultiThreaded, true)) { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": load logo texture from %1% failed!") % m_partplate_list->m_logo_texture_filename; return; } } else { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": can not load logo texture from %1%, unsupported format") % m_partplate_list->m_logo_texture_filename; return; } } else if (m_partplate_list->m_logo_texture.unsent_compressed_data_available()) { // sends to gpu the already available compressed levels of the main texture m_partplate_list->m_logo_texture.send_compressed_data_to_gpu(); // the temporary texture is not needed anymore, reset it //if (temp_texture->get_id() != 0) // temp_texture->reset(); //canvas.request_extra_frame(); } if (m_vbo_id == 0) { unsigned int* vbo_id_ptr = const_cast(&m_vbo_id); glsafe(::glGenBuffers(1, vbo_id_ptr)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, *vbo_id_ptr)); glsafe(::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)m_logo_triangles.get_vertices_data_size(), (const GLvoid*)m_logo_triangles.get_vertices_data(), GL_STATIC_DRAW)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); } if (m_vbo_id != 0 && m_logo_triangles.get_vertices_count() > 0) render_logo_texture(m_partplate_list->m_logo_texture, m_logo_triangles, bottom, m_vbo_id); return; } m_partplate_list->load_bedtype_textures(); m_partplate_list->load_cali_textures(); // btDefault should be skipped auto curr_bed_type = get_bed_type(); if (curr_bed_type == btDefault) { DynamicConfig& proj_cfg = wxGetApp().preset_bundle->project_config; if (proj_cfg.has(std::string("curr_bed_type"))) curr_bed_type = proj_cfg.opt_enum(std::string("curr_bed_type")); } int bed_type_idx = (int)curr_bed_type; // render bed textures for (auto &part : m_partplate_list->bed_texture_info[bed_type_idx].parts) { if (part.texture) { if (part.buffer && part.buffer->get_vertices_count() > 0 //&& part.vbo_id != 0 ) { if (part.offset.x() != m_origin.x() || part.offset.y() != m_origin.y()) { part.offset = Vec2d(m_origin.x(), m_origin.y()); part.update_buffer(); } render_logo_texture(*(part.texture), *(part.buffer), bottom, part.vbo_id); } } } // render cali texture if (render_cali) { for (auto& part : m_partplate_list->cali_texture_info.parts) { if (part.texture) { if (part.buffer && part.buffer->get_vertices_count() > 0) { if (part.offset.x() != m_origin.x() || part.offset.y() != m_origin.y()) { part.offset = Vec2d(m_origin.x(), m_origin.y()); part.update_buffer(); } render_logo_texture(*(part.texture), *(part.buffer), bottom, part.vbo_id); } } } } } void PartPlate::render_exclude_area(bool force_default_color) const { if (force_default_color) //for thumbnail case return; unsigned int triangles_vcount = m_exclude_triangles.get_vertices_count(); std::array select_color{ 0.765f, 0.7686f, 0.7686f, 1.0f }; std::array unselect_color{ 0.9f, 0.9f, 0.9f, 1.0f }; std::array default_color{ 0.9f, 0.9f, 0.9f, 1.0f }; // draw exclude area glsafe(::glDepthMask(GL_FALSE)); if (m_selected) { glsafe(::glColor4fv(select_color.data())); } else { glsafe(::glColor4fv(unselect_color.data())); } glsafe(::glNormal3d(0.0f, 0.0f, 1.0f)); glsafe(::glVertexPointer(3, GL_FLOAT, m_exclude_triangles.get_vertex_data_size(), (GLvoid*)m_exclude_triangles.get_vertices_data())); glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount)); glsafe(::glDepthMask(GL_TRUE)); } /*void PartPlate::render_background_for_picking(const float* render_color) const { unsigned int triangles_vcount = m_triangles.get_vertices_count(); glsafe(::glDepthMask(GL_FALSE)); glsafe(::glColor4fv(render_color)); glsafe(::glNormal3d(0.0f, 0.0f, 1.0f)); glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_vertices_data())); glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount)); glsafe(::glDepthMask(GL_TRUE)); }*/ void PartPlate::render_grid(bool bottom) const { //glsafe(::glEnable(GL_MULTISAMPLE)); // draw grid glsafe(::glLineWidth(1.0f * m_scale_factor)); if (bottom) glsafe(::glColor4fv(LINE_BOTTOM_COLOR.data())); else { if (m_selected) glsafe(m_partplate_list->m_is_dark ? ::glColor4fv(LINE_TOP_SEL_DARK_COLOR.data()) : ::glColor4fv(LINE_TOP_SEL_COLOR.data())); else glsafe(m_partplate_list->m_is_dark ? ::glColor4fv(LINE_TOP_DARK_COLOR.data()) : ::glColor4fv(LINE_TOP_COLOR.data())); } glsafe(::glVertexPointer(3, GL_FLOAT, m_gridlines.get_vertex_data_size(), (GLvoid*)m_gridlines.get_vertices_data())); glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)m_gridlines.get_vertices_count())); glsafe(::glLineWidth(2.0f * m_scale_factor)); glsafe(::glVertexPointer(3, GL_FLOAT, m_gridlines_bolder.get_vertex_data_size(), (GLvoid*)m_gridlines_bolder.get_vertices_data())); glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)m_gridlines_bolder.get_vertices_count())); } void PartPlate::render_height_limit(PartPlate::HeightLimitMode mode) const { if (m_print && m_print->config().print_sequence == PrintSequence::ByObject && mode != HEIGHT_LIMIT_NONE) { // draw lower limit glsafe(::glLineWidth(3.0f * m_scale_factor)); glsafe(::glColor4fv(HEIGHT_LIMIT_BOTTOM_COLOR.data())); glsafe(::glVertexPointer(3, GL_FLOAT, m_height_limit_common.get_vertex_data_size(), (GLvoid*)m_height_limit_common.get_vertices_data())); glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)m_height_limit_common.get_vertices_count())); if ((mode == HEIGHT_LIMIT_BOTTOM) || (mode == HEIGHT_LIMIT_BOTH)) { glsafe(::glLineWidth(3.0f * m_scale_factor)); glsafe(::glColor4fv(HEIGHT_LIMIT_BOTTOM_COLOR.data())); glsafe(::glVertexPointer(3, GL_FLOAT, m_height_limit_bottom.get_vertex_data_size(), (GLvoid*)m_height_limit_bottom.get_vertices_data())); glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)m_height_limit_bottom.get_vertices_count())); } // draw upper limit if ((mode == HEIGHT_LIMIT_TOP) || (mode == HEIGHT_LIMIT_BOTH)){ glsafe(::glLineWidth(3.0f * m_scale_factor)); glsafe(::glColor4fv(HEIGHT_LIMIT_TOP_COLOR.data())); glsafe(::glVertexPointer(3, GL_FLOAT, m_height_limit_top.get_vertex_data_size(), (GLvoid*)m_height_limit_top.get_vertices_data())); glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)m_height_limit_top.get_vertices_count())); } } } void PartPlate::render_icon_texture(int position_id, int tex_coords_id, const GeometryBuffer &buffer, GLTexture &texture, unsigned int &vbo_id) const { if (vbo_id == 0) { glsafe(::glGenBuffers(1, &vbo_id)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, vbo_id)); glsafe(::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)buffer.get_vertices_data_size(), (const GLvoid*)buffer.get_vertices_data(), GL_STATIC_DRAW)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); } unsigned int stride = buffer.get_vertex_data_size(); GLuint tex_id = (GLuint)texture.get_id(); glsafe(::glBindTexture(GL_TEXTURE_2D, tex_id)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, vbo_id)); if (position_id != -1) glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)buffer.get_position_offset())); if (tex_coords_id != -1) glsafe(::glVertexAttribPointer(tex_coords_id, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)buffer.get_tex_coords_offset())); glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)buffer.get_vertices_count())); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); } void PartPlate::render_plate_name_texture(int position_id, int tex_coords_id) { if (m_name_change) { m_name_change = false; if (m_plate_name_vbo_id > 0) { glsafe(::glDeleteBuffers(1, &m_plate_name_vbo_id)); m_plate_name_vbo_id = 0; } if (m_plate_name_edit_vbo_id > 0) { glsafe(::glDeleteBuffers(1, &m_plate_name_edit_vbo_id)); m_plate_name_edit_vbo_id = 0; } } if (m_plate_name_vbo_id==0) { if (generate_plate_name_texture()) { calc_vertex_for_plate_name(m_name_texture, m_plate_name_icon); if (m_plate_name_edit_vbo_id > 0) { //for redo glsafe(::glDeleteBuffers(1, &m_plate_name_edit_vbo_id)); m_plate_name_edit_vbo_id = 0; } calc_vertex_for_plate_name_edit_icon(&m_name_texture, 0, m_plate_name_edit_icon); } else { if (m_plate_name_edit_vbo_id==0) { calc_vertex_for_plate_name_edit_icon(nullptr, 0, m_plate_name_edit_icon); } return; } } if (m_plate_name_vbo_id == 0 && m_plate_name_icon.get_vertices_data_size() > 0) { glsafe(::glGenBuffers(1, &m_plate_name_vbo_id)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_plate_name_vbo_id)); glsafe(::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr) m_plate_name_icon.get_vertices_data_size(), (const GLvoid *) m_plate_name_icon.get_vertices_data(), GL_STATIC_DRAW)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); } unsigned int stride = m_plate_name_icon.get_vertex_data_size(); GLuint tex_id = (GLuint) m_name_texture.get_id(); glsafe(::glBindTexture(GL_TEXTURE_2D, tex_id)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_plate_name_vbo_id)); if (position_id != -1) glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid *) (intptr_t) m_plate_name_icon.get_position_offset())); if (tex_coords_id != -1) glsafe(::glVertexAttribPointer(tex_coords_id, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid *) (intptr_t) m_plate_name_icon.get_tex_coords_offset())); glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei) m_plate_name_icon.get_vertices_count())); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); } void PartPlate::render_icons(bool bottom, bool only_body, int hover_id) { GLShaderProgram* shader = wxGetApp().get_shader("printbed"); if (shader != nullptr) { shader->start_using(); shader->set_uniform("transparent_background", bottom); //shader->set_uniform("svg_source", boost::algorithm::iends_with(m_partplate_list->m_del_texture.get_source(), ".svg")); shader->set_uniform("svg_source", 0); //if (bottom) // glsafe(::glFrontFace(GL_CW)); glsafe(::glDepthMask(GL_FALSE)); GLint position_id = shader->get_attrib_location("v_position"); GLint tex_coords_id = shader->get_attrib_location("v_tex_coords"); if (position_id != -1) { glsafe(::glEnableVertexAttribArray(position_id)); } if (tex_coords_id != -1) { glsafe(::glEnableVertexAttribArray(tex_coords_id)); } render_plate_name_texture(position_id, tex_coords_id); if (!only_body) { if (hover_id == 1) render_icon_texture(position_id, tex_coords_id, m_del_icon, m_partplate_list->m_del_hovered_texture, m_del_vbo_id); else render_icon_texture(position_id, tex_coords_id, m_del_icon, m_partplate_list->m_del_texture, m_del_vbo_id); if (hover_id == 2) render_icon_texture(position_id, tex_coords_id, m_orient_icon, m_partplate_list->m_orient_hovered_texture, m_orient_vbo_id); else render_icon_texture(position_id, tex_coords_id, m_orient_icon, m_partplate_list->m_orient_texture, m_orient_vbo_id); if (hover_id == 3) render_icon_texture(position_id, tex_coords_id, m_arrange_icon, m_partplate_list->m_arrange_hovered_texture, m_arrange_vbo_id); else render_icon_texture(position_id, tex_coords_id, m_arrange_icon, m_partplate_list->m_arrange_texture, m_arrange_vbo_id); if (hover_id == 4) { if (this->is_locked()) render_icon_texture(position_id, tex_coords_id, m_lock_icon, m_partplate_list->m_locked_hovered_texture, m_lock_vbo_id); else render_icon_texture(position_id, tex_coords_id, m_lock_icon, m_partplate_list->m_lockopen_hovered_texture, m_lock_vbo_id); } else { if (this->is_locked()) render_icon_texture(position_id, tex_coords_id, m_lock_icon, m_partplate_list->m_locked_texture, m_lock_vbo_id); else render_icon_texture(position_id, tex_coords_id, m_lock_icon, m_partplate_list->m_lockopen_texture, m_lock_vbo_id); } if (hover_id == 6) render_icon_texture(position_id, tex_coords_id, m_plate_name_edit_icon, m_partplate_list->m_plate_name_edit_hovered_texture, m_plate_name_edit_vbo_id); else render_icon_texture(position_id, tex_coords_id, m_plate_name_edit_icon, m_partplate_list->m_plate_name_edit_texture, m_plate_name_edit_vbo_id); if (m_partplate_list->render_plate_settings) { bool has_plate_settings = get_bed_type() != BedType::btDefault || get_print_seq() != PrintSequence::ByDefault || !get_first_layer_print_sequence().empty() || !get_other_layers_print_sequence().empty() || has_spiral_mode_config(); if (hover_id == 5) { if (!has_plate_settings) render_icon_texture(position_id, tex_coords_id, m_plate_settings_icon, m_partplate_list->m_plate_settings_hovered_texture, m_plate_settings_vbo_id); else render_icon_texture(position_id, tex_coords_id, m_plate_settings_icon, m_partplate_list->m_plate_settings_changed_hovered_texture, m_plate_settings_vbo_id); } else { if (!has_plate_settings) render_icon_texture(position_id, tex_coords_id, m_plate_settings_icon, m_partplate_list->m_plate_settings_texture, m_plate_settings_vbo_id); else render_icon_texture(position_id, tex_coords_id, m_plate_settings_icon, m_partplate_list->m_plate_settings_changed_texture, m_plate_settings_vbo_id); } } if (m_plate_index >= 0 && m_plate_index < MAX_PLATE_COUNT) { render_icon_texture(position_id, tex_coords_id, m_plate_idx_icon, m_partplate_list->m_idx_textures[m_plate_index], m_plate_idx_vbo_id); } } if (tex_coords_id != -1) glsafe(::glDisableVertexAttribArray(tex_coords_id)); if (position_id != -1) glsafe(::glDisableVertexAttribArray(position_id)); //if (bottom) // glsafe(::glFrontFace(GL_CCW)); glsafe(::glDepthMask(GL_TRUE)); shader->stop_using(); } } void PartPlate::render_only_numbers(bool bottom) const { GLShaderProgram* shader = wxGetApp().get_shader("printbed"); if (shader != nullptr) { shader->start_using(); shader->set_uniform("transparent_background", bottom); //shader->set_uniform("svg_source", boost::algorithm::iends_with(m_partplate_list->m_del_texture.get_source(), ".svg")); shader->set_uniform("svg_source", 0); //if (bottom) // glsafe(::glFrontFace(GL_CW)); glsafe(::glDepthMask(GL_FALSE)); GLint position_id = shader->get_attrib_location("v_position"); GLint tex_coords_id = shader->get_attrib_location("v_tex_coords"); if (position_id != -1) { glsafe(::glEnableVertexAttribArray(position_id)); } if (tex_coords_id != -1) { glsafe(::glEnableVertexAttribArray(tex_coords_id)); } if (m_plate_index >=0 && m_plate_index < MAX_PLATE_COUNT) { render_icon_texture(position_id, tex_coords_id, m_plate_idx_icon, m_partplate_list->m_idx_textures[m_plate_index], m_plate_idx_vbo_id); } if (tex_coords_id != -1) glsafe(::glDisableVertexAttribArray(tex_coords_id)); if (position_id != -1) glsafe(::glDisableVertexAttribArray(position_id)); //if (bottom) // glsafe(::glFrontFace(GL_CCW)); glsafe(::glDepthMask(GL_TRUE)); shader->stop_using(); } } void PartPlate::render_rectangle_for_picking(const GeometryBuffer &buffer, const float* render_color) const { unsigned int triangles_vcount = buffer.get_vertices_count(); //glsafe(::glDepthMask(GL_FALSE)); glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); glsafe(::glColor4fv(render_color)); glsafe(::glNormal3d(0.0f, 0.0f, 1.0f)); glsafe(::glVertexPointer(3, GL_FLOAT, buffer.get_vertex_data_size(), (GLvoid*)buffer.get_vertices_data())); glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount)); glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); //glsafe(::glDepthMask(GL_TRUE)); } void PartPlate::render_label(GLCanvas3D& canvas) const { std::string label = (boost::format("Plate %1%") % (m_plate_index + 1)).str(); const Camera& camera = wxGetApp().plater()->get_camera(); Transform3d world_to_eye = camera.get_view_matrix(); Transform3d world_to_screen = camera.get_projection_matrix() * world_to_eye; const std::array& viewport = camera.get_viewport(); BoundingBoxf3* bounding_box = const_cast(&m_bounding_box); Vec3d screen_box_center = world_to_screen * bounding_box->min; float x = 0.0f; float y = 0.0f; if (camera.get_type() == Camera::EType::Perspective) { x = (0.5f + 0.001f * 0.5f * (float)screen_box_center(0)) * viewport[2]; y = (0.5f - 0.001f * 0.5f * (float)screen_box_center(1)) * viewport[3]; } else { x = (0.5f + 0.5f * (float)screen_box_center(0)) * viewport[2]; y = (0.5f - 0.5f * (float)screen_box_center(1)) * viewport[3]; } ImGuiWrapper& imgui = *wxGetApp().imgui(); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.5f); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.75f, 0.75f, 0.75f, 1.0f)); imgui.set_next_window_pos(x, y, ImGuiCond_Always, 0.5f, 0.5f); imgui.begin(label, ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); float win_w = ImGui::GetWindowWidth(); float label_len = imgui.calc_text_size(label).x; ImGui::SetCursorPosX(0.5f * (win_w - label_len)); ImGui::AlignTextToFramePadding(); imgui.text(label); // force re-render while the windows gets to its final size (it takes several frames) if (ImGui::GetWindowContentRegionWidth() + 2.0f * ImGui::GetStyle().WindowPadding.x != ImGui::CalcWindowNextAutoFitSize(ImGui::GetCurrentWindow()).x) canvas.request_extra_frame(); imgui.end(); ImGui::PopStyleColor(); ImGui::PopStyleVar(2); } void PartPlate::render_grabber(const float* render_color, bool use_lighting) const { BoundingBoxf3* bounding_box = const_cast(&m_bounding_box); const Vec3d& center = m_grabber_box.center(); if (use_lighting) glsafe(::glEnable(GL_LIGHTING)); glsafe(::glColor4fv(render_color)); glsafe(::glPushMatrix()); glsafe(::glTranslated(center(0), center(1), center(2))); Vec3d angles(Vec3d::Zero()); glsafe(::glRotated(Geometry::rad2deg(angles(2)), 0.0, 0.0, 1.0)); glsafe(::glRotated(Geometry::rad2deg(angles(1)), 0.0, 1.0, 0.0)); glsafe(::glRotated(Geometry::rad2deg(angles(0)), 1.0, 0.0, 0.0)); float half_x = bounding_box->size().x() * GRABBER_X_FACTOR; float half_y = bounding_box->size().y() * GRABBER_Y_FACTOR; float half_z = GRABBER_Z_VALUE; // face min x glsafe(::glPushMatrix()); glsafe(::glTranslatef(-(GLfloat)half_x, 0, 0.0f)); glsafe(::glRotatef(-90.0f, 0.0f, 1.0f, 0.0f)); render_face(half_z, half_y); glsafe(::glPopMatrix()); // face max x glsafe(::glPushMatrix()); glsafe(::glTranslatef((GLfloat)half_x, 0, 0.0f)); glsafe(::glRotatef(90.0f, 0.0f, 1.0f, 0.0f)); render_face(half_z, half_y); glsafe(::glPopMatrix()); // face min y glsafe(::glPushMatrix()); glsafe(::glTranslatef(0.0f, -(GLfloat)half_y, 0.0f)); glsafe(::glRotatef(90.0f, 1.0f, 0.0f, 0.0f)); render_face(half_x, half_z); glsafe(::glPopMatrix()); // face max y glsafe(::glPushMatrix()); glsafe(::glTranslatef(0.0f, (GLfloat)half_y, 0.0f)); glsafe(::glRotatef(-90.0f, 1.0f, 0.0f, 0.0f)); render_face(half_x, half_z); glsafe(::glPopMatrix()); // face min z glsafe(::glPushMatrix()); glsafe(::glTranslatef(0.0f, 0.0f, -(GLfloat)half_z)); glsafe(::glRotatef(180.0f, 1.0f, 0.0f, 0.0f)); render_face(half_x, half_y); glsafe(::glPopMatrix()); // face max z glsafe(::glPushMatrix()); glsafe(::glTranslatef(0.0f, 0.0f, (GLfloat)half_z)); render_face(half_x, half_y); glsafe(::glPopMatrix()); glsafe(::glPopMatrix()); if (use_lighting) glsafe(::glDisable(GL_LIGHTING)); } void PartPlate::render_face(float x_size, float y_size) const { ::glBegin(GL_TRIANGLES); ::glNormal3f(0.0f, 0.0f, 1.0f); ::glVertex3f(-(GLfloat)x_size, -(GLfloat)y_size, 0.0f); ::glVertex3f((GLfloat)x_size, -(GLfloat)y_size, 0.0f); ::glVertex3f((GLfloat)x_size, (GLfloat)y_size, 0.0f); ::glVertex3f((GLfloat)x_size, (GLfloat)y_size, 0.0f); ::glVertex3f(-(GLfloat)x_size, (GLfloat)y_size, 0.0f); ::glVertex3f(-(GLfloat)x_size, -(GLfloat)y_size, 0.0f); glsafe(::glEnd()); } void PartPlate::render_arrows(const float* render_color, bool use_lighting) const { #if 0 if (m_quadric == nullptr) return; double radius = m_grabber_box.size().y() * 0.5f; double height = radius * 2.0f; double position = m_grabber_box.size().x() * 0.8f; if (use_lighting) glsafe(::glEnable(GL_LIGHTING)); glsafe(::glColor4fv(render_color)); glsafe(::glPushMatrix()); glsafe(::glTranslated(m_grabber_box.center().x(), m_grabber_box.center().y(), m_grabber_box.center().z())); glsafe(::glRotated(90.0, 0.0, 1.0, 0.0)); glsafe(::glTranslated(0.0, 0.0, position)); ::gluQuadricOrientation(m_quadric, GLU_OUTSIDE); ::gluCylinder(m_quadric, 0.9 * radius, 0.0, height, 36, 1); ::gluQuadricOrientation(m_quadric, GLU_INSIDE); ::gluDisk(m_quadric, 0.0, 0.9 * radius, 36, 1); glsafe(::glPopMatrix()); glsafe(::glPushMatrix()); glsafe(::glTranslated(m_grabber_box.center().x(), m_grabber_box.center().y(), m_grabber_box.center().z())); glsafe(::glRotated(-90.0, 0.0, 1.0, 0.0)); glsafe(::glTranslated(0.0, 0.0, position)); ::gluQuadricOrientation(m_quadric, GLU_OUTSIDE); ::gluCylinder(m_quadric, 0.9 * radius, 0.0, height, 36, 1); ::gluQuadricOrientation(m_quadric, GLU_INSIDE); ::gluDisk(m_quadric, 0.0, 0.9 * radius, 36, 1); glsafe(::glPopMatrix()); if (use_lighting) glsafe(::glDisable(GL_LIGHTING)); #endif } void PartPlate::render_left_arrow(const float* render_color, bool use_lighting) const { #if 0 if (m_quadric == nullptr) return; double radius = m_grabber_box.size().y() * 0.5f; double height = radius * 2.0f; double position = m_grabber_box.size().x() * 0.8f; if (use_lighting) glsafe(::glEnable(GL_LIGHTING)); glsafe(::glColor4fv(render_color)); glsafe(::glPushMatrix()); glsafe(::glTranslated(m_grabber_box.center().x(), m_grabber_box.center().y(), m_grabber_box.center().z())); glsafe(::glRotated(-90.0, 0.0, 1.0, 0.0)); glsafe(::glTranslated(0.0, 0.0, position)); ::gluQuadricOrientation(m_quadric, GLU_OUTSIDE); ::gluCylinder(m_quadric, 0.9 * radius, 0.0, height, 36, 1); ::gluQuadricOrientation(m_quadric, GLU_INSIDE); ::gluDisk(m_quadric, 0.0, 0.9 * radius, 36, 1); glsafe(::glPopMatrix()); if (use_lighting) glsafe(::glDisable(GL_LIGHTING)); #endif } void PartPlate::render_right_arrow(const float* render_color, bool use_lighting) const { #if 0 if (m_quadric == nullptr) return; double radius = m_grabber_box.size().y() * 0.5f; double height = radius * 2.0f; double position = m_grabber_box.size().x() * 0.8f; if (use_lighting) glsafe(::glEnable(GL_LIGHTING)); glsafe(::glColor4fv(render_color)); glsafe(::glPushMatrix()); glsafe(::glTranslated(m_grabber_box.center().x(), m_grabber_box.center().y(), m_grabber_box.center().z())); glsafe(::glRotated(90.0, 0.0, 1.0, 0.0)); glsafe(::glTranslated(0.0, 0.0, position)); ::gluQuadricOrientation(m_quadric, GLU_OUTSIDE); ::gluCylinder(m_quadric, 0.9 * radius, 0.0, height, 36, 1); ::gluQuadricOrientation(m_quadric, GLU_INSIDE); ::gluDisk(m_quadric, 0.0, 0.9 * radius, 36, 1); glsafe(::glPopMatrix()); if (use_lighting) glsafe(::glDisable(GL_LIGHTING)); #endif } void PartPlate::on_render_for_picking() const { //glsafe(::glDisable(GL_DEPTH_TEST)); int hover_id = 0; std::array color = picking_color_component(hover_id); m_grabber_color[0] = color[0]; m_grabber_color[1] = color[1]; m_grabber_color[2] = color[2]; m_grabber_color[3] = color[3]; //render_grabber(m_grabber_color, false); render_rectangle_for_picking(m_triangles, m_grabber_color); hover_id = 1; color = picking_color_component(hover_id); m_grabber_color[0] = color[0]; m_grabber_color[1] = color[1]; m_grabber_color[2] = color[2]; m_grabber_color[3] = color[3]; //render_left_arrow(m_grabber_color, false); render_rectangle_for_picking(m_del_icon, m_grabber_color); hover_id = 2; color = picking_color_component(hover_id); m_grabber_color[0] = color[0]; m_grabber_color[1] = color[1]; m_grabber_color[2] = color[2]; m_grabber_color[3] = color[3]; render_rectangle_for_picking(m_orient_icon, m_grabber_color); hover_id = 3; color = picking_color_component(hover_id); m_grabber_color[0] = color[0]; m_grabber_color[1] = color[1]; m_grabber_color[2] = color[2]; m_grabber_color[3] = color[3]; render_rectangle_for_picking(m_arrange_icon, m_grabber_color); hover_id = 4; color = picking_color_component(hover_id); m_grabber_color[0] = color[0]; m_grabber_color[1] = color[1]; m_grabber_color[2] = color[2]; m_grabber_color[3] = color[3]; //render_right_arrow(m_grabber_color, false); render_rectangle_for_picking(m_lock_icon, m_grabber_color); hover_id = 5; color = picking_color_component(hover_id); m_grabber_color[0] = color[0]; m_grabber_color[1] = color[1]; m_grabber_color[2] = color[2]; m_grabber_color[3] = color[3]; if (m_partplate_list->render_plate_settings) render_rectangle_for_picking(m_plate_settings_icon, m_grabber_color); hover_id = 6; color = picking_color_component(hover_id); m_grabber_color[0] = color[0]; m_grabber_color[1] = color[1]; m_grabber_color[2] = color[2]; m_grabber_color[3] = color[3]; // render_left_arrow(m_grabber_color, false); render_rectangle_for_picking(m_plate_name_edit_icon, m_grabber_color); } std::array PartPlate::picking_color_component(int idx) const { static const float INV_255 = 1.0f / 255.0f; unsigned int id = PLATE_BASE_ID - this->m_plate_index * GRABBER_COUNT - idx; return std::array { float((id >> 0) & 0xff)* INV_255, // red float((id >> 8) & 0xff)* INV_255, // greeen float((id >> 16) & 0xff)* INV_255, // blue float(picking_checksum_alpha_channel(id & 0xff, (id >> 8) & 0xff, (id >> 16) & 0xff))* INV_255 }; } void PartPlate::release_opengl_resource() { if (m_vbo_id > 0) { glsafe(::glDeleteBuffers(1, &m_vbo_id)); m_vbo_id = 0; } if (m_del_vbo_id > 0) { glsafe(::glDeleteBuffers(1, &m_del_vbo_id)); m_del_vbo_id = 0; } if (m_orient_vbo_id > 0) { glsafe(::glDeleteBuffers(1, &m_orient_vbo_id)); m_orient_vbo_id = 0; } if (m_arrange_vbo_id > 0) { glsafe(::glDeleteBuffers(1, &m_arrange_vbo_id)); m_arrange_vbo_id = 0; } if (m_lock_vbo_id > 0) { glsafe(::glDeleteBuffers(1, &m_lock_vbo_id)); m_lock_vbo_id = 0; } if (m_plate_settings_vbo_id > 0) { glsafe(::glDeleteBuffers(1, &m_plate_settings_vbo_id)); m_plate_settings_vbo_id = 0; } if (m_plate_idx_vbo_id > 0) { glsafe(::glDeleteBuffers(1, &m_plate_idx_vbo_id)); m_plate_idx_vbo_id = 0; } if (m_plate_name_vbo_id > 0) { glsafe(::glDeleteBuffers(1, &m_plate_name_vbo_id)); m_plate_name_vbo_id = 0; } if (m_plate_name_edit_vbo_id > 0) { glsafe(::glDeleteBuffers(1, &m_plate_name_edit_vbo_id)); m_plate_name_edit_vbo_id = 0; } } std::vector PartPlate::get_extruders(bool conside_custom_gcode) const { std::vector plate_extruders; // if gcode.3mf file if (m_model->objects.empty()) { for (int i = 0; i < slice_filaments_info.size(); i++) { plate_extruders.push_back(slice_filaments_info[i].id + 1); } return plate_extruders; } // if 3mf file const DynamicPrintConfig& glb_config = wxGetApp().preset_bundle->prints.get_edited_preset().config; int glb_support_intf_extr = glb_config.opt_int("support_interface_filament"); int glb_support_extr = glb_config.opt_int("support_filament"); bool glb_support = glb_config.opt_bool("enable_support"); glb_support |= glb_config.opt_int("raft_layers") > 0; for (int obj_idx = 0; obj_idx < m_model->objects.size(); obj_idx++) { if (!contain_instance_totally(obj_idx, 0)) continue; ModelObject* mo = m_model->objects[obj_idx]; for (ModelVolume* mv : mo->volumes) { std::vector volume_extruders = mv->get_extruders(); plate_extruders.insert(plate_extruders.end(), volume_extruders.begin(), volume_extruders.end()); } // layer range for (auto layer_range : mo->layer_config_ranges) { if (layer_range.second.has("extruder")) { if (auto id = layer_range.second.option("extruder")->getInt(); id > 0) plate_extruders.push_back(id); } } bool obj_support = false; const ConfigOption* obj_support_opt = mo->config.option("enable_support"); const ConfigOption *obj_raft_opt = mo->config.option("raft_layers"); if (obj_support_opt != nullptr || obj_raft_opt != nullptr) { if (obj_support_opt != nullptr) obj_support = obj_support_opt->getBool(); if (obj_raft_opt != nullptr) obj_support |= obj_raft_opt->getInt() > 0; } else obj_support = glb_support; if (!obj_support) continue; int obj_support_intf_extr = 0; const ConfigOption* support_intf_extr_opt = mo->config.option("support_interface_filament"); if (support_intf_extr_opt != nullptr) obj_support_intf_extr = support_intf_extr_opt->getInt(); if (obj_support_intf_extr != 0) plate_extruders.push_back(obj_support_intf_extr); else if (glb_support_intf_extr != 0) plate_extruders.push_back(glb_support_intf_extr); int obj_support_extr = 0; const ConfigOption* support_extr_opt = mo->config.option("support_filament"); if (support_extr_opt != nullptr) obj_support_extr = support_extr_opt->getInt(); if (obj_support_extr != 0) plate_extruders.push_back(obj_support_extr); else if (glb_support_extr != 0) plate_extruders.push_back(glb_support_extr); } if (conside_custom_gcode) { //BBS int nums_extruders = 0; if (const ConfigOptionStrings *color_option = dynamic_cast(wxGetApp().preset_bundle->project_config.option("filament_colour"))) { nums_extruders = color_option->values.size(); if (m_model->plates_custom_gcodes.find(m_plate_index) != m_model->plates_custom_gcodes.end()) { for (auto item : m_model->plates_custom_gcodes.at(m_plate_index).gcodes) { if (item.type == CustomGCode::Type::ToolChange && item.extruder <= nums_extruders) plate_extruders.push_back(item.extruder); } } } } std::sort(plate_extruders.begin(), plate_extruders.end()); auto it_end = std::unique(plate_extruders.begin(), plate_extruders.end()); plate_extruders.resize(std::distance(plate_extruders.begin(), it_end)); return plate_extruders; } std::vector PartPlate::get_extruders_under_cli(bool conside_custom_gcode, DynamicPrintConfig& full_config) const { std::vector plate_extruders; // if 3mf file int glb_support_intf_extr = full_config.opt_int("support_interface_filament"); int glb_support_extr = full_config.opt_int("support_filament"); bool glb_support = full_config.opt_bool("enable_support"); glb_support |= full_config.opt_int("raft_layers") > 0; for (std::set>::iterator it = obj_to_instance_set.begin(); it != obj_to_instance_set.end(); ++it) { int obj_id = it->first; int instance_id = it->second; if ((obj_id >= 0) && (obj_id < m_model->objects.size())) { ModelObject* object = m_model->objects[obj_id]; ModelInstance* instance = object->instances[instance_id]; if (!instance->printable) continue; for (ModelVolume* mv : object->volumes) { std::vector volume_extruders = mv->get_extruders(); plate_extruders.insert(plate_extruders.end(), volume_extruders.begin(), volume_extruders.end()); } // layer range for (auto layer_range : object->layer_config_ranges) { if (layer_range.second.has("extruder")) { if (auto id = layer_range.second.option("extruder")->getInt(); id > 0) plate_extruders.push_back(id); } } bool obj_support = false; const ConfigOption* obj_support_opt = object->config.option("enable_support"); const ConfigOption *obj_raft_opt = object->config.option("raft_layers"); if (obj_support_opt != nullptr || obj_raft_opt != nullptr) { if (obj_support_opt != nullptr) obj_support = obj_support_opt->getBool(); if (obj_raft_opt != nullptr) obj_support |= obj_raft_opt->getInt() > 0; } else obj_support = glb_support; if (!obj_support) continue; int obj_support_intf_extr = 0; const ConfigOption* support_intf_extr_opt = object->config.option("support_interface_filament"); if (support_intf_extr_opt != nullptr) obj_support_intf_extr = support_intf_extr_opt->getInt(); if (obj_support_intf_extr != 0) plate_extruders.push_back(obj_support_intf_extr); else if (glb_support_intf_extr != 0) plate_extruders.push_back(glb_support_intf_extr); int obj_support_extr = 0; const ConfigOption* support_extr_opt = object->config.option("support_filament"); if (support_extr_opt != nullptr) obj_support_extr = support_extr_opt->getInt(); if (obj_support_extr != 0) plate_extruders.push_back(obj_support_extr); else if (glb_support_extr != 0) plate_extruders.push_back(glb_support_extr); } } if (conside_custom_gcode) { //BBS int nums_extruders = 0; if (const ConfigOptionStrings *color_option = dynamic_cast(full_config.option("filament_colour"))) { nums_extruders = color_option->values.size(); if (m_model->plates_custom_gcodes.find(m_plate_index) != m_model->plates_custom_gcodes.end()) { for (auto item : m_model->plates_custom_gcodes.at(m_plate_index).gcodes) { if (item.type == CustomGCode::Type::ToolChange && item.extruder <= nums_extruders) plate_extruders.push_back(item.extruder); } } } } std::sort(plate_extruders.begin(), plate_extruders.end()); auto it_end = std::unique(plate_extruders.begin(), plate_extruders.end()); plate_extruders.resize(std::distance(plate_extruders.begin(), it_end)); return plate_extruders; } std::vector PartPlate::get_extruders_without_support(bool conside_custom_gcode) const { std::vector plate_extruders; // if gcode.3mf file if (m_model->objects.empty()) { for (int i = 0; i < slice_filaments_info.size(); i++) { plate_extruders.push_back(slice_filaments_info[i].id + 1); } return plate_extruders; } // if 3mf file const DynamicPrintConfig& glb_config = wxGetApp().preset_bundle->prints.get_edited_preset().config; for (int obj_idx = 0; obj_idx < m_model->objects.size(); obj_idx++) { if (!contain_instance_totally(obj_idx, 0)) continue; ModelObject* mo = m_model->objects[obj_idx]; for (ModelVolume* mv : mo->volumes) { std::vector volume_extruders = mv->get_extruders(); plate_extruders.insert(plate_extruders.end(), volume_extruders.begin(), volume_extruders.end()); } } if (conside_custom_gcode) { //BBS int nums_extruders = 0; if (const ConfigOptionStrings* color_option = dynamic_cast(wxGetApp().preset_bundle->project_config.option("filament_colour"))) { nums_extruders = color_option->values.size(); if (m_model->plates_custom_gcodes.find(m_plate_index) != m_model->plates_custom_gcodes.end()) { for (auto item : m_model->plates_custom_gcodes.at(m_plate_index).gcodes) { if (item.type == CustomGCode::Type::ToolChange && item.extruder <= nums_extruders) plate_extruders.push_back(item.extruder); } } } } std::sort(plate_extruders.begin(), plate_extruders.end()); auto it_end = std::unique(plate_extruders.begin(), plate_extruders.end()); plate_extruders.resize(std::distance(plate_extruders.begin(), it_end)); return plate_extruders; } std::vector PartPlate::get_used_extruders() { std::vector used_extruders; // if gcode.3mf file if (m_model->objects.empty()) { for (int i = 0; i < slice_filaments_info.size(); i++) { used_extruders.push_back(slice_filaments_info[i].id + 1); } return used_extruders; } GCodeProcessorResult* result = get_slice_result(); if (!result) return used_extruders; std::set used_extruders_set; PrintEstimatedStatistics& ps = result->print_statistics; for (const auto& item : ps.total_volumes_per_extruder) used_extruders_set.emplace(item.first + 1); return std::vector(used_extruders_set.begin(), used_extruders_set.end()); } Vec3d PartPlate::estimate_wipe_tower_size(const DynamicPrintConfig & config, const double w, const double wipe_volume, int plate_extruder_size, bool use_global_objects) const { Vec3d wipe_tower_size; double layer_height = 0.08f; // hard code layer height double max_height = 0.f; wipe_tower_size.setZero(); wipe_tower_size(0) = w; const ConfigOption* layer_height_opt = config.option("layer_height"); if (layer_height_opt) layer_height = layer_height_opt->getFloat(); // empty plate if (plate_extruder_size == 0) { std::vector plate_extruders = get_extruders(true); plate_extruder_size = plate_extruders.size(); } if (plate_extruder_size == 0) return wipe_tower_size; for (int obj_idx = 0; obj_idx < m_model->objects.size(); obj_idx++) { if (!use_global_objects && !contain_instance_totally(obj_idx, 0)) continue; BoundingBoxf3 bbox = m_model->objects[obj_idx]->bounding_box(); max_height = std::max(bbox.size().z(), max_height); } wipe_tower_size(2) = max_height; //const DynamicPrintConfig &dconfig = wxGetApp().preset_bundle->prints.get_edited_preset().config; auto timelapse_type = config.option>("timelapse_type"); bool timelapse_enabled = timelapse_type ? (timelapse_type->value == TimelapseType::tlSmooth) : false; double depth = wipe_volume * (plate_extruder_size - 1) / (layer_height * w); if (timelapse_enabled || depth > EPSILON) { float min_wipe_tower_depth = 0.f; auto iter = WipeTower::min_depth_per_height.begin(); while (iter != WipeTower::min_depth_per_height.end()) { auto curr_height_to_depth = *iter; // This is the case that wipe tower height is lower than the first min_depth_to_height member. if (curr_height_to_depth.first >= max_height) { min_wipe_tower_depth = curr_height_to_depth.second; break; } iter++; // If curr_height_to_depth is the last member, use its min_depth. if (iter == WipeTower::min_depth_per_height.end()) { min_wipe_tower_depth = curr_height_to_depth.second; break; } // If wipe tower height is between the current and next member, set the min_depth as linear interpolation between them auto next_height_to_depth = *iter; if (next_height_to_depth.first > max_height) { float height_base = curr_height_to_depth.first; float height_diff = next_height_to_depth.first - curr_height_to_depth.first; float min_depth_base = curr_height_to_depth.second; float depth_diff = next_height_to_depth.second - curr_height_to_depth.second; min_wipe_tower_depth = min_depth_base + (max_height - curr_height_to_depth.first) / height_diff * depth_diff; break; } } depth = std::max((double)min_wipe_tower_depth, depth); } wipe_tower_size(1) = depth; return wipe_tower_size; } arrangement::ArrangePolygon PartPlate::estimate_wipe_tower_polygon(const DynamicPrintConfig& config, int plate_index, int plate_extruder_size, bool use_global_objects) const { float x = dynamic_cast(config.option("wipe_tower_x"))->get_at(plate_index); float y = dynamic_cast(config.option("wipe_tower_y"))->get_at(plate_index); float w = dynamic_cast(config.option("prime_tower_width"))->value; //float a = dynamic_cast(config.option("wipe_tower_rotation_angle"))->value; float v = dynamic_cast(config.option("prime_volume"))->value; Vec3d wipe_tower_size = estimate_wipe_tower_size(config, w, v, plate_extruder_size, use_global_objects); int plate_width=m_width, plate_depth=m_depth; float depth = wipe_tower_size(1); float margin = WIPE_TOWER_MARGIN, wp_brim_width = 0.f; const ConfigOption* wipe_tower_brim_width_opt = config.option("prime_tower_brim_width"); if (wipe_tower_brim_width_opt) { wp_brim_width = wipe_tower_brim_width_opt->getFloat(); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("arrange wipe_tower: wp_brim_width %1%") % wp_brim_width; } x = std::clamp(x, margin, (float)plate_width - w - margin - wp_brim_width); y = std::clamp(y, margin, (float)plate_depth - depth - margin - wp_brim_width); arrangement::ArrangePolygon wipe_tower_ap; Polygon ap({ {scaled(x - wp_brim_width), scaled(y - wp_brim_width)}, {scaled(x + w + wp_brim_width), scaled(y - wp_brim_width)}, {scaled(x + w + wp_brim_width), scaled(y + depth + wp_brim_width)}, {scaled(x - wp_brim_width), scaled(y + depth + wp_brim_width)} }); wipe_tower_ap.bed_idx = plate_index; wipe_tower_ap.setter = NULL; // do not move wipe tower wipe_tower_ap.poly.contour = std::move(ap); wipe_tower_ap.translation = { scaled(0.f), scaled(0.f) }; //wipe_tower_ap.rotation = a; wipe_tower_ap.name = "WipeTower"; wipe_tower_ap.is_virt_object = true; wipe_tower_ap.is_wipe_tower = true; return wipe_tower_ap; } bool PartPlate::operator<(PartPlate& plate) const { int index = plate.get_index(); return (this->m_plate_index < index); } //set the plate's index void PartPlate::set_index(int index) { BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": plate_id update from %1% to %2%") % m_plate_index % index; m_plate_index = index; if (m_print != nullptr) m_print->set_plate_index(index); } void PartPlate::clear(bool clear_sliced_result) { obj_to_instance_set.clear(); instance_outside_set.clear(); if (clear_sliced_result) { m_ready_for_slice = true; update_slice_result_valid_state(false); } return; } /* size and position related functions*/ //set position and size void PartPlate::set_pos_and_size(Vec3d& origin, int width, int depth, int height, bool with_instance_move, bool do_clear) { bool size_changed = false; //size changed means the machine changed bool pos_changed = false; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate_id %1%, before, origin {%2%,%3%,%4%}, plate_width %5%, plate_depth %6%, plate_height %7%")\ % m_plate_index % m_origin.x() % m_origin.y() % m_origin.z() % m_width % m_depth % m_height; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": with_instance_move %1%, after, origin {%2%,%3%,%4%}, plate_width %5%, plate_depth %6%, plate_height %7%")\ % with_instance_move % origin.x() % origin.y() % origin.z() % width % depth % height; size_changed = ((width != m_width) || (depth != m_depth) || (height != m_height)); pos_changed = (m_origin != origin); if ((!size_changed) && (!pos_changed)) { //size and position the same with before, just return return; } if (with_instance_move && m_model) { for (std::set>::iterator it = obj_to_instance_set.begin(); it != obj_to_instance_set.end(); ++it) { int obj_id = it->first; int instance_id = it->second; ModelObject* object = m_model->objects[obj_id]; ModelInstance* instance = object->instances[instance_id]; //move this instance into the new plate's same position Vec3d offset = instance->get_transformation().get_offset(); int off_x, off_y; if (size_changed) { //change position due to the bed size changes //off_x = (width - m_width) * m_plate_index + (width - m_width) / 2; //off_y = (depth - m_depth) * m_plate_index + (depth - m_depth) / 2; off_x = origin.x() - m_origin.x() + (width - m_width) / 2; off_y = origin.y() - m_origin.y() + (depth - m_depth) / 2; } else { //change position due to the plate moves off_x = origin.x() - m_origin.x(); off_y = origin.y() - m_origin.y(); } offset.x() = offset.x() + off_x; offset.y() = offset.y() + off_y; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": object %1%, instance %2%, moved {%3%,%4%} to {%5%, %6%}")\ % obj_id % instance_id % off_x % off_y % offset.x() % offset.y(); instance->set_offset(offset); object->invalidate_bounding_box(); } } else if (do_clear) { clear(); } if (m_print) m_print->set_plate_origin(origin); m_origin = origin; m_width = width; m_depth = depth; m_height = height; return; } //get the plate's center point origin Vec3d PartPlate::get_center_origin() { Vec3d origin; origin(0) = (m_bounding_box.min(0) + m_bounding_box.max(0)) / 2;//m_origin.x() + m_width / 2; origin(1) = (m_bounding_box.min(1) + m_bounding_box.max(1)) / 2; //m_origin.y() + m_depth / 2; origin(2) = m_origin.z(); return origin; } bool PartPlate::generate_plate_name_texture() { auto bed_ext = get_extents(m_shape); int bed_width = bed_ext.size()(0); wxString cur_plate_name = from_u8(m_name); wxGCDC dc; wxString limitTextWidth = wxControl::Ellipsize(cur_plate_name, dc, wxELLIPSIZE_END, bed_width); if (limitTextWidth.size() ==4 && limitTextWidth.rfind("...") != std::string::npos && cur_plate_name.rfind('&') != std::string::npos) { // Avoided a bug where the last bit of Ellipsize api in the wxwidgets is an out of bounds array with the '&' symbol // wxwidgets version:3.2.2.1 for (auto it = cur_plate_name.rbegin(); it != cur_plate_name.rend(); ++it) { if (*it == '&') { cur_plate_name = cur_plate_name.RemoveLast(); } else { break; } } limitTextWidth = wxControl::Ellipsize(cur_plate_name, dc, wxELLIPSIZE_END, bed_width); } if (limitTextWidth.Length()==0) { return false; } // generate m_name_texture texture from m_name with generate_from_text_string m_name_texture.reset(); auto * font = &Label::Head_32; wxColour NumberForeground(PlateTextureForeground[0], PlateTextureForeground[1], PlateTextureForeground[2], PlateTextureForeground[3]); if (!m_name_texture.generate_from_text_string(limitTextWidth.ToUTF8().data(), *font, *wxBLACK, NumberForeground)) { BOOST_LOG_TRIVIAL(error) << "PartPlate::generate_plate_name_texture(): generate_from_text_string() failed"; return false; } return true; } std::string remove_invisible_ascii(const std::string &name) { std::string new_name; for (size_t i = 0; i < name.size(); i++) { if (int(name[i]) >= 0 && int(name[i]) < 32) { // 0x00 - 0x1F continue; } new_name += name[i]; } return new_name; } void PartPlate::set_plate_name(const std::string &name) { // compare if name equal to m_name, case sensitive if (boost::equals(m_name, name)) return; if (Plater::has_illegal_filename_characters(name)) { if(m_plater) Plater::show_illegal_characters_warning(m_plater); return; } if (m_plater) m_plater->take_snapshot("set_plate_name"); m_name = remove_invisible_ascii(name); m_name_change = true; if (m_plater) { ObjectList *obj_list = wxGetApp().obj_list(); if (obj_list) { obj_list->GetModel()->SetCurSelectedPlateFullName(m_plate_index, m_name); } } if (m_print != nullptr) m_print->set_plate_name(m_name); } //get the print's object, result and index void PartPlate::get_print(PrintBase** print, GCodeResult** result, int* index) { if (print && (printer_technology == PrinterTechnology::ptFFF)) *print = m_print; if (result) *result = m_gcode_result; if (index) *index = m_print_index; return; } //set the print object, result and it's index void PartPlate::set_print(PrintBase* print, GCodeResult* result, int index) { if (printer_technology == PrinterTechnology::ptFFF) m_print = static_cast(print); //todo, for other printers m_gcode_result = result; if (index >= 0) m_print_index = index; m_print->set_plate_origin(m_origin); return; } std::string PartPlate::get_gcode_filename() { if (is_slice_result_valid() && get_slice_result()) { return m_gcode_result->filename; } return ""; } bool PartPlate::is_valid_gcode_file() { if (get_gcode_filename().empty()) return false; boost::filesystem::path gcode_file(m_gcode_result->filename); if (!boost::filesystem::exists(gcode_file)) { BOOST_LOG_TRIVIAL(info) << "invalid gcode file, file is missing, file = " << m_gcode_result->filename; return false; } return true; } ModelObjectPtrs PartPlate::get_objects_on_this_plate() { ModelObjectPtrs objects_ptr; int obj_id; for (auto it = obj_to_instance_set.begin(); it != obj_to_instance_set.end(); it++) { obj_id = it->first; objects_ptr.push_back(m_model->objects[obj_id]); } return objects_ptr; } ModelInstance* PartPlate::get_instance(int obj_id, int instance_id) { if (!contain_instance(obj_id, instance_id)) return nullptr; else return m_model->objects[obj_id]->instances[instance_id]; } /* instance related operations*/ //judge whether instance is bound in plate or not bool PartPlate::contain_instance(int obj_id, int instance_id) { bool result = false; std::set>::iterator it; it = obj_to_instance_set.find(std::pair(obj_id, instance_id)); if (it != obj_to_instance_set.end()) { result = true; } return result; } //judge whether instance is bound in plate or not bool PartPlate::contain_instance_totally(ModelObject* object, int instance_id) const { bool result = false; int obj_id = -1; for (int index = 0; index < m_model->objects.size(); index ++) { if (m_model->objects[index] == object) { obj_id = index; break; } } if ((obj_id >= 0 ) && (obj_id < m_model->objects.size())) result = contain_instance_totally(obj_id, instance_id); return result; } //judge whether instance is totally included in plate or not bool PartPlate::contain_instance_totally(int obj_id, int instance_id) const { bool result = false; std::set>::iterator it; it = obj_to_instance_set.find(std::pair(obj_id, instance_id)); if (it != obj_to_instance_set.end()) { it = instance_outside_set.find(std::pair(obj_id, instance_id)); if (it == instance_outside_set.end()) result = true; } return result; } //check whether instance is outside the plate or not bool PartPlate::check_outside(int obj_id, int instance_id, BoundingBoxf3* bounding_box) { bool outside = true; ModelObject* object = m_model->objects[obj_id]; ModelInstance* instance = object->instances[instance_id]; BoundingBoxf3 instance_box = bounding_box? *bounding_box: object->instance_convex_hull_bounding_box(instance_id); Polygon hull = instance->convex_hull_2d(); BoundingBoxf3 plate_box = get_plate_box(); if (instance_box.max.z() > plate_box.min.z()) plate_box.min.z() += instance_box.min.z(); // not considering outsize if sinking if (plate_box.contains(instance_box)) { if (m_exclude_bounding_box.size() > 0) { int index; for (index = 0; index < m_exclude_bounding_box.size(); index ++) { Polygon p = m_exclude_bounding_box[index].polygon(true); // instance convex hull is scaled, so we need to scale here if (intersection({ p }, { hull }).empty() == false) //if (m_exclude_bounding_box[index].intersects(instance_box)) { break; } } if (index >= m_exclude_bounding_box.size()) outside = false; } else outside = false; } return outside; } //judge whether instance is intesected with plate or not bool PartPlate::intersect_instance(int obj_id, int instance_id, BoundingBoxf3* bounding_box) { bool result = false; if (!valid_instance(obj_id, instance_id)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": plate_id %1%, invalid obj_id %2%, instance_id %3%") % m_plate_index % obj_id % instance_id; return false; } if (m_printable) { ModelObject* object = m_model->objects[obj_id]; ModelInstance* instance = object->instances[instance_id]; BoundingBoxf3 instance_box = bounding_box? *bounding_box: object->instance_convex_hull_bounding_box(instance_id); result = get_plate_box().intersects(instance_box); } else { result = is_left_top_of(obj_id, instance_id); } return result; } //judge whether the plate's origin is at the left of instance or not bool PartPlate::is_left_top_of(int obj_id, int instance_id) { bool result = false; if (!valid_instance(obj_id, instance_id)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": plate_id %1%, invalid obj_id %2%, instance_id %3%") % m_plate_index % obj_id % instance_id; return false; } ModelObject* object = m_model->objects[obj_id]; ModelInstance* instance = object->instances[instance_id]; std::pair pair(obj_id, instance_id); BoundingBoxf3 instance_box = object->instance_convex_hull_bounding_box(instance_id); result = (m_origin.x() <= instance_box.min.x()) && (m_origin.y() >= instance_box.min.y()); return result; } //add an instance into plate int PartPlate::add_instance(int obj_id, int instance_id, bool move_position, BoundingBoxf3* bounding_box) { if (!valid_instance(obj_id, instance_id)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": plate_id %1%, invalid obj_id %2%, instance_id %3%, move_position %4%") % m_plate_index % obj_id % instance_id % move_position; return -1; } ModelObject* object = m_model->objects[obj_id]; ModelInstance* instance = object->instances[instance_id]; std::pair pair(obj_id, instance_id); obj_to_instance_set.insert(pair); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": plate_id %1%, add instance obj_id %2%, instance_id %3%, move_position %4%") % m_plate_index % obj_id % instance_id % move_position; if (move_position) { //move this instance into the new position Vec3d center = get_center_origin(); center.z() = instance->get_transformation().get_offset(Z); instance->set_offset(center); object->invalidate_bounding_box(); } //need to judge whether this instance has an outer part bool outside = check_outside(obj_id, instance_id, bounding_box); if (outside) instance_outside_set.insert(pair); if (m_ready_for_slice && outside) { m_ready_for_slice = false; } else if ((obj_to_instance_set.size() == 1) && (!m_ready_for_slice) && !outside) { m_ready_for_slice = true; } BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1% , m_ready_for_slice changes to %2%") % m_plate_index %m_ready_for_slice; return 0; } //remove instance from plate int PartPlate::remove_instance(int obj_id, int instance_id) { bool result; std::set>::iterator it; it = obj_to_instance_set.find(std::pair(obj_id, instance_id)); if (it != obj_to_instance_set.end()) { obj_to_instance_set.erase(it); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":plate_id %1%, found obj_id %2%, instance_id %3%") % m_plate_index % obj_id % instance_id; result = 0; } else { BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": plate_id %1%, can not find obj_id %2%, instance_id %3%") % m_plate_index % obj_id % instance_id; result = -1; return result; } it = instance_outside_set.find(std::pair(obj_id, instance_id)); if (it != instance_outside_set.end()) { instance_outside_set.erase(it); } if (!m_ready_for_slice) update_states(); return result; } BoundingBoxf3 PartPlate::get_objects_bounding_box() { BoundingBoxf3 bbox; for (std::set>::iterator it = obj_to_instance_set.begin(); it != obj_to_instance_set.end(); ++it) { int obj_id = it->first; int instance_id = it->second; if ((obj_id >= 0) && (obj_id < m_model->objects.size())) { ModelObject* object = m_model->objects[obj_id]; if ((instance_id >= 0) && (instance_id < object->instances.size())) { BoundingBoxf3 instance_bbox = object->instance_bounding_box(instance_id); bbox.merge(instance_bbox); } } } return bbox; } //translate instance on the plate void PartPlate::translate_all_instance(Vec3d position) { for (std::set>::iterator it = obj_to_instance_set.begin(); it != obj_to_instance_set.end(); ++it) { int obj_id = it->first; int instance_id = it->second; if ((obj_id >= 0) && (obj_id < m_model->objects.size())) { ModelObject* object = m_model->objects[obj_id]; if ((instance_id >= 0) && (instance_id < object->instances.size())) { ModelInstance* instance = object->instances[instance_id]; const Vec3d& offset = instance->get_offset(); instance->set_offset(offset + position); } } } return; } void PartPlate::duplicate_all_instance(unsigned int dup_count, bool need_skip, std::map& skip_objects) { std::set> old_obj_list = obj_to_instance_set; BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": plate_id %1%, dup_count %2%") % m_plate_index % dup_count; for (std::set>::iterator it = old_obj_list.begin(); it != old_obj_list.end(); ++it) { int obj_id = it->first; int instance_id = it->second; if ((obj_id >= 0) && (obj_id < m_model->objects.size())) { ModelObject* object = m_model->objects[obj_id]; ModelInstance* instance = object->instances[instance_id]; if (need_skip) { if (skip_objects.find(instance->loaded_id) != skip_objects.end()) { instance->printable = false; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": skipped object, loaded_id %1%, name %2%, set to unprintable, no need to duplicate") % instance->loaded_id % object->name; continue; } } for (size_t index = 0; index < dup_count; index ++) { ModelObject* newObj = m_model->add_object(*object); newObj->name = object->name +"_"+ std::to_string(index+1); int new_obj_id = m_model->objects.size() - 1; for ( size_t new_instance_id = 0; new_instance_id < newObj->instances.size(); new_instance_id++ ) { obj_to_instance_set.emplace(std::pair(new_obj_id, new_instance_id)); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": duplicate object into plate: index_pair [%1%,%2%], obj_id %3%") % new_obj_id % new_instance_id % newObj->id().id; } } } } for (std::set>::iterator it = obj_to_instance_set.begin(); it != obj_to_instance_set.end(); ++it) { int obj_id = it->first; int instance_id = it->second; if ((obj_id >= 0) && (obj_id < m_model->objects.size())) { ModelObject* object = m_model->objects[obj_id]; ModelInstance* instance = object->instances[instance_id]; if (instance->printable) { instance->loaded_id = instance->id().id; if (need_skip) { while (skip_objects.find(instance->loaded_id) != skip_objects.end()) { instance->loaded_id ++; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": duplicated id %1% with skip, try new one %2%") %instance->id().id % instance->loaded_id; } } BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": set obj %1% instance %2%'s loaded_id to its id %3%, name %4%") % obj_id %instance_id %instance->loaded_id % object->name; } } } return; } //update instance exclude state void PartPlate::update_instance_exclude_status(int obj_id, int instance_id, BoundingBoxf3* bounding_box) { bool outside; std::set>::iterator it; outside = check_outside(obj_id, instance_id, bounding_box); it = instance_outside_set.find(std::pair(obj_id, instance_id)); if (it == instance_outside_set.end()) { if (outside) instance_outside_set.insert(std::pair(obj_id, instance_id)); } else { if (!outside) instance_outside_set.erase(it); } } //update object's index caused by original object deleted void PartPlate::update_object_index(int obj_idx_removed, int obj_idx_max) { std::set> temp_set; std::set>::iterator it; //update the obj_to_instance_set for (it = obj_to_instance_set.begin(); it != obj_to_instance_set.end(); ++it) { if (it->first >= obj_idx_removed) temp_set.insert(std::pair(it->first-1, it->second)); else temp_set.insert(std::pair(it->first, it->second)); } obj_to_instance_set.clear(); obj_to_instance_set = temp_set; //update the instance_outside_set temp_set.clear(); for (it = instance_outside_set.begin(); it != instance_outside_set.end(); ++it) { if (it->first >= obj_idx_removed) temp_set.insert(std::pair(it->first - 1, it->second)); else temp_set.insert(std::pair(it->first, it->second)); } instance_outside_set.clear(); instance_outside_set = temp_set; } void PartPlate::set_vase_mode_related_object_config(int obj_id) { ModelObjectPtrs obj_ptrs; if (obj_id != -1) { ModelObject* object = m_model->objects[obj_id]; obj_ptrs.push_back(object); } else obj_ptrs = get_objects_on_this_plate(); DynamicPrintConfig* global_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config; DynamicPrintConfig new_conf; new_conf.set_key_value("wall_loops", new ConfigOptionInt(1)); new_conf.set_key_value("top_shell_layers", new ConfigOptionInt(0)); new_conf.set_key_value("sparse_infill_density", new ConfigOptionPercent(0)); new_conf.set_key_value("enable_support", new ConfigOptionBool(false)); new_conf.set_key_value("enforce_support_layers", new ConfigOptionInt(0)); new_conf.set_key_value("ensure_vertical_shell_thickness", new ConfigOptionBool(true)); new_conf.set_key_value("detect_thin_wall", new ConfigOptionBool(false)); new_conf.set_key_value("timelapse_type", new ConfigOptionEnum(tlTraditional)); auto applying_keys = global_config->diff(new_conf); for (ModelObject* object : obj_ptrs) { ModelConfigObject& config = object->config; for (auto opt_key : applying_keys) { config.set_key_value(opt_key, new_conf.option(opt_key)->clone()); } applying_keys = config.get().diff(new_conf); for (auto opt_key : applying_keys) { config.set_key_value(opt_key, new_conf.option(opt_key)->clone()); } } //wxGetApp().obj_list()->update_selections(); } int PartPlate::printable_instance_size() { int size = 0; for (std::set>::iterator it = obj_to_instance_set.begin(); it != obj_to_instance_set.end(); ++it) { int obj_id = it->first; int instance_id = it->second; if (obj_id >= m_model->objects.size()) continue; ModelObject * object = m_model->objects[obj_id]; ModelInstance *instance = object->instances[instance_id]; if ((instance->printable) && (instance_outside_set.find(std::pair(obj_id, instance_id)) == instance_outside_set.end())) { size++; } } return size; } //whether it is has printable instances bool PartPlate::has_printable_instances() { bool result = false; for (std::set>::iterator it = obj_to_instance_set.begin(); it != obj_to_instance_set.end(); ++it) { int obj_id = it->first; int instance_id = it->second; if (obj_id >= m_model->objects.size()) continue; ModelObject* object = m_model->objects[obj_id]; ModelInstance* instance = object->instances[instance_id]; if ((instance->printable)&&(instance_outside_set.find(std::pair(obj_id, instance_id)) == instance_outside_set.end())) { result = true; break; } } return result; } bool PartPlate::is_all_instances_unprintable() { bool result = true; for (std::set>::iterator it = obj_to_instance_set.begin(); it != obj_to_instance_set.end(); ++it) { int obj_id = it->first; int instance_id = it->second; if (obj_id >= m_model->objects.size()) continue; ModelObject * object = m_model->objects[obj_id]; ModelInstance *instance = object->instances[instance_id]; if ((instance->printable)) { result = false; break; } } return result; } //move instances to left or right PartPlate void PartPlate::move_instances_to(PartPlate& left_plate, PartPlate& right_plate, BoundingBoxf3* bounding_box) { for (std::set>::iterator it = obj_to_instance_set.begin(); it != obj_to_instance_set.end(); ++it) { int obj_id = it->first; int instance_id = it->second; if (left_plate.intersect_instance(obj_id, instance_id, bounding_box)) left_plate.add_instance(obj_id, instance_id, false, bounding_box); else right_plate.add_instance(obj_id, instance_id, false, bounding_box); } return; } void PartPlate::generate_logo_polygon(ExPolygon &logo_polygon) { if (m_shape.size() == 4) { //rectangle case for (int i = 0; i < 4; i++) { const Vec2d& p = m_shape[i]; if ((i == 0) || (i == 1)) { logo_polygon.contour.append({ scale_(p(0)), scale_(p(1) - 12.f) }); } else { logo_polygon.contour.append({ scale_(p(0)), scale_(p(1)) }); } } } else { for (const Vec2d& p : m_shape) { logo_polygon.contour.append({ scale_(p(0)), scale_(p(1)) }); } } } void PartPlate::generate_print_polygon(ExPolygon &print_polygon) { auto compute_points = [&print_polygon](Vec2d& center, double radius, double start_angle, double stop_angle, int count) { double angle, angle_steps; angle_steps = (stop_angle - start_angle) / (count - 1); for(int j = 0; j < count; j++ ) { double angle = start_angle + j * angle_steps; double x = center(0) + ::cos(angle) * radius; double y = center(1) + ::sin(angle) * radius; print_polygon.contour.append({ scale_(x), scale_(y) }); } }; int points_count = 8; if (m_shape.size() == 4) { //rectangle case for (int i = 0; i < 4; i++) { const Vec2d& p = m_shape[i]; Vec2d center; double start_angle, stop_angle, angle_steps, radius_x, radius_y, radius; switch (i) { case 0: radius = 5.f; center(0) = p(0) + radius; center(1) = p(1) + radius; start_angle = PI; stop_angle = 1.5 * PI; compute_points(center, radius, start_angle, stop_angle, points_count); break; case 1: print_polygon.contour.append({ scale_(p(0)), scale_(p(1)) }); break; case 2: radius_x = (int)(p(0)) % 10; radius_y = (int)(p(1)) % 10; radius = (radius_x > radius_y)?radius_y: radius_x; if (radius < 5.0) radius = 5.f; center(0) = p(0) - radius; center(1) = p(1) - radius; start_angle = 0; stop_angle = 0.5 * PI; compute_points(center, radius, start_angle, stop_angle, points_count); break; case 3: radius_x = (int)(p(0)) % 10; radius_y = (int)(p(1)) % 10; radius = (radius_x > radius_y)?radius_y: radius_x; if (radius < 5.0) radius = 5.f; center(0) = p(0) + radius; center(1) = p(1) - radius; start_angle = 0.5 * PI; stop_angle = PI; compute_points(center, radius, start_angle, stop_angle, points_count); break; } } } else { for (const Vec2d& p : m_shape) { print_polygon.contour.append({ scale_(p(0)), scale_(p(1)) }); } } } void PartPlate::generate_exclude_polygon(ExPolygon &exclude_polygon) { auto compute_exclude_points = [&exclude_polygon](Vec2d& center, double radius, double start_angle, double stop_angle, int count) { double angle, angle_steps; angle_steps = (stop_angle - start_angle) / (count - 1); for(int j = 0; j < count; j++ ) { double angle = start_angle + j * angle_steps; double x = center(0) + ::cos(angle) * radius; double y = center(1) + ::sin(angle) * radius; exclude_polygon.contour.append({ scale_(x), scale_(y) }); } }; int points_count = 8; if (m_exclude_area.size() == 4) { //rectangle case for (int i = 0; i < 4; i++) { const Vec2d& p = m_exclude_area[i]; Vec2d center; double start_angle, stop_angle, angle_steps, radius_x, radius_y, radius; switch (i) { case 0: radius = 5.f; center(0) = p(0) + radius; center(1) = p(1) + radius; start_angle = PI; stop_angle = 1.5 * PI; compute_exclude_points(center, radius, start_angle, stop_angle, points_count); break; case 1: exclude_polygon.contour.append({ scale_(p(0)), scale_(p(1)) }); break; case 2: radius = 3.f; center(0) = p(0) - radius; center(1) = p(1) - radius; start_angle = 0; stop_angle = 0.5 * PI; compute_exclude_points(center, radius, start_angle, stop_angle, points_count); break; case 3: exclude_polygon.contour.append({ scale_(p(0)), scale_(p(1)) }); break; } } } else { for (const Vec2d& p : m_exclude_area) { exclude_polygon.contour.append({ scale_(p(0)), scale_(p(1)) }); } } } bool PartPlate::set_shape(const Pointfs& shape, const Pointfs& exclude_areas, Vec2d position, float height_to_lid, float height_to_rod) { Pointfs new_shape, new_exclude_areas; m_raw_shape = shape; for (const Vec2d& p : shape) { new_shape.push_back(Vec2d(p.x() + position.x(), p.y() + position.y())); } for (const Vec2d& p : exclude_areas) { new_exclude_areas.push_back(Vec2d(p.x() + position.x(), p.y() + position.y())); } if ((m_shape == new_shape)&&(m_exclude_area == new_exclude_areas) &&(m_height_to_lid == height_to_lid)&&(m_height_to_rod == height_to_rod)) { BOOST_LOG_TRIVIAL(info) << "PartPlate same shape, skip directly"; return false; } m_height_to_lid = height_to_lid; m_height_to_rod = height_to_rod; if ((m_shape != new_shape) || (m_exclude_area != new_exclude_areas)) { /*m_shape.clear(); for (const Vec2d& p : shape) { m_shape.push_back(Vec2d(p.x() + position.x(), p.y() + position.y())); } m_exclude_area.clear(); for (const Vec2d& p : exclude_areas) { m_exclude_area.push_back(Vec2d(p.x() + position.x(), p.y() + position.y())); }*/ m_shape = std::move(new_shape); m_exclude_area = std::move(new_exclude_areas); calc_bounding_boxes(); ExPolygon logo_poly; generate_logo_polygon(logo_poly); if (!m_logo_triangles.set_from_triangles(triangulate_expolygon_2f(logo_poly, NORMALS_UP), GROUND_Z+0.02f)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":Unable to create logo triangles\n"; else { ; } ExPolygon poly; /*for (const Vec2d& p : m_shape) { poly.contour.append({ scale_(p(0)), scale_(p(1)) }); }*/ generate_print_polygon(poly); calc_triangles(poly); ExPolygon exclude_poly; /*for (const Vec2d& p : m_exclude_area) { exclude_poly.contour.append({ scale_(p(0)), scale_(p(1)) }); }*/ generate_exclude_polygon(exclude_poly); calc_exclude_triangles(exclude_poly); const BoundingBox& pp_bbox = poly.contour.bounding_box(); calc_gridlines(poly, pp_bbox); //calc_vertex_for_icons_background(5, m_del_and_background_icon); //calc_vertex_for_icons(4, m_del_icon); calc_vertex_for_icons(0, m_del_icon); calc_vertex_for_icons(1, m_orient_icon); calc_vertex_for_icons(2, m_arrange_icon); calc_vertex_for_icons(3, m_lock_icon); calc_vertex_for_icons(4, m_plate_settings_icon); //calc_vertex_for_number(0, (m_plate_index < 9), m_plate_idx_icon); calc_vertex_for_number(0, false, m_plate_idx_icon); calc_vertex_for_plate_name(m_name_texture, m_plate_name_icon);//if (generate_plate_name_texture()) calc_vertex_for_plate_name_edit_icon(&m_name_texture, 0, m_plate_name_edit_icon); } calc_height_limit(); release_opengl_resource(); return true; } const BoundingBox PartPlate::get_bounding_box_crd() { const auto plate_shape = Slic3r::Polygon::new_scale(m_shape); return plate_shape.bounding_box(); } bool PartPlate::contains(const Vec3d& point) const { return m_bounding_box.contains(point); } bool PartPlate::contains(const GLVolume& v) const { return m_bounding_box.contains(v.bounding_box()); } bool PartPlate::contains(const BoundingBoxf3& bb) const { // Allow the objects to protrude below the print bed BoundingBoxf3 print_volume(Vec3d(m_bounding_box.min(0), m_bounding_box.min(1), 0.0), Vec3d(m_bounding_box.max(0), m_bounding_box.max(1), 1e3)); print_volume.min(2) = -1e10; print_volume.min(0) -= Slic3r::BuildVolume::BedEpsilon; print_volume.min(1) -= Slic3r::BuildVolume::BedEpsilon; print_volume.max(0) += Slic3r::BuildVolume::BedEpsilon; print_volume.max(1) += Slic3r::BuildVolume::BedEpsilon; return print_volume.contains(bb); } bool PartPlate::intersects(const BoundingBoxf3& bb) const { // Allow the objects to protrude below the print bed BoundingBoxf3 print_volume(Vec3d(m_bounding_box.min(0), m_bounding_box.min(1), 0.0), Vec3d(m_bounding_box.max(0), m_bounding_box.max(1), 1e3)); print_volume.min(2) = -1e10; print_volume.min(0) -= Slic3r::BuildVolume::BedEpsilon; print_volume.min(1) -= Slic3r::BuildVolume::BedEpsilon; print_volume.max(0) += Slic3r::BuildVolume::BedEpsilon; print_volume.max(1) += Slic3r::BuildVolume::BedEpsilon; return print_volume.intersects(bb); } void PartPlate::render(bool bottom, bool only_body, bool force_background_color, HeightLimitMode mode, int hover_id, bool render_cali) { glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_BLEND)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); if (!bottom) { // draw background render_background(force_background_color); render_exclude_area(force_background_color); } render_grid(bottom); if (!bottom && m_selected && !force_background_color) { if (m_partplate_list) render_logo(bottom, m_partplate_list->render_cali_logo && render_cali); else render_logo(bottom); } render_height_limit(mode); render_icons(bottom, only_body, hover_id); if (!force_background_color){ render_only_numbers(bottom); } glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); glsafe(::glDisable(GL_BLEND)); //if (with_label) { // render_label(canvas); //} glsafe(::glDisable(GL_DEPTH_TEST)); } void PartPlate::set_selected() { m_selected = true; } void PartPlate::set_unselected() { m_selected = false; } /*status related functions*/ //update status void PartPlate::update_states() { //currently let judge outside partplate when plate is empty /*if (obj_to_instance_set.size() == 0) { m_ready_for_slice = false; return; }*/ m_ready_for_slice = true; for (std::set>::iterator it = obj_to_instance_set.begin(); it != obj_to_instance_set.end(); ++it) { int obj_id = it->first; int instance_id = it->second; //if (check_outside(obj_id, instance_id)) if (instance_outside_set.find(std::pair(obj_id, instance_id)) != instance_outside_set.end()) { m_ready_for_slice = false; //currently only check whether ready to slice break; } } BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1% , m_ready_for_slice changes to %2%") % m_plate_index %m_ready_for_slice; return; } /*slice related functions*/ //invalid sliced result void PartPlate::update_slice_result_valid_state(bool valid) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1% , update slice result from %2% to %3%") % m_plate_index %m_slice_result_valid %valid; m_slice_result_valid = valid; if (valid) m_slice_percent = 100.0f; else { m_slice_percent = -1.0f; } } //update current slice context into backgroud slicing process void PartPlate::update_slice_context(BackgroundSlicingProcess & process) { auto statuscb = [this](const Slic3r::PrintBase::SlicingStatus& status) { Slic3r::SlicingStatusEvent *event = new Slic3r::SlicingStatusEvent(EVT_SLICING_UPDATE, 0, status); //BBS: GUI refactor: add plate info befor message if (status.message_type == Slic3r::PrintStateBase::SlicingDefaultNotification) { auto temp = Slic3r::format(_u8L(" plate %1%: "), std::to_string(m_plate_index + 1)); event->status.text = temp + event->status.text; } wxQueueEvent(m_plater, event); }; process.set_fff_print(m_print); process.set_gcode_result(m_gcode_result); process.select_technology(this->printer_technology); process.set_current_plate(this); m_print->set_status_callback(statuscb); process.switch_print_preprocess(); return; } // BBS: delay calc gcode path in backup dir std::string PartPlate::get_tmp_gcode_path() { if (m_tmp_gcode_path.empty()) { boost::filesystem::path temp_path(m_model->get_backup_path("Metadata")); temp_path /= (boost::format(".%1%.%2%.gcode") % get_current_pid() % GLOBAL_PLATE_INDEX++).str(); m_tmp_gcode_path = temp_path.string(); } return m_tmp_gcode_path; } std::string PartPlate::get_temp_config_3mf_path() { if (m_temp_config_3mf_path.empty()) { boost::filesystem::path temp_path(m_model->get_backup_path("Metadata")); temp_path /= (boost::format(".%1%.%2%_config.3mf") % get_current_pid() % GLOBAL_PLATE_INDEX++).str(); m_temp_config_3mf_path = temp_path.string(); } return m_temp_config_3mf_path; } // load gcode from file int PartPlate::load_gcode_from_file(const std::string& filename) { int ret = 0; // process gcode DynamicPrintConfig full_config = wxGetApp().preset_bundle->full_config(); full_config.apply(m_config, true); m_print->apply(*m_model, full_config); //BBS: need to apply two times, for after the first apply, the m_print got its object, //which will affect the config when new_full_config.normalize_fdm(used_filaments); m_print->apply(*m_model, full_config); // BBS: use backup path to save temp gcode // auto path = get_tmp_gcode_path(); // if (boost::filesystem::exists(boost::filesystem::path(path))) { // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": file %1% exists, delete it firstly") % filename.c_str(); // boost::nowide::remove(path.c_str()); //} // std::error_code error = rename_file(filename, path); // if (error) { // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("Failed to rename the output G-code file from %1% to %2%, error code %3%") % filename.c_str() % path.c_str() % //error.message(); return -1; //} if (boost::filesystem::exists(filename)) { assert(m_tmp_gcode_path.empty()); m_tmp_gcode_path = filename; m_gcode_result->filename = filename; m_print->set_gcode_file_ready(); update_slice_result_valid_state(true); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": found valid gcode file %1%") % filename.c_str(); } else { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": can not find gcode file %1%") % filename.c_str(); ret = -1; } m_ready_for_slice = true; return ret; } int PartPlate::load_thumbnail_data(std::string filename, ThumbnailData& thumb_data) { bool result = true; wxImage img; if (boost::algorithm::iends_with(filename, ".png")) { result = img.LoadFile(wxString::FromUTF8(filename.c_str()), wxBITMAP_TYPE_PNG); img = img.Mirror(false); } if (result) { thumb_data.set(img.GetWidth(), img.GetHeight()); for (int i = 0; i < img.GetWidth() * img.GetHeight(); i++) { memcpy(&thumb_data.pixels[4 * i], (unsigned char*)(img.GetData() + 3 * i), 3); if (img.HasAlpha()) { thumb_data.pixels[4 * i + 3] = *(unsigned char*)(img.GetAlpha() + i); } } } else { return -1; } return 0; } int PartPlate::load_pattern_thumbnail_data(std::string filename) { /*bool result = true; wxImage img; result = load_image(filename, img); if (result) { cali_thumbnail_data.set(img.GetWidth(), img.GetHeight()); for (int i = 0; i < img.GetWidth() * img.GetHeight(); i++) { memcpy(&cali_thumbnail_data.pixels[4 * i], (unsigned char*)(img.GetData() + 3 * i), 3); if (img.HasAlpha()) { cali_thumbnail_data.pixels[4 * i + 3] = *(unsigned char*)(img.GetAlpha() + i); } } } else { return -1; }*/ return 0; } //load pattern box data from file int PartPlate::load_pattern_box_data(std::string filename) { try { nlohmann::json j; boost::nowide::ifstream ifs(filename); ifs >> j; PlateBBoxData bbox_data; bbox_data.from_json(j); cali_bboxes_data = bbox_data; return 0; } catch(std::exception &ex) { BOOST_LOG_TRIVIAL(trace) << boost::format("catch an exception %1%")%ex.what(); return -1; } } std::vector PartPlate::get_first_layer_print_sequence() const { const ConfigOptionInts *op_print_sequence_1st = m_config.option("first_layer_print_sequence"); if (op_print_sequence_1st) return op_print_sequence_1st->values; else return std::vector(); } std::vector PartPlate::get_other_layers_print_sequence() const { const ConfigOptionInts* other_layers_print_sequence_op = m_config.option("other_layers_print_sequence"); const ConfigOptionInt* other_layers_print_sequence_nums_op = m_config.option("other_layers_print_sequence_nums"); if (other_layers_print_sequence_op && other_layers_print_sequence_nums_op) { const std::vector& print_sequence = other_layers_print_sequence_op->values; int sequence_nums = other_layers_print_sequence_nums_op->value; auto other_layers_seqs = Slic3r::get_other_layers_print_sequence(sequence_nums, print_sequence); return other_layers_seqs; } else return {}; } void PartPlate::set_first_layer_print_sequence(const std::vector& sorted_filaments) { if (sorted_filaments.size() > 0) { if (sorted_filaments.size() == 1 && sorted_filaments[0] == 0) { m_config.erase("first_layer_print_sequence"); } else { ConfigOptionInts *op_print_sequence_1st = m_config.option("first_layer_print_sequence"); if (op_print_sequence_1st) op_print_sequence_1st->values = sorted_filaments; else m_config.set_key_value("first_layer_print_sequence", new ConfigOptionInts(sorted_filaments)); } } else { m_config.erase("first_layer_print_sequence"); } } void PartPlate::set_other_layers_print_sequence(const std::vector& layer_seq_list) { if (layer_seq_list.empty()) { m_config.erase("other_layers_print_sequence"); m_config.erase("other_layers_print_sequence_nums"); return; } int sequence_nums; std::vector other_layers_seqs; Slic3r::get_other_layers_print_sequence(layer_seq_list, sequence_nums, other_layers_seqs); ConfigOptionInts* other_layers_print_sequence_op = m_config.option("other_layers_print_sequence"); ConfigOptionInt* other_layers_print_sequence_nums_op = m_config.option("other_layers_print_sequence_nums"); if (other_layers_print_sequence_op) other_layers_print_sequence_op->values = other_layers_seqs; else m_config.set_key_value("other_layers_print_sequence", new ConfigOptionInts(other_layers_seqs)); if (other_layers_print_sequence_nums_op) other_layers_print_sequence_nums_op->value = sequence_nums; else m_config.set_key_value("other_layers_print_sequence_nums", new ConfigOptionInt(sequence_nums)); } void PartPlate::update_first_layer_print_sequence(size_t filament_nums) { auto other_layers_seqs = get_other_layers_print_sequence(); if (!other_layers_seqs.empty()) { bool need_update_data = false; for (auto& other_layers_seq : other_layers_seqs) { std::vector& orders = other_layers_seq.second; if (orders.size() > filament_nums) { orders.erase(std::remove_if(orders.begin(), orders.end(), [filament_nums](int n) { return n > filament_nums; }), orders.end()); need_update_data = true; } if (orders.size() < filament_nums) { for (size_t extruder_id = orders.size(); extruder_id < filament_nums; ++extruder_id) { orders.push_back(extruder_id + 1); need_update_data = true; } } } if (need_update_data) set_other_layers_print_sequence(other_layers_seqs); } ConfigOptionInts * op_print_sequence_1st = m_config.option("first_layer_print_sequence"); if (!op_print_sequence_1st) { return; } std::vector &print_sequence_1st = op_print_sequence_1st->values; if (print_sequence_1st.size() == 0 || print_sequence_1st[0] == 0) return; if (print_sequence_1st.size() > filament_nums) { print_sequence_1st.erase(std::remove_if(print_sequence_1st.begin(), print_sequence_1st.end(), [filament_nums](int n) { return n > filament_nums; }), print_sequence_1st.end()); } else if (print_sequence_1st.size() < filament_nums) { for (size_t extruder_id = print_sequence_1st.size(); extruder_id < filament_nums; ++extruder_id) { print_sequence_1st.push_back(extruder_id + 1); } } } void PartPlate::print() const { unsigned int count=0; BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << boost::format(": plate index %1%, pointer %2%, print_index %3% print pointer %4%") % m_plate_index % this % m_print_index % m_print; BOOST_LOG_TRIVIAL(trace) << boost::format("\t origin {%1%,%2%,%3%}, width %4%, depth %5%, height %6%") % m_origin.x() % m_origin.y() % m_origin.z() % m_width % m_depth % m_height; BOOST_LOG_TRIVIAL(trace) << boost::format("\t m_printable %1%, m_locked %2%, m_ready_for_slice %3%, m_slice_result_valid %4%, m_tmp_gcode_path %5%, set size %6%")\ % m_printable % m_locked % m_ready_for_slice % m_slice_result_valid % m_tmp_gcode_path % obj_to_instance_set.size(); /*for (std::set>::iterator it = obj_to_instance_set.begin(); it != obj_to_instance_set.end(); ++it) { int obj_id = it->first; int instance_id = it->second; BOOST_LOG_TRIVIAL(trace) << boost::format("\t the %1%th instance, obj_id %2%, instance id %3%") % count++ % obj_id % instance_id; }*/ BOOST_LOG_TRIVIAL(trace) << boost::format("excluded instance set size %1%")%instance_outside_set.size(); /*for (std::set>::iterator it = instance_outside_set.begin(); it != instance_outside_set.end(); ++it) { int obj_id = it->first; int instance_id = it->second; BOOST_LOG_TRIVIAL(trace) << boost::format("\t obj_id %1%, instance id %2%") % obj_id % instance_id; }*/ return; } std::map PartPlate::get_diff_object_setting() { std::map out; for (auto it = obj_to_instance_set.cbegin(); it != obj_to_instance_set.cend(); ++it) { const ModelConfigObject& different_object_config = m_model->objects[it->first]->config; for (auto iter = different_object_config.cbegin(); iter != different_object_config.cend(); ++iter) { std::string config_name = iter->first; std::string config_value = iter->second->serialize(); if (out.find(config_name) == out.end()) { out[config_name] = config_value; } } } return out; } std::map PartPlate::get_diff_plate_setting() { std::map out; for (auto it = m_config.cbegin(); it != m_config.cend(); ++it) { std::string diff_config_name = it->first; std::string diff_config_value; if (diff_config_name == "first_layer_print_sequence") { diff_config_value = "cutomize"; } else { diff_config_value = it->second->serialize(); } out[diff_config_name] = diff_config_value; } return out; } /* PartPlate List related functions*/ PartPlateList::PartPlateList(int width, int depth, int height, Plater* platerObj, Model* modelObj, PrinterTechnology tech) :m_plate_width(width), m_plate_depth(depth), m_plate_height(height), m_plater(platerObj), m_model(modelObj), printer_technology(tech), unprintable_plate(this, Vec3d(0.0 + width * (1. + LOGICAL_PART_PLATE_GAP), 0.0, 0.0), width, depth, height, platerObj, modelObj, false, tech) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(":plate_width %1%, plate_depth %2%, plate_height %3%") % width % depth % height; init(); } PartPlateList::PartPlateList(Plater* platerObj, Model* modelObj, PrinterTechnology tech) :m_plate_width(0), m_plate_depth(0), m_plate_height(0), m_plater(platerObj), m_model(modelObj), printer_technology(tech), unprintable_plate(this, Vec3d(0.0, 0.0, 0.0), m_plate_width, m_plate_depth, m_plate_height, platerObj, modelObj, false, tech) { init(); } PartPlateList::~PartPlateList() { clear(true, true); release_icon_textures(); } void PartPlateList::init() { m_intialized = false; PartPlate* first_plate = NULL; first_plate = new PartPlate(this, Vec3d(0.0, 0.0, 0.0), m_plate_width, m_plate_depth, m_plate_height, m_plater, m_model, true, printer_technology); assert(first_plate != NULL); m_plate_list.push_back(first_plate); m_print_index = 0; if (printer_technology == ptFFF) { Print* print = new Print(); GCodeResult* gcode = new GCodeResult(); m_print_list.emplace(m_print_index, print); m_gcode_result_list.emplace(m_print_index, gcode); first_plate->set_print(print, gcode, m_print_index); m_print_index++; } first_plate->set_index(0); m_plate_count = 1; m_plate_cols = 1; m_current_plate = 0; if (m_plater) { // In GUI mode set_default_wipe_tower_pos_for_plate(0); } select_plate(0); unprintable_plate.set_index(1); m_intialized = true; } //compute the origin for printable plate with index i Vec3d PartPlateList::compute_origin(int i, int cols) { Vec3d origin; Vec2d pos = compute_shape_position(i, cols); origin = Vec3d(pos.x(), pos.y(), 0); return origin; } //compute the origin for printable plate with index i using new width Vec3d PartPlateList::compute_origin_using_new_size(int i, int new_width, int new_depth) { Vec3d origin; int row, col; row = i / m_plate_cols; col = i % m_plate_cols; origin(0) = col * (new_width * (1. + LOGICAL_PART_PLATE_GAP)); origin(1) = -row * (new_depth * (1. + LOGICAL_PART_PLATE_GAP)); origin(2) = 0; return origin; } //compute the origin for printable plate with index i Vec3d PartPlateList::compute_origin_for_unprintable() { int max_count = m_plate_cols * m_plate_cols; if (m_plate_count == max_count) return compute_origin(max_count + m_plate_cols - 1, m_plate_cols + 1); else return compute_origin(m_plate_count, m_plate_cols); } //compute shape position Vec2d PartPlateList::compute_shape_position(int index, int cols) { Vec2d pos; int row, col; row = index / cols; col = index % cols; pos(0) = col * plate_stride_x(); pos(1) = -row * plate_stride_y(); return pos; } //generate icon textures void PartPlateList::generate_icon_textures() { // use higher resolution images if graphic card and opengl version allow GLint max_tex_size = OpenGLManager::get_gl_info().get_max_tex_size(), icon_size = max_tex_size / 8; std::string path = resources_dir() + "/images/"; std::string file_name; if (icon_size > 256) icon_size = 256; //if (m_del_texture.get_id() == 0) { file_name = path + (m_is_dark ? "plate_close_dark.svg" : "plate_close.svg"); if (!m_del_texture.load_from_svg_file(file_name, true, false, false, icon_size)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":load file %1% failed") % file_name; } } //if (m_del_hovered_texture.get_id() == 0) { file_name = path + (m_is_dark ? "plate_close_hover_dark.svg" : "plate_close_hover.svg"); if (!m_del_hovered_texture.load_from_svg_file(file_name, true, false, false, icon_size)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":load file %1% failed") % file_name; } } //if (m_arrange_texture.get_id() == 0) { file_name = path + (m_is_dark ? "plate_arrange_dark.svg" : "plate_arrange.svg"); if (!m_arrange_texture.load_from_svg_file(file_name, true, false, false, icon_size)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":load file %1% failed") % file_name; } } //if (m_arrange_hovered_texture.get_id() == 0) { file_name = path + (m_is_dark ? "plate_arrange_hover_dark.svg" : "plate_arrange_hover.svg"); if (!m_arrange_hovered_texture.load_from_svg_file(file_name, true, false, false, icon_size)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":load file %1% failed") % file_name; } } //if (m_orient_texture.get_id() == 0) { file_name = path + (m_is_dark ? "plate_orient_dark.svg" : "plate_orient.svg"); if (!m_orient_texture.load_from_svg_file(file_name, true, false, false, icon_size)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":load file %1% failed") % file_name; } } //if (m_orient_hovered_texture.get_id() == 0) { file_name = path + (m_is_dark ? "plate_orient_hover_dark.svg" : "plate_orient_hover.svg"); if (!m_orient_hovered_texture.load_from_svg_file(file_name, true, false, false, icon_size)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":load file %1% failed") % file_name; } } //if (m_locked_texture.get_id() == 0) { file_name = path + (m_is_dark ? "plate_locked_dark.svg" : "plate_locked.svg"); if (!m_locked_texture.load_from_svg_file(file_name, true, false, false, icon_size)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":load file %1% failed") % file_name; } } //if (m_locked_hovered_texture.get_id() == 0) { file_name = path + (m_is_dark ? "plate_locked_hover_dark.svg" : "plate_locked_hover.svg"); if (!m_locked_hovered_texture.load_from_svg_file(file_name, true, false, false, icon_size)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":load file %1% failed") % file_name; } } //if (m_lockopen_texture.get_id() == 0) { file_name = path + (m_is_dark ? "plate_unlocked_dark.svg" : "plate_unlocked.svg"); if (!m_lockopen_texture.load_from_svg_file(file_name, true, false, false, icon_size)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":load file %1% failed") % file_name; } } //if (m_lockopen_hovered_texture.get_id() == 0) { file_name = path + (m_is_dark ? "plate_unlocked_hover_dark.svg" : "plate_unlocked_hover.svg"); if (!m_lockopen_hovered_texture.load_from_svg_file(file_name, true, false, false, icon_size)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":load file %1% failed") % file_name; } } //if (m_bedtype_texture.get_id() == 0) { file_name = path + (m_is_dark ? "plate_settings_dark.svg" : "plate_settings.svg"); if (!m_plate_settings_texture.load_from_svg_file(file_name, true, false, false, icon_size)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":load file %1% failed") % file_name; } } //if (m_bedtype_changed_texture.get_id() == 0) { file_name = path + (m_is_dark ? "plate_settings_changed_dark.svg" : "plate_settings_changed.svg"); if (!m_plate_settings_changed_texture.load_from_svg_file(file_name, true, false, false, icon_size)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":load file %1% failed") % file_name; } } //if (m_bedtype_hovered_texture.get_id() == 0) { file_name = path + (m_is_dark ? "plate_settings_hover_dark.svg" : "plate_settings_hover.svg"); if (!m_plate_settings_hovered_texture.load_from_svg_file(file_name, true, false, false, icon_size)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":load file %1% failed") % file_name; } } //if (m_bedtype_changed_hovered_texture.get_id() == 0) { file_name = path + (m_is_dark ? "plate_settings_changed_hover_dark.svg" : "plate_settings_changed_hover.svg"); if (!m_plate_settings_changed_hovered_texture.load_from_svg_file(file_name, true, false, false, icon_size)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":load file %1% failed") % file_name; } } // if (m_plate_name_edit_texture.get_id() == 0) { file_name = path + (m_is_dark ? "plate_name_edit_dark.svg" : "plate_name_edit.svg"); if (!m_plate_name_edit_texture.load_from_svg_file(file_name, true, false, false, icon_size)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":load file %1% failed") % file_name; } } // if (m_plate_name_edit_hovered_texture.get_id() == 0) { file_name = path + (m_is_dark ? "plate_name_edit_hover_dark.svg" : "plate_name_edit_hover.svg"); if (!m_plate_name_edit_hovered_texture.load_from_svg_file(file_name, true, false, false, icon_size)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":load file %1% failed") % file_name; } } auto is_font_suitable = [](std::string text_str, wxFont& font, int max_size) { wxMemoryDC memDC; wxCoord w, h; wxString msg(text_str); memDC.SetFont(font); memDC.GetMultiLineTextExtent(msg, &w, &h); if (w <= max_size) return true; else return false;; }; wxFont* font = nullptr; std::string text_str = "01"; int max_size = 32; if (is_font_suitable(text_str, Label::Head_24, max_size)) font = &Label::Head_24; else if (is_font_suitable(text_str, Label::Head_20, max_size)) font = &Label::Head_20; else if (is_font_suitable(text_str, Label::Head_18, max_size)) font = &Label::Head_18; else if (is_font_suitable(text_str, Label::Head_16, max_size)) font = &Label::Head_16; else if (is_font_suitable(text_str, Label::Head_14, max_size)) font = &Label::Head_14; else font = &Label::Head_12; for (int i = 0; i < MAX_PLATE_COUNT; i++) { if (m_idx_textures[i].get_id() == 0) { //file_name = path + (boost::format("plate_%1%.svg") % (i + 1)).str(); if ( i < 9 ) file_name = std::string("0") + std::to_string(i+1); else file_name = std::to_string(i+1); wxColour NumberForeground(PlateTextureForeground[0], PlateTextureForeground[1], PlateTextureForeground[2], PlateTextureForeground[3]); if (!m_idx_textures[i].generate_from_text_string(file_name, *font, *wxBLACK, NumberForeground)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":load file %1% failed") % file_name; } } } } void PartPlateList::release_icon_textures() { m_logo_texture.reset(); m_del_texture.reset(); m_del_hovered_texture.reset(); m_arrange_texture.reset(); m_arrange_hovered_texture.reset(); m_orient_texture.reset(); m_orient_hovered_texture.reset(); m_locked_texture.reset(); m_locked_hovered_texture.reset(); m_lockopen_texture.reset(); m_lockopen_hovered_texture.reset(); m_plate_settings_texture.reset(); m_plate_settings_texture.reset(); m_plate_settings_texture.reset(); m_plate_settings_hovered_texture.reset(); m_plate_name_edit_texture.reset(); m_plate_name_edit_hovered_texture.reset(); for (int i = 0;i < MAX_PLATE_COUNT; i++) { m_idx_textures[i].reset(); } //reset PartPlateList::is_load_bedtype_textures = false; PartPlateList::is_load_cali_texture = false; for (int i = 0; i < btCount; i++) { for (auto& part: bed_texture_info[i].parts) { if (part.texture) { part.texture->reset(); delete part.texture; } if (part.vbo_id != 0) { glsafe(::glDeleteBuffers(1, &part.vbo_id)); part.vbo_id = 0; } if (part.buffer) { delete part.buffer; } } } } void PartPlateList::set_default_wipe_tower_pos_for_plate(int plate_idx) { DynamicConfig & proj_cfg = wxGetApp().preset_bundle->project_config; ConfigOptionFloats *wipe_tower_x = proj_cfg.opt("wipe_tower_x"); ConfigOptionFloats *wipe_tower_y = proj_cfg.opt("wipe_tower_y"); wipe_tower_x->values.resize(m_plate_list.size(), wipe_tower_x->values.front()); wipe_tower_y->values.resize(m_plate_list.size(), wipe_tower_y->values.front()); auto printer_structure_opt = wxGetApp().preset_bundle->printers.get_edited_preset().config.option>("printer_structure"); // set the default position, the same with print config(left top) ConfigOptionFloat wt_x_opt(WIPE_TOWER_DEFAULT_X_POS); ConfigOptionFloat wt_y_opt(WIPE_TOWER_DEFAULT_Y_POS); if (printer_structure_opt && printer_structure_opt->value == PrinterStructure::psI3) { wt_x_opt = ConfigOptionFloat(I3_WIPE_TOWER_DEFAULT_X_POS); wt_y_opt = ConfigOptionFloat(I3_WIPE_TOWER_DEFAULT_Y_POS); } dynamic_cast(proj_cfg.option("wipe_tower_x"))->set_at(&wt_x_opt, plate_idx, 0); dynamic_cast(proj_cfg.option("wipe_tower_y"))->set_at(&wt_y_opt, plate_idx, 0); } //this may be happened after machine changed void PartPlateList::reset_size(int width, int depth, int height, bool reload_objects, bool update_shapes) { Vec3d origin1, origin2; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(":before size: plate_width %1%, plate_depth %2%, plate_height %3%") % m_plate_width % m_plate_depth % m_plate_height; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(":after size: plate_width %1%, plate_depth %2%, plate_height %3%") % width % depth % height; if ((m_plate_width != width) || (m_plate_depth != depth) || (m_plate_height != height)) { m_plate_width = width; m_plate_depth = depth; m_plate_height = height; update_all_plates_pos_and_size(false, false, true); if (update_shapes) { set_shapes(m_shape, m_exclude_areas, m_logo_texture_filename, m_height_to_lid, m_height_to_rod); } if (reload_objects) reload_all_objects(); else clear(false, false, false, -1); } return; } //clear all the instances in the plate, but keep the plates void PartPlateList::clear(bool delete_plates, bool release_print_list, bool except_locked, int plate_index) { for (unsigned int i = 0; i < (unsigned int)m_plate_list.size(); ++i) { PartPlate* plate = m_plate_list[i]; assert(plate != NULL); if (except_locked && plate->is_locked()) plate->clear(false); else if ((plate_index != -1) && (plate_index != i)) plate->clear(false); else plate->clear(); if (delete_plates) delete plate; } if (delete_plates) { //also delete print related to the plate m_plate_list.clear(); m_current_plate = 0; } if (release_print_list) { for (std::map::iterator it = m_print_list.begin(); it != m_print_list.end(); ++it) { PrintBase* print = it->second; assert(print != NULL); delete print; } m_print_list.clear(); for (std::map::iterator it = m_gcode_result_list.begin(); it != m_gcode_result_list.end(); ++it) { GCodeResult* gcode = it->second; assert(gcode != NULL); delete gcode; } m_gcode_result_list.clear(); } unprintable_plate.clear(); } //clear all the instances in the plate, and delete the plates, only keep the first default plate void PartPlateList::reset(bool do_init) { clear(true, false); //m_plate_list.clear(); if (do_init) init(); return; } //reset partplate to init states void PartPlateList::reinit() { clear(true, true); init(); //reset plate 0's position Vec2d pos = compute_shape_position(0, m_plate_cols); m_plate_list[0]->set_shape(m_shape, m_exclude_areas, pos, m_height_to_lid, m_height_to_rod); //reset unprintable plate's position Vec3d origin2 = compute_origin_for_unprintable(); unprintable_plate.set_pos_and_size(origin2, m_plate_width, m_plate_depth, m_plate_height, false); //re-calc the bounding boxes calc_bounding_boxes(); return; } /*basic plate operations*/ //create an empty plate, and return its index //these model instances which are not in any plates should not be affected also int PartPlateList::create_plate(bool adjust_position) { PartPlate* plate = NULL; Vec3d origin; int new_index; new_index = m_plate_list.size(); if (new_index >= MAX_PLATES_COUNT) return -1; int cols = compute_colum_count(new_index + 1); int old_cols = compute_colum_count(new_index); origin = compute_origin(new_index, cols); plate = new PartPlate(this, origin, m_plate_width, m_plate_depth, m_plate_height, m_plater, m_model, true, printer_technology); assert(plate != NULL); if (printer_technology == ptFFF) { Print* print = new Print(); GCodeResult* gcode = new GCodeResult(); m_print_list.emplace(m_print_index, print); m_gcode_result_list.emplace(m_print_index, gcode); plate->set_print(print, gcode, m_print_index); m_print_index++; } plate->set_index(new_index); Vec2d pos = compute_shape_position(new_index, cols); plate->set_shape(m_shape, m_exclude_areas, pos, m_height_to_lid, m_height_to_rod); m_plate_list.emplace_back(plate); update_plate_cols(); if (old_cols != cols) { BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":old_cols %1% -> new_cols %2%") % old_cols % cols; //update the origin of each plate update_all_plates_pos_and_size(adjust_position, false); set_shapes(m_shape, m_exclude_areas, m_logo_texture_filename, m_height_to_lid, m_height_to_rod); if (m_plater) { Vec2d pos = compute_shape_position(m_current_plate, cols); m_plater->set_bed_position(pos); } } else { BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": the same cols %1%") % old_cols; Vec3d origin2 = compute_origin_for_unprintable(); unprintable_plate.set_pos_and_size(origin2, m_plate_width, m_plate_depth, m_plate_height, false); //update bounding_boxes calc_bounding_boxes(); } // update wipe tower config if (m_plater) { // In GUI mode set_default_wipe_tower_pos_for_plate(new_index); } unprintable_plate.set_index(new_index+1); //reload all objects here if (adjust_position) construct_objects_list_for_new_plate(new_index); if (m_plater) { // In GUI mode wxGetApp().obj_list()->on_plate_added(plate); } BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":created a new plate %1%") % new_index; return new_index; } //destroy print's objects and results int PartPlateList::destroy_print(int print_index) { int result = 0; if (print_index >= 0) { std::map::iterator it = m_print_list.find(print_index); if (it != m_print_list.end()) { BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":delete Print %1% for print_index %2%") % it->second % print_index; delete it->second; m_print_list.erase(it); } else { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(":can not find Print for print_index %1%") % print_index; result = -1; } std::map::iterator it2 = m_gcode_result_list.find(print_index); if (it2 != m_gcode_result_list.end()) { BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":delete GCodeResult %1% for print_index %2%") % it2->second % print_index; delete it2->second; m_gcode_result_list.erase(it2); } else { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(":can not find GCodeResult for print_index %1%") % print_index; result = -1; } } return result; } //delete a plate by index //keep its instance at origin position and add them into next plate if have //update the plate index and position after it int PartPlateList::delete_plate(int index) { int ret = 0; PartPlate* plate = NULL; BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":delete plate %1%, count %2%") % index % m_plate_list.size(); if (index >= m_plate_list.size()) { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(":can not find plate"); return -1; } if (m_plate_list.size() <= 1) { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(":only one plate left, can not delete"); return -1; } plate = m_plate_list[index]; if (index != plate->get_index()) { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(":plate %1%, has an invalid index %2%") % index % plate->get_index(); return -1; } if (m_plater) { // In GUI mode // BBS: add wipe tower logic DynamicConfig& proj_cfg = wxGetApp().preset_bundle->project_config; ConfigOptionFloats* wipe_tower_x = proj_cfg.opt("wipe_tower_x"); ConfigOptionFloats* wipe_tower_y = proj_cfg.opt("wipe_tower_y"); // wipe_tower_x and wip_tower_y may be less than plate count in the following case: // 1. wipe_tower is enabled after creating new plates // 2. wipe tower is not enabled if (index < wipe_tower_x->values.size()) wipe_tower_x->values.erase(wipe_tower_x->values.begin() + index); if (index < wipe_tower_y->values.size()) wipe_tower_y->values.erase(wipe_tower_y->values.begin() + index); } int cols = compute_colum_count(m_plate_list.size() - 1); int old_cols = compute_colum_count(m_plate_list.size()); m_plate_list.erase(m_plate_list.begin() + index); update_plate_cols(); //update this plate //move this plate's instance to the end Vec3d current_origin; current_origin = compute_origin_for_unprintable(); plate->set_pos_and_size(current_origin, m_plate_width, m_plate_depth, m_plate_height, true); //update the plates after it for (unsigned int i = index; i < (unsigned int)m_plate_list.size(); ++i) { PartPlate* plate = m_plate_list[i]; assert(plate != NULL); plate->set_index(i); Vec3d origin = compute_origin(i, m_plate_cols); plate->set_pos_and_size(origin, m_plate_width, m_plate_depth, m_plate_height, true); //update render shapes Vec2d pos = compute_shape_position(i, m_plate_cols); plate->set_shape(m_shape, m_exclude_areas, pos, m_height_to_lid, m_height_to_rod); } //update current_plate if delete current if (m_current_plate == index && index == 0) { select_plate(0); } else if (m_current_plate >= index) { select_plate(m_current_plate - 1); } else { //delete the plate behind current, just need to update the position of Bed3D Vec2d pos = compute_shape_position(m_current_plate, m_plate_cols); if (m_plater) m_plater->set_bed_position(pos); } unprintable_plate.set_index(m_plate_list.size()); if (old_cols != cols) { //update the origin of each plate update_all_plates_pos_and_size(); set_shapes(m_shape, m_exclude_areas, m_logo_texture_filename, m_height_to_lid, m_height_to_rod); } else { //update the position of the unprintable plate Vec3d origin2 = compute_origin_for_unprintable(); unprintable_plate.set_pos_and_size(origin2, m_plate_width, m_plate_depth, m_plate_height, true); //update bounding_boxes calc_bounding_boxes(); } plate->move_instances_to(*(m_plate_list[m_plate_list.size()-1]), unprintable_plate); //destroy the print object int print_index; plate->get_print(nullptr, nullptr, &print_index); destroy_print(print_index); delete plate; // FIX: context of BackgroundSliceProcess and gcode preview need to be updated before ObjectList::reload_all_plates(). #if 0 if (m_plater != nullptr) { // In GUI mode wxGetApp().obj_list()->reload_all_plates(); } #endif return ret; } void PartPlateList::delete_selected_plate() { delete_plate(m_current_plate); } //get a plate pointer by index PartPlate* PartPlateList::get_plate(int index) { PartPlate* plate = NULL; if (index >= m_plate_list.size()) { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(":can not find index %1%, size %2%") % index % m_plate_list.size(); return NULL; } plate = m_plate_list[index]; assert(plate != NULL); return plate; } PartPlate* PartPlateList::get_selected_plate() { if (m_current_plate < 0 || m_current_plate >= m_plate_list.size()) { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(":can not find m_current_plate %1%, size %2%") % m_current_plate % m_plate_list.size(); return NULL; } return m_plate_list[m_current_plate]; } std::vector PartPlateList::get_nonempty_plate_list() { std::vector nonempty_plate_list; for (auto plate : m_plate_list){ if (plate->get_extruders().size() != 0) { nonempty_plate_list.push_back(plate); } } return nonempty_plate_list; } std::vector PartPlateList::get_nonempty_plates_slice_results() { std::vector nonempty_plates_slice_result; for (auto plate : get_nonempty_plate_list()) { nonempty_plates_slice_result.push_back(plate->get_slice_result()); } return nonempty_plates_slice_result; } std::set PartPlateList::get_extruders(bool conside_custom_gcode) const { int plate_count = get_plate_count(); std::set extruder_ids; for (size_t i = 0; i < plate_count; i++) { auto plate_extruders = m_plate_list[i]->get_extruders(conside_custom_gcode); extruder_ids.insert(plate_extruders.begin(), plate_extruders.end()); } return extruder_ids; } //select plate int PartPlateList::select_plate(int index) { const std::lock_guard local_lock(m_plates_mutex); if (m_plate_list.empty() || index >= m_plate_list.size()) { return -1; } // BBS: erase unnecessary snapshot if (get_curr_plate_index() != index && m_intialized) { if (m_plater) m_plater->take_snapshot("select partplate!"); } std::vector::iterator it = m_plate_list.begin(); for (it = m_plate_list.begin(); it != m_plate_list.end(); it++) { (*it)->set_unselected(); } m_current_plate = index; m_plate_list[m_current_plate]->set_selected(); //BBS if(m_model) m_model->curr_plate_index = index; //BBS update bed origin if (m_intialized && m_plater) { Vec2d pos = compute_shape_position(index, m_plate_cols); m_plater->set_bed_position(pos); //wxQueueEvent(m_plater, new SimpleEvent(EVT_GLCANVAS_PLATE_SELECT)); } return 0; } void PartPlateList::set_hover_id(int id) { int index = id / PartPlate::GRABBER_COUNT; int sub_hover_id = id % PartPlate::GRABBER_COUNT; m_plate_list[index]->set_hover_id(sub_hover_id); } void PartPlateList::reset_hover_id() { const std::lock_guard local_lock(m_plates_mutex); std::vector::iterator it = m_plate_list.begin(); for (it = m_plate_list.begin(); it != m_plate_list.end(); it++) { (*it)->set_hover_id(-1); } } bool PartPlateList::intersects(const BoundingBoxf3& bb) { bool result = false; std::vector::iterator it = m_plate_list.begin(); for (it = m_plate_list.begin(); it != m_plate_list.end(); it++) { if ((*it)->intersects(bb)) { result = true; } } return result; } bool PartPlateList::contains(const BoundingBoxf3& bb) { bool result = false; std::vector::iterator it = m_plate_list.begin(); for (it = m_plate_list.begin(); it != m_plate_list.end(); it++) { if ((*it)->contains(bb)) { result = true; } } return result; } double PartPlateList::plate_stride_x() { //const auto plate_shape = Slic3r::Polygon::new_scale(m_shape); //double plate_width = plate_shape.bounding_box().size().x(); //return unscaled((1. + LOGICAL_PART_PLATE_GAP) * plate_width); return m_plate_width * (1. + LOGICAL_PART_PLATE_GAP); } double PartPlateList::plate_stride_y() { //const auto plate_shape = Slic3r::Polygon::new_scale(m_shape); //double plate_depth = plate_shape.bounding_box().size().y(); //return unscaled((1. + LOGICAL_PART_PLATE_GAP) * plate_depth); return m_plate_depth * (1. + LOGICAL_PART_PLATE_GAP); } //get the plate counts, not including the invalid plate int PartPlateList::get_plate_count() const { int ret = 0; ret = m_plate_list.size(); return ret; } //update the plate cols due to plate count change void PartPlateList::update_plate_cols() { m_plate_count = m_plate_list.size(); m_plate_cols = compute_colum_count(m_plate_count); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(":m_plate_count %1%, m_plate_cols change to %2%") % m_plate_count % m_plate_cols; return; } void PartPlateList::update_all_plates_pos_and_size(bool adjust_position, bool with_unprintable_move, bool switch_plate_type, bool do_clear) { Vec3d origin1, origin2; for (unsigned int i = 0; i < (unsigned int)m_plate_list.size(); ++i) { PartPlate* plate = m_plate_list[i]; assert(plate != NULL); //compute origin1 for PartPlate origin1 = compute_origin(i, m_plate_cols); plate->set_pos_and_size(origin1, m_plate_width, m_plate_depth, m_plate_height, adjust_position, do_clear); // set default wipe pos when switch plate if (switch_plate_type && m_plater && plate->get_used_extruders().size() <= 0) { set_default_wipe_tower_pos_for_plate(i); } } origin2 = compute_origin_for_unprintable(); unprintable_plate.set_pos_and_size(origin2, m_plate_width, m_plate_depth, m_plate_height, with_unprintable_move); } //move the plate to position index int PartPlateList::move_plate_to_index(int old_index, int new_index) { int ret = 0, delta; Vec3d origin; if (old_index == new_index) { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(":should not happen, the same index %1%") % old_index; return -1; } if (old_index < new_index) { delta = 1; } else { delta = -1; } PartPlate* plate = m_plate_list[old_index]; //update the plates between old_index and new_index for (unsigned int i = (unsigned int)old_index; i != (unsigned int)new_index; i = i + delta) { m_plate_list[i] = m_plate_list[i + delta]; m_plate_list[i]->set_index(i); origin = compute_origin(i, m_plate_cols); m_plate_list[i]->set_pos_and_size(origin, m_plate_width, m_plate_depth, m_plate_height, true); } origin = compute_origin(new_index, m_plate_cols); m_plate_list[new_index] = plate; plate->set_index(new_index); plate->set_pos_and_size(origin, m_plate_width, m_plate_depth, m_plate_height, true); //update the new plate index m_current_plate = new_index; return ret; } //lock plate int PartPlateList::lock_plate(int index, bool state) { int ret = 0; PartPlate* plate = NULL; plate = get_plate(index); if (!plate) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":can not get plate for index %1%, size %2%") % index % m_plate_list.size(); return -1; } BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(":lock plate %1%, to state %2%") % index % state; plate->lock(state); return ret; } //find plate by print index, return -1 if not found int PartPlateList::find_plate_by_print_index(int print_index) { int plate_index = -1; for (unsigned int i = 0; i < (unsigned int)m_plate_list.size(); ++i) { PartPlate* plate = m_plate_list[i]; if (plate->m_print_index == print_index) { plate_index = i; break; } } return plate_index; } /*instance related operations*/ //find instance in which plate, return -1 when not found //this function only judges whether it is intersect with plate int PartPlateList::find_instance(int obj_id, int instance_id) { int ret = -1; //update the plates after it for (unsigned int i = 0; i < (unsigned int)m_plate_list.size(); ++i) { PartPlate* plate = m_plate_list[i]; assert(plate != NULL); if (plate->contain_instance(obj_id, instance_id)) return i; } //return -1 for not found return ret; } /*instance related operations*/ //find instance in which plate, return -1 when not found //this function only judges whether it is intersect with plate int PartPlateList::find_instance(BoundingBoxf3& bounding_box) { int ret = -1; //update the plates after it for (unsigned int i = 0; i < (unsigned int)m_plate_list.size(); ++i) { PartPlate* plate = m_plate_list[i]; assert(plate != NULL); if (plate->intersects(bounding_box)) return i; } //return -1 for not found return ret; } //this function not only judges whether it is intersect with plate, but also judges whether it is fully included in plate //returns -1 when can not find any plate int PartPlateList::find_instance_belongs(int obj_id, int instance_id) { int ret = -1; //update the plates after it for (unsigned int i = 0; i < (unsigned int)m_plate_list.size(); ++i) { PartPlate* plate = m_plate_list[i]; assert(plate != NULL); if (plate->contain_instance_totally(obj_id, instance_id)) return i; } //return -1 for not found return ret; } //notify instance's update, need to refresh the instance in plates //newly added or modified int PartPlateList::notify_instance_update(int obj_id, int instance_id, bool is_new) { int ret = 0, index; PartPlate* plate = NULL; ModelObject* object = NULL; if ((obj_id >= 0) && (obj_id < m_model->objects.size())) { object = m_model->objects[obj_id]; } else if (obj_id >= 1000 && obj_id < 1000 + m_plate_count) { //wipe tower updates PartPlate* plate = m_plate_list[obj_id - 1000]; plate->update_slice_result_valid_state( false ); plate->thumbnail_data.reset(); plate->no_light_thumbnail_data.reset(); plate->top_thumbnail_data.reset(); plate->pick_thumbnail_data.reset(); return 0; } else return -1; BoundingBoxf3 boundingbox = object->instance_convex_hull_bounding_box(instance_id); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": obj_id %1%, instance_id %2%") % obj_id % instance_id; index = find_instance(obj_id, instance_id); if (index != -1) { //found it added before BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": found it in previous plate %1%") % index; plate = m_plate_list[index]; if (!plate->intersect_instance(obj_id, instance_id, &boundingbox)) { //not include anymore, remove it from original plate BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": not in plate %1% anymore, remove it") % index; plate->remove_instance(obj_id, instance_id); } else { BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": still in original plate %1%, no need to be updated") % index; plate->update_instance_exclude_status(obj_id, instance_id, &boundingbox); plate->update_states(); plate->update_slice_result_valid_state(); plate->thumbnail_data.reset(); plate->no_light_thumbnail_data.reset(); plate->top_thumbnail_data.reset(); plate->pick_thumbnail_data.reset(); return 0; } plate->update_slice_result_valid_state(); plate->thumbnail_data.reset(); plate->no_light_thumbnail_data.reset(); plate->top_thumbnail_data.reset(); plate->pick_thumbnail_data.reset(); } else if (unprintable_plate.contain_instance(obj_id, instance_id)) { //found it in the unprintable plate BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": found it in unprintable plate"); if (!unprintable_plate.intersect_instance(obj_id, instance_id, &boundingbox)) { //not include anymore, remove it from original plate BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": not in unprintable plate anymore, remove it"); unprintable_plate.remove_instance(obj_id, instance_id); } else { BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": still in unprintable plate, no need to be updated"); return 0; } } auto is_object_config_compatible_with_spiral_vase = [](ModelObject* object) { const DynamicPrintConfig& config = object->config.get(); if (config.has("wall_loops") && config.opt_int("wall_loops") == 1 && config.has("top_shell_layers") && config.opt_int("top_shell_layers") == 0 && config.has("sparse_infill_density") && config.option("sparse_infill_density")->value == 0 && config.has("enable_support") && !config.opt_bool("enable_support") && config.has("enforce_support_layers") && config.opt_int("enforce_support_layers") == 0 && config.has("ensure_vertical_shell_thickness") && config.opt_bool("ensure_vertical_shell_thickness") && config.has("detect_thin_wall") && !config.opt_bool("detect_thin_wall") && config.has("timelapse_type") && config.opt_enum("timelapse_type") == TimelapseType::tlTraditional) return true; else return false; }; //try to find a new plate for (unsigned int i = 0; i < (unsigned int)m_plate_list.size(); ++i) { PartPlate* plate = m_plate_list[i]; assert(plate != NULL); if (plate->intersect_instance(obj_id, instance_id, &boundingbox)) { //found a new plate, add it to plate plate->add_instance(obj_id, instance_id, false, &boundingbox); // spiral mode, update object setting if (plate->config()->has("spiral_mode") && plate->config()->opt_bool("spiral_mode") && !is_object_config_compatible_with_spiral_vase(object)) { if (!is_new) { auto answer = static_cast(wxGetApp().plate_tab)->show_spiral_mode_settings_dialog(true); if (answer == wxID_YES) { plate->set_vase_mode_related_object_config(obj_id); } } else { plate->set_vase_mode_related_object_config(obj_id); } } plate->update_slice_result_valid_state(); plate->thumbnail_data.reset(); plate->no_light_thumbnail_data.reset(); plate->top_thumbnail_data.reset(); plate->pick_thumbnail_data.reset(); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": add it to new plate %1%") % i; return 0; } } if (unprintable_plate.intersect_instance(obj_id, instance_id, &boundingbox)) { //found in unprintable plate, add it to plate unprintable_plate.add_instance(obj_id, instance_id, false, &boundingbox); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": add it to unprintable plate"); return 0; } return 0; } //notify instance is removed int PartPlateList::notify_instance_removed(int obj_id, int instance_id) { int ret = 0, index, instance_to_delete = instance_id; PartPlate* plate = NULL; BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": obj_id %1%, instance_id %2%") % obj_id % instance_id; if (instance_id == -1) { instance_to_delete = 0; } index = find_instance(obj_id, instance_to_delete); if (index != -1) { //found it added before BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": found it in plate %1%, remove it") % index; plate = m_plate_list[index]; plate->remove_instance(obj_id, instance_to_delete); plate->update_slice_result_valid_state(); plate->thumbnail_data.reset(); plate->no_light_thumbnail_data.reset(); plate->top_thumbnail_data.reset(); plate->pick_thumbnail_data.reset(); } if (unprintable_plate.contain_instance(obj_id, instance_to_delete)) { //found in unprintable plate, add it to plate unprintable_plate.remove_instance(obj_id, instance_to_delete); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": found it in unprintable plate, remove it"); } if (instance_id == -1) { //update all the obj_ids which is bigger for (unsigned int i = 0; i < (unsigned int)m_plate_list.size(); ++i) { PartPlate* plate = m_plate_list[i]; assert(plate != NULL); plate->update_object_index(obj_id, m_model->objects.size()); } unprintable_plate.update_object_index(obj_id, m_model->objects.size()); } return 0; } //add instance to special plate, need to remove from the original plate //called from the right-mouse menu when a instance selected int PartPlateList::add_to_plate(int obj_id, int instance_id, int plate_id) { int ret = 0, index; PartPlate* plate = NULL; BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": plate_id %1%, found obj_id %2%, instance_id %3%") % plate_id % obj_id % instance_id; index = find_instance(obj_id, instance_id); if (index != -1) { //found it added before BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": found it in previous plate %1%") % index; if (index != plate_id) { //remove it from original plate first plate = m_plate_list[index]; plate->remove_instance(obj_id, instance_id); } else { BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": already in this plate, no need to be added"); return 0; } } else { BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": not added to plate before, add it to center"); } plate = get_plate(plate_id); if (!plate) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":can not get plate for index %1%, size %2%") % index % m_plate_list.size(); return -1; } ret = plate->add_instance(obj_id, instance_id, true); return ret; } //reload all objects int PartPlateList::reload_all_objects(bool except_locked, int plate_index) { int ret = 0; unsigned int i, j, k; clear(false, false, except_locked, plate_index); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": m_model->objects.size() is %1%") % m_model->objects.size(); //try to find a new plate for (i = 0; i < (unsigned int)m_model->objects.size(); ++i) { ModelObject* object = m_model->objects[i]; for (j = 0; j < (unsigned int)object->instances.size(); ++j) { ModelInstance* instance = object->instances[j]; BoundingBoxf3 boundingbox = object->instance_convex_hull_bounding_box(j); for (k = 0; k < (unsigned int)m_plate_list.size(); ++k) { PartPlate* plate = m_plate_list[k]; assert(plate != NULL); if (plate->intersect_instance(i, j, &boundingbox)) { //found a new plate, add it to plate plate->add_instance(i, j, false, &boundingbox); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": found plate_id %1%, for obj_id %2%, instance_id %3%") % k % i % j; //need to judge whether this instance has an outer part /*if (plate->check_outside(i, j)) { plate->m_ready_for_slice = false; }*/ break; } } if ((k == m_plate_list.size()) && (unprintable_plate.intersect_instance(i, j, &boundingbox))) { //found in unprintable plate, add it to plate unprintable_plate.add_instance(i, j, false, &boundingbox); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": found in unprintable plate, obj_id %1%, instance_id %2%") % i % j; } } } return ret; } //reload objects for newly created plate int PartPlateList::construct_objects_list_for_new_plate(int plate_index) { int ret = 0; unsigned int i, j, k; PartPlate* new_plate = m_plate_list[plate_index]; bool already_included; BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": m_model->objects.size() is %1%") % m_model->objects.size(); unprintable_plate.clear(); //try to find a new plate for (i = 0; i < (unsigned int)m_model->objects.size(); ++i) { ModelObject* object = m_model->objects[i]; for (j = 0; j < (unsigned int)object->instances.size(); ++j) { ModelInstance* instance = object->instances[j]; already_included = false; for (k = 0; k < (unsigned int)plate_index; ++k) { PartPlate* plate = m_plate_list[k]; if (plate->contain_instance(i, j)) { already_included = true; break; } } if (already_included) continue; BoundingBoxf3 boundingbox = object->instance_convex_hull_bounding_box(j); if (new_plate->intersect_instance(i, j, &boundingbox)) { //found a new plate, add it to plate ret |= new_plate->add_instance(i, j, false, &boundingbox); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": added to plate_id %1%, for obj_id %2%, instance_id %3%") % plate_index % i % j; continue; } if ( (unprintable_plate.intersect_instance(i, j, &boundingbox))) { //found in unprintable plate, add it to plate unprintable_plate.add_instance(i, j, false, &boundingbox); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": found in unprintable plate, obj_id %1%, instance_id %2%") % i % j; } } } return ret; } //compute the plate index int PartPlateList::compute_plate_index(arrangement::ArrangePolygon& arrange_polygon) { int row, col; float col_value = (unscale(arrange_polygon.translation(X))) / plate_stride_x(); float row_value = (plate_stride_y() - unscale(arrange_polygon.translation(Y))) / plate_stride_y(); row = round(row_value); col = round(col_value); return row * m_plate_cols + col; } //preprocess a arrangement::ArrangePolygon, return true if it is in a locked plate bool PartPlateList::preprocess_arrange_polygon(int obj_index, int instance_index, arrangement::ArrangePolygon& arrange_polygon, bool selected) { bool locked = false; int lockplate_cnt = 0; for (unsigned int i = 0; i < (unsigned int)m_plate_list.size(); ++i) { if (m_plate_list[i]->contain_instance(obj_index, instance_index)) { if (m_plate_list[i]->is_locked()) { locked = true; arrange_polygon.bed_idx = i; arrange_polygon.row = i / m_plate_cols; arrange_polygon.col = i % m_plate_cols; arrange_polygon.translation(X) -= scaled(plate_stride_x() * arrange_polygon.col); arrange_polygon.translation(Y) += scaled(plate_stride_y() * arrange_polygon.row); } else { if (!selected) { //will be treated as fixeditem later arrange_polygon.bed_idx = i - lockplate_cnt; arrange_polygon.row = i / m_plate_cols; arrange_polygon.col = i % m_plate_cols; arrange_polygon.translation(X) -= scaled(plate_stride_x() * arrange_polygon.col); arrange_polygon.translation(Y) += scaled(plate_stride_y() * arrange_polygon.row); } } BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": obj_id %1% instance_id %2% already in plate %3%, locked %4%, row %5%, col %6%\n") % obj_index % instance_index % i % locked % arrange_polygon.row % arrange_polygon.col; return locked; } if (m_plate_list[i]->is_locked()) lockplate_cnt++; } //not be contained by any plates if (!selected) arrange_polygon.bed_idx = PartPlateList::MAX_PLATES_COUNT; BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": not in any plates, bed_idx %1%, translation(x) %2%, (y) %3%") % arrange_polygon.bed_idx % unscale(arrange_polygon.translation(X)) % unscale(arrange_polygon.translation(Y)); return locked; } //preprocess a arrangement::ArrangePolygon, return true if it is not in current plate bool PartPlateList::preprocess_arrange_polygon_other_locked(int obj_index, int instance_index, arrangement::ArrangePolygon& arrange_polygon, bool selected) { bool locked = false; if (selected) { //arrange_polygon.translation(X) -= scaled(plate_stride_x() * m_current_plate); } else { locked = true; for (unsigned int i = 0; i < (unsigned int)m_plate_list.size(); ++i) { if (m_plate_list[i]->contain_instance(obj_index, instance_index)) { arrange_polygon.bed_idx = i; arrange_polygon.row = i / m_plate_cols; arrange_polygon.col = i % m_plate_cols; arrange_polygon.translation(X) -= scaled(plate_stride_x() * arrange_polygon.col); arrange_polygon.translation(Y) += scaled(plate_stride_y() * arrange_polygon.row); //BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": obj_id %1% instance_id %2% in plate %3%, locked %4%, row %5%, col %6%\n") % obj_index % instance_index % i % locked % arrange_polygon.row % arrange_polygon.col; return locked; } } arrange_polygon.bed_idx = PartPlateList::MAX_PLATES_COUNT; } return locked; } bool PartPlateList::preprocess_exclude_areas(arrangement::ArrangePolygons& unselected, int num_plates, float inflation) { bool added = false; if (m_exclude_areas.size() > 0) { //has exclude areas PartPlate *plate = m_plate_list[0]; for (int index = 0; index < plate->m_exclude_bounding_box.size(); index ++) { Polygon ap({ {scaled(plate->m_exclude_bounding_box[index].min.x()), scaled(plate->m_exclude_bounding_box[index].min.y())}, {scaled(plate->m_exclude_bounding_box[index].max.x()), scaled(plate->m_exclude_bounding_box[index].min.y())}, {scaled(plate->m_exclude_bounding_box[index].max.x()), scaled(plate->m_exclude_bounding_box[index].max.y())}, {scaled(plate->m_exclude_bounding_box[index].min.x()), scaled(plate->m_exclude_bounding_box[index].max.y())} }); for (int j = 0; j < num_plates; j++) { arrangement::ArrangePolygon ret; ret.poly.contour = ap; ret.translation = Vec2crd(0, 0); ret.rotation = 0.0f; ret.is_virt_object = true; ret.bed_idx = j; ret.height = 1; ret.name = "ExcludedRegion" + std::to_string(index); ret.inflation = inflation; unselected.emplace_back(std::move(ret)); } added = true; } } return added; } bool PartPlateList::preprocess_nonprefered_areas(arrangement::ArrangePolygons& regions, int num_plates, float inflation) { bool added = false; std::vector nonprefered_regions; nonprefered_regions.emplace_back(Vec2d{ 18,0 }, Vec2d{ 240,15 }); // new extrusion & hand-eye calibration region //has exclude areas PartPlate* plate = m_plate_list[0]; for (int index = 0; index < nonprefered_regions.size(); index++) { Polygon ap = scaled(nonprefered_regions[index]).polygon(); for (int j = 0; j < num_plates; j++) { arrangement::ArrangePolygon ret; ret.poly.contour = ap; ret.translation = Vec2crd(0, 0); ret.rotation = 0.0f; ret.is_virt_object = true; ret.is_extrusion_cali_object = true; ret.bed_idx = j; ret.height = 1; ret.name = "NonpreferedRegion" + std::to_string(index); ret.inflation = inflation; regions.emplace_back(std::move(ret)); } added = true; } return added; } //postprocess an arrangement::ArrangePolygon's bed index void PartPlateList::postprocess_bed_index_for_selected(arrangement::ArrangePolygon& arrange_polygon) { BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": bed_idx %1%, locked_plate %2%, translation(x) %3%, (y) %4%") % arrange_polygon.bed_idx % arrange_polygon.locked_plate % unscale(arrange_polygon.translation(X)) % unscale(arrange_polygon.translation(Y)); if (arrange_polygon.bed_idx == -1) { //outarea for large object, can not process here for the plate number maybe increased later BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": can not be arranged inside plate!"); return; } for (unsigned int i = 0; i < (unsigned int)m_plate_list.size(); ++i) { if (m_plate_list[i]->is_locked()) { BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": found locked_plate %1%, increate index by 1") % i; //arrange_polygon.translation(X) += scaled(plate_stride_x()); arrange_polygon.bed_idx += 1; //offset_x += scaled(plate_stride_x()); } else { //judge whether it is at the left side of the plate border if (arrange_polygon.bed_idx <= i) { BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":found in plate_index %1%, bed_idx %2%") % i % arrange_polygon.bed_idx; return; } } } //create a new plate which can hold this arrange_polygon int plate_index = create_plate(false); while (plate_index != -1) { if (arrange_polygon.bed_idx <= plate_index) { BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":new plate_index %1%, matches bed_idx %2%") % plate_index % arrange_polygon.bed_idx; break; } plate_index = create_plate(false); } return; } //postprocess an arrangement::ArrangePolygon's bed index void PartPlateList::postprocess_bed_index_for_unselected(arrangement::ArrangePolygon& arrange_polygon) { BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": bed_idx %1%, locked_plate %2%, translation(x) %3%, (y) %4%") % arrange_polygon.bed_idx % arrange_polygon.locked_plate % unscale(arrange_polygon.translation(X)) % unscale(arrange_polygon.translation(Y)); if (arrange_polygon.bed_idx == PartPlateList::MAX_PLATES_COUNT) return; for (unsigned int i = 0; i < (unsigned int)m_plate_list.size(); ++i) { if (m_plate_list[i]->is_locked()) { BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": found locked_plate %1%, increate index by 1") % i; //arrange_polygon.translation(X) += scaled(plate_stride_x()); arrange_polygon.bed_idx += 1; //offset_x += scaled(plate_stride_x()); } else { //judge whether it is at the left side of the plate border if (arrange_polygon.bed_idx <= i) { BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":found in plate_index %1%, bed_idx %2%") % i % arrange_polygon.bed_idx; return; } } } return; } //postprocess an arrangement::ArrangePolygon, other instances are under locked states void PartPlateList::postprocess_bed_index_for_current_plate(arrangement::ArrangePolygon& arrange_polygon) { BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": bed_idx %1%, locked_plate %2%, translation(x) %3%, (y) %4%") % arrange_polygon.bed_idx % arrange_polygon.locked_plate % unscale(arrange_polygon.translation(X)) % unscale(arrange_polygon.translation(Y)); if (arrange_polygon.bed_idx == -1) { //outarea for large object BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": can not be arranged inside plate!"); } else if (arrange_polygon.bed_idx == 0) arrange_polygon.bed_idx += m_current_plate; else arrange_polygon.bed_idx = m_plate_list.size(); return; } //postprocess an arrangement::ArrangePolygon void PartPlateList::postprocess_arrange_polygon(arrangement::ArrangePolygon& arrange_polygon, bool selected) { BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": bed_idx %1%, selected %2%, translation(x) %3%, (y) %4%") % arrange_polygon.bed_idx % selected % unscale(arrange_polygon.translation(X)) % unscale(arrange_polygon.translation(Y)); if ((selected) || (arrange_polygon.bed_idx != PartPlateList::MAX_PLATES_COUNT)) { if (arrange_polygon.bed_idx == -1) { // outarea for large object arrange_polygon.bed_idx = m_plate_list.size(); BoundingBox apbox = get_extents(arrange_polygon.transformed_poly()); // the item may have been rotated auto apbox_size = apbox.size(); arrange_polygon.translation(X) = 0.5 * apbox_size[0]; arrange_polygon.translation(Y) = scaled(static_cast(m_plate_depth)) - 0.5 * apbox_size[1]; } arrange_polygon.row = arrange_polygon.bed_idx / m_plate_cols; arrange_polygon.col = arrange_polygon.bed_idx % m_plate_cols; arrange_polygon.translation(X) += scaled(plate_stride_x() * arrange_polygon.col); arrange_polygon.translation(Y) -= scaled(plate_stride_y() * arrange_polygon.row); } return; } /*rendering related functions*/ //render void PartPlateList::render(bool bottom, bool only_current, bool only_body, int hover_id, bool render_cali) { const std::lock_guard local_lock(m_plates_mutex); std::vector::iterator it = m_plate_list.begin(); int plate_hover_index = -1; int plate_hover_action = -1; if (hover_id != -1) { plate_hover_index = hover_id / PartPlate::GRABBER_COUNT; plate_hover_action = hover_id % PartPlate::GRABBER_COUNT; } static bool last_dark_mode_status = m_is_dark; if (m_is_dark != last_dark_mode_status) { last_dark_mode_status = m_is_dark; generate_icon_textures(); }else if(m_del_texture.get_id() == 0) generate_icon_textures(); for (it = m_plate_list.begin(); it != m_plate_list.end(); it++) { int current_index = (*it)->get_index(); if (only_current && (current_index != m_current_plate)) continue; if (current_index == m_current_plate) { PartPlate::HeightLimitMode height_mode = (only_current)?PartPlate::HEIGHT_LIMIT_NONE:m_height_limit_mode; if (plate_hover_index == current_index) (*it)->render(bottom, only_body, false, height_mode, plate_hover_action, render_cali); else (*it)->render(bottom, only_body, false, height_mode, -1, render_cali); } else { if (plate_hover_index == current_index) (*it)->render(bottom, only_body, false, PartPlate::HEIGHT_LIMIT_NONE, plate_hover_action, render_cali); else (*it)->render(bottom, only_body, false, PartPlate::HEIGHT_LIMIT_NONE, -1, render_cali); } } } void PartPlateList::render_for_picking_pass() { const std::lock_guard local_lock(m_plates_mutex); std::vector::iterator it = m_plate_list.begin(); for (it = m_plate_list.begin(); it != m_plate_list.end(); it++) { (*it)->render_for_picking(); } } /*int PartPlateList::select_plate_by_hover_id(int hover_id) { int index = hover_id / PartPlate::GRABBER_COUNT; int sub_hover_id = hover_id % PartPlate::GRABBER_COUNT; if (sub_hover_id == 0) { select_plate(index); } else if (sub_hover_id == 1) { if (m_current_plate == 0) { select_plate(0); } else { select_plate(index - 1); } } else if (sub_hover_id == 2) { if (m_current_plate == (get_plate_count() - 1)) { select_plate(m_current_plate); } else { select_plate(index + 1); } } else { return -1; } return 0; }*/ void PartPlateList::set_render_option(bool bedtype_texture, bool plate_settings) { render_bedtype_logo = bedtype_texture; render_plate_settings = plate_settings; } int PartPlateList::select_plate_by_obj(int obj_index, int instance_index) { int ret = 0, index; PartPlate* plate = NULL; BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": obj_id %1%, instance_id %2%") % obj_index % instance_index; index = find_instance(obj_index, instance_index); if (index != -1) { //found it in plate BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": found it in plate %1%") % index; select_plate(index); return 0; } return -1; } void PartPlateList::calc_bounding_boxes() { m_bounding_box.reset(); std::vector::iterator it = m_plate_list.begin(); for (it = m_plate_list.begin(); it != m_plate_list.end(); it++) { m_bounding_box.merge((*it)->get_bounding_box(true)); } } void PartPlateList::select_plate_view() { if (m_current_plate < 0 || m_current_plate >= m_plate_list.size()) return; Vec3d target = m_plate_list[m_current_plate]->get_bounding_box(false).center(); Vec3d position(target.x(), target.y(), m_plater->get_camera().get_distance()); m_plater->get_camera().look_at(position, target, Vec3d::UnitY()); m_plater->get_camera().select_view("topfront"); } bool PartPlateList::set_shapes(const Pointfs& shape, const Pointfs& exclude_areas, const std::string& texture_filename, float height_to_lid, float height_to_rod) { const std::lock_guard local_lock(m_plates_mutex); m_shape = shape; m_exclude_areas = exclude_areas; m_height_to_lid = height_to_lid; m_height_to_rod = height_to_rod; double stride_x = plate_stride_x(); double stride_y = plate_stride_y(); for (unsigned int i = 0; i < (unsigned int)m_plate_list.size(); ++i) { PartPlate* plate = m_plate_list[i]; assert(plate != NULL); Vec2d pos; pos = compute_shape_position(i, m_plate_cols); plate->set_shape(shape, exclude_areas, pos, height_to_lid, height_to_rod); } is_load_bedtype_textures = false;//reload textures calc_bounding_boxes(); update_logo_texture_filename(texture_filename); return true; } void PartPlateList::update_logo_texture_filename(const std::string &texture_filename) { auto check_texture = [](const std::string &texture) { boost::system::error_code ec; // so the exists call does not throw (e.g. after a permission problem) return !texture.empty() && (boost::algorithm::iends_with(texture, ".png") || boost::algorithm::iends_with(texture, ".svg")) && boost::filesystem::exists(texture, ec); }; if (!texture_filename.empty() && !check_texture(texture_filename)) { BOOST_LOG_TRIVIAL(error) << "Unable to load bed texture: " << texture_filename; } else m_logo_texture_filename = texture_filename; } /*slice related functions*/ //update current slice context into backgroud slicing process void PartPlateList::update_slice_context_to_current_plate(BackgroundSlicingProcess& process) { PartPlate* current_plate; current_plate = m_plate_list[m_current_plate]; assert(current_plate != NULL); current_plate->update_slice_context(process); return; } //return the current fff print object Print& PartPlateList::get_current_fff_print() const { PartPlate* current_plate; Print* print; current_plate = m_plate_list[m_current_plate]; //BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(":m_current_plate %1%, current_plate %2%") % m_current_plate % current_plate; assert(current_plate != NULL); current_plate->get_print((PrintBase **)&print, nullptr, nullptr); return *print; } //return the slice result GCodeProcessorResult* PartPlateList::get_current_slice_result() const { PartPlate* current_plate; current_plate = m_plate_list[m_current_plate]; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(":m_current_plate %1%, current_plate %2%") % m_current_plate % current_plate; assert(current_plate != NULL); return current_plate->get_slice_result(); } //invalid all the plater's slice result void PartPlateList::invalid_all_slice_result() { BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": plates count %1%") % m_plate_list.size(); for (unsigned int i = 0; i < (unsigned int)m_plate_list.size(); ++i) { m_plate_list[i]->update_slice_result_valid_state(false); } return; } //check whether all plates's slice result valid bool PartPlateList::is_all_slice_results_valid() const { for (unsigned int i = 0; i < (unsigned int)m_plate_list.size(); ++i) { if (!m_plate_list[i]->is_slice_result_valid()) return false; } return true; } //check whether all plates's slice result valid for print bool PartPlateList::is_all_slice_results_ready_for_print() const { bool res = false; for (unsigned int i = 0; i < (unsigned int) m_plate_list.size(); ++i) { if (!m_plate_list[i]->empty()) { if (m_plate_list[i]->is_all_instances_unprintable()) { continue; } if (!m_plate_list[i]->is_slice_result_ready_for_print()) { return false; } } if (m_plate_list[i]->is_slice_result_ready_for_print()) { res = true; } } return res; } //check whether all plates' slice result valid for export to file bool PartPlateList::is_all_slice_result_ready_for_export() const { bool res = false; for (unsigned int i = 0; i < (unsigned int) m_plate_list.size(); ++i) { if (!m_plate_list[i]->empty()) { if (m_plate_list[i]->is_all_instances_unprintable()) { continue; } if (!m_plate_list[i]->is_slice_result_ready_for_print()) { return false; } } if (m_plate_list[i]->is_slice_result_ready_for_print()) { if (!m_plate_list[i]->has_printable_instances()) { return false; } res = true; } } return res; } //check whether all plates ready for slice bool PartPlateList::is_all_plates_ready_for_slice() const { for (unsigned int i = 0; i < (unsigned int)m_plate_list.size(); ++i) { if (m_plate_list[i]->can_slice()) return true; } return false; } //will create a plate and load gcode, return the plate index int PartPlateList::create_plate_from_gcode_file(const std::string& filename) { int ret = 0; return ret; } void PartPlateList::get_sliced_result(std::vector& sliced_result, std::vector& gcode_paths) { sliced_result.resize(m_plate_list.size()); gcode_paths.resize(m_plate_list.size()); for (unsigned int i = 0; i < (unsigned int)m_plate_list.size(); ++i) { sliced_result[i] = m_plate_list[i]->m_slice_result_valid; gcode_paths[i] = m_plate_list[i]->m_tmp_gcode_path; } } //rebuild data which are not serialized after de-serialize int PartPlateList::rebuild_plates_after_deserialize(std::vector& previous_sliced_result, std::vector& previous_gcode_paths) { int ret = 0; BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": plates count %1%") % m_plate_list.size(); update_plate_cols(); update_all_plates_pos_and_size(false, false, false, false); set_shapes(m_shape, m_exclude_areas, m_logo_texture_filename, m_height_to_lid, m_height_to_rod); for (unsigned int i = 0; i < (unsigned int)m_plate_list.size(); ++i) { bool need_reset_print = false; m_plate_list[i]->m_plater = this->m_plater; m_plate_list[i]->m_partplate_list = this; m_plate_list[i]->m_model = this->m_model; m_plate_list[i]->printer_technology = this->printer_technology; //check the previous sliced result if (m_plate_list[i]->m_slice_result_valid) { if ((i >= previous_sliced_result.size()) || !previous_sliced_result[i]) m_plate_list[i]->update_slice_result_valid_state(false); } if ((i < previous_gcode_paths.size()) && !previous_gcode_paths[i].empty() && (m_plate_list[i]->m_tmp_gcode_path != previous_gcode_paths[i])) { if (boost::filesystem::exists(previous_gcode_paths[i])) { boost::nowide::remove(previous_gcode_paths[i].c_str()); need_reset_print = true; } } std::map::iterator it = m_print_list.find(m_plate_list[i]->m_print_index); std::map::iterator it2 = m_gcode_result_list.find(m_plate_list[i]->m_print_index); if (it != m_print_list.end()) { //find it if (it2 == m_gcode_result_list.end()) { //should not happen assert(0); BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":can not find gcode result for plate %1%, print index %2%") % i % m_plate_list[i]->m_print_index; delete it->second; m_print_list.erase(it); } else { m_plate_list[i]->set_print(it->second, it2->second, m_plate_list[i]->m_print_index); it->second->set_plate_index(i); if (need_reset_print) { Print *print = dynamic_cast(it->second); it2->second->reset(); print->set_gcode_file_invalidated(); if ((i == m_current_plate)&&m_plater) m_plater->reset_gcode_toolpaths(); } continue; } } //can not find, create a new one Print* print = new Print(); GCodeResult* gcode = new GCodeResult(); m_print_list.emplace(m_print_index, print); m_gcode_result_list.emplace(m_print_index, gcode); m_plate_list[i]->set_print(print, gcode, m_print_index); print->set_plate_index(i); m_print_index++; } //go through the print list, and delete the one not used by plate std::map::iterator it = m_print_list.begin(); int print_index; std::vector delete_list; while (it != m_print_list.end()) { print_index = it->first; int plate_index = find_plate_by_print_index(print_index); if (plate_index < 0) { delete_list.push_back(print_index); } it++; } for (unsigned int index = 0; index < delete_list.size(); index++) { destroy_print(delete_list[index]); } //update the bed's position Vec2d pos = compute_shape_position(m_current_plate, m_plate_cols); m_plater->set_bed_position(pos); //not used /*if (m_plate_width == 0) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": jump to the first init state, need to re-set size!"); Vec3d max = m_plater->get_bed().get_bounding_box(false).max; Vec3d min = m_plater->get_bed().get_bounding_box(false).min; double z = m_plater->config()->opt_float("printable_height"); reset_size(max.x() - min.x(), max.y() - min.y(), z); }*/ return ret; } //retruct plates structures after auto-arrangement int PartPlateList::rebuild_plates_after_arrangement(bool recycle_plates, bool except_locked, int plate_index) { int ret = 0; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(":before rebuild, plates count %1%, recycle_plates %2%") % m_plate_list.size() % recycle_plates; // sort by arrange_order std::sort(m_model->objects.begin(), m_model->objects.end(), [](auto a, auto b) {return a->instances[0]->arrange_order < b->instances[0]->arrange_order; }); //for (auto object : m_model->objects) // std::sort(object->instances.begin(), object->instances.end(), [](auto a, auto b) {return a->arrange_order < b->arrange_order; }); ret = reload_all_objects(except_locked, plate_index); if (recycle_plates) { for (unsigned int i = (unsigned int)m_plate_list.size() - 1; i > 0; --i) { if (m_plate_list[i]->empty() || !m_plate_list[i]->has_printable_instances()) { //delete it BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(":delete plate %1% for empty") % i; delete_plate(i); } else if (m_plate_list[i]->is_locked()) { continue; } else { break; } } } #if 0 if (m_plater != nullptr) { // In GUI mode wxGetApp().obj_list()->reload_all_plates(); } #endif BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(":after rebuild, plates count %1%") % m_plate_list.size(); return ret; } int PartPlateList::store_to_3mf_structure(PlateDataPtrs& plate_data_list, bool with_slice_info, int plate_idx) { int ret = 0; plate_data_list.clear(); plate_data_list.reserve(m_plate_list.size()); for (unsigned int i = 0; i < (unsigned int)m_plate_list.size(); ++i) { PlateData* plate_data_item = new PlateData(); plate_data_item->locked = m_plate_list[i]->m_locked; plate_data_item->plate_index = m_plate_list[i]->m_plate_index; plate_data_item->plate_name = m_plate_list[i]->get_plate_name(); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1% before load, width %2%, height %3%, size %4%!") %(i+1) %m_plate_list[i]->thumbnail_data.width %m_plate_list[i]->thumbnail_data.height %m_plate_list[i]->thumbnail_data.pixels.size(); plate_data_item->plate_thumbnail.load_from(m_plate_list[i]->thumbnail_data); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1% after load, width %2%, height %3%, size %4%!") %(i+1) %plate_data_item->plate_thumbnail.width %plate_data_item->plate_thumbnail.height %plate_data_item->plate_thumbnail.pixels.size(); plate_data_item->config.apply(*m_plate_list[i]->config()); if (m_plate_list[i]->no_light_thumbnail_data.is_valid()) plate_data_item->no_light_thumbnail_file = "valid_no_light"; if (m_plate_list[i]->top_thumbnail_data.is_valid()) plate_data_item->top_file = "valid_top"; if (m_plate_list[i]->pick_thumbnail_data.is_valid()) plate_data_item->pick_file = "valid_pick"; if (m_plate_list[i]->obj_to_instance_set.size() > 0) { for (std::set>::iterator it = m_plate_list[i]->obj_to_instance_set.begin(); it != m_plate_list[i]->obj_to_instance_set.end(); ++it) plate_data_item->objects_and_instances.emplace_back(it->first, it->second); } BOOST_LOG_TRIVIAL(info) << __FUNCTION__ <m_gcode_result->filename % with_slice_info %m_plate_list[i]->is_slice_result_valid()%plate_data_item->objects_and_instances.size(); if (with_slice_info) { if (m_plate_list[i]->get_slice_result() && m_plate_list[i]->is_slice_result_valid()) { // BBS only include current palte_idx if (plate_idx == i || plate_idx == PLATE_CURRENT_IDX || plate_idx == PLATE_ALL_IDX) { //load calibration thumbnail //if (m_plate_list[i]->cali_thumbnail_data.is_valid()) // plate_data_item->pattern_file = "valid_pattern"; if (m_plate_list[i]->cali_bboxes_data.is_valid()) plate_data_item->pattern_bbox_file = "valid_pattern_bbox"; plate_data_item->gcode_file = m_plate_list[i]->m_gcode_result->filename; plate_data_item->is_sliced_valid = true; plate_data_item->gcode_prediction = std::to_string( (int) m_plate_list[i]->get_slice_result()->print_statistics.modes[static_cast(PrintEstimatedStatistics::ETimeMode::Normal)].time); plate_data_item->toolpath_outside = m_plate_list[i]->m_gcode_result->toolpath_outside; plate_data_item->timelapse_warning_code = m_plate_list[i]->m_gcode_result->timelapse_warning_code; m_plate_list[i]->set_timelapse_warning_code(plate_data_item->timelapse_warning_code); plate_data_item->is_label_object_enabled = m_plate_list[i]->m_gcode_result->label_object_enabled; Print *print = nullptr; m_plate_list[i]->get_print((PrintBase **) &print, nullptr, nullptr); if (print) { const PrintStatistics &ps = print->print_statistics(); if (ps.total_weight != 0.0) { CNumericLocalesSetter locales_setter; plate_data_item->gcode_weight =wxString::Format("%.2f", ps.total_weight).ToStdString(); } plate_data_item->is_support_used = print->is_support_used(); } else { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("print is null!"); } //parse filament info plate_data_item->parse_filament_info(m_plate_list[i]->get_slice_result()); } else { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "slice result = " << m_plate_list[i]->get_slice_result() << ", result valid = " << m_plate_list[i]->is_slice_result_valid(); } } } plate_data_list.push_back(plate_data_item); } BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(":stored %1% plates!") % m_plate_list.size(); return ret; } int PartPlateList::load_from_3mf_structure(PlateDataPtrs& plate_data_list) { int ret = 0; if (plate_data_list.size() <= 0) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":no plates, should not happen!"); return -1; } clear(true, true); for (unsigned int i = 0; i < (unsigned int)plate_data_list.size(); ++i) { int index = create_plate(false); m_plate_list[index]->m_locked = plate_data_list[i]->locked; m_plate_list[index]->config()->apply(plate_data_list[i]->config); m_plate_list[index]->set_plate_name(plate_data_list[i]->plate_name); if (plate_data_list[i]->plate_index != index) { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(":plate index %1% seems invalid, skip it")% plate_data_list[i]->plate_index; } BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1%, gcode_file %2%, is_sliced_valid %3%, toolpath_outside %4%, is_support_used %5% is_label_object_enabled %6%") %i %plate_data_list[i]->gcode_file %plate_data_list[i]->is_sliced_valid %plate_data_list[i]->toolpath_outside %plate_data_list[i]->is_support_used %plate_data_list[i]->is_label_object_enabled; //load object and instance from 3mf //just test for file correct or not, we will rebuild later /*for (std::vector>::iterator it = plate_data_list[i]->objects_and_instances.begin(); it != plate_data_list[i]->objects_and_instances.end(); ++it) m_plate_list[index]->obj_to_instance_set.insert(std::pair(it->first, it->second));*/ if (!plate_data_list[i]->gcode_file.empty()) { m_plate_list[index]->m_gcode_path_from_3mf = plate_data_list[i]->gcode_file; } GCodeResult* gcode_result = nullptr; PrintBase* fff_print = nullptr; m_plate_list[index]->get_print(&fff_print, &gcode_result, nullptr); PrintStatistics& ps = (dynamic_cast(fff_print))->print_statistics(); gcode_result->print_statistics.modes[static_cast(PrintEstimatedStatistics::ETimeMode::Normal)].time = atoi(plate_data_list[i]->gcode_prediction.c_str()); ps.total_weight = atof(plate_data_list[i]->gcode_weight.c_str()); ps.total_used_filament = 0.f; for (auto filament_item: plate_data_list[i]->slice_filaments_info) { ps.total_used_filament += filament_item.used_m; } ps.total_used_filament *= 1000; //koef gcode_result->toolpath_outside = plate_data_list[i]->toolpath_outside; gcode_result->label_object_enabled = plate_data_list[i]->is_label_object_enabled; gcode_result->timelapse_warning_code = plate_data_list[i]->timelapse_warning_code; m_plate_list[index]->set_timelapse_warning_code(plate_data_list[i]->timelapse_warning_code); m_plate_list[index]->slice_filaments_info = plate_data_list[i]->slice_filaments_info; gcode_result->warnings = plate_data_list[i]->warnings; if (m_plater && !plate_data_list[i]->thumbnail_file.empty()) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1%, load thumbnail from %2%.")%(i+1) %plate_data_list[i]->thumbnail_file; if (boost::filesystem::exists(plate_data_list[i]->thumbnail_file)) { m_plate_list[index]->load_thumbnail_data(plate_data_list[i]->thumbnail_file, m_plate_list[index]->thumbnail_data); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ <thumbnail_data.width %m_plate_list[index]->thumbnail_data.height %m_plate_list[index]->thumbnail_data.pixels.size(); } } if (m_plater && !plate_data_list[i]->no_light_thumbnail_file.empty()) { if (boost::filesystem::exists(plate_data_list[i]->no_light_thumbnail_file)) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1%, load no_light_thumbnail_file from %2%.")%(i+1) %plate_data_list[i]->no_light_thumbnail_file; m_plate_list[index]->load_thumbnail_data(plate_data_list[i]->no_light_thumbnail_file, m_plate_list[index]->no_light_thumbnail_data); } } /*if (m_plater && !plate_data_list[i]->pattern_file.empty()) { if (boost::filesystem::exists(plate_data_list[i]->pattern_file)) { //no need to load pattern data currently //m_plate_list[index]->load_pattern_thumbnail_data(plate_data_list[i]->pattern_file); } }*/ if (m_plater && !plate_data_list[i]->top_file.empty()) { if (boost::filesystem::exists(plate_data_list[i]->top_file)) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1%, load top_thumbnail from %2%.")%(i+1) %plate_data_list[i]->top_file; m_plate_list[index]->load_thumbnail_data(plate_data_list[i]->top_file, m_plate_list[index]->top_thumbnail_data); } } if (m_plater && !plate_data_list[i]->pick_file.empty()) { if (boost::filesystem::exists(plate_data_list[i]->pick_file)) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1%, load pick_thumbnail from %2%.")%(i+1) %plate_data_list[i]->pick_file; m_plate_list[index]->load_thumbnail_data(plate_data_list[i]->pick_file, m_plate_list[index]->pick_thumbnail_data); } } if (m_plater && !plate_data_list[i]->pattern_bbox_file.empty()) { if (boost::filesystem::exists(plate_data_list[i]->pattern_bbox_file)) { m_plate_list[index]->load_pattern_box_data(plate_data_list[i]->pattern_bbox_file); } } } print(); ret = reload_all_objects(); print(); return ret; } //load gcode files int PartPlateList::load_gcode_files() { int ret = 0; //only do this while m_plater valid for gui mode if (!m_plater) return ret; for (unsigned int i = 0; i < (unsigned int)m_plate_list.size(); ++i) { if (!m_plate_list[i]->m_gcode_path_from_3mf.empty()) { //the same as plater::priv::update_print_volume_state(); //BoundingBoxf3 print_volume = m_plate_list[i]->get_bounding_box(false); //print_volume.max(2) = this->m_plate_height; //print_volume.min(2) = -1e10; m_model->update_print_volume_state({m_plate_list[i]->get_shape(), (double)this->m_plate_height }); if (!m_plate_list[i]->load_gcode_from_file(m_plate_list[i]->m_gcode_path_from_3mf)) ret ++; } } BOOST_LOG_TRIVIAL(trace) << boost::format("totally got %1% gcode files") % ret; return ret; } void PartPlateList::print() const { BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << boost::format("PartPlateList %1%, m_plate_count %2%, current_plate %3%, print_count %4%, current print index %5%, plate cols %6%") % this % m_plate_count % m_current_plate % m_print_list.size() % m_print_index % m_plate_cols; BOOST_LOG_TRIVIAL(trace) << boost::format("m_plate_width %1%, m_plate_depth %2%, m_plate_height %3%, plate count %4%\nplate list:") % m_plate_width % m_plate_depth % m_plate_height % m_plate_list.size(); for (unsigned int i = 0; i < (unsigned int)m_plate_list.size(); ++i) { BOOST_LOG_TRIVIAL(trace) << boost::format("the %1%th plate") % i; m_plate_list[i]->print(); } BOOST_LOG_TRIVIAL(trace) << boost::format("the unprintable plate:"); unprintable_plate.print(); flush_logs(); return; } bool PartPlateList::is_load_bedtype_textures = false; bool PartPlateList::is_load_cali_texture = false; void PartPlateList::BedTextureInfo::TexturePart::update_buffer() { if (w == 0 || h == 0) { return; } Pointfs rectangle; rectangle.push_back(Vec2d(x, y)); rectangle.push_back(Vec2d(x+w, y)); rectangle.push_back(Vec2d(x+w, y+h)); rectangle.push_back(Vec2d(x, y+h)); ExPolygon poly; for (int i = 0; i < 4; i++) { const Vec2d & p = rectangle[i]; for (auto& p : rectangle) { Vec2d pp = Vec2d(p.x() + offset.x(), p.y() + offset.y()); poly.contour.append({ scale_(pp(0)), scale_(pp(1)) }); } } if (!buffer) buffer = new GeometryBuffer(); if (buffer->set_from_triangles(triangulate_expolygon_2f(poly, NORMALS_UP), GROUND_Z + 0.02f)) { release_vbo(); unsigned int* vbo_id_ptr = const_cast(&vbo_id); glsafe(::glGenBuffers(1, vbo_id_ptr)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, *vbo_id_ptr)); glsafe(::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)buffer->get_vertices_data_size(), (const GLvoid*)buffer->get_vertices_data(), GL_STATIC_DRAW)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); } else { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":Unable to create buffer triangles\n"; } } void PartPlateList::BedTextureInfo::TexturePart::reset() { if (texture) { texture->reset(); delete texture; } if (buffer) { release_vbo(); delete buffer; } } void PartPlateList::BedTextureInfo::TexturePart::release_vbo() { if (vbo_id != 0) { glsafe(::glDeleteBuffers(1, &vbo_id)); vbo_id = 0; } } void PartPlateList::BedTextureInfo::reset() { for (size_t i = 0; i < parts.size(); i++) parts[i].reset(); } void PartPlateList::init_bed_type_info() { BedTextureInfo::TexturePart pc_part1(10, 130, 10, 110, "bbl_bed_pc_left.svg"); BedTextureInfo::TexturePart pc_part2(74, -10, 148, 12, "bbl_bed_pc_bottom.svg"); BedTextureInfo::TexturePart ep_part1(7.5, 90, 12.5, 150, "bbl_bed_ep_left.svg"); BedTextureInfo::TexturePart ep_part2(74, -10, 148, 12, "bbl_bed_ep_bottom.svg"); BedTextureInfo::TexturePart pei_part1(7.5, 50, 12.5, 190, "bbl_bed_pei_left.svg"); BedTextureInfo::TexturePart pei_part2(74, -10, 148, 12, "bbl_bed_pei_bottom.svg"); BedTextureInfo::TexturePart pte_part1(10, 80, 10, 160, "bbl_bed_pte_left.svg"); BedTextureInfo::TexturePart pte_part2(74, -10, 148, 12, "bbl_bed_pte_bottom.svg"); for (size_t i = 0; i < btCount; i++) { bed_texture_info[i].reset(); bed_texture_info[i].parts.clear(); } bed_texture_info[btPC].parts.push_back(pc_part1); bed_texture_info[btPC].parts.push_back(pc_part2); bed_texture_info[btEP].parts.push_back(ep_part1); bed_texture_info[btEP].parts.push_back(ep_part2); bed_texture_info[btPEI].parts.push_back(pei_part1); bed_texture_info[btPEI].parts.push_back(pei_part2); bed_texture_info[btPTE].parts.push_back(pte_part1); bed_texture_info[btPTE].parts.push_back(pte_part2); auto bed_ext = get_extents(m_shape); int bed_width = bed_ext.size()(0); int bed_height = bed_ext.size()(1); float base_width = 256; float base_height = 256; float x_rate = bed_width / base_width; float y_rate = bed_height / base_height; for (int i = 0; i < btCount; i++) { for (int j = 0; j < bed_texture_info[i].parts.size(); j++) { bed_texture_info[i].parts[j].x *= x_rate; bed_texture_info[i].parts[j].y *= y_rate; bed_texture_info[i].parts[j].w *= x_rate; bed_texture_info[i].parts[j].h *= y_rate; bed_texture_info[i].parts[j].update_buffer(); } } } void PartPlateList::load_bedtype_textures() { if (PartPlateList::is_load_bedtype_textures) return; init_bed_type_info(); GLint max_tex_size = OpenGLManager::get_gl_info().get_max_tex_size(); GLint logo_tex_size = (max_tex_size < 2048) ? max_tex_size : 2048; for (int i = 0; i < (unsigned int)btCount; ++i) { for (int j = 0; j < bed_texture_info[i].parts.size(); j++) { std::string filename = resources_dir() + "/images/" + bed_texture_info[i].parts[j].filename; if (boost::filesystem::exists(filename)) { PartPlateList::bed_texture_info[i].parts[j].texture = new GLTexture(); if (!PartPlateList::bed_texture_info[i].parts[j].texture->load_from_svg_file(filename, true, false, false, logo_tex_size)) { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": load logo texture from %1% failed!") % filename; } } else { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": load logo texture from %1% failed!") % filename; } } } PartPlateList::is_load_bedtype_textures = true; } void PartPlateList::init_cali_texture_info() { BedTextureInfo::TexturePart cali_line(18, 2, 224, 16, "bbl_cali_lines.svg"); cali_texture_info.parts.push_back(cali_line); for (int j = 0; j < cali_texture_info.parts.size(); j++) { cali_texture_info.parts[j].update_buffer(); } } void PartPlateList::load_cali_textures() { if (PartPlateList::is_load_cali_texture) return; init_cali_texture_info(); GLint max_tex_size = OpenGLManager::get_gl_info().get_max_tex_size(); GLint logo_tex_size = (max_tex_size < 2048) ? max_tex_size : 2048; for (int i = 0; i < (unsigned int)btCount; ++i) { for (int j = 0; j < cali_texture_info.parts.size(); j++) { std::string filename = resources_dir() + "/images/" + cali_texture_info.parts[j].filename; if (boost::filesystem::exists(filename)) { PartPlateList::cali_texture_info.parts[j].texture = new GLTexture(); if (!PartPlateList::cali_texture_info.parts[j].texture->load_from_svg_file(filename, true, false, false, logo_tex_size)) { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": load cali texture from %1% failed!") % filename; } } else { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": load cali texture from %1% failed!") % filename; } } } PartPlateList::is_load_cali_texture = true; } }//end namespace GUI }//end namespace slic3r