New BuildVolume class was created, which detects build volume type (rectangular,

circular, convex, concave) and performs efficient collision detection agains these build
volumes. As of now, collision detection is performed against a convex
hull of a concave build volume for efficency.

GCodeProcessor::Result renamed out of GCodeProcessor to GCodeProcessorResult,
so it could be forward declared.

Plater newly exports BuildVolume, not Bed3D. Bed3D is a rendering class,
while BuildVolume is a purely geometric class.

Reduced usage of global wxGetApp, the Bed3D is passed as a parameter
to View3D/Preview/GLCanvas.

Convex hull code was extracted from Geometry.cpp/hpp to Geometry/ConvexHulll.cpp,hpp.
New test inside_convex_polygon().
New efficent point inside polygon test: Decompose convex hull
to bottom / top parts and use the decomposition to detect point inside
a convex polygon in O(log n). decompose_convex_polygon_top_bottom(),
inside_convex_polygon().

New Circle constructing functions: circle_ransac() and circle_taubin_newton().

New polygon_is_convex() test with unit tests.
This commit is contained in:
Vojtech Bubnik 2021-11-16 10:15:51 +01:00
parent b431fd1f7e
commit cc44089440
51 changed files with 1544 additions and 1594 deletions

View file

@ -137,7 +137,7 @@ void Bed3D::Axes::render() const
glsafe(::glDisable(GL_DEPTH_TEST));
}
bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom)
bool Bed3D::set_shape(const Pointfs& bed_shape, const double max_print_height, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom)
{
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)
@ -149,17 +149,13 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c
return !model.empty() && boost::algorithm::iends_with(model, ".stl") && boost::filesystem::exists(model, ec);
};
EType type;
Type type;
std::string model;
std::string texture;
if (force_as_custom)
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
type = EType::Custom;
#else
type = Custom;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
type = Type::Custom;
else {
auto [new_type, system_model, system_texture] = detect_type(shape);
auto [new_type, system_model, system_texture] = detect_type(bed_shape);
type = new_type;
model = system_model;
texture = system_texture;
@ -177,29 +173,18 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c
model_filename.clear();
}
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
EShapeType shape_type = detect_shape_type(shape);
if (m_shape == shape && m_type == type && m_shape_type == shape_type && m_texture_filename == texture_filename && m_model_filename == model_filename)
#else
if (m_shape == shape && m_type == type && m_texture_filename == texture_filename && m_model_filename == model_filename)
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
if (m_build_volume.bed_shape() == bed_shape && m_type == type && m_texture_filename == texture_filename && m_model_filename == model_filename)
// No change, no need to update the UI.
return false;
m_shape = shape;
m_type = type;
m_build_volume = BuildVolume { bed_shape, max_print_height };
m_texture_filename = texture_filename;
m_model_filename = model_filename;
m_type = type;
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
m_shape_type = shape_type;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
m_extended_bounding_box = this->calc_extended_bounding_box();
calc_bounding_boxes();
ExPolygon poly;
for (const Vec2d& p : m_shape) {
poly.contour.append({ scale_(p(0)), scale_(p(1)) });
}
ExPolygon poly{ Polygon::new_scale(bed_shape) };
calc_triangles(poly);
@ -208,13 +193,13 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c
m_polygon = offset(poly.contour, (float)bed_bbox.radius() * 1.7f, jtRound, scale_(0.5))[0];
reset();
this->release_VBOs();
m_texture.reset();
m_model.reset();
// Set the origin and size for rendering the coordinate system axes.
m_axes.set_origin({ 0.0, 0.0, static_cast<double>(GROUND_Z) });
m_axes.set_stem_length(0.1f * static_cast<float>(m_bounding_box.max_size()));
m_axes.set_stem_length(0.1f * static_cast<float>(m_build_volume.bounding_volume().max_size()));
// Let the calee to update the UI.
return true;
@ -240,85 +225,6 @@ void Bed3D::render_for_picking(GLCanvas3D& canvas, bool bottom, float scale_fact
render_internal(canvas, bottom, scale_factor, false, false, true);
}
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
bool Bed3D::is_rectangle(const Pointfs& shape, Vec2d* min, Vec2d* max)
{
const Lines lines = Polygon::new_scale(shape).lines();
bool ret = lines.size() == 4 && lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3]) && lines[0].perpendicular_to(lines[1]);
if (ret) {
if (min != nullptr) {
*min = shape.front();
for (const Vec2d& pt : shape) {
min->x() = std::min(min->x(), pt.x());
min->y() = std::min(min->y(), pt.y());
}
}
if (max != nullptr) {
*max = shape.front();
for (const Vec2d& pt : shape) {
max->x() = std::max(max->x(), pt.x());
max->y() = std::max(max->y(), pt.y());
}
}
}
return ret;
}
bool Bed3D::is_circle(const Pointfs& shape, Vec2d* center, double* radius)
{
if (shape.size() < 3)
return false;
// Analyze the array of points.
// Do they reside on a circle ?
const Vec2d box_center = Geometry::circle_center_taubin_newton(shape);
std::vector<double> vertex_distances;
double avg_dist = 0.0;
for (const Vec2d& pt : shape) {
double distance = (pt - box_center).norm();
vertex_distances.push_back(distance);
avg_dist += distance;
}
avg_dist /= vertex_distances.size();
double tolerance = avg_dist * 0.01;
bool defined_value = true;
for (double el : vertex_distances) {
if (fabs(el - avg_dist) > tolerance)
defined_value = false;
break;
}
if (center != nullptr)
*center = box_center;
if (radius != nullptr)
*radius = avg_dist;
return defined_value;
}
bool Bed3D::is_convex(const Pointfs& shape)
{
return Polygon::new_scale(shape).convex_points().size() == shape.size();
}
Bed3D::EShapeType Bed3D::detect_shape_type(const Pointfs& shape)
{
if (shape.size() < 3)
return EShapeType::Invalid;
else if (is_rectangle(shape))
return EShapeType::Rectangle;
else if (is_circle(shape))
return EShapeType::Circle;
else
return EShapeType::Custom;
}
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
void Bed3D::render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
bool show_axes, bool show_texture, bool picking)
{
@ -334,41 +240,31 @@ void Bed3D::render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
switch (m_type)
{
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
case EType::System: { render_system(canvas, bottom, show_texture); break; }
case Type::System: { render_system(canvas, bottom, show_texture); break; }
default:
case EType::Custom: { render_custom(canvas, bottom, show_texture, picking); break; }
#else
case System: { render_system(canvas, bottom, show_texture); break; }
default:
case Custom: { render_custom(canvas, bottom, show_texture, picking); break; }
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
case Type::Custom: { render_custom(canvas, bottom, show_texture, picking); break; }
}
glsafe(::glDisable(GL_DEPTH_TEST));
}
void Bed3D::calc_bounding_boxes() const
// Calculate an extended bounding box from axes and current model for visualization purposes.
BoundingBoxf3 Bed3D::calc_extended_bounding_box() const
{
BoundingBoxf3* bounding_box = const_cast<BoundingBoxf3*>(&m_bounding_box);
*bounding_box = BoundingBoxf3();
for (const Vec2d& p : m_shape) {
bounding_box->merge({ p.x(), p.y(), 0.0 });
}
BoundingBoxf3* extended_bounding_box = const_cast<BoundingBoxf3*>(&m_extended_bounding_box);
*extended_bounding_box = m_bounding_box;
BoundingBoxf3 out { m_build_volume.bounding_volume() };
// Reset the build volume Z, we don't want to zoom to the top of the build volume if it is empty.
out.min.z() = 0;
out.max.z() = 0;
// extend to contain axes
extended_bounding_box->merge(m_axes.get_origin() + m_axes.get_total_length() * Vec3d::Ones());
extended_bounding_box->merge(extended_bounding_box->min + Vec3d(-Axes::DefaultTipRadius, -Axes::DefaultTipRadius, extended_bounding_box->max(2)));
out.merge(m_axes.get_origin() + m_axes.get_total_length() * Vec3d::Ones());
out.merge(out.min + Vec3d(-Axes::DefaultTipRadius, -Axes::DefaultTipRadius, out.max(2)));
// extend to contain model, if any
BoundingBoxf3 model_bb = m_model.get_bounding_box();
if (model_bb.defined) {
model_bb.translate(m_model_offset);
extended_bounding_box->merge(model_bb);
out.merge(model_bb);
}
return out;
}
void Bed3D::calc_triangles(const ExPolygon& poly)
@ -404,8 +300,9 @@ void Bed3D::calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox)
BOOST_LOG_TRIVIAL(error) << "Unable to create bed grid lines\n";
}
std::tuple<Bed3D::EType, std::string, std::string> Bed3D::detect_type(const Pointfs& shape) const
// Try to match the print bed shape with the shape of an active profile. If such a match exists,
// return the print bed model.
std::tuple<Bed3D::Type, std::string, std::string> Bed3D::detect_type(const Pointfs& shape)
{
auto bundle = wxGetApp().preset_bundle;
if (bundle != nullptr) {
@ -416,11 +313,7 @@ std::tuple<Bed3D::EType, std::string, std::string> Bed3D::detect_type(const Poin
std::string model_filename = PresetUtils::system_printer_bed_model(*curr);
std::string texture_filename = PresetUtils::system_printer_bed_texture(*curr);
if (!model_filename.empty() && !texture_filename.empty())
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
return { EType::System, model_filename, texture_filename };
#else
return { System, model_filename, texture_filename };
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
return { Type::System, model_filename, texture_filename };
}
}
@ -428,16 +321,12 @@ std::tuple<Bed3D::EType, std::string, std::string> Bed3D::detect_type(const Poin
}
}
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
return { EType::Custom, "", "" };
#else
return { Custom, "", "" };
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
return { Type::Custom, {}, {} };
}
void Bed3D::render_axes() const
{
if (!m_shape.empty())
if (m_build_volume.valid())
m_axes.render();
}
@ -596,12 +485,10 @@ void Bed3D::render_model() const
model->set_color(-1, DEFAULT_MODEL_COLOR);
// move the model so that its origin (0.0, 0.0, 0.0) goes into the bed shape center and a bit down to avoid z-fighting with the texture quad
Vec3d shift = m_bounding_box.center();
shift(2) = -0.03;
*const_cast<Vec3d*>(&m_model_offset) = shift;
*const_cast<Vec3d*>(&m_model_offset) = to_3d(m_build_volume.bounding_volume2d().center(), -0.03);
// update extended bounding box
calc_bounding_boxes();
const_cast<BoundingBoxf3&>(m_extended_bounding_box) = this->calc_extended_bounding_box();
}
if (!model->get_filename().empty()) {
@ -673,7 +560,7 @@ void Bed3D::render_default(bool bottom, bool picking) const
}
}
void Bed3D::reset()
void Bed3D::release_VBOs()
{
if (m_vbo_id > 0) {
glsafe(::glDeleteBuffers(1, &m_vbo_id));

View file

@ -5,6 +5,8 @@
#include "3DScene.hpp"
#include "GLModel.hpp"
#include <libslic3r/BuildVolume.hpp>
#include <tuple>
#include <array>
@ -62,41 +64,22 @@ class Bed3D
};
public:
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
enum class EType : unsigned char
enum class Type : unsigned char
{
// The print bed model and texture are available from some printer preset.
System,
// The print bed model is unknown, thus it is rendered procedurally.
Custom
};
enum class EShapeType : unsigned char
{
Rectangle,
Circle,
Custom,
Invalid
};
#else
enum EType : unsigned char
{
System,
Custom,
Num_Types
};
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
private:
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
EType m_type{ EType::Custom };
EShapeType m_shape_type{ EShapeType::Invalid };
#else
EType m_type{ Custom };
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
Pointfs m_shape;
BuildVolume m_build_volume;
Type m_type{ Type::Custom };
std::string m_texture_filename;
std::string m_model_filename;
BoundingBoxf3 m_bounding_box;
// Print volume bounding box exteded with axes and model.
BoundingBoxf3 m_extended_bounding_box;
// Slightly expanded print bed polygon, for collision detection.
Polygon m_polygon;
GeometryBuffer m_triangles;
GeometryBuffer m_gridlines;
@ -112,42 +95,39 @@ private:
public:
Bed3D() = default;
~Bed3D() { reset(); }
~Bed3D() { release_VBOs(); }
EType get_type() const { return m_type; }
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
EShapeType get_shape_type() const { return m_shape_type; }
bool is_custom() const { return m_type == EType::Custom; }
#else
bool is_custom() const { return m_type == Custom; }
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
const Pointfs& get_shape() const { return m_shape; }
// Update print bed model from configuration.
// Return true if the bed shape changed, so the calee will update the UI.
bool set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false);
//FIXME if the build volume max print height is updated, this function still returns zero
// as this class does not use it, thus there is no need to update the UI.
bool set_shape(const Pointfs& bed_shape, const double max_print_height, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false);
const BoundingBoxf3& get_bounding_box(bool extended) const { return extended ? m_extended_bounding_box : m_bounding_box; }
// Build volume geometry for various collision detection tasks.
const BuildVolume& build_volume() const { return m_build_volume; }
// Was the model provided, or was it generated procedurally?
Type get_type() const { return m_type; }
// Was the model generated procedurally?
bool is_custom() const { return m_type == Type::Custom; }
// Bounding box around the print bed, axes and model, for rendering.
const BoundingBoxf3& extended_bounding_box() const { return m_extended_bounding_box; }
// Check against an expanded 2d bounding box.
//FIXME shall one check against the real build volume?
bool contains(const Point& point) const;
Point point_projection(const Point& point) const;
void render(GLCanvas3D& canvas, bool bottom, float scale_factor,
bool show_axes, bool show_texture);
void render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes, bool show_texture);
void render_for_picking(GLCanvas3D& canvas, bool bottom, float scale_factor);
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
static bool is_rectangle(const Pointfs& shape, Vec2d* min = nullptr, Vec2d* max = nullptr);
static bool is_circle(const Pointfs& shape, Vec2d* center = nullptr, double* radius = nullptr);
static bool is_convex(const Pointfs& shape);
static EShapeType detect_shape_type(const Pointfs& shape);
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
private:
void calc_bounding_boxes() const;
// Calculate an extended bounding box from axes and current model for visualization purposes.
BoundingBoxf3 calc_extended_bounding_box() const;
void calc_triangles(const ExPolygon& poly);
void calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox);
std::tuple<EType, std::string, std::string> detect_type(const Pointfs& shape) const;
static std::tuple<Type, std::string, std::string> detect_type(const Pointfs& shape);
void render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
bool show_axes, bool show_texture, bool picking);
void render_axes() const;
@ -156,7 +136,7 @@ private:
void render_model() const;
void render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture, bool picking) const;
void render_default(bool bottom, bool picking) const;
void reset();
void release_VBOs();
};
} // GUI

View file

@ -11,10 +11,8 @@
#include "GUI_App.hpp"
#include "Plater.hpp"
#include "BitmapCache.hpp"
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
#include "3DBed.hpp"
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
#include "libslic3r/BuildVolume.hpp"
#include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/ExtrusionEntityCollection.hpp"
#include "libslic3r/Geometry.hpp"
@ -617,22 +615,6 @@ void GLVolume::render_sinking_contours()
m_sinking_contours.render();
}
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
void GLVolume::calc_convex_hull_3d()
{
const std::vector<float> &src = this->indexed_vertex_array.vertices_and_normals_interleaved;
std::vector<Vec3f> pts;
assert(src.size() % 6 == 0);
pts.reserve(src.size() / 6);
for (auto it = src.begin(); it != src.end(); ) {
it += 3;
pts.push_back({ *it, *(it + 1), *(it + 2) });
it += 3;
}
this->set_convex_hull(TriangleMesh(its_convex_hull(pts)));
}
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
std::vector<int> GLVolumeCollection::load_object(
const ModelObject *model_object,
int obj_idx,
@ -959,136 +941,51 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
glsafe(::glDisable(GL_BLEND));
}
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state, bool as_toolpaths) const
#else
bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state) const
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
bool GLVolumeCollection::check_outside_state(const BuildVolume &build_volume, ModelInstanceEPrintVolumeState *out_state) const
{
if (config == nullptr)
return false;
const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(config->option("bed_shape"));
if (opt == nullptr)
return false;
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
const Polygon bed_poly = offset(Polygon::new_scale(opt->values), static_cast<float>(scale_(BedEpsilon))).front();
const float bed_height = config->opt_float("max_print_height");
const BoundingBox bed_box_2D = get_extents(bed_poly);
BoundingBoxf3 print_volume({ unscale<double>(bed_box_2D.min.x()), unscale<double>(bed_box_2D.min.y()), -1e10 },
{ unscale<double>(bed_box_2D.max.x()), unscale<double>(bed_box_2D.max.y()), bed_height });
auto check_against_rectangular_bed = [&print_volume](GLVolume& volume, ModelInstanceEPrintVolumeState& state) {
const BoundingBoxf3* const bb = (volume.is_sinking() && volume.object_idx() != -1 && volume.volume_idx() != -1) ? &volume.transformed_non_sinking_bounding_box() : &volume.transformed_convex_hull_bounding_box();
volume.is_outside = !print_volume.contains(*bb);
if (volume.printable) {
if (state == ModelInstancePVS_Inside && volume.is_outside)
state = ModelInstancePVS_Fully_Outside;
if (state == ModelInstancePVS_Fully_Outside && volume.is_outside && print_volume.intersects(*bb))
state = ModelInstancePVS_Partly_Outside;
}
};
auto check_against_circular_bed = [bed_height](GLVolume& volume, ModelInstanceEPrintVolumeState& state, const Vec2d& center, double radius) {
const TriangleMesh* mesh = (volume.is_sinking() && volume.object_idx() != -1 && volume.volume_idx() != -1) ? &GUI::wxGetApp().plater()->model().objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : volume.convex_hull();
const double sq_radius = sqr(radius);
size_t outside_count = 0;
size_t valid_count = 0;
for (const Vec3f& v : mesh->its.vertices) {
const Vec3f world_v = volume.world_matrix().cast<float>() * v;
if (0.0f <= world_v.z()) {
++valid_count;
if (sq_radius < sqr(world_v.x() - center.x()) + sqr(world_v.y() - center.y()) || bed_height < world_v.z())
++outside_count;
}
}
volume.is_outside = outside_count > 0;
if (volume.printable) {
if (state == ModelInstancePVS_Inside && volume.is_outside)
state = ModelInstancePVS_Fully_Outside;
if (state == ModelInstancePVS_Fully_Outside && volume.is_outside && outside_count < valid_count)
state = ModelInstancePVS_Partly_Outside;
}
};
auto check_against_convex_bed = [&bed_poly, bed_height](GLVolume& volume, ModelInstanceEPrintVolumeState& state) {
const TriangleMesh* mesh = (volume.is_sinking() && volume.object_idx() != -1 && volume.volume_idx() != -1) ? &GUI::wxGetApp().plater()->model().objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : volume.convex_hull();
const Polygon volume_hull_2d = its_convex_hull_2d_above(mesh->its, volume.world_matrix().cast<float>(), 0.0f);
const BoundingBoxf3* const bb = (volume.is_sinking() && volume.object_idx() != -1 && volume.volume_idx() != -1) ? &volume.transformed_non_sinking_bounding_box() : &volume.transformed_convex_hull_bounding_box();
// Using rotating callipers to check for collision of two convex polygons.
ModelInstanceEPrintVolumeState volume_state = printbed_collision_state(bed_poly, bed_height, volume_hull_2d, bb->min.z(), bb->max.z());
bool contained = (volume_state == ModelInstancePVS_Inside);
bool intersects = (volume_state == ModelInstancePVS_Partly_Outside);
volume.is_outside = !contained;
if (volume.printable) {
if (state == ModelInstancePVS_Inside && volume.is_outside)
state = ModelInstancePVS_Fully_Outside;
if (state == ModelInstancePVS_Fully_Outside && volume.is_outside && intersects)
state = ModelInstancePVS_Partly_Outside;
}
};
#else
const BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
BoundingBoxf3 print_volume({ unscale<double>(bed_box_2D.min.x()), unscale<double>(bed_box_2D.min.y()), 0.0 },
{ unscale<double>(bed_box_2D.max.x()), unscale<double>(bed_box_2D.max.y()), config->opt_float("max_print_height") });
// Allow the objects to protrude below the print bed
print_volume.min.z() = -1e10;
print_volume.min.x() -= BedEpsilon;
print_volume.min.y() -= BedEpsilon;
print_volume.max.x() += BedEpsilon;
print_volume.max.y() += BedEpsilon;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
const Model& model = GUI::wxGetApp().plater()->model();
// Volume is partially below the print bed, thus a pre-calculated convex hull cannot be used.
auto volume_sinking = [](GLVolume& volume) -> bool
{ return volume.is_sinking() && volume.object_idx() != -1 && volume.volume_idx() != -1; };
// Cached bounding box of a volume above the print bed.
auto volume_bbox = [volume_sinking](GLVolume& volume) -> BoundingBoxf3
{ return volume_sinking(volume) ? volume.transformed_non_sinking_bounding_box() : volume.transformed_convex_hull_bounding_box(); };
// Cached 3D convex hull of a volume above the print bed.
auto volume_convex_mesh = [volume_sinking, &model](GLVolume& volume) -> const TriangleMesh&
{ return volume_sinking(volume) ? model.objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : *volume.convex_hull(); };
ModelInstanceEPrintVolumeState overall_state = ModelInstancePVS_Inside;
bool contained_min_one = false;
enum class BedShape { Rectangle, Circle, Convex, NonConvex };
Vec2d center;
double radius;
BedShape bed_shape =
GUI::Bed3D::is_rectangle(opt->values) ? BedShape::Rectangle :
GUI::Bed3D::is_circle(opt->values, &center, &radius) ? BedShape::Circle :
GUI::Bed3D::is_convex(opt->values) ? BedShape::Convex : BedShape::NonConvex;
for (GLVolume* volume : this->volumes) {
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
if (as_toolpaths && !volume->is_extrusion_path)
continue;
else if (!as_toolpaths && (volume->is_modifier || (!volume->shader_outside_printer_detection_enabled && (volume->is_wipe_tower || volume->composite_id.volume_id < 0))))
continue;
switch (bed_shape) {
case BedShape::Rectangle: check_against_rectangular_bed(*volume, overall_state); break;
case BedShape::Circle: check_against_circular_bed(*volume, overall_state, center, radius); break;
case BedShape::Convex: check_against_convex_bed(*volume, overall_state); break;
default: break;
for (GLVolume* volume : this->volumes)
if (! volume->is_modifier && (volume->shader_outside_printer_detection_enabled || (! volume->is_wipe_tower && volume->composite_id.volume_id >= 0))) {
BuildVolume::ObjectState state;
switch (build_volume.type()) {
case BuildVolume::Type::Rectangle:
//FIXME this test does not evaluate collision of a build volume bounding box with non-convex objects.
state = build_volume.volume_state_bbox(volume_bbox(*volume));
break;
case BuildVolume::Type::Circle:
case BuildVolume::Type::Convex:
//FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently.
case BuildVolume::Type::Custom:
state = build_volume.object_state(volume_convex_mesh(*volume).its, volume->world_matrix().cast<float>(), volume_sinking(*volume));
break;
default:
// Ignore, don't produce any collision.
state = BuildVolume::ObjectState::Inside;
break;
}
volume->is_outside = state != BuildVolume::ObjectState::Inside;
if (volume->printable) {
if (overall_state == ModelInstancePVS_Inside && volume->is_outside)
overall_state = ModelInstancePVS_Fully_Outside;
if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && state == BuildVolume::ObjectState::Colliding)
overall_state = ModelInstancePVS_Partly_Outside;
contained_min_one |= !volume->is_outside;
}
}
contained_min_one |= !volume->is_outside;
#else
if (volume->is_modifier || (!volume->shader_outside_printer_detection_enabled && (volume->is_wipe_tower || volume->composite_id.volume_id < 0)))
continue;
const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box();
bool contained = print_volume.contains(bb);
volume->is_outside = !contained;
if (!volume->printable)
continue;
contained_min_one |= contained;
if (overall_state == ModelInstancePVS_Inside && volume->is_outside)
overall_state = ModelInstancePVS_Fully_Outside;
if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && print_volume.intersects(bb))
overall_state = ModelInstancePVS_Partly_Outside;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
}
if (out_state != nullptr)
*out_state = overall_state;

View file

@ -31,6 +31,7 @@
namespace Slic3r {
class SLAPrintObject;
enum SLAPrintObjectStep : unsigned int;
class BuildVolume;
class DynamicPrintConfig;
class ExtrusionPath;
class ExtrusionMultiPath;
@ -281,10 +282,8 @@ private:
std::shared_ptr<const TriangleMesh> m_convex_hull;
// Bounding box of this volume, in unscaled coordinates.
std::optional<BoundingBoxf3> m_transformed_convex_hull_bounding_box;
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
// Bounding box of the non sinking part of this volume, in unscaled coordinates.
std::optional<BoundingBoxf3> m_transformed_non_sinking_bounding_box;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
class SinkingContours
{
@ -475,12 +474,10 @@ public:
BoundingBoxf3 transformed_convex_hull_bounding_box(const Transform3d &trafo) const;
// caching variant
const BoundingBoxf3& transformed_convex_hull_bounding_box() const;
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
// non-caching variant
BoundingBoxf3 transformed_non_sinking_bounding_box(const Transform3d& trafo) const;
// caching variant
const BoundingBoxf3& transformed_non_sinking_bounding_box() const;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
// convex hull
const TriangleMesh* convex_hull() const { return m_convex_hull.get(); }
@ -493,15 +490,11 @@ public:
void finalize_geometry(bool opengl_initialized) { this->indexed_vertex_array.finalize_geometry(opengl_initialized); }
void release_geometry() { this->indexed_vertex_array.release_geometry(); }
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
void set_bounding_boxes_as_dirty() {
m_transformed_bounding_box.reset();
m_transformed_convex_hull_bounding_box.reset();
m_transformed_non_sinking_bounding_box.reset();
}
#else
void set_bounding_boxes_as_dirty() { m_transformed_bounding_box.reset(); m_transformed_convex_hull_bounding_box.reset(); }
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
bool is_sla_support() const;
bool is_sla_pad() const;
@ -518,12 +511,6 @@ public:
// Return an estimate of the memory held by GPU vertex buffers.
size_t gpu_memory_used() const { return this->indexed_vertex_array.gpu_memory_used(); }
size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); }
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
// calculates the 3D convex hull from indexed_vertex_array.vertices_and_normals_interleaved
// must be called before calling indexed_vertex_array.finalize_geometry();
void calc_convex_hull_3d();
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
};
typedef std::vector<GLVolume*> GLVolumePtrs;
@ -540,7 +527,6 @@ public:
All
};
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
struct PrintVolume
{
// see: Bed3D::EShapeType
@ -554,16 +540,9 @@ public:
// [0] = min z, [1] = max z
std::array<float, 2> zs;
};
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
private:
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
PrintVolume m_print_volume;
#else
// min and max vertex of the print box volume
float m_print_box_min[3];
float m_print_box_max[3];
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
// z range for clipping in shaders
float m_z_range[2];
@ -635,14 +614,7 @@ public:
bool empty() const { return volumes.empty(); }
void set_range(double low, double high) { for (GLVolume *vol : this->volumes) vol->set_range(low, high); }
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
void set_print_volume(const PrintVolume& print_volume) { m_print_volume = print_volume; }
#else
void set_print_box(float min_x, float min_y, float min_z, float max_x, float max_y, float max_z) {
m_print_box_min[0] = min_x; m_print_box_min[1] = min_y; m_print_box_min[2] = min_z;
m_print_box_max[0] = max_x; m_print_box_max[1] = max_y; m_print_box_max[2] = max_z;
}
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
void set_z_range(float min_z, float max_z) { m_z_range[0] = min_z; m_z_range[1] = max_z; }
void set_clipping_plane(const double* coeffs) { m_clipping_plane[0] = coeffs[0]; m_clipping_plane[1] = coeffs[1]; m_clipping_plane[2] = coeffs[2]; m_clipping_plane[3] = coeffs[3]; }
@ -657,11 +629,7 @@ public:
// returns true if all the volumes are completely contained in the print volume
// returns the containment state in the given out_state, if non-null
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
bool check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state, bool as_toolpaths = false) const;
#else
bool check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state) const;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
bool check_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state) const;
void reset_outside_state();
void update_colors_by_extruder(const DynamicPrintConfig* config);
@ -699,8 +667,6 @@ struct _3DScene
static void point3_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume);
};
static constexpr float BedEpsilon = 3.f * float(EPSILON);
}
#endif

View file

@ -86,7 +86,7 @@ public:
void set_fff_print(Print *print) { m_fff_print = print; }
void set_sla_print(SLAPrint *print) { m_sla_print = print; m_sla_print->set_printer(&m_sla_archive); }
void set_thumbnail_cb(ThumbnailsGeneratorCallback cb) { m_thumbnail_cb = cb; }
void set_gcode_result(GCodeProcessor::Result* result) { m_gcode_result = result; }
void set_gcode_result(GCodeProcessorResult* result) { m_gcode_result = result; }
// The following wxCommandEvent will be sent to the UI thread / Plater window, when the slicing is finished
// and the background processing will transition into G-code export.
@ -216,7 +216,7 @@ private:
Print *m_fff_print = nullptr;
SLAPrint *m_sla_print = nullptr;
// Data structure, to which the G-code export writes its annotations.
GCodeProcessor::Result *m_gcode_result = nullptr;
GCodeProcessorResult *m_gcode_result = nullptr;
// Callback function, used to write thumbnails into gcode.
ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr;
SL1Archive m_sla_archive;

View file

@ -22,98 +22,7 @@ namespace GUI {
BedShape::BedShape(const ConfigOptionPoints& points)
{
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
if (points.size() < 3) {
m_type = Bed3D::EShapeType::Invalid;
return;
}
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
// is this a rectangle ?
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
Vec2d min;
Vec2d max;
if (Bed3D::is_rectangle(points.values, &min, &max)) {
m_type = Bed3D::EShapeType::Rectangle;
m_rectSize = max - min;
m_rectOrigin = -min;
return;
}
#else
Polygon polygon = Polygon::new_scale(points.values);
if (points.size() == 4) {
auto lines = polygon.lines();
if (lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3])) {
// okay, it's a rectangle
// find origin
coordf_t x_min, x_max, y_min, y_max;
x_max = x_min = points.values[0](0);
y_max = y_min = points.values[0](1);
for (auto pt : points.values)
{
x_min = std::min(x_min, pt(0));
x_max = std::max(x_max, pt(0));
y_min = std::min(y_min, pt(1));
y_max = std::max(y_max, pt(1));
}
m_type = Type::Rectangular;
m_rectSize = Vec2d(x_max - x_min, y_max - y_min);
m_rectOrigin = Vec2d(-x_min, -y_min);
return;
}
}
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
// is this a circle ?
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
Vec2d center;
double radius;
if (Bed3D::is_circle(points.values, &center, &radius)) {
m_type = Bed3D::EShapeType::Circle;
m_diameter = 2.0 * radius;
return;
}
// This is a custom bed shape, use the polygon provided.
m_type = Bed3D::EShapeType::Custom;
#else
{
// Analyze the array of points.Do they reside on a circle ?
auto center = polygon.bounding_box().center();
std::vector<double> vertex_distances;
double avg_dist = 0;
for (auto pt : polygon.points)
{
double distance = (pt - center).cast<double>().norm();
vertex_distances.push_back(distance);
avg_dist += distance;
}
avg_dist /= vertex_distances.size();
bool defined_value = true;
for (auto el : vertex_distances)
{
if (abs(el - avg_dist) > 10 * SCALED_EPSILON)
defined_value = false;
break;
}
if (defined_value) {
// all vertices are equidistant to center
m_type = Type::Circular;
m_diameter = unscale<double>(avg_dist * 2);
return;
}
}
if (points.size() < 3)
return;
// This is a custom bed shape, use the polygon provided.
m_type = Type::Custom;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
m_build_volume = { points.values, 0. };
}
static std::string get_option_label(BedShape::Parameter param)
@ -122,119 +31,101 @@ static std::string get_option_label(BedShape::Parameter param)
case BedShape::Parameter::RectSize : return L("Size");
case BedShape::Parameter::RectOrigin: return L("Origin");
case BedShape::Parameter::Diameter : return L("Diameter");
default: return "";
default: assert(false); return {};
}
}
void BedShape::append_option_line(ConfigOptionsGroupShp optgroup, Parameter param)
{
ConfigOptionDef def;
if (param == Parameter::RectSize) {
t_config_option_key key;
switch (param) {
case Parameter::RectSize:
def.type = coPoints;
def.set_default_value(new ConfigOptionPoints{ Vec2d(200, 200) });
def.min = 0;
def.max = 1200;
def.label = get_option_label(param);
def.tooltip = L("Size in X and Y of the rectangular plate.");
Option option(def, "rect_size");
optgroup->append_single_option_line(option);
}
else if (param == Parameter::RectOrigin) {
key = "rect_size";
break;
case Parameter::RectOrigin:
def.type = coPoints;
def.set_default_value(new ConfigOptionPoints{ Vec2d(0, 0) });
def.min = -600;
def.max = 600;
def.label = get_option_label(param);
def.tooltip = L("Distance of the 0,0 G-code coordinate from the front left corner of the rectangle.");
Option option(def, "rect_origin");
optgroup->append_single_option_line(option);
}
else if (param == Parameter::Diameter) {
key = "rect_origin";
break;
case Parameter::Diameter:
def.type = coFloat;
def.set_default_value(new ConfigOptionFloat(200));
def.sidetext = L("mm");
def.label = get_option_label(param);
def.tooltip = L("Diameter of the print bed. It is assumed that origin (0,0) is located in the center.");
Option option(def, "diameter");
optgroup->append_single_option_line(option);
key = "diameter";
break;
default:
assert(false);
}
optgroup->append_single_option_line({ def, std::move(key) });
}
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
wxString BedShape::get_name(Bed3D::EShapeType type)
wxString BedShape::get_name(PageType type)
{
switch (type) {
case Bed3D::EShapeType::Rectangle: { return _L("Rectangular"); }
case Bed3D::EShapeType::Circle: { return _L("Circular"); }
case Bed3D::EShapeType::Custom: { return _L("Custom"); }
case Bed3D::EShapeType::Invalid:
default: return _L("Invalid");
case PageType::Rectangle: return _L("Rectangular");
case PageType::Circle: return _L("Circular");
case PageType::Custom: return _L("Custom");
}
// make visual studio happy
assert(false);
return {};
}
#else
wxString BedShape::get_name(Type type)
{
switch (type) {
case Type::Rectangular: return _L("Rectangular");
case Type::Circular: return _L("Circular");
case Type::Custom: return _L("Custom");
case Type::Invalid:
default: return _L("Invalid");
}
}
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
size_t BedShape::get_type()
BedShape::PageType BedShape::get_page_type()
{
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
return static_cast<size_t>(m_type == Bed3D::EShapeType::Invalid ? Bed3D::EShapeType::Rectangle : m_type);
#else
return static_cast<size_t>(m_type == Type::Invalid ? Type::Rectangular : m_type);
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
switch (m_build_volume.type()) {
case BuildVolume::Type::Rectangle:
case BuildVolume::Type::Invalid: return PageType::Rectangle;
case BuildVolume::Type::Circle: return PageType::Circle;
case BuildVolume::Type::Convex:
case BuildVolume::Type::Custom: return PageType::Custom;
}
// make visual studio happy
assert(false);
return PageType::Rectangle;
}
wxString BedShape::get_full_name_with_params()
{
wxString out = _L("Shape") + ": " + get_name(m_type);
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
if (m_type == Bed3D::EShapeType::Rectangle) {
#else
if (m_type == Type::Rectangular) {
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
out += "\n" + _(get_option_label(Parameter::RectSize)) + ": [" + ConfigOptionPoint(m_rectSize).serialize() + "]";
out += "\n" + _(get_option_label(Parameter::RectOrigin))+ ": [" + ConfigOptionPoint(m_rectOrigin).serialize() + "]";
wxString out = _L("Shape") + ": " + get_name(this->get_page_type());
switch (m_build_volume.type()) {
case BuildVolume::Type::Circle:
out += "\n" + _L(get_option_label(Parameter::Diameter)) + ": [" + double_to_string(2. * unscaled<double>(m_build_volume.circle().radius)) + "]";
break;
default:
// rectangle, convex, concave...
out += "\n" + _(get_option_label(Parameter::RectSize)) + ": [" + ConfigOptionPoint(to_2d(m_build_volume.bounding_volume().size())).serialize() + "]";
out += "\n" + _(get_option_label(Parameter::RectOrigin)) + ": [" + ConfigOptionPoint(to_2d(m_build_volume.bounding_volume().min)).serialize() + "]";
break;
}
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
else if (m_type == Bed3D::EShapeType::Circle)
#else
else if (m_type == Type::Circular)
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
out += "\n" + _L(get_option_label(Parameter::Diameter)) + ": [" + double_to_string(m_diameter) + "]";
return out;
}
void BedShape::apply_optgroup_values(ConfigOptionsGroupShp optgroup)
{
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
if (m_type == Bed3D::EShapeType::Rectangle || m_type == Bed3D::EShapeType::Invalid) {
#else
if (m_type == Type::Rectangular || m_type == Type::Invalid) {
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
optgroup->set_value("rect_size" , new ConfigOptionPoints{ m_rectSize });
optgroup->set_value("rect_origin" , new ConfigOptionPoints{ m_rectOrigin });
switch (m_build_volume.type()) {
case BuildVolume::Type::Circle:
optgroup->set_value("diameter", double_to_string(2. * unscaled<double>(m_build_volume.circle().radius)));
break;
default:
// rectangle, convex, concave...
optgroup->set_value("rect_size" , new ConfigOptionPoints{ to_2d(m_build_volume.bounding_volume().size()) });
optgroup->set_value("rect_origin" , new ConfigOptionPoints{ to_2d(m_build_volume.bounding_volume().min) });
}
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
else if (m_type == Bed3D::EShapeType::Circle)
#else
else if (m_type == Type::Circular)
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
optgroup->set_value("diameter", double_to_string(m_diameter));
}
void BedShapeDialog::build_dialog(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model)
@ -295,28 +186,16 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf
sbsizer->Add(m_shape_options_book);
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
auto optgroup = init_shape_options_page(BedShape::get_name(Bed3D::EShapeType::Rectangle));
#else
auto optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Rectangular));
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
auto optgroup = init_shape_options_page(BedShape::get_name(BedShape::PageType::Rectangle));
BedShape::append_option_line(optgroup, BedShape::Parameter::RectSize);
BedShape::append_option_line(optgroup, BedShape::Parameter::RectOrigin);
activate_options_page(optgroup);
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
optgroup = init_shape_options_page(BedShape::get_name(Bed3D::EShapeType::Circle));
#else
optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Circular));
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
optgroup = init_shape_options_page(BedShape::get_name(BedShape::PageType::Circle));
BedShape::append_option_line(optgroup, BedShape::Parameter::Diameter);
activate_options_page(optgroup);
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
optgroup = init_shape_options_page(BedShape::get_name(Bed3D::EShapeType::Custom));
#else
optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Custom));
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
optgroup = init_shape_options_page(BedShape::get_name(BedShape::PageType::Custom));
Line line{ "", "" };
line.full_width = 1;
@ -538,8 +417,8 @@ void BedShapePanel::set_shape(const ConfigOptionPoints& points)
{
BedShape shape(points);
m_shape_options_book->SetSelection(shape.get_type());
shape.apply_optgroup_values(m_optgroups[shape.get_type()]);
m_shape_options_book->SetSelection(int(shape.get_page_type()));
shape.apply_optgroup_values(m_optgroups[int(shape.get_page_type())]);
// Copy the polygon to the canvas, make a copy of the array, if custom shape is selected
if (shape.is_custom())
@ -562,17 +441,9 @@ void BedShapePanel::update_shape()
auto page_idx = m_shape_options_book->GetSelection();
auto opt_group = m_optgroups[page_idx];
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
Bed3D::EShapeType page_type = static_cast<Bed3D::EShapeType>(page_idx);
#else
BedShape::Type page_type = static_cast<BedShape::Type>(page_idx);
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
if (page_type == Bed3D::EShapeType::Rectangle) {
#else
if (page_type == BedShape::Type::Rectangular) {
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
switch (static_cast<BedShape::PageType>(page_idx)) {
case BedShape::PageType::Rectangle:
{
Vec2d rect_size(Vec2d::Zero());
Vec2d rect_origin(Vec2d::Zero());
@ -602,12 +473,10 @@ void BedShapePanel::update_shape()
Vec2d(x1, y0),
Vec2d(x1, y1),
Vec2d(x0, y1) };
break;
}
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
else if (page_type == Bed3D::EShapeType::Circle) {
#else
else if (page_type == BedShape::Type::Circular) {
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
case BedShape::PageType::Circle:
{
double diameter;
try { diameter = boost::any_cast<double>(opt_group->get_value("diameter")); }
catch (const std::exception & /* e */) { return; }
@ -615,6 +484,7 @@ void BedShapePanel::update_shape()
if (diameter == 0.0) return ;
auto r = diameter / 2;
auto twopi = 2 * PI;
// Don't change this value without adjusting BuildVolume constructor detecting circle diameter!
auto edges = 72;
std::vector<Vec2d> points;
for (int i = 1; i <= edges; ++i) {
@ -622,13 +492,12 @@ void BedShapePanel::update_shape()
points.push_back(Vec2d(r*cos(angle), r*sin(angle)));
}
m_shape = points;
break;
}
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
else if (page_type == Bed3D::EShapeType::Custom)
#else
else if (page_type == BedShape::Type::Custom)
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
case BedShape::PageType::Custom:
m_shape = m_loaded_shape;
break;
}
update_preview();
}

View file

@ -5,11 +5,10 @@
#include "GUI_Utils.hpp"
#include "2DBed.hpp"
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
#include "3DBed.hpp"
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
#include "I18N.hpp"
#include <libslic3r/BuildVolume.hpp>
#include <wx/dialog.h>
#include <wx/choicebk.h>
@ -22,14 +21,11 @@ using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>;
struct BedShape
{
#if !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
enum class Type {
Rectangular = 0,
Circular,
Custom,
Invalid
enum class PageType {
Rectangle,
Circle,
Custom
};
#endif // !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
enum class Parameter {
RectSize,
@ -39,34 +35,18 @@ struct BedShape
BedShape(const ConfigOptionPoints& points);
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
bool is_custom() { return m_type == Bed3D::EShapeType::Custom; }
#else
bool is_custom() { return m_type == Type::Custom; }
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
bool is_custom() { return m_build_volume.type() == BuildVolume::Type::Convex || m_build_volume.type() == BuildVolume::Type::Custom; }
static void append_option_line(ConfigOptionsGroupShp optgroup, Parameter param);
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
static wxString get_name(Bed3D::EShapeType type);
#else
static wxString get_name(Type type);
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
static wxString get_name(PageType type);
// convert Type to size_t
size_t get_type();
PageType get_page_type();
wxString get_full_name_with_params();
void apply_optgroup_values(ConfigOptionsGroupShp optgroup);
private:
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
Bed3D::EShapeType m_type{ Bed3D::EShapeType::Invalid };
#else
Type m_type {Type::Invalid};
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
Vec2d m_rectSize {200, 200};
Vec2d m_rectOrigin {0, 0};
double m_diameter {0};
BuildVolume m_build_volume;
};
class BedShapePanel : public wxPanel

View file

@ -1,6 +1,7 @@
#include "libslic3r/libslic3r.h"
#include "GCodeViewer.hpp"
#include "libslic3r/BuildVolume.hpp"
#include "libslic3r/Print.hpp"
#include "libslic3r/Geometry.hpp"
#include "libslic3r/Model.hpp"
@ -20,9 +21,6 @@
#include "GLToolbar.hpp"
#include "GUI_Preview.hpp"
#include "GUI_ObjectManipulation.hpp"
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
#include "3DBed.hpp"
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
#include <imgui/imgui_internal.h>
@ -123,7 +121,7 @@ void GCodeViewer::IBuffer::reset()
count = 0;
}
bool GCodeViewer::Path::matches(const GCodeProcessor::MoveVertex& move) const
bool GCodeViewer::Path::matches(const GCodeProcessorResult::MoveVertex& move) const
{
auto matches_percent = [](float value1, float value2, float max_percent) {
return std::abs(value2 - value1) / value1 <= max_percent;
@ -174,7 +172,7 @@ void GCodeViewer::TBuffer::reset()
#endif // ENABLE_SEAMS_USING_MODELS
}
void GCodeViewer::TBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id)
void GCodeViewer::TBuffer::add_path(const GCodeProcessorResult::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id)
{
Path::Endpoint endpoint = { b_id, i_id, s_id, move.position };
// use rounding to reduce the number of generated paths
@ -665,7 +663,7 @@ void GCodeViewer::init()
}
#endif // ENABLE_SEAMS_USING_MODELS
void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized)
void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& print, bool initialized)
{
// avoid processing if called with the same gcode_result
if (m_last_result_id == gcode_result.id)
@ -737,7 +735,7 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print&
{ min.x(), max.y() } };
}
wxGetApp().plater()->set_bed_shape(bed_shape, texture, model, gcode_result.bed_shape.empty());
wxGetApp().plater()->set_bed_shape(bed_shape, gcode_result.max_print_height, texture, model, gcode_result.bed_shape.empty());
}
m_print_statistics = gcode_result.print_statistics;
@ -750,7 +748,7 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print&
}
}
void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors)
void GCodeViewer::refresh(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors)
{
#if ENABLE_GCODE_VIEWER_STATISTICS
auto start_time = std::chrono::high_resolution_clock::now();
@ -779,7 +777,7 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std:
if (i == 0)
continue;
const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i];
const GCodeProcessorResult::MoveVertex& curr = gcode_result.moves[i];
switch (curr.type)
{
@ -1210,7 +1208,7 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
fclose(fp);
}
void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
{
// max index buffer size, in bytes
static const size_t IBUFFER_THRESHOLD_BYTES = 64 * 1024 * 1024;
@ -1232,23 +1230,23 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
};
// format data into the buffers to be rendered as points
auto add_vertices_as_point = [](const GCodeProcessor::MoveVertex& curr, VertexBuffer& vertices) {
auto add_vertices_as_point = [](const GCodeProcessorResult::MoveVertex& curr, VertexBuffer& vertices) {
vertices.push_back(curr.position.x());
vertices.push_back(curr.position.y());
vertices.push_back(curr.position.z());
};
auto add_indices_as_point = [](const GCodeProcessor::MoveVertex& curr, TBuffer& buffer,
auto add_indices_as_point = [](const GCodeProcessorResult::MoveVertex& curr, TBuffer& buffer,
unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) {
buffer.add_path(curr, ibuffer_id, indices.size(), move_id);
indices.push_back(static_cast<IBufferType>(indices.size()));
};
// format data into the buffers to be rendered as lines
auto add_vertices_as_line = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, VertexBuffer& vertices) {
auto add_vertices_as_line = [](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, VertexBuffer& vertices) {
// x component of the normal to the current segment (the normal is parallel to the XY plane)
const float normal_x = (curr.position - prev.position).normalized().y();
auto add_vertex = [&vertices, normal_x](const GCodeProcessor::MoveVertex& vertex) {
auto add_vertex = [&vertices, normal_x](const GCodeProcessorResult::MoveVertex& vertex) {
// add position
vertices.push_back(vertex.position.x());
vertices.push_back(vertex.position.y());
@ -1262,7 +1260,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
// add current vertex
add_vertex(curr);
};
auto add_indices_as_line = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer,
auto add_indices_as_line = [](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, TBuffer& buffer,
unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) {
if (buffer.paths.empty() || prev.type != curr.type || !buffer.paths.back().matches(curr)) {
// add starting index
@ -1283,7 +1281,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
};
// format data into the buffers to be rendered as solid
auto add_vertices_as_solid = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer, unsigned int vbuffer_id, VertexBuffer& vertices, size_t move_id) {
auto add_vertices_as_solid = [](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, TBuffer& buffer, unsigned int vbuffer_id, VertexBuffer& vertices, size_t move_id) {
auto store_vertex = [](VertexBuffer& vertices, const Vec3f& position, const Vec3f& normal) {
// append position
vertices.push_back(position.x());
@ -1340,7 +1338,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
last_path.sub_paths.back().last = { vbuffer_id, vertices.size(), move_id, curr.position };
};
auto add_indices_as_solid = [&](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, const GCodeProcessor::MoveVertex* next,
auto add_indices_as_solid = [&](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, const GCodeProcessorResult::MoveVertex* next,
TBuffer& buffer, size_t& vbuffer_size, unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) {
static Vec3f prev_dir;
static Vec3f prev_up;
@ -1482,7 +1480,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
#if ENABLE_SEAMS_USING_MODELS
// format data into the buffers to be rendered as instanced model
auto add_model_instance = [](const GCodeProcessor::MoveVertex& curr, InstanceBuffer& instances, InstanceIdBuffer& instances_ids, size_t move_id) {
auto add_model_instance = [](const GCodeProcessorResult::MoveVertex& curr, InstanceBuffer& instances, InstanceIdBuffer& instances_ids, size_t move_id) {
// append position
instances.push_back(curr.position.x());
instances.push_back(curr.position.y());
@ -1498,7 +1496,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
#if ENABLE_SEAMS_USING_BATCHED_MODELS
// format data into the buffers to be rendered as batched model
auto add_vertices_as_model_batch = [](const GCodeProcessor::MoveVertex& curr, const GLModel::InitializationData& data, VertexBuffer& vertices, InstanceBuffer& instances, InstanceIdBuffer& instances_ids, size_t move_id) {
auto add_vertices_as_model_batch = [](const GCodeProcessorResult::MoveVertex& curr, const GLModel::InitializationData& data, VertexBuffer& vertices, InstanceBuffer& instances, InstanceIdBuffer& instances_ids, size_t move_id) {
const double width = static_cast<double>(1.5f * curr.width);
const double height = static_cast<double>(1.5f * curr.height);
@ -1542,7 +1540,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
#if ENABLE_GCODE_VIEWER_STATISTICS
auto start_time = std::chrono::high_resolution_clock::now();
m_statistics.results_size = SLIC3R_STDVEC_MEMSIZE(gcode_result.moves, GCodeProcessor::MoveVertex);
m_statistics.results_size = SLIC3R_STDVEC_MEMSIZE(gcode_result.moves, GCodeProcessorResult::MoveVertex);
m_statistics.results_time = gcode_result.time;
#endif // ENABLE_GCODE_VIEWER_STATISTICS
@ -1561,7 +1559,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
wxBusyCursor busy;
// extract approximate paths bounding box from result
for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) {
for (const GCodeProcessorResult::MoveVertex& move : gcode_result.moves) {
if (wxGetApp().is_gcode_viewer())
// for the gcode viewer we need to take in account all moves to correctly size the printbed
m_paths_bounding_box.merge(move.position.cast<double>());
@ -1575,57 +1573,18 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
m_max_bounding_box = m_paths_bounding_box;
m_max_bounding_box.merge(m_paths_bounding_box.max + m_sequential_view.marker.get_bounding_box().size().z() * Vec3d::UnitZ());
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
if (wxGetApp().is_editor()) {
const Bed3D::EShapeType bed_type = wxGetApp().plater()->get_bed().get_shape_type();
if (bed_type == Bed3D::EShapeType::Rectangle) {
BoundingBoxf3 print_volume = wxGetApp().plater()->get_bed().get_bounding_box(false);
print_volume.min.z() = -1e10;
print_volume.max.z() = m_max_print_height;
print_volume.min -= Vec3f(BedEpsilon, BedEpsilon, 0.0f).cast<double>();
print_volume.max += Vec3f(BedEpsilon, BedEpsilon, 0.0f).cast<double>();
m_contained_in_bed = print_volume.contains(m_paths_bounding_box);
}
else if (bed_type == Bed3D::EShapeType::Circle) {
Vec2d center;
double radius;
Bed3D::is_circle(wxGetApp().plater()->get_bed().get_shape(), &center, &radius);
const double sq_radius = sqr(radius);
for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) {
if (move.type == EMoveType::Extrude && move.extrusion_role != erCustom && move.width != 0.0f && move.height != 0.0f) {
if (sq_radius < (Vec2d(move.position.x(), move.position.y()) - center).squaredNorm()) {
m_contained_in_bed = false;
break;
}
}
}
}
else if (bed_type == Bed3D::EShapeType::Custom) {
const Pointfs& shape = wxGetApp().plater()->get_bed().get_shape();
if (Bed3D::is_convex(shape)) {
const Polygon poly = Polygon::new_scale(shape);
for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) {
if (move.type == EMoveType::Extrude && move.extrusion_role != erCustom && move.width != 0.0f && move.height != 0.0f) {
if (!poly.contains(Point::new_scale(Vec2d(move.position.x(), move.position.y())))) {
m_contained_in_bed = false;
break;
}
}
}
}
}
}
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
if (wxGetApp().is_editor())
m_contained_in_bed = wxGetApp().plater()->build_volume().all_paths_inside(gcode_result, m_paths_bounding_box);
#if ENABLE_FIX_SEAMS_SYNCH
m_sequential_view.gcode_ids.clear();
for (size_t i = 0; i < gcode_result.moves.size(); ++i) {
const GCodeProcessor::MoveVertex& move = gcode_result.moves[i];
const GCodeProcessorResult::MoveVertex& move = gcode_result.moves[i];
if (move.type != EMoveType::Seam)
m_sequential_view.gcode_ids.push_back(move.gcode_id);
}
#else
for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) {
for (const GCodeProcessorResult::MoveVertex& move : gcode_result.moves) {
m_sequential_view.gcode_ids.push_back(move.gcode_id);
}
#endif // ENABLE_FIX_SEAMS_SYNCH
@ -1648,7 +1607,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
// toolpaths data -> extract vertices from result
for (size_t i = 0; i < m_moves_count; ++i) {
const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i];
const GCodeProcessorResult::MoveVertex& curr = gcode_result.moves[i];
#if ENABLE_FIX_SEAMS_SYNCH
if (curr.type == EMoveType::Seam) {
++seams_count;
@ -1662,7 +1621,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
if (i == 0)
continue;
const GCodeProcessor::MoveVertex& prev = gcode_result.moves[i - 1];
const GCodeProcessorResult::MoveVertex& prev = gcode_result.moves[i - 1];
// update progress dialog
++progress_count;
@ -2066,7 +2025,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
#endif // ENABLE_FIX_SEAMS_SYNCH
for (size_t i = 0; i < m_moves_count; ++i) {
const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i];
const GCodeProcessorResult::MoveVertex& curr = gcode_result.moves[i];
#if ENABLE_FIX_SEAMS_SYNCH
if (curr.type == EMoveType::Seam)
++seams_count;
@ -2078,8 +2037,8 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
if (i == 0)
continue;
const GCodeProcessor::MoveVertex& prev = gcode_result.moves[i - 1];
const GCodeProcessor::MoveVertex* next = nullptr;
const GCodeProcessorResult::MoveVertex& prev = gcode_result.moves[i - 1];
const GCodeProcessorResult::MoveVertex* next = nullptr;
if (i < m_moves_count - 1)
next = &gcode_result.moves[i + 1];
@ -2286,7 +2245,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
seams_count = 0;
#endif // ENABLE_FIX_SEAMS_SYNCH
for (size_t i = 0; i < m_moves_count; ++i) {
const GCodeProcessor::MoveVertex& move = gcode_result.moves[i];
const GCodeProcessorResult::MoveVertex& move = gcode_result.moves[i];
#if ENABLE_FIX_SEAMS_SYNCH
if (move.type == EMoveType::Seam)
++seams_count;

View file

@ -233,7 +233,7 @@ class GCodeViewer
unsigned char cp_color_id{ 0 };
std::vector<Sub_Path> sub_paths;
bool matches(const GCodeProcessor::MoveVertex& move) const;
bool matches(const GCodeProcessorResult::MoveVertex& move) const;
size_t vertices_count() const {
return sub_paths.empty() ? 0 : sub_paths.back().last.s_id - sub_paths.front().first.s_id + 1;
}
@ -251,7 +251,7 @@ class GCodeViewer
return -1;
}
}
void add_sub_path(const GCodeProcessor::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id) {
void add_sub_path(const GCodeProcessorResult::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id) {
Endpoint endpoint = { b_id, i_id, s_id, move.position };
sub_paths.push_back({ endpoint , endpoint });
}
@ -361,7 +361,7 @@ class GCodeViewer
// b_id index of buffer contained in this->indices
// i_id index of first index contained in this->indices[b_id]
// s_id index of first vertex contained in this->vertices
void add_path(const GCodeProcessor::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id);
void add_path(const GCodeProcessorResult::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id);
unsigned int max_vertices_per_segment() const {
switch (render_primitive_type)
@ -802,7 +802,7 @@ private:
Statistics m_statistics;
#endif // ENABLE_GCODE_VIEWER_STATISTICS
std::array<float, 2> m_detected_point_sizes = { 0.0f, 0.0f };
GCodeProcessor::Result::SettingsIds m_settings_ids;
GCodeProcessorResult::SettingsIds m_settings_ids;
std::array<SequentialRangeCap, 2> m_sequential_range_caps;
std::vector<CustomGCode::Item> m_custom_gcode_per_print_z;
@ -820,9 +820,9 @@ public:
#endif // ENABLE_SEAMS_USING_MODELS
// extract rendering data from the given parameters
void load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized);
void load(const GCodeProcessorResult& gcode_result, const Print& print, bool initialized);
// recalculate ranges in dependence of what is visible and sets tool/print colors
void refresh(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors);
void refresh(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors);
void refresh_render_paths();
void update_shells_color_by_extruder(const DynamicPrintConfig* config);
@ -870,7 +870,7 @@ public:
size_t get_extruders_count() { return m_extruders_count; }
private:
void load_toolpaths(const GCodeProcessor::Result& gcode_result);
void load_toolpaths(const GCodeProcessorResult& gcode_result);
void load_shells(const Print& print, bool initialized);
void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const;
void render_toolpaths();

View file

@ -3,16 +3,18 @@
#include <igl/unproject.h>
#include "libslic3r/BuildVolume.hpp"
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/GCode/ThumbnailData.hpp"
#include "libslic3r/Geometry.hpp"
#include "libslic3r/Geometry/ConvexHull.hpp"
#include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/Layer.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/Technologies.hpp"
#include "libslic3r/Tesselate.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "slic3r/GUI/3DBed.hpp"
#include "slic3r/GUI/3DScene.hpp"
#include "slic3r/GUI/BackgroundSlicingProcess.hpp"
#include "slic3r/GUI/GLShader.hpp"
@ -20,7 +22,6 @@
#include "slic3r/GUI/Tab.hpp"
#include "slic3r/GUI/GUI_Preview.hpp"
#include "slic3r/GUI/OpenGLManager.hpp"
#include "slic3r/GUI/3DBed.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/MainFrame.hpp"
#include "slic3r/Utils/UndoRedo.hpp"
@ -957,9 +958,10 @@ PrinterTechnology GLCanvas3D::current_printer_technology() const
return m_process->current_printer_technology();
}
GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas)
GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed)
: m_canvas(canvas)
, m_context(nullptr)
, m_bed(bed)
#if ENABLE_RETINA_GL
, m_retina_helper(nullptr)
#endif
@ -1115,18 +1117,10 @@ void GLCanvas3D::reset_volumes()
_set_warning_notification(EWarning::ObjectOutside, false);
}
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state(bool as_toolpaths) const
#else
ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state() const
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
{
ModelInstanceEPrintVolumeState state;
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
m_volumes.check_outside_state(m_config, &state, as_toolpaths);
#else
m_volumes.check_outside_state(m_config, &state);
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
m_volumes.check_outside_state(m_bed.build_volume(), &state);
return state;
}
@ -1250,13 +1244,11 @@ BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const
BoundingBoxf3 GLCanvas3D::scene_bounding_box() const
{
BoundingBoxf3 bb = volumes_bounding_box();
bb.merge(wxGetApp().plater()->get_bed().get_bounding_box(true));
if (m_config != nullptr) {
double h = m_config->opt_float("max_print_height");
bb.min(2) = std::min(bb.min(2), -h);
bb.max(2) = std::max(bb.max(2), h);
}
bb.merge(m_bed.extended_bounding_box());
double h = m_bed.build_volume().max_print_height();
//FIXME why -h?
bb.min.z() = std::min(bb.min.z(), -h);
bb.max.z() = std::max(bb.max.z(), h);
return bb;
}
@ -1362,7 +1354,7 @@ void GLCanvas3D::allow_multisample(bool allow)
void GLCanvas3D::zoom_to_bed()
{
_zoom_to_box(wxGetApp().plater()->get_bed().get_bounding_box(false));
_zoom_to_box(m_bed.build_volume().bounding_volume());
}
void GLCanvas3D::zoom_to_volumes()
@ -1423,7 +1415,7 @@ void GLCanvas3D::render()
m_gcode_viewer.init();
#endif // ENABLE_SEAMS_USING_MODELS
if (wxGetApp().plater()->get_bed().get_shape().empty()) {
if (! m_bed.build_volume().valid()) {
// this happens at startup when no data is still saved under <>\AppData\Roaming\Slic3rPE
post_event(SimpleEvent(EVT_GLCANVAS_UPDATE_BED_SHAPE));
return;
@ -2057,7 +2049,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
// checks for geometry outside the print volume to render it accordingly
if (!m_volumes.empty()) {
ModelInstanceEPrintVolumeState state;
const bool contained_min_one = m_volumes.check_outside_state(m_config, &state);
const bool contained_min_one = m_volumes.check_outside_state(m_bed.build_volume(), &state);
const bool partlyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Partly_Outside);
const bool fullyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Fully_Outside);
@ -2109,7 +2101,7 @@ static void reserve_new_volume_finalize_old_volume(GLVolume& vol_new, GLVolume&
vol_old.finalize_geometry(gl_initialized);
}
void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors)
void GLCanvas3D::load_gcode_preview(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors)
{
m_gcode_viewer.load(gcode_result, *this->fff_print(), m_initialized);
@ -2138,10 +2130,6 @@ void GLCanvas3D::load_sla_preview()
// Release OpenGL data before generating new data.
reset_volumes();
_load_sla_shells();
#if !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false);
m_volumes.set_print_box(float(bed_bb.min.x()) - BedEpsilon, float(bed_bb.min.y()) - BedEpsilon, 0.0f, float(bed_bb.max.x()) + BedEpsilon, float(bed_bb.max.y()) + BedEpsilon, (float)m_config->opt_float("max_print_height"));
#endif // !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
_update_sla_shells_outside_state();
_set_warning_notification_if_needed(EWarning::SlaSupportsOutside);
}
@ -2158,20 +2146,12 @@ void GLCanvas3D::load_preview(const std::vector<std::string>& str_tool_colors, c
// Release OpenGL data before generating new data.
this->reset_volumes();
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
bool requires_convex_hulls = wxGetApp().plater()->get_bed().get_shape_type() != Bed3D::EShapeType::Rectangle;
_load_print_toolpaths(requires_convex_hulls);
_load_wipe_tower_toolpaths(str_tool_colors, requires_convex_hulls);
const BuildVolume &build_volume = m_bed.build_volume();
_load_print_toolpaths(build_volume);
_load_wipe_tower_toolpaths(build_volume, str_tool_colors);
for (const PrintObject* object : print->objects())
_load_print_object_toolpaths(*object, str_tool_colors, color_print_values, requires_convex_hulls);
#else
_load_print_toolpaths();
_load_wipe_tower_toolpaths(str_tool_colors);
for (const PrintObject* object : print->objects())
_load_print_object_toolpaths(*object, str_tool_colors, color_print_values);
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
_load_print_object_toolpaths(*object, build_volume, str_tool_colors, color_print_values);
_update_toolpath_volumes_outside_state();
_set_warning_notification_if_needed(EWarning::ToolpathOutside);
}
@ -3770,7 +3750,7 @@ Linef3 GLCanvas3D::mouse_ray(const Point& mouse_pos)
double GLCanvas3D::get_size_proportional_to_max_bed_size(double factor) const
{
return factor * wxGetApp().plater()->get_bed().get_bounding_box(false).max_size();
return factor * m_bed.build_volume().bounding_volume().max_size();
}
void GLCanvas3D::set_cursor(ECursorType type)
@ -4161,7 +4141,7 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, const
}
else
// This happens for empty projects
volumes_box = wxGetApp().plater()->get_bed().get_bounding_box(true);
volumes_box = m_bed.extended_bounding_box();
#endif // ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
Camera camera;
@ -4178,7 +4158,7 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, const
// extends the near and far z of the frustrum to avoid the bed being clipped
// box in eye space
BoundingBoxf3 t_bed_box = wxGetApp().plater()->get_bed().get_bounding_box(true).transformed(camera.get_view_matrix());
BoundingBoxf3 t_bed_box = m_bed.extended_bounding_box().transformed(camera.get_view_matrix());
near_z = -t_bed_box.max.z();
far_z = -t_bed_box.min.z();
}
@ -4861,7 +4841,7 @@ BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_be
bb.merge(BoundingBoxf3(sel_bb_center - extend_by, sel_bb_center + extend_by));
}
bb.merge(wxGetApp().plater()->get_bed().get_bounding_box(include_bed_model));
bb.merge(include_bed_model ? m_bed.extended_bounding_box() : m_bed.build_volume().bounding_volume());
if (!m_main_toolbar.is_enabled())
bb.merge(m_gcode_viewer.get_max_bounding_box());
@ -5035,25 +5015,6 @@ void GLCanvas3D::_rectangular_selection_picking_pass()
_update_volumes_hover_state();
}
#if !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
static BoundingBoxf3 print_volume(const DynamicPrintConfig& config)
{
// tolerance to avoid false detection at bed edges
const double tolerance_x = 0.05;
const double tolerance_y = 0.05;
BoundingBoxf3 ret;
const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(config.option("bed_shape"));
if (opt != nullptr) {
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
ret = BoundingBoxf3(Vec3d(unscale<double>(bed_box_2D.min(0)) - tolerance_x, unscale<double>(bed_box_2D.min(1)) - tolerance_y, 0.0), Vec3d(unscale<double>(bed_box_2D.max(0)) + tolerance_x, unscale<double>(bed_box_2D.max(1)) + tolerance_y, config.opt_float("max_print_height")));
// Allow the objects to protrude below the print bed
ret.min(2) = -1e10;
}
return ret;
}
#endif // !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
void GLCanvas3D::_render_background() const
{
bool use_error_color = false;
@ -5064,15 +5025,7 @@ void GLCanvas3D::_render_background() const
if (!m_volumes.empty())
use_error_color &= _is_any_volume_outside();
else
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
use_error_color &= m_gcode_viewer.has_data() && !m_gcode_viewer.is_contained_in_bed();
#else
{
const BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3();
const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box();
use_error_color &= (test_volume.radius() > 0.0 && paths_volume.radius() > 0.0) ? !test_volume.contains(paths_volume) : false;
}
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
}
glsafe(::glPushMatrix());
@ -5123,7 +5076,7 @@ void GLCanvas3D::_render_bed(bool bottom, bool show_axes)
&& m_gizmos.get_current_type() != GLGizmosManager::Seam
&& m_gizmos.get_current_type() != GLGizmosManager::MmuSegmentation);
wxGetApp().plater()->get_bed().render(*this, bottom, scale_factor, show_axes, show_texture);
m_bed.render(*this, bottom, scale_factor, show_axes, show_texture);
}
void GLCanvas3D::_render_bed_for_picking(bool bottom)
@ -5133,7 +5086,7 @@ void GLCanvas3D::_render_bed_for_picking(bool bottom)
scale_factor = m_retina_helper->get_scale_factor();
#endif // ENABLE_RETINA_GL
wxGetApp().plater()->get_bed().render_for_picking(*this, bottom, scale_factor);
m_bed.render_for_picking(*this, bottom, scale_factor);
}
void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
@ -5148,55 +5101,35 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
if (m_picking_enabled) {
// Update the layer editing selection to the first object selected, update the current object maximum Z.
m_layers_editing.select_object(*m_model, this->is_layers_editing_enabled() ? m_selection.get_object_idx() : -1);
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
}
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
if (m_config != nullptr) {
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
Bed3D::EShapeType type = wxGetApp().plater()->get_bed().get_shape_type();
switch (type)
{
case Bed3D::EShapeType::Circle: {
Vec2d center;
double radius;
if (Bed3D::is_circle(wxGetApp().plater()->get_bed().get_shape(), &center, &radius)) {
m_volumes.set_print_volume({ static_cast<int>(type),
{ float(center.x()), float(center.y()), float(radius) + BedEpsilon, 0.0f },
{ 0.0f, float(m_config->opt_float("max_print_height")) } });
}
if (const BuildVolume &build_volume = m_bed.build_volume(); build_volume.valid()) {
switch (build_volume.type()) {
case BuildVolume::Type::Rectangle: {
const BoundingBox3Base<Vec3d> bed_bb = build_volume.bounding_volume().inflated(BuildVolume::SceneEpsilon);
m_volumes.set_print_volume({ 0, // circle
{ float(bed_bb.min.x()), float(bed_bb.min.y()), float(bed_bb.max.x()), float(bed_bb.max.y()) },
{ 0.0f, float(build_volume.max_print_height()) } });
break;
}
case Bed3D::EShapeType::Rectangle: {
const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false);
m_volumes.set_print_volume({ static_cast<int>(type),
{ float(bed_bb.min.x()) - BedEpsilon, float(bed_bb.min.y()) - BedEpsilon, float(bed_bb.max.x()) + BedEpsilon, float(bed_bb.max.y()) + BedEpsilon },
{ 0.0f, float(m_config->opt_float("max_print_height")) } });
case BuildVolume::Type::Circle: {
m_volumes.set_print_volume({ 1, // rectangle
{ unscaled<float>(build_volume.circle().center.x()), unscaled<float>(build_volume.circle().center.y()), unscaled<float>(build_volume.circle().radius + BuildVolume::SceneEpsilon), 0.0f },
{ 0.0f, float(build_volume.max_print_height() + BuildVolume::SceneEpsilon) } });
break;
}
default:
case Bed3D::EShapeType::Custom: {
case BuildVolume::Type::Custom: {
m_volumes.set_print_volume({ static_cast<int>(type),
{ 0.0f, 0.0f, 0.0f, 0.0f },
{ 0.0f, 0.0f } });
}
}
#else
const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false);
m_volumes.set_print_box((float)bed_bb.min.x() - BedEpsilon, (float)bed_bb.min.y() - BedEpsilon, 0.0f, (float)bed_bb.max.x() + BedEpsilon, (float)bed_bb.max.y() + BedEpsilon, (float)m_config->opt_float("max_print_height"));
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
if (m_requires_check_outside_state) {
m_volumes.check_outside_state(m_config, nullptr);
m_volumes.check_outside_state(build_volume, nullptr);
m_requires_check_outside_state = false;
}
#else
m_volumes.check_outside_state(m_config, nullptr);
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
}
#if !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
}
#endif // !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
if (m_use_clipping_planes)
m_volumes.set_z_range(-m_clipping_planes[0].get_data()[3], m_clipping_planes[1].get_data()[3]);
@ -5206,11 +5139,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data());
m_volumes.set_show_sinking_contours(! m_gizmos.is_hiding_instances());
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_mod");
#else
GLShaderProgram* shader = wxGetApp().get_shader("gouraud");
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
if (shader != nullptr) {
shader->start_using();
@ -5832,11 +5761,7 @@ void GLCanvas3D::_stop_timer()
m_timer.Stop();
}
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
void GLCanvas3D::_load_print_toolpaths(bool generate_convex_hulls)
#else
void GLCanvas3D::_load_print_toolpaths()
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
void GLCanvas3D::_load_print_toolpaths(const BuildVolume &build_volume)
{
const Print *print = this->fff_print();
if (print == nullptr)
@ -5889,18 +5814,11 @@ void GLCanvas3D::_load_print_toolpaths()
reserve_new_volume_finalize_old_volume(*volume, vol, m_initialized);
}
}
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
if (generate_convex_hulls)
volume->calc_convex_hull_3d();
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
volume->is_outside = ! build_volume.all_paths_inside_vertices_and_normals_interleaved(volume->indexed_vertex_array.vertices_and_normals_interleaved, volume->indexed_vertex_array.bounding_box());
volume->indexed_vertex_array.finalize_geometry(m_initialized);
}
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors, const std::vector<CustomGCode::Item>& color_print_values, bool generate_convex_hulls)
#else
void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors, const std::vector<CustomGCode::Item>& color_print_values)
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const BuildVolume& build_volume, const std::vector<std::string>& str_tool_colors, const std::vector<CustomGCode::Item>& color_print_values)
{
std::vector<std::array<float, 4>> tool_colors = _parse_colors(str_tool_colors);
@ -6187,26 +6105,16 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
[](const GLVolume *volume) { return volume->empty(); }),
m_volumes.volumes.end());
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) {
GLVolume* v = m_volumes.volumes[i];
if (generate_convex_hulls)
v->calc_convex_hull_3d();
v->is_outside = ! build_volume.all_paths_inside_vertices_and_normals_interleaved(v->indexed_vertex_array.vertices_and_normals_interleaved, v->indexed_vertex_array.bounding_box());
v->indexed_vertex_array.finalize_geometry(m_initialized);
}
#else
for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_initialized);
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info();
}
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors, bool generate_convex_hulls)
#else
void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors)
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, const std::vector<std::string>& str_tool_colors)
{
const Print *print = this->fff_print();
if (print == nullptr || print->wipe_tower_data().tool_changes.empty())
@ -6357,17 +6265,11 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
[](const GLVolume *volume) { return volume->empty(); }),
m_volumes.volumes.end());
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) {
GLVolume* v = m_volumes.volumes[i];
if (generate_convex_hulls)
v->calc_convex_hull_3d();
v->is_outside = ! build_volume.all_paths_inside_vertices_and_normals_interleaved(v->indexed_vertex_array.vertices_and_normals_interleaved, v->indexed_vertex_array.bounding_box());
v->indexed_vertex_array.finalize_geometry(m_initialized);
}
#else
for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_initialized);
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info();
}
@ -6427,28 +6329,9 @@ void GLCanvas3D::_load_sla_shells()
update_volumes_colors_by_extruder();
}
void GLCanvas3D::_update_toolpath_volumes_outside_state()
{
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
check_volumes_outside_state(true);
#else
BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3();
for (GLVolume* volume : m_volumes.volumes) {
volume->is_outside = (test_volume.radius() > 0.0 && volume->is_extrusion_path) ? !test_volume.contains(volume->bounding_box()) : false;
}
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
}
void GLCanvas3D::_update_sla_shells_outside_state()
{
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
check_volumes_outside_state();
#else
BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3();
for (GLVolume* volume : m_volumes.volumes) {
volume->is_outside = (test_volume.radius() > 0.0 && volume->shader_outside_printer_detection_enabled) ? !test_volume.contains(volume->transformed_convex_hull_bounding_box()) : false;
}
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
}
void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning)

View file

@ -39,6 +39,7 @@ class wxGLContext;
namespace Slic3r {
class BackgroundSlicingProcess;
class BuildVolume;
struct ThumbnailData;
struct ThumbnailsParams;
class ModelObject;
@ -50,6 +51,8 @@ namespace CustomGCode { struct Item; }
namespace GUI {
class Bed3D;
#if ENABLE_RETINA_GL
class RetinaHelper;
#endif
@ -446,6 +449,7 @@ public:
private:
wxGLCanvas* m_canvas;
wxGLContext* m_context;
Bed3D &m_bed;
#if ENABLE_RETINA_GL
std::unique_ptr<RetinaHelper> m_retina_helper;
#endif
@ -600,7 +604,7 @@ private:
m_gizmo_highlighter;
public:
explicit GLCanvas3D(wxGLCanvas* canvas);
explicit GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed);
~GLCanvas3D();
bool is_initialized() const { return m_initialized; }
@ -621,11 +625,7 @@ public:
unsigned int get_volumes_count() const;
const GLVolumeCollection& get_volumes() const { return m_volumes; }
void reset_volumes();
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
ModelInstanceEPrintVolumeState check_volumes_outside_state(bool as_toolpaths = false) const;
#else
ModelInstanceEPrintVolumeState check_volumes_outside_state() const;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
#if ENABLE_SEAMS_USING_MODELS
void init_gcode_viewer() { m_gcode_viewer.init(); }
@ -736,7 +736,7 @@ public:
void reload_scene(bool refresh_immediately, bool force_full_scene_refresh = false);
void load_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors);
void load_gcode_preview(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors);
void refresh_gcode_preview_render_paths();
void set_gcode_view_preview_type(GCodeViewer::EViewType type) { return m_gcode_viewer.set_view_type(type); }
GCodeViewer::EViewType get_gcode_view_preview_type() const { return m_gcode_viewer.get_view_type(); }
@ -955,33 +955,19 @@ private:
void _start_timer();
void _stop_timer();
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
// Create 3D thick extrusion lines for a skirt and brim.
// Adds a new Slic3r::GUI::3DScene::Volume to volumes.
void _load_print_toolpaths(bool generate_convex_hulls = false);
// Adds a new Slic3r::GUI::3DScene::Volume to volumes, updates collision with the build_volume.
void _load_print_toolpaths(const BuildVolume &build_volume);
// Create 3D thick extrusion lines for object forming extrusions.
// Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes,
// one for perimeters, one for infill and one for supports.
void _load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors,
const std::vector<CustomGCode::Item>& color_print_values, bool generate_convex_hulls = false);
// Create 3D thick extrusion lines for wipe tower extrusions
void _load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors, bool generate_convex_hulls = false);
#else
// Create 3D thick extrusion lines for a skirt and brim.
// Adds a new Slic3r::GUI::3DScene::Volume to volumes.
void _load_print_toolpaths();
// Create 3D thick extrusion lines for object forming extrusions.
// Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes,
// one for perimeters, one for infill and one for supports.
void _load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors,
const std::vector<CustomGCode::Item>& color_print_values);
// Create 3D thick extrusion lines for wipe tower extrusions
void _load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors);
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
// one for perimeters, one for infill and one for supports, updates collision with the build_volume.
void _load_print_object_toolpaths(const PrintObject& print_object, const BuildVolume &build_volume,
const std::vector<std::string>& str_tool_colors, const std::vector<CustomGCode::Item>& color_print_values);
// Create 3D thick extrusion lines for wipe tower extrusions, updates collision with the build_volume.
void _load_wipe_tower_toolpaths(const BuildVolume &build_volume, const std::vector<std::string>& str_tool_colors);
// Load SLA objects and support structures for objects, for which the slaposSliceSupports step has been finished.
void _load_sla_shells();
void _update_toolpath_volumes_outside_state();
void _update_sla_shells_outside_state();
void _set_warning_notification_if_needed(EWarning warning);

View file

@ -2652,6 +2652,11 @@ Plater* GUI_App::plater()
return plater_;
}
const Plater* GUI_App::plater() const
{
return plater_;
}
Model& GUI_App::model()
{
return plater_->model();

View file

@ -281,6 +281,7 @@ public:
ObjectList* obj_list();
ObjectLayers* obj_layers();
Plater* plater();
const Plater* plater() const;
Model& model();
NotificationManager * notification_manager();

View file

@ -1758,12 +1758,9 @@ void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name
new_object->invalidate_bounding_box();
new_object->translate(-bb.center());
if (center) {
const BoundingBoxf bed_shape = wxGetApp().plater()->bed_shape_bb();
new_object->instances[0]->set_offset(Slic3r::to_3d(bed_shape.center().cast<double>(), -new_object->origin_translation.z()));
} else {
new_object->instances[0]->set_offset(bb.center());
}
new_object->instances[0]->set_offset(center ?
to_3d(wxGetApp().plater()->build_volume().bounding_volume2d().center(), -new_object->origin_translation.z()) :
bb.center());
new_object->ensure_on_bed();

View file

@ -37,11 +37,11 @@
namespace Slic3r {
namespace GUI {
View3D::View3D(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process)
View3D::View3D(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process)
: m_canvas_widget(nullptr)
, m_canvas(nullptr)
{
init(parent, model, config, process);
init(parent, bed, model, config, process);
}
View3D::~View3D()
@ -50,7 +50,7 @@ View3D::~View3D()
delete m_canvas_widget;
}
bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process)
bool View3D::init(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process)
{
if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 /* disable wxTAB_TRAVERSAL */))
return false;
@ -59,7 +59,7 @@ bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, Ba
if (m_canvas_widget == nullptr)
return false;
m_canvas = new GLCanvas3D(m_canvas_widget);
m_canvas = new GLCanvas3D(m_canvas_widget, bed);
m_canvas->set_context(wxGetApp().init_glcontext(*m_canvas_widget));
m_canvas->allow_multisample(OpenGLManager::can_multisample());
@ -169,18 +169,18 @@ void View3D::render()
}
Preview::Preview(
wxWindow* parent, Model* model, DynamicPrintConfig* config,
BackgroundSlicingProcess* process, GCodeProcessor::Result* gcode_result, std::function<void()> schedule_background_process_func)
wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig* config,
BackgroundSlicingProcess* process, GCodeProcessorResult* gcode_result, std::function<void()> schedule_background_process_func)
: m_config(config)
, m_process(process)
, m_gcode_result(gcode_result)
, m_schedule_background_process(schedule_background_process_func)
{
if (init(parent, model))
if (init(parent, bed, model))
load_print();
}
bool Preview::init(wxWindow* parent, Model* model)
bool Preview::init(wxWindow* parent, Bed3D& bed, Model* model)
{
if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 /* disable wxTAB_TRAVERSAL */))
return false;
@ -196,7 +196,7 @@ bool Preview::init(wxWindow* parent, Model* model)
if (m_canvas_widget == nullptr)
return false;
m_canvas = new GLCanvas3D(m_canvas_widget);
m_canvas = new GLCanvas3D(m_canvas_widget, bed);
m_canvas->set_context(wxGetApp().init_glcontext(*m_canvas_widget));
m_canvas->allow_multisample(OpenGLManager::can_multisample());
m_canvas->set_config(m_config);

View file

@ -44,7 +44,7 @@ class View3D : public wxPanel
GLCanvas3D* m_canvas;
public:
View3D(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process);
View3D(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process);
virtual ~View3D();
wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; }
@ -70,7 +70,7 @@ public:
void render();
private:
bool init(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process);
bool init(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process);
};
class Preview : public wxPanel
@ -93,7 +93,7 @@ class Preview : public wxPanel
DynamicPrintConfig* m_config;
BackgroundSlicingProcess* m_process;
GCodeProcessor::Result* m_gcode_result;
GCodeProcessorResult* m_gcode_result;
#ifdef __linux__
// We are getting mysterious crashes on Linux in gtk due to OpenGL context activation GH #1874 #1955.
@ -129,8 +129,8 @@ public:
Legend
};
Preview(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process,
GCodeProcessor::Result* gcode_result, std::function<void()> schedule_background_process = []() {});
Preview(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process,
GCodeProcessorResult* gcode_result, std::function<void()> schedule_background_process = []() {});
virtual ~Preview();
wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; }
@ -161,7 +161,7 @@ public:
void hide_layers_slider();
private:
bool init(wxWindow* parent, Model* model);
bool init(wxWindow* parent, Bed3D& bed, Model* model);
void bind_event_handlers();
void unbind_event_handlers();

View file

@ -26,10 +26,10 @@
#include "3DScene.hpp"
#include "GLCanvas3D.hpp"
#include "Plater.hpp"
#include "3DBed.hpp"
#include "MsgDialog.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/AppConfig.hpp"
#include "libslic3r/BuildVolume.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/GCode/ThumbnailData.hpp"
#include "libslic3r/Format/OBJ.hpp"
@ -270,9 +270,7 @@ static void generate_thumbnail_from_model(const std::string& filename)
model.objects[0]->center_around_origin(false);
model.objects[0]->ensure_on_bed(false);
const Vec3d bed_center_3d = wxGetApp().plater()->get_bed().get_bounding_box(false).center();
const Vec2d bed_center_2d = { bed_center_3d.x(), bed_center_3d.y()};
model.center_instances_around_point(bed_center_2d);
model.center_instances_around_point(to_2d(wxGetApp().plater()->build_volume().bounding_volume().center()));
GLVolumeCollection volumes;
volumes.volumes.push_back(new GLVolume());

View file

@ -3,6 +3,7 @@
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp"
#include "libslic3r/Geometry/ConvexHull.hpp"
#include "libslic3r/Model.hpp"
#include <numeric>

View file

@ -1,5 +1,6 @@
#include "ArrangeJob.hpp"
#include "libslic3r/BuildVolume.hpp"
#include "libslic3r/MTUtils.hpp"
#include "libslic3r/Model.hpp"
@ -263,7 +264,7 @@ get_wipe_tower_arrangepoly(const Plater &plater)
}
double bed_stride(const Plater *plater) {
double bedwidth = plater->bed_shape_bb().size().x();
double bedwidth = plater->build_volume().bounding_volume().size().x();
return scaled<double>((1. + LOGICAL_BED_GAP) * bedwidth);
}

View file

@ -486,7 +486,7 @@ void MainFrame::update_layout()
case ESettingsLayout::GCodeViewer:
{
m_main_sizer->Add(m_plater, 1, wxEXPAND);
m_plater->set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, "", "", true);
m_plater->set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, 0, {}, {}, true);
m_plater->get_collapse_toolbar().set_enabled(false);
m_plater->collapse_sidebar(true);
m_plater->Show();

View file

@ -1554,7 +1554,7 @@ struct Plater::priv
Slic3r::SLAPrint sla_print;
Slic3r::Model model;
PrinterTechnology printer_technology = ptFFF;
Slic3r::GCodeProcessor::Result gcode_result;
Slic3r::GCodeProcessorResult gcode_result;
// GUI elements
wxSizer* panel_sizer{ nullptr };
@ -1717,8 +1717,6 @@ struct Plater::priv
void update_main_toolbar_tooltips();
// std::shared_ptr<ProgressStatusBar> statusbar();
std::string get_config(const std::string &key) const;
BoundingBoxf bed_shape_bb() const;
BoundingBox scaled_bed_shape_bb() const;
std::vector<size_t> load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config, bool used_inches = false);
std::vector<size_t> load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z = false);
@ -1842,7 +1840,7 @@ struct Plater::priv
// triangulate the bed and store the triangles into m_bed.m_triangles,
// fills the m_bed.m_grid_lines and sets m_bed.m_origin.
// Sets m_bed.m_polygon to limit the object placement.
void set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false);
void set_bed_shape(const Pointfs& shape, const double max_print_height, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false);
bool can_delete() const;
bool can_delete_all() const;
@ -1956,8 +1954,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
sla_print.set_status_callback(statuscb);
this->q->Bind(EVT_SLICING_UPDATE, &priv::on_slicing_update, this);
view3D = new View3D(q, &model, config, &background_process);
preview = new Preview(q, &model, config, &background_process, &gcode_result, [this]() { schedule_background_process(); });
view3D = new View3D(q, bed, &model, config, &background_process);
preview = new Preview(q, bed, &model, config, &background_process, &gcode_result, [this]() { schedule_background_process(); });
#ifdef __APPLE__
// set default view_toolbar icons size equal to GLGizmosManager::Default_Icons_Size
@ -2172,13 +2170,8 @@ void Plater::priv::update(unsigned int flags)
{
// the following line, when enabled, causes flickering on NVIDIA graphics cards
// wxWindowUpdateLocker freeze_guard(q);
if (get_config("autocenter") == "1") {
// auto *bed_shape_opt = config->opt<ConfigOptionPoints>("bed_shape");
// const auto bed_shape = Slic3r::Polygon::new_scale(bed_shape_opt->values);
// const BoundingBox bed_shape_bb = bed_shape.bounding_box();
const Vec2d& bed_center = bed_shape_bb().center();
model.center_instances_around_point(bed_center);
}
if (get_config("autocenter") == "1")
model.center_instances_around_point(this->bed.build_volume().bed_center());
unsigned int update_status = 0;
const bool force_background_processing_restart = this->printer_technology == ptSLA || (flags & (unsigned int)UpdateParams::FORCE_BACKGROUND_PROCESSING_UPDATE);
@ -2281,19 +2274,6 @@ std::string Plater::priv::get_config(const std::string &key) const
return wxGetApp().app_config->get(key);
}
BoundingBoxf Plater::priv::bed_shape_bb() const
{
BoundingBox bb = scaled_bed_shape_bb();
return BoundingBoxf(unscale(bb.min), unscale(bb.max));
}
BoundingBox Plater::priv::scaled_bed_shape_bb() const
{
const auto *bed_shape_opt = config->opt<ConfigOptionPoints>("bed_shape");
const auto bed_shape = Slic3r::Polygon::new_scale(bed_shape_opt->values);
return bed_shape.bounding_box();
}
std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config, bool imperial_units/* = false*/)
{
if (input_files.empty()) { return std::vector<size_t>(); }
@ -2564,7 +2544,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
if (one_by_one) {
if (type_3mf && !is_project_file)
model.center_instances_around_point(bed_shape_bb().center());
model.center_instances_around_point(this->bed.build_volume().bed_center());
auto loaded_idxs = load_model_objects(model.objects, is_project_file);
obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end());
} else {
@ -2623,8 +2603,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z)
{
const BoundingBoxf bed_shape = bed_shape_bb();
const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast<double>(), 1.0) - 2.0 * Vec3d::Ones();
const Vec3d bed_size = Slic3r::to_3d(this->bed.build_volume().bounding_volume2d().size(), 1.0) - 2.0 * Vec3d::Ones();
#ifndef AUTOPLACEMENT_ON_LOAD
// bool need_arrange = false;
@ -2652,7 +2631,7 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& mode
// add a default instance and center object around origin
object->center_around_origin(); // also aligns object to Z = 0
ModelInstance* instance = object->add_instance();
instance->set_offset(Slic3r::to_3d(bed_shape.center().cast<double>(), -object->origin_translation(2)));
instance->set_offset(Slic3r::to_3d(this->bed.build_volume().bed_center(), -object->origin_translation(2)));
#endif /* AUTOPLACEMENT_ON_LOAD */
}
@ -2989,7 +2968,7 @@ void Plater::find_new_position(const ModelInstancePtrs &instances)
if (auto wt = get_wipe_tower_arrangepoly(*this))
fixed.emplace_back(*wt);
arrangement::arrange(movable, fixed, get_bed_shape(*config()), arr_params);
arrangement::arrange(movable, fixed, this->build_volume().polygon(), arr_params);
for (auto & m : movable)
m.apply();
@ -3057,22 +3036,9 @@ void Plater::priv::schedule_background_process()
void Plater::priv::update_print_volume_state()
{
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(this->config->option("bed_shape"));
const Polygon bed_poly_convex = offset(Geometry::convex_hull(Polygon::new_scale(opt->values).points), static_cast<float>(scale_(BedEpsilon))).front();
const float bed_height = this->config->opt_float("max_print_height");
this->q->model().update_print_volume_state(bed_poly_convex, bed_height);
#else
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(this->config->opt<ConfigOptionPoints>("bed_shape")->values));
BoundingBoxf3 print_volume(unscale(bed_box_2D.min(0), bed_box_2D.min(1), 0.0), unscale(bed_box_2D.max(0), bed_box_2D.max(1), scale_(this->config->opt_float("max_print_height"))));
// Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced.
print_volume.offset(BedEpsilon);
print_volume.min(2) = -1e10;
this->q->model().update_print_volume_state(print_volume);
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
this->q->model().update_print_volume_state(this->bed.build_volume());
}
void Plater::priv::process_validation_warning(const std::string& warning) const
{
if (warning.empty())
@ -4588,9 +4554,9 @@ bool Plater::priv::can_reload_from_disk() const
return !paths.empty();
}
void Plater::priv::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom)
void Plater::priv::set_bed_shape(const Pointfs& shape, const double max_print_height, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom)
{
bool new_shape = bed.set_shape(shape, custom_texture, custom_model, force_as_custom);
bool new_shape = bed.set_shape(shape, max_print_height, custom_texture, custom_model, force_as_custom);
if (new_shape) {
if (view3D) view3D->bed_shape_changed();
if (preview) preview->bed_shape_changed();
@ -6278,13 +6244,14 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
void Plater::set_bed_shape() const
{
set_bed_shape(p->config->option<ConfigOptionPoints>("bed_shape")->values,
p->config->option<ConfigOptionFloat>("max_print_height")->value,
p->config->option<ConfigOptionString>("bed_custom_texture")->value,
p->config->option<ConfigOptionString>("bed_custom_model")->value);
}
void Plater::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom) const
void Plater::set_bed_shape(const Pointfs& shape, const double max_print_height, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom) const
{
p->set_bed_shape(shape, custom_texture, custom_model, force_as_custom);
p->set_bed_shape(shape, max_print_height, custom_texture, custom_model, force_as_custom);
}
void Plater::force_filament_colors_update()
@ -6339,7 +6306,7 @@ void Plater::on_activate()
}
// Get vector of extruder colors considering filament color, if extruder color is undefined.
std::vector<std::string> Plater::get_extruder_colors_from_plater_config(const GCodeProcessor::Result* const result) const
std::vector<std::string> Plater::get_extruder_colors_from_plater_config(const GCodeProcessorResult* const result) const
{
if (wxGetApp().is_gcode_viewer() && result != nullptr)
return result->extruder_colors;
@ -6365,7 +6332,7 @@ std::vector<std::string> Plater::get_extruder_colors_from_plater_config(const GC
/* Get vector of colors used for rendering of a Preview scene in "Color print" mode
* It consists of extruder colors and colors, saved in model.custom_gcode_per_print_z
*/
std::vector<std::string> Plater::get_colors_for_color_print(const GCodeProcessor::Result* const result) const
std::vector<std::string> Plater::get_colors_for_color_print(const GCodeProcessorResult* const result) const
{
std::vector<std::string> colors = get_extruder_colors_from_plater_config(result);
colors.reserve(colors.size() + p->model.custom_gcode_per_print_z.gcodes.size());
@ -6431,11 +6398,6 @@ GLCanvas3D* Plater::get_current_canvas3D()
return p->get_current_canvas3D();
}
BoundingBoxf Plater::bed_shape_bb() const
{
return p->bed_shape_bb();
}
void Plater::arrange()
{
p->m_ui_jobs.arrange();
@ -6725,14 +6687,9 @@ unsigned int Plater::get_environment_texture_id() const
}
#endif // ENABLE_ENVIRONMENT_MAP
const Bed3D& Plater::get_bed() const
const BuildVolume& Plater::build_volume() const
{
return p->bed;
}
Bed3D& Plater::get_bed()
{
return p->bed;
return p->bed.build_volume();
}
const GLToolbar& Plater::get_view_toolbar() const

View file

@ -23,6 +23,7 @@ class wxString;
namespace Slic3r {
class BuildVolume;
class Model;
class ModelObject;
enum class ModelObjectCutAttribute : int;
@ -53,7 +54,6 @@ class GLCanvas3D;
class Mouse3DController;
class NotificationManager;
struct Camera;
class Bed3D;
class GLToolbar;
class PlaterPresetComboBox;
@ -265,8 +265,8 @@ public:
void force_print_bed_update();
// On activating the parent window.
void on_activate();
std::vector<std::string> get_extruder_colors_from_plater_config(const GCodeProcessor::Result* const result = nullptr) const;
std::vector<std::string> get_colors_for_color_print(const GCodeProcessor::Result* const result = nullptr) const;
std::vector<std::string> get_extruder_colors_from_plater_config(const GCodeProcessorResult* const result = nullptr) const;
std::vector<std::string> get_colors_for_color_print(const GCodeProcessorResult* const result = nullptr) const;
void update_menus();
void show_action_buttons(const bool is_ready_to_slice) const;
@ -282,7 +282,6 @@ public:
GLCanvas3D* canvas3D();
const GLCanvas3D * canvas3D() const;
GLCanvas3D* get_current_canvas3D();
BoundingBoxf bed_shape_bb() const;
void arrange();
void find_new_position(const ModelInstancePtrs &instances);
@ -339,8 +338,7 @@ public:
unsigned int get_environment_texture_id() const;
#endif // ENABLE_ENVIRONMENT_MAP
const Bed3D& get_bed() const;
Bed3D& get_bed();
const BuildVolume& build_volume() const;
const GLToolbar& get_view_toolbar() const;
GLToolbar& get_view_toolbar();
@ -359,7 +357,7 @@ public:
Mouse3DController& get_mouse3d_controller();
void set_bed_shape() const;
void set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false) const;
void set_bed_shape(const Pointfs& shape, const double max_print_height, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false) const;
NotificationManager * get_notification_manager();
const NotificationManager * get_notification_manager() const;