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 11227 zseam support #2067

Merged
merged 16 commits into from
May 3, 2024
Merged
Show file tree
Hide file tree
Changes from 7 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
1 change: 1 addition & 0 deletions include/InsetOrderOptimizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class InsetOrderOptimizer
const ZSeamConfig& z_seam_config_;
const std::vector<VariableWidthLines>& paths_;
const LayerIndex layer_nr_;
std::vector<ClipperLib::Paths> mesh_paths_;

std::vector<std::vector<ConstPolygonPointer>> inset_polys_; // vector of vectors holding the inset polygons
Polygons 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 Down
127 changes: 126 additions & 1 deletion include/PathOrderOptimizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ namespace cura
template<typename Path>
class PathOrderOptimizer
{
private:
std::vector<ClipperLib::Paths> mesh_paths_{};
size_t min_size_support_zeam_ = 0;

public:
using OrderablePath = PathOrdering<Path>;
/*!
Expand Down Expand Up @@ -118,6 +122,8 @@ class PathOrderOptimizer
, reverse_direction_(reverse_direction)
, _group_outer_walls(group_outer_walls)
, order_requirements_(&order_requirements)
, mesh_paths_{}

{
}

Expand All @@ -131,6 +137,12 @@ class PathOrderOptimizer
paths_.emplace_back(polygon, is_closed);
}

void addMeshPathsinfo(const std::vector<ClipperLib::Paths>& polylines, const size_t min_distance)
{
constexpr bool is_closed = true;
mesh_paths_ = polylines;
min_size_support_zeam_ = min_distance;
}
/*!
* Add a new polyline to be optimized.
* \param polyline The polyline to optimize.
Expand Down Expand Up @@ -561,6 +573,68 @@ class PathOrderOptimizer
return best_candidate->vertices_;
}

/**
* @brief Checks if the given vertex is close to any polygon path defined by the front of the mesh_paths_.
*
* The function iteratively checks the shortest distance from the point to each line segment in the polygon.
* A line segment is defined by each pair of consecutive points in the polygon.
* If the shortest distance is less than or equal to min_size_support_zeam_, the function returns true.
* This implies that the vertex is considered "close" to the polygon path.
* The check is performed against all polygons in the front of the mesh_paths_.
*
* @param point Vertex of interest, provided as a 2D point in the LongLong (LL) coordinate space.
* @return Returns true if the vertex is close to any line segment in the polygon path, false otherwise.
*
* @note The closeness is judged based on the minimum Zeam support size.
* @note The distance check is performed on the squared distance to avoid costly square root operations.
*/
bool isVertexCloseToPolygonPath(Point2LL point)
{
for (const auto& points : ranges::front(mesh_paths_))
{
const int len = points.size();

for (int i = 0; i < len; ++i)
{
const auto& polygon_point1 = points[i];
const auto& polygon_point2 = points[(i + 1) % len]; // Ensures looping back to the first point to create the final segment

// Definitions
double x = static_cast<double>(point.X);
double y = static_cast<double>(point.Y);
double x1 = static_cast<double>(polygon_point1.X);
double y1 = static_cast<double>(polygon_point1.Y);
double x2 = static_cast<double>(polygon_point2.X);
double y2 = static_cast<double>(polygon_point2.Y);

// Calculate the shortest distance from the point to the line segment
double dx = x2 - x1;
double dy = y2 - y1;

// Calculate the t parameter
double t = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy);

// If t is outside bounds [0,1], point is closest to an endpoint of the segment
t = std::max(0.0, std::min(1.0, t));

// Compute the coordinates of the point on the line segment nearest to the external point
double nearestX = x1 + t * dx;
double nearestY = y1 + t * dy;

// Calculate squared distance from external point to its nearest point on the line segment
double dx_nearest = x - nearestX;
double dy_nearest = y - nearestY;
double squared_distance = dx_nearest * dx_nearest + dy_nearest * dy_nearest;

if (squared_distance <= min_size_support_zeam_ * min_size_support_zeam_)
{
return true;
}
}
}
return false;
}

OrderablePath* findClosestPath(Point2LL start_position, std::vector<OrderablePath*> candidate_paths)
{
coord_t best_distance2 = std::numeric_limits<coord_t>::max();
Expand Down Expand Up @@ -604,6 +678,52 @@ class PathOrderOptimizer
return best_candidate;
}

/**
* @brief Analyze the positions in a path and determine the next optimal position based on a proximity criterion.
*
* This function iteratively examines positions along the given path, checking if the position is close to 3D model.
* Each position is specified by an index, starting with `best_pos`. If the position is close to the model according to
* `isVertexCloseToPolygonPath` function, the function recursively calls itself with the next position. This process is
* repeated until all positions have been checked or `number_of_paths_analysed` becomes equal to `path_size`.
* If `number_of_paths_analysed` becomes equal to `path_size`, it logs a warning and returns the current best position.
*
* @param best_pos The index of the initial position for analysis in the path.
* @param path An OrderablePath instance containing the path to be examined.
* @param number_of_paths_analysed Optionally, the initial index of paths analysed. Defaults to 0.
* @return The index of the next optimal position in the path sequence. May be the same as the input `best_pos`,
* or may be incremented to a different location based on the proximity criterion.
*
* @note This function uses recursion to evaluate each position in the path.
* @note The process stops prematurely if no start path is found for the support z seam distance.
* This typically happens when the distance of the support seam from the model is bigger than all the support wall points.
*/

size_t pathIfzeamSupportIsCloseToModel(size_t best_pos, const OrderablePath& path, size_t number_of_paths_analysed)
{
size_t path_size = path.converted_->size();
if (path_size > number_of_paths_analysed)
{
if (! mesh_paths_.empty())
{
Point2LL current_candidate = (path.converted_)->at(best_pos);
if (isVertexCloseToPolygonPath(current_candidate))
saumyaj3 marked this conversation as resolved.
Show resolved Hide resolved
{
size_t next_best_position = (path_size > best_pos + 1) ? best_pos + 1 : 0;
number_of_paths_analysed += 1;
best_pos = pathIfzeamSupportIsCloseToModel(next_best_position, path, number_of_paths_analysed);
}
}
}
else
{
spdlog::warn("No start path found for support z seam distance");
// We can also calculate the best point to start at this point.
// This usually happens when the distance of support seam from model is bigger than the whole support wall points.
}
return best_pos;
}


/*!
* Find the vertex which will be the starting point of printing a polygon or
* polyline.
Expand Down Expand Up @@ -674,7 +794,8 @@ class PathOrderOptimizer
// angles > 0 are convex (right turning)

double corner_shift;
if (seam_config_.type_ == EZSeamType::SHORTEST)

if ((seam_config_.type_ == EZSeamType::SHORTEST) || (seam_config_.type_ == EZSeamType::SUPPORT))
{
// the more a corner satisfies our criteria, the closer it appears to be
// shift 10mm for a very acute corner
Expand Down Expand Up @@ -740,6 +861,10 @@ class PathOrderOptimizer
}
}

if (seam_config_.type_ == EZSeamType::SUPPORT)
{
best_i = pathIfzeamSupportIsCloseToModel(best_i, path, 0);
}
return best_i;
}

Expand Down
1 change: 1 addition & 0 deletions include/settings/EnumSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ enum class EZSeamType
SHORTEST,
USER_SPECIFIED,
SHARPEST_CORNER,
SUPPORT,

/* The 'Skirt/brim' type behaves like shortest, except it doesn't try to do tie-breaking for similar locations to
* the last attempt, as that gives a different result when the seams are next to each other instead of on top.
Expand Down
13 changes: 12 additions & 1 deletion src/FffGcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "utils/linearAlg2D.h"
#include "utils/math.h"
#include "utils/orderOptimizer.h"
#include "utils/polygonUtils.h"

namespace cura
{
Expand Down Expand Up @@ -3451,7 +3452,17 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer
const GCodePathConfig& config = configs[0];
constexpr bool retract_before_outer_wall = false;
constexpr coord_t wipe_dist = 0;
const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false);
EZSeamType z_seam_type = EZSeamType::SHORTEST;
ZSeamConfig z_seam_config;
Point2LL start_pos = gcode_layer.getLastPlannedPositionOrStartingPosition();
if (infill_extruder.settings_.get<bool>("support_z_seam_away_from_model"))
{
z_seam_type = EZSeamType::SUPPORT;
}

z_seam_config = ZSeamConfig(z_seam_type, start_pos, EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false);


InsetOrderOptimizer wall_orderer(
*this,
storage,
Expand Down
16 changes: 16 additions & 0 deletions src/InsetOrderOptimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ InsetOrderOptimizer::InsetOrderOptimizer(
, z_seam_config_(z_seam_config)
, paths_(paths)
, layer_nr_(gcode_layer.getLayerNr())
, mesh_paths_{}
{
}

Expand Down Expand Up @@ -112,6 +113,21 @@ bool InsetOrderOptimizer::addToLayer()
order_optimizer.addPolyline(&line);
}
}
if (z_seam_config_.type_ == EZSeamType::SUPPORT)
{
for (std::shared_ptr<SliceMeshStorage> mesh_ptr : storage_.meshes)
{
auto& mesh = *mesh_ptr;
for (auto& part : mesh.layers[layer_nr_].parts)
{
mesh_paths_.push_back(part.print_outline.paths);
}
}
if (! mesh_paths_.empty())
{
order_optimizer.addMeshPathsinfo(mesh_paths_, settings_.get<coord_t>("support_z_seam_min_distance"));
}
}

order_optimizer.optimize();

Expand Down
2 changes: 2 additions & 0 deletions src/settings/Settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,8 @@ EZSeamType Settings::get<EZSeamType>(const std::string& key) const
return EZSeamType::SHARPEST_CORNER;
case "plugin"_sw:
return EZSeamType::PLUGIN;
case "support"_sw:
return EZSeamType::SUPPORT;
saumyaj3 marked this conversation as resolved.
Show resolved Hide resolved
default:
return EZSeamType::SHORTEST;
}
Expand Down
Loading