mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-11 15:09:02 +08:00
Merge branch 'master' into fs_emboss_temp
This commit is contained in:
commit
4da355e55e
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -24,6 +24,9 @@ struct SlopeDetection
|
||||
};
|
||||
|
||||
uniform vec4 uniform_color;
|
||||
uniform bool use_color_clip_plane;
|
||||
uniform vec4 uniform_color_clip_plane_1;
|
||||
uniform vec4 uniform_color_clip_plane_2;
|
||||
uniform SlopeDetection slope;
|
||||
|
||||
#ifdef ENABLE_ENVIRONMENT_MAP
|
||||
@ -34,6 +37,7 @@ uniform SlopeDetection slope;
|
||||
uniform PrintVolumeDetection print_volume;
|
||||
|
||||
varying vec3 clipping_planes_dots;
|
||||
varying float color_clip_plane_dot;
|
||||
|
||||
// x = diffuse, y = specular;
|
||||
varying vec2 intensity;
|
||||
@ -46,12 +50,18 @@ void main()
|
||||
{
|
||||
if (any(lessThan(clipping_planes_dots, ZERO)))
|
||||
discard;
|
||||
vec3 color = uniform_color.rgb;
|
||||
float alpha = uniform_color.a;
|
||||
|
||||
vec4 color;
|
||||
if (use_color_clip_plane) {
|
||||
color.rgb = (color_clip_plane_dot < 0.0) ? uniform_color_clip_plane_1.rgb : uniform_color_clip_plane_2.rgb;
|
||||
color.a = uniform_color.a;
|
||||
}
|
||||
else
|
||||
color = uniform_color;
|
||||
|
||||
if (slope.actived && world_normal_z < slope.normal_z - EPSILON) {
|
||||
color = vec3(0.7, 0.7, 1.0);
|
||||
alpha = 1.0;
|
||||
color.rgb = vec3(0.7, 0.7, 1.0);
|
||||
color.a = 1.0;
|
||||
}
|
||||
|
||||
// if the fragment is outside the print volume -> use darker color
|
||||
@ -67,13 +77,13 @@ void main()
|
||||
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;
|
||||
}
|
||||
color.rgb = (any(lessThan(pv_check_min, ZERO)) || any(greaterThan(pv_check_max, ZERO))) ? mix(color.rgb, ZERO, 0.3333) : color.rgb;
|
||||
|
||||
#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);
|
||||
gl_FragColor = vec4(0.45 * texture(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + 0.8 * color.rgb * intensity.x, color.a);
|
||||
else
|
||||
#endif
|
||||
gl_FragColor = vec4(vec3(intensity.y) + color * intensity.x, alpha);
|
||||
gl_FragColor = vec4(vec3(intensity.y) + color.rgb * intensity.x, color.a);
|
||||
}
|
||||
|
@ -35,6 +35,8 @@ uniform SlopeDetection slope;
|
||||
uniform vec2 z_range;
|
||||
// Clipping plane - general orientation. Used by the SLA gizmo.
|
||||
uniform vec4 clipping_plane;
|
||||
// Color clip plane - general orientation. Used by the cut gizmo.
|
||||
uniform vec4 color_clip_plane;
|
||||
|
||||
attribute vec3 v_position;
|
||||
attribute vec3 v_normal;
|
||||
@ -43,6 +45,7 @@ attribute vec3 v_normal;
|
||||
varying vec2 intensity;
|
||||
|
||||
varying vec3 clipping_planes_dots;
|
||||
varying float color_clip_plane_dot;
|
||||
|
||||
varying vec4 world_pos;
|
||||
varying float world_normal_z;
|
||||
@ -74,4 +77,5 @@ void main()
|
||||
gl_Position = projection_matrix * position;
|
||||
// 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);
|
||||
color_clip_plane_dot = dot(world_pos, color_clip_plane);
|
||||
}
|
||||
|
@ -24,6 +24,9 @@ struct SlopeDetection
|
||||
};
|
||||
|
||||
uniform vec4 uniform_color;
|
||||
uniform bool use_color_clip_plane;
|
||||
uniform vec4 uniform_color_clip_plane_1;
|
||||
uniform vec4 uniform_color_clip_plane_2;
|
||||
uniform SlopeDetection slope;
|
||||
|
||||
#ifdef ENABLE_ENVIRONMENT_MAP
|
||||
@ -34,6 +37,7 @@ uniform SlopeDetection slope;
|
||||
uniform PrintVolumeDetection print_volume;
|
||||
|
||||
in vec3 clipping_planes_dots;
|
||||
in float color_clip_plane_dot;
|
||||
|
||||
// x = diffuse, y = specular;
|
||||
in vec2 intensity;
|
||||
@ -48,12 +52,18 @@ void main()
|
||||
{
|
||||
if (any(lessThan(clipping_planes_dots, ZERO)))
|
||||
discard;
|
||||
vec3 color = uniform_color.rgb;
|
||||
float alpha = uniform_color.a;
|
||||
|
||||
vec4 color;
|
||||
if (use_color_clip_plane) {
|
||||
color.rgb = (color_clip_plane_dot < 0.0) ? uniform_color_clip_plane_1.rgb : uniform_color_clip_plane_2.rgb;
|
||||
color.a = uniform_color.a;
|
||||
}
|
||||
else
|
||||
color = uniform_color;
|
||||
|
||||
if (slope.actived && world_normal_z < slope.normal_z - EPSILON) {
|
||||
color = vec3(0.7, 0.7, 1.0);
|
||||
alpha = 1.0;
|
||||
color.rgb = vec3(0.7, 0.7, 1.0);
|
||||
color.a = 1.0;
|
||||
}
|
||||
|
||||
// if the fragment is outside the print volume -> use darker color
|
||||
@ -69,13 +79,13 @@ void main()
|
||||
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;
|
||||
}
|
||||
color.rgb = (any(lessThan(pv_check_min, ZERO)) || any(greaterThan(pv_check_max, ZERO))) ? mix(color.rgb, ZERO, 0.3333) : color.rgb;
|
||||
|
||||
#ifdef ENABLE_ENVIRONMENT_MAP
|
||||
if (use_environment_tex)
|
||||
out_color = vec4(0.45 * texture(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + 0.8 * color * intensity.x, alpha);
|
||||
out_color = vec4(0.45 * texture(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + 0.8 * color.rgb * intensity.x, color.a);
|
||||
else
|
||||
#endif
|
||||
out_color = vec4(vec3(intensity.y) + color * intensity.x, alpha);
|
||||
out_color = vec4(vec3(intensity.y) + color.rgb * intensity.x, color.a);
|
||||
}
|
||||
|
@ -35,6 +35,8 @@ uniform SlopeDetection slope;
|
||||
uniform vec2 z_range;
|
||||
// Clipping plane - general orientation. Used by the SLA gizmo.
|
||||
uniform vec4 clipping_plane;
|
||||
// Color clip plane - general orientation. Used by the cut gizmo.
|
||||
uniform vec4 color_clip_plane;
|
||||
|
||||
in vec3 v_position;
|
||||
in vec3 v_normal;
|
||||
@ -43,6 +45,7 @@ in vec3 v_normal;
|
||||
out vec2 intensity;
|
||||
|
||||
out vec3 clipping_planes_dots;
|
||||
out float color_clip_plane_dot;
|
||||
|
||||
out vec4 world_pos;
|
||||
out float world_normal_z;
|
||||
@ -74,4 +77,5 @@ void main()
|
||||
gl_Position = projection_matrix * position;
|
||||
// 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);
|
||||
color_clip_plane_dot = dot(world_pos, color_clip_plane);
|
||||
}
|
||||
|
@ -26,6 +26,9 @@ struct SlopeDetection
|
||||
};
|
||||
|
||||
uniform vec4 uniform_color;
|
||||
uniform bool use_color_clip_plane;
|
||||
uniform vec4 uniform_color_clip_plane_1;
|
||||
uniform vec4 uniform_color_clip_plane_2;
|
||||
uniform SlopeDetection slope;
|
||||
|
||||
#ifdef ENABLE_ENVIRONMENT_MAP
|
||||
@ -36,6 +39,7 @@ uniform SlopeDetection slope;
|
||||
uniform PrintVolumeDetection print_volume;
|
||||
|
||||
varying vec3 clipping_planes_dots;
|
||||
varying float color_clip_plane_dot;
|
||||
|
||||
// x = diffuse, y = specular;
|
||||
varying vec2 intensity;
|
||||
@ -48,12 +52,18 @@ void main()
|
||||
{
|
||||
if (any(lessThan(clipping_planes_dots, ZERO)))
|
||||
discard;
|
||||
vec3 color = uniform_color.rgb;
|
||||
float alpha = uniform_color.a;
|
||||
|
||||
vec4 color;
|
||||
if (use_color_clip_plane) {
|
||||
color.rgb = (color_clip_plane_dot < 0.0) ? uniform_color_clip_plane_1.rgb : uniform_color_clip_plane_2.rgb;
|
||||
color.a = uniform_color.a;
|
||||
}
|
||||
else
|
||||
color = uniform_color;
|
||||
|
||||
if (slope.actived && world_normal_z < slope.normal_z - EPSILON) {
|
||||
color = vec3(0.7, 0.7, 1.0);
|
||||
alpha = 1.0;
|
||||
color.rgb = vec3(0.7, 0.7, 1.0);
|
||||
color.a = 1.0;
|
||||
}
|
||||
|
||||
// if the fragment is outside the print volume -> use darker color
|
||||
@ -70,12 +80,12 @@ void main()
|
||||
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;
|
||||
color.rgb = (any(lessThan(pv_check_min, ZERO)) || any(greaterThan(pv_check_max, ZERO))) ? mix(color.rgb, ZERO, 0.3333) : color.rgb;
|
||||
|
||||
#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);
|
||||
gl_FragColor = vec4(0.45 * texture(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + 0.8 * color.rgb * intensity.x, color.a);
|
||||
else
|
||||
#endif
|
||||
gl_FragColor = vec4(vec3(intensity.y) + color * intensity.x, alpha);
|
||||
gl_FragColor = vec4(vec3(intensity.y) + color.rgb * intensity.x, color.a);
|
||||
}
|
||||
|
@ -35,6 +35,8 @@ uniform SlopeDetection slope;
|
||||
uniform vec2 z_range;
|
||||
// Clipping plane - general orientation. Used by the SLA gizmo.
|
||||
uniform vec4 clipping_plane;
|
||||
// Color clip plane - general orientation. Used by the cut gizmo.
|
||||
uniform vec4 color_clip_plane;
|
||||
|
||||
attribute vec3 v_position;
|
||||
attribute vec3 v_normal;
|
||||
@ -43,6 +45,7 @@ attribute vec3 v_normal;
|
||||
varying vec2 intensity;
|
||||
|
||||
varying vec3 clipping_planes_dots;
|
||||
varying float color_clip_plane_dot;
|
||||
|
||||
varying vec4 world_pos;
|
||||
varying float world_normal_z;
|
||||
@ -74,4 +77,5 @@ void main()
|
||||
gl_Position = projection_matrix * position;
|
||||
// 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);
|
||||
color_clip_plane_dot = dot(world_pos, color_clip_plane);
|
||||
}
|
||||
|
@ -1194,7 +1194,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
m_placeholder_parser.set("first_layer_print_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() }));
|
||||
|
||||
std::vector<unsigned char> is_extruder_used(print.config().nozzle_diameter.size(), 0);
|
||||
for (unsigned int extruder_id : print.extruders())
|
||||
for (unsigned int extruder_id : tool_ordering.all_extruders())
|
||||
is_extruder_used[extruder_id] = true;
|
||||
m_placeholder_parser.set("is_extruder_used", new ConfigOptionBools(is_extruder_used));
|
||||
}
|
||||
@ -2793,8 +2793,14 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
|
||||
acceleration = m_config.first_layer_acceleration_over_raft.value;
|
||||
} else if (m_config.bridge_acceleration.value > 0 && path.role().is_bridge()) {
|
||||
acceleration = m_config.bridge_acceleration.value;
|
||||
} else if (m_config.top_solid_infill_acceleration > 0 && path.role() == ExtrusionRole::TopSolidInfill) {
|
||||
acceleration = m_config.top_solid_infill_acceleration.value;
|
||||
} else if (m_config.solid_infill_acceleration > 0 && path.role().is_solid_infill()) {
|
||||
acceleration = m_config.solid_infill_acceleration.value;
|
||||
} else if (m_config.infill_acceleration.value > 0 && path.role().is_infill()) {
|
||||
acceleration = m_config.infill_acceleration.value;
|
||||
} else if (m_config.external_perimeter_acceleration > 0 && path.role().is_external_perimeter()) {
|
||||
acceleration = m_config.external_perimeter_acceleration.value;
|
||||
} else if (m_config.perimeter_acceleration.value > 0 && path.role().is_perimeter()) {
|
||||
acceleration = m_config.perimeter_acceleration.value;
|
||||
} else {
|
||||
|
@ -720,28 +720,26 @@ void Transformation::reset()
|
||||
}
|
||||
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
void Transformation::reset_rotation()
|
||||
{
|
||||
const Geometry::TransformationSVD svd(*this);
|
||||
m_matrix = get_offset_matrix() * Transform3d(svd.v * svd.s * svd.v.transpose()) * svd.mirror_matrix();
|
||||
}
|
||||
|
||||
void Transformation::reset_scaling_factor()
|
||||
{
|
||||
const Geometry::TransformationSVD svd(*this);
|
||||
m_matrix = get_offset_matrix() * Transform3d(svd.u) * Transform3d(svd.v.transpose()) * svd.mirror_matrix();
|
||||
}
|
||||
|
||||
void Transformation::reset_skew()
|
||||
{
|
||||
Matrix3d rotation;
|
||||
Matrix3d scale;
|
||||
m_matrix.computeRotationScaling(&rotation, &scale);
|
||||
auto new_scale_factor = [](const Matrix3d& s) {
|
||||
return pow(s(0, 0) * s(1, 1) * s(2, 2), 1. / 3.); // scale average
|
||||
};
|
||||
|
||||
const double average_scale = std::cbrt(scale(0, 0) * scale(1, 1) * scale(2, 2));
|
||||
|
||||
scale(0, 0) = is_left_handed() ? -average_scale : average_scale;
|
||||
scale(1, 1) = average_scale;
|
||||
scale(2, 2) = average_scale;
|
||||
|
||||
scale(0, 1) = 0.0;
|
||||
scale(0, 2) = 0.0;
|
||||
scale(1, 0) = 0.0;
|
||||
scale(1, 2) = 0.0;
|
||||
scale(2, 0) = 0.0;
|
||||
scale(2, 1) = 0.0;
|
||||
|
||||
const Vec3d offset = get_offset();
|
||||
m_matrix = rotation * scale;
|
||||
m_matrix.translation() = offset;
|
||||
const Geometry::TransformationSVD svd(*this);
|
||||
m_matrix = get_offset_matrix() * Transform3d(svd.u) * scale_transform(new_scale_factor(svd.s)) * Transform3d(svd.v.transpose()) * svd.mirror_matrix();
|
||||
}
|
||||
|
||||
Transform3d Transformation::get_matrix_no_offset() const
|
||||
@ -838,6 +836,43 @@ Transformation Transformation::volume_to_bed_transformation(const Transformation
|
||||
}
|
||||
#endif // !ENABLE_WORLD_COORDINATE
|
||||
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
TransformationSVD::TransformationSVD(const Transform3d& trafo)
|
||||
{
|
||||
const auto &m0 = trafo.matrix().block<3, 3>(0, 0);
|
||||
mirror = m0.determinant() < 0.0;
|
||||
|
||||
Matrix3d m;
|
||||
if (mirror)
|
||||
m = m0 * Eigen::DiagonalMatrix<double, 3, 3>(-1.0, 1.0, 1.0);
|
||||
else
|
||||
m = m0;
|
||||
const Eigen::JacobiSVD<Matrix3d> svd(m, Eigen::ComputeFullU | Eigen::ComputeFullV);
|
||||
u = svd.matrixU();
|
||||
v = svd.matrixV();
|
||||
s = svd.singularValues().asDiagonal();
|
||||
|
||||
scale = !s.isApprox(Matrix3d::Identity());
|
||||
anisotropic_scale = ! is_approx(s(0, 0), s(1, 1)) || ! is_approx(s(1, 1), s(2, 2));
|
||||
rotation = !v.isApprox(u);
|
||||
|
||||
if (anisotropic_scale) {
|
||||
rotation_90_degrees = true;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
const Vec3d row = v.row(i).cwiseAbs();
|
||||
size_t num_zeros = is_approx(row[0], 0.) + is_approx(row[1], 0.) + is_approx(row[2], 0.);
|
||||
size_t num_ones = is_approx(row[0], 1.) + is_approx(row[1], 1.) + is_approx(row[2], 1.);
|
||||
if (num_zeros != 2 || num_ones != 1) {
|
||||
rotation_90_degrees = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
skew = ! rotation_90_degrees;
|
||||
} else
|
||||
skew = false;
|
||||
}
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
|
||||
// For parsing a transformation matrix from 3MF / AMF.
|
||||
Transform3d transform3d_from_string(const std::string& transform_str)
|
||||
{
|
||||
|
@ -492,8 +492,8 @@ public:
|
||||
void reset();
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
void reset_offset() { set_offset(Vec3d::Zero()); }
|
||||
void reset_rotation() { set_rotation(Vec3d::Zero()); }
|
||||
void reset_scaling_factor() { set_scaling_factor(Vec3d::Ones()); }
|
||||
void reset_rotation();
|
||||
void reset_scaling_factor();
|
||||
void reset_mirror() { set_mirror(Vec3d::Ones()); }
|
||||
void reset_skew();
|
||||
|
||||
@ -538,6 +538,27 @@ private:
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
};
|
||||
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
struct TransformationSVD
|
||||
{
|
||||
Matrix3d u = Matrix3d::Identity();
|
||||
Matrix3d s = Matrix3d::Identity();
|
||||
Matrix3d v = Matrix3d::Identity();
|
||||
|
||||
bool mirror{ false };
|
||||
bool scale{ false };
|
||||
bool anisotropic_scale{ false };
|
||||
bool rotation{ false };
|
||||
bool rotation_90_degrees{ false };
|
||||
bool skew{ false };
|
||||
|
||||
explicit TransformationSVD(const Transformation& trafo) : TransformationSVD(trafo.get_matrix()) {}
|
||||
explicit TransformationSVD(const Transform3d& trafo);
|
||||
|
||||
Eigen::DiagonalMatrix<double, 3, 3> mirror_matrix() const { return Eigen::DiagonalMatrix<double, 3, 3>(this->mirror ? -1. : 1., 1., 1.); }
|
||||
};
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
|
||||
// For parsing a transformation matrix from 3MF / AMF.
|
||||
extern Transform3d transform3d_from_string(const std::string& transform_str);
|
||||
|
||||
|
@ -105,7 +105,7 @@ static void connect_layer_slices(
|
||||
const coord_t offset_below,
|
||||
const coord_t offset_above
|
||||
#ifndef NDEBUG
|
||||
, const coord_t offset_end
|
||||
, const coord_t offset_end
|
||||
#endif // NDEBUG
|
||||
)
|
||||
{
|
||||
@ -127,9 +127,7 @@ static void connect_layer_slices(
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
auto assert_intersection_valid = [this](int i, int j) {
|
||||
assert(i != j);
|
||||
if (i > j)
|
||||
std::swap(i, j);
|
||||
assert(i < j);
|
||||
assert(i >= m_offset_below);
|
||||
assert(i < m_offset_above);
|
||||
assert(j >= m_offset_above);
|
||||
@ -140,35 +138,47 @@ static void connect_layer_slices(
|
||||
if (polynode.Contour.size() >= 3) {
|
||||
// If there is an intersection point, it should indicate which contours (one from layer below, the other from layer above) intersect.
|
||||
// Otherwise the contour is fully inside another contour.
|
||||
int32_t i = 0, j = 0;
|
||||
int32_t i = -1, j = -1;
|
||||
for (int icontour = 0; icontour <= polynode.ChildCount(); ++ icontour) {
|
||||
const bool first = icontour == 0;
|
||||
const ClipperLib_Z::Path &contour = first ? polynode.Contour : polynode.Childs[icontour - 1]->Contour;
|
||||
const ClipperLib_Z::Path &contour = icontour == 0 ? polynode.Contour : polynode.Childs[icontour - 1]->Contour;
|
||||
if (contour.size() >= 3) {
|
||||
if (first) {
|
||||
i = contour.front().z();
|
||||
j = i;
|
||||
if (i < 0) {
|
||||
std::tie(i, j) = m_intersections[-i - 1];
|
||||
assert(assert_intersection_valid(i, j));
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
for (const ClipperLib_Z::IntPoint& pt : contour) {
|
||||
for (const ClipperLib_Z::IntPoint &pt : contour) {
|
||||
j = pt.z();
|
||||
if (j < 0) {
|
||||
std::tie(i, j) = m_intersections[-j - 1];
|
||||
const auto &intersection = m_intersections[-j - 1];
|
||||
assert(intersection.first <= intersection.second);
|
||||
if (intersection.second < m_offset_above) {
|
||||
// Ignore intersection of polygons on the 1st layer.
|
||||
assert(intersection.first >= m_offset_below);
|
||||
j = i;
|
||||
} else if (intersection.first >= m_offset_above) {
|
||||
// Ignore intersection of polygons on the 2nd layer
|
||||
assert(intersection.second < m_offset_end);
|
||||
j = i;
|
||||
} else {
|
||||
std::tie(i, j) = m_intersections[-j - 1];
|
||||
assert(assert_intersection_valid(i, j));
|
||||
goto end;
|
||||
}
|
||||
} else if (i == -1) {
|
||||
// First source contour of this expolygon was found.
|
||||
i = j;
|
||||
} else if (i != j) {
|
||||
// Second source contour of this expolygon was found.
|
||||
if (i > j)
|
||||
std::swap(i, j);
|
||||
assert(assert_intersection_valid(i, j));
|
||||
goto end;
|
||||
}
|
||||
else if (i != j)
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
}
|
||||
end:
|
||||
bool found = false;
|
||||
if (i == j) {
|
||||
if (i == -1) {
|
||||
// This should not happen. It may only happen if the source contours had just self intersections or intersections with contours at the same layer.
|
||||
assert(false);
|
||||
} else if (i == j) {
|
||||
// The contour is completely inside another contour.
|
||||
Point pt(polynode.Contour.front().x(), polynode.Contour.front().y());
|
||||
if (i < m_offset_above) {
|
||||
@ -202,8 +212,6 @@ static void connect_layer_slices(
|
||||
}
|
||||
} else {
|
||||
assert(assert_intersection_valid(i, j));
|
||||
if (i > j)
|
||||
std::swap(i, j);
|
||||
i -= m_offset_below;
|
||||
j -= m_offset_above;
|
||||
assert(i >= 0 && i < m_below.lslices_ex.size());
|
||||
|
@ -1305,7 +1305,9 @@ void ModelObject::synchronize_model_after_cut()
|
||||
void ModelObject::apply_cut_attributes(ModelObjectCutAttributes attributes)
|
||||
{
|
||||
// we don't save cut information, if result will not contains all parts of initial object
|
||||
if (!attributes.has(ModelObjectCutAttribute::KeepUpper) || !attributes.has(ModelObjectCutAttribute::KeepLower))
|
||||
if (!attributes.has(ModelObjectCutAttribute::KeepUpper) ||
|
||||
!attributes.has(ModelObjectCutAttribute::KeepLower) ||
|
||||
attributes.has(ModelObjectCutAttribute::InvalidateCutInfo))
|
||||
return;
|
||||
|
||||
if (cut_id.id().invalid())
|
||||
@ -1425,7 +1427,7 @@ void ModelObject::process_modifier_cut(ModelVolume* volume, const Transform3d& i
|
||||
lower->add_volume(*volume);
|
||||
}
|
||||
|
||||
static void add_cut_volume(TriangleMesh& mesh, ModelObject* object, const ModelVolume* src_volume, const Transform3d& cut_matrix)
|
||||
static void add_cut_volume(TriangleMesh& mesh, ModelObject* object, const ModelVolume* src_volume, const Transform3d& cut_matrix, const std::string& suffix = {})
|
||||
{
|
||||
if (mesh.empty())
|
||||
return;
|
||||
@ -1433,7 +1435,7 @@ static void add_cut_volume(TriangleMesh& mesh, ModelObject* object, const ModelV
|
||||
mesh.transform(cut_matrix);
|
||||
ModelVolume* vol = object->add_volume(mesh);
|
||||
|
||||
vol->name = src_volume->name;
|
||||
vol->name = src_volume->name + suffix;
|
||||
// Don't copy the config's ID.
|
||||
vol->config.assign_config(src_volume->config);
|
||||
assert(vol->config.id().valid());
|
||||
@ -1477,6 +1479,12 @@ void ModelObject::process_solid_part_cut(ModelVolume* volume, const Transform3d&
|
||||
|
||||
// Add required cut parts to the objects
|
||||
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) {
|
||||
add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A");
|
||||
add_cut_volume(lower_mesh, upper, volume, cut_matrix, "_B");
|
||||
return;
|
||||
}
|
||||
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
|
||||
add_cut_volume(upper_mesh, upper, volume, cut_matrix);
|
||||
|
||||
@ -1560,7 +1568,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix,
|
||||
clone_for_cut(&upper);
|
||||
|
||||
ModelObject* lower{ nullptr };
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower))
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower) && !attributes.has(ModelObjectCutAttribute::KeepAsParts))
|
||||
clone_for_cut(&lower);
|
||||
|
||||
std::vector<ModelObject*> dowels;
|
||||
@ -1608,34 +1616,40 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix,
|
||||
|
||||
ModelObjectPtrs res;
|
||||
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper->volumes.empty()) {
|
||||
invalidate_translations(upper, instances[instance]);
|
||||
|
||||
reset_instance_transformation(upper, instance, cut_matrix,
|
||||
attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper),
|
||||
attributes.has(ModelObjectCutAttribute::FlipUpper),
|
||||
local_displace);
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepAsParts) && !upper->volumes.empty()) {
|
||||
reset_instance_transformation(upper, instance, cut_matrix);
|
||||
res.push_back(upper);
|
||||
}
|
||||
else {
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper->volumes.empty()) {
|
||||
invalidate_translations(upper, instances[instance]);
|
||||
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower->volumes.empty()) {
|
||||
invalidate_translations(lower, instances[instance]);
|
||||
reset_instance_transformation(upper, instance, cut_matrix,
|
||||
attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper),
|
||||
attributes.has(ModelObjectCutAttribute::FlipUpper),
|
||||
local_displace);
|
||||
res.push_back(upper);
|
||||
}
|
||||
|
||||
reset_instance_transformation(lower, instance, cut_matrix,
|
||||
attributes.has(ModelObjectCutAttribute::PlaceOnCutLower),
|
||||
attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ? true : attributes.has(ModelObjectCutAttribute::FlipLower));
|
||||
res.push_back(lower);
|
||||
}
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower->volumes.empty()) {
|
||||
invalidate_translations(lower, instances[instance]);
|
||||
|
||||
if (attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) {
|
||||
for (auto dowel : dowels) {
|
||||
invalidate_translations(dowel, instances[instance]);
|
||||
reset_instance_transformation(lower, instance, cut_matrix,
|
||||
attributes.has(ModelObjectCutAttribute::PlaceOnCutLower),
|
||||
attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ? true : attributes.has(ModelObjectCutAttribute::FlipLower));
|
||||
res.push_back(lower);
|
||||
}
|
||||
|
||||
reset_instance_transformation(dowel, instance, Transform3d::Identity(), false, false, local_dowels_displace);
|
||||
if (attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) {
|
||||
for (auto dowel : dowels) {
|
||||
invalidate_translations(dowel, instances[instance]);
|
||||
|
||||
local_dowels_displace += dowel->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-1.5, -1.5, 0.0));
|
||||
dowel->name += "-Dowel-" + dowel->volumes[0]->name;
|
||||
res.push_back(dowel);
|
||||
reset_instance_transformation(dowel, instance, Transform3d::Identity(), false, false, local_dowels_displace);
|
||||
|
||||
local_dowels_displace += dowel->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-1.5, -1.5, 0.0));
|
||||
dowel->name += "-Dowel-" + dowel->volumes[0]->name;
|
||||
res.push_back(dowel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -316,7 +316,7 @@ enum class ModelVolumeType : int {
|
||||
SUPPORT_ENFORCER,
|
||||
};
|
||||
|
||||
enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels };
|
||||
enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, KeepAsParts, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels, InvalidateCutInfo };
|
||||
using ModelObjectCutAttributes = enum_bitmask<ModelObjectCutAttribute>;
|
||||
ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute);
|
||||
|
||||
|
@ -443,6 +443,7 @@ static std::vector<std::string> s_Preset_print_options {
|
||||
"enable_dynamic_overhang_speeds", "dynamic_overhang_speeds", "overhang_overlap_levels",
|
||||
"top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed",
|
||||
"bridge_speed", "gap_fill_speed", "gap_fill_enabled", "travel_speed", "travel_speed_z", "first_layer_speed", "first_layer_speed_over_raft", "perimeter_acceleration", "infill_acceleration",
|
||||
"external_perimeter_acceleration", "top_solid_infill_acceleration", "solid_infill_acceleration",
|
||||
"bridge_acceleration", "first_layer_acceleration", "first_layer_acceleration_over_raft", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield",
|
||||
"min_skirt_length", "brim_width", "brim_separation", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers",
|
||||
"raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion",
|
||||
@ -457,7 +458,7 @@ static std::vector<std::string> s_Preset_print_options {
|
||||
"infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder",
|
||||
"ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width",
|
||||
"perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",
|
||||
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio", "clip_multipart_objects",
|
||||
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio",
|
||||
"elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "gcode_resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
|
||||
"wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width",
|
||||
"wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits",
|
||||
|
@ -74,6 +74,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
"duplicate_distance",
|
||||
"end_gcode",
|
||||
"end_filament_gcode",
|
||||
"external_perimeter_acceleration",
|
||||
"extrusion_axis",
|
||||
"extruder_clearance_height",
|
||||
"extruder_clearance_radius",
|
||||
@ -125,10 +126,12 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
"retract_speed",
|
||||
"single_extruder_multi_material_priming",
|
||||
"slowdown_below_layer_time",
|
||||
"solid_infill_acceleration",
|
||||
"standby_temperature_delta",
|
||||
"start_gcode",
|
||||
"start_filament_gcode",
|
||||
"toolchange_gcode",
|
||||
"top_solid_infill_acceleration",
|
||||
"thumbnails",
|
||||
"thumbnails_format",
|
||||
"use_firmware_retraction",
|
||||
|
@ -598,14 +598,6 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(0.f));
|
||||
|
||||
def = this->add("clip_multipart_objects", coBool);
|
||||
def->label = L("Clip multi-part objects");
|
||||
def->tooltip = L("When printing multi-material objects, this settings will make Slic3r "
|
||||
"to clip the overlapping object parts one by the other "
|
||||
"(2nd part will be clipped by the 1st, 3rd part will be clipped by the 1st and 2nd etc).");
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionBool(true));
|
||||
|
||||
def = this->add("colorprint_heights", coFloats);
|
||||
def->label = L("Colorprint height");
|
||||
def->tooltip = L("Heights at which a filament change is to occur.");
|
||||
@ -1398,12 +1390,31 @@ void PrintConfigDef::init_fff_params()
|
||||
def = this->add("infill_acceleration", coFloat);
|
||||
def->label = L("Infill");
|
||||
def->tooltip = L("This is the acceleration your printer will use for infill. Set zero to disable "
|
||||
"acceleration control for infill.");
|
||||
"acceleration control for infill.");
|
||||
def->sidetext = L("mm/s²");
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
||||
def = this->add("solid_infill_acceleration", coFloat);
|
||||
def->label = L("Solid infill");
|
||||
def->tooltip = L("This is the acceleration your printer will use for solid infill. Set zero to use "
|
||||
"the value for infill.");
|
||||
def->sidetext = L("mm/s²");
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
||||
def = this->add("top_solid_infill_acceleration", coFloat);
|
||||
def->label = L("Top solid infill");
|
||||
def->tooltip = L("This is the acceleration your printer will use for top solid infill. Set zero to use "
|
||||
"the value for solid infill.");
|
||||
def->sidetext = L("mm/s²");
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
||||
|
||||
def = this->add("infill_every_layers", coInt);
|
||||
def->label = L("Combine infill every");
|
||||
def->category = L("Infill");
|
||||
@ -1950,6 +1961,14 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
||||
def = this->add("external_perimeter_acceleration", coFloat);
|
||||
def->label = L("External perimeters");
|
||||
def->tooltip = L("This is the acceleration your printer will use for external perimeters. "
|
||||
"Set zero to use the value for perimeters.");
|
||||
def->sidetext = L("mm/s²");
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
||||
def = this->add("perimeter_extruder", coInt);
|
||||
def->label = L("Perimeter extruder");
|
||||
def->category = L("Extruders");
|
||||
@ -4025,6 +4044,22 @@ void PrintConfigDef::init_sla_params()
|
||||
def->set_default_value(new ConfigOptionFloat(0.001));
|
||||
}
|
||||
|
||||
// Ignore the following obsolete configuration keys:
|
||||
static std::set<std::string> PrintConfigDef_ignore = {
|
||||
"clip_multipart_objects",
|
||||
"duplicate_x", "duplicate_y", "gcode_arcs", "multiply_x", "multiply_y",
|
||||
"support_material_tool", "acceleration", "adjust_overhang_flow",
|
||||
"standby_temperature", "scale", "rotate", "duplicate", "duplicate_grid",
|
||||
"start_perimeters_at_concave_points", "start_perimeters_at_non_overhang", "randomize_start",
|
||||
"seal_position", "vibration_limit", "bed_size",
|
||||
"print_center", "g0", "threads", "pressure_advance", "wipe_tower_per_color_wipe",
|
||||
"serial_port", "serial_speed",
|
||||
// Introduced in some PrusaSlicer 2.3.1 alpha, later renamed or removed.
|
||||
"fuzzy_skin_perimeter_mode", "fuzzy_skin_shape",
|
||||
// Introduced in PrusaSlicer 2.3.0-alpha2, later replaced by automatic calculation based on extrusion width.
|
||||
"wall_add_middle_threshold", "wall_split_middle_threshold",
|
||||
};
|
||||
|
||||
void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value)
|
||||
{
|
||||
// handle legacy options
|
||||
@ -4098,32 +4133,17 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
||||
}
|
||||
}*/
|
||||
|
||||
// Ignore the following obsolete configuration keys:
|
||||
static std::set<std::string> ignore = {
|
||||
"duplicate_x", "duplicate_y", "gcode_arcs", "multiply_x", "multiply_y",
|
||||
"support_material_tool", "acceleration", "adjust_overhang_flow",
|
||||
"standby_temperature", "scale", "rotate", "duplicate", "duplicate_grid",
|
||||
"start_perimeters_at_concave_points", "start_perimeters_at_non_overhang", "randomize_start",
|
||||
"seal_position", "vibration_limit", "bed_size",
|
||||
"print_center", "g0", "threads", "pressure_advance", "wipe_tower_per_color_wipe",
|
||||
"serial_port", "serial_speed",
|
||||
// Introduced in some PrusaSlicer 2.3.1 alpha, later renamed or removed.
|
||||
"fuzzy_skin_perimeter_mode", "fuzzy_skin_shape",
|
||||
// Introduced in PrusaSlicer 2.3.0-alpha2, later replaced by automatic calculation based on extrusion width.
|
||||
"wall_add_middle_threshold", "wall_split_middle_threshold",
|
||||
};
|
||||
|
||||
// In PrusaSlicer 2.3.0-alpha0 the "monotonous" infill was introduced, which was later renamed to "monotonic".
|
||||
if (value == "monotonous" && (opt_key == "top_fill_pattern" || opt_key == "bottom_fill_pattern" || opt_key == "fill_pattern"))
|
||||
value = "monotonic";
|
||||
|
||||
if (ignore.find(opt_key) != ignore.end()) {
|
||||
opt_key = "";
|
||||
if (PrintConfigDef_ignore.find(opt_key) != PrintConfigDef_ignore.end()) {
|
||||
opt_key = {};
|
||||
return;
|
||||
}
|
||||
|
||||
if (! print_config_def.has(opt_key)) {
|
||||
opt_key = "";
|
||||
opt_key = {};
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -487,7 +487,6 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionFloat, brim_separation))
|
||||
((ConfigOptionEnum<BrimType>, brim_type))
|
||||
((ConfigOptionFloat, brim_width))
|
||||
((ConfigOptionBool, clip_multipart_objects))
|
||||
((ConfigOptionBool, dont_support_bridges))
|
||||
((ConfigOptionFloat, elefant_foot_compensation))
|
||||
((ConfigOptionFloatOrPercent, extrusion_width))
|
||||
@ -755,6 +754,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
||||
((ConfigOptionInts, disable_fan_first_layers))
|
||||
((ConfigOptionEnum<DraftShield>, draft_shield))
|
||||
((ConfigOptionFloat, duplicate_distance))
|
||||
((ConfigOptionFloat, external_perimeter_acceleration))
|
||||
((ConfigOptionFloat, extruder_clearance_height))
|
||||
((ConfigOptionFloat, extruder_clearance_radius))
|
||||
((ConfigOptionStrings, extruder_colour))
|
||||
@ -797,12 +797,14 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
||||
((ConfigOptionInt, skirt_height))
|
||||
((ConfigOptionInt, skirts))
|
||||
((ConfigOptionInts, slowdown_below_layer_time))
|
||||
((ConfigOptionFloat, solid_infill_acceleration))
|
||||
((ConfigOptionBool, spiral_vase))
|
||||
((ConfigOptionInt, standby_temperature_delta))
|
||||
((ConfigOptionInts, temperature))
|
||||
((ConfigOptionInt, threads))
|
||||
((ConfigOptionPoints, thumbnails))
|
||||
((ConfigOptionEnum<GCodeThumbnailsFormat>, thumbnails_format))
|
||||
((ConfigOptionFloat, top_solid_infill_acceleration))
|
||||
((ConfigOptionBools, wipe))
|
||||
((ConfigOptionBool, wipe_tower))
|
||||
((ConfigOptionFloat, wipe_tower_x))
|
||||
|
@ -620,8 +620,7 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||
|| opt_key == "slicing_mode") {
|
||||
steps.emplace_back(posSlice);
|
||||
} else if (
|
||||
opt_key == "clip_multipart_objects"
|
||||
|| opt_key == "elefant_foot_compensation"
|
||||
opt_key == "elefant_foot_compensation"
|
||||
|| opt_key == "support_material_contact_distance"
|
||||
|| opt_key == "xy_size_compensation") {
|
||||
steps.emplace_back(posSlice);
|
||||
|
@ -237,9 +237,6 @@ static std::vector<std::vector<ExPolygons>> slices_to_regions(
|
||||
const PrintObjectRegions &print_object_regions,
|
||||
const std::vector<float> &zs,
|
||||
std::vector<VolumeSlices> &&volume_slices,
|
||||
// If clipping is disabled, then ExPolygons produced by different volumes will never be merged, thus they will be allowed to overlap.
|
||||
// It is up to the model designer to handle these overlaps.
|
||||
const bool clip_multipart_objects,
|
||||
const std::function<void()> &throw_on_cancel_callback)
|
||||
{
|
||||
model_volumes_sort_by_id(model_volumes);
|
||||
@ -308,7 +305,7 @@ static std::vector<std::vector<ExPolygons>> slices_to_regions(
|
||||
}
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, zs_complex.size()),
|
||||
[&slices_by_region, &print_object_regions, &zs_complex, &layer_ranges_regions_to_slices, clip_multipart_objects, &throw_on_cancel_callback]
|
||||
[&slices_by_region, &print_object_regions, &zs_complex, &layer_ranges_regions_to_slices, &throw_on_cancel_callback]
|
||||
(const tbb::blocked_range<size_t> &range) {
|
||||
float z = zs_complex[range.begin()].second;
|
||||
auto it_layer_range = layer_range_first(print_object_regions.layer_ranges, z);
|
||||
@ -359,7 +356,7 @@ static std::vector<std::vector<ExPolygons>> slices_to_regions(
|
||||
if (next_region_same_modifier)
|
||||
// To be used in the following iteration.
|
||||
temp_slices[idx_region + 1].expolygons = std::move(source);
|
||||
} else if ((region.model_volume->is_model_part() && clip_multipart_objects) || region.model_volume->is_negative_volume()) {
|
||||
} else if (region.model_volume->is_model_part() || region.model_volume->is_negative_volume()) {
|
||||
// Clip every non-zero region preceding it.
|
||||
for (int idx_region2 = 0; idx_region2 < idx_region; ++ idx_region2)
|
||||
if (! temp_slices[idx_region2].expolygons.empty()) {
|
||||
@ -388,10 +385,7 @@ static std::vector<std::vector<ExPolygons>> slices_to_regions(
|
||||
merged = true;
|
||||
}
|
||||
}
|
||||
// Don't unite the regions if ! clip_multipart_objects. In that case it is user's responsibility
|
||||
// to handle region overlaps. Indeed, one may intentionally let the regions overlap to produce crossing perimeters
|
||||
// for example.
|
||||
if (merged && clip_multipart_objects)
|
||||
if (merged)
|
||||
expolygons = closing_ex(expolygons, float(scale_(EPSILON)));
|
||||
slices_by_region[temp_slices[i].region_id][z_idx] = std::move(expolygons);
|
||||
i = j;
|
||||
@ -696,7 +690,6 @@ void PrintObject::slice_volumes()
|
||||
slice_volumes_inner(
|
||||
print->config(), this->config(), this->trafo_centered(),
|
||||
this->model_object()->volumes, m_shared_regions->layer_ranges, slice_zs, throw_on_cancel_callback),
|
||||
m_config.clip_multipart_objects,
|
||||
throw_on_cancel_callback);
|
||||
|
||||
for (size_t region_id = 0; region_id < region_slices.size(); ++ region_id) {
|
||||
|
@ -269,7 +269,8 @@ void set_current_thread_qos()
|
||||
#ifdef __APPLE__
|
||||
// OSX specific: Set Quality of Service to "user initiated", so that the threads will be scheduled to high performance
|
||||
// cores if available.
|
||||
pthread_set_qos_class_self_np(QOS_CLASS_USER_INITIATED, 0);
|
||||
// With QOS_CLASS_USER_INITIATED the worker threads drop priority once slicer loses user focus.
|
||||
pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
|
||||
#endif // __APPLE__
|
||||
}
|
||||
|
||||
|
@ -814,6 +814,10 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
|
||||
|
||||
shader->set_uniform("z_range", m_z_range);
|
||||
shader->set_uniform("clipping_plane", m_clipping_plane);
|
||||
shader->set_uniform("use_color_clip_plane", m_use_color_clip_plane);
|
||||
shader->set_uniform("color_clip_plane", m_color_clip_plane);
|
||||
shader->set_uniform("uniform_color_clip_plane_1", m_color_clip_plane_colors[0]);
|
||||
shader->set_uniform("uniform_color_clip_plane_2", m_color_clip_plane_colors[1]);
|
||||
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);
|
||||
|
@ -387,6 +387,12 @@ private:
|
||||
// plane coeffs for clipping in shaders
|
||||
std::array<double, 4> m_clipping_plane;
|
||||
|
||||
// plane coeffs for render volumes with different colors in shaders
|
||||
// used by cut gizmo
|
||||
std::array<double, 4> m_color_clip_plane;
|
||||
bool m_use_color_clip_plane{ false };
|
||||
std::array<ColorRGBA, 2> m_color_clip_plane_colors{ ColorRGBA::RED(), ColorRGBA::BLUE() };
|
||||
|
||||
struct Slope
|
||||
{
|
||||
// toggle for slope rendering
|
||||
@ -445,6 +451,14 @@ public:
|
||||
const std::array<float, 2>& get_z_range() const { return m_z_range; }
|
||||
const std::array<double, 4>& get_clipping_plane() const { return m_clipping_plane; }
|
||||
|
||||
void set_use_color_clip_plane(bool use) { m_use_color_clip_plane = use; }
|
||||
void set_color_clip_plane(const Vec3d& cp_normal, double offset) {
|
||||
for (int i = 0; i < 3; ++i)
|
||||
m_color_clip_plane[i] = -cp_normal[i];
|
||||
m_color_clip_plane[3] = offset;
|
||||
}
|
||||
void set_color_clip_plane_colors(const std::array<ColorRGBA, 2>& colors) { m_color_clip_plane_colors = colors; }
|
||||
|
||||
bool is_slope_active() const { return m_slope.active; }
|
||||
void set_slope_active(bool active) { m_slope.active = active; }
|
||||
|
||||
|
@ -261,7 +261,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
|
||||
toggle_field(el, has_top_solid_infill || (has_spiral_vase && has_bottom_solid_infill));
|
||||
|
||||
bool have_default_acceleration = config->opt_float("default_acceleration") > 0;
|
||||
for (auto el : { "perimeter_acceleration", "infill_acceleration",
|
||||
for (auto el : { "perimeter_acceleration", "infill_acceleration", "top_solid_infill_acceleration",
|
||||
"solid_infill_acceleration", "external_perimeter_acceleration"
|
||||
"bridge_acceleration", "first_layer_acceleration" })
|
||||
toggle_field(el, have_default_acceleration);
|
||||
|
||||
|
@ -3175,7 +3175,8 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
||||
|
||||
// apply materials in app_config
|
||||
for (const std::string& section_name : {AppConfig::SECTION_FILAMENTS, AppConfig::SECTION_MATERIALS})
|
||||
app_config->set_section(section_name, appconfig_new.get_section(section_name));
|
||||
if (appconfig_new.has_section(section_name))
|
||||
app_config->set_section(section_name, appconfig_new.get_section(section_name));
|
||||
|
||||
app_config->set_vendors(appconfig_new);
|
||||
|
||||
|
@ -3947,8 +3947,8 @@ void GLCanvas3D::update_sequential_clearance()
|
||||
// the results are then cached for following displacements
|
||||
if (m_sequential_print_clearance_first_displacement) {
|
||||
m_sequential_print_clearance.m_hull_2d_cache.clear();
|
||||
float shrink_factor = static_cast<float>(scale_(0.5 * fff_print()->config().extruder_clearance_radius.value - EPSILON));
|
||||
double mitter_limit = scale_(0.1);
|
||||
const float shrink_factor = static_cast<float>(scale_(0.5 * fff_print()->config().extruder_clearance_radius.value - EPSILON));
|
||||
const double mitter_limit = scale_(0.1);
|
||||
m_sequential_print_clearance.m_hull_2d_cache.reserve(m_model->objects.size());
|
||||
for (size_t i = 0; i < m_model->objects.size(); ++i) {
|
||||
ModelObject* model_object = m_model->objects[i];
|
||||
@ -3956,7 +3956,7 @@ void GLCanvas3D::update_sequential_clearance()
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
Geometry::Transformation trafo = model_instance0->get_transformation();
|
||||
trafo.set_offset({ 0.0, 0.0, model_instance0->get_offset().z() });
|
||||
Polygon hull_2d = offset(model_object->convex_hull_2d(trafo.get_matrix()),
|
||||
const Polygon hull_2d = offset(model_object->convex_hull_2d(trafo.get_matrix()),
|
||||
// Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
|
||||
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
|
||||
shrink_factor,
|
||||
@ -3984,13 +3984,8 @@ void GLCanvas3D::update_sequential_clearance()
|
||||
polygons.reserve(instances_count);
|
||||
for (size_t i = 0; i < instance_transforms.size(); ++i) {
|
||||
const auto& instances = instance_transforms[i];
|
||||
double rotation_z0 = instances.front()->get_rotation().z();
|
||||
for (const auto& instance : instances) {
|
||||
Geometry::Transformation transformation;
|
||||
const Vec3d& offset = instance->get_offset();
|
||||
transformation.set_offset({ offset.x(), offset.y(), 0.0 });
|
||||
transformation.set_rotation(Z, instance->get_rotation().z() - rotation_z0);
|
||||
const Transform3d& trafo = transformation.get_matrix();
|
||||
const Transform3d& trafo = instance->get_matrix();
|
||||
const Pointf3s& hull_2d = m_sequential_print_clearance.m_hull_2d_cache[i];
|
||||
Points inst_pts;
|
||||
inst_pts.reserve(hull_2d.size());
|
||||
|
@ -712,6 +712,10 @@ public:
|
||||
bool get_use_clipping_planes() const { return m_use_clipping_planes; }
|
||||
const std::array<ClippingPlane, 2> &get_clipping_planes() const { return m_clipping_planes; };
|
||||
|
||||
void set_use_color_clip_plane(bool use) { m_volumes.set_use_color_clip_plane(use); }
|
||||
void set_color_clip_plane(const Vec3d& cp_normal, double offset) { m_volumes.set_color_clip_plane(cp_normal, offset); }
|
||||
void set_color_clip_plane_colors(const std::array<ColorRGBA, 2>& colors) { m_volumes.set_color_clip_plane_colors(colors); }
|
||||
|
||||
void refresh_camera_scene_box();
|
||||
|
||||
BoundingBoxf3 volumes_bounding_box() const;
|
||||
|
@ -448,14 +448,21 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||
Selection& selection = canvas->get_selection();
|
||||
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
if (selection.is_single_volume_or_modifier())
|
||||
if (selection.is_single_volume_or_modifier()) {
|
||||
GLVolume* vol = const_cast<GLVolume*>(selection.get_first_volume());
|
||||
Geometry::Transformation trafo = vol->get_volume_transformation();
|
||||
trafo.reset_rotation();
|
||||
vol->set_volume_transformation(trafo);
|
||||
}
|
||||
#else
|
||||
if (selection.is_single_volume() || selection.is_single_modifier())
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
const_cast<GLVolume*>(selection.get_first_volume())->set_volume_rotation(Vec3d::Zero());
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
else if (selection.is_single_full_instance()) {
|
||||
Geometry::Transformation trafo = selection.get_first_volume()->get_instance_transformation();
|
||||
trafo.reset_rotation();
|
||||
for (unsigned int idx : selection.get_volume_idxs()) {
|
||||
const_cast<GLVolume*>(selection.get_volume(idx))->set_instance_rotation(Vec3d::Zero());
|
||||
const_cast<GLVolume*>(selection.get_volume(idx))->set_instance_transformation(trafo);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -484,24 +491,22 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||
Selection& selection = canvas->get_selection();
|
||||
if (selection.is_single_volume_or_modifier()) {
|
||||
const bool is_left_handed = selection.get_first_volume()->get_volume_transformation().is_left_handed();
|
||||
const_cast<GLVolume*>(selection.get_first_volume())->set_volume_scaling_factor(Vec3d::Ones());
|
||||
if (is_left_handed)
|
||||
const_cast<GLVolume*>(selection.get_first_volume())->set_volume_mirror({ -1.0 , 1.0, 1.0 });
|
||||
GLVolume* vol = const_cast<GLVolume*>(selection.get_first_volume());
|
||||
Geometry::Transformation trafo = vol->get_volume_transformation();
|
||||
trafo.reset_scaling_factor();
|
||||
vol->set_volume_transformation(trafo);
|
||||
}
|
||||
else if (selection.is_single_full_instance()) {
|
||||
const bool is_left_handed = selection.get_first_volume()->get_instance_transformation().is_left_handed();
|
||||
Geometry::Transformation trafo = selection.get_first_volume()->get_instance_transformation();
|
||||
trafo.reset_scaling_factor();
|
||||
for (unsigned int idx : selection.get_volume_idxs()) {
|
||||
const_cast<GLVolume*>(selection.get_volume(idx))->set_instance_scaling_factor(Vec3d::Ones());
|
||||
if (is_left_handed)
|
||||
const_cast<GLVolume*>(selection.get_volume(idx))->set_instance_mirror({ -1.0 , 1.0, 1.0 });
|
||||
const_cast<GLVolume*>(selection.get_volume(idx))->set_instance_transformation(trafo);
|
||||
}
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
canvas->do_scale(L("Reset scale"));
|
||||
|
||||
UpdateAndShow(true);
|
||||
#else
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Reset scale"));
|
||||
@ -740,7 +745,6 @@ void ObjectManipulation::update_settings_value(const Selection& selection)
|
||||
m_new_rotate_label_string = L("Rotate (relative)");
|
||||
m_new_position = Vec3d::Zero();
|
||||
m_new_rotation = Vec3d::Zero();
|
||||
m_new_scale = Vec3d(100.0, 100.0, 100.0);
|
||||
m_new_size = selection.get_bounding_box_in_current_reference_system().first.size();
|
||||
#else
|
||||
m_new_rotation = volume->get_instance_rotation() * (180.0 / M_PI);
|
||||
@ -927,93 +931,48 @@ void ObjectManipulation::update_if_dirty()
|
||||
|
||||
|
||||
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
void ObjectManipulation::update_reset_buttons_visibility()
|
||||
{
|
||||
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||
if (!canvas)
|
||||
return;
|
||||
const Selection& selection = canvas->get_selection();
|
||||
|
||||
bool show_drop_to_bed = false;
|
||||
bool show_rotation = false;
|
||||
bool show_scale = false;
|
||||
bool show_drop_to_bed = false;
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
bool show_mirror = false;
|
||||
bool show_skew = false;
|
||||
bool show_mirror_warning = false;
|
||||
|
||||
const Selection& selection = canvas->get_selection();
|
||||
if (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()) {
|
||||
const double min_z = selection.is_single_full_instance() ? selection.get_scaled_instance_bounding_box().min.z() :
|
||||
get_volume_min_z(*selection.get_first_volume());
|
||||
|
||||
show_drop_to_bed = std::abs(min_z) > EPSILON;
|
||||
const GLVolume* volume = selection.get_first_volume();
|
||||
Geometry::Transformation trafo;
|
||||
#else
|
||||
if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) {
|
||||
const GLVolume* volume = selection.get_first_volume();
|
||||
Vec3d rotation;
|
||||
Vec3d scale;
|
||||
double min_z = 0.0;
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
const Geometry::Transformation trafo = selection.is_single_full_instance() ? volume->get_instance_transformation() : volume->get_volume_transformation();
|
||||
|
||||
if (selection.is_single_full_instance()) {
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
trafo = volume->get_instance_transformation();
|
||||
const Selection::IndicesList& idxs = selection.get_volume_idxs();
|
||||
for (unsigned int id : idxs) {
|
||||
const Geometry::Transformation world_trafo(selection.get_volume(id)->world_matrix());
|
||||
show_skew |= world_trafo.has_skew();
|
||||
show_mirror_warning |= world_trafo.get_matrix().matrix().determinant() < 0.0;
|
||||
}
|
||||
#else
|
||||
rotation = volume->get_instance_rotation();
|
||||
scale = volume->get_instance_scaling_factor();
|
||||
min_z = selection.get_scaled_instance_bounding_box().min.z();
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
}
|
||||
else {
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
Geometry::Transformation trafo = volume->get_volume_transformation();
|
||||
const Geometry::Transformation world_trafo(volume->world_matrix());
|
||||
show_skew |= world_trafo.has_skew();
|
||||
show_mirror_warning |= world_trafo.get_matrix().matrix().determinant() < 0.0;
|
||||
#else
|
||||
rotation = volume->get_volume_rotation();
|
||||
scale = volume->get_volume_scaling_factor();
|
||||
min_z = get_volume_min_z(*volume);
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
}
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
const Transform3d rotation = trafo.get_rotation_matrix();
|
||||
const Transform3d scale = trafo.get_scaling_factor_matrix();
|
||||
show_rotation = show_mirror_warning ? !trafo.get_matrix().matrix().block<3, 3>(0, 0).isDiagonal() : !rotation.isApprox(Transform3d::Identity());
|
||||
show_scale = !scale.isApprox(Transform3d::Identity());
|
||||
#else
|
||||
show_rotation = !rotation.isApprox(Vec3d::Zero());
|
||||
show_scale = !scale.isApprox(Vec3d::Ones());
|
||||
show_drop_to_bed = std::abs(min_z) > SINKING_Z_THRESHOLD;
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
const Geometry::TransformationSVD trafo_svd(trafo);
|
||||
show_rotation = trafo_svd.rotation;
|
||||
show_scale = trafo_svd.scale;
|
||||
show_mirror = trafo_svd.mirror;
|
||||
show_skew = trafo_svd.skew;
|
||||
}
|
||||
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
wxGetApp().CallAfter([this, show_rotation, show_scale, show_drop_to_bed, show_skew, show_mirror_warning] {
|
||||
#else
|
||||
wxGetApp().CallAfter([this, show_rotation, show_scale, show_drop_to_bed] {
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
wxGetApp().CallAfter([this, show_drop_to_bed, show_rotation, show_scale, show_mirror, show_skew] {
|
||||
// There is a case (under OSX), when this function is called after the Manipulation panel is hidden
|
||||
// So, let check if Manipulation panel is still shown for this moment
|
||||
if (!this->IsShown())
|
||||
return;
|
||||
m_drop_to_bed_button->Show(show_drop_to_bed);
|
||||
m_reset_rotation_button->Show(show_rotation);
|
||||
m_reset_scale_button->Show(show_scale);
|
||||
m_drop_to_bed_button->Show(show_drop_to_bed);
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
m_mirror_warning_bitmap->SetBitmap(show_mirror ? m_manifold_warning_bmp.bmp() : wxNullBitmap);
|
||||
m_mirror_warning_bitmap->SetMinSize(show_mirror ? m_manifold_warning_bmp.GetSize() : wxSize(0, 0));
|
||||
m_mirror_warning_bitmap->SetToolTip(show_mirror ? _L("Left handed") : "");
|
||||
m_reset_skew_button->Show(show_skew);
|
||||
m_skew_label->Show(show_skew);
|
||||
m_mirror_warning_bitmap->SetBitmap(show_mirror_warning ? m_manifold_warning_bmp.bmp() : wxNullBitmap);
|
||||
m_mirror_warning_bitmap->SetMinSize(show_mirror_warning ? m_manifold_warning_bmp.GetSize() : wxSize(0, 0));
|
||||
m_mirror_warning_bitmap->SetToolTip(show_mirror_warning ? _L("Left handed") : "");
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
|
||||
// Because of CallAfter we need to layout sidebar after Show/hide of reset buttons one more time
|
||||
Sidebar& panel = wxGetApp().sidebar();
|
||||
@ -1024,23 +983,75 @@ void ObjectManipulation::update_reset_buttons_visibility()
|
||||
}
|
||||
});
|
||||
}
|
||||
#else
|
||||
void ObjectManipulation::update_reset_buttons_visibility()
|
||||
{
|
||||
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||
if (!canvas)
|
||||
return;
|
||||
const Selection& selection = canvas->get_selection();
|
||||
|
||||
bool show_rotation = false;
|
||||
bool show_scale = false;
|
||||
bool show_drop_to_bed = false;
|
||||
if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) {
|
||||
const GLVolume* volume = selection.get_first_volume();
|
||||
Vec3d rotation;
|
||||
Vec3d scale;
|
||||
double min_z = 0.0;
|
||||
|
||||
if (selection.is_single_full_instance()) {
|
||||
rotation = volume->get_instance_rotation();
|
||||
scale = volume->get_instance_scaling_factor();
|
||||
min_z = selection.get_scaled_instance_bounding_box().min.z();
|
||||
}
|
||||
else {
|
||||
rotation = volume->get_volume_rotation();
|
||||
scale = volume->get_volume_scaling_factor();
|
||||
min_z = get_volume_min_z(*volume);
|
||||
}
|
||||
show_rotation = !rotation.isApprox(Vec3d::Zero());
|
||||
show_scale = !scale.isApprox(Vec3d::Ones());
|
||||
show_drop_to_bed = std::abs(min_z) > SINKING_Z_THRESHOLD;
|
||||
}
|
||||
|
||||
wxGetApp().CallAfter([this, show_rotation, show_scale, show_drop_to_bed] {
|
||||
// There is a case (under OSX), when this function is called after the Manipulation panel is hidden
|
||||
// So, let check if Manipulation panel is still shown for this moment
|
||||
if (!this->IsShown())
|
||||
return;
|
||||
m_reset_rotation_button->Show(show_rotation);
|
||||
m_reset_scale_button->Show(show_scale);
|
||||
m_drop_to_bed_button->Show(show_drop_to_bed);
|
||||
|
||||
// Because of CallAfter we need to layout sidebar after Show/hide of reset buttons one more time
|
||||
Sidebar& panel = wxGetApp().sidebar();
|
||||
if (!panel.IsFrozen()) {
|
||||
panel.Freeze();
|
||||
panel.Layout();
|
||||
panel.Thaw();
|
||||
}
|
||||
});
|
||||
}
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
|
||||
|
||||
|
||||
void ObjectManipulation::update_mirror_buttons_visibility()
|
||||
{
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
const bool can_mirror = wxGetApp().plater()->can_mirror();
|
||||
for (ScalableButton* button : m_mirror_buttons) {
|
||||
button->Enable(can_mirror);
|
||||
}
|
||||
#else
|
||||
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||
Selection& selection = canvas->get_selection();
|
||||
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
if (is_local_coordinates()) {
|
||||
if (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()) {
|
||||
#else
|
||||
std::array<MirrorButtonState, 3> new_states = { mbHidden, mbHidden, mbHidden };
|
||||
|
||||
if (!m_world_coordinates) {
|
||||
if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) {
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
const GLVolume* volume = selection.get_first_volume();
|
||||
Vec3d mirror;
|
||||
|
||||
@ -1049,19 +1060,10 @@ void ObjectManipulation::update_mirror_buttons_visibility()
|
||||
else
|
||||
mirror = volume->get_volume_mirror();
|
||||
|
||||
#if !ENABLE_WORLD_COORDINATE
|
||||
for (unsigned char i=0; i<3; ++i)
|
||||
new_states[i] = (mirror[i] < 0. ? mbActive : mbShown);
|
||||
#endif // !ENABLE_WORLD_COORDINATE
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
const bool can_mirror = wxGetApp().plater()->can_mirror();
|
||||
for (ScalableButton* button : m_mirror_buttons) {
|
||||
button->Enable(can_mirror);
|
||||
}
|
||||
#else
|
||||
else {
|
||||
// the mirroring buttons should be hidden in world coordinates,
|
||||
// unless we make it actually mirror in world coords.
|
||||
|
@ -198,16 +198,23 @@ void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const
|
||||
}
|
||||
|
||||
void GLGizmoBase::render_grabbers(float size) const
|
||||
{
|
||||
render_grabbers(0, m_grabbers.size() - 1, size, false);
|
||||
}
|
||||
|
||||
void GLGizmoBase::render_grabbers(size_t first, size_t last, float size, bool force_hover) const
|
||||
{
|
||||
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
|
||||
if (shader == nullptr)
|
||||
return;
|
||||
shader->start_using();
|
||||
shader->set_uniform("emission_factor", 0.1f);
|
||||
for (int i = 0; i < (int)m_grabbers.size(); ++i) {
|
||||
glsafe(::glDisable(GL_CULL_FACE));
|
||||
for (size_t i = first; i <= last; ++i) {
|
||||
if (m_grabbers[i].enabled)
|
||||
m_grabbers[i].render(m_hover_id == i, size);
|
||||
m_grabbers[i].render(force_hover ? true : m_hover_id == (int)i, size);
|
||||
}
|
||||
glsafe(::glEnable(GL_CULL_FACE));
|
||||
shader->stop_using();
|
||||
}
|
||||
|
||||
|
@ -219,6 +219,7 @@ protected:
|
||||
|
||||
void render_grabbers(const BoundingBoxf3& box) const;
|
||||
void render_grabbers(float size) const;
|
||||
void render_grabbers(size_t first, size_t last, float size, bool force_hover) const;
|
||||
|
||||
std::string format(float value, unsigned int decimals) const;
|
||||
|
||||
|
@ -21,6 +21,8 @@ namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
static const ColorRGBA GRABBER_COLOR = ColorRGBA::YELLOW();
|
||||
static const ColorRGBA UPPER_PART_COLOR = ColorRGBA::CYAN();
|
||||
static const ColorRGBA LOWER_PART_COLOR = ColorRGBA::MAGENTA();
|
||||
|
||||
// connector colors
|
||||
static const ColorRGBA PLAG_COLOR = ColorRGBA::YELLOW();
|
||||
@ -196,7 +198,7 @@ static void init_from_angle_arc(GLModel& model, double angle, double radius)
|
||||
|
||||
GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
|
||||
: GLGizmoBase(parent, icon_filename, sprite_id)
|
||||
, m_connectors_group_id (3)
|
||||
, m_connectors_group_id (GrabberID::Count)
|
||||
, m_connector_type (CutConnectorType::Plug)
|
||||
, m_connector_style (size_t(CutConnectorStyle::Prizm))
|
||||
, m_connector_shape_id (size_t(CutConnectorShape::Circle))
|
||||
@ -227,13 +229,19 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename,
|
||||
|
||||
m_axis_names = { "X", "Y", "Z" };
|
||||
|
||||
m_part_orientation_names = {
|
||||
{"none", _L("Keep orientation")},
|
||||
{"on_cut", _L("Place on cut")},
|
||||
{"flip", _L("Flip upside down")},
|
||||
};
|
||||
|
||||
update_connector_shape();
|
||||
}
|
||||
|
||||
std::string GLGizmoCut3D::get_tooltip() const
|
||||
{
|
||||
std::string tooltip;
|
||||
if (m_hover_id == Z) {
|
||||
if (m_hover_id == Z || (m_dragging && m_hover_id == CutPlane)) {
|
||||
double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0;
|
||||
std::string unit_str = " " + (m_imperial_units ? _u8L("inch") : _u8L("mm"));
|
||||
const BoundingBoxf3 tbb = transformed_bounding_box(m_plane_center);
|
||||
@ -249,6 +257,11 @@ std::string GLGizmoCut3D::get_tooltip() const
|
||||
}
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
if (!m_dragging && m_hover_id == CutPlane)
|
||||
return _u8L("Click to flip the cut plane\n"
|
||||
"Drag to move the cut plane");
|
||||
|
||||
if (tooltip.empty() && (m_hover_id == X || m_hover_id == Y)) {
|
||||
std::string axis = m_hover_id == X ? "X" : "Y";
|
||||
return axis + ": " + format(float(rad2deg(m_angle)), 1) + _u8L("°");
|
||||
@ -264,6 +277,8 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event)
|
||||
|
||||
if (mouse_event.ShiftDown() && mouse_event.LeftDown())
|
||||
return gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown());
|
||||
if (mouse_event.CmdDown() && mouse_event.LeftDown())
|
||||
return false;
|
||||
if (cut_line_processing()) {
|
||||
if (mouse_event.ShiftDown()) {
|
||||
if (mouse_event.Moving()|| mouse_event.Dragging())
|
||||
@ -278,11 +293,17 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event)
|
||||
|
||||
if (use_grabbers(mouse_event)) {
|
||||
if (m_hover_id >= m_connectors_group_id) {
|
||||
if (mouse_event.LeftDown() && !mouse_event.CmdDown()&& !mouse_event.AltDown())
|
||||
if (mouse_event.LeftDown() && !mouse_event.CmdDown() && !mouse_event.AltDown())
|
||||
unselect_all_connectors();
|
||||
if (mouse_event.LeftUp() && !mouse_event.ShiftDown())
|
||||
gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown());
|
||||
}
|
||||
else if (m_hover_id == CutPlane) {
|
||||
if (mouse_event.LeftDown())
|
||||
m_was_cut_plane_dragged = false;
|
||||
else if (mouse_event.LeftUp() && !m_was_cut_plane_dragged)
|
||||
flip_cut_plane();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -392,9 +413,18 @@ void GLGizmoCut3D::update_clipper()
|
||||
|
||||
// calculate normal for cut plane
|
||||
Vec3d normal = m_cut_normal = end - beg;
|
||||
m_cut_normal.normalize();
|
||||
|
||||
// calculate normal and offset for clipping plane
|
||||
double dist = (m_plane_center - beg).norm();
|
||||
dist = std::clamp(dist, 0.0001, normal.norm());
|
||||
normal.normalize();
|
||||
m_clp_normal = normal;
|
||||
double offset = normal.dot(beg) + dist;
|
||||
|
||||
m_parent.set_color_clip_plane(normal, offset);
|
||||
|
||||
if (!is_looking_forward()) {
|
||||
// recalculate normal and offset for clipping plane, if camera is looking downward to cut plane
|
||||
end = beg = m_plane_center;
|
||||
beg[Z] = box.center().z() + m_radius;
|
||||
end[Z] = box.center().z() - m_radius;
|
||||
@ -402,18 +432,16 @@ void GLGizmoCut3D::update_clipper()
|
||||
rotate_vec3d_around_plane_center(beg);
|
||||
rotate_vec3d_around_plane_center(end);
|
||||
|
||||
// recalculate normal for clipping plane, if camera is looking downward to cut plane
|
||||
normal = end - beg;
|
||||
if (normal == Vec3d::Zero())
|
||||
return;
|
||||
}
|
||||
|
||||
// calculate normal and offset for clipping plane
|
||||
double dist = (m_plane_center - beg).norm();
|
||||
dist = std::clamp(dist, 0.0001, normal.norm());
|
||||
normal.normalize();
|
||||
m_clp_normal = normal;
|
||||
const double offset = normal.dot(beg) + dist;
|
||||
dist = (m_plane_center - beg).norm();
|
||||
dist = std::clamp(dist, 0.0001, normal.norm());
|
||||
normal.normalize();
|
||||
m_clp_normal = normal;
|
||||
offset = normal.dot(beg) + dist;
|
||||
}
|
||||
|
||||
m_c->object_clipper()->set_range_and_pos(normal, offset, dist);
|
||||
|
||||
@ -656,12 +684,15 @@ void GLGizmoCut3D::render_cut_plane()
|
||||
shader->set_uniform("view_model_matrix", view_model_matrix);
|
||||
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
|
||||
|
||||
if (can_perform_cut() && has_valid_contour())
|
||||
// m_plane.set_color({ 0.8f, 0.8f, 0.8f, 0.5f });
|
||||
m_plane.set_color({ 0.9f, 0.9f, 0.9f, 0.5f });
|
||||
if (can_perform_cut() && has_valid_contour()) {
|
||||
if (m_hover_id == CutPlane)
|
||||
m_plane.model.set_color({ 0.9f, 0.9f, 0.9f, 0.5f });
|
||||
else
|
||||
m_plane.model.set_color({ 0.8f, 0.8f, 0.8f, 0.5f });
|
||||
}
|
||||
else
|
||||
m_plane.set_color({ 1.0f, 0.8f, 0.8f, 0.5f });
|
||||
m_plane.render();
|
||||
m_plane.model.set_color({ 1.0f, 0.8f, 0.8f, 0.5f });
|
||||
m_plane.model.render();
|
||||
|
||||
glsafe(::glEnable(GL_CULL_FACE));
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
@ -717,7 +748,7 @@ void GLGizmoCut3D::render_line(GLModel& line_model, const ColorRGBA& color, Tran
|
||||
}
|
||||
}
|
||||
|
||||
void GLGizmoCut3D::render_rotation_snapping(Axis axis, const ColorRGBA& color)
|
||||
void GLGizmoCut3D::render_rotation_snapping(GrabberID axis, const ColorRGBA& color)
|
||||
{
|
||||
GLShaderProgram* line_shader = OpenGLManager::get_gl_info().is_core_profile() ? wxGetApp().get_shader("dashed_thick_lines") : wxGetApp().get_shader("flat");
|
||||
if (!line_shader)
|
||||
@ -760,49 +791,34 @@ void GLGizmoCut3D::render_cut_plane_grabbers()
|
||||
{
|
||||
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
|
||||
|
||||
ColorRGBA color = m_hover_id == Z ? complementary(GRABBER_COLOR) : GRABBER_COLOR;
|
||||
ColorRGBA color = ColorRGBA::GRAY();
|
||||
|
||||
const Transform3d view_matrix = wxGetApp().plater()->get_camera().get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m;
|
||||
|
||||
const double mean_size = get_grabber_mean_size(bounding_box());
|
||||
double size;
|
||||
|
||||
double size = m_dragging && m_hover_id == Z ? get_dragging_half_size(mean_size) : get_half_size(mean_size);
|
||||
const bool dragging_by_cut_plane = m_dragging && m_hover_id == CutPlane;
|
||||
|
||||
Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size);
|
||||
Vec3d offset = 1.25 * size * Vec3d::UnitZ();
|
||||
if (!dragging_by_cut_plane) {
|
||||
render_grabber_connection(GRABBER_COLOR, view_matrix);
|
||||
|
||||
// render Z grabber
|
||||
|
||||
if (!m_dragging && m_hover_id < 0)
|
||||
render_grabber_connection(color, view_matrix);
|
||||
render_model(m_sphere.model, color, view_matrix * scale_transform(size));
|
||||
|
||||
if ((!m_dragging && m_hover_id < 0) || m_hover_id == Z)
|
||||
{
|
||||
const BoundingBoxf3 tbb = transformed_bounding_box(m_plane_center);
|
||||
if (tbb.min.z() <= 0.0)
|
||||
render_model(m_cone.model, color, view_matrix * translation_transform(-offset) * rotation_transform(PI * Vec3d::UnitX()) * scale_transform(cone_scale));
|
||||
|
||||
if (tbb.max.z() >= 0.0)
|
||||
render_model(m_cone.model, color, view_matrix * translation_transform(offset) * scale_transform(cone_scale));
|
||||
}
|
||||
|
||||
// render top sphere for X/Y grabbers
|
||||
|
||||
if ((!m_dragging && m_hover_id < 0) || m_hover_id == X || m_hover_id == Y)
|
||||
{
|
||||
// render sphere grabber
|
||||
size = m_dragging ? get_dragging_half_size(mean_size) : get_half_size(mean_size);
|
||||
color = m_hover_id == Y ? complementary(ColorRGBA::GREEN()) :
|
||||
m_hover_id == X ? complementary(ColorRGBA::RED()) : ColorRGBA::GRAY();
|
||||
color = m_hover_id == Y ? complementary(ColorRGBA::GREEN()) :
|
||||
m_hover_id == X ? complementary(ColorRGBA::RED()) :
|
||||
m_hover_id == Z ? GRABBER_COLOR : ColorRGBA::GRAY();
|
||||
render_model(m_sphere.model, color, view_matrix * translation_transform(m_grabber_connection_len * Vec3d::UnitZ()) * scale_transform(size));
|
||||
}
|
||||
|
||||
const bool no_one_grabber_hovered = !m_dragging && (m_hover_id < 0 || m_hover_id == CutPlane);
|
||||
|
||||
// render X grabber
|
||||
|
||||
if ((!m_dragging && m_hover_id < 0) || m_hover_id == X)
|
||||
if (no_one_grabber_hovered || m_hover_id == X)
|
||||
{
|
||||
size = m_dragging && m_hover_id == X ? get_dragging_half_size(mean_size) : get_half_size(mean_size);
|
||||
cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size);
|
||||
const Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size);
|
||||
color = m_hover_id == X ? complementary(ColorRGBA::RED()) : ColorRGBA::RED();
|
||||
|
||||
if (m_hover_id == X) {
|
||||
@ -810,7 +826,7 @@ void GLGizmoCut3D::render_cut_plane_grabbers()
|
||||
render_rotation_snapping(X, color);
|
||||
}
|
||||
|
||||
offset = Vec3d(0.0, 1.25 * size, m_grabber_connection_len);
|
||||
Vec3d offset = Vec3d(0.0, 1.25 * size, m_grabber_connection_len);
|
||||
render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitX()) * scale_transform(cone_scale));
|
||||
offset = Vec3d(0.0, -1.25 * size, m_grabber_connection_len);
|
||||
render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitX()) * scale_transform(cone_scale));
|
||||
@ -818,10 +834,10 @@ void GLGizmoCut3D::render_cut_plane_grabbers()
|
||||
|
||||
// render Y grabber
|
||||
|
||||
if ((!m_dragging && m_hover_id < 0) || m_hover_id == Y)
|
||||
if (no_one_grabber_hovered || m_hover_id == Y)
|
||||
{
|
||||
size = m_dragging && m_hover_id == Y ? get_dragging_half_size(mean_size) : get_half_size(mean_size);
|
||||
cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size);
|
||||
const Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size);
|
||||
color = m_hover_id == Y ? complementary(ColorRGBA::GREEN()) : ColorRGBA::GREEN();
|
||||
|
||||
if (m_hover_id == Y) {
|
||||
@ -829,7 +845,7 @@ void GLGizmoCut3D::render_cut_plane_grabbers()
|
||||
render_rotation_snapping(Y, color);
|
||||
}
|
||||
|
||||
offset = Vec3d(1.25 * size, 0.0, m_grabber_connection_len);
|
||||
Vec3d offset = Vec3d(1.25 * size, 0.0, m_grabber_connection_len);
|
||||
render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale));
|
||||
offset = Vec3d(-1.25 * size, 0.0, m_grabber_connection_len);
|
||||
render_model(m_cone.model, color, view_matrix * translation_transform(offset)* rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale));
|
||||
@ -898,6 +914,9 @@ std::string GLGizmoCut3D::on_get_name() const
|
||||
void GLGizmoCut3D::on_set_state()
|
||||
{
|
||||
if (m_state == On) {
|
||||
m_parent.set_use_color_clip_plane(true);
|
||||
m_parent.set_color_clip_plane_colors({ UPPER_PART_COLOR , LOWER_PART_COLOR });
|
||||
|
||||
update_bb();
|
||||
m_connectors_editing = !m_selected.empty();
|
||||
|
||||
@ -913,6 +932,7 @@ void GLGizmoCut3D::on_set_state()
|
||||
oc->release();
|
||||
}
|
||||
m_selected.clear();
|
||||
m_parent.set_use_color_clip_plane(false);
|
||||
}
|
||||
force_update_clipper_on_render = m_state == On;
|
||||
}
|
||||
@ -940,8 +960,8 @@ void GLGizmoCut3D::on_register_raycasters_for_picking()
|
||||
m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, Y, *m_cone.mesh_raycaster, Transform3d::Identity()));
|
||||
|
||||
m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, Z, *m_sphere.mesh_raycaster, Transform3d::Identity()));
|
||||
m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, Z, *m_cone.mesh_raycaster, Transform3d::Identity()));
|
||||
m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, Z, *m_cone.mesh_raycaster, Transform3d::Identity()));
|
||||
|
||||
m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlane, *m_plane.mesh_raycaster, Transform3d::Identity()));
|
||||
}
|
||||
|
||||
update_raycasters_for_picking_transform();
|
||||
@ -1018,20 +1038,22 @@ void GLGizmoCut3D::update_raycasters_for_picking_transform()
|
||||
const double size = get_half_size(get_grabber_mean_size(box));
|
||||
Vec3d scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size);
|
||||
|
||||
int id = 0;
|
||||
|
||||
Vec3d offset = Vec3d(0.0, 1.25 * size, m_grabber_connection_len);
|
||||
m_raycasters[0]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitX()) * scale_transform(scale));
|
||||
m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitX()) * scale_transform(scale));
|
||||
offset = Vec3d(0.0, -1.25 * size, m_grabber_connection_len);
|
||||
m_raycasters[1]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitX()) * scale_transform(scale));
|
||||
m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitX()) * scale_transform(scale));
|
||||
|
||||
offset = Vec3d(1.25 * size, 0.0, m_grabber_connection_len);
|
||||
m_raycasters[2]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(scale));
|
||||
m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(scale));
|
||||
offset = Vec3d(-1.25 * size, 0.0, m_grabber_connection_len);
|
||||
m_raycasters[3]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(scale));
|
||||
m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(scale));
|
||||
|
||||
offset = 1.25 * size * Vec3d::UnitZ();
|
||||
m_raycasters[4]->set_transform(trafo * scale_transform(size));
|
||||
m_raycasters[5]->set_transform(trafo * translation_transform(-offset) * rotation_transform(PI * Vec3d::UnitX()) * scale_transform(scale));
|
||||
m_raycasters[6]->set_transform(trafo * translation_transform(offset) * scale_transform(scale));
|
||||
m_raycasters[id++]->set_transform(trafo * translation_transform(m_grabber_connection_len * Vec3d::UnitZ()) * scale_transform(size));
|
||||
|
||||
m_raycasters[id++]->set_transform(trafo);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1046,25 +1068,16 @@ bool GLGizmoCut3D::on_is_activable() const
|
||||
if (object_idx < 0 || selection.is_wipe_tower())
|
||||
return false;
|
||||
|
||||
bool is_dowel_object = false;
|
||||
if (const ModelObject* mo = wxGetApp().plater()->model().objects[object_idx]; mo->is_cut()) {
|
||||
int solid_connector_cnt = 0;
|
||||
int connectors_cnt = 0;
|
||||
for (const ModelVolume* volume : mo->volumes) {
|
||||
if (volume->is_cut_connector()) {
|
||||
connectors_cnt++;
|
||||
if (volume->is_model_part())
|
||||
solid_connector_cnt++;
|
||||
}
|
||||
if (connectors_cnt > 1)
|
||||
break;
|
||||
}
|
||||
is_dowel_object = connectors_cnt == 1 && solid_connector_cnt == 1;
|
||||
if (const ModelObject* mo = wxGetApp().plater()->model().objects[object_idx];
|
||||
mo->is_cut() && mo->volumes.size() == 1) {
|
||||
const ModelVolume* volume = mo->volumes[0];
|
||||
if (volume->is_cut_connector() && volume->cut_info.connector_type == CutConnectorType::Dowel)
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is assumed in GLCanvas3D::do_rotate, do not change this
|
||||
// without updating that function too.
|
||||
return selection.is_single_full_instance() && !is_dowel_object && !m_parent.is_layers_editing_enabled();
|
||||
return selection.is_single_full_instance() && !m_parent.is_layers_editing_enabled();
|
||||
}
|
||||
|
||||
bool GLGizmoCut3D::on_is_selectable() const
|
||||
@ -1072,7 +1085,7 @@ bool GLGizmoCut3D::on_is_selectable() const
|
||||
return wxGetApp().get_mode() != comSimple;
|
||||
}
|
||||
|
||||
Vec3d GLGizmoCut3D::mouse_position_in_local_plane(Axis axis, const Linef3& mouse_ray) const
|
||||
Vec3d GLGizmoCut3D::mouse_position_in_local_plane(GrabberID axis, const Linef3& mouse_ray) const
|
||||
{
|
||||
double half_pi = 0.5 * PI;
|
||||
|
||||
@ -1108,39 +1121,39 @@ Vec3d GLGizmoCut3D::mouse_position_in_local_plane(Axis axis, const Linef3& mouse
|
||||
|
||||
void GLGizmoCut3D::dragging_grabber_z(const GLGizmoBase::UpdateData &data)
|
||||
{
|
||||
Vec3d starting_box_center = m_plane_center - Vec3d::UnitZ(); // some Margin
|
||||
rotate_vec3d_around_plane_center(starting_box_center);
|
||||
const Vec3d grabber_init_pos = (m_hover_id == CutPlane ? 0. : m_grabber_connection_len) * Vec3d::UnitZ();
|
||||
const Vec3d starting_drag_position = translation_transform(m_plane_center) * m_rotation_m * grabber_init_pos;
|
||||
double projection = 0.0;
|
||||
|
||||
const Vec3d&starting_drag_position = m_plane_center;
|
||||
double projection = 0.0;
|
||||
|
||||
Vec3d starting_vec = starting_drag_position - starting_box_center;
|
||||
Vec3d starting_vec = m_rotation_m * Vec3d::UnitZ();
|
||||
if (starting_vec.norm() != 0.0) {
|
||||
Vec3d mouse_dir = data.mouse_ray.unit_vector();
|
||||
const Vec3d mouse_dir = data.mouse_ray.unit_vector();
|
||||
// finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position
|
||||
// use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
|
||||
// in our case plane normal and ray direction are the same (orthogonal view)
|
||||
// when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
|
||||
Vec3d inters = data.mouse_ray.a + (starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir;
|
||||
const Vec3d inters = data.mouse_ray.a + (starting_drag_position - data.mouse_ray.a).dot(mouse_dir) * mouse_dir;
|
||||
// vector from the starting position to the found intersection
|
||||
Vec3d inters_vec = inters - starting_drag_position;
|
||||
const Vec3d inters_vec = inters - starting_drag_position;
|
||||
|
||||
starting_vec.normalize();
|
||||
// finds projection of the vector along the staring direction
|
||||
projection = inters_vec.dot(starting_vec);
|
||||
}
|
||||
if (wxGetKeyState(WXK_SHIFT))
|
||||
projection = m_snap_step * (double)std::round(projection / m_snap_step);
|
||||
projection = m_snap_step * std::round(projection / m_snap_step);
|
||||
|
||||
const Vec3d shift = starting_vec * projection;
|
||||
|
||||
// move cut plane center
|
||||
set_center(m_plane_center + shift);
|
||||
|
||||
m_was_cut_plane_dragged = true;
|
||||
}
|
||||
|
||||
void GLGizmoCut3D::dragging_grabber_xy(const GLGizmoBase::UpdateData &data)
|
||||
{
|
||||
const Vec2d mouse_pos = to_2d(mouse_position_in_local_plane((Axis)m_hover_id, data.mouse_ray));
|
||||
const Vec2d mouse_pos = to_2d(mouse_position_in_local_plane((GrabberID)m_hover_id, data.mouse_ray));
|
||||
|
||||
const Vec2d orig_dir = Vec2d::UnitX();
|
||||
const Vec2d new_dir = mouse_pos.normalized();
|
||||
@ -1197,7 +1210,7 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data)
|
||||
{
|
||||
if (m_hover_id < 0)
|
||||
return;
|
||||
if (m_hover_id == Z)
|
||||
if (m_hover_id == Z || m_hover_id == CutPlane)
|
||||
dragging_grabber_z(data);
|
||||
else if (m_hover_id == X || m_hover_id == Y)
|
||||
dragging_grabber_xy(data);
|
||||
@ -1223,8 +1236,9 @@ void GLGizmoCut3D::on_stop_dragging()
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Rotate cut plane"), UndoRedo::SnapshotType::GizmoAction);
|
||||
m_start_dragging_m = m_rotation_m;
|
||||
}
|
||||
else if (m_hover_id == Z) {
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Move cut plane"), UndoRedo::SnapshotType::GizmoAction);
|
||||
else if (m_hover_id == Z || m_hover_id == CutPlane) {
|
||||
if (m_was_cut_plane_dragged)
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Move cut plane"), UndoRedo::SnapshotType::GizmoAction);
|
||||
m_ar_plane_center = m_plane_center;
|
||||
}
|
||||
}
|
||||
@ -1332,7 +1346,7 @@ bool GLGizmoCut3D::update_bb()
|
||||
set_center_pos(m_bb_center, true);
|
||||
|
||||
m_radius = box.radius();
|
||||
m_grabber_connection_len = 0.75 * m_radius;// std::min<double>(0.75 * m_radius, 35.0);
|
||||
m_grabber_connection_len = 0.5 * m_radius;// std::min<double>(0.75 * m_radius, 35.0);
|
||||
m_grabber_radius = m_grabber_connection_len * 0.85;
|
||||
|
||||
m_snap_coarse_in_radius = m_grabber_radius / 3.0;
|
||||
@ -1372,6 +1386,14 @@ void GLGizmoCut3D::init_picking_models()
|
||||
m_sphere.model.init_from(its);
|
||||
m_sphere.mesh_raycaster = std::make_unique<MeshRaycaster>(std::make_shared<const TriangleMesh>(std::move(its)));
|
||||
}
|
||||
|
||||
if (!m_plane.model.is_initialized() && !m_hide_cut_plane && !m_connectors_editing) {
|
||||
const double cp_width = 0.02 * get_grabber_mean_size(bounding_box());
|
||||
indexed_triangle_set its = its_make_frustum_dowel((double)m_cut_plane_radius_koef * m_radius, cp_width, m_cut_plane_as_circle ? 180 : 4);
|
||||
m_plane.model.init_from(its);
|
||||
m_plane.mesh_raycaster = std::make_unique<MeshRaycaster>(std::make_shared<const TriangleMesh>(std::move(its)));
|
||||
}
|
||||
|
||||
if (m_shapes.empty())
|
||||
init_connector_shapes();
|
||||
}
|
||||
@ -1392,18 +1414,6 @@ void GLGizmoCut3D::init_rendering_items()
|
||||
}
|
||||
if (!m_angle_arc.is_initialized() || m_angle != 0.0)
|
||||
init_from_angle_arc(m_angle_arc, m_angle, m_grabber_connection_len);
|
||||
|
||||
if (!m_plane.is_initialized() && !m_hide_cut_plane && !m_connectors_editing) {
|
||||
#if 1
|
||||
const double cp_width = 0.02 * get_grabber_mean_size(bounding_box());
|
||||
m_plane.init_from(its_make_frustum_dowel((double)m_cut_plane_radius_koef * m_radius, cp_width, m_cut_plane_as_circle ? 180 : 4));
|
||||
#else
|
||||
if (m_cut_plane_as_circle)
|
||||
m_plane.init_from(its_make_frustum_dowel(2. * m_radius, 0.3, 180));
|
||||
else
|
||||
m_plane.init_from(its_make_square_plane(float(m_radius)));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void GLGizmoCut3D::render_clipper_cut()
|
||||
@ -1566,6 +1576,8 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors)
|
||||
reset_connectors();
|
||||
m_imgui->disabled_end();
|
||||
|
||||
render_flip_plane_button(m_connectors_editing && connectors.empty());
|
||||
|
||||
m_imgui->text(_L("Type"));
|
||||
bool type_changed = render_connect_type_radio_button(CutConnectorType::Plug);
|
||||
type_changed |= render_connect_type_radio_button(CutConnectorType::Dowel);
|
||||
@ -1660,6 +1672,57 @@ void GLGizmoCut3D::set_connectors_editing(bool connectors_editing)
|
||||
m_parent.request_extra_frame();
|
||||
}
|
||||
|
||||
void GLGizmoCut3D::flip_cut_plane()
|
||||
{
|
||||
m_rotation_m = m_rotation_m * rotation_transform(PI * Vec3d::UnitX());
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Flip cut plane"), UndoRedo::SnapshotType::GizmoAction);
|
||||
m_start_dragging_m = m_rotation_m;
|
||||
|
||||
update_clipper();
|
||||
}
|
||||
|
||||
void GLGizmoCut3D::render_flip_plane_button(bool disable_pred /*=false*/)
|
||||
{
|
||||
ImGui::SameLine(2.5 * m_label_width);
|
||||
|
||||
if (m_hover_id == CutPlane)
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetColorU32(ImGuiCol_ButtonHovered));
|
||||
|
||||
m_imgui->disabled_begin(disable_pred);
|
||||
if (m_imgui->button(_L("Flip cut plane")))
|
||||
flip_cut_plane();
|
||||
m_imgui->disabled_end();
|
||||
|
||||
if (m_hover_id == CutPlane)
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
void GLGizmoCut3D::add_vertical_scaled_interval(float interval)
|
||||
{
|
||||
ImGui::GetCurrentWindow()->DC.CursorPos.y += m_imgui->scaled(interval);
|
||||
}
|
||||
|
||||
void GLGizmoCut3D::add_horizontal_scaled_interval(float interval)
|
||||
{
|
||||
ImGui::GetCurrentWindow()->DC.CursorPos.x += m_imgui->scaled(interval);
|
||||
}
|
||||
|
||||
void GLGizmoCut3D::add_horizontal_shift(float shift)
|
||||
{
|
||||
ImGui::GetCurrentWindow()->DC.CursorPos.x += shift;
|
||||
}
|
||||
|
||||
void GLGizmoCut3D::render_color_marker(float size, const ImU32& color)
|
||||
{
|
||||
ImGui::SameLine();
|
||||
const float radius = 0.5f * size;
|
||||
ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos;
|
||||
pos.x += size;
|
||||
pos.y += 1.25f * radius;
|
||||
ImGui::GetCurrentWindow()->DrawList->AddNgonFilled(pos, radius, color, 6);
|
||||
ImGuiWrapper::text(" ");
|
||||
}
|
||||
|
||||
void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors)
|
||||
{
|
||||
// WIP : cut plane mode
|
||||
@ -1667,65 +1730,130 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors)
|
||||
|
||||
if (m_mode == size_t(CutMode::cutPlanar)) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(wxString(ImGui::InfoMarkerSmall));
|
||||
ImGuiWrapper::text(wxString(ImGui::InfoMarkerSmall));
|
||||
ImGui::SameLine();
|
||||
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT,
|
||||
get_wraped_wxString(_L("Hold SHIFT key and connect some two points of an object to cut by line"), 40));
|
||||
ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT,
|
||||
get_wraped_wxString(_L("Hold SHIFT key to draw a cut line"), 40));
|
||||
ImGui::Separator();
|
||||
|
||||
render_build_size();
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(_L("Cut position: "));
|
||||
ImGuiWrapper::text(_L("Cut position: "));
|
||||
ImGui::SameLine(m_label_width);
|
||||
render_move_center_input(Z);
|
||||
ImGui::SameLine();
|
||||
|
||||
const bool has_connectors = !connectors.empty();
|
||||
|
||||
const bool is_cut_plane_init = m_rotation_m.isApprox(Transform3d::Identity()) && bounding_box().center() == m_plane_center;
|
||||
m_imgui->disabled_begin(is_cut_plane_init);
|
||||
if (render_reset_button("cut_plane", _u8L("Reset cutting plane")))
|
||||
reset_cut_plane();
|
||||
if (render_reset_button("cut_plane", _u8L("Reset cutting plane")))
|
||||
reset_cut_plane();
|
||||
m_imgui->disabled_end();
|
||||
|
||||
m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower);
|
||||
if (m_imgui->button(_L("Add/Edit connectors")))
|
||||
// render_flip_plane_button();
|
||||
|
||||
add_vertical_scaled_interval(0.75f);
|
||||
|
||||
m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower || m_keep_as_parts);
|
||||
if (m_imgui->button(has_connectors ? _L("Edit connectors") : _L("Add connectors")))
|
||||
set_connectors_editing(true);
|
||||
m_imgui->disabled_end();
|
||||
|
||||
ImGui::SameLine(2.5f * m_label_width);
|
||||
|
||||
m_imgui->disabled_begin(is_cut_plane_init && !has_connectors);
|
||||
if (m_imgui->button(_L("Reset cut"), _L("Reset cutting plane and remove connectors"))) {
|
||||
reset_cut_plane();
|
||||
reset_connectors();
|
||||
}
|
||||
m_imgui->disabled_end();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
float label_width = 0;
|
||||
for (const wxString& label : {_L("Upper part"), _L("Lower part")}) {
|
||||
const float width = m_imgui->calc_text_size(label).x + m_imgui->scaled(1.5f);
|
||||
if (label_width < width)
|
||||
label_width = width;
|
||||
// render "After Cut" section
|
||||
|
||||
ImVec2 label_size;
|
||||
for (const auto& item : m_part_orientation_names) {
|
||||
const ImVec2 text_size = ImGuiWrapper::calc_text_size(item.second);
|
||||
if (label_size.x < text_size.x)
|
||||
label_size.x = text_size.x;
|
||||
if (label_size.y < text_size.y)
|
||||
label_size.y = text_size.y;
|
||||
}
|
||||
|
||||
auto render_part_action_line = [this, label_width, connectors](const wxString& label, const wxString& suffix, bool& keep_part, bool& place_on_cut_part, bool& rotate_part) {
|
||||
const float h_shift = label_size.x + m_imgui->scaled(3.f);
|
||||
const float marker_size = label_size.y;
|
||||
|
||||
auto render_part_name = [this, marker_size, has_connectors](const wxString& name, bool& keep_part, const ImU32& color) {
|
||||
bool keep = true;
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(label);
|
||||
add_horizontal_shift(m_imgui->scaled(1.2f));
|
||||
m_imgui->checkbox((m_keep_as_parts ? _L("Part") : _L("Object")) + " " + name, has_connectors ? keep : keep_part);
|
||||
render_color_marker(marker_size, color);
|
||||
};
|
||||
|
||||
ImGui::SameLine(label_width);
|
||||
|
||||
m_imgui->disabled_begin(!connectors.empty());
|
||||
m_imgui->checkbox(_L("Keep") + suffix, connectors.empty() ? keep_part : keep);
|
||||
m_imgui->disabled_end();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
m_imgui->disabled_begin(!keep_part);
|
||||
if (m_imgui->checkbox(_L("Place on cut") + suffix, place_on_cut_part))
|
||||
auto render_part_actions = [this, h_shift] (const wxString& suffix, const bool& keep_part, bool& place_on_cut_part, bool& rotate_part)
|
||||
{
|
||||
float shift = m_imgui->scaled(1.2f);
|
||||
if (suffix == "##lower")
|
||||
shift += h_shift;
|
||||
m_imgui->disabled_begin(!keep_part || m_keep_as_parts);
|
||||
add_horizontal_shift(shift);
|
||||
if (m_imgui->radio_button(m_part_orientation_names.at("none") + suffix, !place_on_cut_part && !rotate_part)) {
|
||||
rotate_part = false;
|
||||
ImGui::SameLine();
|
||||
if (m_imgui->checkbox(_L("Flip") + suffix, rotate_part))
|
||||
place_on_cut_part = false;
|
||||
}
|
||||
|
||||
add_horizontal_shift(shift);
|
||||
if (m_imgui->radio_button(m_part_orientation_names.at("on_cut") + suffix, place_on_cut_part)) {
|
||||
place_on_cut_part = !place_on_cut_part;
|
||||
rotate_part = false;
|
||||
}
|
||||
|
||||
add_horizontal_shift(shift);
|
||||
if (m_imgui->radio_button(m_part_orientation_names.at("flip") + suffix, rotate_part)) {
|
||||
rotate_part = !rotate_part;
|
||||
place_on_cut_part = false;
|
||||
}
|
||||
m_imgui->disabled_end();
|
||||
};
|
||||
|
||||
m_imgui->text(_L("After cut") + ": ");
|
||||
render_part_action_line( _L("Upper part"), "##upper", m_keep_upper, m_place_on_cut_upper, m_rotate_upper);
|
||||
render_part_action_line( _L("Lower part"), "##lower", m_keep_lower, m_place_on_cut_lower, m_rotate_lower);
|
||||
ImGuiWrapper::text(_L("After cut") + ": ");
|
||||
add_vertical_scaled_interval(0.5f);
|
||||
|
||||
m_imgui->disabled_begin(has_connectors || m_keep_as_parts);
|
||||
render_part_name("A", m_keep_upper, m_imgui->to_ImU32(UPPER_PART_COLOR));
|
||||
ImGui::SameLine(h_shift + ImGui::GetCurrentWindow()->WindowPadding.x);
|
||||
render_part_name("B", m_keep_lower, m_imgui->to_ImU32(LOWER_PART_COLOR));
|
||||
m_imgui->disabled_end();
|
||||
|
||||
add_vertical_scaled_interval(0.5f);
|
||||
|
||||
const ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos;
|
||||
render_part_actions("##upper", m_keep_upper, m_place_on_cut_upper, m_rotate_upper);
|
||||
|
||||
ImGui::GetCurrentWindow()->DC.CursorPos = pos;
|
||||
render_part_actions("##lower", m_keep_lower, m_place_on_cut_lower, m_rotate_lower);
|
||||
|
||||
add_vertical_scaled_interval(0.75f);
|
||||
|
||||
m_imgui->disabled_begin(has_connectors);
|
||||
add_horizontal_shift(m_imgui->scaled(/*1*/.2f));
|
||||
ImGuiWrapper::text(_L("Cut to") + ":");
|
||||
|
||||
ImGui::SameLine();
|
||||
if (m_imgui->radio_button(_L("Objects"), !m_keep_as_parts))
|
||||
m_keep_as_parts = false;
|
||||
ImGui::SameLine();
|
||||
if (m_imgui->radio_button(_L("Parts"), m_keep_as_parts))
|
||||
m_keep_as_parts = true;
|
||||
|
||||
if (m_keep_as_parts) {
|
||||
m_keep_upper = m_keep_lower = true;
|
||||
m_place_on_cut_upper = m_place_on_cut_lower = false;
|
||||
m_rotate_upper = m_rotate_lower = false;
|
||||
}
|
||||
m_imgui->disabled_end();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
@ -2110,11 +2238,13 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
|
||||
plater->cut(object_idx, instance_idx, translation_transform(cut_center_offset) * m_rotation_m,
|
||||
only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) |
|
||||
only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) |
|
||||
only_if(has_connectors ? false: m_keep_as_parts, ModelObjectCutAttribute::KeepAsParts) |
|
||||
only_if(m_place_on_cut_upper, ModelObjectCutAttribute::PlaceOnCutUpper) |
|
||||
only_if(m_place_on_cut_lower, ModelObjectCutAttribute::PlaceOnCutLower) |
|
||||
only_if(m_rotate_upper, ModelObjectCutAttribute::FlipUpper) |
|
||||
only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower) |
|
||||
only_if(create_dowels_as_separate_object, ModelObjectCutAttribute::CreateDowels));
|
||||
only_if(create_dowels_as_separate_object, ModelObjectCutAttribute::CreateDowels) |
|
||||
only_if(!has_connectors, ModelObjectCutAttribute::InvalidateCutInfo));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2349,7 +2479,7 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi
|
||||
if (is_dragging() || m_connector_mode == CutConnectorMode::Auto)
|
||||
return false;
|
||||
|
||||
if ( m_hover_id < 0 && shift_down && ! m_connectors_editing &&
|
||||
if ( (m_hover_id < 0 || m_hover_id == CutPlane) && shift_down && ! m_connectors_editing &&
|
||||
(action == SLAGizmoEventType::LeftDown || action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::Moving) )
|
||||
return process_cut_line(action, mouse_position);
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "slic3r/GUI/GLModel.hpp"
|
||||
#include "libslic3r/TriangleMesh.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
@ -20,6 +21,14 @@ enum class SLAGizmoEventType : unsigned char;
|
||||
|
||||
class GLGizmoCut3D : public GLGizmoBase
|
||||
{
|
||||
enum GrabberID {
|
||||
X = 0,
|
||||
Y,
|
||||
Z,
|
||||
CutPlane,
|
||||
Count,
|
||||
};
|
||||
|
||||
Transform3d m_rotation_m{ Transform3d::Identity() };
|
||||
double m_snap_step{ 1.0 };
|
||||
int m_connectors_group_id;
|
||||
@ -57,10 +66,10 @@ class GLGizmoCut3D : public GLGizmoBase
|
||||
|
||||
Vec2d m_ldown_mouse_position{ Vec2d::Zero() };
|
||||
|
||||
GLModel m_plane;
|
||||
GLModel m_grabber_connection;
|
||||
GLModel m_cut_line;
|
||||
|
||||
PickingModel m_plane;
|
||||
PickingModel m_sphere;
|
||||
PickingModel m_cone;
|
||||
std::map<CutConnectorAttributes, PickingModel> m_shapes;
|
||||
@ -90,6 +99,7 @@ class GLGizmoCut3D : public GLGizmoBase
|
||||
|
||||
bool m_keep_upper{ true };
|
||||
bool m_keep_lower{ true };
|
||||
bool m_keep_as_parts{ false };
|
||||
bool m_place_on_cut_upper{ true };
|
||||
bool m_place_on_cut_lower{ false };
|
||||
bool m_rotate_upper{ false };
|
||||
@ -121,6 +131,7 @@ class GLGizmoCut3D : public GLGizmoBase
|
||||
GLSelectionRectangle m_selection_rectangle;
|
||||
|
||||
bool m_has_invalid_connector{ false };
|
||||
bool m_was_cut_plane_dragged { false };
|
||||
|
||||
bool m_show_shortcuts{ false };
|
||||
std::vector<std::pair<wxString, wxString>> m_shortcuts;
|
||||
@ -154,6 +165,8 @@ class GLGizmoCut3D : public GLGizmoBase
|
||||
|
||||
std::vector<std::string> m_axis_names;
|
||||
|
||||
std::map<std::string, wxString> m_part_orientation_names;
|
||||
|
||||
public:
|
||||
GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
|
||||
|
||||
@ -192,7 +205,7 @@ protected:
|
||||
void on_set_hover_id() override;
|
||||
bool on_is_activable() const override;
|
||||
bool on_is_selectable() const override;
|
||||
Vec3d mouse_position_in_local_plane(Axis axis, const Linef3&mouse_ray) const;
|
||||
Vec3d mouse_position_in_local_plane(GrabberID axis, const Linef3&mouse_ray) const;
|
||||
void dragging_grabber_z(const GLGizmoBase::UpdateData &data);
|
||||
void dragging_grabber_xy(const GLGizmoBase::UpdateData &data);
|
||||
void dragging_connector(const GLGizmoBase::UpdateData &data);
|
||||
@ -211,6 +224,12 @@ protected:
|
||||
void render_build_size();
|
||||
void reset_cut_plane();
|
||||
void set_connectors_editing(bool connectors_editing);
|
||||
void flip_cut_plane();
|
||||
void render_flip_plane_button(bool disable_pred = false);
|
||||
void add_vertical_scaled_interval(float interval);
|
||||
void add_horizontal_scaled_interval(float interval);
|
||||
void add_horizontal_shift(float shift);
|
||||
void render_color_marker(float size, const ImU32& color);
|
||||
void render_cut_plane_input_window(CutConnectors &connectors);
|
||||
void init_input_window_data(CutConnectors &connectors);
|
||||
void render_input_window_warning() const;
|
||||
@ -258,7 +277,7 @@ private:
|
||||
void render_cut_plane();
|
||||
void render_model(GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix);
|
||||
void render_line(GLModel& line_model, const ColorRGBA& color, Transform3d view_model_matrix, float width);
|
||||
void render_rotation_snapping(Axis axis, const ColorRGBA& color);
|
||||
void render_rotation_snapping(GrabberID axis, const ColorRGBA& color);
|
||||
void render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix);
|
||||
void render_cut_plane_grabbers();
|
||||
void render_cut_line();
|
||||
|
@ -519,6 +519,13 @@ bool GLGizmoFdmSupports::has_backend_supports()
|
||||
|
||||
void GLGizmoFdmSupports::auto_generate()
|
||||
{
|
||||
std::string err = wxGetApp().plater()->fff_print().validate();
|
||||
if (!err.empty()) {
|
||||
MessageDialog dlg(GUI::wxGetApp().plater(), _L("Automatic painting requires valid print setup. \n") + err, _L("Warning"), wxOK);
|
||||
dlg.ShowModal();
|
||||
return;
|
||||
}
|
||||
|
||||
ModelObject *mo = m_c->selection_info()->model_object();
|
||||
bool not_painted = std::all_of(mo->volumes.begin(), mo->volumes.end(), [](const ModelVolume* vol){
|
||||
return vol->type() != ModelVolumeType::MODEL_PART || vol->supported_facets.empty();
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include "slic3r/GUI/GLCanvas3D.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
|
||||
#include "slic3r/GUI/GUI_ObjectManipulation.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp"
|
||||
|
||||
#include "libslic3r/Geometry/ConvexHull.hpp"
|
||||
@ -38,6 +38,7 @@ bool GLGizmoFlatten::on_mouse(const wxMouseEvent &mouse_event)
|
||||
// Rotate the object so the normal points downward:
|
||||
selection.flattening_rotate(m_planes[m_hover_id].normal);
|
||||
m_parent.do_rotate(L("Gizmo-Place on Face"));
|
||||
wxGetApp().obj_manipul()->set_dirty();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -339,7 +339,7 @@ double GLGizmoMove3D::calc_projection(const UpdateData& data) const
|
||||
// use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
|
||||
// in our case plane normal and ray direction are the same (orthogonal view)
|
||||
// when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
|
||||
const Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir;
|
||||
const Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) * mouse_dir;
|
||||
// vector from the starting position to the found intersection
|
||||
const Vec3d inters_vec = inters - m_starting_drag_position;
|
||||
|
||||
|
@ -238,7 +238,13 @@ void GLGizmoRotate::init_data_from_selection(const Selection& selection)
|
||||
selection.get_bounding_box_in_reference_system(ECoordinatesType::Local) : selection.get_bounding_box_in_current_reference_system();
|
||||
m_bounding_box = box;
|
||||
m_center = box_trafo.translation();
|
||||
m_orient_matrix = box_trafo;
|
||||
m_orient_matrix = Geometry::translation_transform(m_center);
|
||||
if (!wxGetApp().obj_manipul()->is_world_coordinates()) {
|
||||
const GLVolume& v = *selection.get_first_volume();
|
||||
m_orient_matrix = m_orient_matrix * v.get_instance_transformation().get_rotation_matrix();
|
||||
if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_local_coordinates())
|
||||
m_orient_matrix = m_orient_matrix * v.get_volume_transformation().get_rotation_matrix();
|
||||
}
|
||||
|
||||
m_radius = Offset + m_bounding_box.radius();
|
||||
m_snap_coarse_in_radius = m_radius / 3.0f;
|
||||
|
@ -218,15 +218,7 @@ void GLGizmoScale3D::on_render()
|
||||
m_bounding_box = box;
|
||||
m_center = box_trafo.translation();
|
||||
m_grabbers_transform = box_trafo;
|
||||
m_instance_center = Vec3d::Zero();
|
||||
if (selection.is_single_full_instance() && !wxGetApp().obj_manipul()->is_world_coordinates())
|
||||
m_instance_center = selection.get_first_volume()->get_instance_offset();
|
||||
else if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_instance_coordinates())
|
||||
m_instance_center = m_center;
|
||||
else if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_local_coordinates())
|
||||
m_instance_center = m_center;
|
||||
else
|
||||
m_instance_center = selection.is_single_full_instance() ? selection.get_first_volume()->get_instance_offset() : m_center;
|
||||
m_instance_center = (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()) ? selection.get_first_volume()->get_instance_offset() : m_center;
|
||||
|
||||
// x axis
|
||||
const Vec3d box_half_size = 0.5 * m_bounding_box.size();
|
||||
@ -264,9 +256,8 @@ void GLGizmoScale3D::on_render()
|
||||
#endif // ENABLE_GL_CORE_PROFILE
|
||||
glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f));
|
||||
|
||||
const Transform3d base_matrix = local_transform(selection);
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
m_grabbers[i].matrix = base_matrix;
|
||||
m_grabbers[i].matrix = m_grabbers_transform;
|
||||
}
|
||||
|
||||
const float grabber_mean_size = (float)((m_bounding_box.size().x() + m_bounding_box.size().y() + m_bounding_box.size().z()) / 3.0);
|
||||
@ -281,7 +272,7 @@ void GLGizmoScale3D::on_render()
|
||||
if (shader != nullptr) {
|
||||
shader->start_using();
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * base_matrix);
|
||||
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_grabbers_transform);
|
||||
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
|
||||
#if ENABLE_GL_CORE_PROFILE
|
||||
const std::array<int, 4>& viewport = camera.get_viewport();
|
||||
@ -315,7 +306,7 @@ void GLGizmoScale3D::on_render()
|
||||
if (shader != nullptr) {
|
||||
shader->start_using();
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * base_matrix);
|
||||
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_grabbers_transform);
|
||||
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
|
||||
#if ENABLE_GL_CORE_PROFILE
|
||||
const std::array<int, 4>& viewport = camera.get_viewport();
|
||||
@ -332,8 +323,7 @@ void GLGizmoScale3D::on_render()
|
||||
if (shader != nullptr) {
|
||||
shader->start_using();
|
||||
shader->set_uniform("emission_factor", 0.1f);
|
||||
m_grabbers[0].render(true, grabber_mean_size);
|
||||
m_grabbers[1].render(true, grabber_mean_size);
|
||||
render_grabbers(0, 1, grabber_mean_size, true);
|
||||
shader->stop_using();
|
||||
}
|
||||
}
|
||||
@ -347,7 +337,7 @@ void GLGizmoScale3D::on_render()
|
||||
if (shader != nullptr) {
|
||||
shader->start_using();
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * base_matrix);
|
||||
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_grabbers_transform);
|
||||
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
|
||||
#if ENABLE_GL_CORE_PROFILE
|
||||
const std::array<int, 4>& viewport = camera.get_viewport();
|
||||
@ -364,8 +354,7 @@ void GLGizmoScale3D::on_render()
|
||||
if (shader != nullptr) {
|
||||
shader->start_using();
|
||||
shader->set_uniform("emission_factor", 0.1f);
|
||||
m_grabbers[2].render(true, grabber_mean_size);
|
||||
m_grabbers[3].render(true, grabber_mean_size);
|
||||
render_grabbers(2, 3, grabber_mean_size, true);
|
||||
shader->stop_using();
|
||||
}
|
||||
}
|
||||
@ -379,7 +368,7 @@ void GLGizmoScale3D::on_render()
|
||||
if (shader != nullptr) {
|
||||
shader->start_using();
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * base_matrix);
|
||||
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_grabbers_transform);
|
||||
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
|
||||
#if ENABLE_GL_CORE_PROFILE
|
||||
const std::array<int, 4>& viewport = camera.get_viewport();
|
||||
@ -396,8 +385,7 @@ void GLGizmoScale3D::on_render()
|
||||
if (shader != nullptr) {
|
||||
shader->start_using();
|
||||
shader->set_uniform("emission_factor", 0.1f);
|
||||
m_grabbers[4].render(true, grabber_mean_size);
|
||||
m_grabbers[5].render(true, grabber_mean_size);
|
||||
render_grabbers(4, 5, grabber_mean_size, true);
|
||||
shader->stop_using();
|
||||
}
|
||||
}
|
||||
@ -411,7 +399,7 @@ void GLGizmoScale3D::on_render()
|
||||
if (shader != nullptr) {
|
||||
shader->start_using();
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * base_matrix);
|
||||
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_grabbers_transform);
|
||||
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
|
||||
#if ENABLE_GL_CORE_PROFILE
|
||||
const std::array<int, 4>& viewport = camera.get_viewport();
|
||||
@ -431,9 +419,7 @@ void GLGizmoScale3D::on_render()
|
||||
if (shader != nullptr) {
|
||||
shader->start_using();
|
||||
shader->set_uniform("emission_factor", 0.1f);
|
||||
for (int i = 6; i < 10; ++i) {
|
||||
m_grabbers[i].render(true, grabber_mean_size);
|
||||
}
|
||||
render_grabbers(6, 9, grabber_mean_size, true);
|
||||
shader->stop_using();
|
||||
}
|
||||
}
|
||||
@ -757,7 +743,7 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data)
|
||||
double ratio = calc_ratio(data);
|
||||
if (ratio > 0.0) {
|
||||
Vec3d curr_scale = m_scale;
|
||||
Vec3d starting_scale = m_starting.scale;
|
||||
const Vec3d starting_scale = m_starting.scale;
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
const ECoordinatesType coordinates_type = wxGetApp().obj_manipul()->get_coordinates_type();
|
||||
|
||||
@ -770,13 +756,6 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data)
|
||||
if (m_hover_id == 2 * axis)
|
||||
local_offset *= -1.0;
|
||||
|
||||
Vec3d center_offset = m_starting.instance_center - m_starting.center; // world coordinates (== Vec3d::Zero() for single volume selection)
|
||||
if (selection.is_single_full_instance() && coordinates_type == ECoordinatesType::Local)
|
||||
// from world coordinates to instance coordinates
|
||||
center_offset = selection.get_first_volume()->get_instance_transformation().get_rotation_matrix().inverse() * center_offset;
|
||||
|
||||
local_offset += (ratio - 1.0) * center_offset(axis);
|
||||
|
||||
switch (axis)
|
||||
{
|
||||
case X: { m_offset = local_offset * Vec3d::UnitX(); break; }
|
||||
@ -785,10 +764,6 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data)
|
||||
default: { m_offset = Vec3d::Zero(); break; }
|
||||
}
|
||||
|
||||
if (selection.is_single_full_instance() && coordinates_type == ECoordinatesType::Local)
|
||||
// from instance coordinates to world coordinates
|
||||
m_offset = selection.get_first_volume()->get_instance_transformation().get_rotation_matrix() * m_offset;
|
||||
|
||||
if (selection.is_single_volume_or_modifier()) {
|
||||
if (coordinates_type == ECoordinatesType::Instance)
|
||||
m_offset = selection.get_first_volume()->get_instance_transformation().get_scaling_factor_matrix().inverse() * m_offset;
|
||||
@ -849,18 +824,6 @@ void GLGizmoScale3D::do_scale_uniform(const UpdateData & data)
|
||||
if (m_hover_id == 6 || m_hover_id == 7)
|
||||
m_offset.y() *= -1.0;
|
||||
|
||||
Vec3d center_offset = m_starting.instance_center - m_starting.center; // world coordinates (== Vec3d::Zero() for single volume selection)
|
||||
|
||||
if (selection.is_single_full_instance() && coordinates_type == ECoordinatesType::Local)
|
||||
// from world coordinates to instance coordinates
|
||||
center_offset = selection.get_first_volume()->get_instance_transformation().get_rotation_matrix().inverse() * center_offset;
|
||||
|
||||
m_offset += (ratio - 1.0) * center_offset;
|
||||
|
||||
if (selection.is_single_full_instance() && coordinates_type == ECoordinatesType::Local)
|
||||
// from instance coordinates to world coordinates
|
||||
m_offset = selection.get_first_volume()->get_instance_transformation().get_rotation_matrix() * m_offset;
|
||||
|
||||
if (selection.is_single_volume_or_modifier()) {
|
||||
if (coordinates_type == ECoordinatesType::Instance)
|
||||
m_offset = selection.get_first_volume()->get_instance_transformation().get_scaling_factor_matrix().inverse() * m_offset;
|
||||
@ -904,7 +867,7 @@ double GLGizmoScale3D::calc_ratio(const UpdateData& data) const
|
||||
// use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
|
||||
// in our case plane normal and ray direction are the same (orthogonal view)
|
||||
// when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
|
||||
const Vec3d inters = data.mouse_ray.a + (m_starting.drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir;
|
||||
const Vec3d inters = data.mouse_ray.a + (m_starting.drag_position - data.mouse_ray.a).dot(mouse_dir) * mouse_dir;
|
||||
// vector from the starting position to the found intersection
|
||||
const Vec3d inters_vec = inters - m_starting.drag_position;
|
||||
|
||||
@ -920,20 +883,5 @@ double GLGizmoScale3D::calc_ratio(const UpdateData& data) const
|
||||
return ratio;
|
||||
}
|
||||
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
Transform3d GLGizmoScale3D::local_transform(const Selection& selection) const
|
||||
{
|
||||
Transform3d ret = Geometry::translation_transform(m_center);
|
||||
if (!wxGetApp().obj_manipul()->is_world_coordinates()) {
|
||||
const GLVolume& v = *selection.get_first_volume();
|
||||
Transform3d orient_matrix = v.get_instance_transformation().get_rotation_matrix();
|
||||
if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_local_coordinates())
|
||||
orient_matrix = orient_matrix * v.get_volume_transformation().get_rotation_matrix();
|
||||
ret = ret * orient_matrix;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
@ -102,9 +102,6 @@ private:
|
||||
void do_scale_uniform(const UpdateData& data);
|
||||
|
||||
double calc_ratio(const UpdateData& data) const;
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
Transform3d local_transform(const Selection& selection) const;
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
};
|
||||
|
||||
|
||||
|
@ -453,10 +453,17 @@ void ImGuiWrapper::end()
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::button(const wxString &label)
|
||||
bool ImGuiWrapper::button(const wxString &label, const wxString& tooltip)
|
||||
{
|
||||
auto label_utf8 = into_u8(label);
|
||||
return ImGui::Button(label_utf8.c_str());
|
||||
const bool ret = ImGui::Button(label_utf8.c_str());
|
||||
|
||||
if (!tooltip.IsEmpty() && ImGui::IsItemHovered()) {
|
||||
auto tooltip_utf8 = into_u8(tooltip);
|
||||
ImGui::SetTooltip(tooltip_utf8.c_str());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::button(const wxString& label, float width, float height)
|
||||
|
@ -91,7 +91,7 @@ public:
|
||||
bool begin(const wxString& name, bool* close, int flags = 0);
|
||||
void end();
|
||||
|
||||
bool button(const wxString &label);
|
||||
bool button(const wxString &label, const wxString& tooltip = {});
|
||||
bool button(const wxString& label, float width, float height);
|
||||
bool button(const wxString& label, const ImVec2 &size, bool enable); // default size = ImVec2(0.f, 0.f)
|
||||
bool radio_button(const wxString &label, bool active);
|
||||
|
@ -1038,25 +1038,50 @@ void ogStaticText::SetText(const wxString& value, bool wrap/* = true*/)
|
||||
|
||||
void ogStaticText::SetPathEnd(const std::string& link)
|
||||
{
|
||||
#ifndef __linux__
|
||||
|
||||
Bind(wxEVT_ENTER_WINDOW, [this, link](wxMouseEvent& event) {
|
||||
SetToolTip(OptionsGroup::get_url(get_app_config()->get("suppress_hyperlinks") != "1" ? link : std::string()));
|
||||
FocusText(true);
|
||||
event.Skip();
|
||||
});
|
||||
Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& event) { FocusText(false); event.Skip(); });
|
||||
|
||||
Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent& event) {
|
||||
if (HasCapture())
|
||||
return;
|
||||
this->CaptureMouse();
|
||||
event.Skip();
|
||||
} );
|
||||
});
|
||||
Bind(wxEVT_LEFT_UP, [link, this](wxMouseEvent& event) {
|
||||
if (!HasCapture())
|
||||
return;
|
||||
ReleaseMouse();
|
||||
OptionsGroup::launch_browser(link);
|
||||
event.Skip();
|
||||
} );
|
||||
Bind(wxEVT_ENTER_WINDOW, [this, link](wxMouseEvent& event) {
|
||||
SetToolTip(OptionsGroup::get_url(!get_app_config()->get_bool("suppress_hyperlinks") ? link : std::string()));
|
||||
FocusText(true);
|
||||
event.Skip();
|
||||
});
|
||||
Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& event) { FocusText(false); event.Skip(); });
|
||||
|
||||
#else
|
||||
|
||||
// Workaround: On Linux wxStaticText doesn't receive wxEVT_ENTER(LEAVE)_WINDOW events,
|
||||
// so implement this behaviour trough wxEVT_MOTION events for this control and it's parent
|
||||
Bind(wxEVT_MOTION, [link, this](wxMouseEvent& event) {
|
||||
SetToolTip(OptionsGroup::get_url(!get_app_config()->get_bool("suppress_hyperlinks") ? link : std::string()));
|
||||
FocusText(true);
|
||||
event.Skip();
|
||||
});
|
||||
GetParent()->Bind(wxEVT_MOTION, [this](wxMouseEvent& event) {
|
||||
FocusText(false);
|
||||
event.Skip();
|
||||
});
|
||||
|
||||
// On Linux a mouse capturing causes a totally application freeze
|
||||
Bind(wxEVT_LEFT_UP, [link, this](wxMouseEvent& event) {
|
||||
OptionsGroup::launch_browser(link);
|
||||
event.Skip();
|
||||
});
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void ogStaticText::FocusText(bool focus)
|
||||
@ -1065,7 +1090,10 @@ void ogStaticText::FocusText(bool focus)
|
||||
return;
|
||||
|
||||
SetFont(focus ? Slic3r::GUI::wxGetApp().link_font() :
|
||||
Slic3r::GUI::wxGetApp().normal_font());
|
||||
Slic3r::GUI::wxGetApp().normal_font());
|
||||
#ifdef __linux__
|
||||
this->GetContainingSizer()->Layout();
|
||||
#endif
|
||||
Refresh();
|
||||
}
|
||||
|
||||
|
@ -462,7 +462,7 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
|
||||
choice->set_selection();
|
||||
}
|
||||
|
||||
update();
|
||||
update(true);
|
||||
}
|
||||
|
||||
void PhysicalPrinterDialog::update_printhost_buttons()
|
||||
@ -632,11 +632,12 @@ void PhysicalPrinterDialog::update_host_type(bool printer_change)
|
||||
|
||||
Choice* choice = dynamic_cast<Choice*>(ht);
|
||||
choice->set_values(types);
|
||||
int index_in_choice = (printer_change ? 0 : last_in_conf);
|
||||
int dif = (int)ht->m_opt.enum_def->values().size() - (int)types.size();
|
||||
int index_in_choice = (printer_change ? std::clamp(last_in_conf - ((int)ht->m_opt.enum_def->values().size() - (int)types.size()), 0, (int)ht->m_opt.enum_def->values().size() - 1) : last_in_conf);
|
||||
choice->set_value(index_in_choice);
|
||||
if (link.supported && link.label == _(ht->m_opt.enum_def->label(index_in_choice)))
|
||||
m_config->set_key_value("host_type", new ConfigOptionEnum<PrintHostType>(htPrusaLink));
|
||||
else if (link.supported && link.label == _(ht->m_opt.enum_def->label(index_in_choice)))
|
||||
else if (connect.supported && connect.label == _(ht->m_opt.enum_def->label(index_in_choice)))
|
||||
m_config->set_key_value("host_type", new ConfigOptionEnum<PrintHostType>(htPrusaConnect));
|
||||
else {
|
||||
int host_type = std::clamp(index_in_choice + ((int)ht->m_opt.enum_def->values().size() - (int)types.size()), 0, (int)ht->m_opt.enum_def->values().size() - 1);
|
||||
|
@ -836,108 +836,57 @@ const std::pair<BoundingBoxf3, Transform3d>& Selection::get_bounding_box_in_curr
|
||||
|
||||
std::pair<BoundingBoxf3, Transform3d> Selection::get_bounding_box_in_reference_system(ECoordinatesType type) const
|
||||
{
|
||||
BoundingBoxf3 original_box;
|
||||
//
|
||||
// trafo to current reference system
|
||||
//
|
||||
Transform3d trafo;
|
||||
|
||||
//
|
||||
// calculate box aligned to current reference system
|
||||
//
|
||||
switch (type)
|
||||
{
|
||||
case ECoordinatesType::World:
|
||||
{
|
||||
original_box = get_bounding_box();
|
||||
trafo = Transform3d::Identity();
|
||||
break;
|
||||
}
|
||||
case ECoordinatesType::Instance: {
|
||||
for (unsigned int id : m_list) {
|
||||
const GLVolume& v = *get_volume(id);
|
||||
original_box.merge(v.transformed_convex_hull_bounding_box(v.get_volume_transformation().get_matrix()));
|
||||
}
|
||||
trafo = get_first_volume()->get_instance_transformation().get_matrix();
|
||||
break;
|
||||
}
|
||||
case ECoordinatesType::Local: {
|
||||
assert(is_single_volume_or_modifier() || is_single_volume_instance());
|
||||
const GLVolume& v = *get_first_volume();
|
||||
original_box = v.bounding_box();
|
||||
trafo = v.world_matrix();
|
||||
break;
|
||||
}
|
||||
case ECoordinatesType::World: { trafo = Transform3d::Identity(); break; }
|
||||
case ECoordinatesType::Instance: { trafo = get_first_volume()->get_instance_transformation().get_matrix(); break; }
|
||||
case ECoordinatesType::Local: { trafo = get_first_volume()->world_matrix(); break; }
|
||||
}
|
||||
|
||||
//
|
||||
// calculate box size in world coordinates
|
||||
// trafo basis in world coordinates
|
||||
//
|
||||
auto point_to_Vec4d = [](const Vec3d& p) { return Vec4d(p.x(), p.y(), p.z(), 1.0); };
|
||||
auto Vec4d_to_Vec3d = [](const Vec4d& v) { return Vec3d(v.x(), v.y(), v.z()); };
|
||||
Geometry::Transformation t(trafo);
|
||||
t.reset_scaling_factor();
|
||||
const Transform3d basis_trafo = t.get_matrix_no_offset();
|
||||
std::vector<Vec3d> axes = { Vec3d::UnitX(), Vec3d::UnitY(), Vec3d::UnitZ() };
|
||||
for (size_t i = 0; i < axes.size(); ++i) {
|
||||
axes[i] = basis_trafo * axes[i];
|
||||
}
|
||||
|
||||
auto apply_transform = [](const std::vector<Vec4d>& original, const Transform3d& trafo, bool normalize) {
|
||||
std::vector<Vec4d> transformed(original.size());
|
||||
for (size_t i = 0; i < original.size(); ++i) {
|
||||
transformed[i] = trafo * original[i];
|
||||
if (normalize)
|
||||
transformed[i].normalize();
|
||||
}
|
||||
return transformed;
|
||||
};
|
||||
|
||||
auto calc_box_size = [point_to_Vec4d, Vec4d_to_Vec3d, apply_transform](const BoundingBoxf3& box, const Transform3d& trafo) {
|
||||
Geometry::Transformation transformation(trafo);
|
||||
|
||||
// box aligned to current reference system
|
||||
std::vector<Vec4d> homo_vertices = {
|
||||
point_to_Vec4d({ box.min.x(), box.min.y(), box.min.z() }),
|
||||
point_to_Vec4d({ box.max.x(), box.min.y(), box.min.z() }),
|
||||
point_to_Vec4d({ box.max.x(), box.max.y(), box.min.z() }),
|
||||
point_to_Vec4d({ box.min.x(), box.max.y(), box.min.z() }),
|
||||
point_to_Vec4d({ box.min.x(), box.min.y(), box.max.z() }),
|
||||
point_to_Vec4d({ box.max.x(), box.min.y(), box.max.z() }),
|
||||
point_to_Vec4d({ box.max.x(), box.max.y(), box.max.z() }),
|
||||
point_to_Vec4d({ box.min.x(), box.max.y(), box.max.z() })
|
||||
};
|
||||
|
||||
// box vertices in world coordinates
|
||||
std::vector<Vec4d> transformed_homo_vertices = apply_transform(homo_vertices, trafo, false);
|
||||
|
||||
// project back to current reference system
|
||||
const std::vector<Vec4d> homo_axes = { Vec4d::UnitX(), Vec4d::UnitY(), Vec4d::UnitZ() };
|
||||
std::vector<Vec4d> transformed_homo_axes = apply_transform(homo_axes, Geometry::Transformation(trafo).get_matrix_no_scaling_factor(), true);
|
||||
std::vector<Vec3d> transformed_axes(transformed_homo_axes.size());
|
||||
for (size_t i = 0; i < transformed_homo_axes.size(); ++i) {
|
||||
transformed_axes[i] = Vec4d_to_Vec3d(transformed_homo_axes[i]);
|
||||
}
|
||||
|
||||
Vec3d min = { DBL_MAX, DBL_MAX, DBL_MAX };
|
||||
Vec3d max = { -DBL_MAX, -DBL_MAX, -DBL_MAX };
|
||||
|
||||
for (const Vec4d& v_homo : transformed_homo_vertices) {
|
||||
const Vec3d v = Vec4d_to_Vec3d(v_homo);
|
||||
//
|
||||
// calculate bounding box aligned to trafo basis
|
||||
//
|
||||
Vec3d min = { DBL_MAX, DBL_MAX, DBL_MAX };
|
||||
Vec3d max = { -DBL_MAX, -DBL_MAX, -DBL_MAX };
|
||||
for (unsigned int id : m_list) {
|
||||
const GLVolume& vol = *get_volume(id);
|
||||
const Transform3d vol_world_rafo = vol.world_matrix();
|
||||
const TriangleMesh* mesh = vol.convex_hull();
|
||||
if (mesh == nullptr)
|
||||
mesh = &m_model->objects[vol.object_idx()]->volumes[vol.volume_idx()]->mesh();
|
||||
assert(mesh != nullptr);
|
||||
for (const stl_vertex& v : mesh->its.vertices) {
|
||||
const Vec3d world_v = vol_world_rafo * v.cast<double>();
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
const double dot_i = v.dot(transformed_axes[i]);
|
||||
min(i) = std::min(min(i), dot_i);
|
||||
max(i) = std::max(max(i), dot_i);
|
||||
const double i_comp = world_v.dot(axes[i]);
|
||||
min(i) = std::min(min(i), i_comp);
|
||||
max(i) = std::max(max(i), i_comp);
|
||||
}
|
||||
}
|
||||
|
||||
// return size
|
||||
const Vec3d size = max - min;
|
||||
return size;
|
||||
};
|
||||
|
||||
const Vec3d box_size = calc_box_size(original_box, trafo);
|
||||
const std::vector<Vec4d> box_center = { point_to_Vec4d(original_box.center()) };
|
||||
std::vector<Vec4d> transformed_box_center = apply_transform(box_center, trafo, false);
|
||||
|
||||
//
|
||||
// return box centered at 0, 0, 0
|
||||
//
|
||||
}
|
||||
const Vec3d box_size = max - min;
|
||||
const Vec3d half_box_size = 0.5 * box_size;
|
||||
BoundingBoxf3 out_box(-half_box_size, half_box_size);
|
||||
Geometry::Transformation out_trafo(trafo);
|
||||
out_trafo.set_offset(Vec4d_to_Vec3d(transformed_box_center[0]));
|
||||
const Vec3d center = 0.5 * (min + max);
|
||||
out_trafo.set_offset(basis_trafo * center);
|
||||
return { out_box, out_trafo.get_matrix_no_scaling_factor() };
|
||||
|
||||
}
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
|
||||
@ -1049,7 +998,7 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_
|
||||
|
||||
assert(transformation_type.relative() || (transformation_type.absolute() && transformation_type.local()));
|
||||
|
||||
const Transform3d rotation_matrix = Geometry::rotation_transform(rotation);
|
||||
Transform3d rotation_matrix = Geometry::rotation_transform(rotation);
|
||||
|
||||
for (unsigned int i : m_list) {
|
||||
GLVolume& v = *(*m_volumes)[i];
|
||||
@ -1074,15 +1023,41 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_
|
||||
transform_volume_relative(v, volume_data, transformation_type, rotation_matrix, m_cache.dragging_center);
|
||||
}
|
||||
else {
|
||||
if (transformation_type.local() && transformation_type.absolute()) {
|
||||
if (transformation_type.instance()) {
|
||||
const Geometry::Transformation& vol_trafo = volume_data.get_volume_transform();
|
||||
Matrix3d vol_rotation, vol_scale;
|
||||
vol_trafo.get_matrix().computeRotationScaling(&vol_rotation, &vol_scale);
|
||||
const Transform3d trafo = vol_trafo.get_rotation_matrix() * rotation_matrix;
|
||||
v.set_volume_transformation(vol_trafo.get_offset_matrix() * trafo * Transform3d(vol_scale));
|
||||
const Geometry::Transformation world_trafo = inst_trafo * vol_trafo;
|
||||
// ensure proper sign of rotation for mirrored objects
|
||||
if (world_trafo.is_left_handed() && !rotation.normalized().isApprox(Vec3d::UnitX()))
|
||||
rotation_matrix = rotation_matrix.inverse();
|
||||
|
||||
// ensure that the volume rotates as a rigid body
|
||||
const Geometry::TransformationSVD world_svd(world_trafo);
|
||||
if (world_svd.anisotropic_scale) {
|
||||
const Transform3d vol_scale_matrix = vol_trafo.get_scaling_factor_matrix();
|
||||
rotation_matrix = vol_scale_matrix.inverse() * rotation_matrix * vol_scale_matrix;
|
||||
}
|
||||
const Transform3d vol_rotation_matrix = vol_trafo.get_rotation_matrix();
|
||||
rotation_matrix = vol_rotation_matrix.inverse() * rotation_matrix * vol_rotation_matrix;
|
||||
|
||||
v.set_volume_transformation(vol_trafo.get_matrix() * rotation_matrix);
|
||||
}
|
||||
else
|
||||
else {
|
||||
if (transformation_type.local()) {
|
||||
const Geometry::Transformation& vol_trafo = volume_data.get_volume_transform();
|
||||
const Geometry::Transformation world_trafo = inst_trafo * vol_trafo;
|
||||
// ensure proper sign of rotation for mirrored objects
|
||||
if (world_trafo.is_left_handed() && !rotation.normalized().isApprox(Vec3d::UnitX()))
|
||||
rotation_matrix = rotation_matrix.inverse();
|
||||
|
||||
// ensure that the volume rotates as a rigid body
|
||||
const Geometry::TransformationSVD svd(world_trafo);
|
||||
if (svd.anisotropic_scale) {
|
||||
const Transform3d vol_scale_matrix = vol_trafo.get_scaling_factor_matrix();
|
||||
rotation_matrix = vol_scale_matrix.inverse() * rotation_matrix * vol_scale_matrix;
|
||||
}
|
||||
}
|
||||
transform_volume_relative(v, volume_data, transformation_type, rotation_matrix, m_cache.dragging_center);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1091,14 +1066,7 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_
|
||||
if (m_mode == Instance) {
|
||||
int rot_axis_max = 0;
|
||||
rotation.cwiseAbs().maxCoeff(&rot_axis_max);
|
||||
SyncRotationType synch;
|
||||
if (transformation_type.world() && rot_axis_max == 2)
|
||||
synch = SyncRotationType::NONE;
|
||||
else if (transformation_type.instance())
|
||||
synch = SyncRotationType::FULL;
|
||||
else
|
||||
synch = SyncRotationType::GENERAL;
|
||||
synchronize_unselected_instances(synch);
|
||||
synchronize_unselected_instances((transformation_type.world() && rot_axis_max == 2) ? SyncRotationType::NONE : SyncRotationType::GENERAL);
|
||||
}
|
||||
else if (m_mode == Volume)
|
||||
synchronize_unselected_volumes();
|
||||
@ -1466,31 +1434,29 @@ void Selection::scale_and_translate(const Vec3d& scale, const Vec3d& translation
|
||||
if (!m_valid)
|
||||
return;
|
||||
|
||||
Vec3d relative_scale = scale;
|
||||
if (transformation_type.absolute()) {
|
||||
// converts to relative scale
|
||||
if (m_mode == Instance) {
|
||||
if (is_single_full_instance()) {
|
||||
BoundingBoxf3 current_box = get_bounding_box_in_current_reference_system().first;
|
||||
BoundingBoxf3 original_box;
|
||||
if (transformation_type.world())
|
||||
original_box = get_full_unscaled_instance_bounding_box();
|
||||
else
|
||||
original_box = get_full_unscaled_instance_local_bounding_box();
|
||||
|
||||
relative_scale = original_box.size().cwiseProduct(scale).cwiseQuotient(current_box.size());
|
||||
}
|
||||
}
|
||||
transformation_type.set_relative();
|
||||
}
|
||||
|
||||
for (unsigned int i : m_list) {
|
||||
GLVolume& v = *(*m_volumes)[i];
|
||||
const VolumeCache& volume_data = m_cache.volumes_data[i];
|
||||
const Geometry::Transformation& inst_trafo = volume_data.get_instance_transform();
|
||||
|
||||
Vec3d relative_scale = scale;
|
||||
|
||||
if (transformation_type.absolute()) {
|
||||
if (m_mode == Instance) {
|
||||
if (is_single_full_instance()) {
|
||||
BoundingBoxf3 current_box = m_box.get_bounding_box();
|
||||
BoundingBoxf3 original_box;
|
||||
if (transformation_type.world())
|
||||
original_box = get_full_unscaled_instance_bounding_box();
|
||||
else
|
||||
original_box = get_full_unscaled_instance_local_bounding_box();
|
||||
|
||||
relative_scale = original_box.size().cwiseProduct(scale).cwiseQuotient(current_box.size());
|
||||
transformation_type.set_relative();
|
||||
}
|
||||
}
|
||||
else {
|
||||
}
|
||||
}
|
||||
|
||||
if (m_mode == Instance) {
|
||||
if (transformation_type.instance()) {
|
||||
const Vec3d world_inst_pivot = m_cache.dragging_center - inst_trafo.get_offset();
|
||||
@ -1529,7 +1495,9 @@ void Selection::scale_and_translate(const Vec3d& scale, const Vec3d& translation
|
||||
|
||||
#if !DISABLE_INSTANCES_SYNCH
|
||||
if (m_mode == Instance)
|
||||
synchronize_unselected_instances(SyncRotationType::NONE);
|
||||
// even if there is no rotation, we pass SyncRotationType::GENERAL to force
|
||||
// synchronize_unselected_instances() to apply the scale to the other instances
|
||||
synchronize_unselected_instances(SyncRotationType::GENERAL);
|
||||
else if (m_mode == Volume)
|
||||
synchronize_unselected_volumes();
|
||||
#endif // !DISABLE_INSTANCES_SYNCH
|
||||
@ -2196,7 +2164,6 @@ void Selection::update_type()
|
||||
unsigned int volumes_count = (unsigned int)model_object->volumes.size();
|
||||
unsigned int instances_count = (unsigned int)model_object->instances.size();
|
||||
if (volumes_count * instances_count == 1) {
|
||||
const ModelVolume* model_volume = model_object->volumes[first->volume_idx()];
|
||||
m_type = SingleFullObject;
|
||||
// ensures the correct mode is selected
|
||||
m_mode = Instance;
|
||||
@ -2834,7 +2801,7 @@ void Selection::render_debug_window() const
|
||||
}
|
||||
|
||||
static int current_method_idx = 0;
|
||||
ImGui::Combo("Decomposition method", ¤t_method_idx, "computeRotationScaling\0computeScalingRotation\0");
|
||||
ImGui::Combo("Decomposition method", ¤t_method_idx, "computeRotationScaling\0computeScalingRotation\0SVD\0");
|
||||
|
||||
const GLVolume& v = *get_volume(current_vol_idx);
|
||||
|
||||
@ -2854,23 +2821,56 @@ void Selection::render_debug_window() const
|
||||
ImGui::EndGroup();
|
||||
};
|
||||
|
||||
auto add_matrices_set = [add_matrix](const std::string& name, const Transform3d& m, size_t method) {
|
||||
auto add_matrices_set = [&imgui, add_matrix](const std::string& name, const Transform3d& m, size_t method) {
|
||||
static unsigned int counter = 0;
|
||||
++counter;
|
||||
if (ImGui::CollapsingHeader(name.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
add_matrix("Full", m, 4);
|
||||
|
||||
Matrix3d rotation;
|
||||
Matrix3d scale;
|
||||
if (method == 0)
|
||||
m.computeRotationScaling(&rotation, &scale);
|
||||
else
|
||||
m.computeScalingRotation(&scale, &rotation);
|
||||
if (method == 0 || method == 1) {
|
||||
Matrix3d rotation;
|
||||
Matrix3d scale;
|
||||
if (method == 0)
|
||||
m.computeRotationScaling(&rotation, &scale);
|
||||
else
|
||||
m.computeScalingRotation(&scale, &rotation);
|
||||
|
||||
ImGui::SameLine();
|
||||
add_matrix("Rotation component", Transform3d(rotation), 3);
|
||||
ImGui::SameLine();
|
||||
add_matrix("Scale component", Transform3d(scale), 3);
|
||||
ImGui::SameLine();
|
||||
add_matrix("Rotation component", Transform3d(rotation), 3);
|
||||
ImGui::SameLine();
|
||||
add_matrix("Scale component", Transform3d(scale), 3);
|
||||
}
|
||||
else {
|
||||
const Geometry::TransformationSVD svd(m);
|
||||
|
||||
ImGui::SameLine();
|
||||
add_matrix("U", Transform3d(svd.u), 3);
|
||||
ImGui::SameLine();
|
||||
add_matrix("S", Transform3d(svd.s), 3);
|
||||
ImGui::SameLine();
|
||||
add_matrix("V", Transform3d(svd.v), 3);
|
||||
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
||||
float spacing = 0.0f;
|
||||
if (svd.rotation) {
|
||||
ImGui::SameLine(0.0f, spacing);
|
||||
imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, svd.rotation_90_degrees ? "Rotation 90 degs" : "Rotation");
|
||||
spacing = 10.0f;
|
||||
}
|
||||
if (svd.scale) {
|
||||
ImGui::SameLine(0.0f, spacing);
|
||||
imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, svd.anisotropic_scale ? "Anisotropic scale" : "Isotropic scale");
|
||||
spacing = 10.0f;
|
||||
}
|
||||
if (svd.mirror) {
|
||||
ImGui::SameLine(0.0f, spacing);
|
||||
imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Mirror");
|
||||
spacing = 10.0f;
|
||||
}
|
||||
if (svd.skew) {
|
||||
ImGui::SameLine(0.0f, spacing);
|
||||
imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Skew");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -2968,10 +2968,10 @@ static void verify_instances_rotation_synchronized(const Model &model, const GLV
|
||||
assert(idx_volume_first != -1); // object without instances?
|
||||
if (idx_volume_first == -1)
|
||||
continue;
|
||||
const Transform3d::ConstLinearPart &rotation0 = volumes[idx_volume_first]->get_instance_transformation().get_matrix().linear();
|
||||
const Transform3d::ConstLinearPart& rotation0 = volumes[idx_volume_first]->get_instance_transformation().get_matrix().linear();
|
||||
for (int i = idx_volume_first + 1; i < (int)volumes.size(); ++i)
|
||||
if (volumes[i]->object_idx() == idx_object) {
|
||||
const Transform3d::ConstLinearPart &rotation = volumes[i]->get_instance_transformation().get_matrix().linear();
|
||||
const Transform3d::ConstLinearPart& rotation = volumes[i]->get_instance_transformation().get_matrix().linear();
|
||||
assert(is_rotation_xy_synchronized(rotation, rotation0));
|
||||
}
|
||||
}
|
||||
@ -2994,7 +2994,6 @@ void Selection::synchronize_unselected_instances(SyncRotationType sync_rotation_
|
||||
const int object_idx = volume_i->object_idx();
|
||||
const int instance_idx = volume_i->instance_idx();
|
||||
const Transform3d& curr_inst_trafo_i = volume_i->get_instance_transformation().get_matrix();
|
||||
const bool curr_inst_left_handed = is_left_handed(curr_inst_trafo_i);
|
||||
const Transform3d& old_inst_trafo_i = m_cache.volumes_data[i].get_instance_transform().get_matrix();
|
||||
bool mirrored = is_left_handed(curr_inst_trafo_i) != is_left_handed(old_inst_trafo_i);
|
||||
// bool mirrored = curr_inst_trafo_i.linear().determinant() * old_inst_trafo_i.linear().determinant() < 0;
|
||||
|
@ -499,10 +499,6 @@ public:
|
||||
NONE = 0,
|
||||
// Synchronize after rotation by an axis not parallel with Z.
|
||||
GENERAL = 1,
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
// Fully synchronize rotation.
|
||||
FULL = 2,
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
};
|
||||
void synchronize_unselected_instances(SyncRotationType sync_rotation_type);
|
||||
void synchronize_unselected_volumes();
|
||||
|
@ -1571,7 +1571,10 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("first_layer_speed_over_raft");
|
||||
|
||||
optgroup = page->new_optgroup(L("Acceleration control (advanced)"));
|
||||
optgroup->append_single_option_line("external_perimeter_acceleration");
|
||||
optgroup->append_single_option_line("perimeter_acceleration");
|
||||
optgroup->append_single_option_line("top_solid_infill_acceleration");
|
||||
optgroup->append_single_option_line("solid_infill_acceleration");
|
||||
optgroup->append_single_option_line("infill_acceleration");
|
||||
optgroup->append_single_option_line("bridge_acceleration");
|
||||
optgroup->append_single_option_line("first_layer_acceleration");
|
||||
@ -1638,9 +1641,6 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("xy_size_compensation");
|
||||
optgroup->append_single_option_line("elefant_foot_compensation", "elephant-foot-compensation_114487");
|
||||
|
||||
optgroup = page->new_optgroup(L("Other"));
|
||||
optgroup->append_single_option_line("clip_multipart_objects");
|
||||
|
||||
optgroup = page->new_optgroup(L("Arachne perimeter generator"));
|
||||
optgroup->append_single_option_line("wall_transition_angle");
|
||||
optgroup->append_single_option_line("wall_transition_filter_deviation");
|
||||
@ -1729,9 +1729,7 @@ void TabPrint::update_description_lines()
|
||||
if (m_post_process_explanation) {
|
||||
m_post_process_explanation->SetText(
|
||||
_L("Post processing scripts shall modify G-code file in place."));
|
||||
#ifndef __linux__
|
||||
m_post_process_explanation->SetPathEnd("post-processing-scripts_283913");
|
||||
#endif // __linux__
|
||||
}
|
||||
// upadte G-code substitutions from the current configuration
|
||||
{
|
||||
|
@ -734,11 +734,14 @@ bool PrusaLink::get_storage(wxArrayString& output) const
|
||||
const auto path = section.second.get_optional<std::string>("path");
|
||||
const auto space = section.second.get_optional<std::string>("free_space");
|
||||
const auto read_only = section.second.get_optional<bool>("read_only");
|
||||
const auto ro = section.second.get_optional<bool>("ro"); // In PrusaLink 0.7.0RC2 "read_only" value is stored under "ro".
|
||||
const auto available = section.second.get_optional<bool>("available");
|
||||
if (path && (!available || *available)) {
|
||||
StorageInfo si;
|
||||
si.name = boost::nowide::widen(*path);
|
||||
si.read_only = read_only ? *read_only : false; // If read_only is missing, assume it is NOT read only.
|
||||
// If read_only is missing, assume it is NOT read only.
|
||||
// si.read_only = read_only ? *read_only : false; // version without "ro"
|
||||
si.read_only = (read_only ? *read_only : (ro ? *ro : false));
|
||||
si.free_space = space ? std::stoll(*space) : 1; // If free_space is missing, assume there is free space.
|
||||
storage.emplace_back(std::move(si));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user