mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-27 10:51:22 +00:00
Port wipe tower BBS improvements (#15485)
This commit is contained in:
@@ -1041,10 +1041,12 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
|
||||
|
||||
for (auto el : {"wipe_tower_rotation_angle", "wipe_tower_cone_angle",
|
||||
"wipe_tower_extra_spacing", "wipe_tower_max_purge_speed",
|
||||
"wipe_tower_bridging", "wipe_tower_extra_flow",
|
||||
"wipe_tower_no_sparse_layers"})
|
||||
"wipe_tower_bridging", "wipe_tower_extra_flow"})
|
||||
toggle_line(el, have_prime_tower && supports_wipe_tower_2);
|
||||
|
||||
// Orca: both tower generators skip sparse layers, so this is not a wipe tower 2 exclusive.
|
||||
toggle_line("wipe_tower_no_sparse_layers", have_prime_tower);
|
||||
|
||||
WipeTowerWallType wipe_tower_wall_type = config->opt_enum<WipeTowerWallType>("wipe_tower_wall_type");
|
||||
bool have_rib_wall = (wipe_tower_wall_type == WipeTowerWallType::wtwRib)&&have_prime_tower;
|
||||
toggle_line("wipe_tower_cone_angle", have_prime_tower && supports_wipe_tower_2 && wipe_tower_wall_type == WipeTowerWallType::wtwCone);
|
||||
|
||||
@@ -4316,6 +4316,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
if (can_sequential_clearance_show_in_gizmo())
|
||||
update_sequential_clearance();
|
||||
} else {
|
||||
// Orca: by-layer counterpart, for a prime tower compacted by "No sparse layers".
|
||||
if (current_printer_technology() == ptFFF && can_sequential_clearance_show_in_gizmo())
|
||||
update_compacted_wipe_tower_clearance();
|
||||
if (c == GLGizmosManager::EType::Move ||
|
||||
c == GLGizmosManager::EType::Scale ||
|
||||
c == GLGizmosManager::EType::Rotate)
|
||||
@@ -4549,8 +4552,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
TransformationType trafo_type;
|
||||
trafo_type.set_relative();
|
||||
m_selection.translate(cur_pos - m_mouse.drag.start_position_3D, trafo_type);
|
||||
if (current_printer_technology() == ptFFF && (fff_print()->config().print_sequence == PrintSequence::ByObject))
|
||||
update_sequential_clearance();
|
||||
if (current_printer_technology() == ptFFF) {
|
||||
if (fff_print()->config().print_sequence == PrintSequence::ByObject)
|
||||
update_sequential_clearance();
|
||||
else
|
||||
update_compacted_wipe_tower_clearance();
|
||||
}
|
||||
// BBS
|
||||
//wxGetApp().obj_manipul()->set_dirty();
|
||||
m_dirty = true;
|
||||
@@ -5614,6 +5621,101 @@ bool GLCanvas3D::can_sequential_clearance_show_in_gizmo() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Live preview of the compacted prime tower clearance, the by-layer counterpart of
|
||||
// update_sequential_clearance(). Called while the user drags a volume / gizmo; idle visibility
|
||||
// matches sequential print (hidden when valid, filled when Print::validate reports a collision).
|
||||
// Print::compacted_wipe_tower_clearance_valid() answers the same question authoritatively, but it
|
||||
// reads the tower position from the config, which only catches up once do_move() writes it back on
|
||||
// mouse release. Recomputing from the volumes here is what makes the keep-out zone follow the tower
|
||||
// while it is still under the cursor.
|
||||
void GLCanvas3D::update_compacted_wipe_tower_clearance()
|
||||
{
|
||||
if (current_printer_technology() != ptFFF)
|
||||
return;
|
||||
const Print *print = fff_print();
|
||||
if (print == nullptr)
|
||||
return;
|
||||
const PrintConfig &config = print->config();
|
||||
if (config.print_sequence != PrintSequence::ByLayer || ! wipe_tower_sparse_layers_skipped(config) || ! print->has_wipe_tower())
|
||||
return;
|
||||
|
||||
PartPlateList &plate_list = wxGetApp().plater()->get_partplate_list();
|
||||
PartPlate *plate = plate_list.get_curr_plate();
|
||||
if (plate == nullptr)
|
||||
return;
|
||||
const int plate_id = plate_list.get_curr_plate_index();
|
||||
|
||||
// Once the tower has been generated the scene shows its real mesh with the brim merged in,
|
||||
// otherwise it is a bare estimated cube with no brim at all. Only the latter needs the brim added
|
||||
// here, and the width comes from WipeTowerData, the same source the preview box is sized from, so
|
||||
// the zone cannot be padded against a brim the preview was not built with.
|
||||
const bool preview_carries_brim = print->is_step_done(psWipeTower) && print->wipe_tower_data().wipe_tower_mesh_data.has_value();
|
||||
const double brim = preview_carries_brim ? 0. : double(print->wipe_tower_data(print->extruders().size()).brim_width);
|
||||
const double padding = compacted_tower_footprint_padding(config, brim);
|
||||
|
||||
// Tower footprint straight from the volume the user sees, so that dragging either the tower or an
|
||||
// object updates the zone on the very next frame.
|
||||
Polygon tower_footprint;
|
||||
for (const GLVolume *v : m_volumes.volumes) {
|
||||
if (! v->is_wipe_tower || v->object_idx() - 1000 != plate_id)
|
||||
continue;
|
||||
const BoundingBoxf3 bbox = v->transformed_convex_hull_bounding_box();
|
||||
tower_footprint = Polygon({ Point(scale_(bbox.min.x() - padding), scale_(bbox.min.y() - padding)),
|
||||
Point(scale_(bbox.max.x() + padding), scale_(bbox.min.y() - padding)),
|
||||
Point(scale_(bbox.max.x() + padding), scale_(bbox.max.y() + padding)),
|
||||
Point(scale_(bbox.min.x() - padding), scale_(bbox.max.y() + padding)) });
|
||||
break;
|
||||
}
|
||||
|
||||
const CompactedTowerZone zone = compacted_wipe_tower_zone(config, tower_footprint);
|
||||
if (zone.empty()) {
|
||||
reset_sequential_print_clearance();
|
||||
return;
|
||||
}
|
||||
|
||||
// While dragging, outline every on-plate instance next to the tower ring, the way sequential print
|
||||
// outlines every object. Both carry half of the clearance, so the two outlines meeting is precisely
|
||||
// the moment that object goes over its limit - which is what makes the pair worth drawing at all.
|
||||
// The tier is per object, so a short object gets the narrow nozzle outline rather than the wide
|
||||
// body one it is not subject to; without that, a 3 mm object parked beside the tower would be drawn
|
||||
// deep inside the keep-out ring while passing the check. Only the instances that already exceed
|
||||
// allowed_rise also get a height limit plane.
|
||||
Polygons outlines;
|
||||
std::vector<std::pair<Polygon, float>> height_polygons;
|
||||
bool body_tier_used = false;
|
||||
const BoundingBox plate_bb = plate->get_bounding_box_crd();
|
||||
for (const ModelObject *model_object : m_model->objects) {
|
||||
for (size_t i = 0; i < model_object->instances.size(); ++i) {
|
||||
Geometry::Transformation trafo(model_object->instances[i]->get_transformation());
|
||||
const Vec3d offset = trafo.get_offset();
|
||||
trafo.set_offset(Vec3d(offset.x(), offset.y(), 0.0));
|
||||
const Polygon inst_hull = model_object->convex_hull_2d(trafo.get_matrix());
|
||||
if (inst_hull.points.empty() || ! plate_bb.overlap(inst_hull.bounding_box()))
|
||||
continue;
|
||||
|
||||
// Same tiers and the same rise measured from the plate as
|
||||
// Print::compacted_wipe_tower_clearance_valid(), so that the preview and the validation
|
||||
// that follows it never contradict each other.
|
||||
const double object_top = model_object->get_instance_max_z(i);
|
||||
const CompactedTowerClearance clearance = compacted_wipe_tower_clearance(config, zone, inst_hull, object_top);
|
||||
body_tier_used = body_tier_used || compacted_tower_body_tier(clearance);
|
||||
|
||||
const Polygon outline = compacted_wipe_tower_offender_outline(inst_hull, clearance.body_clearance);
|
||||
outlines.emplace_back(outline);
|
||||
if (object_top <= clearance.allowed_rise + EPSILON)
|
||||
continue;
|
||||
height_polygons.emplace_back(outline, float(clearance.allowed_rise));
|
||||
}
|
||||
}
|
||||
|
||||
Polygons polygons = compacted_wipe_tower_rings(zone, body_tier_used);
|
||||
append(polygons, outlines);
|
||||
|
||||
set_sequential_print_clearance_visible(true);
|
||||
set_sequential_print_clearance_render_fill(false);
|
||||
set_sequential_print_clearance_polygons(polygons, height_polygons);
|
||||
}
|
||||
|
||||
void GLCanvas3D::update_sequential_clearance()
|
||||
{
|
||||
if (current_printer_technology() != ptFFF || (fff_print()->config().print_sequence == PrintSequence::ByLayer))
|
||||
|
||||
@@ -1191,6 +1191,8 @@ public:
|
||||
|
||||
bool can_sequential_clearance_show_in_gizmo();
|
||||
void update_sequential_clearance();
|
||||
// Orca: by-layer counterpart, for a prime tower compacted by "No sparse layers".
|
||||
void update_compacted_wipe_tower_clearance();
|
||||
|
||||
const Print* fff_print() const;
|
||||
const SLAPrint* sla_print() const;
|
||||
|
||||
@@ -1053,7 +1053,13 @@ void PartPlate::render_grid(bool bottom) {
|
||||
|
||||
void PartPlate::render_height_limit(PartPlate::HeightLimitMode mode)
|
||||
{
|
||||
if (m_print && m_print->config().print_sequence == PrintSequence::ByObject && mode != HEIGHT_LIMIT_NONE)
|
||||
// Orca: a prime tower compacted by "No sparse layers" drags the nozzle back down to the plate on
|
||||
// every toolchange, so the rod and the lid limit how tall a neighbouring object may be exactly as
|
||||
// they do in sequential printing. The reference lines are just as useful there.
|
||||
const bool relevant_for_print_mode = m_print && (m_print->config().print_sequence == PrintSequence::ByObject ||
|
||||
(m_print->config().print_sequence == PrintSequence::ByLayer &&
|
||||
wipe_tower_sparse_layers_skipped(m_print->config()) && m_print->has_wipe_tower()));
|
||||
if (relevant_for_print_mode && mode != HEIGHT_LIMIT_NONE)
|
||||
{
|
||||
// draw lower limit
|
||||
// ORCA: OpenGL Core Profile
|
||||
|
||||
@@ -1992,6 +1992,20 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
|
||||
|
||||
// reload scene to update timelapse wipe tower
|
||||
if (opt_key == "timelapse_type") {
|
||||
// Smooth timelapse parks the nozzle on the prime tower every layer, so it needs a tower on
|
||||
// every layer. That is exactly what "No sparse layers" removes, and with both on the tower is
|
||||
// planned full height and then dropped on emission. Drop "No sparse layers" and tell the user.
|
||||
if (boost::any_cast<int>(value) == (int) TimelapseType::tlSmooth && m_config->opt_bool("wipe_tower_no_sparse_layers")) {
|
||||
MessageDialog dlg(wxGetApp().plater(),
|
||||
_L("Smooth timelapse needs a prime tower on every layer, which is not compatible with \"No sparse layers\". "
|
||||
"\"No sparse layers\" has been turned off."),
|
||||
_L("Warning"), wxICON_WARNING | wxOK);
|
||||
dlg.ShowModal();
|
||||
DynamicPrintConfig new_conf = *m_config;
|
||||
new_conf.set_key_value("wipe_tower_no_sparse_layers", new ConfigOptionBool(false));
|
||||
m_config_manipulation.apply(m_config, &new_conf);
|
||||
}
|
||||
|
||||
bool wipe_tower_enabled = m_config->option<ConfigOptionBool>("enable_prime_tower")->value;
|
||||
if (!wipe_tower_enabled && boost::any_cast<int>(value) == (int)TimelapseType::tlSmooth) {
|
||||
MessageDialog dlg(wxGetApp().plater(), _L("A prime tower is required for smooth timelapse mode. There may be flaws on the model without prime tower. Do you want to enable the prime tower\?"),
|
||||
@@ -2007,6 +2021,23 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
|
||||
}
|
||||
}
|
||||
|
||||
// Mirror of the timelapse_type branch above: enabling "No sparse layers" while smooth timelapse
|
||||
// is active would leave the tower on every layer anyway, so fall back to traditional timelapse.
|
||||
if (opt_key == "wipe_tower_no_sparse_layers" && boost::any_cast<bool>(value)) {
|
||||
auto timelapse_type = m_config->option<ConfigOptionEnum<TimelapseType>>("timelapse_type");
|
||||
if (timelapse_type && timelapse_type->value == TimelapseType::tlSmooth) {
|
||||
MessageDialog dlg(wxGetApp().plater(),
|
||||
_L("\"No sparse layers\" is not compatible with smooth timelapse, which needs a prime tower on every layer. "
|
||||
"Timelapse has been switched to traditional mode."),
|
||||
_L("Warning"), wxICON_WARNING | wxOK);
|
||||
dlg.ShowModal();
|
||||
DynamicPrintConfig new_conf = *m_config;
|
||||
new_conf.set_key_value("timelapse_type", new ConfigOptionEnum<TimelapseType>(TimelapseType::tlTraditional));
|
||||
m_config_manipulation.apply(m_config, &new_conf);
|
||||
wxGetApp().plater()->update();
|
||||
}
|
||||
}
|
||||
|
||||
if (opt_key == "print_sequence" && m_config->opt_enum<PrintSequence>("print_sequence") == PrintSequence::ByObject) {
|
||||
auto printer_structure_opt = m_preset_bundle->printers.get_edited_preset().config.option<ConfigOptionEnum<PrinterStructure>>("printer_structure");
|
||||
if (printer_structure_opt && printer_structure_opt->value == PrinterStructure::psI3) {
|
||||
@@ -5109,6 +5140,7 @@ void TabPrinter::build_fff()
|
||||
|
||||
optgroup = page->new_optgroup(L("Extruder Clearance"), "param_extruder_clearance");
|
||||
optgroup->append_single_option_line("extruder_clearance_radius", "printer_basic_information_extruder_clearance#radius");
|
||||
optgroup->append_single_option_line("extruder_clearance_dist_to_rod", "printer_basic_information_extruder_clearance");
|
||||
optgroup->append_single_option_line("extruder_clearance_height_to_rod", "printer_basic_information_extruder_clearance#height-to-rod");
|
||||
optgroup->append_single_option_line("extruder_clearance_height_to_lid", "printer_basic_information_extruder_clearance#height-to-lid");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user