Allow using different infill types for top surface coloring

- Add boundary skin, boundary skin hybrid, and spiral infill types
- Combine perimeter area with infill before solving colors (when 'replace top perimeters with infill' is enabled)
- Allow UI to update while slicing is being cancelled
This commit is contained in:
sentientstardust
2026-05-26 23:15:30 +01:00
parent 01c915be65
commit ef4ba55320
10 changed files with 1034 additions and 68 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -684,6 +684,45 @@ static int top_surface_image_printing_method_from_name(const std::string &name)
return int(TextureMappingZone::TopSurfaceImageSameAngle45Width);
}
static std::string top_surface_contoning_flat_surface_infill_mode_name(int mode)
{
switch (clamp_int(mode,
int(TextureMappingZone::ContoningFlatSurfaceInfillDefault),
int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinHybrid))) {
case int(TextureMappingZone::ContoningFlatSurfaceInfillRectilinear):
return "rectilinear";
case int(TextureMappingZone::ContoningFlatSurfaceInfillConcentric):
return "concentric";
case int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinFixed):
return "boundary_skin_fixed";
case int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinVariable):
return "boundary_skin_variable";
case int(TextureMappingZone::ContoningFlatSurfaceInfillSpiral):
return "spiral";
case int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinHybrid):
return "boundary_skin_hybrid";
default:
return "default";
}
}
static int top_surface_contoning_flat_surface_infill_mode_from_name(const std::string &name)
{
if (name == "rectilinear")
return int(TextureMappingZone::ContoningFlatSurfaceInfillRectilinear);
if (name == "concentric")
return int(TextureMappingZone::ContoningFlatSurfaceInfillConcentric);
if (name == "boundary_skin" || name == "boundary_skin_variable")
return int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinVariable);
if (name == "boundary_skin_fixed")
return int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinFixed);
if (name == "spiral")
return int(TextureMappingZone::ContoningFlatSurfaceInfillSpiral);
if (name == "boundary_skin_hybrid")
return int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinHybrid);
return int(TextureMappingZone::ContoningFlatSurfaceInfillDefault);
}
static std::string top_visible_recolor_aggressiveness_name(int mode)
{
switch (clamp_int(mode,
@@ -1057,6 +1096,7 @@ bool TextureMappingZone::operator==(const TextureMappingZone &rhs) const
top_surface_contoning_replace_top_perimeters_with_infill == rhs.top_surface_contoning_replace_top_perimeters_with_infill &&
top_surface_contoning_recolor_surrounding_perimeters == rhs.top_surface_contoning_recolor_surrounding_perimeters &&
top_surface_contoning_perimeter_mode == rhs.top_surface_contoning_perimeter_mode &&
top_surface_contoning_flat_surface_infill_mode == rhs.top_surface_contoning_flat_surface_infill_mode &&
top_surface_contoning_layer_phase_enabled == rhs.top_surface_contoning_layer_phase_enabled &&
top_surface_contoning_varied_infill_angles_enabled == rhs.top_surface_contoning_varied_infill_angles_enabled &&
top_surface_contoning_blue_noise_error_diffusion_enabled == rhs.top_surface_contoning_blue_noise_error_diffusion_enabled &&
@@ -1448,6 +1488,8 @@ std::string TextureMappingManager::serialize_entries()
clamp_int(zone.top_surface_contoning_perimeter_mode,
int(TextureMappingZone::ContoningPerimeterSegmentBlocks),
int(TextureMappingZone::ContoningPerimeterSegmentInfill));
texture["top_surface_contoning_flat_surface_infill_mode"] =
top_surface_contoning_flat_surface_infill_mode_name(zone.top_surface_contoning_flat_surface_infill_mode);
texture["top_surface_contoning_layer_phase_enabled"] = zone.top_surface_contoning_layer_phase_enabled;
texture["top_surface_contoning_varied_infill_angles_enabled"] =
zone.top_surface_contoning_varied_infill_angles_enabled;
@@ -1721,6 +1763,15 @@ void TextureMappingManager::load_entries(const std::string &serialized,
TextureMappingZone::DefaultTopSurfaceContoningPerimeterMode),
int(TextureMappingZone::ContoningPerimeterSegmentBlocks),
int(TextureMappingZone::ContoningPerimeterSegmentInfill));
auto flat_surface_infill_mode_it = texture.find("top_surface_contoning_flat_surface_infill_mode");
zone.top_surface_contoning_flat_surface_infill_mode =
flat_surface_infill_mode_it != texture.end() && flat_surface_infill_mode_it->is_string() ?
top_surface_contoning_flat_surface_infill_mode_from_name(flat_surface_infill_mode_it->get<std::string>()) :
clamp_int(flat_surface_infill_mode_it != texture.end() && flat_surface_infill_mode_it->is_number_integer() ?
flat_surface_infill_mode_it->get<int>() :
TextureMappingZone::DefaultTopSurfaceContoningFlatSurfaceInfillMode,
int(TextureMappingZone::ContoningFlatSurfaceInfillDefault),
int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinHybrid));
if (zone.top_surface_contoning_replace_top_perimeters_with_infill)
zone.top_surface_contoning_recolor_surrounding_perimeters = false;
zone.top_surface_contoning_layer_phase_enabled =

View File

@@ -78,6 +78,16 @@ struct TextureMappingZone
ContoningPerimeterSegmentInfill = 2
};
enum TopSurfaceContoningFlatSurfaceInfillMode : uint8_t {
ContoningFlatSurfaceInfillDefault = 0,
ContoningFlatSurfaceInfillRectilinear = 1,
ContoningFlatSurfaceInfillConcentric = 2,
ContoningFlatSurfaceInfillBoundarySkinFixed = 3,
ContoningFlatSurfaceInfillBoundarySkinVariable = 4,
ContoningFlatSurfaceInfillSpiral = 5,
ContoningFlatSurfaceInfillBoundarySkinHybrid = 6
};
enum FilamentColorMode : uint8_t {
FilamentColorAny = 0,
FilamentColorRGB = 1,
@@ -179,6 +189,8 @@ struct TextureMappingZone
static constexpr bool DefaultTopSurfaceContoningReplaceTopPerimetersWithInfill = false;
static constexpr bool DefaultTopSurfaceContoningRecolorSurroundingPerimeters = false;
static constexpr int DefaultTopSurfaceContoningPerimeterMode = int(ContoningPerimeterDividedLine);
static constexpr int DefaultTopSurfaceContoningFlatSurfaceInfillMode = int(ContoningFlatSurfaceInfillDefault);
static constexpr int SlicerDefaultTopSurfaceContoningFlatSurfaceInfillMode = int(ContoningFlatSurfaceInfillRectilinear);
static constexpr bool DefaultTopSurfaceContoningLayerPhaseEnabled = false;
static constexpr bool DefaultTopSurfaceContoningVariedInfillAnglesEnabled = false;
static constexpr bool DefaultTopSurfaceContoningBlueNoiseErrorDiffusionEnabled = false;
@@ -217,6 +229,13 @@ struct TextureMappingZone
}
}
static constexpr int effective_top_surface_contoning_flat_surface_infill_mode(int mode)
{
return mode == int(ContoningFlatSurfaceInfillDefault) ?
SlicerDefaultTopSurfaceContoningFlatSurfaceInfillMode :
mode;
}
struct LinearGradientAnchor {
bool valid = false;
size_t object_id = 0;
@@ -287,6 +306,7 @@ struct TextureMappingZone
bool top_surface_contoning_replace_top_perimeters_with_infill = DefaultTopSurfaceContoningReplaceTopPerimetersWithInfill;
bool top_surface_contoning_recolor_surrounding_perimeters = DefaultTopSurfaceContoningRecolorSurroundingPerimeters;
int top_surface_contoning_perimeter_mode = DefaultTopSurfaceContoningPerimeterMode;
int top_surface_contoning_flat_surface_infill_mode = DefaultTopSurfaceContoningFlatSurfaceInfillMode;
bool top_surface_contoning_layer_phase_enabled = DefaultTopSurfaceContoningLayerPhaseEnabled;
bool top_surface_contoning_varied_infill_angles_enabled = DefaultTopSurfaceContoningVariedInfillAnglesEnabled;
bool top_surface_contoning_blue_noise_error_diffusion_enabled = DefaultTopSurfaceContoningBlueNoiseErrorDiffusionEnabled;
@@ -411,6 +431,7 @@ struct TextureMappingZone
top_surface_contoning_replace_top_perimeters_with_infill = DefaultTopSurfaceContoningReplaceTopPerimetersWithInfill;
top_surface_contoning_recolor_surrounding_perimeters = DefaultTopSurfaceContoningRecolorSurroundingPerimeters;
top_surface_contoning_perimeter_mode = DefaultTopSurfaceContoningPerimeterMode;
top_surface_contoning_flat_surface_infill_mode = DefaultTopSurfaceContoningFlatSurfaceInfillMode;
top_surface_contoning_layer_phase_enabled = DefaultTopSurfaceContoningLayerPhaseEnabled;
top_surface_contoning_varied_infill_angles_enabled = DefaultTopSurfaceContoningVariedInfillAnglesEnabled;
top_surface_contoning_blue_noise_error_diffusion_enabled = DefaultTopSurfaceContoningBlueNoiseErrorDiffusionEnabled;

View File

@@ -355,6 +355,12 @@ void BackgroundSlicingProcess::thread_proc()
else {
//BBS: internal cancel
m_internal_cancelled = true;
if (m_internal_cancel_event_requested) {
m_internal_cancel_event_requested = false;
SlicingProcessCompletedEvent evt(m_event_finished_id, 0, SlicingProcessCompletedEvent::Cancelled, exception);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": send internal SlicingProcessCompletedEvent to main, status %1%")%evt.status();
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone());
}
}
m_print->restart();
lck.unlock();
@@ -556,6 +562,7 @@ bool BackgroundSlicingProcess::stop()
if (m_state == STATE_STARTED || m_state == STATE_RUNNING) {
// Cancel any task planned by the background thread on UI thread.
cancel_ui_task(m_ui_task);
m_internal_cancel_event_requested = false;
m_print->cancel();
// Wait until the background processing stops by being canceled.
m_condition.wait(lck, [this](){ return m_state == STATE_CANCELED; });
@@ -586,6 +593,19 @@ bool BackgroundSlicingProcess::cancel()
return m_state == STATE_FINISHED || m_state == STATE_CANCELED;
}
bool BackgroundSlicingProcess::cancel_internal()
{
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< ", enter"<<std::endl;
std::unique_lock<std::mutex> lck(m_mutex);
if (m_state == STATE_STARTED || m_state == STATE_RUNNING) {
cancel_ui_task(m_ui_task);
m_internal_cancel_event_requested = true;
m_print->cancel_internal();
return true;
}
return false;
}
bool BackgroundSlicingProcess::reset()
{
bool stopped = this->stop();

View File

@@ -132,6 +132,7 @@ public:
// A stopped background processing may be restarted with start().
bool stop();
bool cancel();
bool cancel_internal();
// Cancel the background processing and reset the print. Returns false if the background processing was not running.
// Useful when the Model or configuration is being changed drastically.
bool reset();
@@ -278,6 +279,7 @@ private:
GUI::PartPlate* m_current_plate;
PrinterTechnology m_printer_tech = ptUnknown;
bool m_internal_cancelled = false;
bool m_internal_cancel_event_requested = false;
PrintState<BackgroundSlicingProcessStep, bspsCount> m_step_state;
bool set_step_started(BackgroundSlicingProcessStep step);

View File

@@ -2402,6 +2402,18 @@ void NotificationManager::set_slicing_progress_percentage(const std::string& tex
// Slicing progress notification was not found - init it thru plater so correct cancel callback function is appended
wxGetApp().plater()->init_notification_manager();
}
void NotificationManager::set_slicing_progress_canceling(const std::string& text)
{
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
if (notification->get_type() == NotificationType::SlicingProgress) {
SlicingProgressNotification* spn = dynamic_cast<SlicingProgressNotification*>(notification.get());
spn->set_cancel_requested_text(text);
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
return;
}
}
wxGetApp().plater()->init_notification_manager();
}
void NotificationManager::set_slicing_progress_canceled(const std::string& text)
{
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {

View File

@@ -284,6 +284,7 @@ public:
void set_slicing_progress_began();
// percentage negative = canceled, <0-1) = progress, 1 = completed
void set_slicing_progress_percentage(const std::string& text, float percentage);
void set_slicing_progress_canceling(const std::string& text);
void set_slicing_progress_canceled(const std::string& text);
// hides slicing progress notification imidietly
void set_slicing_progress_hidden();

View File

@@ -1908,6 +1908,7 @@ public:
bool top_surface_contoning_replace_top_perimeters_with_infill,
bool top_surface_contoning_recolor_surrounding_perimeters,
int top_surface_contoning_perimeter_mode,
int top_surface_contoning_flat_surface_infill_mode,
bool top_surface_contoning_layer_phase_enabled,
bool top_surface_contoning_varied_infill_angles_enabled,
bool top_surface_contoning_blue_noise_error_diffusion_enabled,
@@ -2532,6 +2533,27 @@ public:
contoning_feature_row->Add(m_top_surface_contoning_min_feature_spin, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap / 2);
contoning_feature_row->Add(new wxStaticText(m_top_surface_contoning_panel, wxID_ANY, _L("mm")), 0, wxALIGN_CENTER_VERTICAL);
top_surface_contoning_root->Add(contoning_feature_row, 0, wxEXPAND);
auto *contoning_flat_infill_row = new wxBoxSizer(wxHORIZONTAL);
contoning_flat_infill_row->Add(new wxStaticText(m_top_surface_contoning_panel, wxID_ANY, _L("Flat surface infill mode")),
0,
wxALIGN_CENTER_VERTICAL | wxRIGHT,
gap);
wxArrayString contoning_flat_infill_choices;
contoning_flat_infill_choices.Add(_L("Default (Rectilinear)"));
contoning_flat_infill_choices.Add(_L("Rectilinear"));
contoning_flat_infill_choices.Add(_L("Concentric"));
contoning_flat_infill_choices.Add(_L("Boundary Skin (fixed width)"));
contoning_flat_infill_choices.Add(_L("Boundary Skin (variable width)"));
contoning_flat_infill_choices.Add(_L("Spiral"));
contoning_flat_infill_choices.Add(_L("Boundary Skin Hybrid"));
m_top_surface_contoning_flat_surface_infill_choice =
new wxChoice(m_top_surface_contoning_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, contoning_flat_infill_choices);
m_top_surface_contoning_flat_surface_infill_choice->SetSelection(
std::clamp(top_surface_contoning_flat_surface_infill_mode,
int(TextureMappingZone::ContoningFlatSurfaceInfillDefault),
int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinHybrid)));
contoning_flat_infill_row->Add(m_top_surface_contoning_flat_surface_infill_choice, 1, wxEXPAND);
top_surface_contoning_root->Add(contoning_flat_infill_row, 0, wxEXPAND | wxTOP, gap);
top_surface_box->Add(m_top_surface_contoning_panel, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, gap);
m_top_surface_image_fixed_coloring_filaments_checkbox =
new wxCheckBox(top_surface_page, wxID_ANY, _L("Fixed top-surface coloring filaments"));
@@ -3036,6 +3058,14 @@ public:
int(TextureMappingZone::ContoningPerimeterSegmentInfill)) :
TextureMappingZone::DefaultTopSurfaceContoningPerimeterMode;
}
int top_surface_contoning_flat_surface_infill_mode() const
{
return m_top_surface_contoning_flat_surface_infill_choice != nullptr ?
std::clamp(m_top_surface_contoning_flat_surface_infill_choice->GetSelection(),
int(TextureMappingZone::ContoningFlatSurfaceInfillDefault),
int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinHybrid)) :
TextureMappingZone::DefaultTopSurfaceContoningFlatSurfaceInfillMode;
}
bool top_surface_contoning_layer_phase_enabled() const
{
return m_top_surface_contoning_layer_phase_checkbox != nullptr &&
@@ -3507,6 +3537,8 @@ private:
m_top_surface_contoning_pattern_filaments_spin->Enable(contoning);
if (m_top_surface_contoning_min_feature_spin != nullptr)
m_top_surface_contoning_min_feature_spin->Enable(contoning);
if (m_top_surface_contoning_flat_surface_infill_choice != nullptr)
m_top_surface_contoning_flat_surface_infill_choice->Enable(contoning);
if (m_top_surface_contoning_checkboxes_panel != nullptr)
m_top_surface_contoning_checkboxes_panel->Show(contoning);
if (m_top_surface_contoning_color_lower_surfaces_checkbox != nullptr) {
@@ -3612,6 +3644,7 @@ private:
wxSpinCtrl *m_top_surface_contoning_stack_layers_spin {nullptr};
wxSpinCtrl *m_top_surface_contoning_pattern_filaments_spin {nullptr};
wxSpinCtrlDouble *m_top_surface_contoning_min_feature_spin {nullptr};
wxChoice *m_top_surface_contoning_flat_surface_infill_choice {nullptr};
wxPanel *m_top_surface_contoning_checkboxes_panel {nullptr};
wxCheckBox *m_top_surface_contoning_color_lower_surfaces_checkbox {nullptr};
wxCheckBox *m_top_surface_contoning_only_color_surface_infill_checkbox {nullptr};
@@ -8628,6 +8661,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
updated.top_surface_contoning_replace_top_perimeters_with_infill,
updated.top_surface_contoning_recolor_surrounding_perimeters,
updated.top_surface_contoning_perimeter_mode,
updated.top_surface_contoning_flat_surface_infill_mode,
updated.top_surface_contoning_layer_phase_enabled,
updated.top_surface_contoning_varied_infill_angles_enabled,
updated.top_surface_contoning_blue_noise_error_diffusion_enabled,
@@ -8693,6 +8727,8 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
updated.top_surface_contoning_recolor_surrounding_perimeters =
dlg.top_surface_contoning_recolor_surrounding_perimeters();
updated.top_surface_contoning_perimeter_mode = dlg.top_surface_contoning_perimeter_mode();
updated.top_surface_contoning_flat_surface_infill_mode =
dlg.top_surface_contoning_flat_surface_infill_mode();
updated.top_surface_contoning_layer_phase_enabled = dlg.top_surface_contoning_layer_phase_enabled();
updated.top_surface_contoning_varied_infill_angles_enabled =
dlg.top_surface_contoning_varied_infill_angles_enabled();
@@ -9525,6 +9561,7 @@ struct Plater::priv
bool m_is_slicing {false};
bool auto_reslice_pending {false};
bool auto_reslice_after_cancel {false};
bool background_process_update_after_cancel {false};
bool m_is_publishing {false};
int m_is_RightClickInLeftUI{-1};
int m_cur_slice_plate;
@@ -12918,7 +12955,8 @@ void Plater::priv::schedule_auto_reslice_if_needed()
if (background_process.running() || m_is_slicing) {
// Remember to restart once the current slice stops and cancel it now.
auto_reslice_after_cancel = true;
background_process.stop();
if (background_process.cancel_internal())
notification_manager->set_slicing_progress_canceling(_u8L("Cancelling..."));
return;
}
@@ -13391,6 +13429,13 @@ void Plater::priv::export_gcode(fs::path output_path, bool output_path_on_remova
}
unsigned int Plater::priv::update_restart_background_process(bool force_update_scene, bool force_update_preview)
{
if (this->background_process.running()) {
if (this->background_process.cancel_internal())
notification_manager->set_slicing_progress_canceling(_u8L("Cancelling..."));
background_process_update_after_cancel = true;
return 0;
}
bool switch_print = true;
//BBS: judge whether can switch print or not
if ((partplate_list.get_plate_count() > 1) && !this->background_process.can_switch_print())
@@ -15000,6 +15045,7 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
m_slice_all_only_has_gcode = false;
}
const bool internal_cancelled = this->background_process.is_internal_cancelled();
// Stop the background task, wait until the thread goes into the "Idle" state.
// At this point of time the thread should be either finished or canceled,
// so the following call just confirms, that the produced data were consumed.
@@ -15045,7 +15091,10 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
}
if (evt.cancelled()) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", cancel event, status: %1%") % evt.status();
this->notification_manager->set_slicing_progress_canceled(_u8L("Slicing Canceled"));
if (internal_cancelled)
this->notification_manager->set_slicing_progress_hidden();
else
this->notification_manager->set_slicing_progress_canceled(_u8L("Slicing Canceled"));
is_finished = true;
}
@@ -15188,6 +15237,10 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
auto_reslice_after_cancel = false;
schedule_auto_reslice_if_needed();
}
if (background_process_update_after_cancel) {
background_process_update_after_cancel = false;
schedule_background_process();
}
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(", exit.");
}

View File

@@ -146,6 +146,12 @@ void NotificationManager::SlicingProgressNotification::set_status_text(const std
}
}
void NotificationManager::SlicingProgressNotification::set_cancel_requested_text(const std::string& text)
{
m_cancel_requested = true;
set_status_text(text);
}
void NotificationManager::SlicingProgressNotification::set_print_info(const std::string& info)
{
if (m_sp_state != SlicingProgressState::SP_COMPLETED) {
@@ -169,8 +175,7 @@ void NotificationManager::SlicingProgressNotification::on_cancel_button()
if (!m_cancel_callback()) {
set_progress_state(SlicingProgressState::SP_NO_SLICING);
} else {
m_cancel_requested = true;
set_status_text(_u8L("Cancelling..."));
set_cancel_requested_text(_u8L("Cancelling..."));
}
}
}

View File

@@ -32,6 +32,7 @@ public:
SlicingProgressState get_progress_state() { return m_sp_state; }
// sets text of notification - call after setting progress state
void set_status_text(const std::string& text);
void set_cancel_requested_text(const std::string& text);
// sets cancel button callback
void set_cancel_callback(std::function<bool()> callback) { m_cancel_callback = callback; }
bool has_cancel_callback() const { return m_cancel_callback != nullptr; }