Brim and Cone Aware Preview

This commit is contained in:
Hanif Koh
2026-09-03 14:52:26 +08:00
committed by HanifKoh
parent 99627c8e93
commit e17965be53
3 changed files with 45 additions and 7 deletions

View File

@@ -20,6 +20,9 @@
#include "libslic3r/AppConfig.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/GCode/WipeTower.hpp"
#include "libslic3r/GCode/WipeTower2.hpp"
#include "libslic3r/GCode/WipeTowerEstimate.hpp"
#include "libslic3r/Tesselate.hpp"
#include "libslic3r/PrintConfig.hpp"
@@ -919,6 +922,33 @@ int GLVolumeCollection::load_wipe_tower_preview(
GUI::PartPlateList& ppl = GUI::wxGetApp().plater()->get_partplate_list();
std::vector<int> plate_extruders = ppl.get_plate(plate_idx)->get_extruders(true);
TriangleMesh wipe_tower_shell = make_cube(width, depth, height);
// The brim is part of the printed footprint: draw it and fold it into the shell so the
// outside-bed shader and the drag clamp react to the true first-layer extent.
const bool show_brim = brim_width > 0.f;
const float brim_height = 0.2f; // one first layer, visual only
TriangleMesh brim_slab;
if (show_brim) {
// A Type2 cone-wall tower's base bulges past the body box — follow the real base
// outline instead of the rectangle. Type1 ignores the cone option.
Polygon cone_base;
{
// Preset enums are ConfigOptionEnumGeneric, so read them by value; the planner is
// resolved as the estimate resolves it, off the printer preset.
const DynamicPrintConfig &print_cfg = GUI::wxGetApp().preset_bundle->prints.get_edited_preset().config;
const DynamicPrintConfig &printer_cfg = GUI::wxGetApp().preset_bundle->printers.get_edited_preset().config;
const ConfigOption *wall_opt = print_cfg.option("wipe_tower_wall_type");
if (wall_opt != nullptr && wall_opt->getInt() == int(WipeTowerWallType::wtwCone) && resolve_wipe_tower_type(printer_cfg) == WipeTowerType::Type2)
cone_base = WipeTower2::cone_base_polygon(width, depth, height, print_cfg.opt_float("wipe_tower_cone_angle"));
}
if (!cone_base.empty()) {
Polygons brim_outline = offset(cone_base, scaled(brim_width));
brim_slab = WipeTower::its_make_rib_brim(brim_outline.empty() ? cone_base : brim_outline.front(), brim_height);
} else {
brim_slab = make_cube(width + 2.f * brim_width, depth + 2.f * brim_width, brim_height);
brim_slab.translate({-brim_width, -brim_width, 0.f});
}
wipe_tower_shell.merge(brim_slab);
}
for (int extruder_id : plate_extruders) {
if (extruder_id <= extruder_colors.size())
colors.push_back(extruder_colors[extruder_id - 1]);
@@ -929,14 +959,19 @@ int GLVolumeCollection::load_wipe_tower_preview(
// Orca: make it transparent
for(auto& color : colors)
color.a(0.66f);
const size_t slab_count = colors.size(); // per-filament body slabs; the brim part comes after
if (show_brim && !colors.empty())
colors.push_back(colors.front());
volumes.emplace_back(new GLWipeTowerVolume(colors));
GLWipeTowerVolume& v = *dynamic_cast<GLWipeTowerVolume*>(volumes.back());
v.model_per_colors.resize(colors.size());
for (int i = 0; i < colors.size(); i++) {
TriangleMesh color_part = make_cube(width, depth / colors.size(), height);
color_part.translate({ 0.f, depth * i / colors.size(), 0. });
for (size_t i = 0; i < slab_count; i++) {
TriangleMesh color_part = make_cube(width, depth / slab_count, height);
color_part.translate({ 0.f, depth * i / slab_count, 0. });
v.model_per_colors[i].init_from(color_part);
}
if (show_brim && !colors.empty())
v.model_per_colors[slab_count].init_from(brim_slab);
v.model.init_from(wipe_tower_shell);
v.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(std::make_shared<const TriangleMesh>(wipe_tower_shell));
v.set_convex_hull(wipe_tower_shell);

View File

@@ -2907,7 +2907,9 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
Vec3d wipe_tower_size(footprint.width, footprint.depth, footprint.height);
// set_default_wipe_tower_pos_for_plate doesn't rerun when painting changes the
// filament count, so redo its clamp here on every reload.
// filament count, so redo its clamp here on every reload — unconditionally: a
// paint-triggered reload can arrive before the background process invalidates
// psWipeTower, so gating on it would skip the clamp exactly when it is needed.
{
Vec3d clamped_pos, clamped_size;
part_plate->estimate_wipe_tower_polygon(full_config, plate_id, clamped_pos, clamped_size);

View File

@@ -1274,9 +1274,10 @@ void Selection::translate(const Vec3d &displacement, TransformationType transfor
Vec3d tower_origin = m_cache.volumes_data[i].get_volume_position();
Vec3d actual_displacement = displacement;
bool show_read_wipe_tower = wxGetApp().plater()->get_partplate_list().get_plate(plate_idx)->fff_print()->is_step_done(psWipeTower);
float brim_width = wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_float("prime_tower_brim_width");
const double margin = show_read_wipe_tower ? WIPE_TOWER_MARGIN : brim_width + 0.5; // 0.5 is the line width of wipe tower
// Both preview volumes carry the brim in their bounding box (the estimate
// preview merges a brim slab, the sliced preview the real brim mesh), so the
// drag clamp only pads by the wipe tower line width.
const double margin = show_read_wipe_tower ? WIPE_TOWER_MARGIN : 0.5; // 0.5 is the line width of wipe tower
actual_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() *
m_cache.volumes_data[i].get_instance_mirror_matrix())