mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-31 14:52:06 +00:00
Add controls for auto-generated mixed filament gradients
Add an app preference for mixed filament gradient auto-generation and sync the manager state from startup and preferences changes. Prompt before generating large gradient sets, preserve custom rows when auto-generation is disabled, and refresh remapping/UI handling accordingly. Add mixed filament coverage and a dumpinfo helper for inspecting Windows minidumps.
This commit is contained in:
128
dumpinfo.cs
Normal file
128
dumpinfo.cs
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Microsoft.Win32.SafeHandles;
|
||||||
|
|
||||||
|
class DumpInfo
|
||||||
|
{
|
||||||
|
const int MiniDumpExceptionStream = 6;
|
||||||
|
const int MiniDumpModuleListStream = 4;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
struct MINIDUMP_LOCATION_DESCRIPTOR { public uint DataSize; public uint Rva; }
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
struct MINIDUMP_DIRECTORY { public uint StreamType; public MINIDUMP_LOCATION_DESCRIPTOR Location; }
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
struct MINIDUMP_EXCEPTION
|
||||||
|
{
|
||||||
|
public uint ExceptionCode;
|
||||||
|
public uint ExceptionFlags;
|
||||||
|
public ulong ExceptionRecord;
|
||||||
|
public ulong ExceptionAddress;
|
||||||
|
public uint NumberParameters;
|
||||||
|
public uint __unusedAlignment;
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 15)] public ulong[] ExceptionInformation;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
struct MINIDUMP_EXCEPTION_STREAM
|
||||||
|
{
|
||||||
|
public uint ThreadId;
|
||||||
|
public uint __alignment;
|
||||||
|
public MINIDUMP_EXCEPTION ExceptionRecord;
|
||||||
|
public MINIDUMP_LOCATION_DESCRIPTOR ThreadContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||||
|
struct MINIDUMP_STRING_HEADER { public uint Length; }
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
struct VS_FIXEDFILEINFO
|
||||||
|
{
|
||||||
|
public uint dwSignature; public uint dwStrucVersion; public uint dwFileVersionMS; public uint dwFileVersionLS;
|
||||||
|
public uint dwProductVersionMS; public uint dwProductVersionLS; public uint dwFileFlagsMask; public uint dwFileFlags;
|
||||||
|
public uint dwFileOS; public uint dwFileType; public uint dwFileSubtype; public uint dwFileDateMS; public uint dwFileDateLS;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
struct MINIDUMP_MODULE
|
||||||
|
{
|
||||||
|
public ulong BaseOfImage;
|
||||||
|
public uint SizeOfImage;
|
||||||
|
public uint CheckSum;
|
||||||
|
public uint TimeDateStamp;
|
||||||
|
public uint ModuleNameRva;
|
||||||
|
public VS_FIXEDFILEINFO VersionInfo;
|
||||||
|
public MINIDUMP_LOCATION_DESCRIPTOR CvRecord;
|
||||||
|
public MINIDUMP_LOCATION_DESCRIPTOR MiscRecord;
|
||||||
|
public ulong Reserved0;
|
||||||
|
public ulong Reserved1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("dbghelp.dll", SetLastError = true)]
|
||||||
|
static extern bool MiniDumpReadDumpStream(IntPtr BaseOfDump, int StreamNumber, out IntPtr Dir, out IntPtr StreamPointer, out uint StreamSize);
|
||||||
|
|
||||||
|
static T PtrTo<T>(IntPtr p) where T : struct => Marshal.PtrToStructure<T>(p);
|
||||||
|
|
||||||
|
static string ReadMinidumpString(IntPtr basePtr, uint rva)
|
||||||
|
{
|
||||||
|
if (rva == 0) return "";
|
||||||
|
var header = PtrTo<MINIDUMP_STRING_HEADER>(IntPtr.Add(basePtr, (int)rva));
|
||||||
|
var strPtr = IntPtr.Add(basePtr, (int)rva + 4);
|
||||||
|
return Marshal.PtrToStringUni(strPtr, (int)header.Length / 2) ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length == 0) { Console.WriteLine("usage: DumpInfo <dump>"); return; }
|
||||||
|
var path = args[0];
|
||||||
|
using var fs = File.OpenRead(path);
|
||||||
|
using var mm = System.IO.MemoryMappedFiles.MemoryMappedFile.CreateFromFile(fs, null, 0, System.IO.MemoryMappedFiles.MemoryMappedFileAccess.Read, null, System.IO.HandleInheritability.None, false);
|
||||||
|
using var view = mm.CreateViewAccessor(0, 0, System.IO.MemoryMappedFiles.MemoryMappedFileAccess.Read);
|
||||||
|
SafeMemoryMappedViewHandle handle = view.SafeMemoryMappedViewHandle;
|
||||||
|
handle.AcquirePointer(ref bytePtr);
|
||||||
|
try {
|
||||||
|
IntPtr basePtr = (IntPtr)bytePtr;
|
||||||
|
if (MiniDumpReadDumpStream(basePtr, MiniDumpExceptionStream, out _, out var exPtr, out _)) {
|
||||||
|
var ex = PtrTo<MINIDUMP_EXCEPTION_STREAM>(exPtr);
|
||||||
|
Console.WriteLine($"ThreadId: {ex.ThreadId}");
|
||||||
|
Console.WriteLine($"ExceptionCode: 0x{ex.ExceptionRecord.ExceptionCode:X8}");
|
||||||
|
Console.WriteLine($"ExceptionAddress: 0x{ex.ExceptionRecord.ExceptionAddress:X16}");
|
||||||
|
ulong addr = ex.ExceptionRecord.ExceptionAddress;
|
||||||
|
if (MiniDumpReadDumpStream(basePtr, MiniDumpModuleListStream, out _, out var modPtr, out _)) {
|
||||||
|
uint count = (uint)Marshal.ReadInt32(modPtr);
|
||||||
|
IntPtr cur = IntPtr.Add(modPtr, 4);
|
||||||
|
string bestName = "";
|
||||||
|
ulong bestBase = 0; uint bestSize = 0;
|
||||||
|
for (uint i = 0; i < count; ++i) {
|
||||||
|
var mod = PtrTo<MINIDUMP_MODULE>(cur);
|
||||||
|
if (addr >= mod.BaseOfImage && addr < mod.BaseOfImage + mod.SizeOfImage) {
|
||||||
|
bestName = ReadMinidumpString(basePtr, mod.ModuleNameRva);
|
||||||
|
bestBase = mod.BaseOfImage;
|
||||||
|
bestSize = mod.SizeOfImage;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cur = IntPtr.Add(cur, Marshal.SizeOf<MINIDUMP_MODULE>());
|
||||||
|
}
|
||||||
|
if (bestName.Length > 0) {
|
||||||
|
Console.WriteLine($"Module: {bestName}");
|
||||||
|
Console.WriteLine($"ModuleBase: 0x{bestBase:X16}");
|
||||||
|
Console.WriteLine($"Offset: 0x{(addr - bestBase):X}");
|
||||||
|
Console.WriteLine($"ModuleSize: 0x{bestSize:X}");
|
||||||
|
} else {
|
||||||
|
Console.WriteLine("Module: <not found>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Console.WriteLine("MiniDumpReadDumpStream for exception stream failed");
|
||||||
|
Console.WriteLine(Marshal.GetLastWin32Error());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
handle.ReleasePointer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static unsafe byte* bytePtr;
|
||||||
|
}
|
||||||
@@ -321,6 +321,10 @@ void AppConfig::set_defaults()
|
|||||||
set_bool("auto_calculate_when_filament_change", true);
|
set_bool("auto_calculate_when_filament_change", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (get("auto_generate_gradients").empty()) {
|
||||||
|
set_bool("auto_generate_gradients", true);
|
||||||
|
}
|
||||||
|
|
||||||
if (get("show_home_page").empty()) {
|
if (get("show_home_page").empty()) {
|
||||||
set_bool("show_home_page", true);
|
set_bool("show_home_page", true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "filament_mixer.h"
|
#include "filament_mixer.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <atomic>
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
@@ -15,6 +16,12 @@
|
|||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::atomic_bool s_mixed_filament_auto_generate_enabled { true };
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
static uint64_t canonical_pair_key(unsigned int a, unsigned int b)
|
static uint64_t canonical_pair_key(unsigned int a, unsigned int b)
|
||||||
{
|
{
|
||||||
const unsigned int lo = std::min(a, b);
|
const unsigned int lo = std::min(a, b);
|
||||||
@@ -810,6 +817,16 @@ uint64_t MixedFilamentManager::normalize_stable_id(uint64_t stable_id)
|
|||||||
return stable_id;
|
return stable_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MixedFilamentManager::set_auto_generate_enabled(bool enabled)
|
||||||
|
{
|
||||||
|
s_mixed_filament_auto_generate_enabled.store(enabled, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MixedFilamentManager::auto_generate_enabled()
|
||||||
|
{
|
||||||
|
return s_mixed_filament_auto_generate_enabled.load(std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
void MixedFilamentManager::auto_generate(const std::vector<std::string> &filament_colours)
|
void MixedFilamentManager::auto_generate(const std::vector<std::string> &filament_colours)
|
||||||
{
|
{
|
||||||
// Keep a copy of the old list so we can preserve user-modified ratios and
|
// Keep a copy of the old list so we can preserve user-modified ratios and
|
||||||
@@ -818,8 +835,6 @@ void MixedFilamentManager::auto_generate(const std::vector<std::string> &filamen
|
|||||||
m_mixed.clear();
|
m_mixed.clear();
|
||||||
|
|
||||||
const size_t n = filament_colours.size();
|
const size_t n = filament_colours.size();
|
||||||
if (n < 2)
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::vector<MixedFilament> custom_rows;
|
std::vector<MixedFilament> custom_rows;
|
||||||
custom_rows.reserve(old.size());
|
custom_rows.reserve(old.size());
|
||||||
@@ -837,6 +852,13 @@ void MixedFilamentManager::auto_generate(const std::vector<std::string> &filamen
|
|||||||
custom_rows.push_back(std::move(custom));
|
custom_rows.push_back(std::move(custom));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (n < 2 || !auto_generate_enabled()) {
|
||||||
|
for (MixedFilament &mf : custom_rows)
|
||||||
|
m_mixed.push_back(std::move(mf));
|
||||||
|
refresh_display_colors(filament_colours);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Generate all C(N,2) pairwise combinations.
|
// Generate all C(N,2) pairwise combinations.
|
||||||
for (size_t i = 0; i < n; ++i) {
|
for (size_t i = 0; i < n; ++i) {
|
||||||
for (size_t j = i + 1; j < n; ++j) {
|
for (size_t j = i + 1; j < n; ++j) {
|
||||||
|
|||||||
@@ -116,6 +116,9 @@ class MixedFilamentManager
|
|||||||
public:
|
public:
|
||||||
MixedFilamentManager() = default;
|
MixedFilamentManager() = default;
|
||||||
|
|
||||||
|
static void set_auto_generate_enabled(bool enabled);
|
||||||
|
static bool auto_generate_enabled();
|
||||||
|
|
||||||
// ---- Auto-generation ------------------------------------------------
|
// ---- Auto-generation ------------------------------------------------
|
||||||
|
|
||||||
// Rebuild the mixed-filament list from the current set of physical
|
// Rebuild the mixed-filament list from the current set of physical
|
||||||
|
|||||||
@@ -3469,7 +3469,7 @@ void PresetBundle::update_multi_material_filament_presets(size_t to_delete_filam
|
|||||||
// Build old->new filament ID remap for painted facet data normalization.
|
// Build old->new filament ID remap for painted facet data normalization.
|
||||||
// This is needed for both deletion and addition of physical filaments so
|
// This is needed for both deletion and addition of physical filaments so
|
||||||
// painted mixed states keep pointing at the same virtual mixed entries.
|
// painted mixed states keep pointing at the same virtual mixed entries.
|
||||||
if (old_num_filaments != num_filaments || deleting_filament)
|
if (old_num_filaments != num_filaments || deleting_filament || old_mixed != this->mixed_filaments.mixed_filaments())
|
||||||
build_filament_id_remap(old_mixed, old_num_filaments, num_filaments, deleting_filament,
|
build_filament_id_remap(old_mixed, old_num_filaments, num_filaments, deleting_filament,
|
||||||
deleting_filament ? unsigned(to_delete_filament_id + 1) : 0u);
|
deleting_filament ? unsigned(to_delete_filament_id + 1) : 0u);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "slic3r/GUI/WCPDownloadManager.hpp"
|
#include "slic3r/GUI/WCPDownloadManager.hpp"
|
||||||
#include "slic3r/Utils/PresetUpdater.hpp"
|
#include "slic3r/Utils/PresetUpdater.hpp"
|
||||||
#include "slic3r/Config/Version.hpp"
|
#include "slic3r/Config/Version.hpp"
|
||||||
|
#include "libslic3r/MixedFilament.hpp"
|
||||||
|
|
||||||
// Localization headers: include libslic3r version first so everything in this file
|
// Localization headers: include libslic3r version first so everything in this file
|
||||||
// uses the slic3r/GUI version (the macros will take precedence over the functions).
|
// uses the slic3r/GUI version (the macros will take precedence over the functions).
|
||||||
@@ -2130,6 +2131,7 @@ void GUI_App::init_app_config()
|
|||||||
}
|
}
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
}
|
}
|
||||||
|
MixedFilamentManager::set_auto_generate_enabled(app_config->get_bool("auto_generate_gradients"));
|
||||||
set_logging_level(Slic3r::level_string_to_boost(app_config->get("log_severity_level")));
|
set_logging_level(Slic3r::level_string_to_boost(app_config->get("log_severity_level")));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1352,6 +1352,7 @@ Sidebar::Sidebar(Plater *parent)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
int filament_count = p->combos_filament.size() + 1;
|
int filament_count = p->combos_filament.size() + 1;
|
||||||
|
wxGetApp().plater()->confirm_auto_generated_gradients(filament_count);
|
||||||
wxColour new_col = Plater::get_next_color_for_filament();
|
wxColour new_col = Plater::get_next_color_for_filament();
|
||||||
std::string new_color = new_col.GetAsString(wxC2S_HTML_SYNTAX).ToStdString();
|
std::string new_color = new_col.GetAsString(wxC2S_HTML_SYNTAX).ToStdString();
|
||||||
wxGetApp().preset_bundle->set_num_filaments(filament_count, new_color);
|
wxGetApp().preset_bundle->set_num_filaments(filament_count, new_color);
|
||||||
@@ -6878,7 +6879,7 @@ void Sidebar::update_mixed_filament_panel(bool sync_manager)
|
|||||||
if (p->m_btn_add_color)
|
if (p->m_btn_add_color)
|
||||||
p->m_btn_add_color->Enable(num_physical >= 2);
|
p->m_btn_add_color->Enable(num_physical >= 2);
|
||||||
|
|
||||||
if (mixed.empty()) {
|
if (num_physical < 2) {
|
||||||
p->m_panel_mixed_filaments_title->Hide();
|
p->m_panel_mixed_filaments_title->Hide();
|
||||||
p->m_panel_mixed_filaments_content->Hide();
|
p->m_panel_mixed_filaments_content->Hide();
|
||||||
Layout();
|
Layout();
|
||||||
@@ -6900,6 +6901,27 @@ void Sidebar::update_mixed_filament_panel(bool sync_manager)
|
|||||||
auto *rows_sizer = new wxBoxSizer(wxVERTICAL);
|
auto *rows_sizer = new wxBoxSizer(wxVERTICAL);
|
||||||
rows_scroller->SetSizer(rows_sizer);
|
rows_scroller->SetSizer(rows_sizer);
|
||||||
|
|
||||||
|
if (mixed.empty()) {
|
||||||
|
auto *empty_label = new wxStaticText(rows_scroller, wxID_ANY,
|
||||||
|
_L("No mixed filaments yet. Use Add Gradient, Add Pattern, or Add Color to create one."));
|
||||||
|
empty_label->SetForegroundColour(mixed_summary_fg);
|
||||||
|
empty_label->SetFont(::Label::Body_13);
|
||||||
|
empty_label->Wrap(FromDIP(360));
|
||||||
|
rows_sizer->Add(empty_label, 0, wxALL | wxEXPAND, FromDIP(12));
|
||||||
|
rows_scroller->Layout();
|
||||||
|
rows_scroller->FitInside();
|
||||||
|
const int empty_content_h = empty_label->GetBestSize().GetHeight() + FromDIP(28);
|
||||||
|
const int empty_rows_h = std::max(FromDIP(86), empty_content_h);
|
||||||
|
rows_scroller->SetMinSize(wxSize(-1, empty_rows_h));
|
||||||
|
rows_scroller->SetMaxSize(wxSize(-1, empty_rows_h));
|
||||||
|
if (content_sizer)
|
||||||
|
content_sizer->Add(rows_scroller, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(SidebarProps::ContentMargin()));
|
||||||
|
p->m_panel_mixed_filaments_content->Layout();
|
||||||
|
Layout();
|
||||||
|
refresh_model_canvas_colors();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto adjust_rows_scroller_height = [this, rows_scroller]() {
|
auto adjust_rows_scroller_height = [this, rows_scroller]() {
|
||||||
if (!rows_scroller)
|
if (!rows_scroller)
|
||||||
return;
|
return;
|
||||||
@@ -7581,6 +7603,7 @@ void Sidebar::add_custom_filament(wxColour new_col) {
|
|||||||
if (p->combos_filament.size() >= MAXIMUM_EXTRUDER_NUMBER) return;
|
if (p->combos_filament.size() >= MAXIMUM_EXTRUDER_NUMBER) return;
|
||||||
|
|
||||||
int filament_count = p->combos_filament.size() + 1;
|
int filament_count = p->combos_filament.size() + 1;
|
||||||
|
wxGetApp().plater()->confirm_auto_generated_gradients(filament_count);
|
||||||
std::string new_color = new_col.GetAsString(wxC2S_HTML_SYNTAX).ToStdString();
|
std::string new_color = new_col.GetAsString(wxC2S_HTML_SYNTAX).ToStdString();
|
||||||
wxGetApp().preset_bundle->set_num_filaments(filament_count, new_color);
|
wxGetApp().preset_bundle->set_num_filaments(filament_count, new_color);
|
||||||
wxGetApp().plater()->on_filaments_change(filament_count);
|
wxGetApp().plater()->on_filaments_change(filament_count);
|
||||||
@@ -8409,9 +8432,13 @@ struct Plater::priv
|
|||||||
static const std::regex pattern_prusa;
|
static const std::regex pattern_prusa;
|
||||||
|
|
||||||
bool m_is_dark = false;
|
bool m_is_dark = false;
|
||||||
|
size_t m_last_auto_gradient_prompt_physical_count = 0;
|
||||||
|
bool m_last_auto_gradient_prompt_accepted = false;
|
||||||
|
|
||||||
priv(Plater *q, MainFrame *main_frame);
|
priv(Plater *q, MainFrame *main_frame);
|
||||||
~priv();
|
~priv();
|
||||||
|
bool confirm_auto_generated_gradients(wxWindow *parent, size_t num_physical);
|
||||||
|
void set_auto_generated_gradient_decision(size_t num_physical, bool create_auto_gradients);
|
||||||
|
|
||||||
|
|
||||||
bool need_update() const { return m_need_update; }
|
bool need_update() const { return m_need_update; }
|
||||||
@@ -9933,9 +9960,10 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||||||
"mixed_filament_surface_indentation"
|
"mixed_filament_surface_indentation"
|
||||||
};
|
};
|
||||||
preset_bundle->project_config.apply_only(config_loaded, imported_project_option_keys, true);
|
preset_bundle->project_config.apply_only(config_loaded, imported_project_option_keys, true);
|
||||||
if (current_num_filaments != desired_physical_filaments)
|
if (current_num_filaments != desired_physical_filaments) {
|
||||||
|
q->confirm_auto_generated_gradients(desired_physical_filaments);
|
||||||
preset_bundle->set_num_filaments(unsigned(desired_physical_filaments));
|
preset_bundle->set_num_filaments(unsigned(desired_physical_filaments));
|
||||||
else
|
} else
|
||||||
preset_bundle->update_multi_material_filament_presets();
|
preset_bundle->update_multi_material_filament_presets();
|
||||||
BOOST_LOG_TRIVIAL(info) << "3MF geometry import applied imported project config"
|
BOOST_LOG_TRIVIAL(info) << "3MF geometry import applied imported project config"
|
||||||
<< " current_num_filaments=" << current_num_filaments
|
<< " current_num_filaments=" << current_num_filaments
|
||||||
@@ -9948,6 +9976,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||||||
new_colors.assign(imported_filament_colors.begin() + current_num_filaments,
|
new_colors.assign(imported_filament_colors.begin() + current_num_filaments,
|
||||||
imported_filament_colors.begin() + desired_physical_filaments);
|
imported_filament_colors.begin() + desired_physical_filaments);
|
||||||
}
|
}
|
||||||
|
q->confirm_auto_generated_gradients(desired_physical_filaments);
|
||||||
preset_bundle->set_num_filaments(unsigned(desired_physical_filaments), new_colors);
|
preset_bundle->set_num_filaments(unsigned(desired_physical_filaments), new_colors);
|
||||||
wxGetApp().plater()->on_filaments_change(desired_physical_filaments);
|
wxGetApp().plater()->on_filaments_change(desired_physical_filaments);
|
||||||
}
|
}
|
||||||
@@ -9956,6 +9985,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||||||
int filament_size = sidebar->combos_filament().size();
|
int filament_size = sidebar->combos_filament().size();
|
||||||
while (filament_size < MAXIMUM_EXTRUDER_NUMBER && filament_size < size) {
|
while (filament_size < MAXIMUM_EXTRUDER_NUMBER && filament_size < size) {
|
||||||
int filament_count = filament_size + 1;
|
int filament_count = filament_size + 1;
|
||||||
|
wxGetApp().plater()->confirm_auto_generated_gradients(filament_count);
|
||||||
wxColour new_col = Plater::get_next_color_for_filament();
|
wxColour new_col = Plater::get_next_color_for_filament();
|
||||||
std::string new_color = new_col.GetAsString(wxC2S_HTML_SYNTAX).ToStdString();
|
std::string new_color = new_col.GetAsString(wxC2S_HTML_SYNTAX).ToStdString();
|
||||||
wxGetApp().preset_bundle->set_num_filaments(filament_count, new_color);
|
wxGetApp().preset_bundle->set_num_filaments(filament_count, new_color);
|
||||||
@@ -13746,6 +13776,76 @@ void Plater::priv::on_repair_model(wxCommandEvent &event)
|
|||||||
wxGetApp().obj_list()->fix_through_netfabb();
|
wxGetApp().obj_list()->fix_through_netfabb();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Plater::priv::confirm_auto_generated_gradients(wxWindow *parent, size_t num_physical)
|
||||||
|
{
|
||||||
|
auto *app_config = wxGetApp().app_config;
|
||||||
|
if (app_config == nullptr)
|
||||||
|
return MixedFilamentManager::auto_generate_enabled();
|
||||||
|
|
||||||
|
const bool pref_enabled = app_config->get_bool("auto_generate_gradients");
|
||||||
|
if (!pref_enabled) {
|
||||||
|
m_last_auto_gradient_prompt_physical_count = 0;
|
||||||
|
m_last_auto_gradient_prompt_accepted = false;
|
||||||
|
MixedFilamentManager::set_auto_generate_enabled(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_physical <= 4) {
|
||||||
|
m_last_auto_gradient_prompt_physical_count = 0;
|
||||||
|
m_last_auto_gradient_prompt_accepted = false;
|
||||||
|
MixedFilamentManager::set_auto_generate_enabled(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent == nullptr || !parent->IsShownOnScreen()) {
|
||||||
|
m_last_auto_gradient_prompt_physical_count = 0;
|
||||||
|
m_last_auto_gradient_prompt_accepted = false;
|
||||||
|
MixedFilamentManager::set_auto_generate_enabled(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_last_auto_gradient_prompt_physical_count == num_physical) {
|
||||||
|
MixedFilamentManager::set_auto_generate_enabled(m_last_auto_gradient_prompt_accepted);
|
||||||
|
return m_last_auto_gradient_prompt_accepted;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t auto_gradient_count = num_physical * (num_physical - 1) / 2;
|
||||||
|
const wxString message = wxString::Format(
|
||||||
|
_L("Using %d physical filaments will create %d auto-generated gradients.\nDo you want to create them now?"),
|
||||||
|
int(num_physical),
|
||||||
|
int(auto_gradient_count));
|
||||||
|
const int result = MessageDialog(parent,
|
||||||
|
message,
|
||||||
|
wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Auto gradients"),
|
||||||
|
wxYES_NO | wxYES_DEFAULT | wxCENTRE | wxICON_QUESTION)
|
||||||
|
.ShowModal();
|
||||||
|
const bool accepted = result == wxID_YES;
|
||||||
|
m_last_auto_gradient_prompt_physical_count = num_physical;
|
||||||
|
m_last_auto_gradient_prompt_accepted = accepted;
|
||||||
|
MixedFilamentManager::set_auto_generate_enabled(accepted);
|
||||||
|
return accepted;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Plater::priv::set_auto_generated_gradient_decision(size_t num_physical, bool create_auto_gradients)
|
||||||
|
{
|
||||||
|
m_last_auto_gradient_prompt_physical_count = num_physical;
|
||||||
|
m_last_auto_gradient_prompt_accepted = create_auto_gradients;
|
||||||
|
MixedFilamentManager::set_auto_generate_enabled(create_auto_gradients);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Plater::confirm_auto_generated_gradients(size_t num_physical)
|
||||||
|
{
|
||||||
|
return p != nullptr ? p->confirm_auto_generated_gradients(this, num_physical) : MixedFilamentManager::auto_generate_enabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Plater::set_auto_generated_gradient_decision(size_t num_physical, bool create_auto_gradients)
|
||||||
|
{
|
||||||
|
if (p != nullptr)
|
||||||
|
p->set_auto_generated_gradient_decision(num_physical, create_auto_gradients);
|
||||||
|
else
|
||||||
|
MixedFilamentManager::set_auto_generate_enabled(create_auto_gradients);
|
||||||
|
}
|
||||||
|
|
||||||
void Plater::priv::on_filament_color_changed(wxCommandEvent &event)
|
void Plater::priv::on_filament_color_changed(wxCommandEvent &event)
|
||||||
{
|
{
|
||||||
//q->update_all_plate_thumbnails(true);
|
//q->update_all_plate_thumbnails(true);
|
||||||
@@ -13760,7 +13860,9 @@ void Plater::priv::on_filament_color_changed(wxCommandEvent &event)
|
|||||||
sidebar->auto_calc_flushing_volumes(modify_id);
|
sidebar->auto_calc_flushing_volumes(modify_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regenerate mixed filaments and update the sidebar panel.
|
// Regenerate mixed filaments and refresh the mixed panel only. Color
|
||||||
|
// changes do not alter filament IDs, so the full on_filaments_change()
|
||||||
|
// path is unnecessary and can re-enter UI rebuilds mid-update.
|
||||||
wxGetApp().preset_bundle->update_multi_material_filament_presets();
|
wxGetApp().preset_bundle->update_multi_material_filament_presets();
|
||||||
sidebar->update_mixed_filament_panel();
|
sidebar->update_mixed_filament_panel();
|
||||||
}
|
}
|
||||||
@@ -19647,6 +19749,8 @@ void Plater::on_filaments_change(size_t num_filaments)
|
|||||||
update_filament_colors_in_full_config();
|
update_filament_colors_in_full_config();
|
||||||
|
|
||||||
const size_t old_num_filaments = sidebar().combos_filament().size();
|
const size_t old_num_filaments = sidebar().combos_filament().size();
|
||||||
|
const bool auto_generate_before = MixedFilamentManager::auto_generate_enabled();
|
||||||
|
const bool allow_auto_gradients = p->confirm_auto_generated_gradients(this, num_filaments);
|
||||||
auto summarize_uint_vector = [](const std::vector<unsigned int> &values, size_t max_items = 24) {
|
auto summarize_uint_vector = [](const std::vector<unsigned int> &values, size_t max_items = 24) {
|
||||||
std::string out = "[";
|
std::string out = "[";
|
||||||
const size_t n = std::min(values.size(), max_items);
|
const size_t n = std::min(values.size(), max_items);
|
||||||
@@ -19681,6 +19785,8 @@ void Plater::on_filaments_change(size_t num_filaments)
|
|||||||
return out;
|
return out;
|
||||||
};
|
};
|
||||||
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
|
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
|
||||||
|
if (preset_bundle != nullptr && auto_generate_before && !allow_auto_gradients)
|
||||||
|
preset_bundle->update_multi_material_filament_presets(size_t(-1), old_num_filaments);
|
||||||
// Consume remap before sidebar refresh, which may trigger config sync
|
// Consume remap before sidebar refresh, which may trigger config sync
|
||||||
// paths that regenerate mixed filaments and clear this remap buffer.
|
// paths that regenerate mixed filaments and clear this remap buffer.
|
||||||
std::vector<unsigned int> id_remap;
|
std::vector<unsigned int> id_remap;
|
||||||
|
|||||||
@@ -492,6 +492,8 @@ public:
|
|||||||
|
|
||||||
void on_filaments_change(size_t extruders_count);
|
void on_filaments_change(size_t extruders_count);
|
||||||
void on_filaments_delete(size_t extruders_count, size_t filament_id, int replace_filament_id = -1);
|
void on_filaments_delete(size_t extruders_count, size_t filament_id, int replace_filament_id = -1);
|
||||||
|
bool confirm_auto_generated_gradients(size_t num_physical);
|
||||||
|
void set_auto_generated_gradient_decision(size_t num_physical, bool create_auto_gradients);
|
||||||
// BBS
|
// BBS
|
||||||
void on_bed_type_change(BedType bed_type);
|
void on_bed_type_change(BedType bed_type);
|
||||||
bool update_filament_colors_in_full_config();
|
bool update_filament_colors_in_full_config();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "MsgDialog.hpp"
|
#include "MsgDialog.hpp"
|
||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
#include "libslic3r/AppConfig.hpp"
|
#include "libslic3r/AppConfig.hpp"
|
||||||
|
#include "libslic3r/MixedFilament.hpp"
|
||||||
#include <wx/language.h>
|
#include <wx/language.h>
|
||||||
#include <wx/notebook.h>
|
#include <wx/notebook.h>
|
||||||
#include "Notebook.hpp"
|
#include "Notebook.hpp"
|
||||||
@@ -778,6 +779,16 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxWindow *pa
|
|||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " sync_user_preset: " << (sync ? "true" : "false");
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " sync_user_preset: " << (sync ? "true" : "false");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (param == "auto_generate_gradients") {
|
||||||
|
MixedFilamentManager::set_auto_generate_enabled(checkbox->GetValue());
|
||||||
|
if (wxGetApp().preset_bundle != nullptr && wxGetApp().plater() != nullptr) {
|
||||||
|
const size_t num_physical = wxGetApp().preset_bundle->filament_presets.size();
|
||||||
|
wxGetApp().plater()->set_auto_generated_gradient_decision(num_physical, checkbox->GetValue());
|
||||||
|
wxGetApp().preset_bundle->update_multi_material_filament_presets();
|
||||||
|
wxGetApp().plater()->on_filaments_change(num_physical);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __WXMSW__
|
#ifdef __WXMSW__
|
||||||
if (param == "associate_3mf") {
|
if (param == "associate_3mf") {
|
||||||
bool pbool = app_config->get("associate_3mf") == "true" ? true : false;
|
bool pbool = app_config->get("associate_3mf") == "true" ? true : false;
|
||||||
@@ -1041,6 +1052,8 @@ PreferencesDialog::PreferencesDialog(wxWindow *parent, wxWindowID id, const wxSt
|
|||||||
j["auto_flushing"] = value;
|
j["auto_flushing"] = value;
|
||||||
value = wxGetApp().app_config->get("auto_calculate_when_filament_change");
|
value = wxGetApp().app_config->get("auto_calculate_when_filament_change");
|
||||||
j["auto_calculate_when_filament_change"] = value;
|
j["auto_calculate_when_filament_change"] = value;
|
||||||
|
value = wxGetApp().app_config->get("auto_generate_gradients");
|
||||||
|
j["auto_generate_gradients"] = value;
|
||||||
agent->track_event("preferences_changed", j.dump());
|
agent->track_event("preferences_changed", j.dump());
|
||||||
}
|
}
|
||||||
} catch(...) {}
|
} catch(...) {}
|
||||||
@@ -1228,6 +1241,8 @@ wxWindow* PreferencesDialog::create_general_page()
|
|||||||
|
|
||||||
auto item_calc_mode = create_item_checkbox(_L("Flushing volumes: Auto-calculate every time the color changed."), page, _L("If enabled, auto-calculate every time the color changed."), 50, "auto_calculate");
|
auto item_calc_mode = create_item_checkbox(_L("Flushing volumes: Auto-calculate every time the color changed."), page, _L("If enabled, auto-calculate every time the color changed."), 50, "auto_calculate");
|
||||||
auto item_calc_in_long_retract = create_item_checkbox(_L("Flushing volumes: Auto-calculate every time when the filament is changed."), page, _L("If enabled, auto-calculate every time when filament is changed"), 50, "auto_calculate_when_filament_change");
|
auto item_calc_in_long_retract = create_item_checkbox(_L("Flushing volumes: Auto-calculate every time when the filament is changed."), page, _L("If enabled, auto-calculate every time when filament is changed"), 50, "auto_calculate_when_filament_change");
|
||||||
|
auto item_auto_generate_gradients = create_item_checkbox(_L("Mixed filaments: Auto-generate gradients."), page, _L("If enabled, Snapmaker Orca automatically creates gradient mixed filaments from physical filament pairs."), 50, "auto_generate_gradients");
|
||||||
|
auto title_full_spectrum = create_item_title(_devL("FullSpectrum"), page, _devL("FullSpectrum"));
|
||||||
auto item_remember_printer_config = create_item_checkbox(_L("Remember printer configuration"), page, _L("If enabled, Orca will remember and switch filament/process configuration for each printer automatically."), 50, "remember_printer_config");
|
auto item_remember_printer_config = create_item_checkbox(_L("Remember printer configuration"), page, _L("If enabled, Orca will remember and switch filament/process configuration for each printer automatically."), 50, "remember_printer_config");
|
||||||
auto item_step_mesh_setting = create_item_checkbox(_L("Show the step mesh parameter setting dialog."), page, _L("If enabled,a parameter settings dialog will appear during STEP file import."), 50, "enable_step_mesh_setting");
|
auto item_step_mesh_setting = create_item_checkbox(_L("Show the step mesh parameter setting dialog."), page, _L("If enabled,a parameter settings dialog will appear during STEP file import."), 50, "enable_step_mesh_setting");
|
||||||
auto item_multi_machine = create_item_checkbox(_L("Multi-device Management (Take effect after restarting Snapmaker Orca)."), page, _L("With this option enabled, you can send a task to multiple devices at the same time and manage multiple devices."), 50, "enable_multi_machine");
|
auto item_multi_machine = create_item_checkbox(_L("Multi-device Management (Take effect after restarting Snapmaker Orca)."), page, _L("With this option enabled, you can send a task to multiple devices at the same time and manage multiple devices."), 50, "enable_multi_machine");
|
||||||
@@ -1328,16 +1343,18 @@ wxWindow* PreferencesDialog::create_general_page()
|
|||||||
sizer_page->Add(camera_orbit_mult, 0, wxTOP, FromDIP(3));
|
sizer_page->Add(camera_orbit_mult, 0, wxTOP, FromDIP(3));
|
||||||
sizer_page->Add(item_show_splash_screen, 0, wxTOP, FromDIP(3));
|
sizer_page->Add(item_show_splash_screen, 0, wxTOP, FromDIP(3));
|
||||||
sizer_page->Add(item_hints, 0, wxTOP, FromDIP(3));
|
sizer_page->Add(item_hints, 0, wxTOP, FromDIP(3));
|
||||||
sizer_page->Add(item_calc_in_long_retract, 0, wxTOP, FromDIP(3));
|
|
||||||
sizer_page->Add(item_multi_machine, 0, wxTOP, FromDIP(3));
|
sizer_page->Add(item_multi_machine, 0, wxTOP, FromDIP(3));
|
||||||
sizer_page->Add(item_step_mesh_setting, 0, wxTOP, FromDIP(3));
|
sizer_page->Add(item_step_mesh_setting, 0, wxTOP, FromDIP(3));
|
||||||
sizer_page->Add(item_auto_arrange, 0, wxTOP, FromDIP(3));
|
sizer_page->Add(item_auto_arrange, 0, wxTOP, FromDIP(3));
|
||||||
sizer_page->Add(title_presets, 0, wxTOP | wxEXPAND, FromDIP(20));
|
sizer_page->Add(title_presets, 0, wxTOP | wxEXPAND, FromDIP(20));
|
||||||
sizer_page->Add(item_calc_mode, 0, wxTOP, FromDIP(3));
|
sizer_page->Add(item_calc_mode, 0, wxTOP, FromDIP(3));
|
||||||
|
sizer_page->Add(item_calc_in_long_retract, 0, wxTOP, FromDIP(3));
|
||||||
sizer_page->Add(item_user_sync, 0, wxTOP, FromDIP(3));
|
sizer_page->Add(item_user_sync, 0, wxTOP, FromDIP(3));
|
||||||
sizer_page->Add(item_system_sync, 0, wxTOP, FromDIP(3));
|
sizer_page->Add(item_system_sync, 0, wxTOP, FromDIP(3));
|
||||||
sizer_page->Add(item_remember_printer_config, 0, wxTOP, FromDIP(3));
|
sizer_page->Add(item_remember_printer_config, 0, wxTOP, FromDIP(3));
|
||||||
sizer_page->Add(item_save_presets, 0, wxTOP, FromDIP(3));
|
sizer_page->Add(item_save_presets, 0, wxTOP, FromDIP(3));
|
||||||
|
sizer_page->Add(title_full_spectrum, 0, wxTOP | wxEXPAND, FromDIP(20));
|
||||||
|
sizer_page->Add(item_auto_generate_gradients, 0, wxTOP, FromDIP(3));
|
||||||
//sizer_page->Add(title_network, 0, wxTOP | wxEXPAND, FromDIP(20));
|
//sizer_page->Add(title_network, 0, wxTOP | wxEXPAND, FromDIP(20));
|
||||||
//sizer_page->Add(item_check_stable_version_only, 0, wxTOP, FromDIP(3));
|
//sizer_page->Add(item_check_stable_version_only, 0, wxTOP, FromDIP(3));
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,22 @@ static unsigned int virtual_id_for_stable_id(const std::vector<MixedFilament> &m
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct MixedAutoGenerateGuard
|
||||||
|
{
|
||||||
|
explicit MixedAutoGenerateGuard(bool enabled)
|
||||||
|
: previous(MixedFilamentManager::auto_generate_enabled())
|
||||||
|
{
|
||||||
|
MixedFilamentManager::set_auto_generate_enabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
~MixedAutoGenerateGuard()
|
||||||
|
{
|
||||||
|
MixedFilamentManager::set_auto_generate_enabled(previous);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool previous = true;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST_CASE("Mixed filament remap follows stable row ids when same-pair rows reorder", "[MixedFilament]")
|
TEST_CASE("Mixed filament remap follows stable row ids when same-pair rows reorder", "[MixedFilament]")
|
||||||
@@ -146,6 +162,32 @@ TEST_CASE("Mixed filament grouped manual patterns normalize and round-trip", "[M
|
|||||||
CHECK(loaded.mixed_filaments().front().mix_b_percent == 13);
|
CHECK(loaded.mixed_filaments().front().mix_b_percent == 13);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Mixed filament auto generation can be disabled without dropping custom rows", "[MixedFilament]")
|
||||||
|
{
|
||||||
|
const std::vector<std::string> colors = {"#FF0000", "#00FF00", "#0000FF"};
|
||||||
|
|
||||||
|
MixedFilamentManager enabled_mgr;
|
||||||
|
enabled_mgr.auto_generate(colors);
|
||||||
|
REQUIRE(enabled_mgr.mixed_filaments().size() == 3);
|
||||||
|
const std::string serialized_auto_rows = enabled_mgr.serialize_custom_entries();
|
||||||
|
|
||||||
|
MixedAutoGenerateGuard guard(false);
|
||||||
|
|
||||||
|
MixedFilamentManager mgr;
|
||||||
|
mgr.add_custom_filament(1, 2, 50, colors);
|
||||||
|
REQUIRE(mgr.mixed_filaments().size() == 1);
|
||||||
|
|
||||||
|
mgr.auto_generate(colors);
|
||||||
|
REQUIRE(mgr.mixed_filaments().size() == 1);
|
||||||
|
CHECK(mgr.mixed_filaments().front().custom);
|
||||||
|
CHECK(mgr.mixed_filaments().front().component_a == 1);
|
||||||
|
CHECK(mgr.mixed_filaments().front().component_b == 2);
|
||||||
|
|
||||||
|
MixedFilamentManager loaded;
|
||||||
|
loaded.load_custom_entries(serialized_auto_rows, colors);
|
||||||
|
CHECK(loaded.mixed_filaments().empty());
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("Mixed filament perimeter resolver uses grouped manual patterns by inset", "[MixedFilament]")
|
TEST_CASE("Mixed filament perimeter resolver uses grouped manual patterns by inset", "[MixedFilament]")
|
||||||
{
|
{
|
||||||
const std::vector<std::string> colors = {"#00FFFF", "#FF00FF"};
|
const std::vector<std::string> colors = {"#00FFFF", "#FF00FF"};
|
||||||
|
|||||||
Reference in New Issue
Block a user