SPE-1560 - Scale to fit command modified to avoid having the top layer exceeding the print volume max height
This commit is contained in:
parent
1873252a30
commit
d99364d74d
3 changed files with 75 additions and 20 deletions
|
@ -293,6 +293,20 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas)
|
||||||
return { w - thickness_bar_width(canvas), 0.0f, w, h };
|
return { w - thickness_bar_width(canvas), 0.0f, w, h };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<SlicingParameters, const std::vector<double>> GLCanvas3D::LayersEditing::get_layers_height_data()
|
||||||
|
{
|
||||||
|
if (m_slicing_parameters != nullptr)
|
||||||
|
return { *m_slicing_parameters, m_layer_height_profile };
|
||||||
|
|
||||||
|
assert(m_model_object != nullptr);
|
||||||
|
this->update_slicing_parameters();
|
||||||
|
PrintObject::update_layer_height_profile(*m_model_object, *m_slicing_parameters, m_layer_height_profile);
|
||||||
|
std::pair<SlicingParameters, const std::vector<double>> ret = { *m_slicing_parameters, m_layer_height_profile };
|
||||||
|
delete m_slicing_parameters;
|
||||||
|
m_slicing_parameters = nullptr;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
bool GLCanvas3D::LayersEditing::is_initialized() const
|
bool GLCanvas3D::LayersEditing::is_initialized() const
|
||||||
{
|
{
|
||||||
return wxGetApp().get_shader("variable_layer_height") != nullptr;
|
return wxGetApp().get_shader("variable_layer_height") != nullptr;
|
||||||
|
@ -4082,6 +4096,14 @@ void GLCanvas3D::apply_retina_scale(Vec2d &screen_coordinate) const
|
||||||
#endif // ENABLE_RETINA_GL
|
#endif // ENABLE_RETINA_GL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<SlicingParameters, const std::vector<double>> GLCanvas3D::get_layers_height_data(int object_id)
|
||||||
|
{
|
||||||
|
m_layers_editing.select_object(*m_model, object_id);
|
||||||
|
std::pair<SlicingParameters, const std::vector<double>> ret = m_layers_editing.get_layers_height_data();
|
||||||
|
m_layers_editing.select_object(*m_model, -1);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
bool GLCanvas3D::_is_shown_on_screen() const
|
bool GLCanvas3D::_is_shown_on_screen() const
|
||||||
{
|
{
|
||||||
return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false;
|
return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false;
|
||||||
|
|
|
@ -291,6 +291,8 @@ class GLCanvas3D
|
||||||
|
|
||||||
std::string get_tooltip(const GLCanvas3D& canvas) const;
|
std::string get_tooltip(const GLCanvas3D& canvas) const;
|
||||||
|
|
||||||
|
std::pair<SlicingParameters, const std::vector<double>> get_layers_height_data();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool is_initialized() const;
|
bool is_initialized() const;
|
||||||
void generate_layer_height_texture();
|
void generate_layer_height_texture();
|
||||||
|
@ -958,6 +960,8 @@ public:
|
||||||
|
|
||||||
void apply_retina_scale(Vec2d &screen_coordinate) const;
|
void apply_retina_scale(Vec2d &screen_coordinate) const;
|
||||||
|
|
||||||
|
std::pair<SlicingParameters, const std::vector<double>> get_layers_height_data(int object_id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _is_shown_on_screen() const;
|
bool _is_shown_on_screen() const;
|
||||||
|
|
||||||
|
|
|
@ -1302,11 +1302,12 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type
|
||||||
|
|
||||||
void Selection::scale_to_fit_print_volume(const BuildVolume& volume)
|
void Selection::scale_to_fit_print_volume(const BuildVolume& volume)
|
||||||
{
|
{
|
||||||
auto fit = [this](double s, Vec3d offset) {
|
auto fit = [this](double s, Vec3d offset, bool undoredo_snapshot) {
|
||||||
if (s <= 0.0 || s == 1.0)
|
if (s <= 0.0 || s == 1.0)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
wxGetApp().plater()->take_snapshot(_L("Scale To Fit"));
|
if (undoredo_snapshot)
|
||||||
|
wxGetApp().plater()->take_snapshot(_L("Scale To Fit"));
|
||||||
|
|
||||||
TransformationType type;
|
TransformationType type;
|
||||||
type.set_world();
|
type.set_world();
|
||||||
|
@ -1331,29 +1332,32 @@ void Selection::scale_to_fit_print_volume(const BuildVolume& volume)
|
||||||
wxGetApp().plater()->canvas3D()->do_move(""); // avoid storing another snapshot
|
wxGetApp().plater()->canvas3D()->do_move(""); // avoid storing another snapshot
|
||||||
|
|
||||||
wxGetApp().obj_manipul()->set_dirty();
|
wxGetApp().obj_manipul()->set_dirty();
|
||||||
|
return undoredo_snapshot;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto fit_rectangle = [this, fit](const BuildVolume& volume) {
|
auto fit_rectangle = [this, fit](const BuildVolume& volume, bool undoredo_snapshot, double* max_height = nullptr) {
|
||||||
const BoundingBoxf3 print_volume = volume.bounding_volume();
|
const BoundingBoxf3 print_volume = volume.bounding_volume();
|
||||||
const Vec3d print_volume_size = print_volume.size();
|
Vec3d print_volume_size = print_volume.size();
|
||||||
|
print_volume_size.z() = (max_height != nullptr) ? *max_height : volume.max_print_height();
|
||||||
|
|
||||||
// adds 1/100th of a mm on all sides to avoid false out of print volume detections due to floating-point roundings
|
// adds 1/100th of a mm on both xy sides to avoid false out of print volume detections due to floating-point roundings
|
||||||
const Vec3d box_size = get_bounding_box().size() + 0.02 * Vec3d::Ones();
|
Vec3d box_size = get_bounding_box().size();
|
||||||
|
box_size.x() += 0.02;
|
||||||
|
box_size.y() += 0.02;
|
||||||
|
|
||||||
const double sx = (box_size.x() != 0.0) ? print_volume_size.x() / box_size.x() : 0.0;
|
const double sx = print_volume_size.x() / box_size.x();
|
||||||
const double sy = (box_size.y() != 0.0) ? print_volume_size.y() / box_size.y() : 0.0;
|
const double sy = print_volume_size.y() / box_size.y();
|
||||||
const double sz = (box_size.z() != 0.0) ? print_volume_size.z() / box_size.z() : 0.0;
|
const double sz = print_volume_size.z() / box_size.z();
|
||||||
|
|
||||||
if (sx != 0.0 && sy != 0.0 && sz != 0.0)
|
return fit(std::min(sx, std::min(sy, sz)), print_volume.center() - get_bounding_box().center(), undoredo_snapshot);
|
||||||
fit(std::min(sx, std::min(sy, sz)), print_volume.center() - get_bounding_box().center());
|
|
||||||
};
|
};
|
||||||
|
|
||||||
auto fit_circle = [this, fit](const BuildVolume& volume) {
|
auto fit_circle = [this, fit](const BuildVolume& volume, bool undoredo_snapshot, double* max_height = nullptr) {
|
||||||
const Geometry::Circled& print_circle = volume.circle();
|
const Geometry::Circled& print_circle = volume.circle();
|
||||||
double print_circle_radius = unscale<double>(print_circle.radius);
|
double print_circle_radius = unscale<double>(print_circle.radius);
|
||||||
|
|
||||||
if (print_circle_radius == 0.0)
|
if (print_circle_radius == 0.0)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
Points points;
|
Points points;
|
||||||
double max_z = 0.0;
|
double max_z = 0.0;
|
||||||
|
@ -1367,31 +1371,56 @@ void Selection::scale_to_fit_print_volume(const BuildVolume& volume)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (points.empty())
|
if (points.empty())
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
const Geometry::Circled circle = Geometry::smallest_enclosing_circle_welzl(points);
|
const Geometry::Circled circle = Geometry::smallest_enclosing_circle_welzl(points);
|
||||||
// adds 1/100th of a mm on all sides to avoid false out of print volume detections due to floating-point roundings
|
// adds 1/100th of a mm on all sides to avoid false out of print volume detections due to floating-point roundings
|
||||||
const double circle_radius = unscale<double>(circle.radius) + 0.01;
|
const double circle_radius = unscale<double>(circle.radius) + 0.01;
|
||||||
|
|
||||||
if (circle_radius == 0.0 || max_z == 0.0)
|
if (circle_radius == 0.0 || max_z == 0.0)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
const double s = std::min(print_circle_radius / circle_radius, volume.max_print_height() / max_z);
|
const double print_volume_max_z = (max_height != nullptr) ? *max_height : volume.max_print_height();
|
||||||
|
const double s = std::min(print_circle_radius / circle_radius, print_volume_max_z / max_z);
|
||||||
const Vec3d sel_center = get_bounding_box().center();
|
const Vec3d sel_center = get_bounding_box().center();
|
||||||
const Vec3d offset = s * (Vec3d(unscale<double>(circle.center.x()), unscale<double>(circle.center.y()), 0.5 * max_z) - sel_center);
|
const Vec3d offset = s * (Vec3d(unscale<double>(circle.center.x()), unscale<double>(circle.center.y()), 0.5 * max_z) - sel_center);
|
||||||
const Vec3d print_center = { unscale<double>(print_circle.center.x()), unscale<double>(print_circle.center.y()), 0.5 * volume.max_print_height() };
|
const Vec3d print_center = { unscale<double>(print_circle.center.x()), unscale<double>(print_circle.center.y()), 0.5 * volume.max_print_height() };
|
||||||
fit(s, print_center - (sel_center + offset));
|
return fit(s, print_center - (sel_center + offset), undoredo_snapshot);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (is_empty() || m_mode == Volume)
|
if (is_empty() || m_mode == Volume)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
assert(is_single_full_instance());
|
||||||
|
|
||||||
|
// used to keep track whether the undo/redo snapshot has already been taken
|
||||||
|
bool undoredo_snapshot = false;
|
||||||
|
|
||||||
switch (volume.type())
|
switch (volume.type())
|
||||||
{
|
{
|
||||||
case BuildVolume::Type::Rectangle: { fit_rectangle(volume); break; }
|
case BuildVolume::Type::Rectangle: { undoredo_snapshot = fit_rectangle(volume, !undoredo_snapshot); break; }
|
||||||
case BuildVolume::Type::Circle: { fit_circle(volume); break; }
|
case BuildVolume::Type::Circle: { undoredo_snapshot = fit_circle(volume, !undoredo_snapshot); break; }
|
||||||
default: { break; }
|
default: { break; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (wxGetApp().plater()->printer_technology() == ptFFF) {
|
||||||
|
// check whether the top layer exceeds the maximum height of the print volume
|
||||||
|
// and, in case, reduce the scale accordingly
|
||||||
|
const auto [slicing_parameters, profile] = wxGetApp().plater()->canvas3D()->get_layers_height_data(get_object_idx());
|
||||||
|
auto layers = generate_object_layers(slicing_parameters, profile);
|
||||||
|
auto layers_it = layers.rbegin();
|
||||||
|
while (layers_it != layers.rend() && *layers_it > volume.bounding_volume().max.z()) {
|
||||||
|
++layers_it;
|
||||||
|
}
|
||||||
|
if (layers_it != layers.rbegin() && layers_it != layers.rend()) {
|
||||||
|
switch (volume.type())
|
||||||
|
{
|
||||||
|
case BuildVolume::Type::Rectangle: { fit_rectangle(volume, !undoredo_snapshot, &(*layers_it)); break; }
|
||||||
|
case BuildVolume::Type::Circle: { fit_circle(volume, !undoredo_snapshot, &(*layers_it)); break; }
|
||||||
|
default: { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_WORLD_COORDINATE
|
#if ENABLE_WORLD_COORDINATE
|
||||||
|
|
Loading…
Reference in a new issue