Merge branch 'master' into fs_realtime_simplify

This commit is contained in:
Filip Sykala 2021-10-19 16:51:26 +02:00
commit 7e2691241b
34 changed files with 1281 additions and 187 deletions

View File

@ -0,0 +1,106 @@
#version 110
#define INTENSITY_CORRECTION 0.6
// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31)
const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION)
#define LIGHT_TOP_SHININESS 20.0
// normalized values for (1./1.43, 0.2/1.43, 1./1.43)
const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
#define INTENSITY_AMBIENT 0.3
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
const vec3 GREEN = vec3(0.0, 0.7, 0.0);
const vec3 YELLOW = vec3(0.5, 0.7, 0.0);
const vec3 RED = vec3(0.7, 0.0, 0.0);
const vec3 WHITE = vec3(1.0, 1.0, 1.0);
const float EPSILON = 0.0001;
const float BANDS_WIDTH = 10.0;
struct PrintVolumeDetection
{
// 0 = rectangle, 1 = circle, 2 = custom, 3 = invalid
int type;
// type = 0 (rectangle):
// x = min.x, y = min.y, z = max.x, w = max.y
// type = 1 (circle):
// x = center.x, y = center.y, z = radius
vec4 xy_data;
// x = min z, y = max z
vec2 z_data;
};
struct SlopeDetection
{
bool actived;
float normal_z;
mat3 volume_world_normal_matrix;
};
uniform vec4 uniform_color;
uniform SlopeDetection slope;
uniform bool offset_depth_buffer;
#ifdef ENABLE_ENVIRONMENT_MAP
uniform sampler2D environment_tex;
uniform bool use_environment_tex;
#endif // ENABLE_ENVIRONMENT_MAP
varying vec3 clipping_planes_dots;
// x = diffuse, y = specular;
varying vec2 intensity;
uniform PrintVolumeDetection print_volume;
varying vec4 model_pos;
varying vec4 world_pos;
varying float world_normal_z;
varying vec3 eye_normal;
void main()
{
if (any(lessThan(clipping_planes_dots, ZERO)))
discard;
vec3 color = uniform_color.rgb;
float alpha = uniform_color.a;
if (slope.actived && world_normal_z < slope.normal_z - EPSILON) {
color = vec3(0.7, 0.7, 1.0);
alpha = 1.0;
}
// if the fragment is outside the print volume -> use darker color
vec3 pv_check_min = ZERO;
vec3 pv_check_max = ZERO;
if (print_volume.type == 0) {
// rectangle
pv_check_min = world_pos.xyz - vec3(print_volume.xy_data.x, print_volume.xy_data.y, print_volume.z_data.x);
pv_check_max = world_pos.xyz - vec3(print_volume.xy_data.z, print_volume.xy_data.w, print_volume.z_data.y);
}
else if (print_volume.type == 1) {
// circle
float delta_radius = print_volume.xy_data.z - distance(world_pos.xy, print_volume.xy_data.xy);
pv_check_min = vec3(delta_radius, 0.0, world_pos.z - print_volume.z_data.x);
pv_check_max = vec3(0.0, 0.0, world_pos.z - print_volume.z_data.y);
}
color = (any(lessThan(pv_check_min, ZERO)) || any(greaterThan(pv_check_max, ZERO))) ? mix(color, ZERO, 0.3333) : color;
#ifdef ENABLE_ENVIRONMENT_MAP
if (use_environment_tex)
gl_FragColor = vec4(0.45 * texture2D(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + 0.8 * color * intensity.x, alpha);
else
#endif
gl_FragColor = vec4(vec3(intensity.y) + color * intensity.x, alpha);
// In the support painting gizmo and the seam painting gizmo are painted triangles rendered over the already
// rendered object. To resolved z-fighting between previously rendered object and painted triangles, values
// inside the depth buffer are offset by small epsilon for painted triangles inside those gizmos.
gl_FragDepth = gl_FragCoord.z - (offset_depth_buffer ? EPSILON : 0.0);
}

View File

@ -0,0 +1,73 @@
#version 110
#define INTENSITY_CORRECTION 0.6
// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31)
const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION)
#define LIGHT_TOP_SHININESS 20.0
// normalized values for (1./1.43, 0.2/1.43, 1./1.43)
const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
//#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION)
//#define LIGHT_FRONT_SHININESS 5.0
#define INTENSITY_AMBIENT 0.3
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
struct SlopeDetection
{
bool actived;
float normal_z;
mat3 volume_world_normal_matrix;
};
uniform mat4 volume_world_matrix;
uniform SlopeDetection slope;
// Clipping plane, x = min z, y = max z. Used by the FFF and SLA previews to clip with a top / bottom plane.
uniform vec2 z_range;
// Clipping plane - general orientation. Used by the SLA gizmo.
uniform vec4 clipping_plane;
// x = diffuse, y = specular;
varying vec2 intensity;
varying vec3 clipping_planes_dots;
varying vec4 model_pos;
varying vec4 world_pos;
varying float world_normal_z;
varying vec3 eye_normal;
void main()
{
// First transform the normal into camera space and normalize the result.
eye_normal = normalize(gl_NormalMatrix * gl_Normal);
// Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
// Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range.
float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0);
intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
vec3 position = (gl_ModelViewMatrix * gl_Vertex).xyz;
intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS);
// Perform the same lighting calculation for the 2nd light source (no specular applied).
NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0);
intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
model_pos = gl_Vertex;
// Point in homogenous coordinates.
world_pos = volume_world_matrix * gl_Vertex;
// z component of normal vector in world coordinate used for slope shading
world_normal_z = slope.actived ? (normalize(slope.volume_world_normal_matrix * gl_Normal)).z : 0.0;
gl_Position = ftransform();
// Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded.
clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z);
}

View File

@ -720,6 +720,9 @@ void GCodeProcessor::UsedFilaments::process_caches(GCodeProcessor* processor)
void GCodeProcessor::Result::reset() { void GCodeProcessor::Result::reset() {
moves = std::vector<GCodeProcessor::MoveVertex>(); moves = std::vector<GCodeProcessor::MoveVertex>();
bed_shape = Pointfs(); bed_shape = Pointfs();
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
max_print_height = 0.0f;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
settings_ids.reset(); settings_ids.reset();
extruders_count = 0; extruders_count = 0;
extruder_colors = std::vector<std::string>(); extruder_colors = std::vector<std::string>();
@ -734,6 +737,9 @@ void GCodeProcessor::Result::reset() {
moves.clear(); moves.clear();
lines_ends.clear(); lines_ends.clear();
bed_shape = Pointfs(); bed_shape = Pointfs();
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
max_print_height = 0.0f;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
settings_ids.reset(); settings_ids.reset();
extruders_count = 0; extruders_count = 0;
extruder_colors = std::vector<std::string>(); extruder_colors = std::vector<std::string>();
@ -883,6 +889,10 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
const ConfigOptionFloatOrPercent* first_layer_height = config.option<ConfigOptionFloatOrPercent>("first_layer_height"); const ConfigOptionFloatOrPercent* first_layer_height = config.option<ConfigOptionFloatOrPercent>("first_layer_height");
if (first_layer_height != nullptr) if (first_layer_height != nullptr)
m_first_layer_height = std::abs(first_layer_height->value); m_first_layer_height = std::abs(first_layer_height->value);
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
m_result.max_print_height = config.max_print_height;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
} }
void GCodeProcessor::apply_config(const DynamicPrintConfig& config) void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
@ -1112,6 +1122,12 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
const ConfigOptionFloatOrPercent* first_layer_height = config.option<ConfigOptionFloatOrPercent>("first_layer_height"); const ConfigOptionFloatOrPercent* first_layer_height = config.option<ConfigOptionFloatOrPercent>("first_layer_height");
if (first_layer_height != nullptr) if (first_layer_height != nullptr)
m_first_layer_height = std::abs(first_layer_height->value); m_first_layer_height = std::abs(first_layer_height->value);
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
const ConfigOptionFloat* max_print_height = config.option<ConfigOptionFloat>("max_print_height");
if (max_print_height != nullptr)
m_result.max_print_height = max_print_height->value;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
} }
void GCodeProcessor::enable_stealth_time_estimator(bool enabled) void GCodeProcessor::enable_stealth_time_estimator(bool enabled)

View File

@ -351,6 +351,9 @@ namespace Slic3r {
// Positions of ends of lines of the final G-code this->filename after TimeProcessor::post_process() finalizes the G-code. // Positions of ends of lines of the final G-code this->filename after TimeProcessor::post_process() finalizes the G-code.
std::vector<size_t> lines_ends; std::vector<size_t> lines_ends;
Pointfs bed_shape; Pointfs bed_shape;
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
float max_print_height;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
SettingsIds settings_ids; SettingsIds settings_ids;
size_t extruders_count; size_t extruders_count;
std::vector<std::string> extruder_colors; std::vector<std::string> extruder_colors;

View File

@ -311,6 +311,15 @@ bool directions_parallel(double angle1, double angle2, double max_diff)
return diff < max_diff || fabs(diff - PI) < max_diff; return diff < max_diff || fabs(diff - PI) < max_diff;
} }
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
bool directions_perpendicular(double angle1, double angle2, double max_diff)
{
double diff = fabs(angle1 - angle2);
max_diff += EPSILON;
return fabs(diff - 0.5 * PI) < max_diff || fabs(diff - 1.5 * PI) < max_diff;
}
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
template<class T> template<class T>
bool contains(const std::vector<T> &vector, const Point &point) bool contains(const std::vector<T> &vector, const Point &point)
{ {

View File

@ -84,6 +84,32 @@ static inline bool is_ccw(const Polygon &poly)
return o == ORIENTATION_CCW; return o == ORIENTATION_CCW;
} }
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
// returns true if the given polygons are identical
static inline bool are_approx(const Polygon& lhs, const Polygon& rhs)
{
if (lhs.points.size() != rhs.points.size())
return false;
size_t rhs_id = 0;
while (rhs_id < rhs.points.size()) {
if (rhs.points[rhs_id].isApprox(lhs.points.front()))
break;
++rhs_id;
}
if (rhs_id == rhs.points.size())
return false;
for (size_t i = 0; i < lhs.points.size(); ++i) {
if (!lhs.points[i].isApprox(rhs.points[(i + rhs_id) % lhs.points.size()]))
return false;
}
return true;
}
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
inline bool ray_ray_intersection(const Vec2d &p1, const Vec2d &v1, const Vec2d &p2, const Vec2d &v2, Vec2d &res) inline bool ray_ray_intersection(const Vec2d &p1, const Vec2d &v1, const Vec2d &p2, const Vec2d &v2, Vec2d &res)
{ {
double denom = v1(0) * v2(1) - v2(0) * v1(1); double denom = v1(0) * v2(1) - v2(0) * v1(1);
@ -336,6 +362,9 @@ Polygon convex_hull(Points points);
Polygon convex_hull(const Polygons &polygons); Polygon convex_hull(const Polygons &polygons);
bool directions_parallel(double angle1, double angle2, double max_diff = 0); bool directions_parallel(double angle1, double angle2, double max_diff = 0);
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
bool directions_perpendicular(double angle1, double angle2, double max_diff = 0);
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
template<class T> bool contains(const std::vector<T> &vector, const Point &point); template<class T> bool contains(const std::vector<T> &vector, const Point &point);
template<typename T> T rad2deg(T angle) { return T(180.0) * angle / T(PI); } template<typename T> T rad2deg(T angle) { return T(180.0) * angle / T(PI); }
double rad2deg_dir(double angle); double rad2deg_dir(double angle);

View File

@ -63,6 +63,13 @@ bool Line::parallel_to(double angle) const
return Slic3r::Geometry::directions_parallel(this->direction(), angle); return Slic3r::Geometry::directions_parallel(this->direction(), angle);
} }
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
bool Line::perpendicular_to(double angle) const
{
return Slic3r::Geometry::directions_perpendicular(this->direction(), angle);
}
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
bool Line::intersection(const Line &l2, Point *intersection) const bool Line::intersection(const Line &l2, Point *intersection) const
{ {
const Line &l1 = *this; const Line &l1 = *this;

View File

@ -105,6 +105,10 @@ public:
double perp_distance_to(const Point &point) const; double perp_distance_to(const Point &point) const;
bool parallel_to(double angle) const; bool parallel_to(double angle) const;
bool parallel_to(const Line &line) const { return this->parallel_to(line.direction()); } bool parallel_to(const Line &line) const { return this->parallel_to(line.direction()); }
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
bool perpendicular_to(double angle) const;
bool perpendicular_to(const Line& line) const { return this->perpendicular_to(line.direction()); }
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
double atan2_() const { return atan2(this->b(1) - this->a(1), this->b(0) - this->a(0)); } double atan2_() const { return atan2(this->b(1) - this->a(1), this->b(0) - this->a(0)); }
double orientation() const; double orientation() const;
double direction() const; double direction() const;

View File

@ -26,6 +26,35 @@
namespace Slic3r { namespace Slic3r {
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const Polygon& obj_hull_2d, double obj_min_z, double obj_max_z)
{
if (!Geometry::intersects(printbed_shape, obj_hull_2d))
return ModelInstancePVS_Fully_Outside;
bool contained_xy = true;
for (const Point& p : obj_hull_2d) {
if (!printbed_shape.contains(p)) {
contained_xy = false;
break;
}
}
const bool contained_z = -1e10 < obj_min_z && obj_max_z < print_volume_height;
return (contained_xy && contained_z) ? ModelInstancePVS_Inside : ModelInstancePVS_Partly_Outside;
}
ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const BoundingBoxf3& box)
{
const Polygon box_hull_2d({
{ scale_(box.min.x()), scale_(box.min.y()) },
{ scale_(box.max.x()), scale_(box.min.y()) },
{ scale_(box.max.x()), scale_(box.max.y()) },
{ scale_(box.min.x()), scale_(box.max.y()) }
});
return printbed_collision_state(printbed_shape, print_volume_height, box_hull_2d, box.min.z(), box.max.z());
}
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
Model& Model::assign_copy(const Model &rhs) Model& Model::assign_copy(const Model &rhs)
{ {
this->copy_id(rhs); this->copy_id(rhs);
@ -330,6 +359,15 @@ BoundingBoxf3 Model::bounding_box() const
return bb; return bb;
} }
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
unsigned int Model::update_print_volume_state(const Polygon& printbed_shape, double print_volume_height)
{
unsigned int num_printable = 0;
for (ModelObject* model_object : this->objects)
num_printable += model_object->check_instances_print_volume_state(printbed_shape, print_volume_height);
return num_printable;
}
#else
unsigned int Model::update_print_volume_state(const BoundingBoxf3 &print_volume) unsigned int Model::update_print_volume_state(const BoundingBoxf3 &print_volume)
{ {
unsigned int num_printable = 0; unsigned int num_printable = 0;
@ -337,6 +375,7 @@ unsigned int Model::update_print_volume_state(const BoundingBoxf3 &print_volume)
num_printable += model_object->check_instances_print_volume_state(print_volume); num_printable += model_object->check_instances_print_volume_state(print_volume);
return num_printable; return num_printable;
} }
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
bool Model::center_instances_around_point(const Vec2d &point) bool Model::center_instances_around_point(const Vec2d &point)
{ {
@ -1529,6 +1568,38 @@ double ModelObject::get_instance_max_z(size_t instance_idx) const
return max_z + inst->get_offset(Z); return max_z + inst->get_offset(Z);
} }
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
unsigned int ModelObject::check_instances_print_volume_state(const Polygon& printbed_shape, double print_volume_height)
{
unsigned int num_printable = 0;
enum {
INSIDE = 1,
OUTSIDE = 2
};
for (ModelInstance* model_instance : this->instances) {
unsigned int inside_outside = 0;
for (const ModelVolume* vol : this->volumes)
if (vol->is_model_part()) {
const Transform3d matrix = model_instance->get_matrix() * vol->get_matrix();
const BoundingBoxf3 bb = vol->mesh().transformed_bounding_box(matrix, 0.0);
const Polygon volume_hull_2d = its_convex_hull_2d_above(vol->mesh().its, matrix.cast<float>(), 0.0f);
ModelInstanceEPrintVolumeState state = printbed_collision_state(printbed_shape, print_volume_height, volume_hull_2d, bb.min.z(), bb.max.z());
if (state == ModelInstancePVS_Inside)
inside_outside |= INSIDE;
else if (state == ModelInstancePVS_Fully_Outside)
inside_outside |= OUTSIDE;
else
inside_outside |= INSIDE | OUTSIDE;
}
model_instance->print_volume_state =
(inside_outside == (INSIDE | OUTSIDE)) ? ModelInstancePVS_Partly_Outside :
(inside_outside == INSIDE) ? ModelInstancePVS_Inside : ModelInstancePVS_Fully_Outside;
if (inside_outside == INSIDE)
++num_printable;
}
return num_printable;
}
#else
unsigned int ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume) unsigned int ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume)
{ {
unsigned int num_printable = 0; unsigned int num_printable = 0;
@ -1556,6 +1627,7 @@ unsigned int ModelObject::check_instances_print_volume_state(const BoundingBoxf3
} }
return num_printable; return num_printable;
} }
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
void ModelObject::print_info() const void ModelObject::print_info() const
{ {

View File

@ -367,7 +367,11 @@ public:
double get_instance_max_z(size_t instance_idx) const; double get_instance_max_z(size_t instance_idx) const;
// Called by Print::validate() from the UI thread. // Called by Print::validate() from the UI thread.
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
unsigned int check_instances_print_volume_state(const Polygon& printbed_shape, double print_volume_height);
#else
unsigned int check_instances_print_volume_state(const BoundingBoxf3& print_volume); unsigned int check_instances_print_volume_state(const BoundingBoxf3& print_volume);
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
// Print object statistics to console. // Print object statistics to console.
void print_info() const; void print_info() const;
@ -904,6 +908,14 @@ enum ModelInstanceEPrintVolumeState : unsigned char
ModelInstanceNum_BedStates ModelInstanceNum_BedStates
}; };
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
// return the state of the given object's volume (extrusion along z of obj_hull_2d from obj_min_z to obj_max_z)
// with respect to the given print volume (extrusion along z of printbed_shape from zero to print_volume_height)
ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const Polygon& obj_hull_2d, double obj_min_z, double obj_max_z);
// return the state of the given box
// with respect to the given print volume (extrusion along z of printbed_shape from zero to print_volume_height)
ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const BoundingBoxf3& box);
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
// A single instance of a ModelObject. // A single instance of a ModelObject.
// Knows the affine transformation of an object. // Knows the affine transformation of an object.
@ -1109,7 +1121,11 @@ public:
BoundingBoxf3 bounding_box() const; BoundingBoxf3 bounding_box() const;
// Set the print_volume_state of PrintObject::instances, // Set the print_volume_state of PrintObject::instances,
// return total number of printable objects. // return total number of printable objects.
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
unsigned int update_print_volume_state(const Polygon& printbed_shape, double print_volume_height);
#else
unsigned int update_print_volume_state(const BoundingBoxf3 &print_volume); unsigned int update_print_volume_state(const BoundingBoxf3 &print_volume);
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
// Returns true if any ModelObject was modified. // Returns true if any ModelObject was modified.
bool center_instances_around_point(const Vec2d &point); bool center_instances_around_point(const Vec2d &point);
void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); } void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); }

View File

@ -1703,15 +1703,18 @@ static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupport
// Don't want to print a layer below the first layer height as it may not stick well. // Don't want to print a layer below the first layer height as it may not stick well.
//FIXME there may be a need for a single layer support, then one may decide to print it either as a bottom contact or a top contact //FIXME there may be a need for a single layer support, then one may decide to print it either as a bottom contact or a top contact
// and it may actually make sense to do it with a thinner layer than the first layer height. // and it may actually make sense to do it with a thinner layer than the first layer height.
const coordf_t min_print_z = slicing_params.raft_layers() > 1 ? slicing_params.raft_interface_top_z + support_layer_height_min + EPSILON : slicing_params.first_print_layer_height - EPSILON; if (print_z < slicing_params.first_print_layer_height - EPSILON) {
if (print_z < min_print_z) {
// This contact layer is below the first layer height, therefore not printable. Don't support this surface. // This contact layer is below the first layer height, therefore not printable. Don't support this surface.
return std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupportMaterial::MyLayer*>(nullptr, nullptr); return std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupportMaterial::MyLayer*>(nullptr, nullptr);
} else if (print_z < slicing_params.first_print_layer_height + EPSILON) { }
// Align the layer with the 1st layer height. const bool has_raft = slicing_params.raft_layers() > 1;
print_z = slicing_params.first_print_layer_height; const coordf_t min_print_z = has_raft ? slicing_params.raft_contact_top_z : slicing_params.first_print_layer_height;
bottom_z = 0; if (print_z < min_print_z + support_layer_height_min) {
height = slicing_params.first_print_layer_height; // Align the layer with the 1st layer height or the raft contact layer.
// With raft active, any contact layer below the raft_contact_top_z will be brought to raft_contact_top_z to extend the raft area.
print_z = min_print_z;
bottom_z = has_raft ? slicing_params.raft_interface_top_z : 0;
height = has_raft ? slicing_params.contact_raft_layer_height : min_print_z;
} else { } else {
// Don't know the height of the top contact layer yet. The top contact layer is printed with a normal flow and // Don't know the height of the top contact layer yet. The top contact layer is printed with a normal flow and
// its height will be set adaptively later on. // its height will be set adaptively later on.
@ -1727,9 +1730,9 @@ static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupport
coordf_t bridging_print_z = layer.print_z - bridging_height - slicing_params.gap_support_object; coordf_t bridging_print_z = layer.print_z - bridging_height - slicing_params.gap_support_object;
if (bridging_print_z >= min_print_z) { if (bridging_print_z >= min_print_z) {
// Not below the first layer height means this layer is printable. // Not below the first layer height means this layer is printable.
if (print_z < slicing_params.first_print_layer_height + EPSILON) { if (print_z < min_print_z + support_layer_height_min) {
// Align the layer with the 1st layer height. // Align the layer with the 1st layer height or the raft contact layer.
bridging_print_z = slicing_params.first_print_layer_height; bridging_print_z = min_print_z;
} }
if (bridging_print_z < print_z - EPSILON) { if (bridging_print_z < print_z - EPSILON) {
// Allocate the new layer. // Allocate the new layer.
@ -3108,7 +3111,7 @@ std::pair<PrintObjectSupportMaterial::MyLayersPtr, PrintObjectSupportMaterial::M
intermediate_layer.polygons); intermediate_layer.polygons);
if (! bottom.empty()) { if (! bottom.empty()) {
//FIXME Remove non-printable tiny islands, let them be printed using the base support. //FIXME Remove non-printable tiny islands, let them be printed using the base support.
//bottom = offset(offset_ex(std::move(bottom), - minimum_island_radius), minimum_island_radius); //bottom = opening(std::move(bottom), minimum_island_radius);
if (! bottom.empty()) { if (! bottom.empty()) {
MyLayer &layer_new = layer_allocate(layer_storage, layer_storage_mutex, type); MyLayer &layer_new = layer_allocate(layer_storage, layer_storage_mutex, type);
layer_new.polygons = std::move(bottom); layer_new.polygons = std::move(bottom);

View File

@ -81,5 +81,9 @@
// Enable rendering modifiers and similar objects always as transparent // Enable rendering modifiers and similar objects always as transparent
#define ENABLE_MODIFIERS_ALWAYS_TRANSPARENT (1 && ENABLE_2_4_0_ALPHA4) #define ENABLE_MODIFIERS_ALWAYS_TRANSPARENT (1 && ENABLE_2_4_0_ALPHA4)
// Enable the fix for the detection of the out of bed state for sinking objects
// and detection of out of bed using the bed perimeter
#define ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS (1 && ENABLE_2_4_0_ALPHA4)
#endif // _prusaslicer_technologies_h_ #endif // _prusaslicer_technologies_h_

View File

@ -435,6 +435,31 @@ BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d &trafo) c
return bbox; return bbox;
} }
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d& trafo, double world_min_z) const
{
BoundingBoxf3 bbox;
const Transform3f ftrafo = trafo.cast<float>();
for (const stl_triangle_vertex_indices& tri : its.indices) {
const Vec3f pts[3] = { ftrafo * its.vertices[tri(0)], ftrafo * its.vertices[tri(1)], ftrafo * its.vertices[tri(2)] };
int iprev = 2;
for (int iedge = 0; iedge < 3; ++iedge) {
const Vec3f& p1 = pts[iprev];
const Vec3f& p2 = pts[iedge];
if ((p1.z() < world_min_z && p2.z() > world_min_z) || (p2.z() < world_min_z && p1.z() > world_min_z)) {
// Edge crosses the z plane. Calculate intersection point with the plane.
const float t = (world_min_z - p1.z()) / (p2.z() - p1.z());
bbox.merge(Vec3f(p1.x() + (p2.x() - p1.x()) * t, p1.y() + (p2.y() - p1.y()) * t, world_min_z).cast<double>());
}
if (p2.z() >= world_min_z)
bbox.merge(p2.cast<double>());
iprev = iedge;
}
}
return bbox;
}
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
TriangleMesh TriangleMesh::convex_hull_3d() const TriangleMesh TriangleMesh::convex_hull_3d() const
{ {
// The qhull call: // The qhull call:

View File

@ -125,6 +125,10 @@ public:
BoundingBoxf3 bounding_box() const; BoundingBoxf3 bounding_box() const;
// Returns the bbox of this TriangleMesh transformed by the given transformation // Returns the bbox of this TriangleMesh transformed by the given transformation
BoundingBoxf3 transformed_bounding_box(const Transform3d &trafo) const; BoundingBoxf3 transformed_bounding_box(const Transform3d &trafo) const;
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
// Variant returning the bbox of the part of this TriangleMesh above the given world_min_z
BoundingBoxf3 transformed_bounding_box(const Transform3d& trafo, double world_min_z) const;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
// Return the size of the mesh in coordinates. // Return the size of the mesh in coordinates.
Vec3d size() const { return m_stats.size.cast<double>(); } Vec3d size() const { return m_stats.size.cast<double>(); }
/// Return the center of the related bounding box. /// Return the center of the related bounding box.

View File

@ -7,11 +7,10 @@
#include "libslic3r/BoundingBox.hpp" #include "libslic3r/BoundingBox.hpp"
#include "libslic3r/Geometry.hpp" #include "libslic3r/Geometry.hpp"
#include "libslic3r/Tesselate.hpp" #include "libslic3r/Tesselate.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "GUI_App.hpp" #include "GUI_App.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "GLCanvas3D.hpp" #include "GLCanvas3D.hpp"
#include "3DScene.hpp"
#include <GL/glew.h> #include <GL/glew.h>
@ -154,7 +153,11 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c
std::string model; std::string model;
std::string texture; std::string texture;
if (force_as_custom) if (force_as_custom)
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
type = EType::Custom;
#else
type = Custom; type = Custom;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
else { else {
auto [new_type, system_model, system_texture] = detect_type(shape); auto [new_type, system_model, system_texture] = detect_type(shape);
type = new_type; type = new_type;
@ -174,7 +177,12 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c
model_filename.clear(); 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) if (m_shape == shape && m_type == type && m_texture_filename == texture_filename && m_model_filename == model_filename)
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
// No change, no need to update the UI. // No change, no need to update the UI.
return false; return false;
@ -182,6 +190,9 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c
m_texture_filename = texture_filename; m_texture_filename = texture_filename;
m_model_filename = model_filename; m_model_filename = model_filename;
m_type = type; m_type = type;
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
m_shape_type = shape_type;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
calc_bounding_boxes(); calc_bounding_boxes();
@ -229,6 +240,84 @@ void Bed3D::render_for_picking(GLCanvas3D& canvas, bool bottom, float scale_fact
render_internal(canvas, bottom, scale_factor, false, false, true); 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 = BoundingBoxf(shape).center();
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, void Bed3D::render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
bool show_axes, bool show_texture, bool picking) bool show_axes, bool show_texture, bool picking)
{ {
@ -244,9 +333,15 @@ void Bed3D::render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
switch (m_type) switch (m_type)
{ {
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
case EType::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; } case System: { render_system(canvas, bottom, show_texture); break; }
default: default:
case Custom: { render_custom(canvas, bottom, show_texture, picking); break; } case Custom: { render_custom(canvas, bottom, show_texture, picking); break; }
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
} }
glsafe(::glDisable(GL_DEPTH_TEST)); glsafe(::glDisable(GL_DEPTH_TEST));
@ -320,7 +415,11 @@ 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 model_filename = PresetUtils::system_printer_bed_model(*curr);
std::string texture_filename = PresetUtils::system_printer_bed_texture(*curr); std::string texture_filename = PresetUtils::system_printer_bed_texture(*curr);
if (!model_filename.empty() && !texture_filename.empty()) 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 }; return { System, model_filename, texture_filename };
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
} }
} }
@ -328,7 +427,11 @@ 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, "", "" }; return { Custom, "", "" };
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
} }
void Bed3D::render_axes() const void Bed3D::render_axes() const

View File

@ -62,15 +62,36 @@ class Bed3D
}; };
public: public:
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
enum class EType : unsigned char
{
System,
Custom
};
enum class EShapeType : unsigned char
{
Rectangle,
Circle,
Custom,
Invalid
};
#else
enum EType : unsigned char enum EType : unsigned char
{ {
System, System,
Custom, Custom,
Num_Types Num_Types
}; };
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
private: private:
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
EType m_type{ EType::Custom };
EShapeType m_shape_type{ EShapeType::Invalid };
#else
EType m_type{ Custom }; EType m_type{ Custom };
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
Pointfs m_shape; Pointfs m_shape;
std::string m_texture_filename; std::string m_texture_filename;
std::string m_model_filename; std::string m_model_filename;
@ -94,16 +115,18 @@ public:
~Bed3D() { reset(); } ~Bed3D() { reset(); }
EType get_type() const { return m_type; } 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; } bool is_custom() const { return m_type == Custom; }
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
const Pointfs& get_shape() const { return m_shape; } const Pointfs& get_shape() const { return m_shape; }
// Return true if the bed shape changed, so the calee will update the UI. // 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); bool set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false);
const BoundingBoxf3& get_bounding_box(bool extended) const { const BoundingBoxf3& get_bounding_box(bool extended) const { return extended ? m_extended_bounding_box : m_bounding_box; }
return extended ? m_extended_bounding_box : m_bounding_box;
}
bool contains(const Point& point) const; bool contains(const Point& point) const;
Point point_projection(const Point& point) const; Point point_projection(const Point& point) const;
@ -113,6 +136,13 @@ public:
void render_for_picking(GLCanvas3D& canvas, bool bottom, float scale_factor); 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: private:
void calc_bounding_boxes() const; void calc_bounding_boxes() const;
void calc_triangles(const ExPolygon& poly); void calc_triangles(const ExPolygon& poly);

View File

@ -10,6 +10,10 @@
#include "GLShader.hpp" #include "GLShader.hpp"
#include "GUI_App.hpp" #include "GUI_App.hpp"
#include "Plater.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/ExtrusionEntity.hpp" #include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/ExtrusionEntityCollection.hpp" #include "libslic3r/ExtrusionEntityCollection.hpp"
@ -17,7 +21,6 @@
#include "libslic3r/Print.hpp" #include "libslic3r/Print.hpp"
#include "libslic3r/SLAPrint.hpp" #include "libslic3r/SLAPrint.hpp"
#include "libslic3r/Slicing.hpp" #include "libslic3r/Slicing.hpp"
#include "slic3r/GUI/BitmapCache.hpp"
#include "libslic3r/Format/STL.hpp" #include "libslic3r/Format/STL.hpp"
#include "libslic3r/Utils.hpp" #include "libslic3r/Utils.hpp"
#include "libslic3r/AppConfig.hpp" #include "libslic3r/AppConfig.hpp"
@ -37,6 +40,12 @@
#include <Eigen/Dense> #include <Eigen/Dense>
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
#include <libqhullcpp/Qhull.h>
#include <libqhullcpp/QhullFacetList.h>
#include <libqhullcpp/QhullVertexSet.h>
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
#ifdef HAS_GLSAFE #ifdef HAS_GLSAFE
void glAssertRecentCallImpl(const char* file_name, unsigned int line, const char* function_name) void glAssertRecentCallImpl(const char* file_name, unsigned int line, const char* function_name)
{ {
@ -261,6 +270,12 @@ void GLIndexedVertexArray::render(
const std::pair<size_t, size_t>& tverts_range, const std::pair<size_t, size_t>& tverts_range,
const std::pair<size_t, size_t>& qverts_range) const const std::pair<size_t, size_t>& qverts_range) const
{ {
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
// this method has been called before calling finalize() ?
if (this->vertices_and_normals_interleaved_VBO_id == 0 && !this->vertices_and_normals_interleaved.empty())
return;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
assert(this->vertices_and_normals_interleaved_VBO_id != 0); assert(this->vertices_and_normals_interleaved_VBO_id != 0);
assert(this->triangle_indices_VBO_id != 0 || this->quad_indices_VBO_id != 0); assert(this->triangle_indices_VBO_id != 0 || this->quad_indices_VBO_id != 0);
@ -319,19 +334,7 @@ void GLVolume::SinkingContours::update()
MeshSlicingParams slicing_params; MeshSlicingParams slicing_params;
slicing_params.trafo = m_parent.world_matrix(); slicing_params.trafo = m_parent.world_matrix();
Polygons polygons = union_(slice_mesh(mesh.its, 0.0f, slicing_params)); Polygons polygons = union_(slice_mesh(mesh.its, 0.0f, slicing_params));
for (Polygon& polygon : polygons) { for (ExPolygon &expoly : diff_ex(expand(polygons, float(scale_(HalfWidth))), shrink(polygons, float(scale_(HalfWidth))))) {
if (polygon.is_clockwise())
polygon.reverse();
Polygons outer_polys = offset(polygon, float(scale_(HalfWidth)));
assert(outer_polys.size() == 1);
if (outer_polys.empty())
// no outer contour, skip
continue;
ExPolygon expoly(std::move(outer_polys.front()));
expoly.holes = offset(polygon, -float(scale_(HalfWidth)));
polygons_reverse(expoly.holes);
GUI::GLModel::InitializationData::Entity entity; GUI::GLModel::InitializationData::Entity entity;
entity.type = GUI::GLModel::PrimitiveType::Triangles; entity.type = GUI::GLModel::PrimitiveType::Triangles;
const std::vector<Vec3d> triangulation = triangulate_expolygon_3d(expoly); const std::vector<Vec3d> triangulation = triangulate_expolygon_3d(expoly);
@ -529,6 +532,23 @@ BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d &
bounding_box().transformed(trafo); bounding_box().transformed(trafo);
} }
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
BoundingBoxf3 GLVolume::transformed_non_sinking_bounding_box(const Transform3d& trafo) const
{
return GUI::wxGetApp().plater()->model().objects[object_idx()]->volumes[volume_idx()]->mesh().transformed_bounding_box(trafo, 0.0);
}
const BoundingBoxf3& GLVolume::transformed_non_sinking_bounding_box() const
{
if (!m_transformed_non_sinking_bounding_box.has_value()) {
std::optional<BoundingBoxf3>* trans_box = const_cast<std::optional<BoundingBoxf3>*>(&m_transformed_non_sinking_bounding_box);
const Transform3d& trafo = world_matrix();
*trans_box = transformed_non_sinking_bounding_box(trafo);
}
return *m_transformed_non_sinking_bounding_box;
}
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
void GLVolume::set_range(double min_z, double max_z) void GLVolume::set_range(double min_z, double max_z)
{ {
this->qverts_range.first = 0; this->qverts_range.first = 0;
@ -603,6 +623,106 @@ void GLVolume::render_sinking_contours()
m_sinking_contours.render(); m_sinking_contours.render();
} }
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
void GLVolume::calc_convex_hull_3d()
{
if (this->indexed_vertex_array.vertices_and_normals_interleaved.empty())
return;
TriangleMesh mesh;
for (size_t i = 0; i < this->indexed_vertex_array.vertices_and_normals_interleaved.size(); i += 6) {
const size_t v_id = 3 + i;
mesh.its.vertices.push_back({ this->indexed_vertex_array.vertices_and_normals_interleaved[v_id + 0],
this->indexed_vertex_array.vertices_and_normals_interleaved[v_id + 1],
this->indexed_vertex_array.vertices_and_normals_interleaved[v_id + 2]
});
}
const std::vector<Vec3f>& vertices = mesh.its.vertices;
// The qhull call:
orgQhull::Qhull qhull;
qhull.disableOutputStream(); // we want qhull to be quiet
std::vector<realT> src_vertices;
try
{
#if REALfloat
qhull.runQhull("", 3, (int)vertices.size(), (const realT*)(vertices.front().data()), "Qt");
#else
src_vertices.reserve(vertices.size() * 3);
// We will now fill the vector with input points for computation:
for (const stl_vertex& v : vertices)
for (int i = 0; i < 3; ++i)
src_vertices.emplace_back(v(i));
qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt");
#endif
}
catch (...)
{
std::cout << "GLVolume::calc_convex_hull_3d() - Unable to create convex hull" << std::endl;
return ;
}
// Let's collect results:
std::vector<Vec3f> dst_vertices;
std::vector<Vec3i> dst_facets;
// Map of QHull's vertex ID to our own vertex ID (pointing to dst_vertices).
std::vector<int> map_dst_vertices;
#ifndef NDEBUG
Vec3f centroid = Vec3f::Zero();
for (const auto& pt : vertices)
centroid += pt;
centroid /= float(vertices.size());
#endif // NDEBUG
for (const orgQhull::QhullFacet& facet : qhull.facetList()) {
// Collect face vertices first, allocate unique vertices in dst_vertices based on QHull's vertex ID.
Vec3i indices;
int cnt = 0;
for (const orgQhull::QhullVertex vertex : facet.vertices()) {
const int id = vertex.id();
assert(id >= 0);
if (id >= int(map_dst_vertices.size()))
map_dst_vertices.resize(next_highest_power_of_2(size_t(id + 1)), -1);
if (int i = map_dst_vertices[id]; i == -1) {
// Allocate a new vertex.
i = int(dst_vertices.size());
map_dst_vertices[id] = i;
orgQhull::QhullPoint pt(vertex.point());
dst_vertices.emplace_back(pt[0], pt[1], pt[2]);
indices[cnt] = i;
}
else
// Reuse existing vertex.
indices[cnt] = i;
if (cnt++ == 3)
break;
}
assert(cnt == 3);
if (cnt == 3) {
// QHull sorts vertices of a face lexicographically by their IDs, not by face normals.
// Calculate face normal based on the order of vertices.
const Vec3f n = (dst_vertices[indices(1)] - dst_vertices[indices(0)]).cross(dst_vertices[indices(2)] - dst_vertices[indices(1)]);
auto* n2 = facet.getBaseT()->normal;
const auto d = n.x() * n2[0] + n.y() * n2[1] + n.z() * n2[2];
#ifndef NDEBUG
const Vec3f n3 = (dst_vertices[indices(0)] - centroid);
const auto d3 = n.dot(n3);
assert((d < 0.f) == (d3 < 0.f));
#endif // NDEBUG
// Get the face normal from QHull.
if (d < 0.f)
// Fix face orientation.
std::swap(indices[1], indices[2]);
dst_facets.emplace_back(indices);
}
}
TriangleMesh out_mesh{ std::move(dst_vertices), std::move(dst_facets) };
this->set_convex_hull(out_mesh);
}
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
std::vector<int> GLVolumeCollection::load_object( std::vector<int> GLVolumeCollection::load_object(
const ModelObject *model_object, const ModelObject *model_object,
int obj_idx, int obj_idx,
@ -761,6 +881,9 @@ int GLVolumeCollection::load_wipe_tower_preview(
volumes.emplace_back(new GLVolume(color)); volumes.emplace_back(new GLVolume(color));
GLVolume& v = *volumes.back(); GLVolume& v = *volumes.back();
v.indexed_vertex_array.load_mesh(mesh); v.indexed_vertex_array.load_mesh(mesh);
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
v.set_convex_hull(mesh.convex_hull_3d());
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
v.indexed_vertex_array.finalize_geometry(opengl_initialized); v.indexed_vertex_array.finalize_geometry(opengl_initialized);
v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0)); v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0));
v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle)); v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle));
@ -867,10 +990,17 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
shader->set_uniform("uniform_color", volume.first->render_color); shader->set_uniform("uniform_color", volume.first->render_color);
shader->set_uniform("z_range", m_z_range, 2); shader->set_uniform("z_range", m_z_range, 2);
shader->set_uniform("clipping_plane", m_clipping_plane, 4); shader->set_uniform("clipping_plane", m_clipping_plane, 4);
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
shader->set_uniform("print_volume.type", static_cast<int>(m_print_volume.type));
shader->set_uniform("print_volume.xy_data", m_print_volume.data);
shader->set_uniform("print_volume.z_data", m_print_volume.zs);
shader->set_uniform("volume_world_matrix", volume.first->world_matrix());
#else
shader->set_uniform("print_box.min", m_print_box_min, 3); shader->set_uniform("print_box.min", m_print_box_min, 3);
shader->set_uniform("print_box.max", m_print_box_max, 3); shader->set_uniform("print_box.max", m_print_box_max, 3);
shader->set_uniform("print_box.actived", volume.first->shader_outside_printer_detection_enabled); shader->set_uniform("print_box.actived", volume.first->shader_outside_printer_detection_enabled);
shader->set_uniform("print_box.volume_world_matrix", volume.first->world_matrix()); shader->set_uniform("print_box.volume_world_matrix", volume.first->world_matrix());
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
shader->set_uniform("slope.actived", m_slope.active && !volume.first->is_modifier && !volume.first->is_wipe_tower); shader->set_uniform("slope.actived", m_slope.active && !volume.first->is_modifier && !volume.first->is_wipe_tower);
shader->set_uniform("slope.volume_world_normal_matrix", static_cast<Matrix3f>(volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>())); shader->set_uniform("slope.volume_world_normal_matrix", static_cast<Matrix3f>(volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>()));
shader->set_uniform("slope.normal_z", m_slope.normal_z); shader->set_uniform("slope.normal_z", m_slope.normal_z);
@ -919,7 +1049,11 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
glsafe(::glDisable(GL_BLEND)); 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 bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state) const
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
{ {
if (config == nullptr) if (config == nullptr)
return false; return false;
@ -928,22 +1062,95 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
if (opt == nullptr) if (opt == nullptr)
return false; 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.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 = [](GLVolume& volume, ModelInstanceEPrintVolumeState& state, const Vec2d& center, double radius) {
const TriangleMesh* mesh = volume.is_sinking() ? &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);
size_t outside_count = 0;
const double sq_radius = sqr(radius);
for (const Point& p : volume_hull_2d.points) {
if (sq_radius < (unscale(p) - center).squaredNorm())
++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 < volume_hull_2d.size())
state = ModelInstancePVS_Partly_Outside;
}
};
auto check_against_convex_bed = [&bed_poly, bed_height](GLVolume& volume, ModelInstanceEPrintVolumeState& state) {
const TriangleMesh* mesh = volume.is_sinking() ? &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.transformed_non_sinking_bounding_box() : &volume.transformed_convex_hull_bounding_box();
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)); 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 }, 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()), { unscale<double>(bed_box_2D.max.x()), unscale<double>(bed_box_2D.max.y()), config->opt_float("max_print_height") });
config->opt_float("max_print_height") });
// Allow the objects to protrude below the print bed // Allow the objects to protrude below the print bed
print_volume.min(2) = -1e10; print_volume.min.z() = -1e10;
print_volume.min(0) -= BedEpsilon; print_volume.min.x() -= BedEpsilon;
print_volume.min(1) -= BedEpsilon; print_volume.min.y() -= BedEpsilon;
print_volume.max(0) += BedEpsilon; print_volume.max.x() += BedEpsilon;
print_volume.max(1) += BedEpsilon; print_volume.max.y() += BedEpsilon;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
ModelInstanceEPrintVolumeState state = ModelInstancePVS_Inside;
ModelInstanceEPrintVolumeState overall_state = ModelInstancePVS_Inside;
bool contained_min_one = false; bool contained_min_one = false;
for (GLVolume* volume : this->volumes) { 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;
if (GUI::Bed3D::is_rectangle(opt->values))
check_against_rectangular_bed(*volume, overall_state);
else {
Vec2d center;
double radius;
if (GUI::Bed3D::is_circle(opt->values, &center, &radius))
check_against_circular_bed(*volume, overall_state, center, radius);
else if (GUI::Bed3D::is_convex(opt->values))
check_against_convex_bed(*volume, overall_state);
}
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))) if (volume->is_modifier || (!volume->shader_outside_printer_detection_enabled && (volume->is_wipe_tower || volume->composite_id.volume_id < 0)))
continue; continue;
@ -956,15 +1163,16 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
contained_min_one |= contained; contained_min_one |= contained;
if (state == ModelInstancePVS_Inside && volume->is_outside) if (overall_state == ModelInstancePVS_Inside && volume->is_outside)
state = ModelInstancePVS_Fully_Outside; overall_state = ModelInstancePVS_Fully_Outside;
if (state == ModelInstancePVS_Fully_Outside && volume->is_outside && print_volume.intersects(bb)) if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && print_volume.intersects(bb))
state = ModelInstancePVS_Partly_Outside; overall_state = ModelInstancePVS_Partly_Outside;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
} }
if (out_state != nullptr) if (out_state != nullptr)
*out_state = state; *out_state = overall_state;
return contained_min_one; return contained_min_one;
} }
@ -1020,35 +1228,28 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con
std::vector<Color> colors(colors_count); std::vector<Color> colors(colors_count);
unsigned char rgb[3]; unsigned char rgb[3];
for (unsigned int i = 0; i < colors_count; ++i) for (unsigned int i = 0; i < colors_count; ++i) {
{
const std::string& txt_color = config->opt_string("extruder_colour", i); const std::string& txt_color = config->opt_string("extruder_colour", i);
if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb)) if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb))
{
colors[i].set(txt_color, rgb); colors[i].set(txt_color, rgb);
} else {
else
{
const std::string& txt_color = config->opt_string("filament_colour", i); const std::string& txt_color = config->opt_string("filament_colour", i);
if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb)) if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb))
colors[i].set(txt_color, rgb); colors[i].set(txt_color, rgb);
} }
} }
for (GLVolume* volume : volumes) for (GLVolume* volume : volumes) {
{ if (volume == nullptr || volume->is_modifier || volume->is_wipe_tower || (volume->volume_idx() < 0))
if ((volume == nullptr) || volume->is_modifier || volume->is_wipe_tower || (volume->volume_idx() < 0))
continue; continue;
int extruder_id = volume->extruder_id - 1; int extruder_id = volume->extruder_id - 1;
if ((extruder_id < 0) || ((int)colors.size() <= extruder_id)) if (extruder_id < 0 || (int)colors.size() <= extruder_id)
extruder_id = 0; extruder_id = 0;
const Color& color = colors[extruder_id]; const Color& color = colors[extruder_id];
if (!color.text.empty()) if (!color.text.empty()) {
{ for (int i = 0; i < 3; ++i) {
for (int i = 0; i < 3; ++i)
{
volume->color[i] = (float)color.rgb[i] * inv_255; volume->color[i] = (float)color.rgb[i] * inv_255;
} }
} }

View File

@ -41,7 +41,6 @@ enum ModelInstanceEPrintVolumeState : unsigned char;
// Return appropriate color based on the ModelVolume. // Return appropriate color based on the ModelVolume.
std::array<float, 4> color_from_model_volume(const ModelVolume& model_volume); std::array<float, 4> color_from_model_volume(const ModelVolume& model_volume);
// A container for interleaved arrays of 3D vertices and normals, // A container for interleaved arrays of 3D vertices and normals,
// possibly indexed by triangles and / or quads. // possibly indexed by triangles and / or quads.
class GLIndexedVertexArray { class GLIndexedVertexArray {
@ -279,6 +278,10 @@ private:
std::shared_ptr<const TriangleMesh> m_convex_hull; std::shared_ptr<const TriangleMesh> m_convex_hull;
// Bounding box of this volume, in unscaled coordinates. // Bounding box of this volume, in unscaled coordinates.
std::optional<BoundingBoxf3> m_transformed_convex_hull_bounding_box; 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 class SinkingContours
{ {
@ -469,6 +472,12 @@ public:
BoundingBoxf3 transformed_convex_hull_bounding_box(const Transform3d &trafo) const; BoundingBoxf3 transformed_convex_hull_bounding_box(const Transform3d &trafo) const;
// caching variant // caching variant
const BoundingBoxf3& transformed_convex_hull_bounding_box() const; 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 // convex hull
const TriangleMesh* convex_hull() const { return m_convex_hull.get(); } const TriangleMesh* convex_hull() const { return m_convex_hull.get(); }
@ -481,7 +490,15 @@ public:
void finalize_geometry(bool opengl_initialized) { this->indexed_vertex_array.finalize_geometry(opengl_initialized); } void finalize_geometry(bool opengl_initialized) { this->indexed_vertex_array.finalize_geometry(opengl_initialized); }
void release_geometry() { this->indexed_vertex_array.release_geometry(); } 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(); } 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_support() const;
bool is_sla_pad() const; bool is_sla_pad() const;
@ -498,6 +515,12 @@ public:
// Return an estimate of the memory held by GPU vertex buffers. // 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 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(); } 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; typedef std::vector<GLVolume*> GLVolumePtrs;
@ -514,10 +537,30 @@ public:
All All
}; };
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
struct PrintVolume
{
// see: Bed3D::EShapeType
int type{ 0 };
// data contains:
// Rectangle:
// [0] = min.x, [1] = min.y, [2] = max.x, [3] = max.y
// Circle:
// [0] = center.x, [1] = center.y, [3] = radius
std::array<float, 4> data;
// [0] = min z, [1] = max z
std::array<float, 2> zs;
};
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
private: private:
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
PrintVolume m_print_volume;
#else
// min and max vertex of the print box volume // min and max vertex of the print box volume
float m_print_box_min[3]; float m_print_box_min[3];
float m_print_box_max[3]; float m_print_box_max[3];
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
// z range for clipping in shaders // z range for clipping in shaders
float m_z_range[2]; float m_z_range[2];
@ -589,10 +632,14 @@ public:
bool empty() const { return volumes.empty(); } bool empty() const { return volumes.empty(); }
void set_range(double low, double high) { for (GLVolume *vol : this->volumes) vol->set_range(low, high); } void set_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) { 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_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; 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_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]; } 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]; }
@ -607,7 +654,11 @@ public:
// returns true if all the volumes are completely contained in the print volume // 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 // 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; bool check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state) const;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
void reset_outside_state(); void reset_outside_state();
void update_colors_by_extruder(const DynamicPrintConfig* config); void update_colors_by_extruder(const DynamicPrintConfig* config);

View File

@ -22,9 +22,25 @@ namespace GUI {
BedShape::BedShape(const ConfigOptionPoints& points) BedShape::BedShape(const ConfigOptionPoints& points)
{ {
auto polygon = Polygon::new_scale(points.values); #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 ? // 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) { if (points.size() == 4) {
auto lines = polygon.lines(); auto lines = polygon.lines();
if (lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3])) { if (lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3])) {
@ -48,8 +64,21 @@ BedShape::BedShape(const ConfigOptionPoints& points)
return; return;
} }
} }
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
// is this a circle ? // 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 ? // Analyze the array of points.Do they reside on a circle ?
auto center = polygon.bounding_box().center(); auto center = polygon.bounding_box().center();
@ -84,6 +113,7 @@ BedShape::BedShape(const ConfigOptionPoints& points)
// This is a custom bed shape, use the polygon provided. // This is a custom bed shape, use the polygon provided.
m_type = Type::Custom; m_type = Type::Custom;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
} }
static std::string get_option_label(BedShape::Parameter param) static std::string get_option_label(BedShape::Parameter param)
@ -134,6 +164,18 @@ void BedShape::append_option_line(ConfigOptionsGroupShp optgroup, Parameter para
} }
} }
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
wxString BedShape::get_name(Bed3D::EShapeType 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");
}
}
#else
wxString BedShape::get_name(Type type) wxString BedShape::get_name(Type type)
{ {
switch (type) { switch (type) {
@ -144,21 +186,34 @@ wxString BedShape::get_name(Type type)
default: return _L("Invalid"); default: return _L("Invalid");
} }
} }
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
size_t BedShape::get_type() size_t BedShape::get_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); return static_cast<size_t>(m_type == Type::Invalid ? Type::Rectangular : m_type);
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
} }
wxString BedShape::get_full_name_with_params() wxString BedShape::get_full_name_with_params()
{ {
wxString out = _L("Shape") + ": " + get_name(m_type); 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) { 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::RectSize)) + ": [" + ConfigOptionPoint(m_rectSize).serialize() + "]";
out += "\n" + _(get_option_label(Parameter::RectOrigin))+ ": [" + ConfigOptionPoint(m_rectOrigin).serialize() + "]"; out += "\n" + _(get_option_label(Parameter::RectOrigin))+ ": [" + ConfigOptionPoint(m_rectOrigin).serialize() + "]";
} }
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
else if (m_type == Bed3D::EShapeType::Circle)
#else
else if (m_type == Type::Circular) 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) + "]"; out += "\n" + _L(get_option_label(Parameter::Diameter)) + ": [" + double_to_string(m_diameter) + "]";
return out; return out;
@ -166,11 +221,19 @@ wxString BedShape::get_full_name_with_params()
void BedShape::apply_optgroup_values(ConfigOptionsGroupShp optgroup) 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) { 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_size" , new ConfigOptionPoints{ m_rectSize });
optgroup->set_value("rect_origin" , new ConfigOptionPoints{ m_rectOrigin }); optgroup->set_value("rect_origin" , new ConfigOptionPoints{ m_rectOrigin });
} }
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
else if (m_type == Bed3D::EShapeType::Circle)
#else
else if (m_type == Type::Circular) else if (m_type == Type::Circular)
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
optgroup->set_value("diameter", double_to_string(m_diameter)); optgroup->set_value("diameter", double_to_string(m_diameter));
} }
@ -222,7 +285,7 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf
m_custom_texture = custom_texture.value.empty() ? NONE : custom_texture.value; m_custom_texture = custom_texture.value.empty() ? NONE : custom_texture.value;
m_custom_model = custom_model.value.empty() ? NONE : custom_model.value; m_custom_model = custom_model.value.empty() ? NONE : custom_model.value;
auto sbsizer = new wxStaticBoxSizer(wxVERTICAL, this, _(L("Shape"))); auto sbsizer = new wxStaticBoxSizer(wxVERTICAL, this, _L("Shape"));
sbsizer->GetStaticBox()->SetFont(wxGetApp().bold_font()); sbsizer->GetStaticBox()->SetFont(wxGetApp().bold_font());
wxGetApp().UpdateDarkUI(sbsizer->GetStaticBox()); wxGetApp().UpdateDarkUI(sbsizer->GetStaticBox());
@ -232,16 +295,28 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf
sbsizer->Add(m_shape_options_book); 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)); auto optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Rectangular));
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
BedShape::append_option_line(optgroup, BedShape::Parameter::RectSize); BedShape::append_option_line(optgroup, BedShape::Parameter::RectSize);
BedShape::append_option_line(optgroup, BedShape::Parameter::RectOrigin); BedShape::append_option_line(optgroup, BedShape::Parameter::RectOrigin);
activate_options_page(optgroup); 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)); optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Circular));
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
BedShape::append_option_line(optgroup, BedShape::Parameter::Diameter); BedShape::append_option_line(optgroup, BedShape::Parameter::Diameter);
activate_options_page(optgroup); 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)); optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Custom));
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
Line line{ "", "" }; Line line{ "", "" };
line.full_width = 1; line.full_width = 1;
@ -265,10 +340,7 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf
wxPanel* texture_panel = init_texture_panel(); wxPanel* texture_panel = init_texture_panel();
wxPanel* model_panel = init_model_panel(); wxPanel* model_panel = init_model_panel();
Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, ([this](wxCommandEvent& e) Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, ([this](wxCommandEvent& e) { update_shape(); }));
{
update_shape();
}));
// right pane with preview canvas // right pane with preview canvas
m_canvas = new Bed_2D(this); m_canvas = new Bed_2D(this);
@ -295,7 +367,7 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf
ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(const wxString& title) ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(const wxString& title)
{ {
wxPanel* panel = new wxPanel(m_shape_options_book); wxPanel* panel = new wxPanel(m_shape_options_book);
ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Settings"))); ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(panel, _L("Settings"));
optgroup->label_width = 10; optgroup->label_width = 10;
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
@ -319,7 +391,7 @@ wxPanel* BedShapePanel::init_texture_panel()
{ {
wxPanel* panel = new wxPanel(this); wxPanel* panel = new wxPanel(this);
wxGetApp().UpdateDarkUI(panel, true); wxGetApp().UpdateDarkUI(panel, true);
ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Texture"))); ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(panel, _L("Texture"));
optgroup->label_width = 10; optgroup->label_width = 10;
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
@ -329,7 +401,7 @@ wxPanel* BedShapePanel::init_texture_panel()
Line line{ "", "" }; Line line{ "", "" };
line.full_width = 1; line.full_width = 1;
line.widget = [this](wxWindow* parent) { line.widget = [this](wxWindow* parent) {
wxButton* load_btn = new wxButton(parent, wxID_ANY, _(L("Load..."))); wxButton* load_btn = new wxButton(parent, wxID_ANY, _L("Load..."));
wxSizer* load_sizer = new wxBoxSizer(wxHORIZONTAL); wxSizer* load_sizer = new wxBoxSizer(wxHORIZONTAL);
load_sizer->Add(load_btn, 1, wxEXPAND); load_sizer->Add(load_btn, 1, wxEXPAND);
@ -338,7 +410,7 @@ wxPanel* BedShapePanel::init_texture_panel()
wxSizer* filename_sizer = new wxBoxSizer(wxHORIZONTAL); wxSizer* filename_sizer = new wxBoxSizer(wxHORIZONTAL);
filename_sizer->Add(filename_lbl, 1, wxEXPAND); filename_sizer->Add(filename_lbl, 1, wxEXPAND);
wxButton* remove_btn = new wxButton(parent, wxID_ANY, _(L("Remove"))); wxButton* remove_btn = new wxButton(parent, wxID_ANY, _L("Remove"));
wxSizer* remove_sizer = new wxBoxSizer(wxHORIZONTAL); wxSizer* remove_sizer = new wxBoxSizer(wxHORIZONTAL);
remove_sizer->Add(remove_btn, 1, wxEXPAND); remove_sizer->Add(remove_btn, 1, wxEXPAND);
@ -347,31 +419,23 @@ wxPanel* BedShapePanel::init_texture_panel()
sizer->Add(load_sizer, 1, wxEXPAND); sizer->Add(load_sizer, 1, wxEXPAND);
sizer->Add(remove_sizer, 1, wxEXPAND | wxTOP, 2); sizer->Add(remove_sizer, 1, wxEXPAND | wxTOP, 2);
load_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) load_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) { load_texture(); }));
{ remove_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) {
load_texture();
}));
remove_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
{
m_custom_texture = NONE; m_custom_texture = NONE;
update_shape(); update_shape();
})); }));
filename_lbl->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e) filename_lbl->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e) {
{
e.SetText(_(boost::filesystem::path(m_custom_texture).filename().string())); e.SetText(_(boost::filesystem::path(m_custom_texture).filename().string()));
wxStaticText* lbl = dynamic_cast<wxStaticText*>(e.GetEventObject()); wxStaticText* lbl = dynamic_cast<wxStaticText*>(e.GetEventObject());
if (lbl != nullptr) if (lbl != nullptr) {
{
bool exists = (m_custom_texture == NONE) || boost::filesystem::exists(m_custom_texture); bool exists = (m_custom_texture == NONE) || boost::filesystem::exists(m_custom_texture);
lbl->SetForegroundColour(exists ? /*wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)*/wxGetApp().get_label_clr_default() : wxColor(*wxRED)); lbl->SetForegroundColour(exists ? /*wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)*/wxGetApp().get_label_clr_default() : wxColor(*wxRED));
wxString tooltip_text = ""; wxString tooltip_text = "";
if (m_custom_texture != NONE) if (m_custom_texture != NONE) {
{
if (!exists) if (!exists)
tooltip_text += _(L("Not found:")) + " "; tooltip_text += _L("Not found:") + " ";
tooltip_text += _(m_custom_texture); tooltip_text += _(m_custom_texture);
} }
@ -382,10 +446,7 @@ wxPanel* BedShapePanel::init_texture_panel()
} }
})); }));
remove_btn->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e) remove_btn->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e) { e.Enable(m_custom_texture != NONE); }));
{
e.Enable(m_custom_texture != NONE);
}));
return sizer; return sizer;
}; };
@ -401,7 +462,7 @@ wxPanel* BedShapePanel::init_model_panel()
{ {
wxPanel* panel = new wxPanel(this); wxPanel* panel = new wxPanel(this);
wxGetApp().UpdateDarkUI(panel, true); wxGetApp().UpdateDarkUI(panel, true);
ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Model"))); ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(panel, _L("Model"));
optgroup->label_width = 10; optgroup->label_width = 10;
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
@ -411,7 +472,7 @@ wxPanel* BedShapePanel::init_model_panel()
Line line{ "", "" }; Line line{ "", "" };
line.full_width = 1; line.full_width = 1;
line.widget = [this](wxWindow* parent) { line.widget = [this](wxWindow* parent) {
wxButton* load_btn = new wxButton(parent, wxID_ANY, _(L("Load..."))); wxButton* load_btn = new wxButton(parent, wxID_ANY, _L("Load..."));
wxSizer* load_sizer = new wxBoxSizer(wxHORIZONTAL); wxSizer* load_sizer = new wxBoxSizer(wxHORIZONTAL);
load_sizer->Add(load_btn, 1, wxEXPAND); load_sizer->Add(load_btn, 1, wxEXPAND);
@ -419,7 +480,7 @@ wxPanel* BedShapePanel::init_model_panel()
wxSizer* filename_sizer = new wxBoxSizer(wxHORIZONTAL); wxSizer* filename_sizer = new wxBoxSizer(wxHORIZONTAL);
filename_sizer->Add(filename_lbl, 1, wxEXPAND); filename_sizer->Add(filename_lbl, 1, wxEXPAND);
wxButton* remove_btn = new wxButton(parent, wxID_ANY, _(L("Remove"))); wxButton* remove_btn = new wxButton(parent, wxID_ANY, _L("Remove"));
wxSizer* remove_sizer = new wxBoxSizer(wxHORIZONTAL); wxSizer* remove_sizer = new wxBoxSizer(wxHORIZONTAL);
remove_sizer->Add(remove_btn, 1, wxEXPAND); remove_sizer->Add(remove_btn, 1, wxEXPAND);
@ -428,31 +489,24 @@ wxPanel* BedShapePanel::init_model_panel()
sizer->Add(load_sizer, 1, wxEXPAND); sizer->Add(load_sizer, 1, wxEXPAND);
sizer->Add(remove_sizer, 1, wxEXPAND | wxTOP, 2); sizer->Add(remove_sizer, 1, wxEXPAND | wxTOP, 2);
load_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) load_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) { load_model(); }));
{
load_model();
}));
remove_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) remove_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) {
{
m_custom_model = NONE; m_custom_model = NONE;
update_shape(); update_shape();
})); }));
filename_lbl->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e) filename_lbl->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e) {
{
e.SetText(_(boost::filesystem::path(m_custom_model).filename().string())); e.SetText(_(boost::filesystem::path(m_custom_model).filename().string()));
wxStaticText* lbl = dynamic_cast<wxStaticText*>(e.GetEventObject()); wxStaticText* lbl = dynamic_cast<wxStaticText*>(e.GetEventObject());
if (lbl != nullptr) if (lbl != nullptr) {
{
bool exists = (m_custom_model == NONE) || boost::filesystem::exists(m_custom_model); bool exists = (m_custom_model == NONE) || boost::filesystem::exists(m_custom_model);
lbl->SetForegroundColour(exists ? /*wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)*/wxGetApp().get_label_clr_default() : wxColor(*wxRED)); lbl->SetForegroundColour(exists ? /*wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)*/wxGetApp().get_label_clr_default() : wxColor(*wxRED));
wxString tooltip_text = ""; wxString tooltip_text = "";
if (m_custom_model != NONE) if (m_custom_model != NONE) {
{
if (!exists) if (!exists)
tooltip_text += _(L("Not found:")) + " "; tooltip_text += _L("Not found:") + " ";
tooltip_text += _(m_custom_model); tooltip_text += _(m_custom_model);
} }
@ -463,10 +517,7 @@ wxPanel* BedShapePanel::init_model_panel()
} }
})); }));
remove_btn->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e) remove_btn->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e) { e.Enable(m_custom_model != NONE); }));
{
e.Enable(m_custom_model != NONE);
}));
return sizer; return sizer;
}; };
@ -511,9 +562,17 @@ void BedShapePanel::update_shape()
auto page_idx = m_shape_options_book->GetSelection(); auto page_idx = m_shape_options_book->GetSelection();
auto opt_group = m_optgroups[page_idx]; 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); 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) { if (page_type == BedShape::Type::Rectangular) {
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
Vec2d rect_size(Vec2d::Zero()); Vec2d rect_size(Vec2d::Zero());
Vec2d rect_origin(Vec2d::Zero()); Vec2d rect_origin(Vec2d::Zero());
@ -544,7 +603,11 @@ void BedShapePanel::update_shape()
Vec2d(x1, y1), Vec2d(x1, y1),
Vec2d(x0, y1) }; Vec2d(x0, y1) };
} }
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
else if (page_type == Bed3D::EShapeType::Circle) {
#else
else if (page_type == BedShape::Type::Circular) { else if (page_type == BedShape::Type::Circular) {
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
double diameter; double diameter;
try { diameter = boost::any_cast<double>(opt_group->get_value("diameter")); } try { diameter = boost::any_cast<double>(opt_group->get_value("diameter")); }
catch (const std::exception & /* e */) { return; } catch (const std::exception & /* e */) { return; }
@ -560,7 +623,11 @@ void BedShapePanel::update_shape()
} }
m_shape = points; m_shape = points;
} }
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
else if (page_type == Bed3D::EShapeType::Custom)
#else
else if (page_type == BedShape::Type::Custom) else if (page_type == BedShape::Type::Custom)
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
m_shape = m_loaded_shape; m_shape = m_loaded_shape;
update_preview(); update_preview();
@ -569,14 +636,13 @@ void BedShapePanel::update_shape()
// Loads an stl file, projects it to the XY plane and calculates a polygon. // Loads an stl file, projects it to the XY plane and calculates a polygon.
void BedShapePanel::load_stl() void BedShapePanel::load_stl()
{ {
wxFileDialog dialog(this, _(L("Choose an STL file to import bed shape from:")), "", "", file_wildcards(FT_STL), wxFD_OPEN | wxFD_FILE_MUST_EXIST); wxFileDialog dialog(this, _L("Choose an STL file to import bed shape from:"), "", "", file_wildcards(FT_STL), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dialog.ShowModal() != wxID_OK) if (dialog.ShowModal() != wxID_OK)
return; return;
std::string file_name = dialog.GetPath().ToUTF8().data(); std::string file_name = dialog.GetPath().ToUTF8().data();
if (!boost::algorithm::iends_with(file_name, ".stl")) if (!boost::algorithm::iends_with(file_name, ".stl")) {
{ show_error(this, _L("Invalid file format."));
show_error(this, _(L("Invalid file format.")));
return; return;
} }
@ -587,7 +653,7 @@ void BedShapePanel::load_stl()
model = Model::read_from_file(file_name); model = Model::read_from_file(file_name);
} }
catch (std::exception &) { catch (std::exception &) {
show_error(this, _(L("Error! Invalid model"))); show_error(this, _L("Error! Invalid model"));
return; return;
} }
@ -595,11 +661,11 @@ void BedShapePanel::load_stl()
auto expolygons = mesh.horizontal_projection(); auto expolygons = mesh.horizontal_projection();
if (expolygons.size() == 0) { if (expolygons.size() == 0) {
show_error(this, _(L("The selected file contains no geometry."))); show_error(this, _L("The selected file contains no geometry."));
return; return;
} }
if (expolygons.size() > 1) { if (expolygons.size() > 1) {
show_error(this, _(L("The selected file contains several disjoint areas. This is not supported."))); show_error(this, _L("The selected file contains several disjoint areas. This is not supported."));
return; return;
} }
@ -614,7 +680,7 @@ void BedShapePanel::load_stl()
void BedShapePanel::load_texture() void BedShapePanel::load_texture()
{ {
wxFileDialog dialog(this, _(L("Choose a file to import bed texture from (PNG/SVG):")), "", "", wxFileDialog dialog(this, _L("Choose a file to import bed texture from (PNG/SVG):"), "", "",
file_wildcards(FT_TEX), wxFD_OPEN | wxFD_FILE_MUST_EXIST); file_wildcards(FT_TEX), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dialog.ShowModal() != wxID_OK) if (dialog.ShowModal() != wxID_OK)
@ -623,9 +689,8 @@ void BedShapePanel::load_texture()
m_custom_texture = NONE; m_custom_texture = NONE;
std::string file_name = dialog.GetPath().ToUTF8().data(); std::string file_name = dialog.GetPath().ToUTF8().data();
if (!boost::algorithm::iends_with(file_name, ".png") && !boost::algorithm::iends_with(file_name, ".svg")) if (!boost::algorithm::iends_with(file_name, ".png") && !boost::algorithm::iends_with(file_name, ".svg")) {
{ show_error(this, _L("Invalid file format."));
show_error(this, _(L("Invalid file format.")));
return; return;
} }
@ -637,7 +702,7 @@ void BedShapePanel::load_texture()
void BedShapePanel::load_model() void BedShapePanel::load_model()
{ {
wxFileDialog dialog(this, _(L("Choose an STL file to import bed model from:")), "", "", wxFileDialog dialog(this, _L("Choose an STL file to import bed model from:"), "", "",
file_wildcards(FT_STL), wxFD_OPEN | wxFD_FILE_MUST_EXIST); file_wildcards(FT_STL), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dialog.ShowModal() != wxID_OK) if (dialog.ShowModal() != wxID_OK)
@ -646,9 +711,8 @@ void BedShapePanel::load_model()
m_custom_model = NONE; m_custom_model = NONE;
std::string file_name = dialog.GetPath().ToUTF8().data(); std::string file_name = dialog.GetPath().ToUTF8().data();
if (!boost::algorithm::iends_with(file_name, ".stl")) if (!boost::algorithm::iends_with(file_name, ".stl")) {
{ show_error(this, _L("Invalid file format."));
show_error(this, _(L("Invalid file format.")));
return; return;
} }

View File

@ -5,6 +5,9 @@
#include "GUI_Utils.hpp" #include "GUI_Utils.hpp"
#include "2DBed.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 "I18N.hpp"
#include <wx/dialog.h> #include <wx/dialog.h>
@ -19,12 +22,14 @@ using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>;
struct BedShape struct BedShape
{ {
#if !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
enum class Type { enum class Type {
Rectangular = 0, Rectangular = 0,
Circular, Circular,
Custom, Custom,
Invalid Invalid
}; };
#endif // !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
enum class Parameter { enum class Parameter {
RectSize, RectSize,
@ -34,10 +39,18 @@ struct BedShape
BedShape(const ConfigOptionPoints& points); 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; } bool is_custom() { return m_type == Type::Custom; }
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
static void append_option_line(ConfigOptionsGroupShp optgroup, Parameter param); 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); static wxString get_name(Type type);
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
// convert Type to size_t // convert Type to size_t
size_t get_type(); size_t get_type();
@ -46,7 +59,11 @@ struct BedShape
void apply_optgroup_values(ConfigOptionsGroupShp optgroup); void apply_optgroup_values(ConfigOptionsGroupShp optgroup);
private: private:
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
Bed3D::EShapeType m_type{ Bed3D::EShapeType::Invalid };
#else
Type m_type {Type::Invalid}; Type m_type {Type::Invalid};
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
Vec2d m_rectSize {200, 200}; Vec2d m_rectSize {200, 200};
Vec2d m_rectOrigin {0, 0}; Vec2d m_rectOrigin {0, 0};
double m_diameter {0}; double m_diameter {0};

View File

@ -6,10 +6,11 @@
#include "libslic3r/Model.hpp" #include "libslic3r/Model.hpp"
#include "libslic3r/Utils.hpp" #include "libslic3r/Utils.hpp"
#include "libslic3r/LocalesUtils.hpp" #include "libslic3r/LocalesUtils.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "GUI_App.hpp" #include "GUI_App.hpp"
#include "MainFrame.hpp" #include "MainFrame.hpp"
#include "Plater.hpp" #include "Plater.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "Camera.hpp" #include "Camera.hpp"
#include "I18N.hpp" #include "I18N.hpp"
#include "GUI_Utils.hpp" #include "GUI_Utils.hpp"
@ -19,6 +20,10 @@
#include "GLToolbar.hpp" #include "GLToolbar.hpp"
#include "GUI_Preview.hpp" #include "GUI_Preview.hpp"
#include "GUI_ObjectManipulation.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> #include <imgui/imgui_internal.h>
#include <GL/glew.h> #include <GL/glew.h>
@ -674,6 +679,10 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print&
if (wxGetApp().is_gcode_viewer()) if (wxGetApp().is_gcode_viewer())
m_custom_gcode_per_print_z = gcode_result.custom_gcode_per_print_z; m_custom_gcode_per_print_z = gcode_result.custom_gcode_per_print_z;
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
m_max_print_height = gcode_result.max_print_height;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
load_toolpaths(gcode_result); load_toolpaths(gcode_result);
if (m_layers.empty()) if (m_layers.empty())
@ -819,6 +828,9 @@ void GCodeViewer::reset()
m_paths_bounding_box = BoundingBoxf3(); m_paths_bounding_box = BoundingBoxf3();
m_max_bounding_box = BoundingBoxf3(); m_max_bounding_box = BoundingBoxf3();
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
m_max_print_height = 0.0f;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
m_tool_colors = std::vector<Color>(); m_tool_colors = std::vector<Color>();
m_extruders_count = 0; m_extruders_count = 0;
m_extruder_ids = std::vector<unsigned char>(); m_extruder_ids = std::vector<unsigned char>();
@ -835,6 +847,9 @@ void GCodeViewer::reset()
#if ENABLE_GCODE_VIEWER_STATISTICS #if ENABLE_GCODE_VIEWER_STATISTICS
m_statistics.reset_all(); m_statistics.reset_all();
#endif // ENABLE_GCODE_VIEWER_STATISTICS #endif // ENABLE_GCODE_VIEWER_STATISTICS
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
m_contained_in_bed = true;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
} }
void GCodeViewer::render() void GCodeViewer::render()
@ -1554,7 +1569,49 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
// set approximate max bounding box (take in account also the tool marker) // set approximate max bounding box (take in account also the tool marker)
m_max_bounding_box = m_paths_bounding_box; 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()[2] * Vec3d::UnitZ()); 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 ENABLE_FIX_SEAMS_SYNCH #if ENABLE_FIX_SEAMS_SYNCH
m_sequential_view.gcode_ids.clear(); m_sequential_view.gcode_ids.clear();

View File

@ -780,6 +780,9 @@ private:
BoundingBoxf3 m_paths_bounding_box; BoundingBoxf3 m_paths_bounding_box;
// bounding box of toolpaths + marker tools // bounding box of toolpaths + marker tools
BoundingBoxf3 m_max_bounding_box; BoundingBoxf3 m_max_bounding_box;
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
float m_max_print_height{ 0.0f };
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
std::vector<Color> m_tool_colors; std::vector<Color> m_tool_colors;
Layers m_layers; Layers m_layers;
std::array<unsigned int, 2> m_layers_z_range; std::array<unsigned int, 2> m_layers_z_range;
@ -804,6 +807,10 @@ private:
std::vector<CustomGCode::Item> m_custom_gcode_per_print_z; std::vector<CustomGCode::Item> m_custom_gcode_per_print_z;
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
bool m_contained_in_bed{ true };
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
public: public:
GCodeViewer(); GCodeViewer();
~GCodeViewer() { reset(); } ~GCodeViewer() { reset(); }
@ -832,6 +839,10 @@ public:
const SequentialView& get_sequential_view() const { return m_sequential_view; } const SequentialView& get_sequential_view() const { return m_sequential_view; }
void update_sequential_view_current(unsigned int first, unsigned int last); void update_sequential_view_current(unsigned int first, unsigned int last);
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
bool is_contained_in_bed() const { return m_contained_in_bed; }
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
EViewType get_view_type() const { return m_view_type; } EViewType get_view_type() const { return m_view_type; }
void set_view_type(EViewType type) { void set_view_type(EViewType type) {
if (type == EViewType::Count) if (type == EViewType::Count)

View File

@ -1108,10 +1108,18 @@ void GLCanvas3D::reset_volumes()
_set_warning_notification(EWarning::ObjectOutside, false); _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 ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state() const
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
{ {
ModelInstanceEPrintVolumeState state; 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); m_volumes.check_outside_state(m_config, &state);
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
return state; return state;
} }
@ -1828,8 +1836,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
assert(volume_idx_wipe_tower_old == -1); assert(volume_idx_wipe_tower_old == -1);
volume_idx_wipe_tower_old = (int)volume_id; volume_idx_wipe_tower_old = (int)volume_id;
} }
if (!m_reload_delayed) if (!m_reload_delayed) {
{
deleted_volumes.emplace_back(volume, volume_id); deleted_volumes.emplace_back(volume, volume_id);
delete volume; delete volume;
} }
@ -2123,8 +2130,10 @@ void GLCanvas3D::load_sla_preview()
// Release OpenGL data before generating new data. // Release OpenGL data before generating new data.
reset_volumes(); reset_volumes();
_load_sla_shells(); _load_sla_shells();
#if !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false); 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")); 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(); _update_sla_shells_outside_state();
_set_warning_notification_if_needed(EWarning::SlaSupportsOutside); _set_warning_notification_if_needed(EWarning::SlaSupportsOutside);
} }
@ -3216,6 +3225,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
} }
} }
else if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) { else if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) {
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
if (evt.LeftUp())
m_selection.stop_dragging();
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
if (m_layers_editing.state != LayersEditing::Unknown) { if (m_layers_editing.state != LayersEditing::Unknown) {
m_layers_editing.state = LayersEditing::Unknown; m_layers_editing.state = LayersEditing::Unknown;
_stop_timer(); _stop_timer();
@ -4984,6 +4998,7 @@ void GLCanvas3D::_rectangular_selection_picking_pass()
_update_volumes_hover_state(); _update_volumes_hover_state();
} }
#if !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
static BoundingBoxf3 print_volume(const DynamicPrintConfig& config) static BoundingBoxf3 print_volume(const DynamicPrintConfig& config)
{ {
// tolerance to avoid false detection at bed edges // tolerance to avoid false detection at bed edges
@ -5000,6 +5015,7 @@ static BoundingBoxf3 print_volume(const DynamicPrintConfig& config)
} }
return ret; return ret;
} }
#endif // !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
void GLCanvas3D::_render_background() const void GLCanvas3D::_render_background() const
{ {
@ -5010,11 +5026,16 @@ void GLCanvas3D::_render_background() const
if (!m_volumes.empty()) if (!m_volumes.empty())
use_error_color &= _is_any_volume_outside(); use_error_color &= _is_any_volume_outside();
else { 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 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3();
const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box(); 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; 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()); glsafe(::glPushMatrix());
@ -5090,13 +5111,55 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
if (m_picking_enabled) { if (m_picking_enabled) {
// Update the layer editing selection to the first object selected, update the current object maximum Z. // 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); 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 (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")) } });
}
break;
}
case Bed3D::EShapeType::Rectangle: {
const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false); const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false);
m_volumes.set_print_box((float)bed_bb.min(0) - BedEpsilon, (float)bed_bb.min(1) - BedEpsilon, 0.0f, (float)bed_bb.max(0) + BedEpsilon, (float)bed_bb.max(1) + BedEpsilon, (float)m_config->opt_float("max_print_height")); 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")) } });
break;
}
default:
case Bed3D::EShapeType::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(m_config, 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) if (m_use_clipping_planes)
m_volumes.set_z_range(-m_clipping_planes[0].get_data()[3], m_clipping_planes[1].get_data()[3]); m_volumes.set_z_range(-m_clipping_planes[0].get_data()[3], m_clipping_planes[1].get_data()[3]);
@ -5106,7 +5169,11 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data()); m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data());
m_volumes.set_show_sinking_contours(! m_gizmos.is_hiding_instances()); 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"); GLShaderProgram* shader = wxGetApp().get_shader("gouraud");
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
if (shader != nullptr) { if (shader != nullptr) {
shader->start_using(); shader->start_using();
@ -5751,7 +5818,7 @@ void GLCanvas3D::_load_print_toolpaths()
total_layer_count = std::max(total_layer_count, print_object->total_layer_count()); total_layer_count = std::max(total_layer_count, print_object->total_layer_count());
} }
size_t skirt_height = print->has_infinite_skirt() ? total_layer_count : std::min<size_t>(print->config().skirt_height.value, total_layer_count); size_t skirt_height = print->has_infinite_skirt() ? total_layer_count : std::min<size_t>(print->config().skirt_height.value, total_layer_count);
if ((skirt_height == 0) && print->has_brim()) if (skirt_height == 0 && print->has_brim())
skirt_height = 1; skirt_height = 1;
// Get first skirt_height layers. // Get first skirt_height layers.
@ -5784,6 +5851,9 @@ void GLCanvas3D::_load_print_toolpaths()
reserve_new_volume_finalize_old_volume(*volume, vol, m_initialized); reserve_new_volume_finalize_old_volume(*volume, vol, m_initialized);
} }
} }
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
volume->calc_convex_hull_3d();
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
volume->indexed_vertex_array.finalize_geometry(m_initialized); volume->indexed_vertex_array.finalize_geometry(m_initialized);
} }
@ -6074,8 +6144,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(), std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
[](const GLVolume *volume) { return volume->empty(); }), [](const GLVolume *volume) { return volume->empty(); }),
m_volumes.volumes.end()); 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];
v->calc_convex_hull_3d();
v->indexed_vertex_array.finalize_geometry(m_initialized);
}
#else
for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_initialized); 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(); BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info();
} }
@ -6083,7 +6161,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors) void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors)
{ {
const Print *print = this->fff_print(); const Print *print = this->fff_print();
if ((print == nullptr) || print->wipe_tower_data().tool_changes.empty()) if (print == nullptr || print->wipe_tower_data().tool_changes.empty())
return; return;
if (!print->is_step_done(psWipeTower)) if (!print->is_step_done(psWipeTower))
@ -6231,8 +6309,16 @@ 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(), std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
[](const GLVolume *volume) { return volume->empty(); }), [](const GLVolume *volume) { return volume->empty(); }),
m_volumes.volumes.end()); 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];
v->calc_convex_hull_3d();
v->indexed_vertex_array.finalize_geometry(m_initialized);
}
#else
for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_initialized); 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(); BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info();
} }
@ -6294,18 +6380,26 @@ void GLCanvas3D::_load_sla_shells()
void GLCanvas3D::_update_toolpath_volumes_outside_state() 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(); BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3();
for (GLVolume* volume : m_volumes.volumes) { 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; 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() 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(); BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3();
for (GLVolume* volume : m_volumes.volumes) { 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; 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) void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning)
@ -6316,12 +6410,18 @@ void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning)
show = _is_any_volume_outside(); show = _is_any_volume_outside();
else { else {
if (wxGetApp().is_editor()) { if (wxGetApp().is_editor()) {
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
if (current_printer_technology() != ptSLA)
show = m_gcode_viewer.has_data() && !m_gcode_viewer.is_contained_in_bed();
#else
BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3();
const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box(); const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box();
if (test_volume.radius() > 0.0 && paths_volume.radius() > 0.0) if (test_volume.radius() > 0.0 && paths_volume.radius() > 0.0)
show = !test_volume.contains(paths_volume); show = !test_volume.contains(paths_volume);
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
} }
} }
_set_warning_notification(warning, show); _set_warning_notification(warning, show);
} }
@ -6396,7 +6496,7 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
bool GLCanvas3D::_is_any_volume_outside() const bool GLCanvas3D::_is_any_volume_outside() const
{ {
for (const GLVolume* volume : m_volumes.volumes) { for (const GLVolume* volume : m_volumes.volumes) {
if ((volume != nullptr) && volume->is_outside) if (volume != nullptr && volume->is_outside)
return true; return true;
} }

View File

@ -475,6 +475,9 @@ private:
const DynamicPrintConfig* m_config; const DynamicPrintConfig* m_config;
Model* m_model; Model* m_model;
BackgroundSlicingProcess *m_process; BackgroundSlicingProcess *m_process;
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
bool m_requires_check_outside_state{ false };
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
std::array<unsigned int, 2> m_old_size{ 0, 0 }; std::array<unsigned int, 2> m_old_size{ 0, 0 };
@ -611,11 +614,18 @@ public:
void post_event(wxEvent &&event); void post_event(wxEvent &&event);
void set_as_dirty(); void set_as_dirty();
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
void requires_check_outside_state() { m_requires_check_outside_state = true; }
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
unsigned int get_volumes_count() const; unsigned int get_volumes_count() const;
const GLVolumeCollection& get_volumes() const { return m_volumes; } const GLVolumeCollection& get_volumes() const { return m_volumes; }
void reset_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; ModelInstanceEPrintVolumeState check_volumes_outside_state() const;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
#if ENABLE_SEAMS_USING_MODELS #if ENABLE_SEAMS_USING_MODELS
void init_gcode_viewer() { m_gcode_viewer.init(); } void init_gcode_viewer() { m_gcode_viewer.init(); }

View File

@ -61,10 +61,15 @@ std::pair<bool, std::string> GLShadersManager::init()
// used to render extrusion and travel paths as lines in gcode preview // used to render extrusion and travel paths as lines in gcode preview
valid &= append_shader("toolpaths_lines", { "toolpaths_lines.vs", "toolpaths_lines.fs" }); valid &= append_shader("toolpaths_lines", { "toolpaths_lines.vs", "toolpaths_lines.fs" });
// used to render objects in 3d editor // used to render objects in 3d editor
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
// When setting this technology to default rename the following from "gouraud_mod" to "gouraud"
valid &= append_shader("gouraud_mod", { "gouraud_mod.vs", "gouraud_mod.fs" }
#else
valid &= append_shader("gouraud", { "gouraud.vs", "gouraud.fs" } valid &= append_shader("gouraud", { "gouraud.vs", "gouraud.fs" }
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
#if ENABLE_ENVIRONMENT_MAP #if ENABLE_ENVIRONMENT_MAP
, { "ENABLE_ENVIRONMENT_MAP"sv } , { "ENABLE_ENVIRONMENT_MAP"sv }
#endif #endif // ENABLE_ENVIRONMENT_MAP
); );
// used to render variable layers heights in 3d editor // used to render variable layers heights in 3d editor
valid &= append_shader("variable_layer_height", { "variable_layer_height.vs", "variable_layer_height.fs" }); valid &= append_shader("variable_layer_height", { "variable_layer_height.vs", "variable_layer_height.fs" });

View File

@ -635,7 +635,6 @@ void ObjectManipulation::update_if_dirty()
update(m_cache.rotation, m_cache.rotation_rounded, meRotation, m_new_rotation); update(m_cache.rotation, m_cache.rotation_rounded, meRotation, m_new_rotation);
} }
if (selection.requires_uniform_scale()) { if (selection.requires_uniform_scale()) {
m_lock_bnt->SetLock(true); m_lock_bnt->SetLock(true);
m_lock_bnt->SetToolTip(_L("You cannot use non-uniform scaling mode for multiple objects/parts selection")); m_lock_bnt->SetToolTip(_L("You cannot use non-uniform scaling mode for multiple objects/parts selection"));
@ -658,8 +657,14 @@ void ObjectManipulation::update_if_dirty()
else else
m_og->disable(); m_og->disable();
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
if (!selection.is_dragging()) {
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
update_reset_buttons_visibility(); update_reset_buttons_visibility();
update_mirror_buttons_visibility(); update_mirror_buttons_visibility();
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
}
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
m_dirty = false; m_dirty = false;
} }

View File

@ -508,20 +508,23 @@ RENDER_AGAIN:
m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x,
m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(0.5f);
const float settings_sliders_left = const float settings_sliders_left =
std::max({m_imgui->calc_text_size(m_desc.at("offset")).x, std::max(std::max({m_imgui->calc_text_size(m_desc.at("offset")).x,
m_imgui->calc_text_size(m_desc.at("quality")).x, m_imgui->calc_text_size(m_desc.at("quality")).x,
m_imgui->calc_text_size(m_desc.at("closing_distance")).x, m_imgui->calc_text_size(m_desc.at("closing_distance")).x,
m_imgui->calc_text_size(m_desc.at("hole_diameter")).x, m_imgui->calc_text_size(m_desc.at("hole_diameter")).x,
m_imgui->calc_text_size(m_desc.at("hole_depth")).x}) m_imgui->calc_text_size(m_desc.at("hole_depth")).x}) + m_imgui->scaled(0.5f), clipping_slider_left);
+ m_imgui->scaled(1.f);
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f);
const float diameter_slider_left = settings_sliders_left; //m_imgui->calc_text_size(m_desc.at("hole_diameter")).x + m_imgui->scaled(1.f); const float diameter_slider_left = settings_sliders_left; //m_imgui->calc_text_size(m_desc.at("hole_diameter")).x + m_imgui->scaled(1.f);
const float minimal_slider_width = m_imgui->scaled(4.f); const float minimal_slider_width = m_imgui->scaled(4.f);
const float button_preview_width = m_imgui->calc_button_size(m_desc.at("preview")).x;
float window_width = minimal_slider_width + std::max({settings_sliders_left, clipping_slider_left, diameter_slider_left}); float window_width = minimal_slider_width + std::max({settings_sliders_left, clipping_slider_left, diameter_slider_left});
window_width = std::max(window_width, m_imgui->calc_text_size(m_desc.at("preview")).x); window_width = std::max(window_width, button_preview_width);
if (m_imgui->button(m_desc["preview"])) if (m_imgui->button(m_desc["preview"]))
hollow_mesh(); hollow_mesh();
@ -544,9 +547,9 @@ RENDER_AGAIN:
float max_tooltip_width = ImGui::GetFontSize() * 20.0f; float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("offset")); m_imgui->text(m_desc.at("offset"));
ImGui::SameLine(settings_sliders_left); ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x);
ImGui::PushItemWidth(window_width - settings_sliders_left); ImGui::PushItemWidth(window_width - settings_sliders_left);
m_imgui->slider_float(" ", &offset, offset_min, offset_max, "%.1f mm"); m_imgui->slider_float("##offset", &offset, offset_min, offset_max, "%.1f mm");
if (ImGui::IsItemHovered()) if (ImGui::IsItemHovered())
m_imgui->tooltip((_utf8(opts[0].second->tooltip)).c_str(), max_tooltip_width); m_imgui->tooltip((_utf8(opts[0].second->tooltip)).c_str(), max_tooltip_width);
@ -557,8 +560,8 @@ RENDER_AGAIN:
if (current_mode >= quality_mode) { if (current_mode >= quality_mode) {
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("quality")); m_imgui->text(m_desc.at("quality"));
ImGui::SameLine(settings_sliders_left); ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x);
m_imgui->slider_float(" ", &quality, quality_min, quality_max, "%.1f"); m_imgui->slider_float("##quality", &quality, quality_min, quality_max, "%.1f");
if (ImGui::IsItemHovered()) if (ImGui::IsItemHovered())
m_imgui->tooltip((_utf8(opts[1].second->tooltip)).c_str(), max_tooltip_width); m_imgui->tooltip((_utf8(opts[1].second->tooltip)).c_str(), max_tooltip_width);
@ -570,8 +573,8 @@ RENDER_AGAIN:
if (current_mode >= closing_d_mode) { if (current_mode >= closing_d_mode) {
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("closing_distance")); m_imgui->text(m_desc.at("closing_distance"));
ImGui::SameLine(settings_sliders_left); ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x);
m_imgui->slider_float(" ", &closing_d, closing_d_min, closing_d_max, "%.1f mm"); m_imgui->slider_float("##closing_distance", &closing_d, closing_d_min, closing_d_max, "%.1f mm");
if (ImGui::IsItemHovered()) if (ImGui::IsItemHovered())
m_imgui->tooltip((_utf8(opts[2].second->tooltip)).c_str(), max_tooltip_width); m_imgui->tooltip((_utf8(opts[2].second->tooltip)).c_str(), max_tooltip_width);
@ -614,11 +617,11 @@ RENDER_AGAIN:
m_new_hole_radius = diameter_upper_cap / 2.f; m_new_hole_radius = diameter_upper_cap / 2.f;
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("hole_diameter")); m_imgui->text(m_desc.at("hole_diameter"));
ImGui::SameLine(diameter_slider_left); ImGui::SameLine(diameter_slider_left, m_imgui->get_item_spacing().x);
ImGui::PushItemWidth(window_width - diameter_slider_left); ImGui::PushItemWidth(window_width - diameter_slider_left);
float diam = 2.f * m_new_hole_radius; float diam = 2.f * m_new_hole_radius;
m_imgui->slider_float("", &diam, 1.f, 15.f, "%.1f mm", 1.f, false); m_imgui->slider_float("##hole_diameter", &diam, 1.f, 15.f, "%.1f mm", 1.f, false);
// Let's clamp the value (which could have been entered by keyboard) to a larger range // Let's clamp the value (which could have been entered by keyboard) to a larger range
// than the slider. This allows entering off-scale values and still protects against // than the slider. This allows entering off-scale values and still protects against
//complete non-sense. //complete non-sense.
@ -630,8 +633,8 @@ RENDER_AGAIN:
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc["hole_depth"]); m_imgui->text(m_desc["hole_depth"]);
ImGui::SameLine(diameter_slider_left); ImGui::SameLine(diameter_slider_left, m_imgui->get_item_spacing().x);
m_imgui->slider_float(" ", &m_new_hole_height, 0.f, 10.f, "%.1f mm", 1.f, false); m_imgui->slider_float("##hole_depth", &m_new_hole_height, 0.f, 10.f, "%.1f mm", 1.f, false);
// Same as above: // Same as above:
m_new_hole_height = std::clamp(m_new_hole_height, 0.f, 100.f); m_new_hole_height = std::clamp(m_new_hole_height, 0.f, 100.f);
@ -697,10 +700,10 @@ RENDER_AGAIN:
} }
} }
ImGui::SameLine(clipping_slider_left); ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x);
ImGui::PushItemWidth(window_width - clipping_slider_left); ImGui::PushItemWidth(window_width - settings_sliders_left);
float clp_dist = m_c->object_clipper()->get_position(); float clp_dist = m_c->object_clipper()->get_position();
if (m_imgui->slider_float(" ", &clp_dist, 0.f, 1.f, "%.2f")) if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f"))
m_c->object_clipper()->set_position(clp_dist, true); m_c->object_clipper()->set_position(clp_dist, true);
// make sure supports are shown/hidden as appropriate // make sure supports are shown/hidden as appropriate

View File

@ -66,12 +66,20 @@ GLGizmoPainterBase::ClippingPlaneDataWrapper GLGizmoPainterBase::get_clipping_pl
void GLGizmoPainterBase::render_triangles(const Selection& selection) const void GLGizmoPainterBase::render_triangles(const Selection& selection) const
{ {
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
auto* shader = wxGetApp().get_shader("gouraud_mod");
#else
auto* shader = wxGetApp().get_shader("gouraud"); auto* shader = wxGetApp().get_shader("gouraud");
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
if (! shader) if (! shader)
return; return;
shader->start_using(); shader->start_using();
shader->set_uniform("slope.actived", false); shader->set_uniform("slope.actived", false);
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
shader->set_uniform("print_volume.type", 0);
#else
shader->set_uniform("print_box.actived", false); shader->set_uniform("print_box.actived", false);
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
shader->set_uniform("clipping_plane", this->get_clipping_plane_data().clp_dataf); shader->set_uniform("clipping_plane", this->get_clipping_plane_data().clp_dataf);
ScopeGuard guard([shader]() { if (shader) shader->stop_using(); }); ScopeGuard guard([shader]() { if (shader) shader->stop_using(); });
@ -98,7 +106,11 @@ void GLGizmoPainterBase::render_triangles(const Selection& selection) const
// to the shader input variable print_box.volume_world_matrix before // to the shader input variable print_box.volume_world_matrix before
// rendering the painted triangles. When this matrix is not set, the // rendering the painted triangles. When this matrix is not set, the
// wrong transformation matrix is used for "Clipping of view". // wrong transformation matrix is used for "Clipping of view".
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
shader->set_uniform("volume_world_matrix", trafo_matrix);
#else
shader->set_uniform("print_box.volume_world_matrix", trafo_matrix); shader->set_uniform("print_box.volume_world_matrix", trafo_matrix);
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
m_triangle_selectors[mesh_id]->render(m_imgui); m_triangle_selectors[mesh_id]->render(m_imgui);
@ -575,7 +587,11 @@ void TriangleSelectorGUI::render(ImGuiWrapper* imgui)
auto* shader = wxGetApp().get_current_shader(); auto* shader = wxGetApp().get_current_shader();
if (! shader) if (! shader)
return; return;
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
assert(shader->get_name() == "gouraud_mod");
#else
assert(shader->get_name() == "gouraud"); assert(shader->get_name() == "gouraud");
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
ScopeGuard guard([shader]() { if (shader) shader->set_uniform("offset_depth_buffer", false);}); ScopeGuard guard([shader]() { if (shader) shader->set_uniform("offset_depth_buffer", false);});
shader->set_uniform("offset_depth_buffer", true); shader->set_uniform("offset_depth_buffer", true);
for (auto iva : {std::make_pair(&m_iva_enforcers, enforcers_color), for (auto iva : {std::make_pair(&m_iva_enforcers, enforcers_color),

View File

@ -673,7 +673,7 @@ RENDER_AGAIN:
// - keep updating the head radius during sliding so it is continuosly refreshed in 3D scene // - keep updating the head radius during sliding so it is continuosly refreshed in 3D scene
// - take correct undo/redo snapshot after the user is done with moving the slider // - take correct undo/redo snapshot after the user is done with moving the slider
float initial_value = m_new_point_head_diameter; float initial_value = m_new_point_head_diameter;
m_imgui->slider_float("", &m_new_point_head_diameter, 0.1f, diameter_upper_cap, "%.1f"); m_imgui->slider_float("##head_diameter", &m_new_point_head_diameter, 0.1f, diameter_upper_cap, "%.1f");
if (ImGui::IsItemClicked()) { if (ImGui::IsItemClicked()) {
if (m_old_point_head_diameter == 0.f) if (m_old_point_head_diameter == 0.f)
m_old_point_head_diameter = initial_value; m_old_point_head_diameter = initial_value;
@ -733,7 +733,7 @@ RENDER_AGAIN:
float density = static_cast<const ConfigOptionInt*>(opts[0])->value; float density = static_cast<const ConfigOptionInt*>(opts[0])->value;
float minimal_point_distance = static_cast<const ConfigOptionFloat*>(opts[1])->value; float minimal_point_distance = static_cast<const ConfigOptionFloat*>(opts[1])->value;
m_imgui->slider_float("", &minimal_point_distance, 0.f, 20.f, "%.f mm"); m_imgui->slider_float("##minimal_point_distance", &minimal_point_distance, 0.f, 20.f, "%.f mm");
bool slider_clicked = ImGui::IsItemClicked(); // someone clicked the slider bool slider_clicked = ImGui::IsItemClicked(); // someone clicked the slider
bool slider_edited = ImGui::IsItemEdited(); // someone is dragging the slider bool slider_edited = ImGui::IsItemEdited(); // someone is dragging the slider
bool slider_released = ImGui::IsItemDeactivatedAfterEdit(); // someone has just released the slider bool slider_released = ImGui::IsItemDeactivatedAfterEdit(); // someone has just released the slider
@ -742,7 +742,7 @@ RENDER_AGAIN:
m_imgui->text(m_desc.at("points_density")); m_imgui->text(m_desc.at("points_density"));
ImGui::SameLine(settings_sliders_left); ImGui::SameLine(settings_sliders_left);
m_imgui->slider_float(" ", &density, 0.f, 200.f, "%.f %%"); m_imgui->slider_float("##points_density", &density, 0.f, 200.f, "%.f %%");
slider_clicked |= ImGui::IsItemClicked(); slider_clicked |= ImGui::IsItemClicked();
slider_edited |= ImGui::IsItemEdited(); slider_edited |= ImGui::IsItemEdited();
slider_released |= ImGui::IsItemDeactivatedAfterEdit(); slider_released |= ImGui::IsItemDeactivatedAfterEdit();
@ -802,7 +802,7 @@ RENDER_AGAIN:
ImGui::SameLine(clipping_slider_left); ImGui::SameLine(clipping_slider_left);
ImGui::PushItemWidth(window_width - clipping_slider_left); ImGui::PushItemWidth(window_width - clipping_slider_left);
float clp_dist = m_c->object_clipper()->get_position(); float clp_dist = m_c->object_clipper()->get_position();
if (m_imgui->slider_float(" ", &clp_dist, 0.f, 1.f, "%.2f")) if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f"))
m_c->object_clipper()->set_position(clp_dist, true); m_c->object_clipper()->set_position(clp_dist, true);

View File

@ -280,7 +280,7 @@ void ImGuiWrapper::render()
m_new_frame_open = false; m_new_frame_open = false;
} }
ImVec2 ImGuiWrapper::calc_text_size(const wxString &text, float wrap_width) ImVec2 ImGuiWrapper::calc_text_size(const wxString &text, float wrap_width) const
{ {
auto text_utf8 = into_u8(text); auto text_utf8 = into_u8(text);
ImVec2 size = ImGui::CalcTextSize(text_utf8.c_str(), NULL, false, wrap_width); ImVec2 size = ImGui::CalcTextSize(text_utf8.c_str(), NULL, false, wrap_width);
@ -293,6 +293,22 @@ ImVec2 ImGuiWrapper::calc_text_size(const wxString &text, float wrap_width)
return size; return size;
} }
ImVec2 ImGuiWrapper::calc_button_size(const wxString &text, const ImVec2 &button_size) const
{
const ImVec2 text_size = this->calc_text_size(text);
const ImGuiContext &g = *GImGui;
const ImGuiStyle &style = g.Style;
return ImGui::CalcItemSize(button_size, text_size.x + style.FramePadding.x * 2.0f, text_size.y + style.FramePadding.y * 2.0f);
}
ImVec2 ImGuiWrapper::get_item_spacing() const
{
const ImGuiContext &g = *GImGui;
const ImGuiStyle &style = g.Style;
return g.Style.ItemSpacing;
}
float ImGuiWrapper::get_slider_float_height() const float ImGuiWrapper::get_slider_float_height() const
{ {
const ImGuiContext& g = *GImGui; const ImGuiContext& g = *GImGui;

View File

@ -53,8 +53,10 @@ public:
float scaled(float x) const { return x * m_font_size; } float scaled(float x) const { return x * m_font_size; }
ImVec2 scaled(float x, float y) const { return ImVec2(x * m_font_size, y * m_font_size); } ImVec2 scaled(float x, float y) const { return ImVec2(x * m_font_size, y * m_font_size); }
ImVec2 calc_text_size(const wxString &text, float wrap_width = -1.0f); ImVec2 calc_text_size(const wxString &text, float wrap_width = -1.0f) const;
ImVec2 calc_button_size(const wxString &text, const ImVec2 &button_size = ImVec2(0, 0)) const;
ImVec2 get_item_spacing() const;
float get_slider_float_height() const; float get_slider_float_height() const;
void set_next_window_pos(float x, float y, int flag, float pivot_x = 0.0f, float pivot_y = 0.0f); void set_next_window_pos(float x, float y, int flag, float pivot_x = 0.0f, float pivot_y = 0.0f);

View File

@ -48,6 +48,7 @@
#include "libslic3r/SLAPrint.hpp" #include "libslic3r/SLAPrint.hpp"
#include "libslic3r/Utils.hpp" #include "libslic3r/Utils.hpp"
#include "libslic3r/PresetBundle.hpp" #include "libslic3r/PresetBundle.hpp"
#include "libslic3r/ClipperUtils.hpp"
#include "GUI.hpp" #include "GUI.hpp"
#include "GUI_App.hpp" #include "GUI_App.hpp"
@ -3007,12 +3008,19 @@ void Plater::priv::schedule_background_process()
void Plater::priv::update_print_volume_state() 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 = offset(Polygon::new_scale(opt->values), 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, bed_height);
#else
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(this->config->opt<ConfigOptionPoints>("bed_shape")->values)); 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")))); 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. // 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.offset(BedEpsilon);
print_volume.min(2) = -1e10; print_volume.min(2) = -1e10;
this->q->model().update_print_volume_state(print_volume); this->q->model().update_print_volume_state(print_volume);
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
} }

View File

@ -112,6 +112,9 @@ Selection::Selection()
, m_type(Empty) , m_type(Empty)
, m_valid(false) , m_valid(false)
, m_scale_factor(1.0f) , m_scale_factor(1.0f)
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
, m_dragging(false)
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
{ {
this->set_bounding_boxes_dirty(); this->set_bounding_boxes_dirty();
} }
@ -690,6 +693,10 @@ void Selection::start_dragging()
if (!m_valid) if (!m_valid)
return; return;
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
m_dragging = true;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
set_caches(); set_caches();
} }
@ -730,6 +737,9 @@ void Selection::translate(const Vec3d& displacement, bool local)
ensure_not_below_bed(); ensure_not_below_bed();
set_bounding_boxes_dirty(); set_bounding_boxes_dirty();
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
wxGetApp().plater()->canvas3D()->requires_check_outside_state();
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
} }
// Rotate an object around one of the axes. Only one rotation component is expected to be changing. // Rotate an object around one of the axes. Only one rotation component is expected to be changing.
@ -842,7 +852,10 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_
volume.set_volume_offset(volume.get_volume_offset() + center_local - center_local_new); volume.set_volume_offset(volume.get_volume_offset() + center_local - center_local_new);
} }
this->set_bounding_boxes_dirty(); set_bounding_boxes_dirty();
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
wxGetApp().plater()->canvas3D()->requires_check_outside_state();
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
} }
void Selection::flattening_rotate(const Vec3d& normal) void Selection::flattening_rotate(const Vec3d& normal)
@ -941,6 +954,9 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type
ensure_on_bed(); ensure_on_bed();
set_bounding_boxes_dirty(); set_bounding_boxes_dirty();
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
wxGetApp().plater()->canvas3D()->requires_check_outside_state();
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
} }
void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config) void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config)
@ -2140,10 +2156,10 @@ void Selection::ensure_not_below_bed()
GLVolume* volume = (*m_volumes)[i]; GLVolume* volume = (*m_volumes)[i];
if (!volume->is_wipe_tower && !volume->is_modifier) { if (!volume->is_wipe_tower && !volume->is_modifier) {
const double max_z = volume->transformed_convex_hull_bounding_box().max.z(); const double max_z = volume->transformed_convex_hull_bounding_box().max.z();
std::pair<int, int> instance = std::make_pair(volume->object_idx(), volume->instance_idx()); const std::pair<int, int> instance = std::make_pair(volume->object_idx(), volume->instance_idx());
InstancesToZMap::iterator it = instances_max_z.find(instance); InstancesToZMap::iterator it = instances_max_z.find(instance);
if (it == instances_max_z.end()) if (it == instances_max_z.end())
it = instances_max_z.insert(InstancesToZMap::value_type(instance, -DBL_MAX)).first; it = instances_max_z.insert({ instance, -DBL_MAX }).first;
it->second = std::max(it->second, max_z); it->second = std::max(it->second, max_z);
} }
@ -2152,17 +2168,17 @@ void Selection::ensure_not_below_bed()
if (is_any_volume()) { if (is_any_volume()) {
for (unsigned int i : m_list) { for (unsigned int i : m_list) {
GLVolume& volume = *(*m_volumes)[i]; GLVolume& volume = *(*m_volumes)[i];
std::pair<int, int> instance = std::make_pair(volume.object_idx(), volume.instance_idx()); const std::pair<int, int> instance = std::make_pair(volume.object_idx(), volume.instance_idx());
InstancesToZMap::iterator it = instances_max_z.find(instance); InstancesToZMap::const_iterator it = instances_max_z.find(instance);
double z_shift = SINKING_MIN_Z_THRESHOLD - it->second; const double z_shift = SINKING_MIN_Z_THRESHOLD - it->second;
if (it != instances_max_z.end() && z_shift > 0.0) if (it != instances_max_z.end() && z_shift > 0.0)
volume.set_volume_offset(Z, volume.get_volume_offset(Z) + z_shift); volume.set_volume_offset(Z, volume.get_volume_offset(Z) + z_shift);
} }
} }
else { else {
for (GLVolume* volume : *m_volumes) { for (GLVolume* volume : *m_volumes) {
std::pair<int, int> instance = std::make_pair(volume->object_idx(), volume->instance_idx()); const std::pair<int, int> instance = std::make_pair(volume->object_idx(), volume->instance_idx());
InstancesToZMap::iterator it = instances_max_z.find(instance); InstancesToZMap::const_iterator it = instances_max_z.find(instance);
if (it != instances_max_z.end() && it->second < SINKING_MIN_Z_THRESHOLD) if (it != instances_max_z.end() && it->second < SINKING_MIN_Z_THRESHOLD)
volume->set_instance_offset(Z, volume->get_instance_offset(Z) + SINKING_MIN_Z_THRESHOLD - it->second); volume->set_instance_offset(Z, volume->get_instance_offset(Z) + SINKING_MIN_Z_THRESHOLD - it->second);
} }

View File

@ -220,6 +220,10 @@ private:
float m_scale_factor; float m_scale_factor;
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
bool m_dragging;
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
public: public:
Selection(); Selection();
@ -312,6 +316,10 @@ public:
const BoundingBoxf3& get_scaled_instance_bounding_box() const; const BoundingBoxf3& get_scaled_instance_bounding_box() const;
void start_dragging(); void start_dragging();
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
void stop_dragging() { m_dragging = false; }
bool is_dragging() const { return m_dragging; }
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
void translate(const Vec3d& displacement, bool local = false); void translate(const Vec3d& displacement, bool local = false);
void rotate(const Vec3d& rotation, TransformationType transformation_type); void rotate(const Vec3d& rotation, TransformationType transformation_type);