diff --git a/include/feature_generation/SkirtBrimAppender.h b/include/feature_generation/SkirtBrimAppender.h index c2f73e42df..620387ccee 100644 --- a/include/feature_generation/SkirtBrimAppender.h +++ b/include/feature_generation/SkirtBrimAppender.h @@ -16,7 +16,7 @@ enum class EPlatformAdhesion; class SkirtBrimAppender : public PrintOperationTransformer { public: - explicit SkirtBrimAppender(); + explicit SkirtBrimAppender() = default; void process(PrintPlan* print_plan) override; }; diff --git a/include/geometry/Shape.h b/include/geometry/Shape.h index 552325676f..f58d17a1ad 100644 --- a/include/geometry/Shape.h +++ b/include/geometry/Shape.h @@ -80,6 +80,8 @@ class Shape : public LinesSet */ [[nodiscard]] Shape unionPolygons() const; + [[nodiscard]] static Shape unionShapes(const std::vector &shapes, ClipperLib::PolyFillType fill_type = ClipperLib::pftNonZero); + [[nodiscard]] Shape intersection(const Shape& other) const; /*! diff --git a/include/print_operation/ContinuousExtruderMoveSequence.h b/include/print_operation/ContinuousExtruderMoveSequence.h index a541da8565..9b0852b90f 100644 --- a/include/print_operation/ContinuousExtruderMoveSequence.h +++ b/include/print_operation/ContinuousExtruderMoveSequence.h @@ -18,6 +18,7 @@ class SliceMeshStorage; class PlanExporter; class LayerPlan; class Point3LL; +class Polyline; class ContinuousExtruderMoveSequence : public PrintOperationSequence { @@ -38,6 +39,8 @@ class ContinuousExtruderMoveSequence : public PrintOperationSequence void reverse(); + std::shared_ptr calculatePolyline() const; + private: Point3LL start_position_; const bool closed_; diff --git a/include/print_operation/FeatureExtrusion.h b/include/print_operation/FeatureExtrusion.h index dba0104b72..20637f7455 100644 --- a/include/print_operation/FeatureExtrusion.h +++ b/include/print_operation/FeatureExtrusion.h @@ -25,6 +25,8 @@ class FeatureExtrusion : public PrintOperationSequence coord_t getNominalLineWidth() const; + Shape calculateFootprint() const; + private: const PrintFeatureType type_; const coord_t nominal_line_width_; diff --git a/include/print_operation/PrintOperationSequence.h b/include/print_operation/PrintOperationSequence.h index bcf2ccbe47..9a1dc8aa18 100644 --- a/include/print_operation/PrintOperationSequence.h +++ b/include/print_operation/PrintOperationSequence.h @@ -54,7 +54,7 @@ class PrintOperationSequence : public PrintOperation, public std::enable_shared_ * * @param search_function The search function that should find the matching operation * @param search_order Whether we should search forwards or backwards - * @param max_depth The maximum depth of children to look for. 0 means only direct children, nullopt means full tree. You case use SearchDepth defines. + * @param max_depth The maximum depth of children to look for. 0 means only direct children, nullopt means full tree. You can use SearchDepth defines. * @return The first found operation, or a null ptr if none was found * @note This function can also be used to iterate over children by providing a search function that always returns false */ @@ -63,14 +63,23 @@ class PrintOperationSequence : public PrintOperation, public std::enable_shared_ const SearchOrder search_order = SearchOrder::Forward, const std::optional max_depth = SearchDepth::DirectChildren) const; + std::vector findOperations( + const std::function& search_function, + const SearchOrder search_order = SearchOrder::Forward, + const std::optional max_depth = SearchDepth::DirectChildren) const; + template std::shared_ptr findOperationByType(const SearchOrder search_order = SearchOrder::Forward, const std::optional max_depth = SearchDepth::DirectChildren) const; + template + std::vector> + findOperationsByType(const SearchOrder search_order = SearchOrder::Forward, const std::optional max_depth = SearchDepth::DirectChildren) const; + const std::vector& getOperations() const noexcept; template - std::vector> getOperationsAs() noexcept; + std::vector> getOperationsAs() const noexcept; // void setOperations(std::vector& operations) noexcept; @@ -109,7 +118,26 @@ std::shared_ptr PrintOperationSequence::findOperationByType(const } template -std::vector> PrintOperationSequence::getOperationsAs() noexcept +std::vector> PrintOperationSequence::findOperationsByType(const SearchOrder search_order, const std::optional max_depth) const +{ + std::vector> found_operations; + findOperations( + [&found_operations](const PrintOperationPtr& operation) + { + if (auto casted_operation = std::dynamic_pointer_cast(operation)) + { + found_operations.push_back(casted_operation); + } + return false; + }, + search_order, + max_depth); + + return found_operations; +} + +template +std::vector> PrintOperationSequence::getOperationsAs() const noexcept { std::vector> result; result.reserve(operations_.size()); diff --git a/src/feature_generation/SkirtBrimAppender.cpp b/src/feature_generation/SkirtBrimAppender.cpp index 64227de399..0c441d5c41 100644 --- a/src/feature_generation/SkirtBrimAppender.cpp +++ b/src/feature_generation/SkirtBrimAppender.cpp @@ -5,21 +5,20 @@ #include #include +#include #include #include - -#include +#include #include "print_operation/ExtruderPlan.h" +#include "print_operation/FeatureExtrusion.h" +#include "print_operation/LayerPlan.h" +#include "print_operation/LayerPlanPtr.h" #include "print_operation/PrintOperationPtr.h" namespace cura { -SkirtBrimAppender::SkirtBrimAppender() -{ -} - void SkirtBrimAppender::process(PrintPlan* print_plan) { const Settings& settings = Application::getInstance().current_slice_->scene.settings; @@ -31,7 +30,7 @@ void SkirtBrimAppender::process(PrintPlan* print_plan) return; } - // Collect actually used extruders. At this point we assume that all added extruder plans are non-empty. + // Collect actually used extruders std::set used_extruders; print_plan->findOperation( [&used_extruders](const PrintOperationPtr& operation) @@ -44,6 +43,34 @@ void SkirtBrimAppender::process(PrintPlan* print_plan) }, PrintOperationSequence::SearchOrder::Forward, 1); + + // Find the maximum height we are going to print the skirt/brim to + size_t height = 1; + if (adhesion_type == EPlatformAdhesion::SKIRT) + { + for (const auto& extruder : Application::getInstance().current_slice_->scene.extruders) + { + if (used_extruders.contains(extruder.extruder_nr_)) + { + height = std::max(height, extruder.settings_.get("skirt_height")); + } + } + } + + // Calculate the united footprint of all the extrusion features on the first layers + std::vector features_footprints; + for (const LayerPlanPtr &layer_plan : print_plan->getOperationsAs()) + { + if (layer_plan->getLayerIndex() < height) + { + for (const FeatureExtrusionPtr &feature_extrusion : layer_plan->findOperationsByType(PrintOperationSequence::SearchOrder::Forward, 1)) + { + features_footprints.push_back(feature_extrusion->calculateFootprint()); + } + } + } + + Shape footprint = Shape::unionShapes(features_footprints).getOutsidePolygons(); } } // namespace cura \ No newline at end of file diff --git a/src/geometry/Shape.cpp b/src/geometry/Shape.cpp index 61d029c4e2..9e81f549a5 100644 --- a/src/geometry/Shape.cpp +++ b/src/geometry/Shape.cpp @@ -210,6 +210,23 @@ Shape Shape::unionPolygons() const return unionPolygons(Shape()); } +Shape Shape::unionShapes(const std::vector& shapes, ClipperLib::PolyFillType fill_type) +{ + if (shapes.empty()) + { + return Shape(); + } + + ClipperLib::Paths ret; + ClipperLib::Clipper clipper(clipper_init); + for (const Shape& shape : shapes) + { + shape.addPaths(clipper, ClipperLib::ptSubject); + } + clipper.Execute(ClipperLib::ctUnion, ret, fill_type, fill_type); + return Shape{ std::move(ret) }; +} + Shape Shape::intersection(const Shape& other) const { if (empty() || other.empty()) diff --git a/src/print_operation/ContinuousExtruderMoveSequence.cpp b/src/print_operation/ContinuousExtruderMoveSequence.cpp index 1ad85ac1ef..70bf10e75e 100644 --- a/src/print_operation/ContinuousExtruderMoveSequence.cpp +++ b/src/print_operation/ContinuousExtruderMoveSequence.cpp @@ -3,9 +3,11 @@ #include "print_operation/ContinuousExtruderMoveSequence.h" +#include #include #include +#include #include #include "print_operation/ExtrusionMove.h" @@ -119,6 +121,40 @@ void ContinuousExtruderMoveSequence::reverse() } } +std::shared_ptr ContinuousExtruderMoveSequence::calculatePolyline() const +{ + const std::vector> extruder_moves = getOperationsAs(); + if (extruder_moves.empty()) + { + return nullptr; + } + + std::shared_ptr result; + if (closed_) + { + constexpr bool explicitely_closed = false; + result = std::make_shared(explicitely_closed); + result->push_back(extruder_moves.back()->getPosition().toPoint2LL()); + + for (const std::shared_ptr &extruder_move : extruder_moves | ranges::views::drop_last(1)) + { + result->push_back(extruder_move->getPosition().toPoint2LL()); + } + } + else + { + result = std::make_shared(); + result->push_back(start_position_.toPoint2LL()); + + for (const std::shared_ptr &extruder_move : extruder_moves) + { + result->push_back(extruder_move->getPosition().toPoint2LL()); + } + } + + return result; +} + void ContinuousExtruderMoveSequence::appendExtruderMove(const std::shared_ptr& extruder_move) { appendOperation(extruder_move); diff --git a/src/print_operation/FeatureExtrusion.cpp b/src/print_operation/FeatureExtrusion.cpp index f3f02ecda5..03363278ee 100644 --- a/src/print_operation/FeatureExtrusion.cpp +++ b/src/print_operation/FeatureExtrusion.cpp @@ -3,8 +3,10 @@ #include "print_operation/FeatureExtrusion.h" +#include +#include + #include "print_operation/ContinuousExtruderMoveSequence.h" -#include "print_operation/ExtruderPlan.h" #include "print_operation/ExtrusionMove.h" namespace cura @@ -33,4 +35,18 @@ coord_t FeatureExtrusion::getNominalLineWidth() const return nominal_line_width_; } +Shape FeatureExtrusion::calculateFootprint() const +{ + // Do not consider individual lines widths because clipper is not able to make a multi-width offset. The result + // is then very approximate but should be good enough in most cases. If not, then this behavior should be improved + MixedLinesSet extrusions_polylines; + + for (const ContinuousExtruderMoveSequencePtr& extruder_move_sequence : getOperationsAs()) + { + extrusions_polylines.push_back(extruder_move_sequence->calculatePolyline()); + } + + return extrusions_polylines.offset(nominal_line_width_ / 2 + 10); +} + } // namespace cura diff --git a/src/print_operation/PrintOperationSequence.cpp b/src/print_operation/PrintOperationSequence.cpp index 10aba532c2..efcd1c3204 100644 --- a/src/print_operation/PrintOperationSequence.cpp +++ b/src/print_operation/PrintOperationSequence.cpp @@ -4,6 +4,7 @@ #include "print_operation/PrintOperationSequence.h" #include +#include #include #include #include @@ -156,6 +157,65 @@ PrintOperationPtr PrintOperationSequence::findOperation( return nullptr; } +std::vector PrintOperationSequence::findOperations( + const std::function& search_function, + const SearchOrder search_order, + const std::optional max_depth) const +{ + std::vector found_operations; + + if (! max_depth.has_value() || max_depth.value() > 0) + { + const std::optional next_depth = max_depth.has_value() ? max_depth.value() - 1 : max_depth; + + const auto find_depth_first = [&search_function, &search_order, &next_depth, &found_operations](auto begin, auto end) -> void + { + for (auto iterator = begin; iterator != end; ++iterator) + { + const PrintOperationPtr& operation = *iterator; + if (search_function(operation)) + { + found_operations.push_back(operation); + } + + if (const auto operation_sequence = std::dynamic_pointer_cast(operation)) + { + ranges::copy(operation_sequence->findOperations(search_function, search_order, next_depth), std::back_inserter(found_operations)); + } + } + }; + + switch (search_order) + { + case SearchOrder::Forward: + find_depth_first(operations_.begin(), operations_.end()); + break; + case SearchOrder::Backward: + find_depth_first(operations_.rbegin(), operations_.rend()); + break; + } + } + else + { + auto find_in = [&search_function, &found_operations](auto begin, auto end) -> void + { + ranges::copy_if(begin, end, std::back_inserter(found_operations), search_function); + }; + + switch (search_order) + { + case SearchOrder::Forward: + find_in(operations_.begin(), operations_.end()); + break; + case SearchOrder::Backward: + find_in(operations_.rbegin(), operations_.rend()); + break; + } + } + + return found_operations; +} + const std::vector& PrintOperationSequence::getOperations() const noexcept { return operations_;