diff --git a/conandata.yml b/conandata.yml index 186ec8825f..f5da580fcb 100644 --- a/conandata.yml +++ b/conandata.yml @@ -1,4 +1,4 @@ -version: "5.8.0-alpha.0" +version: "5.9.0-alpha.0" requirements: - "scripta/0.1.0@ultimaker/testing" requirements_arcus: diff --git a/conanfile.py b/conanfile.py index 8672d9a94b..d40e9c70c4 100644 --- a/conanfile.py +++ b/conanfile.py @@ -125,7 +125,7 @@ def requirements(self): self.requires("protobuf/3.21.12") self.requires("clipper/6.4.2@ultimaker/stable") self.requires("boost/1.82.0") - self.requires("rapidjson/1.1.0") + self.requires("rapidjson/cci.20230929") self.requires("stb/20200203") self.requires("spdlog/1.12.0") self.requires("fmt/10.1.1") diff --git a/include/GCodePathConfig.h b/include/GCodePathConfig.h index 4531b650aa..82a2ea78dd 100644 --- a/include/GCodePathConfig.h +++ b/include/GCodePathConfig.h @@ -18,6 +18,8 @@ namespace cura */ struct GCodePathConfig { + static constexpr double FAN_SPEED_DEFAULT = -1.0; + coord_t z_offset{}; //(const GCodePathConfig& other) const = default; diff --git a/include/PrimeTower/PrimeTowerNormal.h b/include/PrimeTower/PrimeTowerNormal.h index 5235774252..009ca24599 100644 --- a/include/PrimeTower/PrimeTowerNormal.h +++ b/include/PrimeTower/PrimeTowerNormal.h @@ -18,8 +18,11 @@ namespace cura */ class PrimeTowerNormal : public PrimeTower { +private: + const std::vector used_extruders_; + public: - PrimeTowerNormal(); + PrimeTowerNormal(const std::vector& used_extruders); virtual ExtruderPrime getExtruderPrime( const std::vector& extruder_is_used_on_this_layer, diff --git a/include/TreeSupportSettings.h b/include/TreeSupportSettings.h index 2e3162a71a..f74b9cffe3 100644 --- a/include/TreeSupportSettings.h +++ b/include/TreeSupportSettings.h @@ -54,7 +54,7 @@ struct TreeSupportSettings : RestPreference::BUILDPLATE) , xy_distance(mesh_group_settings.get("support_xy_distance")) , bp_radius(mesh_group_settings.get("support_tree_bp_diameter") / 2) - , diameter_scale_bp_radius(std::min(sin(0.7) * static_cast(layer_height / branch_radius), 1.0 / (branch_radius / (support_line_width / 2.0)))) + , diameter_scale_bp_radius(std::min(sin(0.7) * static_cast(layer_height) / static_cast(branch_radius), 1.0 / (branch_radius / (support_line_width / 2.0)))) , // Either 40° or as much as possible so that 2 lines will overlap by at least 50%, whichever is smaller. support_overrides(mesh_group_settings.get("support_xy_overrides_z")) , xy_min_distance(support_overrides == SupportDistPriority::Z_OVERRIDES_XY ? mesh_group_settings.get("support_xy_distance_overhang") : xy_distance) diff --git a/include/gcodeExport.h b/include/gcodeExport.h index 16a1158054..7b211f8516 100644 --- a/include/gcodeExport.h +++ b/include/gcodeExport.h @@ -471,6 +471,15 @@ class GCodeExport : public NoCopy */ void processInitialLayerBedTemperature(); + /*! + * Set extruders temperatures for the initial layer. Called by 'processInitialLayerTemperatures'. + * + * \param storage The slice data storage + * \param wait_start_extruder Indicates whether we should always wait for the start extruder temperature to be reached + * \param start_extruder_nr The index of the start extruder + */ + void processInitialLayerExtrudersTemperatures(const SliceDataStorage& storage, const bool wait_start_extruder, const size_t start_extruder_nr); + public: /*! * Get ready for extrusion moves: diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 82dd57d2a5..a156286295 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -40,7 +40,7 @@ class SlotProxy { Default default_process{}; using value_type = PluginProxy; - std::optional plugin_{ std::nullopt }; + std::vector plugins_; public: static constexpr plugins::v0::SlotID slot_id{ SlotID }; @@ -53,14 +53,15 @@ class SlotProxy SlotProxy() noexcept = default; /** - * @brief Constructs a SlotProxy object with a plugin. - * - * Constructs a SlotProxy object and initializes the plugin using the provided gRPC channel. - * + * @brief Adds a plugin to this proxy. + * @param name The fully-qualified name of the plugin + * @param version The full verison of the plugin * @param channel A shared pointer to the gRPC channel for communication with the plugin. */ - SlotProxy(const std::string& name, const std::string& version, std::shared_ptr channel) - : plugin_{ value_type{ name, version, channel } } {}; + void addPlugin(const std::string& name, const std::string& version, std::shared_ptr channel) + { + plugins_.emplace_back(name, version, channel); + } /** * @brief Executes the plugin operation. @@ -75,18 +76,25 @@ class SlotProxy */ constexpr auto generate(auto&&... args) { - if (plugin_.has_value()) + if (! plugins_.empty()) { - return plugin_.value().generate(std::forward(args)...); + return plugins_.front().generate(std::forward(args)...); } return std::invoke(default_process, std::forward(args)...); } constexpr auto modify(auto& original_value, auto&&... args) { - if (plugin_.has_value()) + if (! plugins_.empty()) { - return plugin_.value().modify(original_value, std::forward(args)...); + auto modified_value = original_value; + + for (value_type& plugin : plugins_) + { + modified_value = plugin.modify(modified_value, std::forward(args)...); + } + + return modified_value; } if constexpr (sizeof...(args) == 0) { @@ -98,9 +106,9 @@ class SlotProxy template void broadcast(auto&&... args) { - if (plugin_.has_value()) + for (value_type& plugin : plugins_) { - plugin_.value().template broadcast(std::forward(args)...); + plugin.template broadcast(std::forward(args)...); } } }; diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 30740be3c4..515a1a41ab 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -160,8 +160,7 @@ class Registry, Unit> : public Registry { if (slot_id == T::slot_id) { - using Tp = typename Unit::value_type; - value_.proxy = Tp{ name, version, std::forward(channel) }; + value_.proxy.addPlugin(name, version, std::forward(channel)); return; } Base::connect(slot_id, name, version, std::forward(channel)); diff --git a/include/utils/ExtrusionLine.h b/include/utils/ExtrusionLine.h index 987ce1b783..fa76eaca93 100644 --- a/include/utils/ExtrusionLine.h +++ b/include/utils/ExtrusionLine.h @@ -5,6 +5,8 @@ #ifndef UTILS_EXTRUSION_LINE_H #define UTILS_EXTRUSION_LINE_H +#include + #include #include #include diff --git a/include/utils/string.h b/include/utils/string.h index 90ddc318fd..7468077279 100644 --- a/include/utils/string.h +++ b/include/utils/string.h @@ -4,6 +4,7 @@ #ifndef UTILS_STRING_H #define UTILS_STRING_H +#include #include // sprintf #include #include // ostringstream @@ -169,6 +170,11 @@ struct PrecisionedDouble uint8_t precision; //!< Number of digits after the decimal mark with which to convert to string double value; //!< The double value + bool wouldWriteZero() const + { + return (std::abs(value) * std::pow(10.0, precision)) < 1.0; + } + friend inline std::ostream& operator<<(std::ostream& out, const PrecisionedDouble precision_and_input) { writeDoubleToStream(precision_and_input.precision, precision_and_input.value, out); diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 64d52a93a4..669bbfd5d8 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2309,7 +2309,7 @@ bool FffGcodeWriter::processSingleLayerInfill( extruder_nr, z_seam_config, tool_paths, - storage.getModelBoundingBox().flatten().getMiddle()); + mesh.bounding_box.flatten().getMiddle()); added_something |= wall_orderer.addToLayer(); } } @@ -2752,7 +2752,7 @@ bool FffGcodeWriter::processInsets( mesh.settings.get("wall_x_extruder_nr").extruder_nr_, z_seam_config, part.wall_toolpaths, - storage.getModelBoundingBox().flatten().getMiddle()); + mesh.bounding_box.flatten().getMiddle()); added_something |= wall_orderer.addToLayer(); } return added_something; @@ -3178,7 +3178,7 @@ void FffGcodeWriter::processSkinPrintFeature( skin_extruder_nr, z_seam_config, skin_paths, - storage.getModelBoundingBox().flatten().getMiddle()); + mesh.bounding_box.flatten().getMiddle()); added_something |= wall_orderer.addToLayer(); } } diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 18ea87a511..ee0b331396 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -1151,7 +1151,9 @@ void LayerPlan::addWall( for (size_t piece = 0; piece < pieces; ++piece) { const double average_progress = (double(piece) + 0.5) / pieces; // How far along this line to sample the line width in the middle of this piece. - const coord_t line_width = p0.w_ + average_progress * delta_line_width; + // Round the line_width value to overcome floating point rounding issues, otherwise we may end up with slightly different values + // and the generated GCodePath objects will not be merged together, which some subsequent algorithms rely on (e.g. coasting) + const coord_t line_width = std::lrint(static_cast(p0.w_) + average_progress * static_cast(delta_line_width)); const Point2LL destination = p0.p_ + normal(line_vector, piece_length * (piece + 1)); if (is_small_feature) { @@ -1881,7 +1883,8 @@ TimeMaterialEstimates ExtruderPlan::computeNaiveTimeEstimates(Point2LL starting_ void ExtruderPlan::processFanSpeedForMinimalLayerTime(Duration minTime, double time_other_extr_plans) { - /* + /* interpolate fan speed + min layer time : : min layer time fan speed min @@ -1892,28 +1895,12 @@ void ExtruderPlan::processFanSpeedForMinimalLayerTime(Duration minTime, double t speed min..|... \:___________ |________________ layer time > - - */ - // interpolate fan speed (for cool_fan_full_layer and for cool_min_layer_time_fan_speed_max) - double totalLayerTime = estimates_.getTotalTime() + time_other_extr_plans; - if (totalLayerTime < minTime) - { - fan_speed = fan_speed_layer_time_settings_.cool_fan_speed_max; - } - else if (minTime >= fan_speed_layer_time_settings_.cool_min_layer_time_fan_speed_max) - { - // ignore gradual increase of fan speed - return; - } - else if (totalLayerTime < fan_speed_layer_time_settings_.cool_min_layer_time_fan_speed_max) - { - // when forceMinimalLayerTime didn't change the extrusionSpeedFactor, we adjust the fan speed - double fan_speed_diff = fan_speed_layer_time_settings_.cool_fan_speed_max - fan_speed; - double layer_time_diff = fan_speed_layer_time_settings_.cool_min_layer_time_fan_speed_max - minTime; - double fraction_of_slope = (totalLayerTime - minTime) / layer_time_diff; - fan_speed = fan_speed_layer_time_settings_.cool_fan_speed_max - fan_speed_diff * fraction_of_slope; - } + + const double total_layer_time = estimates_.getTotalTime() + time_other_extr_plans; + const double layer_time_diff = fan_speed_layer_time_settings_.cool_min_layer_time_fan_speed_max - minTime; + const double fraction_of_slope = (layer_time_diff != 0.0) ? std::clamp((total_layer_time - minTime) / layer_time_diff, 0.0, 1.0) : 1.0; + fan_speed = std::lerp(fan_speed_layer_time_settings_.cool_fan_speed_max, fan_speed, fraction_of_slope); } void ExtruderPlan::processFanSpeedForFirstLayers() @@ -2019,6 +2006,15 @@ void LayerPlan::writeGCode(GCodeExport& gcode) constexpr bool wait = false; gcode.writeBedTemperatureCommand(mesh_group_settings.get("material_bed_temperature"), wait); } + if (mesh_group_settings.get("build_volume_fan_nr") != 0) + { + // The machine has a build volume fan. + if (layer_nr_ == mesh_group_settings.get("build_fan_full_layer")) + { + gcode.writeSpecificFanCommand(100, mesh_group_settings.get("build_volume_fan_nr")); + } + } + gcode.setZ(z_); diff --git a/src/PrimeTower/PrimeTower.cpp b/src/PrimeTower/PrimeTower.cpp index 31699c5412..074d1f501a 100644 --- a/src/PrimeTower/PrimeTower.cpp +++ b/src/PrimeTower/PrimeTower.cpp @@ -306,20 +306,28 @@ void PrimeTower::processExtrudersUse(LayerVector>& extr PrimeTower* PrimeTower::createPrimeTower(SliceDataStorage& storage) { PrimeTower* prime_tower = nullptr; - const Scene& scene = Application::getInstance().current_slice_->scene; + const Settings& settings = Application::getInstance().current_slice_->scene.current_mesh_group->settings; const size_t raft_total_extra_layers = Raft::getTotalExtraLayers(); + const std::vector extruders_used = storage.getExtrudersUsed(); + + std::vector used_extruders_nrs; + for (size_t extruder_nr = 0; extruder_nr < extruders_used.size(); extruder_nr++) + { + if (extruders_used[extruder_nr]) + { + used_extruders_nrs.push_back(extruder_nr); + } + } - if (scene.extruders.size() > 1 && 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 - && storage.max_print_height_second_to_last_extruder >= -static_cast(raft_total_extra_layers)) + if (used_extruders_nrs.size() > 1 && settings.get("prime_tower_enable") && settings.get("prime_tower_min_volume") > 10 + && settings.get("prime_tower_size") > 10 && storage.max_print_height_second_to_last_extruder >= -static_cast(raft_total_extra_layers)) { - const Settings& mesh_group_settings = scene.current_mesh_group->settings; - const PrimeTowerMode method = mesh_group_settings.get("prime_tower_mode"); + const PrimeTowerMode method = settings.get("prime_tower_mode"); switch (method) { case PrimeTowerMode::NORMAL: - prime_tower = new PrimeTowerNormal(); + prime_tower = new PrimeTowerNormal(used_extruders_nrs); break; case PrimeTowerMode::INTERLEAVED: prime_tower = new PrimeTowerInterleaved(); diff --git a/src/PrimeTower/PrimeTowerNormal.cpp b/src/PrimeTower/PrimeTowerNormal.cpp index eb99669423..1ce002eaf8 100644 --- a/src/PrimeTower/PrimeTowerNormal.cpp +++ b/src/PrimeTower/PrimeTowerNormal.cpp @@ -12,8 +12,9 @@ namespace cura { -PrimeTowerNormal::PrimeTowerNormal() +PrimeTowerNormal::PrimeTowerNormal(const std::vector& used_extruders) : PrimeTower() + , used_extruders_(used_extruders) { } @@ -45,13 +46,8 @@ std::map> PrimeTowerNorma const coord_t tower_radius = mesh_group_settings.get("prime_tower_size") / 2; std::map> toolpaths; - // First make a basic list of used extruders numbers - std::vector extruder_order; - extruder_order.reserve(scene.extruders.size()); - for (const ExtruderTrain& extruder : scene.extruders) - { - extruder_order.push_back(extruder.extruder_nr_); - } + // First take all the used extruders numbers, unsorted + std::vector extruder_order = used_extruders_; // Then sort from high adhesion to low adhesion. This will give us the outside to inside extruder processing order. std::sort( diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 21a34796de..67a68e1cf6 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2023,10 +2023,8 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_storage) return; } - Shape outer_walls = TreeSupportUtils::toPolylines(support_layer_storage[layer_idx - 1].getOutsidePolygons()) - .createTubeShape( - closing_dist, - 0); //.unionPolygons(volumes_.getCollision(0, layer_idx - 1, true).offset(-(config.support_line_width+config.xy_min_distance))); + const Shape& relevant_forbidden = volumes_.getCollision(0, layer_idx, true); + Shape outer_walls = TreeSupportUtils::toPolylines(support_layer_storage[layer_idx - 1].getOutsidePolygons()).createTubeShape(closing_dist, 0); Shape holes_below; @@ -2043,6 +2041,11 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_storage) { holes_resting_outside[layer_idx].emplace(idx); } + else if (hole.intersection(PolygonUtils::clipPolygonWithAABB(relevant_forbidden, hole_aabb)).area() > hole.length() * EPSILON) + { + holes_resting_outside[layer_idx].emplace( + idx); // technically not resting outside, also not valid, but the alternative is potentially having lines go though the model + } else { for (auto [idx2, hole2] : holeparts[layer_idx - 1] | ranges::views::enumerate) diff --git a/src/gcodeExport.cpp b/src/gcodeExport.cpp index b508bc3e33..9263c25e73 100644 --- a/src/gcodeExport.cpp +++ b/src/gcodeExport.cpp @@ -768,35 +768,18 @@ void GCodeExport::processInitialLayerBedTemperature() } } -void GCodeExport::processInitialLayerTemperature(const SliceDataStorage& storage, const size_t start_extruder_nr) +void GCodeExport::processInitialLayerExtrudersTemperatures(const SliceDataStorage& storage, const bool wait_start_extruder, const size_t start_extruder_nr) { Scene& scene = Application::getInstance().current_slice_->scene; - const size_t num_extruders = scene.extruders.size(); const bool material_print_temp_prepend = scene.current_mesh_group->settings.get("material_print_temp_prepend"); const bool material_print_temp_wait = scene.current_mesh_group->settings.get("material_print_temp_wait"); - bool wait_start_extruder = false; - std::vector extruders_used = storage.getExtrudersUsed(); - size_t used_extruders = std::count(extruders_used.begin(), extruders_used.end(), true); - switch (getFlavor()) + if (! material_print_temp_prepend && (scene.current_mesh_group == scene.mesh_groups.begin())) { - case EGCodeFlavor::ULTIGCODE: + // Nozzle initial temperatures are handled by start GCode, ignore return; - case EGCodeFlavor::GRIFFIN: - wait_start_extruder = true; - break; - default: - if (used_extruders > 1 || getFlavor() == EGCodeFlavor::REPRAP) - { - std::ostringstream tmp; - tmp << "T" << start_extruder_nr; - writeLine(tmp.str().c_str()); - } - break; } - processInitialLayerBedTemperature(); - struct ExtruderInitialize { size_t nr; @@ -825,25 +808,49 @@ void GCodeExport::processInitialLayerTemperature(const SliceDataStorage& storage } // First set all the required temperatures at once, but without waiting so that all heaters start heating right now - const bool prepend_all_temperatures = material_print_temp_prepend || (scene.current_mesh_group != scene.mesh_groups.begin()); for (ExtruderInitialize& extruder : all_extruders) { - if (extruder.nr == start_extruder_nr || prepend_all_temperatures) - { - writeTemperatureCommand(extruder.nr, extruder.temperature, false, true); - } + writeTemperatureCommand(extruder.nr, extruder.temperature, false, true); } // Now wait for all the required temperatures one after the other for (ExtruderInitialize& extruder : all_extruders) { - if (material_print_temp_wait || (extruder.nr == start_extruder_nr && wait_start_extruder)) + if (material_print_temp_wait || ((extruder.nr == start_extruder_nr) && wait_start_extruder)) { writeTemperatureCommand(extruder.nr, extruder.temperature, true, true); } } } +void GCodeExport::processInitialLayerTemperature(const SliceDataStorage& storage, const size_t start_extruder_nr) +{ + Scene& scene = Application::getInstance().current_slice_->scene; + bool wait_start_extruder = false; + std::vector extruders_used = storage.getExtrudersUsed(); + size_t used_extruders = std::count(extruders_used.begin(), extruders_used.end(), true); + + switch (getFlavor()) + { + case EGCodeFlavor::ULTIGCODE: + return; + case EGCodeFlavor::GRIFFIN: + wait_start_extruder = true; + break; + default: + if (used_extruders > 1 || getFlavor() == EGCodeFlavor::REPRAP) + { + std::ostringstream tmp; + tmp << "T" << start_extruder_nr; + writeLine(tmp.str().c_str()); + } + break; + } + + processInitialLayerBedTemperature(); + processInitialLayerExtrudersTemperatures(storage, wait_start_extruder, start_extruder_nr); +} + bool GCodeExport::needPrimeBlob() const { switch (getFlavor()) @@ -1464,44 +1471,71 @@ void GCodeExport::writeFanCommand(double speed, std::optional extruder) void GCodeExport::writeSpecificFanCommand(double speed, size_t fan_number) { - auto iterator = current_fans_speeds_.find(fan_number); - - if (iterator != current_fans_speeds_.end() && std::abs(iterator->second - speed) < 0.1) - { - return; - } + const auto iterator = current_fans_speeds_.find(fan_number); + const std::optional current_fan_speed = (iterator != current_fans_speeds_.end()) ? std::optional(iterator->second) : std::nullopt; if (flavor_ == EGCodeFlavor::MAKERBOT) { - if (speed >= 50) + // Makerbot cannot PWM the fan speed, only turn it on or off + + bool write_value = true; + const bool new_on = speed >= 50; + if (current_fan_speed.has_value()) { - *output_stream_ << "M126 T0" << new_line_; // Makerbot cannot PWM the fan speed... + const bool old_on = current_fan_speed.value() >= 50; + write_value = new_on != old_on; } - else + + if (write_value) { - *output_stream_ << "M127 T0" << new_line_; + if (new_on) + { + *output_stream_ << "M126 T0" << new_line_; + } + else + { + *output_stream_ << "M127 T0" << new_line_; + } } } - else if (speed > 0) + else { 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 ? static_cast(2) : static_cast(1)), - (should_scale_zero_to_one ? speed : speed * 255) / 100 }; - if (fan_number) + const auto scale_zero_to_one_optional = [should_scale_zero_to_one](double value) -> PrecisionedDouble { - *output_stream_ << " P" << fan_number; + return { (should_scale_zero_to_one ? static_cast(2) : static_cast(1)), (should_scale_zero_to_one ? value : value * 255.0) / 100.0 }; + }; + bool write_value = true; + std::ostringstream new_value; + const auto num_new_val = scale_zero_to_one_optional(speed); + new_value << num_new_val; + const std::string new_value_str = new_value.str(); + if (current_fan_speed.has_value()) + { + std::ostringstream old_value; + old_value << scale_zero_to_one_optional(current_fan_speed.value()); + write_value = new_value_str != old_value.str(); } - *output_stream_ << new_line_; - } - else - { - *output_stream_ << "M107"; - if (fan_number) + + if (write_value) { - *output_stream_ << " P" << fan_number; + if (num_new_val.wouldWriteZero()) + { + // Turn off when the fan value is zero. + *output_stream_ << "M107"; + } + else + { + *output_stream_ << "M106 S" << new_value_str; + } + + if (fan_number) + { + *output_stream_ << " P" << fan_number; + } + + *output_stream_ << new_line_; } - *output_stream_ << new_line_; } current_fans_speeds_[fan_number] = speed; diff --git a/src/geometry/MixedLinesSet.cpp b/src/geometry/MixedLinesSet.cpp index c080becf41..8b0d4a5455 100644 --- a/src/geometry/MixedLinesSet.cpp +++ b/src/geometry/MixedLinesSet.cpp @@ -42,16 +42,17 @@ Shape MixedLinesSet::offset(coord_t distance, ClipperLib::JoinType join_type, do } else { - static auto end_type_fn = [&line, &join_type]() + ClipperLib::EndType end_type; + + if (line->hasClosingSegment()) + { + end_type = ClipperLib::etClosedLine; + } + else { - if (line->hasClosingSegment()) - { - return ClipperLib::etClosedLine; - } - return join_type == ClipperLib::jtMiter ? ClipperLib::etOpenSquare : ClipperLib::etOpenRound; - }; - - const ClipperLib::EndType end_type{ end_type_fn() }; + end_type = (join_type == ClipperLib::jtMiter) ? ClipperLib::etOpenSquare : ClipperLib::etOpenRound; + } + clipper.AddPath(line->getPoints(), join_type, end_type); } } diff --git a/src/geometry/PartsView.cpp b/src/geometry/PartsView.cpp index 5e080b2aea..c1370895c5 100644 --- a/src/geometry/PartsView.cpp +++ b/src/geometry/PartsView.cpp @@ -3,6 +3,9 @@ #include "geometry/PartsView.h" +#include +#include + #include "geometry/Polygon.h" #include "geometry/SingleShape.h" diff --git a/src/geometry/Polyline.cpp b/src/geometry/Polyline.cpp index d64341eada..c476f8e461 100644 --- a/src/geometry/Polyline.cpp +++ b/src/geometry/Polyline.cpp @@ -3,6 +3,7 @@ #include "geometry/Polyline.h" +#include #include #include @@ -104,10 +105,7 @@ Polyline::const_segments_iterator Polyline::endSegments() const { return const_segments_iterator(end(), begin(), end()); } - else - { - return const_segments_iterator(size() > 1 ? std::prev(end()) : end(), begin(), end()); - } + return const_segments_iterator(size() > 1 ? std::prev(end()) : end(), begin(), end()); } Polyline::segments_iterator Polyline::beginSegments() @@ -121,10 +119,7 @@ Polyline::segments_iterator Polyline::endSegments() { return segments_iterator(end(), begin(), end()); } - else - { - return segments_iterator(size() > 1 ? std::prev(end()) : end(), begin(), end()); - } + return segments_iterator(size() > 1 ? std::prev(end()) : end(), begin(), end()); } coord_t Polyline::length() const diff --git a/src/geometry/Shape.cpp b/src/geometry/Shape.cpp index ed5dffbbf0..61d029c4e2 100644 --- a/src/geometry/Shape.cpp +++ b/src/geometry/Shape.cpp @@ -181,14 +181,7 @@ Shape Shape::unionPolygons(const Shape& other, ClipperLib::PolyFillType fill_typ { return {}; } - if (empty() && other.size() <= 1) - { - return other; - } - if (other.empty() && size() <= 1) - { - return *this; - } + // No further early outs, as shapes should be able to be 'unioned' with themselves, which will resolve certain issues like self-overlapping polygons. ClipperLib::Paths ret; ClipperLib::Clipper clipper(clipper_init); addPaths(clipper, ClipperLib::ptSubject); @@ -203,14 +196,7 @@ Shape Shape::unionPolygons(const Polygon& polygon, ClipperLib::PolyFillType fill { return {}; } - if (empty()) - { - return Shape(polygon); - } - if (polygon.empty() && size() <= 1) - { - return *this; - } + // No further early outs, as unioning even with another empty polygon has some beneficial side-effects, such as removing self-overlapping polygons. ClipperLib::Paths ret; ClipperLib::Clipper clipper(clipper_init); addPaths(clipper, ClipperLib::ptSubject); diff --git a/src/infill/ZigzagConnectorProcessor.cpp b/src/infill/ZigzagConnectorProcessor.cpp index 5cc94e9845..7aadebc911 100644 --- a/src/infill/ZigzagConnectorProcessor.cpp +++ b/src/infill/ZigzagConnectorProcessor.cpp @@ -3,6 +3,7 @@ #include "infill/ZigzagConnectorProcessor.h" +#include #include #include "geometry/OpenPolyline.h" @@ -93,17 +94,14 @@ bool ZigzagConnectorProcessor::handleConnectorTooCloseToSegment(const coord_t sc { return false; } - else - { - return std::find_if( - current_connector_.begin(), - current_connector_.end(), - [scanline_x, min_distance_to_scanline](const Point2LL& point) - { - return std::abs(point.X - scanline_x) >= min_distance_to_scanline; - }) - == current_connector_.end(); - } + return std::find_if( + current_connector_.begin(), + current_connector_.end(), + [scanline_x, min_distance_to_scanline](const Point2LL& point) + { + return std::abs(point.X - scanline_x) >= min_distance_to_scanline; + }) + == current_connector_.end(); } void ZigzagConnectorProcessor::registerScanlineSegmentIntersection(const Point2LL& intersection, int scanline_index, coord_t min_distance_to_scanline) diff --git a/src/utils/AABB.cpp b/src/utils/AABB.cpp index 6707775c3f..163c6a5f26 100644 --- a/src/utils/AABB.cpp +++ b/src/utils/AABB.cpp @@ -3,6 +3,7 @@ #include "utils/AABB.h" +#include #include #include "geometry/Polygon.h"