diff --git a/include/BeadingStrategy/BeadingStrategyFactory.h b/include/BeadingStrategy/BeadingStrategyFactory.h index 55cfe6d2ac..d9f7426598 100644 --- a/include/BeadingStrategy/BeadingStrategyFactory.h +++ b/include/BeadingStrategy/BeadingStrategyFactory.h @@ -13,22 +13,19 @@ namespace cura class BeadingStrategyFactory { public: - static BeadingStrategyPtr makeStrategy - ( - const coord_t preferred_bead_width_outer = MM2INT(0.5), - const coord_t preferred_bead_width_inner = MM2INT(0.5), - const coord_t preferred_transition_length = MM2INT(0.4), - const float transitioning_angle = M_PI / 4.0, - const bool print_thin_walls = false, - const coord_t min_bead_width = 0, - const coord_t min_feature_size = 0, - const Ratio wall_split_middle_threshold = 0.5_r, - const Ratio wall_add_middle_threshold = 0.5_r, - const coord_t max_bead_count = 0, - const coord_t outer_wall_offset = 0, - const int inward_distributed_center_wall_count = 2, - const Ratio minimum_variable_line_ratio = 0.5 - ); + static BeadingStrategyPtr makeStrategy(const coord_t preferred_bead_width_outer = 0.5_mm, + const coord_t preferred_bead_width_inner = 0.5_mm, + const coord_t preferred_transition_length = 0.4_mm, + const float transitioning_angle = M_PI / 4.0, + const bool print_thin_walls = false, + const coord_t min_bead_width = 0, + const coord_t min_feature_size = 0, + const Ratio wall_split_middle_threshold = 0.5_r, + const Ratio wall_add_middle_threshold = 0.5_r, + const coord_t max_bead_count = 0, + const coord_t outer_wall_offset = 0, + const int inward_distributed_center_wall_count = 2, + const Ratio minimum_variable_line_ratio = 0.5); }; } // namespace cura diff --git a/include/PathOrderMonotonic.h b/include/PathOrderMonotonic.h index 2980dd6e8d..b4af82b4cc 100644 --- a/include/PathOrderMonotonic.h +++ b/include/PathOrderMonotonic.h @@ -97,7 +97,7 @@ class PathOrderMonotonic : public PathOrder return a_projection < b_projection; }); //Create a bucket grid to be able to find adjacent lines quickly. - SparsePointGridInclusive line_bucket_grid(MM2INT(2)); //Grid size of 2mm. + SparsePointGridInclusive line_bucket_grid(2_mm); // Grid size of 2mm. for(Path* polyline : polylines) { if(polyline->converted->empty()) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index 2ceec94375..b36e1d7f9a 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -579,7 +579,7 @@ class PathOrderOptimizer if(combing_grid == nullptr) { - constexpr coord_t grid_size = 2000; //2mm grid cells. Smaller will use more memory, but reduce chance of unnecessary collision checks. + constexpr coord_t grid_size = 2_mm; // 2mm grid cells. Smaller will use more memory, but reduce chance of unnecessary collision checks. combing_grid = PolygonUtils::createLocToLineGrid(*combing_boundary, grid_size); } diff --git a/include/SkeletalTrapezoidation.h b/include/SkeletalTrapezoidation.h index 8d0addadac..ebd3d3d699 100644 --- a/include/SkeletalTrapezoidation.h +++ b/include/SkeletalTrapezoidation.h @@ -63,8 +63,8 @@ class SkeletalTrapezoidation coord_t transition_filter_dist; //!< Filter transition mids (i.e. anchors) closer together than this coord_t allowed_filter_deviation; //!< The allowed line width deviation induced by filtering coord_t beading_propagation_transition_dist; //!< When there are different beadings propagated from below and from above, use this transitioning distance - static constexpr coord_t central_filter_dist = 20; //!< Filter areas marked as 'central' smaller than this - static constexpr coord_t snap_dist = 20; //!< Generic arithmatic inaccuracy. Only used to determine whether a transition really needs to insert an extra edge. + static constexpr coord_t central_filter_dist = 2 * INT_EPSILON; //!< Filter areas marked as 'central' smaller than this + static constexpr coord_t snap_dist = 2 * INT_EPSILON; //!< Generic arithmatic inaccuracy. Only used to determine whether a transition really needs to insert an extra edge. /*! * The strategy to use to fill a certain shape with lines. diff --git a/include/infill/LightningTreeNode.h b/include/infill/LightningTreeNode.h index b24f1e37c5..f79b99a45f 100644 --- a/include/infill/LightningTreeNode.h +++ b/include/infill/LightningTreeNode.h @@ -15,7 +15,7 @@ namespace cura { -constexpr coord_t locator_cell_size = 4000; +constexpr coord_t locator_cell_size = 4_mm; class LightningTreeNode; diff --git a/include/pathPlanning/Comb.h b/include/pathPlanning/Comb.h index 599c838eb3..915cc149d9 100644 --- a/include/pathPlanning/Comb.h +++ b/include/pathPlanning/Comb.h @@ -122,8 +122,9 @@ class Comb const coord_t offset_from_inside_to_outside; //!< The sum of the offsets for the inside and outside boundary Comb::offset_from_outlines and Comb::offset_from_outlines_outside const coord_t max_crossing_dist2; //!< The maximal distance by which to cross the in_between area between inside and outside static const coord_t max_moveOutside_distance2 = std::numeric_limits::max(); //!< Any point which is not inside should be considered outside. - static constexpr coord_t offset_dist_to_get_from_on_the_polygon_to_outside = 40; //!< in order to prevent on-boundary vs crossing boundary confusions (precision thing) - static constexpr coord_t offset_extra_start_end = 100; //!< Distance to move start point and end point toward eachother to extra avoid collision with the boundaries. + static constexpr coord_t offset_dist_to_get_from_on_the_polygon_to_outside = 4 * INT_EPSILON; //!< in order to prevent on-boundary vs crossing boundary confusions (precision thing) + static constexpr coord_t offset_extra_start_end = 100_mu; //!< Distance to move start point and end point toward eachother to extra avoid collision with the boundaries. + // FIXME: ^ Magic constant (how is it derived? should it scale with units?) Polygons boundary_inside_minimum; //!< The boundary within which to comb. (Will be reordered by the partsView_inside_minimum) Polygons boundary_inside_optimal; //!< The boundary within which to comb. (Will be reordered by the partsView_inside_optimal) diff --git a/include/settings/AdaptiveLayerHeights.h b/include/settings/AdaptiveLayerHeights.h index c9b5d1f9c6..e7d3b93acf 100644 --- a/include/settings/AdaptiveLayerHeights.h +++ b/include/settings/AdaptiveLayerHeights.h @@ -12,7 +12,7 @@ class AdaptiveLayer { public: /*! - * Height of the layer in microns. + * Height of the layer in INT_PER_MM units. */ coord_t layer_height; diff --git a/include/slicer.h b/include/slicer.h index a5de21fdf5..8768d1a840 100644 --- a/include/slicer.h +++ b/include/slicer.h @@ -95,7 +95,7 @@ class SlicerLayer /*! * Connecting polygons that are not closed yet, as models are not always perfect manifold we need to join some stuff up to get proper polygons. - * First link up polygon ends that are within 2 microns. + * First link up polygon ends that are within 2 INT_PER_MM units. * * Clears all open polylines which are used up in the process * diff --git a/include/utils/Coord_t.h b/include/utils/Coord_t.h index 910efb941e..0ccba7a0a2 100644 --- a/include/utils/Coord_t.h +++ b/include/utils/Coord_t.h @@ -13,16 +13,100 @@ namespace cura using coord_t = ClipperLib::cInt; -static inline coord_t operator "" _mu(unsigned long long i) { return i; }; +template +constexpr T ipow(T x, unsigned power) +{ + return power != 0 ? x * ipow(x, power - 1) : T(1); +} + +static constexpr unsigned INT10POW_PER_MM = 3; +static constexpr coord_t INT_PER_MM = ipow(10, INT10POW_PER_MM); +static constexpr coord_t INT_PER_MM2 = INT_PER_MM * INT_PER_MM; +static constexpr coord_t INT_PER_MM3 = INT_PER_MM2 * INT_PER_MM; + +// Minimum distance for snapping, tolerance for arcs, etc. +static constexpr coord_t INT_EPSILON = 10; +// The lenght of unit vectors for normal() and rotation computation, such that the rounding errors are negligible. +static constexpr coord_t INT_PRECISION_COMP = 10000; -#define INT2MM(n) (static_cast(n) / 1000.0) -#define INT2MM2(n) (static_cast(n) / 1000000.0) -#define MM2INT(n) (static_cast((n) * 1000 + 0.5 * (((n) > 0) - ((n) < 0)))) -#define MM2_2INT(n) (static_cast((n) * 1000000 + 0.5 * (((n) > 0) - ((n) < 0)))) -#define MM3_2INT(n) (static_cast((n) * 1000000000 + 0.5 * (((n) > 0) - ((n) < 0)))) +constexpr double coord_to_mm(coord_t n) +{ + return static_cast(n) / INT_PER_MM; +} +constexpr double coord_to_mm2(coord_t n) +{ + return static_cast(n) / INT_PER_MM2; +} +constexpr double coord_to_mm3(coord_t n) +{ + return static_cast(n) / INT_PER_MM3; +} +constexpr coord_t mm_to_coord(double n) +{ + return static_cast(n * INT_PER_MM + (n >= 0. ? 0.5 : -0.5)); +} +constexpr coord_t mm2_to_coord(double n) +{ + return static_cast(n * INT_PER_MM2 + (n >= 0. ? 0.5 : -0.5)); +} +constexpr coord_t mm3_to_coord(double n) +{ + return static_cast(n * INT_PER_MM3 + (n >= 0. ? 0.5 : -0.5)); +} +constexpr coord_t mu2_to_coord(double n) +{ + return static_cast(n * (INT_PER_MM2 / 1000000.) + (n >= 0. ? 0.5 : -0.5)); +} -#define INT2MICRON(n) ((n) / 1) -#define MICRON2INT(n) ((n) * 1) +constexpr coord_t operator"" _mm(long double n) +{ + return mm_to_coord(n); +} +constexpr coord_t operator"" _mm(unsigned long long int n) +{ + return mm_to_coord(n); +} +constexpr coord_t operator"" _mm2(long double n) +{ + return mm2_to_coord(n); +} +constexpr coord_t operator"" _mm2(unsigned long long int n) +{ + return mm2_to_coord(n); +} +constexpr coord_t operator"" _mm3(long double n) +{ + return mm3_to_coord(n); +} +constexpr coord_t operator"" _mm3(unsigned long long int n) +{ + return mm3_to_coord(n); +} + +constexpr coord_t operator"" _mu(long double n) +{ + return mm_to_coord(n * 1e-3); +} +constexpr coord_t operator"" _mu(unsigned long long int n) +{ + return mm_to_coord(n * 1e-3); +} +constexpr coord_t operator"" _mu2(long double n) +{ + return mm2_to_coord(n * 1e-6); +} +constexpr coord_t operator"" _mu2(unsigned long long int n) +{ + return mm2_to_coord(n * 1e-6); +} +constexpr coord_t operator"" _mu3(long double n) +{ + return mm3_to_coord(n * 1e-9); +} +constexpr coord_t operator"" _mu3(unsigned long long int n) +{ + return mm3_to_coord(n * 1e-9); +} } // namespace cura diff --git a/include/utils/IntPoint.h b/include/utils/IntPoint.h index 281436c6b3..db6b35c166 100644 --- a/include/utils/IntPoint.h +++ b/include/utils/IntPoint.h @@ -5,7 +5,7 @@ #define UTILS_INT_POINT_H /** -The integer point classes are used as soon as possible and represent microns in 2D or 3D space. +The integer point classes are used as soon as possible and represent in 2D or 3D space with INT_PER_MM units. Integer points are used to avoid floating point rounding errors, and because ClipperLib uses them. */ #define INLINE static inline @@ -107,8 +107,8 @@ INLINE coord_t vSize(const Point& p0) INLINE double vSizeMM(const Point& p0) { - double fx = INT2MM(p0.X); - double fy = INT2MM(p0.Y); + double fx = coord_to_mm(p0.X); + double fy = coord_to_mm(p0.Y); return sqrt(fx*fx+fy*fy); } diff --git a/include/utils/Point3.h b/include/utils/Point3.h index b37d7737ba..727f7f5a0c 100644 --- a/include/utils/Point3.h +++ b/include/utils/Point3.h @@ -102,9 +102,9 @@ class Point3 double vSizeMM() const { - double fx = INT2MM(x); - double fy = INT2MM(y); - double fz = INT2MM(z); + double fx = coord_to_mm(x); + double fy = coord_to_mm(y); + double fz = coord_to_mm(z); return sqrt(fx*fx+fy*fy+fz*fz); } diff --git a/include/utils/PolylineStitcher.h b/include/utils/PolylineStitcher.h index cab9bf5eec..2977e0442f 100644 --- a/include/utils/PolylineStitcher.h +++ b/include/utils/PolylineStitcher.h @@ -52,7 +52,7 @@ class PolylineStitcher * \param snap_distance Points closer than this distance are considered to * be the same point. */ - static void stitch(const Paths& lines, Paths& result_lines, Paths& result_polygons, coord_t max_stitch_distance = MM2INT(0.1), coord_t snap_distance = 10) + static void stitch(const Paths& lines, Paths& result_lines, Paths& result_polygons, coord_t max_stitch_distance = 0.1_mm, coord_t snap_distance = INT_EPSILON) { if (lines.empty()) { diff --git a/include/utils/SVG.h b/include/utils/SVG.h index c4418ef86f..e74afce828 100644 --- a/include/utils/SVG.h +++ b/include/utils/SVG.h @@ -186,8 +186,7 @@ class SVG : NoCopy * \param stroke_width The width of the grid lines. * \param font_size The size of the font to write the coordinates with. */ - void writeCoordinateGrid(const coord_t grid_size = MM2INT(1), const Color color = Color::BLACK, const float stroke_width = 0.1, const float font_size = 10) const; - + void writeCoordinateGrid(const coord_t grid_size = 1_mm, const Color color = Color::BLACK, const float stroke_width = 0.1, const float font_size = 10) const; }; template diff --git a/include/utils/Simplify.h b/include/utils/Simplify.h index a5a8d95e65..1d5fb3637e 100644 --- a/include/utils/Simplify.h +++ b/include/utils/Simplify.h @@ -131,7 +131,7 @@ class Simplify * If a vertex causes deviation of less than this, it should always be * removed. */ - constexpr static coord_t min_resolution = 5; //5 units, regardless of how big those are, to allow for rounding errors. + constexpr static coord_t min_resolution = INT_EPSILON / 2; // 5 units, regardless of how big those are, to allow for rounding errors. /*! * The main simplification algorithm starts here. diff --git a/include/utils/floatpoint.h b/include/utils/floatpoint.h index 9019f30b24..15d02d6aa6 100644 --- a/include/utils/floatpoint.h +++ b/include/utils/floatpoint.h @@ -23,7 +23,9 @@ class FPoint3 float x,y,z; FPoint3() {} FPoint3(float _x, float _y, float _z): x(_x), y(_y), z(_z) {} - FPoint3(const Point3& p): x(p.x*.001), y(p.y*.001), z(p.z*.001) {} + FPoint3(const Point3& p) : x(coord_to_mm(p.x)), y(coord_to_mm(p.y)), z(coord_to_mm(p.z)) + { + } FPoint3 operator+(const FPoint3& p) const { return FPoint3(x+p.x, y+p.y, z+p.z); } FPoint3 operator-(const FPoint3& p) const { return FPoint3(x-p.x, y-p.y, z-p.z); } @@ -83,7 +85,7 @@ class FPoint3 Point3 toPoint3() { - return Point3(MM2INT(x), MM2INT(y), MM2INT(z)); + return Point3(mm_to_coord(x), mm_to_coord(y), mm_to_coord(z)); } }; diff --git a/include/utils/polygon.h b/include/utils/polygon.h index d27f4c19ef..3de469cf3f 100644 --- a/include/utils/polygon.h +++ b/include/utils/polygon.h @@ -59,7 +59,9 @@ class ListPolyIt; typedef std::list ListPolygon; //!< A polygon represented by a linked list instead of a vector typedef std::vector ListPolygons; //!< Polygons represented by a vector of linked lists instead of a vector of vectors -const static int clipper_init = (0); +// How far a miter can be offseted before being truncated, relative to the offset size (http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperOffset/Properties/MiterLimit.htm) +static constexpr double clipper_miter_limit = 1.2; +static constexpr int clipper_init = 0; #define NO_INDEX (std::numeric_limits::max()) class ConstPolygonPointer; @@ -162,7 +164,7 @@ class ConstPolygonRef return ClipperLib::Orientation(*path); } - Polygons offset(int distance, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miter_limit = 1.2) const; + Polygons offset(int distance, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miter_limit = clipper_miter_limit) const; coord_t polygonLength() const { @@ -394,6 +396,11 @@ class ConstPolygonRef * \param[in,out] backward_is_too_far Whether trying another step backward is blocked by the shortcut length condition. Updated for the next iteration. */ static void smooth_outward_step(const Point p1, const int64_t shortcut_length2, ListPolyIt& p0_it, ListPolyIt& p2_it, bool& forward_is_blocked, bool& backward_is_blocked, bool& forward_is_too_far, bool& backward_is_too_far); + + + /// Allowed deviation for round joints while offsetting (this reduces the number of segment + /// @see http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperOffset/Properties/ArcTolerance.htm + static double calcArcTolerance(int offset); }; @@ -551,19 +558,20 @@ class PolygonRef : public ConstPolygonRef * \param smallest_line_segment_squared maximal squared length of removed line segments * \param allowed_error_distance_squared The square of the distance of the middle point to the line segment of the consecutive and previous point for which the middle point is removed */ - void simplify(const coord_t smallest_line_segment_squared = MM2INT(0.01) * MM2INT(0.01), const coord_t allowed_error_distance_squared = 25); + void simplify(const coord_t smallest_line_segment_squared = 100_mu2, const coord_t allowed_error_distance_squared = 25_mu2); /*! * See simplify(.) */ - void simplifyPolyline(const coord_t smallest_line_segment_squared = 100, const coord_t allowed_error_distance_squared = 25); + void simplifyPolyline(const coord_t smallest_line_segment_squared = 100_mu2, const coord_t allowed_error_distance_squared = 25_mu2); + protected: /*! * Private implementation for both simplify and simplifyPolygons. * * Made private to avoid accidental use of the wrong function. */ - void _simplify(const coord_t smallest_line_segment_squared = 100, const coord_t allowed_error_distance_squared = 25, bool processing_polylines = false); + void _simplify(const coord_t smallest_line_segment_squared = 100_mu2, const coord_t allowed_error_distance_squared = 25_mu2, bool processing_polylines = false); public: void pop_back() @@ -1003,16 +1011,15 @@ class Polygons return ret; } - Polygons offset(int distance, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miter_limit = 1.2) const; + Polygons offset(int distance, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miter_limit = clipper_miter_limit) const; Polygons offsetPolyLine(int distance, ClipperLib::JoinType joinType = ClipperLib::jtMiter) const { Polygons ret; - double miterLimit = 1.2; ClipperLib::EndType end_type = (joinType == ClipperLib::jtMiter)? ClipperLib::etOpenSquare : ClipperLib::etOpenRound; - ClipperLib::ClipperOffset clipper(miterLimit, 10.0); + ClipperLib::ClipperOffset clipper(clipper_miter_limit, ConstPolygonRef::calcArcTolerance(distance)); clipper.AddPaths(paths, joinType, end_type); - clipper.MiterLimit = miterLimit; + clipper.MiterLimit = clipper_miter_limit; clipper.Execute(ret.paths, distance); return ret; } diff --git a/include/utils/string.h b/include/utils/string.h index 7ca857c191..a69606ac63 100644 --- a/include/utils/string.h +++ b/include/utils/string.h @@ -4,11 +4,13 @@ #ifndef UTILS_STRING_H #define UTILS_STRING_H -#include // sprintf -#include -#include // ostringstream +#include +#include +#include +#include -#include +#include "Coord_t.h" +#include "math.h" namespace cura { @@ -27,151 +29,163 @@ static inline int stringcasecompare(const char* a, const char* b) } /*! - * Efficient conversion of micron integer type to millimeter string. - * - * The integer type is half the size of the normal integer type because of implementation details. - * However, half the integer type should suffice, because we made the basic coord_t twice as big as necessary - * so as to support multiplication within the same integer type. - * - * \param coord The micron unit to convert - * \param ss The output stream to write the string to + * \brief Format a fixed point unsigned integer as a decimal ascii string. + * \tparam fixed_precision log10 of the fixed point scaling factor + * \tparam out_precision Number of decimal places to be written + * \param buffer_end Pointer past the end of the output buffer + * \param value The unsigned value to format + * \return A pair of char*, delimiting the result range in the buffer */ -static inline void writeInt2mm(const int32_t coord, std::ostream& ss) +template +static inline std::span format_unsigned_decimal_fixed_point(std::array& buffer, size_t value) { - constexpr size_t buffer_size = 24; - char buffer[buffer_size]; - int char_count = sprintf(buffer, "%d", coord); // we haven't found any way for the windows compiler to accept formatting of a coord_t, so it has to be int32_t instead -#ifdef DEBUG - if (char_count + 1 >= int(buffer_size)) // + 1 for the null character + // Rounding to the required precision + if constexpr (fixed_precision > out_precision) { - spdlog::error("Cannot write {} to buffer of size {}", coord, buffer_size); + value = round_divide(value, ipow(size_t{ 10 }, fixed_precision - out_precision)); } - if (char_count < 0) - { - spdlog::error("Encoding error while writing {}", coord); - } -#endif // DEBUG - int end_pos = char_count; // the first character not to write any more - int trailing_zeros = 1; - while (trailing_zeros < 4 && buffer[char_count - trailing_zeros] == '0') - { - trailing_zeros++; - } - trailing_zeros--; - end_pos = char_count - trailing_zeros; - if (trailing_zeros == 3) - { // no need to write the decimal dot - buffer[char_count - trailing_zeros] = '\0'; - ss << buffer; - return; - } - if (char_count <= 3) + + if (value != 0) { - int start = 0; // where to start writing from the buffer - if (coord < 0) + // Writes pairs of digit backward in the buffer. This is heavily inspired by the fmt library. + size_t quotient = value; + auto begin = buffer.end(); + while (quotient >= 10) { - ss << '-'; - start = 1; + const char* twodigits = &"0001020304050607080910111213141516171819" + "2021222324252627282930313233343536373839" + "4041424344454647484950515253545556575859" + "6061626364656667686970717273747576777879" + "8081828384858687888990919293949596979899"[(quotient % 100) * 2]; + quotient /= 100; + begin -= 2; + assert(begin >= buffer.begin()); + begin[0] = twodigits[0]; + begin[1] = twodigits[1]; } - ss << "0."; - for (int nulls = char_count - start; nulls < 3; nulls++) - { // fill up to 3 decimals with zeros - ss << '0'; + if (quotient > 0) + { // Odd number of digits + assert(begin > buffer.begin()); + *--begin = '0' + static_cast(quotient); } - buffer[char_count - trailing_zeros] = '\0'; - ss << (static_cast(buffer) + start); + else if (*begin == '0') + { // The last division by 100 might have produced a leading zero, drop it + begin++; + } + assert(*begin != '0'); + + // Trims zeros at the right of the fractional part + const auto first_integral_place = buffer.end() - out_precision; + auto end = buffer.end(); + while (end > first_integral_place) + { + if (*(end - 1) != '0') + { // Stop at the first non '0' (will break) + break; + } + else + { + --end; // Trims the rightmost '0' + } + } + if (end > first_integral_place) + { // We have a factional part + if (begin < first_integral_place) + { + // We also have an integral part: move it one char to the left to make room for the '.' + assert(begin > buffer.begin()); + *std::copy(begin, first_integral_place, begin - 1) = '.'; + --begin; + } + else + { + // fractional only: fill '0's at the left of the fractional part + std::fill(first_integral_place, begin, '0'); + // In theory, a leading zero before the decimal point should not required by Gcode, however omitting it crashes PrusaSlicer viewer. + begin = first_integral_place - 2; + assert(begin >= buffer.begin()); + begin[0] = '0'; + begin[1] = '.'; + } + } + assert(*begin != '0' || begin[1] == '.'); + assert(*begin != '.'); + return { begin, end }; } else { - char prev = '.'; - int pos; - for (pos = char_count - 3; pos <= end_pos; pos++) - { // shift all characters and insert the decimal dot - char next_prev = buffer[pos]; - buffer[pos] = prev; - prev = next_prev; - } - buffer[pos] = '\0'; - ss << buffer; + static_assert(buffer_length >= 1); + char* const begin = buffer.end() - 1; + *begin = '0'; + return { begin, buffer.end() }; } } /*! - * Struct to make it possible to inline calls to writeInt2mm with writing other stuff to the output stream + * \brief Format a fixed point signed integer as a decimal ascii string. + * \tparam fixed_precision log10 of the fixed point scaling factor + * \tparam out_precision Number of decimal places to be written + * \param buffer_end Pointer past the end of the output buffer + * \param value The unsigned value to format + * \return A pair of char*, delimiting the result range in the buffer */ -struct MMtoStream +template +static inline std::span format_signed_decimal_fixed_point(std::array& buffer, ptrdiff_t value) { - int64_t value; //!< The coord in micron - - friend inline std::ostream& operator<<(std::ostream& out, const MMtoStream precision_and_input) + bool is_neg = value < 0; + auto abs_value = static_cast(is_neg ? -value : value); + auto result = format_unsigned_decimal_fixed_point(buffer, abs_value); + if (is_neg) { - writeInt2mm(precision_and_input.value, out); - return out; + auto begin = --result.begin(); + *begin = '-'; + return { begin, result.end() }; } -}; + return result; +} /*! - * Efficient writing of a double to a stringstream - * - * writes with \p precision digits after the decimal dot, but removes trailing zeros - * - * \warning only works with precision up to 9 and input up to 10^14 - * - * \param precision The number of (non-zero) digits after the decimal dot - * \param coord double to output - * \param ss The output stream to write the string to + * \brief Wraps a coord_t for writing it in base 10 to a ostream. */ -static inline void writeDoubleToStream(const unsigned int precision, const double coord, std::ostream& ss) +struct MMtoStream { - char format[5] = "%.xF"; // write a float with [x] digits after the dot - format[2] = '0' + precision; // set [x] - constexpr size_t buffer_size = 400; - char buffer[buffer_size]; - int char_count = sprintf(buffer, format, coord); -#ifdef DEBUG - if (char_count + 1 >= int(buffer_size)) // + 1 for the null character - { - spdlog::error("Cannot write {} to buffer of size {}", coord, buffer_size); - } - if (char_count < 0) - { - spdlog::error("Encoding error while writing {}", coord); - } -#endif // DEBUG - if (char_count <= 0) - { - return; - } - if (buffer[char_count - precision - 1] == '.') + coord_t value; //!< The coord in micron + + friend inline std::ostream& operator<<(std::ostream& out, const MMtoStream precision_and_input) { - int non_nul_pos = char_count - 1; - while (buffer[non_nul_pos] == '0') - { - non_nul_pos--; - } - if (buffer[non_nul_pos] == '.') - { - buffer[non_nul_pos] = '\0'; - } - else - { - buffer[non_nul_pos + 1] = '\0'; - } + constexpr size_t buffer_size = std::numeric_limits::digits10 + 3; + // +1 because digits10 is log_10(max) rounded down + // +1 for the sign + // +1 for the decimal separator + std::array buffer; + auto res = format_signed_decimal_fixed_point(buffer, precision_and_input.value); + out.write(&res.front(), res.size()); + return out; } - ss << buffer; -} +}; /*! - * Struct to make it possible to inline calls to writeDoubleToStream with writing other stuff to the output stream + * \brief Wraps a double for writing it in base 10 to a ostream with a fixed number of decimal places. + * \tparam precision The number of decimal places + * \warning Does not allows values larger that LLONG_MAX / 10^precision */ +template struct PrecisionedDouble { - unsigned int precision; //!< Number of digits after the decimal mark with which to convert to string + // unsigned int precision; //!< Number of digits after the decimal mark with which to convert to string double value; //!< The double value - friend inline std::ostream& operator<<(std::ostream& out, const PrecisionedDouble precision_and_input) + friend inline std::ostream& operator<<(std::ostream& out, const PrecisionedDouble input) { - writeDoubleToStream(precision_and_input.precision, precision_and_input.value, out); + constexpr size_t buffer_size = std::numeric_limits::digits10 + 3; + std::array buffer; + + constexpr size_t factor = ipow(size_t{ 10 }, precision); + double scaled_value = input.value * factor; + assert(std::abs(scaled_value) < static_cast(std::numeric_limits::max())); + coord_t fixed_point = std::llround(scaled_value); + auto res = format_signed_decimal_fixed_point(buffer, fixed_point); + out.write(&res.front(), res.size()); return out; } }; diff --git a/src/BeadingStrategy/BeadingStrategy.cpp b/src/BeadingStrategy/BeadingStrategy.cpp index 03c6adb3a1..4f5b8250af 100644 --- a/src/BeadingStrategy/BeadingStrategy.cpp +++ b/src/BeadingStrategy/BeadingStrategy.cpp @@ -38,7 +38,7 @@ coord_t BeadingStrategy::getTransitioningLength(coord_t lower_bead_count) const { if (lower_bead_count == 0) { - return 10; + return INT_EPSILON; } return default_transition_length; } diff --git a/src/BeadingStrategy/LimitedBeadingStrategy.cpp b/src/BeadingStrategy/LimitedBeadingStrategy.cpp index a563cfb779..2350009fd4 100644 --- a/src/BeadingStrategy/LimitedBeadingStrategy.cpp +++ b/src/BeadingStrategy/LimitedBeadingStrategy.cpp @@ -95,7 +95,7 @@ coord_t LimitedBeadingStrategy::getOptimalThickness(coord_t bead_count) const { return parent->getOptimalThickness(bead_count); } - return 10000000; // 10 meter + return 10000_mm; // 10 meter } coord_t LimitedBeadingStrategy::getTransitionThickness(coord_t lower_bead_count) const @@ -106,9 +106,9 @@ coord_t LimitedBeadingStrategy::getTransitionThickness(coord_t lower_bead_count) } if (lower_bead_count == max_bead_count) { - return parent->getOptimalThickness(lower_bead_count + 1) - 10; + return parent->getOptimalThickness(lower_bead_count + 1) - INT_EPSILON; } - return 9000000; // 9 meter + return 9000_mm; // 9 meter } coord_t LimitedBeadingStrategy::getOptimalBeadCount(coord_t thickness) const @@ -120,7 +120,7 @@ coord_t LimitedBeadingStrategy::getOptimalBeadCount(coord_t thickness) const } else if (parent_bead_count == max_bead_count + 1) { - if (thickness < parent->getOptimalThickness(max_bead_count + 1) - 10) + if (thickness < parent->getOptimalThickness(max_bead_count + 1) - INT_EPSILON) return max_bead_count; else return max_bead_count + 1; diff --git a/src/ConicalOverhang.cpp b/src/ConicalOverhang.cpp index d1fdf7dd0d..a9410bcae6 100644 --- a/src/ConicalOverhang.cpp +++ b/src/ConicalOverhang.cpp @@ -14,8 +14,8 @@ namespace cura void ConicalOverhang::apply(Slicer* slicer, const Mesh& mesh) { const AngleRadians angle = mesh.settings.get("conical_overhang_angle"); - const double maxHoleArea = mesh.settings.get("conical_overhang_hole_size"); const double tan_angle = tan(angle); // the XY-component of the angle + const coord_t maxHoleArea = mm2_to_coord(mesh.settings.get("conical_overhang_hole_size")); const coord_t layer_thickness = mesh.settings.get("layer_height"); coord_t max_dist_from_lower_layer = tan_angle * layer_thickness; // max dist which can be bridged @@ -51,7 +51,7 @@ void ConicalOverhang::apply(Slicer* slicer, const Mesh& mesh) { Polygons holePoly; holePoly.add(layerParts[part][hole_nr]); - if (maxHoleArea > 0.0 && INT2MM2(std::abs(holePoly.area())) < maxHoleArea) + if (maxHoleArea > 0.0 && std::abs(holePoly.area()) < maxHoleArea) { Polygons holeWithAbove = holePoly.intersection(above); if(!holeWithAbove.empty()) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 156da269fa..5538396597 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -634,7 +634,7 @@ void FffGcodeWriter::processStartingCode(const SliceDataStorage& storage, const { gcode.writeComment("enable auto-retraction"); std::ostringstream tmp; - tmp << "M227 S" << (mesh_group_settings.get("retraction_amount") * 2560 / 1000) << " P" << (mesh_group_settings.get("retraction_amount") * 2560 / 1000); + tmp << "M227 S" << coord_to_mm(mesh_group_settings.get("retraction_amount") * 2560) << " P" << coord_to_mm(mesh_group_settings.get("retraction_amount") * 2560); gcode.writeLine(tmp.str().c_str()); } else if (gcode.getFlavor() == EGCodeFlavor::GRIFFIN) @@ -667,7 +667,7 @@ void FffGcodeWriter::processStartingCode(const SliceDataStorage& storage, const void FffGcodeWriter::processNextMeshGroupCode(const SliceDataStorage& storage) { gcode.writeFanCommand(0); - gcode.setZ(max_object_height + MM2INT(5)); + gcode.setZ(max_object_height + 5_mm); Application::getInstance().communication->sendCurrentPosition(gcode.getPositionXY()); gcode.writeTravel(gcode.getPositionXY(), Application::getInstance().current_slice->scene.extruders[gcode.getExtruderNr()].settings.get("speed_travel")); @@ -1830,7 +1830,8 @@ bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, // Originally an area of 0.4*0.4*2 (2 line width squares) was found to be a good threshold for removal. // However we found that this doesn't scale well with polygons with larger circumference (https://github.com/Ultimaker/Cura/issues/3992). // Given that the original test worked for approximately 2x2cm models, this scaling by circumference should make it work for any size. - constexpr double minimum_small_area_factor = 0.4 * 0.4 / 40000; + // Note that the result is in mm^2, but circumference is in INT_PER_MM, this is why the denominator get the _mm unit. + constexpr double minimum_small_area_factor = 0.4 * 0.4 / 40_mm; const double minimum_small_area = minimum_small_area_factor * circumference; // This is only for density infill, because after generating the infill might appear unnecessary infill on walls @@ -1955,7 +1956,7 @@ bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, bool FffGcodeWriter::partitionInfillBySkinAbove(Polygons& infill_below_skin, Polygons& infill_not_below_skin, const LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const SliceLayerPart& part, coord_t infill_line_width) { - constexpr coord_t tiny_infill_offset = 20; + constexpr coord_t tiny_infill_offset = 2 * INT_EPSILON; const auto skin_edge_support_layers = mesh.settings.get("skin_edge_support_layers"); Polygons skin_above_combined; // skin regions on the layers above combined with small gaps between @@ -2040,7 +2041,7 @@ bool FffGcodeWriter::partitionInfillBySkinAbove(Polygons& infill_below_skin, Pol constexpr bool remove_small_holes_from_infill_below_skin = true; constexpr double min_area_multiplier = 25; - const double min_area = INT2MM(infill_line_width) * INT2MM(infill_line_width) * min_area_multiplier; + const double min_area = coord_to_mm2(infill_line_width * infill_line_width) * min_area_multiplier; infill_below_skin.removeSmallAreas(min_area, remove_small_holes_from_infill_below_skin); // there is infill below skin, is there also infill that isn't below skin? @@ -2229,7 +2230,7 @@ bool FffGcodeWriter::processInsets(const SliceDataStorage& storage, LayerPlan& g // expanded to take into account the overhang angle, the greater the overhang angle, the larger the supported area is // considered to be const coord_t overhang_width = layer_height * std::tan(overhang_angle / (180 / M_PI)); - Polygons overhang_region = part.outline.offset(-half_outer_wall_width).difference(outlines_below.offset(10 + overhang_width - half_outer_wall_width)).offset(10); + Polygons overhang_region = part.outline.offset(-half_outer_wall_width).difference(outlines_below.offset(INT_EPSILON + overhang_width - half_outer_wall_width)).offset(INT_EPSILON); gcode_layer.setOverhangMask(overhang_region); } } diff --git a/src/FffPolygonGenerator.cpp b/src/FffPolygonGenerator.cpp index 56addd9e6d..672861a31c 100644 --- a/src/FffPolygonGenerator.cpp +++ b/src/FffPolygonGenerator.cpp @@ -940,7 +940,7 @@ void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage) const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; const coord_t layer_height = mesh_group_settings.get("layer_height"); - const unsigned int layer_skip = 500 / layer_height + 1; + const unsigned int layer_skip = 0.5_mm / layer_height + 1; Polygons& draft_shield = storage.draft_protection_shield; for (unsigned int layer_nr = 0; layer_nr < storage.print_layer_count && layer_nr < draft_shield_layers; layer_nr += layer_skip) diff --git a/src/GCodePathConfig.cpp b/src/GCodePathConfig.cpp index d96147862b..240116e763 100644 --- a/src/GCodePathConfig.cpp +++ b/src/GCodePathConfig.cpp @@ -1,9 +1,9 @@ //Copyright (c) 2018 Ultimaker B.V. //CuraEngine is released under the terms of the AGPLv3 or higher. -#include "settings/types/LayerIndex.h" -#include "utils/IntPoint.h" // INT2MM #include "GCodePathConfig.h" +#include "settings/types/LayerIndex.h" +#include "utils/IntPoint.h" // coord_to_mm namespace cura { @@ -99,7 +99,7 @@ Ratio GCodePathConfig::getFlowRatio() const double GCodePathConfig::calculateExtrusion() const { - return INT2MM(line_width) * INT2MM(layer_thickness) * double(flow); + return coord_to_mm2(line_width * layer_thickness) * double(flow); } diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 6a5a5124e8..bf1b5e463a 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -25,8 +25,8 @@ namespace cura { -constexpr int MINIMUM_LINE_LENGTH = 5; // in uM. Generated lines shorter than this may be discarded -constexpr int MINIMUM_SQUARED_LINE_LENGTH = MINIMUM_LINE_LENGTH * MINIMUM_LINE_LENGTH; +constexpr coord_t MINIMUM_LINE_LENGTH = 5_mu; // in uM. Generated lines shorter than this may be discarded +constexpr coord_t MINIMUM_SQUARED_LINE_LENGTH = MINIMUM_LINE_LENGTH * MINIMUM_LINE_LENGTH; ExtruderPlan::ExtruderPlan(const size_t extruder, const LayerIndex layer_nr, @@ -201,7 +201,7 @@ Polygons LayerPlan::computeCombBoundary(const CombBoundary boundary_type) { if (layer_nr < 0) { - comb_boundary = storage.raftOutline.offset(MM2INT(0.1)); + comb_boundary = storage.raftOutline.offset(0.1_mm); } else { @@ -328,7 +328,7 @@ void LayerPlan::setMesh(const std::string mesh_id) void LayerPlan::moveInsideCombBoundary(const coord_t distance, const std::optional& part) { - constexpr coord_t max_dist2 = MM2INT(2.0) * MM2INT(2.0); // if we are further than this distance, we conclude we are not inside even though we thought we were. + constexpr coord_t max_dist2 = 2.0_mm * 2.0_mm; // if we are further than this distance, we conclude we are not inside even though we thought we were. // this function is to be used to move from the boundary of a part to inside the part Point p = getLastPlannedPositionOrStartingPosition(); // copy, since we are going to move p if (PolygonUtils::moveInside(comb_boundary_preferred, p, distance, max_dist2) != NO_INDEX) @@ -527,7 +527,7 @@ GCodePath& LayerPlan::addTravel_simple(Point p, GCodePath* path) void LayerPlan::planPrime(const float& prime_blob_wipe_length) { forceNewPathStart(); - GCodePath& prime_travel = addTravel_simple(getLastPlannedPositionOrStartingPosition() + Point(0, MM2INT(prime_blob_wipe_length))); + GCodePath& prime_travel = addTravel_simple(getLastPlannedPositionOrStartingPosition() + Point(0, mm_to_coord(prime_blob_wipe_length))); prime_travel.retract = false; prime_travel.perform_z_hop = false; prime_travel.perform_prime = true; @@ -632,7 +632,7 @@ void LayerPlan::addPolygonsByOptimizer(const Polygons& polygons, } } -static constexpr float max_non_bridge_line_volume = MM2INT(100); // limit to accumulated "volume" of non-bridge lines which is proportional to distance x extrusion rate +static constexpr float max_non_bridge_line_volume = 100_mm; // limit to accumulated "volume" of non-bridge lines which is proportional to distance x extrusion rate void LayerPlan::addWallLine(const Point& p0, const Point& p1, @@ -645,8 +645,8 @@ void LayerPlan::addWallLine(const Point& p0, Ratio speed_factor, double distance_to_bridge_start) { - const coord_t min_line_len = 5; // we ignore lines less than 5um long - const double acceleration_segment_len = MM2INT(1); // accelerate using segments of this length + const coord_t min_line_len = 5_mu; // we ignore lines less than 5um long + const double acceleration_segment_len = 1_mm; // accelerate using segments of this length const double acceleration_factor = 0.75; // must be < 1, the larger the value, the slower the acceleration const bool spiralize = false; @@ -972,7 +972,7 @@ void LayerPlan::addWall(const ExtrusionLine& wall, Ratio small_feature_speed_factor = settings.get((layer_nr == 0) ? "small_feature_speed_factor_0" : "small_feature_speed_factor"); const Velocity min_speed = fan_speed_layer_time_settings_per_extruder[getLastPlannedExtruderTrain()->extruder_nr].cool_min_speed; small_feature_speed_factor = std::max((double)small_feature_speed_factor, (double)(min_speed / non_bridge_config.getSpeed())); - const coord_t max_area_deviation = std::max(settings.get("meshfix_maximum_extrusion_area_deviation"), 1); // Square micrometres! + const coord_t max_area_deviation = std::max(mu2_to_coord(settings.get("meshfix_maximum_extrusion_area_deviation")), coord_t{ 1 }); // Square micrometres! const coord_t max_resolution = std::max(settings.get("meshfix_maximum_resolution"), coord_t(1)); ExtrusionJunction p0 = wall[start_idx]; @@ -1150,11 +1150,11 @@ void LayerPlan::addLinesByOptimizer(const Polygons& polygons, dist = overlap; } } - dist += 100; // ensure boundary is slightly outside all skin/infill lines + dist += 0.1_mm; // ensure boundary is slightly outside all skin/infill lines } boundary.add(comb_boundary_minimum.offset(dist)); // simplify boundary to cut down processing time - boundary = Simplify(MM2INT(0.1), MM2INT(0.1), 0).polygon(boundary); + boundary = Simplify(0.1_mm, 0.1_mm, 0).polygon(boundary); } constexpr bool detect_loops = true; PathOrderOptimizer order_optimizer(near_start_location.value_or(getLastPlannedPositionOrStartingPosition()), ZSeamConfig(), detect_loops, &boundary, reverse_print_direction); @@ -1336,7 +1336,7 @@ void LayerPlan::spiralizeWallSlice(const GCodePathConfig& config, ConstPolygonRe // outline wall has the correct direction - although this creates a little step, the end result is generally better because when the first // outline wall has the wrong direction (due to it starting from the finish point of the last layer) the visual effect is very noticeable Point join_first_wall_at = LinearAlg2D::getClosestOnLineSegment(origin, wall[seam_vertex_idx % wall.size()], wall[(seam_vertex_idx + 1) % wall.size()]); - if (vSize(join_first_wall_at - origin) > 10) + if (vSize2(join_first_wall_at - origin) > INT_EPSILON * INT_EPSILON) { constexpr Ratio flow = 1.0_r; addExtrusionMove(join_first_wall_at, config, SpaceFillType::Polygons, flow, width_factor, spiralize); @@ -1555,7 +1555,7 @@ TimeMaterialEstimates ExtruderPlan::computeNaiveTimeEstimates(Point starting_pos double length = vSizeMM(p0 - p1); if (is_extrusion_path) { - material_estimate += length * INT2MM(layer_thickness) * INT2MM(path.config->getLineWidth()); + material_estimate += length * coord_to_mm2(layer_thickness * path.config->getLineWidth()); } double thisTime = length / (path.config->getSpeed() * path.speed_factor); *path_time_estimate += thisTime; @@ -1980,12 +1980,12 @@ void LayerPlan::writeGCode(GCodeExport& gcode) if (extruder_plan_idx == extruder_plans.size() - 1 || ! extruder.settings.get("machine_extruder_end_pos_abs")) { // only move the head if it's the last extruder plan; otherwise it's already at the switching bay area // or do it anyway when we switch extruder in-place - gcode.setZ(gcode.getPositionZ() + MM2INT(3.0)); + gcode.setZ(gcode.getPositionZ() + 3.0_mm); gcode.writeTravel(gcode.getPositionXY(), configs_storage.travel_config_per_extruder[extruder_nr].getSpeed()); const Point current_pos = gcode.getPositionXY(); const Point machine_middle = storage.machine_size.flatten().getMiddle(); - const Point toward_middle_of_bed = current_pos - normal(current_pos - machine_middle, MM2INT(20.0)); + const Point toward_middle_of_bed = current_pos - normal(current_pos - machine_middle, 20.0_mm); gcode.writeTravel(toward_middle_of_bed, configs_storage.travel_config_per_extruder[extruder_nr].getSpeed()); } gcode.writeDelay(extruder_plan.extraTime); @@ -2038,6 +2038,7 @@ bool LayerPlan::writePathWithCoasting(GCodeExport& gcode, const size_t extruder_ ExtruderPlan& extruder_plan = extruder_plans[extruder_plan_idx]; const ExtruderTrain& extruder = Application::getInstance().current_slice->scene.extruders[extruder_plan.extruder_nr]; const double coasting_volume = extruder.settings.get("coasting_volume"); + const double coasting_min_volume = extruder.settings.get("coasting_min_volume"); if (coasting_volume <= 0) { return false; @@ -2049,13 +2050,13 @@ bool LayerPlan::writePathWithCoasting(GCodeExport& gcode, const size_t extruder_ return false; } - coord_t coasting_min_dist_considered = MM2INT(0.1); // hardcoded setting for when to not perform coasting + coord_t coasting_min_dist_considered = 0.1_mm; // hardcoded setting for when to not perform coasting const double extrude_speed = path.config->getSpeed() * extruder_plan.getExtrudeSpeedFactor() * path.speed_factor * path.speed_back_pressure_factor; - const coord_t coasting_dist = MM2INT(MM2_2INT(coasting_volume) / layer_thickness) / path.config->getLineWidth(); // closing brackets of MM2INT at weird places for precision issues - const double coasting_min_volume = extruder.settings.get("coasting_min_volume"); - const coord_t coasting_min_dist = MM2INT(MM2_2INT(coasting_min_volume + coasting_volume) / layer_thickness) / path.config->getLineWidth(); // closing brackets of MM2INT at weird places for precision issues + const double extrusion_invsection = 1. / coord_to_mm2(layer_thickness * path.config->getLineWidth()); + const coord_t coasting_dist = mm_to_coord(coasting_volume * extrusion_invsection); + const coord_t coasting_min_dist = mm_to_coord((coasting_min_volume + coasting_volume) * extrusion_invsection); // /\ the minimal distance when coasting will coast the full coasting volume instead of linearly less with linearly smaller paths std::vector accumulated_dist_per_point; // the first accumulated dist is that of the last point! (that of the last point is always zero...) diff --git a/src/PrimeTower.cpp b/src/PrimeTower.cpp index 3bebe9758b..e60cbf1ee6 100644 --- a/src/PrimeTower.cpp +++ b/src/PrimeTower.cpp @@ -41,9 +41,8 @@ PrimeTower::PrimeTower() multiple_extruders_on_first_layer = scene.current_mesh_group->settings.get("machine_extruders_share_nozzle") && ((adhesion_type != EPlatformAdhesion::SKIRT) && (adhesion_type != EPlatformAdhesion::BRIM)); } - enabled = scene.current_mesh_group->settings.get("prime_tower_enable") - && scene.current_mesh_group->settings.get("prime_tower_min_volume") > 10 - && scene.current_mesh_group->settings.get("prime_tower_size") > 10; + enabled = scene.current_mesh_group->settings.get("prime_tower_enable") && scene.current_mesh_group->settings.get("prime_tower_min_volume") > INT_EPSILON + && scene.current_mesh_group->settings.get("prime_tower_size") > INT_EPSILON; would_have_actual_tower = enabled; // Assume so for now. extruder_count = scene.extruders.size(); @@ -113,7 +112,7 @@ void PrimeTower::generatePaths_denseInfill() for (size_t extruder_nr : extruder_order) { const coord_t line_width = scene.extruders[extruder_nr].settings.get("prime_tower_line_width"); - const coord_t required_volume = MM3_2INT(scene.extruders[extruder_nr].settings.get("prime_tower_min_volume")); + const coord_t required_volume = mm3_to_coord(scene.extruders[extruder_nr].settings.get("prime_tower_min_volume")); const Ratio flow = scene.extruders[extruder_nr].settings.get("prime_tower_flow"); coord_t current_volume = 0; ExtrusionMoves& pattern = pattern_per_extruder[extruder_nr]; diff --git a/src/SkeletalTrapezoidation.cpp b/src/SkeletalTrapezoidation.cpp index c5cebcf321..a792b8c997 100644 --- a/src/SkeletalTrapezoidation.cpp +++ b/src/SkeletalTrapezoidation.cpp @@ -666,7 +666,7 @@ void SkeletalTrapezoidation::filterNoncentralRegions() spdlog::warn("Encountered an uninitialized bead at the boundary!"); } assert(edge.to->data.bead_count >= 0 || edge.to->data.distance_to_boundary == 0); - constexpr coord_t max_dist = 400; + constexpr coord_t max_dist = 400_mu; filterNoncentralRegions(&edge, edge.to->data.bead_count, 0, max_dist); } } @@ -678,7 +678,7 @@ bool SkeletalTrapezoidation::filterNoncentralRegions(edge_t* to_edge, coord_t be edge_t* next_edge = to_edge->next; for (; next_edge && next_edge != to_edge->twin; next_edge = next_edge->twin->next) { - if (next_edge->to->data.distance_to_boundary >= r || shorterThen(next_edge->to->p - next_edge->from->p, 10)) + if (next_edge->to->data.distance_to_boundary >= r || shorterThen(next_edge->to->p - next_edge->from->p, INT_EPSILON)) { break; // Only walk upward } @@ -1710,7 +1710,8 @@ void SkeletalTrapezoidation::generateJunctions(ptr_vector_t& // Robustness against odd segments which might lie just slightly outside of the range due to rounding errors // not sure if this is really needed (TODO) - if (junction_idx + 1 < num_junctions && beading->toolpath_locations[junction_idx + 1] <= start_R + 5 && beading->total_thickness < start_R + 5) + constexpr coord_t epsilon = INT_EPSILON / 2; + if (junction_idx + 1 < num_junctions && beading->toolpath_locations[junction_idx + 1] <= start_R + epsilon && beading->total_thickness < start_R + epsilon) { junction_idx++; } @@ -1724,7 +1725,7 @@ void SkeletalTrapezoidation::generateJunctions(ptr_vector_t& break; } Point junction(a + ab * (bead_R - start_R) / (end_R - start_R)); - if (bead_R > start_R - 5) + if (bead_R > start_R - epsilon) { // Snap to start node if it is really close, in order to be able to see 3-way intersection later on more robustly junction = a; } @@ -1739,7 +1740,7 @@ std::shared_ptr SkeletalTrapezo { if (node->data.bead_count == -1) { // This bug is due to too small central edges - constexpr coord_t nearby_dist = 100; // TODO + constexpr coord_t nearby_dist = 100_mu; // //FIXME: < Magic constant (how is it derived? should it scale with units?) auto nearest_beading = getNearestBeading(node, nearby_dist); if (nearest_beading) { @@ -1836,13 +1837,13 @@ void SkeletalTrapezoidation::addToolpathSegment(const ExtrusionJunction& from, c { force_new_path = true; } - if (! force_new_path && shorterThen(generated_toolpaths[inset_idx].back().junctions.back().p - from.p, 10) && std::abs(generated_toolpaths[inset_idx].back().junctions.back().w - from.w) < 10 + if (! force_new_path && shorterThen(generated_toolpaths[inset_idx].back().junctions.back().p - from.p, INT_EPSILON) && std::abs(generated_toolpaths[inset_idx].back().junctions.back().w - from.w) < INT_EPSILON && ! from_is_3way // force new path at 3way intersection ) { generated_toolpaths[inset_idx].back().junctions.push_back(to); } - else if (! force_new_path && shorterThen(generated_toolpaths[inset_idx].back().junctions.back().p - to.p, 10) && std::abs(generated_toolpaths[inset_idx].back().junctions.back().w - to.w) < 10 + else if (! force_new_path && shorterThen(generated_toolpaths[inset_idx].back().junctions.back().p - to.p, INT_EPSILON) && std::abs(generated_toolpaths[inset_idx].back().junctions.back().w - to.w) < INT_EPSILON && ! to_is_3way // force new path at 3way intersection ) { @@ -1956,11 +1957,11 @@ void SkeletalTrapezoidation::connectJunctions(ptr_vector_t& edge_ const bool from_is_odd = quad_start->to->data.bead_count > 0 && quad_start->to->data.bead_count % 2 == 1 // quad contains single bead segment && quad_start->to->data.transition_ratio == 0 // We're not in a transition && junction_rev_idx == segment_count - 1 // Is single bead segment - && shorterThen(from.p - quad_start->to->p, 5); + && shorterThen(from.p - quad_start->to->p, INT_EPSILON / 2); const bool to_is_odd = quad_end->from->data.bead_count > 0 && quad_end->from->data.bead_count % 2 == 1 // quad contains single bead segment && quad_end->from->data.transition_ratio == 0 // We're not in a transition && junction_rev_idx == segment_count - 1 // Is single bead segment - && shorterThen(to.p - quad_end->from->p, 5); + && shorterThen(to.p - quad_end->from->p, INT_EPSILON / 2); const bool is_odd_segment = from_is_odd && to_is_odd; if (is_odd_segment && passed_odd_edges.count(quad_start->next->twin) > 0) // Only generate toolpath for odd segments once diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 9313f98e99..300c496f7e 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -79,10 +79,10 @@ void SkirtBrim::getFirstLayerOutline(SliceDataStorage& storage, const size_t pri first_layer_outline.add(storage.primeTower.outer_poly_first_layer); // don't remove parts of the prime tower, but make a brim for it } } - constexpr coord_t join_distance = 20; + constexpr coord_t join_distance = 20_mu; first_layer_outline = first_layer_outline.offset(join_distance).offset(-join_distance); // merge adjacent models into single polygon - constexpr coord_t smallest_line_length = 200; - constexpr coord_t largest_error_of_removed_point = 50; + constexpr coord_t smallest_line_length = 200_mu; + constexpr coord_t largest_error_of_removed_point = 50_mu; first_layer_outline = Simplify(smallest_line_length, largest_error_of_removed_point, 0).polygon(first_layer_outline); if (first_layer_outline.size() == 0) { @@ -90,6 +90,9 @@ void SkirtBrim::getFirstLayerOutline(SliceDataStorage& storage, const size_t pri } } +// Holes are removed when their area is smaller than the squared line width multiplied by this constant: +static constexpr coord_t brim_area_minimum_hole_size_multiplier = 100; + coord_t SkirtBrim::generatePrimarySkirtBrimLines(const coord_t start_distance, size_t& primary_line_count, const coord_t primary_extruder_minimal_length, const Polygons& first_layer_outline, Polygons& skirt_brim_primary_extruder) { const Settings& adhesion_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("skirt_brim_extruder_nr").settings; @@ -105,7 +108,7 @@ coord_t SkirtBrim::generatePrimarySkirtBrimLines(const coord_t start_distance, s for (unsigned int n = 0; n < outer_skirt_brim_line.size(); n++) { double area = outer_skirt_brim_line[n].area(); - if (area < 0 && area > -primary_extruder_skirt_brim_line_width * primary_extruder_skirt_brim_line_width * 100) + if (area < 0 && area > -primary_extruder_skirt_brim_line_width * primary_extruder_skirt_brim_line_width * brim_area_minimum_hole_size_multiplier) { outer_skirt_brim_line.remove(n--); } @@ -239,8 +242,6 @@ void SkirtBrim::generate(SliceDataStorage& storage, Polygons first_layer_outline void SkirtBrim::generateSupportBrim(SliceDataStorage& storage, const bool merge_with_model_skirtbrim) { - constexpr coord_t brim_area_minimum_hole_size_multiplier = 100; - Scene& scene = Application::getInstance().current_slice->scene; const ExtruderTrain& support_infill_extruder = scene.current_mesh_group->settings.get("support_infill_extruder_nr"); const coord_t brim_line_width = support_infill_extruder.settings.get("skirt_brim_line_width") * support_infill_extruder.settings.get("initial_layer_line_width_factor"); diff --git a/src/TopSurface.cpp b/src/TopSurface.cpp index f616a8adca..c5d3e2e740 100644 --- a/src/TopSurface.cpp +++ b/src/TopSurface.cpp @@ -85,7 +85,22 @@ bool TopSurface::ironing(const SliceDataStorage& storage, const SliceMeshStorage } Polygons ironed_areas = areas.offset(ironing_inset); - Infill infill_generator(pattern, zig_zaggify_infill, connect_polygons, ironed_areas, line_width, line_spacing, infill_overlap, infill_multiplier, direction, layer.z - 10, shift, max_resolution, max_deviation, wall_line_count, infill_origin, skip_line_stitching); + Infill infill_generator(pattern, + zig_zaggify_infill, + connect_polygons, + ironed_areas, + line_width, + line_spacing, + infill_overlap, + infill_multiplier, + direction, + layer.z - INT_EPSILON, + shift, + max_resolution, + max_deviation, + wall_line_count, + infill_origin, + skip_line_stitching); std::vector ironing_paths; Polygons ironing_polygons; Polygons ironing_lines; diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index 7479d5a828..2e92f54192 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -154,7 +154,7 @@ const Polygons& TreeModelVolumes::calculateInternalModel(const RadiusLayerPair& Polygons TreeModelVolumes::calculateMachineBorderCollision(Polygon machine_border) { Polygons machine_volume_border; - machine_volume_border.add(machine_border.offset(MM2INT(1000))); //Put a border of 1m around the print volume so that we don't collide. + machine_volume_border.add(machine_border.offset(1000_mm)); // Put a border of 1m around the print volume so that we don't collide. machine_border.reverse(); //Makes the polygon negative so that we subtract the actual volume from the collision area. machine_volume_border.add(machine_border); return machine_volume_border; diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 824ab14be1..f5d5ec4e12 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -179,7 +179,7 @@ void TreeSupport::drawCircles(SliceDataStorage& storage, const std::vector support_layer_parts = support_layer.splitIntoParts(); @@ -323,10 +323,9 @@ void TreeSupport::dropNodes(std::vector>& contact_nodes) }(); // Avoid collisions. - constexpr size_t rounding_compensation = 100; - const coord_t maximum_move_between_samples = maximum_move_distance + radius_sample_resolution + rounding_compensation; + const coord_t maximum_move_between_samples = maximum_move_distance + radius_sample_resolution + INT_PRECISION_COMP; const Polygons avoidance = group_index == 0 ? volumes_.getAvoidance(branch_radius_node, layer_nr - 1) : volumes_.getCollision(branch_radius_node, layer_nr - 1); - PolygonUtils::moveOutside(avoidance, next_position, radius_sample_resolution + rounding_compensation, maximum_move_between_samples * maximum_move_between_samples); + PolygonUtils::moveOutside(avoidance, next_position, radius_sample_resolution + INT_PRECISION_COMP, maximum_move_between_samples * maximum_move_between_samples); Node* neighbour = nodes_per_part[group_index][neighbours[0]]; size_t new_distance_to_top = std::max(node.distance_to_top, neighbour->distance_to_top) + 1; @@ -434,10 +433,9 @@ void TreeSupport::dropNodes(std::vector>& contact_nodes) }(); // Avoid collisions. - constexpr size_t rounding_compensation = 100; - const coord_t maximum_move_between_samples = maximum_move_distance + radius_sample_resolution + rounding_compensation; + const coord_t maximum_move_between_samples = maximum_move_distance + radius_sample_resolution + INT_PRECISION_COMP; const Polygons avoidance = group_index == 0 ? volumes_.getAvoidance(branch_radius_node, layer_nr - 1) : volumes_.getCollision(branch_radius_node, layer_nr - 1); - PolygonUtils::moveOutside(avoidance, next_layer_vertex, radius_sample_resolution + rounding_compensation, maximum_move_between_samples * maximum_move_between_samples); + PolygonUtils::moveOutside(avoidance, next_layer_vertex, radius_sample_resolution + INT_PRECISION_COMP, maximum_move_between_samples * maximum_move_between_samples); const bool to_buildplate = ! volumes_.getAvoidance(branch_radius_node, layer_nr - 1).inside(next_layer_vertex); Node* next_node = new Node(next_layer_vertex, node.distance_to_top + 1, node.skin_direction, node.support_roof_layers_below - 1, to_buildplate, p_node); diff --git a/src/WallToolPaths.cpp b/src/WallToolPaths.cpp index 0642dacd36..a0f7f00e94 100644 --- a/src/WallToolPaths.cpp +++ b/src/WallToolPaths.cpp @@ -16,8 +16,7 @@ namespace cura { -WallToolPaths::WallToolPaths(const Polygons& outline, const coord_t nominal_bead_width, const size_t inset_count, const coord_t wall_0_inset, - const Settings& settings) +WallToolPaths::WallToolPaths(const Polygons& outline, const coord_t nominal_bead_width, const size_t inset_count, const coord_t wall_0_inset, const Settings& settings) : outline(outline) , bead_width_0(nominal_bead_width) , bead_width_x(nominal_bead_width) @@ -26,14 +25,13 @@ WallToolPaths::WallToolPaths(const Polygons& outline, const coord_t nominal_bead , print_thin_walls(settings.get("fill_outline_gaps")) , min_feature_size(settings.get("min_feature_size")) , min_bead_width(settings.get("min_bead_width")) - , small_area_length(INT2MM(static_cast(nominal_bead_width) / 2)) + , small_area_length(coord_to_mm(nominal_bead_width) / 2.0) , toolpaths_generated(false) , settings(settings) { } -WallToolPaths::WallToolPaths(const Polygons& outline, const coord_t bead_width_0, const coord_t bead_width_x, - const size_t inset_count, const coord_t wall_0_inset, const Settings& settings) +WallToolPaths::WallToolPaths(const Polygons& outline, const coord_t bead_width_0, const coord_t bead_width_x, const size_t inset_count, const coord_t wall_0_inset, const Settings& settings) : outline(outline) , bead_width_0(bead_width_0) , bead_width_x(bead_width_x) @@ -42,7 +40,7 @@ WallToolPaths::WallToolPaths(const Polygons& outline, const coord_t bead_width_0 , print_thin_walls(settings.get("fill_outline_gaps")) , min_feature_size(settings.get("min_feature_size")) , min_bead_width(settings.get("min_bead_width")) - , small_area_length(INT2MM(static_cast(bead_width_0) / 2)) + , small_area_length(coord_to_mm(bead_width_0) / 2.0) , toolpaths_generated(false) , settings(settings) { @@ -53,7 +51,7 @@ const std::vector& WallToolPaths::generate() const coord_t allowed_distance = settings.get("meshfix_maximum_deviation"); const coord_t epsilon_offset = (allowed_distance / 2) - 1; const AngleRadians transitioning_angle = settings.get("wall_transition_angle"); - constexpr coord_t discretization_step_size = MM2INT(0.8); + constexpr coord_t discretization_step_size = 0.8_mm; // Simplify outline for boost::voronoi consumption. Absolutely no self intersections or near-self intersections allowed: // TODO: Open question: Does this indeed fix all (or all-but-one-in-a-million) cases for manifold but otherwise possibly complex polygons? diff --git a/src/Wireframe2gcode.cpp b/src/Wireframe2gcode.cpp index 3c78d2ee29..70c04cc182 100644 --- a/src/Wireframe2gcode.cpp +++ b/src/Wireframe2gcode.cpp @@ -116,7 +116,7 @@ void Wireframe2gcode::writeGCode() { if (vSize2(gcode.getPositionXY() - part.connection.from) > connectionHeight) { - Point3 point_same_height(part.connection.from.x, part.connection.from.y, layer.z1 + MM2INT(0.1)); + Point3 point_same_height(part.connection.from.x, part.connection.from.y, layer.z1 + 0.1_mm); writeMoveWithRetract(point_same_height); } writeMoveWithRetract(part.connection.from); @@ -248,8 +248,8 @@ void Wireframe2gcode::strategy_retract(WeaveConnectionPart& part, unsigned int s Settings& scene_settings = Application::getInstance().current_slice->scene.settings; RetractionConfig retraction_config; // TODO: get these from the settings! - retraction_config.distance = MM2INT(0.5); // INT2MM(getSettingInt("retraction_amount")) - retraction_config.prime_volume = 0; // INT2MM(getSettingInt("retractionPrime + retraction_config.distance = 0.5_mm; + retraction_config.prime_volume = 0; retraction_config.speed = 20; // 40; retraction_config.primeSpeed = 15; // 30; retraction_config.zHop = 0; // getSettingInt("retraction_hop"); @@ -258,7 +258,7 @@ void Wireframe2gcode::strategy_retract(WeaveConnectionPart& part, unsigned int s retraction_config.retraction_min_travel_distance = scene_settings.get("retraction_min_travel"); double top_retract_pause = 2.0; - const coord_t retract_hop_dist = MM2INT(1); + const coord_t retract_hop_dist = 1_mm; bool after_retract_hop = false; // bool go_horizontal_first = true; bool lower_retract_start = true; @@ -504,7 +504,7 @@ Wireframe2gcode::Wireframe2gcode(Weaver& weaver, GCodeExport& gcode) : gcode(gco flowConnection = scene_settings.get("wireframe_flow_connection"); flowFlat = scene_settings.get("wireframe_flow_flat"); - const double line_area = M_PI * square(INT2MM(line_width) / 2.0); + const double line_area = M_PI * square(coord_to_mm(line_width) / 2.0); extrusion_mm3_per_mm_connection = line_area * flowConnection; extrusion_mm3_per_mm_flat = line_area * flowFlat; @@ -610,7 +610,7 @@ void Wireframe2gcode::processStartingCode() { gcode.writeComment("enable auto-retraction"); std::ostringstream tmp; - tmp << "M227 S" << (scene_settings.get("retraction_amount") * 2560 / 1000) << " P" << (scene_settings.get("retraction_amount") * 2560 / 1000); + tmp << "M227 S" << coord_to_mm(scene_settings.get("retraction_amount") * 2560) << " P" << coord_to_mm(scene_settings.get("retraction_amount") * 2560); gcode.writeLine(tmp.str().c_str()); } else if (gcode.getFlavor() == EGCodeFlavor::GRIFFIN) @@ -632,7 +632,7 @@ void Wireframe2gcode::processSkirt() { return; } - Polygons skirt = wireFrame.bottom_outline.offset(MM2INT(100 + 5), ClipperLib::jtRound).offset(MM2INT(-100), ClipperLib::jtRound); + Polygons skirt = wireFrame.bottom_outline.offset(105_mm, ClipperLib::jtRound).offset(-100_mm, ClipperLib::jtRound); PathOrderOptimizer order(Point(INT32_MIN, INT32_MIN)); for (PolygonRef skirt_path : skirt) { @@ -649,7 +649,7 @@ void Wireframe2gcode::processSkirt() Point vertex = (*path.vertices)[(vertex_index + path.start_vertex + 1) % path.vertices->size()]; gcode.writeExtrusion(vertex, scene_settings.get("skirt_brim_speed"), - scene_settings.get("skirt_brim_line_width") * scene_settings.get("initial_layer_line_width_factor") * INT2MM(initial_layer_thickness), + scene_settings.get("skirt_brim_line_width") * scene_settings.get("initial_layer_line_width_factor") * coord_to_mm(initial_layer_thickness), PrintFeatureType::SkirtBrim); } } diff --git a/src/bridge.cpp b/src/bridge.cpp index f6f4b41f04..5cf05fc7b7 100644 --- a/src/bridge.cpp +++ b/src/bridge.cpp @@ -108,8 +108,8 @@ int bridgeAngle(const Settings& settings, const Polygons& skin_outline, const Sl // It needs to be shrunk slightly so that the vertices of the skin polygon that would otherwise fall exactly on // the air boundary do appear to be supported - const int bb_max_dim = std::max(boundary_box.max.X - boundary_box.min.X, boundary_box.max.Y - boundary_box.min.Y); - const Polygons air_below(bb_poly.offset(bb_max_dim).difference(prev_layer_outline).offset(-10)); + const coord_t bb_max_dim = std::max(boundary_box.max.X - boundary_box.min.X, boundary_box.max.Y - boundary_box.min.Y); + const Polygons air_below(bb_poly.offset(bb_max_dim).difference(prev_layer_outline).offset(-INT_EPSILON)); Polygons skin_perimeter_lines; for (ConstPolygonRef poly : skin_outline) diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index 30786e2cce..687eb785d3 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -254,8 +254,8 @@ class ArcusCommunication::PathCompiler */ void addPoint2D(const Point& point) { - points.push_back(INT2MM(point.X)); - points.push_back(INT2MM(point.Y)); + points.push_back(coord_to_mm(point.X)); + points.push_back(coord_to_mm(point.Y)); last_point = point; } @@ -275,8 +275,8 @@ class ArcusCommunication::PathCompiler { addPoint2D(point); line_types.push_back(print_feature_type); - line_widths.push_back(INT2MM(width)); - line_thicknesses.push_back(INT2MM(thickness)); + line_widths.push_back(coord_to_mm(width)); + line_thicknesses.push_back(coord_to_mm(thickness)); line_velocities.push_back(velocity); } }; @@ -389,8 +389,9 @@ void ArcusCommunication::sendFinishedSlicing() const void ArcusCommunication::sendLayerComplete(const LayerIndex& layer_nr, const coord_t& z, const coord_t& thickness) { std::shared_ptr layer = private_data->getOptimizedLayerById(layer_nr); - layer->set_height(z); - layer->set_thickness(thickness); + // FIXME: Currently, the frontend interprets layer heights as microns. Because proto use a float value, this should be interpreted as milimeters instead. + layer->set_height(coord_to_mm(z) * 1000.0); + layer->set_thickness(coord_to_mm(thickness) * 1000.0); } void ArcusCommunication::sendLineTo(const PrintFeatureType& type, const Point& to, const coord_t& line_width, const coord_t& line_thickness, const Velocity& velocity) diff --git a/src/gcodeExport.cpp b/src/gcodeExport.cpp index 4758ec5503..f90882743c 100644 --- a/src/gcodeExport.cpp +++ b/src/gcodeExport.cpp @@ -35,7 +35,7 @@ std::string transliterate(const std::string& text) return stream.str(); } -GCodeExport::GCodeExport() : output_stream(&std::cout), currentPosition(0, 0, MM2INT(20)), layer_nr(0), relative_extrusion(false) +GCodeExport::GCodeExport() : output_stream(&std::cout), currentPosition(0, 0, 20_mm), layer_nr(0), relative_extrusion(false) { *output_stream << std::fixed; @@ -213,14 +213,14 @@ std::string GCodeExport::getFileHeader(const std::vector& extruder_is_used { // Put some small default in there. total_bounding_box.min = Point3(0, 0, 0); - total_bounding_box.max = Point3(10, 10, 10); + total_bounding_box.max = Point3(INT_EPSILON, INT_EPSILON, INT_EPSILON); } - prefix << ";PRINT.SIZE.MIN.X:" << INT2MM(total_bounding_box.min.x) << new_line; - prefix << ";PRINT.SIZE.MIN.Y:" << INT2MM(total_bounding_box.min.y) << new_line; - prefix << ";PRINT.SIZE.MIN.Z:" << INT2MM(total_bounding_box.min.z) << new_line; - prefix << ";PRINT.SIZE.MAX.X:" << INT2MM(total_bounding_box.max.x) << new_line; - prefix << ";PRINT.SIZE.MAX.Y:" << INT2MM(total_bounding_box.max.y) << new_line; - prefix << ";PRINT.SIZE.MAX.Z:" << INT2MM(total_bounding_box.max.z) << new_line; + prefix << ";PRINT.SIZE.MIN.X:" << coord_to_mm(total_bounding_box.min.x) << new_line; + prefix << ";PRINT.SIZE.MIN.Y:" << coord_to_mm(total_bounding_box.min.y) << new_line; + prefix << ";PRINT.SIZE.MIN.Z:" << coord_to_mm(total_bounding_box.min.z) << new_line; + prefix << ";PRINT.SIZE.MAX.X:" << coord_to_mm(total_bounding_box.max.x) << new_line; + prefix << ";PRINT.SIZE.MAX.Y:" << coord_to_mm(total_bounding_box.max.y) << new_line; + prefix << ";PRINT.SIZE.MAX.Z:" << coord_to_mm(total_bounding_box.max.z) << new_line; prefix << ";SLICE_UUID:" << slice_uuid_ << new_line; prefix << ";END_OF_HEADER" << new_line; break; @@ -262,12 +262,12 @@ std::string GCodeExport::getFileHeader(const std::vector& extruder_is_used prefix << new_line; prefix << ";Layer height: " << Application::getInstance().current_slice->scene.current_mesh_group->settings.get("layer_height") << new_line; } - prefix << ";MINX:" << INT2MM(total_bounding_box.min.x) << new_line; - prefix << ";MINY:" << INT2MM(total_bounding_box.min.y) << new_line; - prefix << ";MINZ:" << INT2MM(total_bounding_box.min.z) << new_line; - prefix << ";MAXX:" << INT2MM(total_bounding_box.max.x) << new_line; - prefix << ";MAXY:" << INT2MM(total_bounding_box.max.y) << new_line; - prefix << ";MAXZ:" << INT2MM(total_bounding_box.max.z) << new_line; + prefix << ";MINX:" << coord_to_mm(total_bounding_box.min.x) << new_line; + prefix << ";MINY:" << coord_to_mm(total_bounding_box.min.y) << new_line; + prefix << ";MINZ:" << coord_to_mm(total_bounding_box.min.z) << new_line; + prefix << ";MAXX:" << coord_to_mm(total_bounding_box.max.x) << new_line; + prefix << ";MAXY:" << coord_to_mm(total_bounding_box.max.y) << new_line; + prefix << ";MAXZ:" << coord_to_mm(total_bounding_box.max.z) << new_line; } return prefix.str(); @@ -375,7 +375,7 @@ int GCodeExport::getExtruderNr() const void GCodeExport::setFilamentDiameter(const size_t extruder, const coord_t diameter) { - const double r = INT2MM(diameter) / 2.0; + const double r = coord_to_mm(diameter) / 2.0; const double area = M_PI * r * r; extruder_attr[extruder].filament_area = area; } @@ -671,7 +671,7 @@ void GCodeExport::writeMoveBFB(const int x, const int y, const int z, const Velo { // fprintf(f, "; %f e-per-mm %d mm-width %d mm/s\n", extrusion_per_mm, lineWidth, speed); // fprintf(f, "M108 S%0.1f\r\n", rpm); - *output_stream << "M108 S" << PrecisionedDouble{ 1, rpm } << new_line; + *output_stream << "M108 S" << PrecisionedDouble<1>{ rpm } << new_line; currentSpeed = double(rpm); } // Add M101 or M201 to enable the proper extruder. @@ -697,10 +697,10 @@ void GCodeExport::writeMoveBFB(const int x, const int y, const int z, const Velo } } *output_stream << "G1 X" << MMtoStream{ gcode_pos.X } << " Y" << MMtoStream{ gcode_pos.Y } << " Z" << MMtoStream{ z }; - *output_stream << " F" << PrecisionedDouble{ 1, fspeed } << new_line; + *output_stream << " F" << PrecisionedDouble<1>{ fspeed } << new_line; currentPosition = Point3(x, y, z); - estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), speed, feature); + estimateCalculator.plan(TimeEstimateCalculator::Position(coord_to_mm(currentPosition.x), coord_to_mm(currentPosition.y), coord_to_mm(currentPosition.z), eToMm(current_e_value)), speed, feature); } void GCodeExport::writeTravel(const coord_t x, const coord_t y, const coord_t z, const Velocity& speed) @@ -714,11 +714,11 @@ void GCodeExport::writeTravel(const coord_t x, const coord_t y, const coord_t z, assert(speed < 1000 && speed > 1); // normal F values occurring in UM2 gcode (this code should not be compiled for release) assert(currentPosition != no_point3); assert(Point3(x, y, z) != no_point3); - assert((Point3(x, y, z) - currentPosition).vSize() < MM2INT(1000)); // no crazy positions (this code should not be compiled for release) + assert((Point3(x, y, z) - currentPosition).vSize() < 1000_mm); // no crazy positions (this code should not be compiled for release) #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 int display_width = extruder_attr[current_extruder].retraction_e_amount_current ? 0.2_mm : 0.1_mm; const double layer_height = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("layer_height"); Application::getInstance().communication->sendLineTo(travel_move_type, Point(x, y), display_width, layer_height, speed); @@ -737,7 +737,7 @@ void GCodeExport::writeExtrusion(const coord_t x, const coord_t y, const coord_t assert(speed < 1000 && speed > 1); // normal F values occurring in UM2 gcode (this code should not be compiled for release) assert(currentPosition != no_point3); assert(Point3(x, y, z) != no_point3); - assert((Point3(x, y, z) - currentPosition).vSize() < MM2INT(1000)); // no crazy positions (this code should not be compiled for release) + assert((Point3(x, y, z) - currentPosition).vSize() < 1000_mm); // no crazy positions (this code should not be compiled for release) assert(extrusion_mm3_per_mm >= 0.0); #endif // ASSERT_INSANE_OUTPUT #ifdef DEBUG @@ -801,7 +801,7 @@ void GCodeExport::writeFXYZE(const Velocity& speed, const coord_t x, const coord { if (currentSpeed != speed) { - *output_stream << " F" << PrecisionedDouble{ 1, speed * 60 }; + *output_stream << " F" << PrecisionedDouble<1>{ speed * 60 }; currentSpeed = speed; } @@ -816,13 +816,13 @@ void GCodeExport::writeFXYZE(const Velocity& speed, const coord_t x, const coord if (e + current_e_offset != current_e_value) { const double output_e = (relative_extrusion) ? e + current_e_offset - current_e_value : e + current_e_offset; - *output_stream << " " << extruder_attr[current_extruder].extruderCharacter << PrecisionedDouble{ 5, output_e }; + *output_stream << " " << extruder_attr[current_extruder].extruderCharacter << PrecisionedDouble<5>{ output_e }; } *output_stream << new_line; currentPosition = Point3(x, y, z); current_e_value = e; - estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(x), INT2MM(y), INT2MM(z), eToMm(e)), speed, feature); + estimateCalculator.plan(TimeEstimateCalculator::Position(coord_to_mm(x), coord_to_mm(y), coord_to_mm(z), eToMm(e)), speed, feature); } void GCodeExport::writeUnretractionAndPrime() @@ -840,28 +840,28 @@ void GCodeExport::writeUnretractionAndPrime() 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].extruderCharacter << PrecisionedDouble{ 5, output_e } + *output_stream << "G1 F" << PrecisionedDouble<1>{ extruder_attr[current_extruder].last_retraction_prime_speed * 60 } << " " << extruder_attr[current_extruder].extruderCharacter << PrecisionedDouble<5>{ output_e } << new_line; currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed; } - estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), 25.0, PrintFeatureType::MoveRetraction); + estimateCalculator.plan(TimeEstimateCalculator::Position(coord_to_mm(currentPosition.x), coord_to_mm(currentPosition.y), coord_to_mm(currentPosition.z), eToMm(current_e_value)), 25.0, PrintFeatureType::MoveRetraction); } else { 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].extruderCharacter << PrecisionedDouble{ 5, output_e } << new_line; + *output_stream << "G1 F" << PrecisionedDouble<1>{ extruder_attr[current_extruder].last_retraction_prime_speed * 60 } << " " << extruder_attr[current_extruder].extruderCharacter << PrecisionedDouble<5>{ output_e } << new_line; currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed; - estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed, PrintFeatureType::MoveRetraction); + estimateCalculator.plan(TimeEstimateCalculator::Position(coord_to_mm(currentPosition.x), coord_to_mm(currentPosition.y), coord_to_mm(currentPosition.z), eToMm(current_e_value)), currentSpeed, PrintFeatureType::MoveRetraction); } } 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].extruderCharacter; - *output_stream << PrecisionedDouble{ 5, output_e } << new_line; + *output_stream << "G1 F" << PrecisionedDouble<1>{ extruder_attr[current_extruder].last_retraction_prime_speed * 60 } << " " << extruder_attr[current_extruder].extruderCharacter; + *output_stream << PrecisionedDouble<5>{ output_e } << new_line; currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed; - estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed, PrintFeatureType::NoneType); + estimateCalculator.plan(TimeEstimateCalculator::Position(coord_to_mm(currentPosition.x), coord_to_mm(currentPosition.y), coord_to_mm(currentPosition.z), eToMm(current_e_value)), currentSpeed, PrintFeatureType::NoneType); } extruder_attr[current_extruder].prime_volume = 0.0; @@ -940,7 +940,7 @@ void GCodeExport::writeRetraction(const RetractionConfig& config, bool force, bo } *output_stream << new_line; // Assume default UM2 retraction settings. - estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value + retraction_diff_e_amount)), + estimateCalculator.plan(TimeEstimateCalculator::Position(coord_to_mm(currentPosition.x), coord_to_mm(currentPosition.y), coord_to_mm(currentPosition.z), eToMm(current_e_value + retraction_diff_e_amount)), 25, PrintFeatureType::MoveRetraction); // TODO: hardcoded values! } @@ -949,9 +949,9 @@ void GCodeExport::writeRetraction(const RetractionConfig& config, bool force, bo 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.extruderCharacter << PrecisionedDouble{ 5, output_e } << new_line; + *output_stream << "G1 F" << PrecisionedDouble<1>{ speed * 60 } << " " << extr_attr.extruderCharacter << PrecisionedDouble<5>{ output_e } << new_line; currentSpeed = speed; - estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed, PrintFeatureType::MoveRetraction); + estimateCalculator.plan(TimeEstimateCalculator::Position(coord_to_mm(currentPosition.x), coord_to_mm(currentPosition.y), coord_to_mm(currentPosition.z), eToMm(current_e_value)), currentSpeed, PrintFeatureType::MoveRetraction); extr_attr.last_retraction_prime_speed = config.primeSpeed; } @@ -970,7 +970,7 @@ void GCodeExport::writeZhopStart(const coord_t hop_height, Velocity speed /*= 0* } is_z_hopped = hop_height; currentSpeed = speed; - *output_stream << "G1 F" << PrecisionedDouble{ 1, speed * 60 } << " Z" << MMtoStream{ current_layer_z + is_z_hopped } << new_line; + *output_stream << "G1 F" << PrecisionedDouble<1>{ speed * 60 } << " Z" << MMtoStream{ current_layer_z + is_z_hopped } << new_line; total_bounding_box.includeZ(current_layer_z + is_z_hopped); assert(speed > 0.0 && "Z hop speed should be positive."); } @@ -988,7 +988,7 @@ void GCodeExport::writeZhopEnd(Velocity speed /*= 0*/) is_z_hopped = 0; currentPosition.z = current_layer_z; currentSpeed = speed; - *output_stream << "G1 F" << PrecisionedDouble{ 1, speed * 60 } << " Z" << MMtoStream{ current_layer_z } << new_line; + *output_stream << "G1 F" << PrecisionedDouble<1>{ speed * 60 } << " Z" << MMtoStream{ current_layer_z } << new_line; assert(speed > 0.0 && "Z hop speed should be positive."); } } @@ -1173,8 +1173,16 @@ void GCodeExport::writeFanCommand(double speed) } else if (speed > 0) { + *output_stream << "M106 S"; const bool should_scale_zero_to_one = Application::getInstance().current_slice->scene.settings.get("machine_scale_fan_speed_zero_to_one"); - *output_stream << "M106 S" << PrecisionedDouble{ (should_scale_zero_to_one ? 2u : 1u), (should_scale_zero_to_one ? speed : speed * 255) / 100 }; + if (should_scale_zero_to_one) + { + *output_stream << PrecisionedDouble<2>{ speed / 100. }; + } + else + { + *output_stream << PrecisionedDouble<1>{ speed * (255. / 100) }; + } if (fan_number) { *output_stream << " P" << fan_number; @@ -1256,7 +1264,7 @@ void GCodeExport::writeTemperatureCommand(const size_t extruder, const Temperatu #ifdef ASSERT_INSANE_OUTPUT assert(temperature >= 0); #endif // ASSERT_INSANE_OUTPUT - *output_stream << " S" << PrecisionedDouble{ 1, temperature } << new_line; + *output_stream << " S" << PrecisionedDouble<1>{ temperature } << new_line; if (extruder != current_extruder && always_write_active_tool) { // Some firmwares (ie Smoothieware) change tools every time a "T" command is read - even on a M104 line, so we need to switch back to the active tool. @@ -1284,7 +1292,7 @@ void GCodeExport::writeBedTemperatureCommand(const Temperature& temperature, con if (flavor == EGCodeFlavor::MARLIN) { *output_stream << "M140 S"; // set the temperature, it will be used as target temperature from M105 - *output_stream << PrecisionedDouble{ 1, temperature } << new_line; + *output_stream << PrecisionedDouble<1>{ temperature } << new_line; *output_stream << "M105" << new_line; } } @@ -1298,7 +1306,7 @@ void GCodeExport::writeBedTemperatureCommand(const Temperature& temperature, con } if (wrote_command) { - *output_stream << PrecisionedDouble{ 1, temperature } << new_line; + *output_stream << PrecisionedDouble<1>{ temperature } << new_line; } bed_temperature = temperature; } @@ -1318,7 +1326,7 @@ void GCodeExport::writeBuildVolumeTemperatureCommand(const Temperature& temperat { *output_stream << "M141 S"; } - *output_stream << PrecisionedDouble{ 1, temperature } << new_line; + *output_stream << PrecisionedDouble<1>{ temperature } << new_line; } void GCodeExport::writePrintAcceleration(const Acceleration& acceleration) @@ -1328,20 +1336,20 @@ void GCodeExport::writePrintAcceleration(const Acceleration& acceleration) case EGCodeFlavor::REPETIER: if (current_print_acceleration != acceleration) { - *output_stream << "M201 X" << PrecisionedDouble{ 0, acceleration } << " Y" << PrecisionedDouble{ 0, acceleration } << new_line; + *output_stream << "M201 X" << PrecisionedDouble<0>{ acceleration } << " Y" << PrecisionedDouble<0>{ acceleration } << new_line; } break; case EGCodeFlavor::REPRAP: if (current_print_acceleration != acceleration) { - *output_stream << "M204 P" << PrecisionedDouble{ 0, acceleration } << new_line; + *output_stream << "M204 P" << PrecisionedDouble<0>{ acceleration } << new_line; } break; default: // MARLIN, etc. only have one acceleration for both print and travel if (current_print_acceleration != acceleration) { - *output_stream << "M204 S" << PrecisionedDouble{ 0, acceleration } << new_line; + *output_stream << "M204 S" << PrecisionedDouble<0>{ acceleration } << new_line; } break; } @@ -1356,13 +1364,13 @@ void GCodeExport::writeTravelAcceleration(const Acceleration& acceleration) case EGCodeFlavor::REPETIER: if (current_travel_acceleration != acceleration) { - *output_stream << "M202 X" << PrecisionedDouble{ 0, acceleration } << " Y" << PrecisionedDouble{ 0, acceleration } << new_line; + *output_stream << "M202 X" << PrecisionedDouble<0>{ acceleration } << " Y" << PrecisionedDouble<0>{ acceleration } << new_line; } break; case EGCodeFlavor::REPRAP: if (current_travel_acceleration != acceleration) { - *output_stream << "M204 T" << PrecisionedDouble{ 0, acceleration } << new_line; + *output_stream << "M204 T" << PrecisionedDouble<0>{ acceleration } << new_line; } break; default: @@ -1381,13 +1389,13 @@ void GCodeExport::writeJerk(const Velocity& jerk) switch (getFlavor()) { case EGCodeFlavor::REPETIER: - *output_stream << "M207 X" << PrecisionedDouble{ 2, jerk } << new_line; + *output_stream << "M207 X" << PrecisionedDouble<2>{ jerk } << new_line; break; case EGCodeFlavor::REPRAP: - *output_stream << "M566 X" << PrecisionedDouble{ 2, jerk * 60 } << " Y" << PrecisionedDouble{ 2, jerk * 60 } << new_line; + *output_stream << "M566 X" << PrecisionedDouble<2>{ jerk * 60 } << " Y" << PrecisionedDouble<2>{ jerk * 60 } << new_line; break; default: - *output_stream << "M205 X" << PrecisionedDouble{ 2, jerk } << " Y" << PrecisionedDouble{ 2, jerk } << new_line; + *output_stream << "M205 X" << PrecisionedDouble<2>{ jerk } << " Y" << PrecisionedDouble<2>{ jerk } << new_line; break; } current_jerk = jerk; diff --git a/src/infill/GyroidInfill.cpp b/src/infill/GyroidInfill.cpp index d05767623a..e3215b1a12 100644 --- a/src/infill/GyroidInfill.cpp +++ b/src/infill/GyroidInfill.cpp @@ -23,8 +23,8 @@ void GyroidInfill::generateTotalGyroidInfill(Polygons& result_lines, bool zig_za int pitch = line_distance * 2.41; // this produces similar density to the "line" infill pattern int num_steps = 4; - int step = pitch / num_steps; - while (step > 500 && num_steps < 16) + coord_t step = pitch / num_steps; + while (step > 500_mu && num_steps < 16) { num_steps *= 2; step = pitch / num_steps; @@ -267,7 +267,7 @@ void GyroidInfill::generateTotalGyroidInfill(Polygons& result_lines, bool zig_za { // don't include chain ends that are close to the segment but are beyond the segment ends short beyond = 0; - if (LinearAlg2D::getDist2FromLineSegment(op0, chains[point_index][chain_index], op1, &beyond) < 10 && !beyond) + if (LinearAlg2D::getDist2FromLineSegment(op0, chains[point_index][chain_index], op1, &beyond) < INT_EPSILON && ! beyond) { points_on_outline_point_index.push_back(point_index); points_on_outline_chain_index.push_back(chain_index); @@ -275,7 +275,7 @@ void GyroidInfill::generateTotalGyroidInfill(Polygons& result_lines, bool zig_za } } - if (outline_point_index == 0 || vSize2(op0 - cur_point) > MM2INT(0.1)) + if (outline_point_index == 0 || vSize2(op0 - cur_point) > 100_mu2) { // this is either the first outline point or it is another outline point that is not too close to cur_point @@ -314,7 +314,7 @@ void GyroidInfill::generateTotalGyroidInfill(Polygons& result_lines, bool zig_za // make the chain end the current point and add it to the connector line cur_point = chains[point_index][chain_index]; - if (drawing && connector_points.size() > 0 && vSize2(cur_point - connector_points.back()) < MM2INT(0.1)) + if (drawing && connector_points.size() > 0 && vSize2(cur_point - connector_points.back()) < 100_mu2) { // this chain end will be too close to the last connector point so throw away the last connector point connector_points.pop_back(); diff --git a/src/infill/LightningLayer.cpp b/src/infill/LightningLayer.cpp index 3226c0b546..f422111ec4 100644 --- a/src/infill/LightningLayer.cpp +++ b/src/infill/LightningLayer.cpp @@ -173,7 +173,7 @@ void LightningLayer::reconnectRoots const coord_t wall_supporting_radius ) { - constexpr coord_t tree_connecting_ignore_offset = 100; + constexpr coord_t tree_connecting_ignore_offset = 100_mu; SparseLightningTreeNodeGrid tree_node_locator(locator_cell_size); fillLocator(tree_node_locator); diff --git a/src/infill/LightningTreeNode.cpp b/src/infill/LightningTreeNode.cpp index 33af29678c..495fd217b9 100644 --- a/src/infill/LightningTreeNode.cpp +++ b/src/infill/LightningTreeNode.cpp @@ -265,16 +265,9 @@ LightningTreeNode::RectilinearJunction LightningTreeNode::straighten } } { // remove nodes on linear segments - constexpr coord_t close_enough = 10; - child_p = children.front(); //recursive call to straighten might have removed the child const LightningTreeNodeSPtr& parent_node = parent.lock(); - if - ( - parent_node && - vSize2(child_p->p - parent_node->p) < max_remove_colinear_dist2 && - LinearAlg2D::getDist2FromLineSegment(parent_node->p, p, child_p->p) < close_enough - ) + if (parent_node && vSize2(child_p->p - parent_node->p) < max_remove_colinear_dist2 && LinearAlg2D::getDist2FromLineSegment(parent_node->p, p, child_p->p) < INT_EPSILON) { child_p->parent = parent; for (auto& sibling : parent_node->children) @@ -351,7 +344,7 @@ coord_t LightningTreeNode::prune(const coord_t& pruning_distance) else { // pruning stops in between this node and the child const Point n = b + normal(ba, pruning_distance - dist_pruned_child); - assert(std::abs(vSize(n - b) + dist_pruned_child - pruning_distance) < 10 && "total pruned distance must be equal to the pruning_distance"); + assert(std::abs(vSize(n - b) + dist_pruned_child - pruning_distance) < INT_EPSILON && "total pruned distance must be equal to the pruning_distance"); max_distance_pruned = std::max(max_distance_pruned, pruning_distance); child->setLocation(n); ++child_it; diff --git a/src/infill/SierpinskiFill.cpp b/src/infill/SierpinskiFill.cpp index b345493044..2f036cedf8 100644 --- a/src/infill/SierpinskiFill.cpp +++ b/src/infill/SierpinskiFill.cpp @@ -119,7 +119,7 @@ void SierpinskiFill::createTree(SierpinskiTriangle& sub_root) void SierpinskiFill::createTreeStatistics(SierpinskiTriangle& triangle) { Point ac = triangle.straight_corner - triangle.a; - float area = 0.5 * INT2MM2(vSize2(ac)); + float area = 0.5 * coord_to_mm2(vSize2(ac)); float short_length = .5 * vSizeMM(ac); float long_length = .5 * vSizeMM(triangle.b - triangle.a); triangle.area = area; @@ -141,7 +141,7 @@ void SierpinskiFill::createTreeRequestedLengths(SierpinskiTriangle& triangle) triangle_aabb.include(triangle.straight_corner); AABB3D triangle_aabb3d(Point3(triangle_aabb.min.X, triangle_aabb.min.Y, 0), Point3(triangle_aabb.max.X, triangle_aabb.max.Y, 1)); float density = density_provider(triangle_aabb3d); // The density of the square around the triangle is a rough estimate of the density of the triangle. - triangle.requested_length = density * triangle.area / INT2MM(line_width); + triangle.requested_length = density * triangle.area / coord_to_mm(line_width); } else { // bubble total up requested_length and total_child_realized_length @@ -708,7 +708,7 @@ Polygon SierpinskiFill::generateCross() const ret.add(edge_middle / 2); } - float realized_length = INT2MM(ret.polygonLength()); + float realized_length = coord_to_mm(ret.polygonLength()); float requested_length = root.requested_length; float error = (realized_length - requested_length) / requested_length; spdlog::debug("realized_length: {}, requested_length: {} :: {}% error", realized_length, requested_length, .01 * static_cast(10000 * error)); @@ -754,7 +754,7 @@ Polygon SierpinskiFill::generateCross(coord_t z, coord_t min_dist_to_side, coord const coord_t period = vSize(last_triangle->straight_corner - last_triangle->a); ret.add(get_edge_crossing_location(period, last_triangle->getToEdge())); - if (pocket_size > 10) + if (pocket_size > INT_EPSILON) { // round off corners by half square root 2 of the pocket size so that the whole hole will be sqrt_pocket_size wide // \ / \ / diff --git a/src/infill/SubDivCube.cpp b/src/infill/SubDivCube.cpp index 20e4a3f0e5..3a675c6a19 100644 --- a/src/infill/SubDivCube.cpp +++ b/src/infill/SubDivCube.cpp @@ -268,17 +268,16 @@ void SubDivCube::rotatePoint120(Point& target) void SubDivCube::addLineAndCombine(Polygons& group, Point from, Point to) { - int epsilon = 10; // the smallest distance of two points which are viewed as coincident (dist > 0 due to rounding errors) for (unsigned int idx = 0; idx < group.size(); idx++) { - if (std::abs(from.X - group[idx][1].X) < epsilon && std::abs(from.Y - group[idx][1].Y) < epsilon) + if (std::abs(from.X - group[idx][1].X) < INT_EPSILON && std::abs(from.Y - group[idx][1].Y) < INT_EPSILON) { from = group[idx][0]; group.remove(idx); idx--; continue; } - if (std::abs(to.X - group[idx][0].X) < epsilon && std::abs(to.Y - group[idx][0].Y) < epsilon) + if (std::abs(to.X - group[idx][0].X) < INT_EPSILON && std::abs(to.Y - group[idx][0].Y) < INT_EPSILON) { to = group[idx][1]; group.remove(idx); diff --git a/src/mesh.cpp b/src/mesh.cpp index 82a651a9cf..f192402190 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -9,7 +9,7 @@ namespace cura { -const int vertex_meld_distance = MM2INT(0.03); +const int vertex_meld_distance = 30_mu; /*! * returns a hash for the location, but first divides by the vertex_meld_distance, * so that any point within a box of vertex_meld_distance by vertex_meld_distance would get mapped to the same hash. diff --git a/src/settings/AdaptiveLayerHeights.cpp b/src/settings/AdaptiveLayerHeights.cpp index 0f9d4e9e82..1eac7654ec 100644 --- a/src/settings/AdaptiveLayerHeights.cpp +++ b/src/settings/AdaptiveLayerHeights.cpp @@ -230,8 +230,8 @@ void AdaptiveLayerHeights::calculateMeshTriangleSlopes() z_angle = M_PI; } - face_min_z_values.push_back(MM2INT(min_z)); - face_max_z_values.push_back(MM2INT(max_z)); + face_min_z_values.push_back(mm_to_coord(min_z)); + face_max_z_values.push_back(mm_to_coord(max_z)); face_slopes.push_back(z_angle); } } diff --git a/src/settings/Settings.cpp b/src/settings/Settings.cpp index add349eb05..19aa0c9c1a 100644 --- a/src/settings/Settings.cpp +++ b/src/settings/Settings.cpp @@ -120,7 +120,7 @@ LayerIndex Settings::get(const std::string& key) const template<> coord_t Settings::get(const std::string& key) const { - return MM2INT(get(key)); // The settings are all in millimetres, but we need to interpret them as microns. + return mm_to_coord(get(key)); // The settings are all in millimetres, but we need to interpret them as INT_PER_MM units. } template<> diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index 89deea7c09..2f93b00ee1 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -343,7 +343,7 @@ Polygons SliceDataStorage::getLayerOutlines(const LayerIndex layer_nr, const boo } if (mesh.settings.get("magic_mesh_surface_mode") != ESurfaceMode::NORMAL) { - total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(MM2INT(0.1))); + total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(0.1_mm)); } } } diff --git a/src/slicer.cpp b/src/slicer.cpp index cec4bbc962..d451da704a 100644 --- a/src/slicer.cpp +++ b/src/slicer.cpp @@ -21,9 +21,9 @@ namespace cura { -constexpr int largest_neglected_gap_first_phase = MM2INT(0.01); //!< distance between two line segments regarded as connected -constexpr int largest_neglected_gap_second_phase = MM2INT(0.02); //!< distance between two line segments regarded as connected -constexpr int max_stitch1 = MM2INT(10.0); //!< maximal distance stitched between open polylines to form polygons +constexpr coord_t largest_neglected_gap_first_phase = INT_EPSILON; //!< distance between two line segments regarded as connected +constexpr coord_t largest_neglected_gap_second_phase = 2 * INT_EPSILON; //!< distance between two line segments regarded as connected +constexpr int max_stitch1 = 10.0_mm; //!< maximal distance stitched between open polylines to form polygons void SlicerLayer::makeBasicPolygonLoops(Polygons& open_polylines) { @@ -711,7 +711,7 @@ ClosePolygonResult SlicerLayer::findPolygonPointClosestTo(Point input) if (distOnLine >= 0 && distOnLine <= lineLength) { Point q = p0 + pDiff * distOnLine / lineLength; - if (shorterThen(q - input, MM2INT(0.1))) + if (shorterThen(q - input, 0.1_mm)) { ret.polygonIdx = n; ret.pointIdx = i; diff --git a/src/support.cpp b/src/support.cpp index 9d5841cff3..1e2d14e744 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -354,7 +354,8 @@ void AreaSupport::combineSupportInfillLayers(SliceDataStorage& storage) continue; } - Polygons intersection = infill_area_per_combine[combine_count_here - 1].intersection(lower_layer_part.getInfillArea()).offset(-200).offset(200); + constexpr coord_t opening_offset = 200_mu; // FIXME: < Magic constant (how is it derived? should it scale with units?) + Polygons intersection = infill_area_per_combine[combine_count_here - 1].intersection(lower_layer_part.getInfillArea()).offset(-opening_offset).offset(opening_offset); if (intersection.size() <= 0) { continue; @@ -845,7 +846,7 @@ void AreaSupport::generateSupportAreasForMesh(SliceDataStorage& storage, constexpr bool no_prime_tower = false; const coord_t support_line_width = mesh_group_settings.get("support_infill_extruder_nr").settings.get("support_line_width"); const double sloped_areas_angle = mesh.settings.get("support_bottom_stair_step_min_slope"); - const coord_t sloped_area_detection_width = 10 + static_cast(layer_thickness / std::tan(sloped_areas_angle)) / 2; + const coord_t sloped_area_detection_width = INT_EPSILON + static_cast(layer_thickness / std::tan(sloped_areas_angle)) / 2; xy_disallowed_per_layer[0] = storage.getLayerOutlines(0, no_support, no_prime_tower).offset(xy_distance); cura::parallel_for(1, @@ -860,14 +861,14 @@ void AreaSupport::generateSupportAreasForMesh(SliceDataStorage& storage, sloped_areas_per_layer[layer_idx] = // Take the outer areas of the previous layer, where the outer areas are (mostly) just _inside_ the shape. storage.getLayerOutlines(layer_idx - 1, no_support, no_prime_tower) - .tubeShape(sloped_area_detection_width, 10) + .tubeShape(sloped_area_detection_width, INT_EPSILON) // Intersect those with the outer areas of the current layer, where the outer areas are (mostly) _outside_ the shape. // This will detect every slope (and some/most vertical walls) between those two layers. - .intersection(outlines.tubeShape(10, sloped_area_detection_width)) + .intersection(outlines.tubeShape(INT_EPSILON, sloped_area_detection_width)) // Do an opening operation so we're not stuck with tiny patches. // The later offset is extended with the line-width, so all patches are merged together if there's less than a line-width between them. - .offset(-10) - .offset(10 + sloped_area_detection_width); + .offset(-INT_EPSILON) + .offset(INT_EPSILON + sloped_area_detection_width); // The sloped areas are now ready to be post-processed. if (! is_support_mesh_place_holder) @@ -900,7 +901,7 @@ void AreaSupport::generateSupportAreasForMesh(SliceDataStorage& storage, // expand area_beyond_limit so that the inner hole fills in all the way back to the current layer's outline // and use that to remove the regions in larger_area_below that should not use min XY because the regions are // wide enough for a normal support to be placed there - larger_area_below = larger_area_below.difference(area_beyond_limit.offset(limit_distance + 10)); + larger_area_below = larger_area_below.difference(area_beyond_limit.offset(limit_distance + INT_EPSILON)); } } } @@ -1083,8 +1084,8 @@ void AreaSupport::generateSupportAreasForMesh(SliceDataStorage& storage, if (conical_support) { // with conical support the next layer is allowed to be larger than the previous - touching_buildplate = touching_buildplate.offset(std::abs(conical_support_offset) + 10, ClipperLib::jtMiter, 10); - // + 10 and larger miter limit cause performing an outward offset after an inward offset can disregard sharp corners + touching_buildplate = touching_buildplate.offset(std::abs(conical_support_offset) + INT_EPSILON, ClipperLib::jtMiter, INT_EPSILON); + // + INT_EPSILON and larger miter limit cause performing an outward offset after an inward offset can disregard sharp corners // // conical support can make // layer above layer below @@ -1274,7 +1275,7 @@ std::pair AreaSupport::computeBasicAndFullOverhang(const Sli // Polygons overhang = basic_overhang.unionPolygons(support_extension); // presumably the computation above is slower than the one below - Polygons overhang_extented = basic_overhang.offset(max_dist_from_lower_layer + MM2INT(0.1)); // +0.1mm for easier joining with support from layer above + Polygons overhang_extented = basic_overhang.offset(max_dist_from_lower_layer + 0.1_mm); // +0.1mm for easier joining with support from layer above Polygons full_overhang = overhang_extented.intersection(supportLayer_supportee); return std::make_pair(basic_overhang, full_overhang); } diff --git a/src/utils/FMatrix4x3.cpp b/src/utils/FMatrix4x3.cpp index 1a8260bdcf..d89e9ce900 100644 --- a/src/utils/FMatrix4x3.cpp +++ b/src/utils/FMatrix4x3.cpp @@ -48,11 +48,7 @@ FMatrix4x3::FMatrix4x3() Point3 FMatrix4x3::apply(const FPoint3& p) const { - return Point3( - MM2INT(p.x * m[0][0] + p.y * m[1][0] + p.z * m[2][0] + m[3][0]), - MM2INT(p.x * m[0][1] + p.y * m[1][1] + p.z * m[2][1] + m[3][1]), - MM2INT(p.x * m[0][2] + p.y * m[1][2] + p.z * m[2][2] + m[3][2]) - ); + return Point3(mm_to_coord(p.x * m[0][0] + p.y * m[1][0] + p.z * m[2][0] + m[3][0]), mm_to_coord(p.x * m[0][1] + p.y * m[1][1] + p.z * m[2][1] + m[3][1]), mm_to_coord(p.x * m[0][2] + p.y * m[1][2] + p.z * m[2][2] + m[3][2])); } Point3 FMatrix4x3::apply(const Point3& p) const diff --git a/src/utils/LinearAlg2D.cpp b/src/utils/LinearAlg2D.cpp index 8ac9ccf34d..1479d53754 100644 --- a/src/utils/LinearAlg2D.cpp +++ b/src/utils/LinearAlg2D.cpp @@ -55,7 +55,7 @@ bool LinearAlg2D::getPointOnLineWithDist(const Point& p, const Point& a, const P const Point ab = b - a; const coord_t ab_size = vSize(ab); const Point ap = p - a; - const coord_t ax_size = (ab_size < 50)? dot(normal(ab, 1000), ap) / 1000 : dot(ab, ap) / ab_size; + const coord_t ax_size = (ab_size < 5 * INT_EPSILON) ? dot(normal(ab, INT_PRECISION_COMP), ap) / INT_PRECISION_COMP : dot(ab, ap) / ab_size; const coord_t ap_size2 = vSize2(ap); const coord_t px_size = sqrt(std::max(coord_t(0), ap_size2 - ax_size * ax_size)); if (px_size > dist) @@ -247,10 +247,8 @@ bool LinearAlg2D::isInsideCorner(const Point a, const Point b, const Point c, co */ - - constexpr coord_t normal_length = 10000; //Create a normal vector of reasonable length in order to reduce rounding error. - const Point ba = normal(a - b, normal_length); - const Point bc = normal(c - b, normal_length); + const Point ba = normal(a - b, INT_PRECISION_COMP); + const Point bc = normal(c - b, INT_PRECISION_COMP); const Point bq = query_point - b; const Point perpendicular = turn90CCW(bq); //The query projects to this perpendicular to coordinate 0. const coord_t project_a_perpendicular = dot(ba, perpendicular); //Project vertex A on the perpendicular line. diff --git a/src/utils/PolygonConnector.cpp b/src/utils/PolygonConnector.cpp index 85d4284a10..1d4e3b509e 100644 --- a/src/utils/PolygonConnector.cpp +++ b/src/utils/PolygonConnector.cpp @@ -91,7 +91,7 @@ bool PolygonConnector::isClosed(Polygon&) const bool PolygonConnector::isClosed(ExtrusionLine& polygonal) const { - return vSize2(polygonal.front() - polygonal.back()) < 25; + return vSize2(polygonal.front() - polygonal.back()) < INT_EPSILON * INT_EPSILON / 4; } template<> diff --git a/src/utils/SVG.cpp b/src/utils/SVG.cpp index c6959c4810..d832019578 100644 --- a/src/utils/SVG.cpp +++ b/src/utils/SVG.cpp @@ -394,14 +394,14 @@ void SVG::writeCoordinateGrid(const coord_t grid_size, const Color color, const { writeLine(Point(x, aabb.min.Y), Point(x, aabb.max.Y), color, stroke_width); std::stringstream ss; - ss << INT2MM(x); + ss << coord_to_mm(x); writeText(Point(x, aabb.min.Y + (aabb.max.Y - aabb.min.Y) * dist_from_edge), ss.str(), color, font_size); } for (coord_t y = min_y; y < aabb.max.Y; y += grid_size) { writeLine(Point(aabb.min.X, y), Point(aabb.max.Y, y), color, stroke_width); std::stringstream ss; - ss << INT2MM(y); + ss << coord_to_mm(y); writeText(Point(aabb.min.X + (aabb.max.X - aabb.min.X) * dist_from_edge, y), ss.str(), color, font_size); } } diff --git a/src/utils/Simplify.cpp b/src/utils/Simplify.cpp index 802faa88c6..a0e8393902 100644 --- a/src/utils/Simplify.cpp +++ b/src/utils/Simplify.cpp @@ -18,7 +18,7 @@ Simplify::Simplify(const coord_t max_resolution, const coord_t max_deviation, co Simplify::Simplify(const Settings& settings) : max_resolution(settings.get("meshfix_maximum_resolution")) , max_deviation(settings.get("meshfix_maximum_deviation")) - , max_area_deviation(settings.get("meshfix_maximum_extrusion_area_deviation")) + , max_area_deviation(mu2_to_coord(settings.get("meshfix_maximum_extrusion_area_deviation"))) {} Polygons Simplify::polygon(const Polygons& polygons) const diff --git a/src/utils/ToolpathVisualizer.cpp b/src/utils/ToolpathVisualizer.cpp index 8caf916df0..c13fa2c089 100644 --- a/src/utils/ToolpathVisualizer.cpp +++ b/src/utils/ToolpathVisualizer.cpp @@ -75,9 +75,9 @@ void ToolpathVisualizer::width_legend(const Polygons& input, coord_t nozzle_size all_segments_plus.emplace_back(legend_segment); // colored Point legend_text_offset(nozzle_size, 0); - svg.writeText(legend_top.p + legend_text_offset, to_string(INT2MM(legend_top.w))); - svg.writeText(legend_btm.p + legend_text_offset, to_string(INT2MM(legend_btm.w))); - svg.writeText(legend_mid.p + legend_text_offset, to_string(INT2MM(legend_mid.w))); + svg.writeText(legend_top.p + legend_text_offset, to_string(coord_to_mm(legend_top.w))); + svg.writeText(legend_btm.p + legend_text_offset, to_string(coord_to_mm(legend_btm.w))); + svg.writeText(legend_mid.p + legend_text_offset, to_string(coord_to_mm(legend_mid.w))); svg.writeLine(legend_top.p, legend_top.p + legend_text_offset); svg.writeLine(legend_btm.p, legend_btm.p + legend_text_offset); svg.writeLine(legend_mid.p, legend_mid.p + legend_text_offset); @@ -105,7 +105,7 @@ void ToolpathVisualizer::widths(const std::vector& all_segment ExtrusionSegment ss = all_segments[segment_idx]; // ss.from.w *= w; // ss.to.w *= w; - for (ExtrusionSegment s : ss.discretize(MM2INT(0.1))) + for (ExtrusionSegment s : ss.discretize(0.1_mm)) { coord_t avg_w = (s.from.w + s.to.w) / 2; Point3 clr; diff --git a/src/utils/polygon.cpp b/src/utils/polygon.cpp index 5875600dea..b573da6245 100644 --- a/src/utils/polygon.cpp +++ b/src/utils/polygon.cpp @@ -77,7 +77,7 @@ bool Polygons::empty() const Polygons Polygons::approxConvexHull(int extra_outset) { - constexpr int overshoot = MM2INT(100); //10cm (hard-coded value). + constexpr coord_t overshoot = 100_mm; // 10cm (hard-coded value). Polygons convex_hull; //Perform the offset for each polygon one at a time. @@ -86,7 +86,7 @@ Polygons Polygons::approxConvexHull(int extra_outset) for (const ClipperLib::Path& path : paths) { Polygons offset_result; - ClipperLib::ClipperOffset offsetter(1.2, 10.0); + ClipperLib::ClipperOffset offsetter(clipper_miter_limit, ConstPolygonRef::calcArcTolerance(overshoot)); offsetter.AddPath(path, ClipperLib::jtRound, ClipperLib::etClosedPolygon); offsetter.Execute(offset_result.paths, overshoot); convex_hull.add(offset_result); @@ -269,7 +269,7 @@ unsigned int Polygons::findInside(Point p, bool border_result) Polygons Polygons::intersectionPolyLines(const Polygons& polylines, bool restitch, const coord_t max_stitch_distance) const { Polygons split_polylines = polylines.splitPolylinesIntoSegments(); - + ClipperLib::PolyTree result; ClipperLib::Clipper clipper(clipper_init); clipper.AddPaths(split_polylines.paths, ClipperLib::ptSubject, false); @@ -277,11 +277,11 @@ Polygons Polygons::intersectionPolyLines(const Polygons& polylines, bool restitc clipper.Execute(ClipperLib::ctIntersection, result); Polygons ret; ClipperLib::OpenPathsFromPolyTree(result, ret.paths); - + if (restitch) { Polygons result_lines, result_polygons; - const coord_t snap_distance = 10_mu; + const coord_t snap_distance = INT_EPSILON; PolylineStitcher::stitch(ret, result_lines, result_polygons, max_stitch_distance, snap_distance); ret = result_lines; // if polylines got stitched into polygons, split them back up into a polyline again, because the result only admits polylines @@ -344,25 +344,30 @@ Polygons Polygons::offset(int distance, ClipperLib::JoinType join_type, double m return *this; } Polygons ret; - ClipperLib::ClipperOffset clipper(miter_limit, 10.0); + ClipperLib::ClipperOffset clipper(miter_limit, ConstPolygonRef::calcArcTolerance(distance)); clipper.AddPaths(unionPolygons().paths, join_type, ClipperLib::etClosedPolygon); - clipper.MiterLimit = miter_limit; + assert(clipper.MiterLimit == miter_limit); clipper.Execute(ret.paths, distance); return ret; } +double ConstPolygonRef::calcArcTolerance(int offset) +{ + constexpr double min_tol = std::max(5_mu, INT_EPSILON); + return std::max(min_tol, std::abs(static_cast(offset)) * 1e-2); +} + Polygons ConstPolygonRef::offset(int distance, ClipperLib::JoinType join_type, double miter_limit) const { + Polygons ret; if (distance == 0) { - Polygons ret; ret.add(*this); return ret; } - Polygons ret; - ClipperLib::ClipperOffset clipper(miter_limit, 10.0); + ClipperLib::ClipperOffset clipper(miter_limit, calcArcTolerance(distance)); clipper.AddPath(*path, join_type, ClipperLib::etClosedPolygon); - clipper.MiterLimit = miter_limit; + assert(clipper.MiterLimit == miter_limit); clipper.Execute(ret.paths, distance); return ret; } @@ -532,7 +537,7 @@ void Polygons::removeSmallAreas(const double min_area_size, const bool remove_ho for(auto it = paths.begin(); it < new_end; it++) { // All polygons smaller than target are removed by replacing them with a polygon from the back of the vector - if(std::abs(INT2MM2(ClipperLib::Area(*it))) < min_area_size) + if (std::abs(coord_to_mm2(ClipperLib::Area(*it))) < min_area_size) { new_end--; *it = std::move(*new_end); @@ -545,7 +550,7 @@ void Polygons::removeSmallAreas(const double min_area_size, const bool remove_ho // For each polygon, computes the signed area, move small outlines at the end of the vector and keep references on small holes std::vector small_holes; for(auto it = paths.begin(); it < new_end; it++) { - double area = INT2MM2(ClipperLib::Area(*it)); + double area = coord_to_mm2(ClipperLib::Area(*it)); if (std::abs(area) < min_area_size) { if(area >= 0) @@ -765,7 +770,7 @@ bool ConstPolygonRef::smooth_corner_complex(const Point p1, ListPolyIt& p0_it, L const Point p0_2 = p0_2_it.p(); const Point v02_2 = p0_2 - p2_2; const int64_t v02_2_size = vSize(v02_2); - float progress = std::min(1.0, INT2MM(shortcut_length - v02_size) / INT2MM(v02_2_size - v02_size)); // account for rounding error when v02_2_size is approx equal to v02_size + float progress = std::min(1.0, coord_to_mm(shortcut_length - v02_size) / coord_to_mm(v02_2_size - v02_size)); // account for rounding error when v02_2_size is approx equal to v02_size assert(progress >= 0.0f && progress <= 1.0f && "shortcut length must be between last length and new length"); const Point new_p0 = p0_it.p() + (p0_2 - p0_it.p()) * progress; p0_it = ListPolyIt::insertPointNonDuplicate(p0_2_it, p0_it, new_p0); @@ -918,8 +923,8 @@ void ConstPolygonRef::smooth_corner_simple(const Point p0, const Point p1, const // | // 0 // ideally a1_size == b1_size - if (vSize2(v02) <= shortcut_length * (shortcut_length + 10) // v02 is approximately shortcut length - || (cos_angle > 0.9999 && LinearAlg2D::getDist2FromLine(p2, p0, p1) < 20 * 20)) // p1 is degenerate + if (vSize2(v02) <= shortcut_length * (shortcut_length + INT_EPSILON) // v02 is approximately shortcut length + || (cos_angle > 0.9999 && LinearAlg2D::getDist2FromLine(p2, p0, p1) < 2 * INT_EPSILON * 2 * INT_EPSILON)) // p1 is degenerate { // handle this separately to avoid rounding problems below in the getPointOnLineWithDist function p1_it.remove(); @@ -943,8 +948,8 @@ void ConstPolygonRef::smooth_corner_simple(const Point p0, const Point p1, const Point a = p1 + normal(v10, a1_size); Point b = p1 + normal(v12, a1_size); #ifdef ASSERT_INSANE_OUTPUT - assert(vSize(a) < 4000000); - assert(vSize(b) < 4000000); + assert(vSize(a) < 4000_mm); + assert(vSize(b) < 4000_mm); #endif // #ifdef ASSERT_INSANE_OUTPUT ListPolyIt::insertPointNonDuplicate(p0_it, p1_it, a); ListPolyIt::insertPointNonDuplicate(p1_it, p2_it, b); @@ -968,7 +973,7 @@ void ConstPolygonRef::smooth_corner_simple(const Point p0, const Point p1, const if (success) { // if not success then assume a is negligibly close to 0, but rounding errors caused a problem #ifdef ASSERT_INSANE_OUTPUT - assert(vSize(a) < 4000000); + assert(vSize(a) < 4000_mm); #endif // #ifdef ASSERT_INSANE_OUTPUT ListPolyIt::insertPointNonDuplicate(p0_it, p1_it, a); } @@ -988,7 +993,7 @@ void ConstPolygonRef::smooth_corner_simple(const Point p0, const Point p1, const if (success) { // if not success then assume b is negligibly close to 2, but rounding errors caused a problem #ifdef ASSERT_INSANE_OUTPUT - assert(vSize(b) < 4000000); + assert(vSize(b) < 4000_mm); #endif // #ifdef ASSERT_INSANE_OUTPUT ListPolyIt::insertPointNonDuplicate(p1_it, p2_it, b); } @@ -1026,7 +1031,7 @@ void ConstPolygonRef::smooth_outward(const AngleDegrees min_angle, int shortcut_ do { ListPolyIt next = p1_it.next(); - if (vSize2(p1_it.p() - next.p()) < 10 * 10) + if (vSize2(p1_it.p() - next.p()) < INT_EPSILON * INT_EPSILON) { p1_it.remove(); } @@ -1045,7 +1050,7 @@ void ConstPolygonRef::smooth_outward(const AngleDegrees min_angle, int shortcut_ const Point v10 = p0 - p1; const Point v12 = p2 - p1; - float cos_angle = INT2MM(INT2MM(dot(v10, v12))) / vSizeMM(v10) / vSizeMM(v12); + float cos_angle = coord_to_mm2(dot(v10, v12)) / (vSizeMM(v10) * vSizeMM(v12)); bool is_left_angle = LinearAlg2D::pointIsLeftOfLine(p1, p0, p2) > 0; if (cos_angle > cos_min_angle && is_left_angle) { @@ -1101,9 +1106,7 @@ Polygons Polygons::smooth_outward(const AngleDegrees max_angle, int shortcut_len } - - -void ConstPolygonRef::splitPolylineIntoSegments(Polygons& result) const +void ConstPolygonRef::splitPolylineIntoSegments(Polygons& result) const { Point last = front(); for (size_t idx = 1; idx < size(); idx++) @@ -1114,7 +1117,7 @@ void ConstPolygonRef::splitPolylineIntoSegments(Polygons& result) const } } -Polygons ConstPolygonRef::splitPolylineIntoSegments() const +Polygons ConstPolygonRef::splitPolylineIntoSegments() const { Polygons ret; splitPolylineIntoSegments(ret); @@ -1127,7 +1130,7 @@ void ConstPolygonRef::splitPolygonIntoSegments(Polygons& result) const result.addLine(back(), front()); } -Polygons ConstPolygonRef::splitPolygonIntoSegments() const +Polygons ConstPolygonRef::splitPolygonIntoSegments() const { Polygons ret; splitPolygonIntoSegments(ret); diff --git a/src/utils/polygonUtils.cpp b/src/utils/polygonUtils.cpp index 9a7c8e05f5..3f8fb09768 100644 --- a/src/utils/polygonUtils.cpp +++ b/src/utils/polygonUtils.cpp @@ -192,8 +192,8 @@ Point PolygonUtils::getVertexInwardNormal(ConstPolygonRef poly, unsigned int poi } const Point& p2 = poly[p2_idx]; - Point off0 = turn90CCW(normal(p1 - p0, MM2INT(10.0))); // 10.0 for some precision - Point off1 = turn90CCW(normal(p2 - p1, MM2INT(10.0))); // 10.0 for some precision + Point off0 = turn90CCW(normal(p1 - p0, INT_PRECISION_COMP)); + Point off1 = turn90CCW(normal(p2 - p1, INT_PRECISION_COMP)); Point n = off0 + off1; return n; } @@ -352,8 +352,8 @@ unsigned int PolygonUtils::moveInside(const Polygons& polygons, Point& from, int } else { - Point inward_dir = turn90CCW(normal(ab, MM2INT(10.0)) + normal(p1 - p0, MM2INT(10.0))); // inward direction irrespective of sign of [distance] - // MM2INT(10.0) to retain precision for the eventual normalization + Point inward_dir = turn90CCW(normal(ab, INT_PRECISION_COMP) + normal(p1 - p0, INT_PRECISION_COMP)); // inward direction irrespective of sign of [distance] + // 10.0_mm to retain precision for the eventual normalization ret = x + normal(inward_dir, distance); is_already_on_correct_side_of_boundary = dot(inward_dir, p - x) * distance >= 0; } @@ -477,8 +477,8 @@ unsigned int PolygonUtils::moveInside(const ConstPolygonRef polygon, Point& from } else { - Point inward_dir = turn90CCW(normal(ab, MM2INT(10.0)) + normal(p1 - p0, MM2INT(10.0))); // inward direction irrespective of sign of [distance] - // MM2INT(10.0) to retain precision for the eventual normalization + Point inward_dir = turn90CCW(normal(ab, INT_PRECISION_COMP) + normal(p1 - p0, INT_PRECISION_COMP)); // inward direction irrespective of sign of [distance] + // 10.0_mm to retain precision for the eventual normalization ret = x + normal(inward_dir, distance); is_already_on_correct_side_of_boundary = dot(inward_dir, p - x) * distance >= 0; } @@ -722,7 +722,7 @@ void PolygonUtils::walkToNearestSmallestConnection(ClosestPolygonPoint& poly1_re return; } - int equilibirum_limit = MM2INT(0.1); // hard coded value + int equilibirum_limit = 100; // hard coded value for (int loop_counter = 0; loop_counter < equilibirum_limit; loop_counter++) { unsigned int pos1_before = poly1_result.point_idx; @@ -1060,11 +1060,11 @@ bool PolygonUtils::getNextPointWithDistance(Point from, int64_t dist, ConstPolyg Point pn = next_poly_point - prev_poly_point; - if (shorterThen(pn, 100)) // when precision is limited + if (shorterThen(pn, INT_PRECISION_COMP)) // when precision is limited { Point middle = (next_poly_point + prev_poly_point) / 2; coord_t dist_to_middle = vSize(from - middle); - if (dist_to_middle - dist < 100 && dist_to_middle - dist > -100) + if (dist_to_middle - dist < INT_PRECISION_COMP && dist_to_middle - dist > -INT_PRECISION_COMP) { result.location = middle; result.pos = prev_idx; @@ -1437,7 +1437,7 @@ void PolygonUtils::fixSelfIntersections(const coord_t epsilon, Polygons& thiss) // Points too close to line segments should be moved a little away from those line segments, but less than epsilon, // so at least half-epsilon distance between points can still be guaranteed. - constexpr coord_t grid_size = 2000; + constexpr coord_t grid_size = 2_mm; auto query_grid = PolygonUtils::createLocToLineGrid(thiss, grid_size); const coord_t move_dist = half_epsilon - 2; diff --git a/tests/GCodeExportTest.cpp b/tests/GCodeExportTest.cpp index 2fe477e021..d434ec49a7 100644 --- a/tests/GCodeExportTest.cpp +++ b/tests/GCodeExportTest.cpp @@ -45,7 +45,7 @@ class GCodeExportTest : public testing::Test gcode.output_stream = &output; // Since GCodeExport doesn't support copying, we have to reset everything in-place. - gcode.currentPosition = Point3(0, 0, MM2INT(20)); + gcode.currentPosition = Point3(0, 0, 20_mm); gcode.layer_nr = 0; gcode.current_e_value = 0; gcode.current_e_offset = 0; @@ -204,7 +204,7 @@ class GriffinHeaderTest : public testing::TestWithParam gcode.output_stream = &output; // Since GCodeExport doesn't support copying, we have to reset everything in-place. - gcode.currentPosition = Point3(0, 0, MM2INT(20)); + gcode.currentPosition = Point3(0, 0, 20_mm); gcode.layer_nr = 0; gcode.current_e_value = 0; gcode.current_extruder = 0; @@ -325,7 +325,7 @@ TEST_F(GCodeExportTest, HeaderUltiGCode) ExtruderTrain& train = Application::getInstance().current_slice->scene.extruders.back(); train.settings.add("machine_nozzle_size", "0.4"); } - gcode.total_bounding_box = AABB3D(Point3(0, 0, 0), Point3(1000, 1000, 1000)); + gcode.total_bounding_box = AABB3D(Point3(0, 0, 0), Point3(1_mm, 1_mm, 1_mm)); std::string result = gcode.getFileHeader(extruder_is_used, &print_time, filament_used); @@ -344,7 +344,7 @@ TEST_F(GCodeExportTest, HeaderRepRap) const std::vector extruder_is_used(num_extruders, true); constexpr Duration print_time = 1337; const std::vector filament_used = { 100, 200 }; - gcode.total_bounding_box = AABB3D(Point3(0, 0, 0), Point3(1000, 1000, 1000)); + gcode.total_bounding_box = AABB3D(Point3(0, 0, 0), Point3(1_mm, 1_mm, 1_mm)); std::string result = gcode.getFileHeader(extruder_is_used, &print_time, filament_used); @@ -363,7 +363,7 @@ TEST_F(GCodeExportTest, HeaderMarlin) const std::vector extruder_is_used(num_extruders, true); constexpr Duration print_time = 1337; const std::vector filament_used = { 100, 200 }; - gcode.total_bounding_box = AABB3D(Point3(0, 0, 0), Point3(1000, 1000, 1000)); + gcode.total_bounding_box = AABB3D(Point3(0, 0, 0), Point3(1_mm, 1_mm, 1_mm)); std::string result = gcode.getFileHeader(extruder_is_used, &print_time, filament_used); @@ -380,7 +380,7 @@ TEST_F(GCodeExportTest, HeaderMarlinVolumetric) const std::vector extruder_is_used(num_extruders, true); constexpr Duration print_time = 1337; const std::vector filament_used = { 100, 200 }; - gcode.total_bounding_box = AABB3D(Point3(0, 0, 0), Point3(1000, 1000, 1000)); + gcode.total_bounding_box = AABB3D(Point3(0, 0, 0), Point3(1_mm, 1_mm, 1_mm)); std::string result = gcode.getFileHeader(extruder_is_used, &print_time, filament_used); @@ -426,7 +426,7 @@ TEST_F(GCodeExportTest, EVsMmLinear) EXPECT_EQ(gcode.mmToE(15.0), 15.0) << "Since the E is linear and the input mm is also linear, the output needs to be the same."; EXPECT_EQ(gcode.eToMm(15.0), 15.0) << "Since the E is linear and the output mm is also linear, the output needs to be the same."; - for (int x = -1000; x < 1000; x += 16) + for (int x = -1_mm; x < 1_mm; x += 16) { EXPECT_DOUBLE_EQ(gcode.mmToE(gcode.eToMm(static_cast(x))), static_cast(x)) << "Converting back and forth should lead to the same number."; } @@ -481,8 +481,8 @@ TEST_F(GCodeExportTest, WriteZHopStartDefaultSpeed) { Application::getInstance().current_slice->scene.extruders.emplace_back(0, nullptr); Application::getInstance().current_slice->scene.extruders[gcode.current_extruder].settings.add("speed_z_hop", "1"); // 60mm/min. - gcode.current_layer_z = 2000; - constexpr coord_t hop_height = 3000; + gcode.current_layer_z = 2_mm; + constexpr coord_t hop_height = 3_mm; gcode.writeZhopStart(hop_height); EXPECT_EQ(std::string("G1 F60 Z5\n"), output.str()); } @@ -491,8 +491,8 @@ TEST_F(GCodeExportTest, WriteZHopStartCustomSpeed) { Application::getInstance().current_slice->scene.extruders.emplace_back(0, nullptr); Application::getInstance().current_slice->scene.extruders[gcode.current_extruder].settings.add("speed_z_hop", "1"); // 60mm/min. - gcode.current_layer_z = 2000; - constexpr coord_t hop_height = 3000; + gcode.current_layer_z = 2_mm; + constexpr coord_t hop_height = 3_mm; constexpr Velocity speed = 4; // 240 mm/min. gcode.writeZhopStart(hop_height, speed); EXPECT_EQ(std::string("G1 F240 Z5\n"), output.str()) << "Custom provided speed should be used."; @@ -509,8 +509,8 @@ TEST_F(GCodeExportTest, WriteZHopEndDefaultSpeed) { Application::getInstance().current_slice->scene.extruders.emplace_back(0, nullptr); Application::getInstance().current_slice->scene.extruders[gcode.current_extruder].settings.add("speed_z_hop", "1"); // 60mm/min. - gcode.current_layer_z = 2000; - gcode.is_z_hopped = 3000; + gcode.current_layer_z = 2_mm; + gcode.is_z_hopped = 3_mm; gcode.writeZhopEnd(); EXPECT_EQ(std::string("G1 F60 Z2\n"), output.str()); } @@ -519,8 +519,8 @@ TEST_F(GCodeExportTest, WriteZHopEndCustomSpeed) { Application::getInstance().current_slice->scene.extruders.emplace_back(0, nullptr); Application::getInstance().current_slice->scene.extruders[gcode.current_extruder].settings.add("speed_z_hop", "1"); - gcode.current_layer_z = 2000; - gcode.is_z_hopped = 3000; + gcode.current_layer_z = 2_mm; + gcode.is_z_hopped = 3_mm; constexpr Velocity speed = 4; // 240 mm/min. gcode.writeZhopEnd(speed); EXPECT_EQ(std::string("G1 F240 Z2\n"), output.str()) << "Custom provided speed should be used."; @@ -528,17 +528,17 @@ TEST_F(GCodeExportTest, WriteZHopEndCustomSpeed) TEST_F(GCodeExportTest, insertWipeScriptSingleMove) { - gcode.currentPosition = Point3(1000, 1000, 1000); - gcode.current_layer_z = 1000; + gcode.currentPosition = Point3(1_mm, 1_mm, 1_mm); + gcode.current_layer_z = 1_mm; gcode.use_extruder_offset_to_offset_coords = false; Application::getInstance().current_slice->scene.current_mesh_group->settings.add("layer_height", "0.2"); WipeScriptConfig config; config.retraction_enable = false; config.hop_enable = false; - config.brush_pos_x = 2000; + config.brush_pos_x = 2_mm; config.repeat_count = 1; - config.move_distance = 500; + config.move_distance = 0.5_mm; config.move_speed = 10; config.pause = 0; @@ -560,17 +560,17 @@ TEST_F(GCodeExportTest, insertWipeScriptSingleMove) TEST_F(GCodeExportTest, insertWipeScriptMultipleMoves) { - gcode.currentPosition = Point3(1000, 1000, 1000); - gcode.current_layer_z = 1000; + gcode.currentPosition = Point3(1_mm, 1_mm, 1_mm); + gcode.current_layer_z = 1_mm; gcode.use_extruder_offset_to_offset_coords = false; Application::getInstance().current_slice->scene.current_mesh_group->settings.add("layer_height", "0.2"); WipeScriptConfig config; config.retraction_enable = false; config.hop_enable = false; - config.brush_pos_x = 2000; + config.brush_pos_x = 2_mm; config.repeat_count = 4; - config.move_distance = 500; + config.move_distance = 0.5_mm; config.move_speed = 10; config.pause = 0; @@ -598,17 +598,17 @@ TEST_F(GCodeExportTest, insertWipeScriptMultipleMoves) TEST_F(GCodeExportTest, insertWipeScriptOptionalDelay) { - gcode.currentPosition = Point3(1000, 1000, 1000); - gcode.current_layer_z = 1000; + gcode.currentPosition = Point3(1_mm, 1_mm, 1_mm); + gcode.current_layer_z = 1_mm; gcode.use_extruder_offset_to_offset_coords = false; Application::getInstance().current_slice->scene.current_mesh_group->settings.add("layer_height", "0.2"); WipeScriptConfig config; config.retraction_enable = false; config.hop_enable = false; - config.brush_pos_x = 2000; + config.brush_pos_x = 2_mm; config.repeat_count = 1; - config.move_distance = 500; + config.move_distance = 0.5_mm; config.move_speed = 10; config.pause = 1.5; // 1.5 sec = 1500 ms. @@ -629,8 +629,8 @@ TEST_F(GCodeExportTest, insertWipeScriptOptionalDelay) TEST_F(GCodeExportTest, insertWipeScriptRetractionEnable) { - gcode.currentPosition = Point3(1000, 1000, 1000); - gcode.current_layer_z = 1000; + gcode.currentPosition = Point3(1_mm, 1_mm, 1_mm); + gcode.current_layer_z = 1_mm; gcode.current_e_value = 100; gcode.use_extruder_offset_to_offset_coords = false; gcode.is_volumetric = false; @@ -652,9 +652,9 @@ TEST_F(GCodeExportTest, insertWipeScriptRetractionEnable) config.retraction_config.retraction_extrusion_window = 1; config.retraction_config.retraction_min_travel_distance = 0; // Don't limit retractions for being too short. config.hop_enable = false; - config.brush_pos_x = 2000; + config.brush_pos_x = 2_mm; config.repeat_count = 1; - config.move_distance = 500; + config.move_distance = 0.5_mm; config.move_speed = 10; config.pause = 0; @@ -677,8 +677,8 @@ TEST_F(GCodeExportTest, insertWipeScriptRetractionEnable) TEST_F(GCodeExportTest, insertWipeScriptHopEnable) { - gcode.currentPosition = Point3(1000, 1000, 1000); - gcode.current_layer_z = 1000; + gcode.currentPosition = Point3(1_mm, 1_mm, 1_mm); + gcode.current_layer_z = 1_mm; gcode.use_extruder_offset_to_offset_coords = false; gcode.currentSpeed = 1; Application::getInstance().current_slice->scene.current_mesh_group->settings.add("layer_height", "0.2"); @@ -687,10 +687,10 @@ TEST_F(GCodeExportTest, insertWipeScriptHopEnable) config.retraction_enable = false; config.hop_enable = true; config.hop_speed = 2; // 120 mm/min. - config.hop_amount = 300; - config.brush_pos_x = 2000; + config.hop_amount = 0.3_mm; + config.brush_pos_x = 2_mm; config.repeat_count = 1; - config.move_distance = 500; + config.move_distance = 0.5_mm; config.move_speed = 10; config.pause = 0; diff --git a/tests/LayerPlanTest.cpp b/tests/LayerPlanTest.cpp index 5cf0b20414..89d6ea84b4 100644 --- a/tests/LayerPlanTest.cpp +++ b/tests/LayerPlanTest.cpp @@ -58,7 +58,7 @@ class LayerPlanTest : public testing::Test */ Mesh mesh; - LayerPlanTest() : storage(setUpStorage()), layer_plan(*storage, 100, 10000, 100, 0, fan_speed_layer_time_settings, 20, 10, 5000) + LayerPlanTest() : storage(setUpStorage()), layer_plan(*storage, 0.1_mm, 10_mm, 0.1_mm, 0, fan_speed_layer_time_settings, 20_mu, 10_mu, 5_mm) { } @@ -96,7 +96,7 @@ class LayerPlanTest : public testing::Test settings->add("cool_fan_full_layer", "3"); settings->add("cool_fan_speed_0", "0"); settings->add("cool_fan_speed_min", "75"); - settings->add("cool_fan_speed_max", "100"); + settings->add("cool_fan_speed_max", "0.1_mm"); settings->add("cool_min_speed", "10"); settings->add("cool_min_layer_time", "5"); settings->add("cool_min_layer_time_fan_speed_max", "10"); @@ -117,7 +117,7 @@ class LayerPlanTest : public testing::Test settings->add("machine_height", "1000"); settings->add("machine_nozzle_tip_outer_diameter", "1"); settings->add("machine_width", "1000"); - settings->add("material_flow_layer_0", "100"); + settings->add("material_flow_layer_0", "0.1_mm"); settings->add("meshfix_maximum_travel_resolution", "0"); settings->add("prime_tower_enable", "true"); settings->add("prime_tower_flow", "108"); @@ -309,30 +309,30 @@ class AddTravelTest : public LayerPlanTest, public testing::WithParamInterface("false", "false", "off", false, false, AddTravelTestScene::OPEN)) { - around_start_end.add(Point(-100, -100)); - around_start_end.add(Point(500100, -100)); - around_start_end.add(Point(500100, 500100)); - around_start_end.add(Point(-100, 500100)); - - around_start.add(Point(-100, -100)); - around_start.add(Point(100, -100)); - around_start.add(Point(100, 100)); - around_start.add(Point(-100, 100)); - - around_end.add(Point(249900, 249900)); - around_end.add(Point(250100, 249900)); - around_end.add(Point(250100, 250100)); - around_end.add(Point(249900, 249900)); - - between.add(Point(250000, 240000)); - between.add(Point(260000, 240000)); - between.add(Point(260000, 300000)); - between.add(Point(250000, 300000)); - - between_hole.add(Point(250000, 240000)); - between_hole.add(Point(250000, 300000)); - between_hole.add(Point(260000, 300000)); - between_hole.add(Point(260000, 240000)); + around_start_end.add(Point(-0.1_mm, -0.1_mm)); + around_start_end.add(Point(500.1_mm, -0.1_mm)); + around_start_end.add(Point(500.1_mm, 500.1_mm)); + around_start_end.add(Point(-0.1_mm, 500.1_mm)); + + around_start.add(Point(-0.1_mm, -0.1_mm)); + around_start.add(Point(0.1_mm, -0.1_mm)); + around_start.add(Point(0.1_mm, 0.1_mm)); + around_start.add(Point(-0.1_mm, 0.1_mm)); + + around_end.add(Point(249.9_mm, 249.9_mm)); + around_end.add(Point(250100, 249.9_mm)); + around_end.add(Point(250.1_mm, 250.1_mm)); + around_end.add(Point(249.9_mm, 249.9_mm)); + + between.add(Point(250_mm, 240_mm)); + between.add(Point(260_mm, 240_mm)); + between.add(Point(260_mm, 300_mm)); + between.add(Point(250_mm, 300_mm)); + + between_hole.add(Point(250_mm, 240_mm)); + between_hole.add(Point(250_mm, 300_mm)); + between_hole.add(Point(260_mm, 300_mm)); + between_hole.add(Point(260_mm, 240_mm)); } /*! @@ -385,14 +385,13 @@ class AddTravelTest : public LayerPlanTest, public testing::WithParamInterface(raw_vertices[i_coord] * 1000.F); + auto micrometers = mm_to_coord(raw_vertices[i_coord]); raw_min_coords[i_coord % 3] = std::min(micrometers, raw_min_coords[i_coord % 3]); raw_max_coords[i_coord % 3] = std::max(micrometers, raw_max_coords[i_coord % 3]); } diff --git a/tests/arcus/ArcusCommunicationTest.cpp b/tests/arcus/ArcusCommunicationTest.cpp index 2618f2090d..7d7bd0a939 100644 --- a/tests/arcus/ArcusCommunicationTest.cpp +++ b/tests/arcus/ArcusCommunicationTest.cpp @@ -145,13 +145,13 @@ TEST_F(ArcusCommunicationTest, SendFinishedSlicingTest) TEST_F(ArcusCommunicationTest, SendLayerComplete) { const LayerIndex layer_nr = 10; - constexpr coord_t layer_z = 20; - constexpr coord_t layer_thickness = 30; + constexpr coord_t layer_z = 20_mu; + constexpr coord_t layer_thickness = 30_mu; ac->sendLayerComplete(layer_nr, layer_z, layer_thickness); const std::shared_ptr message = ac->private_data->getOptimizedLayerById(layer_nr); EXPECT_EQ(static_cast(layer_nr), message->id()) << "getOptimizedLayerById() must return a layer with the correct ID."; - EXPECT_EQ(static_cast(layer_z), message->height()); - EXPECT_EQ(static_cast(layer_thickness), message->thickness()); + EXPECT_EQ(coord_to_mm(layer_z) * 1e3f, message->height()); // TODO: Protocol is stuck to microns + EXPECT_EQ(coord_to_mm(layer_thickness) * 1e3f, message->thickness()); } TEST_F(ArcusCommunicationTest, SendProgress) diff --git a/tests/integration/SlicePhaseTest.cpp b/tests/integration/SlicePhaseTest.cpp index 96d74e8962..26abdc5877 100644 --- a/tests/integration/SlicePhaseTest.cpp +++ b/tests/integration/SlicePhaseTest.cpp @@ -73,9 +73,9 @@ TEST_F(SlicePhaseTest, Cube) // Since a cube has the same slice at all heights, every layer must be the same square. Polygon square; square.emplace_back(0, 0); - square.emplace_back(10000, 0); // 10mm cube. - square.emplace_back(10000, 10000); - square.emplace_back(0, 10000); + square.emplace_back(10_mm, 0); // 10mm cube. + square.emplace_back(10_mm, 10_mm); + square.emplace_back(0, 10_mm); for (size_t layer_nr = 0; layer_nr < num_layers; layer_nr++) { @@ -132,7 +132,7 @@ TEST_F(SlicePhaseTest, Cylinder1000) // Since a cylinder has the same slice at all heights, every layer must be the same circle. constexpr size_t num_vertices = 1000; // Create a circle with this number of vertices (first vertex is in the +X direction). - constexpr coord_t radius = 10000; // 10mm radius. + constexpr coord_t radius = 10_mm; // 10mm radius. Polygon circle; circle.reserve(num_vertices); for (size_t i = 0; i < 1000; i++) @@ -152,8 +152,10 @@ TEST_F(SlicePhaseTest, Cylinder1000) { Polygon sliced_polygon = layer.polygons[0]; // Due to the reduction in resolution, the final slice will not have the same vertices as the input. - // Let's say that are allowed to be up to 1/500th of the surface area off. - EXPECT_LE(PolygonUtils::relativeHammingDistance(layer.polygons, circles), 0.002); + // Let's say that are allowed to be up to 1/500th of the surface area off when coord_t(1) = 1_mu. + // One one hand, increasing the integer resolution reduces the area error quadratically, but simplifying + // the polygon is scale invariant, so it's not a direct ^2 relationship. The 1.5 bellow is empirical: + EXPECT_LE(PolygonUtils::relativeHammingDistance(layer.polygons, circles), 0.002 * std::pow(1e3 / INT_PER_MM, 1.3)); } } } diff --git a/tests/settings/SettingsTest.cpp b/tests/settings/SettingsTest.cpp index a59acbda1b..e145209c1b 100644 --- a/tests/settings/SettingsTest.cpp +++ b/tests/settings/SettingsTest.cpp @@ -112,7 +112,7 @@ TEST_F(SettingsTest, AddSettingCoordT) { settings.add("test_setting", "8589934.592"); // 2^33 microns, so this MUST be a 64-bit integer! (Or at least 33-bit, but those don't exist.) - EXPECT_EQ(coord_t(8589934592), settings.get("test_setting")) << "Coordinates must be entered in the setting as millimetres, but are converted to micrometres."; + EXPECT_EQ(8589934.592_mm, settings.get("test_setting")) << "Coordinates must be entered in the setting as millimetres, but are converted to micrometres."; } TEST_F(SettingsTest, AddSettingAngleRadians) diff --git a/tests/utils/LinearAlg2DTest.cpp b/tests/utils/LinearAlg2DTest.cpp index 7f8f857237..75fc19ef0c 100644 --- a/tests/utils/LinearAlg2DTest.cpp +++ b/tests/utils/LinearAlg2DTest.cpp @@ -38,7 +38,7 @@ class GetDist2FromLineSegmentTest : public testing::TestWithParam +class WriteInt2mmTest : public testing::TestWithParam { }; @@ -22,23 +22,23 @@ class WriteInt2mmTest : public testing::TestWithParam */ TEST_P(WriteInt2mmTest, WriteInt2mm) { - const int in = GetParam(); + const coord_t in = GetParam(); std::ostringstream ss; - writeInt2mm(in, ss); + ss << MMtoStream{ in }; ss.flush(); const std::string str = ss.str(); ASSERT_TRUE(ss.good()) << "The integer " << in << " was printed as '" << str << "' which was a bad string!"; - const int out = MM2INT(strtod(str.c_str(), nullptr)); + const coord_t out = mm_to_coord(std::stod(str)); ASSERT_EQ(in, out) << "The integer " << in << " was printed as '" << str << "' which was interpreted as " << out << " rather than " << in << "!"; } -INSTANTIATE_TEST_SUITE_P( - WriteInt2mmTestInstantiation, - WriteInt2mmTest, - testing::Values(-10000, -1000, -100, -10, -1, 0, 1, 10, 100, 1000, 10000, 123456789, std::numeric_limits::max() / 1001)); // For max integer test, divide by 1000 since MM2INT multiplies by 1000 which would cause an overflow. +// Max coord_t value that can be parsed exactly by std::stod 0x7ffffffffffff800 +constexpr coord_t max_integer_as_float = static_cast(std::numeric_limits::max()) * (1 - std::numeric_limits::epsilon()); + +INSTANTIATE_TEST_SUITE_P(WriteInt2mmTestInstantiation, WriteInt2mmTest, testing::Values(-10000, -1000, -100, -10, -1, 0, 1, 10, 100, 1000, 10000, 123456789, max_integer_as_float, -max_integer_as_float)); /* * Fixture to allow parameterized tests for writeDoubleToStream. @@ -53,7 +53,8 @@ TEST_P(WriteDoubleToStreamTest, WriteDoubleToStream) constexpr unsigned int precision = 4; std::ostringstream ss; - writeDoubleToStream(precision, in, ss); + ss << PrecisionedDouble{ in }; + ss.flush(); const std::string str = ss.str(); @@ -68,23 +69,7 @@ TEST_P(WriteDoubleToStreamTest, WriteDoubleToStream) ASSERT_EQ(in_reinterpreted, out) << "The double " << in << " was printed as '" << str << "' which was interpreted as " << out << " rather than " << in_reinterpreted << "!"; } -INSTANTIATE_TEST_SUITE_P(WriteDoubleToStreamTestInstantiation, - WriteDoubleToStreamTest, - testing::Values(-10.000, - -1.000, - -0.100, - -0.010, - -0.001, - 0.010, - 0.100, - 1.000, - 10.000, - 123456.789, - 0.00000001, - std::numeric_limits::min(), - std::numeric_limits::max(), - std::numeric_limits::lowest(), - -std::numeric_limits::lowest())); +INSTANTIATE_TEST_SUITE_P(WriteDoubleToStreamTestInstantiation, WriteDoubleToStreamTest, testing::Values(-10.000, -1.000, -0.100, -0.010, -0.001, 0.010, 0.100, 1.000, 10.000, 123456.789, 0.00000001)); } // namespace cura // NOLINTEND(*-magic-numbers)