From b11f28ebab8ae2c7c834b8e1f813c139f674bba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kerbiriou?= Date: Sun, 7 Aug 2022 12:23:47 +0200 Subject: [PATCH 1/9] Coord_t.h: new conversion functions and units --- include/utils/Coord_t.h | 91 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 83 insertions(+), 8 deletions(-) diff --git a/include/utils/Coord_t.h b/include/utils/Coord_t.h index 910efb941e..85b036266a 100644 --- a/include/utils/Coord_t.h +++ b/include/utils/Coord_t.h @@ -13,16 +13,91 @@ 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); +} -#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)))) +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; -#define INT2MICRON(n) ((n) / 1) -#define MICRON2INT(n) ((n) * 1) +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 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 From fe35307bcba35a7b7afe3e9049782d2cf154117b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kerbiriou?= Date: Sun, 7 Aug 2022 15:09:10 +0200 Subject: [PATCH 2/9] fix comments regarding microns --- include/settings/AdaptiveLayerHeights.h | 2 +- include/slicer.h | 2 +- include/utils/IntPoint.h | 2 +- src/settings/Settings.cpp | 2 +- tests/utils/LinearAlg2DTest.cpp | 2 +- tests/utils/PolygonConnectorTest.cpp | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) 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/IntPoint.h b/include/utils/IntPoint.h index 281436c6b3..fda23ad749 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 diff --git a/src/settings/Settings.cpp b/src/settings/Settings.cpp index add349eb05..b888a29d2f 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 MM2INT(get(key)); // The settings are all in millimetres, but we need to interpret them as INT_PER_MM units. } template<> 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 Date: Sun, 7 Aug 2022 12:59:25 +0200 Subject: [PATCH 3/9] replace INT2MM* with coord_to_mm* conversion functions --- include/utils/IntPoint.h | 4 +-- include/utils/Point3.h | 6 ++-- include/utils/floatpoint.h | 6 ++-- src/ConicalOverhang.cpp | 4 +-- src/FffGcodeWriter.cpp | 4 +-- src/GCodePathConfig.cpp | 6 ++-- src/LayerPlan.cpp | 11 ++++--- src/PrimeTower.cpp | 2 +- src/WallToolPaths.cpp | 10 +++--- src/Wireframe2gcode.cpp | 6 ++-- src/communication/ArcusCommunication.cpp | 13 ++++---- src/gcodeExport.cpp | 40 ++++++++++++------------ src/infill/SierpinskiFill.cpp | 6 ++-- src/settings/AdaptiveLayerHeights.cpp | 4 +-- src/settings/Settings.cpp | 2 +- src/utils/FMatrix4x3.cpp | 6 +--- src/utils/SVG.cpp | 4 +-- src/utils/ToolpathVisualizer.cpp | 6 ++-- src/utils/polygon.cpp | 8 ++--- tests/utils/StringTest.cpp | 10 +++--- 20 files changed, 78 insertions(+), 80 deletions(-) diff --git a/include/utils/IntPoint.h b/include/utils/IntPoint.h index fda23ad749..db6b35c166 100644 --- a/include/utils/IntPoint.h +++ b/include/utils/IntPoint.h @@ -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/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/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..65ed8a6213 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) @@ -2040,7 +2040,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? 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..756db6ed11 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -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; @@ -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; @@ -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; @@ -2053,9 +2054,9 @@ bool LayerPlan::writePathWithCoasting(GCodeExport& gcode, const size_t extruder_ 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..1807aaa584 100644 --- a/src/PrimeTower.cpp +++ b/src/PrimeTower.cpp @@ -113,7 +113,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/WallToolPaths.cpp b/src/WallToolPaths.cpp index 0642dacd36..09e0c547db 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) { diff --git a/src/Wireframe2gcode.cpp b/src/Wireframe2gcode.cpp index 3c78d2ee29..716ea6cb4d 100644 --- a/src/Wireframe2gcode.cpp +++ b/src/Wireframe2gcode.cpp @@ -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) @@ -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/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..32cc0b0b42 100644 --- a/src/gcodeExport.cpp +++ b/src/gcodeExport.cpp @@ -215,12 +215,12 @@ std::string GCodeExport::getFileHeader(const std::vector& extruder_is_used total_bounding_box.min = Point3(0, 0, 0); total_bounding_box.max = Point3(10, 10, 10); } - 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; } @@ -700,7 +700,7 @@ void GCodeExport::writeMoveBFB(const int x, const int y, const int z, const Velo *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) @@ -822,7 +822,7 @@ void GCodeExport::writeFXYZE(const Velocity& speed, const coord_t x, const coord 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() @@ -844,7 +844,7 @@ void GCodeExport::writeUnretractionAndPrime() << 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 { @@ -852,7 +852,7 @@ void GCodeExport::writeUnretractionAndPrime() 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; 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) @@ -861,7 +861,7 @@ void GCodeExport::writeUnretractionAndPrime() *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! } @@ -951,7 +951,7 @@ void GCodeExport::writeRetraction(const RetractionConfig& config, bool force, bo 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; 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; } diff --git a/src/infill/SierpinskiFill.cpp b/src/infill/SierpinskiFill.cpp index b345493044..c70fa8d099 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)); 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 b888a29d2f..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 INT_PER_MM units. + 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/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/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/ToolpathVisualizer.cpp b/src/utils/ToolpathVisualizer.cpp index 8caf916df0..0f7c514ddd 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); diff --git a/src/utils/polygon.cpp b/src/utils/polygon.cpp index 5875600dea..20dbd42c6d 100644 --- a/src/utils/polygon.cpp +++ b/src/utils/polygon.cpp @@ -532,7 +532,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 +545,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 +765,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); @@ -1045,7 +1045,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) { diff --git a/tests/utils/StringTest.cpp b/tests/utils/StringTest.cpp index 8ae5fe1f20..810063be5a 100644 --- a/tests/utils/StringTest.cpp +++ b/tests/utils/StringTest.cpp @@ -31,14 +31,14 @@ TEST_P(WriteInt2mmTest, WriteInt2mm) 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 int out = mm_to_coord(strtod(str.c_str(), nullptr)); 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. +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 mm_to_coord multiplies by 1000 which + // would cause an overflow. /* * Fixture to allow parameterized tests for writeDoubleToStream. From af098bfc7cbc170b48534d746c21a1e280f99aea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kerbiriou?= Date: Sun, 7 Aug 2022 12:42:04 +0200 Subject: [PATCH 4/9] literals use scalable units --- .../BeadingStrategy/BeadingStrategyFactory.h | 29 ++++---- include/PathOrderMonotonic.h | 2 +- include/PathOrderOptimizer.h | 2 +- include/infill/LightningTreeNode.h | 2 +- include/utils/PolylineStitcher.h | 2 +- include/utils/SVG.h | 3 +- include/utils/polygon.h | 7 +- .../LimitedBeadingStrategy.cpp | 4 +- src/FffGcodeWriter.cpp | 5 +- src/FffPolygonGenerator.cpp | 2 +- src/LayerPlan.cpp | 20 +++--- src/SkeletalTrapezoidation.cpp | 2 +- src/SkirtBrim.cpp | 6 +- src/TreeModelVolumes.cpp | 2 +- src/WallToolPaths.cpp | 2 +- src/Wireframe2gcode.cpp | 10 +-- src/gcodeExport.cpp | 8 +-- src/infill/GyroidInfill.cpp | 8 +-- src/infill/LightningLayer.cpp | 2 +- src/sliceDataStorage.cpp | 2 +- src/slicer.cpp | 4 +- src/support.cpp | 2 +- src/utils/ToolpathVisualizer.cpp | 2 +- src/utils/polygon.cpp | 22 +++--- src/utils/polygonUtils.cpp | 4 +- tests/GCodeExportTest.cpp | 72 +++++++++---------- tests/LayerPlanTest.cpp | 71 +++++++++--------- tests/WallsComputationTest.cpp | 24 +++---- tests/arcus/ArcusCommunicationPrivateTest.cpp | 2 +- tests/arcus/ArcusCommunicationTest.cpp | 8 +-- tests/integration/SlicePhaseTest.cpp | 14 ++-- tests/settings/SettingsTest.cpp | 2 +- 32 files changed, 172 insertions(+), 175 deletions(-) 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/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/utils/PolylineStitcher.h b/include/utils/PolylineStitcher.h index cab9bf5eec..0902e8bfe4 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 = 10) { 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/polygon.h b/include/utils/polygon.h index d27f4c19ef..70a30c5c71 100644 --- a/include/utils/polygon.h +++ b/include/utils/polygon.h @@ -551,19 +551,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() diff --git a/src/BeadingStrategy/LimitedBeadingStrategy.cpp b/src/BeadingStrategy/LimitedBeadingStrategy.cpp index a563cfb779..d51ec497d2 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 @@ -108,7 +108,7 @@ coord_t LimitedBeadingStrategy::getTransitionThickness(coord_t lower_bead_count) { return parent->getOptimalThickness(lower_bead_count + 1) - 10; } - return 9000000; // 9 meter + return 9000_mm; // 9 meter } coord_t LimitedBeadingStrategy::getOptimalBeadCount(coord_t thickness) const diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 65ed8a6213..913357d71c 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -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 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/LayerPlan.cpp b/src/LayerPlan.cpp index 756db6ed11..033e4850da 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -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) @@ -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; @@ -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); @@ -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); @@ -2050,7 +2050,7 @@ 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; diff --git a/src/SkeletalTrapezoidation.cpp b/src/SkeletalTrapezoidation.cpp index c5cebcf321..ec2ac5e67e 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); } } diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 9313f98e99..0e4d8a0c87 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) { 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/WallToolPaths.cpp b/src/WallToolPaths.cpp index 09e0c547db..a0f7f00e94 100644 --- a/src/WallToolPaths.cpp +++ b/src/WallToolPaths.cpp @@ -51,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 716ea6cb4d..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; @@ -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) { diff --git a/src/gcodeExport.cpp b/src/gcodeExport.cpp index 32cc0b0b42..a663a16781 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; @@ -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 diff --git a/src/infill/GyroidInfill.cpp b/src/infill/GyroidInfill.cpp index d05767623a..e26ec24305 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; @@ -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/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..ffd918ca69 100644 --- a/src/slicer.cpp +++ b/src/slicer.cpp @@ -23,7 +23,7 @@ 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 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..97ff4368f5 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -1274,7 +1274,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/ToolpathVisualizer.cpp b/src/utils/ToolpathVisualizer.cpp index 0f7c514ddd..c13fa2c089 100644 --- a/src/utils/ToolpathVisualizer.cpp +++ b/src/utils/ToolpathVisualizer.cpp @@ -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 20dbd42c6d..69af349d3c 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. @@ -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,7 +277,7 @@ 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; @@ -943,8 +943,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 +968,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 +988,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); } @@ -1101,9 +1101,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 +1112,7 @@ void ConstPolygonRef::splitPolylineIntoSegments(Polygons& result) const } } -Polygons ConstPolygonRef::splitPolylineIntoSegments() const +Polygons ConstPolygonRef::splitPolylineIntoSegments() const { Polygons ret; splitPolylineIntoSegments(ret); @@ -1127,7 +1125,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..534c7105ba 100644 --- a/src/utils/polygonUtils.cpp +++ b/src/utils/polygonUtils.cpp @@ -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; @@ -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) From 6c397d17ad45904feac28d4d4853c5d7effd5ac4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kerbiriou?= Date: Sun, 7 Aug 2022 12:48:00 +0200 Subject: [PATCH 5/9] scale (or not) magic numbers related to precision and snapping These literals scales values for mitigating rounding errors, set tolerances or detect close enough vertices. We may or may not want them to scale with the integer unit size. --- include/SkeletalTrapezoidation.h | 4 ++-- include/pathPlanning/Comb.h | 5 +++-- include/utils/Coord_t.h | 5 +++++ include/utils/PolylineStitcher.h | 2 +- include/utils/Simplify.h | 2 +- include/utils/polygon.h | 15 +++++++++------ src/BeadingStrategy/BeadingStrategy.cpp | 2 +- .../LimitedBeadingStrategy.cpp | 4 ++-- src/FffGcodeWriter.cpp | 4 ++-- src/LayerPlan.cpp | 6 +++--- src/PrimeTower.cpp | 5 ++--- src/SkeletalTrapezoidation.cpp | 17 +++++++++-------- src/SkirtBrim.cpp | 7 ++++--- src/TopSurface.cpp | 17 ++++++++++++++++- src/TreeSupport.cpp | 12 +++++------- src/bridge.cpp | 4 ++-- src/gcodeExport.cpp | 2 +- src/infill/GyroidInfill.cpp | 2 +- src/infill/LightningTreeNode.cpp | 11 ++--------- src/infill/SierpinskiFill.cpp | 2 +- src/infill/SubDivCube.cpp | 5 ++--- src/mesh.cpp | 2 +- src/slicer.cpp | 4 ++-- src/support.cpp | 19 ++++++++++--------- src/utils/LinearAlg2D.cpp | 8 +++----- src/utils/PolygonConnector.cpp | 2 +- src/utils/polygon.cpp | 14 +++++++------- src/utils/polygonUtils.cpp | 16 ++++++++-------- 28 files changed, 106 insertions(+), 92 deletions(-) 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/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/utils/Coord_t.h b/include/utils/Coord_t.h index 85b036266a..4c37060c07 100644 --- a/include/utils/Coord_t.h +++ b/include/utils/Coord_t.h @@ -24,6 +24,11 @@ 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; + constexpr double coord_to_mm(coord_t n) { return static_cast(n) / INT_PER_MM; diff --git a/include/utils/PolylineStitcher.h b/include/utils/PolylineStitcher.h index 0902e8bfe4..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 = 0.1_mm, 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/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/polygon.h b/include/utils/polygon.h index 70a30c5c71..a8e01cca18 100644 --- a/include/utils/polygon.h +++ b/include/utils/polygon.h @@ -59,7 +59,11 @@ 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); +// Allowed deviation for round joint while offsetting (this reduces the number of segment, http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperOffset/Properties/ArcTolerance.htm) +static constexpr double clipper_arc_tolerance = INT_EPSILON; +// 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 +166,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 { @@ -1004,16 +1008,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, clipper_arc_tolerance); clipper.AddPaths(paths, joinType, end_type); - clipper.MiterLimit = miterLimit; + clipper.MiterLimit = clipper_miter_limit; clipper.Execute(ret.paths, distance); return ret; } 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 d51ec497d2..2350009fd4 100644 --- a/src/BeadingStrategy/LimitedBeadingStrategy.cpp +++ b/src/BeadingStrategy/LimitedBeadingStrategy.cpp @@ -106,7 +106,7 @@ 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 9000_mm; // 9 meter } @@ -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/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 913357d71c..5538396597 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1956,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 @@ -2230,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/LayerPlan.cpp b/src/LayerPlan.cpp index 033e4850da..0fafbf53d2 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, @@ -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); diff --git a/src/PrimeTower.cpp b/src/PrimeTower.cpp index 1807aaa584..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(); diff --git a/src/SkeletalTrapezoidation.cpp b/src/SkeletalTrapezoidation.cpp index ec2ac5e67e..a792b8c997 100644 --- a/src/SkeletalTrapezoidation.cpp +++ b/src/SkeletalTrapezoidation.cpp @@ -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 0e4d8a0c87..300c496f7e 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -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/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/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/gcodeExport.cpp b/src/gcodeExport.cpp index a663a16781..b6e31062e7 100644 --- a/src/gcodeExport.cpp +++ b/src/gcodeExport.cpp @@ -213,7 +213,7 @@ 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:" << 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; diff --git a/src/infill/GyroidInfill.cpp b/src/infill/GyroidInfill.cpp index e26ec24305..e3215b1a12 100644 --- a/src/infill/GyroidInfill.cpp +++ b/src/infill/GyroidInfill.cpp @@ -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); 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 c70fa8d099..2f036cedf8 100644 --- a/src/infill/SierpinskiFill.cpp +++ b/src/infill/SierpinskiFill.cpp @@ -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/slicer.cpp b/src/slicer.cpp index ffd918ca69..d451da704a 100644 --- a/src/slicer.cpp +++ b/src/slicer.cpp @@ -21,8 +21,8 @@ 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 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) diff --git a/src/support.cpp b/src/support.cpp index 97ff4368f5..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 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/polygon.cpp b/src/utils/polygon.cpp index 69af349d3c..4c708a8567 100644 --- a/src/utils/polygon.cpp +++ b/src/utils/polygon.cpp @@ -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, clipper_arc_tolerance); offsetter.AddPath(path, ClipperLib::jtRound, ClipperLib::etClosedPolygon); offsetter.Execute(offset_result.paths, overshoot); convex_hull.add(offset_result); @@ -281,7 +281,7 @@ Polygons Polygons::intersectionPolyLines(const Polygons& polylines, bool restitc if (restitch) { Polygons result_lines, result_polygons; - const coord_t snap_distance = 10_mu; + const coord_t snap_distance = clipper_arc_tolerance; 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,7 +344,7 @@ 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, clipper_arc_tolerance); clipper.AddPaths(unionPolygons().paths, join_type, ClipperLib::etClosedPolygon); clipper.MiterLimit = miter_limit; clipper.Execute(ret.paths, distance); @@ -360,7 +360,7 @@ Polygons ConstPolygonRef::offset(int distance, ClipperLib::JoinType join_type, d return ret; } Polygons ret; - ClipperLib::ClipperOffset clipper(miter_limit, 10.0); + ClipperLib::ClipperOffset clipper(miter_limit, clipper_arc_tolerance); clipper.AddPath(*path, join_type, ClipperLib::etClosedPolygon); clipper.MiterLimit = miter_limit; clipper.Execute(ret.paths, distance); @@ -918,8 +918,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(); @@ -1026,7 +1026,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(); } diff --git a/src/utils/polygonUtils.cpp b/src/utils/polygonUtils.cpp index 534c7105ba..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; } @@ -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; From d0ee44964e565ef4a0dbe82c4ccfd1f1acedc832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kerbiriou?= Date: Fri, 12 Aug 2022 19:04:01 +0200 Subject: [PATCH 6/9] polygon offsets: scale arc tolerance with offset distance --- include/utils/polygon.h | 9 ++++++--- src/utils/polygon.cpp | 21 +++++++++++++-------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/include/utils/polygon.h b/include/utils/polygon.h index a8e01cca18..3de469cf3f 100644 --- a/include/utils/polygon.h +++ b/include/utils/polygon.h @@ -59,8 +59,6 @@ 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 -// Allowed deviation for round joint while offsetting (this reduces the number of segment, http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperOffset/Properties/ArcTolerance.htm) -static constexpr double clipper_arc_tolerance = INT_EPSILON; // 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; @@ -398,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); }; @@ -1014,7 +1017,7 @@ class Polygons { Polygons ret; ClipperLib::EndType end_type = (joinType == ClipperLib::jtMiter)? ClipperLib::etOpenSquare : ClipperLib::etOpenRound; - ClipperLib::ClipperOffset clipper(clipper_miter_limit, clipper_arc_tolerance); + ClipperLib::ClipperOffset clipper(clipper_miter_limit, ConstPolygonRef::calcArcTolerance(distance)); clipper.AddPaths(paths, joinType, end_type); clipper.MiterLimit = clipper_miter_limit; clipper.Execute(ret.paths, distance); diff --git a/src/utils/polygon.cpp b/src/utils/polygon.cpp index 4c708a8567..b573da6245 100644 --- a/src/utils/polygon.cpp +++ b/src/utils/polygon.cpp @@ -86,7 +86,7 @@ Polygons Polygons::approxConvexHull(int extra_outset) for (const ClipperLib::Path& path : paths) { Polygons offset_result; - ClipperLib::ClipperOffset offsetter(clipper_miter_limit, clipper_arc_tolerance); + 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); @@ -281,7 +281,7 @@ Polygons Polygons::intersectionPolyLines(const Polygons& polylines, bool restitc if (restitch) { Polygons result_lines, result_polygons; - const coord_t snap_distance = clipper_arc_tolerance; + 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, clipper_arc_tolerance); + 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, clipper_arc_tolerance); + 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; } From 389bbc64e34867abdda7992cae1c256c2354f5f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kerbiriou?= Date: Thu, 9 Dec 2021 23:25:17 +0100 Subject: [PATCH 7/9] utils/string.h: re-implementation of MMtoStream and PrecisionedDouble Allows setting a different fixed-point precision at compile time. --- include/utils/string.h | 240 +++++++++++++++++++------------------ src/gcodeExport.cpp | 56 +++++---- tests/utils/StringTest.cpp | 37 ++---- 3 files changed, 167 insertions(+), 166 deletions(-) diff --git a/include/utils/string.h b/include/utils/string.h index 7ca857c191..0aac7c57c6 100644 --- a/include/utils/string.h +++ b/include/utils/string.h @@ -4,11 +4,11 @@ #ifndef UTILS_STRING_H #define UTILS_STRING_H -#include // sprintf -#include -#include // ostringstream +#include +#include -#include +#include "Coord_t.h" +#include "math.h" namespace cura { @@ -26,152 +26,160 @@ static inline int stringcasecompare(const char* a, const char* b) return *a - *b; } +//! Same representation as std::string_view, but allows mutation of the buffer +using mut_char_range_t = std::pair; + /*! - * 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 mut_char_range_t format_unsigned_decimal_fixed_point(char* const buffer_end, 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 - { - spdlog::error("Cannot write {} to buffer of size {}", coord, buffer_size); - } - if (char_count < 0) + // Rounding to the required precision + if constexpr (fixed_precision > out_precision) { - 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; + value = round_divide(value, ipow(size_t{ 10 }, fixed_precision - out_precision)); } - 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; + char* begin = buffer_end; + while (quotient >= 10) { - ss << '-'; - start = 1; + const char* twodigits = &"0001020304050607080910111213141516171819" + "2021222324252627282930313233343536373839" + "4041424344454647484950515253545556575859" + "6061626364656667686970717273747576777879" + "8081828384858687888990919293949596979899"[(quotient % 100) * 2]; + quotient /= 100; + begin -= 2; + begin[0] = twodigits[0]; + begin[1] = twodigits[1]; + } + if (quotient > 0) + { // Odd number of digits + *--begin = '0' + static_cast(quotient); } - ss << "0."; - for (int nulls = char_count - start; nulls < 3; nulls++) - { // fill up to 3 decimals with zeros - ss << '0'; + else if (*begin == '0') + { // The last division by 100 might have produced a leading zero, drop it + begin++; } - buffer[char_count - trailing_zeros] = '\0'; - ss << (static_cast(buffer) + start); + assert(*begin != '0'); + + // Trims zeros at the right of the fractional part + char* const first_integral_place = buffer_end - out_precision; + char* 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 '.' + *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; + 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; + 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 mut_char_range_t format_signed_decimal_fixed_point(char* const buffer_end, 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_end, abs_value); + if (is_neg) { - writeInt2mm(precision_and_input.value, out); - return out; + *--result.first = '-'; } -}; + 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 + char buffer[buffer_size]; + auto res = format_signed_decimal_fixed_point(buffer + buffer_size, precision_and_input.value); + out.write(res.first, res.second - res.first); + 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; + char buffer[buffer_size]; + + 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 + buffer_size, fixed_point); + out.write(res.first, res.second - res.first); return out; } }; diff --git a/src/gcodeExport.cpp b/src/gcodeExport.cpp index b6e31062e7..f90882743c 100644 --- a/src/gcodeExport.cpp +++ b/src/gcodeExport.cpp @@ -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,7 +697,7 @@ 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(coord_to_mm(currentPosition.x), coord_to_mm(currentPosition.y), coord_to_mm(currentPosition.z), eToMm(current_e_value)), speed, feature); @@ -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,7 +816,7 @@ 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; @@ -840,7 +840,7 @@ 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; } @@ -850,7 +850,7 @@ void GCodeExport::writeUnretractionAndPrime() { 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(coord_to_mm(currentPosition.x), coord_to_mm(currentPosition.y), coord_to_mm(currentPosition.z), eToMm(current_e_value)), currentSpeed, PrintFeatureType::MoveRetraction); } @@ -858,8 +858,8 @@ void GCodeExport::writeUnretractionAndPrime() 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(coord_to_mm(currentPosition.x), coord_to_mm(currentPosition.y), coord_to_mm(currentPosition.z), eToMm(current_e_value)), currentSpeed, PrintFeatureType::NoneType); } @@ -949,7 +949,7 @@ 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(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/tests/utils/StringTest.cpp b/tests/utils/StringTest.cpp index 810063be5a..36ebcb117a 100644 --- a/tests/utils/StringTest.cpp +++ b/tests/utils/StringTest.cpp @@ -12,7 +12,7 @@ namespace cura /* * Fixture to allow parameterized tests for writeInt2mm. */ -class WriteInt2mmTest : 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 = mm_to_coord(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 mm_to_coord 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) From fc384a599f9fd05c0b19304e1c8889f6d04841c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kerbiriou?= Date: Tue, 27 Sep 2022 09:05:29 +0200 Subject: [PATCH 8/9] utils/string.h: use c++20's span, more assertion safeties --- include/utils/string.h | 46 ++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/include/utils/string.h b/include/utils/string.h index 0aac7c57c6..a69606ac63 100644 --- a/include/utils/string.h +++ b/include/utils/string.h @@ -4,8 +4,10 @@ #ifndef UTILS_STRING_H #define UTILS_STRING_H +#include #include #include +#include #include "Coord_t.h" #include "math.h" @@ -26,9 +28,6 @@ static inline int stringcasecompare(const char* a, const char* b) return *a - *b; } -//! Same representation as std::string_view, but allows mutation of the buffer -using mut_char_range_t = std::pair; - /*! * \brief Format a fixed point unsigned integer as a decimal ascii string. * \tparam fixed_precision log10 of the fixed point scaling factor @@ -37,8 +36,8 @@ using mut_char_range_t = std::pair; * \param value The unsigned value to format * \return A pair of char*, delimiting the result range in the buffer */ -template -static inline mut_char_range_t format_unsigned_decimal_fixed_point(char* const buffer_end, size_t value) +template +static inline std::span format_unsigned_decimal_fixed_point(std::array& buffer, size_t value) { // Rounding to the required precision if constexpr (fixed_precision > out_precision) @@ -50,7 +49,7 @@ static inline mut_char_range_t format_unsigned_decimal_fixed_point(char* const b { // Writes pairs of digit backward in the buffer. This is heavily inspired by the fmt library. size_t quotient = value; - char* begin = buffer_end; + auto begin = buffer.end(); while (quotient >= 10) { const char* twodigits = &"0001020304050607080910111213141516171819" @@ -60,11 +59,13 @@ static inline mut_char_range_t format_unsigned_decimal_fixed_point(char* const b "8081828384858687888990919293949596979899"[(quotient % 100) * 2]; quotient /= 100; begin -= 2; + assert(begin >= buffer.begin()); begin[0] = twodigits[0]; begin[1] = twodigits[1]; } if (quotient > 0) { // Odd number of digits + assert(begin > buffer.begin()); *--begin = '0' + static_cast(quotient); } else if (*begin == '0') @@ -74,8 +75,8 @@ static inline mut_char_range_t format_unsigned_decimal_fixed_point(char* const b assert(*begin != '0'); // Trims zeros at the right of the fractional part - char* const first_integral_place = buffer_end - out_precision; - char* end = buffer_end; + const auto first_integral_place = buffer.end() - out_precision; + auto end = buffer.end(); while (end > first_integral_place) { if (*(end - 1) != '0') @@ -92,6 +93,7 @@ static inline mut_char_range_t format_unsigned_decimal_fixed_point(char* const b 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; } @@ -101,6 +103,7 @@ static inline mut_char_range_t format_unsigned_decimal_fixed_point(char* const b 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] = '.'; } @@ -111,9 +114,10 @@ static inline mut_char_range_t format_unsigned_decimal_fixed_point(char* const b } else { - char* const begin = buffer_end - 1; + static_assert(buffer_length >= 1); + char* const begin = buffer.end() - 1; *begin = '0'; - return { begin, buffer_end }; + return { begin, buffer.end() }; } } @@ -125,15 +129,17 @@ static inline mut_char_range_t format_unsigned_decimal_fixed_point(char* const b * \param value The unsigned value to format * \return A pair of char*, delimiting the result range in the buffer */ -template -static inline mut_char_range_t format_signed_decimal_fixed_point(char* const buffer_end, ptrdiff_t value) +template +static inline std::span format_signed_decimal_fixed_point(std::array& buffer, ptrdiff_t value) { bool is_neg = value < 0; auto abs_value = static_cast(is_neg ? -value : value); - auto result = format_unsigned_decimal_fixed_point(buffer_end, abs_value); + auto result = format_unsigned_decimal_fixed_point(buffer, abs_value); if (is_neg) { - *--result.first = '-'; + auto begin = --result.begin(); + *begin = '-'; + return { begin, result.end() }; } return result; } @@ -151,9 +157,9 @@ struct MMtoStream // +1 because digits10 is log_10(max) rounded down // +1 for the sign // +1 for the decimal separator - char buffer[buffer_size]; - auto res = format_signed_decimal_fixed_point(buffer + buffer_size, precision_and_input.value); - out.write(res.first, res.second - res.first); + std::array buffer; + auto res = format_signed_decimal_fixed_point(buffer, precision_and_input.value); + out.write(&res.front(), res.size()); return out; } }; @@ -172,14 +178,14 @@ struct PrecisionedDouble friend inline std::ostream& operator<<(std::ostream& out, const PrecisionedDouble input) { constexpr size_t buffer_size = std::numeric_limits::digits10 + 3; - char buffer[buffer_size]; + 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 + buffer_size, fixed_point); - out.write(res.first, res.second - res.first); + auto res = format_signed_decimal_fixed_point(buffer, fixed_point); + out.write(&res.front(), res.size()); return out; } }; From 68ed68f93fa68d0b9e5cc982386490e3cab32cba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kerbiriou?= Date: Fri, 30 Sep 2022 17:07:24 +0200 Subject: [PATCH 9/9] fix scaling of max_area_deviation setting - In LayerPlan::addWall: there was no scaling - In Simplify(const Settings& settings): wrongly scaled mm->coord_t, as noted in #1677. --- include/utils/Coord_t.h | 4 ++++ src/LayerPlan.cpp | 2 +- src/utils/Simplify.cpp | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/utils/Coord_t.h b/include/utils/Coord_t.h index 4c37060c07..0ccba7a0a2 100644 --- a/include/utils/Coord_t.h +++ b/include/utils/Coord_t.h @@ -53,6 +53,10 @@ 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)); +} constexpr coord_t operator"" _mm(long double n) { diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 0fafbf53d2..bf1b5e463a 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -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]; 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