Skip to content

Commit

Permalink
Calculate proper print footprint for adhesion features
Browse files Browse the repository at this point in the history
  • Loading branch information
wawanbreton committed Dec 20, 2024
1 parent 1b856f3 commit b6af8b8
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 12 deletions.
2 changes: 1 addition & 1 deletion include/feature_generation/SkirtBrimAppender.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ enum class EPlatformAdhesion;
class SkirtBrimAppender : public PrintOperationTransformer<PrintPlan>
{
public:
explicit SkirtBrimAppender();
explicit SkirtBrimAppender() = default;

void process(PrintPlan* print_plan) override;
};
Expand Down
2 changes: 2 additions & 0 deletions include/geometry/Shape.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ class Shape : public LinesSet<Polygon>
*/
[[nodiscard]] Shape unionPolygons() const;

[[nodiscard]] static Shape unionShapes(const std::vector<Shape> &shapes, ClipperLib::PolyFillType fill_type = ClipperLib::pftNonZero);

[[nodiscard]] Shape intersection(const Shape& other) const;

/*!
Expand Down
3 changes: 3 additions & 0 deletions include/print_operation/ContinuousExtruderMoveSequence.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class SliceMeshStorage;
class PlanExporter;
class LayerPlan;
class Point3LL;
class Polyline;

class ContinuousExtruderMoveSequence : public PrintOperationSequence
{
Expand All @@ -38,6 +39,8 @@ class ContinuousExtruderMoveSequence : public PrintOperationSequence

void reverse();

std::shared_ptr<Polyline> calculatePolyline() const;

private:
Point3LL start_position_;
const bool closed_;
Expand Down
2 changes: 2 additions & 0 deletions include/print_operation/FeatureExtrusion.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_;
Expand Down
34 changes: 31 additions & 3 deletions include/print_operation/PrintOperationSequence.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -63,14 +63,23 @@ class PrintOperationSequence : public PrintOperation, public std::enable_shared_
const SearchOrder search_order = SearchOrder::Forward,
const std::optional<size_t> max_depth = SearchDepth::DirectChildren) const;

std::vector<PrintOperationPtr> findOperations(
const std::function<bool(const PrintOperationPtr&)>& search_function,
const SearchOrder search_order = SearchOrder::Forward,
const std::optional<size_t> max_depth = SearchDepth::DirectChildren) const;

template<class OperationType>
std::shared_ptr<OperationType>
findOperationByType(const SearchOrder search_order = SearchOrder::Forward, const std::optional<size_t> max_depth = SearchDepth::DirectChildren) const;

template<class OperationType>
std::vector<std::shared_ptr<OperationType>>
findOperationsByType(const SearchOrder search_order = SearchOrder::Forward, const std::optional<size_t> max_depth = SearchDepth::DirectChildren) const;

const std::vector<PrintOperationPtr>& getOperations() const noexcept;

template<class OperationType>
std::vector<std::shared_ptr<OperationType>> getOperationsAs() noexcept;
std::vector<std::shared_ptr<OperationType>> getOperationsAs() const noexcept;

// void setOperations(std::vector<PrintOperationPtr>& operations) noexcept;

Expand Down Expand Up @@ -109,7 +118,26 @@ std::shared_ptr<OperationType> PrintOperationSequence::findOperationByType(const
}

template<class OperationType>
std::vector<std::shared_ptr<OperationType>> PrintOperationSequence::getOperationsAs() noexcept
std::vector<std::shared_ptr<OperationType>> PrintOperationSequence::findOperationsByType(const SearchOrder search_order, const std::optional<size_t> max_depth) const
{
std::vector<std::shared_ptr<OperationType>> found_operations;
findOperations(
[&found_operations](const PrintOperationPtr& operation)
{
if (auto casted_operation = std::dynamic_pointer_cast<OperationType>(operation))
{
found_operations.push_back(casted_operation);
}
return false;
},
search_order,
max_depth);

return found_operations;
}

template<class OperationType>
std::vector<std::shared_ptr<OperationType>> PrintOperationSequence::getOperationsAs() const noexcept
{
std::vector<std::shared_ptr<OperationType>> result;
result.reserve(operations_.size());
Expand Down
41 changes: 34 additions & 7 deletions src/feature_generation/SkirtBrimAppender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,20 @@

#include <Application.h>
#include <Slice.h>
#include <geometry/Shape.h>
#include <settings/EnumSettings.h>
#include <settings/Settings.h>

#include <range/v3/algorithm/contains.hpp>
#include <utils/SVG.h>

#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;
Expand All @@ -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<size_t> used_extruders;
print_plan->findOperation(
[&used_extruders](const PrintOperationPtr& operation)
Expand All @@ -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<size_t>("skirt_height"));
}
}
}

// Calculate the united footprint of all the extrusion features on the first layers
std::vector<Shape> features_footprints;
for (const LayerPlanPtr &layer_plan : print_plan->getOperationsAs<LayerPlan>())
{
if (layer_plan->getLayerIndex() < height)
{
for (const FeatureExtrusionPtr &feature_extrusion : layer_plan->findOperationsByType<FeatureExtrusion>(PrintOperationSequence::SearchOrder::Forward, 1))
{
features_footprints.push_back(feature_extrusion->calculateFootprint());
}
}
}

Shape footprint = Shape::unionShapes(features_footprints).getOutsidePolygons();
}

} // namespace cura
17 changes: 17 additions & 0 deletions src/geometry/Shape.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,23 @@ Shape Shape::unionPolygons() const
return unionPolygons(Shape());
}

Shape Shape::unionShapes(const std::vector<Shape>& 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())
Expand Down
36 changes: 36 additions & 0 deletions src/print_operation/ContinuousExtruderMoveSequence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@

#include "print_operation/ContinuousExtruderMoveSequence.h"

#include <geometry/OpenPolyline.h>
#include <utils/ExtrusionLine.h>
#include <utils/types/arachne.h>

#include <range/v3/view/drop_last.hpp>
#include <range/v3/view/sliding.hpp>

#include "print_operation/ExtrusionMove.h"
Expand Down Expand Up @@ -119,6 +121,40 @@ void ContinuousExtruderMoveSequence::reverse()
}
}

std::shared_ptr<Polyline> ContinuousExtruderMoveSequence::calculatePolyline() const
{
const std::vector<std::shared_ptr<ExtruderMove>> extruder_moves = getOperationsAs<ExtruderMove>();
if (extruder_moves.empty())
{
return nullptr;
}

std::shared_ptr<Polyline> result;
if (closed_)
{
constexpr bool explicitely_closed = false;
result = std::make_shared<ClosedPolyline>(explicitely_closed);
result->push_back(extruder_moves.back()->getPosition().toPoint2LL());

for (const std::shared_ptr<ExtruderMove> &extruder_move : extruder_moves | ranges::views::drop_last(1))
{
result->push_back(extruder_move->getPosition().toPoint2LL());
}
}
else
{
result = std::make_shared<OpenPolyline>();
result->push_back(start_position_.toPoint2LL());

for (const std::shared_ptr<ExtruderMove> &extruder_move : extruder_moves)
{
result->push_back(extruder_move->getPosition().toPoint2LL());
}
}

return result;
}

void ContinuousExtruderMoveSequence::appendExtruderMove(const std::shared_ptr<ExtruderMove>& extruder_move)
{
appendOperation(extruder_move);
Expand Down
18 changes: 17 additions & 1 deletion src/print_operation/FeatureExtrusion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

#include "print_operation/FeatureExtrusion.h"

#include <geometry/MixedLinesSet.h>
#include <geometry/Shape.h>

#include "print_operation/ContinuousExtruderMoveSequence.h"
#include "print_operation/ExtruderPlan.h"
#include "print_operation/ExtrusionMove.h"

namespace cura
Expand Down Expand Up @@ -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<ContinuousExtruderMoveSequence>())
{
extrusions_polylines.push_back(extruder_move_sequence->calculatePolyline());
}

return extrusions_polylines.offset(nominal_line_width_ / 2 + 10);
}

} // namespace cura
60 changes: 60 additions & 0 deletions src/print_operation/PrintOperationSequence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "print_operation/PrintOperationSequence.h"

#include <range/v3/algorithm/contains.hpp>
#include <range/v3/algorithm/copy.hpp>
#include <range/v3/algorithm/copy_if.hpp>
#include <range/v3/algorithm/remove_if.hpp>
#include <range/v3/view/remove.hpp>
Expand Down Expand Up @@ -156,6 +157,65 @@ PrintOperationPtr PrintOperationSequence::findOperation(
return nullptr;
}

std::vector<PrintOperationPtr> PrintOperationSequence::findOperations(
const std::function<bool(const PrintOperationPtr&)>& search_function,
const SearchOrder search_order,
const std::optional<size_t> max_depth) const
{
std::vector<PrintOperationPtr> found_operations;

if (! max_depth.has_value() || max_depth.value() > 0)
{
const std::optional<size_t> 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<PrintOperationSequence>(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<PrintOperationPtr>& PrintOperationSequence::getOperations() const noexcept
{
return operations_;
Expand Down

0 comments on commit b6af8b8

Please sign in to comment.