Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CURA-12164 Fix innerwall seam position #2148

Merged
merged 16 commits into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions include/InsetOrderOptimizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ class InsetOrderOptimizer
const Point2LL& model_center_point,
const Shape& disallowed_areas_for_seams = {},
const bool scarf_seam = false,
const bool smooth_speed = false);
const bool smooth_speed = false,
const Shape& overhang_areas = Shape());

/*!
* Adds the insets to the given layer plan.
Expand Down Expand Up @@ -114,6 +115,7 @@ class InsetOrderOptimizer
Shape disallowed_areas_for_seams_;
const bool scarf_seam_;
const bool smooth_speed_;
Shape overhang_areas_;

std::vector<std::vector<const Polygon*>> inset_polys_; // vector of vectors holding the inset polygons
Shape retraction_region_; // After printing an outer wall, move into this region so that retractions do not leave visible blobs. Calculated lazily if needed (see
Expand All @@ -128,7 +130,7 @@ class InsetOrderOptimizer
*
* \param closed_line The polygon to insert the seam point in. (It's assumed to be closed at least.)
*
* \return The index of the inserted seam point, or std::nullopt if no seam point was inserted.
* \return The index of the inserted seam point, or the index of the closest point if an existing one can be used.
*/
std::optional<size_t> insertSeamPoint(ExtrusionLine& closed_line);

Expand Down
49 changes: 5 additions & 44 deletions include/LayerPlan.h
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,11 @@ class LayerPlan : public NoCopy
*/
void setSeamOverhangMask(const Shape& polys);

/*!
* Get the seam overhang mask, which contains the areas where we don't want to place the seam because they are overhanding
*/
const Shape& getSeamOverhangMask() const;

/*!
* Set roofing_mask.
*
Expand Down Expand Up @@ -677,50 +682,6 @@ class LayerPlan : public NoCopy
const bool is_top_layer,
const bool is_bottom_layer);


/*!
* Given a wall polygon and a start vertex index, return the index of the first vertex that is supported (is not above air)
*
* Uses bridge_wall_mask and overhang_mask to determine where there is air below
*
* \param wall The wall polygon
* \param start_idx The index of the starting vertex of \p wall
* \return The index of the first supported vertex - if no vertices are supported, start_idx is returned
*/
template<typename T>
size_t locateFirstSupportedVertex(const T& wall, const size_t start_idx) const
{
if (bridge_wall_mask_.empty() && seam_overhang_mask_.empty())
{
return start_idx;
}

const auto air_below = bridge_wall_mask_.unionPolygons(seam_overhang_mask_);

size_t curr_idx = start_idx;

while (true)
{
const Point2LL& vertex = cura::make_point(wall[curr_idx]);
if (! air_below.inside(vertex, true))
{
// vertex isn't above air so it's OK to use
return curr_idx;
}

if (++curr_idx >= wall.size())
{
curr_idx = 0;
}

if (curr_idx == start_idx)
{
// no vertices are supported so just return the original index
return start_idx;
}
}
}

/*!
* Write the planned paths to gcode
*
Expand Down
278 changes: 178 additions & 100 deletions include/PathOrderOptimizer.h

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions include/path_ordering.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,16 @@ struct PathOrdering
*/
std::optional<size_t> force_start_index_;

/*!
* The start point calculation strategy to be used for this path
*/
ZSeamConfig seam_config_;

/*!
* Indicates whether this path is an outer (or inner) wall
*/
bool is_outer_wall{ false };

/*!
* Get vertex data from the custom path type.
*
Expand Down
7 changes: 4 additions & 3 deletions include/utils/AABB.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
namespace cura
{

class PointsSet;
class Polygon;
class Shape;

Expand All @@ -21,10 +22,10 @@ class AABB
AABB(); //!< initializes with invalid min and max
AABB(const Point2LL& min, const Point2LL& max); //!< initializes with given min and max
AABB(const Shape& shape); //!< Computes the boundary box for the given shape
AABB(const Polygon& poly); //!< Computes the boundary box for the given polygons
AABB(const PointsSet& poly); //!< Computes the boundary box for the given polygons

void calculate(const Shape& shape); //!< Calculates the aabb for the given shape (throws away old min and max data of this aabb)
void calculate(const Polygon& poly); //!< Calculates the aabb for the given polygon (throws away old min and max data of this aabb)
void calculate(const PointsSet& poly); //!< Calculates the aabb for the given polygon (throws away old min and max data of this aabb)

/*!
* Whether the bounding box contains the specified point.
Expand Down Expand Up @@ -80,7 +81,7 @@ class AABB
*/
void include(const Point2LL& point);

void include(const Polygon& polygon);
void include(const PointsSet& polygon);

/*!
* \brief Includes the specified bounding box in the bounding box.
Expand Down
31 changes: 31 additions & 0 deletions include/utils/CriterionScore.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) 2024 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.

#ifndef UTILS_CRITERION_SCORE_H
#define UTILS_CRITERION_SCORE_H

namespace cura
{

/*!
* This structure represents a score given by a single crtierion when calculating a global score to select a best
* candidate among a list with multiple criteria.
*/
struct CriterionScore
{
/*!
* The score given by the criterion. To ensure a proper selection, this value must be contained in [0.0, 1.0] and
* the different given scores must be evenly distributed in this range.
*/
double score{ 0.0 };

/*!
* The weight to be given when taking this score into the global score. A score that contributes "normally" to the
* global score should have a weight of 1.0, and others should be adjusted around this value, to give them more or
* less influence.
*/
double weight{ 0.0 };
};

} // namespace cura
#endif // UTILS_CRITERION_SCORE_H
61 changes: 61 additions & 0 deletions include/utils/Score.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright (c) 2024 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.

#ifndef UTILS_SCORE_H
#define UTILS_SCORE_H

#include <fmt/format.h>

#include "CriterionScore.h"

namespace cura
{

/*!
* This class represents a score to be calculated over different criteria, to select the best candidate among a list.
*/
class Score
{
private:
double value_{ 0.0 };

public:
/*!
* Get the actual score value, should be used for debug purposes only
*/
double getValue() const
{
return value_;
}

/*!
* Add the calculated score of an inidividual criterion to the global score, taking care of its weight
*/
void operator+=(const CriterionScore& criterion_score)
{
value_ += criterion_score.score * criterion_score.weight;
}

/*!
* Comparison operators to allow selecting the best global score
*/
auto operator<=>(const Score&) const = default;
};

} // namespace cura

namespace fmt
{

template<>
struct formatter<cura::Score> : formatter<std::string>
{
auto format(const cura::Score& score, format_context& ctx)
{
return fmt::format_to(ctx.out(), "Score{{{}}}", score.getValue());
}
};

} // namespace fmt

#endif // UTILS_SCORE_H
28 changes: 28 additions & 0 deletions include/utils/math.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,5 +108,33 @@ template<utils::multipliable T>
return (dividend + divisor - 1) / divisor;
}

/*!
* \brief Calculates the "inverse linear interpolation" of a value over a range, i.e. given a range [min, max] the
* value "min" would give a result of 0.0 and the value "max" would give a result of 1.0, values in between will
* be interpolated linearly.
* \note The returned value may be out of the [0.0, 1.0] range if the given value is outside the [min, max] range, it is
* up to the caller to clamp the result if required
* \note The range_min value may be greater than the range_max, inverting the interpolation logic
*/
template<utils::numeric T>
[[nodiscard]] inline double inverse_lerp(T range_min, T range_max, T value)
{
if (range_min == range_max)
{
return 0.0;
}

return static_cast<double>(value - range_min) / (range_max - range_min);
}

/*!
* \brief Get a random floating point number in the range [0.0, 1.0]
*/
template<std::floating_point T>
[[nodiscard]] inline T randf()
{
return static_cast<T>(std::rand()) / static_cast<T>(RAND_MAX);
}

} // namespace cura
#endif // UTILS_MATH_H
3 changes: 2 additions & 1 deletion src/FffGcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3148,7 +3148,8 @@ bool FffGcodeWriter::processInsets(
mesh.bounding_box.flatten().getMiddle(),
disallowed_areas_for_seams,
scarf_seam,
smooth_speed);
smooth_speed,
gcode_layer.getSeamOverhangMask());
added_something |= wall_orderer.addToLayer();
}
return added_something;
Expand Down
30 changes: 19 additions & 11 deletions src/InsetOrderOptimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ InsetOrderOptimizer::InsetOrderOptimizer(
const Point2LL& model_center_point,
const Shape& disallowed_areas_for_seams,
const bool scarf_seam,
const bool smooth_speed)
const bool smooth_speed,
const Shape& overhang_areas)
: gcode_writer_(gcode_writer)
, storage_(storage)
, gcode_layer_(gcode_layer)
Expand All @@ -78,6 +79,7 @@ InsetOrderOptimizer::InsetOrderOptimizer(
, disallowed_areas_for_seams_{ disallowed_areas_for_seams }
, scarf_seam_(scarf_seam)
, smooth_speed_(smooth_speed)
, overhang_areas_(overhang_areas)
{
}

Expand All @@ -93,6 +95,7 @@ bool InsetOrderOptimizer::addToLayer()
const bool current_extruder_is_wall_x = wall_x_extruder_nr_ == extruder_nr_;

const bool reverse = shouldReversePath(use_one_extruder, current_extruder_is_wall_x, outer_to_inner);
const bool use_shortest_for_inner_walls = outer_to_inner;
auto walls_to_be_added = getWallsToBeAdded(reverse, use_one_extruder);

const auto order = pack_by_inset ? getInsetOrder(walls_to_be_added, outer_to_inner) : getRegionOrder(walls_to_be_added, outer_to_inner);
Expand All @@ -114,7 +117,9 @@ bool InsetOrderOptimizer::addToLayer()
reverse,
order,
group_outer_walls,
disallowed_areas_for_seams_);
disallowed_areas_for_seams_,
use_shortest_for_inner_walls,
overhang_areas_);

for (auto& line : walls_to_be_added)
{
Expand All @@ -126,7 +131,7 @@ bool InsetOrderOptimizer::addToLayer()
// If the user indicated that we may deviate from the vertices for the seam, we can insert a seam point, if needed.
force_start = insertSeamPoint(line);
}
order_optimizer.addPolygon(&line, force_start);
order_optimizer.addPolygon(&line, force_start, line.is_outer_wall());
}
else
{
Expand Down Expand Up @@ -202,7 +207,7 @@ std::optional<size_t> InsetOrderOptimizer::insertSeamPoint(ExtrusionLine& closed
Point2LL closest_point;
size_t closest_junction_idx = 0;
coord_t closest_distance_sqd = std::numeric_limits<coord_t>::max();
bool should_reclaculate_closest = false;
bool should_recalculate_closest = false;
if (z_seam_config_.type_ == EZSeamType::USER_SPECIFIED)
{
// For user-defined seams you usually don't _actually_ want the _closest_ point, per-se,
Expand Down Expand Up @@ -248,24 +253,27 @@ std::optional<size_t> InsetOrderOptimizer::insertSeamPoint(ExtrusionLine& closed
closest_junction_idx = i;
}
}
should_reclaculate_closest = true;
should_recalculate_closest = true;
}

const auto& start_pt = closed_line.junctions_[closest_junction_idx];
const auto& end_pt = closed_line.junctions_[(closest_junction_idx + 1) % closed_line.junctions_.size()];
if (should_reclaculate_closest)
if (should_recalculate_closest)
{
// In the second case (see above) the closest point hasn't actually been calculated yet,
// since in that case we'de need the start and end points. So do that here.
closest_point = LinearAlg2D::getClosestOnLineSegment(request_point, start_pt.p_, end_pt.p_);
}
constexpr coord_t smallest_dist_sqd = 25;
if (vSize2(closest_point - start_pt.p_) <= smallest_dist_sqd || vSize2(closest_point - end_pt.p_) <= smallest_dist_sqd)
if (vSize2(closest_point - start_pt.p_) <= smallest_dist_sqd)
{
// Early out if the closest point is too close to the start or end point.
// NOTE: Maybe return the index here anyway, since this is the point the current caller would want to force the seam to.
// However, then the index returned would have a caveat that it _can_ point to an already exisiting point then.
return std::nullopt;
// If the closest point is very close to the start point, just use it instead.
return closest_junction_idx;
}
if (vSize2(closest_point - end_pt.p_) <= smallest_dist_sqd)
{
// If the closest point is very close to the end point, just use it instead.
return (closest_junction_idx + 1) % closed_line.junctions_.size();
}

// NOTE: This could also be done on a single axis (skipping the implied sqrt), but figuring out which one and then using the right values became a bit messy/verbose.
Expand Down
10 changes: 5 additions & 5 deletions src/LayerPlan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1328,11 +1328,6 @@ void LayerPlan::addWall(
{
return;
}
if (is_closed)
{
// make sure wall start point is not above air!
start_idx = locateFirstSupportedVertex(wall, start_idx);
}
const bool actual_scarf_seam = scarf_seam && is_closed;

double non_bridge_line_volume = max_non_bridge_line_volume; // assume extruder is fully pressurised before first non-bridge line is output
Expand Down Expand Up @@ -3049,6 +3044,11 @@ void LayerPlan::setSeamOverhangMask(const Shape& polys)
seam_overhang_mask_ = polys;
}

const Shape& LayerPlan::getSeamOverhangMask() const
{
return seam_overhang_mask_;
}

void LayerPlan::setRoofingMask(const Shape& polys)
{
roofing_mask_ = polys;
Expand Down
Loading