diff --git a/resources/ui_layout/default/print.ui b/resources/ui_layout/default/print.ui index cf24438dbd0..d2315fcf51b 100644 --- a/resources/ui_layout/default/print.ui +++ b/resources/ui_layout/default/print.ui @@ -317,16 +317,17 @@ group:Options for support material and raft group:Options for support material interface setting:support_material_interface_layer_height line:Pattern - setting:support_material_interface_pattern - setting:label$Interface spacing:support_material_interface_spacing + setting:label$Top:support_material_top_interface_pattern + setting:label$Bottom:support_material_bottom_interface_pattern + setting:label$Spacing:support_material_interface_spacing end_line line:Pattern Angle setting:label$_:support_material_interface_angle setting:label$Increment:support_material_interface_angle_increment end_line line:Layer count - setting:support_material_interface_layers - setting:support_material_bottom_interface_layers + setting:label$Top:support_material_interface_layers + setting:label$Bottom:support_material_bottom_interface_layers end_line setting:support_material_interface_contact_loops @@ -501,7 +502,10 @@ group:Wipe tower setting:wipe_tower_rotation_angle setting:wipe_tower_bridging setting:wipe_tower_no_sparse_layers - setting:single_extruder_multi_material_priming + line:Priming + setting:single_extruder_multi_material_priming + setting:priming_position + end_line group:Advanced setting:interface_shells setting:mmu_segmented_region_max_width diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 0cd13a5bd95..a67d1b6358b 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -726,7 +726,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: f->fill_surface_extrusion(&surface_fill.surface, surface_fill.params, fills_by_priority[(size_t)surface_fill.params.priority]->set_entities()); #if _DEBUG //check no over or underextrusion if fill_exactly - if (surface_fill.params.fill_exactly && surface_fill.params.density == 1) { + if (surface_fill.params.fill_exactly && surface_fill.params.density == 1 && !surface_fill.params.flow.bridge()) { ExtrusionVolume compute_volume; ExtrusionVolume compute_volume_no_gap_fill(false); const size_t idx_end = fills_by_priority[(size_t)surface_fill.params.priority]->entities().size(); diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index 5e1fb4e43ff..4604b46f3ca 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -3654,9 +3654,11 @@ FillWithPerimeter::fill_surface_extrusion(const Surface* surface, const FillPara // === extrude perimeter & associated surface at the same time, in the right order === //generate perimeter: - ExPolygons path_perimeter = offset2_ex(ExPolygons{ surface->expolygon }, - scale_d(-this->get_spacing()), scale_d(this->get_spacing() / 2), - ClipperLib::jtMiter, scale_d(this->get_spacing()) * 10); + coord_t offset_for_overlap = scale_d(this->get_spacing() / 2) * ((1 - overlap_ratio) / 2); + ExPolygons path_perimeter = offset2_ex(ExPolygons{surface->expolygon}, + scale_d(-this->get_spacing()) / 2 - offset_for_overlap, + offset_for_overlap, + ClipperLib::jtMiter, scale_d(this->get_spacing()) * 10); //fix a bug that can happens when (positive) offsetting with a big miter limit and two island merge. See https://github.com/supermerill/SuperSlicer/issues/609 path_perimeter = intersection_ex(path_perimeter, offset_ex(surface->expolygon, scale_d(-this->get_spacing() / 2))); for (ExPolygon& expolygon : path_perimeter) { diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index 9d37d1c3286..1d0277d72d8 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -241,6 +241,8 @@ namespace NaiveConnect { class FillWithPerimeter : public Fill { public: + // bewteen 0 (0%) and 1 (100%) overlap + float overlap_ratio = 0; std::unique_ptr infill{ nullptr }; float ratio_fill_inside = 0.f; FillWithPerimeter() : Fill() {} @@ -265,9 +267,9 @@ class ExtrusionSetRole : public ExtrusionVisitor { ExtrusionSetRole(ExtrusionRole role) : new_role(role) {} void use(ExtrusionPath &path) override { path.set_role(new_role); } void use(ExtrusionPath3D &path3D) override { path3D.set_role(new_role); } - void use(ExtrusionMultiPath &multipath) override { for (ExtrusionPath path : multipath.paths) path.set_role(new_role); } - void use(ExtrusionMultiPath3D &multipath) override { for (ExtrusionPath path : multipath.paths) path.set_role(new_role); } - void use(ExtrusionLoop &loop) override { for (ExtrusionPath path : loop.paths) path.set_role(new_role); } + void use(ExtrusionMultiPath &multipath) override { for (ExtrusionPath &path : multipath.paths) path.set_role(new_role); } + void use(ExtrusionMultiPath3D &multipath) override { for (ExtrusionPath &path : multipath.paths) path.set_role(new_role); } + void use(ExtrusionLoop &loop) override { for (ExtrusionPath &path : loop.paths) path.set_role(new_role); } void use(ExtrusionEntityCollection &collection) override { for (ExtrusionEntity *entity : collection.entities()) entity->visit(*this); } }; diff --git a/src/libslic3r/Fill/FillConcentric.cpp b/src/libslic3r/Fill/FillConcentric.cpp index ad5208c183a..66262843b83 100644 --- a/src/libslic3r/Fill/FillConcentric.cpp +++ b/src/libslic3r/Fill/FillConcentric.cpp @@ -363,11 +363,14 @@ FillConcentricWGapFill::fill_surface_extrusion( // } // //goto_next_polyline: //} - if (!polylines.empty() && !is_bridge(good_role)) { + bool fill_bridge = is_bridge(good_role) || params.flow.bridge(); + // allow bridged gapfill, mostly for support bottom interface. + assert(!is_bridge(good_role)); + if (!polylines.empty()) { ExtrusionEntitiesPtr gap_fill_entities = Geometry::thin_variable_width(polylines, erGapFill, params.flow, scale_t(params.config->get_computed_value("resolution_internal")), true); if (!gap_fill_entities.empty()) { - //set role if needed - if (good_role != erSolidInfill) { + // set role if needed + if (fill_bridge || (good_role != erSolidInfill && good_role != erTopSolidInfill)) { ExtrusionSetRole set_good_role(good_role); for (ExtrusionEntity* ptr : gap_fill_entities) ptr->visit(set_good_role); @@ -395,7 +398,7 @@ FillConcentricWGapFill::fill_surface_extrusion( // external gapfill ExPolygons gapfill_areas = diff_ex(ExPolygons{ surface->expolygon }, offset_ex(expp, double(scale_(0.5 * this->get_spacing())))); gapfill_areas = union_safety_offset_ex(gapfill_areas); - if (gapfill_areas.size() > 0) { + if (gapfill_areas.size() > 0 && no_overlap_expolygons.size() > 0) { double minarea = double(params.flow.scaled_width()) * double(params.flow.scaled_width()); if (params.config != nullptr) minarea = scale_d(params.config->gap_fill_min_area.get_abs_value(params.flow.width())) * double(params.flow.scaled_width()); for (int i = 0; i < gapfill_areas.size(); i++) { diff --git a/src/libslic3r/Format/BBConfig.cpp b/src/libslic3r/Format/BBConfig.cpp index ce47ca5f956..ebadab2fba5 100644 --- a/src/libslic3r/Format/BBConfig.cpp +++ b/src/libslic3r/Format/BBConfig.cpp @@ -219,7 +219,7 @@ void init() key_translation_map["sparse_infill_pattern"] = "fill_pattern"; key_translation_map["filename_format"] = "output_filename_format"; key_translation_map["support_base_pattern"] = "support_material_pattern"; - key_translation_map["support_interface_pattern"] = "support_material_interface_pattern"; + key_translation_map["support_interface_pattern"] = "support_material_top_interface_pattern"; key_translation_map["top_surface_pattern"] = "top_fill_pattern"; key_translation_map["support_object_xy_distance"] = "support_material_xy_spacing"; key_translation_map["fuzzy_skin_point_distance"] = "fuzzy_skin_point_dist"; @@ -425,7 +425,7 @@ void init() //key_translation_map["single"]="retract_lift_above"; //key_translation_map["single"]="retract_lift_below"; //key_translation_map["single"]="wipe"; - //patern + //pattern value_translation_map["fill_pattern"]["monotonicline"] = "monotoniclines"; //2.7 value_translation_map["fill_pattern"]["zig-zag"] = "rectilinear"; value_translation_map["fill_pattern"]["tri-hexagon"] = "stars"; @@ -435,7 +435,8 @@ void init() value_translation_map["solid_fill_pattern"] = value_translation_map["fill_pattern"]; value_translation_map["brim_ears_pattern"] = value_translation_map["fill_pattern"]; value_translation_map["bridge_fill_pattern"] = value_translation_map["fill_pattern"]; - value_translation_map["support_material_interface_pattern"] = value_translation_map["fill_pattern"]; + value_translation_map["support_material_top_interface_pattern"] = value_translation_map["fill_pattern"]; + value_translation_map["support_material_bottom_interface_pattern"] = value_translation_map["fill_pattern"]; //specific value_translation_map["fill_pattern"]["default"] = "gyroid"; value_translation_map["top_fill_pattern"]["default"] = "monotonic"; @@ -443,7 +444,8 @@ void init() value_translation_map["solid_fill_pattern"]["default"] = "rectilinear"; value_translation_map["brim_ears_pattern"]["default"] = "concentric"; value_translation_map["bridge_fill_pattern"]["default"] = "rectilinear"; - value_translation_map["support_material_interface_pattern"]["default"] = "auto"; + value_translation_map["support_material_top_interface_pattern"]["default"] = "auto"; + value_translation_map["support_material_bottom_interface_pattern"]["default"] = "auto"; //value_translation_map["support_material_interface_pattern"]["rectilinear_interlaced"] = "???"; //can't convert let the config_substitutions emit the warning //others @@ -508,8 +510,12 @@ void complicated_convert(t_config_option_key &opt_key, std::string &value, const value = "1"; } } - if ("enable_overhang_speed") { - + //if ("enable_overhang_speed") { + // + //} + if ("support_material_top_interface_pattern" == opt_key || "support_interface_pattern" == opt_key) { + output["support_material_top_interface_pattern"] = value; + output["support_material_bottom_interface_pattern"] = value; } } diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp index e88aa6dc0dd..92152ce11bd 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp @@ -1209,7 +1209,7 @@ static ExPolygons get_boundary(const Layer &layer, std::vector &old_2_new_expoly : old_2_new_expolygons) { - assert(old_2_new_expoly.first.contains(old_2_new_expoly.second.contour.split_at_index(0))); + assert(old_2_new_expoly.first.contains(old_2_new_expoly.second.contour.split_at_index(0)) || old_2_new_expoly.first == old_2_new_expoly.second); boundary.push_back(old_2_new_expoly.second); slice_2_boundary.push_back(std::move(old_2_new_expoly)); } diff --git a/src/libslic3r/GCode/PressureAdvance.hpp b/src/libslic3r/GCode/PressureAdvance.hpp index 6a76085da33..20cf4574ee9 100644 --- a/src/libslic3r/GCode/PressureAdvance.hpp +++ b/src/libslic3r/GCode/PressureAdvance.hpp @@ -254,7 +254,7 @@ class PressureAdvance //void _print_in_middle_G1(BufferData& line_to_split, float nb_sec, const std::string& line_to_write); void write_buffer_data(); double compute_pressure_change_mm_filament(const BufferData& command); - void PressureAdvance::update_last_move(size_t last_move_idx, double junction_axle_speed); + void update_last_move(size_t last_move_idx, double junction_axle_speed); double get_pressure_factor() { double val = this->m_config.extruder_pressure_factor.get_float(m_current_extruder); diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 98d97418c0e..7a54df3840d 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -241,45 +241,74 @@ std::vector raycast_visibility(const AABBTreeIndirect::Tree<3, float> &ra return result; } -std::vector calculate_polygon_angles_at_vertices(const Polygon &polygon, const std::vector &lengths, +std::vector calculate_polyline_angles_at_vertices(const PolylineWithEnd &polyline, const std::vector &lengths, float min_arm_length) { - std::vector result(polygon.size()); + std::vector result(polyline.size()); + assert(polyline.size() - 1 == lengths.size()); - if (polygon.size() == 1) { + if (polyline.size() == 1) { result[0] = 0.0f; } size_t idx_prev = 0; size_t idx_curr = 0; size_t idx_next = 0; + size_t idx_start = 0; + size_t idx_end = 0; float distance_to_prev = 0; float distance_to_next = 0; - //push idx_prev far enough back as initialization - while (distance_to_prev < min_arm_length) { - idx_prev = Slic3r::prev_idx_modulo(idx_prev, polygon.size()); - distance_to_prev += lengths[idx_prev]; + bool is_polygon = polyline.front() == polyline.back(); + + //if polygon : start at 0 + if (is_polygon) { + idx_start = 0; + idx_end = polyline.size() -1; // last point is same as first, don't bother. + // push idx_prev far enough back as initialization + while (distance_to_prev < min_arm_length) { + idx_prev = Slic3r::prev_idx_modulo(idx_prev, lengths.size()); + distance_to_prev += lengths[idx_prev]; + } + } else { + //line, start & end are hardcoded + idx_start = 1; + idx_end = polyline.size() -1; + result.front() = polyline.endpoints.first ? -PI / 2 : 0; + result.back() = polyline.endpoints.second ? -PI / 2 : 0; } - for (size_t _i = 0; _i < polygon.size(); ++_i) { + for (size_t _i = idx_start; _i < idx_end; ++_i) { // pull idx_prev to current as much as possible, while respecting the min_arm_length - while (distance_to_prev - lengths[idx_prev] > min_arm_length) { + while (distance_to_prev - lengths[idx_prev] > min_arm_length && idx_prev < lengths.size()) { distance_to_prev -= lengths[idx_prev]; - idx_prev = Slic3r::next_idx_modulo(idx_prev, polygon.size()); + // if polygon, circle, if line, then stop at the end. + if (is_polygon) { + idx_prev = Slic3r::next_idx_modulo(idx_prev, lengths.size()); + } else { + idx_prev++; + } } //push idx_next forward as far as needed - while (distance_to_next < min_arm_length) { + while (distance_to_next < min_arm_length && idx_next < lengths.size()) { distance_to_next += lengths[idx_next]; - idx_next = Slic3r::next_idx_modulo(idx_next, polygon.size()); + // if polygon, circle, if line, then stop at the end. + if (is_polygon) { + idx_next = Slic3r::next_idx_modulo(idx_next, lengths.size()); + } else { + idx_next++; + } } // Calculate angle between idx_prev, idx_curr, idx_next. - const Point &p0 = polygon.points[idx_prev]; - const Point &p1 = polygon.points[idx_curr]; - const Point &p2 = polygon.points[idx_next]; + const Point &p0 = polyline.points[idx_prev]; + const Point &p1 = polyline.points[idx_curr]; + const Point &p2 = polyline.points[idx_next]; result[idx_curr] = float(angle(p1 - p0, p2 - p1)); + if (!is_polygon && polyline.direction == PolylineWithEnd::PolyDir::BOTH) { + result[idx_curr] = std::abs(result[idx_curr]); + } // increase idx_curr by one float curr_distance = lengths[idx_curr]; @@ -287,6 +316,11 @@ std::vector calculate_polygon_angles_at_vertices(const Polygon &polygon, distance_to_prev += curr_distance; distance_to_next -= curr_distance; } + + // polygon: repeat angle + if (is_polygon) { + result.back() = result.front(); + } return result; } @@ -419,14 +453,14 @@ struct GlobalModelInfo { } ; -//Extract perimeter polygons of the given layer -Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition configured_seam_preference, +//Extract perimeter polylines of the given layer +PolylineWithEnds extract_perimeter_polylines(const Layer *layer, const SeamPosition configured_seam_preference, std::vector &corresponding_regions_out, PerimeterGeneratorType perimeter_type) { - Polygons polygons; + PolylineWithEnds polylines; class PerimeterCopy : public ExtrusionVisitorConst { - Polygons* polygons; + PolylineWithEnds* polylines; std::vector* corresponding_regions_out; const LayerRegion* current_layer_region; SeamPosition configured_seam_preference; @@ -434,59 +468,83 @@ Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition confi public: bool also_overhangs = false; bool also_thin_walls = false; - PerimeterCopy(std::vector* regions_out, Polygons* polys, SeamPosition configured_seam, PerimeterGeneratorType perimeter_type) - : corresponding_regions_out(regions_out), configured_seam_preference(configured_seam), polygons(polys), perimeter_type(perimeter_type) { + PerimeterCopy(std::vector* regions_out, PolylineWithEnds* polys, SeamPosition configured_seam, PerimeterGeneratorType perimeter_type) + : corresponding_regions_out(regions_out), configured_seam_preference(configured_seam), polylines(polys), perimeter_type(perimeter_type) { } virtual void default_use(const ExtrusionEntity& entity) {}; virtual void use(const ExtrusionPath &path) override { if (perimeter_type == PerimeterGeneratorType::Arachne && path.role() != erThinWall) { - path.polygons_covered_by_width(*polygons, SCALED_EPSILON); - while (corresponding_regions_out->size() < polygons->size()) { + //path.polygons_covered_by_width(*polygons, SCALED_EPSILON); + assert(corresponding_regions_out->size() == polylines->size()); + polylines->emplace_back(path.polyline.get_points(), true, true, PolylineWithEnd::PolyDir::BOTH); // TODO: more point for arcs + //while (corresponding_regions_out->size() < polylines->size()) { corresponding_regions_out->push_back(current_layer_region); - } + //} } } virtual void use(const ExtrusionLoop& loop) override { - if ((configured_seam_preference == spAllRandom && !loop.paths.empty() && - is_perimeter(loop.paths.front().role())) - || (also_thin_walls && loop.role() == erThinWall)) { - Points p; - loop.collect_points(p); - polygons->emplace_back(std::move(p)); + bool is_ccw = loop.polygon().is_counter_clockwise(); + if ((configured_seam_preference == spAllRandom && !loop.paths.empty() && is_perimeter(loop.paths.front().role())) + || (also_thin_walls && loop.role() == erThinWall)) { + Points pts; + loop.collect_points(pts); + pts.push_back(pts.front()); //polygon + assert(corresponding_regions_out->size() == polylines->size()); + polylines->emplace_back(std::move(pts), true, false, is_ccw ? PolylineWithEnd::PolyDir::CCW : PolylineWithEnd::PolyDir::CW); corresponding_regions_out->push_back(current_layer_region); return; }else { - Points p; + PolylineWithEnds polys; + size_t count_paths_collected = 0; + bool previous_collected = false; + bool current_collected = false; for (const ExtrusionPath &path : loop.paths) { + current_collected = false; if (path.role() == ExtrusionRole::erExternalPerimeter) { - path.collect_points(p); + if(!previous_collected) + polys.emplace_back(false, false, is_ccw ? PolylineWithEnd::PolyDir::CCW : PolylineWithEnd::PolyDir::CW); + path.collect_points(polys.back().points); + count_paths_collected++; + current_collected = true; } if (path.role() == ExtrusionRole::erOverhangPerimeter && also_overhangs) { // TODO find a way to search for external overhangs only - path.collect_points(p); + if(!previous_collected) + polys.emplace_back(false, false, is_ccw ? PolylineWithEnd::PolyDir::CCW : PolylineWithEnd::PolyDir::CW); + path.collect_points(polys.back().points); + count_paths_collected++; + current_collected = true; } //if (path.role() == ExtrusionRole::erThinWall && also_thin_walls) { // path.collect_points(p); // TODO: 2.7: reactivate when it's possible to distinguish between thinwalltravel & thinextrusions // // currently, only looking for thinwall-only loop //} + previous_collected = current_collected; } - if (!p.empty()) { // for random seam alignment, extract all perimeters - polygons->emplace_back(std::move(p)); - corresponding_regions_out->push_back(current_layer_region); + if (!polys.empty()) { // for random seam alignment, extract all perimeters + if (count_paths_collected == loop.paths.size()) { + assert(polys.size() == 1); + assert(polys.front().first_point() == polys.front().last_point()); + } + assert(corresponding_regions_out->size() == polylines->size()); + append(*polylines, std::move(polys)); + while (corresponding_regions_out->size() < polylines->size()) { + corresponding_regions_out->push_back(current_layer_region); + } } } } virtual void use(const ExtrusionMultiPath& collection) override { if (perimeter_type == PerimeterGeneratorType::Arachne) { - Polygons polys; - for (const ExtrusionPath& path : collection.paths) { - path.polygons_covered_by_width(polys, SCALED_EPSILON); - } - polys = union_(polys); - append(*polygons, polys); - while (corresponding_regions_out->size() < polygons->size()) { + for (size_t idx = 0; idx < collection.size(); idx++) { + const ExtrusionPath &path = collection.paths[idx]; + assert(corresponding_regions_out->size() == polylines->size()); + polylines->emplace_back(path.polyline.get_points(), + idx == 0 ? true : false, + idx + 1 < collection.size() ? false : true, + PolylineWithEnd::PolyDir::BOTH); // TODO: more points for arcs corresponding_regions_out->push_back(current_layer_region); } } @@ -497,29 +555,28 @@ Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition confi } } void set_current_layer_region(const LayerRegion *set) { current_layer_region = set; } - } visitor(&corresponding_regions_out, &polygons, configured_seam_preference, perimeter_type); + } visitor(&corresponding_regions_out, &polylines, configured_seam_preference, perimeter_type); for (const LayerRegion *layer_region : layer->regions()) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities()) { visitor.set_current_layer_region(layer_region); ex_entity->visit(visitor); - if (polygons.empty()) { + if (polylines.empty()) { // maybe only thin walls? visitor.also_thin_walls = true; ex_entity->visit(visitor); - if (polygons.empty()) { + if (polylines.empty()) { // can happen if the external is fully an overhang visitor.also_overhangs = true; ex_entity->visit(visitor); visitor.also_overhangs = false; - if (polygons.empty()) { + if (polylines.empty()) { // shouldn't happen - ex_entity->visit(visitor); assert(ex_entity->role() == erThinWall); // no loops // what to do in this case? - Points p; - ex_entity->collect_points(p); - polygons.emplace_back(std::move(p)); + Points pts; + ex_entity->collect_points(pts); + polylines.emplace_back(std::move(pts), true, pts.front() != pts.back(), PolylineWithEnd::PolyDir::BOTH); corresponding_regions_out.push_back(layer_region); } } @@ -527,51 +584,53 @@ Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition confi } } } - if (polygons.empty()) { // If there are no perimeter polygons for whatever reason (disabled perimeters .. ) insert dummy point + if (polylines.empty()) { // If there are no perimeter polylines/polygons for whatever reason (disabled perimeters .. ) insert dummy point // it is easier than checking everywhere if the layer is not emtpy, no seam will be placed to this layer anyway - polygons.emplace_back(std::vector { Point { 0, 0 } }); + polylines.emplace_back(std::vector{ Point { 0, 0 } }, true, true, PolylineWithEnd::PolyDir::BOTH); corresponding_regions_out.push_back(nullptr); } - assert(corresponding_regions_out.size() == polygons.size()); - return polygons; + assert(corresponding_regions_out.size() == polylines.size()); + return polylines; } -// Insert SeamCandidates created from perimeter polygons in to the result vector. +// Insert SeamCandidates created from perimeter polygons/polylines in to the result vector. // Compute its type (Enfrocer,Blocker), angle, and position -//each SeamCandidate also contains pointer to shared Perimeter structure representing the polygon -// if Custom Seam modifiers are present, oversamples the polygon if necessary to better fit user intentions -void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const LayerRegion *region, +//each SeamCandidate also contains pointer to shared Perimeter structure representing the polyline +// if Custom Seam modifiers are present, oversamples the polyline if necessary to better fit user intentions +void process_perimeter_polylines(const PolylineWithEnd &orig_polyline, float z_coord, const LayerRegion *region, const GlobalModelInfo &global_model_info, PrintObjectSeamData::LayerSeams &result) { - if (orig_polygon.size() == 0) { + if (orig_polyline.size() == 0) { return; } - Polygon polygon = orig_polygon; - bool was_clockwise = polygon.make_counter_clockwise(); + PolylineWithEnd polyline = orig_polyline; + bool is_polygon = polyline.first_point() == polyline.last_point(); + assert(!is_polygon || polyline.direction != PolylineWithEnd::PolyDir::BOTH); + if (is_polygon && polyline.direction == PolylineWithEnd::PolyDir::CW) { + polyline.reverse(); + assert(polyline.direction == PolylineWithEnd::PolyDir::CCW); + } float angle_arm_len = region != nullptr ? region->flow(FlowRole::frExternalPerimeter).nozzle_diameter() : 0.5f; std::vector lengths { }; - for (size_t point_idx = 0; point_idx < polygon.size() - 1; ++point_idx) { - lengths.push_back((unscale(polygon[point_idx]) - unscale(polygon[point_idx + 1])).norm()); + for (size_t point_idx = 0; point_idx < polyline.size() - 1; ++point_idx) { + lengths.push_back((unscale(polyline.points[point_idx]) - unscale(polyline.points[point_idx + 1])).norm()); } - lengths.push_back(std::max((unscale(polygon[0]) - unscale(polygon[polygon.size() - 1])).norm(), 0.1)); - std::vector polygon_angles = calculate_polygon_angles_at_vertices(polygon, lengths, - angle_arm_len); + std::vector polyline_angles = calculate_polyline_angles_at_vertices(polyline, lengths, angle_arm_len); result.perimeters.push_back( { }); Perimeter &perimeter = result.perimeters.back(); - std::queue orig_polygon_points { }; - for (size_t index = 0; index < polygon.size(); ++index) { - Vec2f unscaled_p = unscale(polygon[index]).cast(); - orig_polygon_points.emplace(unscaled_p.x(), unscaled_p.y(), z_coord); + std::queue orig_polyline_points { }; + for (size_t index = 0; index < polyline.size(); ++index) { + Vec2f unscaled_p = unscale(polyline.points[index]).cast(); + orig_polyline_points.emplace(unscaled_p.x(), unscaled_p.y(), z_coord); } - Vec3f first = orig_polygon_points.front(); std::queue oversampled_points { }; size_t orig_angle_index = 0; perimeter.start_index = result.points.size(); perimeter.flow_width = region != nullptr ? region->flow(FlowRole::frExternalPerimeter).width() : 0.0f; bool some_point_enforced = false; - while (!orig_polygon_points.empty() || !oversampled_points.empty()) { + while (!orig_polyline_points.empty() || !oversampled_points.empty()) { EnforcedBlockedSeamPoint type = EnforcedBlockedSeamPoint::Neutral; Vec3f position; float local_ccw_angle = 0; @@ -580,9 +639,16 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const position = oversampled_points.front(); oversampled_points.pop(); } else { - position = orig_polygon_points.front(); - orig_polygon_points.pop(); - local_ccw_angle = was_clockwise ? -polygon_angles[orig_angle_index] : polygon_angles[orig_angle_index]; + position = orig_polyline_points.front(); + orig_polyline_points.pop(); + if (orig_polyline.direction == PolylineWithEnd::PolyDir::BOTH) { + local_ccw_angle = polyline_angles[orig_angle_index]; + } else if (orig_polyline.direction == PolylineWithEnd::PolyDir::CW) { + local_ccw_angle = -polyline_angles[orig_angle_index]; + } else { + assert(orig_polyline.direction == PolylineWithEnd::PolyDir::CCW); + local_ccw_angle = polyline_angles[orig_angle_index]; + } orig_angle_index++; orig_point = true; } @@ -596,10 +662,11 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const } some_point_enforced = some_point_enforced || type == EnforcedBlockedSeamPoint::Enforced; - if (orig_point) { - Vec3f pos_of_next = orig_polygon_points.empty() ? first : orig_polygon_points.front(); + if (orig_point && !orig_polyline_points.empty()) { + Vec3f pos_of_next = orig_polyline_points.front(); + Line line(scaled(Vec2f(position.head<2>())), scaled(Vec2f(pos_of_next.head<2>()))); float distance_to_next = (position - pos_of_next).norm(); - if (global_model_info.is_enforced(position, distance_to_next)) { + if (global_model_info.is_enforced( (position + pos_of_next) / 2, distance_to_next / 2)) { Vec3f vec_to_next = (pos_of_next - position).normalized(); float step_size = SeamPlacer::enforcer_oversampling_distance; float step = step_size; @@ -614,6 +681,7 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const } perimeter.end_index = result.points.size(); + assert(!is_polygon || result.points[perimeter.end_index-1].position == result.points[perimeter.start_index].position); if (some_point_enforced) { // We will patches of enforced points (patch: continuous section of enforced points), choose @@ -624,18 +692,26 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const return perimeter.start_index + Slic3r::next_idx_modulo(idx - perimeter.start_index, perimeter_size); }; + assert(perimeter.start_index + 2 <= perimeter.end_index); // at least 2 elt std::vector patches_starts_ends; - for (size_t i = perimeter.start_index; i < perimeter.end_index; ++i) { + for (size_t i = perimeter.start_index; i < perimeter.end_index - 1; ++i) { if (result.points[i].type != EnforcedBlockedSeamPoint::Enforced && - result.points[next_index(i)].type == EnforcedBlockedSeamPoint::Enforced) { - patches_starts_ends.push_back(next_index(i)); + result.points[i+1].type == EnforcedBlockedSeamPoint::Enforced) { + patches_starts_ends.push_back(i+1); } if (result.points[i].type == EnforcedBlockedSeamPoint::Enforced && - result.points[next_index(i)].type != EnforcedBlockedSeamPoint::Enforced) { - patches_starts_ends.push_back(next_index(i)); + result.points[i+1].type != EnforcedBlockedSeamPoint::Enforced) { + patches_starts_ends.push_back(i+1); } } - //if patches_starts_ends are empty, it means that the whole perimeter is enforced.. don't do anything in that case + // also add start & end that can't be found for polyline + if (!is_polygon && result.points[perimeter.start_index].type == EnforcedBlockedSeamPoint::Enforced) { + patches_starts_ends.insert(patches_starts_ends.begin(), perimeter.start_index); + } + if (!is_polygon && result.points[perimeter.end_index - 1].type == EnforcedBlockedSeamPoint::Enforced) { + patches_starts_ends.push_back(perimeter.end_index); + } + // If patches_starts_ends are empty, it means that the whole perimeter is enforced.. don't do anything in that case, as there is no center of a circumference if (!patches_starts_ends.empty()) { //if the first point in the patches is not enforced, it marks a patch end. in that case, put it to the end and start on next // to simplify the processing @@ -969,7 +1045,7 @@ void debug_export_points(const std::vector &lay float max_weight = min_weight; for (const SeamCandidate &point : layers[layer_idx].points) { - Vec3i color = value_to_rgbi(-PI, PI, point.local_ccw_angle); + Vec3i32 color = value_to_rgbi(-PI, PI, point.local_ccw_angle); std::string fill = "rgb(" + std::to_string(color.x()) + "," + std::to_string(color.y()) + "," + std::to_string(color.z()) + ")"; angles_svg.draw(scaled(Vec2f(point.position.head<2>())), fill); @@ -1171,9 +1247,9 @@ void SeamPlacer::gather_seam_candidates(const PrintObject *po, auto unscaled_z = layer->slice_z; std::vector regions; //NOTE corresponding region ptr may be null, if the layer has zero perimeters - Polygons polygons = extract_perimeter_polygons(layer, configured_seam_preference, regions, po->config().perimeter_generator.value); - for (size_t poly_index = 0; poly_index < polygons.size(); ++poly_index) { - process_perimeter_polygon(polygons[poly_index], unscaled_z, + PolylineWithEnds polygons_and_lines = extract_perimeter_polylines(layer, configured_seam_preference, regions, po->config().perimeter_generator.value); + for (size_t poly_index = 0; poly_index < polygons_and_lines.size(); ++poly_index) { + process_perimeter_polylines(polygons_and_lines[poly_index], unscaled_z, regions[poly_index], global_model_info, layer_seams); } auto functor = SeamCandidateCoordinateFunctor { layer_seams.points }; diff --git a/src/libslic3r/GCode/SeamPlacer.hpp b/src/libslic3r/GCode/SeamPlacer.hpp index e65273f4465..e8c5c579923 100644 --- a/src/libslic3r/GCode/SeamPlacer.hpp +++ b/src/libslic3r/GCode/SeamPlacer.hpp @@ -44,6 +44,30 @@ inline double angle(const Eigen::MatrixBase &v1, const Eigen::MatrixBas struct GlobalModelInfo; struct SeamComparator; +class PolylineWithEnd : public Polyline { +public: + enum PolyDir { + CCW, + CW, + BOTH + }; + /// if true => it's an endpoint, if false it join somthign that can't be used for a seam, so don't use this endpoint. + std::pair endpoints; + PolyDir direction; + + PolylineWithEnd() : endpoints(false, false), direction(PolyDir::BOTH) {} + PolylineWithEnd(bool stopstart, bool stopend, PolyDir is_ccw) : endpoints(stopstart, stopend), direction(is_ccw) {} + PolylineWithEnd(const Points &pts, bool stopstart, bool stopend, PolyDir is_ccw) : Polyline(pts), endpoints(stopstart, stopend), direction(is_ccw) {} + PolylineWithEnd(Points &&pts, bool stopstart, bool stopend, PolyDir is_ccw) : Polyline(pts), endpoints(stopstart, stopend), direction(is_ccw) {} + void reverse() { + Polyline::reverse(); + std::swap(this->endpoints.first, this->endpoints.second); + if (direction != PolyDir::BOTH) + direction = (direction == PolyDir::CCW) ? PolyDir::CW : PolyDir::CCW; + } +}; +typedef std::vector PolylineWithEnds; + enum class EnforcedBlockedSeamPoint { Blocked = 0, Neutral = 1, diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index c378aa2d250..f668ecee62d 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -654,7 +654,7 @@ WipeTower::WipeTower(const PrintConfig& config, const PrintObjectConfig& default } // Read absolute value of first layer speed, if given as percentage, // it is taken over wipe_tower_speed. - m_first_layer_speed = config.get_abs_value("first_layer_speed", m_speed); + m_first_layer_speed = default_object_config.first_layer_speed.get_abs_value(m_speed); if (m_first_layer_speed == 0.f) { // just to make sure autospeed doesn't break it. m_first_layer_speed = m_speed; } @@ -776,16 +776,23 @@ std::vector WipeTower::prime( // therefore the homing position is shifted inside the bed by 0.2 in the firmware to [0.2, -2.0]. // box_coordinates cleaning_box(xy(0.5f, - 1.5f), m_wipe_tower_width, wipe_area); - float prime_section_width = std::min(0.9f * m_bed_width / tools.size(), 60.f); + float prime_section_width = std::min((m_bed_shape == CircularBed ? 0.45f : 0.9f) * m_bed_width / tools.size(), 60.f); box_coordinates cleaning_box(Vec2f(0.02f * m_bed_width, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f); if (m_bed_shape == CircularBed) { cleaning_box = box_coordinates(Vec2f(0.f, 0.f), prime_section_width, 100.f); float total_width_half = tools.size() * prime_section_width / 2.f; - cleaning_box.translate(-total_width_half, -std::sqrt(std::max(0.f, std::pow(m_bed_width/2, 2.f) - std::pow(1.05f * total_width_half, 2.f)))); + if (m_config->priming_position.value == Vec2d(0,0)) { + cleaning_box.translate(-total_width_half, -std::sqrt(std::max(0.f, std::pow(m_bed_width/2, 2.f) - std::pow(1.05f * total_width_half, 2.f)))); + } else { + cleaning_box.translate(m_config->priming_position.value.x(), m_config->priming_position.value.y()); + } + } else { + if (m_config->priming_position.value == Vec2d(0,0)) { + cleaning_box.translate(m_bed_bottom_left); + } else { + cleaning_box.translate(m_config->priming_position.value.x(), m_config->priming_position.value.y()); + } } - else - cleaning_box.translate(m_bed_bottom_left); - std::vector results; // Iterate over all priming toolchanges and push respective ToolChangeResults into results vector. diff --git a/src/libslic3r/Geometry/MedialAxis.cpp b/src/libslic3r/Geometry/MedialAxis.cpp index f1d8788f780..d08c8136dba 100644 --- a/src/libslic3r/Geometry/MedialAxis.cpp +++ b/src/libslic3r/Geometry/MedialAxis.cpp @@ -2636,17 +2636,15 @@ MedialAxis::taper_ends(ThickPolylines& pp) if (polyline.length() < length * 2.2) continue; if (polyline.endpoints.first) { polyline.points_width[0] = min_size; - coord_t current_dist = min_size; - coord_t last_dist = min_size; + coord_t current_dist = 0; + coord_t last_dist = 0; for (size_t i = 1; i < polyline.points_width.size(); ++i) { current_dist += (coord_t)polyline.points[i - 1].distance_to(polyline.points[i]); if (current_dist > length) { //create a new point if not near enough - if (current_dist > length + coordf_t(this->m_resolution)) { - coordf_t percent_dist = (length - last_dist) / (current_dist - last_dist); - polyline.points.insert(polyline.points.begin() + i, polyline.points[i - 1].interpolate(percent_dist, polyline.points[i])); - polyline.points_width.insert(polyline.points_width.begin() + i, polyline.points_width[i]); - } + coordf_t percent_dist = (length - last_dist) / (current_dist - last_dist); + polyline.points.insert(polyline.points.begin() + i, polyline.points[i - 1].interpolate(percent_dist, polyline.points[i])); + polyline.points_width.insert(polyline.points_width.begin() + i, polyline.points_width[i]); break; } polyline.points_width[i] = std::max((coordf_t)min_size, min_size + (polyline.points_width[i] - min_size) * current_dist / length); @@ -2655,17 +2653,15 @@ MedialAxis::taper_ends(ThickPolylines& pp) } if (polyline.endpoints.second) { polyline.points_width[polyline.points_width.size() - 1] = min_size; - coord_t current_dist = min_size; - coord_t last_dist = min_size; + coord_t current_dist = 0; + coord_t last_dist = 0; for (size_t i = polyline.points_width.size() - 1; i > 0; --i) { current_dist += (coord_t)polyline.points[i].distance_to(polyline.points[i - 1]); if (current_dist > length) { //create new point if not near enough - if (current_dist > length + coordf_t(this->m_resolution)) { - coordf_t percent_dist = (length - last_dist) / (current_dist - last_dist); - polyline.points.insert(polyline.points.begin() + i, polyline.points[i].interpolate(percent_dist, polyline.points[i - 1])); - polyline.points_width.insert(polyline.points_width.begin() + i, polyline.points_width[i - 1]); - } + coordf_t percent_dist = (length - last_dist) / (current_dist - last_dist); + polyline.points.insert(polyline.points.begin() + i, polyline.points[i].interpolate(percent_dist, polyline.points[i - 1])); + polyline.points_width.insert(polyline.points_width.begin() + i, polyline.points_width[i - 1]); break; } polyline.points_width[i - 1] = std::max((coordf_t)min_size, min_size + (polyline.points_width[i - 1] - min_size) * current_dist / length); @@ -3114,25 +3110,23 @@ unsafe_variable_width(const ThickPolyline& polyline, const ExtrusionRole role, c continue; } } else if (i > 0 && resolution_internal > line_len + prev_line_len) { + assert(prev_line_len > 0 && line_len > 0); //merge lines? //if it's a loop, merge only if the distance is high enough if (polyline.first_point() == polyline.last_point() && polyline.length() < (line_len + prev_line_len) * 6) continue; ThickLine& prev_line = lines[i - 1]; + const coordf_t sum_length = prev_line_len + line_len; + const coordf_t new_length = prev_line.a.distance_to(line.b); + assert(sum_length - new_length > -0.0000000001); + // only merge if the distance is almost the sum (90° = 0.707) + if(new_length < std::max(sum_length * 0.9, std::max(prev_line_len + line_len/2 , prev_line_len /2 + line_len))) + continue; + assert(new_length > prev_line_len && new_length > line_len); coordf_t width = prev_line_len * (prev_line.a_width + prev_line.b_width) / 2; width += line_len * (line.a_width + line.b_width) / 2; + width /= (prev_line_len + line_len); prev_line.b = line.b; - const coordf_t new_length = prev_line.length(); - if (new_length < SCALED_EPSILON) { - // too short, remove it and restart - if (i > 1) { - line.a = lines[i - 2].b; - } - lines.erase(lines.begin() + i - 1); - i -= 2; - continue; - } - width /= new_length; prev_line.a_width = width; prev_line.b_width = width; saved_line_len = new_length; @@ -3154,6 +3148,7 @@ unsafe_variable_width(const ThickPolyline& polyline, const ExtrusionRole role, c // but we can't extrude with a negative spacing, so we have to gradually fall back to spacing if the width is too small. // default: extrude a thin wall that doesn't go outside of the specified width. + assert(line.a_width == line.b_width); double wanted_width = unscaled(line.a_width); //if gapfill or arachne, the width is in fact the spacing. if (role != erThinWall) { @@ -3163,21 +3158,25 @@ unsafe_variable_width(const ThickPolyline& polyline, const ExtrusionRole role, c } else { // Convert from spacing to extrusion width based on the extrusion model // of a square extrusion ended with semi circles. - wanted_width = Flow::rounded_rectangle_extrusion_width_from_spacing(unscaled(line.a_width), flow.height(), flow.spacing_ratio()); + wanted_width = Flow::rounded_rectangle_extrusion_width_from_spacing(wanted_width, flow.height(), flow.spacing_ratio()); } } // check if the width isn't too small (negative spacing) // 1.f spacing ratio, because it's to get the really minimum. 0 spacing ratio will makes that irrelevant. - if (unscale(line.a_width) < 2 * Flow::rounded_rectangle_extrusion_width_from_spacing(0.f, flow.height(), 1.f)) { + if (wanted_width < 2 * Flow::rounded_rectangle_extrusion_width_from_spacing(0.f, flow.height(), 1.f)) { //width (too) small, be sure to not extrude with negative spacing. //we began to fall back to spacing gradually even before the spacing go into the negative - // to make extrusion1 < extrusion2 if width1 < width2 even if width2 is too small. - wanted_width = unscaled(line.a_width) * 0.35 + 1.3 * Flow::rounded_rectangle_extrusion_width_from_spacing(0.f, flow.height(), 1.f); + // to make extrusion1 < extrusion2 if width1 < width2 even if width2 is too small. + wanted_width = wanted_width * 0.35 + 1.3 * Flow::rounded_rectangle_extrusion_width_from_spacing(0.f, flow.height(), 1.f); } if (path.polyline.empty()) { if (wanted_width != current_flow.width()) { - current_flow = current_flow.with_width((float)wanted_width); + if (current_flow.bridge()) { + current_flow = Flow::bridging_flow(current_flow.height(), (float) wanted_width); + } else { + current_flow = current_flow.with_width((float) wanted_width); + } } path.polyline.append(line.a); path.polyline.append(line.b); @@ -3198,7 +3197,11 @@ unsafe_variable_width(const ThickPolyline& polyline, const ExtrusionRole role, c paths.push_back(path); path = ExtrusionPath(role, false); if (wanted_width != current_flow.width()) { - current_flow = current_flow.with_width(wanted_width); + if (current_flow.bridge()) { + current_flow = Flow::bridging_flow(current_flow.height(), (float) wanted_width); + } else { + current_flow = current_flow.with_width((float) wanted_width); + } } path.polyline.append(line.a); path.polyline.append(line.b); @@ -3226,7 +3229,7 @@ ExtrusionEntitiesPtr // this value determines granularity of adaptive width, as G-code does not allow // variable extrusion within a single move; this value shall only affect the amount // of segments, and any pruning shall be performed before we apply this tolerance - const coord_t tolerance = flow.scaled_width() / 10;//scale_(0.05); + const coord_t tolerance = flow.scaled_width() / 20;//scale_(0.05); ExtrusionEntitiesPtr coll; for (const ThickPolyline& p : polylines) { ExtrusionMultiPath multi_paths = variable_width(p, role, flow, resolution_internal, tolerance, can_reverse); diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 3f9b0130ddf..a9a030e91ac 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -1579,7 +1579,7 @@ ProcessSurfaceResult PerimeterGenerator::process_classic(int& contour_count, int bound.remove_point_too_near(ext_perimeter_width / 10); // the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop (*1.2 because of circles approx. and enlrgment from 'div') Slic3r::Geometry::MedialAxis ma{ thin[0], (coord_t)((ext_perimeter_width + ext_perimeter_spacing) * 1.2), - min_width, coord_t(this->layer->height) }; + min_width, scale_t(this->layer->height) }; ma.use_bounds(bound) .use_min_real_width(scale_t(this->ext_perimeter_flow.nozzle_diameter())) .use_tapers(thin_walls_overlap) @@ -3687,9 +3687,12 @@ void PerimeterGenerator::_merge_thin_walls(ExtrusionEntityCollection &extrusions if (searcher.search_result.path != nullptr) { #if _DEBUG searcher.search_result.loop->visit(LoopAssertVisitor{}); + ExtrusionLoop orig_loop = *searcher.search_result.loop; #endif if (!searcher.search_result.from_start) tw.reverse(); + //save old path, as it may be destroyed before being re-created and we want to keep its parameters. + ExtrusionPath path_to_split = *searcher.search_result.path; // TODO: 2.7: just save hte pathsettigns //get the point Point point = tw.front().projection_onto(searcher.search_result.line); //we have to create 3 paths: 1: thinwall extusion, 2: thinwall return, 3: end of the path @@ -3749,14 +3752,14 @@ void PerimeterGenerator::_merge_thin_walls(ExtrusionEntityCollection &extrusions } else { assert(poly_after.length() > SCALED_EPSILON); searcher.search_result.loop->paths.insert(searcher.search_result.loop->paths.begin() + idx_path_to_add, - ExtrusionPath(poly_after, *searcher.search_result.path)); + ExtrusionPath(poly_after, path_to_split)); } assert(idx_path_before > searcher.search_result.loop->paths.size() || searcher.search_result.loop->paths[idx_path_before].polyline.size() > 1); assert(poly_after.size() > 0); //create thin wall path exttrusion ExtrusionEntityCollection tws; - tws.append(Geometry::thin_variable_width({ tw }, erThinWall, this->ext_perimeter_flow, std::max(ext_perimeter_flow.scaled_width() / 4, scale_t(this->print_config->resolution)), false)); + tws.append(Geometry::thin_variable_width({ tw }, erThinWall, this->ext_perimeter_flow, std::max(ext_perimeter_flow.scaled_width() / 10, scale_t(this->print_config->resolution)), false)); assert(!tws.entities().empty()); #if _DEBUG tws.visit(LoopAssertVisitor{}); diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index f0e24405df1..013228f5f82 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -934,7 +934,7 @@ namespace client if (!vector_opt->is_extruder_size()) ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range); } - const ConfigOptionDef* opt_def; + const ConfigOptionDef* opt_def = nullptr; switch (opt.opt->type()) { case coFloat: output.set_d(opt.opt->get_float()); break; case coInt: output.set_i(opt.opt->get_int()); break; @@ -957,7 +957,7 @@ namespace client opt_def = print_config_def.get(opt_key); assert(opt_def != nullptr); double v = opt.opt->get_float() * 0.01; // percent to ratio - for (;;) { + if (opt_def) for (;;) { const ConfigOption *opt_parent = opt_def->ratio_over.empty() ? nullptr : ctx->resolve_symbol(opt_def->ratio_over); if (opt_parent == nullptr) ctx->throw_exception("FloatOrPercent variable failed to resolve the \"ratio_over\" dependencies", opt.it_range); @@ -986,7 +986,7 @@ namespace client } case coInts: opt_def = print_config_def.get(opt_key); - if (opt_def->is_vector_extruder) { + if (opt_def && opt_def->is_vector_extruder) { output.set_i(int(((ConfigOptionVectorBase*)opt.opt)->get_float(int(ctx->current_extruder_id)))); break; } else @@ -1810,7 +1810,7 @@ void PlaceholderParser::parse_custom_variables(const ConfigOptionStrings& filame std::map> name2var_array; const std::vector empty_array(filament_custom_variables.size()); - for (int extruder_id = 0; extruder_id < filament_custom_variables.size(); ++extruder_id) + for (size_t extruder_id = 0; extruder_id < filament_custom_variables.size(); ++extruder_id) { std::string raw_text = filament_custom_variables.get_at(extruder_id); boost::erase_all(raw_text, "\r"); diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index b361fe995cc..5b08fea4afd 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -314,7 +314,7 @@ void Preset::normalize(DynamicPrintConfig &config) static_cast(opt)->resize(n, defaults.option(key)); } // The following keys are mandatory for the UI, but they are not part of FullPrintConfig, therefore they are handled separately. - for (const std::string &key : { "filament_settings_id" }) { + for (const char *key : {"filament_settings_id"}) { auto *opt = config.option(key, false); assert(opt == nullptr || opt->type() == coStrings); if (opt != nullptr && opt->type() == coStrings) @@ -623,7 +623,8 @@ static std::vector s_Preset_print_options { "support_material_angle", "support_material_angle_height", "support_material_interface_layers", "support_material_bottom_interface_layers", - "support_material_interface_pattern", + "support_material_top_interface_pattern", + "support_material_bottom_interface_pattern", "support_material_interface_angle", "support_material_interface_angle_increment", "support_material_interface_spacing", @@ -699,6 +700,7 @@ static std::vector s_Preset_print_options { "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "wipe_tower_speed", "wipe_tower_wipe_starting_speed", "wipe_tower_brim_width", + "priming_position", "mmu_segmented_region_max_width", "single_extruder_multi_material_priming", "wipe_tower_no_sparse_layers", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index f17f9c57851..faf870eba78 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -292,6 +292,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver& /* ne || opt_key == "first_layer_temperature" || opt_key == "gcode_flavor" || opt_key == "high_current_on_filament_swap" + || opt_key == "priming_position" || opt_key == "single_extruder_multi_material" || opt_key == "temperature" || opt_key == "wipe_tower" @@ -1792,7 +1793,7 @@ DynamicConfig PrintStatistics::config() const DynamicConfig PrintStatistics::placeholders() { DynamicConfig config; - for (const std::string &key : { + for (const char *key : { "print_time", "normal_print_time", "silent_print_time", "used_filament", "extruded_volume", "total_cost", "total_weight", "total_toolchanges", "total_wipe_tower_cost", "total_wipe_tower_filament", diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index 532865262c8..4c9e0dd1987 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -444,19 +444,25 @@ class PrintBase : public ObjectBase } void set_status(int percent, const std::string& message, const std::vector& args, unsigned int flags = SlicingStatus::DEFAULT) const { //check if it need an update. Avoid doing a gui update each ms. - if ((flags & SlicingStatus::FORCE_SHOW) == 0 && (flags & SlicingStatus::SECONDARY_STATE) != 0 && message != m_last_status_message) { - std::chrono::time_point current_time = std::chrono::system_clock::now(); - if ((static_cast>(current_time - PrintBase::m_last_status_update)).count() > 0.002 && PrintBase::m_last_status_percent != percent) { - PrintBase::m_last_status_update = current_time; - PrintBase::m_last_status_percent = percent; + { + std::lock_guard lock(m_last_status_mutex); + if ((flags & SlicingStatus::FORCE_SHOW) == 0 && (flags & SlicingStatus::SECONDARY_STATE) != 0 && + message != m_last_status_message) { + std::chrono::time_point current_time = std::chrono::system_clock::now(); + if ((static_cast>(current_time - PrintBase::m_last_status_update)) + .count() > 0.002 && + PrintBase::m_last_status_percent != percent) { + PrintBase::m_last_status_update = current_time; + PrintBase::m_last_status_percent = percent; + } else { + // don't update if it's for the secondary and already done in less than 200ms + return; + } } else { - //don't update if it's for the secondary and already done in less than 200ms - return; + PrintBase::m_last_status_percent = -1; } - } else { - PrintBase::m_last_status_percent = -1; + m_last_status_message = message; } - m_last_status_message = message; if ((flags & SlicingStatus::FlagBits::MAIN_STATE) == 0 && (flags & SlicingStatus::FlagBits::SECONDARY_STATE) == 0) flags = flags | SlicingStatus::FlagBits::MAIN_STATE; if (m_status_callback) { @@ -550,6 +556,7 @@ class PrintBase : public ObjectBase m_last_status_update = {}; inline static int m_last_status_percent = -1; inline static std::string m_last_status_message = ""; + inline static std::mutex m_last_status_mutex; private: std::atomic m_cancel_status; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 4dabc602d9d..8423dd02428 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -4529,6 +4529,17 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert | comPrusa; def->set_default_value(new ConfigOptionStrings()); + def = this->add("priming_position", coPoint); + def->label = L("Priming position"); + def->full_label = L("Priming position"); + def->tooltip = L("Coordinates of the left front corner of the priming patch." + "\nIf set to 0,0 then the position is computed automatically."); + //TODO: enable/disable + def->category = OptionCategory::customgcode; + def->sidetext = L("mm"); + def->mode = comAdvancedE | comSuSi; + def->set_default_value(new ConfigOptionPoint(Vec2d(0,0))); + def = this->add("printer_custom_variables", coString); def->label = L("Custom variables"); def->full_label = L("Custom Printer variables"); @@ -5464,15 +5475,6 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("If enabled, all printing extruders will be primed at the front edge of the print bed at the start of the print."); def->mode = comAdvancedE | comPrusa; def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("wipe_tower_no_sparse_layers", coBool); - def->label = L("No sparse layers (EXPERIMENTAL)"); - def->category = OptionCategory::mmsetup; - def->tooltip = L("If enabled, the wipe tower will not be printed on layers with no toolchanges. " - "On layers with a toolchange, extruder will travel downward to print the wipe tower. " - "User is responsible for ensuring there is no collision with the print."); - def->mode = comAdvancedE | comPrusa; - def->set_default_value(new ConfigOptionBool(false)); def = this->add("solid_infill_acceleration", coFloatOrPercent); def->label = L("Solid "); @@ -5846,11 +5848,36 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvancedE | comPrusa; def->set_default_value(new ConfigOptionEnum(smpRectilinear)); - def = this->add("support_material_interface_pattern", coEnum); - def->label = L("Pattern"); - def->full_label = L("Support interface pattern"); + def = this->add("support_material_bottom_interface_pattern", coEnum); + def->label = L("Bottom Pattern"); + def->full_label = L("Support bottom interface pattern"); def->category = OptionCategory::support; - def->tooltip = L("Pattern for interface layers." + def->tooltip = L("Pattern for the bottom interface layers (the ones that start on the object)." + "\nDefault pattern is the same as the top interface, unless it's Hilbert Curve or Ironing." + "\nNote that 'Hilbert', 'Ironing' , '(filled)' patterns are really discouraged, and meant to be used with soluble supports and 100% fill interface layer."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("auto"); + def->enum_values.push_back("rectilinear"); + def->enum_values.push_back("monotonic"); + def->enum_values.push_back("concentric"); + def->enum_values.push_back("hilbertcurve"); + def->enum_values.push_back("concentricgapfill"); + def->enum_values.push_back("smooth"); + def->enum_labels.push_back(L("Default")); + def->enum_labels.push_back(L("Rectilinear")); + def->enum_labels.push_back(L("Monotonic")); + def->enum_labels.push_back(L("Concentric")); + def->enum_labels.push_back(L("Hilbert Curve")); + def->enum_labels.push_back(L("Concentric (filled)")); + def->enum_labels.push_back(L("Ironing")); + def->mode = comAdvancedE | comSuSi; + def->set_default_value(new ConfigOptionEnum(ipAuto)); + + def = this->add("support_material_top_interface_pattern", coEnum); + def->label = L("Top Pattern"); + def->full_label = L("Support top interface pattern"); + def->category = OptionCategory::support; + def->tooltip = L("Pattern for the top interface layers." "\nNote that 'Hilbert', 'Ironing' and '(filled)' patterns are meant to be used with soluble supports and 100% fill interface layer."); def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); def->enum_values.push_back("auto"); @@ -5871,6 +5898,7 @@ void PrintConfigDef::init_fff_params() def->enum_labels.push_back(L("Ironing")); def->mode = comAdvancedE | comPrusa; def->set_default_value(new ConfigOptionEnum(ipRectilinear)); + def->aliases = {"support_material_interface_pattern"}; def = this->add("support_material_layer_height", coFloatOrPercent); def->label = L("Support layer height"); @@ -6513,6 +6541,14 @@ void PrintConfigDef::init_fff_params() def->aliases = { "wipe_tower_brim" }; // SuperSlicer 2.3 and before def->set_default_value(new ConfigOptionFloatOrPercent(2,false)); + def = this->add("wipe_tower_no_sparse_layers", coBool); + def->label = L("No sparse layers (EXPERIMENTAL)"); + def->category = OptionCategory::mmsetup; + def->tooltip = L("If enabled, the wipe tower will not be printed on layers with no toolchanges. " + "On layers with a toolchange, extruder will travel downward to print the wipe tower. " + "User is responsible for ensuring there is no collision with the print."); + def->mode = comAdvancedE | comPrusa; + def->set_default_value(new ConfigOptionBool(false)); def = this->add("wipe_tower_x", coFloat); def->label = L("X"); def->full_label = L("Wipe tower X"); @@ -6521,6 +6557,7 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvancedE | comPrusa; def->set_default_value(new ConfigOptionFloat(180.)); + def = this->add("wipe_tower_y", coFloat); def->label = L("Y"); def->full_label = L("Wipe tower Y"); @@ -8015,17 +8052,19 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va // In PrusaSlicer 2.3.0-alpha0 the "monotonic" infill was introduced, which was later renamed to "monotonous". if (value == "monotonous" && (opt_key == "top_fill_pattern" || opt_key == "bottom_fill_pattern" || opt_key == "fill_pattern" - || opt_key == "solid_fill_pattern" || opt_key == "bridge_fill_pattern" || opt_key == "support_material_interface_pattern")) + || opt_key == "solid_fill_pattern" || opt_key == "bridge_fill_pattern" || opt_key == "support_material_interface_pattern")) { value = "monotonic"; + } // some changes has occurs between rectilineargapfill and monotonicgapfill. Set them at the right value for each type if (value == "rectilineargapfill" && (opt_key == "top_fill_pattern" || opt_key == "bottom_fill_pattern") ) value = "monotonicgapfill"; - if (opt_key == "fill_pattern" || opt_key == "support_material_interface_pattern") - if (value == "rectilineargapfill") + if (opt_key == "fill_pattern" || opt_key == "support_material_interface_pattern" || opt_key == "support_material_top_interface_pattern" || opt_key == "support_material_bottom_interface_pattern") { + if (value == "rectilineargapfill") { value = "rectilinear"; - else if (value == "monotonicgapfill") + } else if (value == "monotonicgapfill") { value = "monotonic"; - + } + } if (ignore.find(opt_key) != ignore.end()) { opt_key = ""; return; @@ -8035,9 +8074,9 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va opt_key = "raft_first_layer_density"; value = "100"; } - if (boost::starts_with(opt_key, "thin_perimeters") && value == "1") + if (boost::starts_with(opt_key, "thin_perimeters") && value == "1") { value = "100%"; - + } if ("fan_always_on" == opt_key) { if (value != "1") { //min_fan_speed is already converted to default_fan_speed, just has to deactivate it if not always_on @@ -8066,8 +8105,9 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va } } } - if (remove_unkown_keys) + if (remove_unkown_keys) { opt_key = ""; + } return; } } @@ -8247,13 +8287,13 @@ void _convert_from_prusa(CONFIG_CLASS& conf, const DynamicPrintConfig& global_co // set phony entries if (with_phony) { for (auto &[opt_key_width, opt_key_spacing] : - {std::pair{"extrusion_width", "extrusion_spacing"}, - std::pair{"perimeter_extrusion_width", "perimeter_extrusion_spacing"}, - std::pair{"external_perimeter_extrusion_width", "external_perimeter_extrusion_spacing"}, - std::pair{"first_layer_extrusion_width", "first_layer_extrusion_spacing"}, - std::pair{"infill_extrusion_width", "infill_extrusion_spacing"}, - std::pair{"solid_infill_extrusion_width", "solid_infill_extrusion_spacing"}, - std::pair{"top_infill_extrusion_width", "top_infill_extrusion_spacing"}}) { + {std::pair{"extrusion_width", "extrusion_spacing"}, + std::pair{"perimeter_extrusion_width", "perimeter_extrusion_spacing"}, + std::pair{"external_perimeter_extrusion_width", "external_perimeter_extrusion_spacing"}, + std::pair{"first_layer_extrusion_width", "first_layer_extrusion_spacing"}, + std::pair{"infill_extrusion_width", "infill_extrusion_spacing"}, + std::pair{"solid_infill_extrusion_width", "solid_infill_extrusion_spacing"}, + std::pair{"top_infill_extrusion_width", "top_infill_extrusion_spacing"}}) { // if prusa has defined a width, or if the conf has a default spacing that need to be overwritten if (conf.option(opt_key_width) != nullptr || conf.option(opt_key_spacing) != nullptr) { ConfigOption *opt_new = print_config_def.get(opt_key_spacing)->default_value.get()->clone(); @@ -8354,13 +8394,13 @@ void _deserialize_maybe_from_prusa(const std::map{"extrusion_width", "extrusion_spacing"}, - std::pair{"perimeter_extrusion_width", "perimeter_extrusion_spacing"}, - std::pair{"external_perimeter_extrusion_width", "external_perimeter_extrusion_spacing"}, - std::pair{"first_layer_extrusion_width", "first_layer_extrusion_spacing"}, - std::pair{"infill_extrusion_width", "infill_extrusion_spacing"}, - std::pair{"solid_infill_extrusion_width", "solid_infill_extrusion_spacing"}, - std::pair{"top_infill_extrusion_width", "top_infill_extrusion_spacing"}}) { + {std::pair{"extrusion_width", "extrusion_spacing"}, + std::pair{"perimeter_extrusion_width", "perimeter_extrusion_spacing"}, + std::pair{"external_perimeter_extrusion_width", "external_perimeter_extrusion_spacing"}, + std::pair{"first_layer_extrusion_width", "first_layer_extrusion_spacing"}, + std::pair{"infill_extrusion_width", "infill_extrusion_spacing"}, + std::pair{"solid_infill_extrusion_width", "solid_infill_extrusion_spacing"}, + std::pair{"top_infill_extrusion_width", "top_infill_extrusion_spacing"}}) { const ConfigOption *opt_width = config.option(opt_key_width); const ConfigOption *opt_spacing = config.option(opt_key_spacing); if (opt_width && opt_spacing) { @@ -8587,6 +8627,7 @@ std::unordered_set prusa_export_to_remove_keys = { "perimeter_reverse", "perimeter_round_corners", "perimeters_hole", +"priming_position", "print_extrusion_multiplier", "print_first_layer_temperature", "print_custom_variables", @@ -8642,7 +8683,7 @@ std::unordered_set prusa_export_to_remove_keys = { "support_material_interface_angle_increment", "support_material_interface_fan_speed", "support_material_interface_layer_height", -"support_material_interface_pattern", +"support_material_bottom_interface_pattern", "support_material_layer_height", "thin_perimeters_all", "thin_perimeters", @@ -8725,9 +8766,16 @@ std::map PrintConfigDef::to_prusa(t_config_option_key& && ("fill_pattern" == opt_key || "top_fill_pattern" == opt_key)) { value = "alignedrectilinear"; } + if ("support_material_top_interface_pattern" == opt_key) { + opt_key = "support_material_interface_pattern"; + } } else if ("seam_position" == opt_key) { if ("cost" == value) { value = "nearest"; + }else if ("allrandom" == value) { + value = "random"; + }else if ("contiguous" == value) { + value = "aligned"; } } else if ("first_layer_size_compensation" == opt_key) { opt_key = "elefant_foot_compensation"; @@ -8740,13 +8788,14 @@ std::map PrintConfigDef::to_prusa(t_config_option_key& } } else if ("elephant_foot_min_width" == opt_key) { opt_key = "elefant_foot_min_width"; - } else if("first_layer_acceleration" == opt_key) { + } else if("first_layer_acceleration" == opt_key || "first_layer_acceleration_over_raft" == opt_key) { if (value.find("%") != std::string::npos) { // can't support %, so we uese the default accel a baseline for half-assed conversion value = std::to_string(all_conf.get_abs_value(opt_key, all_conf.get_computed_value("default_acceleration"))); } } else if ("infill_acceleration" == opt_key || "bridge_acceleration" == opt_key || "default_acceleration" == opt_key || "perimeter_acceleration" == opt_key - || "overhangs_speed" == opt_key || "ironing_speed" == opt_key || "perimeter_speed" == opt_key || "infill_speed" == opt_key || "bridge_speed" == opt_key || "support_material_speed" == opt_key + || "overhangs_speed" == opt_key || "ironing_speed" == opt_key || "perimeter_speed" == opt_key + || "infill_speed" == opt_key || "bridge_speed" == opt_key || "support_material_speed" == opt_key || "max_print_speed" == opt_key ) { // remove '%' @@ -8866,6 +8915,20 @@ std::map PrintConfigDef::to_prusa(t_config_option_key& new_entries["fan_always_on"] = "1"; } } + + // compute max & min height from % to flat value + if ("min_layer_height" == opt_key || "max_layer_height" == opt_key) { + ConfigOptionFloats computed_opt; + const ConfigOptionFloatsOrPercents *current_opt = all_conf.option(opt_key); + const ConfigOptionFloats *nozzle_diameters = all_conf.option("nozzle_diameter"); + assert(current_opt && nozzle_diameters); + assert(current_opt->size() == nozzle_diameters->size()); + for (int i = 0; i < current_opt->size(); i++) { + computed_opt.set_at(current_opt->get_abs_value(i, nozzle_diameters->get_at(i)), i); + } + assert(computed_opt.size() == nozzle_diameters->size()); + value = computed_opt.serialize(); + } if ("thumbnails" == opt_key) { // add format to thumbnails @@ -8877,8 +8940,6 @@ std::map PrintConfigDef::to_prusa(t_config_option_key& std::string coma = ""; for (std::string &size : sizes) { //if first or second dimension is 0: ignore. - size_t test1 = size.find("0x"); - size_t test2 = size.find("x0"); if (size.find("0x") == 0 || size.find("x0") + 2 == size.size()) continue; assert(size.find('/') == std::string::npos); @@ -8928,8 +8989,10 @@ DynamicPrintConfig* DynamicPrintConfig::new_from_defaults_keys(const std::vector const ConfigOption *MultiPtrPrintConfig::optptr(const t_config_option_key &opt_key) const { - for (auto conf : storages) { + for (ConfigBase *conf : storages) { +#ifdef _DEBUG assert(conf->exists()); +#endif const ConfigOption *opt = conf->optptr(opt_key); if (opt) return opt; @@ -8939,8 +9002,10 @@ const ConfigOption *MultiPtrPrintConfig::optptr(const t_config_option_key &opt_k ConfigOption *MultiPtrPrintConfig::optptr(const t_config_option_key &opt_key, bool create) { assert(!create); - for (auto conf : storages) { + for (ConfigBase *conf : storages) { +#ifdef _DEBUG assert(conf->exists()); +#endif ConfigOption *opt = conf->optptr(opt_key); if (opt) return opt; @@ -8952,8 +9017,10 @@ t_config_option_keys MultiPtrPrintConfig::keys() const assert(false); // shouldn't need ot call that t_config_option_keys keys; - for (auto conf : storages) { + for (ConfigBase *conf : storages) { +#ifdef _DEBUG assert(conf->exists()); +#endif append(keys, conf->keys()); } return keys; @@ -9025,7 +9092,7 @@ double min_object_distance(const ConfigBase *config, double ref_height /* = 0*/) double base_dist = 0; //std::cout << "START min_object_distance =>" << base_dist << "\n"; const ConfigOptionBool* co_opt = config->option("complete_objects"); - if (config->option("parallel_objects_step")->get_float() > 0 || co_opt && co_opt->value) { + if ((config->option("parallel_objects_step")->get_float() > 0) || (co_opt && co_opt->value)) { double skirt_dist = 0; try { std::vector vals = dynamic_cast(config->option("nozzle_diameter"))->get_values(); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 0efff1c1491..e2896730a88 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -787,6 +787,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloatOrPercent, support_material_contact_distance)) // 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)) ((ConfigOptionInt, support_material_enforce_layers)) ((ConfigOptionInt, support_material_extruder)) ((ConfigOptionFloatOrPercent, support_material_extrusion_width)) @@ -800,7 +801,6 @@ PRINT_CONFIG_CLASS_DEFINE( // Spacing between interface lines (the hatching distance). Set zero to get a solid interface. ((ConfigOptionFloat, support_material_interface_spacing)) ((ConfigOptionFloatOrPercent, support_material_interface_speed)) - ((ConfigOptionEnum, support_material_interface_pattern)) ((ConfigOptionEnum, support_material_pattern)) // Morphological closing of support areas. Only used for "sung" supports. ((ConfigOptionFloat, support_material_closing_radius)) @@ -812,6 +812,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionBool, support_material_synchronize_layers)) // Overhang angle threshold. ((ConfigOptionInt, support_material_threshold)) + ((ConfigOptionEnum, support_material_top_interface_pattern)) ((ConfigOptionBool, support_material_with_sheath)) ((ConfigOptionFloatOrPercent, support_material_xy_spacing)) ((ConfigOptionBool, thin_walls_merge)) @@ -1257,6 +1258,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionInts, overhangs_fan_speed)) ((ConfigOptionInts, perimeter_fan_speed)) ((ConfigOptionStrings, post_process)) + ((ConfigOptionPoint, priming_position)) ((ConfigOptionString, print_custom_variables)) ((ConfigOptionString, printer_custom_variables)) ((ConfigOptionString, printer_model)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 207e84c48c2..93c8d3d93e6 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -838,14 +838,15 @@ FillLightning::GeneratorPtr PrintObject::prepare_lightning_infill_data() || opt_key == "support_material_extrusion_width" || opt_key == "support_material_interface_layers" || opt_key == "support_material_bottom_interface_layers" + || opt_key == "support_material_bottom_interface_pattern" || opt_key == "support_material_interface_angle" || opt_key == "support_material_interface_angle_increment" || opt_key == "support_material_interface_contact_loops" || opt_key == "support_material_interface_extruder" - || opt_key == "support_material_interface_pattern" || opt_key == "support_material_interface_spacing" || opt_key == "support_material_pattern" || opt_key == "support_material_style" + || opt_key == "support_material_top_interface_pattern" || opt_key == "support_material_xy_spacing" || opt_key == "support_material_spacing" || opt_key == "support_material_closing_radius" diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index f947923285a..692f13ee7ad 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -19,7 +19,7 @@ #include #include #include - +#pragma optimize("", off) #define SUPPORT_USE_AGG_RASTERIZER #ifdef SUPPORT_USE_AGG_RASTERIZER @@ -427,14 +427,26 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object support_pattern == smpHoneycomb ? ipHoneycomb : m_support_params.support_density > 0.95 || m_support_params.with_sheath ? ipRectilinear : ipSupportBase; m_support_params.interface_fill_pattern = (m_support_params.interface_density > 0.95 ? ipRectilinear : ipSupportBase); - m_support_params.contact_fill_pattern = m_object_config->support_material_interface_pattern; - if (m_support_params.contact_fill_pattern == ipAuto) + m_support_params.contact_top_fill_pattern = m_object_config->support_material_top_interface_pattern; + m_support_params.contact_bottom_fill_pattern = m_object_config->support_material_bottom_interface_pattern; + if (m_support_params.contact_top_fill_pattern == ipAuto) if (m_slicing_params->soluble_interface) - m_support_params.contact_fill_pattern = ipConcentric; + m_support_params.contact_top_fill_pattern = ipConcentric; + else if (m_support_params.interface_density > 0.95) + m_support_params.contact_top_fill_pattern = ipRectilinear; + else + m_support_params.contact_top_fill_pattern = ipSupportBase; + if (m_support_params.contact_bottom_fill_pattern == ipAuto) + if(m_support_params.contact_top_fill_pattern != ipHilbertCurve + && m_support_params.contact_top_fill_pattern != ipSmooth + && m_support_params.contact_top_fill_pattern != ipSawtooth) + m_support_params.contact_bottom_fill_pattern = m_support_params.contact_top_fill_pattern; + else if (m_slicing_params->soluble_interface) + m_support_params.contact_bottom_fill_pattern = ipConcentric; else if (m_support_params.interface_density > 0.95) - m_support_params.contact_fill_pattern = ipRectilinear; + m_support_params.contact_bottom_fill_pattern = ipRectilinear; else - m_support_params.contact_fill_pattern = ipSupportBase; + m_support_params.contact_bottom_fill_pattern = ipSupportBase; } // Using the std::deque as an allocator. @@ -3912,10 +3924,10 @@ void modulate_extrusion_by_overlapping_layers( const PrintObjectSupportMaterial::MyLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; bbox.merge(get_extents(overlapping_layer.polygons)); } - for (ExtrusionEntitiesPtr::const_iterator it = extrusions_in_out.begin(); it != extrusions_in_out.end(); ++ it) { + for (ExtrusionEntitiesPtr::const_iterator it = extrusions_in_out.set_entities().begin(); it != extrusions_in_out.set_entities().end(); ++ it) { ExtrusionPath *path = dynamic_cast(*it); assert(path != nullptr); - bbox.merge(get_extents(path->polyline)); + bbox.merge(get_extents(path->polyline.as_polyline())); } SVG svg(debug_out_path("support-fragments-%d-%lf.svg", iRun, this_layer.print_z).c_str(), bbox); const float transparency = 0.5f; @@ -3932,10 +3944,10 @@ void modulate_extrusion_by_overlapping_layers( svg.draw(to_polylines(overlapping_layer.polygons), dbg_index_to_color(int(i_overlapping_layer)), scale_(0.1)); } // Fill extrusion, the source. - for (ExtrusionEntitiesPtr::const_iterator it = extrusions_in_out.begin(); it != extrusions_in_out.end(); ++ it) { + for (ExtrusionEntitiesPtr::const_iterator it = extrusions_in_out.set_entities().begin(); it != extrusions_in_out.set_entities().end(); ++ it) { ExtrusionPath *path = dynamic_cast(*it); std::string color_name; - switch ((it - extrusions_in_out.begin()) % 9) { + switch ((it - extrusions_in_out.set_entities().begin()) % 9) { case 0: color_name = "magenta"; break; case 1: color_name = "deepskyblue"; break; case 2: color_name = "coral"; break; @@ -3946,7 +3958,7 @@ void modulate_extrusion_by_overlapping_layers( case 7: color_name = "brown"; break; default: color_name = "orchid"; break; } - svg.draw(path->polyline, color_name, scale_(0.2)); + svg.draw(path->polyline.as_polyline(), color_name, scale_(0.2)); } #endif /* SLIC3R_DEBUG */ @@ -4167,14 +4179,16 @@ void PrintObjectSupportMaterial::generate_toolpaths( SupportLayer &support_layer = *support_layers[support_layer_id]; assert(support_layer.support_fills.entities().empty()); MyLayer &raft_layer = *raft_layers[support_layer_id]; - - std::unique_ptr filler_interface = std::unique_ptr(Fill::new_from_type(m_support_params.contact_fill_pattern)); //m_support_params.interface_fill_pattern)); FIXME choose - std::unique_ptr filler_support = std::unique_ptr(Fill::new_from_type(m_support_params.base_fill_pattern)); + + std::unique_ptr filler_top_interface = std::unique_ptr(Fill::new_from_type(m_support_params.contact_top_fill_pattern)); + std::unique_ptr filler_support = std::unique_ptr(Fill::new_from_type(m_support_params.base_fill_pattern)); std::unique_ptr filler_support_with_sheath = std::make_unique((Fill::new_from_type(m_support_params.base_fill_pattern))); + filler_support_with_sheath->overlap = m_support_params.gap_xy == 0 ? 0 : 1; // allow periemter overlap is not touching perimeters filler_support_with_sheath->ratio_fill_inside = 0.2f; std::unique_ptr filler_dense = std::make_unique((Fill::new_from_type(ipRectilinear))); + filler_dense->overlap = m_support_params.gap_xy == 0 ? 0 : 1; filler_dense->ratio_fill_inside = 0.2f; - filler_interface->set_bounding_box(bbox_object); + filler_top_interface->set_bounding_box(bbox_object); filler_support->set_bounding_box(bbox_object); // Print the support base below the support columns, or the support base for the support columns plus the contacts. @@ -4205,7 +4219,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( } } - Fill *filler = filler_interface.get(); + Fill *filler = filler_top_interface.get(); Flow flow = m_support_params.first_layer_flow; float density = 0.f; double spacing = 0.f; @@ -4281,7 +4295,8 @@ void PrintObjectSupportMaterial::generate_toolpaths( size_t idx_layer_intermediate = size_t(-1); size_t idx_layer_interface = size_t(-1); size_t idx_layer_base_interface = size_t(-1); - std::unique_ptr filler_interface = std::unique_ptr(Fill::new_from_type(m_support_params.contact_fill_pattern)); + std::unique_ptr filler_top_interface = std::unique_ptr(Fill::new_from_type(m_support_params.contact_top_fill_pattern)); + std::unique_ptr filler_bottom_interface = std::unique_ptr(Fill::new_from_type(m_support_params.contact_bottom_fill_pattern)); std::unique_ptr filler_intermediate_interface = std::unique_ptr(Fill::new_from_type(ipRectilinear)); // Filler for the base interface (to be used for soluble interface / non soluble base, to produce non soluble interface layer below soluble interface layer). std::unique_ptr filler_base_interface = std::unique_ptr(base_interface_layers.empty() ? nullptr : @@ -4294,7 +4309,8 @@ void PrintObjectSupportMaterial::generate_toolpaths( } else { filler_support.reset(Fill::new_from_type(m_support_params.base_fill_pattern)); } - filler_interface->set_bounding_box(bbox_object); + filler_top_interface->set_bounding_box(bbox_object); + filler_bottom_interface->set_bounding_box(bbox_object); filler_intermediate_interface->set_bounding_box(bbox_object); if (range.begin() == 0) filler_first_layer_ptr->set_bounding_box(bbox_object); @@ -4397,7 +4413,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( Flow interface_flow = layer_ex.layer->bridging ? Flow::bridging_flow(layer_ex.layer->height, m_support_params.support_material_bottom_interface_flow.nozzle_diameter()) : (interface_as_base ? &m_support_params.support_material_flow : &m_support_params.support_material_interface_flow)->with_height(float(layer_ex.layer->height)); - Fill *filler = i == 2 ? filler_intermediate_interface.get() : filler_interface.get(); + Fill *filler = (i == 0) ? filler_top_interface.get() : (i == 1 ? filler_bottom_interface.get() : filler_intermediate_interface.get()); //filler->layer_id = support_layer_id; // don't do that, or the filler will rotate thigns from that layerid filler->z = support_layer.print_z; float supp_density = m_support_params.interface_density; @@ -4489,7 +4505,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( // Destination base_layer.extrusions.set_entities(), // Regions to fill - closing_ex(base_layer.polygons_to_extrude(), float(SCALED_EPSILON), float(SCALED_EPSILON + 0.5 * flow.scaled_width())), + closing_ex(base_layer.polygons_to_extrude(), float(SCALED_EPSILON), float(SCALED_EPSILON)), // Filler and its parameters filler, density, // Extrusion parameters diff --git a/src/libslic3r/SupportMaterial.hpp b/src/libslic3r/SupportMaterial.hpp index 448eeabded2..9a7551fd02e 100644 --- a/src/libslic3r/SupportMaterial.hpp +++ b/src/libslic3r/SupportMaterial.hpp @@ -149,7 +149,8 @@ class PrintObjectSupportMaterial InfillPattern base_fill_pattern; InfillPattern interface_fill_pattern; - InfillPattern contact_fill_pattern; + InfillPattern contact_top_fill_pattern; + InfillPattern contact_bottom_fill_pattern; bool with_sheath; }; diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 8b0cbe11f67..2471b601843 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -357,10 +357,11 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field("perimeter_bonding", config->opt_bool("external_perimeters_first") && !have_arachne && config->option("perimeter_overlap")->get_float() == 100.f && config->option("external_perimeter_overlap")->get_float() == 100.f); - for (auto el : {"perimeter_loop", "extra_perimeters_overhangs", "no_perimeter_unsupported_algo", + for (auto el : {"perimeter_loop", "extra_perimeters_overhangs", "thin_perimeters", "perimeter_round_corners"}) toggle_field(el, have_perimeters && !have_arachne); - + + toggle_field("no_perimeter_unsupported_algo", have_perimeters); toggle_field("only_one_perimeter_top", have_perimeters); toggle_field("only_one_perimeter_first_layer", config->opt_int("perimeters") > 1); toggle_field("overhangs_width", config->option("overhangs_width_speed")->value > 0); @@ -501,7 +502,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) for (auto el : { "support_material_contact_distance", "support_material_bottom_contact_distance" }) toggle_field(el, have_support_material && !have_support_soluble); - for (auto el : { "support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_extruder", + for (auto el : { "support_material_bottom_interface_pattern", "support_material_top_interface_pattern", "support_material_interface_spacing", "support_material_interface_extruder", "support_material_interface_speed", "support_material_interface_contact_loops", "support_material_interface_layer_height" "support_material_interface_angle", "support_material_interface_angle_increment"}) toggle_field(el, have_support_material && have_support_interface); @@ -568,7 +569,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field(el, (has_top_solid_infill && config->option>("top_fill_pattern")->value == InfillPattern::ipSmooth) || (has_bottom_solid_infill && config->option>("bottom_fill_pattern")->value == InfillPattern::ipSmooth) || (has_solid_infill && config->option>("solid_fill_pattern")->value == InfillPattern::ipSmooth) - || (have_support_material && config->option>("support_material_interface_pattern")->value == InfillPattern::ipSmooth)); + || (have_support_material && config->option>("support_material_top_interface_pattern")->value == InfillPattern::ipSmooth) + || (have_support_material && config->option>("support_material_bottom_interface_pattern")->value == InfillPattern::ipSmooth)); //TODO: can the milling_diameter or the milling_cutter be check to enable/disable this? for (auto el : { "milling_after_z", "milling_extra_size", "milling_speed" }) diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index e603dfdd46a..40c59dc5e50 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -172,7 +172,8 @@ static void add_config_substitutions(const ConfigSubstitutions& conf_substitutio def->opt_key == "bottom_fill_pattern" || def->opt_key == "solid_fill_pattern" || def->opt_key == "bridge_fill_pattern" || - def->opt_key == "support_material_interface_pattern" || + def->opt_key == "support_material_top_interface_pattern" || + def->opt_key == "support_material_bottom_interface_pattern" || def->opt_key == "brim_ears_pattern" || def->opt_key == "fill_pattern"; diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 1c944dc7785..74a01993080 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -54,7 +54,7 @@ static SettingsFactory::Bundle FREQ_SETTINGS_BUNDLE_FFF = { OptionCategory::perimeter , { "layer_height" , "perimeters", "top_solid_layers", "bottom_solid_layers" } }, { OptionCategory::infill , { "fill_density", "fill_pattern", "fill_angle" } }, { OptionCategory::support , { "support_material", "support_material_auto", "support_material_threshold", - "support_material_pattern", "support_material_interface_pattern", "support_material_buildplate_only", + "support_material_pattern", "support_material_bottom_interface_pattern", "support_material_top_interface_pattern", "support_material_buildplate_only", "support_material_spacing" } }, { OptionCategory::wipe , { "wipe_into_infill", "wipe_into_objects" } } }; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 02968639dc4..eaebce1c241 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -632,9 +632,15 @@ wxBoxSizer* Preview::create_layers_slider_sizer() Bind(DoubleSlider::wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) { Model& model = wxGetApp().plater()->model(); Info custom_gcode_per_print_z = m_layers_slider->GetTicksValues(); + auto num_of_old_gcodes = model.custom_gcode_per_print_z.gcodes.size(); model.custom_gcode_per_print_z = custom_gcode_per_print_z; - m_schedule_background_process(); + std::string operation_message = (num_of_old_gcodes == model.custom_gcode_per_print_z.gcodes.size()) ? "Edit" : + (num_of_old_gcodes < model.custom_gcode_per_print_z.gcodes.size()) ? "Add" : + "Remove"; + m_schedule_background_process(); + wxGetApp().plater()->take_snapshot(from_u8( + (boost::format(_utf8(L("Change layer gcode: %s element"))) % operation_message).str())); m_keep_current_preview_type = false; reload_print(false); }); diff --git a/src/slic3r/GUI/GraphDialog.cpp b/src/slic3r/GUI/GraphDialog.cpp index f9f63e9e7ea..1053aee5500 100644 --- a/src/slic3r/GUI/GraphDialog.cpp +++ b/src/slic3r/GUI/GraphDialog.cpp @@ -161,7 +161,7 @@ GraphPanel::GraphPanel(wxWindow *parent, GraphData data, const GraphSettings &se //stream.get(); // min & max - Pointfs &data_points = data.data(); + Pointfs data_points = data.data(); if (!data_points.empty()) { for (Vec2d &point : data_points) { m_last_min_y = std::min(m_last_min_y, (point.y())); diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index 40bea57da32..18ac0456057 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -584,18 +584,6 @@ const Option& OptionsSearcher::get_option(const std::string& opt_key, Preset::Ty size_t pos_hash = opt_key.find('#'); if (pos_hash == std::string::npos) { auto it = std::lower_bound(options.begin(), options.end(), Option({ boost::nowide::widen(opt_key), type, idx })); -#ifdef _DEBUG - if (options[it - options.begin()].opt_key_with_idx() != opt_key) { - std::wstring wopt_key = boost::nowide::widen(opt_key); - for (const Option &opt : options) { - if (opt.key == wopt_key) { - if (opt.type == type) { - std::cout << "found\n"; - } - } - } - } -#endif assert(it != options.end()); return options[it - options.begin()]; } else { @@ -603,17 +591,6 @@ const Option& OptionsSearcher::get_option(const std::string& opt_key, Preset::Ty std::string opt_idx = opt_key.substr(pos_hash + 1); idx = atoi(opt_idx.c_str()); auto it = std::lower_bound(options.begin(), options.end(), Option({ boost::nowide::widen(raw_opt_key), type, idx })); -#ifdef _DEBUG - if (options[it - options.begin()].opt_key_with_idx() != opt_key) { - for (const Option &opt : options) { - if (opt.opt_key_with_idx() == opt_key) { - if (opt.type == type) { - std::cout << "found\n"; - } - } - } - } -#endif assert(it != options.end()); return options[it - options.begin()]; }