Skip to content

Commit

Permalink
CURA-12164 Fix innerwall seam position (#2148)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vandresc authored Oct 21, 2024
2 parents 74e380a + 83c254c commit 9a264cb
Show file tree
Hide file tree
Showing 13 changed files with 353 additions and 177 deletions.
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<utils::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 @@ -3161,7 +3161,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

0 comments on commit 9a264cb

Please sign in to comment.