diff options
author | Ghostkeeper <rubend@tutanota.com> | 2022-03-30 12:44:53 +0300 |
---|---|---|
committer | Ghostkeeper <rubend@tutanota.com> | 2022-03-30 12:44:53 +0300 |
commit | 00c4f2754f1c2ea08603cbef2f0842e5a9232212 (patch) | |
tree | 0cd5cfb18db9e2ef2a116444d5612b8df76db96f | |
parent | 12993359f5585df80cd00852c2eb2128e81ff60e (diff) | |
parent | 9cf1833155cf43f775a2212c2a25235ce3d9e077 (diff) |
Merge branch 'master' into VariableWidthPaths_rename
Conflicts:
src/infill.cpp -> Modifications to fix density of concentric infill, while a typedef was removed in this branch.
Contributes to issue CURA-8998.
-rw-r--r-- | CMakeLists.txt | 10 | ||||
-rw-r--r-- | src/BeadingStrategy/BeadingStrategyFactory.cpp | 19 | ||||
-rw-r--r-- | src/BeadingStrategy/BeadingStrategyFactory.h | 12 | ||||
-rw-r--r-- | src/BeadingStrategy/CenterDeviationBeadingStrategy.cpp | 77 | ||||
-rw-r--r-- | src/BeadingStrategy/CenterDeviationBeadingStrategy.h | 43 | ||||
-rw-r--r-- | src/FffGcodeWriter.cpp | 9 | ||||
-rw-r--r-- | src/InsetOrderOptimizer.cpp | 18 | ||||
-rw-r--r-- | src/LayerPlan.cpp | 88 | ||||
-rw-r--r-- | src/LayerPlan.h | 69 | ||||
-rw-r--r-- | src/PathOrderOptimizer.h | 21 | ||||
-rw-r--r-- | src/WallToolPaths.cpp | 3 | ||||
-rw-r--r-- | src/WallToolPaths.h | 1 | ||||
-rw-r--r-- | src/gcodeExport.cpp | 3 | ||||
-rw-r--r-- | src/infill.cpp | 28 | ||||
-rw-r--r-- | src/pathPlanning/Comb.cpp | 26 | ||||
-rw-r--r-- | src/pathPlanning/GCodePath.cpp | 9 | ||||
-rw-r--r-- | src/pathPlanning/GCodePath.h | 6 | ||||
-rw-r--r-- | src/settings/Settings.cpp | 29 | ||||
-rw-r--r-- | src/utils/ExtrusionLine.cpp | 18 | ||||
-rw-r--r-- | tests/ExtruderPlanTest.cpp | 75 | ||||
-rw-r--r-- | tests/WallsComputationTest.cpp | 3 | ||||
-rw-r--r-- | tests/beading_strategy/CenterDeviationBeadingStrategyTest.cpp | 153 | ||||
-rw-r--r-- | tests/utils/ExtrusionLineTest.cpp | 36 |
23 files changed, 239 insertions, 517 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 039693de9..61fc83b30 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -182,7 +182,6 @@ set(engine_SRCS # Except main.cpp. src/BeadingStrategy/BeadingStrategy.cpp src/BeadingStrategy/BeadingStrategyFactory.cpp - src/BeadingStrategy/CenterDeviationBeadingStrategy.cpp src/BeadingStrategy/DistributedBeadingStrategy.cpp src/BeadingStrategy/LimitedBeadingStrategy.cpp src/BeadingStrategy/RedistributeBeadingStrategy.cpp @@ -267,9 +266,6 @@ if (ENABLE_ARCUS) ArcusCommunicationPrivateTest ) endif () -set(engine_TEST_BEADING_STRATEGY - CenterDeviationBeadingStrategyTest -) set(engine_TEST_INFILL ) set(engine_TEST_INTEGRATION @@ -383,12 +379,6 @@ if (BUILD_TESTS) add_dependencies(build_all_tests ${test}) #Make sure that this gets built as part of the build_all_tests target. endforeach() endif() - foreach(test ${engine_TEST_BEADING_STRATEGY}) - add_executable(${test} tests/main.cpp tests/beading_strategy/${test}.cpp) - target_link_libraries(${test} _CuraEngine ${GTEST_BOTH_LIBRARIES} ${GMOCK_BOTH_LIBRARIES}) - add_test(NAME ${test} COMMAND "${test}" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/tests/") - add_dependencies(build_all_tests ${test}) #Make sure that this gets built as part of the build_all_tests target. - endforeach() foreach (test ${engine_TEST_INFILL}) add_executable(${test} tests/main.cpp tests/infill/${test}.cpp) target_link_libraries(${test} _CuraEngine ${GTEST_BOTH_LIBRARIES} ${GMOCK_BOTH_LIBRARIES}) diff --git a/src/BeadingStrategy/BeadingStrategyFactory.cpp b/src/BeadingStrategy/BeadingStrategyFactory.cpp index 8f07d668c..5e90b1cdb 100644 --- a/src/BeadingStrategy/BeadingStrategyFactory.cpp +++ b/src/BeadingStrategy/BeadingStrategyFactory.cpp @@ -4,7 +4,6 @@ #include "BeadingStrategyFactory.h" #include "LimitedBeadingStrategy.h" -#include "CenterDeviationBeadingStrategy.h" #include "WideningBeadingStrategy.h" #include "DistributedBeadingStrategy.h" #include "RedistributeBeadingStrategy.h" @@ -17,7 +16,6 @@ namespace cura BeadingStrategyPtr BeadingStrategyFactory::makeStrategy ( - const StrategyType type, const coord_t preferred_bead_width_outer, const coord_t preferred_bead_width_inner, const coord_t preferred_transition_length, @@ -35,22 +33,7 @@ BeadingStrategyPtr BeadingStrategyFactory::makeStrategy { using std::make_unique; using std::move; - BeadingStrategyPtr ret; - switch (type) - { - case StrategyType::Center: - ret = make_unique<CenterDeviationBeadingStrategy>(preferred_bead_width_inner, transitioning_angle, wall_split_middle_threshold, wall_add_middle_threshold); - break; - case StrategyType::Distributed: - ret = make_unique<DistributedBeadingStrategy>(preferred_bead_width_inner, preferred_transition_length, transitioning_angle, wall_split_middle_threshold, wall_add_middle_threshold, std::numeric_limits<int>::max()); - break; - case StrategyType::InwardDistributed: - ret = make_unique<DistributedBeadingStrategy>(preferred_bead_width_inner, preferred_transition_length, transitioning_angle, wall_split_middle_threshold, wall_add_middle_threshold, inward_distributed_center_wall_count); - break; - default: - logError("Cannot make strategy!\n"); - return nullptr; - } + BeadingStrategyPtr ret = make_unique<DistributedBeadingStrategy>(preferred_bead_width_inner, preferred_transition_length, transitioning_angle, wall_split_middle_threshold, wall_add_middle_threshold, inward_distributed_center_wall_count); logDebug("Applying the Redistribute meta-strategy with outer-wall width = %d, inner-wall width = %d\n", preferred_bead_width_outer, preferred_bead_width_inner); ret = make_unique<RedistributeBeadingStrategy>(preferred_bead_width_outer, minimum_variable_line_ratio, move(ret)); diff --git a/src/BeadingStrategy/BeadingStrategyFactory.h b/src/BeadingStrategy/BeadingStrategyFactory.h index b0fa5bb64..55cfe6d2a 100644 --- a/src/BeadingStrategy/BeadingStrategyFactory.h +++ b/src/BeadingStrategy/BeadingStrategyFactory.h @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Ultimaker B.V. +// Copyright (c) 2022 Ultimaker B.V. // CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef BEADING_STRATEGY_FACTORY_H @@ -10,21 +10,11 @@ namespace cura { -enum class StrategyType -{ - Center, - Distributed, - InwardDistributed, - None, - COUNT -}; - class BeadingStrategyFactory { public: static BeadingStrategyPtr makeStrategy ( - const StrategyType type, const coord_t preferred_bead_width_outer = MM2INT(0.5), const coord_t preferred_bead_width_inner = MM2INT(0.5), const coord_t preferred_transition_length = MM2INT(0.4), diff --git a/src/BeadingStrategy/CenterDeviationBeadingStrategy.cpp b/src/BeadingStrategy/CenterDeviationBeadingStrategy.cpp deleted file mode 100644 index e8433bd7f..000000000 --- a/src/BeadingStrategy/CenterDeviationBeadingStrategy.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) 2022 Ultimaker B.V. -// CuraEngine is released under the terms of the AGPLv3 or higher. -#include <algorithm> - -#include "CenterDeviationBeadingStrategy.h" - -namespace cura -{ -CenterDeviationBeadingStrategy::CenterDeviationBeadingStrategy -( - const coord_t pref_bead_width, - const AngleRadians transitioning_angle, - const Ratio wall_split_middle_threshold, - const Ratio wall_add_middle_threshold -) : - BeadingStrategy(pref_bead_width, wall_split_middle_threshold, wall_add_middle_threshold, pref_bead_width / 2, transitioning_angle) -{ - name = "CenterDeviationBeadingStrategy"; -} - -CenterDeviationBeadingStrategy::Beading CenterDeviationBeadingStrategy::compute(coord_t thickness, coord_t bead_count) const -{ - Beading ret; - - ret.total_thickness = thickness; - if (bead_count > 0) - { - // Set the bead widths - ret.bead_widths = std::vector<coord_t>(static_cast<size_t>(bead_count), optimal_width); - coord_t leftover_thickness = ret.total_thickness; - for (size_t bead_index = 0; bead_index <= bead_count / 2; ++bead_index) - { - const size_t opposite_bead_index = bead_count - (1 + bead_index); - switch (opposite_bead_index - bead_index) - { - case 0: // Single bead in the middle: - ret.bead_widths[bead_index] = leftover_thickness; - break; - case 1: // Two beads in the middle: - ret.bead_widths[bead_index] = leftover_thickness / 2; - ret.bead_widths[opposite_bead_index] = leftover_thickness / 2; - break; - default: // Beads on the outside. - ret.bead_widths[bead_index] = optimal_width; - ret.bead_widths[opposite_bead_index] = optimal_width; - break; - } - leftover_thickness -= ret.bead_widths[bead_index] + ret.bead_widths[opposite_bead_index]; // Incorrect when there is a last single middle line, but the loop will exit anyway then. - } - - // Set the center line location of the bead toolpaths. - ret.toolpath_locations.resize(ret.bead_widths.size()); - ret.toolpath_locations.front() = ret.bead_widths.front() / 2; - for (size_t bead_idx = 1; bead_idx < ret.bead_widths.size(); ++bead_idx) - { - ret.toolpath_locations[bead_idx] = - ret.toolpath_locations[bead_idx - 1] + (ret.bead_widths[bead_idx] + ret.bead_widths[bead_idx - 1]) / 2; - } - ret.left_over = 0; - } - else - { - ret.left_over = thickness; - } - - return ret; -} - -coord_t CenterDeviationBeadingStrategy::getOptimalBeadCount(coord_t thickness) const -{ - const coord_t naive_count = thickness / optimal_width; // How many lines we can fit in for sure. - const coord_t remainder = thickness % optimal_width; // Space left after fitting that many lines. - const coord_t minimum_line_width = optimal_width * (naive_count % 2 == 1 ? wall_split_middle_threshold : wall_add_middle_threshold); - return naive_count + (remainder >= minimum_line_width); // If there's enough space, fit an extra one. -} - -} // namespace cura diff --git a/src/BeadingStrategy/CenterDeviationBeadingStrategy.h b/src/BeadingStrategy/CenterDeviationBeadingStrategy.h deleted file mode 100644 index 260cae72c..000000000 --- a/src/BeadingStrategy/CenterDeviationBeadingStrategy.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2022 Ultimaker B.V. -// CuraEngine is released under the terms of the AGPLv3 or higher. - -#ifndef CENTER_DEVIATION_BEADING_STRATEGY_H -#define CENTER_DEVIATION_BEADING_STRATEGY_H - -#include "../settings/types/Ratio.h" //For the wall transition threshold. -#include "BeadingStrategy.h" -#ifdef BUILD_TESTS -#include <gtest/gtest_prod.h> //Friend tests, so that they can inspect the privates. -#endif - -namespace cura -{ - -/*! - * This beading strategy makes the deviation in the thickness of the part - * entirely compensated by the innermost wall. - * - * The outermost walls all use the ideal width, as far as possible. - */ -class CenterDeviationBeadingStrategy : public BeadingStrategy -{ -#ifdef BUILD_TESTS - FRIEND_TEST(CenterDeviationBeadingStrategy, Construction); -#endif - - public: - CenterDeviationBeadingStrategy - ( - coord_t pref_bead_width, - AngleRadians transitioning_angle, - Ratio wall_split_middle_threshold, - Ratio wall_add_middle_threshold - ); - - ~CenterDeviationBeadingStrategy() override{}; - Beading compute(coord_t thickness, coord_t bead_count) const override; - coord_t getOptimalBeadCount(coord_t thickness) const override; -}; - -} // namespace cura -#endif // CENTER_DEVIATION_BEADING_STRATEGY_H diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 063721ba8..ae0abfffe 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -976,12 +976,13 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn coord_t max_inner_wall_width = 0; for (const SliceMeshStorage& mesh : storage.meshes) { - max_inner_wall_width = std::max(max_inner_wall_width, mesh.settings.get<coord_t>((mesh.settings.get<size_t>("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0")); - if (layer_nr == 0) + coord_t mesh_inner_wall_width = mesh.settings.get<coord_t>((mesh.settings.get<size_t>("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0"); + if(layer_nr == 0) { const ExtruderTrain& train = mesh.settings.get<ExtruderTrain&>((mesh.settings.get<size_t>("wall_line_count") > 1) ? "wall_0_extruder_nr" : "wall_x_extruder_nr"); - max_inner_wall_width *= train.settings.get<Ratio>("initial_layer_line_width_factor"); + mesh_inner_wall_width *= train.settings.get<Ratio>("initial_layer_line_width_factor"); } + max_inner_wall_width = std::max(max_inner_wall_width, mesh_inner_wall_width); } const coord_t comb_offset_from_outlines = max_inner_wall_width * 2; @@ -1453,7 +1454,7 @@ void FffGcodeWriter::addMeshPartToGCode(const SliceDataStorage& storage, const S { innermost_wall_line_width *= mesh.settings.get<Ratio>("initial_layer_line_width_factor"); } - gcode_layer.moveInsideCombBoundary(innermost_wall_line_width); + gcode_layer.moveInsideCombBoundary(innermost_wall_line_width, part); } gcode_layer.setIsInside(false); diff --git a/src/InsetOrderOptimizer.cpp b/src/InsetOrderOptimizer.cpp index 14a1a647c..f606c13bb 100644 --- a/src/InsetOrderOptimizer.cpp +++ b/src/InsetOrderOptimizer.cpp @@ -54,7 +54,6 @@ bool InsetOrderOptimizer::addToLayer() // Settings & configs: const bool pack_by_inset = ! settings.get<bool>("optimize_wall_printing_order"); const InsetDirection inset_direction = settings.get<InsetDirection>("inset_direction"); - const bool center_last = inset_direction == InsetDirection::CENTER_LAST; const bool alternate_walls = settings.get<bool>("material_alternate_walls"); const bool outer_to_inner = inset_direction == InsetDirection::OUTSIDE_IN; @@ -129,23 +128,6 @@ bool InsetOrderOptimizer::addToLayer() getInsetOrder(walls_to_be_added, outer_to_inner) : getRegionOrder(walls_to_be_added, outer_to_inner); - if (center_last) - { - for (const ExtrusionLine* line : walls_to_be_added) - { - if (line->is_odd) - { - for (const ExtrusionLine* other_line : walls_to_be_added) - { - if ( ! other_line->is_odd) - { - order.emplace(std::make_pair(other_line, line)); - } - } - } - } - } - constexpr Ratio flow = 1.0_r; bool added_something = false; diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 1fe0d3c47..159c72ab0 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -1,4 +1,4 @@ -//Copyright (c) 2021 Ultimaker B.V. +//Copyright (c) 2022 Ultimaker B.V. //CuraEngine is released under the terms of the AGPLv3 or higher. #include <cstring> @@ -85,25 +85,24 @@ void ExtruderPlan::applyBackPressureCompensation(const Ratio back_pressure_compe constexpr double epsilon_speed_factor = 0.001; // Don't put on actual 'limit double minimum', because we don't want printers to stall. for (auto& path : paths) { - const Ratio nominal_flow_for_path = path.config->getFlowRatio(); const double nominal_width_for_path = static_cast<double>(path.config->getLineWidth()); - if (path.flow <= 0.0 || nominal_flow_for_path <= 0.0 || nominal_width_for_path <= 0.0 || path.config->isTravelPath() || path.config->isBridgePath()) + if(path.width_factor <= 0.0 || nominal_width_for_path <= 0.0 || path.config->isTravelPath() || path.config->isBridgePath()) { continue; } - const double line_width_for_path = path.flow * nominal_flow_for_path * nominal_width_for_path; + const double line_width_for_path = path.width_factor * nominal_width_for_path; path.speed_back_pressure_factor = std::max(epsilon_speed_factor, 1.0 + (nominal_width_for_path / line_width_for_path - 1.0) * back_pressure_compensation); } } -GCodePath* LayerPlan::getLatestPathWithConfig(const GCodePathConfig& config, SpaceFillType space_fill_type, const Ratio flow, bool spiralize, const Ratio speed_factor) +GCodePath* LayerPlan::getLatestPathWithConfig(const GCodePathConfig& config, SpaceFillType space_fill_type, const Ratio flow, const Ratio width_factor, bool spiralize, const Ratio speed_factor) { std::vector<GCodePath>& paths = extruder_plans.back().paths; - if (paths.size() > 0 && paths.back().config == &config && !paths.back().done && paths.back().flow == flow && paths.back().speed_factor == speed_factor && paths.back().mesh_id == current_mesh) // spiralize can only change when a travel path is in between + if(paths.size() > 0 && paths.back().config == &config && !paths.back().done && paths.back().flow == flow && paths.back().width_factor == width_factor && paths.back().speed_factor == speed_factor && paths.back().mesh_id == current_mesh) // spiralize can only change when a travel path is in between { return &paths.back(); } - paths.emplace_back(config, current_mesh, space_fill_type, flow, spiralize, speed_factor); + paths.emplace_back(config, current_mesh, space_fill_type, flow, width_factor, spiralize, speed_factor); GCodePath* ret = &paths.back(); ret->skip_agressive_merge_hint = mode_skip_agressive_merge; return ret; @@ -301,16 +300,17 @@ void LayerPlan::setMesh(const std::string mesh_id) current_mesh = mesh_id; } -void LayerPlan::moveInsideCombBoundary(const coord_t distance) +void LayerPlan::moveInsideCombBoundary(const coord_t distance, const std::optional<SliceLayerPart>& part) { constexpr coord_t max_dist2 = MM2INT(2.0) * MM2INT(2.0); // if we are further than this distance, we conclude we are not inside even though we thought we were. - // this function is to be used to move from the boudary of a part to inside the part + // this function is to be used to move from the boundary of a part to inside the part Point p = getLastPlannedPositionOrStartingPosition(); // copy, since we are going to move p if (PolygonUtils::moveInside(comb_boundary_preferred, p, distance, max_dist2) != NO_INDEX) { //Move inside again, so we move out of tight 90deg corners PolygonUtils::moveInside(comb_boundary_preferred, p, distance, max_dist2); - if (comb_boundary_preferred.inside(p)) + if (comb_boundary_preferred.inside(p) && + (part == std::nullopt || part->outline.inside(p))) { addTravel_simple(p); //Make sure the that any retraction happens after this move, not before it by starting a new move path. @@ -510,9 +510,9 @@ void LayerPlan::planPrime(const float& prime_blob_wipe_length) forceNewPathStart(); } -void LayerPlan::addExtrusionMove(Point p, const GCodePathConfig& config, SpaceFillType space_fill_type, const Ratio& flow, bool spiralize, Ratio speed_factor, double fan_speed) +void LayerPlan::addExtrusionMove(Point p, const GCodePathConfig& config, SpaceFillType space_fill_type, const Ratio& flow, const Ratio width_factor, bool spiralize, Ratio speed_factor, double fan_speed) { - GCodePath* path = getLatestPathWithConfig(config, space_fill_type, flow, spiralize, speed_factor); + GCodePath* path = getLatestPathWithConfig(config, space_fill_type, flow, width_factor, spiralize, speed_factor); path->points.push_back(p); path->setFanSpeed(fan_speed); last_planned_position = p; @@ -520,19 +520,20 @@ void LayerPlan::addExtrusionMove(Point p, const GCodePathConfig& config, SpaceFi void LayerPlan::addPolygon(ConstPolygonRef polygon, int start_idx, const bool backwards, const GCodePathConfig& config, coord_t wall_0_wipe_dist, bool spiralize, const Ratio& flow_ratio, bool always_retract) { + constexpr Ratio width_ratio = 1.0_r; //Not printed with variable line width. Point p0 = polygon[start_idx]; addTravel(p0, always_retract); const int direction = backwards ? -1 : 1; for(size_t point_idx = 1; point_idx < polygon.size(); point_idx++) { Point p1 = polygon[(start_idx + point_idx * direction + polygon.size()) % polygon.size()]; - addExtrusionMove(p1, config, SpaceFillType::Polygons, flow_ratio, spiralize); + addExtrusionMove(p1, config, SpaceFillType::Polygons, flow_ratio, width_ratio, spiralize); p0 = p1; } if(polygon.size() > 2) { const Point& p1 = polygon[start_idx]; - addExtrusionMove(p1, config, SpaceFillType::Polygons, flow_ratio, spiralize); + addExtrusionMove(p1, config, SpaceFillType::Polygons, flow_ratio, width_ratio, spiralize); if(wall_0_wipe_dist > 0) { // apply outer wall wipe @@ -597,7 +598,7 @@ void LayerPlan::addPolygonsByOptimizer(const Polygons& polygons, const GCodePath static constexpr float max_non_bridge_line_volume = MM2INT(100); // limit to accumulated "volume" of non-bridge lines which is proportional to distance x extrusion rate -void LayerPlan::addWallLine(const Point& p0, const Point& p1, const Settings& settings, const GCodePathConfig& non_bridge_config, const GCodePathConfig& bridge_config, float flow, float& non_bridge_line_volume, Ratio speed_factor, double distance_to_bridge_start) +void LayerPlan::addWallLine(const Point& p0, const Point& p1, const Settings& settings, const GCodePathConfig& non_bridge_config, const GCodePathConfig& bridge_config, float flow, const Ratio width_factor, float& non_bridge_line_volume, Ratio speed_factor, double distance_to_bridge_start) { const coord_t min_line_len = 5; // we ignore lines less than 5um long const double acceleration_segment_len = MM2INT(1); // accelerate using segments of this length @@ -652,15 +653,16 @@ void LayerPlan::addWallLine(const Point& p0, const Point& p1, const Settings& se if ((len - coast_dist) > min_line_len) { // segment is longer than coast distance so extrude using non-bridge config to start of coast - addExtrusionMove(segment_end + coast_dist * (cur_point - segment_end) / len, non_bridge_config, SpaceFillType::Polygons, segment_flow, spiralize, speed_factor); + addExtrusionMove(segment_end + coast_dist * (cur_point - segment_end) / len, non_bridge_config, SpaceFillType::Polygons, segment_flow, width_factor, spiralize, speed_factor); } // then coast to start of bridge segment - addExtrusionMove(segment_end, non_bridge_config, SpaceFillType::Polygons, 0, spiralize, speed_factor); + constexpr Ratio flow = 0.0_r; //Coasting has no flow rate. + addExtrusionMove(segment_end, non_bridge_config, SpaceFillType::Polygons, flow, width_factor, spiralize, speed_factor); } else { // no coasting required, just normal segment using non-bridge config - addExtrusionMove(segment_end, non_bridge_config, SpaceFillType::Polygons, segment_flow, spiralize, + addExtrusionMove(segment_end, non_bridge_config, SpaceFillType::Polygons, segment_flow, width_factor, spiralize, (overhang_mask.empty() || (!overhang_mask.inside(p0, true) && !overhang_mask.inside(p1, true))) ? speed_factor : overhang_speed_factor); } @@ -669,10 +671,10 @@ void LayerPlan::addWallLine(const Point& p0, const Point& p1, const Settings& se else { // no coasting required, just normal segment using non-bridge config - addExtrusionMove(segment_end, non_bridge_config, SpaceFillType::Polygons, segment_flow, spiralize, + addExtrusionMove(segment_end, non_bridge_config, SpaceFillType::Polygons, segment_flow, width_factor, spiralize, (overhang_mask.empty() || (!overhang_mask.inside(p0, true) && !overhang_mask.inside(p1, true))) ? speed_factor : overhang_speed_factor); } - non_bridge_line_volume += vSize(cur_point - segment_end) * segment_flow * speed_factor * non_bridge_config.getSpeed(); + non_bridge_line_volume += vSize(cur_point - segment_end) * segment_flow * width_factor * speed_factor * non_bridge_config.getSpeed(); cur_point = segment_end; speed_factor = 1 - (1 - speed_factor) * acceleration_factor; if (speed_factor >= 0.9) @@ -686,7 +688,7 @@ void LayerPlan::addWallLine(const Point& p0, const Point& p1, const Settings& se if (bridge_wall_mask.empty()) { // no bridges required - addExtrusionMove(p1, non_bridge_config, SpaceFillType::Polygons, flow, spiralize, + addExtrusionMove(p1, non_bridge_config, SpaceFillType::Polygons, flow, width_factor, spiralize, (overhang_mask.empty() || (!overhang_mask.inside(p0, true) && !overhang_mask.inside(p1, true))) ? 1.0_r : overhang_speed_factor); } else @@ -744,7 +746,7 @@ void LayerPlan::addWallLine(const Point& p0, const Point& p1, const Settings& se if (bridge_line_len > min_line_len) { - addExtrusionMove(b1, bridge_config, SpaceFillType::Polygons, flow); + addExtrusionMove(b1, bridge_config, SpaceFillType::Polygons, flow, width_factor); non_bridge_line_volume = 0; cur_point = b1; // after a bridge segment, start slow and accelerate to avoid under-extrusion due to extruder lag @@ -768,7 +770,7 @@ void LayerPlan::addWallLine(const Point& p0, const Point& p1, const Settings& se else if (bridge_wall_mask.inside(p0, true) && vSize(p0 - p1) >= min_bridge_line_len) { // both p0 and p1 must be above air (the result will be ugly!) - addExtrusionMove(p1, bridge_config, SpaceFillType::Polygons, flow); + addExtrusionMove(p1, bridge_config, SpaceFillType::Polygons, flow, width_factor); non_bridge_line_volume = 0; } else @@ -968,12 +970,12 @@ void LayerPlan::addWall(const ExtrusionLine& wall, int start_idx, const Settings if(is_small_feature) { constexpr bool spiralize = false; - addExtrusionMove(destination, non_bridge_config, SpaceFillType::Polygons, flow_ratio * (line_width * nominal_line_width_multiplier), spiralize, small_feature_speed_factor); + addExtrusionMove(destination, non_bridge_config, SpaceFillType::Polygons, flow_ratio, line_width * nominal_line_width_multiplier, spiralize, small_feature_speed_factor); } else { const Point origin = p0.p + normal(line_vector, piece_length * piece); - addWallLine(origin, destination, settings, non_bridge_config, bridge_config, flow_ratio * (line_width * nominal_line_width_multiplier), non_bridge_line_volume, speed_factor, distance_to_bridge_start); + addWallLine(origin, destination, settings, non_bridge_config, bridge_config, flow_ratio, line_width * nominal_line_width_multiplier, non_bridge_line_volume, speed_factor, distance_to_bridge_start); } } @@ -1031,9 +1033,10 @@ void LayerPlan::addInfillWall(const ExtrusionLine& wall, const GCodePathConfig& for (const auto &junction_n : wall) { - const double flow = junction_n.w / Ratio(path_config.getLineWidth()); + const Ratio width_factor = junction_n.w / Ratio(path_config.getLineWidth()); constexpr SpaceFillType space_fill_type = SpaceFillType::Polygons; - addExtrusionMove(junction_n.p, path_config, space_fill_type, flow); + constexpr Ratio flow = 1.0_r; + addExtrusionMove(junction_n.p, path_config, space_fill_type, flow, width_factor); junction = junction_n; } } @@ -1134,7 +1137,11 @@ void LayerPlan::addLinesInGivenOrder( { // Instead of doing a small travel that is shorter than the line width (which is generally done at pretty high jerk & move) do a // "fake" extrusion move - addExtrusionMove(start, config, space_fill_type, 0, false, 1.0, fan_speed); + constexpr Ratio flow = 0.0_r; + constexpr Ratio width_factor = 1.0_r; + constexpr bool spiralize = false; + constexpr Ratio speed_factor = 1.0_r; + addExtrusionMove(start, config, space_fill_type, flow, width_factor, spiralize, speed_factor, fan_speed); } else { @@ -1163,7 +1170,10 @@ void LayerPlan::addLinesInGivenOrder( // ignore line segments that are less than 5uM long if (vSize2(p1 - p0) >= MINIMUM_SQUARED_LINE_LENGTH) { - addExtrusionMove(p1, config, space_fill_type, flow_ratio, false, 1.0, fan_speed); + constexpr Ratio width_factor = 1.0_r; + constexpr bool spiralize = false; + constexpr Ratio speed_factor = 1.0_r; + addExtrusionMove(p1, config, space_fill_type, flow_ratio, width_factor, spiralize, speed_factor, fan_speed); p0 = p1; } } @@ -1199,7 +1209,11 @@ void LayerPlan::addLinesInGivenOrder( if (wipe) { - addExtrusionMove(p1 + normal(p1 - p0, wipe_dist), config, space_fill_type, 0.0, false, 1.0, fan_speed); + constexpr Ratio flow = 0.0_r; + constexpr Ratio width_factor = 1.0_r; + constexpr bool spiralize = false; + constexpr Ratio speed_factor = 1.0_r; + addExtrusionMove(p1 + normal(p1 - p0, wipe_dist), config, space_fill_type, flow, width_factor, spiralize, speed_factor, fan_speed); } } } @@ -1268,6 +1282,8 @@ void LayerPlan::addLinesMonotonic void LayerPlan::spiralizeWallSlice(const GCodePathConfig& config, ConstPolygonRef wall, ConstPolygonRef last_wall, const int seam_vertex_idx, const int last_seam_vertex_idx, const bool is_top_layer, const bool is_bottom_layer) { const bool smooth_contours = Application::getInstance().current_slice->scene.current_mesh_group->settings.get<bool>("smooth_spiralized_contours"); + constexpr bool spiralize = true; //In addExtrusionMove calls, enable spiralize and use nominal line width. + constexpr Ratio width_factor = 1.0_r; // once we are into the spiral we always start at the end point of the last layer (if any) const Point origin = (last_seam_vertex_idx >= 0 && !is_bottom_layer) ? last_wall[last_seam_vertex_idx] : wall[seam_vertex_idx]; @@ -1280,7 +1296,8 @@ void LayerPlan::spiralizeWallSlice(const GCodePathConfig& config, ConstPolygonRe Point join_first_wall_at = LinearAlg2D::getClosestOnLineSegment(origin, wall[seam_vertex_idx], wall[(seam_vertex_idx + 1) % wall.size()]); if (vSize(join_first_wall_at - origin) > 10) { - addExtrusionMove(join_first_wall_at, config, SpaceFillType::Polygons, 1.0, true); + constexpr Ratio flow = 1.0_r; + addExtrusionMove(join_first_wall_at, config, SpaceFillType::Polygons, flow, width_factor, spiralize); } } @@ -1363,18 +1380,18 @@ void LayerPlan::spiralizeWallSlice(const GCodePathConfig& config, ConstPolygonRe if (cpp.isValid() && vSize2(cpp.location - p) <= max_dist2) { // interpolate between cpp.location and p depending on how far we have progressed along wall - addExtrusionMove(cpp.location + (p - cpp.location) * (wall_length / total_length), config, SpaceFillType::Polygons, flow, true, speed_factor); + addExtrusionMove(cpp.location + (p - cpp.location) * (wall_length / total_length), config, SpaceFillType::Polygons, flow, width_factor, spiralize, speed_factor); } else { // no point in the last wall was found close enough to the current wall point so don't interpolate - addExtrusionMove(p, config, SpaceFillType::Polygons, flow, true, speed_factor); + addExtrusionMove(p, config, SpaceFillType::Polygons, flow, width_factor, spiralize, speed_factor); } } else { // no smoothing, use point verbatim - addExtrusionMove(p, config, SpaceFillType::Polygons, flow, true, speed_factor); + addExtrusionMove(p, config, SpaceFillType::Polygons, flow, width_factor, spiralize, speed_factor); } } @@ -1398,7 +1415,8 @@ void LayerPlan::spiralizeWallSlice(const GCodePathConfig& config, ConstPolygonRe distance_coasted += seg_length; } // reduce number of paths created when polygon has many points by limiting precision of flow - addExtrusionMove(p, config, SpaceFillType::Polygons, ((int)(flow * 20)) / 20.0, false, speed_factor); + constexpr bool no_spiralize = false; + addExtrusionMove(p, config, SpaceFillType::Polygons, ((int)(flow * 20)) / 20.0, width_factor, no_spiralize, speed_factor); } } } diff --git a/src/LayerPlan.h b/src/LayerPlan.h index 87adc999b..6a0619900 100644 --- a/src/LayerPlan.h +++ b/src/LayerPlan.h @@ -1,4 +1,4 @@ -//Copyright (c) 2021 Ultimaker B.V. +//Copyright (c) 2022 Ultimaker B.V. //CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef LAYER_PLAN_H @@ -283,7 +283,7 @@ private: * \param speed_factor (optional) a factor which the speed will be multiplied by. * \return A path with the given config which is now the last path in LayerPlan::paths */ - GCodePath* getLatestPathWithConfig(const GCodePathConfig& config, SpaceFillType space_fill_type, const Ratio flow = 1.0_r, bool spiralize = false, const Ratio speed_factor = 1.0_r); + GCodePath* getLatestPathWithConfig(const GCodePathConfig& config, SpaceFillType space_fill_type, const Ratio flow = 1.0_r, const Ratio width_factor = 1.0_r, bool spiralize = false, const Ratio speed_factor = 1.0_r); public: /*! @@ -456,13 +456,20 @@ public: * * \param p The point to extrude to * \param config The config with which to extrude - * \param space_fill_type Of what space filling type this extrusion move is a part - * \param flow A modifier of the extrusion width which would follow from the \p config - * \param speed_factor (optional) A factor the travel speed will be multipled by. - * \param spiralize Whether to gradually increase the z while printing. (Note that this path may be part of a sequence of spiralized paths, forming one polygon) - * \param fan_speed fan speed override for this path + * \param space_fill_type Of what space filling type this extrusion move is + * a part. + * \param flow A modifier of the flow rate which would follow from the + * \p config. + * \param width_factor A modifier of the line width which would follow from + * the \p config. + * \param speed_factor (optional) A factor the travel speed will be + * multiplied by. + * \param spiralize Whether to gradually increase the z while printing. + * (Note that this path may be part of a sequence of spiralized paths, + * forming one polygon.) + * \param fan_speed Fan speed override for this path. */ - void addExtrusionMove(Point p, const GCodePathConfig& config, SpaceFillType space_fill_type, const Ratio& flow = 1.0_r, bool spiralize = false, Ratio speed_factor = 1.0_r, double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT); + void addExtrusionMove(Point p, const GCodePathConfig& config, SpaceFillType space_fill_type, const Ratio& flow = 1.0_r, const Ratio width_factor = 1.0_r, bool spiralize = false, Ratio speed_factor = 1.0_r, double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT); /*! * Add polygon to the gcode starting at vertex \p startIdx @@ -509,18 +516,25 @@ public: /*! * Add a single line that is part of a wall to the gcode. - * \param p0 The start vertex of the line - * \param p1 The end vertex of the line - * \param settings The settings which should apply to this line added to the layer plan. - * \param non_bridge_config The config with which to print the wall lines that are not spanning a bridge - * \param bridge_config The config with which to print the wall lines that are spanning a bridge - * \param flow The ratio with which to multiply the extrusion amount - * \param non_bridge_line_volume A pseudo-volume that is derived from the print speed and flow of the non-bridge lines that have preceeded this line - * \param speed_factor This modifies the print speed when accelerating after a bridge line - * \param distance_to_bridge_start The distance along the wall from p0 to the first bridge segment + * \param p0 The start vertex of the line. + * \param p1 The end vertex of the line. + * \param settings The settings which should apply to this line added to the + * layer plan. + * \param non_bridge_config The config with which to print the wall lines + * that are not spanning a bridge. + * \param bridge_config The config with which to print the wall lines that + * are spanning a bridge. + * \param flow The ratio with which to multiply the extrusion amount. + * \param width_ratio The ratio with which to multiply the line width. + * \param non_bridge_line_volume A pseudo-volume that is derived from the + * print speed and flow of the non-bridge lines that have preceded this + * line. + * \param speed_factor This modifies the print speed when accelerating after + * a bridge line. + * \param distance_to_bridge_start The distance along the wall from p0 to + * the first bridge segment. */ - - void addWallLine(const Point& p0, const Point& p1, const Settings& settings, const GCodePathConfig& non_bridge_config, const GCodePathConfig& bridge_config, float flow, float& non_bridge_line_volume, Ratio speed_factor, double distance_to_bridge_start); + void addWallLine(const Point& p0, const Point& p1, const Settings& settings, const GCodePathConfig& non_bridge_config, const GCodePathConfig& bridge_config, float flow, const Ratio width_factor, float& non_bridge_line_volume, Ratio speed_factor, double distance_to_bridge_start); /*! * Add a wall to the g-code starting at vertex \p start_idx @@ -758,12 +772,17 @@ public: void processFanSpeedAndMinimalLayerTime(Point starting_position); /*! - * Add a travel move to the layer plan to move inside the current layer part by a given distance away from the outline. - * This is supposed to be called when the nozzle is around the boundary of a layer part, not when the nozzle is in the middle of support, or in the middle of the air. - * - * \param distance The distance to the comb boundary after we moved inside it. - */ - void moveInsideCombBoundary(const coord_t distance); + * Add a travel move to the layer plan to move inside the current layer part + * by a given distance away from the outline. + * + * This is supposed to be called when the nozzle is around the boundary of a + * layer part, not when the nozzle is in the middle of support, or in the + * middle of the air. + * \param distance The distance to the comb boundary after we moved inside + * it. + * \param part If given, stay within the boundary of this part. + */ + void moveInsideCombBoundary(const coord_t distance, const std::optional<SliceLayerPart>& part = std::nullopt); /*! * Apply back-pressure compensation to this layer-plan. diff --git a/src/PathOrderOptimizer.h b/src/PathOrderOptimizer.h index c60860d63..e403fe7ac 100644 --- a/src/PathOrderOptimizer.h +++ b/src/PathOrderOptimizer.h @@ -208,7 +208,7 @@ public: assert(before_it != path_to_index.end()); is_blocking[before_it->second].emplace_back(after_it->second); } - + std::vector<bool> picked(paths.size(), false); //Fixed size boolean flag for whether each path is already in the optimized vector. Point current_position = start_point; @@ -414,7 +414,7 @@ protected: return vert; } - // Don't know the path-type here, or wether it has a simplify. Also, simplification occurs in-place, which is not wanted here: Copy the polygon. + // Don't know the path-type here, or whether it has a simplify. Also, simplification occurs in-place, which is not wanted here: Copy the polygon. // A course simplification is needed, since Arachne has a tendency to 'smear' corners out over multiple line segments. // Which in itself is a good thing, but will mess up the detection of sharp corners and such. Polygon simple_poly(*path.converted); @@ -501,7 +501,22 @@ protected: score += 1000; //1 meter penalty. } } - if(score < best_score) + + constexpr float EPSILON = 25.0; + if (fabs(best_score - score) <= EPSILON) + { + // add breaker for two candidate starting location with similar score + // if we don't do this then we (can) get an un-even seam + // ties are broken by favouring points with lower x-coord + // if x-coord for both points are equal then break ties by + // favouring points with lower y-coord + if (here.X != best_point.X ? here.X < best_point.X : here.Y < best_point.Y) + { + best_point = here; + } + best_score = std::min(best_score, score); + } + else if(score < best_score) { best_point = here; best_score = score; diff --git a/src/WallToolPaths.cpp b/src/WallToolPaths.cpp index 8a8bb3755..a83b82de3 100644 --- a/src/WallToolPaths.cpp +++ b/src/WallToolPaths.cpp @@ -22,7 +22,6 @@ WallToolPaths::WallToolPaths(const Polygons& outline, const coord_t nominal_bead , bead_width_x(nominal_bead_width) , inset_count(inset_count) , wall_0_inset(wall_0_inset) - , strategy_type(settings.get<StrategyType>("beading_strategy_type")) , print_thin_walls(settings.get<bool>("fill_outline_gaps")) , min_feature_size(settings.get<coord_t>("min_feature_size")) , min_bead_width(settings.get<coord_t>("min_bead_width")) @@ -39,7 +38,6 @@ WallToolPaths::WallToolPaths(const Polygons& outline, const coord_t bead_width_0 , bead_width_x(bead_width_x) , inset_count(inset_count) , wall_0_inset(wall_0_inset) - , strategy_type(settings.get<StrategyType>("beading_strategy_type")) , print_thin_walls(settings.get<bool>("fill_outline_gaps")) , min_feature_size(settings.get<coord_t>("min_feature_size")) , min_bead_width(settings.get<coord_t>("min_bead_width")) @@ -82,7 +80,6 @@ const std::vector<VariableWidthLines>& WallToolPaths::generate() const size_t max_bead_count = (inset_count < std::numeric_limits<coord_t>::max() / 2) ? 2 * inset_count : std::numeric_limits<coord_t>::max(); const auto beading_strat = BeadingStrategyFactory::makeStrategy ( - strategy_type, bead_width_0, bead_width_x, wall_transition_length, diff --git a/src/WallToolPaths.h b/src/WallToolPaths.h index fa0ae53c4..96feca430 100644 --- a/src/WallToolPaths.h +++ b/src/WallToolPaths.h @@ -111,7 +111,6 @@ private: coord_t bead_width_x; //<! The subsequently extrusion line width with which libArachne generates its walls if WallToolPaths was called with the nominal_bead_width Constructor this is the same as bead_width_0 size_t inset_count; //<! The maximum number of walls to generate coord_t wall_0_inset; //<! How far to inset the outer wall. Should only be applied when printing the actual walls, not extra infill/skin/support walls. - StrategyType strategy_type; //<! The wall generating strategy bool print_thin_walls; //<! Whether to enable the widening beading meta-strategy for thin features coord_t min_feature_size; //<! The minimum size of the features that can be widened by the widening beading meta-strategy. Features thinner than that will not be printed coord_t min_bead_width; //<! The minimum bead size to use when widening thin model features with the widening beading meta-strategy diff --git a/src/gcodeExport.cpp b/src/gcodeExport.cpp index 45f472065..c43941674 100644 --- a/src/gcodeExport.cpp +++ b/src/gcodeExport.cpp @@ -1172,7 +1172,8 @@ void GCodeExport::writeFanCommand(double speed) } else if (speed > 0) { - *output_stream << "M106 S" << PrecisionedDouble{1, speed * 255 / 100}; + const bool should_scale_zero_to_one = Application::getInstance().current_slice->scene.settings.get<bool>("machine_scale_fan_speed_zero_to_one"); + *output_stream << "M106 S" << PrecisionedDouble{(should_scale_zero_to_one ? 2u : 1u), (should_scale_zero_to_one ? speed : speed * 255) / 100}; if (fan_number) { *output_stream << " P" << fan_number; diff --git a/src/infill.cpp b/src/infill.cpp index 036d446d1..79c945e1a 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -79,7 +79,6 @@ void Infill::generate(std::vector<VariableWidthLines>& toolpaths, Polygons& resu //The lines along the edge must lie next to the border, not on it. //This makes those algorithms a lot simpler. if (pattern == EFillMethod::ZIG_ZAG //Zig-zag prints the zags along the walls. - || pattern == EFillMethod::CONCENTRIC //Concentric at high densities needs to print alongside the walls, not overlapping them. || (zig_zaggify && (pattern == EFillMethod::LINES //Zig-zaggified infill patterns print their zags along the walls. || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::GRID @@ -351,29 +350,28 @@ void Infill::generateLightningInfill(const LightningLayer* trees, Polygons& resu void Infill::generateConcentricInfill(std::vector<VariableWidthLines>& toolpaths, const Settings& settings) { - constexpr coord_t wall_0_inset = 0; // Don't apply any outer wall inset for these. That's just for the outer wall. - const bool iterative = line_distance > infill_line_width; // Do it all at once if there is not need for a gap, otherwise, iterate. const coord_t min_area = infill_line_width * infill_line_width; - Polygons current_inset = inner_contour.offset(infill_line_width / 2); - do + + Polygons current_inset = inner_contour; + while(true) { - if (iterative) - { - current_inset = current_inset.offset(-infill_line_width * 2).offset(infill_line_width * 2); - } - current_inset.simplify(); - if (current_inset.area() <= min_area) + //If line_distance is 0, start from the same contour as the previous line, except where the previous line closed up the shape. + //So we add the whole nominal line width first (to allow lines to be closer together than 1 line width if the line distance is smaller) and then subtract line_distance. + current_inset = current_inset.offset(infill_line_width - line_distance); + current_inset.simplify(); //Many insets lead to increasingly detailed shapes. Simplify to speed up processing. + if(current_inset.area() < min_area) //So small that it's inconsequential. Stop here. { break; } - const coord_t inset_wall_count = iterative ? 1 : std::numeric_limits<coord_t>::max(); + constexpr size_t inset_wall_count = 1; //1 wall at a time. + constexpr coord_t wall_0_inset = 0; //Don't apply any outer wall inset for these. That's just for the outer wall. WallToolPaths wall_toolpaths(current_inset, infill_line_width, inset_wall_count, wall_0_inset, settings); const std::vector<VariableWidthLines> inset_paths = wall_toolpaths.getToolPaths(); - toolpaths.insert(toolpaths.end(), inset_paths.begin(), inset_paths.end()); - current_inset = wall_toolpaths.getInnerContour().offset((infill_line_width / 2) - line_distance); - } while (iterative); + + current_inset = wall_toolpaths.getInnerContour(); + } } void Infill::generateGridInfill(Polygons& result) diff --git a/src/pathPlanning/Comb.cpp b/src/pathPlanning/Comb.cpp index 7585d4fef..b10df1aa1 100644 --- a/src/pathPlanning/Comb.cpp +++ b/src/pathPlanning/Comb.cpp @@ -33,7 +33,7 @@ Comb::Comb(const SliceDataStorage& storage, const LayerIndex layer_nr, const Pol : storage(storage) , layer_nr(layer_nr) , offset_from_outlines(comb_boundary_offset) // between second wall and infill / other walls -, max_moveInside_distance2(offset_from_outlines * 2 * offset_from_outlines * 2) +, max_moveInside_distance2(offset_from_outlines * offset_from_outlines) , offset_from_inside_to_outside(offset_from_outlines + travel_avoid_distance) , max_crossing_dist2(offset_from_inside_to_outside * offset_from_inside_to_outside * 2) // so max_crossing_dist = offset_from_inside_to_outside * sqrt(2) =approx 1.5 to allow for slightly diagonal crossings and slightly inaccurate crossing computation , boundary_inside_minimum( comb_boundary_inside_minimum ) // copy the boundary, because the partsView_inside will reorder the polygons @@ -80,7 +80,7 @@ bool Comb::calc(const ExtruderTrain& train, Point start_point, Point end_point, unsigned int end_inside_poly = NO_INDEX; const bool end_inside = moveInside(boundary_inside_optimal, _end_inside, inside_loc_to_line_optimal.get(), end_point, end_inside_poly); - unsigned int start_part_boundary_poly_idx = NO_INDEX; // Added initial value to stop MSVC throwing an exception in debug mode + unsigned int start_part_boundary_poly_idx = NO_INDEX; // Added initial value to stop MSVC throwing an exception in debug mode unsigned int end_part_boundary_poly_idx = NO_INDEX; unsigned int start_part_idx = (start_inside_poly == NO_INDEX)? NO_INDEX : partsView_inside_optimal.getPartContaining(start_inside_poly, &start_part_boundary_poly_idx); unsigned int end_part_idx = (end_inside_poly == NO_INDEX)? NO_INDEX : partsView_inside_optimal.getPartContaining(end_inside_poly, &end_part_boundary_poly_idx); @@ -213,11 +213,29 @@ bool Comb::calc(const ExtruderTrain& train, Point start_point, Point end_point, } else { - bool combing_succeeded = LinePolygonsCrossings::comb(*boundary_outside, getOutsideLocToLine(), start_crossing.out, end_crossing.out, comb_paths.back(), offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles); - if (!combing_succeeded) + CombPath tmp_comb_path; + bool combing_succeeded = LinePolygonsCrossings::comb(*boundary_outside, getOutsideLocToLine(), start_crossing.out, end_crossing.out, tmp_comb_path, offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, true); + + if (combing_succeeded) + { + // add combing travel moves if the combing was successful + comb_paths.push_back(tmp_comb_path); + } + else if (fail_on_unavoidable_obstacles) { return false; } + else + { + // if combing is not possible then move directly to the target destination + // this happens for instance when trying to avoid skin-regions and combing from + // an origin that is on a hole-boundary to a destination that is on the outline-border + comb_paths.emplace_back(); + comb_paths.throughAir = true; + comb_paths.back().cross_boundary = true; + comb_paths.back().push_back(start_crossing.in_or_mid); + comb_paths.back().push_back(end_crossing.in_or_mid); + } } } else diff --git a/src/pathPlanning/GCodePath.cpp b/src/pathPlanning/GCodePath.cpp index 15dbfa02f..bd0d57578 100644 --- a/src/pathPlanning/GCodePath.cpp +++ b/src/pathPlanning/GCodePath.cpp @@ -1,4 +1,4 @@ -//Copyright (c) 2018 Ultimaker B.V. +//Copyright (c) 2022 Ultimaker B.V. //CuraEngine is released under the terms of the AGPLv3 or higher. #include "GCodePath.h" @@ -6,11 +6,12 @@ namespace cura { -GCodePath::GCodePath(const GCodePathConfig& config, std::string mesh_id, const SpaceFillType space_fill_type, const Ratio flow, const bool spiralize, const Ratio speed_factor) : +GCodePath::GCodePath(const GCodePathConfig& config, std::string mesh_id, const SpaceFillType space_fill_type, const Ratio flow, const Ratio width_factor, const bool spiralize, const Ratio speed_factor) : config(&config), mesh_id(mesh_id), space_fill_type(space_fill_type), flow(flow), +width_factor(width_factor), speed_factor(speed_factor), speed_back_pressure_factor(1.0), retract(false), @@ -33,12 +34,12 @@ bool GCodePath::isTravelPath() const double GCodePath::getExtrusionMM3perMM() const { - return flow * config->getExtrusionMM3perMM(); + return flow * width_factor * config->getExtrusionMM3perMM(); } coord_t GCodePath::getLineWidthForLayerView() const { - return flow * config->getLineWidth() * config->getFlowRatio(); + return flow * width_factor * config->getLineWidth() * config->getFlowRatio(); } void GCodePath::setFanSpeed(double fan_speed) diff --git a/src/pathPlanning/GCodePath.h b/src/pathPlanning/GCodePath.h index da6af9320..ec29551e1 100644 --- a/src/pathPlanning/GCodePath.h +++ b/src/pathPlanning/GCodePath.h @@ -1,4 +1,4 @@ -//Copyright (c) 2018 Ultimaker B.V. +//Copyright (c) 2022 Ultimaker B.V. //CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef PATH_PLANNING_G_CODE_PATH_H @@ -32,6 +32,7 @@ public: std::string mesh_id; //!< Which mesh this path belongs to, if any. If it's not part of any mesh, the mesh ID should be 0. SpaceFillType space_fill_type; //!< The type of space filling of which this path is a part Ratio flow; //!< A type-independent flow configuration + Ratio width_factor; //!< Adjustment to the line width. Similar to flow, but causes the speed_back_pressure_factor to be adjusted. Ratio speed_factor; //!< A speed factor that is multiplied with the travel speed. This factor can be used to change the travel speed. Ratio speed_back_pressure_factor; // <! The factor the (non-travel) speed should be multiplied with as a consequence of back pressure compensation. bool retract; //!< Whether the path is a move path preceded by a retraction move; whether the path is a retracted move path. @@ -56,11 +57,12 @@ public: * \param space_fill_type The type of space filling of which this path is a * part. * \param flow The flow rate to print this path with. + * \param width_factor A multiplier on the line width. * \param spiralize Gradually increment the z-coordinate while traversing * \param speed_factor The factor that the travel speed will be multiplied with * this path. */ - GCodePath(const GCodePathConfig& config, std::string mesh_id, const SpaceFillType space_fill_type, const Ratio flow, const bool spiralize, const Ratio speed_factor = 1.0); + GCodePath(const GCodePathConfig& config, std::string mesh_id, const SpaceFillType space_fill_type, const Ratio flow, const Ratio width_factor, const bool spiralize, const Ratio speed_factor = 1.0); /*! * Whether this config is the config of a travel path. diff --git a/src/settings/Settings.cpp b/src/settings/Settings.cpp index b411f53b1..24b3fc6e8 100644 --- a/src/settings/Settings.cpp +++ b/src/settings/Settings.cpp @@ -1,4 +1,4 @@ -//Copyright (c) 2021 Ultimaker B.V. +//Copyright (c) 2022 Ultimaker B.V. //CuraEngine is released under the terms of the AGPLv3 or higher. #include <cctype> @@ -348,27 +348,6 @@ template<> EFillMethod Settings::get<EFillMethod>(const std::string& key) const } } -template<> StrategyType Settings::get<StrategyType>(const std::string& key) const -{ - const std::string& value = get<std::string>(key); - if (value == "center_deviation") - { - return StrategyType::Center; - } - else if (value == "distributed") - { - return StrategyType::Distributed; - } - else if (value == "inward_distributed") - { - return StrategyType::InwardDistributed; - } - else //Default. - { - return StrategyType::None; - } -} - template<> EPlatformAdhesion Settings::get<EPlatformAdhesion>(const std::string& key) const { const std::string& value = get<std::string>(key); @@ -569,11 +548,7 @@ template<> SlicingTolerance Settings::get<SlicingTolerance>(const std::string& k template<> InsetDirection Settings::get<InsetDirection>(const std::string& key) const { const std::string& value = get<std::string>(key); - if(value == "center_last") - { - return InsetDirection::CENTER_LAST; - } - else if(value == "outside_in") + if(value == "outside_in") { return InsetDirection::OUTSIDE_IN; } diff --git a/src/utils/ExtrusionLine.cpp b/src/utils/ExtrusionLine.cpp index 7dd940c93..6523b4428 100644 --- a/src/utils/ExtrusionLine.cpp +++ b/src/utils/ExtrusionLine.cpp @@ -128,9 +128,7 @@ void ExtrusionLine::simplify(const coord_t smallest_line_segment_squared, const // We shouldn't remove middle junctions of colinear segments if the area changed for the C-P segment is exceeding the maximum allowed && extrusion_area_error <= maximum_extrusion_area_deviation) { - // Adjust the width of the entire P-N line as a weighted average of the widths of the P-C and C-N lines and - // then remove the current junction (vertex). - next.w = weighted_average_width; + // Remove the current junction (vertex). continue; } @@ -210,8 +208,8 @@ coord_t ExtrusionLine::calculateExtrusionAreaDeviationError(ExtrusionJunction A, * | |--------------------------| | |***************************| * | | ------------------------------------------ * --------------- ^ ************** - * ^ C.w ^ - * B.w new_width = weighted_average_width + * ^ B.w + C.w / 2 ^ + * A.w + B.w / 2 new_width = weighted_average_width * * * ******** denote the total extrusion area deviation error in the consecutive segments as a result of using the @@ -220,18 +218,20 @@ coord_t ExtrusionLine::calculateExtrusionAreaDeviationError(ExtrusionJunction A, * */ const coord_t ab_length = vSize(B - A); const coord_t bc_length = vSize(C - B); - const coord_t width_diff = llabs(B.w - C.w); + const coord_t width_diff = std::max(std::abs(B.w - A.w), std::abs(C.w - B.w)); if (width_diff > 1) { // Adjust the width only if there is a difference, or else the rounding errors may produce the wrong // weighted average value. - weighted_average_width = (ab_length * B.w + bc_length * C.w) / vSize(C - A); - return llabs(B.w - weighted_average_width) * ab_length + llabs(C.w - weighted_average_width) * bc_length; + const coord_t ab_weight = (A.w + B.w) / 2; + const coord_t bc_weight = (B.w + C.w) / 2; + weighted_average_width = (ab_length * ab_weight + bc_length * bc_weight) / vSize(C - A); + return std::abs(ab_weight - weighted_average_width) * ab_length + std::abs(bc_weight - weighted_average_width) * bc_length; } else { // If the width difference is very small, then select the width of the segment that is longer - weighted_average_width = ab_length > bc_length ? B.w : C.w; + weighted_average_width = ab_length > bc_length ? A.w : B.w; return ab_length > bc_length ? width_diff * bc_length : width_diff * ab_length; } } diff --git a/tests/ExtruderPlanTest.cpp b/tests/ExtruderPlanTest.cpp index a8cca147a..e5b9b72d2 100644 --- a/tests/ExtruderPlanTest.cpp +++ b/tests/ExtruderPlanTest.cpp @@ -86,9 +86,10 @@ public: { const std::string mesh_id = "test_mesh"; constexpr Ratio flow_1 = 1.0_r; + constexpr Ratio width_1 = 1.0_r; constexpr bool no_spiralize = false; constexpr Ratio speed_1 = 1.0_r; - square.assign({GCodePath(extrusion_config, mesh_id, SpaceFillType::PolyLines, flow_1, no_spiralize, speed_1)}); + square.assign({GCodePath(extrusion_config, mesh_id, SpaceFillType::PolyLines, flow_1, width_1, no_spiralize, speed_1)}); square.back().points = { Point(0, 0), Point(1000, 0), @@ -98,11 +99,11 @@ public: }; lines.assign({ - GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_1, no_spiralize, speed_1), - GCodePath(travel_config, mesh_id, SpaceFillType::Lines, flow_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_1, no_spiralize, speed_1), - GCodePath(travel_config, mesh_id, SpaceFillType::Lines, flow_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_1, no_spiralize, speed_1) + GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), + GCodePath(travel_config , mesh_id, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), + GCodePath(travel_config , mesh_id, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1) }); lines[0].points = {Point(0, 0), Point(1000, 0)}; lines[1].points = {Point(1000, 0), Point(1000, 400)}; @@ -114,11 +115,11 @@ public: constexpr Ratio flow_08 = 0.8_r; constexpr Ratio flow_04 = 0.4_r; decreasing_flow.assign({ - GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_12, no_spiralize, speed_1), - GCodePath(travel_config, mesh_id, SpaceFillType::Lines, flow_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_08, no_spiralize, speed_1), - GCodePath(travel_config, mesh_id, SpaceFillType::Lines, flow_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_04, no_spiralize, speed_1) + GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_12, width_1, no_spiralize, speed_1), + GCodePath(travel_config , mesh_id, SpaceFillType::Lines, flow_1 , width_1, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_08, width_1, no_spiralize, speed_1), + GCodePath(travel_config , mesh_id, SpaceFillType::Lines, flow_1 , width_1, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_04, width_1, no_spiralize, speed_1) }); decreasing_flow[0].points = {Point(0, 0), Point(1000, 0)}; decreasing_flow[1].points = {Point(1000, 0), Point(1000, 400)}; @@ -130,11 +131,11 @@ public: constexpr Ratio speed_08 = 0.8_r; constexpr Ratio speed_04 = 0.4_r; decreasing_speed.assign({ - GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_1, no_spiralize, speed_12), - GCodePath(travel_config, mesh_id, SpaceFillType::Lines, flow_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_1, no_spiralize, speed_08), - GCodePath(travel_config, mesh_id, SpaceFillType::Lines, flow_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_1, no_spiralize, speed_04) + GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_12), + GCodePath(travel_config , mesh_id, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_08), + GCodePath(travel_config , mesh_id, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_04) }); decreasing_speed[0].points = {Point(0, 0), Point(1000, 0)}; decreasing_speed[1].points = {Point(1000, 0), Point(1000, 400)}; @@ -143,12 +144,12 @@ public: decreasing_speed[4].points = {Point(0, 800), Point(1000, 800)}; variable_width.assign({ - GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, 0.8_r, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, 0.6_r, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, 0.4_r, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, 0.2_r, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, 0.0_r, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_1, 0.8_r, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_1, 0.6_r, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_1, 0.4_r, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_1, 0.2_r, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh_id, SpaceFillType::Lines, flow_1, 0.0_r, no_spiralize, speed_1), }); variable_width[0].points = {Point(0, 0), Point(1000, 0)}; variable_width[1].points = {Point(1000, 0), Point(2000, 0)}; @@ -194,18 +195,20 @@ public: {} /*! - * Helper method to calculate the flow rate of a path in mm3 per second, ignoring any user specified speed alteration other than the back pressure compensation. + * Helper method to calculate the flow rate of a path in mm3 per second, + * minus the influence of flow rate and ignoring any user specified speed + * alteration other than the back pressure compensation. * \param path The path to calculate the flow rate of. - * \return The flow rate, in cubic millimeters per second (ignoring any user specified speed alteration other than back-pressure compensation). + * \return The flow rate, in cubic millimeters per second. */ - double calculatePathFlow(const GCodePath& path) + double calculatePathWidth(const GCodePath& path) { - return path.getExtrusionMM3perMM() * path.config->getSpeed() * path.speed_back_pressure_factor; + return path.getExtrusionMM3perMM() / path.config->getFlowRatio() / path.flow * path.config->getSpeed() * path.speed_back_pressure_factor; } bool shouldCountPath(const GCodePath& path) const { - return path.flow > 0.0 && path.config->getFlowRatio() > 0.0 && path.config->getLineWidth() > 0.0 && ! path.config->isTravelPath() && ! path.config->isBridgePath(); + return path.flow > 0.0 && path.width_factor > 0.0 && path.config->getFlowRatio() > 0.0 && path.config->getLineWidth() > 0.0 && ! path.config->isTravelPath() && ! path.config->isBridgePath(); } }; @@ -250,20 +253,20 @@ public: TEST_P(ExtruderPlanPathsParameterizedTest, BackPressureCompensationZeroIsUncompensated) { extruder_plan.paths = GetParam(); - std::vector<Ratio> original_flows; + std::vector<Ratio> original_widths; std::vector<Ratio> original_speeds; for(const GCodePath& path : extruder_plan.paths) { - original_flows.push_back(path.flow); + original_widths.push_back(path.width_factor); original_speeds.push_back(path.speed_factor); } extruder_plan.applyBackPressureCompensation(0.0_r); - ASSERT_EQ(extruder_plan.paths.size(), original_flows.size()) << "Number of paths may not have changed."; + ASSERT_EQ(extruder_plan.paths.size(), original_widths.size()) << "Number of paths may not have changed."; for(size_t i = 0; i < extruder_plan.paths.size(); ++i) { - EXPECT_NEAR(original_flows[i], extruder_plan.paths[i].flow, error_margin) << "The flow rate did not change. Back pressure compensation doesn't adjust flow."; + EXPECT_NEAR(original_widths[i], extruder_plan.paths[i].width_factor, error_margin) << "The width did not change. Back pressure compensation doesn't adjust line width."; EXPECT_NEAR(original_speeds[i], extruder_plan.paths[i].speed_factor, error_margin) << "The speed factor did not change, since the compensation factor was 0."; } } @@ -285,7 +288,7 @@ TEST_P(ExtruderPlanPathsParameterizedTest, BackPressureCompensationFull) return; } //All flow rates must be equal to this one. - const double first_flow_mm3_per_sec = calculatePathFlow(*first_extrusion); + const double first_flow_mm3_per_sec = calculatePathWidth(*first_extrusion); for(GCodePath& path : extruder_plan.paths) { @@ -293,7 +296,7 @@ TEST_P(ExtruderPlanPathsParameterizedTest, BackPressureCompensationFull) { continue; //Ignore travel moves. } - const double flow_mm3_per_sec = calculatePathFlow(path); + const double flow_mm3_per_sec = calculatePathWidth(path); EXPECT_NEAR(flow_mm3_per_sec, first_flow_mm3_per_sec, error_margin) << "Every path must have a flow rate equal to the first, since the flow changes were completely compensated for."; } } @@ -313,7 +316,7 @@ TEST_P(ExtruderPlanPathsParameterizedTest, BackPressureCompensationHalf) { continue; //Ignore travel moves. } - original_flows.push_back(calculatePathFlow(path)); + original_flows.push_back(calculatePathWidth(path)); } const double original_average = std::accumulate(original_flows.begin(), original_flows.end(), 0.0) / original_flows.size(); @@ -328,7 +331,7 @@ TEST_P(ExtruderPlanPathsParameterizedTest, BackPressureCompensationHalf) { continue; //Ignore travel moves. } - new_flows.push_back(calculatePathFlow(path)); + new_flows.push_back(calculatePathWidth(path)); } const double new_average = std::accumulate(new_flows.begin(), new_flows.end(), 0.0) / new_flows.size(); //Note that the new average doesn't necessarily need to be the same average! It is most likely a higher average in real-world scenarios. @@ -337,7 +340,7 @@ TEST_P(ExtruderPlanPathsParameterizedTest, BackPressureCompensationHalf) ASSERT_EQ(original_flows.size(), new_flows.size()) << "We need to have the same number of extrusion moves."; for(size_t i = 0; i < new_flows.size(); ++i) { - EXPECT_NEAR((original_flows[i] - original_average) / 2.0, new_flows[i] - new_average, error_margin); + EXPECT_NEAR((original_flows[i] - original_average) / 2.0, new_flows[i] - new_average, error_margin) << "The differences in flow rate needs to be approximately halved, within margin of rounding errors."; } } diff --git a/tests/WallsComputationTest.cpp b/tests/WallsComputationTest.cpp index 925bf5a80..2210240ba 100644 --- a/tests/WallsComputationTest.cpp +++ b/tests/WallsComputationTest.cpp @@ -1,4 +1,4 @@ -//Copyright (c) 2021 Ultimaker B.V. +//Copyright (c) 2022 Ultimaker B.V. //CuraEngine is released under the terms of the AGPLv3 or higher. #include <gtest/gtest.h> @@ -72,7 +72,6 @@ public: //Settings for a simple 2 walls, about as basic as possible. settings.add("alternate_extra_perimeter", "false"); - settings.add("beading_strategy_type", "inward_distributed"); settings.add("fill_outline_gaps", "false"); settings.add("initial_layer_line_width_factor", "100"); settings.add("magic_spiralize", "false"); diff --git a/tests/beading_strategy/CenterDeviationBeadingStrategyTest.cpp b/tests/beading_strategy/CenterDeviationBeadingStrategyTest.cpp deleted file mode 100644 index 6b9f6f35c..000000000 --- a/tests/beading_strategy/CenterDeviationBeadingStrategyTest.cpp +++ /dev/null @@ -1,153 +0,0 @@ -//Copyright (c) 2021 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. - -#include <gtest/gtest.h> - -#include "../../src/BeadingStrategy/CenterDeviationBeadingStrategy.h" //Code under test. - -namespace cura -{ - -/*! - * Tests calling the constructor of the CenterDeviationBeadingStrategy and - * whether it sets members correctly. - */ -TEST(CenterDeviationBeadingStrategy, Construction) -{ - CenterDeviationBeadingStrategy strategy(400, 0.6, 0.5, 0.75); - EXPECT_EQ(strategy.getSplitMiddleThreshold(), 0.5) << "Split-middle threshold should be the one it's constructed with."; - EXPECT_EQ(strategy.getAddMiddleThreshold(), 0.75) << "Add-middle threshold should be the one it's constructed with."; -} - -/*! - * Tests getting the optimal thickness with Center Deviation. - * - * This should simply be a multiplication of the line width, when using Center - * Deviation. It doesn't adjust the line widths by itself. - */ -TEST(CenterDeviationBeadingStrategy, GetOptimalThickness) -{ - constexpr coord_t line_width = 400; - CenterDeviationBeadingStrategy strategy(line_width, 0.6, 0.5, 0.5); - EXPECT_EQ(strategy.getOptimalThickness(0), 0) << "With 0 beads, you'll fill 0 space."; - EXPECT_EQ(strategy.getOptimalThickness(1), line_width) << "With 1 bead, optimally you'll want to print that bead at the optimal line width."; - EXPECT_EQ(strategy.getOptimalThickness(4), 4 * line_width) << "With 4 beads, optimally fill 4 line widths."; -} - -/*! - * Test getting the width at which we need to transition to a greater number of - * lines. - */ -TEST(CenterDeviationBeadingStrategy, GetTransitionThickness) -{ - constexpr coord_t line_width = 400; - - //Transition ratio 25%. - CenterDeviationBeadingStrategy strategy(line_width, 0.6, 0.25, 0.25); - EXPECT_EQ(strategy.getTransitionThickness(0), 0.25 * line_width) << "The transition from 0 beads to 1 happens at 25% line width."; - EXPECT_EQ(strategy.getTransitionThickness(1), 1.25 * line_width) << "The transition from 1 bead to 2 happens at 125% line width."; - EXPECT_EQ(strategy.getTransitionThickness(4), 4.25 * line_width) << "The transition from 4 beads to 5 happens at 4 + 25% line width."; - - //Transition ratio 50%. - strategy = CenterDeviationBeadingStrategy(line_width, 0.6, 0.5, 0.5); - EXPECT_EQ(strategy.getTransitionThickness(0), 0.5 * line_width) << "The transition from 0 beads to 1 happens at 50% line width."; - EXPECT_EQ(strategy.getTransitionThickness(1), 1.5 * line_width) << "The transition from 1 bead to 2 happens at 150% line width."; - EXPECT_EQ(strategy.getTransitionThickness(5), 5.5 * line_width) << "The transition from 5 beads to 6 happens at 5 + 50% line width."; - - //Transition ratio 95%. - strategy = CenterDeviationBeadingStrategy(line_width, 0.6, 0.95, 0.95); - EXPECT_EQ(strategy.getTransitionThickness(0), 0.95 * line_width) << "The transition from 0 beads to 1 happens at 95% line width."; - EXPECT_EQ(strategy.getTransitionThickness(1), 1.95 * line_width) << "The transition from 1 bead to 2 happens at 195% line width."; - EXPECT_EQ(strategy.getTransitionThickness(3), 3.95 * line_width) << "The transition from 3 beads to 4 happens at 3 + 95% line width."; - - //Transition ratio 100%. - strategy = CenterDeviationBeadingStrategy(line_width, 0.6, 1, 1); - EXPECT_EQ(strategy.getTransitionThickness(0), line_width) << "Only transition to have a line if it fits completely."; - EXPECT_EQ(strategy.getTransitionThickness(1), 2 * line_width) << "Only transition to have two lines if they both fit completely."; - EXPECT_EQ(strategy.getTransitionThickness(2), 3 * line_width) << "Only transition to have three lines if they all fit completely."; - - //Transition ratio 0%. - strategy = CenterDeviationBeadingStrategy(line_width, 0.6, 0, 0); - EXPECT_EQ(strategy.getTransitionThickness(0), 0) << "Always transition to 1 line. The minimum line width is 0 after all."; - EXPECT_EQ(strategy.getTransitionThickness(1), line_width) << "If 1 line fits completely, immediately transition to 2 lines."; - EXPECT_EQ(strategy.getTransitionThickness(6), 6 * line_width) << "If 6 lines fit completely, immediately transition to 7 lines."; -} - -/*! - * Test getting the optimal bead count for a given shape width. - */ -TEST(CenterDeviationBeadingStrategy, GetOptimalBeadCount) -{ - constexpr coord_t line_width = 400; - - //Transition ratio 25%. - CenterDeviationBeadingStrategy strategy(line_width, 0.6, 0.25, 0.25); - //Anything below 25% line width should then produce 0 lines. - for(coord_t width = 0; width < 0.25 * line_width; width += 10) - { - EXPECT_EQ(strategy.getOptimalBeadCount(width), 0) << "Width is below the transition thickness from 0 to 1 line, so it should produce 0 lines."; - } - EXPECT_LE(strategy.getOptimalBeadCount(0.25 * line_width), 1) << "At exactly the transition thickness, either 0 or 1 line is acceptable."; - for(coord_t width = 0.25 * line_width + 1; width < 1.25 * line_width; width += 10) - { - EXPECT_EQ(strategy.getOptimalBeadCount(width), 1) << "Width is above the transition thickness from 0 to 1 line but below 1 to 2 lines, so it should produce 1 line."; - } - EXPECT_TRUE(strategy.getOptimalBeadCount(1.25 * line_width) == 1 || strategy.getOptimalBeadCount(1.25 * line_width) == 2) << "At exactly 125% line width, either 1 or 2 lines is acceptable."; - for(coord_t width = 1.25 * line_width + 1; width < 2.25 * line_width; width += 10) - { - EXPECT_EQ(strategy.getOptimalBeadCount(width), 2) << "Width is above the transition thickness from 1 to 2 lines but below 2 to 3 lines, so it should produce 2 lines."; - } - - //Transition ratio 80%. - strategy = CenterDeviationBeadingStrategy(line_width, 0.6, 0.8, 0.8); - //Anything below 80% line width should then produce 0 lines. - for(coord_t width = 0; width < 0.8 * line_width; width += 10) - { - EXPECT_EQ(strategy.getOptimalBeadCount(width), 0) << "Width is below the transition thickness from 0 to 1 line, so it should produce 0 lines."; - } - EXPECT_LE(strategy.getOptimalBeadCount(0.8 * line_width), 1) << "At exactly the transition thickness, either 0 or 1 line is acceptable."; - for(coord_t width = 0.8 * line_width + 1; width < 1.8 * line_width; width += 10) - { - EXPECT_EQ(strategy.getOptimalBeadCount(width), 1) << "Width is above the transition thickness from 0 to 1 line but below 1 to 2 lines, so it should produce 1 line."; - } - EXPECT_TRUE(strategy.getOptimalBeadCount(1.8 * line_width) == 1 || strategy.getOptimalBeadCount(1.8 * line_width) == 2) << "At exactly 180% line width, either 1 or 2 lines is acceptable."; - for(coord_t width = 1.8 * line_width + 1; width < 2.8 * line_width; width += 10) - { - EXPECT_EQ(strategy.getOptimalBeadCount(width), 2) << "Width is above the transition thickness from 1 to 2 lines but below 2 to 3 lines, so it should produce 2 lines."; - } -} - -/*! - * Tests whether the line compactness setting does what it is supposed to do, - * producing fewer, wider lines when the setting is high than when the setting - * is low. - * - * This is a test for requirements. The exact outcome of a function is not - * tested, but properties of the outcome is tested. - */ -TEST(CenterDeviationBeadingStrategy, LineCompactnessMonotonic) -{ - constexpr coord_t line_width = 400; - constexpr coord_t widths[] = {0, 1, 99, 101, 150, 200, 299, 300, 301, 399, 400, 401, 410, 450, 500, 660, 770, 880, 910, 1000, 1200}; //Bunch of widths to test with. - constexpr float compactnesses[] = {0, 0.1, 0.2, 0.24, 0.25, 0.26, 0.3, 0.5, 0.7, 0.75, 0.99, 1}; //Bunch of line compactness factors to test with. - constexpr size_t num_compactnesses = sizeof(compactnesses) / sizeof(float); - - for(coord_t width : widths) - { - for(size_t low_index = 0; low_index < num_compactnesses; ++low_index) - { - const float low_compactness = compactnesses[low_index]; - for(size_t high_index = low_index; high_index < num_compactnesses; ++high_index) - { - const float high_compactness = compactnesses[high_index]; - - EXPECT_GE( - CenterDeviationBeadingStrategy(line_width, 0.6, low_compactness, low_compactness).getOptimalBeadCount(width), - CenterDeviationBeadingStrategy(line_width, 0.6, high_compactness, high_compactness).getOptimalBeadCount(width) - ) << "When the compactness is low, the number of beads should always be greater or equal to when the compactness is high."; - } - } - } -} - -}
\ No newline at end of file diff --git a/tests/utils/ExtrusionLineTest.cpp b/tests/utils/ExtrusionLineTest.cpp index d26820a98..100ae790f 100644 --- a/tests/utils/ExtrusionLineTest.cpp +++ b/tests/utils/ExtrusionLineTest.cpp @@ -8,6 +8,7 @@ #include <../src/utils/linearAlg2D.h> #include <limits> +#include <numeric> namespace cura { @@ -405,36 +406,39 @@ namespace cura TEST(ExtrusionLineTest, simplifyLineWidthVariance) { //Generate a line with several vertices halfway. - constexpr coord_t spacing = 100; - ExtrusionLine colinear_polylines(0, false);; + ExtrusionLine colinear_polylines(0, false); auto& colinear = colinear_polylines.junctions; colinear.emplace_back(Point(0, 0), 200, 0); - colinear.emplace_back(Point(spacing / 4, 0), 200, 0); - colinear.emplace_back(Point(spacing / 2, 0), 400, 0); - colinear.emplace_back(Point(spacing / 2 + spacing / 4, 0), 600, 0); - colinear.emplace_back(Point(spacing, 0), 800, 0); + colinear.emplace_back(Point(500, 0), 200, 0); + colinear.emplace_back(Point(1000, 0), 400, 0); + colinear.emplace_back(Point(1500, 0), 600, 0); + colinear.emplace_back(Point(2000, 0), 800, 0); + + // Get 'before' average width: + coord_t averge_width_before = 0; + for (size_t i_junction = 0; i_junction < (colinear.size() - 1); ++i_junction) + { + averge_width_before += ((colinear[i_junction].w + colinear[i_junction + 1].w) / 2) * vSize(colinear[i_junction].p - colinear[i_junction + 1].p); + } - colinear_polylines.simplify(25 * 25, 25 * 25, std::numeric_limits<coord_t>::max()); + colinear_polylines.simplify(20 * 20, 5 * 5, std::numeric_limits<coord_t>::max()); //Regardless of parameters, it should always remove those middle vertices. ASSERT_EQ(colinear_polylines.junctions.size(), 2) << "The degenerate vertices should have been removed."; - ASSERT_EQ(colinear[1].w, 500) << "The width of the end-junction should be the average of all removed."; // Since the distances where equal, and they should all have been merged with that one. } TEST(ExtrusionLineTest, simplifyNoLineWidthVariance) { //Generate a line with several vertices halfway. - constexpr coord_t spacing = 100; - ExtrusionLine colinear_polylines(0, false);; + ExtrusionLine colinear_polylines(0, false); auto& colinear = colinear_polylines.junctions; colinear.emplace_back(Point(0, 0), 200, 0); - colinear.emplace_back(Point(spacing / 4, 0), 200, 0); - colinear.emplace_back(Point(spacing / 2, 0), 400, 0); - colinear.emplace_back(Point(spacing / 2 + spacing / 4, 0), 600, 0); - colinear.emplace_back(Point(spacing, 0), 800, 0); + colinear.emplace_back(Point(500, 0), 200, 0); + colinear.emplace_back(Point(1000, 0), 400, 0); + colinear.emplace_back(Point(1500, 0), 600, 0); + colinear.emplace_back(Point(2000, 0), 800, 0); - colinear_polylines.simplify(25 * 25, 25 * 25, 1); + colinear_polylines.simplify(20 * 20, 5 * 5, 1); // Now it should _not_ remove the vertices, since the total width altered will be more than the max area ... ASSERT_EQ(colinear_polylines.junctions.size(), 5) << "No junctions should have been removed."; // ... even though they are co-linear! - ASSERT_EQ(colinear.back().w, 800) << "The width of the end-junction should not have been changed."; } } |