mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-14 06:15:58 +08:00
multipart merge
This commit is contained in:
parent
02c18dbc52
commit
3670280ab6
@ -4955,7 +4955,7 @@ exposure_time = 5
|
||||
initial_exposure_time = 35
|
||||
material_type = Tough
|
||||
material_vendor = Made for Prusa
|
||||
material_colour = #007EFD
|
||||
material_colour = #79FFFF
|
||||
|
||||
[sla_material:Prusa Super Low Odor Magenta Tough @0.025]
|
||||
inherits = *common 0.025*
|
||||
@ -5665,7 +5665,7 @@ exposure_time = 7.5
|
||||
initial_exposure_time = 35
|
||||
material_type = Tough
|
||||
material_vendor = Made for Prusa
|
||||
material_colour = #FFEEE6
|
||||
material_colour = #FF8040
|
||||
|
||||
[sla_material:Prusa Grey Tough @0.05]
|
||||
inherits = *common 0.05*
|
||||
@ -5710,7 +5710,7 @@ exposure_time = 6
|
||||
initial_exposure_time = 35
|
||||
material_type = Tough
|
||||
material_vendor = Made for Prusa
|
||||
material_colour =
|
||||
material_colour = #79FFFF
|
||||
|
||||
[sla_material:Prusa Super Low Odor Magenta Tough @0.05]
|
||||
inherits = *common 0.05*
|
||||
@ -6125,7 +6125,7 @@ exposure_time = 1.8
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = Made for Prusa
|
||||
material_colour =
|
||||
material_colour = #79FFFF
|
||||
|
||||
[sla_material:Prusa Magenta Tough @0.025 SL1S]
|
||||
inherits = *0.025_sl1s*
|
||||
@ -6371,7 +6371,7 @@ exposure_time = 2
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = Made for Prusa
|
||||
material_colour =
|
||||
material_colour = #79FFFF
|
||||
|
||||
[sla_material:Prusa Magenta Tough @0.05 SL1S]
|
||||
inherits = *0.05_sl1s*
|
||||
@ -6617,7 +6617,7 @@ exposure_time = 2.6
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = Made for Prusa
|
||||
material_colour =
|
||||
material_colour = #79FFFF
|
||||
|
||||
[sla_material:Prusa Magenta Tough @0.1 SL1S]
|
||||
inherits = *0.1_sl1s*
|
||||
@ -6657,7 +6657,7 @@ exposure_time = 2.6
|
||||
initial_exposure_time = 25
|
||||
material_type = Flexible
|
||||
material_vendor = Made for Prusa
|
||||
material_colour =
|
||||
material_colour = #007EFD
|
||||
|
||||
[sla_material:Prusa Grey Tough @0.1 SL1S]
|
||||
inherits = *0.1_sl1s*
|
||||
|
@ -22,6 +22,19 @@ 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;
|
||||
@ -44,11 +57,10 @@ varying vec3 clipping_planes_dots;
|
||||
// x = diffuse, y = specular;
|
||||
varying vec2 intensity;
|
||||
|
||||
varying vec3 delta_box_min;
|
||||
varying vec3 delta_box_max;
|
||||
uniform PrintVolumeDetection print_volume;
|
||||
|
||||
varying vec4 model_pos;
|
||||
varying float world_pos_z;
|
||||
varying vec4 world_pos;
|
||||
varying float world_normal_z;
|
||||
varying vec3 eye_normal;
|
||||
|
||||
@ -63,15 +75,30 @@ void main()
|
||||
color = vec3(0.7, 0.7, 1.0);
|
||||
alpha = 1.0;
|
||||
}
|
||||
|
||||
// if the fragment is outside the print volume -> use darker color
|
||||
color = (any(lessThan(delta_box_min, ZERO)) || any(greaterThan(delta_box_max, ZERO))) ? mix(color, ZERO, 0.3333) : 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.
|
||||
|
@ -18,14 +18,6 @@ const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
|
||||
|
||||
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
|
||||
|
||||
struct PrintBoxDetection
|
||||
{
|
||||
bool actived;
|
||||
vec3 min;
|
||||
vec3 max;
|
||||
mat4 volume_world_matrix;
|
||||
};
|
||||
|
||||
struct SlopeDetection
|
||||
{
|
||||
bool actived;
|
||||
@ -33,7 +25,7 @@ struct SlopeDetection
|
||||
mat3 volume_world_normal_matrix;
|
||||
};
|
||||
|
||||
uniform PrintBoxDetection print_box;
|
||||
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.
|
||||
@ -44,46 +36,33 @@ uniform vec4 clipping_plane;
|
||||
// x = diffuse, y = specular;
|
||||
varying vec2 intensity;
|
||||
|
||||
varying vec3 delta_box_min;
|
||||
varying vec3 delta_box_max;
|
||||
|
||||
varying vec3 clipping_planes_dots;
|
||||
|
||||
varying vec4 model_pos;
|
||||
varying float world_pos_z;
|
||||
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);
|
||||
// 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);
|
||||
// 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);
|
||||
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;
|
||||
// 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.
|
||||
vec4 world_pos = print_box.volume_world_matrix * gl_Vertex;
|
||||
world_pos_z = world_pos.z;
|
||||
|
||||
// compute deltas for out of print volume detection (world coordinates)
|
||||
if (print_box.actived) {
|
||||
delta_box_min = world_pos.xyz - print_box.min;
|
||||
delta_box_max = world_pos.xyz - print_box.max;
|
||||
} else {
|
||||
delta_box_min = ZERO;
|
||||
delta_box_max = ZERO;
|
||||
}
|
||||
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;
|
||||
|
@ -1,106 +0,0 @@
|
||||
#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);
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
#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);
|
||||
}
|
@ -111,7 +111,7 @@ public:
|
||||
void translate(coordf_t x, coordf_t y, coordf_t z) { assert(this->defined); PointClass v(x, y, z); this->min += v; this->max += v; }
|
||||
void translate(const Vec3d &v) { this->min += v; this->max += v; }
|
||||
void offset(coordf_t delta);
|
||||
BoundingBoxBase<PointClass> inflated(coordf_t delta) const throw() { BoundingBoxBase<PointClass> out(*this); out.offset(delta); return out; }
|
||||
BoundingBox3Base<PointClass> inflated(coordf_t delta) const throw() { BoundingBox3Base<PointClass> out(*this); out.offset(delta); return out; }
|
||||
PointClass center() const;
|
||||
coordf_t max_size() const;
|
||||
|
||||
|
405
src/libslic3r/BuildVolume.cpp
Normal file
405
src/libslic3r/BuildVolume.cpp
Normal file
@ -0,0 +1,405 @@
|
||||
#include "BuildVolume.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Geometry/ConvexHull.hpp"
|
||||
#include "GCode/GCodeProcessor.hpp"
|
||||
#include "Point.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
BuildVolume::BuildVolume(const std::vector<Vec2d> &bed_shape, const double max_print_height) : m_bed_shape(bed_shape), m_max_print_height(max_print_height)
|
||||
{
|
||||
assert(max_print_height >= 0);
|
||||
|
||||
m_polygon = Polygon::new_scale(bed_shape);
|
||||
|
||||
// Calcuate various metrics of the input polygon.
|
||||
m_convex_hull = Geometry::convex_hull(m_polygon.points);
|
||||
m_bbox = get_extents(m_convex_hull);
|
||||
m_area = m_polygon.area();
|
||||
|
||||
BoundingBoxf bboxf = get_extents(bed_shape);
|
||||
m_bboxf = BoundingBoxf3{ to_3d(bboxf.min, 0.), to_3d(bboxf.max, max_print_height) };
|
||||
|
||||
if (bed_shape.size() >= 4 && std::abs((m_area - double(m_bbox.size().x()) * double(m_bbox.size().y()))) < sqr(SCALED_EPSILON)) {
|
||||
// Square print bed, use the bounding box for collision detection.
|
||||
m_type = Type::Rectangle;
|
||||
m_circle.center = 0.5 * (m_bbox.min.cast<double>() + m_bbox.max.cast<double>());
|
||||
m_circle.radius = 0.5 * m_bbox.size().cast<double>().norm();
|
||||
} else if (bed_shape.size() > 3) {
|
||||
// Circle was discretized, formatted into text with limited accuracy, thus the circle was deformed.
|
||||
// RANSAC is slightly more accurate than the iterative Taubin / Newton method with such an input.
|
||||
// m_circle = Geometry::circle_taubin_newton(bed_shape);
|
||||
m_circle = Geometry::circle_ransac(bed_shape);
|
||||
bool is_circle = true;
|
||||
#ifndef NDEBUG
|
||||
// Measuring maximum absolute error of interpolating an input polygon with circle.
|
||||
double max_error = 0;
|
||||
#endif // NDEBUG
|
||||
Vec2d prev = bed_shape.back();
|
||||
for (const Vec2d &p : bed_shape) {
|
||||
#ifndef NDEBUG
|
||||
max_error = std::max(max_error, std::abs((p - m_circle.center).norm() - m_circle.radius));
|
||||
#endif // NDEBUG
|
||||
if (// Polygon vertices must lie very close the circle.
|
||||
std::abs((p - m_circle.center).norm() - m_circle.radius) > 0.005 ||
|
||||
// Midpoints of polygon edges must not undercat more than 3mm. This corresponds to 72 edges per circle generated by BedShapePanel::update_shape().
|
||||
m_circle.radius - (0.5 * (prev + p) - m_circle.center).norm() > 3.) {
|
||||
is_circle = false;
|
||||
break;
|
||||
}
|
||||
prev = p;
|
||||
}
|
||||
if (is_circle) {
|
||||
m_type = Type::Circle;
|
||||
m_circle.center = scaled<double>(m_circle.center);
|
||||
m_circle.radius = scaled<double>(m_circle.radius);
|
||||
}
|
||||
}
|
||||
|
||||
if (bed_shape.size() >= 3 && m_type == Type::Invalid) {
|
||||
// Circle check is not used for Convex / Custom shapes, fill it with something reasonable.
|
||||
m_circle = Geometry::smallest_enclosing_circle_welzl(m_convex_hull.points);
|
||||
m_type = (m_convex_hull.area() - m_area) < sqr(SCALED_EPSILON) ? Type::Convex : Type::Custom;
|
||||
// Initialize the top / bottom decomposition for inside convex polygon check. Do it with two different epsilons applied.
|
||||
auto convex_decomposition = [](const Polygon &in, double epsilon) {
|
||||
Polygon src = expand(in, float(epsilon)).front();
|
||||
std::vector<Vec2d> pts;
|
||||
pts.reserve(src.size());
|
||||
for (const Point &pt : src.points)
|
||||
pts.emplace_back(unscaled<double>(pt.cast<double>().eval()));
|
||||
return Geometry::decompose_convex_polygon_top_bottom(pts);
|
||||
};
|
||||
m_top_bottom_convex_hull_decomposition_scene = convex_decomposition(m_convex_hull, SceneEpsilon);
|
||||
m_top_bottom_convex_hull_decomposition_bed = convex_decomposition(m_convex_hull, BedEpsilon);
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "BuildVolume bed_shape clasified as: " << this->type_name();
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Tests intersections of projected triangles, not just their vertices against a bounding box.
|
||||
// This test also correctly evaluates collision of a non-convex object with the bounding box.
|
||||
// Not used, slower than simple bounding box collision check and nobody complained about the inaccuracy of the simple test.
|
||||
static inline BuildVolume::ObjectState rectangle_test(const indexed_triangle_set &its, const Transform3f &trafo, const Vec2f min, const Vec2f max, const float max_z)
|
||||
{
|
||||
bool inside = false;
|
||||
bool outside = false;
|
||||
|
||||
auto sign = [](const Vec3f& pt) -> char { return pt.z() > 0 ? 1 : pt.z() < 0 ? -1 : 0; };
|
||||
|
||||
// Returns true if both inside and outside are set, thus early exit.
|
||||
auto test_intersection = [&inside, &outside, min, max, max_z](const Vec3f& p1, const Vec3f& p2, const Vec3f& p3) -> bool {
|
||||
// First test whether the triangle is completely inside or outside the bounding box.
|
||||
Vec3f pmin = p1.cwiseMin(p2).cwiseMin(p3);
|
||||
Vec3f pmax = p1.cwiseMax(p2).cwiseMax(p3);
|
||||
bool tri_inside = false;
|
||||
bool tri_outside = false;
|
||||
if (pmax.x() < min.x() || pmin.x() > max.x() || pmax.y() < min.y() || pmin.y() > max.y()) {
|
||||
// Separated by one of the rectangle sides.
|
||||
tri_outside = true;
|
||||
} else if (pmin.x() >= min.x() && pmax.x() <= max.x() && pmin.y() >= min.y() && pmax.y() <= max.y()) {
|
||||
// Fully inside the rectangle.
|
||||
tri_inside = true;
|
||||
} else {
|
||||
// Bounding boxes overlap. Test triangle sides against the bbox corners.
|
||||
Vec2f v1(- p2.y() + p1.y(), p2.x() - p1.x());
|
||||
Vec2f v2(- p2.y() + p2.y(), p3.x() - p2.x());
|
||||
Vec2f v3(- p1.y() + p3.y(), p1.x() - p3.x());
|
||||
bool ccw = cross2(v1, v2) > 0;
|
||||
for (const Vec2f &p : { Vec2f{ min.x(), min.y() }, Vec2f{ min.x(), max.y() }, Vec2f{ max.x(), min.y() }, Vec2f{ max.x(), max.y() } }) {
|
||||
auto dot = v1.dot(p);
|
||||
if (ccw ? dot >= 0 : dot <= 0)
|
||||
tri_inside = true;
|
||||
else
|
||||
tri_outside = true;
|
||||
}
|
||||
}
|
||||
inside |= tri_inside;
|
||||
outside |= tri_outside;
|
||||
return inside && outside;
|
||||
};
|
||||
|
||||
// Edge crosses the z plane. Calculate intersection point with the plane.
|
||||
auto clip_edge = [](const Vec3f &p1, const Vec3f &p2) -> Vec3f {
|
||||
const float t = (world_min_z - p1.z()) / (p2.z() - p1.z());
|
||||
return { p1.x() + (p2.x() - p1.x()) * t, p1.y() + (p2.y() - p1.y()) * t, world_min_z };
|
||||
};
|
||||
|
||||
// Clip at (p1, p2), p3 must be on the clipping plane.
|
||||
// Returns true if both inside and outside are set, thus early exit.
|
||||
auto clip_and_test1 = [&test_intersection, &clip_edge](const Vec3f &p1, const Vec3f &p2, const Vec3f &p3, bool p1above) -> bool {
|
||||
Vec3f pa = clip_edge(p1, p2);
|
||||
return p1above ? test_intersection(p1, pa, p3) : test_intersection(pa, p2, p3);
|
||||
};
|
||||
|
||||
// Clip at (p1, p2) and (p2, p3).
|
||||
// Returns true if both inside and outside are set, thus early exit.
|
||||
auto clip_and_test2 = [&test_intersection, &clip_edge](const Vec3f &p1, const Vec3f &p2, const Vec3f &p3, bool p2above) -> bool {
|
||||
Vec3f pa = clip_edge(p1, p2);
|
||||
Vec3f pb = clip_edge(p2, p3);
|
||||
return p2above ? test_intersection(pa, p2, pb) : test_intersection(p1, pa, p3) || test_intersection(p3, pa, pb);
|
||||
};
|
||||
|
||||
for (const stl_triangle_vertex_indices &tri : its.indices) {
|
||||
const Vec3f pts[3] = { trafo * its.vertices[tri(0)], trafo * its.vertices[tri(1)], trafo * its.vertices[tri(2)] };
|
||||
char signs[3] = { sign(pts[0]), sign(pts[1]), sign(pts[2]) };
|
||||
bool clips[3] = { signs[0] * signs[1] == -1, signs[1] * signs[2] == -1, signs[2] * signs[0] == -1 };
|
||||
if (clips[0]) {
|
||||
if (clips[1]) {
|
||||
// Clipping at (pt0, pt1) and (pt1, pt2).
|
||||
if (clip_and_test2(pts[0], pts[1], pts[2], signs[1] > 0))
|
||||
break;
|
||||
} else if (clips[2]) {
|
||||
// Clipping at (pt0, pt1) and (pt0, pt2).
|
||||
if (clip_and_test2(pts[2], pts[0], pts[1], signs[0] > 0))
|
||||
break;
|
||||
} else {
|
||||
// Clipping at (pt0, pt1), pt2 must be on the clipping plane.
|
||||
if (clip_and_test1(pts[0], pts[1], pts[2], signs[0] > 0))
|
||||
break;
|
||||
}
|
||||
} else if (clips[1]) {
|
||||
if (clips[2]) {
|
||||
// Clipping at (pt1, pt2) and (pt0, pt2).
|
||||
if (clip_and_test2(pts[0], pts[1], pts[2], signs[1] > 0))
|
||||
break;
|
||||
} else {
|
||||
// Clipping at (pt1, pt2), pt0 must be on the clipping plane.
|
||||
if (clip_and_test1(pts[1], pts[2], pts[0], signs[1] > 0))
|
||||
break;
|
||||
}
|
||||
} else if (clips[2]) {
|
||||
// Clipping at (pt0, pt2), pt1 must be on the clipping plane.
|
||||
if (clip_and_test1(pts[2], pts[0], pts[1], signs[2] > 0))
|
||||
break;
|
||||
} else if (signs[0] >= 0 && signs[1] >= 0 && signs[2] >= 0) {
|
||||
// The triangle is above or on the clipping plane.
|
||||
if (test_intersection(pts[0], pts[1], pts[2]))
|
||||
break;
|
||||
}
|
||||
}
|
||||
return inside ? (outside ? BuildVolume::ObjectState::Colliding : BuildVolume::ObjectState::Inside) : BuildVolume::ObjectState::Outside;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Trim the input transformed triangle mesh with print bed and test the remaining vertices with is_inside callback.
|
||||
// Return inside / colliding / outside state.
|
||||
template<typename InsideFn>
|
||||
BuildVolume::ObjectState object_state_templ(const indexed_triangle_set &its, const Transform3f &trafo, bool may_be_below_bed, InsideFn is_inside)
|
||||
{
|
||||
size_t num_inside = 0;
|
||||
size_t num_above = 0;
|
||||
bool inside = false;
|
||||
bool outside = false;
|
||||
static constexpr const auto world_min_z = float(-BuildVolume::SceneEpsilon);
|
||||
|
||||
if (may_be_below_bed)
|
||||
{
|
||||
// Slower test, needs to clip the object edges with the print bed plane.
|
||||
// 1) Allocate transformed vertices with their position with respect to print bed surface.
|
||||
std::vector<char> sides;
|
||||
sides.reserve(its.vertices.size());
|
||||
|
||||
const auto sign = [](const stl_vertex& pt) { return pt.z() > world_min_z ? 1 : pt.z() < world_min_z ? -1 : 0; };
|
||||
|
||||
for (const stl_vertex &v : its.vertices) {
|
||||
const stl_vertex pt = trafo * v;
|
||||
const int s = sign(pt);
|
||||
sides.emplace_back(s);
|
||||
if (s >= 0) {
|
||||
// Vertex above or on print bed surface. Test whether it is inside the build volume.
|
||||
++ num_above;
|
||||
if (is_inside(pt))
|
||||
++ num_inside;
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Calculate intersections of triangle edges with the build surface.
|
||||
inside = num_inside > 0;
|
||||
outside = num_inside < num_above;
|
||||
if (num_above < its.vertices.size() && ! (inside && outside)) {
|
||||
// Not completely above the build surface and status may still change by testing edges intersecting the build platform.
|
||||
for (const stl_triangle_vertex_indices &tri : its.indices) {
|
||||
const int s[3] = { sides[tri(0)], sides[tri(1)], sides[tri(2)] };
|
||||
if (std::min(s[0], std::min(s[1], s[2])) < 0 && std::max(s[0], std::max(s[1], s[2])) > 0) {
|
||||
// Some edge of this triangle intersects the build platform. Calculate the intersection.
|
||||
int iprev = 2;
|
||||
for (int iedge = 0; iedge < 3; ++ iedge) {
|
||||
if (s[iprev] * s[iedge] == -1) {
|
||||
// edge intersects the build surface. Calculate intersection point.
|
||||
const stl_vertex p1 = trafo * its.vertices[tri(iprev)];
|
||||
const stl_vertex p2 = trafo * its.vertices[tri(iedge)];
|
||||
assert(sign(p1) == s[iprev]);
|
||||
assert(sign(p2) == s[iedge]);
|
||||
assert(p1.z() * p2.z() < 0);
|
||||
// Edge crosses the z plane. Calculate intersection point with the plane.
|
||||
const float t = (world_min_z - p1.z()) / (p2.z() - p1.z());
|
||||
(is_inside(Vec3f(p1.x() + (p2.x() - p1.x()) * t, p1.y() + (p2.y() - p1.y()) * t, world_min_z)) ? inside : outside) = true;
|
||||
}
|
||||
iprev = iedge;
|
||||
}
|
||||
if (inside && outside)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Much simpler and faster code, not clipping the object with the print bed.
|
||||
assert(! may_be_below_bed);
|
||||
num_above = its.vertices.size();
|
||||
for (const stl_vertex &v : its.vertices) {
|
||||
const stl_vertex pt = trafo * v;
|
||||
assert(pt.z() >= world_min_z);
|
||||
if (is_inside(pt))
|
||||
++ num_inside;
|
||||
}
|
||||
inside = num_inside > 0;
|
||||
outside = num_inside < num_above;
|
||||
}
|
||||
|
||||
return inside ? (outside ? BuildVolume::ObjectState::Colliding : BuildVolume::ObjectState::Inside) : BuildVolume::ObjectState::Outside;
|
||||
}
|
||||
|
||||
BuildVolume::ObjectState BuildVolume::object_state(const indexed_triangle_set &its, const Transform3f &trafo, bool may_be_below_bed) const
|
||||
{
|
||||
switch (m_type) {
|
||||
case Type::Rectangle:
|
||||
{
|
||||
BoundingBox3Base<Vec3d> build_volume = this->bounding_volume().inflated(SceneEpsilon);
|
||||
BoundingBox3Base<Vec3f> build_volumef(build_volume.min.cast<float>(), build_volume.max.cast<float>());
|
||||
if (m_max_print_height == 0)
|
||||
build_volume.max.z() = std::numeric_limits<double>::max();
|
||||
// The following test correctly interprets intersection of a non-convex object with a rectangular build volume.
|
||||
//return rectangle_test(its, trafo, to_2d(build_volume.min), to_2d(build_volume.max), build_volume.max.z());
|
||||
//FIXME This test does NOT correctly interprets intersection of a non-convex object with a rectangular build volume.
|
||||
return object_state_templ(its, trafo, may_be_below_bed, [build_volumef](const Vec3f &pt) { return build_volumef.contains(pt); });
|
||||
}
|
||||
case Type::Circle:
|
||||
{
|
||||
Geometry::Circlef circle { unscaled<float>(m_circle.center), unscaled<float>(m_circle.radius + SceneEpsilon) };
|
||||
return m_max_print_height == 0 ?
|
||||
object_state_templ(its, trafo, may_be_below_bed, [circle](const Vec3f &pt) { return circle.contains(to_2d(pt)); }) :
|
||||
object_state_templ(its, trafo, may_be_below_bed, [circle, z = m_max_print_height + SceneEpsilon](const Vec3f &pt) { return pt.z() < z && circle.contains(to_2d(pt)); });
|
||||
}
|
||||
case Type::Convex:
|
||||
//FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently.
|
||||
case Type::Custom:
|
||||
return m_max_print_height == 0 ?
|
||||
object_state_templ(its, trafo, may_be_below_bed, [this](const Vec3f &pt) { return Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_scene, to_2d(pt).cast<double>()); }) :
|
||||
object_state_templ(its, trafo, may_be_below_bed, [this, z = m_max_print_height + SceneEpsilon](const Vec3f &pt) { return pt.z() < z && Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_scene, to_2d(pt).cast<double>()); });
|
||||
case Type::Invalid:
|
||||
default:
|
||||
return ObjectState::Inside;
|
||||
}
|
||||
}
|
||||
|
||||
BuildVolume::ObjectState BuildVolume::volume_state_bbox(const BoundingBoxf3 &volume_bbox) const
|
||||
{
|
||||
assert(m_type == Type::Rectangle);
|
||||
BoundingBox3Base<Vec3d> build_volume = this->bounding_volume().inflated(SceneEpsilon);
|
||||
if (m_max_print_height == 0)
|
||||
build_volume.max.z() = std::numeric_limits<double>::max();
|
||||
return build_volume.contains(volume_bbox) ? ObjectState::Inside : build_volume.intersects(volume_bbox) ? ObjectState::Colliding : ObjectState::Outside;
|
||||
}
|
||||
|
||||
bool BuildVolume::all_paths_inside(const GCodeProcessorResult &paths, const BoundingBoxf3 &paths_bbox) const
|
||||
{
|
||||
auto move_valid = [](const GCodeProcessorResult::MoveVertex &move) {
|
||||
return move.type == EMoveType::Extrude && move.extrusion_role != erCustom && move.width != 0.f && move.height != 0.f;
|
||||
};
|
||||
static constexpr const double epsilon = BedEpsilon;
|
||||
|
||||
switch (m_type) {
|
||||
case Type::Rectangle:
|
||||
{
|
||||
BoundingBox3Base<Vec3d> build_volume = this->bounding_volume().inflated(epsilon);
|
||||
if (m_max_print_height == 0)
|
||||
build_volume.max.z() = std::numeric_limits<double>::max();
|
||||
return build_volume.contains(paths_bbox);
|
||||
}
|
||||
case Type::Circle:
|
||||
{
|
||||
const Vec2f c = unscaled<float>(m_circle.center);
|
||||
const float r = unscaled<double>(m_circle.radius) + epsilon;
|
||||
const float r2 = sqr(r);
|
||||
return m_max_print_height == 0 ?
|
||||
std::all_of(paths.moves.begin(), paths.moves.end(), [move_valid, c, r2](const GCodeProcessorResult::MoveVertex &move)
|
||||
{ return ! move_valid(move) || (to_2d(move.position) - c).squaredNorm() <= r2; }) :
|
||||
std::all_of(paths.moves.begin(), paths.moves.end(), [move_valid, c, r2, z = m_max_print_height + epsilon](const GCodeProcessorResult::MoveVertex& move)
|
||||
{ return ! move_valid(move) || ((to_2d(move.position) - c).squaredNorm() <= r2 && move.position.z() <= z); });
|
||||
}
|
||||
case Type::Convex:
|
||||
//FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently.
|
||||
case Type::Custom:
|
||||
return m_max_print_height == 0 ?
|
||||
std::all_of(paths.moves.begin(), paths.moves.end(), [move_valid, this](const GCodeProcessorResult::MoveVertex &move)
|
||||
{ return ! move_valid(move) || Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_bed, to_2d(move.position).cast<double>()); }) :
|
||||
std::all_of(paths.moves.begin(), paths.moves.end(), [move_valid, this, z = m_max_print_height + epsilon](const GCodeProcessorResult::MoveVertex &move)
|
||||
{ return ! move_valid(move) || (Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_bed, to_2d(move.position).cast<double>()) && move.position.z() <= z); });
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Fn>
|
||||
inline bool all_inside_vertices_normals_interleaved(const std::vector<float> &paths, Fn fn)
|
||||
{
|
||||
for (auto it = paths.begin(); it != paths.end(); ) {
|
||||
it += 3;
|
||||
if (! fn({ *it, *(it + 1), *(it + 2) }))
|
||||
return false;
|
||||
it += 3;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BuildVolume::all_paths_inside_vertices_and_normals_interleaved(const std::vector<float> &paths, const Eigen::AlignedBox<float, 3> &paths_bbox) const
|
||||
{
|
||||
assert(paths.size() % 6 == 0);
|
||||
static constexpr const double epsilon = BedEpsilon;
|
||||
switch (m_type) {
|
||||
case Type::Rectangle:
|
||||
{
|
||||
BoundingBox3Base<Vec3d> build_volume = this->bounding_volume().inflated(epsilon);
|
||||
if (m_max_print_height == 0)
|
||||
build_volume.max.z() = std::numeric_limits<double>::max();
|
||||
return build_volume.contains(paths_bbox.min().cast<double>()) && build_volume.contains(paths_bbox.max().cast<double>());
|
||||
}
|
||||
case Type::Circle:
|
||||
{
|
||||
const Vec2f c = unscaled<float>(m_circle.center);
|
||||
const float r = unscaled<double>(m_circle.radius) + float(epsilon);
|
||||
const float r2 = sqr(r);
|
||||
return m_max_print_height == 0 ?
|
||||
all_inside_vertices_normals_interleaved(paths, [c, r2](Vec3f p) { return (to_2d(p) - c).squaredNorm() <= r2; }) :
|
||||
all_inside_vertices_normals_interleaved(paths, [c, r2, z = m_max_print_height + epsilon](Vec3f p) { return (to_2d(p) - c).squaredNorm() <= r2 && p.z() <= z; });
|
||||
}
|
||||
case Type::Convex:
|
||||
//FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently.
|
||||
case Type::Custom:
|
||||
return m_max_print_height == 0 ?
|
||||
all_inside_vertices_normals_interleaved(paths, [this](Vec3f p) { return Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_bed, to_2d(p).cast<double>()); }) :
|
||||
all_inside_vertices_normals_interleaved(paths, [this, z = m_max_print_height + epsilon](Vec3f p) { return Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_bed, to_2d(p).cast<double>()) && p.z() <= z; });
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::string_view BuildVolume::type_name(Type type)
|
||||
{
|
||||
using namespace std::literals;
|
||||
switch (type) {
|
||||
case Type::Invalid: return "Invalid"sv;
|
||||
case Type::Rectangle: return "Rectangle"sv;
|
||||
case Type::Circle: return "Circle"sv;
|
||||
case Type::Convex: return "Convex"sv;
|
||||
case Type::Custom: return "Custom"sv;
|
||||
}
|
||||
// make visual studio happy
|
||||
assert(false);
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
120
src/libslic3r/BuildVolume.hpp
Normal file
120
src/libslic3r/BuildVolume.hpp
Normal file
@ -0,0 +1,120 @@
|
||||
#ifndef slic3r_BuildVolume_hpp_
|
||||
#define slic3r_BuildVolume_hpp_
|
||||
|
||||
#include "Point.hpp"
|
||||
#include "Geometry/Circle.hpp"
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
struct GCodeProcessorResult;
|
||||
|
||||
// For collision detection of objects and G-code (extrusion paths) against the build volume.
|
||||
class BuildVolume
|
||||
{
|
||||
public:
|
||||
enum class Type : unsigned char
|
||||
{
|
||||
// Not set yet or undefined.
|
||||
Invalid,
|
||||
// Rectangular print bed. Most common, cheap to work with.
|
||||
Rectangle,
|
||||
// Circular print bed. Common on detals, cheap to work with.
|
||||
Circle,
|
||||
// Convex print bed. Complex to process.
|
||||
Convex,
|
||||
// Some non convex shape.
|
||||
Custom
|
||||
};
|
||||
|
||||
// Initialized to empty, all zeros, Invalid.
|
||||
BuildVolume() {}
|
||||
// Initialize from PrintConfig::bed_shape and PrintConfig::max_print_height
|
||||
BuildVolume(const std::vector<Vec2d> &bed_shape, const double max_print_height);
|
||||
|
||||
// Source data, unscaled coordinates.
|
||||
const std::vector<Vec2d>& bed_shape() const { return m_bed_shape; }
|
||||
double max_print_height() const { return m_max_print_height; }
|
||||
|
||||
// Derived data
|
||||
Type type() const { return m_type; }
|
||||
// Format the type for console output.
|
||||
static std::string_view type_name(Type type);
|
||||
std::string_view type_name() const { return type_name(m_type); }
|
||||
bool valid() const { return m_type != Type::Invalid; }
|
||||
// Same as bed_shape(), but scaled coordinates.
|
||||
const Polygon& polygon() const { return m_polygon; }
|
||||
// Bounding box of polygon(), scaled.
|
||||
const BoundingBox& bounding_box() const { return m_bbox; }
|
||||
// Bounding volume of bed_shape(), max_print_height(), unscaled.
|
||||
const BoundingBoxf3& bounding_volume() const { return m_bboxf; }
|
||||
BoundingBoxf bounding_volume2d() const { return { to_2d(m_bboxf.min), to_2d(m_bboxf.max) }; };
|
||||
|
||||
// Center of the print bed, unscaled.
|
||||
Vec2d bed_center() const { return to_2d(m_bboxf.center()); }
|
||||
// Convex hull of polygon(), scaled.
|
||||
const Polygon& convex_hull() const { return m_convex_hull; }
|
||||
// Smallest enclosing circle of polygon(), scaled.
|
||||
const Geometry::Circled& circle() const { return m_circle; }
|
||||
|
||||
enum class ObjectState : unsigned char
|
||||
{
|
||||
// Inside the build volume, thus printable.
|
||||
Inside,
|
||||
// Colliding with the build volume boundary, thus not printable and error is shown.
|
||||
Colliding,
|
||||
// Outside of the build volume means the object is ignored: Not printed and no error is shown.
|
||||
Outside
|
||||
};
|
||||
|
||||
// 1) Tests called on the plater.
|
||||
// Using SceneEpsilon for all tests.
|
||||
static constexpr const double SceneEpsilon = EPSILON;
|
||||
// Called by Plater to update Inside / Colliding / Outside state of ModelObjects before slicing.
|
||||
// Called from Model::update_print_volume_state() -> ModelObject::update_instances_print_volume_state()
|
||||
// Using SceneEpsilon
|
||||
ObjectState object_state(const indexed_triangle_set &its, const Transform3f &trafo, bool may_be_below_bed) const;
|
||||
// Called by GLVolumeCollection::check_outside_state() after an object is manipulated with gizmos for example.
|
||||
// Called for a rectangular bed:
|
||||
ObjectState volume_state_bbox(const BoundingBoxf3 &volume_bbox) const;
|
||||
|
||||
// 2) Test called on G-code paths.
|
||||
// Using BedEpsilon for all tests.
|
||||
static constexpr const double BedEpsilon = 3. * EPSILON;
|
||||
// Called on final G-code paths.
|
||||
//FIXME The test does not take the thickness of the extrudates into account!
|
||||
bool all_paths_inside(const GCodeProcessorResult &paths, const BoundingBoxf3 &paths_bbox) const;
|
||||
// Called on initial G-code preview on OpenGL vertex buffer interleaved normals and vertices.
|
||||
bool all_paths_inside_vertices_and_normals_interleaved(const std::vector<float> &paths, const Eigen::AlignedBox<float, 3> &bbox) const;
|
||||
|
||||
private:
|
||||
// Source definition of the print bed geometry (PrintConfig::bed_shape)
|
||||
std::vector<Vec2d> m_bed_shape;
|
||||
// Source definition of the print volume height (PrintConfig::max_print_height)
|
||||
double m_max_print_height;
|
||||
|
||||
// Derived values.
|
||||
Type m_type { Type::Invalid };
|
||||
// Geometry of the print bed, scaled copy of m_bed_shape.
|
||||
Polygon m_polygon;
|
||||
// Scaled snug bounding box around m_polygon.
|
||||
BoundingBox m_bbox;
|
||||
// 3D bounding box around m_shape, m_max_print_height.
|
||||
BoundingBoxf3 m_bboxf;
|
||||
// Area of m_polygon, scaled.
|
||||
double m_area { 0. };
|
||||
// Convex hull of m_polygon, scaled.
|
||||
Polygon m_convex_hull;
|
||||
// For collision detection against a convex build volume. Only filled in for m_type == Convex or Custom.
|
||||
// Variant with SceneEpsilon applied.
|
||||
std::pair<std::vector<Vec2d>, std::vector<Vec2d>> m_top_bottom_convex_hull_decomposition_scene;
|
||||
// Variant with BedEpsilon applied.
|
||||
std::pair<std::vector<Vec2d>, std::vector<Vec2d>> m_top_bottom_convex_hull_decomposition_bed;
|
||||
// Smallest enclosing circle of m_polygon, scaled.
|
||||
Geometry::Circled m_circle { Vec2d::Zero(), 0 };
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_BuildVolume_hpp_
|
@ -23,6 +23,8 @@ add_library(libslic3r STATIC
|
||||
BridgeDetector.hpp
|
||||
Brim.cpp
|
||||
Brim.hpp
|
||||
BuildVolume.cpp
|
||||
BuildVolume.hpp
|
||||
clipper.cpp
|
||||
clipper.hpp
|
||||
ClipperUtils.cpp
|
||||
@ -116,6 +118,8 @@ add_library(libslic3r STATIC
|
||||
Geometry.hpp
|
||||
Geometry/Circle.cpp
|
||||
Geometry/Circle.hpp
|
||||
Geometry/ConvexHull.cpp
|
||||
Geometry/ConvexHull.hpp
|
||||
Geometry/MedialAxis.cpp
|
||||
Geometry/MedialAxis.hpp
|
||||
Geometry/Voronoi.hpp
|
||||
|
@ -542,6 +542,8 @@ static inline Polygons _clipper(ClipperLib::ClipType clipType, TSubj &&subject,
|
||||
return to_polygons(clipper_do<ClipperLib::Paths>(clipType, std::forward<TSubj>(subject), std::forward<TClip>(clip), ClipperLib::pftNonZero, do_safety_offset));
|
||||
}
|
||||
|
||||
Slic3r::Polygons diff(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
|
||||
{ return _clipper(ClipperLib::ctDifference, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::SinglePathProvider(clip.points), do_safety_offset); }
|
||||
Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
|
||||
{ return _clipper(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); }
|
||||
Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset)
|
||||
|
@ -331,6 +331,8 @@ Slic3r::ExPolygons union_safety_offset_ex(const Slic3r::Polygons &polygons);
|
||||
Slic3r::ExPolygons union_safety_offset_ex(const Slic3r::ExPolygons &expolygons);
|
||||
|
||||
// Aliases for the various offset(...) functions, conveying the purpose of the offset.
|
||||
inline Slic3r::Polygons expand(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
|
||||
{ assert(delta > 0); return offset(polygon, delta, joinType, miterLimit); }
|
||||
inline Slic3r::Polygons expand(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
|
||||
{ assert(delta > 0); return offset(polygons, delta, joinType, miterLimit); }
|
||||
inline Slic3r::ExPolygons expand_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
|
||||
@ -378,6 +380,7 @@ inline Slic3r::ExPolygons opening_ex(const Slic3r::Surfaces &surfaces, const flo
|
||||
Slic3r::Lines _clipper_ln(ClipperLib::ClipType clipType, const Slic3r::Lines &subject, const Slic3r::Polygons &clip);
|
||||
|
||||
// Safety offset is applied to the clipping polygons only.
|
||||
Slic3r::Polygons diff(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
|
@ -1764,6 +1764,8 @@ public:
|
||||
// By setting min=0, only nonnegative input is allowed.
|
||||
int min = INT_MIN;
|
||||
int max = INT_MAX;
|
||||
// To check if it's not a typo and a % is missing
|
||||
double max_literal = 1;
|
||||
ConfigOptionMode mode = comSimple;
|
||||
// Legacy names for this configuration option.
|
||||
// Used when parsing legacy configuration file.
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "ExPolygonCollection.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "Geometry/ConvexHull.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include "Exception.hpp"
|
||||
#include "ExtrusionEntity.hpp"
|
||||
#include "EdgeGrid.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "Geometry/ConvexHull.hpp"
|
||||
#include "GCode/PrintExtents.hpp"
|
||||
#include "GCode/WipeTower.hpp"
|
||||
#include "ShortestPath.hpp"
|
||||
@ -622,7 +622,7 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
|
||||
namespace DoExport {
|
||||
// static void update_print_estimated_times_stats(const GCodeProcessor& processor, PrintStatistics& print_statistics)
|
||||
// {
|
||||
// const GCodeProcessor::Result& result = processor.get_result();
|
||||
// const GCodeProcessorResult& result = processor.get_result();
|
||||
// print_statistics.estimated_normal_print_time = get_time_dhms(result.print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].time);
|
||||
// print_statistics.estimated_silent_print_time = processor.is_stealth_time_estimator_enabled() ?
|
||||
// get_time_dhms(result.print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].time) : "N/A";
|
||||
@ -630,7 +630,7 @@ namespace DoExport {
|
||||
|
||||
static void update_print_estimated_stats(const GCodeProcessor& processor, const std::vector<Extruder>& extruders, PrintStatistics& print_statistics)
|
||||
{
|
||||
const GCodeProcessor::Result& result = processor.get_result();
|
||||
const GCodeProcessorResult& result = processor.get_result();
|
||||
print_statistics.estimated_normal_print_time = get_time_dhms(result.print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].time);
|
||||
print_statistics.estimated_silent_print_time = processor.is_stealth_time_estimator_enabled() ?
|
||||
get_time_dhms(result.print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].time) : "N/A";
|
||||
@ -723,7 +723,7 @@ namespace DoExport {
|
||||
}
|
||||
} // namespace DoExport
|
||||
|
||||
void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb)
|
||||
void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb)
|
||||
{
|
||||
PROFILE_CLEAR();
|
||||
|
||||
|
@ -143,7 +143,7 @@ public:
|
||||
|
||||
// throws std::runtime_exception on error,
|
||||
// throws CanceledException through print->throw_if_canceled().
|
||||
void do_export(Print* print, const char* path, GCodeProcessor::Result* result = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
|
||||
void do_export(Print* print, const char* path, GCodeProcessorResult* result = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
|
||||
|
||||
// Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests.
|
||||
const Vec2d& origin() const { return m_origin; }
|
||||
|
@ -343,7 +343,7 @@ void GCodeProcessor::TimeProcessor::reset()
|
||||
machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].enabled = true;
|
||||
}
|
||||
|
||||
void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector<MoveVertex>& moves, std::vector<size_t>& lines_ends)
|
||||
void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector<GCodeProcessorResult::MoveVertex>& moves, std::vector<size_t>& lines_ends)
|
||||
{
|
||||
FilePtr in{ boost::nowide::fopen(filename.c_str(), "rb") };
|
||||
if (in.f == nullptr)
|
||||
@ -636,7 +636,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
|
||||
// updates moves' gcode ids which have been modified by the insertion of the M73 lines
|
||||
unsigned int curr_offset_id = 0;
|
||||
unsigned int total_offset = 0;
|
||||
for (MoveVertex& move : moves) {
|
||||
for (GCodeProcessorResult::MoveVertex& move : moves) {
|
||||
while (curr_offset_id < static_cast<unsigned int>(offsets.size()) && offsets[curr_offset_id].first <= move.gcode_id) {
|
||||
total_offset += offsets[curr_offset_id].second;
|
||||
++curr_offset_id;
|
||||
@ -716,12 +716,10 @@ void GCodeProcessor::UsedFilaments::process_caches(GCodeProcessor* processor)
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
void GCodeProcessor::Result::reset() {
|
||||
moves = std::vector<GCodeProcessor::MoveVertex>();
|
||||
void GCodeProcessorResult::reset() {
|
||||
moves = std::vector<GCodeProcessorResult::MoveVertex>();
|
||||
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();
|
||||
extruders_count = 0;
|
||||
extruder_colors = std::vector<std::string>();
|
||||
@ -731,14 +729,12 @@ void GCodeProcessor::Result::reset() {
|
||||
time = 0;
|
||||
}
|
||||
#else
|
||||
void GCodeProcessor::Result::reset() {
|
||||
void GCodeProcessorResult::reset() {
|
||||
|
||||
moves.clear();
|
||||
lines_ends.clear();
|
||||
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();
|
||||
extruders_count = 0;
|
||||
extruder_colors = std::vector<std::string>();
|
||||
@ -889,9 +885,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
|
||||
if (first_layer_height != nullptr)
|
||||
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)
|
||||
@ -1122,11 +1116,9 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
||||
if (first_layer_height != nullptr)
|
||||
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)
|
||||
@ -1256,7 +1248,7 @@ void GCodeProcessor::process_file(const std::string& filename, std::function<voi
|
||||
m_result.filename = filename;
|
||||
m_result.id = ++s_result_id;
|
||||
// 1st move must be a dummy move
|
||||
m_result.moves.emplace_back(MoveVertex());
|
||||
m_result.moves.emplace_back(GCodeProcessorResult::MoveVertex());
|
||||
size_t parse_line_callback_cntr = 10000;
|
||||
m_parser.parse_file(filename, [this, cancel_callback, &parse_line_callback_cntr](GCodeReader& reader, const GCodeReader::GCodeLine& line) {
|
||||
if (-- parse_line_callback_cntr == 0) {
|
||||
@ -1284,7 +1276,7 @@ void GCodeProcessor::initialize(const std::string& filename)
|
||||
m_result.filename = filename;
|
||||
m_result.id = ++s_result_id;
|
||||
// 1st move must be a dummy move
|
||||
m_result.moves.emplace_back(MoveVertex());
|
||||
m_result.moves.emplace_back(GCodeProcessorResult::MoveVertex());
|
||||
}
|
||||
|
||||
void GCodeProcessor::process_buffer(const std::string &buffer)
|
||||
@ -1298,7 +1290,7 @@ void GCodeProcessor::process_buffer(const std::string &buffer)
|
||||
void GCodeProcessor::finalize(bool post_process)
|
||||
{
|
||||
// update width/height of wipe moves
|
||||
for (MoveVertex& move : m_result.moves) {
|
||||
for (GCodeProcessorResult::MoveVertex& move : m_result.moves) {
|
||||
if (move.type == EMoveType::Wipe) {
|
||||
move.width = Wipe_Width;
|
||||
move.height = Wipe_Height;
|
||||
|
@ -76,6 +76,63 @@ namespace Slic3r {
|
||||
}
|
||||
};
|
||||
|
||||
struct GCodeProcessorResult
|
||||
{
|
||||
struct SettingsIds
|
||||
{
|
||||
std::string print;
|
||||
std::vector<std::string> filament;
|
||||
std::string printer;
|
||||
|
||||
void reset() {
|
||||
print.clear();
|
||||
filament.clear();
|
||||
printer.clear();
|
||||
}
|
||||
};
|
||||
|
||||
struct MoveVertex
|
||||
{
|
||||
unsigned int gcode_id{ 0 };
|
||||
EMoveType type{ EMoveType::Noop };
|
||||
ExtrusionRole extrusion_role{ erNone };
|
||||
unsigned char extruder_id{ 0 };
|
||||
unsigned char cp_color_id{ 0 };
|
||||
Vec3f position{ Vec3f::Zero() }; // mm
|
||||
float delta_extruder{ 0.0f }; // mm
|
||||
float feedrate{ 0.0f }; // mm/s
|
||||
float width{ 0.0f }; // mm
|
||||
float height{ 0.0f }; // mm
|
||||
float mm3_per_mm{ 0.0f };
|
||||
float fan_speed{ 0.0f }; // percentage
|
||||
float temperature{ 0.0f }; // Celsius degrees
|
||||
float time{ 0.0f }; // s
|
||||
|
||||
float volumetric_rate() const { return feedrate * mm3_per_mm; }
|
||||
};
|
||||
|
||||
std::string filename;
|
||||
unsigned int id;
|
||||
std::vector<MoveVertex> moves;
|
||||
// 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;
|
||||
Pointfs bed_shape;
|
||||
float max_print_height;
|
||||
SettingsIds settings_ids;
|
||||
size_t extruders_count;
|
||||
std::vector<std::string> extruder_colors;
|
||||
std::vector<float> filament_diameters;
|
||||
std::vector<float> filament_densities;
|
||||
PrintEstimatedStatistics print_statistics;
|
||||
std::vector<CustomGCode::Item> custom_gcode_per_print_z;
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
int64_t time{ 0 };
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
void reset();
|
||||
};
|
||||
|
||||
|
||||
class GCodeProcessor
|
||||
{
|
||||
static const std::vector<std::string> Reserved_Tags;
|
||||
@ -190,26 +247,6 @@ namespace Slic3r {
|
||||
float time() const;
|
||||
};
|
||||
|
||||
struct MoveVertex
|
||||
{
|
||||
unsigned int gcode_id{ 0 };
|
||||
EMoveType type{ EMoveType::Noop };
|
||||
ExtrusionRole extrusion_role{ erNone };
|
||||
unsigned char extruder_id{ 0 };
|
||||
unsigned char cp_color_id{ 0 };
|
||||
Vec3f position{ Vec3f::Zero() }; // mm
|
||||
float delta_extruder{ 0.0f }; // mm
|
||||
float feedrate{ 0.0f }; // mm/s
|
||||
float width{ 0.0f }; // mm
|
||||
float height{ 0.0f }; // mm
|
||||
float mm3_per_mm{ 0.0f };
|
||||
float fan_speed{ 0.0f }; // percentage
|
||||
float temperature{ 0.0f }; // Celsius degrees
|
||||
float time{ 0.0f }; // s
|
||||
|
||||
float volumetric_rate() const { return feedrate * mm3_per_mm; }
|
||||
};
|
||||
|
||||
private:
|
||||
struct TimeMachine
|
||||
{
|
||||
@ -304,7 +341,7 @@ namespace Slic3r {
|
||||
|
||||
// post process the file with the given filename to add remaining time lines M73
|
||||
// and updates moves' gcode ids accordingly
|
||||
void post_process(const std::string& filename, std::vector<MoveVertex>& moves, std::vector<size_t>& lines_ends);
|
||||
void post_process(const std::string& filename, std::vector<GCodeProcessorResult::MoveVertex>& moves, std::vector<size_t>& lines_ends);
|
||||
};
|
||||
|
||||
struct UsedFilaments // filaments per ColorChange
|
||||
@ -331,43 +368,6 @@ namespace Slic3r {
|
||||
};
|
||||
|
||||
public:
|
||||
struct Result
|
||||
{
|
||||
struct SettingsIds
|
||||
{
|
||||
std::string print;
|
||||
std::vector<std::string> filament;
|
||||
std::string printer;
|
||||
|
||||
void reset() {
|
||||
print.clear();
|
||||
filament.clear();
|
||||
printer.clear();
|
||||
}
|
||||
};
|
||||
std::string filename;
|
||||
unsigned int id;
|
||||
std::vector<MoveVertex> moves;
|
||||
// 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;
|
||||
Pointfs bed_shape;
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
float max_print_height;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
SettingsIds settings_ids;
|
||||
size_t extruders_count;
|
||||
std::vector<std::string> extruder_colors;
|
||||
std::vector<float> filament_diameters;
|
||||
std::vector<float> filament_densities;
|
||||
PrintEstimatedStatistics print_statistics;
|
||||
std::vector<CustomGCode::Item> custom_gcode_per_print_z;
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
int64_t time{ 0 };
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
void reset();
|
||||
};
|
||||
|
||||
class SeamsDetector
|
||||
{
|
||||
bool m_active{ false };
|
||||
@ -394,12 +394,12 @@ namespace Slic3r {
|
||||
// custom gcode markes
|
||||
class OptionsZCorrector
|
||||
{
|
||||
Result& m_result;
|
||||
GCodeProcessorResult& m_result;
|
||||
std::optional<size_t> m_move_id;
|
||||
std::optional<size_t> m_custom_gcode_per_print_z_id;
|
||||
|
||||
public:
|
||||
explicit OptionsZCorrector(Result& result) : m_result(result) {
|
||||
explicit OptionsZCorrector(GCodeProcessorResult& result) : m_result(result) {
|
||||
}
|
||||
|
||||
void set() {
|
||||
@ -413,7 +413,7 @@ namespace Slic3r {
|
||||
|
||||
const Vec3f position = m_result.moves.back().position;
|
||||
|
||||
MoveVertex& move = m_result.moves.emplace_back(m_result.moves[*m_move_id]);
|
||||
GCodeProcessorResult::MoveVertex& move = m_result.moves.emplace_back(m_result.moves[*m_move_id]);
|
||||
move.position = position;
|
||||
move.height = height;
|
||||
m_result.moves.erase(m_result.moves.begin() + *m_move_id);
|
||||
@ -562,7 +562,7 @@ namespace Slic3r {
|
||||
TimeProcessor m_time_processor;
|
||||
UsedFilaments m_used_filaments;
|
||||
|
||||
Result m_result;
|
||||
GCodeProcessorResult m_result;
|
||||
static unsigned int s_result_id;
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
@ -582,8 +582,8 @@ namespace Slic3r {
|
||||
void enable_machine_envelope_processing(bool enabled) { m_time_processor.machine_envelope_processing_enabled = enabled; }
|
||||
void reset();
|
||||
|
||||
const Result& get_result() const { return m_result; }
|
||||
Result&& extract_result() { return std::move(m_result); }
|
||||
const GCodeProcessorResult& get_result() const { return m_result; }
|
||||
GCodeProcessorResult&& extract_result() { return std::move(m_result); }
|
||||
|
||||
// Load a G-code into a stand-alone G-code viewer.
|
||||
// throws CanceledException through print->throw_if_canceled() (sent by the caller as callback).
|
||||
|
@ -24,107 +24,8 @@
|
||||
#define BOOST_NO_CXX17_HDR_STRING_VIEW
|
||||
#endif
|
||||
|
||||
#include <boost/multiprecision/integer.hpp>
|
||||
|
||||
namespace Slic3r { namespace Geometry {
|
||||
|
||||
// This implementation is based on Andrew's monotone chain 2D convex hull algorithm
|
||||
Polygon convex_hull(Points pts)
|
||||
{
|
||||
std::sort(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a.x() < b.x() || (a.x() == b.x() && a.y() < b.y()); });
|
||||
pts.erase(std::unique(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a.x() == b.x() && a.y() == b.y(); }), pts.end());
|
||||
|
||||
Polygon hull;
|
||||
int n = (int)pts.size();
|
||||
if (n >= 3) {
|
||||
int k = 0;
|
||||
hull.points.resize(2 * n);
|
||||
// Build lower hull
|
||||
for (int i = 0; i < n; ++ i) {
|
||||
while (k >= 2 && pts[i].ccw(hull[k-2], hull[k-1]) <= 0)
|
||||
-- k;
|
||||
hull[k ++] = pts[i];
|
||||
}
|
||||
// Build upper hull
|
||||
for (int i = n-2, t = k+1; i >= 0; i--) {
|
||||
while (k >= t && pts[i].ccw(hull[k-2], hull[k-1]) <= 0)
|
||||
-- k;
|
||||
hull[k ++] = pts[i];
|
||||
}
|
||||
hull.points.resize(k);
|
||||
assert(hull.points.front() == hull.points.back());
|
||||
hull.points.pop_back();
|
||||
}
|
||||
return hull;
|
||||
}
|
||||
|
||||
Pointf3s convex_hull(Pointf3s points)
|
||||
{
|
||||
assert(points.size() >= 3);
|
||||
// sort input points
|
||||
std::sort(points.begin(), points.end(), [](const Vec3d &a, const Vec3d &b){ return a.x() < b.x() || (a.x() == b.x() && a.y() < b.y()); });
|
||||
|
||||
int n = points.size(), k = 0;
|
||||
Pointf3s hull;
|
||||
|
||||
if (n >= 3)
|
||||
{
|
||||
hull.resize(2 * n);
|
||||
|
||||
// Build lower hull
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
Point p = Point::new_scale(points[i](0), points[i](1));
|
||||
while (k >= 2)
|
||||
{
|
||||
Point k1 = Point::new_scale(hull[k - 1](0), hull[k - 1](1));
|
||||
Point k2 = Point::new_scale(hull[k - 2](0), hull[k - 2](1));
|
||||
|
||||
if (p.ccw(k2, k1) <= 0)
|
||||
--k;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
hull[k++] = points[i];
|
||||
}
|
||||
|
||||
// Build upper hull
|
||||
for (int i = n - 2, t = k + 1; i >= 0; --i)
|
||||
{
|
||||
Point p = Point::new_scale(points[i](0), points[i](1));
|
||||
while (k >= t)
|
||||
{
|
||||
Point k1 = Point::new_scale(hull[k - 1](0), hull[k - 1](1));
|
||||
Point k2 = Point::new_scale(hull[k - 2](0), hull[k - 2](1));
|
||||
|
||||
if (p.ccw(k2, k1) <= 0)
|
||||
--k;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
hull[k++] = points[i];
|
||||
}
|
||||
|
||||
hull.resize(k);
|
||||
|
||||
assert(hull.front() == hull.back());
|
||||
hull.pop_back();
|
||||
}
|
||||
|
||||
return hull;
|
||||
}
|
||||
|
||||
Polygon convex_hull(const Polygons &polygons)
|
||||
{
|
||||
Points pp;
|
||||
for (Polygons::const_iterator p = polygons.begin(); p != polygons.end(); ++p) {
|
||||
pp.insert(pp.end(), p->points.begin(), p->points.end());
|
||||
}
|
||||
return convex_hull(std::move(pp));
|
||||
}
|
||||
|
||||
bool directions_parallel(double angle1, double angle2, double max_diff)
|
||||
{
|
||||
double diff = fabs(angle1 - angle2);
|
||||
@ -132,14 +33,12 @@ bool directions_parallel(double angle1, double angle2, double 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>
|
||||
bool contains(const std::vector<T> &vector, const Point &point)
|
||||
@ -761,205 +660,4 @@ double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to)
|
||||
return (axis.z() < 0) ? -angle : angle;
|
||||
}
|
||||
|
||||
namespace rotcalip {
|
||||
|
||||
using int256_t = boost::multiprecision::int256_t;
|
||||
using int128_t = boost::multiprecision::int128_t;
|
||||
|
||||
template<class Scalar = int64_t>
|
||||
inline Scalar magnsq(const Point &p)
|
||||
{
|
||||
return Scalar(p.x()) * p.x() + Scalar(p.y()) * p.y();
|
||||
}
|
||||
|
||||
template<class Scalar = int64_t>
|
||||
inline Scalar dot(const Point &a, const Point &b)
|
||||
{
|
||||
return Scalar(a.x()) * b.x() + Scalar(a.y()) * b.y();
|
||||
}
|
||||
|
||||
template<class Scalar = int64_t>
|
||||
inline Scalar dotperp(const Point &a, const Point &b)
|
||||
{
|
||||
return Scalar(a.x()) * b.y() - Scalar(a.y()) * b.x();
|
||||
}
|
||||
|
||||
using boost::multiprecision::abs;
|
||||
|
||||
// Compares the angle enclosed by vectors dir and dirA (alpha) with the angle
|
||||
// enclosed by -dir and dirB (beta). Returns -1 if alpha is less than beta, 0
|
||||
// if they are equal and 1 if alpha is greater than beta. Note that dir is
|
||||
// reversed for beta, because it represents the opposite side of a caliper.
|
||||
int cmp_angles(const Point &dir, const Point &dirA, const Point &dirB) {
|
||||
int128_t dotA = dot(dir, dirA);
|
||||
int128_t dotB = dot(-dir, dirB);
|
||||
int256_t dcosa = int256_t(magnsq(dirB)) * int256_t(abs(dotA)) * dotA;
|
||||
int256_t dcosb = int256_t(magnsq(dirA)) * int256_t(abs(dotB)) * dotB;
|
||||
int256_t diff = dcosa - dcosb;
|
||||
|
||||
return diff > 0? -1 : (diff < 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
// A helper class to navigate on a polygon. Given a vertex index, one can
|
||||
// get the edge belonging to that vertex, the coordinates of the vertex, the
|
||||
// next and previous edges. Stuff that is needed in the rotating calipers algo.
|
||||
class Idx
|
||||
{
|
||||
size_t m_idx;
|
||||
const Polygon *m_poly;
|
||||
public:
|
||||
explicit Idx(const Polygon &p): m_idx{0}, m_poly{&p} {}
|
||||
explicit Idx(size_t idx, const Polygon &p): m_idx{idx}, m_poly{&p} {}
|
||||
|
||||
size_t idx() const { return m_idx; }
|
||||
void set_idx(size_t i) { m_idx = i; }
|
||||
size_t next() const { return (m_idx + 1) % m_poly->size(); }
|
||||
size_t inc() { return m_idx = (m_idx + 1) % m_poly->size(); }
|
||||
Point prev_dir() const {
|
||||
return pt() - (*m_poly)[(m_idx + m_poly->size() - 1) % m_poly->size()];
|
||||
}
|
||||
|
||||
const Point &pt() const { return (*m_poly)[m_idx]; }
|
||||
const Point dir() const { return (*m_poly)[next()] - pt(); }
|
||||
const Point next_dir() const
|
||||
{
|
||||
return (*m_poly)[(m_idx + 2) % m_poly->size()] - (*m_poly)[next()];
|
||||
}
|
||||
const Polygon &poly() const { return *m_poly; }
|
||||
};
|
||||
|
||||
enum class AntipodalVisitMode { Full, EdgesOnly };
|
||||
|
||||
// Visit all antipodal pairs starting from the initial ia, ib pair which
|
||||
// has to be a valid antipodal pair (not checked). fn is called for every
|
||||
// antipodal pair encountered including the initial one.
|
||||
// The callback Fn has a signiture of bool(size_t i, size_t j, const Point &dir)
|
||||
// where i,j are the vertex indices of the antipodal pair and dir is the
|
||||
// direction of the calipers touching the i vertex.
|
||||
template<AntipodalVisitMode mode = AntipodalVisitMode::Full, class Fn>
|
||||
void visit_antipodals (Idx& ia, Idx &ib, Fn &&fn)
|
||||
{
|
||||
// Set current caliper direction to be the lower edge angle from X axis
|
||||
int cmp = cmp_angles(ia.prev_dir(), ia.dir(), ib.dir());
|
||||
Idx *current = cmp <= 0 ? &ia : &ib, *other = cmp <= 0 ? &ib : &ia;
|
||||
Idx *initial = current;
|
||||
bool visitor_continue = true;
|
||||
|
||||
size_t start = initial->idx();
|
||||
bool finished = false;
|
||||
|
||||
while (visitor_continue && !finished) {
|
||||
Point current_dir_a = current == &ia ? current->dir() : -current->dir();
|
||||
visitor_continue = fn(ia.idx(), ib.idx(), current_dir_a);
|
||||
|
||||
// Parallel edges encountered. An additional pair of antipodals
|
||||
// can be yielded.
|
||||
if constexpr (mode == AntipodalVisitMode::Full)
|
||||
if (cmp == 0 && visitor_continue) {
|
||||
visitor_continue = fn(current == &ia ? ia.idx() : ia.next(),
|
||||
current == &ib ? ib.idx() : ib.next(),
|
||||
current_dir_a);
|
||||
}
|
||||
|
||||
cmp = cmp_angles(current->dir(), current->next_dir(), other->dir());
|
||||
|
||||
current->inc();
|
||||
if (cmp > 0) {
|
||||
std::swap(current, other);
|
||||
}
|
||||
|
||||
if (initial->idx() == start) finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rotcalip
|
||||
|
||||
bool convex_polygons_intersect(const Polygon &A, const Polygon &B)
|
||||
{
|
||||
using namespace rotcalip;
|
||||
|
||||
// Establish starting antipodals as extremes in XY plane. Use the
|
||||
// easily obtainable bounding boxes to check if A and B is disjoint
|
||||
// and return false if the are.
|
||||
struct BB
|
||||
{
|
||||
size_t xmin = 0, xmax = 0, ymin = 0, ymax = 0;
|
||||
const Polygon &P;
|
||||
static bool cmpy(const Point &l, const Point &u)
|
||||
{
|
||||
return l.y() < u.y() || (l.y() == u.y() && l.x() < u.x());
|
||||
}
|
||||
|
||||
BB(const Polygon &poly): P{poly}
|
||||
{
|
||||
for (size_t i = 0; i < P.size(); ++i) {
|
||||
if (P[i] < P[xmin]) xmin = i;
|
||||
if (P[xmax] < P[i]) xmax = i;
|
||||
if (cmpy(P[i], P[ymin])) ymin = i;
|
||||
if (cmpy(P[ymax], P[i])) ymax = i;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BB bA{A}, bB{B};
|
||||
BoundingBox bbA{{A[bA.xmin].x(), A[bA.ymin].y()}, {A[bA.xmax].x(), A[bA.ymax].y()}};
|
||||
BoundingBox bbB{{B[bB.xmin].x(), B[bB.ymin].y()}, {B[bB.xmax].x(), B[bB.ymax].y()}};
|
||||
|
||||
// if (!bbA.overlap(bbB))
|
||||
// return false;
|
||||
|
||||
// Establish starting antipodals as extreme vertex pairs in X or Y direction
|
||||
// which reside on different polygons. If no such pair is found, the two
|
||||
// polygons are certainly not disjoint.
|
||||
Idx imin{bA.xmin, A}, imax{bB.xmax, B};
|
||||
if (B[bB.xmin] < imin.pt()) imin = Idx{bB.xmin, B};
|
||||
if (imax.pt() < A[bA.xmax]) imax = Idx{bA.xmax, A};
|
||||
if (&imin.poly() == &imax.poly()) {
|
||||
imin = Idx{bA.ymin, A};
|
||||
imax = Idx{bB.ymax, B};
|
||||
if (B[bB.ymin] < imin.pt()) imin = Idx{bB.ymin, B};
|
||||
if (imax.pt() < A[bA.ymax]) imax = Idx{bA.ymax, A};
|
||||
}
|
||||
|
||||
if (&imin.poly() == &imax.poly())
|
||||
return true;
|
||||
|
||||
bool found_divisor = false;
|
||||
visit_antipodals<AntipodalVisitMode::EdgesOnly>(
|
||||
imin, imax,
|
||||
[&imin, &imax, &found_divisor](size_t ia, size_t ib, const Point &dir) {
|
||||
// std::cout << "A" << ia << " B" << ib << " dir " <<
|
||||
// dir.x() << " " << dir.y() << std::endl;
|
||||
const Polygon &A = imin.poly(), &B = imax.poly();
|
||||
|
||||
Point ref_a = A[(ia + 2) % A.size()], ref_b = B[(ib + 2) % B.size()];
|
||||
|
||||
bool is_left_a = dotperp( dir, ref_a - A[ia]) > 0;
|
||||
bool is_left_b = dotperp(-dir, ref_b - B[ib]) > 0;
|
||||
|
||||
// If both reference points are on the left (or right) of their
|
||||
// respective support lines and the opposite support line is to
|
||||
// the right (or left), the divisor line is found. We only test
|
||||
// the reference point, as by definition, if that is on one side,
|
||||
// all the other points must be on the same side of a support
|
||||
// line. If the support lines are collinear, the polygons must be
|
||||
// on the same side of their respective support lines.
|
||||
|
||||
auto d = dotperp(dir, B[ib] - A[ia]);
|
||||
if (d == 0) {
|
||||
// The caliper lines are collinear, not just parallel
|
||||
found_divisor = (is_left_a && is_left_b) || (!is_left_a && !is_left_b);
|
||||
} else if (d > 0) { // B is to the left of (A, A+1)
|
||||
found_divisor = !is_left_a && !is_left_b;
|
||||
} else { // B is to the right of (A, A+1)
|
||||
found_divisor = is_left_a && is_left_b;
|
||||
}
|
||||
|
||||
return !found_divisor;
|
||||
});
|
||||
|
||||
// Intersects if the divisor was not found
|
||||
return !found_divisor;
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::Geometry
|
||||
|
@ -72,32 +72,6 @@ static inline bool is_ccw(const Polygon &poly)
|
||||
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)
|
||||
{
|
||||
double denom = v1(0) * v2(1) - v2(0) * v1(1);
|
||||
@ -313,14 +287,8 @@ bool liang_barsky_line_clipping(
|
||||
return liang_barsky_line_clipping(x0clip, x1clip, bbox);
|
||||
}
|
||||
|
||||
Pointf3s convex_hull(Pointf3s points);
|
||||
Polygon convex_hull(Points points);
|
||||
Polygon convex_hull(const Polygons &polygons);
|
||||
|
||||
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<typename T> T rad2deg(T angle) { return T(180.0) * angle / T(PI); }
|
||||
double rad2deg_dir(double angle);
|
||||
@ -479,10 +447,6 @@ inline bool is_rotation_ninety_degrees(const Vec3d &rotation)
|
||||
return is_rotation_ninety_degrees(rotation.x()) && is_rotation_ninety_degrees(rotation.y()) && is_rotation_ninety_degrees(rotation.z());
|
||||
}
|
||||
|
||||
// Returns true if the intersection of the two convex polygons A and B
|
||||
// is not an empty set.
|
||||
bool convex_polygons_intersect(const Polygon &A, const Polygon &B);
|
||||
|
||||
} } // namespace Slicer::Geometry
|
||||
|
||||
#endif
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "../Polygon.hpp"
|
||||
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
namespace Slic3r { namespace Geometry {
|
||||
@ -94,4 +95,44 @@ Vec2d circle_center_taubin_newton(const Vec2ds::const_iterator& input_begin, con
|
||||
return center + centroid;
|
||||
}
|
||||
|
||||
Circled circle_taubin_newton(const Vec2ds& input, size_t cycles)
|
||||
{
|
||||
Circled out;
|
||||
if (input.size() < 3) {
|
||||
out = Circled::make_invalid();
|
||||
} else {
|
||||
out.center = circle_center_taubin_newton(input, cycles);
|
||||
out.radius = std::accumulate(input.begin(), input.end(), 0., [&out](double acc, const Vec2d& pt) { return (pt - out.center).norm() + acc; });
|
||||
out.radius /= double(input.size());
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Circled circle_ransac(const Vec2ds& input, size_t iterations)
|
||||
{
|
||||
if (input.size() < 3)
|
||||
return Circled::make_invalid();
|
||||
|
||||
std::mt19937 rng;
|
||||
std::vector<Vec2d> samples;
|
||||
Circled circle_best = Circled::make_invalid();
|
||||
double err_min = std::numeric_limits<double>::max();
|
||||
for (size_t iter = 0; iter < iterations; ++ iter) {
|
||||
samples.clear();
|
||||
std::sample(input.begin(), input.end(), std::back_inserter(samples), 3, rng);
|
||||
Circled c;
|
||||
c.center = Geometry::circle_center(samples[0], samples[1], samples[2], EPSILON);
|
||||
c.radius = std::accumulate(input.begin(), input.end(), 0., [&c](double acc, const Vec2d& pt) { return (pt - c.center).norm() + acc; });
|
||||
c.radius /= double(input.size());
|
||||
double err = 0;
|
||||
for (const Vec2d &pt : input)
|
||||
err = std::max(err, std::abs((pt - c.center).norm() - c.radius));
|
||||
if (err < err_min) {
|
||||
err_min = err;
|
||||
circle_best = c;
|
||||
}
|
||||
}
|
||||
return circle_best;
|
||||
}
|
||||
|
||||
} } // namespace Slic3r::Geometry
|
||||
|
@ -7,14 +7,6 @@
|
||||
|
||||
namespace Slic3r { namespace Geometry {
|
||||
|
||||
/// Find the center of the circle corresponding to the vector of Points as an arc.
|
||||
Point circle_center_taubin_newton(const Points::const_iterator& input_start, const Points::const_iterator& input_end, size_t cycles = 20);
|
||||
inline Point circle_center_taubin_newton(const Points& input, size_t cycles = 20) { return circle_center_taubin_newton(input.cbegin(), input.cend(), cycles); }
|
||||
|
||||
/// Find the center of the circle corresponding to the vector of Pointfs as an arc.
|
||||
Vec2d circle_center_taubin_newton(const Vec2ds::const_iterator& input_start, const Vec2ds::const_iterator& input_end, size_t cycles = 20);
|
||||
inline Vec2d circle_center_taubin_newton(const Vec2ds& input, size_t cycles = 20) { return circle_center_taubin_newton(input.cbegin(), input.cend(), cycles); }
|
||||
|
||||
// https://en.wikipedia.org/wiki/Circumscribed_circle
|
||||
// Circumcenter coordinates, Cartesian coordinates
|
||||
template<typename Vector>
|
||||
@ -100,6 +92,18 @@ using Circled = Circle<Vec2d>;
|
||||
using CircleSqf = CircleSq<Vec2f>;
|
||||
using CircleSqd = CircleSq<Vec2d>;
|
||||
|
||||
/// Find the center of the circle corresponding to the vector of Points as an arc.
|
||||
Point circle_center_taubin_newton(const Points::const_iterator& input_start, const Points::const_iterator& input_end, size_t cycles = 20);
|
||||
inline Point circle_center_taubin_newton(const Points& input, size_t cycles = 20) { return circle_center_taubin_newton(input.cbegin(), input.cend(), cycles); }
|
||||
|
||||
/// Find the center of the circle corresponding to the vector of Pointfs as an arc.
|
||||
Vec2d circle_center_taubin_newton(const Vec2ds::const_iterator& input_start, const Vec2ds::const_iterator& input_end, size_t cycles = 20);
|
||||
inline Vec2d circle_center_taubin_newton(const Vec2ds& input, size_t cycles = 20) { return circle_center_taubin_newton(input.cbegin(), input.cend(), cycles); }
|
||||
Circled circle_taubin_newton(const Vec2ds& input, size_t cycles = 20);
|
||||
|
||||
// Find circle using RANSAC randomized algorithm.
|
||||
Circled circle_ransac(const Vec2ds& input, size_t iterations = 20);
|
||||
|
||||
// Randomized algorithm by Emo Welzl, working with squared radii for efficiency. The returned circle radius is inflated by epsilon.
|
||||
template<typename Vector, typename Points>
|
||||
CircleSq<Vector> smallest_enclosing_circle2_welzl(const Points &points, const typename Vector::Scalar epsilon)
|
||||
|
398
src/libslic3r/Geometry/ConvexHull.cpp
Normal file
398
src/libslic3r/Geometry/ConvexHull.cpp
Normal file
@ -0,0 +1,398 @@
|
||||
#include "libslic3r.h"
|
||||
#include "ConvexHull.hpp"
|
||||
|
||||
#include <boost/multiprecision/integer.hpp>
|
||||
|
||||
namespace Slic3r { namespace Geometry {
|
||||
|
||||
// This implementation is based on Andrew's monotone chain 2D convex hull algorithm
|
||||
Polygon convex_hull(Points pts)
|
||||
{
|
||||
std::sort(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a.x() < b.x() || (a.x() == b.x() && a.y() < b.y()); });
|
||||
pts.erase(std::unique(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a.x() == b.x() && a.y() == b.y(); }), pts.end());
|
||||
|
||||
Polygon hull;
|
||||
int n = (int)pts.size();
|
||||
if (n >= 3) {
|
||||
int k = 0;
|
||||
hull.points.resize(2 * n);
|
||||
// Build lower hull
|
||||
for (int i = 0; i < n; ++ i) {
|
||||
while (k >= 2 && pts[i].ccw(hull[k-2], hull[k-1]) <= 0)
|
||||
-- k;
|
||||
hull[k ++] = pts[i];
|
||||
}
|
||||
// Build upper hull
|
||||
for (int i = n-2, t = k+1; i >= 0; i--) {
|
||||
while (k >= t && pts[i].ccw(hull[k-2], hull[k-1]) <= 0)
|
||||
-- k;
|
||||
hull[k ++] = pts[i];
|
||||
}
|
||||
hull.points.resize(k);
|
||||
assert(hull.points.front() == hull.points.back());
|
||||
hull.points.pop_back();
|
||||
}
|
||||
return hull;
|
||||
}
|
||||
|
||||
Pointf3s convex_hull(Pointf3s points)
|
||||
{
|
||||
assert(points.size() >= 3);
|
||||
// sort input points
|
||||
std::sort(points.begin(), points.end(), [](const Vec3d &a, const Vec3d &b){ return a.x() < b.x() || (a.x() == b.x() && a.y() < b.y()); });
|
||||
|
||||
int n = points.size(), k = 0;
|
||||
Pointf3s hull;
|
||||
|
||||
if (n >= 3)
|
||||
{
|
||||
hull.resize(2 * n);
|
||||
|
||||
// Build lower hull
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
Point p = Point::new_scale(points[i](0), points[i](1));
|
||||
while (k >= 2)
|
||||
{
|
||||
Point k1 = Point::new_scale(hull[k - 1](0), hull[k - 1](1));
|
||||
Point k2 = Point::new_scale(hull[k - 2](0), hull[k - 2](1));
|
||||
|
||||
if (p.ccw(k2, k1) <= 0)
|
||||
--k;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
hull[k++] = points[i];
|
||||
}
|
||||
|
||||
// Build upper hull
|
||||
for (int i = n - 2, t = k + 1; i >= 0; --i)
|
||||
{
|
||||
Point p = Point::new_scale(points[i](0), points[i](1));
|
||||
while (k >= t)
|
||||
{
|
||||
Point k1 = Point::new_scale(hull[k - 1](0), hull[k - 1](1));
|
||||
Point k2 = Point::new_scale(hull[k - 2](0), hull[k - 2](1));
|
||||
|
||||
if (p.ccw(k2, k1) <= 0)
|
||||
--k;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
hull[k++] = points[i];
|
||||
}
|
||||
|
||||
hull.resize(k);
|
||||
|
||||
assert(hull.front() == hull.back());
|
||||
hull.pop_back();
|
||||
}
|
||||
|
||||
return hull;
|
||||
}
|
||||
|
||||
Polygon convex_hull(const Polygons &polygons)
|
||||
{
|
||||
Points pp;
|
||||
for (Polygons::const_iterator p = polygons.begin(); p != polygons.end(); ++p) {
|
||||
pp.insert(pp.end(), p->points.begin(), p->points.end());
|
||||
}
|
||||
return convex_hull(std::move(pp));
|
||||
}
|
||||
|
||||
|
||||
namespace rotcalip {
|
||||
|
||||
using int256_t = boost::multiprecision::int256_t;
|
||||
using int128_t = boost::multiprecision::int128_t;
|
||||
|
||||
template<class Scalar = int64_t>
|
||||
inline Scalar magnsq(const Point &p)
|
||||
{
|
||||
return Scalar(p.x()) * p.x() + Scalar(p.y()) * p.y();
|
||||
}
|
||||
|
||||
template<class Scalar = int64_t>
|
||||
inline Scalar dot(const Point &a, const Point &b)
|
||||
{
|
||||
return Scalar(a.x()) * b.x() + Scalar(a.y()) * b.y();
|
||||
}
|
||||
|
||||
template<class Scalar = int64_t>
|
||||
inline Scalar dotperp(const Point &a, const Point &b)
|
||||
{
|
||||
return Scalar(a.x()) * b.y() - Scalar(a.y()) * b.x();
|
||||
}
|
||||
|
||||
using boost::multiprecision::abs;
|
||||
|
||||
// Compares the angle enclosed by vectors dir and dirA (alpha) with the angle
|
||||
// enclosed by -dir and dirB (beta). Returns -1 if alpha is less than beta, 0
|
||||
// if they are equal and 1 if alpha is greater than beta. Note that dir is
|
||||
// reversed for beta, because it represents the opposite side of a caliper.
|
||||
int cmp_angles(const Point &dir, const Point &dirA, const Point &dirB) {
|
||||
int128_t dotA = dot(dir, dirA);
|
||||
int128_t dotB = dot(-dir, dirB);
|
||||
int256_t dcosa = int256_t(magnsq(dirB)) * int256_t(abs(dotA)) * dotA;
|
||||
int256_t dcosb = int256_t(magnsq(dirA)) * int256_t(abs(dotB)) * dotB;
|
||||
int256_t diff = dcosa - dcosb;
|
||||
|
||||
return diff > 0? -1 : (diff < 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
// A helper class to navigate on a polygon. Given a vertex index, one can
|
||||
// get the edge belonging to that vertex, the coordinates of the vertex, the
|
||||
// next and previous edges. Stuff that is needed in the rotating calipers algo.
|
||||
class Idx
|
||||
{
|
||||
size_t m_idx;
|
||||
const Polygon *m_poly;
|
||||
public:
|
||||
explicit Idx(const Polygon &p): m_idx{0}, m_poly{&p} {}
|
||||
explicit Idx(size_t idx, const Polygon &p): m_idx{idx}, m_poly{&p} {}
|
||||
|
||||
size_t idx() const { return m_idx; }
|
||||
void set_idx(size_t i) { m_idx = i; }
|
||||
size_t next() const { return (m_idx + 1) % m_poly->size(); }
|
||||
size_t inc() { return m_idx = (m_idx + 1) % m_poly->size(); }
|
||||
Point prev_dir() const {
|
||||
return pt() - (*m_poly)[(m_idx + m_poly->size() - 1) % m_poly->size()];
|
||||
}
|
||||
|
||||
const Point &pt() const { return (*m_poly)[m_idx]; }
|
||||
const Point dir() const { return (*m_poly)[next()] - pt(); }
|
||||
const Point next_dir() const
|
||||
{
|
||||
return (*m_poly)[(m_idx + 2) % m_poly->size()] - (*m_poly)[next()];
|
||||
}
|
||||
const Polygon &poly() const { return *m_poly; }
|
||||
};
|
||||
|
||||
enum class AntipodalVisitMode { Full, EdgesOnly };
|
||||
|
||||
// Visit all antipodal pairs starting from the initial ia, ib pair which
|
||||
// has to be a valid antipodal pair (not checked). fn is called for every
|
||||
// antipodal pair encountered including the initial one.
|
||||
// The callback Fn has a signiture of bool(size_t i, size_t j, const Point &dir)
|
||||
// where i,j are the vertex indices of the antipodal pair and dir is the
|
||||
// direction of the calipers touching the i vertex.
|
||||
template<AntipodalVisitMode mode = AntipodalVisitMode::Full, class Fn>
|
||||
void visit_antipodals (Idx& ia, Idx &ib, Fn &&fn)
|
||||
{
|
||||
// Set current caliper direction to be the lower edge angle from X axis
|
||||
int cmp = cmp_angles(ia.prev_dir(), ia.dir(), ib.dir());
|
||||
Idx *current = cmp <= 0 ? &ia : &ib, *other = cmp <= 0 ? &ib : &ia;
|
||||
Idx *initial = current;
|
||||
bool visitor_continue = true;
|
||||
|
||||
size_t start = initial->idx();
|
||||
bool finished = false;
|
||||
|
||||
while (visitor_continue && !finished) {
|
||||
Point current_dir_a = current == &ia ? current->dir() : -current->dir();
|
||||
visitor_continue = fn(ia.idx(), ib.idx(), current_dir_a);
|
||||
|
||||
// Parallel edges encountered. An additional pair of antipodals
|
||||
// can be yielded.
|
||||
if constexpr (mode == AntipodalVisitMode::Full)
|
||||
if (cmp == 0 && visitor_continue) {
|
||||
visitor_continue = fn(current == &ia ? ia.idx() : ia.next(),
|
||||
current == &ib ? ib.idx() : ib.next(),
|
||||
current_dir_a);
|
||||
}
|
||||
|
||||
cmp = cmp_angles(current->dir(), current->next_dir(), other->dir());
|
||||
|
||||
current->inc();
|
||||
if (cmp > 0) {
|
||||
std::swap(current, other);
|
||||
}
|
||||
|
||||
if (initial->idx() == start) finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rotcalip
|
||||
|
||||
bool convex_polygons_intersect(const Polygon &A, const Polygon &B)
|
||||
{
|
||||
using namespace rotcalip;
|
||||
|
||||
// Establish starting antipodals as extremes in XY plane. Use the
|
||||
// easily obtainable bounding boxes to check if A and B is disjoint
|
||||
// and return false if the are.
|
||||
struct BB
|
||||
{
|
||||
size_t xmin = 0, xmax = 0, ymin = 0, ymax = 0;
|
||||
const Polygon &P;
|
||||
static bool cmpy(const Point &l, const Point &u)
|
||||
{
|
||||
return l.y() < u.y() || (l.y() == u.y() && l.x() < u.x());
|
||||
}
|
||||
|
||||
BB(const Polygon &poly): P{poly}
|
||||
{
|
||||
for (size_t i = 0; i < P.size(); ++i) {
|
||||
if (P[i] < P[xmin]) xmin = i;
|
||||
if (P[xmax] < P[i]) xmax = i;
|
||||
if (cmpy(P[i], P[ymin])) ymin = i;
|
||||
if (cmpy(P[ymax], P[i])) ymax = i;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BB bA{A}, bB{B};
|
||||
BoundingBox bbA{{A[bA.xmin].x(), A[bA.ymin].y()}, {A[bA.xmax].x(), A[bA.ymax].y()}};
|
||||
BoundingBox bbB{{B[bB.xmin].x(), B[bB.ymin].y()}, {B[bB.xmax].x(), B[bB.ymax].y()}};
|
||||
|
||||
// if (!bbA.overlap(bbB))
|
||||
// return false;
|
||||
|
||||
// Establish starting antipodals as extreme vertex pairs in X or Y direction
|
||||
// which reside on different polygons. If no such pair is found, the two
|
||||
// polygons are certainly not disjoint.
|
||||
Idx imin{bA.xmin, A}, imax{bB.xmax, B};
|
||||
if (B[bB.xmin] < imin.pt()) imin = Idx{bB.xmin, B};
|
||||
if (imax.pt() < A[bA.xmax]) imax = Idx{bA.xmax, A};
|
||||
if (&imin.poly() == &imax.poly()) {
|
||||
imin = Idx{bA.ymin, A};
|
||||
imax = Idx{bB.ymax, B};
|
||||
if (B[bB.ymin] < imin.pt()) imin = Idx{bB.ymin, B};
|
||||
if (imax.pt() < A[bA.ymax]) imax = Idx{bA.ymax, A};
|
||||
}
|
||||
|
||||
if (&imin.poly() == &imax.poly())
|
||||
return true;
|
||||
|
||||
bool found_divisor = false;
|
||||
visit_antipodals<AntipodalVisitMode::EdgesOnly>(
|
||||
imin, imax,
|
||||
[&imin, &imax, &found_divisor](size_t ia, size_t ib, const Point &dir) {
|
||||
// std::cout << "A" << ia << " B" << ib << " dir " <<
|
||||
// dir.x() << " " << dir.y() << std::endl;
|
||||
const Polygon &A = imin.poly(), &B = imax.poly();
|
||||
|
||||
Point ref_a = A[(ia + 2) % A.size()], ref_b = B[(ib + 2) % B.size()];
|
||||
|
||||
bool is_left_a = dotperp( dir, ref_a - A[ia]) > 0;
|
||||
bool is_left_b = dotperp(-dir, ref_b - B[ib]) > 0;
|
||||
|
||||
// If both reference points are on the left (or right) of their
|
||||
// respective support lines and the opposite support line is to
|
||||
// the right (or left), the divisor line is found. We only test
|
||||
// the reference point, as by definition, if that is on one side,
|
||||
// all the other points must be on the same side of a support
|
||||
// line. If the support lines are collinear, the polygons must be
|
||||
// on the same side of their respective support lines.
|
||||
|
||||
auto d = dotperp(dir, B[ib] - A[ia]);
|
||||
if (d == 0) {
|
||||
// The caliper lines are collinear, not just parallel
|
||||
found_divisor = (is_left_a && is_left_b) || (!is_left_a && !is_left_b);
|
||||
} else if (d > 0) { // B is to the left of (A, A+1)
|
||||
found_divisor = !is_left_a && !is_left_b;
|
||||
} else { // B is to the right of (A, A+1)
|
||||
found_divisor = is_left_a && is_left_b;
|
||||
}
|
||||
|
||||
return !found_divisor;
|
||||
});
|
||||
|
||||
// Intersects if the divisor was not found
|
||||
return !found_divisor;
|
||||
}
|
||||
|
||||
// Decompose source convex hull points into a top / bottom chains with monotonically increasing x,
|
||||
// creating an implicit trapezoidal decomposition of the source convex polygon.
|
||||
// The source convex polygon has to be CCW oriented. O(n) time complexity.
|
||||
std::pair<std::vector<Vec2d>, std::vector<Vec2d>> decompose_convex_polygon_top_bottom(const std::vector<Vec2d> &src)
|
||||
{
|
||||
std::pair<std::vector<Vec2d>, std::vector<Vec2d>> out;
|
||||
std::vector<Vec2d> &bottom = out.first;
|
||||
std::vector<Vec2d> &top = out.second;
|
||||
|
||||
// Find the minimum point.
|
||||
auto left_bottom = std::min_element(src.begin(), src.end(), [](const auto &l, const auto &r) { return l.x() < r.x() || (l.x() == r.x() && l.y() < r.y()); });
|
||||
auto right_top = std::max_element(src.begin(), src.end(), [](const auto &l, const auto &r) { return l.x() < r.x() || (l.x() == r.x() && l.y() < r.y()); });
|
||||
if (left_bottom != src.end() && left_bottom != right_top) {
|
||||
// Produce the bottom and bottom chains.
|
||||
if (left_bottom < right_top) {
|
||||
bottom.assign(left_bottom, right_top + 1);
|
||||
size_t cnt = (src.end() - right_top) + (left_bottom + 1 - src.begin());
|
||||
top.reserve(cnt);
|
||||
top.assign(right_top, src.end());
|
||||
top.insert(top.end(), src.begin(), left_bottom + 1);
|
||||
} else {
|
||||
size_t cnt = (src.end() - left_bottom) + (right_top + 1 - src.begin());
|
||||
bottom.reserve(cnt);
|
||||
bottom.assign(left_bottom, src.end());
|
||||
bottom.insert(bottom.end(), src.begin(), right_top + 1);
|
||||
top.assign(right_top, left_bottom + 1);
|
||||
}
|
||||
// Remove strictly vertical segments at the end.
|
||||
if (bottom.size() > 1) {
|
||||
auto it = bottom.end();
|
||||
for (-- it; it != bottom.begin() && (it - 1)->x() == bottom.back().x(); -- it) ;
|
||||
bottom.erase(it + 1, bottom.end());
|
||||
}
|
||||
if (top.size() > 1) {
|
||||
auto it = top.end();
|
||||
for (-- it; it != top.begin() && (it - 1)->x() == top.back().x(); -- it) ;
|
||||
top.erase(it + 1, top.end());
|
||||
}
|
||||
std::reverse(top.begin(), top.end());
|
||||
}
|
||||
|
||||
if (top.size() < 2 || bottom.size() < 2) {
|
||||
// invalid
|
||||
top.clear();
|
||||
bottom.clear();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Convex polygon check using a top / bottom chain decomposition with O(log n) time complexity.
|
||||
bool inside_convex_polygon(const std::pair<std::vector<Vec2d>, std::vector<Vec2d>> &top_bottom_decomposition, const Vec2d &pt)
|
||||
{
|
||||
auto it_bottom = std::lower_bound(top_bottom_decomposition.first.begin(), top_bottom_decomposition.first.end(), pt, [](const auto &l, const auto &r){ return l.x() < r.x(); });
|
||||
auto it_top = std::lower_bound(top_bottom_decomposition.second.begin(), top_bottom_decomposition.second.end(), pt, [](const auto &l, const auto &r){ return l.x() < r.x(); });
|
||||
if (it_bottom == top_bottom_decomposition.first.end()) {
|
||||
// Above max x.
|
||||
assert(it_top == top_bottom_decomposition.second.end());
|
||||
return false;
|
||||
}
|
||||
if (it_bottom == top_bottom_decomposition.first.begin()) {
|
||||
// Below or at min x.
|
||||
if (pt.x() < it_bottom->x()) {
|
||||
// Below min x.
|
||||
assert(pt.x() < it_top->x());
|
||||
return false;
|
||||
}
|
||||
// At min x.
|
||||
assert(pt.x() == it_bottom->x());
|
||||
assert(pt.x() == it_top->x());
|
||||
assert(it_bottom->y() <= pt.y() <= it_top->y());
|
||||
return pt.y() >= it_bottom->y() && pt.y() <= it_top->y();
|
||||
}
|
||||
|
||||
// Trapezoid or a triangle.
|
||||
assert(it_bottom != top_bottom_decomposition.first .begin() && it_bottom != top_bottom_decomposition.first .end());
|
||||
assert(it_top != top_bottom_decomposition.second.begin() && it_top != top_bottom_decomposition.second.end());
|
||||
assert(pt.x() <= it_bottom->x());
|
||||
assert(pt.x() <= it_top->x());
|
||||
auto it_top_prev = it_top - 1;
|
||||
auto it_bottom_prev = it_bottom - 1;
|
||||
assert(pt.x() >= it_top_prev->x());
|
||||
assert(pt.x() >= it_bottom_prev->x());
|
||||
double det = cross2(*it_bottom - *it_bottom_prev, pt - *it_bottom_prev);
|
||||
if (det < 0)
|
||||
return false;
|
||||
det = cross2(*it_top - *it_top_prev, pt - *it_top_prev);
|
||||
return det <= 0;
|
||||
}
|
||||
|
||||
} // namespace Geometry
|
||||
} // namespace Slic3r
|
||||
|
27
src/libslic3r/Geometry/ConvexHull.hpp
Normal file
27
src/libslic3r/Geometry/ConvexHull.hpp
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef slic3r_Geometry_ConvexHull_hpp_
|
||||
#define slic3r_Geometry_ConvexHull_hpp_
|
||||
|
||||
#include "../Polygon.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace Geometry {
|
||||
|
||||
Pointf3s convex_hull(Pointf3s points);
|
||||
Polygon convex_hull(Points points);
|
||||
Polygon convex_hull(const Polygons &polygons);
|
||||
|
||||
// Returns true if the intersection of the two convex polygons A and B
|
||||
// is not an empty set.
|
||||
bool convex_polygons_intersect(const Polygon &A, const Polygon &B);
|
||||
|
||||
// Decompose source convex hull points into top / bottom chains with monotonically increasing x,
|
||||
// creating an implicit trapezoidal decomposition of the source convex polygon.
|
||||
// The source convex polygon has to be CCW oriented. O(n) time complexity.
|
||||
std::pair<std::vector<Vec2d>, std::vector<Vec2d>> decompose_convex_polygon_top_bottom(const std::vector<Vec2d> &src);
|
||||
|
||||
// Convex polygon check using a top / bottom chain decomposition with O(log n) time complexity.
|
||||
bool inside_convex_polygon(const std::pair<std::vector<Vec2d>, std::vector<Vec2d>> &top_bottom_decomposition, const Vec2d &pt);
|
||||
|
||||
} } // namespace Slicer::Geometry
|
||||
|
||||
#endif
|
@ -70,7 +70,6 @@ bool Line::parallel_to(const Line& line) const
|
||||
return sqr(cross2(v1, v2)) < sqr(EPSILON) * v1.squaredNorm() * v2.squaredNorm();
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool Line::perpendicular_to(double angle) const
|
||||
{
|
||||
return Slic3r::Geometry::directions_perpendicular(this->direction(), angle);
|
||||
@ -82,7 +81,6 @@ bool Line::perpendicular_to(const Line& line) const
|
||||
const Vec2d v2 = (line.b - line.a).cast<double>();
|
||||
return sqr(v1.dot(v2)) < sqr(EPSILON) * v1.squaredNorm() * v2.squaredNorm();
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
bool Line::intersection(const Line &l2, Point *intersection) const
|
||||
{
|
||||
|
@ -105,10 +105,8 @@ public:
|
||||
double perp_distance_to(const Point &point) const;
|
||||
bool parallel_to(double angle) const;
|
||||
bool parallel_to(const Line& line) const;
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool perpendicular_to(double angle) const;
|
||||
bool perpendicular_to(const Line& line) const;
|
||||
#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 orientation() const;
|
||||
double direction() const;
|
||||
|
@ -1,9 +1,10 @@
|
||||
#include "Model.hpp"
|
||||
#include "libslic3r.h"
|
||||
#include "BuildVolume.hpp"
|
||||
#include "Exception.hpp"
|
||||
#include "Model.hpp"
|
||||
#include "ModelArrange.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "Geometry/ConvexHull.hpp"
|
||||
#include "MTUtils.hpp"
|
||||
#include "TriangleMeshSlicer.hpp"
|
||||
#include "TriangleSelector.hpp"
|
||||
@ -27,39 +28,6 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// Using rotating callipers to check for collision of two convex polygons. Thus both printbed_shape and obj_hull_2d are convex polygons.
|
||||
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::convex_polygons_intersect(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)
|
||||
{
|
||||
this->copy_id(rhs);
|
||||
@ -364,24 +332,13 @@ BoundingBoxf3 Model::bounding_box() const
|
||||
return bb;
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// printbed_shape is convex polygon
|
||||
unsigned int Model::update_print_volume_state(const Polygon& printbed_shape, double print_volume_height)
|
||||
unsigned int Model::update_print_volume_state(const BuildVolume &build_volume)
|
||||
{
|
||||
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);
|
||||
num_printable += model_object->update_instances_print_volume_state(build_volume);
|
||||
return num_printable;
|
||||
}
|
||||
#else
|
||||
unsigned int Model::update_print_volume_state(const BoundingBoxf3 &print_volume)
|
||||
{
|
||||
unsigned int num_printable = 0;
|
||||
for (ModelObject *model_object : this->objects)
|
||||
num_printable += model_object->check_instances_print_volume_state(print_volume);
|
||||
return num_printable;
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
bool Model::center_instances_around_point(const Vec2d &point)
|
||||
{
|
||||
@ -1573,9 +1530,7 @@ double ModelObject::get_instance_max_z(size_t instance_idx) const
|
||||
return max_z + inst->get_offset(Z);
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// printbed_shape is convex polygon
|
||||
unsigned int ModelObject::check_instances_print_volume_state(const Polygon& printbed_shape, double print_volume_height)
|
||||
unsigned int ModelObject::update_instances_print_volume_state(const BuildVolume &build_volume)
|
||||
{
|
||||
unsigned int num_printable = 0;
|
||||
enum {
|
||||
@ -1587,16 +1542,10 @@ unsigned int ModelObject::check_instances_print_volume_state(const Polygon& prin
|
||||
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);
|
||||
if (!bb.defined) {
|
||||
// this may happen if the part is fully below the printbed, leading to a crash in the following call to its_convex_hull_2d_above()
|
||||
continue;
|
||||
}
|
||||
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)
|
||||
BuildVolume::ObjectState state = build_volume.object_state(vol->mesh().its, matrix.cast<float>(), true /* may be below print bed */);
|
||||
if (state == BuildVolume::ObjectState::Inside)
|
||||
inside_outside |= INSIDE;
|
||||
else if (state == ModelInstancePVS_Fully_Outside)
|
||||
else if (state == BuildVolume::ObjectState::Outside)
|
||||
inside_outside |= OUTSIDE;
|
||||
else
|
||||
inside_outside |= INSIDE | OUTSIDE;
|
||||
@ -1609,35 +1558,6 @@ unsigned int ModelObject::check_instances_print_volume_state(const Polygon& prin
|
||||
}
|
||||
return num_printable;
|
||||
}
|
||||
#else
|
||||
unsigned int ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume)
|
||||
{
|
||||
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()) {
|
||||
BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(model_instance->get_matrix() * vol->get_matrix());
|
||||
if (print_volume.contains(bb))
|
||||
inside_outside |= INSIDE;
|
||||
else if (print_volume.intersects(bb))
|
||||
inside_outside |= INSIDE | OUTSIDE;
|
||||
else
|
||||
inside_outside |= 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;
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
void ModelObject::print_info() const
|
||||
{
|
||||
@ -1716,7 +1636,7 @@ TriangleMeshStats ModelObject::get_object_stl_stats() const
|
||||
|
||||
// another used satistics value
|
||||
if (volume->is_model_part()) {
|
||||
Transform3d trans = instances[0]->get_matrix() * volume->get_matrix();
|
||||
Transform3d trans = instances.empty() ? volume->get_matrix() : (volume->get_matrix() * instances[0]->get_matrix());
|
||||
full_stats.volume += stats.volume * std::fabs(trans.matrix().block(0, 0, 3, 3).determinant());
|
||||
full_stats.number_of_parts += stats.number_of_parts;
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ namespace cereal {
|
||||
namespace Slic3r {
|
||||
enum class ConversionType;
|
||||
|
||||
class BuildVolume;
|
||||
class Model;
|
||||
class ModelInstance;
|
||||
class ModelMaterial;
|
||||
@ -366,13 +367,6 @@ public:
|
||||
double get_instance_min_z(size_t instance_idx) const;
|
||||
double get_instance_max_z(size_t instance_idx) const;
|
||||
|
||||
// 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);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
// Print object statistics to console.
|
||||
void print_info() const;
|
||||
|
||||
@ -505,6 +499,9 @@ private:
|
||||
sla_support_points, sla_points_status, sla_drain_holes, printable, origin_translation,
|
||||
m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid);
|
||||
}
|
||||
|
||||
// Called by Print::validate() from the UI thread.
|
||||
unsigned int update_instances_print_volume_state(const BuildVolume &build_volume);
|
||||
};
|
||||
|
||||
enum class EnforcerBlockerType : int8_t {
|
||||
@ -908,17 +905,6 @@ enum ModelInstanceEPrintVolumeState : unsigned char
|
||||
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)
|
||||
// Using rotating callipers to check for collision of two convex polygons.
|
||||
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)
|
||||
// Commented out, using rotating callipers is quite expensive for a bounding box test.
|
||||
//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.
|
||||
// Knows the affine transformation of an object.
|
||||
class ModelInstance final : public ObjectBase
|
||||
@ -1123,12 +1109,7 @@ public:
|
||||
BoundingBoxf3 bounding_box() const;
|
||||
// Set the print_volume_state of PrintObject::instances,
|
||||
// return total number of printable objects.
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// printbed_shape is convex polygon
|
||||
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);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
unsigned int update_print_volume_state(const BuildVolume &build_volume);
|
||||
// Returns true if any ModelObject was modified.
|
||||
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); }
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "ModelArrange.hpp"
|
||||
|
||||
#include <libslic3r/Model.hpp>
|
||||
#include <libslic3r/Geometry/ConvexHull.hpp>
|
||||
#include "MTUtils.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
@ -201,6 +201,14 @@ BoundingBox get_extents(const std::vector<Points> &pts)
|
||||
return bbox;
|
||||
}
|
||||
|
||||
BoundingBoxf get_extents(const std::vector<Vec2d> &pts)
|
||||
{
|
||||
BoundingBoxf bbox;
|
||||
for (const Vec2d &p : pts)
|
||||
bbox.merge(p);
|
||||
return bbox;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream &stm, const Vec2d &pointf)
|
||||
{
|
||||
return stm << pointf(0) << "," << pointf(1);
|
||||
|
@ -16,6 +16,7 @@
|
||||
namespace Slic3r {
|
||||
|
||||
class BoundingBox;
|
||||
class BoundingBoxf;
|
||||
class Line;
|
||||
class MultiPoint;
|
||||
class Point;
|
||||
@ -133,6 +134,7 @@ public:
|
||||
Point(const Eigen::MatrixBase<OtherDerived> &other) : Vec2crd(other) {}
|
||||
static Point new_scale(coordf_t x, coordf_t y) { return Point(coord_t(scale_(x)), coord_t(scale_(y))); }
|
||||
static Point new_scale(const Vec2d &v) { return Point(coord_t(scale_(v.x())), coord_t(scale_(v.y()))); }
|
||||
static Point new_scale(const Vec2f &v) { return Point(coord_t(scale_(v.x())), coord_t(scale_(v.y()))); }
|
||||
|
||||
// This method allows you to assign Eigen expressions to MyVectorType
|
||||
template<typename OtherDerived>
|
||||
@ -213,6 +215,7 @@ inline Point lerp(const Point &a, const Point &b, double t)
|
||||
|
||||
BoundingBox get_extents(const Points &pts);
|
||||
BoundingBox get_extents(const std::vector<Points> &pts);
|
||||
BoundingBoxf get_extents(const std::vector<Vec2d> &pts);
|
||||
|
||||
// Test for duplicate points in a vector of points.
|
||||
// The points are copied, sorted and checked for duplicates globally.
|
||||
|
@ -334,6 +334,25 @@ extern std::vector<BoundingBox> get_extents_vector(const Polygons &polygons)
|
||||
return out;
|
||||
}
|
||||
|
||||
// Polygon must be valid (at least three points), collinear points and duplicate points removed.
|
||||
bool polygon_is_convex(const Points &poly)
|
||||
{
|
||||
if (poly.size() < 3)
|
||||
return false;
|
||||
|
||||
Point p0 = poly[poly.size() - 2];
|
||||
Point p1 = poly[poly.size() - 1];
|
||||
for (size_t i = 0; i < poly.size(); ++ i) {
|
||||
Point p2 = poly[i];
|
||||
auto det = cross2((p1 - p0).cast<int64_t>(), (p2 - p1).cast<int64_t>());
|
||||
if (det < 0)
|
||||
return false;
|
||||
p0 = p1;
|
||||
p1 = p2;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool has_duplicate_points(const Polygons &polys)
|
||||
{
|
||||
#if 1
|
||||
|
@ -84,6 +84,10 @@ BoundingBox get_extents_rotated(const Polygon &poly, double angle);
|
||||
BoundingBox get_extents_rotated(const Polygons &polygons, double angle);
|
||||
std::vector<BoundingBox> get_extents_vector(const Polygons &polygons);
|
||||
|
||||
// Polygon must be valid (at least three points), collinear points and duplicate points removed.
|
||||
bool polygon_is_convex(const Points &poly);
|
||||
inline bool polygon_is_convex(const Polygon &poly) { return polygon_is_convex(poly.points); }
|
||||
|
||||
// Test for duplicate points. The points are copied, sorted and checked for duplicates globally.
|
||||
inline bool has_duplicate_points(Polygon &&poly) { return has_duplicate_points(std::move(poly.points)); }
|
||||
inline bool has_duplicate_points(const Polygon &poly) { return has_duplicate_points(poly.points); }
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Extruder.hpp"
|
||||
#include "Flow.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "Geometry/ConvexHull.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "ShortestPath.hpp"
|
||||
#include "SupportMaterial.hpp"
|
||||
@ -850,7 +850,7 @@ void Print::process()
|
||||
// The export_gcode may die for various reasons (fails to process output_filename_format,
|
||||
// write error into the G-code, cannot execute post-processing scripts).
|
||||
// It is up to the caller to show an error message.
|
||||
std::string Print::export_gcode(const std::string& path_template, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb)
|
||||
std::string Print::export_gcode(const std::string& path_template, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb)
|
||||
{
|
||||
// output everything to a G-code file
|
||||
// The following call may die if the output_filename_format template substitution fails.
|
||||
|
@ -521,7 +521,7 @@ public:
|
||||
void process() override;
|
||||
// Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file.
|
||||
// If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r).
|
||||
std::string export_gcode(const std::string& path_template, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
|
||||
std::string export_gcode(const std::string& path_template, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
|
||||
|
||||
// methods for handling state
|
||||
bool is_step_done(PrintStep step) const { return Inherited::is_step_done(step); }
|
||||
|
@ -374,6 +374,7 @@ void PrintConfigDef::init_fff_params()
|
||||
"Detour length could be specified either as an absolute value or as percentage (for example 50%) of a direct travel path.");
|
||||
def->sidetext = L("mm or % (zero to disable)");
|
||||
def->min = 0;
|
||||
def->max_literal = 1000;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(0., false));
|
||||
|
||||
@ -720,6 +721,7 @@ void PrintConfigDef::init_fff_params()
|
||||
"If expressed as percentage (for example 200%), it will be computed over layer height.");
|
||||
def->sidetext = L("mm or %");
|
||||
def->min = 0;
|
||||
def->max_literal = 50;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
||||
|
||||
@ -831,6 +833,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->sidetext = L("mm or %");
|
||||
def->min = 0;
|
||||
def->max = 1000;
|
||||
def->max_literal = 50;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
||||
|
||||
@ -1181,6 +1184,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->sidetext = L("mm or %");
|
||||
def->ratio_over = "first_layer_height";
|
||||
def->min = 0;
|
||||
def->max_literal = 50;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(200, true));
|
||||
|
||||
@ -1200,6 +1204,7 @@ void PrintConfigDef::init_fff_params()
|
||||
"(for example: 40%) it will scale the default speeds.");
|
||||
def->sidetext = L("mm/s or %");
|
||||
def->min = 0;
|
||||
def->max_literal = 20;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(30, false));
|
||||
|
||||
@ -1374,6 +1379,7 @@ void PrintConfigDef::init_fff_params()
|
||||
"Set this parameter to zero to disable anchoring perimeters connected to a single infill line.");
|
||||
def->sidetext = L("mm or %");
|
||||
def->ratio_over = "infill_extrusion_width";
|
||||
def->max_literal = 1000;
|
||||
def->gui_type = ConfigOptionDef::GUIType::f_enum_open;
|
||||
def->enum_values.push_back("0");
|
||||
def->enum_values.push_back("1");
|
||||
@ -1401,6 +1407,7 @@ void PrintConfigDef::init_fff_params()
|
||||
"Set this parameter to zero to disable anchoring.");
|
||||
def->sidetext = def_infill_anchor_min->sidetext;
|
||||
def->ratio_over = def_infill_anchor_min->ratio_over;
|
||||
def->max_literal = def_infill_anchor_min->max_literal;
|
||||
def->gui_type = def_infill_anchor_min->gui_type;
|
||||
def->enum_values = def_infill_anchor_min->enum_values;
|
||||
def->enum_labels.push_back(L("0 (not anchored)"));
|
||||
@ -1429,6 +1436,7 @@ void PrintConfigDef::init_fff_params()
|
||||
"If expressed as percentage (for example 90%) it will be computed over layer height.");
|
||||
def->sidetext = L("mm or %");
|
||||
def->min = 0;
|
||||
def->max_literal = 50;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
||||
|
||||
@ -1928,6 +1936,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->sidetext = L("mm or %");
|
||||
def->aliases = { "perimeters_extrusion_width" };
|
||||
def->min = 0;
|
||||
def->max_literal = 50;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
||||
|
||||
@ -2300,6 +2309,7 @@ void PrintConfigDef::init_fff_params()
|
||||
"If expressed as percentage (for example 90%) it will be computed over layer height.");
|
||||
def->sidetext = L("mm or %");
|
||||
def->min = 0;
|
||||
def->max_literal = 50;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
||||
|
||||
@ -2474,6 +2484,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->sidetext = L("mm or %");
|
||||
def->ratio_over = "external_perimeter_extrusion_width";
|
||||
def->min = 0;
|
||||
def->max_literal = 10;
|
||||
def->mode = comAdvanced;
|
||||
// Default is half the external perimeter width.
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(50, true));
|
||||
@ -2560,6 +2571,7 @@ void PrintConfigDef::init_fff_params()
|
||||
"If expressed as percentage (for example 90%) it will be computed over layer height.");
|
||||
def->sidetext = L("mm or %");
|
||||
def->min = 0;
|
||||
def->max_literal = 50;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
||||
|
||||
@ -2793,6 +2805,7 @@ void PrintConfigDef::init_fff_params()
|
||||
"If expressed as percentage (for example 90%) it will be computed over layer height.");
|
||||
def->sidetext = L("mm or %");
|
||||
def->min = 0;
|
||||
def->max_literal = 50;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
||||
|
||||
|
@ -80,9 +80,6 @@
|
||||
|
||||
// Enable rendering modifiers and similar objects always as transparent
|
||||
#define ENABLE_MODIFIERS_ALWAYS_TRANSPARENT (1 && ENABLE_2_4_0_BETA1)
|
||||
// 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_BETA1)
|
||||
|
||||
|
||||
//====================
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "MeshSplitImpl.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "Geometry/ConvexHull.hpp"
|
||||
#include "Point.hpp"
|
||||
#include "Execution/ExecutionTBB.hpp"
|
||||
#include "Execution/ExecutionSeq.hpp"
|
||||
@ -435,30 +436,56 @@ BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d &trafo) c
|
||||
return bbox;
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d& trafo, double world_min_z) const
|
||||
BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d& trafod, 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;
|
||||
// 1) Allocate transformed vertices with their position with respect to print bed surface.
|
||||
std::vector<char> sides;
|
||||
size_t num_above = 0;
|
||||
Eigen::AlignedBox<float, 3> bbox;
|
||||
Transform3f trafo = trafod.cast<float>();
|
||||
sides.reserve(its.vertices.size());
|
||||
for (const stl_vertex &v : this->its.vertices) {
|
||||
const stl_vertex pt = trafo * v;
|
||||
const int sign = pt.z() > world_min_z ? 1 : pt.z() < world_min_z ? -1 : 0;
|
||||
sides.emplace_back(sign);
|
||||
if (sign >= 0) {
|
||||
// Vertex above or on print bed surface. Test whether it is inside the build volume.
|
||||
++ num_above;
|
||||
bbox.extend(pt);
|
||||
}
|
||||
}
|
||||
return bbox;
|
||||
|
||||
// 2) Calculate intersections of triangle edges with the build surface.
|
||||
if (num_above < its.vertices.size()) {
|
||||
// Not completely above the build surface and status may still change by testing edges intersecting the build platform.
|
||||
for (const stl_triangle_vertex_indices &tri : its.indices) {
|
||||
const int s[3] = { sides[tri(0)], sides[tri(1)], sides[tri(2)] };
|
||||
if (std::min(s[0], std::min(s[1], s[2])) < 0 && std::max(s[0], std::max(s[1], s[2])) > 0) {
|
||||
// Some edge of this triangle intersects the build platform. Calculate the intersection.
|
||||
int iprev = 2;
|
||||
for (int iedge = 0; iedge < 3; ++ iedge) {
|
||||
if (s[iprev] * s[iedge] == -1) {
|
||||
// edge intersects the build surface. Calculate intersection point.
|
||||
const stl_vertex p1 = trafo * its.vertices[tri(iprev)];
|
||||
const stl_vertex p2 = trafo * its.vertices[tri(iedge)];
|
||||
// Edge crosses the z plane. Calculate intersection point with the plane.
|
||||
const float t = (world_min_z - p1.z()) / (p2.z() - p1.z());
|
||||
bbox.extend(Vec3f(p1.x() + (p2.x() - p1.x()) * t, p1.y() + (p2.y() - p1.y()) * t, world_min_z));
|
||||
}
|
||||
iprev = iedge;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BoundingBoxf3 out;
|
||||
if (! bbox.isEmpty()) {
|
||||
out.min = bbox.min().cast<double>();
|
||||
out.max = bbox.max().cast<double>();
|
||||
out.defined = true;
|
||||
};
|
||||
return out;
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
TriangleMesh TriangleMesh::convex_hull_3d() const
|
||||
{
|
||||
|
@ -125,10 +125,8 @@ public:
|
||||
BoundingBoxf3 bounding_box() const;
|
||||
// Returns the bbox of this TriangleMesh transformed by the given transformation
|
||||
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.
|
||||
Vec3d size() const { return m_stats.size.cast<double>(); }
|
||||
/// Return the center of the related bounding box.
|
||||
|
@ -137,7 +137,7 @@ void Bed3D::Axes::render() const
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
}
|
||||
|
||||
bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom)
|
||||
bool Bed3D::set_shape(const Pointfs& bed_shape, const double max_print_height, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom)
|
||||
{
|
||||
auto check_texture = [](const std::string& texture) {
|
||||
boost::system::error_code ec; // so the exists call does not throw (e.g. after a permission problem)
|
||||
@ -149,17 +149,13 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c
|
||||
return !model.empty() && boost::algorithm::iends_with(model, ".stl") && boost::filesystem::exists(model, ec);
|
||||
};
|
||||
|
||||
EType type;
|
||||
Type type;
|
||||
std::string model;
|
||||
std::string texture;
|
||||
if (force_as_custom)
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
type = EType::Custom;
|
||||
#else
|
||||
type = Custom;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
type = Type::Custom;
|
||||
else {
|
||||
auto [new_type, system_model, system_texture] = detect_type(shape);
|
||||
auto [new_type, system_model, system_texture] = detect_type(bed_shape);
|
||||
type = new_type;
|
||||
model = system_model;
|
||||
texture = system_texture;
|
||||
@ -177,29 +173,18 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c
|
||||
model_filename.clear();
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
EShapeType shape_type = detect_shape_type(shape);
|
||||
if (m_shape == shape && m_type == type && m_shape_type == shape_type && m_texture_filename == texture_filename && m_model_filename == model_filename)
|
||||
#else
|
||||
if (m_shape == shape && m_type == type && m_texture_filename == texture_filename && m_model_filename == model_filename)
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
if (m_build_volume.bed_shape() == bed_shape && m_type == type && m_texture_filename == texture_filename && m_model_filename == model_filename)
|
||||
// No change, no need to update the UI.
|
||||
return false;
|
||||
|
||||
m_shape = shape;
|
||||
m_type = type;
|
||||
m_build_volume = BuildVolume { bed_shape, max_print_height };
|
||||
m_texture_filename = texture_filename;
|
||||
m_model_filename = model_filename;
|
||||
m_type = type;
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
m_shape_type = shape_type;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
m_extended_bounding_box = this->calc_extended_bounding_box();
|
||||
|
||||
calc_bounding_boxes();
|
||||
|
||||
ExPolygon poly;
|
||||
for (const Vec2d& p : m_shape) {
|
||||
poly.contour.append({ scale_(p(0)), scale_(p(1)) });
|
||||
}
|
||||
ExPolygon poly{ Polygon::new_scale(bed_shape) };
|
||||
|
||||
calc_triangles(poly);
|
||||
|
||||
@ -208,13 +193,13 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c
|
||||
|
||||
m_polygon = offset(poly.contour, (float)bed_bbox.radius() * 1.7f, jtRound, scale_(0.5))[0];
|
||||
|
||||
reset();
|
||||
this->release_VBOs();
|
||||
m_texture.reset();
|
||||
m_model.reset();
|
||||
|
||||
// Set the origin and size for rendering the coordinate system axes.
|
||||
m_axes.set_origin({ 0.0, 0.0, static_cast<double>(GROUND_Z) });
|
||||
m_axes.set_stem_length(0.1f * static_cast<float>(m_bounding_box.max_size()));
|
||||
m_axes.set_stem_length(0.1f * static_cast<float>(m_build_volume.bounding_volume().max_size()));
|
||||
|
||||
// Let the calee to update the UI.
|
||||
return true;
|
||||
@ -240,85 +225,6 @@ void Bed3D::render_for_picking(GLCanvas3D& canvas, bool bottom, float scale_fact
|
||||
render_internal(canvas, bottom, scale_factor, false, false, true);
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool Bed3D::is_rectangle(const Pointfs& shape, Vec2d* min, Vec2d* max)
|
||||
{
|
||||
const Lines lines = Polygon::new_scale(shape).lines();
|
||||
bool ret = lines.size() == 4 && lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3]) && lines[0].perpendicular_to(lines[1]);
|
||||
if (ret) {
|
||||
if (min != nullptr) {
|
||||
*min = shape.front();
|
||||
for (const Vec2d& pt : shape) {
|
||||
min->x() = std::min(min->x(), pt.x());
|
||||
min->y() = std::min(min->y(), pt.y());
|
||||
}
|
||||
}
|
||||
if (max != nullptr) {
|
||||
*max = shape.front();
|
||||
for (const Vec2d& pt : shape) {
|
||||
max->x() = std::max(max->x(), pt.x());
|
||||
max->y() = std::max(max->y(), pt.y());
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Bed3D::is_circle(const Pointfs& shape, Vec2d* center, double* radius)
|
||||
{
|
||||
if (shape.size() < 3)
|
||||
return false;
|
||||
|
||||
// Analyze the array of points.
|
||||
// Do they reside on a circle ?
|
||||
const Vec2d box_center = Geometry::circle_center_taubin_newton(shape);
|
||||
|
||||
std::vector<double> vertex_distances;
|
||||
double avg_dist = 0.0;
|
||||
for (const Vec2d& pt : shape) {
|
||||
double distance = (pt - box_center).norm();
|
||||
vertex_distances.push_back(distance);
|
||||
avg_dist += distance;
|
||||
}
|
||||
|
||||
avg_dist /= vertex_distances.size();
|
||||
|
||||
double tolerance = avg_dist * 0.01;
|
||||
|
||||
bool defined_value = true;
|
||||
for (double el : vertex_distances) {
|
||||
if (fabs(el - avg_dist) > tolerance)
|
||||
defined_value = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (center != nullptr)
|
||||
*center = box_center;
|
||||
|
||||
if (radius != nullptr)
|
||||
*radius = avg_dist;
|
||||
|
||||
return defined_value;
|
||||
}
|
||||
|
||||
bool Bed3D::is_convex(const Pointfs& shape)
|
||||
{
|
||||
return Polygon::new_scale(shape).convex_points().size() == shape.size();
|
||||
}
|
||||
|
||||
Bed3D::EShapeType Bed3D::detect_shape_type(const Pointfs& shape)
|
||||
{
|
||||
if (shape.size() < 3)
|
||||
return EShapeType::Invalid;
|
||||
else if (is_rectangle(shape))
|
||||
return EShapeType::Rectangle;
|
||||
else if (is_circle(shape))
|
||||
return EShapeType::Circle;
|
||||
else
|
||||
return EShapeType::Custom;
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
void Bed3D::render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
|
||||
bool show_axes, bool show_texture, bool picking)
|
||||
{
|
||||
@ -334,41 +240,31 @@ void Bed3D::render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
|
||||
|
||||
switch (m_type)
|
||||
{
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
case EType::System: { render_system(canvas, bottom, show_texture); break; }
|
||||
case Type::System: { render_system(canvas, bottom, show_texture); break; }
|
||||
default:
|
||||
case EType::Custom: { render_custom(canvas, bottom, show_texture, picking); break; }
|
||||
#else
|
||||
case System: { render_system(canvas, bottom, show_texture); break; }
|
||||
default:
|
||||
case Custom: { render_custom(canvas, bottom, show_texture, picking); break; }
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
case Type::Custom: { render_custom(canvas, bottom, show_texture, picking); break; }
|
||||
}
|
||||
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
}
|
||||
|
||||
void Bed3D::calc_bounding_boxes() const
|
||||
// Calculate an extended bounding box from axes and current model for visualization purposes.
|
||||
BoundingBoxf3 Bed3D::calc_extended_bounding_box() const
|
||||
{
|
||||
BoundingBoxf3* bounding_box = const_cast<BoundingBoxf3*>(&m_bounding_box);
|
||||
*bounding_box = BoundingBoxf3();
|
||||
for (const Vec2d& p : m_shape) {
|
||||
bounding_box->merge({ p.x(), p.y(), 0.0 });
|
||||
}
|
||||
|
||||
BoundingBoxf3* extended_bounding_box = const_cast<BoundingBoxf3*>(&m_extended_bounding_box);
|
||||
*extended_bounding_box = m_bounding_box;
|
||||
|
||||
BoundingBoxf3 out { m_build_volume.bounding_volume() };
|
||||
// Reset the build volume Z, we don't want to zoom to the top of the build volume if it is empty.
|
||||
out.min.z() = 0;
|
||||
out.max.z() = 0;
|
||||
// extend to contain axes
|
||||
extended_bounding_box->merge(m_axes.get_origin() + m_axes.get_total_length() * Vec3d::Ones());
|
||||
extended_bounding_box->merge(extended_bounding_box->min + Vec3d(-Axes::DefaultTipRadius, -Axes::DefaultTipRadius, extended_bounding_box->max(2)));
|
||||
|
||||
out.merge(m_axes.get_origin() + m_axes.get_total_length() * Vec3d::Ones());
|
||||
out.merge(out.min + Vec3d(-Axes::DefaultTipRadius, -Axes::DefaultTipRadius, out.max(2)));
|
||||
// extend to contain model, if any
|
||||
BoundingBoxf3 model_bb = m_model.get_bounding_box();
|
||||
if (model_bb.defined) {
|
||||
model_bb.translate(m_model_offset);
|
||||
extended_bounding_box->merge(model_bb);
|
||||
out.merge(model_bb);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void Bed3D::calc_triangles(const ExPolygon& poly)
|
||||
@ -404,8 +300,9 @@ void Bed3D::calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox)
|
||||
BOOST_LOG_TRIVIAL(error) << "Unable to create bed grid lines\n";
|
||||
}
|
||||
|
||||
|
||||
std::tuple<Bed3D::EType, std::string, std::string> Bed3D::detect_type(const Pointfs& shape) const
|
||||
// Try to match the print bed shape with the shape of an active profile. If such a match exists,
|
||||
// return the print bed model.
|
||||
std::tuple<Bed3D::Type, std::string, std::string> Bed3D::detect_type(const Pointfs& shape)
|
||||
{
|
||||
auto bundle = wxGetApp().preset_bundle;
|
||||
if (bundle != nullptr) {
|
||||
@ -416,11 +313,7 @@ std::tuple<Bed3D::EType, std::string, std::string> Bed3D::detect_type(const Poin
|
||||
std::string model_filename = PresetUtils::system_printer_bed_model(*curr);
|
||||
std::string texture_filename = PresetUtils::system_printer_bed_texture(*curr);
|
||||
if (!model_filename.empty() && !texture_filename.empty())
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
return { EType::System, model_filename, texture_filename };
|
||||
#else
|
||||
return { System, model_filename, texture_filename };
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
return { Type::System, model_filename, texture_filename };
|
||||
}
|
||||
}
|
||||
|
||||
@ -428,16 +321,12 @@ std::tuple<Bed3D::EType, std::string, std::string> Bed3D::detect_type(const Poin
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
return { EType::Custom, "", "" };
|
||||
#else
|
||||
return { Custom, "", "" };
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
return { Type::Custom, {}, {} };
|
||||
}
|
||||
|
||||
void Bed3D::render_axes() const
|
||||
{
|
||||
if (!m_shape.empty())
|
||||
if (m_build_volume.valid())
|
||||
m_axes.render();
|
||||
}
|
||||
|
||||
@ -596,12 +485,10 @@ void Bed3D::render_model() const
|
||||
model->set_color(-1, DEFAULT_MODEL_COLOR);
|
||||
|
||||
// move the model so that its origin (0.0, 0.0, 0.0) goes into the bed shape center and a bit down to avoid z-fighting with the texture quad
|
||||
Vec3d shift = m_bounding_box.center();
|
||||
shift(2) = -0.03;
|
||||
*const_cast<Vec3d*>(&m_model_offset) = shift;
|
||||
*const_cast<Vec3d*>(&m_model_offset) = to_3d(m_build_volume.bounding_volume2d().center(), -0.03);
|
||||
|
||||
// update extended bounding box
|
||||
calc_bounding_boxes();
|
||||
const_cast<BoundingBoxf3&>(m_extended_bounding_box) = this->calc_extended_bounding_box();
|
||||
}
|
||||
|
||||
if (!model->get_filename().empty()) {
|
||||
@ -673,7 +560,7 @@ void Bed3D::render_default(bool bottom, bool picking) const
|
||||
}
|
||||
}
|
||||
|
||||
void Bed3D::reset()
|
||||
void Bed3D::release_VBOs()
|
||||
{
|
||||
if (m_vbo_id > 0) {
|
||||
glsafe(::glDeleteBuffers(1, &m_vbo_id));
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include "3DScene.hpp"
|
||||
#include "GLModel.hpp"
|
||||
|
||||
#include <libslic3r/BuildVolume.hpp>
|
||||
|
||||
#include <tuple>
|
||||
#include <array>
|
||||
|
||||
@ -62,41 +64,22 @@ class Bed3D
|
||||
};
|
||||
|
||||
public:
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
enum class EType : unsigned char
|
||||
enum class Type : unsigned char
|
||||
{
|
||||
// The print bed model and texture are available from some printer preset.
|
||||
System,
|
||||
// The print bed model is unknown, thus it is rendered procedurally.
|
||||
Custom
|
||||
};
|
||||
|
||||
enum class EShapeType : unsigned char
|
||||
{
|
||||
Rectangle,
|
||||
Circle,
|
||||
Custom,
|
||||
Invalid
|
||||
};
|
||||
#else
|
||||
enum EType : unsigned char
|
||||
{
|
||||
System,
|
||||
Custom,
|
||||
Num_Types
|
||||
};
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
private:
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
EType m_type{ EType::Custom };
|
||||
EShapeType m_shape_type{ EShapeType::Invalid };
|
||||
#else
|
||||
EType m_type{ Custom };
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
Pointfs m_shape;
|
||||
BuildVolume m_build_volume;
|
||||
Type m_type{ Type::Custom };
|
||||
std::string m_texture_filename;
|
||||
std::string m_model_filename;
|
||||
BoundingBoxf3 m_bounding_box;
|
||||
// Print volume bounding box exteded with axes and model.
|
||||
BoundingBoxf3 m_extended_bounding_box;
|
||||
// Slightly expanded print bed polygon, for collision detection.
|
||||
Polygon m_polygon;
|
||||
GeometryBuffer m_triangles;
|
||||
GeometryBuffer m_gridlines;
|
||||
@ -112,42 +95,39 @@ private:
|
||||
|
||||
public:
|
||||
Bed3D() = default;
|
||||
~Bed3D() { reset(); }
|
||||
~Bed3D() { release_VBOs(); }
|
||||
|
||||
EType get_type() const { return m_type; }
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
EShapeType get_shape_type() const { return m_shape_type; }
|
||||
bool is_custom() const { return m_type == EType::Custom; }
|
||||
#else
|
||||
bool is_custom() const { return m_type == Custom; }
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
const Pointfs& get_shape() const { return m_shape; }
|
||||
// Update print bed model from configuration.
|
||||
// Return true if the bed shape changed, so the calee will update the UI.
|
||||
bool set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false);
|
||||
//FIXME if the build volume max print height is updated, this function still returns zero
|
||||
// as this class does not use it, thus there is no need to update the UI.
|
||||
bool set_shape(const Pointfs& bed_shape, const double max_print_height, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false);
|
||||
|
||||
const BoundingBoxf3& get_bounding_box(bool extended) const { return extended ? m_extended_bounding_box : m_bounding_box; }
|
||||
// Build volume geometry for various collision detection tasks.
|
||||
const BuildVolume& build_volume() const { return m_build_volume; }
|
||||
|
||||
// Was the model provided, or was it generated procedurally?
|
||||
Type get_type() const { return m_type; }
|
||||
// Was the model generated procedurally?
|
||||
bool is_custom() const { return m_type == Type::Custom; }
|
||||
|
||||
// Bounding box around the print bed, axes and model, for rendering.
|
||||
const BoundingBoxf3& extended_bounding_box() const { return m_extended_bounding_box; }
|
||||
|
||||
// Check against an expanded 2d bounding box.
|
||||
//FIXME shall one check against the real build volume?
|
||||
bool contains(const Point& point) const;
|
||||
Point point_projection(const Point& point) const;
|
||||
|
||||
void render(GLCanvas3D& canvas, bool bottom, float scale_factor,
|
||||
bool show_axes, bool show_texture);
|
||||
|
||||
void render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes, bool show_texture);
|
||||
void render_for_picking(GLCanvas3D& canvas, bool bottom, float scale_factor);
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
static bool is_rectangle(const Pointfs& shape, Vec2d* min = nullptr, Vec2d* max = nullptr);
|
||||
static bool is_circle(const Pointfs& shape, Vec2d* center = nullptr, double* radius = nullptr);
|
||||
static bool is_convex(const Pointfs& shape);
|
||||
static EShapeType detect_shape_type(const Pointfs& shape);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
private:
|
||||
void calc_bounding_boxes() const;
|
||||
// Calculate an extended bounding box from axes and current model for visualization purposes.
|
||||
BoundingBoxf3 calc_extended_bounding_box() const;
|
||||
void calc_triangles(const ExPolygon& poly);
|
||||
void calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox);
|
||||
std::tuple<EType, std::string, std::string> detect_type(const Pointfs& shape) const;
|
||||
static std::tuple<Type, std::string, std::string> detect_type(const Pointfs& shape);
|
||||
void render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
|
||||
bool show_axes, bool show_texture, bool picking);
|
||||
void render_axes() const;
|
||||
@ -156,7 +136,7 @@ private:
|
||||
void render_model() const;
|
||||
void render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture, bool picking) const;
|
||||
void render_default(bool bottom, bool picking) const;
|
||||
void reset();
|
||||
void release_VBOs();
|
||||
};
|
||||
|
||||
} // GUI
|
||||
|
@ -11,10 +11,8 @@
|
||||
#include "GUI_App.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "BitmapCache.hpp"
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
#include "3DBed.hpp"
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
#include "libslic3r/BuildVolume.hpp"
|
||||
#include "libslic3r/ExtrusionEntity.hpp"
|
||||
#include "libslic3r/ExtrusionEntityCollection.hpp"
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
@ -27,6 +25,7 @@
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/Tesselate.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -264,11 +263,9 @@ void GLIndexedVertexArray::render(
|
||||
const std::pair<size_t, size_t>& tverts_range,
|
||||
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->triangle_indices_VBO_id != 0 || this->quad_indices_VBO_id != 0);
|
||||
@ -526,7 +523,6 @@ BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d &
|
||||
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);
|
||||
@ -541,7 +537,6 @@ const BoundingBoxf3& GLVolume::transformed_non_sinking_bounding_box() const
|
||||
}
|
||||
return *m_transformed_non_sinking_bounding_box;
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
void GLVolume::set_range(double min_z, double max_z)
|
||||
{
|
||||
@ -617,22 +612,6 @@ void GLVolume::render_sinking_contours()
|
||||
m_sinking_contours.render();
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
void GLVolume::calc_convex_hull_3d()
|
||||
{
|
||||
const std::vector<float> &src = this->indexed_vertex_array.vertices_and_normals_interleaved;
|
||||
std::vector<Vec3f> pts;
|
||||
assert(src.size() % 6 == 0);
|
||||
pts.reserve(src.size() / 6);
|
||||
for (auto it = src.begin(); it != src.end(); ) {
|
||||
it += 3;
|
||||
pts.push_back({ *it, *(it + 1), *(it + 2) });
|
||||
it += 3;
|
||||
}
|
||||
this->set_convex_hull(TriangleMesh(its_convex_hull(pts)));
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
std::vector<int> GLVolumeCollection::load_object(
|
||||
const ModelObject *model_object,
|
||||
int obj_idx,
|
||||
@ -791,9 +770,7 @@ int GLVolumeCollection::load_wipe_tower_preview(
|
||||
volumes.emplace_back(new GLVolume(color));
|
||||
GLVolume& v = *volumes.back();
|
||||
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.set_volume_offset(Vec3d(pos_x, pos_y, 0.0));
|
||||
v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle));
|
||||
@ -900,17 +877,10 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
|
||||
shader->set_uniform("uniform_color", volume.first->render_color);
|
||||
shader->set_uniform("z_range", m_z_range, 2);
|
||||
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.max", m_print_box_max, 3);
|
||||
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());
|
||||
#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.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);
|
||||
@ -959,136 +929,51 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state, bool as_toolpaths) const
|
||||
#else
|
||||
bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state) const
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool GLVolumeCollection::check_outside_state(const BuildVolume &build_volume, ModelInstanceEPrintVolumeState *out_state) const
|
||||
{
|
||||
if (config == nullptr)
|
||||
return false;
|
||||
|
||||
const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(config->option("bed_shape"));
|
||||
if (opt == nullptr)
|
||||
return false;
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
const Polygon bed_poly = offset(Polygon::new_scale(opt->values), static_cast<float>(scale_(BedEpsilon))).front();
|
||||
const float bed_height = config->opt_float("max_print_height");
|
||||
const BoundingBox bed_box_2D = get_extents(bed_poly);
|
||||
BoundingBoxf3 print_volume({ unscale<double>(bed_box_2D.min.x()), unscale<double>(bed_box_2D.min.y()), -1e10 },
|
||||
{ unscale<double>(bed_box_2D.max.x()), unscale<double>(bed_box_2D.max.y()), bed_height });
|
||||
|
||||
auto check_against_rectangular_bed = [&print_volume](GLVolume& volume, ModelInstanceEPrintVolumeState& state) {
|
||||
const BoundingBoxf3* const bb = (volume.is_sinking() && volume.object_idx() != -1 && volume.volume_idx() != -1) ? &volume.transformed_non_sinking_bounding_box() : &volume.transformed_convex_hull_bounding_box();
|
||||
volume.is_outside = !print_volume.contains(*bb);
|
||||
if (volume.printable) {
|
||||
if (state == ModelInstancePVS_Inside && volume.is_outside)
|
||||
state = ModelInstancePVS_Fully_Outside;
|
||||
if (state == ModelInstancePVS_Fully_Outside && volume.is_outside && print_volume.intersects(*bb))
|
||||
state = ModelInstancePVS_Partly_Outside;
|
||||
}
|
||||
};
|
||||
|
||||
auto check_against_circular_bed = [bed_height](GLVolume& volume, ModelInstanceEPrintVolumeState& state, const Vec2d& center, double radius) {
|
||||
const TriangleMesh* mesh = (volume.is_sinking() && volume.object_idx() != -1 && volume.volume_idx() != -1) ? &GUI::wxGetApp().plater()->model().objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : volume.convex_hull();
|
||||
const double sq_radius = sqr(radius);
|
||||
size_t outside_count = 0;
|
||||
size_t valid_count = 0;
|
||||
for (const Vec3f& v : mesh->its.vertices) {
|
||||
const Vec3f world_v = volume.world_matrix().cast<float>() * v;
|
||||
if (0.0f <= world_v.z()) {
|
||||
++valid_count;
|
||||
if (sq_radius < sqr(world_v.x() - center.x()) + sqr(world_v.y() - center.y()) || bed_height < world_v.z())
|
||||
++outside_count;
|
||||
}
|
||||
}
|
||||
volume.is_outside = outside_count > 0;
|
||||
if (volume.printable) {
|
||||
if (state == ModelInstancePVS_Inside && volume.is_outside)
|
||||
state = ModelInstancePVS_Fully_Outside;
|
||||
if (state == ModelInstancePVS_Fully_Outside && volume.is_outside && outside_count < valid_count)
|
||||
state = ModelInstancePVS_Partly_Outside;
|
||||
}
|
||||
};
|
||||
|
||||
auto check_against_convex_bed = [&bed_poly, bed_height](GLVolume& volume, ModelInstanceEPrintVolumeState& state) {
|
||||
const TriangleMesh* mesh = (volume.is_sinking() && volume.object_idx() != -1 && volume.volume_idx() != -1) ? &GUI::wxGetApp().plater()->model().objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : volume.convex_hull();
|
||||
const Polygon volume_hull_2d = its_convex_hull_2d_above(mesh->its, volume.world_matrix().cast<float>(), 0.0f);
|
||||
const BoundingBoxf3* const bb = (volume.is_sinking() && volume.object_idx() != -1 && volume.volume_idx() != -1) ? &volume.transformed_non_sinking_bounding_box() : &volume.transformed_convex_hull_bounding_box();
|
||||
// Using rotating callipers to check for collision of two convex polygons.
|
||||
ModelInstanceEPrintVolumeState volume_state = printbed_collision_state(bed_poly, bed_height, volume_hull_2d, bb->min.z(), bb->max.z());
|
||||
bool contained = (volume_state == ModelInstancePVS_Inside);
|
||||
bool intersects = (volume_state == ModelInstancePVS_Partly_Outside);
|
||||
|
||||
volume.is_outside = !contained;
|
||||
if (volume.printable) {
|
||||
if (state == ModelInstancePVS_Inside && volume.is_outside)
|
||||
state = ModelInstancePVS_Fully_Outside;
|
||||
|
||||
if (state == ModelInstancePVS_Fully_Outside && volume.is_outside && intersects)
|
||||
state = ModelInstancePVS_Partly_Outside;
|
||||
}
|
||||
};
|
||||
#else
|
||||
const BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
|
||||
BoundingBoxf3 print_volume({ unscale<double>(bed_box_2D.min.x()), unscale<double>(bed_box_2D.min.y()), 0.0 },
|
||||
{ unscale<double>(bed_box_2D.max.x()), unscale<double>(bed_box_2D.max.y()), config->opt_float("max_print_height") });
|
||||
// Allow the objects to protrude below the print bed
|
||||
print_volume.min.z() = -1e10;
|
||||
print_volume.min.x() -= BedEpsilon;
|
||||
print_volume.min.y() -= BedEpsilon;
|
||||
print_volume.max.x() += BedEpsilon;
|
||||
print_volume.max.y() += BedEpsilon;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
const Model& model = GUI::wxGetApp().plater()->model();
|
||||
// Volume is partially below the print bed, thus a pre-calculated convex hull cannot be used.
|
||||
auto volume_sinking = [](GLVolume& volume) -> bool
|
||||
{ return volume.is_sinking() && volume.object_idx() != -1 && volume.volume_idx() != -1; };
|
||||
// Cached bounding box of a volume above the print bed.
|
||||
auto volume_bbox = [volume_sinking](GLVolume& volume) -> BoundingBoxf3
|
||||
{ return volume_sinking(volume) ? volume.transformed_non_sinking_bounding_box() : volume.transformed_convex_hull_bounding_box(); };
|
||||
// Cached 3D convex hull of a volume above the print bed.
|
||||
auto volume_convex_mesh = [volume_sinking, &model](GLVolume& volume) -> const TriangleMesh&
|
||||
{ return volume_sinking(volume) ? model.objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : *volume.convex_hull(); };
|
||||
|
||||
ModelInstanceEPrintVolumeState overall_state = ModelInstancePVS_Inside;
|
||||
bool contained_min_one = false;
|
||||
|
||||
enum class BedShape { Rectangle, Circle, Convex, NonConvex };
|
||||
Vec2d center;
|
||||
double radius;
|
||||
BedShape bed_shape =
|
||||
GUI::Bed3D::is_rectangle(opt->values) ? BedShape::Rectangle :
|
||||
GUI::Bed3D::is_circle(opt->values, ¢er, &radius) ? BedShape::Circle :
|
||||
GUI::Bed3D::is_convex(opt->values) ? BedShape::Convex : BedShape::NonConvex;
|
||||
|
||||
for (GLVolume* volume : this->volumes) {
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
if (as_toolpaths && !volume->is_extrusion_path)
|
||||
continue;
|
||||
else if (!as_toolpaths && (volume->is_modifier || (!volume->shader_outside_printer_detection_enabled && (volume->is_wipe_tower || volume->composite_id.volume_id < 0))))
|
||||
continue;
|
||||
|
||||
switch (bed_shape) {
|
||||
case BedShape::Rectangle: check_against_rectangular_bed(*volume, overall_state); break;
|
||||
case BedShape::Circle: check_against_circular_bed(*volume, overall_state, center, radius); break;
|
||||
case BedShape::Convex: check_against_convex_bed(*volume, overall_state); break;
|
||||
default: break;
|
||||
for (GLVolume* volume : this->volumes)
|
||||
if (! volume->is_modifier && (volume->shader_outside_printer_detection_enabled || (! volume->is_wipe_tower && volume->composite_id.volume_id >= 0))) {
|
||||
BuildVolume::ObjectState state;
|
||||
switch (build_volume.type()) {
|
||||
case BuildVolume::Type::Rectangle:
|
||||
//FIXME this test does not evaluate collision of a build volume bounding box with non-convex objects.
|
||||
state = build_volume.volume_state_bbox(volume_bbox(*volume));
|
||||
break;
|
||||
case BuildVolume::Type::Circle:
|
||||
case BuildVolume::Type::Convex:
|
||||
//FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently.
|
||||
case BuildVolume::Type::Custom:
|
||||
state = build_volume.object_state(volume_convex_mesh(*volume).its, volume->world_matrix().cast<float>(), volume_sinking(*volume));
|
||||
break;
|
||||
default:
|
||||
// Ignore, don't produce any collision.
|
||||
state = BuildVolume::ObjectState::Inside;
|
||||
break;
|
||||
}
|
||||
volume->is_outside = state != BuildVolume::ObjectState::Inside;
|
||||
if (volume->printable) {
|
||||
if (overall_state == ModelInstancePVS_Inside && volume->is_outside)
|
||||
overall_state = ModelInstancePVS_Fully_Outside;
|
||||
if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && state == BuildVolume::ObjectState::Colliding)
|
||||
overall_state = ModelInstancePVS_Partly_Outside;
|
||||
contained_min_one |= !volume->is_outside;
|
||||
}
|
||||
}
|
||||
|
||||
contained_min_one |= !volume->is_outside;
|
||||
#else
|
||||
if (volume->is_modifier || (!volume->shader_outside_printer_detection_enabled && (volume->is_wipe_tower || volume->composite_id.volume_id < 0)))
|
||||
continue;
|
||||
|
||||
const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box();
|
||||
bool contained = print_volume.contains(bb);
|
||||
|
||||
volume->is_outside = !contained;
|
||||
if (!volume->printable)
|
||||
continue;
|
||||
|
||||
contained_min_one |= contained;
|
||||
|
||||
if (overall_state == ModelInstancePVS_Inside && volume->is_outside)
|
||||
overall_state = ModelInstancePVS_Fully_Outside;
|
||||
|
||||
if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && print_volume.intersects(bb))
|
||||
overall_state = ModelInstancePVS_Partly_Outside;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
}
|
||||
|
||||
if (out_state != nullptr)
|
||||
*out_state = overall_state;
|
||||
|
||||
@ -1136,7 +1021,9 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con
|
||||
|
||||
if (static_cast<PrinterTechnology>(config->opt_int("printer_technology")) == ptSLA)
|
||||
{
|
||||
const std::string& txt_color = config->opt_string("material_colour");
|
||||
const std::string& txt_color = config->opt_string("material_colour").empty() ?
|
||||
print_config_def.get("material_colour")->get_default_value<ConfigOptionString>()->value :
|
||||
config->opt_string("material_colour");
|
||||
if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb)) {
|
||||
colors.resize(1);
|
||||
colors[0].set(txt_color, rgb);
|
||||
|
@ -31,6 +31,7 @@
|
||||
namespace Slic3r {
|
||||
class SLAPrintObject;
|
||||
enum SLAPrintObjectStep : unsigned int;
|
||||
class BuildVolume;
|
||||
class DynamicPrintConfig;
|
||||
class ExtrusionPath;
|
||||
class ExtrusionMultiPath;
|
||||
@ -281,10 +282,8 @@ private:
|
||||
std::shared_ptr<const TriangleMesh> m_convex_hull;
|
||||
// Bounding box of this volume, in unscaled coordinates.
|
||||
std::optional<BoundingBoxf3> m_transformed_convex_hull_bounding_box;
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// Bounding box of the non sinking part of this volume, in unscaled coordinates.
|
||||
std::optional<BoundingBoxf3> m_transformed_non_sinking_bounding_box;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
class SinkingContours
|
||||
{
|
||||
@ -475,12 +474,10 @@ public:
|
||||
BoundingBoxf3 transformed_convex_hull_bounding_box(const Transform3d &trafo) const;
|
||||
// caching variant
|
||||
const BoundingBoxf3& transformed_convex_hull_bounding_box() const;
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// non-caching variant
|
||||
BoundingBoxf3 transformed_non_sinking_bounding_box(const Transform3d& trafo) const;
|
||||
// caching variant
|
||||
const BoundingBoxf3& transformed_non_sinking_bounding_box() const;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// convex hull
|
||||
const TriangleMesh* convex_hull() const { return m_convex_hull.get(); }
|
||||
|
||||
@ -493,15 +490,11 @@ public:
|
||||
void finalize_geometry(bool opengl_initialized) { this->indexed_vertex_array.finalize_geometry(opengl_initialized); }
|
||||
void release_geometry() { this->indexed_vertex_array.release_geometry(); }
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
void set_bounding_boxes_as_dirty() {
|
||||
m_transformed_bounding_box.reset();
|
||||
m_transformed_convex_hull_bounding_box.reset();
|
||||
m_transformed_non_sinking_bounding_box.reset();
|
||||
}
|
||||
#else
|
||||
void set_bounding_boxes_as_dirty() { m_transformed_bounding_box.reset(); m_transformed_convex_hull_bounding_box.reset(); }
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
bool is_sla_support() const;
|
||||
bool is_sla_pad() const;
|
||||
@ -518,12 +511,6 @@ public:
|
||||
// Return an estimate of the memory held by GPU vertex buffers.
|
||||
size_t gpu_memory_used() const { return this->indexed_vertex_array.gpu_memory_used(); }
|
||||
size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); }
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// calculates the 3D convex hull from indexed_vertex_array.vertices_and_normals_interleaved
|
||||
// must be called before calling indexed_vertex_array.finalize_geometry();
|
||||
void calc_convex_hull_3d();
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
};
|
||||
|
||||
typedef std::vector<GLVolume*> GLVolumePtrs;
|
||||
@ -540,7 +527,6 @@ public:
|
||||
All
|
||||
};
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
struct PrintVolume
|
||||
{
|
||||
// see: Bed3D::EShapeType
|
||||
@ -554,16 +540,9 @@ public:
|
||||
// [0] = min z, [1] = max z
|
||||
std::array<float, 2> zs;
|
||||
};
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
private:
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
PrintVolume m_print_volume;
|
||||
#else
|
||||
// min and max vertex of the print box volume
|
||||
float m_print_box_min[3];
|
||||
float m_print_box_max[3];
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
// z range for clipping in shaders
|
||||
float m_z_range[2];
|
||||
@ -635,14 +614,7 @@ public:
|
||||
bool empty() const { return volumes.empty(); }
|
||||
void set_range(double low, double high) { for (GLVolume *vol : this->volumes) vol->set_range(low, high); }
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
void set_print_volume(const PrintVolume& print_volume) { m_print_volume = print_volume; }
|
||||
#else
|
||||
void set_print_box(float min_x, float min_y, float min_z, float max_x, float max_y, float max_z) {
|
||||
m_print_box_min[0] = min_x; m_print_box_min[1] = min_y; m_print_box_min[2] = min_z;
|
||||
m_print_box_max[0] = max_x; m_print_box_max[1] = max_y; m_print_box_max[2] = max_z;
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
void set_z_range(float min_z, float max_z) { m_z_range[0] = min_z; m_z_range[1] = max_z; }
|
||||
void set_clipping_plane(const double* coeffs) { m_clipping_plane[0] = coeffs[0]; m_clipping_plane[1] = coeffs[1]; m_clipping_plane[2] = coeffs[2]; m_clipping_plane[3] = coeffs[3]; }
|
||||
@ -657,11 +629,7 @@ public:
|
||||
|
||||
// returns true if all the volumes are completely contained in the print volume
|
||||
// returns the containment state in the given out_state, if non-null
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state, bool as_toolpaths = false) const;
|
||||
#else
|
||||
bool check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state) const;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool check_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state) const;
|
||||
void reset_outside_state();
|
||||
|
||||
void update_colors_by_extruder(const DynamicPrintConfig* config);
|
||||
@ -699,8 +667,6 @@ struct _3DScene
|
||||
static void point3_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume);
|
||||
};
|
||||
|
||||
static constexpr float BedEpsilon = 3.f * float(EPSILON);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -86,7 +86,7 @@ public:
|
||||
void set_fff_print(Print *print) { m_fff_print = print; }
|
||||
void set_sla_print(SLAPrint *print) { m_sla_print = print; m_sla_print->set_printer(&m_sla_archive); }
|
||||
void set_thumbnail_cb(ThumbnailsGeneratorCallback cb) { m_thumbnail_cb = cb; }
|
||||
void set_gcode_result(GCodeProcessor::Result* result) { m_gcode_result = result; }
|
||||
void set_gcode_result(GCodeProcessorResult* result) { m_gcode_result = result; }
|
||||
|
||||
// The following wxCommandEvent will be sent to the UI thread / Plater window, when the slicing is finished
|
||||
// and the background processing will transition into G-code export.
|
||||
@ -216,7 +216,7 @@ private:
|
||||
Print *m_fff_print = nullptr;
|
||||
SLAPrint *m_sla_print = nullptr;
|
||||
// Data structure, to which the G-code export writes its annotations.
|
||||
GCodeProcessor::Result *m_gcode_result = nullptr;
|
||||
GCodeProcessorResult *m_gcode_result = nullptr;
|
||||
// Callback function, used to write thumbnails into gcode.
|
||||
ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr;
|
||||
SL1Archive m_sla_archive;
|
||||
|
@ -22,98 +22,7 @@ namespace GUI {
|
||||
|
||||
BedShape::BedShape(const ConfigOptionPoints& points)
|
||||
{
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
if (points.size() < 3) {
|
||||
m_type = Bed3D::EShapeType::Invalid;
|
||||
return;
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
// is this a rectangle ?
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
Vec2d min;
|
||||
Vec2d max;
|
||||
if (Bed3D::is_rectangle(points.values, &min, &max)) {
|
||||
m_type = Bed3D::EShapeType::Rectangle;
|
||||
m_rectSize = max - min;
|
||||
m_rectOrigin = -min;
|
||||
return;
|
||||
}
|
||||
#else
|
||||
Polygon polygon = Polygon::new_scale(points.values);
|
||||
if (points.size() == 4) {
|
||||
auto lines = polygon.lines();
|
||||
if (lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3])) {
|
||||
// okay, it's a rectangle
|
||||
// find origin
|
||||
coordf_t x_min, x_max, y_min, y_max;
|
||||
x_max = x_min = points.values[0](0);
|
||||
y_max = y_min = points.values[0](1);
|
||||
for (auto pt : points.values)
|
||||
{
|
||||
x_min = std::min(x_min, pt(0));
|
||||
x_max = std::max(x_max, pt(0));
|
||||
y_min = std::min(y_min, pt(1));
|
||||
y_max = std::max(y_max, pt(1));
|
||||
}
|
||||
|
||||
m_type = Type::Rectangular;
|
||||
m_rectSize = Vec2d(x_max - x_min, y_max - y_min);
|
||||
m_rectOrigin = Vec2d(-x_min, -y_min);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
// is this a circle ?
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
Vec2d center;
|
||||
double radius;
|
||||
if (Bed3D::is_circle(points.values, ¢er, &radius)) {
|
||||
m_type = Bed3D::EShapeType::Circle;
|
||||
m_diameter = 2.0 * radius;
|
||||
return;
|
||||
}
|
||||
|
||||
// This is a custom bed shape, use the polygon provided.
|
||||
m_type = Bed3D::EShapeType::Custom;
|
||||
#else
|
||||
{
|
||||
// Analyze the array of points.Do they reside on a circle ?
|
||||
auto center = polygon.bounding_box().center();
|
||||
std::vector<double> vertex_distances;
|
||||
double avg_dist = 0;
|
||||
for (auto pt : polygon.points)
|
||||
{
|
||||
double distance = (pt - center).cast<double>().norm();
|
||||
vertex_distances.push_back(distance);
|
||||
avg_dist += distance;
|
||||
}
|
||||
|
||||
avg_dist /= vertex_distances.size();
|
||||
bool defined_value = true;
|
||||
for (auto el : vertex_distances)
|
||||
{
|
||||
if (abs(el - avg_dist) > 10 * SCALED_EPSILON)
|
||||
defined_value = false;
|
||||
break;
|
||||
}
|
||||
if (defined_value) {
|
||||
// all vertices are equidistant to center
|
||||
m_type = Type::Circular;
|
||||
m_diameter = unscale<double>(avg_dist * 2);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (points.size() < 3)
|
||||
return;
|
||||
|
||||
// This is a custom bed shape, use the polygon provided.
|
||||
m_type = Type::Custom;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
m_build_volume = { points.values, 0. };
|
||||
}
|
||||
|
||||
static std::string get_option_label(BedShape::Parameter param)
|
||||
@ -122,119 +31,101 @@ static std::string get_option_label(BedShape::Parameter param)
|
||||
case BedShape::Parameter::RectSize : return L("Size");
|
||||
case BedShape::Parameter::RectOrigin: return L("Origin");
|
||||
case BedShape::Parameter::Diameter : return L("Diameter");
|
||||
default: return "";
|
||||
default: assert(false); return {};
|
||||
}
|
||||
}
|
||||
|
||||
void BedShape::append_option_line(ConfigOptionsGroupShp optgroup, Parameter param)
|
||||
{
|
||||
ConfigOptionDef def;
|
||||
|
||||
if (param == Parameter::RectSize) {
|
||||
t_config_option_key key;
|
||||
switch (param) {
|
||||
case Parameter::RectSize:
|
||||
def.type = coPoints;
|
||||
def.set_default_value(new ConfigOptionPoints{ Vec2d(200, 200) });
|
||||
def.min = 0;
|
||||
def.max = 1200;
|
||||
def.label = get_option_label(param);
|
||||
def.tooltip = L("Size in X and Y of the rectangular plate.");
|
||||
|
||||
Option option(def, "rect_size");
|
||||
optgroup->append_single_option_line(option);
|
||||
}
|
||||
else if (param == Parameter::RectOrigin) {
|
||||
key = "rect_size";
|
||||
break;
|
||||
case Parameter::RectOrigin:
|
||||
def.type = coPoints;
|
||||
def.set_default_value(new ConfigOptionPoints{ Vec2d(0, 0) });
|
||||
def.min = -600;
|
||||
def.max = 600;
|
||||
def.label = get_option_label(param);
|
||||
def.tooltip = L("Distance of the 0,0 G-code coordinate from the front left corner of the rectangle.");
|
||||
|
||||
Option option(def, "rect_origin");
|
||||
optgroup->append_single_option_line(option);
|
||||
}
|
||||
else if (param == Parameter::Diameter) {
|
||||
key = "rect_origin";
|
||||
break;
|
||||
case Parameter::Diameter:
|
||||
def.type = coFloat;
|
||||
def.set_default_value(new ConfigOptionFloat(200));
|
||||
def.sidetext = L("mm");
|
||||
def.label = get_option_label(param);
|
||||
def.tooltip = L("Diameter of the print bed. It is assumed that origin (0,0) is located in the center.");
|
||||
|
||||
Option option(def, "diameter");
|
||||
optgroup->append_single_option_line(option);
|
||||
key = "diameter";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
optgroup->append_single_option_line({ def, std::move(key) });
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
wxString BedShape::get_name(Bed3D::EShapeType type)
|
||||
wxString BedShape::get_name(PageType type)
|
||||
{
|
||||
switch (type) {
|
||||
case Bed3D::EShapeType::Rectangle: { return _L("Rectangular"); }
|
||||
case Bed3D::EShapeType::Circle: { return _L("Circular"); }
|
||||
case Bed3D::EShapeType::Custom: { return _L("Custom"); }
|
||||
case Bed3D::EShapeType::Invalid:
|
||||
default: return _L("Invalid");
|
||||
case PageType::Rectangle: return _L("Rectangular");
|
||||
case PageType::Circle: return _L("Circular");
|
||||
case PageType::Custom: return _L("Custom");
|
||||
}
|
||||
// make visual studio happy
|
||||
assert(false);
|
||||
return {};
|
||||
}
|
||||
#else
|
||||
wxString BedShape::get_name(Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case Type::Rectangular: return _L("Rectangular");
|
||||
case Type::Circular: return _L("Circular");
|
||||
case Type::Custom: return _L("Custom");
|
||||
case Type::Invalid:
|
||||
default: return _L("Invalid");
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
size_t BedShape::get_type()
|
||||
BedShape::PageType BedShape::get_page_type()
|
||||
{
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
return static_cast<size_t>(m_type == Bed3D::EShapeType::Invalid ? Bed3D::EShapeType::Rectangle : m_type);
|
||||
#else
|
||||
return static_cast<size_t>(m_type == Type::Invalid ? Type::Rectangular : m_type);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
switch (m_build_volume.type()) {
|
||||
case BuildVolume::Type::Rectangle:
|
||||
case BuildVolume::Type::Invalid: return PageType::Rectangle;
|
||||
case BuildVolume::Type::Circle: return PageType::Circle;
|
||||
case BuildVolume::Type::Convex:
|
||||
case BuildVolume::Type::Custom: return PageType::Custom;
|
||||
}
|
||||
// make visual studio happy
|
||||
assert(false);
|
||||
return PageType::Rectangle;
|
||||
}
|
||||
|
||||
wxString BedShape::get_full_name_with_params()
|
||||
{
|
||||
wxString out = _L("Shape") + ": " + get_name(m_type);
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
if (m_type == Bed3D::EShapeType::Rectangle) {
|
||||
#else
|
||||
if (m_type == Type::Rectangular) {
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
out += "\n" + _(get_option_label(Parameter::RectSize)) + ": [" + ConfigOptionPoint(m_rectSize).serialize() + "]";
|
||||
out += "\n" + _(get_option_label(Parameter::RectOrigin))+ ": [" + ConfigOptionPoint(m_rectOrigin).serialize() + "]";
|
||||
wxString out = _L("Shape") + ": " + get_name(this->get_page_type());
|
||||
switch (m_build_volume.type()) {
|
||||
case BuildVolume::Type::Circle:
|
||||
out += "\n" + _L(get_option_label(Parameter::Diameter)) + ": [" + double_to_string(2. * unscaled<double>(m_build_volume.circle().radius)) + "]";
|
||||
break;
|
||||
default:
|
||||
// rectangle, convex, concave...
|
||||
out += "\n" + _(get_option_label(Parameter::RectSize)) + ": [" + ConfigOptionPoint(to_2d(m_build_volume.bounding_volume().size())).serialize() + "]";
|
||||
out += "\n" + _(get_option_label(Parameter::RectOrigin)) + ": [" + ConfigOptionPoint(to_2d(m_build_volume.bounding_volume().min)).serialize() + "]";
|
||||
break;
|
||||
}
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
else if (m_type == Bed3D::EShapeType::Circle)
|
||||
#else
|
||||
else if (m_type == Type::Circular)
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
out += "\n" + _L(get_option_label(Parameter::Diameter)) + ": [" + double_to_string(m_diameter) + "]";
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void BedShape::apply_optgroup_values(ConfigOptionsGroupShp optgroup)
|
||||
{
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
if (m_type == Bed3D::EShapeType::Rectangle || m_type == Bed3D::EShapeType::Invalid) {
|
||||
#else
|
||||
if (m_type == Type::Rectangular || m_type == Type::Invalid) {
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
optgroup->set_value("rect_size" , new ConfigOptionPoints{ m_rectSize });
|
||||
optgroup->set_value("rect_origin" , new ConfigOptionPoints{ m_rectOrigin });
|
||||
switch (m_build_volume.type()) {
|
||||
case BuildVolume::Type::Circle:
|
||||
optgroup->set_value("diameter", double_to_string(2. * unscaled<double>(m_build_volume.circle().radius)));
|
||||
break;
|
||||
default:
|
||||
// rectangle, convex, concave...
|
||||
optgroup->set_value("rect_size" , new ConfigOptionPoints{ to_2d(m_build_volume.bounding_volume().size()) });
|
||||
optgroup->set_value("rect_origin" , new ConfigOptionPoints{ to_2d(m_build_volume.bounding_volume().min) });
|
||||
}
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
else if (m_type == Bed3D::EShapeType::Circle)
|
||||
#else
|
||||
else if (m_type == Type::Circular)
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
optgroup->set_value("diameter", double_to_string(m_diameter));
|
||||
}
|
||||
|
||||
void BedShapeDialog::build_dialog(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model)
|
||||
@ -295,28 +186,16 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf
|
||||
|
||||
sbsizer->Add(m_shape_options_book);
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
auto optgroup = init_shape_options_page(BedShape::get_name(Bed3D::EShapeType::Rectangle));
|
||||
#else
|
||||
auto optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Rectangular));
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
auto optgroup = init_shape_options_page(BedShape::get_name(BedShape::PageType::Rectangle));
|
||||
BedShape::append_option_line(optgroup, BedShape::Parameter::RectSize);
|
||||
BedShape::append_option_line(optgroup, BedShape::Parameter::RectOrigin);
|
||||
activate_options_page(optgroup);
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
optgroup = init_shape_options_page(BedShape::get_name(Bed3D::EShapeType::Circle));
|
||||
#else
|
||||
optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Circular));
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
optgroup = init_shape_options_page(BedShape::get_name(BedShape::PageType::Circle));
|
||||
BedShape::append_option_line(optgroup, BedShape::Parameter::Diameter);
|
||||
activate_options_page(optgroup);
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
optgroup = init_shape_options_page(BedShape::get_name(Bed3D::EShapeType::Custom));
|
||||
#else
|
||||
optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Custom));
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
optgroup = init_shape_options_page(BedShape::get_name(BedShape::PageType::Custom));
|
||||
|
||||
Line line{ "", "" };
|
||||
line.full_width = 1;
|
||||
@ -538,8 +417,8 @@ void BedShapePanel::set_shape(const ConfigOptionPoints& points)
|
||||
{
|
||||
BedShape shape(points);
|
||||
|
||||
m_shape_options_book->SetSelection(shape.get_type());
|
||||
shape.apply_optgroup_values(m_optgroups[shape.get_type()]);
|
||||
m_shape_options_book->SetSelection(int(shape.get_page_type()));
|
||||
shape.apply_optgroup_values(m_optgroups[int(shape.get_page_type())]);
|
||||
|
||||
// Copy the polygon to the canvas, make a copy of the array, if custom shape is selected
|
||||
if (shape.is_custom())
|
||||
@ -562,17 +441,9 @@ void BedShapePanel::update_shape()
|
||||
auto page_idx = m_shape_options_book->GetSelection();
|
||||
auto opt_group = m_optgroups[page_idx];
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
Bed3D::EShapeType page_type = static_cast<Bed3D::EShapeType>(page_idx);
|
||||
#else
|
||||
BedShape::Type page_type = static_cast<BedShape::Type>(page_idx);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
if (page_type == Bed3D::EShapeType::Rectangle) {
|
||||
#else
|
||||
if (page_type == BedShape::Type::Rectangular) {
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
switch (static_cast<BedShape::PageType>(page_idx)) {
|
||||
case BedShape::PageType::Rectangle:
|
||||
{
|
||||
Vec2d rect_size(Vec2d::Zero());
|
||||
Vec2d rect_origin(Vec2d::Zero());
|
||||
|
||||
@ -602,12 +473,10 @@ void BedShapePanel::update_shape()
|
||||
Vec2d(x1, y0),
|
||||
Vec2d(x1, y1),
|
||||
Vec2d(x0, y1) };
|
||||
break;
|
||||
}
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
else if (page_type == Bed3D::EShapeType::Circle) {
|
||||
#else
|
||||
else if (page_type == BedShape::Type::Circular) {
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
case BedShape::PageType::Circle:
|
||||
{
|
||||
double diameter;
|
||||
try { diameter = boost::any_cast<double>(opt_group->get_value("diameter")); }
|
||||
catch (const std::exception & /* e */) { return; }
|
||||
@ -615,6 +484,7 @@ void BedShapePanel::update_shape()
|
||||
if (diameter == 0.0) return ;
|
||||
auto r = diameter / 2;
|
||||
auto twopi = 2 * PI;
|
||||
// Don't change this value without adjusting BuildVolume constructor detecting circle diameter!
|
||||
auto edges = 72;
|
||||
std::vector<Vec2d> points;
|
||||
for (int i = 1; i <= edges; ++i) {
|
||||
@ -622,13 +492,12 @@ void BedShapePanel::update_shape()
|
||||
points.push_back(Vec2d(r*cos(angle), r*sin(angle)));
|
||||
}
|
||||
m_shape = points;
|
||||
break;
|
||||
}
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
else if (page_type == Bed3D::EShapeType::Custom)
|
||||
#else
|
||||
else if (page_type == BedShape::Type::Custom)
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
case BedShape::PageType::Custom:
|
||||
m_shape = m_loaded_shape;
|
||||
break;
|
||||
}
|
||||
|
||||
update_preview();
|
||||
}
|
||||
|
@ -5,11 +5,10 @@
|
||||
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "2DBed.hpp"
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
#include "3DBed.hpp"
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include <libslic3r/BuildVolume.hpp>
|
||||
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/choicebk.h>
|
||||
|
||||
@ -22,14 +21,11 @@ using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>;
|
||||
|
||||
struct BedShape
|
||||
{
|
||||
#if !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
enum class Type {
|
||||
Rectangular = 0,
|
||||
Circular,
|
||||
Custom,
|
||||
Invalid
|
||||
enum class PageType {
|
||||
Rectangle,
|
||||
Circle,
|
||||
Custom
|
||||
};
|
||||
#endif // !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
enum class Parameter {
|
||||
RectSize,
|
||||
@ -39,34 +35,18 @@ struct BedShape
|
||||
|
||||
BedShape(const ConfigOptionPoints& points);
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool is_custom() { return m_type == Bed3D::EShapeType::Custom; }
|
||||
#else
|
||||
bool is_custom() { return m_type == Type::Custom; }
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool is_custom() { return m_build_volume.type() == BuildVolume::Type::Convex || m_build_volume.type() == BuildVolume::Type::Custom; }
|
||||
|
||||
static void append_option_line(ConfigOptionsGroupShp optgroup, Parameter param);
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
static wxString get_name(Bed3D::EShapeType type);
|
||||
#else
|
||||
static wxString get_name(Type type);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
static wxString get_name(PageType type);
|
||||
|
||||
// convert Type to size_t
|
||||
size_t get_type();
|
||||
PageType get_page_type();
|
||||
|
||||
wxString get_full_name_with_params();
|
||||
void apply_optgroup_values(ConfigOptionsGroupShp optgroup);
|
||||
|
||||
private:
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
Bed3D::EShapeType m_type{ Bed3D::EShapeType::Invalid };
|
||||
#else
|
||||
Type m_type {Type::Invalid};
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
Vec2d m_rectSize {200, 200};
|
||||
Vec2d m_rectOrigin {0, 0};
|
||||
double m_diameter {0};
|
||||
BuildVolume m_build_volume;
|
||||
};
|
||||
|
||||
class BedShapePanel : public wxPanel
|
||||
|
@ -315,7 +315,7 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
|
||||
set_value(double_to_string(val), true);
|
||||
}
|
||||
else if (((m_opt.sidetext.rfind("mm/s") != std::string::npos && val > m_opt.max) ||
|
||||
(m_opt.sidetext.rfind("mm ") != std::string::npos && val > 1)) &&
|
||||
(m_opt.sidetext.rfind("mm ") != std::string::npos && val > /*1*/m_opt.max_literal)) &&
|
||||
(m_value.empty() || std::string(str.ToUTF8().data()) != boost::any_cast<std::string>(m_value)))
|
||||
{
|
||||
if (!check_value) {
|
||||
@ -330,7 +330,6 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
|
||||
const wxString msg_text = from_u8((boost::format(_utf8(L("Do you mean %s%% instead of %s %s?\n"
|
||||
"Select YES if you want to change this value to %s%%, \n"
|
||||
"or NO if you are sure that %s %s is a correct value."))) % stVal % stVal % sidetext % stVal % stVal % sidetext).str());
|
||||
// wxMessageDialog dialog(m_parent, msg_text, _(L("Parameter validation")) + ": " + m_opt_id , wxICON_WARNING | wxYES | wxNO);
|
||||
WarningDialog dialog(m_parent, msg_text, _L("Parameter validation") + ": " + m_opt_id, wxYES | wxNO);
|
||||
if ((!infill_anchors || val > 100) && dialog.ShowModal() == wxID_YES) {
|
||||
set_value(from_u8((boost::format("%s%%") % stVal).str()), false/*true*/);
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "GCodeViewer.hpp"
|
||||
|
||||
#include "libslic3r/BuildVolume.hpp"
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
@ -20,9 +21,6 @@
|
||||
#include "GLToolbar.hpp"
|
||||
#include "GUI_Preview.hpp"
|
||||
#include "GUI_ObjectManipulation.hpp"
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
#include "3DBed.hpp"
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
#include <imgui/imgui_internal.h>
|
||||
|
||||
@ -123,7 +121,7 @@ void GCodeViewer::IBuffer::reset()
|
||||
count = 0;
|
||||
}
|
||||
|
||||
bool GCodeViewer::Path::matches(const GCodeProcessor::MoveVertex& move) const
|
||||
bool GCodeViewer::Path::matches(const GCodeProcessorResult::MoveVertex& move) const
|
||||
{
|
||||
auto matches_percent = [](float value1, float value2, float max_percent) {
|
||||
return std::abs(value2 - value1) / value1 <= max_percent;
|
||||
@ -174,7 +172,7 @@ void GCodeViewer::TBuffer::reset()
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
}
|
||||
|
||||
void GCodeViewer::TBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id)
|
||||
void GCodeViewer::TBuffer::add_path(const GCodeProcessorResult::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id)
|
||||
{
|
||||
Path::Endpoint endpoint = { b_id, i_id, s_id, move.position };
|
||||
// use rounding to reduce the number of generated paths
|
||||
@ -665,7 +663,7 @@ void GCodeViewer::init()
|
||||
}
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized)
|
||||
void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& print, bool initialized)
|
||||
{
|
||||
// avoid processing if called with the same gcode_result
|
||||
if (m_last_result_id == gcode_result.id)
|
||||
@ -683,9 +681,7 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print&
|
||||
if (wxGetApp().is_gcode_viewer())
|
||||
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);
|
||||
|
||||
@ -737,7 +733,7 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print&
|
||||
{ min.x(), max.y() } };
|
||||
}
|
||||
|
||||
wxGetApp().plater()->set_bed_shape(bed_shape, texture, model, gcode_result.bed_shape.empty());
|
||||
wxGetApp().plater()->set_bed_shape(bed_shape, gcode_result.max_print_height, texture, model, gcode_result.bed_shape.empty());
|
||||
}
|
||||
|
||||
m_print_statistics = gcode_result.print_statistics;
|
||||
@ -750,7 +746,7 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print&
|
||||
}
|
||||
}
|
||||
|
||||
void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors)
|
||||
void GCodeViewer::refresh(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors)
|
||||
{
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
auto start_time = std::chrono::high_resolution_clock::now();
|
||||
@ -779,7 +775,7 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std:
|
||||
if (i == 0)
|
||||
continue;
|
||||
|
||||
const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i];
|
||||
const GCodeProcessorResult::MoveVertex& curr = gcode_result.moves[i];
|
||||
|
||||
switch (curr.type)
|
||||
{
|
||||
@ -833,9 +829,7 @@ void GCodeViewer::reset()
|
||||
|
||||
m_paths_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_extruders_count = 0;
|
||||
m_extruder_ids = std::vector<unsigned char>();
|
||||
@ -852,9 +846,7 @@ void GCodeViewer::reset()
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
m_statistics.reset_all();
|
||||
#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()
|
||||
@ -1211,7 +1203,7 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
|
||||
{
|
||||
// max index buffer size, in bytes
|
||||
static const size_t IBUFFER_THRESHOLD_BYTES = 64 * 1024 * 1024;
|
||||
@ -1233,23 +1225,23 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
};
|
||||
|
||||
// format data into the buffers to be rendered as points
|
||||
auto add_vertices_as_point = [](const GCodeProcessor::MoveVertex& curr, VertexBuffer& vertices) {
|
||||
auto add_vertices_as_point = [](const GCodeProcessorResult::MoveVertex& curr, VertexBuffer& vertices) {
|
||||
vertices.push_back(curr.position.x());
|
||||
vertices.push_back(curr.position.y());
|
||||
vertices.push_back(curr.position.z());
|
||||
};
|
||||
auto add_indices_as_point = [](const GCodeProcessor::MoveVertex& curr, TBuffer& buffer,
|
||||
auto add_indices_as_point = [](const GCodeProcessorResult::MoveVertex& curr, TBuffer& buffer,
|
||||
unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) {
|
||||
buffer.add_path(curr, ibuffer_id, indices.size(), move_id);
|
||||
indices.push_back(static_cast<IBufferType>(indices.size()));
|
||||
};
|
||||
|
||||
// format data into the buffers to be rendered as lines
|
||||
auto add_vertices_as_line = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, VertexBuffer& vertices) {
|
||||
auto add_vertices_as_line = [](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, VertexBuffer& vertices) {
|
||||
// x component of the normal to the current segment (the normal is parallel to the XY plane)
|
||||
const float normal_x = (curr.position - prev.position).normalized().y();
|
||||
|
||||
auto add_vertex = [&vertices, normal_x](const GCodeProcessor::MoveVertex& vertex) {
|
||||
auto add_vertex = [&vertices, normal_x](const GCodeProcessorResult::MoveVertex& vertex) {
|
||||
// add position
|
||||
vertices.push_back(vertex.position.x());
|
||||
vertices.push_back(vertex.position.y());
|
||||
@ -1263,7 +1255,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
// add current vertex
|
||||
add_vertex(curr);
|
||||
};
|
||||
auto add_indices_as_line = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer,
|
||||
auto add_indices_as_line = [](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, TBuffer& buffer,
|
||||
unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) {
|
||||
if (buffer.paths.empty() || prev.type != curr.type || !buffer.paths.back().matches(curr)) {
|
||||
// add starting index
|
||||
@ -1284,7 +1276,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
};
|
||||
|
||||
// format data into the buffers to be rendered as solid
|
||||
auto add_vertices_as_solid = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer, unsigned int vbuffer_id, VertexBuffer& vertices, size_t move_id) {
|
||||
auto add_vertices_as_solid = [](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, TBuffer& buffer, unsigned int vbuffer_id, VertexBuffer& vertices, size_t move_id) {
|
||||
auto store_vertex = [](VertexBuffer& vertices, const Vec3f& position, const Vec3f& normal) {
|
||||
// append position
|
||||
vertices.push_back(position.x());
|
||||
@ -1341,7 +1333,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
|
||||
last_path.sub_paths.back().last = { vbuffer_id, vertices.size(), move_id, curr.position };
|
||||
};
|
||||
auto add_indices_as_solid = [&](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, const GCodeProcessor::MoveVertex* next,
|
||||
auto add_indices_as_solid = [&](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, const GCodeProcessorResult::MoveVertex* next,
|
||||
TBuffer& buffer, size_t& vbuffer_size, unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) {
|
||||
static Vec3f prev_dir;
|
||||
static Vec3f prev_up;
|
||||
@ -1483,7 +1475,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
// format data into the buffers to be rendered as instanced model
|
||||
auto add_model_instance = [](const GCodeProcessor::MoveVertex& curr, InstanceBuffer& instances, InstanceIdBuffer& instances_ids, size_t move_id) {
|
||||
auto add_model_instance = [](const GCodeProcessorResult::MoveVertex& curr, InstanceBuffer& instances, InstanceIdBuffer& instances_ids, size_t move_id) {
|
||||
// append position
|
||||
instances.push_back(curr.position.x());
|
||||
instances.push_back(curr.position.y());
|
||||
@ -1499,7 +1491,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
// format data into the buffers to be rendered as batched model
|
||||
auto add_vertices_as_model_batch = [](const GCodeProcessor::MoveVertex& curr, const GLModel::InitializationData& data, VertexBuffer& vertices, InstanceBuffer& instances, InstanceIdBuffer& instances_ids, size_t move_id) {
|
||||
auto add_vertices_as_model_batch = [](const GCodeProcessorResult::MoveVertex& curr, const GLModel::InitializationData& data, VertexBuffer& vertices, InstanceBuffer& instances, InstanceIdBuffer& instances_ids, size_t move_id) {
|
||||
const double width = static_cast<double>(1.5f * curr.width);
|
||||
const double height = static_cast<double>(1.5f * curr.height);
|
||||
|
||||
@ -1543,7 +1535,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
auto start_time = std::chrono::high_resolution_clock::now();
|
||||
m_statistics.results_size = SLIC3R_STDVEC_MEMSIZE(gcode_result.moves, GCodeProcessor::MoveVertex);
|
||||
m_statistics.results_size = SLIC3R_STDVEC_MEMSIZE(gcode_result.moves, GCodeProcessorResult::MoveVertex);
|
||||
m_statistics.results_time = gcode_result.time;
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
|
||||
@ -1562,7 +1554,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
wxBusyCursor busy;
|
||||
|
||||
// extract approximate paths bounding box from result
|
||||
for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) {
|
||||
for (const GCodeProcessorResult::MoveVertex& move : gcode_result.moves) {
|
||||
if (wxGetApp().is_gcode_viewer())
|
||||
// for the gcode viewer we need to take in account all moves to correctly size the printbed
|
||||
m_paths_bounding_box.merge(move.position.cast<double>());
|
||||
@ -1576,57 +1568,18 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
m_max_bounding_box = m_paths_bounding_box;
|
||||
m_max_bounding_box.merge(m_paths_bounding_box.max + m_sequential_view.marker.get_bounding_box().size().z() * Vec3d::UnitZ());
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
if (wxGetApp().is_editor()) {
|
||||
const Bed3D::EShapeType bed_type = wxGetApp().plater()->get_bed().get_shape_type();
|
||||
if (bed_type == Bed3D::EShapeType::Rectangle) {
|
||||
BoundingBoxf3 print_volume = wxGetApp().plater()->get_bed().get_bounding_box(false);
|
||||
print_volume.min.z() = -1e10;
|
||||
print_volume.max.z() = m_max_print_height;
|
||||
print_volume.min -= Vec3f(BedEpsilon, BedEpsilon, 0.0f).cast<double>();
|
||||
print_volume.max += Vec3f(BedEpsilon, BedEpsilon, 0.0f).cast<double>();
|
||||
m_contained_in_bed = print_volume.contains(m_paths_bounding_box);
|
||||
}
|
||||
else if (bed_type == Bed3D::EShapeType::Circle) {
|
||||
Vec2d center;
|
||||
double radius;
|
||||
Bed3D::is_circle(wxGetApp().plater()->get_bed().get_shape(), ¢er, &radius);
|
||||
const double sq_radius = sqr(radius);
|
||||
for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) {
|
||||
if (move.type == EMoveType::Extrude && move.extrusion_role != erCustom && move.width != 0.0f && move.height != 0.0f) {
|
||||
if (sq_radius < (Vec2d(move.position.x(), move.position.y()) - center).squaredNorm()) {
|
||||
m_contained_in_bed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (bed_type == Bed3D::EShapeType::Custom) {
|
||||
const Pointfs& shape = wxGetApp().plater()->get_bed().get_shape();
|
||||
if (Bed3D::is_convex(shape)) {
|
||||
const Polygon poly = Polygon::new_scale(shape);
|
||||
for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) {
|
||||
if (move.type == EMoveType::Extrude && move.extrusion_role != erCustom && move.width != 0.0f && move.height != 0.0f) {
|
||||
if (!poly.contains(Point::new_scale(Vec2d(move.position.x(), move.position.y())))) {
|
||||
m_contained_in_bed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
if (wxGetApp().is_editor())
|
||||
m_contained_in_bed = wxGetApp().plater()->build_volume().all_paths_inside(gcode_result, m_paths_bounding_box);
|
||||
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
m_sequential_view.gcode_ids.clear();
|
||||
for (size_t i = 0; i < gcode_result.moves.size(); ++i) {
|
||||
const GCodeProcessor::MoveVertex& move = gcode_result.moves[i];
|
||||
const GCodeProcessorResult::MoveVertex& move = gcode_result.moves[i];
|
||||
if (move.type != EMoveType::Seam)
|
||||
m_sequential_view.gcode_ids.push_back(move.gcode_id);
|
||||
}
|
||||
#else
|
||||
for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) {
|
||||
for (const GCodeProcessorResult::MoveVertex& move : gcode_result.moves) {
|
||||
m_sequential_view.gcode_ids.push_back(move.gcode_id);
|
||||
}
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
@ -1649,7 +1602,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
|
||||
// toolpaths data -> extract vertices from result
|
||||
for (size_t i = 0; i < m_moves_count; ++i) {
|
||||
const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i];
|
||||
const GCodeProcessorResult::MoveVertex& curr = gcode_result.moves[i];
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
if (curr.type == EMoveType::Seam) {
|
||||
++seams_count;
|
||||
@ -1663,7 +1616,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
if (i == 0)
|
||||
continue;
|
||||
|
||||
const GCodeProcessor::MoveVertex& prev = gcode_result.moves[i - 1];
|
||||
const GCodeProcessorResult::MoveVertex& prev = gcode_result.moves[i - 1];
|
||||
|
||||
// update progress dialog
|
||||
++progress_count;
|
||||
@ -2067,7 +2020,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
for (size_t i = 0; i < m_moves_count; ++i) {
|
||||
const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i];
|
||||
const GCodeProcessorResult::MoveVertex& curr = gcode_result.moves[i];
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
if (curr.type == EMoveType::Seam)
|
||||
++seams_count;
|
||||
@ -2079,8 +2032,8 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
if (i == 0)
|
||||
continue;
|
||||
|
||||
const GCodeProcessor::MoveVertex& prev = gcode_result.moves[i - 1];
|
||||
const GCodeProcessor::MoveVertex* next = nullptr;
|
||||
const GCodeProcessorResult::MoveVertex& prev = gcode_result.moves[i - 1];
|
||||
const GCodeProcessorResult::MoveVertex* next = nullptr;
|
||||
if (i < m_moves_count - 1)
|
||||
next = &gcode_result.moves[i + 1];
|
||||
|
||||
@ -2287,7 +2240,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
seams_count = 0;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
for (size_t i = 0; i < m_moves_count; ++i) {
|
||||
const GCodeProcessor::MoveVertex& move = gcode_result.moves[i];
|
||||
const GCodeProcessorResult::MoveVertex& move = gcode_result.moves[i];
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
if (move.type == EMoveType::Seam)
|
||||
++seams_count;
|
||||
|
@ -233,7 +233,7 @@ class GCodeViewer
|
||||
unsigned char cp_color_id{ 0 };
|
||||
std::vector<Sub_Path> sub_paths;
|
||||
|
||||
bool matches(const GCodeProcessor::MoveVertex& move) const;
|
||||
bool matches(const GCodeProcessorResult::MoveVertex& move) const;
|
||||
size_t vertices_count() const {
|
||||
return sub_paths.empty() ? 0 : sub_paths.back().last.s_id - sub_paths.front().first.s_id + 1;
|
||||
}
|
||||
@ -251,7 +251,7 @@ class GCodeViewer
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
void add_sub_path(const GCodeProcessor::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id) {
|
||||
void add_sub_path(const GCodeProcessorResult::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id) {
|
||||
Endpoint endpoint = { b_id, i_id, s_id, move.position };
|
||||
sub_paths.push_back({ endpoint , endpoint });
|
||||
}
|
||||
@ -361,7 +361,7 @@ class GCodeViewer
|
||||
// b_id index of buffer contained in this->indices
|
||||
// i_id index of first index contained in this->indices[b_id]
|
||||
// s_id index of first vertex contained in this->vertices
|
||||
void add_path(const GCodeProcessor::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id);
|
||||
void add_path(const GCodeProcessorResult::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id);
|
||||
|
||||
unsigned int max_vertices_per_segment() const {
|
||||
switch (render_primitive_type)
|
||||
@ -780,9 +780,7 @@ private:
|
||||
BoundingBoxf3 m_paths_bounding_box;
|
||||
// bounding box of toolpaths + marker tools
|
||||
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;
|
||||
Layers m_layers;
|
||||
std::array<unsigned int, 2> m_layers_z_range;
|
||||
@ -802,14 +800,12 @@ private:
|
||||
Statistics m_statistics;
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
std::array<float, 2> m_detected_point_sizes = { 0.0f, 0.0f };
|
||||
GCodeProcessor::Result::SettingsIds m_settings_ids;
|
||||
GCodeProcessorResult::SettingsIds m_settings_ids;
|
||||
std::array<SequentialRangeCap, 2> m_sequential_range_caps;
|
||||
|
||||
std::vector<CustomGCode::Item> m_custom_gcode_per_print_z;
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool m_contained_in_bed{ true };
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
public:
|
||||
GCodeViewer();
|
||||
@ -820,9 +816,9 @@ public:
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
// extract rendering data from the given parameters
|
||||
void load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized);
|
||||
void load(const GCodeProcessorResult& gcode_result, const Print& print, bool initialized);
|
||||
// recalculate ranges in dependence of what is visible and sets tool/print colors
|
||||
void refresh(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors);
|
||||
void refresh(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors);
|
||||
void refresh_render_paths();
|
||||
void update_shells_color_by_extruder(const DynamicPrintConfig* config);
|
||||
|
||||
@ -839,9 +835,7 @@ public:
|
||||
const SequentialView& get_sequential_view() const { return m_sequential_view; }
|
||||
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; }
|
||||
void set_view_type(EViewType type) {
|
||||
@ -870,7 +864,7 @@ public:
|
||||
size_t get_extruders_count() { return m_extruders_count; }
|
||||
|
||||
private:
|
||||
void load_toolpaths(const GCodeProcessor::Result& gcode_result);
|
||||
void load_toolpaths(const GCodeProcessorResult& gcode_result);
|
||||
void load_shells(const Print& print, bool initialized);
|
||||
void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const;
|
||||
void render_toolpaths();
|
||||
|
@ -3,16 +3,18 @@
|
||||
|
||||
#include <igl/unproject.h>
|
||||
|
||||
#include "libslic3r/BuildVolume.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/GCode/ThumbnailData.hpp"
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
#include "libslic3r/Geometry/ConvexHull.hpp"
|
||||
#include "libslic3r/ExtrusionEntity.hpp"
|
||||
#include "libslic3r/Layer.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Technologies.hpp"
|
||||
#include "libslic3r/Tesselate.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "slic3r/GUI/3DBed.hpp"
|
||||
#include "slic3r/GUI/3DScene.hpp"
|
||||
#include "slic3r/GUI/BackgroundSlicingProcess.hpp"
|
||||
#include "slic3r/GUI/GLShader.hpp"
|
||||
@ -20,7 +22,6 @@
|
||||
#include "slic3r/GUI/Tab.hpp"
|
||||
#include "slic3r/GUI/GUI_Preview.hpp"
|
||||
#include "slic3r/GUI/OpenGLManager.hpp"
|
||||
#include "slic3r/GUI/3DBed.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/MainFrame.hpp"
|
||||
#include "slic3r/Utils/UndoRedo.hpp"
|
||||
@ -957,9 +958,10 @@ PrinterTechnology GLCanvas3D::current_printer_technology() const
|
||||
return m_process->current_printer_technology();
|
||||
}
|
||||
|
||||
GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas)
|
||||
GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed)
|
||||
: m_canvas(canvas)
|
||||
, m_context(nullptr)
|
||||
, m_bed(bed)
|
||||
#if ENABLE_RETINA_GL
|
||||
, m_retina_helper(nullptr)
|
||||
#endif
|
||||
@ -1115,18 +1117,10 @@ void GLCanvas3D::reset_volumes()
|
||||
_set_warning_notification(EWarning::ObjectOutside, false);
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state(bool as_toolpaths) const
|
||||
#else
|
||||
ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state() const
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
{
|
||||
ModelInstanceEPrintVolumeState state;
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
m_volumes.check_outside_state(m_config, &state, as_toolpaths);
|
||||
#else
|
||||
m_volumes.check_outside_state(m_config, &state);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
m_volumes.check_outside_state(m_bed.build_volume(), &state);
|
||||
return state;
|
||||
}
|
||||
|
||||
@ -1250,13 +1244,11 @@ BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const
|
||||
BoundingBoxf3 GLCanvas3D::scene_bounding_box() const
|
||||
{
|
||||
BoundingBoxf3 bb = volumes_bounding_box();
|
||||
bb.merge(wxGetApp().plater()->get_bed().get_bounding_box(true));
|
||||
if (m_config != nullptr) {
|
||||
double h = m_config->opt_float("max_print_height");
|
||||
bb.min(2) = std::min(bb.min(2), -h);
|
||||
bb.max(2) = std::max(bb.max(2), h);
|
||||
}
|
||||
|
||||
bb.merge(m_bed.extended_bounding_box());
|
||||
double h = m_bed.build_volume().max_print_height();
|
||||
//FIXME why -h?
|
||||
bb.min.z() = std::min(bb.min.z(), -h);
|
||||
bb.max.z() = std::max(bb.max.z(), h);
|
||||
return bb;
|
||||
}
|
||||
|
||||
@ -1362,7 +1354,10 @@ void GLCanvas3D::allow_multisample(bool allow)
|
||||
|
||||
void GLCanvas3D::zoom_to_bed()
|
||||
{
|
||||
_zoom_to_box(wxGetApp().plater()->get_bed().get_bounding_box(false));
|
||||
BoundingBoxf3 box = m_bed.build_volume().bounding_volume();
|
||||
box.min.z() = 0.0;
|
||||
box.max.z() = 0.0;
|
||||
_zoom_to_box(box);
|
||||
}
|
||||
|
||||
void GLCanvas3D::zoom_to_volumes()
|
||||
@ -1423,7 +1418,7 @@ void GLCanvas3D::render()
|
||||
m_gcode_viewer.init();
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
if (wxGetApp().plater()->get_bed().get_shape().empty()) {
|
||||
if (! m_bed.build_volume().valid()) {
|
||||
// this happens at startup when no data is still saved under <>\AppData\Roaming\Slic3rPE
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_UPDATE_BED_SHAPE));
|
||||
return;
|
||||
@ -2057,7 +2052,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
||||
// checks for geometry outside the print volume to render it accordingly
|
||||
if (!m_volumes.empty()) {
|
||||
ModelInstanceEPrintVolumeState state;
|
||||
const bool contained_min_one = m_volumes.check_outside_state(m_config, &state);
|
||||
const bool contained_min_one = m_volumes.check_outside_state(m_bed.build_volume(), &state);
|
||||
const bool partlyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Partly_Outside);
|
||||
const bool fullyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Fully_Outside);
|
||||
|
||||
@ -2109,7 +2104,7 @@ static void reserve_new_volume_finalize_old_volume(GLVolume& vol_new, GLVolume&
|
||||
vol_old.finalize_geometry(gl_initialized);
|
||||
}
|
||||
|
||||
void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors)
|
||||
void GLCanvas3D::load_gcode_preview(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors)
|
||||
{
|
||||
m_gcode_viewer.load(gcode_result, *this->fff_print(), m_initialized);
|
||||
|
||||
@ -2138,10 +2133,6 @@ void GLCanvas3D::load_sla_preview()
|
||||
// Release OpenGL data before generating new data.
|
||||
reset_volumes();
|
||||
_load_sla_shells();
|
||||
#if !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false);
|
||||
m_volumes.set_print_box(float(bed_bb.min.x()) - BedEpsilon, float(bed_bb.min.y()) - BedEpsilon, 0.0f, float(bed_bb.max.x()) + BedEpsilon, float(bed_bb.max.y()) + BedEpsilon, (float)m_config->opt_float("max_print_height"));
|
||||
#endif // !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
_update_sla_shells_outside_state();
|
||||
_set_warning_notification_if_needed(EWarning::SlaSupportsOutside);
|
||||
}
|
||||
@ -2158,20 +2149,12 @@ void GLCanvas3D::load_preview(const std::vector<std::string>& str_tool_colors, c
|
||||
// Release OpenGL data before generating new data.
|
||||
this->reset_volumes();
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool requires_convex_hulls = wxGetApp().plater()->get_bed().get_shape_type() != Bed3D::EShapeType::Rectangle;
|
||||
_load_print_toolpaths(requires_convex_hulls);
|
||||
_load_wipe_tower_toolpaths(str_tool_colors, requires_convex_hulls);
|
||||
const BuildVolume &build_volume = m_bed.build_volume();
|
||||
_load_print_toolpaths(build_volume);
|
||||
_load_wipe_tower_toolpaths(build_volume, str_tool_colors);
|
||||
for (const PrintObject* object : print->objects())
|
||||
_load_print_object_toolpaths(*object, str_tool_colors, color_print_values, requires_convex_hulls);
|
||||
#else
|
||||
_load_print_toolpaths();
|
||||
_load_wipe_tower_toolpaths(str_tool_colors);
|
||||
for (const PrintObject* object : print->objects())
|
||||
_load_print_object_toolpaths(*object, str_tool_colors, color_print_values);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
_load_print_object_toolpaths(*object, build_volume, str_tool_colors, color_print_values);
|
||||
|
||||
_update_toolpath_volumes_outside_state();
|
||||
_set_warning_notification_if_needed(EWarning::ToolpathOutside);
|
||||
}
|
||||
|
||||
@ -2605,9 +2588,7 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
|
||||
displacement = multiplier * direction;
|
||||
|
||||
m_selection.translate(displacement);
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
m_selection.stop_dragging();
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
m_dirty = true;
|
||||
}
|
||||
);
|
||||
@ -2706,9 +2687,7 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
|
||||
auto do_rotate = [this](double angle_z_rad) {
|
||||
m_selection.start_dragging();
|
||||
m_selection.rotate(Vec3d(0.0, 0.0, angle_z_rad), TransformationType(TransformationType::World_Relative_Joint));
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
m_selection.stop_dragging();
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
m_dirty = true;
|
||||
// wxGetApp().obj_manipul()->set_dirty();
|
||||
};
|
||||
@ -3262,10 +3241,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
}
|
||||
}
|
||||
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) {
|
||||
m_layers_editing.state = LayersEditing::Unknown;
|
||||
@ -3770,7 +3747,7 @@ Linef3 GLCanvas3D::mouse_ray(const Point& mouse_pos)
|
||||
|
||||
double GLCanvas3D::get_size_proportional_to_max_bed_size(double factor) const
|
||||
{
|
||||
return factor * wxGetApp().plater()->get_bed().get_bounding_box(false).max_size();
|
||||
return factor * m_bed.build_volume().bounding_volume().max_size();
|
||||
}
|
||||
|
||||
void GLCanvas3D::set_cursor(ECursorType type)
|
||||
@ -4161,7 +4138,7 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, const
|
||||
}
|
||||
else
|
||||
// This happens for empty projects
|
||||
volumes_box = wxGetApp().plater()->get_bed().get_bounding_box(true);
|
||||
volumes_box = m_bed.extended_bounding_box();
|
||||
#endif // ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
|
||||
Camera camera;
|
||||
@ -4178,7 +4155,7 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, const
|
||||
// extends the near and far z of the frustrum to avoid the bed being clipped
|
||||
|
||||
// box in eye space
|
||||
BoundingBoxf3 t_bed_box = wxGetApp().plater()->get_bed().get_bounding_box(true).transformed(camera.get_view_matrix());
|
||||
BoundingBoxf3 t_bed_box = m_bed.extended_bounding_box().transformed(camera.get_view_matrix());
|
||||
near_z = -t_bed_box.max.z();
|
||||
far_z = -t_bed_box.min.z();
|
||||
}
|
||||
@ -4861,7 +4838,7 @@ BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_be
|
||||
bb.merge(BoundingBoxf3(sel_bb_center - extend_by, sel_bb_center + extend_by));
|
||||
}
|
||||
|
||||
bb.merge(wxGetApp().plater()->get_bed().get_bounding_box(include_bed_model));
|
||||
bb.merge(include_bed_model ? m_bed.extended_bounding_box() : m_bed.build_volume().bounding_volume());
|
||||
|
||||
if (!m_main_toolbar.is_enabled())
|
||||
bb.merge(m_gcode_viewer.get_max_bounding_box());
|
||||
@ -5035,25 +5012,6 @@ void GLCanvas3D::_rectangular_selection_picking_pass()
|
||||
_update_volumes_hover_state();
|
||||
}
|
||||
|
||||
#if !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
static BoundingBoxf3 print_volume(const DynamicPrintConfig& config)
|
||||
{
|
||||
// tolerance to avoid false detection at bed edges
|
||||
const double tolerance_x = 0.05;
|
||||
const double tolerance_y = 0.05;
|
||||
|
||||
BoundingBoxf3 ret;
|
||||
const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(config.option("bed_shape"));
|
||||
if (opt != nullptr) {
|
||||
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
|
||||
ret = BoundingBoxf3(Vec3d(unscale<double>(bed_box_2D.min(0)) - tolerance_x, unscale<double>(bed_box_2D.min(1)) - tolerance_y, 0.0), Vec3d(unscale<double>(bed_box_2D.max(0)) + tolerance_x, unscale<double>(bed_box_2D.max(1)) + tolerance_y, config.opt_float("max_print_height")));
|
||||
// Allow the objects to protrude below the print bed
|
||||
ret.min(2) = -1e10;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif // !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
void GLCanvas3D::_render_background() const
|
||||
{
|
||||
bool use_error_color = false;
|
||||
@ -5064,15 +5022,7 @@ void GLCanvas3D::_render_background() const
|
||||
if (!m_volumes.empty())
|
||||
use_error_color &= _is_any_volume_outside();
|
||||
else
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
use_error_color &= m_gcode_viewer.has_data() && !m_gcode_viewer.is_contained_in_bed();
|
||||
#else
|
||||
{
|
||||
const BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3();
|
||||
const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box();
|
||||
use_error_color &= (test_volume.radius() > 0.0 && paths_volume.radius() > 0.0) ? !test_volume.contains(paths_volume) : false;
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
}
|
||||
|
||||
glsafe(::glPushMatrix());
|
||||
@ -5123,7 +5073,7 @@ void GLCanvas3D::_render_bed(bool bottom, bool show_axes)
|
||||
&& m_gizmos.get_current_type() != GLGizmosManager::Seam
|
||||
&& m_gizmos.get_current_type() != GLGizmosManager::MmuSegmentation);
|
||||
|
||||
wxGetApp().plater()->get_bed().render(*this, bottom, scale_factor, show_axes, show_texture);
|
||||
m_bed.render(*this, bottom, scale_factor, show_axes, show_texture);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_bed_for_picking(bool bottom)
|
||||
@ -5133,7 +5083,7 @@ void GLCanvas3D::_render_bed_for_picking(bool bottom)
|
||||
scale_factor = m_retina_helper->get_scale_factor();
|
||||
#endif // ENABLE_RETINA_GL
|
||||
|
||||
wxGetApp().plater()->get_bed().render_for_picking(*this, bottom, scale_factor);
|
||||
m_bed.render_for_picking(*this, bottom, scale_factor);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
|
||||
@ -5148,55 +5098,35 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
|
||||
if (m_picking_enabled) {
|
||||
// Update the layer editing selection to the first object selected, update the current object maximum Z.
|
||||
m_layers_editing.select_object(*m_model, this->is_layers_editing_enabled() ? m_selection.get_object_idx() : -1);
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
if (m_config != nullptr) {
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
Bed3D::EShapeType type = wxGetApp().plater()->get_bed().get_shape_type();
|
||||
switch (type)
|
||||
{
|
||||
case Bed3D::EShapeType::Circle: {
|
||||
Vec2d center;
|
||||
double radius;
|
||||
if (Bed3D::is_circle(wxGetApp().plater()->get_bed().get_shape(), ¢er, &radius)) {
|
||||
m_volumes.set_print_volume({ static_cast<int>(type),
|
||||
{ float(center.x()), float(center.y()), float(radius) + BedEpsilon, 0.0f },
|
||||
{ 0.0f, float(m_config->opt_float("max_print_height")) } });
|
||||
}
|
||||
if (const BuildVolume &build_volume = m_bed.build_volume(); build_volume.valid()) {
|
||||
switch (build_volume.type()) {
|
||||
case BuildVolume::Type::Rectangle: {
|
||||
const BoundingBox3Base<Vec3d> bed_bb = build_volume.bounding_volume().inflated(BuildVolume::SceneEpsilon);
|
||||
m_volumes.set_print_volume({ 0, // circle
|
||||
{ float(bed_bb.min.x()), float(bed_bb.min.y()), float(bed_bb.max.x()), float(bed_bb.max.y()) },
|
||||
{ 0.0f, float(build_volume.max_print_height()) } });
|
||||
break;
|
||||
}
|
||||
case Bed3D::EShapeType::Rectangle: {
|
||||
const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false);
|
||||
m_volumes.set_print_volume({ static_cast<int>(type),
|
||||
{ float(bed_bb.min.x()) - BedEpsilon, float(bed_bb.min.y()) - BedEpsilon, float(bed_bb.max.x()) + BedEpsilon, float(bed_bb.max.y()) + BedEpsilon },
|
||||
{ 0.0f, float(m_config->opt_float("max_print_height")) } });
|
||||
case BuildVolume::Type::Circle: {
|
||||
m_volumes.set_print_volume({ 1, // rectangle
|
||||
{ unscaled<float>(build_volume.circle().center.x()), unscaled<float>(build_volume.circle().center.y()), unscaled<float>(build_volume.circle().radius + BuildVolume::SceneEpsilon), 0.0f },
|
||||
{ 0.0f, float(build_volume.max_print_height() + BuildVolume::SceneEpsilon) } });
|
||||
break;
|
||||
}
|
||||
default:
|
||||
case Bed3D::EShapeType::Custom: {
|
||||
case BuildVolume::Type::Custom: {
|
||||
m_volumes.set_print_volume({ static_cast<int>(type),
|
||||
{ 0.0f, 0.0f, 0.0f, 0.0f },
|
||||
{ 0.0f, 0.0f } });
|
||||
}
|
||||
}
|
||||
#else
|
||||
const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false);
|
||||
m_volumes.set_print_box((float)bed_bb.min.x() - BedEpsilon, (float)bed_bb.min.y() - BedEpsilon, 0.0f, (float)bed_bb.max.x() + BedEpsilon, (float)bed_bb.max.y() + BedEpsilon, (float)m_config->opt_float("max_print_height"));
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
if (m_requires_check_outside_state) {
|
||||
m_volumes.check_outside_state(m_config, nullptr);
|
||||
m_volumes.check_outside_state(build_volume, nullptr);
|
||||
m_requires_check_outside_state = false;
|
||||
}
|
||||
#else
|
||||
m_volumes.check_outside_state(m_config, nullptr);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
}
|
||||
#if !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
}
|
||||
#endif // !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
if (m_use_clipping_planes)
|
||||
m_volumes.set_z_range(-m_clipping_planes[0].get_data()[3], m_clipping_planes[1].get_data()[3]);
|
||||
@ -5206,11 +5136,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
|
||||
m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data());
|
||||
m_volumes.set_show_sinking_contours(! m_gizmos.is_hiding_instances());
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_mod");
|
||||
#else
|
||||
GLShaderProgram* shader = wxGetApp().get_shader("gouraud");
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
if (shader != nullptr) {
|
||||
shader->start_using();
|
||||
|
||||
@ -5832,11 +5758,7 @@ void GLCanvas3D::_stop_timer()
|
||||
m_timer.Stop();
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
void GLCanvas3D::_load_print_toolpaths(bool generate_convex_hulls)
|
||||
#else
|
||||
void GLCanvas3D::_load_print_toolpaths()
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
void GLCanvas3D::_load_print_toolpaths(const BuildVolume &build_volume)
|
||||
{
|
||||
const Print *print = this->fff_print();
|
||||
if (print == nullptr)
|
||||
@ -5889,18 +5811,11 @@ void GLCanvas3D::_load_print_toolpaths()
|
||||
reserve_new_volume_finalize_old_volume(*volume, vol, m_initialized);
|
||||
}
|
||||
}
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
if (generate_convex_hulls)
|
||||
volume->calc_convex_hull_3d();
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
volume->is_outside = ! build_volume.all_paths_inside_vertices_and_normals_interleaved(volume->indexed_vertex_array.vertices_and_normals_interleaved, volume->indexed_vertex_array.bounding_box());
|
||||
volume->indexed_vertex_array.finalize_geometry(m_initialized);
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors, const std::vector<CustomGCode::Item>& color_print_values, bool generate_convex_hulls)
|
||||
#else
|
||||
void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors, const std::vector<CustomGCode::Item>& color_print_values)
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const BuildVolume& build_volume, const std::vector<std::string>& str_tool_colors, const std::vector<CustomGCode::Item>& color_print_values)
|
||||
{
|
||||
std::vector<std::array<float, 4>> tool_colors = _parse_colors(str_tool_colors);
|
||||
|
||||
@ -6187,26 +6102,16 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
|
||||
std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
|
||||
[](const GLVolume *volume) { return volume->empty(); }),
|
||||
m_volumes.volumes.end());
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) {
|
||||
GLVolume* v = m_volumes.volumes[i];
|
||||
if (generate_convex_hulls)
|
||||
v->calc_convex_hull_3d();
|
||||
v->is_outside = ! build_volume.all_paths_inside_vertices_and_normals_interleaved(v->indexed_vertex_array.vertices_and_normals_interleaved, v->indexed_vertex_array.bounding_box());
|
||||
v->indexed_vertex_array.finalize_geometry(m_initialized);
|
||||
}
|
||||
#else
|
||||
for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
|
||||
m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_initialized);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info();
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors, bool generate_convex_hulls)
|
||||
#else
|
||||
void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors)
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, const std::vector<std::string>& str_tool_colors)
|
||||
{
|
||||
const Print *print = this->fff_print();
|
||||
if (print == nullptr || print->wipe_tower_data().tool_changes.empty())
|
||||
@ -6357,17 +6262,11 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
|
||||
std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
|
||||
[](const GLVolume *volume) { return volume->empty(); }),
|
||||
m_volumes.volumes.end());
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) {
|
||||
GLVolume* v = m_volumes.volumes[i];
|
||||
if (generate_convex_hulls)
|
||||
v->calc_convex_hull_3d();
|
||||
v->is_outside = ! build_volume.all_paths_inside_vertices_and_normals_interleaved(v->indexed_vertex_array.vertices_and_normals_interleaved, v->indexed_vertex_array.bounding_box());
|
||||
v->indexed_vertex_array.finalize_geometry(m_initialized);
|
||||
}
|
||||
#else
|
||||
for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
|
||||
m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_initialized);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info();
|
||||
}
|
||||
@ -6427,28 +6326,9 @@ void GLCanvas3D::_load_sla_shells()
|
||||
update_volumes_colors_by_extruder();
|
||||
}
|
||||
|
||||
void GLCanvas3D::_update_toolpath_volumes_outside_state()
|
||||
{
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
check_volumes_outside_state(true);
|
||||
#else
|
||||
BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3();
|
||||
for (GLVolume* volume : m_volumes.volumes) {
|
||||
volume->is_outside = (test_volume.radius() > 0.0 && volume->is_extrusion_path) ? !test_volume.contains(volume->bounding_box()) : false;
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
}
|
||||
|
||||
void GLCanvas3D::_update_sla_shells_outside_state()
|
||||
{
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
check_volumes_outside_state();
|
||||
#else
|
||||
BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3();
|
||||
for (GLVolume* volume : m_volumes.volumes) {
|
||||
volume->is_outside = (test_volume.radius() > 0.0 && volume->shader_outside_printer_detection_enabled) ? !test_volume.contains(volume->transformed_convex_hull_bounding_box()) : false;
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
}
|
||||
|
||||
void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning)
|
||||
@ -6459,15 +6339,8 @@ void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning)
|
||||
show = _is_any_volume_outside();
|
||||
else {
|
||||
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();
|
||||
const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box();
|
||||
if (test_volume.radius() > 0.0 && paths_volume.radius() > 0.0)
|
||||
show = !test_volume.contains(paths_volume);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,7 @@ class wxGLContext;
|
||||
namespace Slic3r {
|
||||
|
||||
class BackgroundSlicingProcess;
|
||||
class BuildVolume;
|
||||
struct ThumbnailData;
|
||||
struct ThumbnailsParams;
|
||||
class ModelObject;
|
||||
@ -50,6 +51,8 @@ namespace CustomGCode { struct Item; }
|
||||
|
||||
namespace GUI {
|
||||
|
||||
class Bed3D;
|
||||
|
||||
#if ENABLE_RETINA_GL
|
||||
class RetinaHelper;
|
||||
#endif
|
||||
@ -446,6 +449,7 @@ public:
|
||||
private:
|
||||
wxGLCanvas* m_canvas;
|
||||
wxGLContext* m_context;
|
||||
Bed3D &m_bed;
|
||||
#if ENABLE_RETINA_GL
|
||||
std::unique_ptr<RetinaHelper> m_retina_helper;
|
||||
#endif
|
||||
@ -475,9 +479,7 @@ private:
|
||||
const DynamicPrintConfig* m_config;
|
||||
Model* m_model;
|
||||
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 };
|
||||
|
||||
@ -600,7 +602,7 @@ private:
|
||||
m_gizmo_highlighter;
|
||||
|
||||
public:
|
||||
explicit GLCanvas3D(wxGLCanvas* canvas);
|
||||
explicit GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed);
|
||||
~GLCanvas3D();
|
||||
|
||||
bool is_initialized() const { return m_initialized; }
|
||||
@ -614,18 +616,12 @@ public:
|
||||
void post_event(wxEvent &&event);
|
||||
|
||||
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;
|
||||
const GLVolumeCollection& get_volumes() const { return m_volumes; }
|
||||
void reset_volumes();
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
ModelInstanceEPrintVolumeState check_volumes_outside_state(bool as_toolpaths = false) const;
|
||||
#else
|
||||
ModelInstanceEPrintVolumeState check_volumes_outside_state() const;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
void init_gcode_viewer() { m_gcode_viewer.init(); }
|
||||
@ -736,7 +732,7 @@ public:
|
||||
|
||||
void reload_scene(bool refresh_immediately, bool force_full_scene_refresh = false);
|
||||
|
||||
void load_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors);
|
||||
void load_gcode_preview(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors);
|
||||
void refresh_gcode_preview_render_paths();
|
||||
void set_gcode_view_preview_type(GCodeViewer::EViewType type) { return m_gcode_viewer.set_view_type(type); }
|
||||
GCodeViewer::EViewType get_gcode_view_preview_type() const { return m_gcode_viewer.get_view_type(); }
|
||||
@ -955,33 +951,19 @@ private:
|
||||
void _start_timer();
|
||||
void _stop_timer();
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// Create 3D thick extrusion lines for a skirt and brim.
|
||||
// Adds a new Slic3r::GUI::3DScene::Volume to volumes.
|
||||
void _load_print_toolpaths(bool generate_convex_hulls = false);
|
||||
// Adds a new Slic3r::GUI::3DScene::Volume to volumes, updates collision with the build_volume.
|
||||
void _load_print_toolpaths(const BuildVolume &build_volume);
|
||||
// Create 3D thick extrusion lines for object forming extrusions.
|
||||
// Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes,
|
||||
// one for perimeters, one for infill and one for supports.
|
||||
void _load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors,
|
||||
const std::vector<CustomGCode::Item>& color_print_values, bool generate_convex_hulls = false);
|
||||
// Create 3D thick extrusion lines for wipe tower extrusions
|
||||
void _load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors, bool generate_convex_hulls = false);
|
||||
#else
|
||||
// Create 3D thick extrusion lines for a skirt and brim.
|
||||
// Adds a new Slic3r::GUI::3DScene::Volume to volumes.
|
||||
void _load_print_toolpaths();
|
||||
// Create 3D thick extrusion lines for object forming extrusions.
|
||||
// Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes,
|
||||
// one for perimeters, one for infill and one for supports.
|
||||
void _load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors,
|
||||
const std::vector<CustomGCode::Item>& color_print_values);
|
||||
// Create 3D thick extrusion lines for wipe tower extrusions
|
||||
void _load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// one for perimeters, one for infill and one for supports, updates collision with the build_volume.
|
||||
void _load_print_object_toolpaths(const PrintObject& print_object, const BuildVolume &build_volume,
|
||||
const std::vector<std::string>& str_tool_colors, const std::vector<CustomGCode::Item>& color_print_values);
|
||||
// Create 3D thick extrusion lines for wipe tower extrusions, updates collision with the build_volume.
|
||||
void _load_wipe_tower_toolpaths(const BuildVolume &build_volume, const std::vector<std::string>& str_tool_colors);
|
||||
|
||||
// Load SLA objects and support structures for objects, for which the slaposSliceSupports step has been finished.
|
||||
void _load_sla_shells();
|
||||
void _update_toolpath_volumes_outside_state();
|
||||
void _update_sla_shells_outside_state();
|
||||
void _set_warning_notification_if_needed(EWarning warning);
|
||||
|
||||
|
@ -61,12 +61,7 @@ std::pair<bool, std::string> GLShadersManager::init()
|
||||
// used to render extrusion and travel paths as lines in gcode preview
|
||||
valid &= append_shader("toolpaths_lines", { "toolpaths_lines.vs", "toolpaths_lines.fs" });
|
||||
// used to render objects in 3d editor
|
||||
#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" }
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
#if ENABLE_ENVIRONMENT_MAP
|
||||
, { "ENABLE_ENVIRONMENT_MAP"sv }
|
||||
#endif // ENABLE_ENVIRONMENT_MAP
|
||||
|
@ -2652,6 +2652,11 @@ Plater* GUI_App::plater()
|
||||
return plater_;
|
||||
}
|
||||
|
||||
const Plater* GUI_App::plater() const
|
||||
{
|
||||
return plater_;
|
||||
}
|
||||
|
||||
Model& GUI_App::model()
|
||||
{
|
||||
return plater_->model();
|
||||
|
@ -281,6 +281,7 @@ public:
|
||||
ObjectList* obj_list();
|
||||
ObjectLayers* obj_layers();
|
||||
Plater* plater();
|
||||
const Plater* plater() const;
|
||||
Model& model();
|
||||
NotificationManager * notification_manager();
|
||||
|
||||
|
@ -657,14 +657,10 @@ void ObjectManipulation::update_if_dirty()
|
||||
else
|
||||
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_mirror_buttons_visibility();
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
update_reset_buttons_visibility();
|
||||
update_mirror_buttons_visibility();
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
m_dirty = false;
|
||||
}
|
||||
@ -820,9 +816,7 @@ void ObjectManipulation::change_position_value(int axis, double value)
|
||||
Selection& selection = canvas->get_selection();
|
||||
selection.start_dragging();
|
||||
selection.translate(position - m_cache.position, selection.requires_local_axes());
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
selection.stop_dragging();
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
canvas->do_move(L("Set Position"));
|
||||
|
||||
m_cache.position = position;
|
||||
@ -854,9 +848,7 @@ void ObjectManipulation::change_rotation_value(int axis, double value)
|
||||
selection.rotate(
|
||||
(M_PI / 180.0) * (transformation_type.absolute() ? rotation : rotation - m_cache.rotation),
|
||||
transformation_type);
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
selection.stop_dragging();
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
canvas->do_rotate(L("Set Orientation"));
|
||||
|
||||
m_cache.rotation = rotation;
|
||||
@ -935,9 +927,7 @@ void ObjectManipulation::do_scale(int axis, const Vec3d &scale) const
|
||||
|
||||
selection.start_dragging();
|
||||
selection.scale(scaling_factor, transformation_type);
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
selection.stop_dragging();
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
wxGetApp().plater()->canvas3D()->do_scale(L("Set Scale"));
|
||||
}
|
||||
|
||||
|
@ -37,11 +37,11 @@
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
View3D::View3D(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process)
|
||||
View3D::View3D(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process)
|
||||
: m_canvas_widget(nullptr)
|
||||
, m_canvas(nullptr)
|
||||
{
|
||||
init(parent, model, config, process);
|
||||
init(parent, bed, model, config, process);
|
||||
}
|
||||
|
||||
View3D::~View3D()
|
||||
@ -50,7 +50,7 @@ View3D::~View3D()
|
||||
delete m_canvas_widget;
|
||||
}
|
||||
|
||||
bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process)
|
||||
bool View3D::init(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process)
|
||||
{
|
||||
if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 /* disable wxTAB_TRAVERSAL */))
|
||||
return false;
|
||||
@ -59,7 +59,7 @@ bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, Ba
|
||||
if (m_canvas_widget == nullptr)
|
||||
return false;
|
||||
|
||||
m_canvas = new GLCanvas3D(m_canvas_widget);
|
||||
m_canvas = new GLCanvas3D(m_canvas_widget, bed);
|
||||
m_canvas->set_context(wxGetApp().init_glcontext(*m_canvas_widget));
|
||||
|
||||
m_canvas->allow_multisample(OpenGLManager::can_multisample());
|
||||
@ -169,18 +169,18 @@ void View3D::render()
|
||||
}
|
||||
|
||||
Preview::Preview(
|
||||
wxWindow* parent, Model* model, DynamicPrintConfig* config,
|
||||
BackgroundSlicingProcess* process, GCodeProcessor::Result* gcode_result, std::function<void()> schedule_background_process_func)
|
||||
wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig* config,
|
||||
BackgroundSlicingProcess* process, GCodeProcessorResult* gcode_result, std::function<void()> schedule_background_process_func)
|
||||
: m_config(config)
|
||||
, m_process(process)
|
||||
, m_gcode_result(gcode_result)
|
||||
, m_schedule_background_process(schedule_background_process_func)
|
||||
{
|
||||
if (init(parent, model))
|
||||
if (init(parent, bed, model))
|
||||
load_print();
|
||||
}
|
||||
|
||||
bool Preview::init(wxWindow* parent, Model* model)
|
||||
bool Preview::init(wxWindow* parent, Bed3D& bed, Model* model)
|
||||
{
|
||||
if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 /* disable wxTAB_TRAVERSAL */))
|
||||
return false;
|
||||
@ -196,7 +196,7 @@ bool Preview::init(wxWindow* parent, Model* model)
|
||||
if (m_canvas_widget == nullptr)
|
||||
return false;
|
||||
|
||||
m_canvas = new GLCanvas3D(m_canvas_widget);
|
||||
m_canvas = new GLCanvas3D(m_canvas_widget, bed);
|
||||
m_canvas->set_context(wxGetApp().init_glcontext(*m_canvas_widget));
|
||||
m_canvas->allow_multisample(OpenGLManager::can_multisample());
|
||||
m_canvas->set_config(m_config);
|
||||
|
@ -44,7 +44,7 @@ class View3D : public wxPanel
|
||||
GLCanvas3D* m_canvas;
|
||||
|
||||
public:
|
||||
View3D(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process);
|
||||
View3D(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process);
|
||||
virtual ~View3D();
|
||||
|
||||
wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; }
|
||||
@ -70,7 +70,7 @@ public:
|
||||
void render();
|
||||
|
||||
private:
|
||||
bool init(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process);
|
||||
bool init(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process);
|
||||
};
|
||||
|
||||
class Preview : public wxPanel
|
||||
@ -93,7 +93,7 @@ class Preview : public wxPanel
|
||||
|
||||
DynamicPrintConfig* m_config;
|
||||
BackgroundSlicingProcess* m_process;
|
||||
GCodeProcessor::Result* m_gcode_result;
|
||||
GCodeProcessorResult* m_gcode_result;
|
||||
|
||||
#ifdef __linux__
|
||||
// We are getting mysterious crashes on Linux in gtk due to OpenGL context activation GH #1874 #1955.
|
||||
@ -129,8 +129,8 @@ public:
|
||||
Legend
|
||||
};
|
||||
|
||||
Preview(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process,
|
||||
GCodeProcessor::Result* gcode_result, std::function<void()> schedule_background_process = []() {});
|
||||
Preview(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process,
|
||||
GCodeProcessorResult* gcode_result, std::function<void()> schedule_background_process = []() {});
|
||||
virtual ~Preview();
|
||||
|
||||
wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; }
|
||||
@ -161,7 +161,7 @@ public:
|
||||
void hide_layers_slider();
|
||||
|
||||
private:
|
||||
bool init(wxWindow* parent, Model* model);
|
||||
bool init(wxWindow* parent, Bed3D& bed, Model* model);
|
||||
|
||||
void bind_event_handlers();
|
||||
void unbind_event_handlers();
|
||||
|
@ -26,10 +26,10 @@
|
||||
#include "3DScene.hpp"
|
||||
#include "GLCanvas3D.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "3DBed.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
#include "libslic3r/BuildVolume.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/GCode/ThumbnailData.hpp"
|
||||
#include "libslic3r/Format/OBJ.hpp"
|
||||
@ -270,9 +270,7 @@ static void generate_thumbnail_from_model(const std::string& filename)
|
||||
model.objects[0]->center_around_origin(false);
|
||||
model.objects[0]->ensure_on_bed(false);
|
||||
|
||||
const Vec3d bed_center_3d = wxGetApp().plater()->get_bed().get_bounding_box(false).center();
|
||||
const Vec2d bed_center_2d = { bed_center_3d.x(), bed_center_3d.y()};
|
||||
model.center_instances_around_point(bed_center_2d);
|
||||
model.center_instances_around_point(to_2d(wxGetApp().plater()->build_volume().bounding_volume().center()));
|
||||
|
||||
GLVolumeCollection volumes;
|
||||
volumes.volumes.push_back(new GLVolume());
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "slic3r/GUI/GLCanvas3D.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp"
|
||||
|
||||
#include "libslic3r/Geometry/ConvexHull.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
|
||||
#include <numeric>
|
||||
|
@ -66,20 +66,12 @@ GLGizmoPainterBase::ClippingPlaneDataWrapper GLGizmoPainterBase::get_clipping_pl
|
||||
|
||||
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");
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
if (! shader)
|
||||
return;
|
||||
shader->start_using();
|
||||
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);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
shader->set_uniform("clipping_plane", this->get_clipping_plane_data().clp_dataf);
|
||||
ScopeGuard guard([shader]() { if (shader) shader->stop_using(); });
|
||||
|
||||
@ -106,11 +98,7 @@ void GLGizmoPainterBase::render_triangles(const Selection& selection) const
|
||||
// to the shader input variable print_box.volume_world_matrix before
|
||||
// rendering the painted triangles. When this matrix is not set, the
|
||||
// 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);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
m_triangle_selectors[mesh_id]->render(m_imgui);
|
||||
|
||||
@ -609,11 +597,7 @@ void TriangleSelectorGUI::render(ImGuiWrapper* imgui)
|
||||
auto* shader = wxGetApp().get_current_shader();
|
||||
if (! shader)
|
||||
return;
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
assert(shader->get_name() == "gouraud_mod");
|
||||
#else
|
||||
assert(shader->get_name() == "gouraud");
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
ScopeGuard guard([shader]() { if (shader) shader->set_uniform("offset_depth_buffer", false);});
|
||||
shader->set_uniform("offset_depth_buffer", true);
|
||||
for (auto iva : {std::make_pair(&m_iva_enforcers, enforcers_color),
|
||||
|
@ -35,31 +35,61 @@ static void call_after_if_active(std::function<void()> fn, GUI_App* app = &wxGet
|
||||
});
|
||||
}
|
||||
|
||||
static ModelVolume* get_model_volume(const Selection& selection, Model& model)
|
||||
static Slic3r::ModelVolumes get_model_volumes(const Selection &selection)
|
||||
{
|
||||
const Selection::IndicesList& idxs = selection.get_volume_idxs();
|
||||
// only one selected volume
|
||||
if (idxs.size() != 1)
|
||||
return nullptr;
|
||||
const GLVolume* selected_volume = selection.get_volume(*idxs.begin());
|
||||
if (selected_volume == nullptr)
|
||||
return nullptr;
|
||||
Model &model = *selection.get_model();
|
||||
const Selection::IndicesList& volume_ids = selection.get_volume_idxs();
|
||||
ModelVolumes result;
|
||||
result.reserve(volume_ids.size());
|
||||
for (auto volume_id : volume_ids) {
|
||||
const GLVolume *selected_volume = selection.get_volume(volume_id);
|
||||
assert(selected_volume != nullptr);
|
||||
if (selected_volume == nullptr) continue;
|
||||
|
||||
const GLVolume::CompositeID& cid = selected_volume->composite_id;
|
||||
const ModelObjectPtrs& objs = model.objects;
|
||||
if (cid.object_id < 0 || objs.size() <= static_cast<size_t>(cid.object_id))
|
||||
return nullptr;
|
||||
const ModelObject* obj = objs[cid.object_id];
|
||||
if (cid.volume_id < 0 || obj->volumes.size() <= static_cast<size_t>(cid.volume_id))
|
||||
return nullptr;
|
||||
return obj->volumes[cid.volume_id];
|
||||
const GLVolume::CompositeID &cid = selected_volume->composite_id;
|
||||
const ModelObjectPtrs & objs = model.objects;
|
||||
|
||||
// should not appear
|
||||
assert(cid.object_id >= 0);
|
||||
if (cid.object_id < 0) continue;
|
||||
assert(objs.size() > static_cast<size_t>(cid.object_id));
|
||||
if (objs.size() <= static_cast<size_t>(cid.object_id)) continue;
|
||||
|
||||
const ModelObject *obj = objs[cid.object_id];
|
||||
const ModelVolume *volume = obj->volumes[cid.volume_id];
|
||||
|
||||
// prevent selection of volume without inidces
|
||||
if (volume->mesh().its.indices.empty()) continue;
|
||||
result.emplace_back(volume);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::string create_volumes_name(const ModelVolumes &volumes) {
|
||||
assert(!volumes.empty());
|
||||
std::string name;
|
||||
bool is_first = true;
|
||||
for (const auto &volume : volumes) {
|
||||
if (is_first)
|
||||
is_first = false;
|
||||
else
|
||||
name += " + ";
|
||||
name += volume->name;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
static uint32_t get_triangle_count(std::vector<const Slic3r::ModelVolume *> volumes)
|
||||
{
|
||||
uint32_t count = 0;
|
||||
for (const auto &volume : volumes)
|
||||
count += volume->mesh().its.indices.size();
|
||||
return count;
|
||||
}
|
||||
|
||||
GLGizmoSimplify::GLGizmoSimplify(GLCanvas3D & parent,
|
||||
const std::string &icon_filename,
|
||||
unsigned int sprite_id)
|
||||
: GLGizmoBase(parent, icon_filename, -1)
|
||||
, m_volume(nullptr)
|
||||
, m_show_wireframe(false)
|
||||
, m_move_to_center(false)
|
||||
// translation for GUI size
|
||||
@ -74,7 +104,6 @@ GLGizmoSimplify::~GLGizmoSimplify()
|
||||
stop_worker_thread_request();
|
||||
if (m_worker.joinable())
|
||||
m_worker.join();
|
||||
m_glmodel.reset();
|
||||
}
|
||||
|
||||
bool GLGizmoSimplify::on_esc_key_down() {
|
||||
@ -145,9 +174,8 @@ std::string GLGizmoSimplify::on_get_name() const
|
||||
void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limit)
|
||||
{
|
||||
create_gui_cfg();
|
||||
const Selection &selection = m_parent.get_selection();
|
||||
const ModelVolume *act_volume = get_model_volume(selection, wxGetApp().plater()->model());
|
||||
if (act_volume == nullptr) {
|
||||
auto act_volumes = get_model_volumes(m_parent.get_selection());
|
||||
if (act_volumes.empty()) {
|
||||
stop_worker_thread_request();
|
||||
close();
|
||||
return;
|
||||
@ -161,7 +189,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
||||
std::lock_guard lk(m_state_mutex);
|
||||
is_cancelling = m_state.status == State::cancelling;
|
||||
is_worker_running = m_state.status == State::running;
|
||||
is_result_ready = bool(m_state.result);
|
||||
is_result_ready = !m_state.result.empty();
|
||||
progress = m_state.progress;
|
||||
}
|
||||
|
||||
@ -171,18 +199,20 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
||||
|
||||
// Check selection of new volume
|
||||
// Do not reselect object when processing
|
||||
if (act_volume != m_volume) {
|
||||
bool change_window_position = (m_volume == nullptr);
|
||||
if (m_volumes != act_volumes) {
|
||||
bool change_window_position = m_volumes.empty();
|
||||
// select different model
|
||||
|
||||
// close suggestion notification
|
||||
auto notification_manager = wxGetApp().plater()->get_notification_manager();
|
||||
notification_manager->remove_simplify_suggestion_with_id(act_volume->get_object()->id());
|
||||
for (const auto &volume : act_volumes)
|
||||
notification_manager->remove_simplify_suggestion_with_id(volume->get_object()->id());
|
||||
|
||||
m_volume = act_volume;
|
||||
m_volumes = std::move(act_volumes);
|
||||
m_configuration.decimate_ratio = 50.; // default value
|
||||
m_configuration.fix_count_by_ratio(m_volume->mesh().its.indices.size());
|
||||
init_model(m_volume->mesh().its);
|
||||
|
||||
m_configuration.fix_count_by_ratio(get_triangle_count(m_volumes));
|
||||
init_model(m_volumes);
|
||||
|
||||
// Start processing. If we switched from another object, process will
|
||||
// stop the background thread and it will restart itself later.
|
||||
@ -219,17 +249,16 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
||||
m_imgui->begin(on_get_name(), flag);
|
||||
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr_mesh_name + ":");
|
||||
ImGui::SameLine(m_gui_cfg->top_left_width);
|
||||
std::string name = m_volume->name;
|
||||
std::string name = create_volumes_name(m_volumes);
|
||||
if (name.length() > m_gui_cfg->max_char_in_name)
|
||||
name = name.substr(0, m_gui_cfg->max_char_in_name - 3) + "...";
|
||||
m_imgui->text(name);
|
||||
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr_triangles + ":");
|
||||
ImGui::SameLine(m_gui_cfg->top_left_width);
|
||||
|
||||
size_t orig_triangle_count = m_volume->mesh().its.indices.size();
|
||||
size_t orig_triangle_count = get_triangle_count(m_volumes);
|
||||
m_imgui->text(std::to_string(orig_triangle_count));
|
||||
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if(ImGui::RadioButton("##use_error", !m_configuration.use_count)) {
|
||||
@ -359,9 +388,9 @@ void GLGizmoSimplify::worker_finished()
|
||||
m_worker.join();
|
||||
if (GLGizmoBase::m_state == Off)
|
||||
return;
|
||||
if (m_state.result)
|
||||
init_model(*m_state.result);
|
||||
if (m_state.config != m_configuration || m_state.mv != m_volume) {
|
||||
if (!m_state.result.empty())
|
||||
update_model(m_state.result);
|
||||
if (m_state.config != m_configuration || m_state.mvs != m_volumes) {
|
||||
// Settings were changed, restart the worker immediately.
|
||||
process();
|
||||
}
|
||||
@ -370,16 +399,16 @@ void GLGizmoSimplify::worker_finished()
|
||||
|
||||
void GLGizmoSimplify::process()
|
||||
{
|
||||
if (m_volume == nullptr || m_volume->mesh().its.indices.empty())
|
||||
return;
|
||||
if (m_volumes.empty()) return;
|
||||
|
||||
// m_volume->mesh().its.indices.empty()
|
||||
bool configs_match = false;
|
||||
bool result_valid = false;
|
||||
bool is_worker_running = false;
|
||||
{
|
||||
std::lock_guard lk(m_state_mutex);
|
||||
configs_match = (m_volume == m_state.mv && m_state.config == m_configuration);
|
||||
result_valid = bool(m_state.result);
|
||||
configs_match = (m_volumes == m_state.mvs && m_state.config == m_configuration);
|
||||
result_valid = !m_state.result.empty();
|
||||
is_worker_running = m_state.status == State::running;
|
||||
}
|
||||
|
||||
@ -404,15 +433,18 @@ void GLGizmoSimplify::process()
|
||||
|
||||
// Copy configuration that will be used.
|
||||
m_state.config = m_configuration;
|
||||
m_state.mv = m_volume;
|
||||
m_state.mvs = m_volumes;
|
||||
m_state.status = State::running;
|
||||
|
||||
// Create a copy of current mesh to pass to the worker thread.
|
||||
// Create a copy of current meshes to pass to the worker thread.
|
||||
// Using unique_ptr instead of pass-by-value to avoid an extra
|
||||
// copy (which would happen when passing to std::thread).
|
||||
auto its = std::make_unique<indexed_triangle_set>(m_volume->mesh().its);
|
||||
|
||||
m_worker = std::thread([this](std::unique_ptr<indexed_triangle_set> its) {
|
||||
State::Data its;
|
||||
its.reserve(m_volumes.size());
|
||||
for (const auto &volume : m_volumes)
|
||||
its.emplace_back(std::make_unique<indexed_triangle_set>(volume->mesh().its));
|
||||
|
||||
m_worker = std::thread([this](State::Data its) {
|
||||
|
||||
// Checks that the UI thread did not request cancellation, throws if so.
|
||||
std::function<void(void)> throw_on_cancel = [this]() {
|
||||
@ -439,13 +471,14 @@ void GLGizmoSimplify::process()
|
||||
if (! m_state.config.use_count)
|
||||
max_error = m_state.config.max_error;
|
||||
m_state.progress = 0;
|
||||
m_state.result.reset();
|
||||
m_state.result.clear();
|
||||
m_state.status = State::Status::running;
|
||||
}
|
||||
|
||||
// Start the actual calculation.
|
||||
try {
|
||||
its_quadric_edge_collapse(*its, triangle_count, &max_error, throw_on_cancel, statusfn);
|
||||
for (const auto& it: its)
|
||||
its_quadric_edge_collapse(*it, triangle_count, &max_error, throw_on_cancel, statusfn);
|
||||
} catch (SimplifyCanceledException &) {
|
||||
std::lock_guard lk(m_state_mutex);
|
||||
m_state.status = State::idle;
|
||||
@ -464,24 +497,35 @@ void GLGizmoSimplify::process()
|
||||
}
|
||||
|
||||
void GLGizmoSimplify::apply_simplify() {
|
||||
// worker must be stopped
|
||||
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
auto volumes = get_model_volumes(selection);
|
||||
if(m_state.mvs != volumes) return;
|
||||
|
||||
int object_idx = selection.get_object_idx();
|
||||
|
||||
auto plater = wxGetApp().plater();
|
||||
plater->take_snapshot(_u8L("Simplify ") + m_volume->name);
|
||||
plater->clear_before_change_mesh(object_idx);
|
||||
|
||||
ModelVolume* mv = get_model_volume(selection, wxGetApp().model());
|
||||
assert(mv == m_volume);
|
||||
|
||||
mv->set_mesh(std::move(*m_state.result));
|
||||
m_state.result.reset();
|
||||
mv->calculate_convex_hull();
|
||||
mv->set_new_unique_id();
|
||||
mv->get_object()->invalidate_bounding_box();
|
||||
mv->get_object()->ensure_on_bed(true); // allow negative z
|
||||
plater->take_snapshot(_u8L("Simplify ") + create_volumes_name(m_volumes));
|
||||
plater->clear_before_change_mesh(object_idx); // TODO: fix for all
|
||||
|
||||
const Selection::IndicesList &volume_ids = selection.get_volume_idxs();
|
||||
Model* model = selection.get_model();
|
||||
size_t index = 0;
|
||||
for (auto& volume_id : volume_ids) {
|
||||
const GLVolume *selected_volume = selection.get_volume(volume_id);
|
||||
const GLVolume::CompositeID &cid = selected_volume->composite_id;
|
||||
const ModelObjectPtrs & objs = model->objects;
|
||||
ModelObject * obj = objs[cid.object_id];
|
||||
ModelVolume * volume = obj->volumes[cid.volume_id];
|
||||
volume->set_mesh(std::move(*m_state.result[index]));
|
||||
volume->calculate_convex_hull();
|
||||
volume->set_new_unique_id();
|
||||
obj->invalidate_bounding_box();
|
||||
obj->ensure_on_bed(true); // allow negative z
|
||||
++index;
|
||||
}
|
||||
m_state.result.clear();
|
||||
// fix hollowing, sla support points, modifiers, ...
|
||||
plater->changed_mesh(object_idx);
|
||||
// Fix warning icon in object list
|
||||
@ -501,8 +545,8 @@ void GLGizmoSimplify::on_set_state()
|
||||
m_parent.toggle_model_objects_visibility(true);
|
||||
|
||||
stop_worker_thread_request();
|
||||
m_volume = nullptr; // invalidate selected model
|
||||
m_glmodel.reset();
|
||||
m_volumes.clear(); // invalidate selected model
|
||||
m_glmodels.clear();
|
||||
} else if (GLGizmoBase::m_state == GLGizmoBase::On) {
|
||||
// when open by hyperlink it needs to show up
|
||||
request_rerender();
|
||||
@ -544,26 +588,54 @@ void GLGizmoSimplify::set_center_position() {
|
||||
}
|
||||
|
||||
|
||||
void GLGizmoSimplify::init_model(const indexed_triangle_set& its)
|
||||
void GLGizmoSimplify::init_model(const ModelVolumes &volumes)
|
||||
{
|
||||
if (its.indices.empty())
|
||||
return;
|
||||
|
||||
m_glmodel.reset();
|
||||
m_glmodel.init_from(its);
|
||||
m_parent.toggle_model_objects_visibility(true); // selected volume may have changed
|
||||
m_parent.toggle_model_objects_visibility(false, m_c->selection_info()->model_object(),
|
||||
m_c->selection_info()->get_active_instance(), m_volume);
|
||||
const auto info = m_c->selection_info();
|
||||
|
||||
m_glmodels.clear();
|
||||
m_glmodels.reserve(volumes.size());
|
||||
for (const auto &volume : volumes) {
|
||||
m_glmodels.push_back({});
|
||||
auto &glmodel = m_glmodels.back();
|
||||
glmodel.init_from(volume->mesh().its);
|
||||
// TODO: fix color
|
||||
//if (const Selection&sel = m_parent.get_selection(); sel.get_volume_idxs().size() == 1)
|
||||
// glmodel.set_color(-1, sel.get_volume(*sel.get_volume_idxs().begin())->color);
|
||||
|
||||
// TODO: check what is purpose?
|
||||
m_parent.toggle_model_objects_visibility(false, volume->get_object(), -1, volume);
|
||||
}
|
||||
|
||||
if (const Selection&sel = m_parent.get_selection(); sel.get_volume_idxs().size() == 1)
|
||||
m_glmodel.set_color(-1, sel.get_volume(*sel.get_volume_idxs().begin())->color);
|
||||
m_triangle_count = its.indices.size();
|
||||
// set actual triangle count
|
||||
m_triangle_count = get_triangle_count(volumes);
|
||||
}
|
||||
|
||||
void GLGizmoSimplify::update_model(const State::Data &data)
|
||||
{
|
||||
// check that model exist
|
||||
if (m_glmodels.empty()) return;
|
||||
|
||||
// check that result is for actual gl models
|
||||
size_t model_count = m_glmodels.size();
|
||||
if (data.size() != model_count) return;
|
||||
|
||||
//m_parent.toggle_model_objects_visibility(true); // selected volume may have changed
|
||||
|
||||
m_triangle_count = 0;
|
||||
for (size_t i = 0; i < model_count; ++i) {
|
||||
auto &glmodel = m_glmodels[i];
|
||||
const indexed_triangle_set &its = *data[i];
|
||||
glmodel.init_from(its);
|
||||
m_triangle_count += its.indices.size();
|
||||
|
||||
//m_parent.toggle_model_objects_visibility(false, volume->get_object(), -1, volume);
|
||||
}
|
||||
}
|
||||
|
||||
void GLGizmoSimplify::on_render()
|
||||
{
|
||||
if (! m_glmodel.is_initialized())
|
||||
return;
|
||||
if (m_glmodels.empty()) return;
|
||||
|
||||
const auto& selection = m_parent.get_selection();
|
||||
const auto& volume_idxs = selection.get_volume_idxs();
|
||||
@ -571,7 +643,7 @@ void GLGizmoSimplify::on_render()
|
||||
const GLVolume *selected_volume = selection.get_volume(*volume_idxs.begin());
|
||||
|
||||
// Check that the GLVolume still belongs to the ModelObject we work on.
|
||||
if (m_volume != get_model_volume(selection, wxGetApp().model()))
|
||||
if (m_volumes != get_model_volumes(selection))
|
||||
return;
|
||||
|
||||
const Transform3d trafo_matrix = selected_volume->world_matrix();
|
||||
@ -582,7 +654,7 @@ void GLGizmoSimplify::on_render()
|
||||
glsafe(::glPushAttrib(GL_DEPTH_TEST));
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
gouraud_shader->start_using();
|
||||
m_glmodel.render();
|
||||
for (auto &glmodel : m_glmodels) glmodel.render();
|
||||
gouraud_shader->stop_using();
|
||||
|
||||
if (m_show_wireframe) {
|
||||
@ -593,7 +665,7 @@ void GLGizmoSimplify::on_render()
|
||||
//ScopeGuard offset_fill_guard([]() { glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); });
|
||||
//glsafe(::glEnable(GL_POLYGON_OFFSET_FILL));
|
||||
//glsafe(::glPolygonOffset(5.0, 5.0));
|
||||
m_glmodel.render();
|
||||
for (auto &glmodel : m_glmodels) glmodel.render();
|
||||
glsafe(::glPolygonMode(GL_FRONT_AND_BACK, GL_FILL));
|
||||
contour_shader->stop_using();
|
||||
}
|
||||
|
@ -11,8 +11,11 @@
|
||||
|
||||
namespace Slic3r {
|
||||
class ModelVolume;
|
||||
class ModelObject;
|
||||
class Model;
|
||||
|
||||
using ModelVolumes = std::vector<const ModelVolume*>;
|
||||
|
||||
namespace GUI {
|
||||
class NotificationManager; // for simplify suggestion
|
||||
|
||||
@ -51,7 +54,6 @@ private:
|
||||
|
||||
void create_gui_cfg();
|
||||
void request_rerender(bool force = false);
|
||||
void init_model(const indexed_triangle_set& its);
|
||||
|
||||
void set_center_position();
|
||||
|
||||
@ -76,10 +78,10 @@ private:
|
||||
|
||||
bool m_move_to_center; // opening gizmo
|
||||
|
||||
const ModelVolume *m_volume; // keep pointer to actual working volume
|
||||
ModelVolumes m_volumes; // keep pointers to actual working volumes
|
||||
|
||||
bool m_show_wireframe;
|
||||
GLModel m_glmodel;
|
||||
std::vector<GLModel> m_glmodels;
|
||||
size_t m_triangle_count; // triangle count of the model currently shown
|
||||
|
||||
// Timestamp of the last rerender request. Only accessed from UI thread.
|
||||
@ -88,6 +90,7 @@ private:
|
||||
// Following struct is accessed by both UI and worker thread.
|
||||
// Accesses protected by a mutex.
|
||||
struct State {
|
||||
using Data = std::vector<std::unique_ptr<indexed_triangle_set>>;
|
||||
enum Status {
|
||||
idle,
|
||||
running,
|
||||
@ -97,10 +100,15 @@ private:
|
||||
Status status = idle;
|
||||
int progress = 0; // percent of done work
|
||||
Configuration config; // Configuration we started with.
|
||||
const ModelVolume* mv = nullptr;
|
||||
std::unique_ptr<indexed_triangle_set> result;
|
||||
const ModelObject* mo = nullptr;
|
||||
|
||||
std::vector<const ModelVolume *> mvs; // (M)odel (V)olume(S)
|
||||
Data result;
|
||||
};
|
||||
|
||||
void init_model(const ModelVolumes &volumes);
|
||||
void update_model(const State::Data &data);
|
||||
|
||||
std::thread m_worker;
|
||||
std::mutex m_state_mutex; // guards m_state
|
||||
State m_state; // accessed by both threads
|
||||
|
@ -715,22 +715,18 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
|
||||
}
|
||||
else if (evt.LeftUp() && m_current == Flatten && m_gizmos[m_current]->get_hover_id() != -1) {
|
||||
// to avoid to loose the selection when user clicks an the white faces of a different object while the Flatten gizmo is active
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
selection.stop_dragging();
|
||||
wxGetApp().obj_manipul()->set_dirty();
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
processed = true;
|
||||
}
|
||||
else if (evt.RightUp() && (m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) && !m_parent.is_mouse_dragging()) {
|
||||
gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), control_down);
|
||||
processed = true;
|
||||
}
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
else if (evt.LeftUp()) {
|
||||
selection.stop_dragging();
|
||||
wxGetApp().obj_manipul()->set_dirty();
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
}
|
||||
else {
|
||||
// mouse inside toolbar
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "ArrangeJob.hpp"
|
||||
|
||||
#include "libslic3r/BuildVolume.hpp"
|
||||
#include "libslic3r/MTUtils.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
|
||||
@ -263,7 +264,7 @@ get_wipe_tower_arrangepoly(const Plater &plater)
|
||||
}
|
||||
|
||||
double bed_stride(const Plater *plater) {
|
||||
double bedwidth = plater->bed_shape_bb().size().x();
|
||||
double bedwidth = plater->build_volume().bounding_volume().size().x();
|
||||
return scaled<double>((1. + LOGICAL_BED_GAP) * bedwidth);
|
||||
}
|
||||
|
||||
|
@ -486,7 +486,7 @@ void MainFrame::update_layout()
|
||||
case ESettingsLayout::GCodeViewer:
|
||||
{
|
||||
m_main_sizer->Add(m_plater, 1, wxEXPAND);
|
||||
m_plater->set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, "", "", true);
|
||||
m_plater->set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, 0, {}, {}, true);
|
||||
m_plater->get_collapse_toolbar().set_enabled(false);
|
||||
m_plater->collapse_sidebar(true);
|
||||
m_plater->Show();
|
||||
|
@ -137,14 +137,15 @@ public:
|
||||
ObjectInfo(wxWindow *parent);
|
||||
|
||||
wxStaticBitmap *manifold_warning_icon;
|
||||
wxStaticBitmap *info_icon;
|
||||
wxStaticText *info_size;
|
||||
wxStaticText *info_volume;
|
||||
wxStaticText *info_facets;
|
||||
wxStaticText *info_materials;
|
||||
// wxStaticText *info_materials;
|
||||
wxStaticText *info_manifold;
|
||||
|
||||
wxStaticText *label_volume;
|
||||
wxStaticText *label_materials;
|
||||
// wxStaticText *label_materials; // ysFIXME - delete after next release if anyone will not complain about this
|
||||
std::vector<wxStaticText *> sla_hidden_items;
|
||||
|
||||
bool showing_manifold_warning_icon;
|
||||
@ -161,23 +162,33 @@ ObjectInfo::ObjectInfo(wxWindow *parent) :
|
||||
|
||||
auto *grid_sizer = new wxFlexGridSizer(4, 5, 15);
|
||||
grid_sizer->SetFlexibleDirection(wxHORIZONTAL);
|
||||
// grid_sizer->AddGrowableCol(1, 1);
|
||||
// grid_sizer->AddGrowableCol(3, 1);
|
||||
|
||||
auto init_info_label = [parent, grid_sizer](wxStaticText **info_label, wxString text_label) {
|
||||
auto *text = new wxStaticText(parent, wxID_ANY, text_label + (text_label.empty() ? "" : ":"));
|
||||
auto init_info_label = [parent, grid_sizer](wxStaticText **info_label, wxString text_label, wxSizer* sizer_with_icon=nullptr) {
|
||||
auto *text = new wxStaticText(parent, wxID_ANY, text_label + ":");
|
||||
text->SetFont(wxGetApp().small_font());
|
||||
*info_label = new wxStaticText(parent, wxID_ANY, "");
|
||||
(*info_label)->SetFont(wxGetApp().small_font());
|
||||
grid_sizer->Add(text, 0);
|
||||
grid_sizer->Add(*info_label, 0);
|
||||
if (sizer_with_icon) {
|
||||
sizer_with_icon->Insert(0, *info_label, 0);
|
||||
grid_sizer->Add(sizer_with_icon, 0, wxEXPAND);
|
||||
}
|
||||
else
|
||||
grid_sizer->Add(*info_label, 0);
|
||||
return text;
|
||||
};
|
||||
|
||||
init_info_label(&info_size, _L("Size"));
|
||||
label_volume = init_info_label(&info_volume, _L("Volume"));
|
||||
|
||||
info_icon = new wxStaticBitmap(parent, wxID_ANY, create_scaled_bitmap("info"));
|
||||
info_icon->SetToolTip(_L("For a multipart object, this value isn't accurate.\n"
|
||||
"It doesn't take account of intersections and negative volumes."));
|
||||
auto* volume_info_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
volume_info_sizer->Add(info_icon, 0, wxLEFT, 10);
|
||||
label_volume = init_info_label(&info_volume, _L("Volume"), volume_info_sizer);
|
||||
|
||||
init_info_label(&info_facets, _L("Facets"));
|
||||
label_materials = init_info_label(&info_materials, /*_L("Materials")*/"");
|
||||
// label_materials = init_info_label(&info_materials, _L("Materials"));
|
||||
Add(grid_sizer, 0, wxEXPAND);
|
||||
|
||||
info_manifold = new wxStaticText(parent, wxID_ANY, "");
|
||||
@ -188,7 +199,7 @@ ObjectInfo::ObjectInfo(wxWindow *parent) :
|
||||
sizer_manifold->Add(info_manifold, 0, wxLEFT, 2);
|
||||
Add(sizer_manifold, 0, wxEXPAND | wxTOP, 4);
|
||||
|
||||
sla_hidden_items = { label_volume, info_volume, label_materials, info_materials };
|
||||
sla_hidden_items = { label_volume, info_volume, /*label_materials, info_materials*/ };
|
||||
}
|
||||
|
||||
void ObjectInfo::show_sizer(bool show)
|
||||
@ -1252,6 +1263,8 @@ void Sidebar::show_info_sizer()
|
||||
p->object_info->manifold_warning_icon->SetToolTip(tooltip);
|
||||
|
||||
p->object_info->show_sizer(true);
|
||||
if (vol || model_object->volumes.size() == 1)
|
||||
p->object_info->info_icon->Hide();
|
||||
|
||||
if (p->plater->printer_technology() == ptSLA) {
|
||||
for (auto item: p->object_info->sla_hidden_items)
|
||||
@ -1569,7 +1582,7 @@ struct Plater::priv
|
||||
Slic3r::SLAPrint sla_print;
|
||||
Slic3r::Model model;
|
||||
PrinterTechnology printer_technology = ptFFF;
|
||||
Slic3r::GCodeProcessor::Result gcode_result;
|
||||
Slic3r::GCodeProcessorResult gcode_result;
|
||||
|
||||
// GUI elements
|
||||
wxSizer* panel_sizer{ nullptr };
|
||||
@ -1732,8 +1745,6 @@ struct Plater::priv
|
||||
void update_main_toolbar_tooltips();
|
||||
// std::shared_ptr<ProgressStatusBar> statusbar();
|
||||
std::string get_config(const std::string &key) const;
|
||||
BoundingBoxf bed_shape_bb() const;
|
||||
BoundingBox scaled_bed_shape_bb() const;
|
||||
|
||||
std::vector<size_t> load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config, bool used_inches = false);
|
||||
std::vector<size_t> load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z = false);
|
||||
@ -1857,7 +1868,7 @@ struct Plater::priv
|
||||
// triangulate the bed and store the triangles into m_bed.m_triangles,
|
||||
// fills the m_bed.m_grid_lines and sets m_bed.m_origin.
|
||||
// Sets m_bed.m_polygon to limit the object placement.
|
||||
void set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false);
|
||||
void set_bed_shape(const Pointfs& shape, const double max_print_height, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false);
|
||||
|
||||
bool can_delete() const;
|
||||
bool can_delete_all() const;
|
||||
@ -1971,8 +1982,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
sla_print.set_status_callback(statuscb);
|
||||
this->q->Bind(EVT_SLICING_UPDATE, &priv::on_slicing_update, this);
|
||||
|
||||
view3D = new View3D(q, &model, config, &background_process);
|
||||
preview = new Preview(q, &model, config, &background_process, &gcode_result, [this]() { schedule_background_process(); });
|
||||
view3D = new View3D(q, bed, &model, config, &background_process);
|
||||
preview = new Preview(q, bed, &model, config, &background_process, &gcode_result, [this]() { schedule_background_process(); });
|
||||
|
||||
#ifdef __APPLE__
|
||||
// set default view_toolbar icons size equal to GLGizmosManager::Default_Icons_Size
|
||||
@ -2187,13 +2198,8 @@ void Plater::priv::update(unsigned int flags)
|
||||
{
|
||||
// the following line, when enabled, causes flickering on NVIDIA graphics cards
|
||||
// wxWindowUpdateLocker freeze_guard(q);
|
||||
if (get_config("autocenter") == "1") {
|
||||
// auto *bed_shape_opt = config->opt<ConfigOptionPoints>("bed_shape");
|
||||
// const auto bed_shape = Slic3r::Polygon::new_scale(bed_shape_opt->values);
|
||||
// const BoundingBox bed_shape_bb = bed_shape.bounding_box();
|
||||
const Vec2d& bed_center = bed_shape_bb().center();
|
||||
model.center_instances_around_point(bed_center);
|
||||
}
|
||||
if (get_config("autocenter") == "1")
|
||||
model.center_instances_around_point(this->bed.build_volume().bed_center());
|
||||
|
||||
unsigned int update_status = 0;
|
||||
const bool force_background_processing_restart = this->printer_technology == ptSLA || (flags & (unsigned int)UpdateParams::FORCE_BACKGROUND_PROCESSING_UPDATE);
|
||||
@ -2296,19 +2302,6 @@ std::string Plater::priv::get_config(const std::string &key) const
|
||||
return wxGetApp().app_config->get(key);
|
||||
}
|
||||
|
||||
BoundingBoxf Plater::priv::bed_shape_bb() const
|
||||
{
|
||||
BoundingBox bb = scaled_bed_shape_bb();
|
||||
return BoundingBoxf(unscale(bb.min), unscale(bb.max));
|
||||
}
|
||||
|
||||
BoundingBox Plater::priv::scaled_bed_shape_bb() const
|
||||
{
|
||||
const auto *bed_shape_opt = config->opt<ConfigOptionPoints>("bed_shape");
|
||||
const auto bed_shape = Slic3r::Polygon::new_scale(bed_shape_opt->values);
|
||||
return bed_shape.bounding_box();
|
||||
}
|
||||
|
||||
std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config, bool imperial_units/* = false*/)
|
||||
{
|
||||
if (input_files.empty()) { return std::vector<size_t>(); }
|
||||
@ -2579,7 +2572,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
|
||||
if (one_by_one) {
|
||||
if (type_3mf && !is_project_file)
|
||||
model.center_instances_around_point(bed_shape_bb().center());
|
||||
model.center_instances_around_point(this->bed.build_volume().bed_center());
|
||||
auto loaded_idxs = load_model_objects(model.objects, is_project_file);
|
||||
obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end());
|
||||
} else {
|
||||
@ -2638,8 +2631,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
|
||||
std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z)
|
||||
{
|
||||
const BoundingBoxf bed_shape = bed_shape_bb();
|
||||
const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast<double>(), 1.0) - 2.0 * Vec3d::Ones();
|
||||
const Vec3d bed_size = Slic3r::to_3d(this->bed.build_volume().bounding_volume2d().size(), 1.0) - 2.0 * Vec3d::Ones();
|
||||
|
||||
#ifndef AUTOPLACEMENT_ON_LOAD
|
||||
// bool need_arrange = false;
|
||||
@ -2667,7 +2659,7 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& mode
|
||||
// add a default instance and center object around origin
|
||||
object->center_around_origin(); // also aligns object to Z = 0
|
||||
ModelInstance* instance = object->add_instance();
|
||||
instance->set_offset(Slic3r::to_3d(bed_shape.center().cast<double>(), -object->origin_translation(2)));
|
||||
instance->set_offset(Slic3r::to_3d(this->bed.build_volume().bed_center(), -object->origin_translation(2)));
|
||||
#endif /* AUTOPLACEMENT_ON_LOAD */
|
||||
}
|
||||
|
||||
@ -3004,7 +2996,7 @@ void Plater::find_new_position(const ModelInstancePtrs &instances)
|
||||
if (auto wt = get_wipe_tower_arrangepoly(*this))
|
||||
fixed.emplace_back(*wt);
|
||||
|
||||
arrangement::arrange(movable, fixed, get_bed_shape(*config()), arr_params);
|
||||
arrangement::arrange(movable, fixed, this->build_volume().polygon(), arr_params);
|
||||
|
||||
for (auto & m : movable)
|
||||
m.apply();
|
||||
@ -3072,22 +3064,9 @@ void Plater::priv::schedule_background_process()
|
||||
|
||||
void Plater::priv::update_print_volume_state()
|
||||
{
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(this->config->option("bed_shape"));
|
||||
const Polygon bed_poly_convex = offset(Geometry::convex_hull(Polygon::new_scale(opt->values).points), static_cast<float>(scale_(BedEpsilon))).front();
|
||||
const float bed_height = this->config->opt_float("max_print_height");
|
||||
this->q->model().update_print_volume_state(bed_poly_convex, bed_height);
|
||||
#else
|
||||
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(this->config->opt<ConfigOptionPoints>("bed_shape")->values));
|
||||
BoundingBoxf3 print_volume(unscale(bed_box_2D.min(0), bed_box_2D.min(1), 0.0), unscale(bed_box_2D.max(0), bed_box_2D.max(1), scale_(this->config->opt_float("max_print_height"))));
|
||||
// Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced.
|
||||
print_volume.offset(BedEpsilon);
|
||||
print_volume.min(2) = -1e10;
|
||||
this->q->model().update_print_volume_state(print_volume);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
this->q->model().update_print_volume_state(this->bed.build_volume());
|
||||
}
|
||||
|
||||
|
||||
void Plater::priv::process_validation_warning(const std::string& warning) const
|
||||
{
|
||||
if (warning.empty())
|
||||
@ -4603,9 +4582,9 @@ bool Plater::priv::can_reload_from_disk() const
|
||||
return !paths.empty();
|
||||
}
|
||||
|
||||
void Plater::priv::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom)
|
||||
void Plater::priv::set_bed_shape(const Pointfs& shape, const double max_print_height, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom)
|
||||
{
|
||||
bool new_shape = bed.set_shape(shape, custom_texture, custom_model, force_as_custom);
|
||||
bool new_shape = bed.set_shape(shape, max_print_height, custom_texture, custom_model, force_as_custom);
|
||||
if (new_shape) {
|
||||
if (view3D) view3D->bed_shape_changed();
|
||||
if (preview) preview->bed_shape_changed();
|
||||
@ -6293,13 +6272,14 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
|
||||
void Plater::set_bed_shape() const
|
||||
{
|
||||
set_bed_shape(p->config->option<ConfigOptionPoints>("bed_shape")->values,
|
||||
p->config->option<ConfigOptionFloat>("max_print_height")->value,
|
||||
p->config->option<ConfigOptionString>("bed_custom_texture")->value,
|
||||
p->config->option<ConfigOptionString>("bed_custom_model")->value);
|
||||
}
|
||||
|
||||
void Plater::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom) const
|
||||
void Plater::set_bed_shape(const Pointfs& shape, const double max_print_height, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom) const
|
||||
{
|
||||
p->set_bed_shape(shape, custom_texture, custom_model, force_as_custom);
|
||||
p->set_bed_shape(shape, max_print_height, custom_texture, custom_model, force_as_custom);
|
||||
}
|
||||
|
||||
void Plater::force_filament_colors_update()
|
||||
@ -6354,7 +6334,7 @@ void Plater::on_activate()
|
||||
}
|
||||
|
||||
// Get vector of extruder colors considering filament color, if extruder color is undefined.
|
||||
std::vector<std::string> Plater::get_extruder_colors_from_plater_config(const GCodeProcessor::Result* const result) const
|
||||
std::vector<std::string> Plater::get_extruder_colors_from_plater_config(const GCodeProcessorResult* const result) const
|
||||
{
|
||||
if (wxGetApp().is_gcode_viewer() && result != nullptr)
|
||||
return result->extruder_colors;
|
||||
@ -6380,7 +6360,7 @@ std::vector<std::string> Plater::get_extruder_colors_from_plater_config(const GC
|
||||
/* Get vector of colors used for rendering of a Preview scene in "Color print" mode
|
||||
* It consists of extruder colors and colors, saved in model.custom_gcode_per_print_z
|
||||
*/
|
||||
std::vector<std::string> Plater::get_colors_for_color_print(const GCodeProcessor::Result* const result) const
|
||||
std::vector<std::string> Plater::get_colors_for_color_print(const GCodeProcessorResult* const result) const
|
||||
{
|
||||
std::vector<std::string> colors = get_extruder_colors_from_plater_config(result);
|
||||
colors.reserve(colors.size() + p->model.custom_gcode_per_print_z.gcodes.size());
|
||||
@ -6446,11 +6426,6 @@ GLCanvas3D* Plater::get_current_canvas3D()
|
||||
return p->get_current_canvas3D();
|
||||
}
|
||||
|
||||
BoundingBoxf Plater::bed_shape_bb() const
|
||||
{
|
||||
return p->bed_shape_bb();
|
||||
}
|
||||
|
||||
void Plater::arrange()
|
||||
{
|
||||
p->m_ui_jobs.arrange();
|
||||
@ -6740,14 +6715,9 @@ unsigned int Plater::get_environment_texture_id() const
|
||||
}
|
||||
#endif // ENABLE_ENVIRONMENT_MAP
|
||||
|
||||
const Bed3D& Plater::get_bed() const
|
||||
const BuildVolume& Plater::build_volume() const
|
||||
{
|
||||
return p->bed;
|
||||
}
|
||||
|
||||
Bed3D& Plater::get_bed()
|
||||
{
|
||||
return p->bed;
|
||||
return p->bed.build_volume();
|
||||
}
|
||||
|
||||
const GLToolbar& Plater::get_view_toolbar() const
|
||||
|
@ -23,6 +23,7 @@ class wxString;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class BuildVolume;
|
||||
class Model;
|
||||
class ModelObject;
|
||||
enum class ModelObjectCutAttribute : int;
|
||||
@ -53,7 +54,6 @@ class GLCanvas3D;
|
||||
class Mouse3DController;
|
||||
class NotificationManager;
|
||||
struct Camera;
|
||||
class Bed3D;
|
||||
class GLToolbar;
|
||||
class PlaterPresetComboBox;
|
||||
|
||||
@ -265,8 +265,8 @@ public:
|
||||
void force_print_bed_update();
|
||||
// On activating the parent window.
|
||||
void on_activate();
|
||||
std::vector<std::string> get_extruder_colors_from_plater_config(const GCodeProcessor::Result* const result = nullptr) const;
|
||||
std::vector<std::string> get_colors_for_color_print(const GCodeProcessor::Result* const result = nullptr) const;
|
||||
std::vector<std::string> get_extruder_colors_from_plater_config(const GCodeProcessorResult* const result = nullptr) const;
|
||||
std::vector<std::string> get_colors_for_color_print(const GCodeProcessorResult* const result = nullptr) const;
|
||||
|
||||
void update_menus();
|
||||
void show_action_buttons(const bool is_ready_to_slice) const;
|
||||
@ -282,7 +282,6 @@ public:
|
||||
GLCanvas3D* canvas3D();
|
||||
const GLCanvas3D * canvas3D() const;
|
||||
GLCanvas3D* get_current_canvas3D();
|
||||
BoundingBoxf bed_shape_bb() const;
|
||||
|
||||
void arrange();
|
||||
void find_new_position(const ModelInstancePtrs &instances);
|
||||
@ -339,8 +338,7 @@ public:
|
||||
unsigned int get_environment_texture_id() const;
|
||||
#endif // ENABLE_ENVIRONMENT_MAP
|
||||
|
||||
const Bed3D& get_bed() const;
|
||||
Bed3D& get_bed();
|
||||
const BuildVolume& build_volume() const;
|
||||
|
||||
const GLToolbar& get_view_toolbar() const;
|
||||
GLToolbar& get_view_toolbar();
|
||||
@ -359,7 +357,7 @@ public:
|
||||
Mouse3DController& get_mouse3d_controller();
|
||||
|
||||
void set_bed_shape() const;
|
||||
void set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false) const;
|
||||
void set_bed_shape(const Pointfs& shape, const double max_print_height, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false) const;
|
||||
|
||||
NotificationManager * get_notification_manager();
|
||||
const NotificationManager * get_notification_manager() const;
|
||||
|
@ -819,8 +819,11 @@ void PlaterPresetComboBox::update()
|
||||
|
||||
bitmap_key += single_bar ? filament_rgb : filament_rgb + extruder_rgb;
|
||||
}
|
||||
else if (m_type == Preset::TYPE_SLA_MATERIAL)
|
||||
else if (m_type == Preset::TYPE_SLA_MATERIAL) {
|
||||
material_rgb = is_selected ? m_preset_bundle->sla_materials.get_edited_preset().config.opt_string("material_colour") : preset.config.opt_string("material_colour");
|
||||
if (material_rgb.empty())
|
||||
material_rgb = print_config_def.get("material_colour")->get_default_value<ConfigOptionString>()->value;
|
||||
}
|
||||
|
||||
wxBitmap* bmp = get_bmp(bitmap_key, wide_icons, bitmap_type_name,
|
||||
preset.is_compatible, preset.is_system || preset.is_default,
|
||||
|
@ -112,9 +112,7 @@ Selection::Selection()
|
||||
, m_type(Empty)
|
||||
, m_valid(false)
|
||||
, 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();
|
||||
}
|
||||
@ -693,10 +691,7 @@ void Selection::start_dragging()
|
||||
if (!m_valid)
|
||||
return;
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
m_dragging = true;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
set_caches();
|
||||
}
|
||||
|
||||
@ -737,9 +732,7 @@ void Selection::translate(const Vec3d& displacement, bool local)
|
||||
|
||||
ensure_not_below_bed();
|
||||
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.
|
||||
@ -853,9 +846,7 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_
|
||||
}
|
||||
|
||||
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)
|
||||
@ -954,9 +945,7 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type
|
||||
|
||||
ensure_on_bed();
|
||||
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)
|
||||
|
@ -219,10 +219,7 @@ private:
|
||||
GLModel m_curved_arrow;
|
||||
|
||||
float m_scale_factor;
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool m_dragging;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
public:
|
||||
Selection();
|
||||
@ -316,10 +313,8 @@ public:
|
||||
const BoundingBoxf3& get_scaled_instance_bounding_box() const;
|
||||
|
||||
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 rotate(const Vec3d& rotation, TransformationType transformation_type);
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "libslic3r/Line.hpp"
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
#include "libslic3r/Geometry/Circle.hpp"
|
||||
#include "libslic3r/Geometry/ConvexHull.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/ShortestPath.hpp"
|
||||
|
||||
@ -119,82 +120,28 @@ SCENARIO("Intersections of line segments", "[Geometry]"){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Tests for unused methods still written in perl
|
||||
{
|
||||
my $polygon = Slic3r::Polygon->new(
|
||||
[45919000, 515273900], [14726100, 461246400], [14726100, 348753500], [33988700, 315389800],
|
||||
[43749700, 343843000], [45422300, 352251500], [52362100, 362637800], [62748400, 369577600],
|
||||
[75000000, 372014700], [87251500, 369577600], [97637800, 362637800], [104577600, 352251500],
|
||||
[107014700, 340000000], [104577600, 327748400], [97637800, 317362100], [87251500, 310422300],
|
||||
[82789200, 309534700], [69846100, 294726100], [254081000, 294726100], [285273900, 348753500],
|
||||
[285273900, 461246400], [254081000, 515273900],
|
||||
);
|
||||
|
||||
# this points belongs to $polyline
|
||||
# note: it's actually a vertex, while we should better check an intermediate point
|
||||
my $point = Slic3r::Point->new(104577600, 327748400);
|
||||
|
||||
local $Slic3r::Geometry::epsilon = 1E-5;
|
||||
is_deeply Slic3r::Geometry::polygon_segment_having_point($polygon, $point)->pp,
|
||||
[ [107014700, 340000000], [104577600, 327748400] ],
|
||||
'polygon_segment_having_point';
|
||||
}
|
||||
{
|
||||
auto point = Point(736310778.185108, 5017423926.8924);
|
||||
auto line = Line(Point((long int) 627484000, (long int) 3695776000), Point((long int) 750000000, (long int)3720147000));
|
||||
//is Slic3r::Geometry::point_in_segment($point, $line), 0, 'point_in_segment';
|
||||
}
|
||||
|
||||
// Possible to delete
|
||||
{
|
||||
//my $p1 = [10, 10];
|
||||
//my $p2 = [10, 20];
|
||||
//my $p3 = [10, 30];
|
||||
//my $p4 = [20, 20];
|
||||
//my $p5 = [0, 20];
|
||||
|
||||
THEN("Points in a line give the correct angles"){
|
||||
//is Slic3r::Geometry::angle3points($p2, $p3, $p1), PI(), 'angle3points';
|
||||
//is Slic3r::Geometry::angle3points($p2, $p1, $p3), PI(), 'angle3points';
|
||||
SCENARIO("polygon_is_convex works") {
|
||||
GIVEN("A square of dimension 10") {
|
||||
WHEN("Polygon is convex clockwise") {
|
||||
Polygon cw_square { { {0, 0}, {0,10}, {10,10}, {10,0} } };
|
||||
THEN("it is not convex") {
|
||||
REQUIRE(! polygon_is_convex(cw_square));
|
||||
}
|
||||
}
|
||||
THEN("Left turns give the correct angle"){
|
||||
//is Slic3r::Geometry::angle3points($p2, $p4, $p3), PI()/2, 'angle3points';
|
||||
//is Slic3r::Geometry::angle3points($p2, $p1, $p4), PI()/2, 'angle3points';
|
||||
}
|
||||
THEN("Right turns give the correct angle"){
|
||||
//is Slic3r::Geometry::angle3points($p2, $p3, $p4), PI()/2*3, 'angle3points';
|
||||
//is Slic3r::Geometry::angle3points($p2, $p1, $p5), PI()/2*3, 'angle3points';
|
||||
}
|
||||
//my $p1 = [30, 30];
|
||||
//my $p2 = [20, 20];
|
||||
//my $p3 = [10, 10];
|
||||
//my $p4 = [30, 10];
|
||||
|
||||
//is Slic3r::Geometry::angle3points($p2, $p1, $p3), PI(), 'angle3points';
|
||||
//is Slic3r::Geometry::angle3points($p2, $p1, $p4), PI()/2*3, 'angle3points';
|
||||
//is Slic3r::Geometry::angle3points($p2, $p1, $p1), 2*PI(), 'angle3points';
|
||||
}
|
||||
|
||||
SCENARIO("polygon_is_convex works"){
|
||||
GIVEN("A square of dimension 10"){
|
||||
//my $cw_square = [ [0,0], [0,10], [10,10], [10,0] ];
|
||||
THEN("It is not convex clockwise"){
|
||||
//is polygon_is_convex($cw_square), 0, 'cw square is not convex';
|
||||
}
|
||||
THEN("It is convex counter-clockwise"){
|
||||
//is polygon_is_convex([ reverse @$cw_square ]), 1, 'ccw square is convex';
|
||||
WHEN("Polygon is convex counter-clockwise") {
|
||||
Polygon ccw_square { { {0, 0}, {10,0}, {10,10}, {0,10} } };
|
||||
THEN("it is convex") {
|
||||
REQUIRE(polygon_is_convex(ccw_square));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
GIVEN("A concave polygon"){
|
||||
//my $convex1 = [ [0,0], [10,0], [10,10], [0,10], [0,6], [4,6], [4,4], [0,4] ];
|
||||
THEN("It is concave"){
|
||||
//is polygon_is_convex($convex1), 0, 'concave polygon';
|
||||
GIVEN("A concave polygon") {
|
||||
Polygon concave = { {0,0}, {10,0}, {10,10}, {0,10}, {0,6}, {4,6}, {4,4}, {0,4} };
|
||||
THEN("It is not convex") {
|
||||
REQUIRE(! polygon_is_convex(concave));
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("Creating a polyline generates the obvious lines", "[Geometry]"){
|
||||
Slic3r::Polyline polyline;
|
||||
|
@ -3,6 +3,7 @@
|
||||
%{
|
||||
#include <xsinit.h>
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
#include "libslic3r/Geometry/ConvexHull.hpp"
|
||||
#include "libslic3r/ShortestPath.hpp"
|
||||
%}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user