diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d3cb270ed..7cc8b55647 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -163,6 +163,7 @@ set(engine_SRCS # Except main.cpp. src/geometry/Polyline.cpp src/geometry/ClosedPolyline.cpp src/geometry/MixedLinesSet.cpp + src/geometry/Point2D.cpp ) add_library(_CuraEngine STATIC ${engine_SRCS} ${engine_PB_SRCS}) diff --git a/Cura.proto b/Cura.proto index 3fc57b7385..b9eaff656c 100644 --- a/Cura.proto +++ b/Cura.proto @@ -87,6 +87,8 @@ message Polygon { MoveRetractionType = 9; SupportInterfaceType = 10; PrimeTowerType = 11; + MoveUnretractionType = 12; + } Type type = 1; // Type of move bytes points = 2; // The points of the polygon, or two points if only a line segment (Currently only line segments are used) diff --git a/include/GCodePathConfig.h b/include/GCodePathConfig.h index 4531b650aa..eba47c6f42 100644 --- a/include/GCodePathConfig.h +++ b/include/GCodePathConfig.h @@ -19,7 +19,7 @@ namespace cura struct GCodePathConfig { coord_t z_offset{}; //(const GCodePathConfig& other) const = default; @@ -56,6 +57,8 @@ struct GCodePathConfig [[nodiscard]] bool isTravelPath() const noexcept; + [[nodiscard]] bool isUnretractionMove() const noexcept; + [[nodiscard]] bool isBridgePath() const noexcept; [[nodiscard]] double getFanSpeed() const noexcept; @@ -66,6 +69,8 @@ struct GCodePathConfig [[nodiscard]] PrintFeatureType getPrintFeatureType() const noexcept; + void setPrintFeatureType(PrintFeatureType type); + private: [[nodiscard]] double calculateExtrusion() const noexcept; }; diff --git a/include/InsetOrderOptimizer.h b/include/InsetOrderOptimizer.h index f03a7fc461..29dfd2898b 100644 --- a/include/InsetOrderOptimizer.h +++ b/include/InsetOrderOptimizer.h @@ -36,6 +36,7 @@ class InsetOrderOptimizer * line widths, flows, speeds, etc to print this mesh with. * \param part The part from which to read the previously generated insets. * \param layer_nr The current layer number. + * \param is_outer_shell determines if the element added is the outer wall/shell of the mesh */ InsetOrderOptimizer( const FffGcodeWriter& gcode_writer, @@ -56,8 +57,10 @@ class InsetOrderOptimizer const size_t wall_x_extruder_nr, const ZSeamConfig& z_seam_config, const std::vector& paths, + const bool is_outer_shell = false, const Shape& disallowed_areas_for_seams = {}); + /*! * Adds the insets to the given layer plan. * @@ -107,6 +110,7 @@ class InsetOrderOptimizer const ZSeamConfig& z_seam_config_; const std::vector& paths_; const LayerIndex layer_nr_; + const bool is_outer_shell_; Shape disallowed_areas_for_seams_; std::vector> inset_polys_; // vector of vectors holding the inset polygons diff --git a/include/LayerPlan.h b/include/LayerPlan.h index b1377d4f3d..ea2ee6b81a 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -313,10 +313,10 @@ class LayerPlan : public NoCopy * The first travel move in a layer will result in a bogus travel move with * no combing and no retraction. This travel move needs to be fixed * afterwards. - * \param p The point to travel to. + * \param pos The point to travel to. * \param force_retract Whether to force a retraction to occur. */ - GCodePath& addTravel(const Point2LL& p, const bool force_retract = false, const coord_t z_offset = 0); + GCodePath& addTravel(const Point2LL& pos, const bool force_retract = false, const coord_t z_offset = 0); /*! * Add a travel path to a certain point and retract if needed. @@ -502,6 +502,8 @@ class LayerPlan : public NoCopy * polyline). * \param is_reversed Whether to print this wall in reverse direction. * \param is_linked_path Whether the path is a continuation off the previous path + * \param smooth_approach Whether we should make a smoothed approach to the first point, + * or just move straight to it */ void addWall( const ExtrusionLine& wall, @@ -515,7 +517,8 @@ class LayerPlan : public NoCopy bool always_retract, const bool is_closed, const bool is_reversed, - const bool is_linked_path); + const bool is_linked_path, + const bool smooth_approach = false); /*! * Add an infill wall to the g-code @@ -806,6 +809,28 @@ class LayerPlan : public NoCopy const coord_t wipe_dist, const Ratio flow_ratio, const double fan_speed); + + void addTravelBeforeSeam(const Point2LL& p, const GCodePathConfig& config); + + /*! + * Begin the first wall with a travel move, executing specific procedures to optimize the process. + * + * Special conditions for this function are: + * - If `always_retract` is true, a retraction is always performed regardless of other factors. + * - If there's an optional `next` point provided, the function adds an intermediate position where the nozzle should go to begin printing the segment with optimal nozzle speed + * and extrusion parameters. + * - The focus of this method is the travel move for the first wall or layer in a print action, specifically optimizing retraction, travel speed, and the starting point for + * extrusion, if any. + * + * Note: This function is for handling very specific cases (such as initial layer or wall) in 3D printing G-code generation and should be used appropriately. + * + * \param pos The point to travel to. + * \param always_retract Whether to always enforce a retraction action. + * \param next the subsequent point of the segment moving towards. If provided, an intermediate travel point is added, when feasible, to optimize the start of the actual print + * action with proper nozzle speed and extrusion. + */ + + void addFirstWallTravel(const Point2LL& pos, const bool force_retract, const Point2LL* next, const GCodePathConfig& config, const Ratio& flow, const Ratio& width_factor); }; } // namespace cura diff --git a/include/PrintFeature.h b/include/PrintFeature.h index 699dcf7b29..d693302c96 100644 --- a/include/PrintFeature.h +++ b/include/PrintFeature.h @@ -4,7 +4,7 @@ namespace cura { -enum class PrintFeatureType: unsigned char +enum class PrintFeatureType : unsigned char { NoneType = 0, // used to mark unspecified jumps in polygons. libArcus depends on it OuterWall = 1, @@ -18,14 +18,13 @@ enum class PrintFeatureType: unsigned char MoveRetraction = 9, SupportInterface = 10, PrimeTower = 11, - NumPrintFeatureTypes = 12 // this number MUST be the last one because other modules will + MoveUnretraction = 12, + NumPrintFeatureTypes = 13 // this number MUST be the last one because other modules will // use this symbol to get the total number of types, which can // be used to create an array or so }; - - } // namespace cura #endif // PRINT_FEATURE diff --git a/include/gcodeExport.h b/include/gcodeExport.h index 9d91325d97..c47c0b7b83 100644 --- a/include/gcodeExport.h +++ b/include/gcodeExport.h @@ -8,6 +8,7 @@ #ifdef BUILD_TESTS #include //To allow tests to use protected members. #endif +#include #include // for stream.str() #include @@ -26,6 +27,7 @@ namespace cura class RetractionConfig; class SliceDataStorage; +class SliceLayerPart; struct WipeScriptConfig; // The GCodeExport class writes the actual GCode. This is the only class that knows how GCode looks and feels. @@ -192,6 +194,8 @@ class GCodeExport : public NoCopy */ double mm3ToE(double mm3); + double mm3ToMm(double mm3); + /*! * Convert a distance value to an E value (which might be linear/distance based as well) for the current extruder. * @@ -250,7 +254,7 @@ class GCodeExport : public NoCopy bool getExtruderIsUsed(const int extruder_nr) const; //!< return whether the extruder has been used throughout printing all meshgroup up till now - Point2LL getGcodePos(const coord_t x, const coord_t y, const int extruder_train) const; + Point2LL getGcodePos(const coord_t x, const coord_t y, const size_t extruder_train) const; void setFlavor(EGCodeFlavor flavor); EGCodeFlavor getFlavor() const; @@ -446,13 +450,26 @@ class GCodeExport : public NoCopy * Write the F, X, Y, Z and E value (if they are not different from the last) * * convenience function called from writeExtrusion and writeTravel + */ + // void writeFXYZE(const Velocity& speed, const coord_t x, const coord_t y, const coord_t z, const double e, const PrintFeatureType& feature); + + /*! + * \brief Write a G0/G1 command * * This function also applies the gcode offset by calling \ref GCodeExport::getGcodePos * This function updates the \ref GCodeExport::total_bounding_box * It estimates the time in \ref GCodeExport::estimateCalculator for the correct feature * It updates \ref GCodeExport::currentPosition, \ref GCodeExport::current_e_value and \ref GCodeExport::currentSpeed + * + * \param command_name The actual command name, which should be "G0" or "G1" (but no check is performed) + * \param Tpeed movement speed + * \param x Target X position + * \param y Target Y position + * \param z Target Z position + * \param e_delta Extrusion amount to be performed while moving + * \param feature The type of feature being printed */ - void writeFXYZE(const Velocity& speed, const coord_t x, const coord_t y, const coord_t z, const double e, const PrintFeatureType& feature); + void writeGCommand(const char* command_name, const Velocity& speed, const coord_t x, const coord_t y, const coord_t z, const double e_delta, const PrintFeatureType feature); /*! * The writeTravel and/or writeExtrusion when flavor == BFB @@ -470,6 +487,8 @@ class GCodeExport : public NoCopy */ void processInitialLayerBedTemperature(); + void sendTravelLine(const Point2LL& pos, PrintFeatureType line_type, const Velocity& speed); + public: /*! * Get ready for extrusion moves: @@ -479,7 +498,7 @@ class GCodeExport : public NoCopy * It estimates the time in \ref GCodeExport::estimateCalculator * It updates \ref GCodeExport::current_e_value and \ref GCodeExport::currentSpeed */ - void writeUnretractionAndPrime(); + void writeUnretractionAndPrime(const std::optional& pos = {}, const std::optional& override_speed = {}, const std::optional& override_amount = {}); void writeRetraction(const RetractionConfig& config, bool force = false, bool extruder_switch = false); /*! @@ -613,6 +632,13 @@ class GCodeExport : public NoCopy * \param wipe_config Config with wipe script settings. */ void insertWipeScript(const WipeScriptConfig& wipe_config); + + void writeApproachToSeam( + const Point2LL& pos, + const Velocity& speed, + const std::vector& current_mesh_parts, + const coord_t wall_line_width, + const coord_t z_seam_approach_inset); }; } // namespace cura diff --git a/include/geometry/ClosedPolyline.h b/include/geometry/ClosedPolyline.h index 8f4bc3e753..30c7ecfc4c 100644 --- a/include/geometry/ClosedPolyline.h +++ b/include/geometry/ClosedPolyline.h @@ -133,6 +133,8 @@ class ClosedPolyline : public Polyline * \return An open polyline instance, with the end point at the same position of the start point */ [[nodiscard]] OpenPolyline toPseudoOpenPolyline() const; + + [[nodiscard]] const_segments_iterator loopOverSegments(const const_segments_iterator& start, const_segments_iterator::difference_type diff) const; }; } // namespace cura diff --git a/include/geometry/Point2D.h b/include/geometry/Point2D.h new file mode 100644 index 0000000000..1b2474c3d5 --- /dev/null +++ b/include/geometry/Point2D.h @@ -0,0 +1,52 @@ +// Copyright (c) 2024 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher. + +#ifndef UTILS_POINT2D_H +#define UTILS_POINT2D_H + +#include "geometry/Point2LL.h" + +namespace cura +{ + +class Point2D +{ +private: + double x_{ 0.0 }; + double y_{ 0.0 }; + +public: + Point2D(double x, double y); + + Point2D(const Point2LL& other); + + [[nodiscard]] Point2D operator*(const coord_t scale) const; + + [[nodiscard]] Point2D operator*(const double scale) const; + + [[nodiscard]] Point2D operator/(const double scale) const; + + [[nodiscard]] double getX() const + { + return x_; + } + + [[nodiscard]] double getY() const + { + return y_; + } + + [[nodiscard]] double size() const; + + [[nodiscard]] double size2() const; + + [[nodiscard]] Point2D normalized() const; + + void normalize(); + + Point2LL toPoint2LL() const; +}; + +} // namespace cura + +#endif // UTILS_POINT2D_H diff --git a/include/geometry/Point2LL.h b/include/geometry/Point2LL.h index 08a556a961..d9849e84c5 100644 --- a/include/geometry/Point2LL.h +++ b/include/geometry/Point2LL.h @@ -157,6 +157,12 @@ INLINE double vSizeMM(const Point2LL& p0) return std::sqrt(fx * fx + fy * fy); } +INLINE Point2LL vResize(const Point2LL& point, coord_t size) +{ + double size_factor = static_cast(size) / static_cast(vSize(point)); + return point * size_factor; +} + INLINE Point2LL normal(const Point2LL& p0, coord_t length) { const coord_t len{ vSize(p0) }; diff --git a/include/geometry/Polygon.h b/include/geometry/Polygon.h index 987851ab8d..32f24b71e4 100644 --- a/include/geometry/Polygon.h +++ b/include/geometry/Polygon.h @@ -86,6 +86,11 @@ class Polygon : public ClosedPolyline return ClipperLib::Area(getPoints()); } + [[nodiscard]] bool isHole() const + { + return area() < 0.0; + } + [[nodiscard]] Point2LL centerOfMass() const; [[nodiscard]] Shape offset(int distance, ClipperLib::JoinType join_type = ClipperLib::jtMiter, double miter_limit = 1.2) const; diff --git a/include/geometry/Polyline.h b/include/geometry/Polyline.h index a26c942fcf..500a4d0b72 100644 --- a/include/geometry/Polyline.h +++ b/include/geometry/Polyline.h @@ -98,10 +98,10 @@ class Polyline : public PointsSet [[nodiscard]] const_segments_iterator endSegments() const; /*! \brief Provides a begin iterator to iterate over all the segments of the line */ - segments_iterator beginSegments(); + [[nodiscard]] segments_iterator beginSegments(); /*! \brief Provides an end iterator to iterate over all the segments of the line */ - segments_iterator endSegments(); + [[nodiscard]] segments_iterator endSegments(); /*! * Split these poly line objects into several line segment objects consisting of only two verts diff --git a/include/geometry/SegmentIterator.h b/include/geometry/SegmentIterator.h index 5164c7d32f..bb3405a775 100644 --- a/include/geometry/SegmentIterator.h +++ b/include/geometry/SegmentIterator.h @@ -42,6 +42,10 @@ struct SegmentIterator source_iterator_type before_end_; public: + SegmentIterator() + { + } + SegmentIterator(source_iterator_type pos, source_iterator_type begin, source_iterator_type end) : current_pos_(pos) , begin_(begin) @@ -58,12 +62,24 @@ struct SegmentIterator return Segment{ *current_pos_, *std::next(current_pos_) }; } + SegmentIterator& operator--() + { + current_pos_--; + return *this; + } + SegmentIterator& operator++() { current_pos_++; return *this; } + SegmentIterator& operator+=(difference_type diff) + { + current_pos_ += (diff); + return *this; + } + bool operator==(const SegmentIterator& other) const { return current_pos_ == other.current_pos_; diff --git a/include/geometry/Shape.h b/include/geometry/Shape.h index 46091d49a3..a6dfa23ec7 100644 --- a/include/geometry/Shape.h +++ b/include/geometry/Shape.h @@ -113,21 +113,6 @@ class Shape : public LinesSet */ [[nodiscard]] bool inside(const Point2LL& p, bool border_result = false) const; - /*! - * Find the polygon inside which point \p p resides. - * - * We do this by tracing from the point towards the positive X direction, - * every line we cross increments the crossings counter. If we have an even number of crossings then we are not inside the polygon. - * We then find the polygon with an uneven number of crossings which is closest to \p p. - * - * If \p border_result, we return the first polygon which is exactly on \p p. - * - * \param p The point for which to check in which polygon it is. - * \param border_result Whether a point exactly on a polygon counts as inside - * \return The index of the polygon inside which the point \p p resides - */ - [[nodiscard]] size_t findInside(const Point2LL& p, bool border_result = false) const; - /*! * \brief Approximates the convex hull of the polygons. * \p extra_outset Extra offset outward diff --git a/include/pathPlanning/GCodePath.h b/include/pathPlanning/GCodePath.h index 09160202d9..4facb76913 100644 --- a/include/pathPlanning/GCodePath.h +++ b/include/pathPlanning/GCodePath.h @@ -49,6 +49,7 @@ struct GCodePath bool done{ false }; //!< Path is finished, no more moves should be added, and a new path should be started instead of any appending done to this one. double fan_speed{ GCodePathConfig::FAN_SPEED_DEFAULT }; //!< fan speed override for this path, value should be within range 0-100 (inclusive) and ignored otherwise TimeMaterialEstimates estimates{}; //!< Naive time and material estimates + bool is_approach_move{ false }; // Whether the current path needs unretracting while reaching the seam position /*! * Whether this config is the config of a travel path. diff --git a/include/utils/Coord_t.h b/include/utils/Coord_t.h index bd8f3e088b..9042ff0086 100644 --- a/include/utils/Coord_t.h +++ b/include/utils/Coord_t.h @@ -16,7 +16,12 @@ using coord_t = ClipperLib::cInt; static inline coord_t operator"" _mu(unsigned long long i) { return static_cast(i); -}; +} + +static inline double toDouble(const coord_t value) +{ + return static_cast(value); +} #define INT2MM(n) (static_cast(n) / 1000.0) #define INT2MM2(n) (static_cast(n) / 1000000.0) diff --git a/include/utils/linearAlg2D.h b/include/utils/linearAlg2D.h index b9f08fb45b..7194aa18b2 100644 --- a/include/utils/linearAlg2D.h +++ b/include/utils/linearAlg2D.h @@ -4,6 +4,8 @@ #ifndef UTILS_LINEAR_ALG_2D_H #define UTILS_LINEAR_ALG_2D_H +#include + #include "geometry/Point2LL.h" namespace cura @@ -69,6 +71,8 @@ class LinearAlg2D static bool lineLineIntersection(const Point2LL& a, const Point2LL& b, const Point2LL& c, const Point2LL& d, Point2LL& output); + static std::optional segmentSegmentIntersection(const Point2LL& s1_start, const Point2LL& s1_end, const Point2LL& s2_start, const Point2LL& s2_end); + /*! * Find whether a point projected on a line segment would be projected to * - properly on the line : zero returned diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 5154971d39..2b924052d9 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1276,7 +1276,7 @@ FffGcodeWriter::ProcessLayerResult FffGcodeWriter::processLayer(const SliceDataS } } - gcode_layer.applyModifyPlugin(); + // gcode_layer.applyModifyPlugin(); time_keeper.registerTime("Modify plugin"); gcode_layer.applyBackPressureCompensation(); @@ -2770,7 +2770,8 @@ bool FffGcodeWriter::processInsets( mesh.settings.get("wall_0_extruder_nr").extruder_nr_, mesh.settings.get("wall_x_extruder_nr").extruder_nr_, z_seam_config, - part.wall_toolpaths); + part.wall_toolpaths, + true); added_something |= wall_orderer.addToLayer(); } return added_something; @@ -3480,6 +3481,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer extruder_nr, z_seam_config, wall_toolpaths, + false, disallowed_area_for_seams); added_something |= wall_orderer.addToLayer(); } diff --git a/src/GCodePathConfig.cpp b/src/GCodePathConfig.cpp index 35e81edbb3..fac6242d7b 100644 --- a/src/GCodePathConfig.cpp +++ b/src/GCodePathConfig.cpp @@ -40,7 +40,7 @@ namespace cura [[nodiscard]] PrintFeatureType GCodePathConfig::getPrintFeatureType() const noexcept { - return type; + return type_; } [[nodiscard]] bool GCodePathConfig::isTravelPath() const noexcept @@ -68,5 +68,10 @@ namespace cura return INT2MM(line_width) * INT2MM(layer_thickness) * double(flow); } +void GCodePathConfig::setPrintFeatureType(PrintFeatureType type) +{ + type_ = type; +} + } // namespace cura diff --git a/src/InsetOrderOptimizer.cpp b/src/InsetOrderOptimizer.cpp index 7be4d8f6ba..0b5f9d4575 100644 --- a/src/InsetOrderOptimizer.cpp +++ b/src/InsetOrderOptimizer.cpp @@ -51,6 +51,7 @@ InsetOrderOptimizer::InsetOrderOptimizer( const size_t wall_x_extruder_nr, const ZSeamConfig& z_seam_config, const std::vector& paths, + const bool is_outer_shell, const Shape& disallowed_areas_for_seams) : gcode_writer_(gcode_writer) , storage_(storage) @@ -71,7 +72,9 @@ InsetOrderOptimizer::InsetOrderOptimizer( , z_seam_config_(z_seam_config) , paths_(paths) , layer_nr_(gcode_layer.getLayerNr()) + , is_outer_shell_(is_outer_shell) , disallowed_areas_for_seams_{ disallowed_areas_for_seams } + { } @@ -158,7 +161,8 @@ bool InsetOrderOptimizer::addToLayer() retract_before, path.is_closed_, backwards, - linked_path); + linked_path, + is_outer_shell_ && is_outer_wall); added_something = true; } return added_something; diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 7f923ef879..d919e2be69 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -76,8 +76,10 @@ const Shape* LayerPlan::getCombBoundaryInside() const void LayerPlan::forceNewPathStart() { std::vector& paths = extruder_plans_.back().paths_; - if (paths.size() > 0) - paths[paths.size() - 1].done = true; + if (! paths.empty()) + { + paths.back().done = true; + } } LayerPlan::LayerPlan( @@ -370,7 +372,30 @@ std::optional> LayerPlan::getFirstTravelDestinationSta return ret; } -GCodePath& LayerPlan::addTravel(const Point2LL& p, const bool force_retract, const coord_t z_offset) +void LayerPlan::addFirstWallTravel(const Point2LL& pos, const bool force_retract, const Point2LL* next, const GCodePathConfig& config, const Ratio& flow, const Ratio& width_factor) +{ + const ExtruderTrain* extruder = getLastPlannedExtruderTrain(); + const Settings& mesh_or_extruder_settings = current_mesh_ ? current_mesh_->settings : extruder->settings_; + const coord_t z_seam_approach_distance = mesh_or_extruder_settings.get("z_seam_approach_distance"); + + if (z_seam_approach_distance != 0 && next && (*next != pos)) + { + // First travel to intermediate position + Point2LL direction = *next - pos; + direction = (direction * 1000) / vSize(direction); + Point2LL approach_point = pos - direction * (z_seam_approach_distance / 1000); + addTravel(approach_point, force_retract); + + // Add special travel to start of seam location so that it starts printing seamlessly + addTravelBeforeSeam(pos, config); + } + else + { + addTravel(pos, force_retract); + } +} + +GCodePath& LayerPlan::addTravel(const Point2LL& pos, const bool force_retract, const coord_t z_offset) { const GCodePathConfig& travel_config = configs_storage_.travel_config_per_extruder[getExtruder()]; @@ -394,7 +419,7 @@ GCodePath& LayerPlan::addTravel(const Point2LL& p, const bool force_retract, con if (is_first_travel_of_layer) { bypass_combing = true; // first travel move is bogus; it is added after this and the previous layer have been planned in LayerPlanBuffer::addConnectingTravelMove - first_travel_destination_ = p; + first_travel_destination_ = pos; first_travel_destination_is_inside_ = is_inside_; if (layer_nr_ == 0 && retraction_enable && mesh_or_extruder_settings.get("retraction_hop_enabled")) { @@ -403,7 +428,7 @@ GCodePath& LayerPlan::addTravel(const Point2LL& p, const bool force_retract, con } forceNewPathStart(); // force a new travel path after this first bogus move } - else if (force_retract && last_planned_position_ && ! shorterThen(*last_planned_position_ - p, retraction_config.retraction_min_travel_distance)) + else if (force_retract && last_planned_position_ && ! shorterThen(*last_planned_position_ - pos, retraction_config.retraction_min_travel_distance)) { // path is not shorter than min travel distance, force a retraction path->retract = true; @@ -429,7 +454,7 @@ GCodePath& LayerPlan::addTravel(const Point2LL& p, const bool force_retract, con perform_z_hops_only_when_collides, *extruder, *last_planned_position_, - p, + pos, combPaths, was_inside_, is_inside_, @@ -458,6 +483,7 @@ GCodePath& LayerPlan::addTravel(const Point2LL& p, const bool force_retract, con } const coord_t maximum_travel_resolution = mesh_or_extruder_settings.get("meshfix_maximum_travel_resolution"); + const coord_t z_seam_approach_distance = mesh_or_extruder_settings.get("z_seam_approach_distance"); coord_t distance = 0; Point2LL last_point((last_planned_position_) ? *last_planned_position_ : Point2LL(0, 0)); for (CombPath& combPath : combPaths) @@ -475,7 +501,7 @@ GCodePath& LayerPlan::addTravel(const Point2LL& p, const bool force_retract, con last_point = comb_point; } } - distance += vSize(last_point - p); + distance += vSize(last_point - pos); const coord_t retract_threshold = mesh_or_extruder_settings.get("retraction_combing_max_distance"); path->retract = retract || (retract_threshold > 0 && distance > retract_threshold && retraction_enable); // don't perform a z-hop @@ -484,21 +510,21 @@ GCodePath& LayerPlan::addTravel(const Point2LL& p, const bool force_retract, con // This should be true when traveling towards an outer wall to make sure that the unretraction will happen before the // last travel move BEFORE going to that wall. This way, the nozzle doesn't sit still on top of the outer wall's // path while it is unretracting, avoiding possible blips. - path->unretract_before_last_travel_move = path->retract && unretract_before_last_travel_move; + path->unretract_before_last_travel_move = path->retract && unretract_before_last_travel_move && z_seam_approach_distance == 0; } } // CURA-6675: // Retraction Minimal Travel Distance should work for all travel moves. If the travel move is shorter than the // Retraction Minimal Travel Distance, retraction should be disabled. - if (! is_first_travel_of_layer && last_planned_position_ && shorterThen(*last_planned_position_ - p, retraction_config.retraction_min_travel_distance)) + if (! is_first_travel_of_layer && last_planned_position_ && shorterThen(*last_planned_position_ - pos, retraction_config.retraction_min_travel_distance)) { path->retract = false; path->perform_z_hop = false; } // no combing? retract only when path is not shorter than minimum travel distance - if (! combed && ! is_first_travel_of_layer && last_planned_position_ && ! shorterThen(*last_planned_position_ - p, retraction_config.retraction_min_travel_distance)) + if (! combed && ! is_first_travel_of_layer && last_planned_position_ && ! shorterThen(*last_planned_position_ - pos, retraction_config.retraction_min_travel_distance)) { if (was_inside_) // when the previous location was from printing something which is considered inside (not support or prime tower etc) { // then move inside the printed part, so that we don't ooze on the outer wall while retraction, but on the inside of the print. @@ -518,7 +544,7 @@ GCodePath& LayerPlan::addTravel(const Point2LL& p, const bool force_retract, con // must start new travel path as retraction can be enabled or not depending on path length, etc. forceNewPathStart(); - GCodePath& ret = addTravel_simple(p, path); + GCodePath& ret = addTravel_simple(pos, path); was_inside_ = is_inside_; return ret; } @@ -570,6 +596,18 @@ void LayerPlan::addExtrusionMove( last_planned_position_ = p; } +void LayerPlan::addTravelBeforeSeam(const Point2LL& pos, const GCodePathConfig& config) +{ + GCodePathConfig unretraction_config = config; + unretraction_config.setPrintFeatureType(cura::PrintFeatureType::MoveUnretraction); + + GCodePath* path = getLatestPathWithConfig(unretraction_config, SpaceFillType::None); + path->is_approach_move = true; + path->points.push_back(pos); + + last_planned_position_ = pos; +} + void LayerPlan::addPolygon( const Polygon& polygon, int start_idx, @@ -666,8 +704,6 @@ void LayerPlan::addPolygonsByOptimizer( static constexpr double max_non_bridge_line_volume = MM2INT(100); // limit to accumulated "volume" of non-bridge lines which is proportional to distance x extrusion rate -static int i = 0; - void LayerPlan::addWallLine( const Point2LL& p0, const Point2LL& p1, @@ -1009,9 +1045,10 @@ void LayerPlan::addWall( bool always_retract, const bool is_closed, const bool is_reversed, - const bool is_linked_path) + const bool is_linked_path, + const bool smooth_approach) { - if (wall.empty()) + if (wall.size() < 2) { return; } @@ -1112,7 +1149,6 @@ void LayerPlan::addWall( } }; - bool first_line = true; const coord_t small_feature_max_length = settings.get("small_feature_max_length"); const bool is_small_feature = (small_feature_max_length > 0) && (layer_nr_ == 0 || wall.inset_idx_ == 0) && wall.shorterThan(small_feature_max_length); Ratio small_feature_speed_factor = settings.get((layer_nr_ == 0) ? "small_feature_speed_factor_0" : "small_feature_speed_factor"); @@ -1121,10 +1157,25 @@ void LayerPlan::addWall( const coord_t max_area_deviation = std::max(settings.get("meshfix_maximum_extrusion_area_deviation"), 1); // Square micrometres! const coord_t max_resolution = std::max(settings.get("meshfix_maximum_resolution"), coord_t(1)); - ExtrusionJunction p0 = wall[start_idx]; - const int direction = is_reversed ? -1 : 1; const size_t max_index = is_closed ? wall.size() + 1 : wall.size(); + + ExtrusionJunction p0 = wall[start_idx]; + if (smooth_approach) + { + addFirstWallTravel( + p0.p_, + always_retract, + &(wall[(wall.size() + start_idx + direction) % wall.size()].p_), + default_config, + flow_ratio, + p0.w_ * nominal_line_width_multiplier); + } + else + { + addTravel(p0.p_, always_retract); + } + for (size_t point_idx = 1; point_idx < max_index; point_idx++) { const ExtrusionJunction& p1 = wall[(wall.size() + start_idx + point_idx * direction) % wall.size()]; @@ -1134,12 +1185,6 @@ void LayerPlan::addWall( computeDistanceToBridgeStart((wall.size() + start_idx + point_idx * direction - 1) % wall.size()); } - if (first_line) - { - addTravel(p0.p_, always_retract); - first_line = false; - } - /* If the line has variable width, break it up into pieces with the following constraints: @@ -2135,7 +2180,6 @@ void LayerPlan::writeGCode(GCodeExport& gcode) cumulative_path_time = 0.; // reset to 0 for current path. GCodePath& path = paths[path_idx]; - if (path.perform_prime) { gcode.writePrimeTrain(extruder.settings_.get("speed_travel")); @@ -2235,10 +2279,10 @@ void LayerPlan::writeGCode(GCodeExport& gcode) } } } - const auto& extruder_changed = ! last_extrusion_config.has_value() || (last_extrusion_config.value().type != path.config.type); + const auto& extruder_changed = ! last_extrusion_config.has_value() || (last_extrusion_config.value().type_ != path.config.type_); if (! path.config.isTravelPath() && extruder_changed) { - gcode.writeTypeComment(path.config.type); + gcode.writeTypeComment(path.config.type_); if (path.config.isBridgePath()) { gcode.writeComment("BRIDGE"); @@ -2298,6 +2342,13 @@ void LayerPlan::writeGCode(GCodeExport& gcode) } continue; } + else if (path.is_approach_move) + { + const coord_t wall_line_width = path.mesh->settings.get("wall_line_width_0"); + const coord_t z_seam_approach_inset = path.mesh->settings.get("z_seam_approach_inset"); + gcode.writeApproachToSeam(path.points.back(), path.config.getSpeed(), path.mesh->layers[layer_nr_].parts, wall_line_width, z_seam_approach_inset); + continue; + } bool spiralize = path.spiralize; if (! spiralize) // normal (extrusion) move (with coasting) @@ -2320,8 +2371,8 @@ void LayerPlan::writeGCode(GCodeExport& gcode) insertTempOnTime(time, path_idx); const double extrude_speed = speed * path.speed_back_pressure_factor; - communication->sendLineTo(path.config.type, path.points[point_idx], path.getLineWidthForLayerView(), path.config.getLayerThickness(), extrude_speed); - gcode.writeExtrusion(path.points[point_idx], extrude_speed, path.getExtrusionMM3perMM(), path.config.type, update_extrusion_offset); + communication->sendLineTo(path.config.type_, path.points[point_idx], path.getLineWidthForLayerView(), path.config.getLayerThickness(), extrude_speed); + gcode.writeExtrusion(path.points[point_idx], extrude_speed, path.getExtrusionMM3perMM(), path.config.type_, update_extrusion_offset); prev_point = path.points[point_idx]; } @@ -2358,12 +2409,12 @@ void LayerPlan::writeGCode(GCodeExport& gcode) const double extrude_speed = speed * spiral_path.speed_back_pressure_factor; communication->sendLineTo( - spiral_path.config.type, + spiral_path.config.type_, spiral_path.points[point_idx], spiral_path.getLineWidthForLayerView(), spiral_path.config.getLayerThickness(), extrude_speed); - gcode.writeExtrusion(spiral_path.points[point_idx], extrude_speed, spiral_path.getExtrusionMM3perMM(), spiral_path.config.type, update_extrusion_offset); + gcode.writeExtrusion(spiral_path.points[point_idx], extrude_speed, spiral_path.getExtrusionMM3perMM(), spiral_path.config.type_, update_extrusion_offset); } // for layer display only - the loop finished at the seam vertex but as we started from // the location of the previous layer's seam vertex the loop may have a gap if this layer's @@ -2375,7 +2426,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) // the less the vertices are shifted and the less obvious is the ridge. If the layer display // really displayed a spiral rather than slices of a spiral, this would not be required. communication - ->sendLineTo(spiral_path.config.type, spiral_path.points[0], spiral_path.getLineWidthForLayerView(), spiral_path.config.getLayerThickness(), speed); + ->sendLineTo(spiral_path.config.type_, spiral_path.points[0], spiral_path.getLineWidthForLayerView(), spiral_path.config.getLayerThickness(), speed); } path_idx--; // the last path_idx didnt spiralize, so it's not part of the current spiralize path } @@ -2410,6 +2461,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) scripta::CellVDI{ "unretract_before_last_travel_move", &GCodePath::unretract_before_last_travel_move }, scripta::CellVDI{ "perform_z_hop", &GCodePath::perform_z_hop }, scripta::CellVDI{ "perform_prime", &GCodePath::perform_prime }, + scripta::CellVDI{ "is_approach_move", &GCodePath::is_approach_move }, scripta::CellVDI{ "fan_speed", &GCodePath::getFanSpeed }, scripta::CellVDI{ "is_travel_path", &GCodePath::isTravelPath }, scripta::CellVDI{ "extrusion_mm3_per_mm", &GCodePath::getExtrusionMM3perMM }); @@ -2561,13 +2613,13 @@ bool LayerPlan::writePathWithCoasting( auto [_, time] = extruder_plan.getPointToPointTime(prev_pt, path.points[point_idx], path); insertTempOnTime(time, path_idx); - communication->sendLineTo(path.config.type, path.points[point_idx], path.getLineWidthForLayerView(), path.config.getLayerThickness(), extrude_speed); - gcode.writeExtrusion(path.points[point_idx], extrude_speed, path.getExtrusionMM3perMM(), path.config.type); + communication->sendLineTo(path.config.type_, path.points[point_idx], path.getLineWidthForLayerView(), path.config.getLayerThickness(), extrude_speed); + gcode.writeExtrusion(path.points[point_idx], extrude_speed, path.getExtrusionMM3perMM(), path.config.type_); prev_pt = path.points[point_idx]; } - communication->sendLineTo(path.config.type, start, path.getLineWidthForLayerView(), path.config.getLayerThickness(), extrude_speed); - gcode.writeExtrusion(start, extrude_speed, path.getExtrusionMM3perMM(), path.config.type); + communication->sendLineTo(path.config.type_, start, path.getLineWidthForLayerView(), path.config.getLayerThickness(), extrude_speed); + gcode.writeExtrusion(start, extrude_speed, path.getExtrusionMM3perMM(), path.config.type_); } // write coasting path @@ -2603,6 +2655,7 @@ void LayerPlan::applyModifyPlugin() scripta::CellVDI{ "unretract_before_last_travel_move", &GCodePath::unretract_before_last_travel_move }, scripta::CellVDI{ "perform_z_hop", &GCodePath::perform_z_hop }, scripta::CellVDI{ "perform_prime", &GCodePath::perform_prime }, + scripta::CellVDI{ "is_approach_move", &GCodePath::is_approach_move }, scripta::CellVDI{ "fan_speed", &GCodePath::getFanSpeed }, scripta::CellVDI{ "is_travel_path", &GCodePath::isTravelPath }, scripta::CellVDI{ "extrusion_mm3_per_mm", &GCodePath::getExtrusionMM3perMM }); @@ -2623,6 +2676,7 @@ void LayerPlan::applyModifyPlugin() scripta::CellVDI{ "unretract_before_last_travel_move", &GCodePath::unretract_before_last_travel_move }, scripta::CellVDI{ "perform_z_hop", &GCodePath::perform_z_hop }, scripta::CellVDI{ "perform_prime", &GCodePath::perform_prime }, + scripta::CellVDI{ "is_approach_move", &GCodePath::is_approach_move }, scripta::CellVDI{ "fan_speed", &GCodePath::getFanSpeed }, scripta::CellVDI{ "is_travel_path", &GCodePath::isTravelPath }, scripta::CellVDI{ "extrusion_mm3_per_mm", &GCodePath::getExtrusionMM3perMM }); diff --git a/src/gcodeExport.cpp b/src/gcodeExport.cpp index 98d95d3f07..f4fd8cebcf 100644 --- a/src/gcodeExport.cpp +++ b/src/gcodeExport.cpp @@ -17,9 +17,12 @@ #include "Slice.h" #include "WipeScriptConfig.h" #include "communication/Communication.h" //To send layer view data. +#include "geometry/OpenPolyline.h" +#include "geometry/Point2D.h" #include "settings/types/LayerIndex.h" #include "sliceDataStorage.h" #include "utils/Date.h" +#include "utils/linearAlg2D.h" #include "utils/string.h" // MMtoStream, PrecisionedDouble namespace cura @@ -359,7 +362,7 @@ bool GCodeExport::getExtruderIsUsed(const int extruder_nr) const return extruder_attr_[extruder_nr].is_used_; } -Point2LL GCodeExport::getGcodePos(const coord_t x, const coord_t y, const int extruder_train) const +Point2LL GCodeExport::getGcodePos(const coord_t x, const coord_t y, const size_t extruder_train) const { return Point2LL(x, y) - extruder_attr_[extruder_train].nozzle_offset_; } @@ -478,10 +481,15 @@ double GCodeExport::mm3ToE(double mm3) } else { - return mm3 / extruder_attr_[current_extruder_].filament_area_; + return mm3ToMm(mm3); } } +double GCodeExport::mm3ToMm(double mm3) +{ + return mm3 / extruder_attr_[current_extruder_].filament_area_; +} + double GCodeExport::mmToE(double mm) { if (is_volumetric_) @@ -612,6 +620,7 @@ void GCodeExport::writeTypeComment(const PrintFeatureType& type) break; case PrintFeatureType::MoveCombing: case PrintFeatureType::MoveRetraction: + case PrintFeatureType::MoveUnretraction: case PrintFeatureType::NoneType: case PrintFeatureType::NumPrintFeatureTypes: // do nothing @@ -763,6 +772,14 @@ void GCodeExport::processInitialLayerBedTemperature() } } +void GCodeExport::sendTravelLine(const Point2LL& pos, PrintFeatureType line_type, const Velocity& speed) +{ + const int display_width = (line_type == PrintFeatureType::MoveRetraction || line_type == PrintFeatureType::MoveUnretraction) ? MM2INT(0.2) : MM2INT(0.1); + const double layer_height = Application::getInstance().current_slice_->scene.current_mesh_group->settings.get("layer_height"); + + Application::getInstance().communication_->sendLineTo(line_type, pos, display_width, layer_height, speed); +} + void GCodeExport::processInitialLayerTemperature(const SliceDataStorage& storage, const size_t start_extruder_nr) { Scene& scene = Application::getInstance().current_slice_->scene; @@ -871,6 +888,181 @@ void GCodeExport::writeTravel(const Point3LL& p, const Velocity& speed) writeTravel(p.x_, p.y_, p.z_ + is_z_hopped_, speed); } +void GCodeExport::writeApproachToSeam( + const Point2LL& pos, + const Velocity& speed, + const std::vector& current_mesh_parts, + const coord_t wall_line_width, + const coord_t z_seam_approach_inset) +{ + Point2LL current_position{ current_position_.x_, current_position_.y_ }; + + if (pos != current_position) + { + // Find current model part + const SliceLayerPart* current_part = nullptr; + for (const SliceLayerPart& slice_layer_part : current_mesh_parts) + { + if (! current_part && slice_layer_part.outline.inside(pos, true)) + { + current_part = &slice_layer_part; + } + } + + if (! current_part) + { + spdlog::error("Unable to find associated mesh layer part"); + assert(false && "Unable to find associated mesh layer part"); + std::exit(1); + } + + coord_t inset = wall_line_width / 2; // Move to the middle of the wall extrusion line + inset += std::max(z_seam_approach_inset, 10_mu); // Move inside the model, with a minimum value because 0 may cause unwanted intersections + Shape model_outline = current_part->outline.offset(-inset); + + // Compute required unretraction duration + const double total_unretraction_amount + = eToMm(extruder_attr_[current_extruder_].retraction_e_amount_current_) + mm3ToMm(extruder_attr_[current_extruder_].prime_volume_); // in mm + const double unretraction_speed = extruder_attr_[current_extruder_].last_retraction_prime_speed_; // in mm/s + const double total_unretraction_duration = total_unretraction_amount / unretraction_speed; // in seconds + + // Compute straight approach vector + const Point2LL approach_vector = pos - current_position; // in µm + const coord_t approach_distance = vSize(approach_vector); // in µm + const double approach_duration = INT2MM(approach_distance) / speed; // in seconds + const Point2D approach_direction_vector = Point2D(approach_vector).normalized(); // unitary vector, no unit + + // If true, we have enough time to unretract during the approach, split it into one travel move and one unretraction move. + // If false, We don't have enough time to unretract during the approach, so first make a partial static unretract, then do the full move while unretracting + bool approach_longer_than_unretract = approach_duration > total_unretraction_duration; + coord_t unretraction_path_length; // in µm + + if (approach_longer_than_unretract) + { + unretraction_path_length = MM2INT(total_unretraction_duration * speed); + } + else + { + unretraction_path_length = approach_distance; + } + + // Generate best-case scenario full straight segment + const Point2LL full_straight_vector = (approach_direction_vector * unretraction_path_length).toPoint2LL(); // in µm + const OpenPolyline full_straight_segment({ pos - full_straight_vector, pos }); // in µm + OpenPolyline unretract_move({ pos }); // in µm + + // Compute intersections of the full straight segment with the model + struct ModelIntersection + { + coord_t distance2_to_pos; // in µm2 + Point2LL intersection; // in µm + const Polygon* intersected_polygon; + Polyline::const_segments_iterator segment_iterator; + }; + + std::vector intersections; + for (const Polygon& polygon : model_outline) + { + for (auto segment_it = polygon.beginSegments(); segment_it != polygon.endSegments(); ++segment_it) + { + std::optional intersection // in µm + = LinearAlg2D::segmentSegmentIntersection(full_straight_segment[0], full_straight_segment[1], (*segment_it).start, (*segment_it).end); + if (intersection.has_value()) + { + const coord_t distance2_to_pos = vSize2(pos - intersection.value()); // in µm2 + intersections.emplace_back(distance2_to_pos, intersection.value(), &polygon, segment_it); + } + } + } + + // Now interpret the intersections: as the model outline has been offsetted inwards, the target position should now be outside the outline. + if (intersections.size() == 1) + { + // 1 intersection means the segment comes once inside the model and doesn't get out => that's the best case scenario, keep it as is + unretract_move = full_straight_segment; + } + else + { + const Polygon* walk_around_polygon{ nullptr }; + Polyline::const_segments_iterator segment_iterator; + if (! intersections.empty()) + { + // 2+ intersection means the segment comes inside the model but later gets out => keep the first part and generate the rest by following the external wall + // Sort intersection by closeness to the target position: the first in the list is the closest + std::sort( + intersections.begin(), + intersections.end(), + [](const ModelIntersection& intersection1, const ModelIntersection& intersection2) + { + return intersection1.distance2_to_pos < intersection2.distance2_to_pos; + }); + + unretract_move.insert(unretract_move.begin(), intersections[1].intersection); + walk_around_polygon = intersections[1].intersected_polygon; + segment_iterator = intersections[1].segment_iterator; + } + else + { + // 0 intersection means the full segment is outside the model => We have to create a path inside by following the external wall + ClosestPointPolygon closest_point = PolygonUtils::findClosest(pos, model_outline); + if (closest_point.isValid()) + { + walk_around_polygon = closest_point.poly_; + segment_iterator = walk_around_polygon->loopOverSegments( + walk_around_polygon->beginSegments(), + static_cast(closest_point.point_idx_) + 2); + } + } + + if (walk_around_polygon) + { + do + { + const coord_t current_unretraction_path_length = unretract_move.length(); // in µm + Point2LL new_segment = unretract_move.front() - (*segment_iterator).start; // in µm + const coord_t new_segment_length = vSize(new_segment); // in µm + const coord_t new_retraction_path_length = current_unretraction_path_length + new_segment_length; // in µm + + if (new_retraction_path_length > unretraction_path_length) + { + // Shorten segment + new_segment = vResize(new_segment, unretraction_path_length - current_unretraction_path_length); + } + + unretract_move.insert(unretract_move.begin(), unretract_move.front() - new_segment); + + segment_iterator = walk_around_polygon->loopOverSegments(segment_iterator, 1); + } while (unretract_move.length() < unretraction_path_length); + } + } + + // Whatever comes after, travel at print speed to start of unretract move + if (vSize2(unretract_move.front() - current_position_) > (1000 * 1000)) + { + writeTravel(unretract_move.front(), speed); + } + + const coord_t actual_unretraction_path_length = unretract_move.length(); // in µm + const double actual_unretraction_duration = INT2MM(actual_unretraction_path_length) / speed; // in seconds + const double actual_unretracted_amount = actual_unretraction_duration * unretraction_speed; // in mm + const double missing_unretraction_amount = total_unretraction_amount - actual_unretracted_amount; // in mm + + if (missing_unretraction_amount > 0.001) + { + // Unretraction path is too short for some reason, first make a partial static unretract + writeUnretractionAndPrime({}, {}, mmToE(missing_unretraction_amount)); + } + + // Now process the different parts of the unretraction move + for (auto it = unretract_move.beginSegments(); it != unretract_move.endSegments(); ++it) + { + double unretraction_factor = static_cast(vSize((*it).end - (*it).start)) / static_cast(actual_unretraction_path_length); + double unretraction_amount_segment = unretraction_factor * actual_unretracted_amount; + writeUnretractionAndPrime((*it).end, speed, mmToE(unretraction_amount_segment)); + } + } +} + void GCodeExport::writeExtrusion(const Point3LL& p, const Velocity& speed, double extrusion_mm3_per_mm, PrintFeatureType feature, bool update_extrusion_offset) { if (flavor_ == EGCodeFlavor::BFB) @@ -898,8 +1090,6 @@ void GCodeExport::writeMoveBFB(const int x, const int y, const int z, const Velo double extrusion_per_mm = mm3ToE(extrusion_mm3_per_mm); - Point2LL gcode_pos = getGcodePos(x, y, current_extruder_); - // For Bits From Bytes machines, we need to handle this completely differently. As they do not use E values but RPM values. double fspeed = speed * 60; double rpm = extrusion_per_mm * speed * 60; @@ -939,14 +1129,8 @@ void GCodeExport::writeMoveBFB(const int x, const int y, const int z, const Velo = 1.0; // 1.0 used as stub; BFB doesn't use the actual retraction amount; it performs retraction on the firmware automatically } } - *output_stream_ << "G1 X" << MMtoStream{ gcode_pos.X } << " Y" << MMtoStream{ gcode_pos.Y } << " Z" << MMtoStream{ z }; - *output_stream_ << " F" << PrecisionedDouble{ 1, fspeed } << new_line_; - current_position_ = Point3LL(x, y, z); - estimate_calculator_.plan( - TimeEstimateCalculator::Position(INT2MM(current_position_.x_), INT2MM(current_position_.y_), INT2MM(current_position_.z_), eToMm(current_e_value_)), - speed, - feature); + writeGCommand("G1", fspeed, x, y, z, 0.0, feature); } void GCodeExport::writeTravel(const coord_t x, const coord_t y, const coord_t z, const Velocity& speed) @@ -964,12 +1148,9 @@ void GCodeExport::writeTravel(const coord_t x, const coord_t y, const coord_t z, #endif // ASSERT_INSANE_OUTPUT const PrintFeatureType travel_move_type = extruder_attr_[current_extruder_].retraction_e_amount_current_ ? PrintFeatureType::MoveRetraction : PrintFeatureType::MoveCombing; - const int display_width = extruder_attr_[current_extruder_].retraction_e_amount_current_ ? MM2INT(0.2) : MM2INT(0.1); - const double layer_height = Application::getInstance().current_slice_->scene.current_mesh_group->settings.get("layer_height"); - Application::getInstance().communication_->sendLineTo(travel_move_type, Point2LL(x, y), display_width, layer_height, speed); + sendTravelLine(Point2LL(x, y), travel_move_type, speed); - *output_stream_ << "G0"; - writeFXYZE(speed, x, y, z, current_e_value_, travel_move_type); + writeGCommand("G0", speed, x, y, z, 0.0, travel_move_type); } void GCodeExport::writeExtrusion( @@ -1044,98 +1225,120 @@ void GCodeExport::writeExtrusion( } extruder_attr_[current_extruder_].last_e_value_after_wipe_ += extrusion_per_mm * diff_length; - const double new_e_value = current_e_value_ + extrusion_per_mm * diff_length; + const double e_delta = extrusion_per_mm * diff_length; - *output_stream_ << "G1"; - writeFXYZE(speed, x, y, z, new_e_value, feature); + writeGCommand("G1", speed, x, y, z, e_delta, feature); } -void GCodeExport::writeFXYZE(const Velocity& speed, const coord_t x, const coord_t y, const coord_t z, const double e, const PrintFeatureType& feature) +void GCodeExport::writeGCommand( + const char* command_name, + const Velocity& speed, + const coord_t x, + const coord_t y, + const coord_t z, + const double e_delta, + const PrintFeatureType feature) { - if (current_speed_ != speed) - { - *output_stream_ << " F" << PrecisionedDouble{ 1, speed * 60 }; - current_speed_ = speed; - } - - Point2LL gcode_pos = getGcodePos(x, y, current_extruder_); + const Point2LL gcode_pos = getGcodePos(x, y, current_extruder_); total_bounding_box_.include(Point3LL(gcode_pos.X, gcode_pos.Y, z)); - *output_stream_ << " X" << MMtoStream{ gcode_pos.X } << " Y" << MMtoStream{ gcode_pos.Y }; - if (z != current_position_.z_) - { - *output_stream_ << " Z" << MMtoStream{ z }; - } - if (e + current_e_offset_ != current_e_value_) + const double actual_e_delta = e_delta + current_e_offset_; + const bool add_speed = current_speed_ != speed; + const bool add_x = gcode_pos.X != current_position_.x_; + const bool add_y = gcode_pos.Y != current_position_.y_; + const bool add_z = z != current_position_.z_; + const bool add_e = std::fabs(actual_e_delta) >= 0.00001; + + if (add_speed || add_x || add_y || add_z || add_e) { - const double output_e = (relative_extrusion_) ? e + current_e_offset_ - current_e_value_ : e + current_e_offset_; - *output_stream_ << " " << extruder_attr_[current_extruder_].extruder_character_ << PrecisionedDouble{ 5, output_e }; - } - *output_stream_ << new_line_; + *output_stream_ << command_name; - current_position_ = Point3LL(x, y, z); - current_e_value_ = e; - estimate_calculator_.plan(TimeEstimateCalculator::Position(INT2MM(x), INT2MM(y), INT2MM(z), eToMm(e)), speed, feature); -} + if (add_speed) + { + *output_stream_ << " F" << PrecisionedDouble{ 1, speed * 60 }; + current_speed_ = speed; + } -void GCodeExport::writeUnretractionAndPrime() -{ - const double prime_volume = extruder_attr_[current_extruder_].prime_volume_; - const double prime_volume_e = mm3ToE(prime_volume); - current_e_value_ += prime_volume_e; - if (extruder_attr_[current_extruder_].retraction_e_amount_current_) - { - if (extruder_attr_[current_extruder_].machine_firmware_retract_) - { // note that BFB is handled differently - *output_stream_ << "G11" << new_line_; - // Assume default UM2 retraction settings. - if (prime_volume != 0) - { - const double output_e = (relative_extrusion_) ? prime_volume_e : current_e_value_; - *output_stream_ << "G1 F" << PrecisionedDouble{ 1, extruder_attr_[current_extruder_].last_retraction_prime_speed_ * 60 } << " " - << extruder_attr_[current_extruder_].extruder_character_ << PrecisionedDouble{ 5, output_e } << new_line_; - current_speed_ = extruder_attr_[current_extruder_].last_retraction_prime_speed_; - } - estimate_calculator_.plan( - TimeEstimateCalculator::Position(INT2MM(current_position_.x_), INT2MM(current_position_.y_), INT2MM(current_position_.z_), eToMm(current_e_value_)), - 25.0, - PrintFeatureType::MoveRetraction); + if (add_x) + { + *output_stream_ << " X" << MMtoStream{ gcode_pos.X }; + current_position_.x_ = x; } - else + if (add_y) + { + *output_stream_ << " Y" << MMtoStream{ gcode_pos.Y }; + current_position_.y_ = y; + } + if (add_z) { - current_e_value_ += extruder_attr_[current_extruder_].retraction_e_amount_current_; - const double output_e = (relative_extrusion_) ? extruder_attr_[current_extruder_].retraction_e_amount_current_ + prime_volume_e : current_e_value_; - *output_stream_ << "G1 F" << PrecisionedDouble{ 1, extruder_attr_[current_extruder_].last_retraction_prime_speed_ * 60 } << " " - << extruder_attr_[current_extruder_].extruder_character_ << PrecisionedDouble{ 5, output_e } << new_line_; - current_speed_ = extruder_attr_[current_extruder_].last_retraction_prime_speed_; - estimate_calculator_.plan( - TimeEstimateCalculator::Position(INT2MM(current_position_.x_), INT2MM(current_position_.y_), INT2MM(current_position_.z_), eToMm(current_e_value_)), - current_speed_, - PrintFeatureType::MoveRetraction); + *output_stream_ << " Z" << MMtoStream{ z }; + current_position_.z_ = z; } + + if (add_e) + { + current_e_value_ += actual_e_delta; + const double output_e = (relative_extrusion_) ? actual_e_delta : current_e_value_; + *output_stream_ << " " << extruder_attr_[current_extruder_].extruder_character_ << PrecisionedDouble{ 5, output_e }; + } + + *output_stream_ << new_line_; + + estimate_calculator_.plan(TimeEstimateCalculator::Position(INT2MM(x), INT2MM(y), INT2MM(z), eToMm(current_e_value_)), speed, feature); } - else if (prime_volume != 0.0) - { - const double output_e = (relative_extrusion_) ? prime_volume_e : current_e_value_; - *output_stream_ << "G1 F" << PrecisionedDouble{ 1, extruder_attr_[current_extruder_].last_retraction_prime_speed_ * 60 } << " " - << extruder_attr_[current_extruder_].extruder_character_; - *output_stream_ << PrecisionedDouble{ 5, output_e } << new_line_; - current_speed_ = extruder_attr_[current_extruder_].last_retraction_prime_speed_; +} + +void GCodeExport::writeUnretractionAndPrime(const std::optional& pos, const std::optional& override_speed, const std::optional& override_amount) +{ + const double prime_volume = extruder_attr_[current_extruder_].prime_volume_; + const double prime_volume_e = mm3ToE(prime_volume); + double unretract_e = extruder_attr_[current_extruder_].retraction_e_amount_current_; + const Point2LL actual_pos = pos.value_or(Point2LL(current_position_.x_, current_position_.y_)); + + if (extruder_attr_[current_extruder_].machine_firmware_retract_) + { // note that BFB unretract is handled differently + *output_stream_ << "G11" << new_line_; + current_e_value_ += unretract_e; + unretract_e = 0.0; + + // Assume default UM2 retraction settings. estimate_calculator_.plan( TimeEstimateCalculator::Position(INT2MM(current_position_.x_), INT2MM(current_position_.y_), INT2MM(current_position_.z_), eToMm(current_e_value_)), - current_speed_, - PrintFeatureType::NoneType); + 25.0, + PrintFeatureType::MoveUnretraction); + } + + const double total_remaining_unretract = unretract_e + prime_volume_e; + const double actual_total_remaining_unretract = override_amount.value_or(total_remaining_unretract); + + if (actual_total_remaining_unretract != 0.0) + { + Velocity actual_speed = override_speed.value_or(extruder_attr_[current_extruder_].last_retraction_prime_speed_); + + PrintFeatureType line_type = unretract_e != 0.0 ? PrintFeatureType::MoveUnretraction : PrintFeatureType::NoneType; + writeGCommand("G1", actual_speed, actual_pos.X, actual_pos.Y, current_position_.z_, actual_total_remaining_unretract, line_type); + + if (pos.has_value()) + { + sendTravelLine(pos.value(), line_type, actual_speed); + } } + extruder_attr_[current_extruder_].prime_volume_ = 0.0; - if (getCurrentExtrudedVolume() > 10000.0 && flavor_ != EGCodeFlavor::BFB - && flavor_ != EGCodeFlavor::MAKERBOT) // According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it - // every 10m, just to be sure. + if (override_amount.has_value()) { - resetExtrusionValue(); + extruder_attr_[current_extruder_].retraction_e_amount_current_ = total_remaining_unretract - override_amount.value(); } - if (extruder_attr_[current_extruder_].retraction_e_amount_current_) + else { + if (getCurrentExtrudedVolume() > 10000.0 && flavor_ != EGCodeFlavor::BFB + && flavor_ != EGCodeFlavor::MAKERBOT) // According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset + // it every 10m, just to be sure. + { + resetExtrusionValue(); + } + extruder_attr_[current_extruder_].retraction_e_amount_current_ = 0.0; } } @@ -1215,15 +1418,9 @@ void GCodeExport::writeRetraction(const RetractionConfig& config, bool force, bo else { double speed = ((retraction_diff_e_amount < 0.0) ? config.speed : extr_attr.last_retraction_prime_speed_); - current_e_value_ += retraction_diff_e_amount; - const double output_e = (relative_extrusion_) ? retraction_diff_e_amount : current_e_value_; - *output_stream_ << "G1 F" << PrecisionedDouble{ 1, speed * 60 } << " " << extr_attr.extruder_character_ << PrecisionedDouble{ 5, output_e } << new_line_; - current_speed_ = speed; - estimate_calculator_.plan( - TimeEstimateCalculator::Position(INT2MM(current_position_.x_), INT2MM(current_position_.y_), INT2MM(current_position_.z_), eToMm(current_e_value_)), - current_speed_, - PrintFeatureType::MoveRetraction); extr_attr.last_retraction_prime_speed_ = config.primeSpeed; + + writeGCommand("G1", speed, current_position_.x_, current_position_.y_, current_position_.z_, retraction_diff_e_amount, PrintFeatureType::MoveRetraction); } extr_attr.retraction_e_amount_current_ = new_retraction_e_amount; // suppose that for UM2 the retraction amount in the firmware is equal to the provided amount diff --git a/src/geometry/ClosedPolyline.cpp b/src/geometry/ClosedPolyline.cpp index c4890c48f1..19ded8d79c 100644 --- a/src/geometry/ClosedPolyline.cpp +++ b/src/geometry/ClosedPolyline.cpp @@ -54,4 +54,25 @@ OpenPolyline ClosedPolyline::toPseudoOpenPolyline() const return open_polyline; } +ClosedPolyline::const_segments_iterator ClosedPolyline::loopOverSegments(const const_segments_iterator& start, const_segments_iterator::difference_type diff) const +{ + const auto segments_count = static_cast(segmentsCount()); + if (segments_count > 1) + { + const const_segments_iterator::difference_type start_index = std::distance(beginSegments(), start); + const_segments_iterator::difference_type target_index = start_index + diff; + while (target_index < 0) + { + target_index += segments_count; + } + target_index = target_index % segments_count; + + return std::next(beginSegments(), target_index); + } + else + { + return start; + } +} + } // namespace cura diff --git a/src/geometry/Point2D.cpp b/src/geometry/Point2D.cpp new file mode 100644 index 0000000000..85a2678eae --- /dev/null +++ b/src/geometry/Point2D.cpp @@ -0,0 +1,65 @@ +// Copyright (c) 2024 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher. + +#include "geometry/Point2D.h" + +#include + +namespace cura +{ + +Point2D::Point2D(double x, double y) + : x_(x) + , y_(y) +{ +} + +Point2D::Point2D(const Point2LL& other) + : x_(static_cast(other.X)) + , y_(static_cast(other.Y)) +{ +} + +Point2D Point2D::operator*(const coord_t scale) const +{ + return Point2D(x_ * static_cast(scale), y_ * static_cast(scale)); +} + +Point2D Point2D::operator*(const double scale) const +{ + return Point2D(x_ * scale, y_ * scale); +} + +Point2D Point2D::operator/(const double scale) const +{ + return Point2D(x_ / scale, y_ / scale); +} + +double Point2D::size() const +{ + return std::sqrt(size2()); +} + +double Point2D::size2() const +{ + return x_ * x_ + y_ * y_; +} + +Point2D Point2D::normalized() const +{ + return (*this) / size(); +} + +void Point2D::normalize() +{ + double actual_size = size(); + x_ /= actual_size; + y_ /= actual_size; +} + +Point2LL Point2D::toPoint2LL() const +{ + return Point2LL(std::llrint(x_), std::llrint(y_)); +} + +} // namespace cura diff --git a/src/geometry/Shape.cpp b/src/geometry/Shape.cpp index 8156bb5fa2..8e713ad42b 100644 --- a/src/geometry/Shape.cpp +++ b/src/geometry/Shape.cpp @@ -227,68 +227,6 @@ bool Shape::inside(const Point2LL& p, bool border_result) const return (poly_count_inside % 2) == 1; } -size_t Shape::findInside(const Point2LL& p, bool border_result) const -{ - if (empty()) - { - return 0; - } - - // NOTE: Keep these vectors fixed-size, they replace an (non-standard, sized at runtime) arrays. - std::vector min_x(size(), std::numeric_limits::max()); - std::vector crossings(size()); - - for (size_t poly_idx = 0; poly_idx < size(); poly_idx++) - { - const Polygon& poly = (*this)[poly_idx]; - Point2LL p0 = poly.back(); - for (const Point2LL& p1 : poly) - { - short comp = LinearAlg2D::pointLiesOnTheRightOfLine(p, p0, p1); - if (comp == 1) - { - crossings[poly_idx]++; - int64_t x; - if (p1.Y == p0.Y) - { - x = p0.X; - } - else - { - x = p0.X + (p1.X - p0.X) * (p.Y - p0.Y) / (p1.Y - p0.Y); - } - min_x[poly_idx] = std::min(x, min_x[poly_idx]); - } - else if (border_result && comp == 0) - { - return poly_idx; - } - p0 = p1; - } - } - - int64_t min_x_uneven = std::numeric_limits::max(); - size_t ret = NO_INDEX; - size_t n_unevens = 0; - for (size_t array_idx = 0; array_idx < size(); array_idx++) - { - if (crossings[array_idx] % 2 == 1) - { - n_unevens++; - if (min_x[array_idx] < min_x_uneven) - { - min_x_uneven = min_x[array_idx]; - ret = array_idx; - } - } - } - if (n_unevens % 2 == 0) - { - ret = NO_INDEX; - } - return ret; -} - template OpenLinesSet Shape::intersection(const LinesSet& polylines, bool restitch, const coord_t max_stitch_distance) const { diff --git a/src/plugins/converters.cpp b/src/plugins/converters.cpp index f53d2cbc7c..2e28b16311 100644 --- a/src/plugins/converters.cpp +++ b/src/plugins/converters.cpp @@ -352,9 +352,10 @@ gcode_paths_modify_request::value_type gcode_path->set_perform_prime(path.perform_prime); gcode_path->set_skip_agressive_merge_hint(path.skip_agressive_merge_hint); gcode_path->set_done(path.done); + // gcode_path->set_is_approach_move(path.is_approach_move); gcode_path->set_fan_speed(path.getFanSpeed()); gcode_path->set_mesh_name(path.mesh ? path.mesh->mesh_name : ""); - gcode_path->set_feature(getPrintFeature(path.config.type)); + gcode_path->set_feature(getPrintFeature(path.config.type_)); gcode_path->mutable_speed_derivatives()->set_velocity(path.config.getSpeed()); gcode_path->mutable_speed_derivatives()->set_acceleration(path.config.getAcceleration()); gcode_path->mutable_speed_derivatives()->set_jerk(path.config.getJerk()); @@ -423,7 +424,7 @@ gcode_paths_modify_request::value_type [[nodiscard]] GCodePathConfig gcode_paths_modify_response::buildConfig(const v0::GCodePath& path) { return { .z_offset = path.z_offset(), - .type = getPrintFeatureType(path.feature()), + .type_ = getPrintFeatureType(path.feature()), .line_width = path.line_width(), .layer_thickness = path.layer_thickness(), .flow = path.flow_ratio(), @@ -470,6 +471,7 @@ gcode_paths_modify_response::native_value_type .skip_agressive_merge_hint = gcode_path_msg.skip_agressive_merge_hint(), .done = gcode_path_msg.done(), .fan_speed = gcode_path_msg.fan_speed(), + //.is_approach_move = gcode_path_msg.is_approach_move(), }; path.points = gcode_path_msg.path().path() diff --git a/src/settings/MeshPathConfigs.cpp b/src/settings/MeshPathConfigs.cpp index fffc0d311a..623efcfea1 100644 --- a/src/settings/MeshPathConfigs.cpp +++ b/src/settings/MeshPathConfigs.cpp @@ -12,7 +12,7 @@ namespace cura { MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t layer_thickness, const LayerIndex layer_nr, const std::vector& line_width_factor_per_extruder) - : inset0_config{ .type = PrintFeatureType::OuterWall, + : inset0_config{ .type_ = PrintFeatureType::OuterWall, .line_width = static_cast( mesh.settings.get("wall_line_width_0") * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr_]), .layer_thickness = layer_thickness, @@ -20,7 +20,7 @@ MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t lay .speed_derivatives = { .speed = mesh.settings.get("speed_wall_0"), .acceleration = mesh.settings.get("acceleration_wall_0"), .jerk = mesh.settings.get("jerk_wall_0") } } - , insetX_config{ .type = PrintFeatureType::InnerWall, + , insetX_config{ .type_ = PrintFeatureType::InnerWall, .line_width = static_cast( mesh.settings.get("wall_line_width_x") * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr_]), .layer_thickness = layer_thickness, @@ -28,7 +28,7 @@ MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t lay .speed_derivatives = { .speed = mesh.settings.get("speed_wall_x"), .acceleration = mesh.settings.get("acceleration_wall_x"), .jerk = mesh.settings.get("jerk_wall_x") } } - , inset0_roofing_config{ .type = PrintFeatureType::OuterWall, + , inset0_roofing_config{ .type_ = PrintFeatureType::OuterWall, .line_width = static_cast( mesh.settings.get("wall_line_width_0") * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr_]), @@ -38,7 +38,7 @@ MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t lay .speed_derivatives = { .speed = mesh.settings.get("speed_wall_0_roofing"), .acceleration = mesh.settings.get("acceleration_wall_0_roofing"), .jerk = mesh.settings.get("jerk_wall_0_roofing") } } - , insetX_roofing_config{ .type = PrintFeatureType::InnerWall, + , insetX_roofing_config{ .type_ = PrintFeatureType::InnerWall, .line_width = static_cast( mesh.settings.get("wall_line_width_x") * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr_]), @@ -48,7 +48,7 @@ MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t lay .speed_derivatives = { .speed = mesh.settings.get("speed_wall_x_roofing"), .acceleration = mesh.settings.get("acceleration_wall_x_roofing"), .jerk = mesh.settings.get("jerk_wall_x_roofing") } } - , bridge_inset0_config{ .type = PrintFeatureType::OuterWall, + , bridge_inset0_config{ .type_ = PrintFeatureType::OuterWall, .line_width = static_cast( mesh.settings.get("wall_line_width_0") * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr_]), @@ -59,7 +59,7 @@ MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t lay .jerk = mesh.settings.get("jerk_wall_0") }, .is_bridge_path = true, .fan_speed = mesh.settings.get("bridge_fan_speed") * 100.0 } - , bridge_insetX_config{ .type = PrintFeatureType::InnerWall, + , bridge_insetX_config{ .type_ = PrintFeatureType::InnerWall, .line_width = static_cast( mesh.settings.get("wall_line_width_x") * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr_]), @@ -70,7 +70,7 @@ MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t lay .jerk = mesh.settings.get("jerk_wall_x") }, .is_bridge_path = true, .fan_speed = mesh.settings.get("bridge_fan_speed") * 100.0 } - , skin_config{ .type = PrintFeatureType::Skin, + , skin_config{ .type_ = PrintFeatureType::Skin, .line_width = static_cast( mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr_]), .layer_thickness = layer_thickness, @@ -78,7 +78,7 @@ MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t lay .speed_derivatives = { .speed = mesh.settings.get("speed_topbottom"), .acceleration = mesh.settings.get("acceleration_topbottom"), .jerk = mesh.settings.get("jerk_topbottom") } } - , bridge_skin_config{ .type = PrintFeatureType::Skin, + , bridge_skin_config{ .type_ = PrintFeatureType::Skin, .line_width = static_cast( mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr_]), @@ -89,7 +89,7 @@ MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t lay .jerk = mesh.settings.get("jerk_topbottom") }, .is_bridge_path = true, .fan_speed = mesh.settings.get("bridge_fan_speed") * 100.0 } - , bridge_skin_config2{ .type = PrintFeatureType::Skin, + , bridge_skin_config2{ .type_ = PrintFeatureType::Skin, .line_width = static_cast( mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr_]), @@ -100,7 +100,7 @@ MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t lay .jerk = mesh.settings.get("jerk_topbottom") }, .is_bridge_path = true, .fan_speed = mesh.settings.get("bridge_fan_speed_2") * 100.0 } - , bridge_skin_config3{ .type = PrintFeatureType::Skin, + , bridge_skin_config3{ .type_ = PrintFeatureType::Skin, .line_width = static_cast( mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr_]), @@ -111,14 +111,14 @@ MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t lay .jerk = mesh.settings.get("jerk_topbottom") }, .is_bridge_path = true, .fan_speed = mesh.settings.get("bridge_fan_speed_3") * 100.0 } - , roofing_config{ .type = PrintFeatureType::Skin, + , roofing_config{ .type_ = PrintFeatureType::Skin, .line_width = mesh.settings.get("roofing_line_width"), .layer_thickness = layer_thickness, .flow = mesh.settings.get("roofing_material_flow") * (layer_nr == 0 ? mesh.settings.get("material_flow_layer_0") : Ratio{ 1.0 }), .speed_derivatives = { .speed = mesh.settings.get("speed_roofing"), .acceleration = mesh.settings.get("acceleration_roofing"), .jerk = mesh.settings.get("jerk_roofing") } } - , ironing_config{ .type = PrintFeatureType::Skin, + , ironing_config{ .type_ = PrintFeatureType::Skin, .line_width = mesh.settings.get("ironing_line_spacing"), .layer_thickness = layer_thickness, .flow = mesh.settings.get("ironing_flow"), @@ -132,7 +132,7 @@ MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t lay for (const auto combine_idx : ranges::views::iota(1, MAX_INFILL_COMBINE + 1)) { infill_config.emplace_back(GCodePathConfig{ - .type = PrintFeatureType::Infill, + .type_ = PrintFeatureType::Infill, .line_width = static_cast( mesh.settings.get("infill_line_width") * line_width_factor_per_extruder[mesh.settings.get("infill_extruder_nr").extruder_nr_]), .layer_thickness = layer_thickness, diff --git a/src/settings/PathConfigStorage.cpp b/src/settings/PathConfigStorage.cpp index c0edd6d0e8..2bb44376b8 100644 --- a/src/settings/PathConfigStorage.cpp +++ b/src/settings/PathConfigStorage.cpp @@ -43,21 +43,21 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye , support_roof_train(Application::getInstance().current_slice_->scene.extruders[support_roof_extruder_nr]) , support_bottom_train(Application::getInstance().current_slice_->scene.extruders[support_bottom_extruder_nr]) , line_width_factor_per_extruder(PathConfigStorage::getLineWidthFactorPerExtruder(layer_nr)) - , raft_base_config(GCodePathConfig{ .type = PrintFeatureType::SupportInterface, + , raft_base_config(GCodePathConfig{ .type_ = PrintFeatureType::SupportInterface, .line_width = raft_base_train.settings_.get("raft_base_line_width"), .layer_thickness = raft_base_train.settings_.get("raft_base_thickness"), .flow = Ratio(1.0), .speed_derivatives = SpeedDerivatives{ .speed = raft_base_train.settings_.get("raft_base_speed"), .acceleration = raft_base_train.settings_.get("raft_base_acceleration"), .jerk = raft_base_train.settings_.get("raft_base_jerk") } }) - , raft_interface_config(GCodePathConfig{ .type = PrintFeatureType::Support, + , raft_interface_config(GCodePathConfig{ .type_ = PrintFeatureType::Support, .line_width = raft_interface_train.settings_.get("raft_interface_line_width"), .layer_thickness = raft_interface_train.settings_.get("raft_interface_thickness"), .flow = Ratio(1.0), .speed_derivatives = SpeedDerivatives{ .speed = raft_interface_train.settings_.get("raft_interface_speed"), .acceleration = raft_interface_train.settings_.get("raft_interface_acceleration"), .jerk = raft_interface_train.settings_.get("raft_interface_jerk") } }) - , raft_surface_config(GCodePathConfig{ .type = PrintFeatureType::SupportInterface, + , raft_surface_config(GCodePathConfig{ .type_ = PrintFeatureType::SupportInterface, .line_width = raft_surface_train.settings_.get("raft_surface_line_width"), .layer_thickness = raft_surface_train.settings_.get("raft_surface_thickness"), .flow = Ratio(1.0), @@ -65,7 +65,7 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye .acceleration = raft_surface_train.settings_.get("raft_surface_acceleration"), .jerk = raft_surface_train.settings_.get("raft_surface_jerk") } }) , support_roof_config(GCodePathConfig{ - .type = PrintFeatureType::SupportInterface, + .type_ = PrintFeatureType::SupportInterface, .line_width = static_cast(support_roof_train.settings_.get("support_roof_line_width") * line_width_factor_per_extruder[support_roof_extruder_nr]), .layer_thickness = layer_thickness, .flow = support_roof_train.settings_.get("support_roof_material_flow") @@ -74,7 +74,7 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye .acceleration = support_roof_train.settings_.get("acceleration_support_roof"), .jerk = support_roof_train.settings_.get("jerk_support_roof") } }) , support_bottom_config(GCodePathConfig{ - .type = PrintFeatureType::SupportInterface, + .type_ = PrintFeatureType::SupportInterface, .line_width = static_cast(support_bottom_train.settings_.get("support_bottom_line_width") * line_width_factor_per_extruder[support_bottom_extruder_nr]), .layer_thickness = layer_thickness, .flow = support_roof_train.settings_.get("support_bottom_material_flow") @@ -91,7 +91,7 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye for (size_t extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++) { const ExtruderTrain& train = Application::getInstance().current_slice_->scene.extruders[extruder_nr]; - travel_config_per_extruder.emplace_back(GCodePathConfig{ .type = PrintFeatureType::MoveCombing, + travel_config_per_extruder.emplace_back(GCodePathConfig{ .type_ = PrintFeatureType::MoveCombing, .line_width = 0, .layer_thickness = 0, .flow = 0.0, @@ -99,7 +99,7 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye .acceleration = train.settings_.get("acceleration_travel"), .jerk = train.settings_.get("jerk_travel") } }); skirt_brim_config_per_extruder.emplace_back( - GCodePathConfig{ .type = PrintFeatureType::SkirtBrim, + GCodePathConfig{ .type_ = PrintFeatureType::SkirtBrim, .line_width = static_cast( train.settings_.get("skirt_brim_line_width") * ((mesh_group_settings.get("adhesion_type") == EPlatformAdhesion::RAFT) @@ -112,7 +112,7 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye .acceleration = train.settings_.get("acceleration_skirt_brim"), .jerk = train.settings_.get("jerk_skirt_brim") } }); prime_tower_config_per_extruder.emplace_back(GCodePathConfig{ - .type = PrintFeatureType::PrimeTower, + .type_ = PrintFeatureType::PrimeTower, .line_width = static_cast( train.settings_.get("prime_tower_line_width") * ((mesh_group_settings.get("adhesion_type") == EPlatformAdhesion::RAFT) ? 1.0_r : line_width_factor_per_extruder[extruder_nr])), @@ -135,7 +135,7 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye for (int combine_idx = 0; combine_idx < MAX_INFILL_COMBINE; combine_idx++) { support_infill_config.emplace_back( - GCodePathConfig{ .type = PrintFeatureType::Support, + GCodePathConfig{ .type_ = PrintFeatureType::Support, .line_width = static_cast(support_infill_train.settings_.get("support_line_width") * support_infill_line_width_factor), .layer_thickness = layer_thickness, .flow = support_infill_train.settings_.get("support_material_flow") diff --git a/src/utils/linearAlg2D.cpp b/src/utils/linearAlg2D.cpp index 64b50cee10..6d94fea2a1 100644 --- a/src/utils/linearAlg2D.cpp +++ b/src/utils/linearAlg2D.cpp @@ -304,4 +304,21 @@ bool LinearAlg2D::lineLineIntersection(const Point2LL& a, const Point2LL& b, con return true; } +std::optional LinearAlg2D::segmentSegmentIntersection(const Point2LL& s1_start, const Point2LL& s1_end, const Point2LL& s2_start, const Point2LL& s2_end) +{ + std::optional result; + + Point2LL line_intersection_result; + if (lineLineIntersection(s1_start, s1_end, s2_start, s2_end, line_intersection_result)) + { + // Lines formed by the segments are not parallel, now check that the intersection is actually part of both the segments + if (pointIsProjectedBeyondLine(line_intersection_result, s1_start, s1_end) == 0 && pointIsProjectedBeyondLine(line_intersection_result, s2_start, s2_end) == 0) + { + result = line_intersection_result; + } + } + + return result; +} + } // namespace cura diff --git a/tests/ExtruderPlanTest.cpp b/tests/ExtruderPlanTest.cpp index ed15fc4a9f..9e219572c7 100644 --- a/tests/ExtruderPlanTest.cpp +++ b/tests/ExtruderPlanTest.cpp @@ -72,12 +72,12 @@ class ExtruderPlanTestPathCollection GCodePathConfig travel_config; ExtruderPlanTestPathCollection() - : extrusion_config(GCodePathConfig{ .type = PrintFeatureType::OuterWall, + : extrusion_config(GCodePathConfig{ .type_ = PrintFeatureType::OuterWall, .line_width = 400, .layer_thickness = 100, .flow = 1.0_r, .speed_derivatives = SpeedDerivatives{ .speed = 50.0, .acceleration = 1000.0, .jerk = 10.0 } }) - , travel_config(GCodePathConfig{ .type = PrintFeatureType::MoveCombing, + , travel_config(GCodePathConfig{ .type_ = PrintFeatureType::MoveCombing, .line_width = 0, .layer_thickness = 100, .flow = 0.0_r,