mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-17 16:02:11 +00:00
Merge branch 'main' into feat/plugin-feature
This commit is contained in:
@@ -212,14 +212,26 @@ void install_slice_context_log_sink()
|
||||
// -v). Unlike the static reference/placeholder checks, this expands every custom *_gcode - including
|
||||
// change_filament_gcode at the one filament change - against the printer's fully-resolved config, so
|
||||
// undefined-placeholder / invalid-flow bugs surface here. Reports every offending printer and returns 1
|
||||
// if any failed, 0 otherwise. The sweep is SEQUENTIAL by necessity: Print::process() keeps
|
||||
// process-global state, so slicing printers concurrently in one process races even with per-slice
|
||||
// Model+Print. Load in validation mode so the vendors are read straight from the -p profiles dir
|
||||
// (no data_dir/system tree) and -v scoping is honoured for free.
|
||||
int slice_all_printers(const std::string &vendor)
|
||||
// if any failed, 0 otherwise. When outdir is non-empty, each printer's g-code is also written there as
|
||||
// "<vendor>__<printer>.gcode" for manual inspection. The sweep is SEQUENTIAL by necessity:
|
||||
// Print::process() keeps process-global state, so slicing printers concurrently in one process races
|
||||
// even with per-slice Model+Print. Load in validation mode so the vendors are read straight from the -p
|
||||
// profiles dir (no data_dir/system tree) and -v scoping is honoured for free.
|
||||
int slice_all_printers(const std::string &vendor, const std::string &outdir)
|
||||
{
|
||||
install_slice_context_log_sink();
|
||||
|
||||
if (!outdir.empty()) {
|
||||
boost::system::error_code ec;
|
||||
fs::create_directories(outdir, ec);
|
||||
if (ec) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Could not create output directory \"" << outdir << "\": " << ec.message();
|
||||
std::cout << "Validation failed" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
std::cout << "Saving sliced g-code to " << outdir << std::endl;
|
||||
}
|
||||
|
||||
PresetBundle bundle;
|
||||
bundle.set_is_validation_mode(true);
|
||||
bundle.set_vendor_to_validate(vendor); // empty == all vendors
|
||||
@@ -323,6 +335,10 @@ int slice_all_printers(const std::string &vendor)
|
||||
|
||||
try {
|
||||
const std::string out = slice_two_color_cube_and_export(cfg, bundle.is_bbl_vendor());
|
||||
if (!outdir.empty() && !out.empty()) {
|
||||
const fs::path f = fs::path(outdir) / (sanitize_filename(vendor_name) + "__" + sanitize_filename(printer) + ".gcode");
|
||||
save_string_file(f, out);
|
||||
}
|
||||
if (out.empty() || out.find("G1") == std::string::npos) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Printer \"" << printer << "\" produced no g-code";
|
||||
++failures;
|
||||
@@ -364,6 +380,7 @@ int main(int argc, char* argv[])
|
||||
("vendor,v", po::value<std::string>()->default_value(""), "Vendor name. Optional, all profiles present in the folder will be validated if not specified")
|
||||
("generate_presets,g", po::value<bool>()->default_value(false), "Generate user presets for mock test")
|
||||
("slice,s", po::bool_switch()->default_value(false), "Slice a two-colour cube through every printer to expand all custom g-code (catches placeholder/flow errors that static checks miss). Off unless this flag is present.")
|
||||
("outdir,o", po::value<std::string>()->default_value(""), "With -s, also save each printer's g-code to this folder (as <vendor>__<printer>.gcode) for manual inspection. Optional.")
|
||||
("check_filament_subtypes,f", po::bool_switch()->default_value(false), "Also flag printers with duplicate (ambiguous) filament subtypes. Off unless this flag is present.")
|
||||
("log_level,l", po::value<int>()->default_value(2), "Log level. Optional, default is 2 (warning). Higher values produce more detailed logs.");
|
||||
// clang-format on
|
||||
@@ -389,6 +406,7 @@ int main(int argc, char* argv[])
|
||||
int log_level = vm["log_level"].as<int>();
|
||||
bool generate_user_preset = vm["generate_presets"].as<bool>();
|
||||
bool slice_mode = vm["slice"].as<bool>();
|
||||
std::string slice_outdir = vm["outdir"].as<std::string>();
|
||||
bool check_filament_subtypes = vm["check_filament_subtypes"].as<bool>();
|
||||
|
||||
// check if path is valid, and return error if not
|
||||
@@ -419,7 +437,7 @@ int main(int argc, char* argv[])
|
||||
// Slice mode expands every printer's custom g-code by actually slicing (see slice_all_printers).
|
||||
// A distinct opt-in mode so the default static checks stay fast for every profile PR.
|
||||
if (slice_mode)
|
||||
return slice_all_printers(vendor);
|
||||
return slice_all_printers(vendor, slice_outdir);
|
||||
|
||||
auto preset_bundle = new PresetBundle();
|
||||
// preset_bundle->setup_directories();
|
||||
|
||||
@@ -352,8 +352,7 @@ namespace Slic3r
|
||||
int k,
|
||||
const std::vector<unsigned int>& used_filaments,
|
||||
const std::unordered_map<int, std::vector<int>>& unplaceable_limits,
|
||||
int* cost,
|
||||
int timeout_ms)
|
||||
int* cost)
|
||||
{
|
||||
auto distance_evaluator = std::make_shared<FlushDistanceEvaluator>(ctx.model_info.flush_matrix, used_filaments, ctx.model_info.layer_filaments);
|
||||
KMediods PAM(k, (int)used_filaments.size(), distance_evaluator, ctx.machine_info.master_extruder_id);
|
||||
@@ -369,7 +368,7 @@ namespace Slic3r
|
||||
}
|
||||
PAM.set_cluster_group_size(cluster_size_limit);
|
||||
|
||||
PAM.do_clustering(ctx, timeout_ms, 30);
|
||||
PAM.do_clustering(ctx, m_clustering_budget);
|
||||
|
||||
m_memoryed_heap = PAM.get_memoryed_groups();
|
||||
|
||||
@@ -793,7 +792,7 @@ namespace Slic3r
|
||||
2.1 In each cluster, make the point that minimizes the sum of distances within the cluster the medoid
|
||||
2.2 Reassign each point to the cluster defined by the closest medoid determined in the previous step
|
||||
*/
|
||||
void KMediods::do_clustering(const FilamentGroupContext &context, int timeout_ms, int retry)
|
||||
void KMediods::do_clustering(const FilamentGroupContext& context, const ClusteringBudget& budget)
|
||||
{
|
||||
FlushTimeMachine T;
|
||||
T.time_machine_start();
|
||||
@@ -817,7 +816,11 @@ namespace Slic3r
|
||||
double best_cluster_cost = std::numeric_limits<double>::max();
|
||||
int retry_count = 0;
|
||||
|
||||
while (retry_count < retry && T.time_machine_end() < timeout_ms) {
|
||||
// Run at least one restart; otherwise every filament would stay in the default group.
|
||||
const int retry = std::max(1, budget.max_restarts);
|
||||
auto within_budget = [&]() { return budget.timeout_ms <= 0 || T.time_machine_end() < budget.timeout_ms; };
|
||||
|
||||
while (retry_count < retry && within_budget()) {
|
||||
std::vector<int> curr_cluster_centers = init_cluster_center(m_placeable_limits, m_unplaceable_limits, m_max_cluster_size, m_cluster_group_size, retry_count);
|
||||
std::vector<int> curr_cluster_labels = assign_cluster_label(curr_cluster_centers, m_placeable_limits, m_unplaceable_limits, m_max_cluster_size, m_cluster_group_size);
|
||||
double curr_cluster_cost = evaluate_labels(curr_cluster_labels);
|
||||
@@ -826,7 +829,7 @@ namespace Slic3r
|
||||
update_memoryed_groups(g, memory_threshold, memoryed_groups);
|
||||
|
||||
bool mediods_changed = true;
|
||||
while (mediods_changed && T.time_machine_end() < timeout_ms) {
|
||||
while (mediods_changed && within_budget()) {
|
||||
mediods_changed = false;
|
||||
double best_swap_cost = curr_cluster_cost;
|
||||
int best_swap_cluster = -1;
|
||||
@@ -889,7 +892,7 @@ namespace Slic3r
|
||||
if (estimated < ENUM_THRESHOLD)
|
||||
result = calc_group_by_enum(k, used_filaments, unplaceable_limits, cost);
|
||||
else
|
||||
result = calc_group_by_kmedoids(k, used_filaments, unplaceable_limits, cost, 3000);
|
||||
result = calc_group_by_kmedoids(k, used_filaments, unplaceable_limits, cost);
|
||||
|
||||
change_memoryed_heaps_to_arrays(m_memoryed_heap, ctx.group_info.total_filament_num, used_filaments, m_memoryed_groups);
|
||||
|
||||
|
||||
@@ -142,6 +142,16 @@ namespace Slic3r
|
||||
FilamentGroupContext::SpeedInfo m_speed_info;
|
||||
};
|
||||
|
||||
// Search budget for the k-medoids clustering, an anytime search. Each restart is seeded from its
|
||||
// own index, so what it returns depends on how many restarts complete before the clock expires,
|
||||
// and therefore on the speed of the machine. A timeout_ms <= 0 removes the clock and bounds the
|
||||
// search by max_restarts alone.
|
||||
struct ClusteringBudget
|
||||
{
|
||||
int timeout_ms = 3000;
|
||||
int max_restarts = 30;
|
||||
};
|
||||
|
||||
class FilamentGroup
|
||||
{
|
||||
using MemoryedGroup = FilamentGroupUtils::MemoryedGroup;
|
||||
@@ -149,6 +159,8 @@ namespace Slic3r
|
||||
public:
|
||||
explicit FilamentGroup(const FilamentGroupContext& ctx_) :ctx(ctx_) {}
|
||||
public:
|
||||
void set_clustering_budget(const ClusteringBudget& budget) { m_clustering_budget = budget; }
|
||||
|
||||
std::vector<int> calc_filament_group(int * cost = nullptr);
|
||||
std::vector<std::vector<int>> get_memoryed_groups()const { return m_memoryed_groups; }
|
||||
|
||||
@@ -162,7 +174,7 @@ namespace Slic3r
|
||||
std::vector<int> calc_group_by_enum(int k, const std::vector<unsigned int>& used_filaments,
|
||||
const std::unordered_map<int, std::vector<int>>& unplaceable_limits, int* cost = nullptr);
|
||||
std::vector<int> calc_group_by_kmedoids(int k, const std::vector<unsigned int>& used_filaments,
|
||||
const std::unordered_map<int, std::vector<int>>& unplaceable_limits, int* cost = nullptr, int timeout_ms = 500);
|
||||
const std::unordered_map<int, std::vector<int>>& unplaceable_limits, int* cost = nullptr);
|
||||
|
||||
std::map<int, int> rebuild_unprintables(const std::vector<unsigned int>& used_filaments, const std::map<int,int>& extruder_unprintables);
|
||||
std::unordered_map<int, std::vector<int>> rebuild_nozzle_unprintables(const std::vector<unsigned int>& used_filaments, const std::unordered_map<int, std::vector<int>>& extruder_unprintables, const std::vector<int>& filament_volume_map);
|
||||
@@ -175,6 +187,7 @@ namespace Slic3r
|
||||
FilamentGroupContext ctx;
|
||||
MemoryedGroupHeap m_memoryed_heap;
|
||||
std::vector<std::vector<int>> m_memoryed_groups;
|
||||
ClusteringBudget m_clustering_budget;
|
||||
public:
|
||||
std::optional<std::function<bool(int, std::vector<int>&)>> get_custom_seq;
|
||||
};
|
||||
@@ -220,7 +233,7 @@ namespace Slic3r
|
||||
void set_memory_threshold(double threshold) { memory_threshold = threshold; }
|
||||
MemoryedGroupHeap get_memoryed_groups()const { return memoryed_groups; }
|
||||
|
||||
void do_clustering(const FilamentGroupContext& context, int timeout_ms = 100, int retry = 10);
|
||||
void do_clustering(const FilamentGroupContext& context, const ClusteringBudget& budget);
|
||||
std::vector<int> get_cluster_labels()const { return m_cluster_labels; }
|
||||
|
||||
protected:
|
||||
|
||||
Reference in New Issue
Block a user