diff --git a/include/InsetOrderOptimizer.h b/include/InsetOrderOptimizer.h index 624f19ab13..34910c8628 100644 --- a/include/InsetOrderOptimizer.h +++ b/include/InsetOrderOptimizer.h @@ -56,7 +56,7 @@ class InsetOrderOptimizer const size_t wall_x_extruder_nr, const ZSeamConfig& z_seam_config, const std::vector& paths, - const Point2LL& center_point, + const Point2LL& model_center_point, const Shape& disallowed_areas_for_seams = {}); /*! @@ -108,7 +108,7 @@ class InsetOrderOptimizer const ZSeamConfig& z_seam_config_; const std::vector& paths_; const LayerIndex layer_nr_; - const Point2LL center_point_; + const Point2LL model_center_point_; // Center of the model (= all meshes) axis-aligned bounding-box. Shape disallowed_areas_for_seams_; std::vector> inset_polys_; // vector of vectors holding the inset polygons @@ -121,6 +121,10 @@ 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. */ std::optional insertSeamPoint(ExtrusionLine& closed_line); diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index 18db16ebe8..be8e61873b 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -698,6 +698,7 @@ class PathOrderOptimizer if (path.force_start_index_.has_value()) { + // Start index already known, since we forced it, return. return path.force_start_index_.value(); } diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index 6e126feee4..be95224cbf 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -425,6 +425,9 @@ 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; /*! diff --git a/include/utils/linearAlg2D.h b/include/utils/linearAlg2D.h index 71cd4d3e80..58059055c3 100644 --- a/include/utils/linearAlg2D.h +++ b/include/utils/linearAlg2D.h @@ -67,6 +67,18 @@ 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 lineSegmentLineSegmentIntersection(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); diff --git a/src/InsetOrderOptimizer.cpp b/src/InsetOrderOptimizer.cpp index 005e9c8377..edc7177907 100644 --- a/src/InsetOrderOptimizer.cpp +++ b/src/InsetOrderOptimizer.cpp @@ -51,7 +51,7 @@ InsetOrderOptimizer::InsetOrderOptimizer( const size_t wall_x_extruder_nr, const ZSeamConfig& z_seam_config, const std::vector& paths, - const Point2LL& center_point, + const Point2LL& model_center_point, const Shape& disallowed_areas_for_seams) : gcode_writer_(gcode_writer) , storage_(storage) @@ -72,7 +72,7 @@ InsetOrderOptimizer::InsetOrderOptimizer( , z_seam_config_(z_seam_config) , paths_(paths) , layer_nr_(gcode_layer.getLayerNr()) - , center_point_(center_point) + , model_center_point_(model_center_point) , disallowed_areas_for_seams_{ disallowed_areas_for_seams } { } @@ -112,7 +112,6 @@ bool InsetOrderOptimizer::addToLayer() group_outer_walls, disallowed_areas_for_seams_); - for (auto& line : walls_to_be_added) { if (line.is_closed_) @@ -120,6 +119,7 @@ bool InsetOrderOptimizer::addToLayer() std::optional force_start; if (! settings_.get("z_seam_on_vertex")) { + // 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); @@ -191,14 +191,18 @@ std::optional InsetOrderOptimizer::insertSeamPoint(ExtrusionLine& closed } // Find the 'closest' point on the polygon to the request_point. - // This isn't actually the closest, since that'd make for pertty messy seams on 'round' objects, but instead on a ray from the ray-origin to the request_point. Point2LL closest_point; size_t closest_junction_idx = 0; coord_t closest_distance_sqd = std::numeric_limits::max(); bool should_reclaculate_closest = false; if (z_seam_config_.type_ == EZSeamType::USER_SPECIFIED) { - const Point2LL ray_origin = center_point_; + // 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) @@ -222,6 +226,10 @@ std::optional InsetOrderOptimizer::insertSeamPoint(ExtrusionLine& closed } if (closest_distance_sqd >= std::numeric_limits::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) { const auto& next_junction = closed_line.junctions_[(i + 1) % closed_line.junctions_.size()]; @@ -239,11 +247,16 @@ std::optional InsetOrderOptimizer::insertSeamPoint(ExtrusionLine& closed const auto& end_pt = closed_line.junctions_[(closest_junction_idx + 1) % closed_line.junctions_.size()]; 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) { + // 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; } diff --git a/src/utils/linearAlg2D.cpp b/src/utils/linearAlg2D.cpp index e6093e0af0..e3df0ccf34 100644 --- a/src/utils/linearAlg2D.cpp +++ b/src/utils/linearAlg2D.cpp @@ -272,7 +272,6 @@ Point3Matrix LinearAlg2D::rotateAround(const Point2LL& middle, double rotation) return Point3Matrix::translate(middle).compose(rotation_matrix_homogeneous).compose(Point3Matrix::translate(-middle)); } -// A single-shot line-segment/line-segment intersection that returns the parameters and doesn't require a grid-calculation beforehand. bool LinearAlg2D::lineSegmentLineSegmentIntersection(const Point2LL& p1, const Point2LL& p2, const Point2LL& p3, const Point2LL& p4, float* t, float* u) { const float x1mx2 = p1.X - p2.X;