OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
anonymous_namespace{OrcaSlicer_profile_validator.cpp} Namespace Reference

Typedefs

using Templates = std::set<std::pair<std::string, std::string>>

Functions

Vec2d printable_area_center (const DynamicPrintConfig &cfg)
Vec2d place_wipe_tower (DynamicPrintConfig &cfg, const Vec2d &center)
std::string slice_two_color_cube_and_export (DynamicPrintConfig cfg, bool is_bbl, bool by_object)
void select_printer_default_presets (PresetBundle &bundle)
void install_slice_context_log_sink ()
DynamicPrintConfig slice_config (PresetBundle &bundle)
std::string slice_selection (PresetBundle &bundle, const std::string &what, bool by_object, const std::string &outdir, const std::string &file_base)
Templates preset_templates (const DynamicPrintConfig &cfg)
int slice_all_printers (const std::string &vendor, const std::string &outdir)

Variables

static std::string g_slice_context

Typedef Documentation

◆ Templates

using anonymous_namespace{OrcaSlicer_profile_validator.cpp}::Templates = std::set<std::pair<std::string, std::string>>

Function Documentation

◆ install_slice_context_log_sink()

void anonymous_namespace{OrcaSlicer_profile_validator.cpp}::install_slice_context_log_sink ( )
313{
314 namespace logging = boost::log;
315 namespace sinks = boost::log::sinks;
316 namespace expr = boost::log::expressions;
317
318 auto backend = boost::make_shared<sinks::text_ostream_backend>();
319 backend->add_stream(boost::shared_ptr<std::ostream>(&std::clog, boost::null_deleter()));
320 backend->auto_flush(true);
321
322 auto sink = boost::make_shared<sinks::synchronous_sink<sinks::text_ostream_backend>>(backend);
323 sink->set_formatter([](const logging::record_view &rec, logging::formatting_ostream &strm) {
324 strm << "[" << rec[logging::trivial::severity] << "]";
325 if (!g_slice_context.empty())
326 strm << " [" << g_slice_context << "]";
327 strm << " " << rec[expr::smessage];
328 });
329
330 logging::core::get()->remove_all_sinks(); // drop the default trivial sink so lines are not doubled
331 logging::core::get()->add_sink(sink);
332}
static std::string g_slice_context
Definition OrcaSlicer_profile_validator.cpp:306

References g_slice_context.

Referenced by slice_all_printers().

Here is the caller graph for this function:

◆ place_wipe_tower()

Vec2d anonymous_namespace{OrcaSlicer_profile_validator.cpp}::place_wipe_tower ( DynamicPrintConfig & cfg,
const Vec2d & center )
159{
160 const auto *area = cfg.option<ConfigOptionPoints>("printable_area");
161 if (area == nullptr || area->values.size() < 3)
162 return Vec2d::Zero();
163 const WipeTowerFootprint footprint = estimate_wipe_tower_footprint(cfg, resolve_wipe_tower_type(cfg), {0, 1}, cfg.opt_float("layer_height"), 10.);
164 if (footprint.depth < EPSILON)
165 return Vec2d::Zero();
166 const double margin = WIPE_TOWER_MARGIN + footprint.brim_width;
167 // The position is the tower's own origin; a rotated tower extends from it in another
168 // direction, so place the rotated box's extents rather than the origin.
169 Slic3r::Polygon box({Point::new_scale(0., 0.), Point::new_scale(footprint.width, 0.), Point::new_scale(footprint.width, footprint.depth), Point::new_scale(0., footprint.depth)});
170 box.rotate(Geometry::deg2rad(cfg.opt_float("wipe_tower_rotation_angle")));
171 const BoundingBox local = get_extents(box);
172 const Vec2d lo = unscale(local.min);
173 const Vec2d size = unscale(local.max) - lo;
174 Vec2d pos(center.x() + 5. + margin + 5. - lo.x(), center.y() - size.y() / 2. - lo.y());
175 box.translate(Point::new_scale(pos.x(), pos.y()));
176 // A bed too small for the pair keeps the cube at its centre and places the tower alone.
177 const Polygons bed{Polygon::new_scale(area->values)};
178 const BoundingBox tower = get_extents(box);
179 BoundingBox pair = tower;
180 pair.merge(Point::new_scale(center.x() - 5., center.y() - 5.));
181 pair.merge(Point::new_scale(center.x() + 5., center.y() + 5.));
182 const Point room = get_extents(bed).size() - Point::new_scale(2. * margin, 2. * margin);
183 const bool rigid = pair.size().x() < room.x() && pair.size().y() < room.y();
184 const Vec2d move = WipeTower::move_box_inside_polygon(rigid ? pair : tower, bed, scaled<coord_t>(margin)).cast<double>();
185 pos += move;
186 cfg.option<ConfigOptionFloats>("wipe_tower_x", true)->values = {pos.x()};
187 cfg.option<ConfigOptionFloats>("wipe_tower_y", true)->values = {pos.y()};
188 return rigid ? move : Vec2d::Zero();
189}
#define EPSILON
Definition ColorUtils.cpp:64
void merge(const PointType &point)
Definition BoundingBox.cpp:75
PointType size() const
Definition BoundingBox.cpp:178
ConfigOption * option(const t_config_option_key &opt_key, bool create=false)
Definition Config.hpp:2768
Definition Config.hpp:1575
double & opt_float(const t_config_option_key &opt_key)
Definition Config.hpp:3047
void rotate(double angle)
Definition MultiPoint.hpp:40
static Point new_scale(coordf_t x, coordf_t y)
Definition Point.hpp:214
Definition Polygon.hpp:31
static Vec2f move_box_inside_polygon(const BoundingBox &box, const Polygons &polygons, coord_t offset=0)
Definition WipeTower.cpp:1826
static constexpr double WIPE_TOWER_MARGIN
Definition libslic3r.h:100
constexpr T deg2rad(const T angle)
Definition Geometry.hpp:312
@ local
Definition Time.hpp:13
Kernel::Point_3 Point
Definition MeshRepair.cpp:81
Vec3d pos(const Pt &p)
Definition ReprojectPointsOnMesh.hpp:16
BoundingBox scaled(const BoundingBoxf &bb)
Definition BoundingBox.hpp:284
WipeTowerFootprint estimate_wipe_tower_footprint(const ConfigBase &config, WipeTowerType tower_type, const std::vector< unsigned int > &filament_slots, double layer_height, double max_object_height)
Definition WipeTowerEstimate.cpp:86
double brim_width
Definition WipeTowerEstimate.hpp:23
void center(ExPolygonsWithIds &e)
Definition Emboss.cpp:1382
BoundingBox get_extents(const ExPolygonsWithIds &e)
Definition Emboss.cpp:1374
double area(const ExPolygon &poly)
Definition ExPolygon.hpp:511
std::vector< Polygon, PointsAllocator< Polygon > > Polygons
Definition Polygon.hpp:22
double depth
Definition WipeTowerEstimate.hpp:21
double width
Definition WipeTowerEstimate.hpp:20
T unscale(Q v)
Definition libslic3r.h:138
Eigen::Matrix< double, 2, 1, Eigen::DontAlign > Vec2d
Definition Point.hpp:67
ConfigOptionFloatsTempl< false > ConfigOptionFloats
Definition Config.hpp:1015
WipeTowerType resolve_wipe_tower_type(const ConfigBase &config)
Definition WipeTowerEstimate.cpp:62
Definition WipeTowerEstimate.hpp:19

References Slic3r::area(), Slic3r::WipeTowerFootprint::brim_width, Slic3r::center(), Slic3r::Geometry::deg2rad(), Slic3r::WipeTowerFootprint::depth, EPSILON, Slic3r::estimate_wipe_tower_footprint(), Slic3r::get_extents(), Slic3r::BoundingBoxBase< PointType, APointsType >::merge(), Slic3r::WipeTower::move_box_inside_polygon(), Slic3r::Point::new_scale(), Slic3r::Polygon::new_scale(), Slic3r::DynamicConfig::opt_float(), Slic3r::ConfigBase::option(), Slic3r::resolve_wipe_tower_type(), Slic3r::MultiPoint::rotate(), Slic3r::scaled(), Slic3r::BoundingBoxBase< PointType, APointsType >::size(), Slic3r::unscale(), Slic3r::WipeTowerFootprint::width, and WIPE_TOWER_MARGIN.

Referenced by slice_two_color_cube_and_export().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ preset_templates()

Templates anonymous_namespace{OrcaSlicer_profile_validator.cpp}::preset_templates ( const DynamicPrintConfig & cfg)
411{
412 Templates out;
413 for (const std::string &key : cfg.keys()) {
414 if (key != "filename_format" && !boost::algorithm::ends_with(key, "_gcode"))
415 continue;
416 const ConfigOption *opt = cfg.option(key);
417 if (opt->type() == coString) {
418 if (const std::string &text = static_cast<const ConfigOptionString *>(opt)->value; !text.empty())
419 out.emplace(key, text);
420 } else if (opt->type() == coStrings) {
421 for (const std::string &text : static_cast<const ConfigOptionStrings *>(opt)->values)
422 if (!text.empty())
423 out.emplace(key, text);
424 }
425 }
426 return out;
427}
Definition Config.hpp:1158
bool empty() const
Definition Config.hpp:1170
Definition Config.hpp:1190
Definition Config.hpp:284
t_config_option_keys keys() const override
Definition Config.cpp:1919
Definition BruteforceOptimizer.hpp:11
@ coString
Definition Config.hpp:192
@ coStrings
Definition Config.hpp:194
std::set< std::pair< std::string, std::string > > Templates
Definition OrcaSlicer_profile_validator.cpp:409

References Slic3r::coString, Slic3r::coStrings, Slic3r::ConfigOptionString::empty(), Slic3r::DynamicConfig::keys(), and Slic3r::ConfigBase::option().

Referenced by slice_all_printers().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ printable_area_center()

Vec2d anonymous_namespace{OrcaSlicer_profile_validator.cpp}::printable_area_center ( const DynamicPrintConfig & cfg)
142{
143 const auto *opt = cfg.option<ConfigOptionPoints>("printable_area");
144 if (opt == nullptr || opt->values.empty())
145 return Vec2d(100., 100.);
146 Vec2d lo = opt->values.front(), hi = opt->values.front();
147 for (const Vec2d &p : opt->values) { lo = lo.cwiseMin(p); hi = hi.cwiseMax(p); }
148 return 0.5 * (lo + hi);
149}

References Slic3r::ConfigBase::option().

Referenced by slice_two_color_cube_and_export().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ select_printer_default_presets()

void anonymous_namespace{OrcaSlicer_profile_validator.cpp}::select_printer_default_presets ( PresetBundle & bundle)
282{
283 const Preset &printer_preset = bundle.printers.get_selected_preset();
284 const std::string def_print = printer_preset.config.opt_string("default_print_profile");
285 if (!def_print.empty())
286 bundle.prints.select_preset_by_name(def_print, /*force=*/true);
287 if (const auto *def_fil = printer_preset.config.option<ConfigOptionStrings>("default_filament_profile");
288 def_fil != nullptr && !def_fil->values.empty())
289 bundle.filaments.select_preset_by_name(def_fil->values.front(), /*force=*/true);
290 // Re-seed the per-slot filament list from that selection, or the sweep's result depends on the
291 // printer sliced before it. Once there are 2+ slots, full_config() builds the filament config from
292 // filament_presets and ignores the selected preset (PresetBundle::full_fff_config), while
293 // update_compatible() only replaces a slot that has gone *incompatible* - and when it does, it ranks
294 // the outgoing preset's alias, then its filament type, above the printer's own default. The sweep
295 // grows every printer to 2 slots and update_multi_material_filament_presets() never shrinks them, so
296 // a material picked up on the first printer rides the whole run. With all vendors loaded the first
297 // printer inherits a TPU (the load-time pick is whichever filament sorts first), the type match
298 // re-resolves it to "Generic TPU @System", and its alias then pins every later printer to that
299 // vendor's own "Generic TPU @..." - which the BBL dual-nozzle profiles rightly refuse to group.
300 bundle.filament_presets.assign(1, bundle.filaments.get_selected_preset_name());
301}
std::vector< T > values
Definition Config.hpp:418
std::string & opt_string(const t_config_option_key &opt_key, bool create=false)
Definition Config.hpp:3042
PrinterPresetCollection printers
Definition PresetBundle.hpp:503
PresetCollection prints
Definition PresetBundle.hpp:497
PresetCollection filaments
Definition PresetBundle.hpp:499
std::vector< std::string > filament_presets
Definition PresetBundle.hpp:507
Preset & get_selected_preset()
Definition Preset.hpp:731
bool select_preset_by_name(const std::string &name, bool force)
Definition Preset.cpp:4166
std::string get_selected_preset_name() const
Definition Preset.hpp:741
Definition Preset.hpp:241
DynamicPrintConfig config
Definition Preset.hpp:300

References Slic3r::Preset::config, Slic3r::PresetBundle::filament_presets, Slic3r::PresetBundle::filaments, Slic3r::PresetCollection::get_selected_preset(), Slic3r::PresetCollection::get_selected_preset_name(), Slic3r::DynamicConfig::opt_string(), Slic3r::ConfigBase::option(), Slic3r::PresetBundle::printers, Slic3r::PresetBundle::prints, Slic3r::PresetCollection::select_preset_by_name(), and Slic3r::ConfigOptionVector< T >::values.

Referenced by slice_all_printers().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ slice_all_printers()

int anonymous_namespace{OrcaSlicer_profile_validator.cpp}::slice_all_printers ( const std::string & vendor,
const std::string & outdir )
450{
453
454 if (!outdir.empty()) {
455 boost::system::error_code ec;
456 fs::create_directories(outdir, ec);
457 if (ec) {
458 BOOST_LOG_TRIVIAL(error) << "Could not create output directory \"" << outdir << "\": " << ec.message();
459 std::cout << "Validation failed" << std::endl;
460 return 1;
461 }
462 std::cout << "Saving sliced g-code to " << outdir << std::endl;
463 }
464
465 PresetBundle bundle;
466 bundle.set_is_validation_mode(true);
467 bundle.set_vendor_to_validate(vendor); // empty == all vendors
468 AppConfig app_config;
469 app_config.set("preset_folder", "default");
470 try {
471 bundle.load_presets(app_config, ForwardCompatibilitySubstitutionRule::Disable);
472 } catch (const std::exception &ex) {
473 BOOST_LOG_TRIVIAL(error) << ex.what();
474 std::cout << "Validation failed" << std::endl;
475 return 1;
476 }
477
478 // Enable every instantiable model/variant in AppConfig - system printers are hidden until enabled;
479 // without this select_preset_by_name silently falls back to the "Default Printer".
480 std::vector<std::pair<std::string, std::string>> printers; // (vendor name, preset name)
481 for (const Preset &p : bundle.printers.get_presets()) {
482 if (p.vendor == nullptr) continue; // skips the Default Printer
483 const std::string model = p.config.opt_string("printer_model");
484 const std::string variant = p.config.opt_string("printer_variant");
485 if (model.empty() || variant.empty()) continue; // skip non-instantiable base/common configs
486 app_config.set_variant(p.vendor->id, model, variant, true);
487 printers.push_back({p.vendor->name, p.name});
488 }
489 bundle.load_installed_printers(app_config);
490
491 if (printers.empty()) {
492 BOOST_LOG_TRIVIAL(error) << "No instantiable printer presets found"
493 << (vendor.empty() ? "" : " for vendor " + vendor);
494 std::cout << "Validation failed" << std::endl;
495 return 1;
496 }
497 std::cout << "Slicing " << printers.size() << " printer preset(s)"
498 << (vendor.empty() ? "" : " for vendor " + vendor) << "..." << std::endl;
499
500 Templates covered; // the process and filament templates some slice has expanded
501 auto cover = [&covered](const Preset &preset) {
502 const Templates templates = preset_templates(preset.config);
503 covered.insert(templates.begin(), templates.end());
504 };
505 auto is_covered = [&covered](const Preset &preset) {
506 const Templates templates = preset_templates(preset.config);
507 return std::includes(covered.begin(), covered.end(), templates.begin(), templates.end());
508 };
509 int failures = 0, extra_processes = 0, extra_filaments = 0, by_object_slices = 0;
510 for (const auto &[vendor_name, printer] : printers) {
511 g_slice_context = vendor_name + " / " + printer; // tag every engine log line from this slice
512 const bool selected = bundle.printers.select_preset_by_name(printer, /*force=*/true);
513 if (!selected || bundle.printers.get_selected_preset_name() != printer) {
514 BOOST_LOG_TRIVIAL(error) << "Printer preset \"" << printer << "\" could not be selected";
515 ++failures;
516 continue;
517 }
518
519 select_printer_default_presets(bundle); // slice with the printer's shipped process/filament
520 bundle.update_multi_material_filament_presets(); // size filament_presets to nozzle count
521 bundle.update_compatible(PresetSelectCompatibleType::Always);
522
523 // Never slice with a generic default preset - that would validate stand-in settings, not the
524 // real profile (a legit per-profile error).
526 BOOST_LOG_TRIVIAL(error) << "Printer \"" << printer << "\" fell back to a default preset (process=\""
527 << bundle.prints.get_selected_preset_name() << "\", filament=\""
528 << bundle.filaments.get_selected_preset_name() << "\")";
529 ++failures;
530 continue;
531 }
532
533 const std::string print_name = bundle.prints.get_selected_preset_name();
534 const std::string filament_name = bundle.filaments.get_selected_preset_name();
535 const std::string what = "Printer \"" + printer + "\"";
536 const std::string file_base = sanitize_filename(vendor_name) + "__" + sanitize_filename(printer);
537 // A belt printer has no wipe tower (it purges into a prism object on the belt), so its
538 // filament change is the plain tool change set_extruder() emits rather than the tower's block.
539 const bool belt = bundle.printers.get_selected_preset().config.opt_bool("belt_printer");
540 const std::string marker = belt ? "\nT1\n" : "CP TOOLCHANGE START";
541 if (const std::string out = slice_selection(bundle, what, false, outdir, file_base); out.empty())
542 ++failures;
543 else if (out.find(marker) == std::string::npos) {
544 // The filament change never fired, so change_filament_gcode was not exercised.
545 BOOST_LOG_TRIVIAL(error) << what << " sliced but the filament change never fired (no "
546 << (belt ? "T1" : "CP TOOLCHANGE START") << ")";
547 ++failures;
548 }
549 cover(bundle.prints.get_selected_preset());
550 cover(bundle.filaments.get_selected_preset());
551
552 const std::string &by_object_gcode = bundle.printers.get_selected_preset().config.opt_string("printing_by_object_gcode");
553 if (!by_object_gcode.empty() && covered.emplace("printing_by_object_gcode", by_object_gcode).second) {
554 ++by_object_slices;
555 g_slice_context = vendor_name + " / " + printer + " / by object";
556 if (slice_selection(bundle, what + " printing by object", true, outdir, file_base + "__by_object").empty())
557 ++failures;
558 }
559
560 // The first process of this printer that compatible_prints lets an otherwise incompatible filament
561 // use, judged as update_compatible() would with that process selected; empty if there is none.
562 const PresetWithVendorProfile printer_with_vendor = bundle.printers.get_edited_preset_with_vendor_profile();
563 auto limiting_process = [&bundle, &printer_with_vendor](const Preset &filament) -> std::string {
564 const auto *processes = filament.config.option<ConfigOptionStrings>("compatible_prints");
565 if (processes == nullptr || processes->values.empty())
566 return {};
567 const PresetWithVendorProfile filament_with_vendor = bundle.filaments.get_preset_with_vendor_profile(filament);
568 if (!is_compatible_with_printer(filament_with_vendor, printer_with_vendor))
569 return {};
570 for (const std::string &name : processes->values)
571 if (const Preset *process = bundle.prints.find_preset(name);
572 process != nullptr && process->is_visible && process->is_compatible &&
573 is_compatible_with_print(filament_with_vendor, bundle.prints.get_preset_with_vendor_profile(*process), printer_with_vendor))
574 return name;
575 return {};
576 };
577 // The compatibility flags are still this printer's, from update_compatible() above, which judged the
578 // filaments against the printer's own process. A hidden preset cannot be selected, so it is counted,
579 // not sliced.
580 std::vector<std::pair<const Preset *, std::string>> extras; // (extra preset, process to slice it with)
581 size_t hidden = 0;
582 for (const PresetCollection *presets : {&bundle.prints, &bundle.filaments})
583 for (const Preset &preset : presets->get_presets()) {
584 if (!preset.is_system || preset.is_default)
585 continue;
586 std::string process;
587 if (preset.is_compatible)
588 process = preset.type == Preset::TYPE_PRINT ? preset.name : print_name;
589 else if (preset.type == Preset::TYPE_FILAMENT)
590 process = limiting_process(preset);
591 if (process.empty() || is_covered(preset))
592 continue;
593 if (preset.is_visible)
594 extras.emplace_back(&preset, process);
595 else
596 ++hidden;
597 }
598 if (hidden > 0)
599 BOOST_LOG_TRIVIAL(warning) << "Printer \"" << printer << "\": " << hidden
600 << " compatible system preset(s) with unexpanded templates are hidden and were not sliced";
601
602 // Each extra slice swaps one preset into the printer's own selection. update_compatible() is not
603 // run again: it could swap filament slots. The filament change is not required to ride the tower
604 // here (a vase-mode process has none): the printer's own slice above already expanded its hooks.
605 for (const auto &[preset, process] : extras) {
606 if (is_covered(*preset))
607 continue; // an earlier extra slice expanded the same texts
608 const bool is_print = preset->type == Preset::TYPE_PRINT;
609 const std::string &filament = is_print ? filament_name : preset->name;
610 std::string extra = std::string(is_print ? "process" : "filament") + " \"" + preset->name + "\"";
611 if (!is_print && process != print_name)
612 extra += " and process \"" + process + "\"";
613 g_slice_context = vendor_name + " / " + printer + " / " + extra;
614 if (is_print)
615 ++extra_processes;
616 else
617 ++extra_filaments;
618 cover(*preset);
619 bundle.prints.select_preset_by_name(process, /*force=*/true);
620 bundle.filaments.select_preset_by_name(filament, /*force=*/true);
621 bundle.filament_presets.assign(1, bundle.filaments.get_selected_preset_name());
623 // select_preset_by_name() falls back to another preset, and still returns true, when it cannot select this one.
624 if (bundle.prints.get_selected_preset_name() != process || bundle.filaments.get_selected_preset_name() != filament) {
625 BOOST_LOG_TRIVIAL(error) << what << " could not select " << extra;
626 ++failures;
627 continue;
628 }
629 if (slice_selection(bundle, what + " with " + extra, false, outdir, file_base + "__" + sanitize_filename(preset->name)).empty())
630 ++failures;
631 }
632 // Leave the printer's own selection for the next printer to start from, as without the extra slices.
633 bundle.prints.select_preset_by_name(print_name, /*force=*/true);
634 bundle.filaments.select_preset_by_name(filament_name, /*force=*/true);
635 }
636 g_slice_context.clear();
637
638 const int slices = int(printers.size()) + extra_processes + extra_filaments + by_object_slices;
639 std::cout << "Sliced " << printers.size() << " printer preset(s), " << extra_processes << " more process preset(s), "
640 << extra_filaments << " more filament preset(s) and " << by_object_slices << " printer(s) by object" << std::endl;
641 if (failures > 0) {
642 std::cout << failures << " of " << slices << " slice(s) failed" << std::endl;
643 std::cout << "Validation failed" << std::endl;
644 return 1;
645 }
646 std::cout << "All " << slices << " slice(s) succeeded" << std::endl;
647 std::cout << "Validation completed successfully" << std::endl;
648 return 0;
649}
void set_variant(const std::string &vendor, const std::string &model, const std::string &variant, bool enable)
Definition AppConfig.cpp:1421
void set(const std::string &section, const std::string &key, const std::string &value)
Definition AppConfig.hpp:167
bool opt_bool(const t_config_option_key &opt_key) const
Definition Config.hpp:3072
static bool check_inactive_branches
Definition PlaceholderParser.hpp:37
void load_installed_printers(const AppConfig &config)
Definition PresetBundle.cpp:3685
void update_compatible(PresetSelectCompatibleType select_other_print_if_incompatible, PresetSelectCompatibleType select_other_filament_if_incompatible)
Definition PresetBundle.cpp:8517
void update_multi_material_filament_presets(size_t to_delete_filament_id=size_t(-1))
Definition PresetBundle.cpp:8399
PresetsConfigSubstitutions load_presets(AppConfig &config, ForwardCompatibilitySubstitutionRule rule, const PresetPreferences &preferred_selection=PresetPreferences(), std::string *errors=nullptr, bool read_only=false)
Definition PresetBundle.cpp:881
void set_vendor_to_validate(std::string vendor)
Definition PresetBundle.hpp:467
void set_is_validation_mode(bool mode)
Definition PresetBundle.hpp:466
const std::deque< Preset > & get_presets() const
Definition Preset.hpp:781
PresetWithVendorProfile get_preset_with_vendor_profile(const Preset &preset) const
Definition Preset.cpp:3705
PresetWithVendorProfile get_edited_preset_with_vendor_profile() const
Definition Preset.hpp:773
Preset * find_preset(const std::string &name, bool first_visible_if_not_found=false, bool real=false, bool only_from_library=false)
Definition Preset.cpp:3772
bool is_default
Definition Preset.hpp:265
@ TYPE_PRINT
Definition Preset.hpp:246
@ TYPE_FILAMENT
Definition Preset.hpp:248
std::string sanitize_filename(const std::string &filename)
Definition utils.cpp:2319
bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_printer, const DynamicPrintConfig *extra_config)
Definition Preset.cpp:909
@ Preset
Definition calib.hpp:39
bool is_compatible_with_print(const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_print, const PresetWithVendorProfile &active_printer)
Definition Preset.cpp:871
bool empty(const BoundingBoxBase< PointType, PointsType > &bb)
Definition BoundingBox.hpp:273
bool process(std::exception_ptr &eptr)
Definition EmbossJob.cpp:1427
void select_printer_default_presets(PresetBundle &bundle)
Definition OrcaSlicer_profile_validator.cpp:281
std::string slice_selection(PresetBundle &bundle, const std::string &what, bool by_object, const std::string &outdir, const std::string &file_base)
Definition OrcaSlicer_profile_validator.cpp:390
Templates preset_templates(const DynamicPrintConfig &cfg)
Definition OrcaSlicer_profile_validator.cpp:410
void install_slice_context_log_sink()
Definition OrcaSlicer_profile_validator.cpp:312

References Slic3r::Always, Slic3r::PlaceholderParser::check_inactive_branches, Slic3r::Preset::config, Slic3r::Disable, Slic3r::empty(), Slic3r::PresetBundle::filament_presets, Slic3r::PresetBundle::filaments, Slic3r::PresetCollection::find_preset(), g_slice_context, Slic3r::PresetCollection::get_edited_preset_with_vendor_profile(), Slic3r::PresetCollection::get_preset_with_vendor_profile(), Slic3r::PresetCollection::get_presets(), Slic3r::PresetCollection::get_selected_preset(), Slic3r::PresetCollection::get_selected_preset_name(), install_slice_context_log_sink(), Slic3r::is_compatible_with_print(), Slic3r::is_compatible_with_printer(), Slic3r::Preset::is_default, Slic3r::PresetBundle::load_installed_printers(), Slic3r::PresetBundle::load_presets(), Slic3r::DynamicConfig::opt_bool(), Slic3r::DynamicConfig::opt_string(), preset_templates(), Slic3r::PresetBundle::printers, Slic3r::PresetBundle::prints, Slic3r::sanitize_filename(), Slic3r::PresetCollection::select_preset_by_name(), select_printer_default_presets(), Slic3r::AppConfig::set(), Slic3r::PresetBundle::set_is_validation_mode(), Slic3r::AppConfig::set_variant(), Slic3r::PresetBundle::set_vendor_to_validate(), slice_selection(), Slic3r::Preset::TYPE_FILAMENT, Slic3r::Preset::TYPE_PRINT, Slic3r::PresetBundle::update_compatible(), and Slic3r::PresetBundle::update_multi_material_filament_presets().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ slice_config()

DynamicPrintConfig anonymous_namespace{OrcaSlicer_profile_validator.cpp}::slice_config ( PresetBundle & bundle)
336{
337 // Grow to a 2nd filament so the cube can change colour; never shrink a multi-nozzle printer
338 // below its nozzle count, or full_config()'s flush-volume matrix no longer matches validate().
339 const size_t nozzles = bundle.printers.get_selected_preset().config.option<ConfigOptionFloats>("nozzle_diameter")->size();
340 bundle.set_num_filaments((unsigned int) std::max<size_t>(2, nozzles));
341
342 // Mirror the app's manual filament->nozzle assignment for a multi-nozzle BBL printer: put each
343 // filament on its own nozzle and pin the map (fmmManual) so full_config() collapses every filament to
344 // the variant of the nozzle it actually prints from, and the engine keeps that assignment instead of
345 // auto-remapping it during process(). Without this the synthetic 2nd filament keeps nozzle 1's variant
346 // while the auto map moves it to nozzle 2 - harmless, but on the one printer whose nozzles differ in
347 // type (Direct Drive + Bowden) the mismatched lookup spams [error] lines. Single-nozzle and non-BBL
348 // printers keep the default map (their toolchange rides the AMS/tool-changer path unchanged).
349 const bool pin_filament_map = bundle.is_bbl_vendor() && nozzles > 1;
350 auto &fmap = bundle.project_config.option<ConfigOptionInts>("filament_map", true)->values;
351 if (pin_filament_map) {
352 for (size_t i = 0; i < fmap.size(); ++i)
353 fmap[i] = int(i % nozzles) + 1;
354 }
355
356 // A fresh printer selection uses its declared nozzle volumes, just like the
357 // app. Otherwise a high-flow preset is silently sliced with Standard tuning.
359 bundle.project_config.option<ConfigOptionInts>("filament_volume_map", true)->values =
361
362 DynamicPrintConfig cfg = bundle.full_config();
363 cfg.set_key_value("enable_prime_tower", new ConfigOptionBool(true)); // force a purge tower so the change is detectable
364 // The map above drives full_config()'s per-filament variant collapse; fmmManual on the sliced config
365 // stops process() from auto-remapping filaments back onto a different nozzle (which would re-introduce
366 // the variant mismatch this pinning avoids).
367 if (pin_filament_map)
368 cfg.set_key_value("filament_map_mode", new ConfigOptionEnum<FilamentMapMode>(fmmManual));
369
370 // full_config() grows filament_extruder_variant to one entry per filament, but because the synthetic
371 // 2nd filament is a duplicate of the first (set_num_filaments copies the same preset), it leaves
372 // filament_self_index at size 1. That makes update_values_to_printer_extruders_for_multiple_filaments
373 // fail to resolve the 2nd filament's variant - a benign fallback that spams [error] lines. A real
374 // 2-colour project ships filament_self_index = 1,2,...; mirror that so the sweep log stays clean. The
375 // slice output is unaffected: the duplicated filament's per-variant values are identical to the first.
376 if (auto *variants = cfg.option<ConfigOptionStrings>("filament_extruder_variant")) {
377 auto &self_index = cfg.option<ConfigOptionInts>("filament_self_index", true)->values;
378 if (self_index.size() != variants->size()) {
379 self_index.resize(variants->size());
380 for (size_t i = 0; i < self_index.size(); ++i)
381 self_index[i] = int(i) + 1;
382 }
383 }
384 return cfg;
385}
Definition Config.hpp:1908
Definition Config.hpp:2076
bool set_key_value(const std::string &opt_key, ConfigOption *opt)
Definition Config.hpp:3022
Definition PrintConfig.hpp:915
DynamicPrintConfig project_config
Definition PresetBundle.hpp:521
std::vector< int > get_default_nozzle_volume_types_for_filaments(std::vector< int > &f_maps)
Definition PresetBundle.cpp:5411
bool is_bbl_vendor()
Definition PresetBundle.hpp:374
void set_num_filaments(unsigned int n, std::string new_col="")
Definition PresetBundle.cpp:4328
void reset_default_nozzle_volume_type()
Definition PresetBundle.cpp:5358
DynamicPrintConfig full_config(bool apply_extruder=true, std::optional< std::vector< int > >filament_maps=std::nullopt, std::optional< std::vector< int > > filament_volume_maps=std::nullopt) const
Definition PresetBundle.cpp:5427
ConfigOptionIntsTempl< false > ConfigOptionInts
Definition Config.hpp:1154
@ fmmManual
Definition PrintConfig.hpp:635

References Slic3r::Preset::config, Slic3r::fmmManual, Slic3r::PresetBundle::full_config(), Slic3r::PresetBundle::get_default_nozzle_volume_types_for_filaments(), Slic3r::PresetCollection::get_selected_preset(), Slic3r::PresetBundle::is_bbl_vendor(), Slic3r::ConfigBase::option(), Slic3r::PresetBundle::printers, Slic3r::PresetBundle::project_config, Slic3r::PresetBundle::reset_default_nozzle_volume_type(), Slic3r::DynamicConfig::set_key_value(), and Slic3r::PresetBundle::set_num_filaments().

Referenced by slice_selection().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ slice_selection()

std::string anonymous_namespace{OrcaSlicer_profile_validator.cpp}::slice_selection ( PresetBundle & bundle,
const std::string & what,
bool by_object,
const std::string & outdir,
const std::string & file_base )
391{
392 try {
393 const std::string out = slice_two_color_cube_and_export(slice_config(bundle), bundle.is_bbl_vendor(), by_object);
394 if (!outdir.empty() && !out.empty())
395 save_string_file(fs::path(outdir) / (file_base + ".gcode"), out);
396 if (out.empty() || out.find("G1") == std::string::npos) {
397 BOOST_LOG_TRIVIAL(error) << what << " produced no g-code";
398 return {};
399 }
400 return out;
401 } catch (const std::exception &ex) {
402 BOOST_LOG_TRIVIAL(error) << what << " failed to slice: " << ex.what();
403 return {};
404 }
405}
void save_string_file(const boost::filesystem::path &p, const std::string &str)
Definition utils.cpp:2156
DynamicPrintConfig slice_config(PresetBundle &bundle)
Definition OrcaSlicer_profile_validator.cpp:335
std::string slice_two_color_cube_and_export(DynamicPrintConfig cfg, bool is_bbl, bool by_object)
Definition OrcaSlicer_profile_validator.cpp:202

References Slic3r::PresetBundle::is_bbl_vendor(), Slic3r::save_string_file(), slice_config(), and slice_two_color_cube_and_export().

Referenced by slice_all_printers().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ slice_two_color_cube_and_export()

std::string anonymous_namespace{OrcaSlicer_profile_validator.cpp}::slice_two_color_cube_and_export ( DynamicPrintConfig cfg,
bool is_bbl,
bool by_object )
203{
205 const bool belt = cfg.opt_bool("belt_printer");
206 std::vector<Vec2d> cube_mins;
207 if (by_object) {
208 // By-object printing fires the hook only without a wipe tower, and rules out clumping detection and
209 // smooth timelapse. No skirt, so the two cubes' own skirts cannot overlap.
210 cfg.set_key_value("print_sequence", new ConfigOptionEnum<PrintSequence>(PrintSequence::ByObject));
211 cfg.set_key_value("enable_prime_tower", new ConfigOptionBool(false));
212 cfg.set_key_value("enable_wrapping_detection", new ConfigOptionBool(false));
214 cfg.set_key_value("skirt_loops", new ConfigOptionInt(0));
215 cube_mins = {center + Vec2d(-20., -5.), center + Vec2d(10., -5.)};
216 } else if (belt) {
217 // A belt object's slicing Z starts at the belt below its leading end, well below its first
218 // printed layer, so a height range in slicing Z does not map onto the part. Two cubes one
219 // behind the other along the belt, the second on filament 2, give the one filament change
220 // instead (the purge prism is an object the GUI adds, so there is no tower to place).
221 cube_mins = {center - Vec2d(5., 15.), center + Vec2d(-5., 5.)};
222 } else {
223 // Clumping detection changes the tower footprint, so turn it on before placing the tower.
224 if (!cfg.opt_string("wrapping_detection_gcode").empty())
225 cfg.set_key_value("enable_wrapping_detection", new ConfigOptionBool(true));
226 cube_mins = {center - Vec2d(5., 5.) + place_wipe_tower(cfg, center)};
227 }
228
229 Model model;
230 Print print;
231 print.is_BBL_printer() = is_bbl;
232 for (const Vec2d &cube_min : cube_mins) {
233 TriangleMesh m = make_cube(10, 10, 10);
234 m.translate(static_cast<float>(cube_min.x()), static_cast<float>(cube_min.y()), 0.f);
235 ModelObject *obj = model.add_object();
236 obj->name = "cube"; // populates [input_filename_base] the way a loaded model does
237 obj->add_volume(m);
238 obj->add_instance();
239 if (belt && !by_object) {
240 // The second cube along the belt is on filament 2 (see cube_mins above).
241 if (&cube_min == &cube_mins.back())
242 obj->config.set_key_value("extruder", new ConfigOptionInt(2));
243 } else {
244 // Filament 2 is used only above z=4, so the upper layers carry a single filament change.
245 DynamicPrintConfig range_config;
246 range_config.set_key_value("extruder", new ConfigOptionInt(2));
247 // Every range must carry a layer_height; use the process's own so a fine nozzle (e.g. 0.15 mm
248 // printing ~0.1 mm layers) isn't forced to a height its extrusion width can't support - that
249 // trips Flow::with_spacing.
250 range_config.set_key_value("layer_height", new ConfigOptionFloat(cfg.opt_float("layer_height")));
251 obj->layer_config_ranges[{4.0, 10.0}].assign_config(std::move(range_config));
252 }
253 obj->ensure_on_bed();
254 print.auto_assign_extruders(obj);
255 }
256 // Custom g-codes per print_z apply to layer-by-layer printing only (ToolOrdering::assign_custom_gcodes).
257 if (!by_object)
258 model.plates_custom_gcodes[model.curr_plate_index].gcodes = {{2., CustomGCode::PausePrint, 1, "", ""},
259 {7., CustomGCode::Template, 1, "", ""}};
260 print.apply(model, cfg);
261 print.validate();
262
263 // Process + export to a temp file, then read it back (the app's own export path is where the
264 // custom *_gcode placeholders expand).
265 print.set_status_silent();
266 print.process();
267 const fs::path tmp = fs::temp_directory_path() / fs::unique_path("orca-validate-%%%%-%%%%.gcode");
268 print.export_gcode(tmp.string(), nullptr, nullptr);
269 std::ifstream in(tmp.string());
270 std::string out((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
271 in.close();
272 boost::system::error_code ec;
273 fs::remove(tmp, ec);
274 print.output_filename(); // names the export as the app does, expanding filename_format
275 return out;
276}
Definition Config.hpp:1019
bool set_key_value(const std::string &opt_key, ConfigOption *opt)
Definition PrintConfig.hpp:2635
void ensure_on_bed(bool allow_negative_z=false)
Definition Model.cpp:1896
ModelConfigObject config
Definition Model.hpp:395
std::string name
Definition Model.hpp:384
t_layer_config_ranges layer_config_ranges
Definition Model.hpp:397
ModelVolume * add_volume(const TriangleMesh &mesh, bool modify_to_center_geometry=true)
Definition Model.cpp:1389
ModelInstance * add_instance()
Definition Model.cpp:1554
int curr_plate_index
Definition Model.hpp:1742
ModelObject * add_object()
Definition Model.cpp:607
std::map< int, CustomGCode::Info > plates_custom_gcodes
Definition Model.hpp:1743
void set_status_silent()
Definition PrintBase.hpp:486
void auto_assign_extruders(ModelObject *model_object) const
Definition Print.cpp:3116
void process(long long *time_cost_with_cache=nullptr, bool use_cache=false) override
Definition Print.cpp:3261
std::string export_gcode(const std::string &path_template, GCodeProcessorResult *result, ThumbnailsGeneratorCallback thumbnail_cb=nullptr)
Definition Print.cpp:3977
ApplyStatus apply(const Model &model, DynamicPrintConfig config, bool extruder_applied=false) override
Definition PrintApply.cpp:1335
std::string output_filename(const std::string &filename_base=std::string()) const override
Definition Print.cpp:5645
StringObjectException validate(std::vector< StringObjectException > *warnings=nullptr, Polygons *collison_polygons=nullptr, std::vector< std::pair< Polygon, float > > *height_polygons=nullptr) const override
Definition Print.cpp:1969
bool & is_BBL_printer()
Definition Print.hpp:1341
void translate(float x, float y, float z)
Definition TriangleMesh.cpp:297
@ PausePrint
Definition CustomGCode.hpp:20
@ Template
Definition CustomGCode.hpp:22
zaa_minimize_perimeter_height ConfigOptionFloat
Definition PrintConfig.hpp:1954
TriangleMesh make_cube(double x, double y, double z)
Definition TriangleMesh.hpp:378
@ tlTraditional
Definition PrintConfig.hpp:469
Vec2d printable_area_center(const DynamicPrintConfig &cfg)
Definition OrcaSlicer_profile_validator.cpp:141
Vec2d place_wipe_tower(DynamicPrintConfig &cfg, const Vec2d &center)
Definition OrcaSlicer_profile_validator.cpp:158

References Slic3r::ModelObject::add_instance(), Slic3r::Model::add_object(), Slic3r::ModelObject::add_volume(), Slic3r::Print::apply(), Slic3r::Print::auto_assign_extruders(), Slic3r::ByObject, Slic3r::center(), Slic3r::ModelObject::config, Slic3r::ConfigOptionFloat, Slic3r::Model::curr_plate_index, Slic3r::ModelObject::ensure_on_bed(), Slic3r::Print::export_gcode(), Slic3r::Print::is_BBL_printer(), Slic3r::ModelObject::layer_config_ranges, Slic3r::make_cube(), Slic3r::ModelObject::name, Slic3r::DynamicConfig::opt_bool(), Slic3r::DynamicConfig::opt_float(), Slic3r::DynamicConfig::opt_string(), Slic3r::Print::output_filename(), Slic3r::CustomGCode::PausePrint, place_wipe_tower(), Slic3r::Model::plates_custom_gcodes, printable_area_center(), Slic3r::Print::process(), Slic3r::DynamicConfig::set_key_value(), Slic3r::ModelConfig::set_key_value(), Slic3r::PrintBase::set_status_silent(), Slic3r::CustomGCode::Template, Slic3r::tlTraditional, Slic3r::TriangleMesh::translate(), and Slic3r::Print::validate().

Referenced by slice_selection().

Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ g_slice_context

std::string anonymous_namespace{OrcaSlicer_profile_validator.cpp}::g_slice_context
static