Skip to content

Commit

Permalink
[CURA-11974] better non vertex seams (#2100)
Browse files Browse the repository at this point in the history
  • Loading branch information
HellAholic authored Jun 21, 2024
2 parents 6b461c7 + 98c0407 commit f4abac6
Show file tree
Hide file tree
Showing 10 changed files with 168 additions and 41 deletions.
8 changes: 7 additions & 1 deletion include/InsetOrderOptimizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class InsetOrderOptimizer
const size_t wall_x_extruder_nr,
const ZSeamConfig& z_seam_config,
const std::vector<VariableWidthLines>& paths,
const Point2LL& model_center_point,
const Shape& disallowed_areas_for_seams = {});

/*!
Expand Down Expand Up @@ -107,6 +108,7 @@ class InsetOrderOptimizer
const ZSeamConfig& z_seam_config_;
const std::vector<VariableWidthLines>& paths_;
const LayerIndex layer_nr_;
const Point2LL model_center_point_; // Center of the model (= all meshes) axis-aligned bounding-box.
Shape disallowed_areas_for_seams_;

std::vector<std::vector<const Polygon*>> inset_polys_; // vector of vectors holding the inset polygons
Expand All @@ -119,8 +121,12 @@ class InsetOrderOptimizer
* 'best' vertex on that polygon. Under certain circumstances, the seam-placing algorithm can
* however still deviate from this, for example when the seam-point placed here isn't suppored
* by the layer below.
*
* \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.
*/
void insertSeamPoint(ExtrusionLine& closed_line);
std::optional<size_t> insertSeamPoint(ExtrusionLine& closed_line);

/*!
* Determine if the paths should be reversed
Expand Down
9 changes: 8 additions & 1 deletion include/PathOrderOptimizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,11 @@ class PathOrderOptimizer
* Add a new polygon to be optimized.
* \param polygon The polygon to optimize.
*/
void addPolygon(const Path& polygon)
void addPolygon(const Path& polygon, std::optional<size_t> force_start_index = std::nullopt)
{
constexpr bool is_closed = true;
paths_.emplace_back(polygon, is_closed);
paths_.back().force_start_index_ = force_start_index;
}

/*!
Expand Down Expand Up @@ -695,6 +696,12 @@ class PathOrderOptimizer
return vert;
}

if (path.force_start_index_.has_value())
{
// Start index already known, since we forced it, return.
return path.force_start_index_.value();
}

// Precompute segments lengths because we are going to need them multiple times
std::vector<coord_t> segments_sizes(path.converted_->size());
coord_t total_length = 0;
Expand Down
6 changes: 6 additions & 0 deletions include/path_ordering.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ struct PathOrdering
*/
bool backwards_;

/*!
* Force the start point of the path to be at a specific location.
* Will only happen if not empty, and this point is actually on the path.
*/
std::optional<size_t> force_start_index_;

/*!
* Get vertex data from the custom path type.
*
Expand Down
5 changes: 5 additions & 0 deletions include/sliceDataStorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,11 @@ class SliceDataStorage : public NoCopy
const int extruder_nr = -1,
const bool include_models = true) const;

/*!
* Get the axis-aligned bounding-box of the complete model (all meshes).
*/
AABB3D getModelBoundingBox() const;

/*!
* Get the extruders used.
*
Expand Down
15 changes: 15 additions & 0 deletions include/utils/linearAlg2D.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,21 @@ class LinearAlg2D
return -1;
}

/*!
* A single-shot line-segment/line-segment intersection that returns the parameters and doesn't require a grid-calculation beforehand.
*
* \param p1 The start point of the first line segment.
* \param p2 The end point of the first line segment.
* \param p3 The start point of the second line segment.
* \param p4 The end point of the second line segment.
* \param t The parameter of the intersection on the first line segment (intersection = p1 + t * (p2 - p1)).
* \param u The parameter of the intersection on the second line segment (intersection = p3 + u * (p4 - p3)).
*
* \return Whether the two line segments intersect.
*/
static bool segmentSegmentIntersection(const Point2LL& p1, const Point2LL& p2, const Point2LL& p3, const Point2LL& p4, float& t, float& u);
static bool lineLineIntersection(const Point2LL& p1, const Point2LL& p2, const Point2LL& p3, const Point2LL& p4, float& t, float& u);

static bool lineLineIntersection(const Point2LL& a, const Point2LL& b, const Point2LL& c, const Point2LL& d, Point2LL& output);

/*!
Expand Down
28 changes: 19 additions & 9 deletions src/FffGcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -709,7 +709,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage)
base_extruder_nr,
base_extruder_nr,
z_seam_config,
raft_paths);
raft_paths,
storage.getModelBoundingBox().flatten().getMiddle());
wall_orderer.addToLayer();
}

Expand Down Expand Up @@ -864,7 +865,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage)
interface_extruder_nr,
interface_extruder_nr,
z_seam_config,
raft_paths);
raft_paths,
storage.getModelBoundingBox().flatten().getMiddle());
wall_orderer.addToLayer();
}

Expand Down Expand Up @@ -1027,7 +1029,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage)
surface_extruder_nr,
surface_extruder_nr,
z_seam_config,
raft_paths);
raft_paths,
storage.getModelBoundingBox().flatten().getMiddle());
wall_orderer.addToLayer();
}

Expand Down Expand Up @@ -2320,7 +2323,8 @@ bool FffGcodeWriter::processSingleLayerInfill(
extruder_nr,
extruder_nr,
z_seam_config,
tool_paths);
tool_paths,
storage.getModelBoundingBox().flatten().getMiddle());
added_something |= wall_orderer.addToLayer();
}
}
Expand Down Expand Up @@ -2789,7 +2793,8 @@ bool FffGcodeWriter::processInsets(
mesh.settings.get<ExtruderTrain&>("wall_0_extruder_nr").extruder_nr_,
mesh.settings.get<ExtruderTrain&>("wall_x_extruder_nr").extruder_nr_,
z_seam_config,
part.wall_toolpaths);
part.wall_toolpaths,
storage.getModelBoundingBox().flatten().getMiddle());
added_something |= wall_orderer.addToLayer();
}
return added_something;
Expand Down Expand Up @@ -3214,7 +3219,8 @@ void FffGcodeWriter::processSkinPrintFeature(
skin_extruder_nr,
skin_extruder_nr,
z_seam_config,
skin_paths);
skin_paths,
storage.getModelBoundingBox().flatten().getMiddle());
added_something |= wall_orderer.addToLayer();
}
}
Expand Down Expand Up @@ -3499,6 +3505,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer
extruder_nr,
z_seam_config,
wall_toolpaths,
storage.getModelBoundingBox().flatten().getMiddle(),
disallowed_area_for_seams);
added_something |= wall_orderer.addToLayer();
}
Expand Down Expand Up @@ -3677,7 +3684,8 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer
extruder_nr,
extruder_nr,
z_seam_config,
wall_toolpaths_here);
wall_toolpaths_here,
storage.getModelBoundingBox().flatten().getMiddle());
added_something |= wall_orderer.addToLayer();
}
}
Expand Down Expand Up @@ -3812,7 +3820,8 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, con
roof_extruder_nr,
roof_extruder_nr,
z_seam_config,
roof_paths);
roof_paths,
storage.getModelBoundingBox().flatten().getMiddle());
wall_orderer.addToLayer();
}
gcode_layer.addLinesByOptimizer(roof_lines, current_roof_config, (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines);
Expand Down Expand Up @@ -3925,7 +3934,8 @@ bool FffGcodeWriter::addSupportBottomsToGCode(const SliceDataStorage& storage, L
bottom_extruder_nr,
bottom_extruder_nr,
z_seam_config,
bottom_paths);
bottom_paths,
storage.getModelBoundingBox().flatten().getMiddle());
wall_orderer.addToLayer();
}
gcode_layer.addLinesByOptimizer(
Expand Down
78 changes: 65 additions & 13 deletions src/InsetOrderOptimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ InsetOrderOptimizer::InsetOrderOptimizer(
const size_t wall_x_extruder_nr,
const ZSeamConfig& z_seam_config,
const std::vector<VariableWidthLines>& paths,
const Point2LL& model_center_point,
const Shape& disallowed_areas_for_seams)
: gcode_writer_(gcode_writer)
, storage_(storage)
Expand All @@ -71,6 +72,7 @@ InsetOrderOptimizer::InsetOrderOptimizer(
, z_seam_config_(z_seam_config)
, paths_(paths)
, layer_nr_(gcode_layer.getLayerNr())
, model_center_point_(model_center_point)
, disallowed_areas_for_seams_{ disallowed_areas_for_seams }
{
}
Expand Down Expand Up @@ -114,11 +116,13 @@ bool InsetOrderOptimizer::addToLayer()
{
if (line.is_closed_)
{
std::optional<size_t> force_start;
if (! settings_.get<bool>("z_seam_on_vertex"))
{
insertSeamPoint(line);
// 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);
order_optimizer.addPolygon(&line, force_start);
}
else
{
Expand Down Expand Up @@ -168,7 +172,7 @@ bool InsetOrderOptimizer::addToLayer()
return added_something;
}

void InsetOrderOptimizer::insertSeamPoint(ExtrusionLine& closed_line)
std::optional<size_t> InsetOrderOptimizer::insertSeamPoint(ExtrusionLine& closed_line)
{
assert(closed_line.is_closed_);
assert(closed_line.size() >= 3);
Expand All @@ -183,30 +187,77 @@ void InsetOrderOptimizer::insertSeamPoint(ExtrusionLine& closed_line)
request_point = gcode_layer_.getLastPlannedPositionOrStartingPosition();
break;
default:
return;
return std::nullopt;
}

// NOTE: Maybe rewrite this once we can use C++23 ranges::views::adjacent
// Find the 'closest' point on the polygon to the request_point.
Point2LL closest_point;
size_t closest_junction_idx = 0;
coord_t closest_distance_sqd = std::numeric_limits<coord_t>::max();
for (const auto& [i, junction] : closed_line.junctions_ | ranges::views::enumerate)
bool should_reclaculate_closest = false;
if (z_seam_config_.type_ == EZSeamType::USER_SPECIFIED)
{
const auto& next_junction = closed_line.junctions_[(i + 1) % closed_line.junctions_.size()];
const coord_t distance_sqd = LinearAlg2D::getDist2FromLineSegment(junction.p_, request_point, next_junction.p_);
if (distance_sqd < closest_distance_sqd)
// For user-defined seams you usually don't _actually_ want the _closest_ point, per-se,
// since you want the seam-line to be continuous in 3D space.
// To that end, take the center of the 3D model (not of the current polygon, as that would give the same problems)
// and project the point along the ray from the center to the request_point.

const Point2LL ray_origin = model_center_point_;
request_point = ray_origin + (request_point - ray_origin) * 10;

for (const auto& [i, junction] : closed_line.junctions_ | ranges::views::enumerate)
{
// NOTE: Maybe rewrite this once we can use C++23 ranges::views::adjacent
const auto& next_junction = closed_line.junctions_[(i + 1) % closed_line.junctions_.size()];

float t, u;
if (LinearAlg2D::segmentSegmentIntersection(ray_origin, request_point, junction.p_, next_junction.p_, t, u))
{
const Point2LL intersection = ray_origin + (request_point - ray_origin) * t;
const coord_t distance_sqd = vSize2(request_point - intersection);
if (distance_sqd < closest_distance_sqd)
{
closest_point = intersection;
closest_distance_sqd = distance_sqd;
closest_junction_idx = i;
}
}
}
}
if (closest_distance_sqd >= std::numeric_limits<coord_t>::max())
{
// If it the method isn't 'user-defined', or the attempt to do user-defined above failed
// (since we don't take the center of the polygon, but of the model, there's a chance there's no intersection),
// then just find the closest point on the polygon.

for (const auto& [i, junction] : closed_line.junctions_ | ranges::views::enumerate)
{
closest_distance_sqd = distance_sqd;
closest_junction_idx = i;
const auto& next_junction = closed_line.junctions_[(i + 1) % closed_line.junctions_.size()];
const coord_t distance_sqd = LinearAlg2D::getDist2FromLineSegment(junction.p_, request_point, next_junction.p_);
if (distance_sqd < closest_distance_sqd)
{
closest_distance_sqd = distance_sqd;
closest_junction_idx = i;
}
}
should_reclaculate_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()];
const auto closest_point = LinearAlg2D::getClosestOnLineSegment(request_point, start_pt.p_, end_pt.p_);
if (should_reclaculate_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)
{
return;
// 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;
}

// 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 All @@ -216,6 +267,7 @@ void InsetOrderOptimizer::insertSeamPoint(ExtrusionLine& closed_line)
const coord_t w = ((end_pt.w_ * end_dist) / total_dist) + ((start_pt.w_ * start_dist) / total_dist);

closed_line.junctions_.insert(closed_line.junctions_.begin() + closest_junction_idx + 1, ExtrusionJunction(closest_point, w, start_pt.perimeter_index_));
return closest_junction_idx + 1;
}

InsetOrderOptimizer::value_type InsetOrderOptimizer::getRegionOrder(const std::vector<ExtrusionLine>& extrusion_lines, const bool outer_to_inner)
Expand Down
3 changes: 2 additions & 1 deletion src/TopSurface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ bool TopSurface::ironing(const SliceDataStorage& storage, const SliceMeshStorage
extruder_nr,
extruder_nr,
z_seam_config,
ironing_paths);
ironing_paths,
storage.getModelBoundingBox().flatten().getMiddle());
wall_orderer.addToLayer();
added = true;
}
Expand Down
10 changes: 10 additions & 0 deletions src/sliceDataStorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,16 @@ Shape SliceDataStorage::getLayerOutlines(
}
}

AABB3D SliceDataStorage::getModelBoundingBox() const
{
AABB3D bounding_box;
for (const auto& mesh : meshes)
{
bounding_box.include(mesh->bounding_box);
}
return bounding_box;
}

std::vector<bool> SliceDataStorage::getExtrudersUsed() const
{
std::vector<bool> ret;
Expand Down
Loading

0 comments on commit f4abac6

Please sign in to comment.