Compare commits

...

12 Commits

Author SHA1 Message Date
Hanif Koh
9bf2b0c832 Add safeguard against extruder_pintable_heights and extruder_areas vector size mismatch 2026-08-28 12:54:24 +08:00
Hanif Koh
2a2fe3d660 Use PartPlate's m_height to allow CLI to perform proper BuildVolume check 2026-08-28 11:57:13 +08:00
Hanif Koh
9f8d943b05 Fix exported first_layer_time. Fixes GitHub Issue #14740 2026-08-28 11:57:13 +08:00
Hanif Koh
b0554a9447 Fix OOB heap write from stale filament_self_index on --load-filaments. Fixes Github Issue #14181 2026-08-28 11:57:13 +08:00
Hanif Koh
88b9ad3d81 Fix CLI wipe-tower position silently reused across plates when the array is undersized 2026-08-28 11:57:13 +08:00
Hanif Koh
b2836c48ac Read Nozzle Height and Align Center in Arrange Config 2026-08-28 11:57:13 +08:00
Hanif Koh
334b8acdd1 Fix incorrect early exit for CLI mode no-support preventing parameters from being read 2026-08-28 11:57:13 +08:00
Hanif Koh
cd94c46df2 Update Option Type for LogFile argument 2026-08-28 11:57:13 +08:00
Hanif Koh
3256e20865 Reject invalid CLI argument values instead of silently accepting them
read_cli()'s coBool branch discarded deserialize()'s return value, so an
invalid value like --allow-rotations=false silently left the previous/
default value in place instead of erroring, unlike every other option
type. The vector branch (coBools/coFloats/coInts/coPercents/
coFloatsOrPercents) had the same gap, plus a worse failure mode: passing
"nil" into a non-nullable vector option (the common case - only options
explicitly marked nullable tolerate it) throws Slic3r::ConfigurationError
instead of returning false, which was uncaught and aborted the whole
process (e.g. --nozzle-diameter=nil, --filament-soluble=nil). Both
branches now check/catch and report "Invalid value for option --X" the
same way the existing generic scalar branch already does.

Also corrected --help: six boolean CLI flags (load_defaultfila, min_save,
normative_check, skip_modified_gcodes, allow_newer_file, allow_mix_temp)
carried a "option" hint implying a space-separated argument they never
actually consume (coBool/coBools flags only take a value via --flag=0/1,
never a following token) - removed it to match how every other plain
bool flag is already declared. And fixed a --load_settings/
--load_filaments typo (underscores) in --help's own footer; the real
flags are dash-separated, as shown directly above it in the same output.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCuPoRbBMoPxKpcYZmfdan
2026-08-28 11:57:13 +08:00
Hanif Koh
13d7bda6f8 Use printable area bounding box instead of naive vertex calculation. Fixes Github issue #15363 2026-08-28 11:57:13 +08:00
Hanif Koh
739ae609aa Safeguard CLI mode get_at() config functions to prevent crash from missing filament_id 2026-08-28 11:57:13 +08:00
Hanif Koh
d5fb17e563 Create Directories Recursively, and early exit gracefully if failed to create 2026-08-28 11:57:13 +08:00
6 changed files with 160 additions and 65 deletions

View File

@@ -1793,8 +1793,9 @@ int CLI::run(int argc, char **argv)
old_printable_area = config.option<ConfigOptionPoints>("printable_area", true)->values; old_printable_area = config.option<ConfigOptionPoints>("printable_area", true)->values;
old_exclude_area = config.option<ConfigOptionPoints>("bed_exclude_area", true)->values; old_exclude_area = config.option<ConfigOptionPoints>("bed_exclude_area", true)->values;
if (old_printable_area.size() >= 4) { if (old_printable_area.size() >= 4) {
old_printable_width = (int)(old_printable_area[2].x() - old_printable_area[0].x()); BoundingBoxf old_printable_bbox(old_printable_area);
old_printable_depth = (int)(old_printable_area[2].y() - old_printable_area[0].y()); old_printable_width = static_cast<int>(old_printable_bbox.size().x());
old_printable_depth = static_cast<int>(old_printable_bbox.size().y());
} }
old_printable_height = (int)(config.opt_float("printable_height")); old_printable_height = (int)(config.opt_float("printable_height"));
@@ -2343,8 +2344,9 @@ int CLI::run(int argc, char **argv)
Pointfs orig_printable_area; Pointfs orig_printable_area;
orig_printable_area = config.option<ConfigOptionPoints>("printable_area", true)->values; orig_printable_area = config.option<ConfigOptionPoints>("printable_area", true)->values;
if (orig_printable_area.size() >= 4) { if (orig_printable_area.size() >= 4) {
orig_printable_width = (int)(orig_printable_area[2].x() - orig_printable_area[0].x()); BoundingBoxf orig_printable_bbox(orig_printable_area);
orig_printable_depth = (int)(orig_printable_area[2].y() - orig_printable_area[0].y()); orig_printable_width = static_cast<int>(orig_printable_bbox.size().x());
orig_printable_depth = static_cast<int>(orig_printable_bbox.size().y());
} }
orig_printable_height = (int)(config.opt_float("printable_height")); orig_printable_height = (int)(config.opt_float("printable_height"));
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(":%1%, check printable size: old_printable_width=%2%, orig_printable_width=%3%, old_printable_depth=%4%, orig_printable_depth=%5%, old_printable_height=%6%, orig_printable_height=%7%") BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(":%1%, check printable size: old_printable_width=%2%, orig_printable_width=%3%, old_printable_depth=%4%, orig_printable_depth=%5%, old_printable_height=%6%, orig_printable_height=%7%")
@@ -3138,7 +3140,22 @@ int CLI::run(int argc, char **argv)
std::vector<int> old_variant_counts(filament_count, 1), new_variant_counts; std::vector<int> old_variant_counts(filament_count, 1), new_variant_counts;
ConfigOptionInts* filament_self_index_opt = m_print_config.option<ConfigOptionInts>("filament_self_index"); ConfigOptionInts* filament_self_index_opt = m_print_config.option<ConfigOptionInts>("filament_self_index");
if (!filament_self_index_opt) { bool need_regenerate_self_index = !filament_self_index_opt;
if (filament_self_index_opt) {
// a filament_self_index carried over from a project with a different
// filament_count can imply more distinct filament groups than currently exist.
// old_start_indice/old_variant_counts below are sized to filament_count, so an
// unreconciled index walks old_start_indice[++k] past its bounds (heap corruption).
int max_self_index = 0;
for (int v : filament_self_index_opt->values)
max_self_index = std::max(max_self_index, v);
if (max_self_index > filament_count) {
BOOST_LOG_TRIVIAL(warning) << boost::format("filament_self_index implies %1% filament groups but filament_count is %2%, regenerating")
% max_self_index % filament_count;
need_regenerate_self_index = true;
}
}
if (need_regenerate_self_index) {
filament_self_index_opt = m_print_config.option<ConfigOptionInts>("filament_self_index", true); filament_self_index_opt = m_print_config.option<ConfigOptionInts>("filament_self_index", true);
std::vector<int>& filament_self_indice = filament_self_index_opt->values; std::vector<int>& filament_self_indice = filament_self_index_opt->values;
filament_self_indice.resize(filament_count); filament_self_indice.resize(filament_count);
@@ -3732,6 +3749,8 @@ int CLI::run(int argc, char **argv)
double height_to_lid = m_print_config.opt_float("extruder_clearance_height_to_lid"); double height_to_lid = m_print_config.opt_float("extruder_clearance_height_to_lid");
double height_to_rod = m_print_config.opt_float("extruder_clearance_height_to_rod"); double height_to_rod = m_print_config.opt_float("extruder_clearance_height_to_rod");
double clearance_radius = m_print_config.opt_float("extruder_clearance_radius"); double clearance_radius = m_print_config.opt_float("extruder_clearance_radius");
double nozzle_height = m_print_config.opt_float("nozzle_height");
Vec2d align_center = m_print_config.option<ConfigOptionPoint>("best_object_pos")->value;
int shared_printable_width = 0, shared_printable_depth = 0, shared_printable_height = 0, shared_center_x = 0, shared_center_y = 0; int shared_printable_width = 0, shared_printable_depth = 0, shared_printable_height = 0, shared_center_x = 0, shared_center_y = 0;
//double plate_stride; //double plate_stride;
std::string bed_texture; std::string bed_texture;
@@ -3742,8 +3761,11 @@ int CLI::run(int argc, char **argv)
if (m_print_config.opt<ConfigOptionFloatsNullable>("extruder_printable_height")) { if (m_print_config.opt<ConfigOptionFloatsNullable>("extruder_printable_height")) {
current_extruder_print_heights = m_print_config.opt<ConfigOptionFloatsNullable>("extruder_printable_height")->values; current_extruder_print_heights = m_print_config.opt<ConfigOptionFloatsNullable>("extruder_printable_height")->values;
} }
current_printable_width = current_printable_area[2].x() - current_printable_area[0].x(); {
current_printable_depth = current_printable_area[2].y() - current_printable_area[0].y(); BoundingBoxf current_printable_bbox(current_printable_area);
current_printable_width = static_cast<int>(current_printable_bbox.size().x());
current_printable_depth = static_cast<int>(current_printable_bbox.size().y());
}
current_printable_height = print_height; current_printable_height = print_height;
if (old_printable_width == 0) if (old_printable_width == 0)
old_printable_width = current_printable_width; old_printable_width = current_printable_width;
@@ -3944,6 +3966,13 @@ int CLI::run(int argc, char **argv)
ConfigOptionFloats *wipe_x_option = dynamic_cast<ConfigOptionFloats *>(print_config.option("wipe_tower_x")); ConfigOptionFloats *wipe_x_option = dynamic_cast<ConfigOptionFloats *>(print_config.option("wipe_tower_x"));
ConfigOptionFloats *wipe_y_option = dynamic_cast<ConfigOptionFloats *>(print_config.option("wipe_tower_y")); ConfigOptionFloats *wipe_y_option = dynamic_cast<ConfigOptionFloats *>(print_config.option("wipe_tower_y"));
// get_at() clamps an out-of-range index to entry 0 instead of erroring, which
// would silently reuse another plate's wipe tower position here. Warn so a mismatched
// wipe_tower_x/y array (e.g. from a project saved before this plate was added) is visible.
if (static_cast<size_t>(plate_index) >= wipe_x_option->values.size() || static_cast<size_t>(plate_index) >= wipe_y_option->values.size()) {
BOOST_LOG_TRIVIAL(warning) << boost::format("plate %1%: wipe_tower_x/y only has %2%/%3% entries, reusing entry 0's position")
%(plate_index+1) %wipe_x_option->values.size() %wipe_y_option->values.size();
}
plate_obj_size_info.wipe_x = wipe_x_option->get_at(plate_index); plate_obj_size_info.wipe_x = wipe_x_option->get_at(plate_index);
plate_obj_size_info.wipe_y = wipe_y_option->get_at(plate_index); plate_obj_size_info.wipe_y = wipe_y_option->get_at(plate_index);
@@ -4139,8 +4168,9 @@ int CLI::run(int argc, char **argv)
temp_extruder_print_heights = config.option<ConfigOptionFloatsNullable>("extruder_printable_height", true)->values; temp_extruder_print_heights = config.option<ConfigOptionFloatsNullable>("extruder_printable_height", true)->values;
if (temp_printable_area.size() >= 4) { if (temp_printable_area.size() >= 4) {
printer_plate.printable_width = (int)(temp_printable_area[2].x() - temp_printable_area[0].x()); BoundingBoxf temp_printable_bbox(temp_printable_area);
printer_plate.printable_depth = (int)(temp_printable_area[2].y() - temp_printable_area[0].y()); printer_plate.printable_width = static_cast<int>(temp_printable_bbox.size().x());
printer_plate.printable_depth = static_cast<int>(temp_printable_bbox.size().y());
printer_plate.printable_height = (int)(config.opt_float("printable_height")); printer_plate.printable_height = (int)(config.opt_float("printable_height"));
} }
if (temp_exclude_area.size() >= 4) { if (temp_exclude_area.size() >= 4) {
@@ -4788,6 +4818,8 @@ int CLI::run(int argc, char **argv)
arrange_cfg.clearance_height_to_rod = height_to_rod; arrange_cfg.clearance_height_to_rod = height_to_rod;
arrange_cfg.clearance_height_to_lid = height_to_lid; arrange_cfg.clearance_height_to_lid = height_to_lid;
arrange_cfg.clearance_radius = clearance_radius; arrange_cfg.clearance_radius = clearance_radius;
arrange_cfg.nozzle_height = nozzle_height;
arrange_cfg.align_center = align_center;
arrange_cfg.printable_height = print_height; arrange_cfg.printable_height = print_height;
arrange_cfg.min_obj_distance = 0; arrange_cfg.min_obj_distance = 0;
if (arrange_cfg.is_seq_print) { if (arrange_cfg.is_seq_print) {
@@ -5238,6 +5270,8 @@ int CLI::run(int argc, char **argv)
arrange_cfg.clearance_height_to_rod = height_to_rod; arrange_cfg.clearance_height_to_rod = height_to_rod;
arrange_cfg.clearance_height_to_lid = height_to_lid; arrange_cfg.clearance_height_to_lid = height_to_lid;
arrange_cfg.clearance_radius = clearance_radius; arrange_cfg.clearance_radius = clearance_radius;
arrange_cfg.nozzle_height = nozzle_height;
arrange_cfg.align_center = align_center;
arrange_cfg.printable_height = print_height; arrange_cfg.printable_height = print_height;
arrange_cfg.min_obj_distance = 0; arrange_cfg.min_obj_distance = 0;
if (arrange_cfg.is_seq_print) { if (arrange_cfg.is_seq_print) {
@@ -5529,6 +5563,26 @@ int CLI::run(int argc, char **argv)
} }
finished_arrange = true; finished_arrange = true;
} }
// CLI has no m_plater, so PartPlateList::create_plate() never backfills
// wipe_tower_x/y for plates created here during arrange overflow (unlike GUI's
// set_default_wipe_tower_pos_for_plate()). Keep both arrays sized to the actual
// plate count so a later per-plate get_at() never silently reuses another plate's
// wipe tower position via ConfigOptionVector's out-of-range clamp.
{
int final_plate_count = partplate_list.get_plate_count();
ConfigOptionFloats* wipe_x_opt = m_print_config.option<ConfigOptionFloats>("wipe_tower_x");
ConfigOptionFloats* wipe_y_opt = m_print_config.option<ConfigOptionFloats>("wipe_tower_y");
if (wipe_x_opt && !wipe_x_opt->values.empty() && wipe_x_opt->values.size() < static_cast<size_t>(final_plate_count)) {
BOOST_LOG_TRIVIAL(info) << boost::format("wipe_tower_x had %1% entries for %2% plates, backfilling with entry 0")
% wipe_x_opt->values.size() % final_plate_count;
wipe_x_opt->values.resize(final_plate_count, wipe_x_opt->values.front());
}
if (wipe_y_opt && !wipe_y_opt->values.empty() && wipe_y_opt->values.size() < static_cast<size_t>(final_plate_count)) {
BOOST_LOG_TRIVIAL(info) << boost::format("wipe_tower_y had %1% entries for %2% plates, backfilling with entry 0")
% wipe_y_opt->values.size() % final_plate_count;
wipe_y_opt->values.resize(final_plate_count, wipe_y_opt->values.front());
}
}
original_model.clear_objects(); original_model.clear_objects();
original_model.clear_materials(); original_model.clear_materials();
} }
@@ -6516,10 +6570,20 @@ int CLI::run(int argc, char **argv)
plate_data->nozzle_diameters = nozzle_diameter_str; plate_data->nozzle_diameters = nozzle_diameter_str;
for (auto it = plate_data->slice_filaments_info.begin(); it != plate_data->slice_filaments_info.end(); it++) { for (auto it = plate_data->slice_filaments_info.begin(); it != plate_data->slice_filaments_info.end(); it++) {
// ConfigOptionVector::get_at() falls back to values.front() when the index is out of
// range, but that is undefined behavior when values is empty outright (e.g. filament_ids
// is never populated on a from-scratch slice with no --load-filaments) - guard every
// get_at() here on the vector actually having an entry at it->id before calling it.
bool valid_id = it->id >= 0;
std::string display_filament_type; std::string display_filament_type;
if (valid_id && filament_types && static_cast<size_t>(it->id) < filament_types->values.size())
it->type = m_print_config.get_filament_type(display_filament_type, it->id); it->type = m_print_config.get_filament_type(display_filament_type, it->id);
it->color = filament_color ? filament_color->get_at(it->id) : "#FFFFFF"; it->color = (valid_id && filament_color && static_cast<size_t>(it->id) < filament_color->values.size()) ?
it->filament_id = filament_id?filament_id->get_at(it->id):""; filament_color->get_at(it->id) :
"#FFFFFF";
it->filament_id = (valid_id && filament_id && static_cast<size_t>(it->id) < filament_id->values.size()) ?
filament_id->get_at(it->id) :
"";
} }
if (!plate_data->plate_thumbnail.is_valid()) { if (!plate_data->plate_thumbnail.is_valid()) {
@@ -7311,6 +7375,10 @@ bool CLI::setup(int argc, char **argv)
m_config.option(optdef.first, true); m_config.option(optdef.first, true);
set_data_dir(m_config.opt_string("datadir")); set_data_dir(m_config.opt_string("datadir"));
if (!data_dir().empty() && !boost::filesystem::exists(data_dir())) {
boost::nowide::cerr << "Could not create data directory: " << data_dir() << std::endl;
return false;
}
//FIXME Validating at this stage most likely does not make sense, as the config is not fully initialized yet. //FIXME Validating at this stage most likely does not make sense, as the config is not fully initialized yet.
if (!validity.empty()) { if (!validity.empty()) {
@@ -7384,7 +7452,7 @@ void CLI::print_help(bool include_print_options, PrinterTechnology printer_techn
<< std::endl << std::endl
<< "Print setting priorities:" << std::endl << "Print setting priorities:" << std::endl
<< "\t1) setting values from the command line (highest priority)"<< std::endl << "\t1) setting values from the command line (highest priority)"<< std::endl
<< "\t2) setting values loaded with --load_settings and --load_filaments" << std::endl << "\t2) setting values loaded with --load-settings and --load-filaments" << std::endl
<< "\t3) setting values loaded from 3mf(lowest priority)" << std::endl; << "\t3) setting values loaded from 3mf(lowest priority)" << std::endl;
/*if (include_print_options) { /*if (include_print_options) {
@@ -7423,6 +7491,10 @@ bool CLI::export_models(IO::ExportFormat format, std::string path_dir)
for (ModelObject* model_object : model.objects) for (ModelObject* model_object : model.objects)
{ {
const std::string path = this->output_filepath(*model_object, index++, format, path_dir); const std::string path = this->output_filepath(*model_object, index++, format, path_dir);
if (path.empty()) {
boost::nowide::cerr << "Could not create output directory for STL export" << std::endl;
return false;
}
success = Slic3r::store_stl(path.c_str(), model_object, true); success = Slic3r::store_stl(path.c_str(), model_object, true);
if (success) if (success)
BOOST_LOG_TRIVIAL(info) << "Model successfully exported to " << path << std::endl; BOOST_LOG_TRIVIAL(info) << "Model successfully exported to " << path << std::endl;
@@ -7548,8 +7620,19 @@ std::string CLI::output_filepath(const ModelObject &object, unsigned int index,
output_path = subdir + "/"+file_name; output_path = subdir + "/"+file_name;
boost::filesystem::path subdir_path(subdir); boost::filesystem::path subdir_path(subdir);
if (!boost::filesystem::exists(subdir_path)) if (!boost::filesystem::exists(subdir_path)) {
boost::filesystem::create_directory(subdir_path); try {
boost::filesystem::create_directories(subdir_path);
} catch (const boost::filesystem::filesystem_error &ex) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": failed to create output directory " << subdir_path.string() << ": " << ex.what();
}
if (!boost::filesystem::exists(subdir_path)) {
// Directory creation failed and won't succeed on a retry (same path, same cause) -
// signal failure now instead of letting every object in the model repeat the same
// doomed attempt and fail with a less specific "export failed" error later.
return std::string();
}
}
return output_path; return output_path;
} }

View File

@@ -13,7 +13,6 @@ BuildVolume::BuildVolume(const std::vector<Vec2d> &printable_area, const double
: m_bed_shape(printable_area), m_max_print_height(printable_height), m_extruder_shapes(extruder_areas), m_extruder_printable_height(extruder_printable_heights) : m_bed_shape(printable_area), m_max_print_height(printable_height), m_extruder_shapes(extruder_areas), m_extruder_printable_height(extruder_printable_heights)
{ {
assert(printable_height >= 0); assert(printable_height >= 0);
//assert(extruder_printable_heights.size() == extruder_areas.size());
m_polygon = Polygon::new_scale(printable_area); m_polygon = Polygon::new_scale(printable_area);
assert(m_polygon.is_counter_clockwise()); assert(m_polygon.is_counter_clockwise());
@@ -100,7 +99,12 @@ BuildVolume::BuildVolume(const std::vector<Vec2d> &printable_area, const double
return; return;
} }
if ((extruder_shape == printable_area)&&(extruder_printable_heights[index] == printable_height)) { if (index >= extruder_printable_heights.size())
BOOST_LOG_TRIVIAL(warning) << boost::format("extruder_printable_height has only %1% entries but extruder_printable_area has %2%; using bed printable_height for extruder %3%")
% extruder_printable_heights.size() % m_extruder_shapes.size() % index;
const double extruder_height = index < extruder_printable_heights.size() ? extruder_printable_heights[index] : printable_height;
if ((extruder_shape == printable_area)&&(extruder_height == printable_height)) {
extruder_volume.same_with_bed = true; extruder_volume.same_with_bed = true;
extruder_volume.type = m_type; extruder_volume.type = m_type;
extruder_volume.bbox = m_bbox; extruder_volume.bbox = m_bbox;
@@ -113,7 +117,7 @@ BuildVolume::BuildVolume(const std::vector<Vec2d> &printable_area, const double
double poly_area = poly.area(); double poly_area = poly.area();
extruder_volume.bbox = get_extents(poly); extruder_volume.bbox = get_extents(poly);
BoundingBoxf temp_bboxf = get_extents(extruder_shape); BoundingBoxf temp_bboxf = get_extents(extruder_shape);
extruder_volume.bboxf = BoundingBoxf3{ to_3d(temp_bboxf.min, 0.), to_3d(temp_bboxf.max, extruder_printable_heights[index]) }; extruder_volume.bboxf = BoundingBoxf3{ to_3d(temp_bboxf.min, 0.), to_3d(temp_bboxf.max, extruder_height) };
if (extruder_shape.size() >= 4 && std::abs((poly_area - double(extruder_volume.bbox.size().x()) * double(extruder_volume.bbox.size().y()))) < sqr(SCALED_EPSILON)) if (extruder_shape.size() >= 4 && std::abs((poly_area - double(extruder_volume.bbox.size().x()) * double(extruder_volume.bbox.size().y()))) < sqr(SCALED_EPSILON))
{ {

View File

@@ -1812,17 +1812,31 @@ bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option
// to the end of the value. // to the end of the value.
if (opt_base->type() == coBools && value.empty()) if (opt_base->type() == coBools && value.empty())
static_cast<ConfigOptionBools*>(opt_base)->values.push_back(!no); static_cast<ConfigOptionBools*>(opt_base)->values.push_back(!no);
else else {
// Deserialize any other vector value (ConfigOptionInts, Floats, Percents, Points) the same way // Deserialize any other vector value (ConfigOptionInts, Floats, Percents, Points) the same way
// they get deserialized from an .ini file. For ConfigOptionStrings, that means that the C-style unescape // they get deserialized from an .ini file. For ConfigOptionStrings, that means that the C-style unescape
// will be applied for values enclosed in quotes, while values non-enclosed in quotes are left to be // will be applied for values enclosed in quotes, while values non-enclosed in quotes are left to be
// unescaped by the calling shell. // unescaped by the calling shell.
opt_vector->deserialize(value, true); bool deserialized = false;
try {
deserialized = opt_vector->deserialize(value, true);
} catch (const std::exception &ex) {
// e.g. "nil" deserialized into a non-nullable vector option throws instead of
// returning false - treat that the same as any other invalid value here.
deserialized = false;
}
if (! deserialized) {
boost::nowide::cerr << "Invalid value for option --" << token.c_str() << std::endl;
return false;
}
}
} else if (opt_base->type() == coBool) { } else if (opt_base->type() == coBool) {
if (value.empty()) if (value.empty())
static_cast<ConfigOptionBool*>(opt_base)->value = !no; static_cast<ConfigOptionBool*>(opt_base)->value = !no;
else else if (! opt_base->deserialize(value)) {
opt_base->deserialize(value); boost::nowide::cerr << "Invalid value for option --" << token.c_str() << std::endl;
return false;
}
} else if (opt_base->type() == coString) { } else if (opt_base->type() == coString) {
// Do not unescape single string values, the unescaping is left to the calling shell. // Do not unescape single string values, the unescaping is left to the calling shell.
static_cast<ConfigOptionString*>(opt_base)->value = value; static_cast<ConfigOptionString*>(opt_base)->value = value;

View File

@@ -11948,13 +11948,11 @@ CLIActionsConfigDef::CLIActionsConfigDef()
def = this->add("load_defaultfila", coBool); def = this->add("load_defaultfila", coBool);
def->label = L("Load default filaments"); def->label = L("Load default filaments");
def->tooltip = L("Load first filament as default for those not loaded."); def->tooltip = L("Load first filament as default for those not loaded.");
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(false)); def->set_default_value(new ConfigOptionBool(false));
def = this->add("min_save", coBool); def = this->add("min_save", coBool);
def->label = L("Minimum save"); def->label = L("Minimum save");
def->tooltip = L("Export 3MF with minimum size."); def->tooltip = L("Export 3MF with minimum size.");
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(false)); def->set_default_value(new ConfigOptionBool(false));
def = this->add("mtcpp", coInt); def = this->add("mtcpp", coInt);
@@ -11980,7 +11978,6 @@ CLIActionsConfigDef::CLIActionsConfigDef()
def = this->add("normative_check", coBool); def = this->add("normative_check", coBool);
def->label = L("Normative check"); def->label = L("Normative check");
def->tooltip = L("Check the normative items."); def->tooltip = L("Check the normative items.");
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(true)); def->set_default_value(new ConfigOptionBool(true));
/*def = this->add("help_fff", coBool); /*def = this->add("help_fff", coBool);
@@ -12247,7 +12244,7 @@ CLIMiscConfigDef::CLIMiscConfigDef()
def->cli_params = "level"; def->cli_params = "level";
def->set_default_value(new ConfigOptionInt(1)); def->set_default_value(new ConfigOptionInt(1));
def = this->add("logfile", coInt); def = this->add("logfile", coString);
def->label = L("Log file"); def->label = L("Log file");
def->tooltip = L("Redirects debug logging to file.\n"); def->tooltip = L("Redirects debug logging to file.\n");
def->cli_params = "file"; def->cli_params = "file";
@@ -12295,7 +12292,6 @@ CLIMiscConfigDef::CLIMiscConfigDef()
def = this->add("skip_modified_gcodes", coBool); def = this->add("skip_modified_gcodes", coBool);
def->label = L("Skip modified G-code in 3MF"); def->label = L("Skip modified G-code in 3MF");
def->tooltip = L("Skip the modified G-code in 3MF from printer or filament presets."); def->tooltip = L("Skip the modified G-code in 3MF from printer or filament presets.");
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(false)); def->set_default_value(new ConfigOptionBool(false));
def = this->add("makerlab_name", coString); def = this->add("makerlab_name", coString);
@@ -12325,14 +12321,12 @@ CLIMiscConfigDef::CLIMiscConfigDef()
def = this->add("allow_newer_file", coBool); def = this->add("allow_newer_file", coBool);
def->label = L("Allow 3MF with newer version to be sliced"); def->label = L("Allow 3MF with newer version to be sliced");
def->tooltip = L("Allow 3MF with newer version to be sliced."); def->tooltip = L("Allow 3MF with newer version to be sliced.");
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(false)); def->set_default_value(new ConfigOptionBool(false));
def = this->add("allow_mix_temp", coBool); def = this->add("allow_mix_temp", coBool);
// internal use only, don't need translation // internal use only, don't need translation
def->label = "Allow filaments with high/low temperature to be printed together"; def->label = "Allow filaments with high/low temperature to be printed together";
def->tooltip = "Allow filaments with high/low temperature to be printed together."; def->tooltip = "Allow filaments with high/low temperature to be printed together.";
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(false)); def->set_default_value(new ConfigOptionBool(false));
} }

View File

@@ -310,7 +310,11 @@ void set_data_dir(const std::string &dir)
{ {
g_data_dir = dir; g_data_dir = dir;
if (!g_data_dir.empty() && !boost::filesystem::exists(g_data_dir)) { if (!g_data_dir.empty() && !boost::filesystem::exists(g_data_dir)) {
boost::filesystem::create_directory(g_data_dir); try {
boost::filesystem::create_directories(g_data_dir);
} catch (const boost::filesystem::filesystem_error &ex) {
BOOST_LOG_TRIVIAL(error) << "set_data_dir: failed to create data directory " << g_data_dir << ": " << ex.what();
}
} }
} }

View File

@@ -1757,9 +1757,7 @@ std::vector<int> PartPlate::get_extruders_under_cli(bool conside_custom_gcode, D
else else
obj_support = glb_support; obj_support = glb_support;
if (!obj_support) if (obj_support) {
continue;
int obj_support_intf_extr = 0; int obj_support_intf_extr = 0;
const ConfigOption* support_intf_extr_opt = object->config.option("support_interface_filament"); const ConfigOption* support_intf_extr_opt = object->config.option("support_interface_filament");
if (support_intf_extr_opt != nullptr) if (support_intf_extr_opt != nullptr)
@@ -1777,6 +1775,7 @@ std::vector<int> PartPlate::get_extruders_under_cli(bool conside_custom_gcode, D
plate_extruders.push_back(obj_support_extr); plate_extruders.push_back(obj_support_extr);
else if (glb_support_extr != 0) else if (glb_support_extr != 0)
plate_extruders.push_back(glb_support_extr); plate_extruders.push_back(glb_support_extr);
}
int obj_outer_wall_extr = 0; int obj_outer_wall_extr = 0;
if (const ConfigOption* wall_opt = object->config.option("outer_wall_filament_id"); wall_opt != nullptr) if (const ConfigOption* wall_opt = object->config.option("outer_wall_filament_id"); wall_opt != nullptr)
@@ -2771,18 +2770,15 @@ bool PartPlate::check_outside(int obj_id, int instance_id, BoundingBoxf3* boundi
plate_box.min.z() += instance_box.min.z(); // not considering outsize if sinking plate_box.min.z() += instance_box.min.z(); // not considering outsize if sinking
if (instance_box.min.z() < SINKING_Z_THRESHOLD) { if (instance_box.min.z() < SINKING_Z_THRESHOLD) {
// Orca: For sinking object, we use a more expensive algorithm so part below build plate won't be considered // For sinking object, we use a more expensive algorithm so part below build plate won't be considered.
// m_plater is null in CLI mode. // m_height mirrors the printer's printable height independent of m_plater, so this runs in CLI too.
if (m_plater && plate_box.intersects(instance_box)) { if (plate_box.intersects(instance_box)) {
// TODO: FIXME: this does not take exclusion area into account // TODO: FIXME: this does not take exclusion area into account
const BuildVolume build_volume(get_shape(), m_plater->build_volume().printable_height(), m_extruder_areas, m_extruder_heights); const BuildVolume build_volume(get_shape(), m_height, m_extruder_areas, m_extruder_heights);
const auto state = instance->calc_print_volume_state(build_volume); const auto state = instance->calc_print_volume_state(build_volume);
outside = state == ModelInstancePVS_Partly_Outside; outside = state == ModelInstancePVS_Partly_Outside;
} }
} } else if (plate_box.contains(instance_box)) {
else
if (plate_box.contains(instance_box))
{
if (m_exclude_bounding_box.size() > 0) if (m_exclude_bounding_box.size() > 0)
{ {
Polygon hull = instance->convex_hull_2d(); Polygon hull = instance->convex_hull_2d();
@@ -6470,7 +6466,7 @@ int PartPlateList::store_to_3mf_structure(PlateDataPtrs& plate_data_list, bool w
plate_data_item->filament_change_sequence = m_plate_list[i]->m_gcode_result->filament_change_sequence; plate_data_item->filament_change_sequence = m_plate_list[i]->m_gcode_result->filament_change_sequence;
plate_data_item->nozzle_change_sequence = m_plate_list[i]->m_gcode_result->nozzle_change_sequence; plate_data_item->nozzle_change_sequence = m_plate_list[i]->m_gcode_result->nozzle_change_sequence;
plate_data_item->optimal_assignment = m_plate_list[i]->m_gcode_result->optimal_assignment; plate_data_item->optimal_assignment = m_plate_list[i]->m_gcode_result->optimal_assignment;
plate_data_item->first_layer_time = std::to_string(m_plate_list[i]->cali_bboxes_data.first_layer_time); plate_data_item->first_layer_time = std::to_string(m_plate_list[i]->m_gcode_result->initial_layer_time);
Print *print = nullptr; Print *print = nullptr;
m_plate_list[i]->get_print((PrintBase **) &print, nullptr, nullptr); m_plate_list[i]->get_print((PrintBase **) &print, nullptr, nullptr);
if (print) { if (print) {