From 06f0481c57151c8dbb14dfa9d1ce73b521f1168a Mon Sep 17 00:00:00 2001 From: supermerill Date: Tue, 28 Jan 2025 14:30:57 +0100 Subject: [PATCH 1/9] update last_pos if not set --- src/libslic3r/GCode.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 64d4664e4d..d42158cc67 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2572,7 +2572,8 @@ std::string GCodeGenerator::placeholder_parser_process( } else { //update our current position Point pt_updated = this->gcode_to_point({pos[0], pos[1]}); - if (!is_approx(pt_updated.x(), last_pos().x(), SCALED_EPSILON) || + if (!last_pos_defined() || + !is_approx(pt_updated.x(), last_pos().x(), SCALED_EPSILON) || !is_approx(pt_updated.y(), last_pos().y(), SCALED_EPSILON)) { set_last_pos(pt_updated); } @@ -7931,7 +7932,7 @@ std::string GCodeGenerator::set_extruder(uint16_t extruder_id, double print_z, b ensure_end_object_change_labels(gcode); //just for testing - assert(is_approx(this->writer().get_position().z(), print_z, EPSILON)); + assert(m_layer == nullptr || is_approx(this->writer().get_position().z(), print_z, EPSILON)); // if we are running a single-extruder setup, just set the extruder and return nothing if (!m_writer.multiple_extruders) { From 2a71454ea9f994930d32435ab74e98a1cf455e93 Mon Sep 17 00:00:00 2001 From: supermerill Date: Tue, 28 Jan 2025 16:32:18 +0100 Subject: [PATCH 2/9] Fix organic support with zdFilament zgap type Also grey out all support option unuseable with organic support. --- src/libslic3r/PrintConfig.cpp | 3 ++- src/libslic3r/PrintConfig.hpp | 4 ++-- src/libslic3r/Support/SupportParameters.cpp | 2 +- src/libslic3r/Support/TreeSupport.cpp | 2 +- src/libslic3r/Support/TreeSupportCommon.cpp | 17 ++++++++++++++++- src/slic3r/GUI/ConfigManipulation.cpp | 10 ++++++++++ 6 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 548f6651bc..a14837bcab 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -6242,7 +6242,8 @@ void PrintConfigDef::init_fff_params() def->category = OptionCategory::support; def->tooltip = L("Style and shape of the support towers. Projecting the supports into a regular grid " "will create more stable supports, while snug support towers will save material and reduce " - "object scarring."); + "object scarring." + "\nOrganic: create tree support structure, but this algorithm force to synchronize the support layers with object layers, and only allow for one layer height for each object."); def->set_enum({ { "grid", L("Grid") }, { "snug", L("Snug") }, diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index a4e64aa431..d91850f83b 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -808,6 +808,8 @@ PRINT_CONFIG_CLASS_DEFINE( // support_material_bottom_contact_distance (PS 2.4) == support_material_contact_distance_bottom (SuSi 2.3 &-) ((ConfigOptionFloatOrPercent, support_material_bottom_contact_distance)) ((ConfigOptionEnum, support_material_bottom_interface_pattern)) + // Morphological closing of support areas. Only used for "sung" supports. + ((ConfigOptionFloat, support_material_closing_radius)) ((ConfigOptionInt, support_material_enforce_layers)) ((ConfigOptionInt, support_material_extruder)) ((ConfigOptionFloatOrPercent, support_material_extrusion_width)) @@ -822,8 +824,6 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, support_material_interface_spacing)) ((ConfigOptionFloatOrPercent, support_material_interface_speed)) ((ConfigOptionEnum, support_material_pattern)) - // Morphological closing of support areas. Only used for "sung" supports. - ((ConfigOptionFloat, support_material_closing_radius)) ((ConfigOptionFloatOrPercent, support_material_layer_height)) // Spacing between support material lines (the hatching distance). ((ConfigOptionFloat, support_material_spacing)) diff --git a/src/libslic3r/Support/SupportParameters.cpp b/src/libslic3r/Support/SupportParameters.cpp index 9c507ce9a8..63367684da 100644 --- a/src/libslic3r/Support/SupportParameters.cpp +++ b/src/libslic3r/Support/SupportParameters.cpp @@ -148,7 +148,7 @@ SupportParameters::SupportParameters(const PrintObject &object) this->base_angle = Geometry::deg2rad(float(object_config.support_material_angle.value)); this->base_angle_height = object_config.support_material_angle_height.value; - this->interface_angle = Geometry::deg2rad(float(object_config.support_material_angle.value + 90.)); + this->interface_angle = Geometry::deg2rad(float(object_config.support_material_interface_angle.value)); this->interface_angle_incr = Geometry::deg2rad(float(object_config.support_material_interface_angle_increment.value)); this->raft_angle_1st_layer = 0.f; this->raft_angle_base = 0.f; diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 65e9f2493c..a7509d2f7f 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -128,7 +128,7 @@ static std::vector>> group_me for (size_t object_id : print_object_ids) { const PrintObject &print_object = *print.get_object(object_id); const PrintObjectConfig &object_config = print_object.config(); - if (object_config.support_material_contact_distance < EPSILON) + if (object_config.support_material_contact_distance_type.value == zdNone) // || min_feature_size < scaled(0.1) that is the minimum line width TreeSupportSettings::soluble = true; } diff --git a/src/libslic3r/Support/TreeSupportCommon.cpp b/src/libslic3r/Support/TreeSupportCommon.cpp index 0861d704b2..f1db8d754e 100644 --- a/src/libslic3r/Support/TreeSupportCommon.cpp +++ b/src/libslic3r/Support/TreeSupportCommon.cpp @@ -31,7 +31,7 @@ TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(const PrintObject &pr const PrintRegion ®ion = print_object.printing_region(region_id); external_perimeter_width = std::max(external_perimeter_width, region.flow(print_object, frExternalPerimeter, config.layer_height, 2 /*not first layer, even layer*/).width()); } - + this->layer_height = scaled(config.layer_height.value); this->resolution = scaled(print_config.resolution_internal.value); // Arache feature <- why? it's not even editable when the organic support are activated! And it doesn't take into account the %! I'll fix it to 25% of external_perimeter_width. @@ -53,8 +53,23 @@ TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(const PrintObject &pr this->support_xy_distance = scaled(config.support_material_xy_spacing.get_abs_value(external_perimeter_width)); // Separation of interfaces, it is likely smaller than support_xy_distance. this->support_xy_distance_overhang = std::min(this->support_xy_distance, scaled(0.5 * external_perimeter_width)); + // compute separate support_material_contact_distance_type. As organic support only support one layer height, + // it has to be done here, where we have access to the printobject this->support_top_distance = scaled(slicing_params.gap_support_object); this->support_bottom_distance = scaled(slicing_params.gap_object_support); + if (config.support_material_contact_distance_type.value == zdNone) { + this->support_top_distance = 0; + this->support_bottom_distance = 0; + } else if (config.support_material_contact_distance_type.value == zdFilament && print_object.layers().size() > 0 && + print_object.layers().front()->regions().size() > 0) { + //get one region, with organic support there is only one layer height anyway + assert(print_object.num_printing_regions() > 0); + const LayerRegion *lr = print_object.layers().front()->regions().front(); + assert(is_approx(lr->layer()->height, config.layer_height.value, EPSILON)); + coord_t diff_lh_filamenth = scale_t(lr->bridging_height_avg()) - this->layer_height; + this->support_top_distance += diff_lh_filamenth; + this->support_bottom_distance += diff_lh_filamenth; + } // this->support_interface_skip_height = // this->support_infill_angles = this->support_roof_enable = config.support_material_interface_layers.value > 0; diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 19d7b6beb0..b1744f8add 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -530,6 +530,16 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field(el, have_support_material && have_support_interface); toggle_field("support_material_synchronize_layers", have_support_soluble); + // organic suport don't use soem fields, force disable them. + if (has_organic_supports) { + for (const std::string &key : + {"support_material_interface_layer_height", "support_material_bottom_interface_pattern", + "support_material_interface_contact_loops", "support_material_with_sheath", "support_material_pattern", + "support_material_spacing", "support_material_angle", "support_material_angle_height", "support_material_layer_height", + "support_material_bottom_interface_pattern"}) + toggle_field(key, false); + } + toggle_field("perimeter_extrusion_width", have_perimeters || have_brim); toggle_field("perimeter_extrusion_spacing", have_perimeters || have_brim); toggle_field("perimeter_extrusion_change_odd_layers", have_perimeters || have_brim); From 34fe23737a9ab050fa83e3ea49e22aed509630cf Mon Sep 17 00:00:00 2001 From: supermerill Date: Tue, 28 Jan 2025 16:40:37 +0100 Subject: [PATCH 3/9] write temperature comment only if gcode_comments also fix chamber_temperature tooltip --- src/libslic3r/GCode/GCodeWriter.cpp | 42 +++++++++++++++++++++-------- src/libslic3r/PrintConfig.cpp | 2 +- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/GCode/GCodeWriter.cpp b/src/libslic3r/GCode/GCodeWriter.cpp index 24741cae2b..dfe8f50f30 100644 --- a/src/libslic3r/GCode/GCodeWriter.cpp +++ b/src/libslic3r/GCode/GCodeWriter.cpp @@ -295,12 +295,20 @@ std::string GCodeWriter::set_temperature(const int16_t temperature, bool wait, i FLAVOR_IS_NOT(gcfRepRap)) { gcode << " T" << tool; } - gcode << " ; " << comment << "\n"; + if (this->config.gcode_comments) { + gcode << " ; " << comment; + } + gcode << "\n"; } // emit wait (for gcfTeacup, gcfRepRap, gcfNematX) if (wait && !can_M109) { - if ((FLAVOR_IS(gcfTeacup) || FLAVOR_IS(gcfRepRap))) - gcode << "M116 ; wait for temperature to be reached\n"; + if ((FLAVOR_IS(gcfTeacup) || FLAVOR_IS(gcfRepRap))) { + gcode << "M116"; + if (this->config.gcode_comments) { + gcode << " ; wait for temperature to be reached"; + } + gcode << "\n"; + } } // update internal var to prevent repeat m_last_temperature = temperature; @@ -329,7 +337,7 @@ std::string GCodeWriter::set_bed_temperature(uint32_t temperature, bool wait) code = "M140"sv; comment = "set bed temperature"sv; } - + std::ostringstream gcode; gcode << code << " "; if (FLAVOR_IS(gcfMach3) || FLAVOR_IS(gcfMachinekit)) { @@ -337,11 +345,20 @@ std::string GCodeWriter::set_bed_temperature(uint32_t temperature, bool wait) } else { gcode << "S"; } - gcode << temperature << " ; " << comment << "\n"; - - if (FLAVOR_IS(gcfTeacup) && wait) - gcode << "M116 ; wait for bed temperature to be reached\n"; - + gcode << temperature; + if (this->config.gcode_comments) { + gcode << " ; " << comment; + } + gcode << "\n"; + + if (FLAVOR_IS(gcfTeacup) && wait) { + gcode << "M116"; + if (this->config.gcode_comments) { + gcode << " ; wait for temperature to be reached"; + } + gcode << "\n"; + } + return gcode.str(); } @@ -368,8 +385,11 @@ std::string GCodeWriter::set_chamber_temperature(uint32_t temperature, bool wait } std::ostringstream gcode; - gcode << code << " " << "S"; - gcode << temperature << " ; " << comment << "\n"; + gcode << code << " " << "S" << temperature; + if (this->config.gcode_comments) { + gcode << " ; " << comment; + } + gcode << "\n"; return gcode.str(); } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index a14837bcab..6ef25d28e3 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1101,7 +1101,7 @@ void PrintConfigDef::init_fff_params() def->label = L("Chamber"); def->full_label = L("Chamber temperature"); def->category = OptionCategory::cooling; - def->tooltip = L("Chamber temperature. Note that this setting doesn't do anything, but you can access it in Start G-code, Tool change G-code and the other ones, like for other temperature settings."); + def->tooltip = L("Chamber temperature."); def->sidetext = L("°C"); def->min = 0; def->max = 300; From 4cb44ce29bfa78d785689351a0ca810536457abd Mon Sep 17 00:00:00 2001 From: supermerill Date: Wed, 29 Jan 2025 19:11:45 +0100 Subject: [PATCH 4/9] move "Shape Gallery" from "window" menu to "generate" --- src/slic3r/GUI/MainFrame.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index ee2c5f7f3a..4b289a0ebc 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -2005,17 +2005,6 @@ void MainFrame::init_menubar_as_editor() []() {return true; }, this); m_changeable_menu_items.push_back(item_printer_tab); - windowMenu->AppendSeparator(); - append_menu_item(windowMenu, wxID_ANY, _L("Shape Gallery"), _L("Open the dialog to modify shape gallery"), - [this](wxCommandEvent&) { - if (gallery_dialog()->show(true) == wxID_OK) { - wxArrayString input_files; - m_gallery_dialog->get_input_files(input_files); - if (!input_files.IsEmpty()) - m_plater->sidebar().obj_list()->load_shape_object_from_gallery(input_files); - } - }, "shape_gallery", nullptr, []() {return true; }, this); - windowMenu->AppendSeparator(); append_menu_item(windowMenu, wxID_ANY, _L("Print &Host Upload Queue") + "\tCtrl+J", _L("Display the Print Host Upload Queue window"), [this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, "upload_queue", nullptr, []() {return true; }, this); @@ -2090,6 +2079,17 @@ void MainFrame::init_menubar_as_editor() if (wxGetApp().is_editor()) { generationMenu = new wxMenu(); + append_menu_item(generationMenu, wxID_ANY, _L("Shape Gallery"), _L("Open the dialog to modify shape gallery"), + [this](wxCommandEvent&) { + if (gallery_dialog()->show(true) == wxID_OK) { + wxArrayString input_files; + m_gallery_dialog->get_input_files(input_files); + if (!input_files.IsEmpty()) + m_plater->sidebar().obj_list()->load_shape_object_from_gallery(input_files); + } + }, "shape_gallery", nullptr, []() {return true; }, this); + + generationMenu->AppendSeparator(); append_menu_item(generationMenu, wxID_ANY, _(L("FreeCad python script")), _(L("Create an object by writing little easy script.")), [this](wxCommandEvent&) { wxGetApp().freecad_script_dialog(); }); append_menu_item(generationMenu, wxID_ANY, _(L("Script help page")), _(L("How to use the FreeCad python script window.")), From 4e06935f524a03bebb257b35cfd2374b42167959 Mon Sep 17 00:00:00 2001 From: supermerill Date: Wed, 29 Jan 2025 19:14:09 +0100 Subject: [PATCH 5/9] fix revert setting on array type --- src/slic3r/GUI/OptionsGroup.cpp | 2 +- src/slic3r/GUI/OptionsGroup.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 060f8874f0..544d553b46 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -1078,7 +1078,7 @@ std::pair ConfigOptionsGroup::get_custom_ctrl_with_blinki } // Change an option on m_config, possibly call ModelConfig::touch(). -void ConfigOptionsGroup::change_opt_value(const t_config_option_key& opt_key, bool enable, const boost::any& value, int opt_index /*= 0*/) +void ConfigOptionsGroup::change_opt_value(const t_config_option_key& opt_key, bool enable, const boost::any& value, int opt_index /*= -1*/) { if (m_config_mutable) { ConfigOption *opt = m_config_mutable->option(opt_key); diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp index a2cfe1e42f..83837805ce 100644 --- a/src/slic3r/GUI/OptionsGroup.hpp +++ b/src/slic3r/GUI/OptionsGroup.hpp @@ -373,7 +373,7 @@ class ConfigOptionsGroup: public OptionsGroup { int m_config_type; // Change an option on m_config, possibly call ModelConfig::touch(). - void change_opt_value(const t_config_option_key& opt_key, bool enable, const boost::any& value, int opt_index = 0); + void change_opt_value(const t_config_option_key& opt_key, bool enable, const boost::any& value, int opt_index = -1); }; // Static text shown among the options. From 3389630cf9dea5d7a181fa6a63f994bd8a87905f Mon Sep 17 00:00:00 2001 From: supermerill Date: Thu, 30 Jan 2025 14:01:11 +0100 Subject: [PATCH 6/9] fix flow calibration z --- src/slic3r/GUI/CalibrationFlowDialog.cpp | 34 ++++++++++++++---------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/CalibrationFlowDialog.cpp b/src/slic3r/GUI/CalibrationFlowDialog.cpp index c228a297c7..3c5ddd2c66 100644 --- a/src/slic3r/GUI/CalibrationFlowDialog.cpp +++ b/src/slic3r/GUI/CalibrationFlowDialog.cpp @@ -91,6 +91,8 @@ void CalibrationFlowDialog::create_geometry(float start, float delta) { layer_height = check_z_step(layer_height, printerConfig->option("z_step")->value); // If z_step is not 0 the slicer will scale to the nearest multiple of z_step so account for that here first_layer_height = std::max(first_layer_height, nozzle_diameter / 2.); + // (zscale / 2) represents the midpoint of the filament_flow_test_cube. Note: we ned to use the height of filament_flow_test_cube before scaling. + float z_origin = 0.5f; float zscale = first_layer_height + 5 * layer_height; //do scaling if (xyScale < 0.9 || 1.2 < xyScale) { @@ -100,15 +102,19 @@ void CalibrationFlowDialog::create_geometry(float start, float delta) { for (size_t i = 0; i < 5; i++) model.objects[objs_idx[i]]->scale(1, 1, zscale); } + //// move to bed + //for (size_t i = 0; i < 5; i++) + // model.objects[objs_idx[i]]->translate(0, 0, ); // base: 10 10 1 + //add sub-part after scale float zscale_number = (first_layer_height + layer_height) / 0.4; /* zshift is calculated using the following: - (zscale / 2) represents the midpoint of the filament_flow_test_cube + z_origin: we go back to 0 by removing it. ((first_layer_height + layer_height) / 2) represents the midpoint of our indicator tab (it is scaled to be 2 layers tall) The 0.3 constant is the same as the delta calculated in add_part below, this should probably be calculated per the model object */ - float zshift = -(zscale / 2) + ((first_layer_height + layer_height) / 2) + 0.3; + float zshift_number = -z_origin + ((first_layer_height + layer_height) / 2) + 0.3; // it's rotated but not around the good origin: correct that double init_z_rotate_angle = Geometry::deg2rad(plat->config()->opt_float("init_z_rotate")); @@ -121,21 +127,21 @@ void CalibrationFlowDialog::create_geometry(float start, float delta) { }; if (delta == 10.f && start == 80.f) { - add_part(model.objects[objs_idx[0]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "m20.amf").string(), Vec3d{ 10 * xyScale,0,zshift }, Vec3d{ xyScale , xyScale, zscale_number}); - add_part(model.objects[objs_idx[1]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "m10.amf").string(), Vec3d{ 10 * xyScale,0,zshift }, Vec3d{ xyScale , xyScale, zscale_number }); - add_part(model.objects[objs_idx[2]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "_0.amf").string(), Vec3d{ 10 * xyScale,0,zshift }, Vec3d{ xyScale , xyScale, zscale_number }); - add_part(model.objects[objs_idx[3]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "p10.amf").string(), Vec3d{ 10 * xyScale,0,zshift }, Vec3d{ xyScale , xyScale, zscale_number }); - add_part(model.objects[objs_idx[4]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "p20.amf").string(), Vec3d{ 10 * xyScale,0,zshift }, Vec3d{ xyScale , xyScale, zscale_number }); + add_part(model.objects[objs_idx[0]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "m20.amf").string(), Vec3d{ 10 * xyScale,0,zshift_number }, Vec3d{ xyScale , xyScale, zscale_number}); + add_part(model.objects[objs_idx[1]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "m10.amf").string(), Vec3d{ 10 * xyScale,0,zshift_number }, Vec3d{ xyScale , xyScale, zscale_number }); + add_part(model.objects[objs_idx[2]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "_0.amf").string(), Vec3d{ 10 * xyScale,0,zshift_number }, Vec3d{ xyScale , xyScale, zscale_number }); + add_part(model.objects[objs_idx[3]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "p10.amf").string(), Vec3d{ 10 * xyScale,0,zshift_number }, Vec3d{ xyScale , xyScale, zscale_number }); + add_part(model.objects[objs_idx[4]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "p20.amf").string(), Vec3d{ 10 * xyScale,0,zshift_number }, Vec3d{ xyScale , xyScale, zscale_number }); } else if (delta == 2.f && start == 92.f) { - add_part(model.objects[objs_idx[0]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "m8.amf").string(), Vec3d{ 10 * xyScale,0,zshift }, Vec3d{ xyScale , xyScale, zscale_number}); - add_part(model.objects[objs_idx[1]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "m6.amf").string(), Vec3d{ 10 * xyScale,0,zshift }, Vec3d{ xyScale , xyScale, zscale_number}); - add_part(model.objects[objs_idx[2]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "m4.amf").string(), Vec3d{ 10 * xyScale,0,zshift }, Vec3d{ xyScale , xyScale, zscale_number}); - add_part(model.objects[objs_idx[3]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "m2.amf").string(), Vec3d{ 10 * xyScale,0,zshift }, Vec3d{ xyScale , xyScale, zscale_number}); - add_part(model.objects[objs_idx[4]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "_0.amf").string(), Vec3d{ 10 * xyScale,0,zshift }, Vec3d{ xyScale , xyScale, zscale_number}); + add_part(model.objects[objs_idx[0]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "m8.amf").string(), Vec3d{ 10 * xyScale,0,zshift_number }, Vec3d{ xyScale , xyScale, zscale_number}); + add_part(model.objects[objs_idx[1]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "m6.amf").string(), Vec3d{ 10 * xyScale,0,zshift_number }, Vec3d{ xyScale , xyScale, zscale_number}); + add_part(model.objects[objs_idx[2]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "m4.amf").string(), Vec3d{ 10 * xyScale,0,zshift_number }, Vec3d{ xyScale , xyScale, zscale_number}); + add_part(model.objects[objs_idx[3]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "m2.amf").string(), Vec3d{ 10 * xyScale,0,zshift_number }, Vec3d{ xyScale , xyScale, zscale_number}); + add_part(model.objects[objs_idx[4]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "_0.amf").string(), Vec3d{ 10 * xyScale,0,zshift_number }, Vec3d{ xyScale , xyScale, zscale_number}); } for (size_t i = 0; i < 5; i++) { - translate_from_rotation(i, Vec3d{ 10 * xyScale,0,zshift }); - add_part(model.objects[objs_idx[i]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "O.amf").string(), Vec3d{ 0,0,zscale/2.f + 0.5 }, Vec3d{xyScale , xyScale, layer_height / 0.2}); // base: 0.2mm height + translate_from_rotation(i, Vec3d{ 10 * xyScale, 0, zscale/2 - z_origin }); + add_part(model.objects[objs_idx[i]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "O.amf").string(), Vec3d{ 0,0, zscale }, Vec3d{xyScale , xyScale, layer_height / 0.2}); // base: 0.2mm height } From 1b3600b29c2abd25f0a674ca30f79a09514fe51b Mon Sep 17 00:00:00 2001 From: supermerill Date: Thu, 30 Jan 2025 14:28:41 +0100 Subject: [PATCH 7/9] fix (crash) hole perimeter merging into contour --- src/libslic3r/PerimeterGenerator.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 07b737c269..98885e646a 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -4005,6 +4005,7 @@ void grow_holes_only(std::vector &unmoveable_contours, ok_holes.erase(ok_holes.begin() + idx_hole); idx_hole--; it_contour_candidate_for_fuse = ex_contour_offset.erase(it_contour_candidate_for_fuse); + break; // stop iterating the while, we already fused the hole } else { ++it_contour_candidate_for_fuse; } From 680679ee35abe3b073858f63ae730bdb18a4ecef Mon Sep 17 00:00:00 2001 From: supermerill Date: Fri, 31 Jan 2025 15:31:00 +0100 Subject: [PATCH 8/9] Fix ArcPolyline::split_at() add point::round, even if it's not really useful. add & fix assert around ArcPolyline. --- src/libslic3r/CutSurface.cpp | 4 +- src/libslic3r/ElephantFootCompensation.cpp | 8 +- src/libslic3r/Fill/FillGyroid.cpp | 2 +- src/libslic3r/GCode.cpp | 31 ++-- src/libslic3r/GCode/SmoothPath.cpp | 12 +- src/libslic3r/GCode/Wipe.cpp | 14 +- src/libslic3r/Geometry/ArcWelder.cpp | 43 +++-- src/libslic3r/Geometry/ArcWelder.hpp | 2 + src/libslic3r/Geometry/Circle.hpp | 2 +- src/libslic3r/Point.hpp | 6 +- src/libslic3r/Polyline.cpp | 175 +++++++++++++++------ src/libslic3r/Polyline.hpp | 2 +- 12 files changed, 203 insertions(+), 98 deletions(-) diff --git a/src/libslic3r/CutSurface.cpp b/src/libslic3r/CutSurface.cpp index 67ae7b053e..7325fcacb6 100644 --- a/src/libslic3r/CutSurface.cpp +++ b/src/libslic3r/CutSurface.cpp @@ -1891,8 +1891,8 @@ uint32_t priv::get_closest_point_index(const SearchData &sd, const Polygon &poly = (id.polygon_index == 0) ? shape.contour : shape.holes[id.polygon_index - 1]; - Point p_ = p.cast(); - return p_ == poly[id.point_index]; + assert((p.cast() == poly[id.point_index]) == (Point::round(p) == poly[id.point_index])); + return Point::round(p) == poly[id.point_index]; }; if (use_index) { diff --git a/src/libslic3r/ElephantFootCompensation.cpp b/src/libslic3r/ElephantFootCompensation.cpp index 923557a9a3..810f7bce69 100644 --- a/src/libslic3r/ElephantFootCompensation.cpp +++ b/src/libslic3r/ElephantFootCompensation.cpp @@ -62,7 +62,7 @@ std::vector contour_distance(const EdgeGrid::Grid &grid, const size_t idx this->idx_point_start = aidx_point_start; this->pt = apt_start.cast() + SCALED_EPSILON * dir; dir *= radius; - this->pt_start = this->pt.cast(); + this->pt_start = Point::round(this->pt); // Trim the vector by the grid's bounding box. const BoundingBox &bbox = this->grid.bbox(); double t = 1.; @@ -77,7 +77,7 @@ std::vector contour_distance(const EdgeGrid::Grid &grid, const size_t idx this->dir = dir; if (t < 1.) dir *= t; - this->pt_end = (this->pt + dir).cast(); + this->pt_end = Point::round(this->pt + dir); this->t_min = 1.; assert(this->grid.bbox().contains(this->pt_start) && this->grid.bbox().contains(this->pt_end)); } @@ -321,7 +321,7 @@ std::vector contour_distance2(const EdgeGrid::Grid &grid, const size_t id // Simple case: Just measure the shortest distance. this->distance = dist; #ifdef CONTOUR_DISTANCE_DEBUG_SVG - this->closest_point = foot.cast(); + this->closest_point = Point::round(foot); #endif /* CONTOUR_DISTANCE_DEBUG_SVG */ this->found = true; } @@ -436,7 +436,7 @@ Points resample_polygon(const Points &contour, double dist, std::vector()); + out.push_back(Point::round(new_pt)); resampled_point_parameters.emplace_back(idx_this, true, l_step); } out.emplace_back(pt); diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp index 49c542e517..22f8ef5626 100644 --- a/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -60,7 +60,7 @@ static inline Polyline make_wave( point(1) = std::clamp(double(point.y()), 0., height); if (vertical) std::swap(point(0), point(1)); - polyline.points.emplace_back((point * scaleFactor).cast()); + polyline.points.push_back(Point::round(point * scaleFactor)); } return polyline; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index d42158cc67..7d77653d22 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -4106,7 +4106,7 @@ std::string GCodeGenerator::extrude_loop_vase(const ExtrusionLoop &original_loop double l2 = v.squaredNorm(); // Shift by no more than a nozzle diameter. //FIXME Hiding the seams will not work nicely for very densely discretized contours! - inward_point = (/*(nd * nd >= l2) ? p2 : */(p1 + v * (nd / sqrt(l2)))).cast(); + inward_point = (/*(nd * nd >= l2) ? p2 : */Point::round(p1 + v * (nd / sqrt(l2)))); inward_point.rotate(angle, paths.front().polyline.front()); } @@ -4256,7 +4256,7 @@ std::string GCodeGenerator::extrude_loop_vase(const ExtrusionLoop &original_loop double l2 = v.squaredNorm(); // Shift by no more than a nozzle diameter. //FIXME Hiding the seams will not work nicely for very densely discretized contours! - inward_point = (/*(nd * nd >= l2) ? p2 : */(p1 + v * (nd / sqrt(l2)))).cast(); + inward_point = (/*(nd * nd >= l2) ? p2 : */Point::round(p1 + v * (nd / sqrt(l2)))); inward_point.rotate(angle, paths.front().polyline.front()); // generate the travel move @@ -4589,9 +4589,9 @@ void GCodeGenerator::seam_notch(const ExtrusionLoop& original_loop, //use a vec that is the mean between the two. vec_start = (vec_start + vec_end) / 2; - Point moved_start = (start_point.cast() + vec_start * notch_value).cast(); + Point moved_start = Point::round(start_point.cast() + vec_start * notch_value); moved_start.rotate(angle, start_point); - Point moved_end = (end_point.cast() + vec_start * notch_value).cast(); + Point moved_end = Point::round(end_point.cast() + vec_start * notch_value); moved_end.rotate(angle, end_point); //check if the current angle isn't too sharp @@ -4633,6 +4633,7 @@ void GCodeGenerator::seam_notch(const ExtrusionLoop& original_loop, //reduce the flow of the notch path, as it's longer than previously path.attributes_mutable().width = path.width() * ratio; path.attributes_mutable().mm3_per_mm = path.mm3_per_mm() * ratio; + assert(path.polyline.is_valid()); }; for (ExtrusionPath &ep : notch_extrusion_start) { assert(!ep.can_reverse()); @@ -4685,9 +4686,12 @@ void GCodeGenerator::seam_notch(const ExtrusionLoop& original_loop, create_new_extrusion(notch_extrusion_end, model, 0.f, p1, moved_end); } //else : keep the path as-is } - for (ExtrusionPath &ep : notch_extrusion_start) { - assert(!ep.can_reverse()); - } + for (ExtrusionPath &ep : notch_extrusion_start) { + assert(!ep.can_reverse()); + } + for(auto &e : building_paths) assert(e.polyline.empty() || e.polyline.is_valid()); + for(auto &e : notch_extrusion_start) assert(e.polyline.empty() || e.polyline.is_valid()); + for(auto &e : notch_extrusion_end) assert(e.polyline.empty() || e.polyline.is_valid()); } std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &original_loop, const std::string_view description, double speed) @@ -4891,7 +4895,7 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &original_loop, con double vec_norm = vec_dist.norm(); const double setting_max_depth = (m_config.wipe_inside_depth.get_abs_value(m_writer.tool()->id(), nozzle_diam)); coordf_t dist = scale_d(nozzle_diam) / 2; - Point pt = (current_pos + vec_dist * (2 * dist / vec_norm)).cast(); + Point pt = Point::round(current_pos + vec_dist * (2 * dist / vec_norm)); pt.rotate(angle, current_point); //check if we can go to higher dist if (nozzle_diam != 0 && setting_max_depth > nozzle_diam * 0.55) { @@ -4900,7 +4904,7 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &original_loop, con if (m_writer.tool()->need_unretract()) { this->m_throw_if_canceled(); dist = coordf_t(check_wipe::max_depth(wipe_paths, scale_t(setting_max_depth), scale_t(nozzle_diam), [current_pos, current_point, vec_dist, vec_norm, angle](coord_t dist)->Point { - Point pt = (current_pos + vec_dist * (2 * dist / vec_norm)).cast(); + Point pt = Point::round(current_pos + vec_dist * (2 * dist / vec_norm)); pt.rotate(angle, current_point); return pt; })); @@ -4908,7 +4912,7 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &original_loop, con } // Shift by no more than a nozzle diameter. //FIXME Hiding the seams will not work nicely for very densely discretized contours! - pt = (/*(nd >= vec_norm) ? next_pos : */(current_pos + vec_dist * ( 2 * dist / vec_norm))).cast(); + pt = Point::round(/*(nd >= vec_norm) ? next_pos : */(current_pos + vec_dist * ( 2 * dist / vec_norm))); pt.rotate(angle, current_point); //gcode += m_writer.travel_to_xy(this->point_to_gcode(pt), 0.0, "move inwards before retraction/seam"); //this->set_last_pos(pt); @@ -5078,14 +5082,13 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &original_loop, con if (nozzle_diam != 0 && setting_max_depth > nozzle_diam * 0.55) dist = coordf_t(check_wipe::max_depth(wipe_paths, scale_t(setting_max_depth), scale_t(nozzle_diam), [current_pos, current_point, vec_dist, vec_norm, angle, sin_a](coord_t dist)->Point { - Point pt = (current_pos + vec_dist * (dist / (vec_norm * sin_a))).cast(); + Point pt = Point::round(current_pos + vec_dist * (dist / (vec_norm * sin_a))); pt.rotate(angle, current_point); return pt; })); // Shift by no more than a nozzle diameter. // FIXME Hiding the seams will not work nicely for very densely discretized contours! - Point pt_inside = (/*(nd >= vec_norm) ? next_pos : */ (current_pos + vec_dist * ( dist / (vec_norm * sin_a)))) - .cast(); + Point pt_inside = Point::round(/*(nd >= vec_norm) ? next_pos : */ (current_pos + vec_dist * ( dist / (vec_norm * sin_a)))); pt_inside.rotate(angle, current_point); if (EXTRUDER_CONFIG_WITH_DEFAULT(wipe_inside_end, true)) { @@ -5460,6 +5463,7 @@ void GCodeGenerator::use(const ExtrusionEntityCollection &collection) { std::string GCodeGenerator::extrude_path(const ExtrusionPath &path, const std::string_view description, double speed_mm_per_sec) { std::string gcode; ExtrusionPath simplifed_path = path; + assert(simplifed_path.polyline.empty() || simplifed_path.polyline.is_valid()); for (int i = 1; i < simplifed_path.polyline.size(); ++i) assert(!simplifed_path.polyline.get_point(i - 1).coincides_with_epsilon(simplifed_path.polyline.get_point(i))); @@ -5511,6 +5515,7 @@ std::string GCodeGenerator::extrude_path(const ExtrusionPath &path, const std::s 0; if (scaled_min_length > 0 && simplifed_path.length() < scaled_min_length) { m_last_too_small = simplifed_path; + assert(m_last_too_small.polyline.empty() || m_last_too_small.polyline.is_valid()); m_last_description = description; m_last_speed_mm_per_sec = speed_mm_per_sec; return gcode; diff --git a/src/libslic3r/GCode/SmoothPath.cpp b/src/libslic3r/GCode/SmoothPath.cpp index f27f1f2318..b2f96ee281 100644 --- a/src/libslic3r/GCode/SmoothPath.cpp +++ b/src/libslic3r/GCode/SmoothPath.cpp @@ -41,7 +41,7 @@ std::optional sample_path_point_at_distance_from_start(const SmoothPath & Vec2d v = (point - prev_point).cast(); double lsqr = v.squaredNorm(); if (lsqr > sqr(distance)) - return std::make_optional(prev_point + (v * (distance / sqrt(lsqr))).cast()); + return std::make_optional(prev_point + Point::round(v * (distance / sqrt(lsqr)))); distance -= sqrt(lsqr); } else { // Circular segment @@ -49,8 +49,8 @@ std::optional sample_path_point_at_distance_from_start(const SmoothPath & double len = std::abs(it->radius) * angle; if (len > distance) { // Rotate the segment end point in reverse towards the start point. - return std::make_optional(prev_point.rotated(- angle * (distance / len), - Geometry::ArcWelder::arc_center(prev_point.cast(), point.cast(), it->radius, it->ccw()).cast())); + return std::make_optional(prev_point.rotated(- angle * (distance / len), Point::round( + Geometry::ArcWelder::arc_center(prev_point.cast(), point.cast(), it->radius, it->ccw())))); } distance -= len; } @@ -78,7 +78,7 @@ std::optional sample_path_point_at_distance_from_end(const SmoothPath &pa Vec2d v = (point - prev_point).cast(); double lsqr = v.squaredNorm(); if (lsqr > sqr(distance)) - return std::make_optional(prev_point + (v * (distance / sqrt(lsqr))).cast()); + return std::make_optional(prev_point + Point::round(v * (distance / sqrt(lsqr)))); distance -= sqrt(lsqr); } else { @@ -87,8 +87,8 @@ std::optional sample_path_point_at_distance_from_end(const SmoothPath &pa double len = std::abs(it->radius) * angle; if (len > distance) { // Rotate the segment end point in reverse towards the start point. - return std::make_optional(prev_point.rotated(-angle * (distance / len), - Geometry::ArcWelder::arc_center(prev_point.cast(), point.cast(), it->radius, it->ccw()).cast())); + return std::make_optional(prev_point.rotated(-angle * (distance / len), Point::round( + Geometry::ArcWelder::arc_center(prev_point.cast(), point.cast(), it->radius, it->ccw()).cast()))); } distance -= len; } diff --git a/src/libslic3r/GCode/Wipe.cpp b/src/libslic3r/GCode/Wipe.cpp index 29488bf0a6..8aaddaa51a 100644 --- a/src/libslic3r/GCode/Wipe.cpp +++ b/src/libslic3r/GCode/Wipe.cpp @@ -554,7 +554,7 @@ std::optional sample_path_point_at_distance_from_start(const ExtrusionPat Vec2d v = (point - prev_point).cast(); double lsqr = v.squaredNorm(); if (lsqr > sqr(distance)) - return std::make_optional(prev_point + (v * (distance / sqrt(lsqr))).cast()); + return std::make_optional(prev_point + Point::round(v * (distance / sqrt(lsqr)))); distance -= sqrt(lsqr); } else { // Circular segment @@ -562,8 +562,8 @@ std::optional sample_path_point_at_distance_from_start(const ExtrusionPat double len = std::abs(it->radius) * angle; if (len > distance) { // Rotate the segment end point in reverse towards the start point. - return std::make_optional(prev_point.rotated(- angle * (distance / len), - Geometry::ArcWelder::arc_center(prev_point.cast(), point.cast(), it->radius, it->ccw()).cast())); + return std::make_optional(prev_point.rotated(- angle * (distance / len), Point::round( + Geometry::ArcWelder::arc_center(prev_point.cast(), point.cast(), it->radius, it->ccw()).cast()))); } distance -= len; } @@ -592,7 +592,7 @@ std::optional sample_path_point_at_distance_from_end(const ExtrusionPaths Vec2d v = (point - prev_point).cast(); double lsqr = v.squaredNorm(); if (lsqr > sqr(distance)) - return std::make_optional(prev_point + (v * (distance / sqrt(lsqr))).cast()); + return std::make_optional(prev_point + Point::round(v * (distance / sqrt(lsqr)))); distance -= sqrt(lsqr); } else { @@ -601,8 +601,8 @@ std::optional sample_path_point_at_distance_from_end(const ExtrusionPaths double len = std::abs(it->radius) * angle; if (len > distance) { // Rotate the segment end point in reverse towards the start point. - return std::make_optional(prev_point.rotated(-angle * (distance / len), - Geometry::ArcWelder::arc_center(prev_point.cast(), point.cast(), it->radius, it->ccw()).cast())); + return std::make_optional(prev_point.rotated(-angle * (distance / len), Point::round( + Geometry::ArcWelder::arc_center(prev_point.cast(), point.cast(), it->radius, it->ccw()).cast()))); } distance -= len; } @@ -666,7 +666,7 @@ std::optional wipe_hide_seam(const ExtrusionPaths &paths, bool is_hole, d } // Rotate the forward segment inside by 1/3 of the wedge angle. auto v_rotated = Eigen::Rotation2D(angle_inside) * (p_next - p_current).cast().normalized(); - return std::make_optional(p_current + (v_rotated * wipe_length).cast()); + return std::make_optional(p_current + Point::round(v_rotated * wipe_length)); } return {}; diff --git a/src/libslic3r/Geometry/ArcWelder.cpp b/src/libslic3r/Geometry/ArcWelder.cpp index b176084b62..219eea1a97 100644 --- a/src/libslic3r/Geometry/ArcWelder.cpp +++ b/src/libslic3r/Geometry/ArcWelder.cpp @@ -79,7 +79,7 @@ Points arc_discretize(const Point &p1, const Point &p2, const double radius, con if (! is_ccw) angle_step *= -1.; for (size_t i = 1; i < num_steps; ++ i) - out.emplace_back(p1.rotated(angle_step * i, center.cast())); + out.emplace_back(p1.rotated(angle_step * i, Point::round(center))); out.emplace_back(p2); return out; } @@ -99,7 +99,7 @@ Points arc_discretize(const Point &p1, const Point &p2, const double radius, con if (!is_ccw) angle_step *= -1.; for (size_t i = 1; i < num_steps; ++i) - out.emplace_back(p1.rotated(angle_step * i, center.cast())); + out.emplace_back(p1.rotated(angle_step * i, Point::round(center))); out.emplace_back(p2); return out; } @@ -115,7 +115,7 @@ struct Circle static std::optional try_create_circle(const Point &p1, const Point &p2, const Point &p3, const double max_radius) { if (auto center = Slic3r::Geometry::try_circle_center(p1.cast(), p2.cast(), p3.cast(), SCALED_EPSILON); center) { - Point c = center->cast(); + Point c = Point::round(*center); if (double r = c.distance_to(p1); r <= max_radius) { return std::make_optional({c, float(r)}); } @@ -132,7 +132,7 @@ static bool foot_pt_on_segment(const Point &p1, const Point &p2, const Point &pt if (l2 > int64_t(SCALED_EPSILON)) { if (int64_t t = (pt - p1).cast().dot(v21); t >= int64_t(SCALED_EPSILON) && t < l2 - int64_t(SCALED_EPSILON)) { - out = p1 + ((double(t) / double(l2)) * v21.cast()).cast(); + out = p1 + Point::round((double(t) / double(l2)) * v21.cast()); return true; } } @@ -291,7 +291,7 @@ static std::optional try_create_circle(const Points::const_iterator begi if (std::optional opt_center = ArcWelder::arc_fit_center_gauss_newton_ls(first_point, last_point, center_point, fpts.begin(), fpts.end(), 3); opt_center) { - out->center = opt_center->cast(); + out->center = Point::round(*opt_center); out->radius = (out->radius > 0 ? 1.f : -1.f) * (*opt_center - first_point).norm(); } if (! circle_approximation_sufficient_from_first_last(*out, begin, end, tolerance)) @@ -336,7 +336,7 @@ static std::optional try_create_circle(const Points::const_iterator begi if (sideness < 0) { // Calculate the intersection point. Vec2d p = c.cast() + vd * double(d) / ld; - point_on_bisector = p.cast(); + point_on_bisector = Point::round(p); break; } if (sideness == 0) { @@ -377,7 +377,7 @@ static std::optional try_create_circle(const Points::const_iterator begi if (opt_center) { // Fitted radius must not be excessively large. If so, it is better to fit with a line segment. if (const double r2 = (*opt_center - first_point).squaredNorm(); r2 < max_radius * max_radius) { - circle->center = opt_center->cast(); + circle->center = Point::round(*opt_center); circle->radius = (circle->radius > 0 ? 1.f : -1.f) * sqrt(r2); if (circle_approximation_sufficient_from_first_last(*circle, begin, end, tolerance)) { out = circle; @@ -486,6 +486,19 @@ class Arc { Orientation direction { Orientation::Unknown }; }; + +Orientation arc_orientation(const Point &from, const Point &to, const Point ¢er, float radius) { + float length1 = arc_length(from, to, center, true); + float length2 = arc_length(from, to, center, false); + if (length1 == length2 || radius == 0) { + return Orientation::Unknown; + } else if ((radius > 0 && length1 < length2) || (radius < 0 && length1 > length2)) { + return Orientation::CCW; + } else { + return Orientation::CW; + } +} + // Return orientation of a polyline with regard to the center. // Successive points are expected to take less than a PI angle step. Orientation arc_orientation( @@ -821,12 +834,12 @@ Path fit_path(const Points &src_in, double tolerance, double fit_circle_percent_ const Vec2d center = arc_center(seg_start.point.cast(), seg_end.point.cast(), double(seg_end.radius), seg_end.ccw()); assert(seg_start.point == *begin); assert(seg_end.point == *std::prev(end)); - assert(arc_orientation(center.cast(), begin, end) == arc->direction); + assert(arc_orientation(Point::round(center), begin, end) == arc->direction); for (auto it = std::next(begin); it != end; ++ it) { Point ptstart = *std::prev(it); Point ptend = *it; Point closest_point; - if (foot_pt_on_segment(ptstart, ptend, center.cast(), closest_point)) { + if (foot_pt_on_segment(ptstart, ptend, Point::round(center), closest_point)) { double distance_from_center = (closest_point.cast() - center).norm(); assert(std::abs(distance_from_center - std::abs(seg_end.radius)) < tolerance + SCALED_EPSILON); } @@ -834,7 +847,7 @@ Path fit_path(const Points &src_in, double tolerance, double fit_circle_percent_ double len = v.norm(); auto num_segments = std::min(10, ceil(2. * len / fit_circle_percent_tolerance)); for (size_t i = 0; i < num_segments; ++ i) { - Point p = ptstart + (v * (double(i) / double(num_segments))).cast(); + Point p = ptstart + Point::round(v * (double(i) / double(num_segments))); assert(i == 0 || inside_arc_wedge( seg_start.point.cast(), seg_end.point.cast(), @@ -867,7 +880,7 @@ Path fit_path(const Points &src_in, double tolerance, double fit_circle_percent_ double len = v.norm(); auto num_segments = std::min(10, ceil(2. * len / fit_circle_percent_tolerance)); for (size_t i = 0; i <= num_segments; ++ i) { - Point p = start + (v * (double(i) / double(num_segments))).cast(); + Point p = start + Point::round(v * (double(i) / double(num_segments))); PathSegmentProjection proj = point_to_path_projection(out, p); assert(proj.valid()); assert(proj.distance2 < sqr(tolerance + SCALED_EPSILON)); @@ -974,7 +987,7 @@ double clip_end(Path &path, coordf_t distance) Vec2d v = (path.back().point - last.point).cast(); double lsqr = v.squaredNorm(); if (lsqr > sqr(distance + SCALED_EPSILON)) { - path.push_back({ last.point + (v * (distance / sqrt(lsqr))).cast() }); + path.push_back({ last.point + Point::round(v * (distance / sqrt(lsqr))) }); // Length to go is zero. return 0; } @@ -993,8 +1006,8 @@ double clip_end(Path &path, coordf_t distance) if (last.ccw()) angle *= -1.; path.push_back({ - last.point.rotated(angle * (distance / len), - arc_center(path.back().point.cast(), last.point.cast(), double(last.radius), last.ccw()).cast()), + last.point.rotated(angle * (distance / len), Point::round( + arc_center(path.back().point.cast(), last.point.cast(), double(last.radius), last.ccw()))), last.radius, last.orientation }); #ifdef _DEBUG path.back().length = segment_length(path[path.size()-2], path.back()); @@ -1071,7 +1084,7 @@ PathSegmentProjection point_to_path_projection(const Path &path, const Point &po if (double d2 = sqr(rtest - r); d2 < out.distance2) { if (rtest > SCALED_EPSILON) { // Project vp to the arc. - out.point = center + (vp.cast() * (r / rtest)).cast(); + out.point = center + Point::round(vp.cast() * (r / rtest)); if (out.point.coincides_with_epsilon(prev)) { out.point = prev; } else if (out.point.coincides_with_epsilon(it->point)) { diff --git a/src/libslic3r/Geometry/ArcWelder.hpp b/src/libslic3r/Geometry/ArcWelder.hpp index 05eb35457f..4c7310d7e6 100644 --- a/src/libslic3r/Geometry/ArcWelder.hpp +++ b/src/libslic3r/Geometry/ArcWelder.hpp @@ -429,6 +429,8 @@ Orientation arc_orientation( const Points::const_iterator begin, const Points::const_iterator end); +Orientation arc_orientation(const Point &from, const Point &to, const Point ¢er, float radius); + // Single segment of a smooth path. struct Segment { diff --git a/src/libslic3r/Geometry/Circle.hpp b/src/libslic3r/Geometry/Circle.hpp index 3b33f4024c..a83081d157 100644 --- a/src/libslic3r/Geometry/Circle.hpp +++ b/src/libslic3r/Geometry/Circle.hpp @@ -259,7 +259,7 @@ class ArcCircle { Point get_closest_point(const Point& input) { Vec2d v = (input - center).cast().normalized(); - return (center + (v * radius).cast()); + return (center + Point::round(v * radius)); } static bool try_create_circle(const Point& p1, const Point& p2, const Point& p3, const double max_radius, ArcCircle& new_circle); diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index 2f40f392a6..c0be094d2e 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -226,11 +226,13 @@ class Point : public Vec2crd Point(int64_t x, int64_t y) : Vec2crd(coord_t(x), coord_t(y)) {} Point(double x, double y) : Vec2crd(coord_t(std::round(x)), coord_t(std::round(y))) {} Point(const Point &rhs) { *this = rhs; } + // I don't know how to call it, as it call the implicit below explicit Point(const Vec2d& rhs) : Vec2crd(coord_t(std::round(rhs.x())), coord_t(std::round(rhs.y()))) {} // This constructor allows you to construct Point from Eigen expressions // This constructor has to be implicit (non-explicit) to allow implicit conversion from Eigen expressions. template Point(const Eigen::MatrixBase &other) : Vec2crd(other) {} + static Point round(const Vec2d& rhs) { return Point(coord_t(std::round(rhs.x())), coord_t(std::round(rhs.y()))); } static Point new_scale(coordf_t x, coordf_t y) { return Point(coord_t(scale_(x)), coord_t(scale_(y))); } static Point new_scale(const Point &p) { return Point(scale_t(p.x()), scale_t(p.y())); } template @@ -253,8 +255,8 @@ class Point : public Vec2crd void rotate(double cos_a, double sin_a) { double cur_x = (double)this->x(); double cur_y = (double)this->y(); - this->x() = (coord_t)round(cos_a * cur_x - sin_a * cur_y); - this->y() = (coord_t)round(cos_a * cur_y + sin_a * cur_x); + this->x() = (coord_t)std::round(cos_a * cur_x - sin_a * cur_y); + this->y() = (coord_t)std::round(cos_a * cur_y + sin_a * cur_x); } void rotate(double angle, const Point ¢er); diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index dec5c523f1..9884e8fc51 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -63,7 +63,7 @@ void Polyline::clip_end(coordf_t distance) Vec2d v = this->last_point().cast() - last_point; coordf_t lsqr = v.squaredNorm(); if (lsqr > distance * distance) { - this->points.emplace_back((last_point + v * (distance / sqrt(lsqr))).cast()); + this->points.push_back(Point::round(last_point + v * (distance / sqrt(lsqr)))); return; } distance -= sqrt(lsqr); @@ -83,14 +83,14 @@ void Polyline::extend_end(coordf_t distance) { // relocate last point by extending the last segment by the specified length Vec2d v = (this->points.back() - *(this->points.end() - 2)).cast().normalized(); - this->points.back() += (v * distance).cast(); + this->points.back() += Point::round(v * distance); } void Polyline::extend_start(coordf_t distance) { // relocate first point by extending the first segment by the specified length Vec2d v = (this->points.front() - this->points[1]).cast().normalized(); - this->points.front() += (v * distance).cast(); + this->points.front() += Point::round(v * distance); } /* this method returns a collection of points picked on the polygon contour @@ -114,7 +114,7 @@ Points Polyline::equally_spaced_points(coordf_t distance) const continue; } coordf_t take = segment_length - (len - distance); // how much we take of this segment - points.emplace_back((p1 + v * (take / segment_length)).cast()); + points.push_back(Point::round(p1 + v * (take / segment_length))); -- it; len = - take; } @@ -347,7 +347,7 @@ void ThickPolyline::clip_end(coordf_t distance) coordf_t vec_length_sqr = vec.squaredNorm(); if (vec_length_sqr > distance * distance) { coordf_t t = (distance / std::sqrt(vec_length_sqr)); - this->points.emplace_back((last_point + vec * t).cast()); + this->points.push_back(Point::round(last_point + vec * t)); this->points_width.emplace_back(last_width + width_diff * t); assert(this->points_width.size() == this->points.size()); return; @@ -362,14 +362,14 @@ void ThickPolyline::extend_end(coordf_t distance) { // relocate last point by extending the last segment by the specified length Vec2d v = (this->points.back() - *(this->points.end() - 2)).cast().normalized(); - this->points.back() += (v * distance).cast(); + this->points.back() += Point::round(v * distance); } void ThickPolyline::extend_start(coordf_t distance) { // relocate first point by extending the first segment by the specified length Vec2d v = (this->points.front() - this->points[1]).cast().normalized(); - this->points.front() += (v * distance).cast(); + this->points.front() += Point::round(v * distance); } void ThickPolyline::start_at_index(int index) @@ -523,7 +523,7 @@ void concatThickPolylines(ThickPolylines& pp) { // Vec2d v = (point - prev_point).cast(); // double lsqr = v.squaredNorm(); // if (lsqr > sqr(distance)) -// return std::make_optional(prev_point + (v * (distance / sqrt(lsqr))).cast()); +// return std::make_optional(prev_point + Point::round(v * (distance / sqrt(lsqr)))); // distance -= sqrt(lsqr); // } else { // // Circular segment @@ -533,9 +533,9 @@ void concatThickPolylines(ThickPolylines& pp) { // // Rotate the segment end point in reverse towards the start point. // return std::make_optional( // prev_point.rotated(-angle * (distance / len), -// Geometry::ArcWelder::arc_center(prev_point.cast(), point.cast(), it->radius, +// Point::round(Geometry::ArcWelder::arc_center(prev_point.cast(), point.cast(), it->radius, // it->ccw()) -// .cast())); +// ))); // } // distance -= len; // } @@ -562,7 +562,7 @@ void concatThickPolylines(ThickPolylines& pp) { // Vec2d v = (point - prev_point).cast(); // double lsqr = v.squaredNorm(); // if (lsqr > sqr(distance)) -// return std::make_optional(prev_point + (v * (distance / sqrt(lsqr))).cast()); +// return std::make_optional(prev_point + Point::round(v * (distance / sqrt(lsqr)))); // distance -= sqrt(lsqr); // } else { // // Circular segment @@ -572,9 +572,9 @@ void concatThickPolylines(ThickPolylines& pp) { // // Rotate the segment end point in reverse towards the start point. // return std::make_optional( // prev_point.rotated(-angle * (distance / len), -// Geometry::ArcWelder::arc_center(prev_point.cast(), point.cast(), it->radius, +// Point::round(Geometry::ArcWelder::arc_center(prev_point.cast(), point.cast(), it->radius, // it->ccw()) -// .cast())); +// ))); // } // distance -= len; // } @@ -604,6 +604,11 @@ bool ArcPolyline::has_arc() const { return !m_only_strait; } +void ArcPolyline::clear() { + m_path.clear(); + m_only_strait = true; +} + void ArcPolyline::append(const Points &src) { for (const Point &point : src) @@ -869,6 +874,9 @@ void ArcPolyline::split_at(coordf_t distance, ArcPolyline &p1, ArcPolyline &p2) if (distance < SCALED_EPSILON) return; assert(this->is_valid()); p1.m_path.push_back(m_path.front()); +#ifdef _DEBUG + coordf_t length_tot_split = 0; +#endif size_t idx = 1; while(distance > 0 && idx < m_path.size()) { const Geometry::ArcWelder::Segment current = m_path[idx]; @@ -877,7 +885,11 @@ void ArcPolyline::split_at(coordf_t distance, ArcPolyline &p1, ArcPolyline &p2) Vec2d v = (current.point - p1.back()).cast(); double lsqr = v.squaredNorm(); if (lsqr > sqr(distance)) { - Point split_point = p1.back() + (v * (distance / sqrt(lsqr))).cast(); +#ifdef _DEBUG + length_tot_split = current.length; + assert(length_tot_split == 0); +#endif + Point split_point = p1.back() + Point::round(v * (distance / sqrt(lsqr))); p1.m_path.push_back({split_point, 0, Geometry::ArcWelder::Orientation::Unknown}); p2.m_path.push_back({split_point, 0, Geometry::ArcWelder::Orientation::Unknown}); p2.m_path.push_back(current); @@ -888,24 +900,55 @@ void ArcPolyline::split_at(coordf_t distance, ArcPolyline &p1, ArcPolyline &p2) distance -= sqrt(lsqr); } } else { +#ifdef _DEBUG + length_tot_split = current.length; + assert(length_tot_split > 0); + Point startp = p1.back(); +#endif // Circular segment //double angle = Geometry::ArcWelder::arc_angle(path.back().point.cast(), last.point.cast(), last.radius); double angle = Geometry::ArcWelder::arc_angle(p1.back().cast(), current.point.cast(), current.radius); + assert(angle > 0); //double len = std::abs(last.radius) * angle; double len = std::abs(current.radius) * angle; if (len > distance) { + assert(distance > 0); // Rotate the segment end point in reverse towards the start point. - if (current.ccw()) + if (!current.ccw()) angle *= -1.; - //path.push_back({ - // last.point.rotated(angle * (distance / len), - // arc_center(path.back().point.cast(), last.point.cast(), double(last.radius), last.ccw()).cast()), - // last.radius, last.orientation }); - Point split_point = current.point.rotated(angle * (distance / len), - Geometry::ArcWelder::arc_center(p1.back().cast(), current.point.cast(), double(current.radius), current.ccw()).cast()); + const Vec2d center = Geometry::ArcWelder::arc_center(p1.back().cast(), current.point.cast(), double(current.radius), current.ccw()); + const Point split_point = p1.back().rotated(angle * (distance / len), Point::round(center)); p1.m_path.push_back({split_point, current.radius, current.orientation }); p2.m_path.push_back({split_point, 0, Geometry::ArcWelder::Orientation::Unknown}); p2.m_path.push_back(current); + // the arc is now shorter, if radius negative then is was using the big one. In this case, check if the segment isn't now the shorter one. + if (current.radius < 0) { + assert(std::abs(angle) > PI); + int nb_reverse = 0; + if (std::abs(angle) * (distance / len) < PI) { + p1.m_path.back().radius = (-p1.m_path.back().radius); + nb_reverse++; + assert(p1.m_path.back().radius > 0); + } + if (std::abs(angle) * (1- (distance / len)) < PI) { + p2.m_path[1].radius = (-p2.m_path[1].radius); + nb_reverse++; + assert(p2.m_path[1].radius > 0); + } + assert(nb_reverse > 0); + } +#ifdef _DEBUG + Point almost_current = startp.rotated(angle, Point::round(center)); + Point almost_current2 = startp.rotated(angle, current.center); + assert(almost_current.coincides_with_epsilon(current.point)); + assert(p1.back() == split_point); + double part1 = Geometry::ArcWelder::arc_length(startp, split_point, double(p1.m_path.back().radius)); + double part1b = Geometry::ArcWelder::arc_length(startp, split_point, double(-p1.m_path.back().radius)); + assert(current.point == p2.m_path[1].point); + double part2 = Geometry::ArcWelder::arc_length(split_point, current.point, double(p2.m_path[1].radius)); + double tot = Geometry::ArcWelder::arc_length(startp, current.point, double(current.radius)); + assert(is_approx(part1 + part2, tot, 1. * SCALED_EPSILON)); +#endif // Length to go is zero. distance = 0; } else { @@ -916,6 +959,8 @@ void ArcPolyline::split_at(coordf_t distance, ArcPolyline &p1, ArcPolyline &p2) //increment ++idx; } + assert(!p2.empty()); + assert(p1.m_path.back().point == p2.m_path[0].point); //now fill p2 while (idx < m_path.size()) { p2.m_path.push_back(m_path[idx]); @@ -941,6 +986,28 @@ void ArcPolyline::split_at(coordf_t distance, ArcPolyline &p1, ArcPolyline &p2) p1.m_only_strait = not_arc(p1); p2.m_only_strait = not_arc(p2); } +#ifdef _DEBUG + assert(p1.m_path.back().point == p2.m_path[0].point); + if (p1.size() > 1 && p2.size() > 1 && (p1.m_path.back().radius != 0 || p2.m_path[1].radius != 0) ) { + assert(std::abs(p1.m_path.back().radius) == std::abs(p2.m_path[1].radius)); + assert(p1.m_path.back().length ==0 && p2.m_path[1].length > 0); + coordf_t length_old_p1 = p1.m_path.back().length; + coordf_t length_old_p2 = p2.m_path[1].length; + p1.m_path.back().length = Geometry::ArcWelder::segment_length(p1.m_path[p1.m_path.size()-2], p1.m_path.back()); + p1.m_path.back().center = Geometry::ArcWelder::arc_center_scalar(p1.m_path[p1.m_path.size()-2].point, p1.m_path.back().point, p1.m_path.back().radius, p1.m_path.back().ccw()); + p2.m_path[1].length = Geometry::ArcWelder::segment_length(p2.m_path[0], p2.m_path[1]); + p2.m_path[1].center = Geometry::ArcWelder::arc_center_scalar(p2.m_path[0].point, p2.m_path[1].point, p2.m_path[1].radius, p2.m_path[1].ccw()); + assert(is_approx(length_tot_split, p1.m_path.back().length + p2.m_path[1].length, 1. * SCALED_EPSILON)); + } + for (size_t i = 1; i < p1.m_path.size(); i++) { + if(p1.m_path[i].radius) + assert(is_approx(Geometry::ArcWelder::segment_length(p1.m_path[i-1], p1.m_path[i]), p1.m_path[i].length, EPSILON)); + } + for (size_t i = 1; i < p2.m_path.size(); i++) { + if(p2.m_path[i].radius) + assert(is_approx(Geometry::ArcWelder::segment_length(p2.m_path[i-1], p2.m_path[i]), p2.m_path[i].length, EPSILON)); + } +#endif assert(p1.is_valid()); assert(p2.is_valid()); assert(is_approx(this->length(), p1.length() + p2.length(), coordf_t(SCALED_EPSILON))); @@ -1122,7 +1189,7 @@ Point ArcPolyline::get_point_from_begin(coord_t distance) const { double lsqr = v.squaredNorm(); if (lsqr >= sqr(distance)) { // Length to go is zero. - return last.point + (v * (distance / sqrt(lsqr))).cast(); + return last.point + Point::round(v * (distance / sqrt(lsqr))); } distance -= sqrt(lsqr); } else { @@ -1133,8 +1200,8 @@ Point ArcPolyline::get_point_from_begin(coord_t distance) const { // Rotate the segment end point towards the current point. if (current.ccw()) angle *= -1.; - return last.point.rotated( -angle * (distance / len), - Geometry::ArcWelder::arc_center(last.point.cast(), current.point.cast(), double(current.radius), current.ccw()).cast()); + return last.point.rotated( -angle * (distance / len), Point::round( + Geometry::ArcWelder::arc_center(last.point.cast(), current.point.cast(), double(current.radius), current.ccw()))); } distance -= len; } @@ -1157,7 +1224,7 @@ Point ArcPolyline::get_point_from_end(coord_t distance) const { double lsqr = v.squaredNorm(); if (lsqr >= sqr(distance)) { // Length to go is zero. - return last.point + (v * (distance / sqrt(lsqr))).cast(); + return last.point + Point::round(v * (distance / sqrt(lsqr))); } distance -= sqrt(lsqr); } else { @@ -1168,8 +1235,8 @@ Point ArcPolyline::get_point_from_end(coord_t distance) const { // Rotate the segment end point in reverse towards the start point. if (last.ccw()) angle *= -1.; - return last.point.rotated(angle * (distance / len), - Geometry::ArcWelder::arc_center(current.point.cast(), last.point.cast(), double(last.radius), last.ccw()).cast()); + return last.point.rotated(angle * (distance / len), Point::round( + Geometry::ArcWelder::arc_center(current.point.cast(), last.point.cast(), double(last.radius), last.ccw()))); } distance -= len; } @@ -1626,18 +1693,30 @@ void ArcPolyline::make_arc(ArcFittingType with_fitting_arc, coordf_t tolerance, assert(path.size() >= 2); Geometry::ArcWelder::Segment &start = path[path.size() -2]; Geometry::ArcWelder::Segment &end = path.back(); + //set length & center + end.center = Slic3r::Geometry::ArcWelder::arc_center_scalar(start.point, end.point, double(end.radius), end.ccw()); + end.length = Geometry::ArcWelder::segment_length(start, end); Vec2d center = Slic3r::Geometry::ArcWelder::arc_center(start.point.cast(), end.point.cast(), double(end.radius), end.ccw()); + Vec2d center_bad = Slic3r::Geometry::ArcWelder::arc_center(start.point.cast(), end.point.cast(), double(end.radius), !end.ccw()); + Vec2d center_bad2 = Slic3r::Geometry::ArcWelder::arc_center(start.point.cast(), end.point.cast(), double(-end.radius), end.ccw()); + Vec2d center_bad3 = Slic3r::Geometry::ArcWelder::arc_center(start.point.cast(), end.point.cast(), double(-end.radius), !end.ccw()); + Point centert = Point::round(center); + assert(data.arc_data.center.distance_to(centert) < 100*SCALED_EPSILON); double angle = Slic3r::Geometry::ArcWelder::arc_angle(start.point.cast(), end.point.cast(), double(end.radius)); - double ccw_angle = angle_ccw(start.point - center.cast(), end.point - center.cast()); + double ccw_angle = angle_ccw(start.point - centert, end.point - centert); if (!end.ccw()) ccw_angle = (-ccw_angle); if (ccw_angle < 0) ccw_angle = 2 * PI + ccw_angle; assert(is_approx(ccw_angle, angle, EPSILON)); - //set length & center - end.center = Slic3r::Geometry::ArcWelder::arc_center_scalar(start.point, end.point, end.radius, end.ccw()); - assert(end.center == center.cast()); - end.length = Geometry::ArcWelder::segment_length(start, end); + assert(data.arc_data.center.distance_to(end.center) < 100*SCALED_EPSILON); + Point center_round = centert; + assert(is_approx(1,2,2) && is_approx(2,1,2)); + assert(is_approx(end.center.x(), centert.x(), coord_t(2)) && is_approx(end.center.y(), centert.y(), coord_t(2))); + assert(is_approx(data.arc_data.length, end.length, 2.*SCALED_EPSILON)); + double length1 = Slic3r::Geometry::ArcWelder::arc_length(start.point, end.point, double(end.radius)); + double length2 = Slic3r::Geometry::ArcWelder::arc_length(start.point, end.point, end.center, end.ccw()); + assert(is_approx(length1,length2, 20.*SCALED_EPSILON)); #endif } else { assert(false); @@ -1666,7 +1745,7 @@ void ArcPolyline::make_arc(ArcFittingType with_fitting_arc, coordf_t tolerance, if(seg.radius != 0){ Vec2d center = Slic3r::Geometry::ArcWelder::arc_center(prev.cast(), seg.point.cast(), double(seg.radius), seg.ccw()); double angle = Slic3r::Geometry::ArcWelder::arc_angle(prev.cast(), seg.point.cast(), double(seg.radius)); - double ccw_angle = angle_ccw(prev - center.cast(), seg.point - center.cast()); + double ccw_angle = angle_ccw(prev - Point::round(center), seg.point - Point::round(center)); double ccw_angle1 = ccw_angle; if (!seg.ccw()) ccw_angle = (-ccw_angle); @@ -1729,25 +1808,29 @@ bool ArcPolyline::is_valid() const { for (size_t i = 1; i < m_path.size(); ++i) { assert(!m_path[i - 1].point.coincides_with_epsilon(m_path[i].point)); if (m_path[i].radius != 0) { - Vec2d center = Slic3r::Geometry::ArcWelder::arc_center(m_path[i-1].point.cast(), m_path[i].point.cast(), double(m_path[i].radius), m_path[i].ccw()); - double angle = Slic3r::Geometry::ArcWelder::arc_angle(m_path[i-1].point.cast(), m_path[i].point.cast(), double(m_path[i].radius)); + Vec2d center = Slic3r::Geometry::ArcWelder::arc_center(m_path[i-1].point.cast(), m_path[i].point.cast(), coordf_t(m_path[i].radius), m_path[i].ccw()); + double angle = Slic3r::Geometry::ArcWelder::arc_angle(m_path[i-1].point.cast(), m_path[i].point.cast(), coordf_t(m_path[i].radius)); double ccw_angle = angle_ccw(m_path[i-1].point.cast() - center, m_path[i].point.cast() - center); if (!m_path[i].ccw()) ccw_angle = (-ccw_angle); if (ccw_angle < 0) ccw_angle = 2 * PI + ccw_angle; assert(is_approx(ccw_angle, angle, EPSILON)); - coordf_t new_length = Slic3r::Geometry::ArcWelder::segment_length(m_path[i - 1], m_path[i]); - assert(is_approx(new_length, m_path[i].length, SCALED_EPSILON*1.)); - //assert(is_approx(coord_t(center.x()), m_path[i].center.x(), SCALED_EPSILON)); - //assert(is_approx(coord_t(center.y()), m_path[i].center.y(), SCALED_EPSILON)); - //if (first_center == Point(0, 0)) { - // first_center = m_path[i].center; - //} - //assert(is_approx(first_center.x(), m_path[i].center.x(), SCALED_EPSILON*100)); - //assert(is_approx(first_center.y(), m_path[i].center.y(), SCALED_EPSILON*100)); - //if(min_radius==0 || min_radius > m_path[i].radius) min_radius = m_path[i].radius; - //if(max_radius==0 || max_radius < m_path[i].radius) max_radius = m_path[i].radius; + coordf_t new_length = Slic3r::Geometry::ArcWelder::arc_length(m_path[i - 1].point, m_path[i].point, coordf_t(m_path[i].radius)); + //coordf_t new_length2 = Slic3r::Geometry::ArcWelder::arc_length(m_path[i - 1].point, m_path[i].point, Point::round(center), m_path[i].ccw()); + Vec2d startd = m_path[i - 1].point.cast(); + Vec2d endd = m_path[i].point.cast(); + coordf_t new_length2 = Geometry::ArcWelder::arc_length(startd, endd, center, m_path[i].ccw()); + Vec2d centerd = m_path[i].center.cast(); + coordf_t new_length3 = Geometry::ArcWelder::arc_length(startd, endd, centerd, m_path[i].ccw()); + assert(is_approx(new_length, new_length2, SCALED_EPSILON*4.)); + assert(is_approx(new_length2, new_length3, SCALED_EPSILON*10.)); + assert(is_approx(new_length2, m_path[i].length, SCALED_EPSILON*1.)); + Slic3r::Geometry::ArcWelder::Orientation orientation = Slic3r::Geometry::ArcWelder::arc_orientation(m_path[i - 1].point, m_path[i].point, m_path[i].center, m_path[i].radius); + assert(orientation == m_path[i].orientation); + Slic3r::Geometry::ArcWelder::Orientation orientation2 = Slic3r::Geometry::ArcWelder::arc_orientation(m_path[i - 1].point, m_path[i].point, Point::round(center), m_path[i].radius); + assert(is_approx(coord_t(center.x()), m_path[i].center.x(), SCALED_EPSILON)); + assert(is_approx(coord_t(center.y()), m_path[i].center.y(), SCALED_EPSILON)); } //assert(std::abs(max_radius - min_radius) <= std::abs(max_radius) * 0.01); } diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index 4974532987..5fbd3ddb40 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -318,7 +318,7 @@ class ArcPolyline void append(const Points::const_iterator &begin, const Points::const_iterator &end); void append(const ArcPolyline &src); void append(ArcPolyline &&src); - void clear() { m_path.clear(); } + void clear(); void swap(ArcPolyline &other) { m_path.swap(other.m_path); this->m_only_strait = other.m_only_strait; assert(is_valid()); } void reverse() { Geometry::ArcWelder::reverse(m_path); } From 3e70560ab2b6238ec92dfc53c6c95fc8f6adfa73 Mon Sep 17 00:00:00 2001 From: supermerill Date: Sat, 1 Feb 2025 12:33:54 +0100 Subject: [PATCH 9/9] Fix arachne overhang detection --- src/libslic3r/ClipperUtils.cpp | 13 +- src/libslic3r/PerimeterGenerator.cpp | 228 ++++++++++++++++++++++++--- 2 files changed, 217 insertions(+), 24 deletions(-) diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 55a3fcaa85..cac5452e48 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -1192,12 +1192,13 @@ ClipperLib_Z::Paths clip_extrusion(const ClipperLib_Z::Paths& subjects, const Cl ClipperLib_Z::PolyTreeToPaths(clipped_polytree, clipped_paths); } - // cleaning: can contain 0-length segments - for (ClipperLib_Z::Path& path : clipped_paths) { - for (size_t i = 1; i < path.size(); i++) { - if ((path[i - 1] - path[i]).squaredNorm() < SCALED_EPSILON) { - path.erase(path.begin() + i); - i--; + // cleaning + for (size_t i_path = 0; i_path < clipped_paths.size(); ++i_path) { + ClipperLib_Z::Path &path = clipped_paths[i_path]; + for (size_t i_pt = 1; i_pt < path.size() - 1; i_pt++) { + if ((path[i_pt - 1] - path[i_pt]).squaredNorm() < SCALED_EPSILON) { + path.erase(path.begin() + i_pt); + i_pt--; } } } diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 98885e646a..18fd2b891f 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -1021,10 +1021,12 @@ void PerimeterGenerator::_sort_overhangs(const Parameters ¶ms, chain_and_reorder_extrusion_paths(paths, &overhang_params.first_point); // merge path that are smaller than epsilon + int nb_erased = 0; for (auto &path : paths) assert(path.length() > SCALED_EPSILON || path.size() == 2); while (paths.size() > 1 && paths.front().size() == 2 && paths.front().length() < coordf_t(SCALED_EPSILON)) { paths[1].polyline.set_front(paths.front().first_point()); paths.erase(paths.begin()); + nb_erased++; } for (size_t idx_path = 1; idx_path < paths.size(); ++idx_path) { ExtrusionPath &path = paths[idx_path]; @@ -1033,8 +1035,9 @@ void PerimeterGenerator::_sort_overhangs(const Parameters ¶ms, // del paths.erase(paths.begin() + idx_path); --idx_path; + nb_erased++; } else { - assert(paths[idx_path-1].last_point() == paths[idx_path].first_point()); + assert(paths[idx_path-1].last_point().coincides_with_epsilon(paths[idx_path].first_point())); } } @@ -1058,6 +1061,9 @@ void PerimeterGenerator::_sort_overhangs(const Parameters ¶ms, } #ifdef _DEBUGINFO + for (size_t idx_path = 1; idx_path < paths.size(); ++idx_path) { + assert(paths[idx_path - 1].last_point() == paths[idx_path].first_point()); + } if (overhang_params.is_loop) { ExtrusionLoop loop_test; loop_test.paths = paths; @@ -1787,6 +1793,77 @@ struct cmpClipperLib_Z { } }; +bool is_length_more_than_epsilon(ClipperLib_Z::Path &path) { + coordf_t length = 0; + for (size_t i = 1; i < path.size(); i++) { + length += (path[i - 1] - path[i]).cast().norm(); + if (length > SCALED_EPSILON) { + return true; + } + } + return false; +} + +bool merge_path(const ClipperLib_Z::Path &tomerge, ClipperLib_Z::Paths &receiver) { +#ifdef _DEBUG + { + // check there seems to be a continous path from start to end + const ClipperLib_Z::Path &path = tomerge; + bool found_another_path_after = false; + bool found_another_path_before = false; + bool found_almost_another_path_after = false; + bool found_almost_another_path_before = false; + int other_paths_count = 0; + for (size_t idx_path2 = 0; idx_path2 < receiver.size(); ++idx_path2) { + other_paths_count++; + found_another_path_after = found_another_path_after || (path.back() == receiver[idx_path2].front()); + found_another_path_before = found_another_path_before || (path.front() == receiver[idx_path2].back()); + found_almost_another_path_after = found_almost_another_path_after || (path.back() - receiver[idx_path2].front()).cast().norm() < SCALED_EPSILON; + found_almost_another_path_before = found_almost_another_path_before || (path.front() - receiver[idx_path2].back()).cast().norm() < SCALED_EPSILON; + } + bool found_another_path_after_strict = found_another_path_after; + bool found_another_path_before_strict = found_another_path_before; + bool found_almost_another_path_after_strict = found_almost_another_path_after; + bool found_almost_another_path_before_strict = found_almost_another_path_before; + //assert(other_paths_count == 0 || found_another_path_after || found_another_path_before); + for (size_t idx_path2 = 0; idx_path2 < receiver.size(); ++idx_path2) { + found_another_path_after = found_another_path_after || path.back() == receiver[idx_path2].front() || path.back() == receiver[idx_path2].back(); + found_another_path_before = found_another_path_before || path.front() == receiver[idx_path2].back() || path.front() == receiver[idx_path2].front(); + found_almost_another_path_after = found_almost_another_path_after || (path.back() - receiver[idx_path2].back()).cast().norm() < SCALED_EPSILON; + found_almost_another_path_before = found_almost_another_path_before || (path.front() - receiver[idx_path2].front()).cast().norm() < SCALED_EPSILON; + } + assert(other_paths_count == 0 || found_another_path_after_strict || found_another_path_before_strict); + } +#endif + size_t idx_first; + bool found_first = false; + // search start + for (idx_first = 0; idx_first < receiver.size(); ++idx_first) { + if (receiver[idx_first].back() == tomerge.front()) { + found_first = true; + receiver[idx_first].insert(receiver[idx_first].end(), tomerge.begin() + 1, tomerge.end()); + break; + } + } + bool found_last = false; + if (found_first) { + //find the last, add it and remove it. + size_t idx_last; + for (idx_last = 0; idx_last < receiver.size(); ++idx_last) { + if (idx_last == idx_first) { + continue; + } + if (receiver[idx_last].front() == receiver[idx_first].back()) { + found_last = true; + receiver[idx_first].insert(receiver[idx_first].end(), receiver[idx_last].begin() + 1, receiver[idx_last].end()); + receiver.erase(receiver.begin() + idx_last); + break; + } + } + } + return found_first && found_last; +} + //TODO: transform to ExtrusionMultiPath instead of ExtrusionPaths ExtrusionPaths PerimeterGenerator::create_overhangs_arachne(const Parameters & params, const ClipperLib_Z::Path &arachne_path, @@ -1854,15 +1931,50 @@ ExtrusionPaths PerimeterGenerator::create_overhangs_arachne(const Parameters & #endif dynamic_speed = clip_extrusion(*previous, clipped_zpaths, ClipperLib_Z::ctDifference); #ifdef _DEBUG - for (ClipperLib_Z::Path& poly : dynamic_speed) // assert dynamic_speed - for (int i = 0; i < poly.size() - 1; i++) // assert dynamic_speed - assert(poly[i] != poly[i + 1]); // assert dynamic_speed + for (ClipperLib_Z::Path &poly : dynamic_speed) // assert dynamic_speed + for (int i = 0; i < poly.size() - 1; i++) // assert dynamic_speed + assert(poly[i] != poly[i + 1]); // assert dynamic_speed #endif if (!dynamic_speed.empty()) { *previous = clip_extrusion(*previous, clipped_zpaths, ClipperLib_Z::ctIntersection); #ifdef _DEBUG - test_overhangs(dynamic_speed, *previous, outer_points); - test_overhangs(*previous, dynamic_speed, outer_points); + test_overhangs(dynamic_speed, *previous, outer_points); + test_overhangs(*previous, dynamic_speed, outer_points); + //for (ClipperLib_Z::Path &poly : dynamic_speed) { + // assert(poly.size() > 1); + // assert(is_length_more_than_epsilon(poly)); + //} + //for (ClipperLib_Z::Path &poly : *previous) { + // assert (poly.size() > 1); + // assert(is_length_more_than_epsilon(poly)); + //} +#endif + // merge epsilon-length from dynamic_speed into previous + for (size_t path_idx = 0; path_idx < dynamic_speed.size(); ++path_idx) { + ClipperLib_Z::Path &poly = dynamic_speed[path_idx]; + if (!is_length_more_than_epsilon(poly)) { + merge_path(poly, *previous); //TODO + dynamic_speed.erase(dynamic_speed.begin() + path_idx); + path_idx--; + } + } + for (size_t path_idx = 0; path_idx < previous->size(); ++path_idx) { + ClipperLib_Z::Path &poly = (*previous)[path_idx]; + if (!is_length_more_than_epsilon(poly)) { + merge_path(poly, dynamic_speed); //TODO + previous->erase(previous->begin() + path_idx); + path_idx--; + } + } +#ifdef _DEBUG + for (ClipperLib_Z::Path &poly : dynamic_speed) { + assert(poly.size() > 1); + assert(is_length_more_than_epsilon(poly)); + } + for (ClipperLib_Z::Path &poly : *previous) { + assert (poly.size() > 1); + assert(is_length_more_than_epsilon(poly)); + } #endif previous = &dynamic_speed; } @@ -1887,9 +1999,26 @@ ExtrusionPaths PerimeterGenerator::create_overhangs_arachne(const Parameters & if (!small_speed.empty()) { *previous = clip_extrusion(*previous, clipped_zpaths, ClipperLib_Z::ctIntersection); #ifdef _DEBUG - test_overhangs(small_speed, *previous, outer_points); - test_overhangs(*previous, small_speed, outer_points); + test_overhangs(small_speed, *previous, outer_points); + test_overhangs(*previous, small_speed, outer_points); #endif + // merge epsilon-length from small_speed into previous + for (size_t path_idx = 0; path_idx < small_speed.size(); ++path_idx) { + ClipperLib_Z::Path &poly = small_speed[path_idx]; + if (!is_length_more_than_epsilon(poly)) { + merge_path(poly, *previous); //TODO + small_speed.erase(small_speed.begin() + path_idx); + path_idx--; + } + } + for (size_t path_idx = 0; path_idx < previous->size(); ++path_idx) { + ClipperLib_Z::Path &poly = (*previous)[path_idx]; + if (!is_length_more_than_epsilon(poly)) { + merge_path(poly, small_speed); //TODO + previous->erase(previous->begin() + path_idx); + path_idx--; + } + } previous = &small_speed; } } @@ -1911,9 +2040,26 @@ ExtrusionPaths PerimeterGenerator::create_overhangs_arachne(const Parameters & if (!big_speed.empty()) { *previous = clip_extrusion(*previous, clipped_zpaths, ClipperLib_Z::ctIntersection); #ifdef _DEBUG - test_overhangs(big_speed, *previous, outer_points); - test_overhangs(*previous, big_speed, outer_points); + test_overhangs(big_speed, *previous, outer_points); + test_overhangs(*previous, big_speed, outer_points); #endif + // merge epsilon-length from big_speed into previous + for (size_t path_idx = 0; path_idx < big_speed.size(); ++path_idx) { + ClipperLib_Z::Path &poly = big_speed[path_idx]; + if (!is_length_more_than_epsilon(poly)) { + merge_path(poly, *previous); //TODO + big_speed.erase(big_speed.begin() + path_idx); + path_idx--; + } + } + for (size_t path_idx = 0; path_idx < previous->size(); ++path_idx) { + ClipperLib_Z::Path &poly = (*previous)[path_idx]; + if (!is_length_more_than_epsilon(poly)) { + merge_path(poly, big_speed); //TODO + previous->erase(previous->begin() + path_idx); + path_idx--; + } + } previous = &big_speed; } } @@ -1937,9 +2083,26 @@ ExtrusionPaths PerimeterGenerator::create_overhangs_arachne(const Parameters & if (!small_flow.empty()) { *previous = clip_extrusion(*previous, clipped_zpaths, ClipperLib_Z::ctIntersection); #ifdef _DEBUG - test_overhangs(small_flow, *previous, outer_points); - test_overhangs(*previous, small_flow, outer_points); + test_overhangs(small_flow, *previous, outer_points); + test_overhangs(*previous, small_flow, outer_points); #endif + // merge epsilon-length from small_flow into previous + for (size_t path_idx = 0; path_idx < small_flow.size(); ++path_idx) { + ClipperLib_Z::Path &poly = small_flow[path_idx]; + if (!is_length_more_than_epsilon(poly)) { + merge_path(poly, *previous); //TODO + small_flow.erase(small_flow.begin() + path_idx); + path_idx--; + } + } + for (size_t path_idx = 0; path_idx < previous->size(); ++path_idx) { + ClipperLib_Z::Path &poly = (*previous)[path_idx]; + if (!is_length_more_than_epsilon(poly)) { + merge_path(poly, small_flow); //TODO + previous->erase(previous->begin() + path_idx); + path_idx--; + } + } previous = &small_flow; } } @@ -1961,9 +2124,26 @@ ExtrusionPaths PerimeterGenerator::create_overhangs_arachne(const Parameters & if (!big_flow.empty()) { *previous = clip_extrusion(*previous, clipped_zpaths, ClipperLib_Z::ctIntersection); #ifdef _DEBUG - test_overhangs(big_flow, *previous, outer_points); - test_overhangs(*previous, big_flow, outer_points); + test_overhangs(big_flow, *previous, outer_points); + test_overhangs(*previous, big_flow, outer_points); #endif + // merge epsilon-length from big_flow into previous + for (size_t path_idx = 0; path_idx < big_flow.size(); ++path_idx) { + ClipperLib_Z::Path &poly = big_flow[path_idx]; + if (!is_length_more_than_epsilon(poly)) { + merge_path(poly, *previous); //TODO + big_flow.erase(big_flow.begin() + path_idx); + path_idx--; + } + } + for (size_t path_idx = 0; path_idx < previous->size(); ++path_idx) { + ClipperLib_Z::Path &poly = (*previous)[path_idx]; + if (!is_length_more_than_epsilon(poly)) { + merge_path(poly, big_flow); //TODO + previous->erase(previous->begin() + path_idx); + path_idx--; + } + } previous = &big_flow; } } @@ -1973,10 +2153,20 @@ ExtrusionPaths PerimeterGenerator::create_overhangs_arachne(const Parameters & // ensure polylines are valid (at least EPSILON between two points), unless the path is itself shorter than epsilon (then it's two points) for (ClipperLib_Z::Paths *polylines : {&ok_polylines, &dynamic_speed, &small_speed, &big_speed, &small_flow, &big_flow}) { for (ClipperLib_Z::Path &poly : *polylines) { - auto it_end = Slic3r::douglas_peucker(poly.begin(), poly.end(), poly.begin(), double(SCALED_EPSILON), [](const ClipperLib_Z::IntPoint &p) { return Point(p.x(), p.y()); }); - assert(it_end <= poly.end()); - poly.resize(std::distance(poly.begin(), it_end)); - assert(poly.size() >= 2); + if (poly.size() == 1) { + // this polyline can be removed + assert(false); + } else { + assert(poly.size() >= 2); + assert(is_length_more_than_epsilon(poly)); + ClipperLib_Z::Path old_poly = poly; + auto it_end = Slic3r::douglas_peucker( + poly.begin(), poly.end(), poly.begin(), double(SCALED_EPSILON), + [](const ClipperLib_Z::IntPoint &p) { return Point(p.x(), p.y()); }); + assert(it_end <= poly.end()); + poly.resize(std::distance(poly.begin(), it_end)); + assert(poly.size() >= 2); + } } } @@ -2218,6 +2408,8 @@ ExtrusionPaths PerimeterGenerator::create_overhangs_arachne(const Parameters & } bool found_another_path_after_strict = found_another_path_after; bool found_another_path_before_strict = found_another_path_before; + bool found_almost_another_path_after_strict = found_almost_another_path_after; + bool found_almost_another_path_before_strict = found_almost_another_path_before; //assert(other_paths_count == 0 || found_another_path_after || found_another_path_before); for (size_t idx_path2 = 0; idx_path2 < paths.size(); ++idx_path2) { if (idx_path == idx_path2)