mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-19 13:41:03 +00:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
252bbbf9f5 | ||
|
|
4c2ae6d202 | ||
|
|
ac022b56ad |
No files matched your search
@@ -15648,6 +15648,12 @@ msgstr ""
|
||||
msgid "Enable this option to add EXCLUDE OBJECT command in G-code."
|
||||
msgstr ""
|
||||
|
||||
msgid "Outline tolerance"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum deviation of the object outline written to EXCLUDE_OBJECT_DEFINE from the original outline. Larger values give fewer points, which helps firmware that stalls while parsing G-code with many objects. 0 keeps every point."
|
||||
msgstr ""
|
||||
|
||||
msgid "Verbose G-code"
|
||||
msgstr ""
|
||||
|
||||
|
||||
+13
-37
@@ -841,12 +841,8 @@ public:
|
||||
{
|
||||
UNUSED(append);
|
||||
std::istringstream iss(str);
|
||||
double value;
|
||||
iss >> value;
|
||||
if (iss.fail())
|
||||
return false;
|
||||
this->value = value;
|
||||
return true;
|
||||
iss >> this->value;
|
||||
return !iss.fail();
|
||||
}
|
||||
|
||||
bool operator==(const ConfigOption &rhs) const override
|
||||
@@ -1037,12 +1033,8 @@ public:
|
||||
{
|
||||
UNUSED(append);
|
||||
std::istringstream iss(str);
|
||||
int value;
|
||||
iss >> value;
|
||||
if (iss.fail())
|
||||
return false;
|
||||
this->value = value;
|
||||
return true;
|
||||
iss >> this->value;
|
||||
return !iss.fail();
|
||||
}
|
||||
|
||||
ConfigOptionInt& operator=(const ConfigOption *opt)
|
||||
@@ -1260,12 +1252,8 @@ public:
|
||||
UNUSED(append);
|
||||
// don't try to parse the trailing % since it's optional
|
||||
std::istringstream iss(str);
|
||||
double value;
|
||||
iss >> value;
|
||||
if (iss.fail())
|
||||
return false;
|
||||
this->value = value;
|
||||
return true;
|
||||
iss >> this->value;
|
||||
return !iss.fail();
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -1377,14 +1365,10 @@ public:
|
||||
bool deserialize(const std::string &str, bool append = false) override
|
||||
{
|
||||
UNUSED(append);
|
||||
std::istringstream iss(str);
|
||||
double value;
|
||||
iss >> value;
|
||||
if (iss.fail())
|
||||
return false;
|
||||
this->value = value;
|
||||
this->percent = str.find_first_of("%") != std::string::npos;
|
||||
return true;
|
||||
std::istringstream iss(str);
|
||||
iss >> this->value;
|
||||
return !iss.fail();
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -1554,12 +1538,8 @@ public:
|
||||
{
|
||||
UNUSED(append);
|
||||
char dummy;
|
||||
Vec2d value;
|
||||
if (sscanf(str.data(), " %lf , %lf %c", &value(0), &value(1), &dummy) != 2 &&
|
||||
sscanf(str.data(), " %lf x %lf %c", &value(0), &value(1), &dummy) != 2)
|
||||
return false;
|
||||
this->value = value;
|
||||
return true;
|
||||
return sscanf(str.data(), " %lf , %lf %c", &this->value(0), &this->value(1), &dummy) == 2 ||
|
||||
sscanf(str.data(), " %lf x %lf %c", &this->value(0), &this->value(1), &dummy) == 2;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -1678,12 +1658,8 @@ public:
|
||||
{
|
||||
UNUSED(append);
|
||||
char dummy;
|
||||
Vec3d value;
|
||||
if (sscanf(str.data(), " %lf , %lf , %lf %c", &value(0), &value(1), &value(2), &dummy) != 3 &&
|
||||
sscanf(str.data(), " %lf x %lf x %lf %c", &value(0), &value(1), &value(2), &dummy) != 3)
|
||||
return false;
|
||||
this->value = value;
|
||||
return true;
|
||||
return sscanf(str.data(), " %lf , %lf , %lf %c", &this->value(0), &this->value(1), &this->value(2), &dummy) == 3 ||
|
||||
sscanf(str.data(), " %lf x %lf x %lf %c", &this->value(0), &this->value(1), &this->value(2), &dummy) == 3;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -988,7 +988,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
|
||||
MetadataList metadata;
|
||||
VolumeMetadataList volumes;
|
||||
bool auto_drop { true };
|
||||
std::optional<bool> auto_drop;
|
||||
};
|
||||
|
||||
struct CutObjectInfo
|
||||
@@ -1208,6 +1208,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
//IdToGeometryMap m_orig_geometries; // backup & restore
|
||||
CurrentConfig m_curr_config;
|
||||
IdToMetadataMap m_objects_metadata;
|
||||
std::set<int> m_items_auto_drop_off; // object ids with a build item auto_drop="0" (2.4.x layout)
|
||||
IdToCutObjectInfoMap m_cut_object_infos;
|
||||
IdToLayerHeightsProfileMap m_layer_heights_profiles;
|
||||
IdToLayerConfigRangesMap m_layer_config_ranges;
|
||||
@@ -1463,6 +1464,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
m_objects.clear();
|
||||
m_instances.clear();
|
||||
m_objects_metadata.clear();
|
||||
m_items_auto_drop_off.clear();
|
||||
m_curr_metadata_name.clear();
|
||||
m_curr_characters.clear();
|
||||
|
||||
@@ -1502,6 +1504,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
m_curr_config.object_id = -1;
|
||||
m_curr_config.volume_id = -1;
|
||||
m_objects_metadata.clear();
|
||||
m_items_auto_drop_off.clear();
|
||||
m_layer_heights_profiles.clear();
|
||||
m_layer_config_ranges.clear();
|
||||
m_brim_ear_points.clear();
|
||||
@@ -2257,11 +2260,14 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
ObjectMetadata::VolumeMetadataList volumes;
|
||||
ObjectMetadata::VolumeMetadataList* volumes_ptr = nullptr;
|
||||
|
||||
// 2.4.x stored auto_drop on build items, shared by all instances of an object; any "off" wins
|
||||
const bool legacy_auto_drop = !m_items_auto_drop_off.count(object.first.second);
|
||||
|
||||
IdToMetadataMap::iterator obj_metadata = m_objects_metadata.find(object.first.second);
|
||||
if (obj_metadata != m_objects_metadata.end()) {
|
||||
// config data has been found, this model was saved using slic3r pe
|
||||
|
||||
model_object->auto_drop = obj_metadata->second.auto_drop;
|
||||
model_object->auto_drop = obj_metadata->second.auto_drop.value_or(legacy_auto_drop);
|
||||
|
||||
// apply object's name and config data
|
||||
for (const Metadata& metadata : obj_metadata->second.metadata) {
|
||||
@@ -2279,6 +2285,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
}
|
||||
else {
|
||||
// config data not found, this model was not saved using slic3r pe
|
||||
model_object->auto_drop = legacy_auto_drop;
|
||||
|
||||
// add the entire geometry as the single volume to generate
|
||||
//volumes.emplace_back(0, (int)obj_geometry->second.triangles.size() - 1);
|
||||
@@ -4121,6 +4128,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
std::string path = bbs_get_attribute_value_string(attributes, num_attributes, PPATH_ATTR);
|
||||
Transform3d transform = bbs_get_transform_from_3mf_specs_string(bbs_get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
|
||||
int printable = bbs_get_attribute_value_bool(attributes, num_attributes, PRINTABLE_ATTR);
|
||||
if (const char* auto_drop = bbs_get_attribute_value_charptr(attributes, num_attributes, AUTO_DROP_ATTR); auto_drop != nullptr && !::atoi(auto_drop))
|
||||
m_items_auto_drop_off.insert(object_id);
|
||||
return !m_load_model || _create_object_instance(path, object_id, transform, printable, 1);
|
||||
}
|
||||
|
||||
@@ -4489,7 +4498,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
// int instances_count_id = bbs_get_attribute_value_int(attributes, num_attributes, INSTANCESCOUNT_ATTR);
|
||||
|
||||
ObjectMetadata metadata;
|
||||
metadata.auto_drop = bbs_get_attribute_value_bool(attributes, num_attributes, AUTO_DROP_ATTR);
|
||||
if (bbs_get_attribute_value_charptr(attributes, num_attributes, AUTO_DROP_ATTR) != nullptr)
|
||||
metadata.auto_drop = bbs_get_attribute_value_bool(attributes, num_attributes, AUTO_DROP_ATTR);
|
||||
m_objects_metadata.insert({ object_id, std::move(metadata) });
|
||||
m_curr_config.object_id = object_id;
|
||||
return true;
|
||||
|
||||
@@ -1421,6 +1421,7 @@ static std::vector<std::string> s_Preset_print_options{
|
||||
"initial_layer_travel_acceleration",
|
||||
"initial_layer_travel_jerk",
|
||||
"exclude_object",
|
||||
"exclude_object_tolerance",
|
||||
"slow_down_layers",
|
||||
"infill_anchor",
|
||||
"infill_anchor_max",
|
||||
|
||||
@@ -309,6 +309,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
"gcode_comments",
|
||||
"gcode_label_objects",
|
||||
"exclude_object",
|
||||
"exclude_object_tolerance",
|
||||
"support_material_interface_fan_speed",
|
||||
"internal_bridge_fan_speed", // ORCA: Add support for separate internal bridge fan speed control
|
||||
"ironing_fan_speed",
|
||||
@@ -6929,7 +6930,7 @@ BoundingBoxf3 PrintInstance::get_bounding_box() const {
|
||||
|
||||
Polygon PrintInstance::get_convex_hull_2d() {
|
||||
Polygon poly = print_object->model_object()->convex_hull_2d(model_instance->get_matrix());
|
||||
poly.douglas_peucker(scale_(0.1));
|
||||
poly.douglas_peucker(scale_(print_object->print()->config().exclude_object_tolerance.value));
|
||||
return poly;
|
||||
}
|
||||
|
||||
|
||||
@@ -4629,6 +4629,17 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("exclude_object_tolerance", coFloat);
|
||||
def->label = L("Outline tolerance");
|
||||
def->tooltip = L("Maximum deviation of the object outline written to EXCLUDE_OBJECT_DEFINE from the "
|
||||
"original outline. Larger values give fewer points, which helps firmware that stalls "
|
||||
"while parsing G-code with many objects. 0 keeps every point.");
|
||||
def->sidetext = L("mm");
|
||||
def->min = 0;
|
||||
def->max = 5;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(0.1));
|
||||
|
||||
def = this->add("gcode_comments", coBool);
|
||||
def->label = L("Verbose G-code");
|
||||
def->tooltip = L("Enable this to get a commented G-code file, with each line explained by a descriptive text. "
|
||||
|
||||
@@ -2130,6 +2130,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
||||
((ConfigOptionPercents, filament_shrinkage_compensation_z))
|
||||
((ConfigOptionBool, gcode_label_objects))
|
||||
((ConfigOptionBool, exclude_object))
|
||||
((ConfigOptionFloat, exclude_object_tolerance))
|
||||
((ConfigOptionFloats, grab_length))
|
||||
((ConfigOptionBool, gcode_comments))
|
||||
((ConfigOptionInt, slow_down_layers))
|
||||
|
||||
@@ -1245,6 +1245,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
|
||||
if(gcf_is_klipper)
|
||||
toggle_field("accel_to_decel_factor", config->opt_bool("accel_to_decel_enable"));
|
||||
|
||||
toggle_line("exclude_object_tolerance", gcf_is_klipper && config->opt_bool("exclude_object"));
|
||||
|
||||
bool have_make_overhang_printable = config->opt_bool("make_overhang_printable");
|
||||
toggle_line("make_overhang_printable_angle", have_make_overhang_printable);
|
||||
toggle_line("make_overhang_printable_hole_size", have_make_overhang_printable);
|
||||
|
||||
@@ -3269,6 +3269,7 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("gcode_comments", "others_settings_g_code_output#verbose-g-code");
|
||||
optgroup->append_single_option_line("gcode_label_objects", "others_settings_g_code_output#label-objects");
|
||||
optgroup->append_single_option_line("exclude_object", "others_settings_g_code_output#exclude-objects");
|
||||
optgroup->append_single_option_line("exclude_object_tolerance", "others_settings_g_code_output#outline-tolerance");
|
||||
option = optgroup->get_option("filename_format");
|
||||
// option.opt.full_width = true;
|
||||
option.opt.is_code = true;
|
||||
|
||||
@@ -51,6 +51,44 @@ TEST_CASE("Klipper object labels name each copy without the characters Klipper c
|
||||
}
|
||||
}
|
||||
|
||||
// The outline written to EXCLUDE_OBJECT_DEFINE is simplified with exclude_object_tolerance. A larger value must
|
||||
// leave fewer points, which keeps the exclude-object array small on printers that stall while parsing it.
|
||||
TEST_CASE("A larger exclude object tolerance writes fewer outline points", "[GCode]")
|
||||
{
|
||||
const auto slice_with_tolerance = [](const std::string &tolerance) {
|
||||
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
||||
config.set_deserialize_strict({{"gcode_flavor", "klipper"}, {"exclude_object", "1"},
|
||||
{"exclude_object_tolerance", tolerance}});
|
||||
// Default facet angle (2 degrees): 180 hull points, well inside either tolerance.
|
||||
return Test::slice({make_cylinder(20., 5.)}, config);
|
||||
};
|
||||
// Points of the POLYGON=[[x,y],...] on the first EXCLUDE_OBJECT_DEFINE line.
|
||||
const auto polygon_points = [](const std::string &gcode) {
|
||||
const size_t define = gcode.find("EXCLUDE_OBJECT_DEFINE");
|
||||
REQUIRE(define != std::string::npos);
|
||||
const size_t begin = gcode.find("POLYGON=", define);
|
||||
REQUIRE(begin != std::string::npos);
|
||||
const std::string polygon = gcode.substr(begin, gcode.find('\n', begin) - begin);
|
||||
size_t points = 1;
|
||||
for (size_t pos = polygon.find("],["); pos != std::string::npos; pos = polygon.find("],[", pos + 1))
|
||||
++points;
|
||||
return points;
|
||||
};
|
||||
|
||||
const size_t fine = polygon_points(slice_with_tolerance("0.1"));
|
||||
const size_t coarse = polygon_points(slice_with_tolerance("0.4"));
|
||||
INFO("points at 0.1 mm: " << fine << ", at 0.4 mm: " << coarse);
|
||||
CHECK(coarse >= 4);
|
||||
CHECK(coarse < fine);
|
||||
}
|
||||
|
||||
// Existing profiles must keep writing the outlines they wrote when the tolerance was a constant 0.1 mm.
|
||||
TEST_CASE("The exclude object tolerance defaults to the former fixed 0.1 mm", "[GCode]")
|
||||
{
|
||||
CHECK_THAT(DynamicPrintConfig::full_print_config().opt_float("exclude_object_tolerance"),
|
||||
Catch::Matchers::WithinAbs(0.1, 1e-9));
|
||||
}
|
||||
|
||||
// Firmwares read the compatibility header lines (Anycubic's GoKlipper heats the bed with
|
||||
// first_layer_bed_temperature), so they must describe the filament the plate prints, not filament 1.
|
||||
TEST_CASE("The G-code header names the first layer temperatures of the filament the plate prints", "[GCode]")
|
||||
|
||||
@@ -505,6 +505,28 @@ TEST_CASE("A project whose components reference themselves fails to load", "[3mf
|
||||
REQUIRE_FALSE(loaded);
|
||||
}
|
||||
|
||||
TEST_CASE("A project in the 2.4.x layout keeps auto drop off from its build item", "[3mf][Regression]")
|
||||
{
|
||||
// 2.4.x wrote auto_drop on the build item and left it off the object, so the object is re-laid out here.
|
||||
ScopedTemporaryFile temp(".3mf");
|
||||
{
|
||||
Model model;
|
||||
ModelObject* object = model.add_object();
|
||||
object->add_volume(make_cube(10., 10., 10.));
|
||||
object->add_instance();
|
||||
object->auto_drop = false;
|
||||
store_project(model, temp.string(), SaveStrategy::Zip64 | SaveStrategy::Silence);
|
||||
}
|
||||
REQUIRE(replace_in_3mf_entry(temp.string(), "model_settings.config", " auto_drop=\"0\"", ""));
|
||||
REQUIRE(replace_in_3mf_entry(temp.string(), "3dmodel.model", "printable=\"1\"/>", "printable=\"1\" auto_drop=\"0\"/>"));
|
||||
|
||||
ScopedTemporaryDir backup_dir("orca_auto_drop_dst");
|
||||
Model model;
|
||||
REQUIRE(load_project(temp.string(), model, backup_dir));
|
||||
REQUIRE(model.objects.size() == 1);
|
||||
CHECK_FALSE(model.objects.front()->auto_drop);
|
||||
}
|
||||
|
||||
TEST_CASE("An object loads up to the component reference budget and fails past it", "[3mf][Regression]")
|
||||
{
|
||||
// The importer queues at most 100000 component references per object. Every reference besides the
|
||||
|
||||
@@ -1263,55 +1263,6 @@ TEST_CASE("read_cli rejects an invalid scalar numeric value", "[Config]") {
|
||||
REQUIRE_FALSE(config.read_cli(3, argv, &extra, &keys));
|
||||
}
|
||||
|
||||
TEST_CASE("A scalar option keeps its value when deserializing fails", "[Config]") {
|
||||
using Catch::Matchers::WithinAbs;
|
||||
|
||||
SECTION("float") {
|
||||
ConfigOptionFloat opt(7.5);
|
||||
REQUIRE_FALSE(opt.deserialize("abc"));
|
||||
CHECK_THAT(opt.value, WithinAbs(7.5, 1e-9));
|
||||
}
|
||||
SECTION("int") {
|
||||
ConfigOptionInt opt(3);
|
||||
REQUIRE_FALSE(opt.deserialize("abc"));
|
||||
CHECK(opt.value == 3);
|
||||
}
|
||||
SECTION("percent") {
|
||||
ConfigOptionPercent opt(40.);
|
||||
REQUIRE_FALSE(opt.deserialize("abc%"));
|
||||
CHECK_THAT(opt.value, WithinAbs(40., 1e-9));
|
||||
}
|
||||
SECTION("float or percent") {
|
||||
ConfigOptionFloatOrPercent opt(0.4, false);
|
||||
REQUIRE_FALSE(opt.deserialize("abc%"));
|
||||
CHECK_THAT(opt.value, WithinAbs(0.4, 1e-9));
|
||||
CHECK_FALSE(opt.percent);
|
||||
}
|
||||
SECTION("point") {
|
||||
ConfigOptionPoint opt(Vec2d(1., 2.));
|
||||
REQUIRE_FALSE(opt.deserialize("5,abc"));
|
||||
CHECK(opt.value == Vec2d(1., 2.));
|
||||
}
|
||||
SECTION("point3") {
|
||||
ConfigOptionPoint3 opt(Vec3d(1., 2., 3.));
|
||||
REQUIRE_FALSE(opt.deserialize("5,6,abc"));
|
||||
CHECK(opt.value == Vec3d(1., 2., 3.));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("A scalar option still takes a valid value", "[Config]") {
|
||||
using Catch::Matchers::WithinAbs;
|
||||
|
||||
ConfigOptionFloatOrPercent opt(0.4, false);
|
||||
REQUIRE(opt.deserialize("50%"));
|
||||
CHECK_THAT(opt.value, WithinAbs(50., 1e-9));
|
||||
CHECK(opt.percent);
|
||||
|
||||
ConfigOptionPoint point;
|
||||
REQUIRE(point.deserialize("3x4"));
|
||||
CHECK(point.value == Vec2d(3., 4.));
|
||||
}
|
||||
|
||||
TEST_CASE("read_cli appends values when a vector option is repeated", "[Config]") {
|
||||
Slic3r::DynamicPrintConfig config;
|
||||
t_config_option_keys extra, keys;
|
||||
|
||||
@@ -150,6 +150,13 @@ public:
|
||||
return m_close_frames;
|
||||
}
|
||||
|
||||
// The printer answers a close frame before its own read completes, so the agent's stop can return first.
|
||||
bool wait_for_close_frames(int count)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
return m_changed.wait_for(lock, 10s, [&] { return m_close_frames >= count; });
|
||||
}
|
||||
|
||||
// Hangs up every connection that is waiting.
|
||||
void release()
|
||||
{
|
||||
@@ -277,6 +284,7 @@ private:
|
||||
if (ec == websocket::error::closed) {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
++m_close_frames;
|
||||
m_changed.notify_all();
|
||||
}
|
||||
if (ec)
|
||||
reading = false;
|
||||
@@ -449,6 +457,7 @@ TEST_CASE("Stopping the status stream sends the printer a close frame", "[Moonra
|
||||
REQUIRE(f.printer.wait_for_ready_connections(1));
|
||||
|
||||
CHECK(f.stops_within(3s));
|
||||
CHECK(f.printer.wait_for_close_frames(1));
|
||||
CHECK(f.printer.close_frames() == 1);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user